Coverage Report

Created: 2026-10-28 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/MsWks3Text.cxx
Line
Count
Source
1
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2
3
/* libmwaw
4
* Version: MPL 2.0 / LGPLv2+
5
*
6
* The contents of this file are subject to the Mozilla Public License Version
7
* 2.0 (the "License"); you may not use this file except in compliance with
8
* the License or as specified alternatively below. You may obtain a copy of
9
* the License at http://www.mozilla.org/MPL/
10
*
11
* Software distributed under the License is distributed on an "AS IS" basis,
12
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
* for the specific language governing rights and limitations under the
14
* License.
15
*
16
* Major Contributor(s):
17
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20
* Copyright (C) 2006, 2007 Andrew Ziem
21
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22
*
23
*
24
* All Rights Reserved.
25
*
26
* For minor contributions see the git repository.
27
*
28
* Alternatively, the contents of this file may be used under the terms of
29
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30
* in which case the provisions of the LGPLv2+ are applicable
31
* instead of those above.
32
*/
33
34
#include <iomanip>
35
#include <iostream>
36
#include <limits>
37
#include <map>
38
#include <sstream>
39
40
#include <librevenge/librevenge.h>
41
42
#include "MWAWDebug.hxx"
43
#include "MWAWFont.hxx"
44
#include "MWAWFontConverter.hxx"
45
#include "MWAWListener.hxx"
46
#include "MWAWParagraph.hxx"
47
#include "MWAWParser.hxx"
48
#include "MWAWSubDocument.hxx"
49
50
#include "MsWksDocument.hxx"
51
52
#include "MsWks3Text.hxx"
53
54
/** Internal: the structures of a MsWks3Text */
55
namespace MsWks3TextInternal
56
{
57
////////////////////////////////////////
58
//! Internal: header zone
59
struct LineZone {
60
  //! the constructor
61
  LineZone()
62
134M
    : m_type(-1)
63
134M
    , m_pos()
64
134M
    , m_id(0)
65
    , m_flags()
66
134M
    , m_height(0)
67
134M
  {
68
134M
  }
69
  //! operator<<
70
  friend std::ostream &operator<<(std::ostream &o, LineZone const &z)
71
0
  {
72
0
    switch (z.m_type>>5) {
73
0
    case 0:
74
0
    case 1:
75
0
    case 2:
76
0
    case 3:
77
0
      o << "Text,";
78
0
      break;
79
0
    case 4:
80
0
      o << "Tabs,";
81
0
      break;
82
0
    default:
83
0
      o << "##type=" << std::hex << (z.m_type >> 5) << std::dec << ",";
84
0
    }
85
0
    if (z.m_type&0x8) o << "note,";
86
0
    if (z.m_type&0x7) o << "#type=" << std::hex << (z.m_type&0x7) << std::dec << ",";
87
0
    if (z.m_id) o  << "id=" << z.m_id << ",";
88
0
    if (z.m_flags&1) o << "softBreak,";
89
0
    if (z.m_flags&2) o << "hardBreak,";
90
0
    if (!(z.m_flags&4)) o << "beginDoc,";
91
0
    if (z.m_flags&8) o << "graphics,";
92
0
    if (z.m_flags&0x20) o << "noteFl,";
93
0
    if (z.m_flags&0x40) o << "fields,";
94
0
    if (z.m_flags&0x90) o << "#flags=" << std::hex << (z.m_flags&0x90) << std::dec << ",";
95
0
    if (z.m_height) o << "h=" << z.m_height << ",";
96
0
    return o;
97
0
  }
98
  //! return true if this is a note
99
  bool isNote() const
100
1.43M
  {
101
1.43M
    return (m_type&0x8)==0x8;
102
1.43M
  }
103
  //! return true if this is a tabs
104
  bool isRuler() const
105
40.4k
  {
106
40.4k
    return (m_type&0xE0)==0x80;
107
40.4k
  }
108
  //! the type
109
  int m_type;
110
  //! the file position
111
  MWAWEntry m_pos;
112
  //! the id
113
  int m_id;
114
  //! the zone flags
115
  int m_flags;
116
  //! the height
117
  int m_height;
118
};
119
120
////////////////////////////////////////
121
//! Internal: the fonts
122
struct Font {
123
  //! the constructor
124
2.42M
  Font(): m_font(), m_extra("")
125
2.42M
  {
126
7.26M
    for (int &flag : m_flags) flag = 0;
127
2.42M
  }
128
129
  //! operator<<
130
  friend std::ostream &operator<<(std::ostream &o, Font const &font)
131
0
  {
132
0
    for (int i = 0; i < 3; i++) {
133
0
      if (!font.m_flags[i]) continue;
134
0
      o << "ft" << i << "=";
135
0
      if (i == 0) o << std::hex;
136
0
      o << font.m_flags[i] << std::dec << ",";
137
0
    }
138
0
    if (font.m_extra.length())
139
0
      o << font.m_extra << ",";
140
0
    return o;
141
0
  }
142
143
  //! the font
144
  MWAWFont m_font;
145
  //! some unknown flag
146
  int m_flags[3];
147
  //! extra data
148
  std::string m_extra;
149
};
150
151
////////////////////////////////////////
152
//! Internal: the text zone
153
struct TextZone {
154
  enum Type { Header, Footer, Main, Unknown };
155
  //! constructor
156
  TextZone()
157
68.1M
    : m_type(Unknown)
158
68.1M
    , m_id(0)
159
68.1M
    , m_zonesList()
160
68.1M
    , m_linesHeight()
161
68.1M
    , m_pagesHeight()
162
68.1M
    , m_pagesPosition()
163
68.1M
    , m_footnoteMap()
164
68.1M
    , m_text("")
165
68.1M
    , m_isSent(false)
166
68.1M
  {
167
68.1M
  }
168
169
  //! return true if this is the main zone
170
  bool isMain() const
171
132k
  {
172
132k
    return m_type == Main;
173
132k
  }
174
  //! the zone type;
175
  int m_type;
176
  //! the zone id
177
  int m_id;
178
  //! the list of zones
179
  std::vector<LineZone> m_zonesList;
180
  //! the line height
181
  std::vector<int> m_linesHeight;
182
  //! the pages height
183
  std::vector<int> m_pagesHeight;
184
  //! the zone id -> hard break
185
  std::map<int, bool> m_pagesPosition;
186
  //! the note id -> zone limit
187
  std::map<int, MWAWVec2i> m_footnoteMap;
188
  //! a string used to store v1-2 files header/footer
189
  std::string m_text;
190
  //! flag to know if the zone is send or not
191
  bool m_isSent;
192
};
193
194
////////////////////////////////////////
195
//! Internal: the state of a MsWks3Text
196
struct State {
197
  //! constructor
198
  State()
199
82.8k
    : m_version(-1)
200
82.8k
    , m_zones()
201
82.8k
    , m_numPages(1)
202
82.8k
    , m_actualPage(1)
203
82.8k
  {
204
82.8k
  }
205
  //! the file version
206
  mutable int m_version;
207
  //! the main zone
208
  std::vector<TextZone> m_zones;
209
210
  int m_numPages /* the number of pages */, m_actualPage /* the actual page */;
211
};
212
213
////////////////////////////////////////
214
//! Internal: the subdocument of a MsWks3Text
215
class SubDocument final : public MWAWSubDocument
216
{
217
public:
218
  enum Type { Zone, Text };
219
  SubDocument(MsWks3Text &pars, MWAWInputStreamPtr const &input, int zoneId, int noteId)
220
193k
    : MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
221
193k
    , m_textParser(&pars)
222
193k
    , m_id(zoneId)
223
193k
    , m_noteId(noteId)
224
193k
  {
225
193k
  }
226
227
  //! destructor
228
0
  ~SubDocument() final {}
229
230
  //! operator!=
231
  bool operator!=(MWAWSubDocument const &doc) const final;
232
233
  //! the parser function
234
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
235
236
protected:
237
  /** the text parser */
238
  MsWks3Text *m_textParser;
239
  /** the subdocument id*/
240
  int m_id;
241
  /** the note id */
242
  int m_noteId;
243
private:
244
  SubDocument(SubDocument const &orig) = delete;
245
  SubDocument operator=(SubDocument const &orig) = delete;
246
};
247
248
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
249
192k
{
250
192k
  if (!listener.get()) {
251
0
    MWAW_DEBUG_MSG(("MsWks3TextInternal::SubDocument::parse: no listener\n"));
252
0
    return;
253
0
  }
254
192k
  if (!m_textParser) {
255
0
    MWAW_DEBUG_MSG(("MsWks3TextInternal::SubDocument::parse: no parser\n"));
256
0
    return;
257
0
  }
258
259
192k
  long pos = m_input->tell();
260
192k
  m_textParser->sendNote(m_id, m_noteId);
261
192k
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
262
192k
}
263
264
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
265
116k
{
266
116k
  if (MWAWSubDocument::operator!=(doc)) return true;
267
116k
  auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
268
116k
  if (!sDoc) return true;
269
0
  if (m_textParser != sDoc->m_textParser) return true;
270
0
  if (m_id != sDoc->m_id) return true;
271
0
  if (m_noteId != sDoc->m_noteId) return true;
272
0
  return false;
273
0
}
274
275
}
276
277
////////////////////////////////////////////////////////////
278
// constructor/destructor, ...
279
////////////////////////////////////////////////////////////
280
MsWks3Text::MsWks3Text(MsWksDocument &document)
281
82.8k
  : m_parserState()
