Coverage Report

Created: 2026-09-14 06:37

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
137k
    : m_type(P_Unknown)
69
137k
    , m_id(-1)
70
137k
    , m_extra("")
71
137k
  {
72
137k
  }
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
11.8M
    : m_type(-1)
116
11.8M
    , m_format(0)
117
11.8M
    , m_pictEntry()
118
11.8M
    , m_dataSize(0)
119
11.8M
    , m_dim(0,0)
120
11.8M
    , m_date(0xFFFFFFFF)
121
11.8M
    , m_extra("")
122
11.8M
  {
123
11.8M
  }
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
297k
{
148
297k
  switch (m_type) {
149
233
  case 2:
150
233
    switch (m_format) {
151
51
    case 1:
152
58
    case 3: // must be roman
153
58
      listener.insertField(MWAWField(MWAWField::PageNumber));
154
58
      break;
155
125
    case 2:
156
128
    case 4: // must be roman
157
128
      listener.insertField(MWAWField(MWAWField::PageNumber));
158
128
      listener.insertUnicodeString(librevenge::RVNGString(" of "));
159
128
      listener.insertField(MWAWField(MWAWField::PageCount));
160
128
      break;
161
47
    default:
162
47
      listener.insertField(MWAWField(MWAWField::PageNumber));
163
47
      break;
164
233
    }
165
233
    return true;
166
345
  case 0x15:
167
368
  case 0x16: {
168
368
    MWAWField field(m_type==0x15 ? MWAWField::Date : MWAWField::Time);
169
368
    field.m_DTFormat=getDTFormat();
170
368
    listener.insertField(field);
171
368
    return true;
172
345
  }
173
297k
  default:
174
297k
    break;
175
297k
  }
176
297k
  return false;
177
297k
}
178
179
std::string Token::getDTFormat() const
180
368
{
181
368
  switch (m_type) {
182
345
  case 0x15:
183
345
    switch (m_format) {
184
0
    case 0xa:
185
0
      return "%m/%d/%y";
186
312
    case 0xb:
187
312
      return "%b %d, %Y";
188
1
    case 0xc:
189
1
      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
0
    case 0x10:
197
0
      return "%B %d";
198
0
    case 0x11:
199
0
      return "%a, %b %d, %Y";
200
7
    case 0x12:
201
7
      return "%A, %B %d, %Y";
202
22
    default:
203
22
      break;
204
345
    }
205
22
    break;
206
23
  case 0x16:
207
23
    switch (m_format) {
208
0
    case 0x14:
209
0
      return "%I:%M %p";
210
0
    case 0x15:
211
0
      return "%I:%M:%S %p";
212
3
    case 0x16:
213
3
      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
20
    default:
221
20
      break;
222
23
    }
223
20
    break;
224
20
  default:
225
0
    break;
226
368
  }
227
42
  return "";
228
368
}
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
807k
    : m_pos()
285
807k
    , m_page(0)
286
807k
    , m_extra("")
287
807k
  {
288
807k
  }
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
302k
    : m_type(-1)
311
302k
    , m_numFonts(0)
312
302k
    , m_numRulers(0)
313
302k
    , m_numLines(0)
314
302k
    , m_numTokens(0)
315
302k
    , m_numChar(0)
316
302k
    , m_numCharPLC(0)
317
302k
    , m_numFrames(0)
318
302k
    , m_fontList()
319
302k
    , m_rulerList()
320
302k
    , m_tokenList()
321
302k
    , m_frameList()
322
302k
    , m_textEntry()
323
302k
    , m_posPLCMap()
324
302k
    , m_parsed(false)
325
302k
    , m_extra("")
326
302k
  {
327
302k
  }
328
  //! returns true if this is the main zone
329
  bool isMain() const
330
68.5k
  {
331
68.5k
    return m_type==3;
332
68.5k
  }
333
  //! check if the data read are or not ok
334
  bool ok() const
335
86.0k
  {
336
86.0k
    if (m_type<0 || m_type > 5)
337
1.12k
      return false;
338
84.9k
    if (m_numFonts<=0 || m_numRulers<=0 || m_numLines<0 || m_numTokens<=0 || m_numChar<0 ||
339
75.4k
        m_numCharPLC<=0 || m_numFrames<0)
340
14.0k
      return false;
341
70.8k
    return true;
342
84.9k
  }
343
  //! returns the data size
344
  long size() const
345
70.8k
  {
346
70.8k
    return 22*m_numFonts+192*m_numRulers+6*m_numCharPLC
347
70.8k
           +18*m_numTokens+14*m_numLines+22*m_numFrames+m_numChar;
348
70.8k
  }
349
  //! returns true if the data has graphic
350
  bool hasGraphics() const
351
298
  {
352
326
    for (auto const &token : m_tokenList) {
353
326
      if (token.m_type==4)
354
0
        return true;
355
326
    }
356
298
    return false;
357
298
  }
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
115k
    : m_fileIdFontIdMap()
432
115k
    , m_zonesList()
433
115k
    , m_version(-1)
434
115k
    , m_numPages(-1)
435
115k
  {
436
115k
  }
437
  //! returns the font id corresponding to a fId
438
  int getFId(int fileId) const
439
2.95M
  {
440
2.95M
    if (m_fileIdFontIdMap.find(fileId)==m_fileIdFontIdMap.end())
441
2.80M
      return fileId;
442
141k
    return m_fileIdFontIdMap.find(fileId)->second;
443
2.95M
  }
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
115k
  : m_document(document)
459
115k
  , m_parserState(document.m_parserState)
460
115k
  , m_state(new GreatWksTextInternal::State)
461
115k
  , m_mainParser(&document.getMainParser())
462
115k
{
463
115k
}
464
465
GreatWksText::~GreatWksText()
466
115k
{ }
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
19.2k
{
477
19.2k
  if (m_state->m_numPages >= 0)
478
2.58k
    return m_state->m_numPages;
479
480
16.7k
  int nPages=1;
481
16.7k
  for (auto const &zone : m_state->m_zonesList) {
482
6.45k
    if (!zone.isMain() || zone.m_frameList.empty())
483
2.35k
      continue;
484
4.10k
    if (zone.m_frameList.back().m_page>1)
485
2.58k
      nPages = zone.m_frameList.back().m_page;
486
4.10k
    break;
487
6.45k
  }
488
489
16.7k
  return m_state->m_numPages = nPages;
490
19.2k
}
491
492
int GreatWksText::getFontId(int fileId) const
493
59.1k
{
494
59.1k
  return m_state->getFId(fileId);
495
59.1k
}
496
497
int GreatWksText::numHFZones() const
498
5.05k
{
499
5.05k
  int nHF=0;
500
6.45k
  for (auto const &zone : m_state->m_zonesList) {
501
6.45k
    if (zone.isMain())
502
5.05k
      break;
503
1.39k
    nHF++;
504
1.39k
  }
505
5.05k
  return nHF;
506
5.05k
}
507
508
bool GreatWksText::canSendTextBoxAsGraphic(MWAWEntry const &entry)
509
518
{
510
518
  if (!entry.valid())
511
35
    return false;
512
483
  MWAWInputStreamPtr &input= m_parserState->m_input;
513
483
  long pos=input->tell();
514
483
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
515
483
  GreatWksTextInternal::Zone zone;
516
483
  bool ok=!readZone(zone) ||!zone.hasGraphics();
517
483
  input->seek(pos, librevenge::RVNG_SEEK_SET);
518
483
  return ok;
519
518
}
520
521
bool GreatWksText::sendTextbox(MWAWEntry const &entry, MWAWListenerPtr listener)
522
49.3k
{
523
49.3k
  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
49.3k
  if (!entry.valid())
528
568
    return false;
529
48.7k
  MWAWInputStreamPtr &input= m_parserState->m_input;
530
48.7k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
531
48.7k
  GreatWksTextInternal::Zone zone;
532
48.7k
  if (readZone(zone)) {
533
4.00k
    sendZone(zone, listener);
534
4.00k
    return true;
535
4.00k
  }
536
537
44.7k
  return sendSimpleTextbox(entry, listener);
538
48.7k
}
539
540
////////////////////////////////////////////////////////////
541
// Intermediate level
542
////////////////////////////////////////////////////////////
543
bool GreatWksText::createZones(int expectedHF)
544
20.9k
{
545
20.9k
  MWAWInputStreamPtr &input= m_parserState->m_input;
546
20.9k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
547
20.9k
  libmwaw::DebugStream f;
548
549
20.9k
  long pos = input->tell();
550
20.9k
  f << "Entries(TZoneHeader):";
551
20.9k
  auto val=static_cast<int>(input->readULong(2));
552
20.9k
  if (val)
553
15.6k
    f << "numPages=" << val << ",";
554
20.9k
  val=static_cast<int>(input->readULong(2));
555
20.9k
  if (val) // related to number of header/footer?
556
11.9k
    f << "f0=" << val << ",";
557
20.9k
  f << "height[total]=" << input->readLong(4) << ","; // checkme
558
20.9k
  ascFile.addDelimiter(input->tell(),'|');
559
20.9k
  ascFile.addPos(pos);
560
20.9k
  ascFile.addNote(f.str().c_str());
561
562
20.9k
  pos += 68;
563
20.9k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
564
565
20.9k
  if (!readFontNames()) {
566
15.8k
    MWAW_DEBUG_MSG(("GreatWksText::createZones: can not find the font names\n"));
567
15.8k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
568
15.8k
  }
569
570
20.9k
  bool findMainZone=false;
571
20.9k
  int nAuxi=0;
572
58.4k
  while (!input->isEnd()) {
573
57.7k
    pos=input->tell();
574
57.7k
    GreatWksTextInternal::Zone zone;
575
57.7k
    if (!readZone(zone)) {
576
47.0k
      input->seek(pos,librevenge::RVNG_SEEK_SET);
577
47.0k
      if (findMainZone)
578
5.02k
        break;
579
580
42.0k
      if (!findNextZone() || !readZone(zone)) {
581
15.2k
        input->seek(pos,librevenge::RVNG_SEEK_SET);
582
15.2k
        break;
583
15.2k
      }
584
42.0k
    }
585
37.4k
    m_state->m_zonesList.push_back(zone);
586
37.4k
    if (zone.isMain())
587
5.06k
      findMainZone=true;
588
32.3k
    else
589
32.3k
      nAuxi++;
590
37.4k
  }
591
20.9k
  if (nAuxi!=expectedHF) {
592
5.37k
    MWAW_DEBUG_MSG(("GreatWksText::createZones: unexpected HF zones: %d/%d\n", nAuxi, expectedHF));
593
5.37k
  }
594
20.9k
  return findMainZone;
595
20.9k
}
596
597
bool GreatWksText::findNextZone()
598
42.0k
{
599
42.0k
  MWAWInputStreamPtr &input= m_parserState->m_input;
600
601
42.0k
  long searchPos=input->tell(), pos=searchPos;
602
42.0k
  int const headerSize=24+22+184;
603
42.0k
  if (!input->checkPosition(pos+headerSize))
604
2.47k
    return false;
605
606
  // first look for ruler
607
39.5k
  input->seek(pos+headerSize, librevenge::RVNG_SEEK_SET);
608
38.6M
  while (true) {
609
38.6M
    if (input->isEnd())
610
9.30k
      return false;
611
38.6M
    pos = input->tell();
612
38.6M
    unsigned long val=input->readULong(4);
613
38.6M
    if (val==0x20FFFF)
614
26.7k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
615
38.6M
    else if (val==0x20FFFFFF)
616
28.8k
      input->seek(pos-1, librevenge::RVNG_SEEK_SET);
617
38.6M
    else if (val==0xFFFFFFFF)
618
787k
      input->seek(pos-2, librevenge::RVNG_SEEK_SET);
619
37.8M
    else if (val==0xFFFFFF2E)
620
22.3k
      input->seek(pos-3, librevenge::RVNG_SEEK_SET);
621
37.7M
    else
622
37.7M
      continue;
623
865k
    if (input->readULong(4)!=0x20FFFF || input->readULong(4)!=0xFFFF2E00) {
624
835k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
625
835k
      continue;
626
835k
    }
627
    // ok a empty tabs stop
628
128k
    while (!input->isEnd()) {
629
128k
      pos = input->tell();
630
128k
      if (input->readULong(4)!=0x20FFFF || input->readULong(4)!=0xFFFF2E00) {
631
30.0k
        input->seek(pos, librevenge::RVNG_SEEK_SET);
632
30.0k
        break;
633
30.0k
      }
634
128k
    }
635
30.2k
    break;
636
865k
  }
637
638
30.2k
  pos=input->tell();
639
30.2k
  int nFonts=0;
640
30.2k
  GreatWksTextInternal::Zone zone;
641
1.17M
  while (true) {
642
1.17M
    long hSize=headerSize+22*nFonts++;
643
1.17M
    if (pos-hSize < searchPos)
644
3.46k
      break;
645
1.17M
    input->seek(pos-hSize, librevenge::RVNG_SEEK_SET);
646
1.17M
    if (input->readLong(4))
647
910k
      continue;
648
262k
    auto val=static_cast<int>(input->readULong(2));
649
262k
    if (val & 0xFEFE)
650
43.4k
      continue;
651
219k
    input->seek(2,librevenge::RVNG_SEEK_CUR);
652
219k
    if (input->readLong(2)!=nFonts)
653
187k
      continue;
654
31.7k
    input->seek(pos-hSize, librevenge::RVNG_SEEK_SET);
655
31.7k
    if (readZone(zone)) {
656
26.8k
      input->seek(pos-hSize, librevenge::RVNG_SEEK_SET);
657
26.8k
      return true;
658
26.8k
    }
659
31.7k
  }
660
661
3.46k
  MWAW_DEBUG_MSG(("GreatWksText::findNextZone: can not find begin of zone for pos=%lx\n", static_cast<long unsigned int>(pos)));
662
3.46k
  input->seek(searchPos, librevenge::RVNG_SEEK_SET);
663
3.46k
  return false;
664
30.2k
}
665
666
bool GreatWksText::readZone(GreatWksTextInternal::Zone &zone)
667
165k
{
668
165k
  zone=GreatWksTextInternal::Zone();
669
165k
  MWAWInputStreamPtr &input= m_parserState->m_input;
670
165k
  long pos=input->tell();
671
165k
  long endPos=pos+24;
672
165k
  if (!input->checkPosition(endPos))
673
868
    return false;
674
675
164k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
676
164k
  libmwaw::DebugStream f;
677
164k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
678
164k
  if (input->readLong(4)) {
679
67.1k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
680
67.1k
    return false;
681
67.1k
  }
682
286k
  for (int i=0; i < 2; ++i) {
683
193k
    auto val=static_cast<int>(input->readLong(1));
684
193k
    if (val==0) continue;
685
49.1k
    if (val!=1)
686
3.83k
      return false;
687
45.3k
    if (i==0)
688
5.30k
      f << "smartquote,";
689
40.0k
    else
690
40.0k
      f << "hidepict,";
691
45.3k
  }
692
93.6k
  zone.m_type=static_cast<int>(input->readLong(1));
693
93.6k
  if (input->readLong(1)) { // simple|complex field
694
7.57k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
695
7.57k
    return false;
696
7.57k
  }
697
86.0k
  zone.m_numFonts=static_cast<int>(input->readULong(2));
698
86.0k
  zone.m_numRulers=static_cast<int>(input->readULong(2));
699
86.0k
  zone.m_numCharPLC=static_cast<int>(input->readULong(2));
700
86.0k
  zone.m_numTokens=static_cast<int>(input->readULong(2));
701
86.0k
  zone.m_numLines=static_cast<int>(input->readULong(2));
702
86.0k
  zone.m_numFrames=static_cast<int>(input->readULong(2));
703
86.0k
  zone.m_numChar=long(input->readULong(4));
704
86.0k
  if (!zone.ok() || !input->checkPosition(endPos+zone.size())) {
705
17.4k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
706
17.4k
    return false;
707
17.4k
  }
708
68.5k
  zone.m_extra=f.str();
709
68.5k
  f.str("");
710
68.5k
  f << "Entries(HeaderText):" << zone;
711
68.5k
  if (input->tell()!=endPos) {
712
0
    ascFile.addDelimiter(input->tell(),'|');
713
0
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
714
0
  }
715
68.5k
  ascFile.addPos(pos);
716
68.5k
  ascFile.addNote(f.str().c_str());
717
718
2.95M
  for (int i=0; i<zone.m_numFonts; ++i) {
719
2.89M
    pos = input->tell();
720
2.89M
    f.str("");
721
2.89M
    f << "Entries(FontDef)-F" << i << ":";
722
723
2.89M
    MWAWFont font;
724
2.89M
    if (!readFont(font)) {
725
0
      f << "###";
726
0
      font = MWAWFont();
727
0
      input->seek(pos+22, librevenge::RVNG_SEEK_SET);
728
0
    }
729
2.89M
    zone.m_fontList.push_back(font);
730
2.89M
    f << font.getDebugString(m_parserState->m_fontConverter) << ",";
731
2.89M
    ascFile.addPos(pos);
732
2.89M
    ascFile.addNote(f.str().c_str());
733
2.89M
  }
734
68.5k
  MWAWParagraph para;
735
900k
  for (int i=0; i<zone.m_numRulers; ++i) {
736
832k
    pos = input->tell();
737
832k
    f.str("");
738
832k
    f << "Entries(RulerDef)-R" << i << ":";
739
832k
    if (!readRuler(para)) {
740
0
      f << "###";
741
0
      para = MWAWParagraph();
742
0
      input->seek(pos+192, librevenge::RVNG_SEEK_SET);
743
0
    }
744
832k
    zone.m_rulerList.push_back(para);
745
832k
    f << para;
746
832k
    ascFile.addPos(pos);
747
832k
    ascFile.addNote(f.str().c_str());
748
832k
  }
749
750
68.5k
  pos=input->tell();
751
68.5k
  f.str("");
752
68.5k
  f << "Entries(CharPLC):";
753
68.5k
  GreatWksTextInternal::PLC plc;
754
68.5k
  plc.m_type = GreatWksTextInternal::P_Font;
755
68.5k
  long textPos = 0;
756
20.3M
  for (int i=0; i < zone.m_numCharPLC; ++i) {
757
20.2M
    plc.m_id=static_cast<int>(input->readULong(2));
758
20.2M
    zone.m_posPLCMap.insert
759
20.2M
    (std::multimap<long, GreatWksTextInternal::PLC>::value_type(textPos, plc));
760
20.2M
    f << "F" << plc.m_id << ":" << std::hex << textPos << std::dec << ",";
761
20.2M
    textPos+= static_cast<int>(input->readULong(4));
762
20.2M
  }
763
68.5k
  ascFile.addPos(pos);
764
68.5k
  ascFile.addNote(f.str().c_str());
765
  // the token
766
68.5k
  plc.m_type = GreatWksTextInternal::P_Token;
767
68.5k
  textPos = 0;
768
2.53M
  for (int i=0; i < zone.m_numTokens; ++i) {
769
2.46M
    pos = input->tell();
770
2.46M
    f.str("");
771
2.46M
    plc.m_id=i;
772
2.46M
    f << "Entries(Token)-Tn" << i << ":";
773
2.46M
    GreatWksTextInternal::Token tkn;
774
2.46M
    long numC;
775
2.46M
    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
2.46M
    zone.m_posPLCMap.insert
782
2.46M
    (std::multimap<long, GreatWksTextInternal::PLC>::value_type(textPos, plc));
783
2.46M
    zone.m_tokenList.push_back(tkn);
784
2.46M
    f << tkn << ",pos=" << std::hex << textPos << std::dec;
785
2.46M
    textPos+= numC;
786
2.46M
    input->seek(pos+18, librevenge::RVNG_SEEK_SET);
787
2.46M
    ascFile.addPos(pos);
788
2.46M
    ascFile.addNote(f.str().c_str());
789
2.46M
  }
790
68.5k
  if (!readZonePositions(zone))
791
0
    return false;
792
  // fill the text entry
793
68.5k
  pos=input->tell();
794
68.5k
  zone.m_textEntry.setBegin(pos);
795
68.5k
  zone.m_textEntry.setLength(zone.m_numChar);
796
68.5k
  ascFile.addPos(pos);
797
68.5k
  ascFile.addNote("_");
798
68.5k
  pos += zone.m_numChar;
799
68.5k
  ascFile.addPos(pos);
800
68.5k
  ascFile.addNote("_");
801
802
1.88M
  for (auto &tkn : zone.m_tokenList) {
803
1.88M
    if (tkn.m_type != 4)
804
1.86M
      continue;
805
17.4k
    if (tkn.m_dataSize <= 0 || !input->checkPosition(pos+tkn.m_dataSize)) {
806
13.7k
      MWAW_DEBUG_MSG(("GreatWksText::readZone: can not determine the picture size\n"));
807
13.7k
      break;
808
13.7k
    }
809
3.67k
    tkn.m_pictEntry.setBegin(pos);
810
3.67k
    tkn.m_pictEntry.setLength(tkn.m_dataSize);
811
3.67k
    pos+=tkn.m_dataSize;
812
3.67k
  }
813
68.5k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
814
68.5k
  return true;
815
68.5k
}
816
817
bool GreatWksText::readZonePositions(GreatWksTextInternal::Zone &zone)
818
68.5k
{
819
68.5k
  MWAWInputStreamPtr &input= m_parserState->m_input;
820
68.5k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
821
68.5k
  libmwaw::DebugStream f;
822
823
  // the line
824
68.5k
  GreatWksTextInternal::PLC plc;
825
68.5k
  plc.m_type = GreatWksTextInternal::P_Ruler;
826
68.5k
  long textPos = 0;
827
68.5k
  std::vector<long> linesPos;
828
68.5k
  linesPos.push_back(0);
829
1.35M
  for (int i=0; i < zone.m_numLines; ++i) {
830
1.29M
    long pos=input->tell();
831
1.29M
    f.str("");
832
1.29M
    plc.m_id=static_cast<int>(input->readULong(2));
833
1.29M
    auto numC = long(input->readULong(4));
834
1.29M
    f << "y=" << double(input->readLong(4))/65536.;
835
1.29M
    f << "->" << double(input->readLong(4))/65536.;
836
1.29M
    plc.m_extra=f.str();
837
1.29M
    zone.m_posPLCMap.insert
838
1.29M
    (std::multimap<long, GreatWksTextInternal::PLC>::value_type(textPos, plc));
839
1.29M
    f.str("");
840
1.29M
    f << "Entries(Line)-L" << i << ":" << plc <<  ":"
841
1.29M
      << std::hex << textPos << std::dec;
842
1.29M
    textPos+=numC;
843
1.29M
    linesPos.push_back(textPos);
844
1.29M
    input->seek(pos+14, librevenge::RVNG_SEEK_SET);
845
1.29M
    ascFile.addPos(pos);
846
1.29M
    ascFile.addNote(f.str().c_str());
847
1.29M
  }
848
  // the page dimension
849
68.5k
  plc.m_type = GreatWksTextInternal::P_Page;
850
875k
  for (int i=0; i < zone.m_numFrames; ++i) {
851
807k
    GreatWksTextInternal::Frame frame;
852
807k
    long pos=input->tell();
853
807k
    plc.m_id=i;
854
807k
    f.str("");
855
807k
    float dim[4];
856
3.22M
    for (auto &d : dim) d=float(input->readLong(4))/65536.f;
857
807k
    frame.m_pos=MWAWBox2f(MWAWVec2f(dim[1],dim[0]),MWAWVec2f(dim[3],dim[2]));
858
807k
    auto val=static_cast<int>(input->readLong(2)); // always 0
859
807k
    if (val) f << "#unkn=" << val << ",";
860
807k
    frame.m_page=static_cast<int>(input->readLong(2));
861
807k
    auto line=static_cast<int>(input->readLong(2));
862
807k
    plc.m_extra=f.str();
863
807k
    if (line >= 0 && line < int(linesPos.size())) {
864
314k
      textPos=linesPos[size_t(line)];
865
314k
      zone.m_posPLCMap.insert
866
314k
      (std::multimap<long, GreatWksTextInternal::PLC>::value_type(textPos, plc));
867
314k
      if (textPos)
868
51.5k
        f << "pos=" << std::hex << textPos << std::dec;
869
314k
    }
870
492k
    else {
871
492k
      MWAW_DEBUG_MSG(("GreatWksText::readZone: can not find begin pos for page %d\n",line));
872
492k
      f << "##pos[line]=" << line << ",";
873
492k
    }
874
807k
    frame.m_extra=f.str();
875
807k
    zone.m_frameList.push_back(frame);
876
807k
    f.str("");
877
807k
    f << "Entries(TFrames)-" << i << ":" << frame;
878
807k
    input->seek(pos+22, librevenge::RVNG_SEEK_SET);
879
807k
    ascFile.addPos(pos);
880
807k
    ascFile.addNote(f.str().c_str());
881
807k
  }
882
68.5k
  return true;
883
68.5k
}
884
885
//////////////////////////////////////////////
886
// Fonts
887
//////////////////////////////////////////////
888
bool GreatWksText::readFontNames()
889
75.4k
{
890
75.4k
  MWAWInputStreamPtr &input= m_parserState->m_input;
891
75.4k
  long pos=input->tell();
892
75.4k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
893
75.4k
  libmwaw::DebugStream f;
894
75.4k
  f << "Entries(FontNames):";
895
75.4k
  auto sz= long(input->readULong(4));
896
75.4k
  long endPos = input->tell()+sz;
897
75.4k
  if (sz < 2 || !input->checkPosition(endPos)) {
898
45.7k
    MWAW_DEBUG_MSG(("GreatWksText::readFontNames: can not read field size\n"));
899
45.7k
    f << "###";
900
45.7k
    ascFile.addPos(pos);
901
45.7k
    ascFile.addNote(f.str().c_str());
902
45.7k
    return false;
903
45.7k
  }
904
29.6k
  auto N=static_cast<int>(input->readLong(2));
905
29.6k
  f << "N=" << N << ",";
906
29.6k
  if (N*5+2 > sz) {
907
1.67k
    MWAW_DEBUG_MSG(("GreatWksText::readFontNames: can not read N\n"));
908
1.67k
    f << "###";
909
1.67k
    ascFile.addPos(pos);
910
1.67k
    ascFile.addNote(f.str().c_str());
911
1.67k
    return false;
912
1.67k
  }
913
28.0k
  ascFile.addPos(pos);
914
28.0k
  ascFile.addNote(f.str().c_str());
915
2.66M
  for (int i=0; i < N; ++i) {
916
2.64M
    pos = input->tell();
917
2.64M
    f.str("");
918
2.64M
    f << "FontNames-" << i << ":";
919
2.64M
    auto fId=static_cast<int>(input->readULong(2));
920
2.64M
    f << "fId=" << fId << ",";
921
2.64M
    auto val=static_cast<int>(input->readLong(2)); // always 0 ?
922
2.64M
    if (val)
923
1.11M
      f << "unkn=" << val << ",";
924
2.64M
    auto fSz=static_cast<int>(input->readULong(1));
925
2.64M
    if (pos+5+fSz>endPos) {
926
7.14k
      MWAW_DEBUG_MSG(("GreatWksText::readFontNames: can not read font %d\n", i));
927
7.14k
      f << "###";
928
7.14k
      ascFile.addPos(pos);
929
7.14k
      ascFile.addNote(f.str().c_str());
930
7.14k
      input->seek(endPos, librevenge::RVNG_SEEK_SET);
931
7.14k
      return i>0;
932
7.14k
    }
933
2.63M
    std::string name("");
934
53.0M
    for (int c=0; c < fSz; ++c)
935
50.3M
      name+=char(input->readULong(1));
936
2.63M
    if (!name.empty())
937
907k
      m_state->m_fileIdFontIdMap[fId]=m_parserState->m_fontConverter->getId(name);
938
2.63M
    if ((fSz%2)==0)
939
2.14M
      input->seek(1, librevenge::RVNG_SEEK_CUR);
940
941
2.63M
    f << "\"" << name << "\",";
942
2.63M
    ascFile.addPos(pos);
943
2.63M
    ascFile.addNote(f.str().c_str());
944
2.63M
  }
945
20.8k
  pos = input->tell();
946
20.8k
  if (pos!=endPos) {
947
11.3k
    MWAW_DEBUG_MSG(("GreatWksText::readFontNames: find extra data\n"));
948
11.3k
    ascFile.addPos(pos);
949
11.3k
    ascFile.addNote("FontNames:###");
950
11.3k
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
951
11.3k
  }
952
20.8k
  return true;
953
28.0k
}
954
955
bool GreatWksText::readFont(MWAWFont &font)
956
2.89M
{
957
2.89M
  font=MWAWFont();
958
2.89M
  MWAWInputStreamPtr &input= m_parserState->m_input;
959
2.89M
  long pos=input->tell();
960
2.89M
  long endPos=pos+22;
961
2.89M
  if (!input->checkPosition(endPos))
962
0
    return false;
963
964
2.89M
  libmwaw::DebugStream f;
965
2.89M
  input->seek(pos, librevenge::RVNG_SEEK_SET);
966
2.89M
  auto val=static_cast<int>(input->readLong(2)); // small number between 0 and 5
967
2.89M
  if (val==0) f << "unused,";
968
1.95M
  else if (val!=1) f << "nbUsed=" << val << ",";
969
  // some dim dim[0]>dim[1]: minH, maxH ?
970
2.89M
  int dim[2];
971
5.78M
  for (auto &d : dim) d=static_cast<int>(input->readLong(2));
972
2.89M
  f << "dim?=" << dim[1] << "x" << dim[0] << ",";
973
974
8.67M
  for (int i=0; i < 2; ++i) { // f0=0|4(with italic), f1=0
975
5.78M
    val=static_cast<int>(input->readLong(2));
976
5.78M
    if (val) f << "f" << i << "=" << val << ",";
977
5.78M
  }
978
2.89M
  font.setId(m_state->getFId(static_cast<int>(input->readULong(2))));
979
2.89M
  auto flag =static_cast<int>(input->readULong(2));
980
2.89M
  uint32_t flags=0;
981
2.89M
  if (flag&0x1) flags |= MWAWFont::boldBit;
982
2.89M
  if (flag&0x2) flags |= MWAWFont::italicBit;
983
2.89M
  if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
984
2.89M
  if (flag&0x8) flags |= MWAWFont::embossBit;
985
2.89M
  if (flag&0x10) flags |= MWAWFont::shadowBit;
986
2.89M
  if (flag&0x20) font.setDeltaLetterSpacing(-1);
987
2.89M
  if (flag&0x40) font.setDeltaLetterSpacing(1);
988
2.89M
  if (flag&0x100) font.set(MWAWFont::Script::super100());
989
2.89M
  if (flag&0x200) font.set(MWAWFont::Script::sub100());
990
2.89M
  if (flag&0x800) font.setStrikeOutStyle(MWAWFont::Line::Simple);
991
2.89M
  if (flag&0x2000) {
992
792k
    font.setUnderlineStyle(MWAWFont::Line::Simple);
993
792k
    font.setUnderlineType(MWAWFont::Line::Double);
994
792k
  }
995
2.89M
  flag &=0xD480;
996
2.89M
  if (flag) f << "#fl=" << std::hex << flag << std::dec << ",";
997
2.89M
  font.setFlags(flags);
998
2.89M
  font.setSize(float(input->readULong(2)));
999
2.89M
  unsigned char color[3];
1000
8.67M
  for (auto &c : color) c = static_cast<unsigned char>(input->readULong(2)>>8);
1001
2.89M
  font.setColor(MWAWColor(color[0],color[1],color[2]));
1002
2.89M
  font.m_extra=f.str();
1003
1004
2.89M
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
1005
2.89M
  return true;
1006
2.89M
}
1007
//////////////////////////////////////////////
1008
// Ruler
1009
//////////////////////////////////////////////
1010
bool GreatWksText::readRuler(MWAWParagraph &para)
1011
832k
{
1012
832k
  para = MWAWParagraph();
1013
832k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1014
832k
  long pos=input->tell();
1015
832k
  long endPos=pos+192;
1016
832k
  if (!input->checkPosition(endPos))
1017
0
    return false;
1018
1019
832k
  libmwaw::DebugStream f;
1020
832k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1021
832k
  auto val=static_cast<int>(input->readLong(2));
1022
832k
  if (val==0) f << "unused,";
1023
598k
  else if (val!=1) f << "nbUsed=" << val << ",";
1024
832k
  val=static_cast<int>(input->readLong(2));
1025
832k
  switch (val) {
1026
292k
  case 0:
1027
292k
    break;
1028
69.6k
  case 1:
1029
69.6k
    para.m_justify = MWAWParagraph::JustificationCenter;
1030
69.6k
    break;
1031
6.39k
  case 2:
1032
6.39k
    para.m_justify = MWAWParagraph::JustificationRight ;
1033
6.39k
    break;
1034
5.78k
  case 3:
1035
5.78k
    para.m_justify = MWAWParagraph::JustificationFull;
1036
5.78k
    break;
1037
457k
  default:
1038
457k
    f << "#align" << val << ",";
1039
457k
    break;
1040
832k
  }
1041
832k
  para.m_marginsUnit=librevenge::RVNG_POINT;
1042
832k
  for (auto &margin : para.m_margins)
1043
2.49M
    margin = double(input->readLong(4))/65536.;
1044
832k
  para.m_margins[0]=*para.m_margins[0]-*para.m_margins[1];
1045
832k
  double spacing[3];
1046
832k
  for (double &i : spacing)
1047
2.49M
    i = double(input->readLong(4))/65536.;
1048
832k
  int unit[3];
1049
2.49M
  for (auto &u : unit) u = static_cast<int>(input->readLong(1));
1050
832k
  switch (unit[0]) {
1051
29.2k
  case 1:
1052
44.4k
  case 2:
1053
51.5k
  case 3:
1054
64.6k
  case 4:
1055
73.9k
  case 5:
1056
73.9k
    para.setInterline(spacing[0], librevenge::RVNG_POINT);
1057
73.9k
    break;
1058
15.2k
  case 6:
1059
15.2k
    para.setInterline(spacing[0], librevenge::RVNG_PERCENT);
1060
15.2k
    break;
1061
743k
  default:
1062
743k
    f << "#interline=" << spacing[0] << "[unit=" << unit[0] << "],";
1063
832k
  }
1064
2.49M
  for (int w=1; w < 3; ++w) {
1065
1.66M
    if (unit[w]==6)
1066
10.9k
      para.m_spacings[w]=spacing[w]*12./72.;
1067
1.65M
    else if (unit[w]>0 && unit[w]<6)
1068
215k
      para.m_spacings[w]=spacing[w]/72.;
1069
1.43M
    else
1070
1.43M
      f << "#spac" << w << "=" << spacing[w] << "[unit=" << unit[w] << "],";
1071
1.66M
  }
1072
832k
  val = static_cast<int>(input->readLong(1));
1073
832k
  if (val) f << "#f0=" << val << ",";
1074
1075
17.4M
  for (int i=0; i < 20; ++i) {
1076
16.6M
    MWAWTabStop tab;
1077
16.6M
    val = static_cast<int>(input->readLong(1));
1078
16.6M
    switch (val) {
1079
8.09M
    case 0: // left
1080
8.09M
      break;
1081
793k
    case 1:
1082
793k
      tab.m_alignment = MWAWTabStop::CENTER;
1083
793k
      break;
1084
305k
    case 2:
1085
305k
      tab.m_alignment = MWAWTabStop::RIGHT;
1086
305k
      break;
1087
140k
    case 3:
1088
140k
      tab.m_alignment = MWAWTabStop::DECIMAL;
1089
140k
      break;
1090
7.31M
    default:
1091
7.31M
      f << "#tab" << i << "[align]=" << val << ",";
1092
7.31M
      break;
1093
16.6M
    }
1094
16.6M
    auto leaderChar = char(input->readULong(1));
1095
16.6M
    if (leaderChar) {
1096
9.18M
      int unicode= m_parserState->m_fontConverter->unicode(3, static_cast<unsigned char>(leaderChar));
1097
9.18M
      if (unicode==-1)
1098
3.17M
        tab.m_leaderCharacter = uint16_t(leaderChar);
1099
6.01M
      else
1100
6.01M
        tab.m_leaderCharacter = uint16_t(unicode);
1101
9.18M
    }
1102
16.6M
    long tPos=input->readLong(4);
1103
16.6M
    if (tPos==-1) {
1104
671k
      input->seek(2, librevenge::RVNG_SEEK_CUR);
1105
671k
      continue;
1106
671k
    }
1107
15.9M
    tab.m_position=double(tPos)/72./65536.;
1108
15.9M
    auto decimalChar = char(input->readULong(1));
1109
15.9M
    if (decimalChar) {
1110
8.47M
      int unicode= m_parserState->m_fontConverter->unicode(3, static_cast<unsigned char>(decimalChar));
1111
8.47M
      if (unicode==-1)
1112
3.40M
        tab.m_decimalCharacter = uint16_t(decimalChar);
1113
5.06M
      else
1114
5.06M
        tab.m_decimalCharacter = uint16_t(unicode);
1115
8.47M
    }
1116
15.9M
    val = static_cast<int>(input->readLong(1));
1117
15.9M
    if (val) f << "#tab" << i << "[f0=" << val << ",";
1118
15.9M
    para.m_tabs->push_back(tab);
1119
15.9M
  }
1120
832k
  para.m_extra=f.str();
1121
1122
832k
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
1123
832k
  return true;
1124
832k
}
1125
1126
//////////////////////////////////////////////
1127
// Token
1128
//////////////////////////////////////////////
1129
bool GreatWksText::readToken(GreatWksTextInternal::Token &token, long &nChar)
1130
2.46M
{
1131
2.46M
  token = GreatWksTextInternal::Token();
1132
2.46M
  MWAWInputStreamPtr &input= m_parserState->m_input;
1133
2.46M
  long pos=input->tell();
1134
2.46M
  long endPos=pos+18;
1135
2.46M
  if (!input->checkPosition(endPos))
1136
0
    return false;
1137
1138
2.46M
  libmwaw::DebugStream f;
1139
2.46M
  token.m_type=static_cast<int>(input->readULong(1));
1140
2.46M
  token.m_format=static_cast<int>(input->readULong(1));
1141
2.46M
  nChar=input->readLong(4);
1142
2.46M
  if (token.m_type==0x15||token.m_type==0x16)
1143
61.9k
    token.m_date=static_cast<uint32_t>(input->readULong(4));
1144
2.40M
  else if (token.m_type==4) {
1145
37.5k
    token.m_dataSize = input->readLong(4);
1146
37.5k
    float dim[2];
1147
75.0k
    for (auto &d : dim) d = float(input->readLong(4))/65536.f;
1148
37.5k
    token.m_dim=MWAWVec2f(dim[0],dim[1]); // checkme
1149
37.5k
  }
1150
2.46M
  int nUnread=int(endPos-input->tell())/2;
1151
16.9M
  for (int i=0; i < nUnread; i++) {
1152
14.4M
    auto val=static_cast<int>(input->readLong(2));
1153
14.4M
    if (val) f << "f" << i << "=" << val << ",";
1154
14.4M
  }
1155
2.46M
  token.m_extra=f.str();
1156
1157
2.46M
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
1158
2.46M
  return true;
1159
2.46M
}
1160
1161
//////////////////////////////////////////////
1162
// send data
1163
//////////////////////////////////////////////
1164
bool GreatWksText::sendMainText()
1165
11.1k
{
1166
11.1k
  for (auto const &zone : m_state->m_zonesList) {
1167
6.45k
    if (!zone.isMain())
1168
1.39k
      continue;
1169
5.05k
    return sendZone(zone);
1170
6.45k
  }
1171
6.10k
  return false;
1172
11.1k
}
1173
1174
bool GreatWksText::sendHF(int id)
1175
1.30k
{
1176
1.30k
  for (auto const &zone : m_state->m_zonesList) {
1177
1.30k
    if (zone.isMain())
1178
0
      continue;
1179
1.30k
    if (id--==0)
1180
1.30k
      return sendZone(zone);
1181
1.30k
  }
1182
0
  MWAW_DEBUG_MSG(("GreatWksText::sendHF: can not find a header/footer\n"));
1183
0
  return false;
1184
1.30k
}
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
44.7k
{
1202
44.7k
  if (!listener)
1203
885
    listener=m_parserState->getMainListener();
1204
44.7k
  if (!listener || !listener->canWriteText()) {
1205
0
    MWAW_DEBUG_MSG(("GreatWksText::sendSimpleTextbox: can not find a listener\n"));
1206
0
    return false;
1207
0
  }
1208
44.7k
  if (entry.length()<51) {
1209
3.94k
    MWAW_DEBUG_MSG(("GreatWksText::sendSimpleTextbox: the entry seems to short\n"));
1210
3.94k
    return false;
1211
3.94k
  }
1212
40.8k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1213
40.8k
  long pos=entry.begin();
1214
40.8k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1215
1216
40.8k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1217
40.8k
  libmwaw::DebugStream f;
1218
40.8k
  f << "Entries(Texbox):";
1219
40.8k
  bool ok = input->readLong(4)==0;
1220
56.0k
  for (int i=0; ok && i < 2; ++i) {
1221
16.2k
    auto val=static_cast<int>(input->readLong(1));
1222
16.2k
    if (val==0) continue;
1223
2.26k
    if (val!=1) {
1224
992
      f << "#fl" << i << "=" << val << ",";
1225
992
      ok = false;
1226
992
      break;
1227
992
    }
1228
1.27k
    if (i==0)
1229
653
      f << "smartquote,";
1230
619
    else
1231
619
      f << "hidepict,";
1232
1.27k
  }
1233
40.8k
  auto type=static_cast<int>(input->readLong(1));
1234
40.8k
  if (ok) {
1235
7.34k
    if (type<0 || type>5) {
1236
816
      f << "#type=" << type << ",";
1237
816
      ok = false;
1238
816
    }
1239
6.53k
    else  if (type==1)
1240
688
      f << "textbox[draw],";
1241
5.84k
    else if (type!=2)
1242
5.57k
      f << "type=" << type << ",";
1243
7.34k
  }
1244
40.8k
  if (ok) ok=input->readLong(1)==1;
1245
1246
40.8k
  if (!ok) {
1247
38.8k
    MWAW_DEBUG_MSG(("GreatWksText::sendSimpleTextbox: the header seems bad\n"));
1248
38.8k
    f << "###";
1249
38.8k
    ascFile.addPos(pos);
1250
38.8k
    ascFile.addNote(f.str().c_str());
1251
38.8k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1252
38.8k
    return false;
1253
38.8k
  }
1254
1.92k
  MWAWFont font;
1255
1.92k
  font.setId(m_state->getFId(static_cast<int>(input->readULong(2))));
1256
1.92k
  auto flag =static_cast<int>(input->readULong(2));
1257
1.92k
  uint32_t flags=0;
1258
1.92k
  if (flag&0x1) flags |= MWAWFont::boldBit;
1259
1.92k
  if (flag&0x2) flags |= MWAWFont::italicBit;
1260
1.92k
  if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
1261
1.92k
  if (flag&0x8) flags |= MWAWFont::embossBit;
1262
1.92k
  if (flag&0x10) flags |= MWAWFont::shadowBit;
1263
1.92k
  if (flag&0x20) font.setDeltaLetterSpacing(-1);
1264
1.92k
  if (flag&0x40) font.setDeltaLetterSpacing(1);
1265
1.92k
  if (flag&0x100) font.set(MWAWFont::Script::super100());
1266
1.92k
  if (flag&0x200) font.set(MWAWFont::Script::sub100());
1267
1.92k
  if (flag&0x800) font.setStrikeOutStyle(MWAWFont::Line::Simple);
1268
1.92k
  if (flag&0x2000) {
1269
641
    font.setUnderlineStyle(MWAWFont::Line::Simple);
1270
641
    font.setUnderlineType(MWAWFont::Line::Double);
1271
641
  }
1272
1.92k
  flag &=0xD480;
1273
1.92k
  if (flag) f << "font[#fl]=" << std::hex << flag << std::dec << ",";
1274
1.92k
  font.setFlags(flags);
1275
1.92k
  font.setSize(float(input->readULong(2)));
1276
1.92k
  unsigned char color[3];
1277
5.76k
  for (auto &c : color) c = static_cast<unsigned char>(input->readULong(2)>>8);
1278
1.92k
  font.setColor(MWAWColor(color[0],color[1],color[2]));
1279
1.92k
  f << "font=[" << font.getDebugString(m_parserState->m_fontConverter) << "],";
1280
1.92k
  listener->setFont(font);
1281
1282
1.92k
  int val;
1283
1.92k
  f << "unkn=[" << std::hex;
1284
13.4k
  for (int i=0; i<6; i++) { // junk ?
1285
11.5k
    val=static_cast<int>(input->readULong(2));
1286
11.5k
    if (val)
1287
6.59k
      f << val << ",";
1288
4.92k
    else
1289
4.92k
      f << "_,";
1290
11.5k
  }
1291
1.92k
  f << std::dec << "],";
1292
1.92k
  MWAWParagraph para;
1293
1.92k
  val=static_cast<int>(input->readLong(2));
1294
1.92k
  switch (val) {
1295
978
  case 0:
1296
978
    break;
1297
19
  case 1:
1298
19
    para.m_justify = MWAWParagraph::JustificationCenter;
1299
19
    break;
1300
25
  case 2:
1301
25
    para.m_justify = MWAWParagraph::JustificationRight ;
1302
25
    break;
1303
116
  case 3:
1304
116
    para.m_justify = MWAWParagraph::JustificationFull;
1305
116
    break;
1306
782
  default:
1307
782
    f << "#align" << val << ",";
1308
782
    break;
1309
1.92k
  }
1310
1.92k
  f << para <<",";
1311
1.92k
  listener->setParagraph(para);
1312
1.92k
  double dim[4];
1313
7.68k
  for (auto &d : dim) d=double(input->readLong(4))/65536.;
1314
1.92k
  f << "dim=" << dim[1] << "x" << dim[0] << "<->" << dim[3] << "x" << dim[2] << "," ;
1315
1.92k
  auto fSz=static_cast<int>(input->readULong(1));
1316
1.92k
  if (50+fSz > entry.length()) {
1317
78
    MWAW_DEBUG_MSG(("GreatWksText::sendSimpleTextbox: the text size seems too big\n"));
1318
78
    f << "###";
1319
78
    ascFile.addPos(pos);
1320
78
    ascFile.addNote(f.str().c_str());
1321
78
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1322
78
    return false;
1323
78
  }
1324
1325
1.84k
  ascFile.addPos(pos);
1326
1.84k
  ascFile.addNote(f.str().c_str());
1327
1328
1.84k
  pos=input->tell();
1329
1.84k
  f.str("");
1330
1.84k
  f << "Entries(Text):";
1331
96.3k
  for (int i=0; i < fSz; ++i) {
1332
94.4k
    auto c = char(input->readULong(1));
1333
94.4k
    f << c;
1334
94.4k
    switch (c) {
1335
3.91k
    case 0x9:
1336
3.91k
      listener->insertTab();
1337
3.91k
      break;
1338
10.0k
    case 0xd:
1339
10.0k
      listener->insertEOL();
1340
10.0k
      break;
1341
80.4k
    default:
1342
80.4k
      listener->insertCharacter(static_cast<unsigned char>(c));
1343
80.4k
      break;
1344
94.4k
    }
1345
94.4k
  }
1346
1.84k
  input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
1347
1.84k
  ascFile.addPos(pos);
1348
1.84k
  ascFile.addNote(f.str().c_str());
1349
1.84k
  return true;
1350
1.84k
}
1351
1352
bool GreatWksText::sendZone(GreatWksTextInternal::Zone const &zone, MWAWListenerPtr listener)
1353
10.3k
{
1354
10.3k
  bool inLocalZone=false;
1355
10.3k
  if (listener)
1356
2.82k
    inLocalZone=true;
1357
7.54k
  else
1358
7.54k
    listener=m_parserState->getMainListener();
1359
10.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
10.3k
  bool isMain = zone.isMain();
1364
10.3k
  int actPage = 1, actCol = 0, numCol=1;
1365
10.3k
  if (isMain && !listener->canOpenSectionAddBreak()) {
1366
11
    MWAW_DEBUG_MSG(("GreatWksText::sendZone: in a main zone, but can not open section\n"));
1367
11
    isMain = false;
1368
11
  }
1369
10.3k
  else if (isMain) {
1370
5.05k
    m_document.newPage(1);
1371
5.05k
    MWAWSection sec=m_document.getMainSection();
1372
5.05k
    numCol = sec.numColumns();
1373
5.05k
    if (numCol>1) {
1374
3.75k
      if (listener->isSectionOpened())
1375
0
        listener->closeSection();
1376
3.75k
      listener->openSection(sec);
1377
3.75k
    }
1378
5.05k
  }
1379
10.3k
  if (!zone.m_textEntry.valid())
1380
651
    return true;
1381
9.72k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1382
9.72k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1383
9.72k
  libmwaw::DebugStream f;
1384
9.72k
  f << "Entries(Text):";
1385
9.72k
  zone.m_parsed=true;
1386
9.72k
  long pos = zone.m_textEntry.begin(), endPos = zone.m_textEntry.end();
1387
9.72k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1388
1389
9.72k
  long cPos=0;
1390
6.89M
  while (1) {
1391
6.89M
    long actPos = input->tell();
1392
6.89M
    bool done = input->isEnd() || actPos==endPos;
1393
1394
6.89M
    char c = done ? char(0) : char(input->readULong(1));
1395
6.89M
    if (pos!=actPos && (c==0xd || done)) {
1396
63.5k
      ascFile.addPos(pos);
1397
63.5k
      ascFile.addNote(f.str().c_str());
1398
63.5k
      f.str("");
1399
63.5k
      f << "Text:";
1400
63.5k
      pos = actPos+1;
1401
63.5k
    }
1402
6.89M
    if (done) break;
1403
1404
6.88M
    auto pIt=zone.m_posPLCMap.find(cPos);
1405
6.88M
    GreatWksTextInternal::Token token;
1406
7.15M
    while (pIt!=zone.m_posPLCMap.end() && pIt->first==cPos) {
1407
267k
      GreatWksTextInternal::PLC const &plc=(pIt++)->second;
1408
267k
      f << "[" << plc << "]";
1409
267k
      switch (plc.m_type) {
1410
38.3k
      case GreatWksTextInternal::P_Font:
1411
38.3k
        if (plc.m_id < 0 || plc.m_id >= static_cast<int>(zone.m_fontList.size())) {
1412
5.46k
          MWAW_DEBUG_MSG(("GreatWksText::sendZone: can not find font %d\n", plc.m_id));
1413
5.46k
          break;
1414
5.46k
        }
1415
32.8k
        listener->setFont(zone.m_fontList[size_t(plc.m_id)]);
1416
32.8k
        break;
1417
42.0k
      case GreatWksTextInternal::P_Ruler:
1418
42.0k
        if (plc.m_id < 0 || plc.m_id >= static_cast<int>(zone.m_rulerList.size())) {
1419
4.33k
          MWAW_DEBUG_MSG(("GreatWksText::sendZone: can not find ruler %d\n", plc.m_id));
1420
4.33k
          break;
1421
4.33k
        }
1422
37.7k
        listener->setParagraph(zone.m_rulerList[size_t(plc.m_id)]);
1423
37.7k
        break;
1424
30.8k
      case GreatWksTextInternal::P_Token:
1425
30.8k
        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
30.8k
        token=zone.m_tokenList[size_t(plc.m_id)];
1430
30.8k
        break;
1431
155k
      case GreatWksTextInternal::P_Page:
1432
155k
      case GreatWksTextInternal::P_Unknown:
1433
#if !defined(__clang__)
1434
      default:
1435
#endif
1436
155k
        break;
1437
267k
      }
1438
267k
    }
1439
6.88M
    if (c!=0xd) f << c;
1440
6.88M
    switch (c) {
1441
73.7k
    case 0x4: {
1442
73.7k
      f << "[pict]";
1443
73.7k
      if (token.m_type!=4 || !token.m_pictEntry.valid()) {
1444
73.7k
        MWAW_DEBUG_MSG(("GreatWksText::sendZone: can not find a picture\n"));
1445
73.7k
        f << "###";
1446
73.7k
        break;
1447
73.7k
      }
1448
17
      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
17
      MWAWPosition pictPos(MWAWVec2f(0,0), token.m_dim, librevenge::RVNG_POINT);
1453
17
      pictPos.setRelativePosition(MWAWPosition::Char, MWAWPosition::XLeft, MWAWPosition::YBottom);
1454
17
      m_document.sendPicture(token.m_pictEntry, pictPos);
1455
17
      break;
1456
17
    }
1457
30.1k
    case 0x9:
1458
30.1k
      listener->insertTab();
1459
30.1k
      break;
1460
25.3k
    case 0xb:
1461
25.3k
      f << "[colbreak]";
1462
25.3k
      if (!isMain) {
1463
12.5k
        MWAW_DEBUG_MSG(("GreatWksText::sendZone: find column break in auxilliary block\n"));
1464
12.5k
        break;
1465
12.5k
      }
1466
12.7k
      if (actCol < numCol-1 && numCol > 1) {
1467
9.79k
        listener->insertBreak(MWAWListener::ColumnBreak);
1468
9.79k
        actCol++;
1469
9.79k
      }
1470
2.94k
      else {
1471
2.94k
        actCol = 0;
1472
2.94k
        m_document.newPage(++actPage);
1473
2.94k
      }
1474
12.7k
      break;
1475
34.8k
    case 0xc:
1476
34.8k
      f << "[pagebreak]";
1477
34.8k
      if (!isMain) {
1478
13.8k
        f << "###";
1479
13.8k
        MWAW_DEBUG_MSG(("GreatWksText::sendZone: find page break in auxilliary zone\n"));
1480
13.8k
        break;
1481
13.8k
      }
1482
20.9k
      m_document.newPage(++actPage);
1483
20.9k
      actCol = 0;
1484
20.9k
      break;
1485
77.2k
    case 0xd:
1486
77.2k
      listener->insertEOL();
1487
77.2k
      break;
1488
260k
    case 2: // page number
1489
282k
    case 0x15: // date
1490
297k
    case 0x16: // time
1491
297k
      if (token.sendTo(*listener))
1492
601
        break;
1493
297k
      f << "###";
1494
297k
      MWAW_DEBUG_MSG(("GreatWksText::sendZone: can not send token\n"));
1495
297k
      break;
1496
6.34M
    default:
1497
6.34M
      listener->insertCharacter(static_cast<unsigned char>(c));
1498
6.34M
      break;
1499
6.88M
    }
1500
6.88M
    cPos++;
1501
6.88M
  }
1502
9.72k
  return true;
1503
9.72k
}
1504
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: