Coverage Report

Created: 2026-09-14 06:37

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