282
82.8k
  , m_state(new MsWks3TextInternal::State)
283
82.8k
  , m_mainParser(&document.getMainParser())
284
82.8k
  , m_document(document)
285
82.8k
{
286
82.8k
  m_parserState=m_mainParser->getParserState();
287
82.8k
}
288
289
MsWks3Text::~MsWks3Text()
290
82.8k
{
291
82.8k
}
292
293
int MsWks3Text::version() const
294
2.39M
{
295
2.39M
  if (m_state->m_version < 0)
296
30.1k
    m_state->m_version = m_parserState->m_version;
297
2.39M
  return m_state->m_version;
298
2.39M
}
299
300
int MsWks3Text::numPages(int zoneId) const
301
30.9k
{
302
30.9k
  if (zoneId < 0 || zoneId >= int(m_state->m_zones.size())) {
303
0
    MWAW_DEBUG_MSG(("MsWks3Text::numPages: unknown zone %d\n", zoneId));
304
0
    return 0;
305
0
  }
306
30.9k
  auto const &zone = m_state->m_zones[size_t(zoneId)];
307
30.9k
  int nPages = 1 + int(zone.m_pagesPosition.size());
308
30.9k
  if (zone.isMain()) {
309
30.9k
    m_state->m_actualPage = 1;
310
30.9k
    m_state->m_numPages = nPages;
311
30.9k
  }
312
0
  else {
313
0
    MWAW_DEBUG_MSG(("MsWks3Text::numPages: called on no main zone: %d\n", zoneId));
314
0
  }
315
30.9k
  return nPages;
316
30.9k
}
317
318
bool MsWks3Text::getLinesPagesHeight
319
(int zoneId, std::vector<int> &lines, std::vector<int> &pages)
320
59.8k
{
321
59.8k
  lines.resize(0);
322
59.8k
  pages.resize(0);
323
59.8k
  if (zoneId < 0 || zoneId >= int(m_state->m_zones.size())) {
324
37.0k
    MWAW_DEBUG_MSG(("MsWks3Text::getLinesPagesHeight: unknown zone %d\n", zoneId));
325
37.0k
    return false;
326
37.0k
  }
327
22.8k
  lines = m_state->m_zones[size_t(zoneId)].m_linesHeight;
328
22.8k
  pages = m_state->m_zones[size_t(zoneId)].m_pagesHeight;
329
22.8k
  return true;
330
59.8k
}
331
332
////////////////////////////////////////////////////////////
333
// Intermediate level
334
////////////////////////////////////////////////////////////
335
int MsWks3Text::getHeader() const
336
65.8k
{
337
98.3k
  for (size_t i = 0; i < m_state->m_zones.size(); i++)
338
35.3k
    if (m_state->m_zones[i].m_type == MsWks3TextInternal::TextZone::Header)
339
2.80k
      return int(i);
340
63.0k
  return -1;
341
65.8k
}
342
343
int MsWks3Text::getFooter() const
344
65.8k
{
345
101k
  for (size_t i = 0; i < m_state->m_zones.size(); i++)
346
37.6k
    if (m_state->m_zones[i].m_type == MsWks3TextInternal::TextZone::Footer)
347
2.12k
      return int(i);
348
63.6k
  return -1;
349
65.8k
}
350
351
////////////////////////////////////////////////////////////
352
// try to find the different zone
353
////////////////////////////////////////////////////////////
354
int MsWks3Text::createZones(int numLines, bool mainZone)
355
68.1M
{
356
68.1M
  MsWks3TextInternal::LineZone zone;
357
68.1M
  auto zoneId = int(m_state->m_zones.size());
358
68.1M
  m_state->m_zones.push_back(MsWks3TextInternal::TextZone());
359
68.1M
  auto &actualZone = m_state->m_zones.back();
360
68.1M
  actualZone.m_id = zoneId;
361
68.1M
  if (mainZone)
362
59.8k
    actualZone.m_type = MsWks3TextInternal::TextZone::Main;
363
68.1M
  bool hasNote=false;
364
68.1M
  int firstNote=0;
365
68.1M
  MWAWInputStreamPtr input=m_document.getInput();
366
68.8M
  while (!input->isEnd()) {
367
66.6M
    if (numLines==0) break;
368
66.6M
    if (numLines>0) numLines--;
369
66.6M
    long pos = input->tell();
370
66.6M
    if (!readZoneHeader(zone)) {
371
65.9M
      input->seek(pos, librevenge::RVNG_SEEK_SET);
372
65.9M
      break;
373
65.9M
    }
374
675k
    if (!hasNote && zone.isNote()) {
375
11.1k
      firstNote = int(actualZone.m_zonesList.size());
376
11.1k
      hasNote = true;
377
11.1k
    }
378
675k
    actualZone.m_zonesList.push_back(zone);
379
675k
    input->seek(zone.m_pos.end(), librevenge::RVNG_SEEK_SET);
380
675k
  }
381
68.1M
  auto numLineZones = int(actualZone.m_zonesList.size());
382
68.1M
  if (numLineZones == 0) {
383
68.1M
    m_state->m_zones.pop_back();
384
68.1M
    return -1;
385
68.1M
  }
386
387
36.0k
  update(actualZone);
388
36.0k
  if (hasNote)
389
11.1k
    updateNotes(actualZone, firstNote);
390
36.0k
  return zoneId;
391
68.1M
}
392
393
void MsWks3Text::update(MsWks3TextInternal::TextZone &zone)
394
36.0k
{
395
36.0k
  size_t numLineZones = zone.m_zonesList.size();
396
36.0k
  if (numLineZones == 0) return;
397
398
36.0k
  auto textHeight = int(72.0*m_mainParser->getPageSpan().getPageLength());
399
400
36.0k
  int actH = 0, actualPH = 0;
401
36.0k
  zone.m_linesHeight.push_back(0);
402
711k
  for (size_t i = 0; i < numLineZones; i++) {
403
675k
    auto const &z = zone.m_zonesList[i];
404
675k
    if (z.isNote()) continue; // a note
405
628k
    actH += z.m_height;
406
628k
    zone.m_linesHeight.push_back(actH);
407
628k
    bool newPage = ((z.m_flags&1) && actualPH) || (z.m_flags&2);
408
628k
    actualPH += z.m_height;
409
628k
    if (newPage || (actualPH > textHeight && textHeight > 0)) {
410
38.9k
      zone.m_pagesPosition[int(i)]=(z.m_flags&2);
411
38.9k
      zone.m_pagesHeight.push_back(actualPH-z.m_height);
412
38.9k
      actualPH=z.m_height;
413
38.9k
    }
414
628k
  }
415
36.0k
}
416
417
void MsWks3Text::updateNotes(MsWks3TextInternal::TextZone &zone, int firstNote)
418
11.1k
{
419
11.1k
  auto numLineZones = int(zone.m_zonesList.size());
420
11.1k
  if (firstNote < 0 || firstNote >= numLineZones) {
421
0
    MWAW_DEBUG_MSG(("MsWks3Text::updateNotes: can not find first note position\n"));
422
0
    return;
423
0
  }
424
425
11.1k
  MWAWInputStreamPtr input=m_document.getInput();
426
11.1k
  MsWks3TextInternal::Font font;
427
11.1k
  int noteId = -1;
428
11.1k
  long lastIndentPos = -1;
429
11.1k
  MWAWVec2i notePos;
430
431
51.5k
  for (int n = firstNote; n < numLineZones; n++) {
432
46.0k
    auto const &z = zone.m_zonesList[size_t(n)];
433
46.0k
    if (!z.isNote()) {
434
5.57k
      noteId=-1;
435
5.57k
      MWAW_DEBUG_MSG(("MsWks3Text::updateNotes: find extra data in notes, stop recording note\n"));
436
5.57k
      break;
437
5.57k
    }
438
40.4k
    if (z.isRuler()) {
439
3.09k
      lastIndentPos = n;
440
3.09k
      continue;
441
3.09k
    }
442
37.3k
    if (z.m_pos.length() < 8) continue;
443
31.7k
    long actPos = z.m_pos.begin();
444
31.7k
    input->seek(actPos+6,librevenge::RVNG_SEEK_SET);
445
446
31.7k
    auto c = static_cast<int>(input->readULong(1));
447
31.7k
    if ((c == 1 || c == 2) && readFont(font, z.m_pos.end())) {
448
7.61k
      if (long(input->tell())+2 > z.m_pos.end())
449
420
        continue;
450
7.19k
      c = static_cast<int>(input->readULong(1));
451
7.19k
      if (c <= 4) {
452
3.81k
        if (long(input->tell())+2 > z.m_pos.end())
453
358
          continue;
454
3.46k
        c = static_cast<int>(input->readULong(1));
455
3.46k
      }
456
7.19k
    }
457
30.9k
    if (c != 0x14) continue;
458
19.7k
    if (noteId >= 0) {
459
16.0k
      notePos[1] = (lastIndentPos != -1) ? int(lastIndentPos) : n;
460
16.0k
      if (zone.m_footnoteMap.find(noteId)==zone.m_footnoteMap.end())
461
10.9k
        zone.m_footnoteMap[noteId] = notePos;
462
5.10k
      else {
463
5.10k
        MWAW_DEBUG_MSG(("MsWks3Text::updateNotes: note %d is already defined, ignored\n", noteId));
464
5.10k
      }
465
16.0k
    }
466
19.7k
    noteId = static_cast<int>(input->readULong(2));
467
19.7k
    notePos[0] = (lastIndentPos != -1) ? int(lastIndentPos) : n;
468
19.7k
    lastIndentPos = -1;
469
19.7k
  }
470
11.1k
  if (noteId >= 0) {
471
2.36k
    notePos[1] = numLineZones;
472
2.36k
    if (zone.m_footnoteMap.find(noteId)==zone.m_footnoteMap.end())
473
2.06k
      zone.m_footnoteMap[noteId] = notePos;
474
300
    else {
475
300
      MWAW_DEBUG_MSG(("MsWks3Text::updateNotes: note %d is already defined, ignored\n", noteId));
476
300
    }
477
2.36k
  }
478
11.1k
}
479
480
bool MsWks3Text::readZoneHeader(MsWks3TextInternal::LineZone &zone) const
481
66.6M
{
482
66.6M
  zone = MsWks3TextInternal::LineZone();
483
66.6M
  MWAWInputStreamPtr input=m_document.getInput();
484
66.6M
  long pos = input->tell();
485
66.6M
  if (!input->checkPosition(pos+6)) return false;
486
59.8M
  zone.m_pos.setBegin(pos);
487
59.8M
  zone.m_type = static_cast<int>(input->readULong(1));
488
59.8M
  if (zone.m_type & 0x17) return false;
489
38.9M
  zone.m_id = static_cast<int>(input->readULong(1));
490
38.9M
  zone.m_flags = static_cast<int>(input->readULong(1));
491
38.9M
  zone.m_height = static_cast<int>(input->readULong(1));
492
38.9M
  zone.m_pos.setLength(6+long(input->readULong(2)));
493
38.9M
  if (!input->checkPosition(zone.m_pos.end())) return false;
494
675k
  return true;
495
38.9M
}
496
497
////////////////////////////////////////////////////////////
498
// the text:
499
////////////////////////////////////////////////////////////
500
bool MsWks3Text::sendText(MsWks3TextInternal::LineZone &zone, int zoneId)
501
886k
{
502
886k
  MWAWListenerPtr listener=m_parserState->getMainListener();
503
886k
  if (!listener) {
504
0
    MWAW_DEBUG_MSG(("MsWks3Text::sendText: can not find the listener\n"));
505
0
    return true;
506
0
  }
507
886k
  MWAWInputStreamPtr input=m_document.getInput();
508
886k
  input->seek(zone.m_pos.begin()+6, librevenge::RVNG_SEEK_SET);
509
886k
  int vers = version();
510
886k
  libmwaw::DebugFile &ascFile = m_document.ascii();
511
886k
  libmwaw::DebugStream f;
512
886k
  f << "Entries(TextZone):" << zone << ",";
513
886k
  MsWks3TextInternal::Font font;
514
886k
  if (zone.m_height > 0) {
515
139k
    MWAWParagraph para=listener->getParagraph();
516
139k
    para.setInterline(zone.m_height, librevenge::RVNG_POINT);
517
139k
    listener->setParagraph(para);
518
139k
  }
519
25.7M
  while (!input->isEnd()) {
520
25.7M
    long pos = input->tell();
521
25.7M
    if (pos >= zone.m_pos.end()) break;
522
24.9M
    auto c = static_cast<int>(input->readULong(1));
523
24.9M
    if ((c == 1 || c == 2) && readFont(font, zone.m_pos.end())) {
524
1.43M
      listener->setFont(font.m_font);
525
1.43M
      f << "[" << font.m_font.getDebugString(m_parserState->m_fontConverter) << font << "]";
526
1.43M
      continue;
527
1.43M
    }
528
23.4M
    if (c == 0) {
529
11.5M
      f << "#";
530
11.5M
      continue;
531
11.5M
    }
532
11.8M
    f << char(c);
533
11.8M
    switch (c) {
534
182k
    case 0x9:
535
182k
      listener->insertTab();
536
182k
      break;
537
101k
    case 0x10: // cursor pos
538
185k
    case 0x11:
539
185k
      break;
540
11.5M
    default:
541
11.5M
      if (c >= 0x14 && c <= 0x19 && vers >= 3) {
542
585k
        int sz = (c==0x19) ? 0 : (c == 0x18) ? 1 : 2;
543
585k
        int id = (sz && pos+1+sz <=  zone.m_pos.end()) ? int(input->readLong(sz)) : 0;
544
585k
        if (id) f << "[" << id << "]";
545
585k
        switch (c) {
546
38.9k
        case 0x19:
547
38.9k
          listener->insertField(MWAWField(MWAWField::Title));
548
38.9k
          break;
549
44.8k
        case 0x18:
550
44.8k
          listener->insertField(MWAWField(MWAWField::PageNumber));
551
44.8k
          break;
552
35.6k
        case 0x16:
553
35.6k
          listener->insertField(MWAWField(MWAWField::Time));
554
35.6k
          break;
555
48.5k
        case 0x17: // id = 0 : short date ; id=9 : long date
556
48.5k
          listener->insertField(MWAWField(MWAWField::Date));
557
48.5k
          break;
558
33.6k
        case 0x15:
559
33.6k
          MWAW_DEBUG_MSG(("MsWks3Text::sendText: find unknown field type 0x15\n"));
560
33.6k
          break;
561
383k
        case 0x14:
562
383k
          if (!zone.isNote()) {
563
193k
            MWAWSubDocumentPtr subdoc
564
193k
            (new MsWks3TextInternal::SubDocument(*this, m_document.getInput(), zoneId, id));
565
193k
            listener->insertNote(MWAWNote(MWAWNote::FootNote), subdoc);
566
193k
          }
567
383k
          break;
568
0
        default:
569
0
          break;
570
585k
        }
571
585k
      }
572
10.9M
      else if (c <= 0x1f) {
573
3.24M
        f << "#" << std::hex << c << std::dec << "]";
574
3.24M
        ascFile.addDelimiter(pos,'#');
575
3.24M
        MWAW_DEBUG_MSG(("MsWks3Text::sendText: find char=%x\n", static_cast<unsigned int>(c)));
576
3.24M
      }
577
7.68M
      else
578
7.68M
        listener->insertCharacter(static_cast<unsigned char>(c), input, zone.m_pos.end());
579
11.5M
      break;
580
11.8M
    }
581
11.8M
  }
582
886k
  if (listener)
583
886k
    listener->insertEOL();
584
886k
  ascFile.addPos(zone.m_pos.begin());
585
886k
  ascFile.addNote(f.str().c_str());
586
886k
  return true;
587
886k
}
588
589
bool MsWks3Text::sendString(std::string &str)
590
12.1k
{
591
12.1k
  MWAWListenerPtr listener=m_parserState->getMainListener();
592
12.1k
  if (!listener)
593
0
    return true;
594
12.1k
  MsWks3TextInternal::Font defFont;
595
12.1k
  defFont.m_font = MWAWFont(20,12);
596
12.1k
  listener->setFont(defFont.m_font);
597
598
458k
  for (auto c : str) {
599
458k
    switch (c) {
600
15.1k
    case 0x9:
601
15.1k
      listener->insertTab();
602
15.1k
      break;
603
7.49k
    case 0x10: // cursor pos
604
17.8k
    case 0x11:
605
17.8k
      break;
606
6.41k
    case 0x19:
607
6.41k
      listener->insertField(MWAWField(MWAWField::Title));
608
6.41k
      break;
609
13.7k
    case 0x18:
610
13.7k
      listener->insertField(MWAWField(MWAWField::PageNumber));
611
13.7k
      break;
612
8.85k
    case 0x16:
613
8.85k
      listener->insertField(MWAWField(MWAWField::Time));
614
8.85k
      break;
615
21.1k
    case 0x17: // id = 0 : short date ; id=9 : long date
616
21.1k
      listener->insertField(MWAWField(MWAWField::Date));
617
21.1k
      break;
618
6.28k
    case 0x15:
619
6.28k
      MWAW_DEBUG_MSG(("MsWks3Text::sendString: find unknown field type 0x15\n"));
620
6.28k
      break;
621
12.7k
    case 0x14: // fixme
622
12.7k
      MWAW_DEBUG_MSG(("MsWks3Text::sendString: footnote are not implemented\n"));
623
12.7k
      break;
624
355k
    default:
625
355k
      listener->insertCharacter(static_cast<unsigned char>(c));
626
355k
      break;
627
458k
    }
628
458k
  }
629
12.1k
  return true;
630
12.1k
}
631
632
////////////////////////////////////////////////////////////
633
// the font:
634
////////////////////////////////////////////////////////////
635
bool MsWks3Text::readFont(MsWks3TextInternal::Font &font, long endPos)
636
1.51M
{
637
1.51M
  int vers = version();
638
1.51M
  font = MsWks3TextInternal::Font();
639
1.51M
  MWAWInputStreamPtr input=m_document.getInput();
640
1.51M
  long pos  = input->tell();
641
1.51M
  input->seek(-1, librevenge::RVNG_SEEK_CUR);
642
1.51M
  auto type = static_cast<int>(input->readLong(1));
643
1.51M
  if ((type != 1 && type != 2) || pos+type+3 > endPos) {
644
63.8k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
645
63.8k
    return false;
646
63.8k
  }
647
1.44M
  libmwaw::DebugStream f;
648
1.44M
  auto flag = static_cast<int>(input->readULong(1)); // check or font ?
649
1.44M
  if (flag) f << "#f0=" << flag << ",";
650
1.44M
  font.m_font.setId(static_cast<int>(input->readULong(1)));
651
1.44M
  font.m_font.setSize(float(input->readULong(1)));
652
1.44M
  flag = static_cast<int>(input->readULong(1));
653
1.44M
  uint32_t flags = 0;
654
1.44M
  if (flag & 0x1) flags |= MWAWFont::boldBit;
655
1.44M
  if (flag & 0x2) flags |= MWAWFont::italicBit;
656
1.44M
  if (flag & 0x4) font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
657
1.44M
  if (flag & 0x8) flags |= MWAWFont::embossBit;
658
1.44M
  if (flag & 0x10) flags |= MWAWFont::shadowBit;
659
1.44M
  if (flag & 0x20) {
660
208k
    if (vers==1)
661
13.2k
      font.m_font.set(MWAWFont::Script(20,librevenge::RVNG_PERCENT,80));
662
194k
    else
663
194k
      font.m_font.set(MWAWFont::Script::super100());
664
208k
  }
665
1.44M
  if (flag & 0x40) {
666
239k
    if (vers==1)
667
12.6k
      font.m_font.set(MWAWFont::Script(-20,librevenge::RVNG_PERCENT,80));
668
227k
    else
669
227k
      font.m_font.set(MWAWFont::Script::sub100());
670
239k
  }
671
1.44M
  if ((flag & 0x80) && !(flag & 0x60)) f << "fFl80#,";
672
1.44M
  font.m_font.setFlags(flags);
673
1.44M
  int color = 1;
674
1.44M
  if (type == 2) {
675
494k
    color=static_cast<int>(input->readULong(1));
676
494k
  }
677
953k
  else if (pos+type+5 <= endPos) {
678
945k
    auto val = static_cast<int>(input->readULong(1));
679
945k
    if (val == 0)
680
331k
      f << "end0#,";
681
613k
    else
682
613k
      input->seek(-1, librevenge::RVNG_SEEK_CUR);
683
945k
  }
684
1.44M
  if (color != 1) {
685
479k
    MWAWColor col;
686
479k
    if (m_document.getColor(color,col,vers))
687
404k
      font.m_font.setColor(col);
688
75.0k
    else
689
75.0k
      f << "#fColor=" << color << ",";
690
479k
  }
691
1.44M
  font.m_extra = f.str();
692
1.44M
  return true;
693
1.51M
}
694
695
////////////////////////////////////////////////////////////
696
// the tabulations:
697
////////////////////////////////////////////////////////////
698
bool MsWks3Text::readParagraph(MsWks3TextInternal::LineZone &zone, MWAWParagraph &parag)
699
63.6k
{
700
63.6k
  int dataSize = int(zone.m_pos.length())-6;
701
63.6k
  if (dataSize < 15) return false;
702
55.6k
  MWAWInputStreamPtr input=m_document.getInput();
703
55.6k
  input->seek(zone.m_pos.begin()+6, librevenge::RVNG_SEEK_SET);
704
705
55.6k
  parag = MWAWParagraph();
706
55.6k
  libmwaw::DebugFile &ascFile = m_document.ascii();
707
55.6k
  libmwaw::DebugStream f;
708
709
55.6k
  int fl[2];
710
55.6k
  bool firstFlag = (dataSize & 1) == 0;
711
55.6k
  fl[0] = firstFlag ? static_cast<int>(input->readULong(1)) : 0x4c;
712
55.6k
  switch (fl[0]) {
713
14.5k
  case 0x4c:
714
14.5k
    break;
715
1.29k
  case 0x43:
716
1.29k
    parag.m_justify = MWAWParagraph::JustificationCenter;
717
1.29k
    break;
718
1.86k
  case 0x52:
719
1.86k
    parag.m_justify = MWAWParagraph::JustificationRight;
720
1.86k
    break;
721
1.74k
  case 0x46:
722
1.74k
    parag.m_justify = MWAWParagraph::JustificationFull;
723
1.74k
    break;
724
36.2k
  default:
725
36.2k
    f << "#align=" << std::hex << fl[0] << ",";
726
36.2k
    MWAW_DEBUG_MSG(("MsWks3Text::readParagraph: unknown alignment %x\n", static_cast<unsigned int>(fl[0])));
727
36.2k
    break;
728
55.6k
  }
729
55.6k
  fl[1] = static_cast<int>(input->readULong(1));
730
55.6k
  if (fl[1])
731
42.1k
    f << "fl0=" <<fl[1] << std::dec << ",";
732
55.6k
  int dim[3];
733
55.6k
  bool ok = true;
734
222k
  for (int i = 0; i < 3; i++) {
735
166k
    dim[i] = static_cast<int>(input->readULong(2));
736
166k
    if (i==0&&(dim[0]&0x8000)) {
737
6.68k
      dim[0] &= 0x7FFF;
738
6.68k
      f << "6linesByInches,";
739
6.68k
    }
740
166k
    if (dim[i] > 3000) ok = false;
741
166k
  }
742
55.6k
  if (!ok) {
743
43.0k
    MWAW_DEBUG_MSG(("MsWks3Text::readParagraph: size is very odd\n"));
744
43.0k
    f << "##";
745
43.0k
  }
746
55.6k
  if (dim[0] || dim[1] || dim[2]) {
747
53.2k
    f << "size=" << dim[1] << "x" << dim[0];
748
53.2k
    if (dim[2]) f << "[" << dim[2] << "]";
749
53.2k
    f << ",";
750
53.2k
  }
751
752
55.6k
  int fl2[2];
753
55.6k
  for (auto &flag : fl2) // between -16 and 16
754
111k
    flag = static_cast<int>(input->readULong(1));
755
55.6k
  if (fl2[0] || fl2[1])
756
37.5k
    f << "fl2=(" << std::hex << fl2[0] << ", " <<fl2[1] << ")" << std::dec << ",";
757
758
222k
  for (int i = 0; i < 3; i++) {
759
166k
    auto val = static_cast<int>(input->readULong(2));
760
166k
    int flag = (val & 0xc000) >> 14;
761
166k
    val = (val & 0x3fff);
762
166k
    if (val > 8000 || flag) {
763
75.2k
      MWAW_DEBUG_MSG(("MsWks3Text::readParagraph: find odd margin %d\n", i));
764
75.2k
      f << "#margin" << i << "=" << val  << "(" << flag << "),";
765
75.2k
    }
766
166k
    if (val > 8000) continue;
767
    // i = 0 (last), i = 1 (firstL), i=2 (nextL)
768
112k
    parag.m_margins[2-i] = val/72.0;
769
112k
  }
770
55.6k
  *(parag.m_margins[0]) -= *(parag.m_margins[1]);
771
55.6k
  if (parag.m_margins[2].get() > 0.0)
772
31.6k
    parag.m_margins[2] = m_mainParser->getPageWidth()-*(parag.m_margins[2]);
773
55.6k
  if (parag.m_margins[2].get() > 56./72.) *(parag.m_margins[2]) -= 28./72.;
774
36.7k
  else if (parag.m_margins[2].get() >=0.0) *(parag.m_margins[2]) *= 0.5;
775
11.9k
  else parag.m_margins[2] = 0.0;
776
55.6k
  int numVal = (dataSize-9)/2-3;
777
55.6k
  if (numVal > 20) {
778
10.6k
    MWAW_DEBUG_MSG(("MsWks3Text::readParagraph: find odd number of tabs %d\n", numVal));
779
10.6k
    return false;
780
10.6k
  }
781
45.0k
  parag.m_tabs->resize(size_t(numVal));
782
45.0k
  size_t numTabs = 0;
783
784
  // checkme: in order to avoid x_tabs > textWidth (appears sometimes when i=0)
785
45.0k
  auto maxWidth = long(m_mainParser->getPageWidth()*72-36);
786
45.0k
  if (dim[1] > maxWidth) maxWidth = dim[1];
787
788
222k
  for (int i = 0; i < numVal; i++) {
789
177k
    auto val = static_cast<int>(input->readULong(2));
790
177k
    MWAWTabStop::Alignment align = MWAWTabStop::LEFT;
791
177k
    switch (val >> 14) {
792
18.2k
    case 1:
793
18.2k
      align = MWAWTabStop::DECIMAL;
794
18.2k
      break;
795
6.57k
    case 2:
796
6.57k
      align = MWAWTabStop::RIGHT;
797
6.57k
      break;
798
24.1k
    case 3:
799
24.1k
      align = MWAWTabStop::CENTER;
800
24.1k
      break;
801
129k
    default:
802
129k
      break;
803
177k
    }
804
177k
    val = (val & 0x3fff);
805
177k
    if (val > maxWidth) {
806
47.5k
      static bool first = true;
807
47.5k
      if (first) {
808
34
        MWAW_DEBUG_MSG(("MsWks3Text::readParagraph: finds some odd tabs (ignored)\n"));
809
34
        first = false;
810
34
      }
811
47.5k
      f << "#tabs" << i << "=" << val << ",";
812
47.5k
      continue;
813
47.5k
    }
814
130k
    (*parag.m_tabs)[numTabs].m_alignment = align;
815
130k
    (*parag.m_tabs)[numTabs++].m_position = val/72.0;
816
130k
  }
817
45.0k
  if (int(numTabs)!=numVal) parag.m_tabs->resize(numTabs);
818
45.0k
  parag.m_extra = f.str();
819
820
45.0k
  f.str("");
821
45.0k
  f << "Entries(Paragraph):" << zone << "," << parag << ",";
822
45.0k
  ascFile.addPos(zone.m_pos.begin());
823
45.0k
  ascFile.addNote(f.str().c_str());
824
825
45.0k
  return true;
826
45.0k
}
827
828
////////////////////////////////////////////////////////////
829
// read v1-v2 header/footer string
830
////////////////////////////////////////////////////////////
831
std::string MsWks3Text::readHeaderFooterString(bool header)
832
15.8k
{
833
15.8k
  std::string res("");
834
15.8k
  MWAWInputStreamPtr input=m_document.getInput();
835
15.8k
  auto numChar = static_cast<int>(input->readULong(1));
836
15.8k
  if (!numChar) return res;
837
12.0k
  if (!input->checkPosition(input->tell()+numChar)) {
838
1.04k
    MWAW_DEBUG_MSG(("MsWks3Text::readHeaderFooterString: the number of char seems bad\n"));
839
1.04k
    return res;
840
1.04k
  }
841
386k
  for (int i = 0; i < numChar; i++) {
842
383k
    auto c = static_cast<unsigned char>(input->readULong(1));
843
383k
    if (c == 0) {
844
7.88k
      input->seek(-1, librevenge::RVNG_SEEK_CUR);
845
7.88k
      break;
846
7.88k
    }
847
375k
    if (c == '&') {
848
20.3k
      auto nextC = static_cast<unsigned char>(input->readULong(1));
849
20.3k
      bool field = true;
850
20.3k
      switch (nextC) {
851
153
      case 'F':
852
153
        res += char(0x19);
853
153
        break; // file name
854
2.50k
      case 'D':
855
2.50k
        res += char(0x17);
856
2.50k
        break; // date
857
1.38k
      case 'P':
858
1.38k
        res += char(0x18);
859
1.38k
        break; // page
860
5.10k
      case 'T':
861
5.10k
        res += char(0x16);
862
5.10k
        break; // time
863
11.2k
      default:
864
11.2k
        field = false;
865
20.3k
      }
866
20.3k
      if (field) continue;
867
11.2k
      input->seek(-1, librevenge::RVNG_SEEK_CUR);
868
11.2k
    }
869
366k
    res += char(c);
870
366k
  }
871
10.9k
  if (res.length()) {
872
10.5k
    m_state->m_zones.push_back(MsWks3TextInternal::TextZone());
873
10.5k
    auto &zone = m_state->m_zones.back();
874
10.5k
    zone.m_id = int(m_state->m_zones.size())-1;
875
10.5k
    zone.m_type = header ? MsWks3TextInternal::TextZone::Header :
876
10.5k
                  MsWks3TextInternal::TextZone::Footer;
877
10.5k
    zone.m_text = res;
878
10.5k
  }
879
10.9k
  return res;
880
10.9k
}
881
882
////////////////////////////////////////////////////////////
883
//
884
// Low level
885
//
886
////////////////////////////////////////////////////////////
887
void MsWks3Text::send(MsWks3TextInternal::TextZone &zone, MWAWVec2i limit)
888
171k
{
889
171k
  auto numZones = int(zone.m_zonesList.size());
890
  // set the default font
891
171k
  if (m_parserState->getMainListener())
892
171k
    m_parserState->getMainListener()->setFont(MWAWFont(20,12));
893
171k
  if (numZones == 0 && zone.m_text.length()) {
894
12.1k
    sendString(zone.m_text);
895
12.1k
    zone.m_isSent = true;
896
12.1k
    return;
897
12.1k
  }
898
158k
  bool isMain = false;
899
158k
  MWAWVec2i notePos(-1, -1);
900
158k
  if (limit[0] < 0) {
901
101k
    limit = MWAWVec2i(0,numZones);
902
101k
    isMain = zone.isMain();
903
    // find the notes in the text zones
904
101k
    for (auto const &noteIt : zone.m_footnoteMap) {
905
43.9k
      if (notePos[0]==-1) {
906
11.7k
        notePos = noteIt.second;
907
11.7k
        continue;
908
11.7k
      }
909
32.1k
      if (notePos[0] > noteIt.second[0])
910
11.0k
        notePos[0] = noteIt.second[0];
911
32.1k
      if (notePos[1] < noteIt.second[1])
912
8.00k
        notePos[1] = noteIt.second[1];
913
32.1k
    }
914
101k
  }
915
916
1.12M
  for (int i = limit[0]; i < limit[1]; i++) {
917
962k
    if (i == notePos[0]) {
918
11.7k
      i = notePos[1]-1;
919
11.7k
      continue;
920
11.7k
    }
921
950k
    if (isMain && zone.m_pagesPosition.find(i) != zone.m_pagesPosition.end()) {
922
31.1k
      ++m_state->m_actualPage;
923
31.1k
      m_document.newPage(m_state->m_actualPage, zone.m_pagesPosition[i]);
924
31.1k
    }
925
950k
    auto &z = zone.m_zonesList[size_t(i)];
926
950k
    if (z.m_type & 0x80) {
927
63.6k
      MWAWParagraph parag;
928
63.6k
      if (readParagraph(z, parag) && m_parserState->getMainListener())
929
45.0k
        m_parserState->getMainListener()->setParagraph(parag);
930
63.6k
    }
931
886k
    else
932
886k
      sendText(z, zone.m_id);
933
950k
  }
934
158k
  zone.m_isSent = true;
935
158k
}
936
937
void MsWks3Text::sendNote(int zoneId, int noteId)
938
192k
{
939
192k
  MWAWListenerPtr listener=m_parserState->getMainListener();
940
192k
  if (zoneId < 0 || zoneId >= int(m_state->m_zones.size())) {
941
0
    if (listener) listener->insertChar(' ');
942
0
    MWAW_DEBUG_MSG(("MsWks3Text::sendNote: unknown zone %d\n", zoneId));
943
0
    return;
944
0
  }
945
192k
  auto &zone=m_state->m_zones[size_t(zoneId)];
946
192k
  auto noteIt = zone.m_footnoteMap.find(noteId);
947
192k
  if (noteIt==zone.m_footnoteMap.end()) {
948
134k
    MWAW_DEBUG_MSG(("MsWks3Text::sendNote: unknown note %d-%d\n", zoneId, noteId));
949
134k
    if (listener) listener->insertChar(' ');
950
134k
  }
951
57.2k
  else
952
57.2k
    send(zone, noteIt->second);
953
192k
}
954
955
void MsWks3Text::sendZone(int zoneId)
956
102k
{
957
102k
  if (zoneId < 0 || zoneId >= int(m_state->m_zones.size())) {
958
0
    MWAW_DEBUG_MSG(("MsWks3Text::sendZone: unknown zone %d\n", zoneId));
959
0
    return;
960
0
  }
961
102k
  send(m_state->m_zones[size_t(zoneId)]);
962
102k
}
963
964
965
void MsWks3Text::flushExtra()
966
59.8k
{
967
59.8k
  for (auto &zone : m_state->m_zones) {
968
36.9k
    if (zone.m_isSent)
969
25.9k
      continue;
970
10.9k
    send(zone);
971
10.9k
  }
972
59.8k
}
973
974
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: