Coverage Report

Created: 2026-09-14 06:37

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.21k
    : m_cIdList()
65
1.21k
    , m_textList()
66
1.21k
  {
67
1.21k
  }
68
  //! returns true if the table is empty
69
  bool empty() const
70
557
  {
71
557
    return m_textList.empty();
72
557
  }
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.21k
    : m_font(3,9)
85
1.21k
    , m_chapterResetPage(false)
86
1.21k
    , m_userInfo()
87
1.21k
    , m_extra("")
88
1.21k
  {
89
7.29k
    for (auto &item : m_items) item=0;
90
1.21k
  }
91
  //! returns true if the footer is empty
92
  bool empty() const
93
168k
  {
94
168k
    for (auto item : m_items)
95
168k
      if (item)
96
168k
        return false;
97
149
    return true;
98
168k
  }
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.39k
    : m_pos()
137
1.39k
    , m_justify(MWAWParagraph::JustificationLeft)
138
1.39k
    , m_backgroundColor(MWAWColor::white())
139
1.39k
    , m_useFooter(true)
140
1.39k
    , m_name("")
141
1.39k
    , m_posFontMap()
142
1.39k
    , m_numPages(0)
143
1.39k
    , m_parsed(false)
144
1.39k
  {
145
5.56k
    for (auto &margin : m_margins) margin=54;
146
1.39k
  }
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.21k
    : m_version(-1)
172
1.21k
    , m_numPages(-1)
173
1.21k
    , m_actualPage(0)
174
1.21k
    , m_pageWidth(8.5)
175
1.21k
    , m_idZoneMap()
176
1.21k
    , m_footer()
177
1.21k
    , m_toc()
178
1.21k
  {
179
1.21k
  }
180
  //! returns the zone corresponding to an id
181
  Zone &getZone(int id)
182
169k
  {
183
169k
    if (m_idZoneMap.find(id)==m_idZoneMap.end())
184
696
      m_idZoneMap[id]=Zone();
185
169k
    return m_idZoneMap.find(id)->second;
186
169k
  }
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
213
    : MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
210
213
    , m_textParser(&pars)
211
213
    , m_id(id)
212
213
    , m_text("")
213
213
    , 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
213
  ~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
167k
{
247
167k
  if (!listener.get()) {
248
0
    MWAW_DEBUG_MSG(("SubDocument::parse: no listener\n"));
249
0
    return;
250
0
  }
251
167k
  if (!m_textParser) {
252
0
    MWAW_DEBUG_MSG(("SubDocument::parse: no text parser\n"));
253
0
    return;
254
0
  }
255
256
167k
  long pos = m_input->tell();
257
167k
  if (m_type == libmwaw::DOC_HEADER_FOOTER)
258
167k
    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
167k
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
267
167k
}
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.21k
  : m_parserState(parser.getParserState())
287
1.21k
  , m_state(new DocMkrTextInternal::State)
288
1.21k
  , m_mainParser(&parser)
289
1.21k
{
290
1.21k
}
291
292
DocMkrText::~DocMkrText()
293
1.21k
{
294
1.21k
}
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
335
{
305
335
  if (m_state->m_numPages >= 0)
306
0
    return m_state->m_numPages;
307
335
  m_state->m_actualPage = 1;
308
309
335
  int nPages = 0;
310
  // do not replace by auto it : m_state->m_idZoneMap as it->second.m_numPages is modified
311
696
  for (auto const &it : m_state->m_idZoneMap) {
312
696
    auto const &zone= it.second;
313
696
    computeNumPages(zone);
314
696
    nPages += zone.m_numPages;
315
696
  }
316
335
  m_state->m_numPages = nPages;
317
335
  return m_state->m_numPages;
318
335
}
319
320
int DocMkrText::numChapters() const
321
335
{
322
335
  return int(m_state->m_idZoneMap.size());
323
335
}
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
696
{
342
696
  if (zone.m_numPages || !zone.m_pos.valid())
343
423
    return;
344
273
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
345
273
  input->seek(zone.m_pos.begin(), librevenge::RVNG_SEEK_SET);
346
273
  long len = zone.m_pos.length();
347
273
  int nPages=1;
348
1.43M
  for (long i=0; i < len; i++) {
349
1.43M
    if (input->isEnd())
350
0
      break;
351
1.43M
    if (input->readLong(1)==0)
352
222k
      nPages++;
353
1.43M
  }
354
273
  zone.m_numPages = nPages;
355
273
}
356
357
void DocMkrText::updatePageSpanList(std::vector<MWAWPageSpan> &spanList)
358
335
{
359
335
  numPages();
360
335
  spanList.resize(0);
361
335
  MWAWPageSpan ps;
362
335
  ps.setMarginTop(0.1);
363
335
  ps.setMarginBottom(0.015);
364
335
  ps.setMarginLeft(0.1);
365
335
  ps.setMarginRight(0.1);
366
335
  bool hasFooter = !m_state->m_footer.empty();
367
335
  bool needResetPage = m_state->m_footer.m_chapterResetPage;
368
335
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
369
696
  for (auto it : m_state->m_idZoneMap) {
370
696
    int zId = it.first;
371
696
    DocMkrTextInternal::Zone const &zone= it.second;
372
696
    if (zone.m_numPages <= 0)
373
423
      continue;
374
273
    MWAWPageSpan span(ps);
375
273
    if (needResetPage)
376
0
      span.setPageNumber(1);
377
273
    if (zone.m_margins[0]>=0)
378
273
      span.setMarginLeft(double(zone.m_margins[0])/72.);
379
273
    if (zone.m_margins[1]>=0)
380
273
      span.setMarginTop(double(zone.m_margins[1])/72.);
381
273
    if (zone.m_margins[2]>=0)
382
273
      span.setMarginRight(double(zone.m_margins[2])/72.);
383
273
    if (zone.m_margins[3]>=0)
384
165
      span.setMarginBottom(double(zone.m_margins[3])/72.);
385
273
    span.setBackgroundColor(zone.m_backgroundColor);
386
273
    if (hasFooter && zone.m_useFooter) {
387
213
      MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
388
213
      footer.m_subDocument.reset
389
213
      (new DocMkrTextInternal::SubDocument(*this, input, zId, libmwaw::DOC_HEADER_FOOTER));
390
213
      span.setHeaderFooter(footer);
391
213
    }
392
222k
    for (int i = 0; i < zone.m_numPages; i++) {
393
222k
      spanList.push_back(span);
394
222k
      span.setPageNumber(-1);
395
222k
    }
396
273
  }
397
335
  if (spanList.size()==0 || !m_state->m_toc.empty())
398
113
    spanList.push_back(ps);
399
335
}
400
401
////////////////////////////////////////////////////////////
402
// Intermediate level
403
////////////////////////////////////////////////////////////
404
405
// find the different zones
406
bool DocMkrText::createZones()
407
482
{
408
482
  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
482
  MWAWRSRCParserPtr rsrcParser = m_mainParser->getRSRCParser();
413
482
  auto const &entryMap = rsrcParser->getEntriesMap();
414
415
  // entry 128: font name and size
416
482
  auto it = entryMap.lower_bound("rQDF");
417
818
  while (it != entryMap.end()) {
418
711
    if (it->first != "rQDF")
419
375
      break;
420
336
    MWAWEntry const &entry = it++->second;
421
336
    readFontNames(entry);
422
336
  }
423
  // Footer: foot:128
424
482
  it = entryMap.lower_bound("foot");
425
819
  while (it != entryMap.end()) {
426
714
    if (it->first != "foot")
427
377
      break;
428
337
    MWAWEntry const &entry = it++->second;
429
337
    readFooter(entry);
430
337
  }
431
  // entry 128: ?
432
482
  it = entryMap.lower_bound("cnt#");
433
482
  while (it != entryMap.end()) {
434
377
    if (it->first != "cnt#")
435
377
      break;
436
0
    MWAWEntry const &entry = it++->second;
437
0
    readTOC(entry);
438
0
  }
439
440
  // the chapter zone zone
441
482
  it = entryMap.lower_bound("styl");
442
1.15k
  while (it != entryMap.end()) {
443
1.04k
    if (it->first != "styl")
444
375
      break;
445
670
    MWAWEntry const &entry = it++->second;
446
670
    readStyles(entry);
447
670
  }
448
482
  it = entryMap.lower_bound("TEXT");
449
1.15k
  while (it != entryMap.end()) {
450
1.04k
    if (it->first != "TEXT")
451
377
      break;
452
670
    MWAWEntry const &entry = it++->second;
453
670
    m_state->getZone(entry.id()).m_pos = entry;
454
670
  }
455
456
482
  it = entryMap.lower_bound("Wndo");
457
1.16k
  while (it != entryMap.end()) {
458
1.05k
    if (it->first != "Wndo")
459
377
      break;
460
682
    MWAWEntry const &entry = it++->second;
461
682
    readWindows(entry);
462
682
  }
463
  // font color
464
482
  it = entryMap.lower_bound("clut");
465
1.00k
  while (it != entryMap.end()) {
466
896
    if (it->first != "clut")
467
377
      break;
468
519
    MWAWEntry const &entry = it++->second;
469
519
    std::vector<MWAWColor> cmap;
470
519
    rsrcParser->parseClut(entry, cmap);
471
519
    if (entry.id() != 128)
472
407
      continue;
473
134
    for (size_t i = 0; i < cmap.size(); i++) {
474
22
      if (m_state->m_idZoneMap.find(int(i)+128)==m_state->m_idZoneMap.end())
475
0
        continue;
476
22
      m_state->m_idZoneMap.find(int(i)+128)->second.m_backgroundColor=cmap[i];
477
22
    }
478
112
  }
479
480
482
  it = entryMap.lower_bound("STR ");
481
1.49k
  while (it != entryMap.end()) {
482
1.39k
    if (it->first != "STR ")
483
377
      break;
484
1.01k
    MWAWEntry const &entry = it++->second;
485
    // 1000: footer (user information)
486
1.01k
    if (entry.id()==1000 && entry.length()>0) {
487
11
      std::string userInfo("");
488
11
      rsrcParser->parseSTR(entry,userInfo);
489
11
      m_state->m_footer.m_userInfo = userInfo;
490
11
    }
491
    // 200?: chapter name
492
1.00k
    else if (entry.id()>2000 &&
493
398
             m_state->m_idZoneMap.find(entry.id()-2001+128)!=m_state->m_idZoneMap.end()) {
494
62
      std::string name("");
495
62
      rsrcParser->parseSTR(entry,name);
496
62
      m_state->getZone(entry.id()-2001+128).m_name=name;
497
62
    }
498
1.01k
  }
499
482
  return m_state->m_idZoneMap.size();
500
482
}
501
502
////////////////////////////////////////////////////////////
503
//    Text
504
////////////////////////////////////////////////////////////
505
bool DocMkrText::sendText(DocMkrTextInternal::Zone const &zone)
506
696
{
507
696
  MWAWTextListenerPtr listener=m_parserState->m_textListener;
508
696
  if (!listener) {
509
0
    MWAW_DEBUG_MSG(("DocMkrText::sendText: can not find the listener\n"));
510
0
    return false;
511
0
  }
512
696
  if (!zone.m_pos.valid()) {
513
423
    MWAW_DEBUG_MSG(("DocMkrText::sendText: the entry is bad\n"));
514
423
    return false;
515
423
  }
516
273
  zone.m_parsed = true;
517
273
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
518
273
  libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
519
273
  long pos = zone.m_pos.begin(), debPos=pos-4;
520
273
  input->seek(pos, librevenge::RVNG_SEEK_SET);
521
273
  libmwaw::DebugStream f;
522
273
  f << "Entries(TEXT)[" << zone.m_pos.id() << "]:";
523
273
  listener->setFont(MWAWFont(3,12));
524
273
  MWAWParagraph para;
525
273
  para.m_justify=zone.m_justify;
526
273
  listener->setParagraph(para);
527
273
  std::map<long, MWAWFont >::const_iterator fontIt;
528
273
  int nPict=0, zId=zone.m_pos.id()-128;
529
273
  double w = m_state->m_pageWidth-double(zone.m_margins[0]+zone.m_margins[2])/72.;
530
1.43M
  for (long i = 0; i <= zone.m_pos.length(); i++) {
531
1.43M
    bool endPos = (i==zone.m_pos.length());
532
1.43M
    unsigned char c=endPos ? static_cast<unsigned char>(0) : static_cast<unsigned char>(input->readULong(1));
533
1.43M
    if (endPos || c==0xd || c==0) {
534
222k
      ascFile.addPos(debPos);
535
222k
      ascFile.addNote(f.str().c_str());
536
222k
      debPos = input->tell();
537
222k
      if (endPos) break;
538
222k
      f.str("");
539
222k
      f << "TEXT:";
540
222k
    }
541
1.43M
    fontIt=zone.m_posFontMap.find(i);
542
1.43M
    if (fontIt != zone.m_posFontMap.end())
543
9.11k
      listener->setFont(fontIt->second);
544
1.43M
    if (c)
545
1.21M
      f << c;
546
1.43M
    switch (c) {
547
222k
    case 0:
548
222k
      m_mainParser->newPage(++m_state->m_actualPage);
549
222k
      break;
550
1.26k
    case 0x9:
551
1.26k
      listener->insertTab();
552
1.26k
      break;
553
430
    case 0xd:
554
430
      listener->insertEOL();
555
430
      break;
556
5.28k
    case 0x11: // command key
557
5.28k
      listener->insertUnicode(0x2318);
558
5.28k
      break;
559
9.05k
    case 0x14: // apple logo: check me
560
9.05k
      listener->insertUnicode(0xf8ff);
561
9.05k
      break;
562
14
    case 0xca:
563
14
      m_mainParser->sendPicture(zId, ++nPict, w);
564
14
      break;
565
1.19M
    default:
566
1.19M
      i += listener->insertCharacter(c, input, zone.m_pos.end());
567
1.19M
      break;
568
1.43M
    }
569
1.43M
  }
570
273
  return true;
571
273
}
572
573
////////////////////////////////////////////////////////////
574
//     Fonts
575
////////////////////////////////////////////////////////////
576
bool DocMkrText::readFontNames(MWAWEntry const &entry)
577
336
{
578
336
  if (!entry.valid() || entry.length()<2) {
579
136
    MWAW_DEBUG_MSG(("DocMkrText::readFontNames: the entry is bad\n"));
580
136
    return false;
581
136
  }
582
200
  entry.setParsed(true);
583
200
  long pos = entry.begin();
584
200
  long endPos = entry.end();
585
200
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
586
200
  libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
587
200
  input->seek(pos, librevenge::RVNG_SEEK_SET);
588
589
200
  libmwaw::DebugStream f;
590
200
  auto N=static_cast<int>(input->readULong(2));
591
200
  f << "Entries(FontName)[" << entry.type() << "-" << entry.id() << "]:N="<<N;
592
200
  ascFile.addPos(pos-4);
593
200
  ascFile.addNote(f.str().c_str());
594
595
8.70k
  for (int i = 0; i < N; i++) {
596
8.69k
    f.str("");
597
8.69k
    f << "FontName-" << i << ":";
598
8.69k
    pos = input->tell();
599
8.69k
    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.69k
    auto sz = static_cast<int>(input->readULong(1));
608
8.69k
    if (pos+1+sz+2 > endPos) {
609
138
      f.str("");
610
138
      f << "###";
611
138
      ascFile.addPos(pos);
612
138
      ascFile.addNote(f.str().c_str());
613
614
138
      MWAW_DEBUG_MSG(("DocMkrText::readFontNames: fontname size %d is bad\n", i));
615
138
      return false;
616
138
    }
617
618
8.55k
    std::string str("");
619
521k
    for (int c=0; c < sz; c++)
620
512k
      str += char(input->readULong(1));
621
8.55k
    f << str << ",";
622
623
8.55k
    auto val=static_cast<int>(input->readULong(1));
624
8.55k
    if (val) f << "unkn=" << val << ",";
625
8.55k
    auto N1=static_cast<int>(input->readULong(1));
626
8.55k
    if (pos+1+sz+2+N1 > endPos) {
627
49
      f.str("");
628
49
      f << "###";
629
49
      ascFile.addPos(pos);
630
49
      ascFile.addNote(f.str().c_str());
631
632
49
      MWAW_DEBUG_MSG(("DocMkrText::readFontNames: fontname size %d is bad\n", i));
633
49
      return false;
634
49
    }
635
8.50k
    f << "fontSz=[";
636
551k
    for (int j = 0; j < N1; j++)
637
543k
      f << input->readULong(1) << ",";
638
8.50k
    f << "],";
639
8.50k
    ascFile.addPos(pos);
640
8.50k
    ascFile.addNote(f.str().c_str());
641
8.50k
  }
642
13
  return true;
643
200
}
644
645
////////////////////////////////////////////////////////////
646
// the styles
647
////////////////////////////////////////////////////////////
648
bool DocMkrText::readStyles(MWAWEntry const &entry)
649
670
{
650
670
  if (!entry.valid() || entry.length()<2) {
651
233
    MWAW_DEBUG_MSG(("DocMkrText::readStyles: the entry is bad\n"));
652
233
    return false;
653
233
  }
654
437
  entry.setParsed(true);
655
437
  long pos = entry.begin();
656
437
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
657
437
  libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
658
437
  input->seek(pos, librevenge::RVNG_SEEK_SET);
659
660
437
  libmwaw::DebugStream f;
661
437
  auto N=static_cast<int>(input->readULong(2));
662
437
  f << "Entries(Style)[" << entry.type() << "-" << entry.id() << "]:N="<<N;
663
437
  if (20*N+2 != entry.length()) {
664
417
    MWAW_DEBUG_MSG(("DocMkrText::readStyles: the number of values seems bad\n"));
665
417
    f << "###";
666
417
    ascFile.addPos(pos-4);
667
417
    ascFile.addNote(f.str().c_str());
668
417
  }
669
437
  ascFile.addPos(pos-4);
670
437
  ascFile.addNote(f.str().c_str());
671
437
  DocMkrTextInternal::Zone &zone = m_state->getZone(entry.id());
672
9.63M
  for (int i = 0; i < N; i++) {
673
9.63M
    MWAWFont font;
674
9.63M
    f.str("");
675
9.63M
    pos = input->tell();
676
9.63M
    long cPos = input->readLong(4);
677
9.63M
    int dim[2];
678
9.63M
    for (int &j : dim)
679
19.2M
      j = static_cast<int>(input->readLong(2));
680
9.63M
    f << "height?=" << dim[0] << ":" << dim[1] << ",";
681
9.63M
    font.setId(static_cast<int>(input->readULong(2)));
682
9.63M
    auto flag=static_cast<int>(input->readULong(1));
683
9.63M
    uint32_t flags = 0;
684
    // bit 1 = plain
685
9.63M
    if (flag&0x1) flags |= MWAWFont::boldBit;
686
9.63M
    if (flag&0x2) flags |= MWAWFont::italicBit;
687
9.63M
    if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
688
9.63M
    if (flag&0x8) flags |= MWAWFont::embossBit;
689
9.63M
    if (flag&0x10) flags |= MWAWFont::shadowBit;
690
9.63M
    if (flag&0xe0) f << "#flags=" << std::hex << (flag&0xe0) << std::dec << ",";
691
9.63M
    flag=static_cast<int>(input->readULong(1));
692
9.63M
    if (flag) f << "#flags1=" << std::hex << flag << std::dec << ",";
693
9.63M
    font.setSize(float(input->readULong(2)));
694
9.63M
    font.setFlags(flags);
695
9.63M
    unsigned char col[3];
696
28.9M
    for (auto &c : col) c = static_cast<unsigned char>(input->readULong(2)>>8);
697
9.63M
    font.setColor(MWAWColor(col[0],col[1],col[2]));
698
9.63M
    font.m_extra=f.str();
699
9.63M
    if (zone.m_posFontMap.find(cPos) != zone.m_posFontMap.end()) {
700
8.90M
      MWAW_DEBUG_MSG(("DocMkrText::readStyles: a style for pos=%lx already exist\n", static_cast<long unsigned int>(cPos)));
701
8.90M
    }
702
732k
    else
703
732k
      zone.m_posFontMap[cPos] = font;
704
9.63M
    f.str("");
705
9.63M
    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.63M
    ascFile.addPos(pos);
710
9.63M
    ascFile.addNote(f.str().c_str());
711
9.63M
  }
712
437
  return true;
713
670
}
714
715
//     Table of Content information
716
////////////////////////////////////////////////////////////
717
bool DocMkrText::sendTOC()
718
335
{
719
335
  MWAWTextListenerPtr listener=m_parserState->m_textListener;
720
335
  if (!listener) {
721
0
    MWAW_DEBUG_MSG(("DocMkrText::sendTOC: can not find the listener\n"));
722
0
    return false;
723
0
  }
724
335
  auto const &toc=m_state->m_toc;
725
335
  if (toc.empty())
726
335
    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
682
{
835
682
  if (!entry.valid() || entry.length()<20) {
836
255
    MWAW_DEBUG_MSG(("DocMkrText::readWindows: the entry seems very short\n"));
837
255
    return false;
838
255
  }
839
840
427
  entry.setParsed(true);
841
427
  long pos = entry.begin();
842
427
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
843
427
  libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
844
427
  input->seek(pos, librevenge::RVNG_SEEK_SET);
845
846
427
  DocMkrTextInternal::Zone &zone = m_state->getZone(entry.id());
847
427
  libmwaw::DebugStream f;
848
427
  f << "Entries(Windows)[" << entry.type() << "-" << entry.id() << "]:";
849
427
  auto val=static_cast<int>(input->readLong(2)); // always 0?
850
427
  if (val) f << "unkn=" << val << ",";
851
427
  int wDim[3];
852
1.28k
  for (auto &w : wDim) w=static_cast<int>(input->readLong(2));
853
427
  f << "windows=[left=" << wDim[0] << ",right=" << wDim[2]
854
427
    << ",bottom=" << wDim[1] << "],";
855
856
1.70k
  for (auto &margin : zone.m_margins) margin=static_cast<int>(input->readLong(2));
857
427
  f << "margins=[" << zone.m_margins[1] << "x" << zone.m_margins[0]
858
427
    << "<->" << zone.m_margins[3] << "x" << zone.m_margins[2] << ",";
859
427
  auto flag = static_cast<int>(input->readULong(1));
860
427
  if (flag==1) {
861
0
    zone.m_useFooter = false;
862
0
    f << "noFooter,";
863
0
  }
864
427
  else if (flag) f << "#footer=" << flag << ",";
865
427
  flag =static_cast<int>(input->readULong(1)); //9|3e|6d|a8|
866
427
  if (flag)
867
427
    f << "fl=" << std::hex << flag << std::dec << ",";
868
427
  val = static_cast<int>(input->readLong(2)); // always 0?
869
427
  switch (val) {
870
19
  case 0:
871
19
    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
404
  default:
881
404
    f << "#justify=" << val << ",";
882
404
    break;
883
427
  }
884
885
427
  if (input->tell()!=entry.end())
886
408
    ascFile.addDelimiter(input->tell(),'|');
887
427
  ascFile.addPos(pos-4);
888
427
  ascFile.addNote(f.str().c_str());
889
427
  return true;
890
427
}
891
892
//     Footer
893
////////////////////////////////////////////////////////////
894
bool DocMkrText::sendFooter(int zId)
895
167k
{
896
167k
  MWAWTextListenerPtr listener=m_parserState->m_textListener;
897
167k
  if (!listener) {
898
0
    MWAW_DEBUG_MSG(("DocMkrText::sendFooter: can not find my listener\n"));
899
0
    return false;
900
0
  }
901
167k
  DocMkrTextInternal::Footer const &ft=m_state->m_footer;
902
167k
  if (ft.empty()) {
903
0
    MWAW_DEBUG_MSG(("DocMkrText::sendFooter: oops, the footer is empty\n"));
904
0
    return false;
905
0
  }
906
167k
  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
167k
  listener->setFont(ft.m_font);
911
912
167k
  auto const &zone=m_state->getZone(zId);
913
167k
  double w = m_state->m_pageWidth-double(zone.m_margins[0]+zone.m_margins[2])/72.;
914
167k
  MWAWParagraph para;
915
167k
  MWAWTabStop tab;
916
167k
  tab.m_alignment = MWAWTabStop::CENTER;
917
167k
  tab.m_position = w/2.0;
918
167k
  para.m_tabs->push_back(tab);
919
167k
  tab.m_alignment = MWAWTabStop::RIGHT;
920
167k
  tab.m_position = w;
921
167k
  para.m_tabs->push_back(tab);
922
167k
  listener->setParagraph(para);
923
924
503k
  for (int st=0; st<2; st++) {
925
335k
    int id=3*st;
926
335k
    if (ft.m_items[id]<=0&&ft.m_items[id+1]<=0&&ft.m_items[id+2]<=0)
927
0
      continue;
928
1.34M
    for (int f = 0; f < 3; f++, id++) {
929
1.00M
      switch (ft.m_items[id]) {
930
20
      case 3: {
931
20
        MWAWField field(MWAWField::Time);
932
20
        field.m_DTFormat="%H:%M";
933
20
        listener->insertField(field);
934
20
        break;
935
0
      }
936
20
      case 4: {
937
20
        MWAWField field(MWAWField::Date);
938
20
        field.m_DTFormat="%a, %b %d, %Y";
939
20
        listener->insertField(field);
940
20
        break;
941
0
      }
942
40
      case 5:
943
40
        listener->insertUnicodeString(librevenge::RVNGString("Page "));
944
40
        listener->insertField(MWAWField(MWAWField::PageNumber));
945
40
        break;
946
0
      case 6:
947
0
        listener->insertField(MWAWField(MWAWField::Title));
948
0
        break;
949
20
      case 7:
950
20
        sendString(zone.m_name);
951
20
        break;
952
20
      case 8:
953
20
        sendString(ft.m_userInfo);
954
20
        break;
955
1.00M
      default:
956
1.00M
        break;
957
1.00M
      }
958
1.00M
      if (f!=2)
959
671k
        listener->insertTab();
960
1.00M
    }
961
335k
    if (st==0)
962
167k
      listener->insertEOL();
963
335k
  }
964
167k
  return true;
965
167k
}
966
967
bool DocMkrText::readFooter(MWAWEntry const &entry)
968
337
{
969
337
  if (!entry.valid() || entry.length()<22) {
970
151
    MWAW_DEBUG_MSG(("DocMkrText::readFooter: the entry seems bad\n"));
971
151
    return false;
972
151
  }
973
186
  entry.setParsed(true);
974
186
  long pos = entry.begin();
975
186
  MWAWInputStreamPtr input = m_mainParser->rsrcInput();
976
186
  libmwaw::DebugFile &ascFile = m_mainParser->rsrcAscii();
977
186
  input->seek(pos, librevenge::RVNG_SEEK_SET);
978
979
186
  libmwaw::DebugStream f;
980
186
  DocMkrTextInternal::Footer &footer=m_state->m_footer;
981
1.11k
  for (auto &item : footer.m_items) item=static_cast<int>(input->readLong(2));
982
1.30k
  for (int i = 0; i < 6; i++) {
983
1.11k
    auto val =static_cast<int>(input->readLong(1));
984
1.11k
    if (!val) continue;
985
1.04k
    if (val!=1) {
986
1.03k
      f << "#fl" << i << "=" << val << ",";
987
1.03k
      continue;
988
1.03k
    }
989
11
    switch (i) {
990
0
    case 0:
991
0
      footer.m_chapterResetPage = true;
992
0
      break;
993
11
    case 2:
994
11
      f << "hasSep,";
995
11
      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
11
    }
1003
11
  }
1004
186
  footer.m_font.setId(static_cast<int>(input->readULong(2)));
1005
186
  footer.m_font.setSize(float(input->readULong(2)));
1006
1007
186
  footer.m_extra=f.str();
1008
1009
186
  f.str("");
1010
186
  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
186
  if (input->tell()!=entry.end())
1016
175
    ascFile.addDelimiter(input->tell(),'|');
1017
186
  ascFile.addPos(pos-4);
1018
186
  ascFile.addNote(f.str().c_str());
1019
186
  return true;
1020
186
}
1021
1022
////////////////////////////////////////////////////////////
1023
//
1024
// Low level
1025
//
1026
////////////////////////////////////////////////////////////
1027
1028
void DocMkrText::sendString(std::string const &str) const
1029
40
{
1030
40
  MWAWTextListenerPtr listener=m_parserState->m_textListener;
1031
40
  if (!listener) return;
1032
1033
40
  for (char s : str)
1034
460
    listener->insertCharacter(static_cast<unsigned char>(s));
1035
40
}
1036
1037
bool DocMkrText::sendMainText()
1038
335
{
1039
335
  if (!m_parserState->m_textListener) return true;
1040
1041
696
  for (auto const &it : m_state->m_idZoneMap) {
1042
696
    DocMkrTextInternal::Zone const &zone= it.second;
1043
696
    if (zone.m_parsed)
1044
0
      continue;
1045
696
    if (sendText(zone))
1046
273
      m_mainParser->newPage(++m_state->m_actualPage);
1047
696
  }
1048
335
  return true;
1049
335
}
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: