Coverage Report

Created: 2026-07-20 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/StudentWritingCParser.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 <array>
35
#include <cstring>
36
#include <iomanip>
37
#include <iostream>
38
#include <limits>
39
#include <set>
40
#include <sstream>
41
#include <stack>
42
43
#include <librevenge/librevenge.h>
44
45
#include "MWAWCell.hxx"
46
#include "MWAWTextListener.hxx"
47
#include "MWAWFont.hxx"
48
#include "MWAWFontConverter.hxx"
49
#include "MWAWHeader.hxx"
50
#include "MWAWParagraph.hxx"
51
#include "MWAWPosition.hxx"
52
#include "MWAWPictMac.hxx"
53
#include "MWAWPrinter.hxx"
54
#include "MWAWSection.hxx"
55
#include "MWAWStringStream.hxx"
56
#include "MWAWSubDocument.hxx"
57
#include "MWAWTable.hxx"
58
59
#include "StudentWritingCParser.hxx"
60
61
/** Internal: the structures of a StudentWritingCParser */
62
namespace StudentWritingCParserInternal
63
{
64
typedef std::pair<int, int> ZoneEntry;
65
//! an structure of StudentWritingCParserInternal to store the position of a frame and its entries
66
struct FrameStruct {
67
  //! constructor
68
  explicit FrameStruct(int type)
69
0
    : m_type(type)
70
0
    , m_page(0)
71
0
    , m_id()
72
0
  {
73
0
  }
74
  //! the frame type
75
  int m_type;
76
  //! the page
77
  int m_page;
78
  //! the zone id
79
  ZoneEntry m_id;
80
  //! the bounding boxes
81
  MWAWBox2f m_boxes[2];
82
};
83
84
//! an structure of StudentWritingCParserInternal to store the page's data
85
struct PageStruct {
86
  //! constructor
87
  PageStruct()
88
0
    : m_pageNumber(0)
89
0
    , m_firstChar(0)
90
0
    , m_numColumns(0)
91
0
  {
92
0
  }
93
  //! the page number
94
  int m_pageNumber;
95
  //! the first character
96
  int m_firstChar;
97
  //! the number of columns
98
  int m_numColumns;
99
  //! the header/footer id
100
  ZoneEntry m_hfIds[2];
101
};
102
103
//! an structure of StudentWritingCParserInternal to store the position of a picture and its entries
104
struct PictureStruct {
105
  //! constructor
106
  PictureStruct()
107
0
    : m_box()
108
0
  {
109
0
    for (auto &id : m_ids) id=0;
110
0
  }
111
  //! the bounding box
112
  MWAWBox2f m_box;
113
  //! the zone id and the zone sub ids
114
  int m_ids[2];
115
};
116
117
//! an structure of StudentWritingCParserInternal to store the position of a zone and its entries
118
struct ZoneStruct {
119
  //! constructor
120
  ZoneStruct()
121
0
    : m_flags(0)
122
0
    , m_subZones2(1)
123
0
  {
124
0
    for (auto &m : m_margins) m=0;
125
0
  }
126
  //! the zone ids
127
  ZoneEntry m_ids[9];
128
  //! the zone flag
129
  int m_flags;
130
  //! the number of subzone in zone2
131
  int m_subZones2;
132
  //! the margins: LTRB
133
  float m_margins[4];
134
};
135
136
//! a list of entry of StudentWritingCParser defining a zone
137
struct Zone {
138
  //! constructor
139
  Zone(int type, int id)
140
0
    : m_type(type)
141
0
    , m_id(id)
142
0
    , m_idToEntryMap()
143
144
0
    , m_idToParagraphMap()
145
146
0
    , m_idToDataMap()
147
0
    , m_idToPageMap()
148
0
    , m_frames()
149
0
    , m_frameDates()
150
0
    , m_idToFrameNoteMap()
151
0
    , m_idToFrameBiblioMap()
152
0
    , m_idToPictureMap()
153
0
    , m_idToObjectMap()
154
0
  {
155
0
  }
156
157
  //! insert a new entry
158
  bool insert(int id, MWAWEntry const &entry)
159
0
  {
160
0
    if (m_idToEntryMap.find(id)!=m_idToEntryMap.end()) {
161
0
      MWAW_DEBUG_MSG(("StudentWritingCParserInternal::Zone::insert: entry %d already exists\n", id));
162
0
      return false;
163
0
    }
164
0
    m_idToEntryMap[id]=entry;
165
0
    return true;
166
0
  }
167
  //! the zone type
168
  int m_type;
169
  //! the zone id
170
  int m_id;
171
  //! the id to sub zone map
172
  std::map<int, MWAWEntry> m_idToEntryMap;
173
174
  // specific data
175
176
  //! a map id to paragraph
177
  std::map<int, MWAWParagraph> m_idToParagraphMap;
178
179
  //! a map id to main zone data, ...
180
  std::map<int, ZoneStruct> m_idToDataMap;
181
  //! a map id to page positions, ...
182
  std::map<int, PageStruct> m_idToPageMap;
183
  //! a list of frames
184
  std::vector<FrameStruct> m_frames;
185
  //! a list of frames date
186
  std::vector<std::array<int, 3> > m_frameDates;
187
  //! a map id to frame note entry
188
  std::map<int, ZoneEntry> m_idToFrameNoteMap;
189
  //! a map id to biblio text
190
  std::map<int, librevenge::RVNGString> m_idToFrameBiblioMap;
191
  //! a map id to picture positions, ...
192
  std::map<int, PictureStruct> m_idToPictureMap;
193
  //! a map id to an embedded object
194
  std::map<int, MWAWEmbeddedObject> m_idToObjectMap;
195
};
196
197
////////////////////////////////////////
198
//! Internal: the state of a StudentWritingCParser
199
struct State {
200
  //! constructor
201
  State()
202
410
    : m_actPage(0)
203
410
    , m_numPages(0)
204
205
410
    , m_isUncompressed(false)
206
410
    , m_zones()
207
410
    , m_idToZoneMap()
208
209
410
    , m_idToFontNameMap()
210
410
    , m_idToFontNameUsed()
211
212
410
    , m_sendZoneSet()
213
410
    , m_sendBoxesStack()
214
410
  {
215
410
  }
216
217
  //! try to return a color corresponding to a color id
218
  bool getColor(int id, MWAWColor &color) const
219
0
  {
220
0
    if (id<0 || id>15) {
221
0
      MWAW_DEBUG_MSG(("StudentWritingCParserInternal::State::getColor: unknown id=%d\n", id));
222
0
      return false;
223
0
    }
224
0
    static uint32_t colors[]= { 0, 0xffffff, 0x838300, 0x808080, 0xc9c9c9,
225
0
                                0xff0000, 0xff00, 0xff, 0xffff, 0xff00ff,
226
0
                                0xffff00, 0x8f8f, 0x8f00, 0x8f0000, 0x8f,
227
0
                                0xb000b0
228
0
                              };
229
0
    color=MWAWColor(colors[id]);
230
0
    return true;
231
0
  }
232
  //! small function to know if a zone with given type exists
233
  bool checkIfZone(int id, int type) const
234
0
  {
235
0
    auto const &zId=m_idToZoneMap.find(id);
236
0
    return zId!=m_idToZoneMap.end() && zId->second->m_type==type;
237
0
  }
238
  int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
239
240
  //! a flag to know if we have uncompress the data
241
  bool m_isUncompressed;
242
  //! the main zone id and the font id
243
  ZoneEntry m_ids[2];
244
  //! the list of zone
245
  std::vector<std::shared_ptr<Zone> > m_zones;
246
  //! a map id to zone data
247
  std::map<int, std::shared_ptr<Zone> > m_idToZoneMap;
248
249
  //! a map id to font name
250
  std::map<int, std::string> m_idToFontNameMap;
251
  //! a set to store the font name used
252
  std::set<int> m_idToFontNameUsed;
253
254
  //! a set to keep the list of send zone (to avoid loop)
255
  std::set<int> m_sendZoneSet;
256
  //! a stack of send bounding box (use to send background picture)
257
  std::stack<MWAWBox2f> m_sendBoxesStack;
258
};
259
260
////////////////////////////////////////
261
//! Internal: the subdocument of a StudentWritingCParser
262
class SubDocument final : public MWAWSubDocument
263
{
264
public:
265
  SubDocument(StudentWritingCParser &pars, MWAWInputStreamPtr const &input, int id)
266
0
    : MWAWSubDocument(&pars, input, MWAWEntry())
267
0
    , m_zoneId(id)
268
0
  {
269
0
  }
270
271
  //! destructor
272
0
  ~SubDocument() final {}
273
274
  //! operator!=
275
  bool operator!=(MWAWSubDocument const &doc) const final
276
0
  {
277
0
    if (MWAWSubDocument::operator!=(doc)) return true;
278
0
    auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
279
0
    if (!sDoc) return true;
280
0
    if (m_zoneId != sDoc->m_zoneId) return true;
281
0
    return false;
282
0
  }
283
284
  //! the parser function
285
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
286
287
protected:
288
  //! the zone id
289
  int m_zoneId;
290
};
291
292
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
293
0
{
294
0
  if (!listener.get()) {
295
0
    MWAW_DEBUG_MSG(("StudentWritingCParserInternal::SubDocument::parse: no listener\n"));
296
0
    return;
297
0
  }
298
0
  auto *parser=dynamic_cast<StudentWritingCParser *>(m_parser);
299
0
  if (!parser) {
300
0
    MWAW_DEBUG_MSG(("StudentWritingCParserInternal::SubDocument::parse: no parser\n"));
301
0
    return;
302
0
  }
303
304
0
  long pos = m_input->tell();
305
0
  parser->sendZone(m_zoneId);
306
0
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
307
0
}
308
}
309
310
////////////////////////////////////////////////////////////
311
// constructor/destructor, ...
312
////////////////////////////////////////////////////////////
313
StudentWritingCParser::StudentWritingCParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
314
204
  : MWAWTextParser(input, rsrcParser, header)
315
204
  , m_state()
316
204
{
317
204
  setAsciiName("main-1");
318
319
204
  m_state.reset(new StudentWritingCParserInternal::State);
320
321
  // reduce the margin (in case, the page is not defined)
322
204
  getPageSpan().setMargins(0.1);
323
204
}
324
325
StudentWritingCParser::~StudentWritingCParser()
326
204
{
327
204
}
328
329
////////////////////////////////////////////////////////////
330
// the parser
331
////////////////////////////////////////////////////////////
332
void StudentWritingCParser::parse(librevenge::RVNGTextInterface *docInterface)
333
52
{
334
52
  if (!getInput().get() || !checkHeader(nullptr))  throw(libmwaw::ParseException());
335
52
  bool ok = true;
336
52
  try {
337
52
    getParserState()->m_input=decode();
338
52
    if (!getInput())
339
50
      throw(libmwaw::ParseException());
340
2
    m_state->m_isUncompressed=true;
341
    // create the asciiFile
342
2
    ascii().setStream(getInput());
343
2
    ascii().open(asciiName());
344
345
2
    checkHeader(nullptr);
346
2
    ok = createZones();
347
2
    if (ok) {
348
0
      createDocument(docInterface);
349
0
      sendZone(m_state->m_ids[0].first);
350
0
    }
351
352
2
    ascii().reset();
353
2
  }
354
52
  catch (...) {
355
50
    MWAW_DEBUG_MSG(("StudentWritingCParser::parse: exception catched when parsing\n"));
356
50
    ok = false;
357
50
  }
358
359
52
  resetTextListener();
360
52
  if (!ok) throw(libmwaw::ParseException());
361
52
}
362
363
////////////////////////////////////////////////////////////
364
// create the document
365
////////////////////////////////////////////////////////////
366
void StudentWritingCParser::createDocument(librevenge::RVNGTextInterface *documentInterface)
367
0
{
368
0
  if (!documentInterface) throw(libmwaw::ParseException());
369
0
  if (getTextListener()) {
370
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::createDocument: listener already exist\n"));
371
0
    return;
372
0
  }
373
374
0
  std::vector<MWAWPageSpan> pageList;
375
  // first find the main zone to look for header/footer
376
0
  auto mainIt=m_state->m_idToZoneMap.find(m_state->m_ids[0].first);
377
0
  if (mainIt==m_state->m_idToZoneMap.end() || !mainIt->second || mainIt->second->m_type!=5) {
378
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::createDocument: can not find the main zone\n"));
379
0
    throw (libmwaw::ParseException());
380
0
  }
381
382
0
  auto const &mainZone=*mainIt->second;
383
0
  if (mainZone.m_idToDataMap.find(1)!=mainZone.m_idToDataMap.end()) {
384
0
    auto const &mainStruct=mainZone.m_idToDataMap.find(1)->second;
385
0
    bool hasTitlePage=mainStruct.m_ids[7].first>0;
386
0
    if (hasTitlePage)
387
0
      ++m_state->m_numPages;
388
    // look also if we need to add a final page for the biblio at the end
389
0
    if (mainStruct.m_ids[1].first) {
390
0
      auto biblioIt=m_state->m_idToZoneMap.find(mainStruct.m_ids[1].first);
391
0
      if (biblioIt!=m_state->m_idToZoneMap.end() && biblioIt->second && !biblioIt->second->m_idToFrameBiblioMap.empty())
392
0
        ++m_state->m_numPages;
393
0
    }
394
    // now look for header/footer
395
0
    auto const &flags=mainStruct.m_flags;
396
0
    bool hasHF[2];
397
0
    for (int i=0; i<2; ++i) hasHF[i]=mainStruct.m_ids[i+4].first>0;
398
0
    bool const hasFirstHF[2]= {hasHF[0] &&(flags&0x800)==0, hasHF[1] &&(flags&0x1000)==0};
399
0
    bool needFirstPage=(hasTitlePage && (hasHF[0] || hasHF[1])) || hasHF[0]!=hasFirstHF[0] || hasHF[1]!=hasFirstHF[1];
400
0
    for (auto st=0; st<2; ++st) {
401
0
      MWAWPageSpan ps(getPageSpan());
402
0
      if (st==0) {
403
0
        if (!needFirstPage)
404
0
          continue;
405
0
        ps.setPageSpan(1);
406
0
      }
407
0
      else
408
0
        ps.setPageSpan(m_state->m_numPages+(needFirstPage ? 0 : 1));
409
0
      ps.setMarginTop(mainStruct.m_margins[1]);
410
0
      ps.setMarginBottom(mainStruct.m_margins[3]);
411
0
      ps.setMarginLeft(mainStruct.m_margins[0]);
412
0
      ps.setMarginRight(mainStruct.m_margins[2]);
413
0
      for (int wh=0; wh<2; ++wh) {
414
0
        if ((st==0 && (hasTitlePage || !hasFirstHF[wh])) || (st==1 && !hasHF[wh]))
415
0
          continue;
416
0
        MWAWHeaderFooter hf(wh==1 ? MWAWHeaderFooter::FOOTER : MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL);
417
0
        hf.m_subDocument.reset(new StudentWritingCParserInternal::SubDocument(*this, getInput(),mainStruct.m_ids[wh+4].first));
418
0
        ps.setHeaderFooter(hf);
419
0
      }
420
0
      pageList.push_back(ps);
421
0
    }
422
0
  }
423
0
  else {
424
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::createDocument: can not find the main zone's structure\n"));
425
0
    MWAWPageSpan ps(getPageSpan());
426
0
    ps.setPageSpan(m_state->m_numPages+1);
427
0
    pageList.push_back(ps);
428
0
  }
429
430
  //
431
0
  MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface));
432
0
  setTextListener(listen);
433
0
  listen->startDocument();
434
0
}
435
436
437
////////////////////////////////////////////////////////////
438
//
439
// Intermediate level
440
//
441
////////////////////////////////////////////////////////////
442
bool StudentWritingCParser::createZones()
443
2
{
444
2
  MWAWInputStreamPtr input = getInput();
445
2
  if (!input || !input->checkPosition(352))
446
0
    return false;
447
448
2
  libmwaw::DebugStream f;
449
2
  input->seek(4, librevenge::RVNG_SEEK_SET);
450
2
  int val=int(input->readLong(1));
451
2
  if (val==3)
452
0
    f << "template,";
453
2
  else if (val!=2)
454
0
    f << "unk=" << val << ",";
455
2
  val=int(input->readLong(2));
456
2
  if (val!=0x4646)
457
0
    f << "f0=" << std::hex << val << std::dec << ",";
458
2
  val=int(input->readLong(1));
459
2
  if (val) f << "f1=" << val << ",";
460
461
  // first block: 220
462
  // then 124?
463
2
  val=int(input->readLong(2)); // 0-4
464
2
  switch (val) {
465
0
  case 0:
466
0
    f << "report,";
467
0
    break;
468
2
  case 1:
469
2
    f << "journal,";
470
2
    break;
471
0
  case 2:
472
0
    f << "sign,";
473
0
    break;
474
0
  case 3:
475
0
    f << "newletter,";
476
0
    break;
477
0
  case 4:
478
0
    f << "letter,";
479
0
    break;
480
0
  default:
481
0
    f << "type=" << val << ",";
482
0
    break;
483
2
  }
484
485
10
  for (int i=0; i<4; ++i) {
486
8
    val=int(input->readLong(2));
487
8
    int const expected[]= {2, 1, 0, 0};
488
8
    if (val==expected[i]) continue;
489
8
    f << "f" << i+2 << "=" << val << ",";
490
8
  }
491
6
  for (int st=0; st<2; ++st) {
492
4
    int cId=int(input->readLong(2));
493
4
    int type=int(input->readLong(2));
494
4
    m_state->m_ids[st]=std::make_pair(cId,type);
495
4
    if (!cId) continue;
496
0
    f << (st==0 ? "main" : "font") << "=Z" << cId << ":" << type << ",";
497
0
  }
498
6
  for (int i=0; i<2; ++i) {
499
4
    val=int(input->readLong(2));
500
4
    if (val==(i==0 ? 1 : 0)) continue;
501
2
    f << "f" << i+6 << "=" << val << ",";
502
2
  }
503
2
  val=int(input->readLong(1));
504
2
  if (val) f << "f7=" << val << ",";
505
6
  for (int st=0; st<2; ++st) {
506
4
    long aPos=input->tell();
507
4
    std::string name;
508
4
    for (int i=0; i<32; ++i) {
509
4
      char c=char(input->readLong(1));
510
4
      if (!c)
511
4
        break;
512
0
      name+=c;
513
0
    }
514
4
    if (!name.empty()) f << "text" << st << "=" << name << ",";
515
4
    input->seek(aPos+32, librevenge::RVNG_SEEK_SET);
516
4
  }
517
2
  ascii().addPos(0);
518
2
  ascii().addNote(f.str().c_str());
519
520
2
  long pos=input->tell();
521
2
  f.str("");
522
2
  f << "FileHeader-A:";
523
122
  for (int i=0; i<60; ++i) {
524
120
    val=int(input->readLong(2));
525
120
    if (val)
526
0
      f << "f" << i << "=" << val << ",";
527
120
  }
528
2
  ascii().addPos(pos);
529
2
  ascii().addNote(f.str().c_str());
530
531
2
  pos=input->tell();
532
2
  f.str("");
533
2
  f << "FileHeader-B:";
534
2
  val=int(input->readLong(1));
535
2
  if (val)
536
0
    f << "f0=" << val << ",";
537
8
  for (int i=0; i<3; ++i) {
538
6
    val=int(input->readLong(2));
539
6
    int const expected[]= {0x28 /* or aa*/, 0, 0x7cb};
540
6
    if (val==expected[i])
541
2
      continue;
542
4
    if (i==0)
543
2
      f << "day=" << val << ",";
544
2
    else if (i==2)
545
2
      f << "year=" << val << ",";
546
0
    else
547
0
      f << "f" << i+1 << "=" << val << ",";
548
4
  }
549
6
  for (int i=0; i<2; ++i) {  // f3=0|2|4
550
4
    val=int(input->readLong(1));
551
4
    if (val)
552
4
      f << "f" << i+3 << "=" << val << ",";
553
4
  }
554
38
  for (int i=0; i<18; ++i) {
555
36
    val=int(input->readLong(2));
556
36
    if (val)
557
36
      f << "f" << i+5 << "=" << val << ",";
558
36
  }
559
26
  for (int i=0; i<12; ++i) {
560
24
    val=int(input->readLong(2));
561
24
    int const expected[]= {0x320, 0, 0x7c, 0x78, 0,
562
24
                           0x11f, 1, 0, 0xaea, 0x86f,
563
24
                           0x64, 1
564
24
                          };
565
24
    if (val==expected[i])
566
0
      continue;
567
24
    f << "g" << i << "=" << val << ",";
568
24
  }
569
570
70
  for (int i=0; i<34; ++i) {
571
68
    val=int(input->readLong(2));
572
68
    if (val)
573
4
      f << "h" << i << "=" << val << ",";
574
68
  }
575
2
  ascii().addPos(pos);
576
2
  ascii().addNote(f.str().c_str());
577
578
2
  pos=input->tell();
579
2
  if (!readPrintInfo())
580
2
    return false;
581
0
  input->seek(pos+120, librevenge::RVNG_SEEK_SET);
582
583
0
  while (!input->isEnd()) {
584
0
    pos=input->tell();
585
0
    if (!input->checkPosition(pos+10))
586
0
      break;
587
0
    long dataSize=long(input->readLong(4));
588
0
    long endPos=pos+10+dataSize;
589
0
    if (endPos<pos+10 || !input->checkPosition(endPos)) {
590
0
      input->seek(pos, librevenge::RVNG_SEEK_SET);
591
0
      break;
592
0
    }
593
0
    int ids[3];
594
0
    for (int i=0; i<3; ++i)
595
0
      ids[i]=int(input->readLong(2));
596
597
0
    MWAWEntry entry;
598
0
    entry.setBegin(pos);
599
0
    entry.setEnd(endPos);
600
0
    entry.setId(ids[0]);
601
0
    if (m_state->m_idToZoneMap.find(ids[0])==m_state->m_idToZoneMap.end()) {
602
0
      m_state->m_zones.push_back(std::make_shared<StudentWritingCParserInternal::Zone>(ids[1], ids[0]));
603
0
      m_state->m_idToZoneMap[ids[0]]=m_state->m_zones.back();
604
0
    }
605
0
    auto &zone=*m_state->m_idToZoneMap.find(ids[0])->second;
606
0
    if (zone.m_type!=ids[1] || !zone.insert(ids[2],entry)) {
607
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::createZones: find a bad zone type\n"));
608
0
      ascii().addPos(pos);
609
0
      ascii().addNote("Entries(BadZone):###");
610
0
    }
611
0
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
612
0
  }
613
614
0
  if (!input->isEnd()) {
615
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::createZones: find extra data\n"));
616
0
    ascii().addPos(input->tell());
617
0
    ascii().addNote("Entries(Unknown):###");
618
0
  }
619
620
0
  bool mainZoneFound=false;
621
0
  for (auto const &zIt : m_state->m_idToZoneMap) {
622
0
    int zoneId=zIt.first;
623
0
    auto &zone=*zIt.second;
624
625
0
    switch (zone.m_type) {
626
0
    case 0:
627
0
      for (auto const &dataIt : zone.m_idToEntryMap) {
628
0
        auto const &id=dataIt.first;
629
0
        auto const &entry=dataIt.second;
630
0
        f.str("");
631
0
        f << "Entries(End):";
632
0
        if (zoneId!=0 || id!=0 || entry.length()!=10) {
633
0
          MWAW_DEBUG_MSG(("StudentWritingCParser::createZones: unexpected end zone data\n"));
634
0
          f << "###";
635
0
        }
636
0
        ascii().addPos(entry.begin());
637
0
        ascii().addNote(f.str().c_str());
638
0
      }
639
0
      break;
640
0
    case 1:
641
0
      readTextZone(zone);
642
0
      break;
643
0
    case 2:
644
0
      for (auto const &dataIt : zone.m_idToEntryMap) {
645
0
        auto const &id=dataIt.first;
646
0
        auto const &entry=dataIt.second;
647
0
        input->seek(entry.begin()+10, librevenge::RVNG_SEEK_SET);
648
649
0
        f.str("");
650
0
        f << "Entries(Data2)[Z" << zoneId << "]:id=" << id << ",";
651
0
        if (entry.length()!=10+0x54) {
652
0
          MWAW_DEBUG_MSG(("StudentWritingCParser::createZones[data2]: unexpected end zone size\n"));
653
0
          f << "###";
654
0
        }
655
0
        else {
656
0
          StudentWritingCParserInternal::PageStruct page;
657
0
          for (int i=0; i<7; ++i) {
658
0
            val=int(input->readLong(2));
659
0
            int const expected[]= {1, 0, 0, 0, 1, 1, 0 };
660
0
            if (val!=expected[i])
661
0
              f << "f" << i << "=" << val << ",";
662
0
          }
663
0
          for (int st=0; st<2; ++st) {
664
0
            int cId=int(input->readLong(2));
665
0
            int type=int(input->readLong(2));
666
0
            page.m_hfIds[st]=std::make_pair(cId,type);
667
0
            if (!cId) continue;
668
0
            f << (st==0 ? "pageNumber[header]" : "pageNumber[footer]") << "=Z" << cId << ":" << type << ",";
669
0
            if (!m_state->checkIfZone(cId, type)) {
670
0
              MWAW_DEBUG_MSG(("StudentWritingCParser::createZones[data2]: unexpected zone5 id/size\n"));
671
0
              f << "###";
672
0
            }
673
0
          }
674
0
          val=int(input->readLong(2));
675
0
          if (val!=1)
676
0
            f << "g0=" << val << ",";
677
0
          page.m_pageNumber=int(input->readLong(2));
678
0
          if (page.m_pageNumber!=1)
679
0
            f << "page[number]=" << page.m_pageNumber << ",";
680
0
          val=int(input->readULong(2));
681
0
          page.m_numColumns=1+(val&7);
682
0
          if (page.m_numColumns!=1)
683
0
            f << "num[columns]=" << page.m_numColumns << ",";
684
0
          val &= 0xfff8;
685
0
          if (val)
686
0
            f << "fl=" << std::hex << val << std::dec << ",";
687
0
          for (int i=0; i<9; ++i) { // g2 related to g0 height?
688
0
            val=int(input->readLong(2));
689
0
            int const expected[]= { 0xc8, 0x48, 0, 0, 0, 0, 0, 1, 0};
690
0
            if (val==expected[i])
691
0
              continue;
692
0
            if (i==5) {
693
0
              page.m_firstChar=val;
694
0
              f << "first[char]=" << val << ",";
695
0
            }
696
0
            else
697
0
              f << "g" << i+1 << "=" << val << ",";
698
0
          }
699
0
          for (int i=0; i<12; ++i) { // h1=h4
700
0
            val=int(input->readLong(2));
701
0
            if (val)
702
0
              f << "h" << i << "=" << val << ",";
703
0
          }
704
0
          for (int i=0; i<7; ++i) { // k0=small number
705
0
            val=int(input->readLong(2));
706
0
            int const expected[]= {0, 0x40, 0x78, 0, 0, 0, 0};
707
0
            if (val!=expected[i])
708
0
              f << "k" << i << "=" << val << ",";
709
0
          }
710
0
          zone.m_idToPageMap[id]=page;
711
0
        }
712
0
        ascii().addPos(entry.begin());
713
0
        ascii().addNote(f.str().c_str());
714
0
      }
715
0
      break;
716
0
    case 3:
717
0
      readFrame(zone);
718
0
      break;
719
0
    case 4:
720
0
      readParagraph(zone);
721
0
      break;
722
0
    case 5:
723
0
      if (zoneId==m_state->m_ids[0].first)
724
0
        mainZoneFound=true;
725
0
      for (auto const &dataIt : zone.m_idToEntryMap) {
726
0
        auto const &id=dataIt.first;
727
0
        auto const &entry=dataIt.second;
728
0
        f.str("");
729
0
        f << "Entries(Data5)[Z" << zoneId << "]:id=" << id << ",";
730
0
        input->seek(entry.begin()+10, librevenge::RVNG_SEEK_SET);
731
732
0
        if (id!=1 || entry.length()!=10+0x72) {
733
0
          MWAW_DEBUG_MSG(("StudentWritingCParser::createZones[data5]: unexpected end zone id/size\n"));
734
0
          f << "###";
735
0
        }
736
0
        else {
737
0
          StudentWritingCParserInternal::ZoneStruct data;
738
0
          f << "IDS=[";
739
0
          for (int i=0; i<7; ++i) // big number multiple of 4: change for each save
740
0
            f << std::hex << input->readULong(4) << std::dec << ",";
741
0
          f << "],";
742
0
          for (int i=0; i<2; ++i) { // 0
743
0
            val=int(input->readULong(2));
744
0
            if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
745
0
          }
746
0
          for (int i=0; i<8; ++i) { // zone2-zone5, pagenumber:5, next zone5, zone1, title page
747
0
            int cId=int(input->readLong(2));
748
0
            int type=int(input->readLong(2));
749
0
            data.m_ids[i]=std::make_pair(cId, type);
750
0
            if (cId==0)
751
0
              continue;
752
0
            char const *what[]= {"pages", "frames", "paragraph", "Zone5",
753
0
                                 "header", "footer", "text", "title"
754
0
                                };
755
0
            if (what[i])
756
0
              f << what[i] << "=Z" << cId << ":" << type << ",";
757
0
            else
758
0
              f << "unkn" << i << "=Z" << cId << ":" << type << ",";
759
0
            if (!m_state->checkIfZone(cId, type)) {
760
0
              MWAW_DEBUG_MSG(("StudentWritingCParser::createZones[data5]: unexpected child id/type\n"));
761
0
              f << "###";
762
0
            }
763
0
          }
764
0
          data.m_flags=val=int(input->readULong(2));
765
0
          if (val&7)
766
0
            f << "num[columns]=" << 1+int(val&7) << ",";
767
0
          if (val&0x40)
768
0
            f << "has[master,head],";
769
0
          if (val&0x800)
770
0
            f << "header[first,skip],";
771
0
          if (val&0x1000)
772
0
            f << "footer[first,skip],";
773
0
          if (val&0x4000)
774
0
            f << "pagenumber[bottom],";
775
0
          if (val&0x8000)
776
0
            f << "pagenumber[center/right],";
777
0
          val&=0x27b8;
778
0
          if (val)
779
0
            f << "fl0=" << std::hex << val << std::dec << ",";
780
0
          val=int(input->readULong(2)); // 2: header?, 3: footer?
781
0
          if (val)
782
0
            f << "fl1=" << std::hex << val << std::dec << ",";
783
0
          f << "IDS2=" << std::hex << input->readULong(4) << std::dec << ",";
784
0
          f << "margins=["; // L, T, R, B
785
0
          for (auto &m : data.m_margins) {
786
0
            m=float(input->readLong(2))/1000;
787
0
            f << m << "in,";
788
0
          }
789
0
          f << "],";
790
0
          val=int(input->readULong(2));
791
0
          if (val!=0xc8)
792
0
            f << "f2=" << val << ",";
793
0
          val=int(input->readULong(2)); // &1: line between column
794
0
          if (val)
795
0
            f << "fl2=" << std::hex << val << std::dec << ",";
796
0
          for (int i=0; i<12; ++i) { // N8 related to page number align ?
797
0
            val=int(input->readLong(2));
798
0
            int const expected[]= {1,0,0,1,1,
799
0
                                   0,0,0/*or 402|404*/,0x64,0x78,
800
0
                                   0 /* 0|2|7|9|14|84|97*/, 0 /*0|2|4|5*/
801
0
                                  };
802
0
            if (val==expected[i]) continue;
803
0
            if (i==0) {
804
0
              data.m_subZones2=val;
805
0
              f << "N[data2]=" << val << ",";
806
0
            }
807
0
            else
808
0
              f << "N" << i << "=" << val << ",";
809
0
          }
810
0
          int cId=int(input->readLong(2));
811
0
          int type=int(input->readLong(2));
812
0
          data.m_ids[8]=std::make_pair(cId, type);
813
0
          if (cId) {
814
0
            f << "bgPict?=Z" << cId << ":" << type << ",";
815
0
            if (!m_state->checkIfZone(cId, type)) {
816
0
              MWAW_DEBUG_MSG(("StudentWritingCParser::createZones[data5]: unexpected picture id/type\n"));
817
0
              f << "###";
818
0
            }
819
0
          }
820
0
          val=int(input->readLong(2)); // 0
821
0
          if (val)
822
0
            f << "f3=" << val << ",";
823
0
          zone.m_idToDataMap[id]=data;
824
0
        }
825
0
        ascii().addPos(entry.begin());
826
0
        ascii().addNote(f.str().c_str());
827
0
      }
828
0
      break;
829
0
    case 6:
830
0
      readPicture(zone);
831
0
      break;
832
0
    case 7:
833
0
      for (auto const &dataIt : zone.m_idToEntryMap) {
834
0
        auto const &id=dataIt.first;
835
0
        auto const &entry=dataIt.second;
836
0
        f.str("");
837
0
        f << "Entries(Fonts)[Z" << zoneId << "]:";
838
0
        if (id!=1 || !readFontsList(entry)) {
839
0
          MWAW_DEBUG_MSG(("StudentWritingCParser::createZones[fonts]: unexpected id=%d\n", id));
840
0
          f << "###";
841
0
          ascii().addPos(entry.begin());
842
0
          ascii().addNote(f.str().c_str());
843
0
        }
844
0
      }
845
0
      break;
846
0
    default:
847
0
      for (auto const &dataIt : zone.m_idToEntryMap) {
848
0
        auto const &id=dataIt.first;
849
0
        auto const &entry=dataIt.second;
850
0
        f.str("");
851
0
        f << "Entries(Zone" << zone.m_type << "A)[Z" << zoneId << "]:";
852
0
        if (id!=1)
853
0
          f << "id=" << id << ",";
854
0
        ascii().addPos(entry.begin());
855
0
        ascii().addNote(f.str().c_str());
856
0
      }
857
0
      break;
858
0
    }
859
0
  }
860
0
  if (!mainZoneFound) {
861
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::createZone: can not found the main zone\n"));
862
0
    return false;
863
0
  }
864
0
  return mainZoneFound;
865
0
}
866
867
bool StudentWritingCParser::readTextZone(StudentWritingCParserInternal::Zone const &zone)
868
0
{
869
0
  auto input=getInput();
870
0
  if (!input || zone.m_type!=1) {
871
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::readTextZone: called will incorrect zone type\n"));
872
0
    return false;
873
0
  }
874
0
  libmwaw::DebugStream f;
875
0
  int val;
876
0
  std::map<int, int> textIdNumChars;
877
0
  std::map<int, std::array<int, 4> > styleIdValues;
878
0
  for (auto const &dataIt : zone.m_idToEntryMap) {
879
0
    auto const &id=dataIt.first;
880
0
    auto const &entry=dataIt.second;
881
0
    input->seek(entry.begin()+10, librevenge::RVNG_SEEK_SET);
882
0
    switch (id) {
883
0
    case 1: {
884
0
      f.str("");
885
0
      f << "Entries(TZone)[Z" << zone.m_id << "]:header,";
886
0
      if (entry.length()<10+10) {
887
0
        MWAW_DEBUG_MSG(("StudentWritingCParser::readTextZone[header]: the entry seems too short\n"));
888
0
        f << "###";
889
0
        break;
890
0
      }
891
0
      val=int(input->readLong(2));
892
0
      if (val)
893
0
        f << "f0=" << val << ",";
894
895
0
      int begPos[2];
896
0
      int N[2];
897
0
      f << "zones=[";
898
0
      for (int i=0; i<2; ++i) {
899
0
        f << "[";
900
0
        begPos[i]=int(input->readULong(2));
901
0
        f << "pos=" <<10+begPos[i] << ",";
902
0
        N[i]=int(input->readLong(2));
903
0
        f << "N=" << N[i] << ",";
904
0
        if (begPos[i]<10 || N[i]<0 || (entry.length()-10-begPos[i])/10<N[i] || begPos[i]+10+10*N[i]>entry.length()) {
905
0
          MWAW_DEBUG_MSG(("StudentWritingCParser::readTextZone[header]: a sub zone seems bad\n"));
906
0
          f << "###";
907
0
          N[i]=0;
908
0
        }
909
0
        f << "],";
910
0
      }
911
0
      f << "],";
912
0
      input->seek(entry.begin()+10+begPos[0], librevenge::RVNG_SEEK_SET);
913
0
      f << "text=[";
914
0
      for (int n=0; n<N[0]; ++n) {
915
0
        f << "[";
916
0
        int cId=int(input->readULong(2));
917
0
        f << "id=" << cId << ",";
918
0
        for (int j=0; j<4; ++j) {
919
0
          val=int(input->readLong(2));
920
0
          if (j==1)
921
0
            textIdNumChars[cId]=val;
922
0
          int const expected[]= {0xff0, 0xff0, 0, 0}; // num[max], num[char], 0, 0
923
0
          if (val==expected[j])
924
0
            continue;
925
0
          if (j==0)
926
0
            f << "zone[sz]=" << val << ",";
927
0
          else if (j==1)
928
0
            f << "num[char]=" << val << ",";
929
0
          else
930
0
            f << "f" << j << "=" << val << ",";
931
0
        }
932
0
        f << "],";
933
0
      }
934
0
      f << "],";
935
936
0
      input->seek(entry.begin()+10+begPos[1], librevenge::RVNG_SEEK_SET);
937
0
      f << "style=[";
938
0
      for (int n=0; n<N[1]; ++n) {
939
0
        f << "[";
940
0
        int cId=int(input->readULong(2));
941
0
        f << "id=" << cId << ",";
942
0
        std::array<int, 4> values;
943
0
        for (size_t j=0; j<4; ++j) {
944
0
          values[j]=int(input->readLong(2));
945
0
          int const expected[]= {0xff, 0, 0, 0};
946
0
          if (values[j]==expected[j]) continue;
947
0
          switch (j) {
948
0
          case 0:
949
0
            f << "numStyle[max]=" << values[j] << ",";
950
0
            break;
951
0
          case 1:
952
0
            f << "numStyle=" << values[j] << ",";
953
0
            break;
954
0
          case 2:
955
0
            f << "f0=" << values[j] << ",";
956
0
            break;
957
0
          case 3:
958
0
          default:
959
0
            f << "numChar=" << values[j] << ",";
960
0
            break;
961
0
          }
962
0
        }
963
0
        styleIdValues[cId]=values;
964
0
        f << "],";
965
0
      }
966
0
      f << "],";
967
0
      break;
968
0
    }
969
0
    default: {
970
0
      auto tId=textIdNumChars.find(id);
971
0
      if (tId!=textIdNumChars.end()) {
972
0
        int n=tId->second;
973
0
        f.str("");
974
0
        f << "Entries(TZone)[Z" << zone.m_id << "]:text,id=" << id << ",";
975
0
        if (10+n<10 || 10+n>entry.length()) {
976
0
          MWAW_DEBUG_MSG(("StudentWritingCParser::readTextZone: bad number of characters\n"));
977
0
          f << "###";
978
0
          break;
979
0
        }
980
0
        std::string text;
981
0
        for (int i=0; i<n; ++i) {
982
0
          char c=char(input->readLong(1));
983
0
          if (!c)
984
0
            text+="#[0]";
985
0
          else
986
0
            text+=c;
987
0
        }
988
0
        f << text << ",";
989
0
        if (input->tell()!=entry.end()) {
990
0
          ascii().addPos(input->tell());
991
0
          ascii().addNote("_");
992
0
          input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
993
0
        }
994
0
        break;
995
0
      }
996
0
      auto sIt=styleIdValues.find(id);
997
0
      if (sIt!=styleIdValues.end()) {
998
0
        f.str("");
999
0
        f << "Entries(TZone)[Z" << zone.m_id << "]:style,id=" << id << ",";
1000
0
        int numStyles=sIt->second[1];
1001
0
        if (numStyles<0 || entry.length()<10+6*numStyles || (entry.length()-10)/6<numStyles) {
1002
0
          MWAW_DEBUG_MSG(("StudentWritingCParser::readTextZone: bad number of style\n"));
1003
0
          f << "###N=" << numStyles << ",";
1004
0
          break;
1005
0
        }
1006
0
        libmwaw::DebugStream f2;
1007
0
        int cPos=0;
1008
0
        for (int i=0; i<numStyles; ++i) {
1009
0
          long pos=input->tell();
1010
0
          f2.str("");
1011
0
          f2 << "TZone-S" << i << ":";
1012
0
          int type=int(input->readULong(2));
1013
0
          int nChar=int(input->readLong(2));
1014
0
          val=int(input->readULong(2));
1015
0
          if (cPos)
1016
0
            f2 << "pos=" << cPos << ",";
1017
0
          cPos+=nChar;
1018
0
          switch (type) {
1019
          // 0,2-3
1020
0
          case 0x1:
1021
0
            f2 << "endNote=F" << val << ",";
1022
0
            break;
1023
0
          case 0x2:
1024
0
            f2 << "setDate=F" << val << ",";
1025
0
            break;
1026
          // 10-18
1027
0
          case 0x10:
1028
0
            f2 << "font,bold=" << val << ","; // true/false
1029
0
            break;
1030
0
          case 0x11:
1031
0
            f2 << "font,italic=" << val << ","; // true/false
1032
0
            break;
1033
0
          case 0x12:
1034
0
            f2 << "font,underline=" << val << ","; // true/false
1035
0
            break;
1036
0
          case 0x13:
1037
0
            f2 << "font,FN" << val << ",";
1038
0
            break;
1039
0
          case 0x14:
1040
0
            f2 << "font,size=" << float(val)/10 << ",";
1041
0
            break;
1042
0
          case 0x15:
1043
0
            f2 << "font,outline=" << val << ","; // true/false
1044
0
            break;
1045
0
          case 0x16:
1046
0
            f2 << "font,color=" << val << ",";
1047
0
            break;
1048
0
          case 0x17:
1049
0
            f2 << "font,sub/super=" << val << ","; // 0: none 1: super, 2: subs
1050
0
            break;
1051
0
          case 0x18:
1052
0
            f2 << "font,shadow=" << val << ","; // true/false
1053
0
            break;
1054
          // 20-22
1055
0
          case 0x20: // checkme
1056
0
            f2 << "page[number],";
1057
0
            if (val) f << "f0=" << val << ",";
1058
0
            break;
1059
0
          case 0x21:
1060
0
            f2 << "date,form="  << std::hex << val << std::dec << ",";
1061
0
            break;
1062
0
          case 0x22:
1063
0
            f2 << "bullet,";
1064
0
            if (val) f << "f0=" << val << ",";
1065
0
            break;
1066
1067
0
          case 0x100:
1068
0
            f2 << "para,P" << val << ",";
1069
0
            break;
1070
1071
0
          case 0x300:
1072
0
            f2 << "col[break],col=" << val << ",";
1073
0
            break;
1074
0
          case 0x500:
1075
0
            ++m_state->m_numPages;
1076
0
            f2 << "page[break],page=" << val << ",";
1077
0
            break;
1078
0
          case 0x700:
1079
0
            f2 << "zone[break]=" << val << ",";
1080
0
            break;
1081
1082
0
          default:
1083
0
            f2 << "type=" << std::hex << type << std::dec << ",";
1084
0
          }
1085
0
          ascii().addPos(pos);
1086
0
          ascii().addNote(f2.str().c_str());
1087
0
          input->seek(pos+6, librevenge::RVNG_SEEK_SET);
1088
0
        }
1089
0
        if (input->tell()!=entry.end()) {
1090
0
          ascii().addPos(input->tell());
1091
0
          ascii().addNote("_");
1092
0
          input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
1093
0
        }
1094
0
        break;
1095
0
      }
1096
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::readTextZone[header]: find unknown zone\n"));
1097
0
      f.str("");
1098
0
      f << "Entries(TZone)[Z" << zone.m_id << "]:id=" << id << "," << "###";
1099
0
      break;
1100
0
    }
1101
0
    }
1102
0
    if (input->tell()!=entry.end())
1103
0
      ascii().addDelimiter(input->tell(),'|');
1104
0
    ascii().addPos(entry.begin());
1105
0
    ascii().addNote(f.str().c_str());
1106
0
  }
1107
0
  return true;
1108
0
}
1109
1110
bool StudentWritingCParser::readFrame(StudentWritingCParserInternal::Zone &zone)
1111
0
{
1112
0
  auto input=getInput();
1113
0
  if (!input || zone.m_type!=3) {
1114
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame: called will incorrect zone type\n"));
1115
0
    return false;
1116
0
  }
1117
0
  libmwaw::DebugStream f;
1118
0
  std::set<int> biblioIds;
1119
0
  for (auto const &dataIt : zone.m_idToEntryMap) {
1120
0
    auto const &id=dataIt.first;
1121
0
    auto const &entry=dataIt.second;
1122
0
    input->seek(entry.begin()+10, librevenge::RVNG_SEEK_SET);
1123
0
    f.str("");
1124
0
    f << "Entries(Frame)[Z" << zone.m_id << "]:";
1125
1126
0
    int val;
1127
0
    if (id==0) {
1128
0
      f << "none,";
1129
0
      if (entry.length()!=10) {
1130
0
        MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame[empty]: find some data\n"));
1131
0
        f << "###";
1132
0
      }
1133
0
    }
1134
0
    else if (biblioIds.find(id)!=biblioIds.end()) {
1135
0
      f << "biblio,id=" << id << ",";
1136
0
      if (entry.length()<10+58) {
1137
0
        MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame[biblio]: the entry seems too short\n"));
1138
0
        f << "###";
1139
0
      }
1140
0
      else {
1141
        // f2=1,f3=1,f7=5,f14=2,f15=0 => 9 data
1142
0
        for (int i=0; i<16; ++i) {
1143
0
          val=int(input->readLong(2));
1144
0
          int const expected[]= { 5, 0x275, 0, 0, 1, 0, 4, 2, 0, 0, 0, 0, 0, 0, 1, 2 };
1145
0
          if (val!=expected[i])
1146
0
            f << "f" << i << "=" << val << ",";
1147
0
        }
1148
0
        f << "IDS=[";
1149
0
        for (int i=0; i<4; ++i) f << std::hex << input->readULong(4) << std::dec << ",";
1150
0
        f << "],";
1151
0
        val=int(input->readLong(2));
1152
0
        if (val) f << "g0=" << val << ",";
1153
        // a list of strings, the number probably depends on some f_i values...
1154
        // there are added at the end of the files
1155
0
        auto &fontConverter=getFontConverter();
1156
0
        librevenge::RVNGString finalText;
1157
0
        while (input->tell()<entry.end()) {
1158
0
          long actPos=input->tell();
1159
0
          librevenge::RVNGString text;
1160
0
          bool endFound=false;
1161
0
          while (input->tell()<entry.end()) {
1162
0
            char c=char(input->readLong(1));
1163
0
            if (!c) {
1164
0
              endFound=true;
1165
0
              break;
1166
0
            }
1167
0
            else if (c!=0x9 && c<0x1f)
1168
0
              break;
1169
0
            int unicode=fontConverter->unicode(12, (unsigned char) c);
1170
0
            if (unicode!=-1)
1171
0
              libmwaw::appendUnicode(uint32_t(unicode),text);
1172
0
            else
1173
0
              text.append(c);
1174
0
          }
1175
0
          if (!endFound) {
1176
0
            ascii().addDelimiter(actPos, '|');
1177
0
            MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame: can not find text\n"));
1178
0
            f << "###";
1179
0
            break;
1180
0
          }
1181
0
          if (text.empty())
1182
0
            continue;
1183
0
          f << text.cstr() << ",";
1184
0
          if (!finalText.empty())
1185
0
            finalText.append(", ");
1186
0
          finalText.append(text);
1187
0
        }
1188
0
        if (!finalText.empty())
1189
0
          zone.m_idToFrameBiblioMap[id]=finalText;
1190
0
      }
1191
0
    }
1192
0
    else {
1193
0
      if (id!=1)
1194
0
        f << "id=" << id << ",";
1195
0
      if (entry.length()<10+4) {
1196
0
        MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame[main]: the size seems bad\n"));
1197
0
        f << "###";
1198
0
      }
1199
0
      else {
1200
0
        int zType=int(input->readLong(2));
1201
0
        switch (zType) {
1202
0
        case 2: {
1203
0
          f << "list,";
1204
0
          if (entry.length()<10+16) {
1205
0
            MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame[main-2]: the size seems bad\n"));
1206
0
            f << "###";
1207
0
            break;
1208
0
          }
1209
0
          int page=0; // 0: means background?
1210
0
          for (int i=0; i<6; ++i) { // f2=0-1
1211
0
            val=int(input->readLong(2));
1212
0
            if (!val) continue;
1213
0
            if (i==0)
1214
0
              f << "next=" << val << ",";
1215
0
            else if (i==1)
1216
0
              f << "prev=" << val << ",";
1217
0
            else if (i==2) {
1218
0
              page=val;
1219
0
              f << "page=" << val << ",";
1220
0
            }
1221
0
            else
1222
0
              f << "f" << i << "=" << val << ",";
1223
0
          }
1224
0
          int n=int(input->readLong(2));
1225
0
          f << "n=" << n << ",";
1226
0
          if (n<0 || (entry.length()-26)/28<n) {
1227
0
            MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame[main-2]: the number of sub zone seems bad\n"));
1228
0
            f << "###";
1229
0
            break;
1230
0
          }
1231
0
          libmwaw::DebugStream f2;
1232
0
          for (int i=0; i<n; ++i) {
1233
0
            long pos=input->tell();
1234
0
            f2.str("");
1235
0
            f2 << "Frame-F" << i << ":";
1236
0
            StudentWritingCParserInternal::FrameStruct frame(2);
1237
0
            frame.m_page=page;
1238
0
            int cId=int(input->readLong(2));
1239
0
            int type=int(input->readLong(2));
1240
0
            frame.m_id=std::make_pair(cId,type);
1241
0
            if (cId) {
1242
0
              f2 << "content=Z" << cId << ":" << type << ",";
1243
0
              if (!m_state->checkIfZone(cId, type)) {
1244
0
                MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame: unexpected id/type\n"));
1245
0
                f2 << "###";
1246
0
              }
1247
0
            }
1248
0
            float dim[4];
1249
0
            for (auto &d : dim) d=float(input->readLong(2))/14; // unit ?
1250
0
            frame.m_boxes[0]=MWAWBox2f(MWAWVec2f(dim[0],dim[1]),MWAWVec2f(dim[2],dim[3]));
1251
0
            f2 << "box=" << frame.m_boxes[0] << ",";
1252
0
            for (auto &d : dim) d=float(input->readLong(2));
1253
0
            frame.m_boxes[1]=MWAWBox2f(MWAWVec2f(dim[0],dim[1]),MWAWVec2f(dim[2],dim[3]));
1254
0
            f2 << "box2=" << frame.m_boxes[1] << ",";
1255
0
            for (int j=0; j<2; ++j) { // 0|1
1256
0
              val=int(input->readLong(2));
1257
0
              if (val)
1258
0
                f2 << "f" << i << "=" << val << ",";
1259
0
            }
1260
0
            val=int(input->readULong(2));
1261
0
            f2 << "border=[";
1262
0
            if (val==0)
1263
0
              f << "none,";
1264
0
            else {
1265
0
              if (val&0x80) f2 << "L";
1266
0
              if (val&0x100) f2 << "T";
1267
0
              if (val&0x200) f2 << "R";
1268
0
              if (val&0x400) f2 << "B";
1269
0
              f2 << ":";
1270
0
              if ((val&0x7)!=1) f2 << "style=" << (val&0x7) << ",";
1271
0
              if (((val>>3)&0xf)!=0) f2 << "color=" << ((val>>3)&0xf) << ","; // the color are not in the same order than text color, 0=black, 1=yellow, 2=red .. 15=white
1272
0
              if (val&0x800) f2 << "shade:"; // background=gray
1273
0
              val&=0xf000;
1274
0
              if (val) f2 << "fl=" << std::hex << val << std::dec << ",";
1275
0
            }
1276
0
            f2 << "],";
1277
0
            val=int(input->readLong(2)); // 0
1278
0
            if (val) f2 << "f2=" << val << ",";
1279
0
            ascii().addPos(pos);
1280
0
            ascii().addNote(f2.str().c_str());
1281
0
            input->seek(pos+28, librevenge::RVNG_SEEK_SET);
1282
0
            zone.m_frames.push_back(frame);
1283
0
          }
1284
0
          if (input->tell()<entry.end()) {
1285
0
            ascii().addPos(input->tell());
1286
0
            ascii().addNote("_");
1287
0
          }
1288
0
          break;
1289
0
        }
1290
0
        case 3: {
1291
0
          f << "note,";
1292
0
          if (entry.length()<10+16) {
1293
0
            MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame[note]: the zone seems too short\n"));
1294
0
            f << "###,";
1295
0
            break;
1296
0
          }
1297
0
          int cId=int(input->readLong(2));
1298
0
          int type=int(input->readLong(2));
1299
0
          if (cId) {
1300
0
            f << "Z" << cId << ":" << type << ",";
1301
0
            if (!m_state->checkIfZone(cId, type)) {
1302
0
              MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame: unexpected type\n"));
1303
0
              f << "###";
1304
0
            }
1305
0
          }
1306
0
          for (int i=0; i<6; ++i) { // f1: small number
1307
0
            val=int(input->readLong(2));
1308
0
            if (!val) continue;
1309
0
            f << "f" << i << "=" << val << ",";
1310
0
          }
1311
0
          zone.m_idToFrameNoteMap[id]=std::make_pair(cId,type);
1312
0
          break;
1313
0
        }
1314
0
        case 5: {
1315
0
          f << "biblio,";
1316
0
          int n=int(input->readLong(2));
1317
0
          if (n<0 || entry.length()<10+4+2*n) {
1318
0
            MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame[biblio]: can not find the number of id\n"));
1319
0
            f << "###n=" << n << ",";
1320
0
            break;
1321
0
          }
1322
0
          f << "ids=[";
1323
0
          for (int i=0; i<n; ++i) {
1324
0
            val=int(input->readLong(2));
1325
0
            biblioIds.insert(val);
1326
0
            f << val << ",";
1327
0
          }
1328
0
          f << "],";
1329
0
          break;
1330
0
        }
1331
0
        case 6: {
1332
0
          int n=int(input->readLong(2));
1333
0
          f << "n=" << n << ",";
1334
0
          if (entry.length()<10+8+6*n) {
1335
0
            MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame[main:6]: the entry seems too short\n"));
1336
0
            f << "###";
1337
0
            break;
1338
0
          }
1339
0
          for (int i=0; i<2; ++i) {
1340
0
            val=int(input->readLong(2));
1341
0
            if (val)
1342
0
              f << "f" << i << "=" << val << ",";
1343
0
          }
1344
0
          if (!zone.m_frameDates.empty()) {
1345
0
            MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame[main:6]: oops, we have already found a date list\n"));
1346
0
            f << "###";
1347
0
          }
1348
0
          f << "dates=[";
1349
0
          for (int i=0; i<n; ++i) {
1350
0
            f << "[";
1351
0
            std::array<int, 3> date; // year,month,day
1352
0
            for (size_t j=0; j<3; ++j) date[j]=int(input->readLong(j==0 ? 2 : 1));
1353
0
            f << date[0] << "/" << date[1] << "/" << date[2] << ",";
1354
0
            val=int(input->readLong(2));
1355
0
            if (val) f << "hours?=" << std::hex << val << std::dec << ",";
1356
0
            f << "],";
1357
0
            zone.m_frameDates.push_back(date);
1358
0
          }
1359
0
          f << "],";
1360
0
          break;
1361
0
        }
1362
0
        default:
1363
0
          MWAW_DEBUG_MSG(("StudentWritingCParser::readFrame[main:%d]: unknown type\n", zType));
1364
0
          f << "##zType=" << zType << ",";
1365
0
          break;
1366
0
        }
1367
0
      }
1368
0
    }
1369
0
    ascii().addPos(entry.begin());
1370
0
    ascii().addNote(f.str().c_str());
1371
0
  }
1372
0
  return true;
1373
0
}
1374
1375
bool StudentWritingCParser::readParagraph(StudentWritingCParserInternal::Zone &zone)
1376
0
{
1377
0
  auto input=getInput();
1378
0
  if (!input || zone.m_type!=4) {
1379
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::readParagraph: called will incorrect zone type\n"));
1380
0
    return false;
1381
0
  }
1382
0
  libmwaw::DebugStream f;
1383
0
  std::set<int> cIds;
1384
0
  bool first=true;
1385
0
  std::map<int, MWAWParagraph> &idToPara=zone.m_idToParagraphMap;
1386
0
  for (auto const &dataIt : zone.m_idToEntryMap) {
1387
0
    auto const &id=dataIt.first;
1388
0
    auto const &entry=dataIt.second;
1389
0
    input->seek(entry.begin()+10, librevenge::RVNG_SEEK_SET);
1390
0
    f.str("");
1391
0
    f << "Entries(Paragraph)[Z" << zone.m_id << "]:";
1392
0
    if (first) {
1393
0
      f << "id=" << id << ",";
1394
      // normally, begin with id=1 but find some case where the first id=6 and data in id=7
1395
0
      f << "header,";
1396
0
      if (entry.length()<10+4) {
1397
0
        MWAW_DEBUG_MSG(("StudentWritingCParser::readParagraph: the first entry seems bad\n"));
1398
0
        f << "###";
1399
0
      }
1400
0
      else {
1401
0
        int N[2];
1402
0
        for (int i=0; i<2; ++i) {
1403
0
          N[i]=int(input->readLong(2));
1404
0
          if (N[i]==0)
1405
0
            continue;
1406
0
          if (i==0)
1407
0
            f << "N=" << N[i] << ",";
1408
0
          else {
1409
0
            MWAW_DEBUG_MSG(("StudentWritingCParser::readParagraph: find unknown value for N1\n"));
1410
0
            f << "N1=" << N[i] << ",###";
1411
0
          }
1412
0
        }
1413
0
        if (N[0]<0 || (entry.length()-14)/4<N[0]) {
1414
0
          MWAW_DEBUG_MSG(("StudentWritingCParser::readParagraph: the value for N0 seems bad\n"));
1415
0
          f << "###";
1416
0
          N[0]=0;
1417
0
        }
1418
0
        f << "zones=[";
1419
0
        for (int i=0; i<N[0]; ++i) {
1420
0
          f << "[";
1421
0
          f << std::hex << input->readULong(2) << std::dec << ','; // unknown big number
1422
0
          int cId=int(input->readLong(2));
1423
0
          cIds.insert(cId);
1424
0
          f << cId << ",";
1425
0
          f << "],";
1426
0
        }
1427
0
        f << "],";
1428
0
      }
1429
0
      first=false;
1430
0
    }
1431
0
    else if (cIds.find(id)==cIds.end() || entry.length()!=10+0x5a) {
1432
0
      f << "id=" << id << ",";
1433
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::readParagraph: find unexpected zone\n"));
1434
0
      f << "###";
1435
0
    }
1436
0
    else {
1437
0
      f << "P" << id << ",";
1438
0
      MWAWParagraph para;
1439
0
      para.m_marginsUnit=librevenge::RVNG_POINT;
1440
0
      for (int i=0; i<3; ++i) {
1441
0
        int val=int(input->readLong(2));
1442
0
        para.m_margins[i]=double(val)/20;
1443
0
      }
1444
0
      *para.m_margins[1]+=*para.m_margins[0];
1445
0
      int val=int(input->readLong(1)); // 0
1446
0
      if (val) f << "f0=" << val << ",";
1447
0
      val=int(input->readULong(1)); // 0-3
1448
0
      switch (val&3) {
1449
0
      case 0:// left
1450
0
      default:
1451
0
        break;
1452
0
      case 1:
1453
0
        para.m_justify=MWAWParagraph::JustificationCenter;
1454
0
        break;
1455
0
      case 2:
1456
0
        para.m_justify=MWAWParagraph::JustificationFull;
1457
0
        break;
1458
0
      case 3:
1459
0
        para.m_justify=MWAWParagraph::JustificationRight;
1460
0
        break;
1461
0
      }
1462
0
      if (val&0xfc) f << "fl1=" << std::hex << (val&0xfc) << std::dec << ",";
1463
0
      val=int(input->readULong(1));
1464
0
      if (val&3)
1465
0
        para.setInterline(1+double(val&3)/2, librevenge::RVNG_PERCENT);
1466
0
      if (val&0xfc) f << "fl2=" << std::hex << (val&0xfc) << std::dec << ",";
1467
0
      int N=int(input->readLong(1));
1468
0
      if (N<0 || N>20) {
1469
0
        MWAW_DEBUG_MSG(("StudentWritingCParser::readParagraph: the number of tabs seems bad\n"));
1470
0
        f << "###N=" << N << ",";
1471
0
        N=0;
1472
0
      }
1473
0
      for (int i=0; i<N; ++i) {
1474
0
        MWAWTabStop tab;
1475
0
        val=int(input->readULong(2));
1476
0
        switch (val&3) {
1477
0
        case 0: // left
1478
0
        default:
1479
0
          break;
1480
0
        case 1:
1481
0
          tab.m_alignment=MWAWTabStop::CENTER;
1482
0
          break;
1483
0
        case 2:
1484
0
          tab.m_alignment=MWAWTabStop::RIGHT;
1485
0
          break;
1486
0
        case 3:
1487
0
          tab.m_alignment=MWAWTabStop::DECIMAL;
1488
0
          break;
1489
0
        }
1490
0
        tab.m_position=double(input->readLong(2))/20/72;
1491
0
        para.m_tabs->push_back(tab);
1492
0
        if (val&0xfffc)
1493
0
          f << "#tab" << i << "=" << std::hex << (val&0xfffc) << std::dec << ",";;
1494
0
      }
1495
0
      f << para << ",";
1496
0
      idToPara[id]=para;
1497
0
    }
1498
0
    if (input->tell()!=entry.end())
1499
0
      ascii().addDelimiter(input->tell(),'|');
1500
0
    ascii().addPos(entry.begin());
1501
0
    ascii().addNote(f.str().c_str());
1502
0
  }
1503
0
  return true;
1504
0
}
1505
1506
bool StudentWritingCParser::readPicture(StudentWritingCParserInternal::Zone &zone)
1507
0
{
1508
0
  auto input=getInput();
1509
0
  if (!input || zone.m_type!=6) {
1510
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::readPicture: called will incorrect zone type\n"));
1511
0
    return false;
1512
0
  }
1513
0
  libmwaw::DebugStream f;
1514
0
  std::set<int> pictIds;
1515
0
  for (auto const &dataIt : zone.m_idToEntryMap) {
1516
0
    auto const &id=dataIt.first;
1517
0
    auto const &entry=dataIt.second;
1518
0
    input->seek(entry.begin()+10, librevenge::RVNG_SEEK_SET);
1519
0
    f.str("");
1520
0
    f << "Entries(Picture)[Z" << zone.m_id << "]:id=" << id << ",";
1521
0
    switch (id) {
1522
0
    case 1: {
1523
0
      if (entry.length()!=10+30) {
1524
0
        MWAW_DEBUG_MSG(("StudentWritingCParser::readPicture: the first entry seems bad\n"));
1525
0
        f << "###";
1526
0
        break;
1527
0
      }
1528
0
      StudentWritingCParserInternal::PictureStruct pictEntry;
1529
0
      float fDim[4];
1530
0
      for (auto &d : fDim) d=float(input->readLong(2))/20; // checkme unit?
1531
0
      pictEntry.m_box=MWAWBox2f(MWAWVec2f(fDim[0],fDim[1]), MWAWVec2f(fDim[2],fDim[3]));
1532
0
      f << "box=" << pictEntry.m_box << ",";
1533
0
      for (int i=0; i<4; ++i) { // 0
1534
0
        int val=int(input->readLong(2));
1535
0
        if (val)
1536
0
          f << "f" << i << "=" << val << ",";
1537
0
      }
1538
0
      int dim[2];
1539
0
      for (auto &d : dim) d=int(input->readLong(2));
1540
0
      f << "res?=" << MWAWVec2i(dim[0],dim[1]) << ",";
1541
0
      int val=int(input->readULong(2)); // 0|10|12
1542
0
      if (val) f << "fl=" << std::hex << val << std::dec << ",";
1543
0
      val=int(input->readULong(2));
1544
0
      if (val!=2) f << "f4=" << val << ",";
1545
0
      int cIds[3];
1546
0
      for (int &cId : cIds) cId=int(input->readLong(2));
1547
0
      pictEntry.m_ids[0]=cIds[0];
1548
0
      pictEntry.m_ids[1]=cIds[2];
1549
0
      if (cIds[0]==zone.m_id && cIds[1]==6)
1550
0
        pictIds.insert(cIds[2]);
1551
0
      else {
1552
0
        MWAW_DEBUG_MSG(("StudentWritingCParser::readPicture: reading picture in other zone is not implemented\n"));
1553
0
        f << "###";
1554
0
      }
1555
0
      f << "Z" << cIds[0] << ":" << cIds[2] << ",";
1556
0
      zone.m_idToPictureMap[id]=pictEntry;
1557
0
      break;
1558
0
    }
1559
0
    default:
1560
0
      if (pictIds.find(id)==pictIds.end()) {
1561
0
        MWAW_DEBUG_MSG(("StudentWritingCParser::readPicture: find unknown id=%d\n", id));
1562
0
        f << "###";
1563
0
        break;
1564
0
      }
1565
0
      if (entry.length()<10+20) {
1566
0
        MWAW_DEBUG_MSG(("StudentWritingCParser::readPicture: the picture size seems to short\n"));
1567
0
        f << "###";
1568
0
        break;
1569
0
      }
1570
0
      std::shared_ptr<MWAWPict> pict(MWAWPictData::get(input, int(entry.length()-10)));
1571
0
      MWAWEmbeddedObject object;
1572
0
      if (pict && pict->getBinary(object) && !object.m_dataList.empty()) {
1573
0
        zone.m_idToObjectMap[id]=object;
1574
#ifdef DEBUG_WITH_FILES
1575
        static int volatile pictName = 0;
1576
        libmwaw::DebugStream f2;
1577
        f2 << "PICT-" << ++pictName << ".pct";
1578
        libmwaw::Debug::dumpFile(object.m_dataList[0], f2.str().c_str());
1579
        ascii().skipZone(entry.begin()+10, entry.end()-1);
1580
#endif
1581
0
      }
1582
0
      break;
1583
0
    }
1584
0
    if (input->tell()!=entry.end())
1585
0
      ascii().addDelimiter(input->tell(),'|');
1586
0
    ascii().addPos(entry.begin());
1587
0
    ascii().addNote(f.str().c_str());
1588
0
  }
1589
0
  return true;
1590
0
}
1591
1592
bool StudentWritingCParser::readFontsList(MWAWEntry const &entry)
1593
0
{
1594
0
  MWAWInputStreamPtr input = getInput();
1595
0
  long endPos=entry.end();
1596
0
  if (entry.length()<10+36 || !input->checkPosition(endPos)) {
1597
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::readFontsList: the entry seems too short\n"));
1598
0
    return false;
1599
0
  }
1600
0
  input->seek(entry.begin()+10, librevenge::RVNG_SEEK_SET);
1601
0
  libmwaw::DebugStream f;
1602
0
  f << "Entries(Fonts):";
1603
0
  bool isMain=true;
1604
0
  if (entry.id()!=m_state->m_ids[1].first) {
1605
0
    static bool first=true;
1606
0
    if (first) {
1607
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::readFontsList: find multiple fonts list zones\n"));
1608
0
      first=false;
1609
0
    }
1610
0
    f << "zone=Z" << entry.id() << ",";
1611
0
    isMain=false;
1612
0
  }
1613
0
  int val=int(input->readLong(2));
1614
0
  if (val!=0x14)
1615
0
    f << "f0=" << val << ",";
1616
0
  int N=int(input->readLong(2));
1617
0
  f << "N=" << N << ",";
1618
0
  if ((entry.length()-10-36)/34<N || N<0) {
1619
0
    f << "###";
1620
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::readFontsList: can not read the number of entries\n"));
1621
0
    ascii().addPos(entry.begin());
1622
0
    ascii().addNote(f.str().c_str());
1623
0
    return true;
1624
0
  }
1625
0
  for (int i=0; i<16; ++i) {
1626
0
    val=int(input->readLong(2));
1627
0
    if (val)
1628
0
      f << "f" << i+1 << "=" << val << ",";
1629
0
  }
1630
0
  ascii().addPos(entry.begin());
1631
0
  ascii().addNote(f.str().c_str());
1632
1633
0
  for (int i=0; i<N; ++i) {
1634
0
    long pos=input->tell();
1635
0
    f.str("");
1636
0
    f << "Fonts-FN" << i << ":";
1637
0
    f << "f0=" << input->readLong(2) << ","; // unsure, number between -500 and 600, can be dupplicated in the same files
1638
0
    std::string name;
1639
0
    for (int j=0; j<32; ++j) {
1640
0
      char c=char(input->readLong(1));
1641
0
      if (!c)
1642
0
        break;
1643
0
      name+=c;
1644
0
    }
1645
0
    if (!name.empty() && isMain)
1646
0
      m_state->m_idToFontNameMap[i]=name;
1647
0
    f << name << ",";
1648
0
    input->seek(pos+34, librevenge::RVNG_SEEK_SET);
1649
0
    ascii().addPos(pos);
1650
0
    ascii().addNote(f.str().c_str());
1651
0
  }
1652
1653
0
  if (input->tell()!=endPos) {
1654
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::readFontsList: find extra data\n"));
1655
0
    ascii().addPos(input->tell());
1656
0
    ascii().addNote("Fonts-Extra:###");
1657
0
  }
1658
0
  return true;
1659
0
}
1660
1661
////////////////////////////////////////////////////////////
1662
// read the print info
1663
////////////////////////////////////////////////////////////
1664
bool StudentWritingCParser::readPrintInfo()
1665
2
{
1666
2
  MWAWInputStreamPtr input = getInput();
1667
2
  long pos = input->tell();
1668
2
  if (!input->checkPosition(pos+0x78)) {
1669
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::readPrintInfo: the entry seems too short\n"));
1670
0
    return false;
1671
0
  }
1672
2
  libmwaw::DebugStream f;
1673
  // print info
1674
2
  libmwaw::PrinterInfo info;
1675
2
  if (!info.read(input)) return false;
1676
0
  f << "Entries(PrintInfo):"<< info;
1677
1678
0
  MWAWVec2i paperSize = info.paper().size();
1679
0
  MWAWVec2i pageSize = info.page().size();
1680
0
  if (pageSize.x() <= 0 || pageSize.y() <= 0 ||
1681
0
      paperSize.x() <= 0 || paperSize.y() <= 0) {
1682
0
    f << "###";
1683
0
    ascii().addPos(pos);
1684
0
    ascii().addNote(f.str().c_str());
1685
0
    return true;
1686
0
  }
1687
1688
  // define margin from print info
1689
0
  MWAWVec2i lTopMargin= -1 * info.paper().pos(0);
1690
0
  MWAWVec2i rBotMargin=info.paper().size() - info.page().size();
1691
1692
  // move margin left | top
1693
0
  int decalX = lTopMargin.x() > 14 ? lTopMargin.x()-14 : 0;
1694
0
  int decalY = lTopMargin.y() > 14 ? lTopMargin.y()-14 : 0;
1695
0
  lTopMargin -= MWAWVec2i(decalX, decalY);
1696
0
  rBotMargin += MWAWVec2i(decalX, decalY);
1697
1698
  // decrease right | bottom
1699
0
  int rightMarg = rBotMargin.x() -50;
1700
0
  if (rightMarg < 0) rightMarg=0;
1701
0
  int botMarg = rBotMargin.y() -50;
1702
0
  if (botMarg < 0) botMarg=0;
1703
1704
0
  getPageSpan().setMarginTop(lTopMargin.y()/72.0);
1705
0
  getPageSpan().setMarginBottom(botMarg/72.0);
1706
0
  getPageSpan().setMarginLeft(lTopMargin.x()/72.0);
1707
0
  getPageSpan().setMarginRight(rightMarg/72.0);
1708
0
  getPageSpan().setFormLength(paperSize.y()/72.);
1709
0
  getPageSpan().setFormWidth(paperSize.x()/72.);
1710
1711
0
  if (input->tell()!=pos+0x78)
1712
0
    ascii().addDelimiter(input->tell(),'|');
1713
0
  ascii().addPos(pos);
1714
0
  ascii().addNote(f.str().c_str());
1715
1716
0
  return true;
1717
0
}
1718
1719
////////////////////////////////////////////////////////////
1720
//
1721
// Low level
1722
//
1723
////////////////////////////////////////////////////////////
1724
1725
////////////////////////////////////////////////////////////
1726
// read the header
1727
////////////////////////////////////////////////////////////
1728
bool StudentWritingCParser::checkHeader(MWAWHeader *header, bool /*strict*/)
1729
206
{
1730
206
  *m_state = StudentWritingCParserInternal::State();
1731
1732
206
  MWAWInputStreamPtr input = getInput();
1733
206
  if (!input || !input->hasDataFork())
1734
0
    return false;
1735
1736
206
  if (!input->checkPosition(352+120)) {
1737
86
    MWAW_DEBUG_MSG(("StudentWritingCParser::checkHeader: file is too short\n"));
1738
86
    return false;
1739
86
  }
1740
120
  input->seek(0,librevenge::RVNG_SEEK_SET);
1741
120
  if (input->readULong(4) != 0x1a544c43)
1742
0
    return false;
1743
120
  int val=int(input->readULong(1));
1744
120
#ifndef DEBUG
1745
120
  if (val<2 || val>3)
1746
0
    return false;
1747
#else
1748
  if (val>5) {
1749
    MWAW_DEBUG_MSG(("StudentWritingCParser::checkHeader: unexpected document type\n"));
1750
    return false;
1751
  }
1752
#endif
1753
120
  if (input->readULong(2) != 0x4646)
1754
0
    return false;
1755
1756
120
  ascii().addPos(0);
1757
120
  ascii().addNote("FileHeader:");
1758
1759
120
  if (header)
1760
66
    header->reset(MWAWDocument::MWAW_T_STUDENTWRITING, 1);
1761
1762
120
  return true;
1763
120
}
1764
1765
////////////////////////////////////////////////////////////
1766
// send data
1767
////////////////////////////////////////////////////////////
1768
bool StudentWritingCParser::sendZone(int id)
1769
0
{
1770
0
  if (m_state->m_sendZoneSet.find(id)!=m_state->m_sendZoneSet.end()) {
1771
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendZone: oops, find a loop for zone %d\n", id));
1772
0
    return false;
1773
0
  }
1774
1775
0
  auto input=getInput();
1776
0
  auto listener=getTextListener();
1777
0
  if (!input || !listener) {
1778
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendZone: called with no listener\n"));
1779
0
    return false;
1780
0
  }
1781
1782
0
  auto const &it=m_state->m_idToZoneMap.find(id);
1783
0
  if (it==m_state->m_idToZoneMap.end() || !it->second) {
1784
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendZone: unknown zone=%d\n", id));
1785
0
    return false;
1786
0
  }
1787
1788
0
  auto const &zone=*it->second;
1789
0
  if (zone.m_type!=5) {
1790
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendZone: sending a zone with type=%d is not implemented\n", zone.m_type));
1791
0
    return false;
1792
0
  }
1793
0
  bool isMainZone=id==m_state->m_ids[0].first;
1794
0
  if (zone.m_idToDataMap.find(1)==zone.m_idToDataMap.end()) {
1795
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendZone: can not find the main sub zone\n"));
1796
0
    return false;
1797
0
  }
1798
1799
0
  m_state->m_sendZoneSet.insert(id);
1800
0
  auto const &mainData=zone.m_idToDataMap.find(1)->second;
1801
  //
1802
  // background picture
1803
  //
1804
0
  if (mainData.m_ids[8].first) {
1805
0
    auto const &pictIt=m_state->m_idToZoneMap.find(mainData.m_ids[8].first);
1806
0
    if (pictIt==m_state->m_idToZoneMap.end() || !pictIt->second || pictIt->second->m_type!=6) {
1807
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::sendZone[background]: can not find picture=%d\n", mainData.m_ids[8].first));
1808
0
    }
1809
0
    else if (!m_state->m_sendBoxesStack.empty()) {
1810
      // FIXME: the text hides the picture...
1811
0
      MWAWPosition pos(MWAWVec2f(0,0), m_state->m_sendBoxesStack.top().size(), librevenge::RVNG_POINT);
1812
0
      pos.setRelativePosition(MWAWPosition::Frame);
1813
0
      pos.m_wrapping = MWAWPosition::WBackground;
1814
1815
0
      sendPicture(pos, mainData.m_ids[8].first);
1816
0
    }
1817
0
    else {
1818
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::sendZone: oops, sending background picture is not implemented\n"));
1819
0
    }
1820
0
  }
1821
  //
1822
  // FRAMES
1823
  //
1824
0
  auto const &frameIt=m_state->m_idToZoneMap.find(mainData.m_ids[1].first);
1825
0
  if (frameIt==m_state->m_idToZoneMap.end() || !frameIt->second || frameIt->second->m_type!=3) {
1826
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendZone: can not find the frame zone=%d\n", mainData.m_ids[1].first));
1827
0
  }
1828
0
  else {
1829
0
    auto const &frames=*frameIt->second;
1830
0
    for (auto const &frame : frames.m_frames) {
1831
0
      MWAWPosition pos(frame.m_boxes[0][0], frame.m_boxes[0].size(), librevenge::RVNG_POINT);
1832
0
      pos.setRelativePosition(isMainZone ? MWAWPosition::Page : MWAWPosition::Frame);
1833
0
      if (isMainZone && frame.m_page)
1834
0
        pos.setPage(frame.m_page+(mainData.m_ids[7].first ? 1 : 0));
1835
0
      pos.m_wrapping = MWAWPosition::WDynamic;
1836
1837
0
      if (frame.m_id.second==5) {
1838
0
        m_state->m_sendBoxesStack.push(frame.m_boxes[0]);
1839
0
        auto subdoc=std::make_shared<StudentWritingCParserInternal::SubDocument>(*this, input,frame.m_id.first);
1840
0
        listener->insertTextBox(pos, subdoc);
1841
0
        m_state->m_sendBoxesStack.pop();
1842
0
      }
1843
0
      else if (frame.m_id.second==6)
1844
0
        sendPicture(pos, frame.m_id.first);
1845
0
      else {
1846
0
        MWAW_DEBUG_MSG(("StudentWritingCParser::sendZone: find unexpected sub zone type=%d\n", frame.m_id.second));
1847
0
      }
1848
0
    }
1849
0
  }
1850
0
  if (mainData.m_ids[7].first) {
1851
    // we need to retrieve the title page
1852
0
    auto const &titleIt=m_state->m_idToZoneMap.find(mainData.m_ids[7].first);
1853
0
    if (titleIt==m_state->m_idToZoneMap.end() || !titleIt->second || titleIt->second->m_type!=1) {
1854
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::sendZone[background]: can not find title page=%d\n", mainData.m_ids[7].first));
1855
0
    }
1856
0
    else {
1857
0
      sendText(*titleIt->second, zone, id==m_state->m_ids[0].first);
1858
0
      listener->insertBreak(MWAWListener::PageBreak);
1859
0
    }
1860
0
  }
1861
1862
0
  auto const &pageIt=m_state->m_idToZoneMap.find(mainData.m_ids[6].first);
1863
0
  if (pageIt==m_state->m_idToZoneMap.end() || !pageIt->second || pageIt->second->m_type!=1) {
1864
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendZone[background]: can not find text page=%d\n", mainData.m_ids[6].first));
1865
0
    return false;
1866
0
  }
1867
0
  bool result=sendText(*pageIt->second, zone, id==m_state->m_ids[0].first);
1868
1869
0
  if (id==m_state->m_ids[0].first && frameIt!=m_state->m_idToZoneMap.end() &&
1870
0
      !frameIt->second->m_idToFrameBiblioMap.empty()) {
1871
0
    listener->insertBreak(MWAWListener::PageBreak);
1872
    // reset to default
1873
0
    listener->setFont(MWAWFont());
1874
0
    MWAWParagraph para;
1875
0
    para.m_justify=MWAWParagraph::JustificationCenter;
1876
0
    listener->setParagraph(para);
1877
0
    listener->insertUnicodeString("Bibliography");
1878
0
    listener->insertEOL();
1879
0
    para.m_justify=MWAWParagraph::JustificationLeft;
1880
0
    listener->setParagraph(para);
1881
0
    listener->insertEOL();
1882
0
    for (auto const &biblioIt : frameIt->second->m_idToFrameBiblioMap) {
1883
0
      listener->insertUnicode(0x2022);
1884
0
      listener->insertChar(' ');
1885
0
      listener->insertUnicodeString(biblioIt.second);
1886
0
      listener->insertChar('.');
1887
0
      listener->insertEOL();
1888
0
    }
1889
0
  }
1890
0
  m_state->m_sendZoneSet.erase(id);
1891
0
  return result;
1892
0
}
1893
1894
bool StudentWritingCParser::sendPicture(MWAWPosition const &pos, int id)
1895
0
{
1896
0
  auto listener=getTextListener();
1897
0
  auto it=m_state->m_idToZoneMap.find(id);
1898
0
  if (!listener || it==m_state->m_idToZoneMap.end() || !it->second || it->second->m_type!=6) {
1899
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendPicture: can not find picture %d\n", id));
1900
0
    return false;
1901
0
  }
1902
0
  auto const *zone=it->second.get();
1903
0
  auto const &pIt=zone->m_idToPictureMap.find(1);
1904
0
  if (pIt==zone->m_idToPictureMap.end()) {
1905
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendPicture: can not find the picture header for id=%d\n", id));
1906
0
    return false;
1907
0
  }
1908
1909
0
  int ids[]= {pIt->second.m_ids[0], pIt->second.m_ids[1]};
1910
0
  if (ids[0]!=id) {
1911
0
    it=m_state->m_idToZoneMap.find(ids[0]);
1912
0
    if (it==m_state->m_idToZoneMap.end() || !it->second || it->second->m_type!=6)  {
1913
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::sendPicture: can not find the picture final zone %d\n", ids[0]));
1914
0
      return false;
1915
0
    }
1916
0
    zone=it->second.get();
1917
0
  }
1918
0
  auto const &objIt=zone->m_idToObjectMap.find(ids[1]);
1919
0
  if (objIt==zone->m_idToObjectMap.end()) {
1920
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendPicture: can not find the embedded picture in zone %d[%d]\n", ids[0], ids[1]));
1921
0
    return false;
1922
0
  }
1923
0
  listener->insertPicture(pos,objIt->second);
1924
0
  return true;
1925
0
}
1926
1927
bool StudentWritingCParser::sendText(StudentWritingCParserInternal::Zone const &textZone,
1928
                                     StudentWritingCParserInternal::Zone const &zone, bool isMain)
1929
0
{
1930
0
  auto input=getInput();
1931
0
  auto listener=getTextListener();
1932
0
  if (!input || !listener || zone.m_type!=5 || textZone.m_type!=1 || zone.m_idToDataMap.find(1)==zone.m_idToDataMap.end()) {
1933
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: called with bad data\n"));
1934
0
    return false;
1935
0
  }
1936
1937
0
  auto const &mainData=zone.m_idToDataMap.find(1)->second;
1938
1939
  // retrieve the paragraph zone
1940
0
  auto const &paraIt=m_state->m_idToZoneMap.find(mainData.m_ids[2].first);
1941
0
  std::map<int, MWAWParagraph> const *paraIdMap=nullptr;
1942
0
  if (paraIt==m_state->m_idToZoneMap.end() || !paraIt->second || paraIt->second->m_type!=4) {
1943
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: can not find the para zone=%d\n", mainData.m_ids[2].first));
1944
0
  }
1945
0
  else
1946
0
    paraIdMap=&paraIt->second->m_idToParagraphMap;
1947
  // retrieve the list of sub zone
1948
0
  auto const &subIt=m_state->m_idToZoneMap.find(mainData.m_ids[0].first);
1949
0
  std::map<int, StudentWritingCParserInternal::PageStruct const *> pageLimits;
1950
0
  if (subIt==m_state->m_idToZoneMap.end() || !subIt->second || subIt->second->m_type!=2) {
1951
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: can not find the sub zone=%d\n", mainData.m_ids[0].first));
1952
0
  }
1953
0
  else {
1954
0
    for (auto const &p : subIt->second->m_idToPageMap)
1955
0
      pageLimits[p.second.m_firstChar] = &p.second;
1956
0
  }
1957
  // retrieve the note entries, dates
1958
0
  auto const &frameIt=m_state->m_idToZoneMap.find(mainData.m_ids[1].first);
1959
0
  std::vector<std::array<int, 3> > dates;
1960
0
  std::map<int, StudentWritingCParserInternal::ZoneEntry> const *idToFrameNoteMap=nullptr;
1961
0
  if (frameIt==m_state->m_idToZoneMap.end() || !frameIt->second || frameIt->second->m_type!=3) {
1962
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: can not find the frame zone=%d\n", mainData.m_ids[1].first));
1963
0
  }
1964
0
  else {
1965
0
    dates=frameIt->second->m_frameDates;
1966
0
    idToFrameNoteMap=&frameIt->second->m_idToFrameNoteMap;
1967
0
  }
1968
1969
0
  auto const &mainIt=textZone.m_idToEntryMap.find(1);
1970
0
  if (mainIt==textZone.m_idToEntryMap.end() || !mainIt->second.valid() || mainIt->second.length()<10+10) {
1971
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: can not find the main zone\n"));
1972
0
    return false;
1973
0
  }
1974
0
  auto const &entry=mainIt->second;
1975
0
  input->seek(entry.begin()+10+2, librevenge::RVNG_SEEK_SET);
1976
1977
0
  int begPos[2];
1978
0
  int N[2];
1979
0
  for (int i=0; i<2; ++i) {
1980
0
    begPos[i]=int(input->readULong(2));
1981
0
    N[i]=int(input->readLong(2));
1982
0
    if (begPos[i]<10 || N[i]<0 || (entry.length()-10-begPos[i])/10<N[i] || begPos[i]+10+10*N[i]>entry.length()) {
1983
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: a sub zone seems bad\n"));
1984
0
      N[i]=0;
1985
0
    }
1986
0
  }
1987
1988
0
  input->seek(entry.begin()+10+begPos[0], librevenge::RVNG_SEEK_SET);
1989
0
  std::vector<std::array<int, 5> > textData;
1990
0
  std::array<int, 5> values; // text: zone[id], num[max], num[char], 0, 0
1991
0
  for (int n=0; n<N[0]; ++n) {
1992
0
    for (auto &v : values) v=int(input->readLong(2));
1993
0
    textData.push_back(values);
1994
0
  }
1995
1996
0
  input->seek(entry.begin()+10+begPos[1], librevenge::RVNG_SEEK_SET);
1997
0
  std::vector<std::array<int, 5> > styleData;
1998
  // style: zone[id], num[max], num[style], 0, num[char]
1999
0
  for (int n=0; n<N[1]; ++n) {
2000
0
    for (auto &v : values) v=int(input->readLong(2));
2001
0
    styleData.push_back(values);
2002
0
  }
2003
2004
  // first retrieve the styles data
2005
0
  std::vector<std::array<int, 3> > styles; // type, numChar, values
2006
0
  for (auto const &st : styleData) {
2007
0
    auto const &stIt=textZone.m_idToEntryMap.find(st[0]);
2008
0
    if (stIt==textZone.m_idToEntryMap.end() || !stIt->second.valid() || st[2]<0 || stIt->second.length()<10+6*st[2]) {
2009
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: can not find style zone=%d\n", st[0]));
2010
0
      break;
2011
0
    }
2012
0
    input->seek(stIt->second.begin()+10, librevenge::RVNG_SEEK_SET);
2013
0
    std::array<int, 3> data;
2014
0
    for (int i=0; i<st[2]; ++i) {
2015
0
      for (auto &d : data) d=int(input->readULong(2));
2016
0
      styles.push_back(data);
2017
0
    }
2018
0
  }
2019
2020
  // now retrieve the text
2021
0
  int actChar=0, actStyleChar=0;
2022
0
  std::array<int, 3> actDate= {{0,0,0}};
2023
0
  auto stIt=styles.begin();
2024
0
  MWAWFont font;
2025
0
  int numColumns=1;
2026
0
  for (auto const &txt : textData) {
2027
0
    auto const &txtIt=textZone.m_idToEntryMap.find(txt[0]);
2028
0
    if (txtIt==textZone.m_idToEntryMap.end() || !txtIt->second.valid() || txt[2]<0 || txtIt->second.length()<10+txt[2]) {
2029
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: can not find text zone=%d\n", txt[0]));
2030
0
      actChar=-1;
2031
0
      continue;
2032
0
    }
2033
0
    input->seek(txtIt->second.begin()+10, librevenge::RVNG_SEEK_SET);
2034
0
    for (int i=0; i<txt[2]; ++i) {
2035
0
      auto const &sectIt=pageLimits.find(actChar);
2036
0
      if (sectIt!=pageLimits.end() && sectIt->second) {
2037
0
        auto const &page=*sectIt->second;
2038
0
        if (page.m_numColumns!=numColumns && page.m_numColumns>=1) {
2039
0
          if (listener->isSectionOpened())
2040
0
            listener->closeSection();
2041
0
          numColumns=page.m_numColumns;
2042
0
          if (numColumns>1) {
2043
0
            MWAWSection section;
2044
            // CHANGEME: use margins, etc...
2045
0
            section.setColumns(numColumns, getPageSpan().getPageWidth()/double(numColumns), librevenge::RVNG_INCH);
2046
0
            listener->openSection(section);
2047
0
          }
2048
0
        }
2049
0
      }
2050
0
      bool isSpecialChar=false;
2051
0
      while (actStyleChar<=actChar && stIt!=styles.end()) {
2052
0
        auto data=*(stIt++);
2053
0
        if (data[0]==1) {
2054
0
          isSpecialChar=true; // with 0xb
2055
0
          if (!idToFrameNoteMap || idToFrameNoteMap->find(data[2])==idToFrameNoteMap->end()) {
2056
0
            MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: can not retrieve the note=%d\n", data[2]));
2057
0
          }
2058
0
          else {
2059
0
            MWAWSubDocumentPtr subdoc(new StudentWritingCParserInternal::SubDocument(*this, input, idToFrameNoteMap->find(data[2])->second.first));
2060
0
            listener->insertNote(MWAWNote(MWAWNote::EndNote), subdoc);
2061
0
          }
2062
0
        }
2063
0
        else if (data[0]==2) {
2064
0
          isSpecialChar=true; // with 0x7
2065
0
          if (data[2]>0 && data[2]<=int(dates.size()))
2066
0
            actDate=dates[size_t(data[2]-1)];
2067
0
          else {
2068
0
            if (data[2]) {
2069
0
              MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: can not retrieve the actual date=%d\n", data[2]));
2070
0
            }
2071
0
            actDate= {{0,0,0}};
2072
0
          }
2073
0
        }
2074
0
        else if (data[0]>=0x10 && data[0]<=0x18) {
2075
0
          uint32_t flags=font.flags();
2076
0
          switch (data[0]) {
2077
0
          case 0x10:
2078
0
          case 0x11:
2079
0
          case 0x15:
2080
0
          case 0x18: {
2081
0
            uint32_t fl=data[0]==0x10 ? MWAWFont::boldBit :
2082
0
                        data[0]==0x11 ? MWAWFont::italicBit :
2083
0
                        data[0]==0x15 ? MWAWFont::outlineBit : MWAWFont::shadowBit;
2084
0
            if (data[2]==1)
2085
0
              flags|=fl;
2086
0
            else if (data[2]==0)
2087
0
              flags&=uint32_t(~fl);
2088
0
            else {
2089
0
              MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: unexpected flag\n"));
2090
0
            }
2091
0
            break;
2092
0
          }
2093
0
          case 0x12:
2094
0
            if (data[2]==1)
2095
0
              font.setUnderlineStyle(MWAWFont::Line::Simple);
2096
0
            else if (data[2]==0)
2097
0
              font.setUnderlineStyle(MWAWFont::Line::None);
2098
0
            else {
2099
0
              MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: unexpected underline flag\n"));
2100
0
            }
2101
0
            break;
2102
0
          case 0x13: {
2103
0
            auto const &nameIt=m_state->m_idToFontNameMap.find(data[2]);
2104
0
            if (nameIt==m_state->m_idToFontNameMap.end()) {
2105
0
              MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: can not find font FN%d\n", data[2]));
2106
0
              break;
2107
0
            }
2108
0
            if (m_state->m_idToFontNameUsed.find(data[2])==m_state->m_idToFontNameUsed.end()) {
2109
0
              getFontConverter()->setCorrespondance(data[2], nameIt->second);
2110
0
              m_state->m_idToFontNameUsed.insert(data[2]);
2111
0
            }
2112
0
            font.setId(data[2]);
2113
0
            break;
2114
0
          }
2115
0
          case 0x14:
2116
0
            if (data[2]<=0) {
2117
0
              MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: the font size=%d seems bad\n", data[2]));
2118
0
              break;
2119
0
            }
2120
0
            font.setSize(float(data[2])/10);
2121
0
            break;
2122
0
          case 0x16: {
2123
0
            MWAWColor color;
2124
0
            if (m_state->getColor(data[2], color))
2125
0
              font.setColor(color);
2126
0
            else
2127
0
              font.setColor(MWAWColor::black());
2128
0
            break;
2129
0
          }
2130
0
          case 0x17:
2131
0
            switch (data[2]) {
2132
0
            case 0:
2133
0
              font.set(MWAWFont::Script());
2134
0
              break;
2135
0
            case 1:
2136
0
              font.set(MWAWFont::Script::super100());
2137
0
              break;
2138
0
            case 2:
2139
0
              font.set(MWAWFont::Script::sub100());
2140
0
              break;
2141
0
            default:
2142
0
              MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: unknown script=%d\n", data[2]));
2143
0
              break;
2144
0
            }
2145
0
            break;
2146
0
          default:
2147
0
            MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: unexpected type=%d\n", data[0]));
2148
0
            break;
2149
0
          }
2150
0
          font.setFlags(flags);
2151
0
          listener->setFont(font);
2152
0
        }
2153
0
        else if (data[0]>=0x20 && data[0]<=0x22) {
2154
0
          isSpecialChar=true; // 0xb
2155
0
          if (data[0]==0x20)
2156
0
            listener->insertField(MWAWField(MWAWField::PageNumber));
2157
0
          else if (data[0]==0x21) {
2158
0
            if (actDate[0]) { // fixme, use a real date field with a fixed date
2159
0
              std::stringstream s;
2160
0
              s << actDate[1] << "/" << actDate[2] << "/" << actDate[0]-1;
2161
0
              listener->insertUnicodeString(librevenge::RVNGString(s.str().c_str()));
2162
0
            }
2163
0
            else {
2164
0
              MWAWField date(MWAWField::Date);
2165
0
              date.m_DTFormat = "%a, %b %d, %Y";
2166
0
              listener->insertField(date);
2167
0
            }
2168
0
          }
2169
0
          else
2170
0
            listener->insertUnicode(0x2022);
2171
0
        }
2172
0
        else if (data[0]==0x100) {
2173
0
          if (paraIdMap) {
2174
0
            auto const &it=paraIdMap->find(data[2]);
2175
0
            if (it!=paraIdMap->end())
2176
0
              listener->setParagraph(it->second);
2177
0
          }
2178
0
          else {
2179
0
            MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: unknown paragraph id=%d\n", data[2]));
2180
0
          }
2181
0
        }
2182
0
        else if (data[0]==0x300) { // column break
2183
0
          if (data[2]>0 && data[2]<=numColumns)
2184
0
            listener->insertBreak(MWAWListener::ColumnBreak);
2185
0
        }
2186
0
        else if (data[0]==0x500) {
2187
0
          if (isMain)
2188
0
            listener->insertBreak(MWAWListener::PageBreak);
2189
0
        }
2190
0
        else {
2191
0
        }
2192
0
        actStyleChar+=data[1];
2193
0
      }
2194
0
      unsigned char c=(unsigned char)(input->readULong(1));
2195
0
      switch (c) {
2196
0
      case 0x9:
2197
0
        listener->insertTab();
2198
0
        break;
2199
0
      case 0xd:
2200
0
        listener->insertEOL();
2201
0
        break;
2202
0
      default:
2203
0
        if (c<0x1f) {
2204
0
          if (!isSpecialChar) {
2205
0
            MWAW_DEBUG_MSG(("StudentWritingCParser::sendText: find odd char c=%d\n", int(c)));
2206
0
          }
2207
0
        }
2208
0
        else
2209
0
          listener->insertCharacter(c);
2210
0
        break;
2211
0
      }
2212
0
      if (actChar>=0) ++actChar;
2213
0
    }
2214
0
  }
2215
0
  if (listener->isSectionOpened())
2216
0
    listener->closeSection();
2217
0
  return true;
2218
0
}
2219
2220
2221
////////////////////////////////////////////////////////////
2222
// decoder
2223
////////////////////////////////////////////////////////////
2224
namespace StudentWritingCParserInternal
2225
{
2226
/** a basic LWZ decoder
2227
2228
    \note this code is freely inspired from https://github.com/MichaelDipperstein/lzw GLP 3
2229
 */
2230
struct LWZDecoder {
2231
  static int const e_firstCode=(1<<8);
2232
  static int const e_maxCodeLen=14;
2233
  static int const e_maxCode=(1<<e_maxCodeLen);
2234
2235
  //! constructor
2236
  LWZDecoder(unsigned char const *data, unsigned long len)
2237
0
    : m_data(data)
2238
0
    , m_len(len)
2239
2240
0
    , m_pos(0)
2241
0
    , m_bit(0)
2242
0
    , m_dictionary()
2243
0
  {
2244
0
    initDictionary();
2245
0
  }
2246
2247
  //! decode data
2248
  bool decode(std::vector<unsigned char> &output);
2249
protected:
2250
  void initDictionary()
2251
0
  {
2252
0
    m_dictionary.resize(2); // 100 and 101
2253
0
    m_dictionary.reserve(e_maxCode - e_firstCode); // max table 4000
2254
0
  }
2255
2256
  unsigned getBit() const
2257
0
  {
2258
0
    if (m_pos>=m_len)
2259
0
      throw libmwaw::ParseException();
2260
0
    unsigned val=(m_data[m_pos]>>(7-m_bit++))&1;
2261
0
    if (m_bit==8) {
2262
0
      ++m_pos;
2263
0
      m_bit=0;
2264
0
    }
2265
0
    return val;
2266
0
  }
2267
  unsigned getCodeWord(unsigned codeLen) const
2268
0
  {
2269
0
    unsigned code=0;
2270
0
    for (unsigned i=0; i<codeLen;) {
2271
0
      if (m_bit==0 && (codeLen-i)>=8 && m_pos<m_len) {
2272
0
        code = (code<<8) | unsigned(m_data[m_pos++]);
2273
0
        i+=8;
2274
0
        continue;
2275
0
      }
2276
0
      code = (code<<1) | getBit();
2277
0
      ++i;
2278
0
    }
2279
0
    return code;
2280
0
  }
2281
2282
  struct LWZEntry {
2283
    //! constructor
2284
    LWZEntry(unsigned int prefixCode=0, unsigned char suffix=0)
2285
0
      : m_suffix(suffix)
2286
0
      , m_prefixCode(prefixCode)
2287
0
    {
2288
0
    }
2289
    /** last char in encoded string */
2290
    unsigned char m_suffix;
2291
    /** code for remaining chars in string */
2292
    unsigned int m_prefixCode;
2293
  };
2294
2295
  unsigned char decodeRec(unsigned int code, std::vector<unsigned char> &output)
2296
0
  {
2297
0
    unsigned char c;
2298
0
    unsigned char firstChar;
2299
2300
0
    if (code >= e_firstCode) {
2301
0
      if (code-e_firstCode >= m_dictionary.size()) {
2302
0
        MWAW_DEBUG_MSG(("StudentWritingCParserInternal::LWZDecoder::decodeRec: bad id=%x/%x\n", code, unsigned(m_dictionary.size())));
2303
0
        throw libmwaw::ParseException();
2304
0
      }
2305
      /* code word is string + c */
2306
0
      c = m_dictionary[code - e_firstCode].m_suffix;
2307
0
      code = m_dictionary[code - e_firstCode].m_prefixCode;
2308
2309
      /* evaluate new code word for remaining string */
2310
0
      firstChar = decodeRec(code, output);
2311
0
    }
2312
0
    else /* code word is just c */
2313
0
      firstChar = c = (unsigned char)code;
2314
2315
0
    output.push_back(c);
2316
0
    return firstChar;
2317
0
  }
2318
2319
  LWZDecoder(LWZDecoder const &)=delete;
2320
  LWZDecoder &operator=(LWZDecoder const &)=delete;
2321
  unsigned char const *m_data;
2322
  unsigned long m_len;
2323
  mutable unsigned long m_pos, m_bit;
2324
2325
  std::vector<LWZEntry> m_dictionary;
2326
};
2327
2328
bool LWZDecoder::decode(std::vector<unsigned char> &output)
2329
0
try
2330
0
{
2331
0
  unsigned int currentCodeLen = 9;
2332
0
  unsigned lastCode=0;
2333
0
  unsigned char c=(unsigned char) 0;
2334
0
  unsigned endDictCode=0x1ff;
2335
0
  bool first=true;
2336
0
  while (true) {
2337
0
    unsigned code=getCodeWord(currentCodeLen);
2338
0
    if (code==0x100) {
2339
0
      initDictionary();
2340
0
      currentCodeLen = 9;
2341
0
      endDictCode=0x1ff;
2342
2343
0
      lastCode=0;
2344
0
      c=(unsigned char) 0;
2345
0
      first=true;
2346
0
      continue;
2347
0
    }
2348
0
    if (code==0x101) { // end of code
2349
0
      if (m_pos+2<m_len) {
2350
0
        MWAW_DEBUG_MSG(("StudentWritingCParserInternal::LWZDecoder::decode: unexpected end at position %ld/%ld\n", m_pos, m_len));
2351
0
      }
2352
0
      break;
2353
0
    }
2354
0
    if (code < e_firstCode+m_dictionary.size())
2355
      /* we have a known code.  decode it */
2356
0
      c = decodeRec(code, output);
2357
0
    else {
2358
      /***************************************************************
2359
       * We got a code that's not in our dictionary.  This must be due
2360
       * to the string + char + string + char + string exception.
2361
       * Build the decoded string using the last character + the
2362
       * string from the last code.
2363
       ***************************************************************/
2364
0
      unsigned char tmp = c;
2365
0
      c = decodeRec(lastCode, output);
2366
0
      output.push_back(tmp);
2367
0
    }
2368
    /* if room, add new code to the dictionary */
2369
0
    if (!first && m_dictionary.size()+e_firstCode < e_maxCode-1) {
2370
0
      if (lastCode>=e_firstCode+m_dictionary.size()) {
2371
0
        MWAW_DEBUG_MSG(("StudentWritingCParserInternal::LWZDecoder::decode: oops a loop with %x/%x\n", lastCode, unsigned(m_dictionary.size())));
2372
0
        break;
2373
0
      }
2374
0
      m_dictionary.push_back(LWZEntry(lastCode, c));
2375
0
      if (m_dictionary.size()+e_firstCode>endDictCode) {
2376
0
        ++currentCodeLen;
2377
0
        endDictCode=2*endDictCode+1;
2378
0
      }
2379
0
    }
2380
2381
    /* save character and code for use in unknown code word case */
2382
0
    lastCode = code;
2383
0
    first=false;
2384
0
  }
2385
0
  return true;
2386
0
}
2387
0
catch (...)
2388
0
{
2389
0
  return false;
2390
0
}
2391
2392
}
2393
2394
MWAWInputStreamPtr StudentWritingCParser::decode()
2395
52
{
2396
52
  MWAWInputStreamPtr bad;
2397
52
  auto input=getInput();
2398
52
  if (m_state->m_isUncompressed)
2399
0
    return input;
2400
52
  long const begPos=0x1d8;
2401
52
  if (!input->checkPosition(begPos)) {
2402
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::decode: the file is too short\n"));
2403
0
    return bad;
2404
0
  }
2405
52
  std::set<long> listBeginPosition;
2406
52
  listBeginPosition.insert(input->size());
2407
52
  input->seek(begPos, librevenge::RVNG_SEEK_SET);
2408
52
  while (!input->isEnd()) {
2409
50
    long pos=input->tell();
2410
50
    if (!input->checkPosition(pos+18) || input->readLong(4)!=0x1a46461a) {
2411
50
      MWAW_DEBUG_MSG(("StudentWritingCParser::decode: oops code break at position %ld\n", pos));
2412
50
      return bad;
2413
50
    }
2414
0
    long nextPos=long(input->readLong(4));
2415
0
    if (nextPos==0) { // ok last position
2416
0
      listBeginPosition.insert(pos);
2417
0
      break;
2418
0
    }
2419
0
    if (nextPos<begPos+18 || !input->checkPosition(nextPos) || listBeginPosition.find(nextPos)!=listBeginPosition.end()) {
2420
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::decode: oops code break at position %ld\n", pos));
2421
0
      return bad;
2422
0
    }
2423
0
    listBeginPosition.insert(pos);
2424
0
    input->seek(nextPos, librevenge::RVNG_SEEK_SET);
2425
0
  }
2426
2427
2
  input->seek(0, librevenge::RVNG_SEEK_SET);
2428
2
  unsigned long read;
2429
2
  const unsigned char *data = input->read((unsigned long)(begPos), read);
2430
2
  if (!data || read != (unsigned long)(begPos)) {
2431
0
    MWAW_DEBUG_MSG(("StudentWritingCParser::decode: can not retrieve the begin data\n"));
2432
0
    return bad;
2433
0
  }
2434
2435
2
  auto stream=std::make_shared<MWAWStringStream>(data, unsigned(begPos));
2436
2
  for (auto posIt=listBeginPosition.begin(); posIt!=listBeginPosition.end();) {
2437
2
    long first=*(posIt++);
2438
2
    if (posIt==listBeginPosition.end()) break;
2439
0
    long end=*posIt;
2440
0
    if (first+18>end) {
2441
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::decode: oops the zone at position %ld seems too short\n", first));
2442
0
      return bad;
2443
0
    }
2444
0
    input->seek(first+8, librevenge::RVNG_SEEK_SET);
2445
0
    long dataSize=long(input->readLong(4));
2446
0
    if (dataSize<0 || dataSize>=10000000) {
2447
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::decode: oops can not read the data size of the zone at position %ld\n", first));
2448
0
      return bad;
2449
0
    }
2450
2451
0
    input->seek(-4, librevenge::RVNG_SEEK_CUR);
2452
0
    data=input->read(10, read);
2453
0
    if (!data || read!=10) {
2454
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::decode: can not retrieve zone's header at position %ld\n", first));
2455
0
      return bad;
2456
0
    }
2457
0
    stream->append(data, 10);
2458
2459
0
    if (dataSize==0)
2460
0
      continue;
2461
2462
0
    data=input->read(size_t(end-18-first), read);
2463
0
    if (!data || read!=(unsigned long)(end-18-first)) {
2464
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::decode: can not retrieve zone %ld-%ld\n", first, end));
2465
0
      continue;
2466
0
    }
2467
0
    StudentWritingCParserInternal::LWZDecoder decoder(data, read);
2468
0
    std::vector<unsigned char> output;
2469
0
    output.reserve(size_t(dataSize));
2470
0
    decoder.decode(output);
2471
0
    if (output.size()!=size_t(dataSize)) {
2472
0
      MWAW_DEBUG_MSG(("StudentWritingCParser::decode: unexpected output size %lx-%lx\n", long(output.size()), long(dataSize)));
2473
0
      continue;
2474
0
    }
2475
0
    stream->append(output.data(), unsigned(output.size()));
2476
0
  }
2477
2478
2
  MWAWInputStreamPtr res(new MWAWInputStream(stream, false));
2479
2
  res->seek(0, librevenge::RVNG_SEEK_SET);
2480
2
  return res;
2481
2
}
2482
2483
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: