Coverage Report

Created: 2026-04-29 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/DocMkrText.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 "MWAWTextListener.hxx"
43
#include "MWAWDebug.hxx"
44
#include "MWAWFont.hxx"
45
#include "MWAWFontConverter.hxx"
46
#include "MWAWPageSpan.hxx"
47
#include "MWAWParagraph.hxx"
48
#include "MWAWPosition.hxx"
49
#include "MWAWRSRCParser.hxx"
50
#include "MWAWSubDocument.hxx"
51
52
#include "DocMkrParser.hxx"
53
54
#include "DocMkrText.hxx"
55
56
/** Internal: the structures of a DocMkrText */
57
namespace DocMkrTextInternal
58
{
59
////////////////////////////////////////
60
//! Internal: structure to store a table of contents of a DocMkrText
61
struct TOC {
62
  //! constructor
63
  TOC()
64
1.11k
    : m_cIdList()
65
1.11k
    , m_textList()
66
1.11k
  {
67
1.11k
  }
68
  //! returns true if the table is empty
69
  bool empty() const
70
521
  {
71
521
    return m_textList.empty();
72
521
  }
73
  //! the toc chapter id
74
  std::vector<int> m_cIdList;
75
  //! the toc texts
76
  std::vector<std::string> m_textList;
77
};
78
79
////////////////////////////////////////
80
//! Internal: structure to store a footer data of a DocMkrText
81
struct Footer {
82
  //! constructor
83
  Footer()
84
1.11k
    : m_font(3,9)
85
1.11k
    , m_chapterResetPage(false)
86
1.11k
    , m_userInfo()
87
1.11k
    , m_extra("")
88
1.11k
  {
89
6.68k
    for (auto &item : m_items) item=0;
90
1.11k
  }
91
  //! returns true if the footer is empty
92
  bool empty() const
93
154k
  {
94
154k
    for (auto item : m_items)
95
155k
      if (item)
96
154k
        return false;
97
142
    return true;
98
154k
  }
99
  //! operator<<
100
  friend std::ostream &operator<<(std::ostream &o, Footer const &f)
101
0
  {
102
0
    static char const* const where[]= {"TL", "TC", "TR", "BL", "BC", "BR" };
103
0
    static char const* const what[]= { "nothing", "unkn1", "unkn2", "time", "date",
104
0
                                 "page", "fileName", "chapName", "userText"
105
0
                               };
106
0
    if (f.m_chapterResetPage)
107
0
      o << "pageReset[chapter],";
108
0
    for (int i = 0; i < 6; i++) {
109
0
      if (!f.m_items[i]) continue;
110
0
      o << where[i] << "=";
111
0
      if (f.m_items[i] > 0 && f.m_items[i] <= 8)
112
0
        o << what[f.m_items[i]] << ",";
113
0
      else
114
0
        o << "#unkn" << f.m_items[i] << ",";
115
0
    }
116
0
    o << f.m_extra;
117
0
    return o;
118
0
  }
119
  //! the font
120
  MWAWFont m_font;
121
  //! true if a chapter reset page
122
  bool m_chapterResetPage;
123
  //! the item values
124
  int m_items[6];
125
  //! the user information entry
126
  std::string m_userInfo;
127
  //! extra data
128
  std::string m_extra;
129
};
130
131
////////////////////////////////////////
132
//! Internal: structure to store a the data of a DocMkrText Zone
133
struct Zone {
134
  //! constructor
135
  Zone()
136
1.30k
    : m_pos()
137
1.30k
    , m_justify(MWAWParagraph::JustificationLeft)
138
1.30k
    , m_backgroundColor(MWAWColor::white())
139
1.30k
    , m_useFooter(true)
140
1.30k
    , m_name("")
141
1.30k
    , m_posFontMap()
142
1.30k
    , m_numPages(0)
143
1.30k
    , m_parsed(false)
144
1.30k
  {
145
5.20k
    for (auto &margin : m_margins) margin=54;
146
1.30k
  }
147
  //! the text entry
148
  MWAWEntry m_pos;
149
  //! the paragraph justification
150
  MWAWParagraph::Justification m_justify;
151
  //! the background color
152
  MWAWColor m_backgroundColor;
153
  //! print or ignore the footer
154
  bool m_useFooter;
155
  //! the margins L,T,R,B in points
156
  int m_margins[4];
157
  //! the name
158
  std::string m_name;
159
  //! the map of id -> font
160
  std::map<long, MWAWFont > m_posFontMap;
161
  //! the number of page
162
  mutable int m_numPages;
163
  //! a flag to know if we have send the data to the listener
164
  mutable bool m_parsed;
165
};
166
////////////////////////////////////////
167
//! Internal: the state of a DocMkrText
168
struct State {
169
  //! constructor
170
  State()
171
1.11k
    : m_version(-1)
172
1.11k
    , m_numPages(-1)
173
1.11k
    , m_actualPage(0)
174
1.11k
    , m_pageWidth(8.5)
175
1.11k
    , m_idZoneMap()
176
1.11k
    , m_footer()
177
1.11k
    , m_toc()
178
1.11k
  {
179
1.11k
  }
180
  //! returns the zone corresponding to an id
181
  Zone &getZone(int id)
182
156k
  {
183
156k
    if (m_idZoneMap.find(id)==m_idZoneMap.end())
184
650
      m_idZoneMap[id]=Zone();
185
156k
    return m_idZoneMap.find(id)->second;
186
156k
  }
187
188
  //! the file version
189
  mutable int m_version;
190
191
  int m_numPages /* the number of pages */, m_actualPage /* the actual page */;
192
  //! the page width ( not really define so default value 8.5)
193
  double m_pageWidth;
194
  //! the map of id -> text zone
195
  std::map<int, Zone > m_idZoneMap;
196
  //! the footer
197
  Footer m_footer;
198
  //! the table of content
199
  TOC m_toc;
200
};
201
202
////////////////////////////////////////
203
//! Internal: the subdocument of a DocMkrText
204
class SubDocument final : public MWAWSubDocument
205
{
206
public:
207
  //! constructor for a footer zone
208
  SubDocument(DocMkrText &pars, MWAWInputStreamPtr const &input, int id, libmwaw::SubDocumentType type)
209
195
    : MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
210
195
    , m_textParser(&pars)
211
195
    , m_id(id)
212
195
    , m_text("")
213
195
    , m_type(type) {}
214
215
  //! constructor for a comment zone
216
  SubDocument(DocMkrText &pars, MWAWInputStreamPtr const &input, std::string const &text, libmwaw::SubDocumentType type)
217
0
    : MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
218
0
    , m_textParser(&pars)
219
0
    , m_id(-1)
220
0
    , m_text(text)
221
0
    , m_type(type) {}
222
223
  //! destructor
224
195
  ~SubDocument() final {}
225
226
  //! operator!=
227
  bool operator!=(MWAWSubDocument const &doc) const final;
228
  //! the parser function
229
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
230
231
protected:
232
  /** the text parser */
233
  DocMkrText *m_textParser;
234
  //! the subdocument id
235
  int m_id;
236
  //! the string text
237
  std::string m_text;
238
  //! the subdocument type
239
  libmwaw::SubDocumentType m_type;
240
private:
241
  SubDocument(SubDocument const &orig) = delete;
242
  SubDocument &operator=(SubDocument const &orig) = delete;
243
};
244
245
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
246
154k
{
247
154k
  if (!listener.get()) {
248
0
    MWAW_DEBUG_MSG(("SubDocument::parse: no listener\n"));
249
0
    return;
250
0
  }
251
154k
  if (!m_textParser) {
252
0
    MWAW_DEBUG_MSG(("SubDocument::parse: no text parser\n"));
253
0
    return;
254
0
  }
255
256
154k
  long pos = m_input->tell();
257
154k
  if (m_type == libmwaw::DOC_HEADER_FOOTER)
258
154k
    m_textParser->sendFooter(m_id);
259
0
  else if (m_type == libmwaw::DOC_COMMENT_ANNOTATION) {
260
0
    listener->setFont(MWAWFont(3,10));
261
0
    m_textParser->sendString(m_text);
262
0
  }
263
0
  else {
264
0
    MWAW_DEBUG_MSG(("SubDocument::parse: oops do not know how to send this kind of document\n"));
265
0
  }
266
154k
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
267
154k
}
268
269
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
270
0
{
271
0
  if (MWAWSubDocument::operator!=(doc)) return true;
272
0
  auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
273
0
  if (!sDoc) return true;
274
0
  if (m_textParser != sDoc->m_textParser) return true;
275
0
  if (m_id != sDoc->m_id) return true;
276
0
  if (m_text != sDoc->m_text) return true;
277
0
  if (m_type != sDoc->m_type) return true;
278
0
  return false;
279
0
}
280
}
281
282
////////////////////////////////////////////////////////////
283
// constructor/destructor, ...
284
////////////////////////////////////////////////////////////
285
DocMkrText::DocMkrText(DocMkrParser &parser)
286
1.11k
  : m_parserState(parser.getParserState())
287
1.11k
  , m_state(new DocMkrTextInternal::State)
288
1.11k
  , m_mainParser(&parser)
289
1.11k
{
290
1.11k
}
291
292
DocMkrText::~DocMkrText()
293
1.11k
{
294
1.11k
}
295
296
int DocMkrText::version() const
297
0
{
298
0
  if (m_state->m_version < 0)
299
0
    m_state->m_version = m_parserState->m_version;
300
0
  return m_state->m_version;
301
0
}
302
303
int DocMkrText::numPages() const
304
312
{
305
312
  if (m_state->m_numPages >= 0)
306
0
    return m_state->m_numPages;
307
312
  m_state->m_actualPage = 1;
308
309
312
  int nPages = 0;
310
  // do not replace by auto it : m_state->m_idZoneMap as it->second.m_numPages is modified
311
650
  for (auto const &it : m_state->m_idZoneMap) {
312
650
    auto const &zone= it.second;
313
650
    computeNumPages(zone);
314
650
    nPages += zone.m_numPages;
315
650
  }
316
312
  m_state->m_numPages = nPages;
317
312
  return m_state->m_numPages;
318
312
}
319
320
int DocMkrText::numChapters() const
321
312
{
322
312
  return int(m_state->m_idZoneMap.size());
323
312
}
324
325
void DocMkrText::sendComment(std::string const &str)
326
0
{
327
0
  if (!m_parserState->m_textListener) {
328
0
    MWAW_DEBUG_MSG(("DocMkrText::sendComment: called without listener\n"));
329
0
    return;
330
0
  }
331
0
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
332
0
  std::shared_ptr<MWAWSubDocument> comment
333
0
  (new DocMkrTextInternal::SubDocument(*this, input, str, libmwaw::DOC_COMMENT_ANNOTATION));
334
0
  m_parserState->m_textListener->insertComment(comment);
335
0
}
336
337
////////////////////////////////////////////////////////////
338
// pages/...
339
////////////////////////////////////////////////////////////
340
void DocMkrText::computeNumPages(DocMkrTextInternal::Zone const &zone) const
341
650
{
342
650
  if (zone.m_numPages || !zone.m_pos.valid())
343
395
    return;
344
255
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
345
255
  input->seek(zone.m_pos.begin(), librevenge::RVNG_SEEK_SET);
346
255
  long len = zone.m_pos.length();
347
255
  int nPages=1;
348
1.35M
  for (long i=0; i < len; i++) {
349
1.35M
    if (input->isEnd())
350
0
      break;
351
1.35M
    if (input->readLong(1)==0)
352
209k
      nPages++;
353
1.35M
  }
354
255
  zone.m_numPages = nPages;
355
255
}
356
357
void DocMkrText::updatePageSpanList(std::vector<MWAWPageSpan> &spanList)
358
312
{
359
312
  numPages();
360
312
  spanList.resize(0);
361
312
  MWAWPageSpan ps;
362
312
  ps.setMarginTop(0.1);
363
312
  ps.setMarginBottom(0.015);
364
312
  ps.setMarginLeft(0.1);
365
312
  ps.setMarginRight(0.1);
366
312
  bool hasFooter = !m_state->m_footer.empty();
367
312
  bool needResetPage = m_state->m_footer.m_chapterResetPage;
368
312
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
369
650
  for (auto it : m_state->m_idZoneMap) {
370
650
    int zId = it.first;
371
650
    DocMkrTextInternal::Zone const &zone= it.second;
372
650
    if (zone.m_numPages <= 0)
373
395
      continue;
374
255
    MWAWPageSpan span(ps);
375
255
    if (needResetPage)
376
0
      span.setPageNumber(1);
377
255
    if (zone.m_margins[0]>=0)
378
255
      span.setMarginLeft(double(zone.m_margins[0])/72.);
379
255
    if (zone.m_margins[1]>=0)
380
255
      span.setMarginTop(double(zone.m_margins[1])/72.);
381
255
    if (zone.m_margins[2]>=0)
382
255
      span.setMarginRight(double(zone.m_margins[2])/72.);
383
255
    if (zone.m_margins[3]>=0)
384
152
      span.setMarginBottom(double(zone.m_margins[3])/72.);
385
255
    span.setBackgroundColor(zone.m_backgroundColor);
386
255
    if (hasFooter && zone.m_useFooter) {
387
195
      MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
388
195
      footer.m_subDocument.reset
389
195
      (new DocMkrTextInternal::SubDocument(*this, input, zId, libmwaw::DOC_HEADER_FOOTER));
390
195
      span.setHeaderFooter(footer);
391
195
    }
392
210k
    for (int i = 0; i < zone.m_numPages; i++) {
393
210k
      spanList.push_back(span);
394
210k
      span.setPageNumber(-1);
395
210k
    }
396
255
  }
397
312
  if (spanList.size()==0 || !m_state->m_toc.empty())
398
103
    spanList.push_back(ps);
399
312
}
400
401
////////////////////////////////////////////////////////////
402
// Intermediate level
403
////////////////////////////////////////////////////////////
404
405
// find the different zones
406
bool DocMkrText::createZones()
407
435
{
408
435
  if (!m_mainParser->getRSRCParser()) {
409
0
    MWAW_DEBUG_MSG(("DocMkrText::createZones: can not find the entry map\n"));
410
0
    return false;
411
0
  }
412
435
  MWAWRSRCParserPtr rsrcParser = m_mainParser->getRSRCParser();
413
435
  auto const &entryMap = rsrcParser->getEntriesMap();
414
415
  // entry 128: font name and size
416
435
  auto it = entryMap.lower_bound("rQDF");
417
747
  while (it != entryMap.end()) {
418
656
    if (it->first != "rQDF")
419
344
      break;
420
312
    MWAWEntry const &entry = it++->second;
421
312
    readFontNames(entry);
422
312
  }
423
  // Footer: foot:128
424
435
  it = entryMap.lower_bound("foot");
425
747
  while (it != entryMap.end()) {
426
657
    if (it->first != "foot")
427
345
      break;
428
312
    MWAWEntry const &entry = it++->second;
429
312
    readFooter(entry);
430
312
  }
431
  // entry 128: ?
432
435
  it = entryMap.lower_bound("cnt#");
433
435
  while (it != entryMap.end()) {
434
345
    if (it->first != "cnt#")
435
345
      break;
436
0
    MWAWEntry const &entry = it++->second;
437
0
    readTOC(entry);
438
0
  }
439
440
  // the chapter zone zone
441
435
  it = entryMap.lower_bound("styl");
442
1.05k
  while (it != entryMap.end()) {
443
966
    if (it->first != "styl")
444
344
      break;
445
622
    MWAWEntry const &entry = it++->second;
446
622
    readStyles(entry);
447
622
  }
448
435
  it = entryMap.lower_bound("TEXT");
449
1.05k
  while (it != entryMap.end()) {
450
969
    if (it->first != "TEXT")
451
345
      break;
452
624
    MWAWEntry const &entry = it++->second;
453
624
    m_state->getZone(entry.id()).m_pos = entry;
454
624
  }
455
456
435
  it = entryMap.lower_bound("Wndo");
457
1.07k
  while (it != entryMap.end()) {
458
981
    if (it->first != "Wndo")
459
345
      break;
460
636
    MWAWEntry const &entry = it++->second;
461
636
    readWindows(entry);
462
636
  }
463
  // font color
464
435
  it = entryMap.lower_bound("clut");
465
914
  while (it != entryMap.end()) {
466
824
    if (it->first != "clut")
467
345
      break;
468
479
    MWAWEntry const &entry = it++->second;
469
479
    std::vector<MWAWColor> cmap;
470
479
    rsrcParser->parseClut(entry, cmap);
471
479
    if (entry.id() != 128)
472
374
      continue;
473
125
    for (size_t i = 0; i < cmap.size(); i++) {
474
20
      if (m_state->m_idZoneMap.find(int(i)+128)==m_state->m_idZoneMap.end())
475
0
        continue;
476
20
      m_state->m_idZoneMap.find(int(i)+128)->second.m_backgroundColor=cmap[i];
477
20
    }
478
105
  }
479
480
435
  it = entryMap.lower_bound("STR ");
481
1.37k
  while (it != entryMap.end()) {
482
1.28k
    if (it->first != "STR ")
483
345
      break;
484
939
    MWAWEntry const &entry = it++->second;
485
    // 1000: footer (user information)
486
939
    if (entry.id()==1000 && entry.length()>0) {
487
10
      std::string userInfo("");
488
10
      rsrcParser->parseSTR(entry,userInfo);
489
10
      m_state->m_footer.m_userInfo = userInfo;
490
10
    }
491
    // 200?: chapter name
492
929
    else if (entry.id()>2000 &&
493
372
             m_state->m_idZoneMap.find(entry.id()-2001+128)!=m_state->m_idZoneMap.end()) {
494
58
      std::string name("");
495
58
      rsrcParser->parseSTR(entry,name);
496
58
      m_state->getZone(entry.id()-2001+128).m_name=name;
497
58
    }
498
939
  }
499
435
  return m_state->m_idZoneMap.size();
500
435
}
501
502
////////////////////////////////////////////////////////////
503
//    Text
504
////////////////////////////////////////////////////////////
505
bool DocMkrText::sendText(DocMkrTextInternal::Zone const &zone)
506
650
{
507
650
  MWAWTextListenerPtr listener=m_parserState->m_textListener;
508
650
  if (!listener) {
509
0
    MWAW_DEBUG_MSG(("DocMkrText::sendText: can not find the listener\n"));
510
0
    return false;
511
0
  }
512
650
  if (!zone.m_pos.valid()) {
513
395
    MWAW_DEBUG_MSG(("DocMkrText::sendText: the entry is bad\n"));
514
395
    return false;
515
395
  }
516
255
  zone.m_parsed = true;
517
255
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
518
255
  libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
519
255
  long pos = zone.m_pos.begin(), debPos=pos-4;
520
255
  input->seek(pos, librevenge::RVNG_SEEK_SET);
521
255
  libmwaw::DebugStream f;
522
255
  f << "Entries(TEXT)[" << zone.m_pos.id() << "]:";
523
255
  listener->setFont(MWAWFont(3,12));
524
255
  MWAWParagraph para;
525
255
  para.m_justify=zone.m_justify;
526
255
  listener->setParagraph(para);
527
255
  std::map<long, MWAWFont >::const_iterator fontIt;
528
255
  int nPict=0, zId=zone.m_pos.id()-128;
529
255
  double w = m_state->m_pageWidth-double(zone.m_margins[0]+zone.m_margins[2])/72.;
530
1.35M
  for (long i = 0; i <= zone.m_pos.length(); i++) {
531
1.35M
    bool endPos = (i==zone.m_pos.length());
532
1.35M
    unsigned char c=endPos ? static_cast<unsigned char>(0) : static_cast<unsigned char>(input->readULong(1));
533
1.35M
    if (endPos || c==0xd || c==0) {
534
210k
      ascFile.addPos(debPos);
535
210k
      ascFile.addNote(f.str().c_str());
536
210k
      debPos = input->tell();
537
210k
      if (endPos) break;
538
210k
      f.str("");
539
210k
      f << "TEXT:";
540
210k
    }
541
1.35M
    fontIt=zone.m_posFontMap.find(i);
542
1.35M
    if (fontIt != zone.m_posFontMap.end())
543
8.84k
      listener->setFont(fontIt->second);
544
1.35M
    if (c)
545
1.14M
      f << c;
546
1.35M
    switch (c) {
547
209k
    case 0:
548
209k
      m_mainParser->newPage(++m_state->m_actualPage);
549
209k
      break;
550
1.18k
    case 0x9:
551
1.18k
      listener->insertTab();
552
1.18k
      break;
553
397
    case 0xd:
554
397
      listener->insertEOL();
555
397
      break;
556
5.05k
    case 0x11: // command key
557
5.05k
      listener->insertUnicode(0x2318);
558
5.05k
      break;
559
8.57k
    case 0x14: // apple logo: check me
560
8.57k
      listener->insertUnicode(0xf8ff);
561
8.57k
      break;
562
11
    case 0xca:
563
11
      m_mainParser->sendPicture(zId, ++nPict, w);
564
11
      break;
565
1.12M
    default:
566
1.12M
      i += listener->insertCharacter(c, input, zone.m_pos.end());
567
1.12M
      break;
568
1.35M
    }
569
1.35M
  }
570
255
  return true;
571
255
}
572
573
////////////////////////////////////////////////////////////
574
//     Fonts
575
////////////////////////////////////////////////////////////
576
bool DocMkrText::readFontNames(MWAWEntry const &entry)
577
312
{
578
312
  if (!entry.valid() || entry.length()<2) {
579
122
    MWAW_DEBUG_MSG(("DocMkrText::readFontNames: the entry is bad\n"));
580
122
    return false;
581
122
  }
582
190
  entry.setParsed(true);
583
190
  long pos = entry.begin();
584
190
  long endPos = entry.end();
585
190
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
586
190
  libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
587
190
  input->seek(pos, librevenge::RVNG_SEEK_SET);
588
589
190
  libmwaw::DebugStream f;
590
190
  auto N=static_cast<int>(input->readULong(2));
591
190
  f << "Entries(FontName)[" << entry.type() << "-" << entry.id() << "]:N="<<N;
592
190
  ascFile.addPos(pos-4);
593
190
  ascFile.addNote(f.str().c_str());
594
595
8.35k
  for (int i = 0; i < N; i++) {
596
8.34k
    f.str("");
597
8.34k
    f << "FontName-" << i << ":";
598
8.34k
    pos = input->tell();
599
8.34k
    if (pos+1 > endPos) {
600
0
      f << "###";
601
0
      ascFile.addPos(pos);
602
0
      ascFile.addNote(f.str().c_str());
603
604
0
      MWAW_DEBUG_MSG(("DocMkrText::readFontNames: can not read fontname %d\n", i));
605
0
      return false;
606
0
    }
607
8.34k
    auto sz = static_cast<int>(input->readULong(1));
608
8.34k
    if (pos+1+sz+2 > endPos) {
609
132
      f.str("");
610
132
      f << "###";
611
132
      ascFile.addPos(pos);
612
132
      ascFile.addNote(f.str().c_str());
613
614
132
      MWAW_DEBUG_MSG(("DocMkrText::readFontNames: fontname size %d is bad\n", i));
615
132
      return false;
616
132
    }
617
618
8.21k
    std::string str("");
619
497k
    for (int c=0; c < sz; c++)
620
489k
      str += char(input->readULong(1));
621
8.21k
    f << str << ",";
622
623
8.21k
    auto val=static_cast<int>(input->readULong(1));
624
8.21k
    if (val) f << "unkn=" << val << ",";
625
8.21k
    auto N1=static_cast<int>(input->readULong(1));
626
8.21k
    if (pos+1+sz+2+N1 > endPos) {
627
47
      f.str("");
628
47
      f << "###";
629
47
      ascFile.addPos(pos);
630
47
      ascFile.addNote(f.str().c_str());
631
632
47
      MWAW_DEBUG_MSG(("DocMkrText::readFontNames: fontname size %d is bad\n", i));
633
47
      return false;
634
47
    }
635
8.16k
    f << "fontSz=[";
636
526k
    for (int j = 0; j < N1; j++)
637
518k
      f << input->readULong(1) << ",";
638
8.16k
    f << "],";
639
8.16k
    ascFile.addPos(pos);
640
8.16k
    ascFile.addNote(f.str().c_str());
641
8.16k
  }
642
11
  return true;
643
190
}
644
645
////////////////////////////////////////////////////////////
646
// the styles
647
////////////////////////////////////////////////////////////
648
bool DocMkrText::readStyles(MWAWEntry const &entry)
649
622
{
650
622
  if (!entry.valid() || entry.length()<2) {
651
209
    MWAW_DEBUG_MSG(("DocMkrText::readStyles: the entry is bad\n"));
652
209
    return false;
653
209
  }
654
413
  entry.setParsed(true);
655
413
  long pos = entry.begin();
656
413
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
657
413
  libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
658
413
  input->seek(pos, librevenge::RVNG_SEEK_SET);
659
660
413
  libmwaw::DebugStream f;
661
413
  auto N=static_cast<int>(input->readULong(2));
662
413
  f << "Entries(Style)[" << entry.type() << "-" << entry.id() << "]:N="<<N;
663
413
  if (20*N+2 != entry.length()) {
664
395
    MWAW_DEBUG_MSG(("DocMkrText::readStyles: the number of values seems bad\n"));
665
395
    f << "###";
666
395
    ascFile.addPos(pos-4);
667
395
    ascFile.addNote(f.str().c_str());
668
395
  }
669
413
  ascFile.addPos(pos-4);
670
413
  ascFile.addNote(f.str().c_str());
671
413
  DocMkrTextInternal::Zone &zone = m_state->getZone(entry.id());
672
9.24M
  for (int i = 0; i < N; i++) {
673
9.24M
    MWAWFont font;
674
9.24M
    f.str("");
675
9.24M
    pos = input->tell();
676
9.24M
    long cPos = input->readLong(4);
677
9.24M
    int dim[2];
678
9.24M
    for (int &j : dim)
679
18.4M
      j = static_cast<int>(input->readLong(2));
680
9.24M
    f << "height?=" << dim[0] << ":" << dim[1] << ",";
681
9.24M
    font.setId(static_cast<int>(input->readULong(2)));
682
9.24M
    auto flag=static_cast<int>(input->readULong(1));
683
9.24M
    uint32_t flags = 0;
684
    // bit 1 = plain
685
9.24M
    if (flag&0x1) flags |= MWAWFont::boldBit;
686
9.24M
    if (flag&0x2) flags |= MWAWFont::italicBit;
687
9.24M
    if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
688
9.24M
    if (flag&0x8) flags |= MWAWFont::embossBit;
689
9.24M
    if (flag&0x10) flags |= MWAWFont::shadowBit;
690
9.24M
    if (flag&0xe0) f << "#flags=" << std::hex << (flag&0xe0) << std::dec << ",";
691
9.24M
    flag=static_cast<int>(input->readULong(1));
692
9.24M
    if (flag) f << "#flags1=" << std::hex << flag << std::dec << ",";
693
9.24M
    font.setSize(float(input->readULong(2)));
694
9.24M
    font.setFlags(flags);
695
9.24M
    unsigned char col[3];
696
27.7M
    for (auto &c : col) c = static_cast<unsigned char>(input->readULong(2)>>8);
697
9.24M
    font.setColor(MWAWColor(col[0],col[1],col[2]));
698
9.24M
    font.m_extra=f.str();
699
9.24M
    if (zone.m_posFontMap.find(cPos) != zone.m_posFontMap.end()) {
700
8.53M
      MWAW_DEBUG_MSG(("DocMkrText::readStyles: a style for pos=%lx already exist\n", static_cast<long unsigned int>(cPos)));
701
8.53M
    }
702
709k
    else
703
709k
      zone.m_posFontMap[cPos] = font;
704
9.24M
    f.str("");
705
9.24M
    f << "Style-" << i << ":" << "cPos=" << std::hex << cPos << std::dec << ",";
706
#ifdef DEBUG
707
    f << ",font=[" << font.getDebugString(m_parserState->m_fontConverter) << "]";
708
#endif
709
9.24M
    ascFile.addPos(pos);
710
9.24M
    ascFile.addNote(f.str().c_str());
711
9.24M
  }
712
413
  return true;
713
622
}
714
715
//     Table of Content information
716
////////////////////////////////////////////////////////////
717
bool DocMkrText::sendTOC()
718
312
{
719
312
  MWAWTextListenerPtr listener=m_parserState->m_textListener;
720
312
  if (!listener) {
721
0
    MWAW_DEBUG_MSG(("DocMkrText::sendTOC: can not find the listener\n"));
722
0
    return false;
723
0
  }
724
312
  auto const &toc=m_state->m_toc;
725
312
  if (toc.empty())
726
312
    return true;
727
0
  if (toc.m_cIdList.size() != toc.m_textList.size()) {
728
0
    MWAW_DEBUG_MSG(("DocMkrText::sendTOC: the TOC is bad\n"));
729
0
    return false;
730
0
  }
731
732
0
  MWAWFont cFont(3,12);
733
0
  cFont.setFlags(MWAWFont::boldBit);
734
0
  MWAWFont actFont(3,10);
735
0
  listener->setFont(actFont);
736
0
  double w = m_state->m_pageWidth;
737
0
  MWAWParagraph para;
738
0
  MWAWTabStop tab;
739
0
  tab.m_alignment = MWAWTabStop::RIGHT;
740
0
  tab.m_leaderCharacter='.';
741
0
  tab.m_position = w;
742
0
  para.m_tabs->push_back(tab);
743
0
  listener->setParagraph(para);
744
745
0
  std::stringstream ss;
746
0
  int prevId=-1;
747
0
  for (size_t i = 0; i < toc.m_textList.size(); i++) {
748
0
    int zId=toc.m_cIdList[i];
749
0
    ss.str("");
750
0
    ss << "C" << zId;
751
752
0
    if (zId!=prevId) {
753
0
      prevId=zId;
754
0
      listener->setFont(cFont);
755
756
0
      listener->insertUnicodeString(librevenge::RVNGString(ss.str().c_str()));
757
0
      listener->insertChar(' ');
758
0
      if (m_state->m_idZoneMap.find(127+zId)!=m_state->m_idZoneMap.end())
759
0
        sendString(m_state->m_idZoneMap.find(127+zId)->second.m_name);
760
0
      listener->insertEOL();
761
0
      listener->setFont(actFont);
762
0
    }
763
0
    sendString(toc.m_textList[i]);
764
0
    listener->insertTab();
765
0
    listener->insertUnicodeString(librevenge::RVNGString(ss.str().c_str()));
766
0
    listener->insertEOL();
767
0
  }
768
0
  return true;
769
0
}
770
771
bool DocMkrText::readTOC(MWAWEntry const &entry)
772
0
{
773
0
  if (!entry.valid() || entry.length()<2) {
774
0
    MWAW_DEBUG_MSG(("DocMkrText::readTOC: the entry is bad\n"));
775
0
    return false;
776
0
  }
777
0
  entry.setParsed(true);
778
0
  long pos = entry.begin();
779
0
  long endPos = entry.end();
780
0
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
781
0
  libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
782
0
  input->seek(pos, librevenge::RVNG_SEEK_SET);
783
784
0
  libmwaw::DebugStream f;
785
0
  f << "Entries(TOC)[" << entry.type() << ":" << entry.id() << "]:";
786
0
  auto N=static_cast<int>(input->readULong(2));
787
0
  ascFile.addPos(pos-4);
788
0
  ascFile.addNote(f.str().c_str());
789
790
0
  for (int i = 0; i < N; i++) {
791
0
    f.str("");
792
0
    f << "TOC-" << i << ":";
793
0
    pos = input->tell();
794
0
    if (pos+7 > endPos) {
795
0
      f << "###";
796
0
      ascFile.addPos(pos);
797
0
      ascFile.addNote(f.str().c_str());
798
799
0
      MWAW_DEBUG_MSG(("DocMkrText::readTOC: can not read string %d\n", i));
800
0
      return false;
801
0
    }
802
0
    auto zId = static_cast<int>(input->readLong(2));
803
0
    if (zId) f << "zId=" << zId+127 << ",";
804
0
    int cPos[2];
805
0
    for (int &j : cPos)
806
0
      j = static_cast<int>(input->readULong(2));
807
0
    f << "cPos=" << std::hex << cPos[0] << "<->" << cPos[1] << std::dec << ",";
808
0
    auto sz = static_cast<int>(input->readULong(1));
809
0
    if (pos+7+sz > endPos) {
810
0
      f.str("");
811
0
      f << "###";
812
0
      ascFile.addPos(pos);
813
0
      ascFile.addNote(f.str().c_str());
814
815
0
      MWAW_DEBUG_MSG(("DocMkrText::readTOC: string size %d is bad\n", i));
816
0
      return false;
817
0
    }
818
819
0
    std::string str("");
820
0
    for (int c=0; c < sz; c++)
821
0
      str += char(input->readULong(1));
822
0
    f << str << ",";
823
0
    m_state->m_toc.m_cIdList.push_back(zId);
824
0
    m_state->m_toc.m_textList.push_back(str);
825
0
    ascFile.addPos(pos);
826
0
    ascFile.addNote(f.str().c_str());
827
0
  }
828
0
  return true;
829
0
}
830
831
//     Windows information
832
////////////////////////////////////////////////////////////
833
bool DocMkrText::readWindows(MWAWEntry const &entry)
834
636
{
835
636
  if (!entry.valid() || entry.length()<20) {
836
228
    MWAW_DEBUG_MSG(("DocMkrText::readWindows: the entry seems very short\n"));
837
228
    return false;
838
228
  }
839
840
408
  entry.setParsed(true);
841
408
  long pos = entry.begin();
842
408
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
843
408
  libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
844
408
  input->seek(pos, librevenge::RVNG_SEEK_SET);
845
846
408
  DocMkrTextInternal::Zone &zone = m_state->getZone(entry.id());
847
408
  libmwaw::DebugStream f;
848
408
  f << "Entries(Windows)[" << entry.type() << "-" << entry.id() << "]:";
849
408
  auto val=static_cast<int>(input->readLong(2)); // always 0?
850
408
  if (val) f << "unkn=" << val << ",";
851
408
  int wDim[3];
852
1.22k
  for (auto &w : wDim) w=static_cast<int>(input->readLong(2));
853
408
  f << "windows=[left=" << wDim[0] << ",right=" << wDim[2]
854
408
    << ",bottom=" << wDim[1] << "],";
855
856
1.63k
  for (auto &margin : zone.m_margins) margin=static_cast<int>(input->readLong(2));
857
408
  f << "margins=[" << zone.m_margins[1] << "x" << zone.m_margins[0]
858
408
    << "<->" << zone.m_margins[3] << "x" << zone.m_margins[2] << ",";
859
408
  auto flag = static_cast<int>(input->readULong(1));
860
408
  if (flag==1) {
861
0
    zone.m_useFooter = false;
862
0
    f << "noFooter,";
863
0
  }
864
408
  else if (flag) f << "#footer=" << flag << ",";
865
408
  flag =static_cast<int>(input->readULong(1)); //9|3e|6d|a8|
866
408
  if (flag)
867
408
    f << "fl=" << std::hex << flag << std::dec << ",";
868
408
  val = static_cast<int>(input->readLong(2)); // always 0?
869
408
  switch (val) {
870
17
  case 0:
871
17
    break;
872
0
  case 1:
873
0
    zone.m_justify = MWAWParagraph::JustificationCenter;
874
0
    f << "just=center,";
875
0
    break;
876
4
  case -1:
877
4
    zone.m_justify = MWAWParagraph::JustificationRight;
878
4
    f << "just=right,";
879
4
    break;
880
387
  default:
881
387
    f << "#justify=" << val << ",";
882
387
    break;
883
408
  }
884
885
408
  if (input->tell()!=entry.end())
886
391
    ascFile.addDelimiter(input->tell(),'|');
887
408
  ascFile.addPos(pos-4);
888
408
  ascFile.addNote(f.str().c_str());
889
408
  return true;
890
408
}
891
892
//     Footer
893
////////////////////////////////////////////////////////////
894
bool DocMkrText::sendFooter(int zId)
895
154k
{
896
154k
  MWAWTextListenerPtr listener=m_parserState->m_textListener;
897
154k
  if (!listener) {
898
0
    MWAW_DEBUG_MSG(("DocMkrText::sendFooter: can not find my listener\n"));
899
0
    return false;
900
0
  }
901
154k
  DocMkrTextInternal::Footer const &ft=m_state->m_footer;
902
154k
  if (ft.empty()) {
903
0
    MWAW_DEBUG_MSG(("DocMkrText::sendFooter: oops, the footer is empty\n"));
904
0
    return false;
905
0
  }
906
154k
  if (m_state->m_idZoneMap.find(zId)==m_state->m_idZoneMap.end()) {
907
0
    MWAW_DEBUG_MSG(("DocMkrText::sendFooter: oops, can not find the zone\n"));
908
0
    return false;
909
0
  }
910
154k
  listener->setFont(ft.m_font);
911
912
154k
  auto const &zone=m_state->getZone(zId);
913
154k
  double w = m_state->m_pageWidth-double(zone.m_margins[0]+zone.m_margins[2])/72.;
914
154k
  MWAWParagraph para;
915
154k
  MWAWTabStop tab;
916
154k
  tab.m_alignment = MWAWTabStop::CENTER;
917
154k
  tab.m_position = w/2.0;
918
154k
  para.m_tabs->push_back(tab);
919
154k
  tab.m_alignment = MWAWTabStop::RIGHT;
920
154k
  tab.m_position = w;
921
154k
  para.m_tabs->push_back(tab);
922
154k
  listener->setParagraph(para);
923
924
463k
  for (int st=0; st<2; st++) {
925
309k
    int id=3*st;
926
309k
    if (ft.m_items[id]<=0&&ft.m_items[id+1]<=0&&ft.m_items[id+2]<=0)
927
0
      continue;
928
1.23M
    for (int f = 0; f < 3; f++, id++) {
929
927k
      switch (ft.m_items[id]) {
930
18
      case 3: {
931
18
        MWAWField field(MWAWField::Time);
932
18
        field.m_DTFormat="%H:%M";
933
18
        listener->insertField(field);
934
18
        break;
935
0
      }
936
18
      case 4: {
937
18
        MWAWField field(MWAWField::Date);
938
18
        field.m_DTFormat="%a, %b %d, %Y";
939
18
        listener->insertField(field);
940
18
        break;
941
0
      }
942
36
      case 5:
943
36
        listener->insertUnicodeString(librevenge::RVNGString("Page "));
944
36
        listener->insertField(MWAWField(MWAWField::PageNumber));
945
36
        break;
946
0
      case 6:
947
0
        listener->insertField(MWAWField(MWAWField::Title));
948
0
        break;
949
18
      case 7:
950
18
        sendString(zone.m_name);
951
18
        break;
952
18
      case 8:
953
18
        sendString(ft.m_userInfo);
954
18
        break;
955
927k
      default:
956
927k
        break;
957
927k
      }
958
927k
      if (f!=2)
959
618k
        listener->insertTab();
960
927k
    }
961
309k
    if (st==0)
962
154k
      listener->insertEOL();
963
309k
  }
964
154k
  return true;
965
154k
}
966
967
bool DocMkrText::readFooter(MWAWEntry const &entry)
968
312
{
969
312
  if (!entry.valid() || entry.length()<22) {
970
142
    MWAW_DEBUG_MSG(("DocMkrText::readFooter: the entry seems bad\n"));
971
142
    return false;
972
142
  }
973
170
  entry.setParsed(true);
974
170
  long pos = entry.begin();
975
170
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
976
170
  libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
977
170
  input->seek(pos, librevenge::RVNG_SEEK_SET);
978
979
170
  libmwaw::DebugStream f;
980
170
  DocMkrTextInternal::Footer &footer=m_state->m_footer;
981
1.02k
  for (auto &item : footer.m_items) item=static_cast<int>(input->readLong(2));
982
1.19k
  for (int i = 0; i < 6; i++) {
983
1.02k
    auto val =static_cast<int>(input->readLong(1));
984
1.02k
    if (!val) continue;
985
960
    if (val!=1) {
986
950
      f << "#fl" << i << "=" << val << ",";
987
950
      continue;
988
950
    }
989
10
    switch (i) {
990
0
    case 0:
991
0
      footer.m_chapterResetPage = true;
992
0
      break;
993
10
    case 2:
994
10
      f << "hasSep,";
995
10
      break;
996
0
    case 4:
997
0
      f << "graySep,";
998
0
      break;
999
0
    default:
1000
0
      f << "#fl" << i << "=" << 1 << ",";
1001
0
      break;
1002
10
    }
1003
10
  }
1004
170
  footer.m_font.setId(static_cast<int>(input->readULong(2)));
1005
170
  footer.m_font.setSize(float(input->readULong(2)));
1006
1007
170
  footer.m_extra=f.str();
1008
1009
170
  f.str("");
1010
170
  f << "Entries(Footer)[" << entry.type() << "-" << entry.id() << "]:"<<footer;
1011
#ifdef DEBUG
1012
  f << ",font=[" << footer.m_font.getDebugString(m_parserState->m_fontConverter) << "]";
1013
#endif
1014
1015
170
  if (input->tell()!=entry.end())
1016
160
    ascFile.addDelimiter(input->tell(),'|');
1017
170
  ascFile.addPos(pos-4);
1018
170
  ascFile.addNote(f.str().c_str());
1019
170
  return true;
1020
170
}
1021
1022
////////////////////////////////////////////////////////////
1023
//
1024
// Low level
1025
//
1026
////////////////////////////////////////////////////////////
1027
1028
void DocMkrText::sendString(std::string const &str) const
1029
36
{
1030
36
  MWAWTextListenerPtr listener=m_parserState->m_textListener;
1031
36
  if (!listener) return;
1032
1033
36
  for (char s : str)
1034
414
    listener->insertCharacter(static_cast<unsigned char>(s));
1035
36
}
1036
1037
bool DocMkrText::sendMainText()
1038
312
{
1039
312
  if (!m_parserState->m_textListener) return true;
1040
1041
650
  for (auto const &it : m_state->m_idZoneMap) {
1042
650
    DocMkrTextInternal::Zone const &zone= it.second;
1043
650
    if (zone.m_parsed)
1044
0
      continue;
1045
650
    if (sendText(zone))
1046
255
      m_mainParser->newPage(++m_state->m_actualPage);
1047
650
  }
1048
312
  return true;
1049
312
}
1050
1051
1052
void DocMkrText::flushExtra()
1053
0
{
1054
0
  return;
1055
0
}
1056
1057
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: