Coverage Report

Created: 2026-07-20 07:04

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
130M
    : m_type(-1)
63
130M
    , m_pos()
64
130M
    , m_id(0)
65
    , m_flags()
66
130M
    , m_height(0)
67
130M
  {
68
130M
  }
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
4.60M
  {
101
4.60M
    return (m_type&0x8)==0x8;
102
4.60M
  }
103
  //! return true if this is a tabs
104
  bool isRuler() const
105
38.4k
  {
106
38.4k
    return (m_type&0xE0)==0x80;
107
38.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
13.4M
  Font(): m_font(), m_extra("")
125
13.4M
  {
126
40.2M
    for (int &flag : m_flags) flag = 0;
127
13.4M
  }
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
65.2M
    : m_type(Unknown)
158
65.2M
    , m_id(0)
159
65.2M
    , m_zonesList()
160
65.2M
    , m_linesHeight()
161
65.2M
    , m_pagesHeight()
162
65.2M
    , m_pagesPosition()
163
65.2M
    , m_footnoteMap()
164
65.2M
    , m_text("")
165
65.2M
    , m_isSent(false)
166
65.2M
  {
167
65.2M
  }
168
169
  //! return true if this is the main zone
170
  bool isMain() const
171
1.33M
  {
172
1.33M
    return m_type == Main;
173
1.33M
  }
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
77.1k
    : m_version(-1)
200
77.1k
    , m_zones()
201
77.1k
    , m_numPages(1)
202
77.1k
    , m_actualPage(1)
203
77.1k
  {
204
77.1k
  }
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
472k
    : MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
221
472k
    , m_textParser(&pars)
222
472k
    , m_id(zoneId)
223
472k
    , m_noteId(noteId)
224
472k
  {
225
472k
  }
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
472k
{
250
472k
  if (!listener.get()) {
251
0
    MWAW_DEBUG_MSG(("MsWks3TextInternal::SubDocument::parse: no listener\n"));
252
0
    return;
253
0
  }
254
472k
  if (!m_textParser) {
255
0
    MWAW_DEBUG_MSG(("MsWks3TextInternal::SubDocument::parse: no parser\n"));
256
0
    return;
257
0
  }
258
259
472k
  long pos = m_input->tell();
260
472k
  m_textParser->sendNote(m_id, m_noteId);
261
472k
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
262
472k
}
263
264
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
265
400k
{
266
400k
  if (MWAWSubDocument::operator!=(doc)) return true;
267
400k
  auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
268
400k
  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
77.1k
  : m_parserState()
282
77.1k
  , m_state(new MsWks3TextInternal::State)
283
77.1k
  , m_mainParser(&document.getMainParser())
284
77.1k
  , m_document(document)
285
77.1k
{
286
77.1k
  m_parserState=m_mainParser->getParserState();
287
77.1k
}
288
289
MsWks3Text::~MsWks3Text()
290
77.1k
{
291
77.1k
}
292
293
int MsWks3Text::version() const
294
13.3M
{
295
13.3M
  if (m_state->m_version < 0)
296
28.6k
    m_state->m_version = m_parserState->m_version;
297
13.3M
  return m_state->m_version;
298
13.3M
}
299
300
int MsWks3Text::numPages(int zoneId) const
301
29.7k
{
302
29.7k
  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
29.7k
  auto const &zone = m_state->m_zones[size_t(zoneId)];
307
29.7k
  int nPages = 1 + int(zone.m_pagesPosition.size());
308
29.7k
  if (zone.isMain()) {
309
29.7k
    m_state->m_actualPage = 1;
310
29.7k
    m_state->m_numPages = nPages;
311
29.7k
  }
312
0
  else {
313
0
    MWAW_DEBUG_MSG(("MsWks3Text::numPages: called on no main zone: %d\n", zoneId));
314
0
  }
315
29.7k
  return nPages;
316
29.7k
}
317
318
bool MsWks3Text::getLinesPagesHeight
319
(int zoneId, std::vector<int> &lines, std::vector<int> &pages)
320
54.9k
{
321
54.9k
  lines.resize(0);
322
54.9k
  pages.resize(0);
323
54.9k
  if (zoneId < 0 || zoneId >= int(m_state->m_zones.size())) {
324
32.8k
    MWAW_DEBUG_MSG(("MsWks3Text::getLinesPagesHeight: unknown zone %d\n", zoneId));
325
32.8k
    return false;
326
32.8k
  }
327
22.0k
  lines = m_state->m_zones[size_t(zoneId)].m_linesHeight;
328
22.0k
  pages = m_state->m_zones[size_t(zoneId)].m_pagesHeight;
329
22.0k
  return true;
330
54.9k
}
331
332
////////////////////////////////////////////////////////////
333
// Intermediate level
334
////////////////////////////////////////////////////////////
335
int MsWks3Text::getHeader() const
336
60.4k
{
337
91.3k
  for (size_t i = 0; i < m_state->m_zones.size(); i++)
338
33.3k
    if (m_state->m_zones[i].m_type == MsWks3TextInternal::TextZone::Header)
339
2.45k
      return int(i);
340
58.0k
  return -1;
341
60.4k
}
342
343
int MsWks3Text::getFooter() const
344
60.4k
{
345
93.9k
  for (size_t i = 0; i < m_state->m_zones.size(); i++)
346
35.3k
    if (m_state->m_zones[i].m_type == MsWks3TextInternal::TextZone::Footer)
347
1.85k
      return int(i);
348
58.6k
  return -1;
349
60.4k
}
350
351
////////////////////////////////////////////////////////////
352
// try to find the different zone
353
////////////////////////////////////////////////////////////
354
int MsWks3Text::createZones(int numLines, bool mainZone)
355
65.2M
{
356
65.2M
  MsWks3TextInternal::LineZone zone;
357
65.2M
  auto zoneId = int(m_state->m_zones.size());
358
65.2M
  m_state->m_zones.push_back(MsWks3TextInternal::TextZone());
359
65.2M
  auto &actualZone = m_state->m_zones.back();
360
65.2M
  actualZone.m_id = zoneId;
361
65.2M
  if (mainZone)
362
54.9k
    actualZone.m_type = MsWks3TextInternal::TextZone::Main;
363
65.2M
  bool hasNote=false;
364
65.2M
  int firstNote=0;
365
65.2M
  MWAWInputStreamPtr input=m_document.getInput();
366
67.2M
  while (!input->isEnd()) {
367
65.2M
    if (numLines==0) break;
368
65.2M
    if (numLines>0) numLines--;
369
65.2M
    long pos = input->tell();
370
65.2M
    if (!readZoneHeader(zone)) {
371
63.2M
      input->seek(pos, librevenge::RVNG_SEEK_SET);
372
63.2M
      break;
373
63.2M
    }
374
1.95M
    if (!hasNote && zone.isNote()) {
375
10.7k
      firstNote = int(actualZone.m_zonesList.size());
376
10.7k
      hasNote = true;
377
10.7k
    }
378
1.95M
    actualZone.m_zonesList.push_back(zone);
379
1.95M
    input->seek(zone.m_pos.end(), librevenge::RVNG_SEEK_SET);
380
1.95M
  }
381
65.2M
  auto numLineZones = int(actualZone.m_zonesList.size());
382
65.2M
  if (numLineZones == 0) {
383
65.2M
    m_state->m_zones.pop_back();
384
65.2M
    return -1;
385
65.2M
  }
386
387
34.5k
  update(actualZone);
388
34.5k
  if (hasNote)
389
10.7k
    updateNotes(actualZone, firstNote);
390
34.5k
  return zoneId;
391
65.2M
}
392
393
void MsWks3Text::update(MsWks3TextInternal::TextZone &zone)
394
34.5k
{
395
34.5k
  size_t numLineZones = zone.m_zonesList.size();
396
34.5k
  if (numLineZones == 0) return;
397
398
34.5k
  auto textHeight = int(72.0*m_mainParser->getPageSpan().getPageLength());
399
400
34.5k
  int actH = 0, actualPH = 0;
401
34.5k
  zone.m_linesHeight.push_back(0);
402
1.98M
  for (size_t i = 0; i < numLineZones; i++) {
403
1.95M
    auto const &z = zone.m_zonesList[i];
404
1.95M
    if (z.isNote()) continue; // a note
405
1.90M
    actH += z.m_height;
406
1.90M
    zone.m_linesHeight.push_back(actH);
407
1.90M
    bool newPage = ((z.m_flags&1) && actualPH) || (z.m_flags&2);
408
1.90M
    actualPH += z.m_height;
409
1.90M
    if (newPage || (actualPH > textHeight && textHeight > 0)) {
410
34.8k
      zone.m_pagesPosition[int(i)]=(z.m_flags&2);
411
34.8k
      zone.m_pagesHeight.push_back(actualPH-z.m_height);
412
34.8k
      actualPH=z.m_height;
413
34.8k
    }
414
1.90M
  }
415
34.5k
}
416
417
void MsWks3Text::updateNotes(MsWks3TextInternal::TextZone &zone, int firstNote)
418
10.7k
{
419
10.7k
  auto numLineZones = int(zone.m_zonesList.size());
420
10.7k
  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
10.7k
  MWAWInputStreamPtr input=m_document.getInput();
426
10.7k
  MsWks3TextInternal::Font font;
427
10.7k
  int noteId = -1;
428
10.7k
  long lastIndentPos = -1;
429
10.7k
  MWAWVec2i notePos;
430
431
49.1k
  for (int n = firstNote; n < numLineZones; n++) {
432
43.8k
    auto const &z = zone.m_zonesList[size_t(n)];
433
43.8k
    if (!z.isNote()) {
434
5.42k
      noteId=-1;
435
5.42k
      MWAW_DEBUG_MSG(("MsWks3Text::updateNotes: find extra data in notes, stop recording note\n"));
436
5.42k
      break;
437
5.42k
    }
438
38.4k
    if (z.isRuler()) {
439
3.40k
      lastIndentPos = n;
440
3.40k
      continue;
441
3.40k
    }
442
35.0k
    if (z.m_pos.length() < 8) continue;
443
30.0k
    long actPos = z.m_pos.begin();
444
30.0k
    input->seek(actPos+6,librevenge::RVNG_SEEK_SET);
445
446
30.0k
    auto c = static_cast<int>(input->readULong(1));
447
30.0k
    if ((c == 1 || c == 2) && readFont(font, z.m_pos.end())) {
448
7.77k
      if (long(input->tell())+2 > z.m_pos.end())
449
338
        continue;
450
7.43k
      c = static_cast<int>(input->readULong(1));
451
7.43k
      if (c <= 4) {
452
4.22k
        if (long(input->tell())+2 > z.m_pos.end())
453
285
          continue;
454
3.94k
        c = static_cast<int>(input->readULong(1));
455
3.94k
      }
456
7.43k
    }
457
29.4k
    if (c != 0x14) continue;
458
17.3k
    if (noteId >= 0) {
459
14.2k
      notePos[1] = (lastIndentPos != -1) ? int(lastIndentPos) : n;
460
14.2k
      if (zone.m_footnoteMap.find(noteId)==zone.m_footnoteMap.end())
461
9.82k
        zone.m_footnoteMap[noteId] = notePos;
462
4.39k
      else {
463
4.39k
        MWAW_DEBUG_MSG(("MsWks3Text::updateNotes: note %d is already defined, ignored\n", noteId));
464
4.39k
      }
465
14.2k
    }
466
17.3k
    noteId = static_cast<int>(input->readULong(2));
467
17.3k
    notePos[0] = (lastIndentPos != -1) ? int(lastIndentPos) : n;
468
17.3k
    lastIndentPos = -1;
469
17.3k
  }
470
10.7k
  if (noteId >= 0) {
471
1.94k
    notePos[1] = numLineZones;
472
1.94k
    if (zone.m_footnoteMap.find(noteId)==zone.m_footnoteMap.end())
473
1.67k
      zone.m_footnoteMap[noteId] = notePos;
474
272
    else {
475
272
      MWAW_DEBUG_MSG(("MsWks3Text::updateNotes: note %d is already defined, ignored\n", noteId));
476
272
    }
477
1.94k
  }
478
10.7k
}
479
480
bool MsWks3Text::readZoneHeader(MsWks3TextInternal::LineZone &zone) const
481
65.2M
{
482
65.2M
  zone = MsWks3TextInternal::LineZone();
483
65.2M
  MWAWInputStreamPtr input=m_document.getInput();
484
65.2M
  long pos = input->tell();
485
65.2M
  if (!input->checkPosition(pos+6)) return false;
486
59.6M
  zone.m_pos.setBegin(pos);
487
59.6M
  zone.m_type = static_cast<int>(input->readULong(1));
488
59.6M
  if (zone.m_type & 0x17) return false;
489
38.6M
  zone.m_id = static_cast<int>(input->readULong(1));
490
38.6M
  zone.m_flags = static_cast<int>(input->readULong(1));
491
38.6M
  zone.m_height = static_cast<int>(input->readULong(1));
492
38.6M
  zone.m_pos.setLength(6+long(input->readULong(2)));
493
38.6M
  if (!input->checkPosition(zone.m_pos.end())) return false;
494
1.95M
  return true;
495
38.6M
}
496
497
////////////////////////////////////////////////////////////
498
// the text:
499
////////////////////////////////////////////////////////////
500
bool MsWks3Text::sendText(MsWks3TextInternal::LineZone &zone, int zoneId)
501
4.62M
{
502
4.62M
  MWAWListenerPtr listener=m_parserState->getMainListener();
503
4.62M
  if (!listener) {
504
0
    MWAW_DEBUG_MSG(("MsWks3Text::sendText: can not find the listener\n"));
505
0
    return true;
506
0
  }
507
4.62M
  MWAWInputStreamPtr input=m_document.getInput();
508
4.62M
  input->seek(zone.m_pos.begin()+6, librevenge::RVNG_SEEK_SET);
509
4.62M
  int vers = version();
510
4.62M
  libmwaw::DebugFile &ascFile = m_document.ascii();
511
4.62M
  libmwaw::DebugStream f;
512
4.62M
  f << "Entries(TextZone):" << zone << ",";
513
4.62M
  MsWks3TextInternal::Font font;
514
4.62M
  if (zone.m_height > 0) {
515
968k
    MWAWParagraph para=listener->getParagraph();
516
968k
    para.setInterline(zone.m_height, librevenge::RVNG_POINT);
517
968k
    listener->setParagraph(para);
518
968k
  }
519
132M
  while (!input->isEnd()) {
520
132M
    long pos = input->tell();
521
132M
    if (pos >= zone.m_pos.end()) break;
522
128M
    auto c = static_cast<int>(input->readULong(1));
523
128M
    if ((c == 1 || c == 2) && readFont(font, zone.m_pos.end())) {
524
8.02M
      listener->setFont(font.m_font);
525
8.02M
      f << "[" << font.m_font.getDebugString(m_parserState->m_fontConverter) << font << "]";
526
8.02M
      continue;
527
8.02M
    }
528
120M
    if (c == 0) {
529
56.0M
      f << "#";
530
56.0M
      continue;
531
56.0M
    }
532
64.0M
    f << char(c);
533
64.0M
    switch (c) {
534
1.19M
    case 0x9:
535
1.19M
      listener->insertTab();
536
1.19M
      break;
537
360k
    case 0x10: // cursor pos
538
812k
    case 0x11:
539
812k
      break;
540
62.0M
    default:
541
62.0M
      if (c >= 0x14 && c <= 0x19 && vers >= 3) {
542
2.43M
        int sz = (c==0x19) ? 0 : (c == 0x18) ? 1 : 2;
543
2.43M
        int id = (sz && pos+1+sz <=  zone.m_pos.end()) ? int(input->readLong(sz)) : 0;
544
2.43M
        if (id) f << "[" << id << "]";
545
2.43M
        switch (c) {
546
125k
        case 0x19:
547
125k
          listener->insertField(MWAWField(MWAWField::Title));
548
125k
          break;
549
394k
        case 0x18:
550
394k
          listener->insertField(MWAWField(MWAWField::PageNumber));
551
394k
          break;
552
200k
        case 0x16:
553
200k
          listener->insertField(MWAWField(MWAWField::Time));
554
200k
          break;
555
228k
        case 0x17: // id = 0 : short date ; id=9 : long date
556
228k
          listener->insertField(MWAWField(MWAWField::Date));
557
228k
          break;
558
124k
        case 0x15:
559
124k
          MWAW_DEBUG_MSG(("MsWks3Text::sendText: find unknown field type 0x15\n"));
560
124k
          break;
561
1.36M
        case 0x14:
562
1.36M
          if (!zone.isNote()) {
563
472k
            MWAWSubDocumentPtr subdoc
564
472k
            (new MsWks3TextInternal::SubDocument(*this, m_document.getInput(), zoneId, id));
565
472k
            listener->insertNote(MWAWNote(MWAWNote::FootNote), subdoc);
566
472k
          }
567
1.36M
          break;
568
0
        default:
569
0
          break;
570
2.43M
        }
571
2.43M
      }
572
59.6M
      else if (c <= 0x1f) {
573
15.3M
        f << "#" << std::hex << c << std::dec << "]";
574
15.3M
        ascFile.addDelimiter(pos,'#');
575
15.3M
        MWAW_DEBUG_MSG(("MsWks3Text::sendText: find char=%x\n", static_cast<unsigned int>(c)));
576
15.3M
      }
577
44.3M
      else
578
44.3M
        listener->insertCharacter(static_cast<unsigned char>(c), input, zone.m_pos.end());
579
62.0M
      break;
580
64.0M
    }
581
64.0M
  }
582
4.62M
  if (listener)
583
4.62M
    listener->insertEOL();
584
4.62M
  ascFile.addPos(zone.m_pos.begin());
585
4.62M
  ascFile.addNote(f.str().c_str());
586
4.62M
  return true;
587
4.62M
}
588
589
bool MsWks3Text::sendString(std::string &str)
590
10.3k
{
591
10.3k
  MWAWListenerPtr listener=m_parserState->getMainListener();
592
10.3k
  if (!listener)
593
0
    return true;
594
10.3k
  MsWks3TextInternal::Font defFont;
595
10.3k
  defFont.m_font = MWAWFont(20,12);
596
10.3k
  listener->setFont(defFont.m_font);
597
598
403k
  for (auto c : str) {
599
403k
    switch (c) {
600
12.2k
    case 0x9:
601
12.2k
      listener->insertTab();
602
12.2k
      break;
603
6.29k
    case 0x10: // cursor pos
604
15.8k
    case 0x11:
605
15.8k
      break;
606
4.59k
    case 0x19:
607
4.59k
      listener->insertField(MWAWField(MWAWField::Title));
608
4.59k
      break;
609
11.9k
    case 0x18:
610
11.9k
      listener->insertField(MWAWField(MWAWField::PageNumber));
611
11.9k
      break;
612
6.35k
    case 0x16:
613
6.35k
      listener->insertField(MWAWField(MWAWField::Time));
614
6.35k
      break;
615
17.8k
    case 0x17: // id = 0 : short date ; id=9 : long date
616
17.8k
      listener->insertField(MWAWField(MWAWField::Date));
617
17.8k
      break;
618
5.40k
    case 0x15:
619
5.40k
      MWAW_DEBUG_MSG(("MsWks3Text::sendString: find unknown field type 0x15\n"));
620
5.40k
      break;
621
10.8k
    case 0x14: // fixme
622
10.8k
      MWAW_DEBUG_MSG(("MsWks3Text::sendString: footnote are not implemented\n"));
623
10.8k
      break;
624
318k
    default:
625
318k
      listener->insertCharacter(static_cast<unsigned char>(c));
626
318k
      break;
627
403k
    }
628
403k
  }
629
10.3k
  return true;
630
10.3k
}
631
632
////////////////////////////////////////////////////////////
633
// the font:
634
////////////////////////////////////////////////////////////
635
bool MsWks3Text::readFont(MsWks3TextInternal::Font &font, long endPos)
636
8.75M
{
637
8.75M
  int vers = version();
638
8.75M
  font = MsWks3TextInternal::Font();
639
8.75M
  MWAWInputStreamPtr input=m_document.getInput();
640
8.75M
  long pos  = input->tell();
641
8.75M
  input->seek(-1, librevenge::RVNG_SEEK_CUR);
642
8.75M
  auto type = static_cast<int>(input->readLong(1));
643
8.75M
  if ((type != 1 && type != 2) || pos+type+3 > endPos) {
644
719k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
645
719k
    return false;
646
719k
  }
647
8.03M
  libmwaw::DebugStream f;
648
8.03M
  auto flag = static_cast<int>(input->readULong(1)); // check or font ?
649
8.03M
  if (flag) f << "#f0=" << flag << ",";
650
8.03M
  font.m_font.setId(static_cast<int>(input->readULong(1)));
651
8.03M
  font.m_font.setSize(float(input->readULong(1)));
652
8.03M
  flag = static_cast<int>(input->readULong(1));
653
8.03M
  uint32_t flags = 0;
654
8.03M
  if (flag & 0x1) flags |= MWAWFont::boldBit;
655
8.03M
  if (flag & 0x2) flags |= MWAWFont::italicBit;
656
8.03M
  if (flag & 0x4) font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
657
8.03M
  if (flag & 0x8) flags |= MWAWFont::embossBit;
658
8.03M
  if (flag & 0x10) flags |= MWAWFont::shadowBit;
659
8.03M
  if (flag & 0x20) {
660
1.35M
    if (vers==1)
661
19.7k
      font.m_font.set(MWAWFont::Script(20,librevenge::RVNG_PERCENT,80));
662
1.33M
    else
663
1.33M
      font.m_font.set(MWAWFont::Script::super100());
664
1.35M
  }
665
8.03M
  if (flag & 0x40) {
666
1.90M
    if (vers==1)
667
20.0k
      font.m_font.set(MWAWFont::Script(-20,librevenge::RVNG_PERCENT,80));
668
1.88M
    else
669
1.88M
      font.m_font.set(MWAWFont::Script::sub100());
670
1.90M
  }
671
8.03M
  if ((flag & 0x80) && !(flag & 0x60)) f << "fFl80#,";
672
8.03M
  font.m_font.setFlags(flags);
673
8.03M
  int color = 1;
674
8.03M
  if (type == 2) {
675
3.35M
    color=static_cast<int>(input->readULong(1));
676
3.35M
  }
677
4.68M
  else if (pos+type+5 <= endPos) {
678
4.61M
    auto val = static_cast<int>(input->readULong(1));
679
4.61M
    if (val == 0)
680
1.72M
      f << "end0#,";
681
2.88M
    else
682
2.88M
      input->seek(-1, librevenge::RVNG_SEEK_CUR);
683
4.61M
  }
684
8.03M
  if (color != 1) {
685
3.25M
    MWAWColor col;
686
3.25M
    if (m_document.getColor(color,col,vers))
687
3.16M
      font.m_font.setColor(col);
688
93.4k
    else
689
93.4k
      f << "#fColor=" << color << ",";
690
3.25M
  }
691
8.03M
  font.m_extra = f.str();
692
8.03M
  return true;
693
8.75M
}
694
695
////////////////////////////////////////////////////////////
696
// the tabulations:
697
////////////////////////////////////////////////////////////
698
bool MsWks3Text::readParagraph(MsWks3TextInternal::LineZone &zone, MWAWParagraph &parag)
699
780k
{
700
780k
  int dataSize = int(zone.m_pos.length())-6;
701
780k
  if (dataSize < 15) return false;
702
733k
  MWAWInputStreamPtr input=m_document.getInput();
703
733k
  input->seek(zone.m_pos.begin()+6, librevenge::RVNG_SEEK_SET);
704
705
733k
  parag = MWAWParagraph();
706
733k
  libmwaw::DebugFile &ascFile = m_document.ascii();
707
733k
  libmwaw::DebugStream f;
708
709
733k
  int fl[2];
710
733k
  bool firstFlag = (dataSize & 1) == 0;
711
733k
  fl[0] = firstFlag ? static_cast<int>(input->readULong(1)) : 0x4c;
712
733k
  switch (fl[0]) {
713
149k
  case 0x4c:
714
149k
    break;
715
15.0k
  case 0x43:
716
15.0k
    parag.m_justify = MWAWParagraph::JustificationCenter;
717
15.0k
    break;
718
14.3k
  case 0x52:
719
14.3k
    parag.m_justify = MWAWParagraph::JustificationRight;
720
14.3k
    break;
721
18.9k
  case 0x46:
722
18.9k
    parag.m_justify = MWAWParagraph::JustificationFull;
723
18.9k
    break;
724
535k
  default:
725
535k
    f << "#align=" << std::hex << fl[0] << ",";
726
535k
    MWAW_DEBUG_MSG(("MsWks3Text::readParagraph: unknown alignment %x\n", static_cast<unsigned int>(fl[0])));
727
535k
    break;
728
733k
  }
729
733k
  fl[1] = static_cast<int>(input->readULong(1));
730
733k
  if (fl[1])
731
618k
    f << "fl0=" <<fl[1] << std::dec << ",";
732
733k
  int dim[3];
733
733k
  bool ok = true;
734
2.93M
  for (int i = 0; i < 3; i++) {
735
2.19M
    dim[i] = static_cast<int>(input->readULong(2));
736
2.19M
    if (i==0&&(dim[0]&0x8000)) {
737
63.1k
      dim[0] &= 0x7FFF;
738
63.1k
      f << "6linesByInches,";
739
63.1k
    }
740
2.19M
    if (dim[i] > 3000) ok = false;
741
2.19M
  }
742
733k
  if (!ok) {
743
570k
    MWAW_DEBUG_MSG(("MsWks3Text::readParagraph: size is very odd\n"));
744
570k
    f << "##";
745
570k
  }
746
733k
  if (dim[0] || dim[1] || dim[2]) {
747
702k
    f << "size=" << dim[1] << "x" << dim[0];
748
702k
    if (dim[2]) f << "[" << dim[2] << "]";
749
702k
    f << ",";
750
702k
  }
751
752
733k
  int fl2[2];
753
733k
  for (auto &flag : fl2) // between -16 and 16
754
1.46M
    flag = static_cast<int>(input->readULong(1));
755
733k
  if (fl2[0] || fl2[1])
756
300k
    f << "fl2=(" << std::hex << fl2[0] << ", " <<fl2[1] << ")" << std::dec << ",";
757
758
2.93M
  for (int i = 0; i < 3; i++) {
759
2.19M
    auto val = static_cast<int>(input->readULong(2));
760
2.19M
    int flag = (val & 0xc000) >> 14;
761
2.19M
    val = (val & 0x3fff);
762
2.19M
    if (val > 8000 || flag) {
763
968k
      MWAW_DEBUG_MSG(("MsWks3Text::readParagraph: find odd margin %d\n", i));
764
968k
      f << "#margin" << i << "=" << val  << "(" << flag << "),";
765
968k
    }
766
2.19M
    if (val > 8000) continue;
767
    // i = 0 (last), i = 1 (firstL), i=2 (nextL)
768
1.71M
    parag.m_margins[2-i] = val/72.0;
769
1.71M
  }
770
733k
  *(parag.m_margins[0]) -= *(parag.m_margins[1]);
771
733k
  if (parag.m_margins[2].get() > 0.0)
772
504k
    parag.m_margins[2] = m_mainParser->getPageWidth()-*(parag.m_margins[2]);
773
733k
  if (parag.m_margins[2].get() > 56./72.) *(parag.m_margins[2]) -= 28./72.;
774
340k
  else if (parag.m_margins[2].get() >=0.0) *(parag.m_margins[2]) *= 0.5;
775
111k
  else parag.m_margins[2] = 0.0;
776
733k
  int numVal = (dataSize-9)/2-3;
777
733k
  parag.m_tabs->resize(size_t(numVal));
778
733k
  size_t numTabs = 0;
779
780
  // checkme: in order to avoid x_tabs > textWidth (appears sometimes when i=0)
781
733k
  auto maxWidth = long(m_mainParser->getPageWidth()*72-36);
782
733k
  if (dim[1] > maxWidth) maxWidth = dim[1];
783
784
65.6M
  for (int i = 0; i < numVal; i++) {
785
64.9M
    auto val = static_cast<int>(input->readULong(2));
786
64.9M
    MWAWTabStop::Alignment align = MWAWTabStop::LEFT;
787
64.9M
    switch (val >> 14) {
788
2.93M
    case 1:
789
2.93M
      align = MWAWTabStop::DECIMAL;
790
2.93M
      break;
791
3.67M
    case 2:
792
3.67M
      align = MWAWTabStop::RIGHT;
793
3.67M
      break;
794
3.65M
    case 3:
795
3.65M
      align = MWAWTabStop::CENTER;
796
3.65M
      break;
797
54.7M
    default:
798
54.7M
      break;
799
64.9M
    }
800
64.9M
    val = (val & 0x3fff);
801
64.9M
    if (val > maxWidth) {
802
7.47M
      static bool first = true;
803
7.47M
      if (first) {
804
34
        MWAW_DEBUG_MSG(("MsWks3Text::readParagraph: finds some odd tabs (ignored)\n"));
805
34
        first = false;
806
34
      }
807
7.47M
      f << "#tabs" << i << "=" << val << ",";
808
7.47M
      continue;
809
7.47M
    }
810
57.4M
    (*parag.m_tabs)[numTabs].m_alignment = align;
811
57.4M
    (*parag.m_tabs)[numTabs++].m_position = val/72.0;
812
57.4M
  }
813
733k
  if (int(numTabs)!=numVal) parag.m_tabs->resize(numTabs);
814
733k
  parag.m_extra = f.str();
815
816
733k
  f.str("");
817
733k
  f << "Entries(Paragraph):" << zone << "," << parag << ",";
818
733k
  ascFile.addPos(zone.m_pos.begin());
819
733k
  ascFile.addNote(f.str().c_str());
820
821
733k
  return true;
822
733k
}
823
824
////////////////////////////////////////////////////////////
825
// read v1-v2 header/footer string
826
////////////////////////////////////////////////////////////
827
std::string MsWks3Text::readHeaderFooterString(bool header)
828
13.8k
{
829
13.8k
  std::string res("");
830
13.8k
  MWAWInputStreamPtr input=m_document.getInput();
831
13.8k
  auto numChar = static_cast<int>(input->readULong(1));
832
13.8k
  if (!numChar) return res;
833
10.3k
  if (!input->checkPosition(input->tell()+numChar)) {
834
947
    MWAW_DEBUG_MSG(("MsWks3Text::readHeaderFooterString: the number of char seems bad\n"));
835
947
    return res;
836
947
  }
837
340k
  for (int i = 0; i < numChar; i++) {
838
337k
    auto c = static_cast<unsigned char>(input->readULong(1));
839
337k
    if (c == 0) {
840
6.75k
      input->seek(-1, librevenge::RVNG_SEEK_CUR);
841
6.75k
      break;
842
6.75k
    }
843
331k
    if (c == '&') {
844
18.0k
      auto nextC = static_cast<unsigned char>(input->readULong(1));
845
18.0k
      bool field = true;
846
18.0k
      switch (nextC) {
847
132
      case 'F':
848
132
        res += char(0x19);
849
132
        break; // file name
850
2.39k
      case 'D':
851
2.39k
        res += char(0x17);
852
2.39k
        break; // date
853
1.27k
      case 'P':
854
1.27k
        res += char(0x18);
855
1.27k
        break; // page
856
4.37k
      case 'T':
857
4.37k
        res += char(0x16);
858
4.37k
        break; // time
859
9.90k
      default:
860
9.90k
        field = false;
861
18.0k
      }
862
18.0k
      if (field) continue;
863
9.90k
      input->seek(-1, librevenge::RVNG_SEEK_CUR);
864
9.90k
    }
865
322k
    res += char(c);
866
322k
  }
867
9.44k
  if (res.length()) {
868
9.01k
    m_state->m_zones.push_back(MsWks3TextInternal::TextZone());
869
9.01k
    auto &zone = m_state->m_zones.back();
870
9.01k
    zone.m_id = int(m_state->m_zones.size())-1;
871
9.01k
    zone.m_type = header ? MsWks3TextInternal::TextZone::Header :
872
9.01k
                  MsWks3TextInternal::TextZone::Footer;
873
9.01k
    zone.m_text = res;
874
9.01k
  }
875
9.44k
  return res;
876
9.44k
}
877
878
////////////////////////////////////////////////////////////
879
//
880
// Low level
881
//
882
////////////////////////////////////////////////////////////
883
void MsWks3Text::send(MsWks3TextInternal::TextZone &zone, MWAWVec2i limit)
884
1.44M
{
885
1.44M
  auto numZones = int(zone.m_zonesList.size());
886
  // set the default font
887
1.44M
  if (m_parserState->getMainListener())
888
1.44M
    m_parserState->getMainListener()->setFont(MWAWFont(20,12));
889
1.44M
  if (numZones == 0 && zone.m_text.length()) {
890
10.3k
    sendString(zone.m_text);
891
10.3k
    zone.m_isSent = true;
892
10.3k
    return;
893
10.3k
  }
894
1.43M
  bool isMain = false;
895
1.43M
  MWAWVec2i notePos(-1, -1);
896
1.43M
  if (limit[0] < 0) {
897
1.30M
    limit = MWAWVec2i(0,numZones);
898
1.30M
    isMain = zone.isMain();
899
    // find the notes in the text zones
900
1.30M
    for (auto const &noteIt : zone.m_footnoteMap) {
901
644k
      if (notePos[0]==-1) {
902
174k
        notePos = noteIt.second;
903
174k
        continue;
904
174k
      }
905
470k
      if (notePos[0] > noteIt.second[0])
906
170k
        notePos[0] = noteIt.second[0];
907
470k
      if (notePos[1] < noteIt.second[1])
908
114k
        notePos[1] = noteIt.second[1];
909
470k
    }
910
1.30M
  }
911
912
7.01M
  for (int i = limit[0]; i < limit[1]; i++) {
913
5.58M
    if (i == notePos[0]) {
914
174k
      i = notePos[1]-1;
915
174k
      continue;
916
174k
    }
917
5.40M
    if (isMain && zone.m_pagesPosition.find(i) != zone.m_pagesPosition.end()) {
918
28.6k
      ++m_state->m_actualPage;
919
28.6k
      m_document.newPage(m_state->m_actualPage, zone.m_pagesPosition[i]);
920
28.6k
    }
921
5.40M
    auto &z = zone.m_zonesList[size_t(i)];
922
5.40M
    if (z.m_type & 0x80) {
923
780k
      MWAWParagraph parag;
924
780k
      if (readParagraph(z, parag) && m_parserState->getMainListener())
925
733k
        m_parserState->getMainListener()->setParagraph(parag);
926
780k
    }
927
4.62M
    else
928
4.62M
      sendText(z, zone.m_id);
929
5.40M
  }
930
1.43M
  zone.m_isSent = true;
931
1.43M
}
932
933
void MsWks3Text::sendNote(int zoneId, int noteId)
934
472k
{
935
472k
  MWAWListenerPtr listener=m_parserState->getMainListener();
936
472k
  if (zoneId < 0 || zoneId >= int(m_state->m_zones.size())) {
937
0
    if (listener) listener->insertChar(' ');
938
0
    MWAW_DEBUG_MSG(("MsWks3Text::sendNote: unknown zone %d\n", zoneId));
939
0
    return;
940
0
  }
941
472k
  auto &zone=m_state->m_zones[size_t(zoneId)];
942
472k
  auto noteIt = zone.m_footnoteMap.find(noteId);
943
472k
  if (noteIt==zone.m_footnoteMap.end()) {
944
335k
    MWAW_DEBUG_MSG(("MsWks3Text::sendNote: unknown note %d-%d\n", zoneId, noteId));
945
335k
    if (listener) listener->insertChar(' ');
946
335k
  }
947
136k
  else
948
136k
    send(zone, noteIt->second);
949
472k
}
950
951
void MsWks3Text::sendZone(int zoneId)
952
1.30M
{
953
1.30M
  if (zoneId < 0 || zoneId >= int(m_state->m_zones.size())) {
954
0
    MWAW_DEBUG_MSG(("MsWks3Text::sendZone: unknown zone %d\n", zoneId));
955
0
    return;
956
0
  }
957
1.30M
  send(m_state->m_zones[size_t(zoneId)]);
958
1.30M
}
959
960
961
void MsWks3Text::flushExtra()
962
54.9k
{
963
54.9k
  for (auto &zone : m_state->m_zones) {
964
34.1k
    if (zone.m_isSent)
965
24.9k
      continue;
966
9.19k
    send(zone);
967
9.19k
  }
968
54.9k
}
969
970
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: