Coverage Report

Created: 2026-06-13 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/JazzWriterParser.cxx
Line
Count
Source
1
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2
3
/* libmwaw
4
* Version: MPL 2.0 / LGPLv2+
5
*
6
* The contents of this file are subject to the Mozilla Public License Version
7
* 2.0 (the "License"); you may not use this file except in compliance with
8
* the License or as specified alternatively below. You may obtain a copy of
9
* the License at http://www.mozilla.org/MPL/
10
*
11
* Software distributed under the License is distributed on an "AS IS" basis,
12
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
* for the specific language governing rights and limitations under the
14
* License.
15
*
16
* Major Contributor(s):
17
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20
* Copyright (C) 2006, 2007 Andrew Ziem
21
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22
*
23
*
24
* All Rights Reserved.
25
*
26
* For minor contributions see the git repository.
27
*
28
* Alternatively, the contents of this file may be used under the terms of
29
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30
* in which case the provisions of the LGPLv2+ are applicable
31
* instead of those above.
32
*/
33
34
#include <iomanip>
35
#include <iostream>
36
#include <limits>
37
#include <set>
38
#include <sstream>
39
40
#include <librevenge/librevenge.h>
41
42
#include "MWAWTextListener.hxx"
43
#include "MWAWFontConverter.hxx"
44
#include "MWAWHeader.hxx"
45
#include "MWAWParagraph.hxx"
46
#include "MWAWPictData.hxx"
47
#include "MWAWPosition.hxx"
48
#include "MWAWPrinter.hxx"
49
#include "MWAWRSRCParser.hxx"
50
#include "MWAWSubDocument.hxx"
51
52
#include "JazzWriterParser.hxx"
53
54
/** Internal: the structures of a JazzWriterParser */
55
namespace JazzWriterParserInternal
56
{
57
58
////////////////////////////////////////
59
//! Internal: the structure used to store a paragraph/section
60
struct Paragraph {
61
  //! constructor
62
  Paragraph()
63
0
    : m_paragraph()
64
0
    , m_dimension(0,0)
65
0
    , m_nextParagraphId(0)
66
0
    , m_plcId(0)
67
0
  {
68
0
  }
69
  //! the paragraph
70
  MWAWParagraph m_paragraph;
71
  //! the dimension
72
  MWAWVec2i m_dimension;
73
  //! the next paragraph id
74
  unsigned m_nextParagraphId;
75
  //! the plc id
76
  unsigned m_plcId;
77
};
78
79
////////////////////////////////////////
80
//! Internal: the structure used to store a zone
81
struct Zone {
82
  //! constructor
83
  Zone()
84
0
    : m_paragraphId(0)
85
0
    , m_entry()
86
0
  {
87
0
    for (auto &id : m_hfIds) id=0;
88
0
  }
89
  //! the header/footer id
90
  unsigned m_hfIds[2];
91
  //! the paragraph id
92
  unsigned m_paragraphId;
93
  //! the text position in the data fork
94
  MWAWEntry m_entry;
95
};
96
97
////////////////////////////////////////
98
//! Internal: the state of a JazzWriterParser
99
struct State {
100
  //! constructor
101
  State()
102
0
    : m_idToZones()
103
0
    , m_idToParagraphs()
104
0
  {
105
0
  }
106
107
  /// map WDOC id to zones
108
  std::map<unsigned, Zone> m_idToZones;
109
  /// map WPPD id to zones
110
  std::map<unsigned, Paragraph> m_idToParagraphs;
111
};
112
113
////////////////////////////////////////
114
//! Internal: the subdocument of a JazzWriterParser
115
class SubDocument final : public MWAWSubDocument
116
{
117
public:
118
  SubDocument(JazzWriterParser &pars, MWAWInputStreamPtr const &input, MWAWInputStreamPtr const &rsrcInput, unsigned id)
119
0
    : MWAWSubDocument(&pars, input, MWAWEntry())
120
0
    , m_rsrcInput(rsrcInput)
121
0
    , m_zId(id)
122
0
  {
123
0
  }
124
125
  //! destructor
126
0
  ~SubDocument() final {}
127
128
  //! operator!=
129
  bool operator!=(MWAWSubDocument const &doc) const final;
130
131
  //! the parser function
132
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
133
134
protected:
135
  //! the resource fork input
136
  MWAWInputStreamPtr m_rsrcInput;
137
  //! the zone id
138
  unsigned m_zId;
139
};
140
141
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
142
0
{
143
0
  if (!listener.get()) {
144
0
    MWAW_DEBUG_MSG(("JazzWriterParserInternal::SubDocument::parse: no listener\n"));
145
0
    return;
146
0
  }
147
0
  auto *parser=dynamic_cast<JazzWriterParser *>(m_parser);
148
0
  if (!parser) {
149
0
    MWAW_DEBUG_MSG(("JazzWriterParserInternal::SubDocument::parse: no parser\n"));
150
0
    return;
151
0
  }
152
0
  if (!m_input || !m_rsrcInput) {
153
0
    MWAW_DEBUG_MSG(("JazzWriterParserInternal::SubDocument::parse: no input\n"));
154
0
    return;
155
0
  }
156
0
  long pos = m_input->tell();
157
0
  long rPos = m_rsrcInput->tell();
158
0
  parser->sendZone(m_zId);
159
0
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
160
0
  m_rsrcInput->seek(rPos, librevenge::RVNG_SEEK_SET);
161
0
}
162
163
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
164
0
{
165
0
  if (MWAWSubDocument::operator!=(doc)) return true;
166
0
  auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
167
0
  if (!sDoc) return true;
168
0
  if (m_zId != sDoc->m_zId) return true;
169
0
  if (m_rsrcInput != sDoc->m_rsrcInput) return true;
170
0
  return false;
171
0
}
172
}
173
174
175
////////////////////////////////////////////////////////////
176
// constructor/destructor, ...
177
////////////////////////////////////////////////////////////
178
JazzWriterParser::JazzWriterParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
179
0
  : MWAWTextParser(input, rsrcParser, header)
180
0
  , m_state(new JazzWriterParserInternal::State)
181
0
{
182
0
  setAsciiName("main-1");
183
0
}
184
185
JazzWriterParser::~JazzWriterParser()
186
0
{
187
0
}
188
189
MWAWInputStreamPtr JazzWriterParser::rsrcInput()
190
0
{
191
0
  return getRSRCParser()->getInput();
192
0
}
193
194
libmwaw::DebugFile &JazzWriterParser::rsrcAscii()
195
0
{
196
0
  return getRSRCParser()->ascii();
197
0
}
198
199
////////////////////////////////////////////////////////////
200
// the parser
201
////////////////////////////////////////////////////////////
202
void JazzWriterParser::parse(librevenge::RVNGTextInterface *docInterface)
203
0
{
204
0
  if (!getInput().get() || !getRSRCParser() || !checkHeader(nullptr))  throw(libmwaw::ParseException());
205
0
  bool ok = false;
206
0
  try {
207
    // create the asciiFile
208
0
    ascii().setStream(getInput());
209
0
    ascii().open(asciiName());
210
0
    checkHeader(nullptr);
211
0
    ok = createZones();
212
0
    if (ok) {
213
0
      createDocument(docInterface);
214
0
      sendZone(257);
215
0
      if (!getInput()->isEnd()) {
216
0
        MWAW_DEBUG_MSG(("JazzWriterParser::parse: find some unsent characters\n"));
217
0
      }
218
0
    }
219
0
  }
220
0
  catch (...) {
221
0
    MWAW_DEBUG_MSG(("JazzWriterParser::parse: exception catched when parsing\n"));
222
0
    ok = false;
223
0
  }
224
225
0
  resetTextListener();
226
0
  if (!ok) throw(libmwaw::ParseException());
227
0
}
228
229
////////////////////////////////////////////////////////////
230
// create the document
231
////////////////////////////////////////////////////////////
232
void JazzWriterParser::createDocument(librevenge::RVNGTextInterface *documentInterface)
233
0
{
234
0
  if (!documentInterface) return;
235
0
  if (getTextListener()) {
236
0
    MWAW_DEBUG_MSG(("JazzWriterParser::createDocument: listener already exist\n"));
237
0
    return;
238
0
  }
239
240
  // found the numbers of page and update the page
241
0
  int numPages = 1;
242
0
  auto input=getInput();
243
0
  input->seek(0, librevenge::RVNG_SEEK_SET);
244
0
  while (!input->isEnd()) {
245
0
    if (input->readULong(1)==0xc)
246
0
      ++numPages;
247
0
  }
248
249
  // create the page list
250
0
  MWAWPageSpan ps(getPageSpan());
251
0
  ps.setPageSpan(numPages+1);
252
0
  auto it=m_state->m_idToZones.find(257);
253
0
  if (it==m_state->m_idToZones.end()) {
254
0
    MWAW_DEBUG_MSG(("JazzWriterParser::createDocument: can not find the main zone\n"));
255
0
    throw (libmwaw::ParseException());
256
0
  }
257
258
0
  for (int wh=0; wh<2; ++wh) {
259
0
    if (!it->second.m_hfIds[wh])
260
0
      continue;
261
0
    MWAWHeaderFooter header(wh==0 ? MWAWHeaderFooter::HEADER : MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL);
262
0
    header.m_subDocument.reset(new JazzWriterParserInternal::SubDocument(*this, getInput(), rsrcInput(), it->second.m_hfIds[wh]));
263
0
    ps.setHeaderFooter(header);
264
0
  }
265
266
0
  std::vector<MWAWPageSpan> pageList(1,ps);
267
0
  MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface));
268
0
  setTextListener(listen);
269
0
  listen->startDocument();
270
0
}
271
272
273
////////////////////////////////////////////////////////////
274
//
275
// Intermediate level
276
//
277
////////////////////////////////////////////////////////////
278
bool JazzWriterParser::createZones()
279
0
{
280
0
  MWAWRSRCParserPtr rsrcParser = getRSRCParser();
281
0
  if (!rsrcParser) {
282
0
    MWAW_DEBUG_MSG(("JazzWriterParser::createZones: can not find the entry map\n"));
283
0
    return false;
284
0
  }
285
286
0
  auto const &entryMap = rsrcParser->getEntriesMap();
287
0
  MWAWInputStreamPtr input = rsrcInput();
288
0
  libmwaw::DebugFile &ascFile = rsrcAscii();
289
290
0
  char const *zNames[] = {"LFRF", "LLNK", "WDOC", "WPPD"};
291
0
  char const *what[]= {"FileRef", "Link", "Zone", "Paragraph"};
292
0
  libmwaw::DebugStream f;
293
0
  for (int wh=0; wh<4; ++wh) {
294
0
    auto it = entryMap.lower_bound(zNames[wh]);
295
0
    while (it != entryMap.end()) {
296
0
      if (it->first != zNames[wh] || !it->second.valid())
297
0
        break;
298
0
      MWAWEntry const &entry = it++->second;
299
0
      if (!input->checkPosition(entry.end())) {
300
0
        MWAW_DEBUG_MSG(("JazzWriterParser::createZones: find bad entry\n"));
301
0
        continue;
302
0
      }
303
0
      entry.setParsed(true);
304
0
      bool ok=false, done=false;
305
0
      int val;
306
0
      f.str("");
307
0
      f << "Entries(" << what[wh] << ")[" << entry.id() << "]:";
308
0
      input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
309
0
      switch (wh) {
310
0
      case 0: // link name
311
0
        f << entry.name() << ","; // the file name
312
0
        ok=true;
313
0
        if (entry.length()!=2) {
314
0
          MWAW_DEBUG_MSG(("JazzWriterParser::createZones[lref]: the entry length seems bad\n"));
315
0
          f << "###";
316
0
          break;
317
0
        }
318
0
        val=int(input->readLong(2));
319
0
        if (val!=1) f << "f0=" << val << ",";
320
0
        break;
321
0
      case 1: {
322
0
        librevenge::RVNGString text;
323
0
        ok=true;
324
0
        if (!readString(input, text, entry.end())) {
325
0
          MWAW_DEBUG_MSG(("JazzWriterParser::createZones[lnk]: can not find the text\n"));
326
0
          f << "###";
327
0
          break;
328
0
        }
329
0
        if (!text.empty())
330
0
          f << text.cstr() << ",";
331
0
        long cPos=input->tell();
332
0
        if (cPos+2>entry.end()) {
333
0
          MWAW_DEBUG_MSG(("JazzWriterParser::createZones[lnk]: can not find the file ref\n"));
334
0
          f << "###";
335
0
          break;
336
0
        }
337
0
        f << "file[ref]=" << input->readULong(2) << ",";
338
0
        break;
339
0
      }
340
0
      case 2:
341
0
        done=readZone(entry);
342
0
        break;
343
0
      case 3:
344
0
        done=readParagraph(entry);
345
0
        break;
346
0
      default:
347
0
        break;
348
0
      }
349
0
      if (done)
350
0
        continue;
351
0
      if (!ok)
352
0
        f << "###";
353
0
      ascFile.addPos(entry.begin()-4);
354
0
      ascFile.addNote(f.str().c_str());
355
0
    }
356
0
  }
357
0
  return checkZones();
358
0
}
359
360
bool JazzWriterParser::checkZones()
361
0
{
362
0
  auto it=m_state->m_idToZones.find(257);
363
0
  if (it==m_state->m_idToZones.end()) {
364
0
    MWAW_DEBUG_MSG(("JazzWriterParser::checkZones: can not find the main zone\n"));
365
0
    return false;
366
0
  }
367
0
  unsigned const zones[3]= {it->second.m_hfIds[0],it->second.m_hfIds[1],257};
368
0
  std::set<unsigned> seens;
369
0
  long pos=0;
370
0
  for (auto const &id : zones) {
371
0
    it=m_state->m_idToZones.find(id);
372
0
    if (it==m_state->m_idToZones.end()) {
373
0
      MWAW_DEBUG_MSG(("JazzWriterParser::checkZones: can not find the %x zone\n", id));
374
0
      return false;
375
0
    }
376
0
    long length=0;
377
0
    if (!checkParagraphs(it->second.m_paragraphId, length, seens))
378
0
      return false;
379
0
    MWAWEntry entry;
380
0
    entry.setBegin(pos);
381
0
    entry.setLength(length);
382
0
    it->second.m_entry=entry;
383
0
    pos+=length;
384
0
  }
385
0
  if (!getInput() || getInput()->size()<pos) {
386
0
    MWAW_DEBUG_MSG(("JazzWriterParser::createZones: the data fork seems too short\n"));
387
0
    return false;
388
0
  }
389
0
  getInput()->seek(0, librevenge::RVNG_SEEK_SET);
390
391
0
  return true;
392
0
}
393
394
bool JazzWriterParser::checkParagraphs(unsigned id, long &num, std::set<unsigned> &seens) const
395
0
{
396
0
  if (id==0)
397
0
    return true;
398
0
  if (seens.find(id) != seens.end()) {
399
0
    MWAW_DEBUG_MSG(("JazzWriterParser::checkParagraphs: paragraph %x is already seen\n", id));
400
0
    return false;
401
0
  }
402
0
  seens.insert(id);
403
0
  auto it=m_state->m_idToParagraphs.find(id);
404
0
  if (it==m_state->m_idToParagraphs.end()) {
405
0
    MWAW_DEBUG_MSG(("JazzWriterParser::checkParagraphs: can not find paragraph %x\n", id));
406
0
    return false;
407
0
  }
408
0
  long n=0;
409
0
  if (!const_cast<JazzWriterParser *>(this)->countCharactersInPLC(it->second.m_plcId, n))
410
0
    return false;
411
0
  num+=n;
412
0
  checkParagraphs(it->second.m_nextParagraphId, num, seens);
413
0
  return true;
414
0
}
415
416
bool JazzWriterParser::countCharactersInPLC(unsigned plcId, long &n)
417
0
{
418
0
  MWAWRSRCParserPtr rsrcParser = getRSRCParser();
419
0
  if (!rsrcParser) {
420
0
    MWAW_DEBUG_MSG(("JazzWriterParser::countCharactersInPLC: can not find the rsrc parser\n"));
421
0
    return false;
422
0
  }
423
0
  MWAWInputStreamPtr input = rsrcInput();
424
0
  MWAWEntry entry=rsrcParser->getEntry("WSCR", int(plcId));
425
0
  if (!entry.valid() || !input || !input->checkPosition(entry.end())) {
426
0
    MWAW_DEBUG_MSG(("JazzWriterParser::countCharactersInPLC: can not find the %x WSCR\n", plcId));
427
0
    return false;
428
0
  }
429
0
  n=0;
430
0
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
431
0
  long endPos=entry.end();
432
0
  while (input->tell()+6<=endPos) {
433
0
    long pos=input->tell();
434
0
    int type=int(input->readULong(2));
435
0
    switch (type) {
436
0
    case 1:
437
0
      n+=int(input->readULong(4));
438
0
      break;
439
0
    case 3:
440
0
      input->seek(3, librevenge::RVNG_SEEK_CUR);
441
0
      n+=int(input->readULong(1));
442
0
      break;
443
0
    case 5:
444
0
      pos+=6;
445
0
      break;
446
0
    default:
447
0
      break;
448
0
    }
449
0
    input->seek(pos+6, librevenge::RVNG_SEEK_SET);
450
0
  }
451
0
  return true;
452
0
}
453
454
bool JazzWriterParser::readParagraph(MWAWEntry const &entry)
455
0
{
456
0
  if (entry.length()!=122) {
457
0
    MWAW_DEBUG_MSG(("JazzWriterParser::readParagraph: unexpected size\n"));
458
0
    return false;
459
0
  }
460
0
  MWAWInputStreamPtr input = rsrcInput();
461
0
  libmwaw::DebugFile &ascFile = rsrcAscii();
462
0
  libmwaw::DebugStream f;
463
464
0
  JazzWriterParserInternal::Paragraph para;
465
0
  f << "Entries(Paragraph)[" << entry.id() << "]:";
466
0
  int val;
467
0
  para.m_nextParagraphId=unsigned(input->readLong(2));
468
0
  if (para.m_nextParagraphId) f << "next[para]=" << para.m_nextParagraphId << ",";
469
0
  val=int(input->readLong(2));
470
0
  if (val) f << "f0=" << val << ",";
471
0
  val=int(input->readLong(2));
472
0
  if (val!=2)
473
0
    f << "ID1=" << input->readULong(2) << ",";
474
0
  else
475
0
    f << "ID1=" << input->readULong(2) << "[" << val << "],";
476
0
  f << "dim0=" << input->readLong(2) << ",";
477
0
  int dims[2];
478
0
  for (auto &d : dims) d=int(input->readLong(2));
479
0
  para.m_dimension=MWAWVec2i(dims[1],dims[0]);
480
0
  f << "dim=" << para.m_dimension << ",";
481
0
  val=int(input->readLong(2));
482
0
  if (val==2)
483
0
    f << "ID2=" << input->readULong(2) << ",";
484
0
  else
485
0
    f << "ID2=" << input->readULong(2) << "[" << val << "],";
486
0
  for (int i=0; i<11; ++i) { // f5,f7 some heigths?, f8=0|600
487
0
    val=int(input->readLong(2));
488
0
    if (val) f << "f" << i+1 << "=" << val << ",";
489
0
  }
490
0
  f << "unk=" << input->readLong(2) << ","; // 1000|5000
491
0
  for (int i=0; i<2; ++i) {
492
0
    val=int(input->readLong(2));
493
0
    if (val!=(i==1 ? 4 : 0))
494
0
      f << "g" << i << "=" << val << ",";
495
0
  }
496
0
  f << "fl=" << std::hex << input->readULong(2) << std::dec << ","; // 1002|1003
497
0
  val=int(input->readLong(2));
498
0
  if (val!=0xc00) f << "g2=" << val << ",";
499
0
  for (int i=0; i<4; ++i) { // g3=0|8
500
0
    val=int(input->readULong(2));
501
0
    if (i==2 && (val&0x300)) {
502
0
      f << "align=" << ((val>>8)&3) << ",";
503
0
      switch ((val>>8)&3) {
504
0
      case 1:
505
0
        para.m_paragraph.m_justify=MWAWParagraph::JustificationRight;
506
0
        break;
507
0
      case 2:
508
0
        para.m_paragraph.m_justify=MWAWParagraph::JustificationCenter;
509
0
        break;
510
0
      case 3:
511
0
        para.m_paragraph.m_justify=MWAWParagraph::JustificationFull;
512
0
        break;
513
0
      default:
514
0
        break;
515
0
      }
516
0
      val &= 0xfcff;
517
0
    }
518
0
    if (i==3 && (val&3)) {
519
0
      f << "line spacing=" << 1+float(val&3)/2 << ",";
520
0
      para.m_paragraph.setInterline(1+float(val&3)/2, librevenge::RVNG_PERCENT);
521
0
      val &= 0xfffc;
522
0
    }
523
0
    if (!val) continue;
524
0
    if (i==0)
525
0
      f << "g3=" << std::hex << val << std::dec << ",";
526
0
    else
527
0
      f << "g" << i+3 << "=" << val << ",";
528
0
  }
529
0
  ascFile.addPos(entry.begin()-4);
530
0
  ascFile.addNote(f.str().c_str());
531
532
0
  input->seek(entry.begin()+58, librevenge::RVNG_SEEK_SET);
533
0
  long pos=input->tell();
534
0
  f.str("");
535
0
  f << "Paragraph-A:";
536
0
  int N=int(input->readLong(2));
537
0
  f << "num[tabs]=" << N << ",";
538
0
  if (N<0 || N>12) {
539
0
    MWAW_DEBUG_MSG(("JazzWriterParser::readParagraph: the number of tabs seems bads\n"));
540
0
    f << "###";
541
0
    N=0;
542
0
  }
543
0
  para.m_paragraph.m_marginsUnit=librevenge::RVNG_POINT;
544
0
  for (int i=0; i<3; ++i) {
545
0
    val=int(input->readLong(2));
546
0
    int const expected[]= {72, 0x21c, 72};
547
0
    para.m_paragraph.m_margins[i==2 ? 0 : i+1]=val;
548
0
    if (val==expected[i]) continue;
549
0
    f << (i==0 ? "marg[left]" : i==1 ? "marg[right]" : "first[ident]") << "=" << val << ",";
550
0
  }
551
0
  *para.m_paragraph.m_margins[0]-=*para.m_paragraph.m_margins[1];
552
0
  para.m_paragraph.m_margins[2]=0; // margin from left, so ignore
553
0
  f << "tabs[";
554
0
  for (int i=0; i<N; ++i) {
555
0
    MWAWTabStop tab;
556
0
    val=int(input->readLong(2));
557
0
    if (val>=0) {
558
0
      tab.m_position=double(val)/72.;
559
0
      f << val << ",";
560
0
    }
561
0
    else {
562
0
      tab.m_position=double(-val)/72.;
563
0
      tab.m_alignment = MWAWTabStop::CENTER;
564
0
      f << -val << "[C],";
565
0
    }
566
0
    para.m_paragraph.m_tabs->push_back(tab);
567
0
  }
568
0
  f << "],";
569
0
  input->seek(pos+8+24, librevenge::RVNG_SEEK_SET);
570
0
  for (int i=0; i<6; ++i) {
571
0
    val=int(input->readLong(2));
572
0
    if (val) f << "g" << i << "=" << val << ",";
573
0
  }
574
0
  val=int(input->readLong(2));
575
0
  if (val!=2)
576
0
    f << "h0=" << val << ",";
577
0
  f << "ID1=" << input->readULong(2) << ",";
578
0
  for (int i=0; i<8; ++i) {
579
0
    val=int(input->readULong(2));
580
0
    if (!val) continue;
581
0
    if (i==4) {
582
0
      para.m_plcId=unsigned(val);
583
0
      f << "plc[id]=" << val << ",";
584
0
    }
585
0
    else
586
0
      f << "h" << i+1 << "=" << val << ",";
587
0
  }
588
0
  if (m_state->m_idToParagraphs.find(unsigned(entry.id()))==m_state->m_idToParagraphs.end())
589
0
    m_state->m_idToParagraphs[unsigned(entry.id())]=para;
590
0
  else {
591
0
    MWAW_DEBUG_MSG(("JazzWriterParser::readParagraph: paragraph %d already exists\n", entry.id()));
592
0
    f << "###id,";
593
0
  }
594
0
  ascFile.addPos(pos);
595
0
  ascFile.addNote(f.str().c_str());
596
0
  return true;
597
0
}
598
599
bool JazzWriterParser::readZone(MWAWEntry const &entry)
600
0
{
601
0
  if (entry.length()!=44) {
602
0
    MWAW_DEBUG_MSG(("JazzWriterParser::readZone: unexpected size\n"));
603
0
    return false;
604
0
  }
605
0
  MWAWInputStreamPtr input = rsrcInput();
606
0
  libmwaw::DebugFile &ascFile = rsrcAscii();
607
0
  libmwaw::DebugStream f;
608
609
0
  JazzWriterParserInternal::Zone zone;
610
0
  f << "Entries(Zone)[" << entry.id() << "]:";
611
0
  zone.m_paragraphId=unsigned(input->readULong(2));
612
0
  f << "para[id]=" << zone.m_paragraphId << ",";
613
0
  int id1=int(input->readULong(2));
614
0
  f << "ID1=" << id1 << ",";
615
0
  int val=int(input->readULong(2));
616
0
  if (unsigned(val)!=zone.m_paragraphId)
617
0
    f << "para[id1]=" << val << ",";
618
0
  val=int(input->readULong(2));
619
0
  if (val!=id1)
620
0
    f << "ID2=" << val << ",";
621
0
  for (int i=0; i<3; ++i) {
622
0
    val=int(input->readLong(2));
623
0
    if (val)
624
0
      f << "f" << i << "=" << val << ",";
625
0
  }
626
0
  int dims[2];
627
0
  for (auto &d : dims) d=int(input->readLong(2));
628
0
  f << "dim=" << MWAWVec2i(dims[1],dims[0]) << ",";
629
0
  val=int(input->readLong(2));
630
0
  if (val)
631
0
    f << "f0=" << val << ",";
632
0
  zone.m_hfIds[0]=unsigned(input->readULong(2));
633
0
  if (zone.m_hfIds[0]) f << "zone[header]=" << zone.m_hfIds[0] << ",";
634
0
  val=int(input->readULong(2));
635
0
  if (val)
636
0
    f << "ID[header]=" << val << ",";
637
0
  zone.m_hfIds[1]=unsigned(input->readULong(2));
638
0
  if (zone.m_hfIds[1]) f << "zone[footer]=" << zone.m_hfIds[1] << ",";
639
0
  val=int(input->readULong(2));
640
0
  if (val)
641
0
    f << "ID[footer]=" << val << ",";
642
0
  for (int i=0; i<8; ++i) { // f3=1 header/footer, c:main?
643
0
    val=int(input->readLong(2));
644
0
    if (val) f << "f" << i+1 << "=" << val << ",";
645
0
  }
646
0
  if (m_state->m_idToZones.find(unsigned(entry.id()))==m_state->m_idToZones.end())
647
0
    m_state->m_idToZones[unsigned(entry.id())]=zone;
648
0
  else {
649
0
    MWAW_DEBUG_MSG(("JazzWriterParser::readZone: zone %d already exists\n", entry.id()));
650
0
    f << "###id,";
651
0
  }
652
0
  ascFile.addPos(entry.begin()-4);
653
0
  ascFile.addNote(f.str().c_str());
654
0
  return true;
655
0
}
656
657
////////////////////////////////////////////////////////////
658
//
659
// Low level
660
//
661
////////////////////////////////////////////////////////////
662
bool JazzWriterParser::readString(MWAWInputStreamPtr input, librevenge::RVNGString &string, long endPos)
663
0
{
664
0
  string.clear();
665
0
  if (!input) {
666
0
    MWAW_DEBUG_MSG(("JazzWriterParser::readString: can not find the input\n"));
667
0
    return false;
668
0
  }
669
0
  auto fontConverter=getFontConverter();
670
0
  int defaultFont=3;
671
0
  long pos=input->tell();
672
0
  int n=int(input->readULong(1));
673
0
  if (!input->checkPosition(pos+1+n) || pos+1+n>endPos) {
674
0
    MWAW_DEBUG_MSG(("JazzWriterParser::readString: can not read the string length\n"));
675
0
    return false;
676
0
  }
677
0
  for (int i=0; i<n; ++i) {
678
0
    char c=char(input->readULong(1));
679
0
    int unicode = fontConverter->unicode(defaultFont, static_cast<unsigned char>(c));
680
0
    if (unicode>0)
681
0
      libmwaw::appendUnicode(uint32_t(unicode), string);
682
0
    else {
683
0
      MWAW_DEBUG_MSG(("JazzWriterParser::readString: find unknown unicode for char=%d\n", int(c)));
684
0
    }
685
0
  }
686
0
  return true;
687
0
}
688
689
////////////////////////////////////////////////////////////
690
// read the header
691
////////////////////////////////////////////////////////////
692
bool JazzWriterParser::checkHeader(MWAWHeader *header, bool /*strict*/)
693
0
{
694
0
  *m_state = JazzWriterParserInternal::State();
695
0
  if (!getRSRCParser())
696
0
    return false;
697
  // check if the WDOC entry exists
698
0
  MWAWEntry entry = getRSRCParser()->getEntry("WDOC", 257);
699
0
  if (entry.begin()<=0) { // length can be not 0, so ...
700
0
    MWAW_DEBUG_MSG(("JazzWriterParser::checkHeader: can not find the WDOC[257] resource\n"));
701
0
    return false;
702
0
  }
703
0
  if (!getInput()->hasDataFork() || getInput()->size()<=0) {
704
    // checkme: is this possible when the document contains only a picture
705
0
    MWAW_DEBUG_MSG(("JazzWriterParser::checkHeader: can not find any data fork\n"));
706
0
    return false;
707
0
  }
708
0
  if (header)
709
0
    header->reset(MWAWDocument::MWAW_T_JAZZLOTUS, 1);
710
711
0
  return true;
712
0
}
713
714
////////////////////////////////////////////////////////////
715
// send the data
716
////////////////////////////////////////////////////////////
717
bool JazzWriterParser::sendZone(unsigned zId)
718
0
{
719
0
  if (!getTextListener()) {
720
0
    MWAW_DEBUG_MSG(("JazzWriterParser::checkHeader: can not find the main listener\n"));
721
0
    return false;
722
0
  }
723
0
  auto it=m_state->m_idToZones.find(zId);
724
0
  if (it==m_state->m_idToZones.end()) {
725
0
    MWAW_DEBUG_MSG(("JazzWriterParser::checkZones: can not find the %x zone\n", zId));
726
0
    return false;
727
0
  }
728
729
0
  auto input=getInput();
730
0
  input->seek(it->second.m_entry.begin(), librevenge::RVNG_SEEK_SET);
731
0
  sendParagraph(it->second.m_paragraphId);
732
0
  return true;
733
0
}
734
735
bool JazzWriterParser::sendParagraph(unsigned pId)
736
0
{
737
0
  auto it=m_state->m_idToParagraphs.find(pId);
738
0
  if (it==m_state->m_idToParagraphs.end()) {
739
0
    MWAW_DEBUG_MSG(("JazzWriterParser::checkZones: can not find the %x paragraph\n", pId));
740
0
    return false;
741
0
  }
742
0
  auto const &para=it->second;
743
0
  getTextListener()->setParagraph(para.m_paragraph);
744
0
  sendPLC(para.m_plcId);
745
0
  if (para.m_nextParagraphId)
746
0
    sendParagraph(para.m_nextParagraphId);
747
0
  return true;
748
0
}
749
750
bool JazzWriterParser::sendPLC(unsigned pId)
751
0
{
752
0
  MWAWRSRCParserPtr rsrcParser = getRSRCParser();
753
0
  if (!rsrcParser) {
754
0
    MWAW_DEBUG_MSG(("JazzWriterParser::sendPLC: can not find the rsrc parser\n"));
755
0
    return false;
756
0
  }
757
0
  auto listener=getTextListener();
758
0
  if (!listener) {
759
0
    MWAW_DEBUG_MSG(("JazzWriterParser::sendPLC: can not find the text listener\n"));
760
0
    return false;
761
0
  }
762
0
  MWAWInputStreamPtr input = getInput();
763
0
  MWAWInputStreamPtr rInput = rsrcInput();
764
0
  libmwaw::DebugFile &rAscFile = rsrcAscii();
765
0
  libmwaw::DebugStream f;
766
767
0
  MWAWEntry entry=rsrcParser->getEntry("WSCR", int(pId));
768
0
  if (!entry.valid()) {
769
0
    MWAW_DEBUG_MSG(("JazzWriterParser::sendPLC: can not find the %x plc\n", pId));
770
0
    return false;
771
0
  }
772
773
0
  f << "Entries(PLC)[" << entry.id() << "]:";
774
0
  rAscFile.addPos(entry.begin()-4);
775
0
  rAscFile.addNote(f.str().c_str());
776
777
0
  rInput->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
778
0
  int N=int(entry.length()/6);
779
0
  int val;
780
0
  auto fontConverter=getFontConverter();
781
0
  for (int i=0; i<N; ++i) {
782
0
    long rPos=rInput->tell();
783
0
    f.str("");
784
0
    f << "PLC-" << i << ":";
785
0
    int wh=int(rInput->readLong(2));
786
0
    int numChar=0;
787
0
    switch (wh) {
788
0
    case 1:
789
0
      numChar=int(rInput->readULong(4));
790
0
      f << "num=" << numChar << ",";
791
0
      break;
792
0
    case 2: {
793
0
      MWAWFont font;
794
0
      f << "font,";
795
0
      f << "h=" << rInput->readULong(1) << ",";
796
0
      font.setId(int(rInput->readULong(1)));
797
0
      font.setSize(float(rInput->readULong(1)));
798
0
      val=int(rInput->readULong(1));
799
0
      uint32_t flags = 0;
800
0
      if (val&0x1) flags |= MWAWFont::boldBit;
801
0
      if (val&0x2) flags |= MWAWFont::italicBit;
802
0
      if (val&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
803
0
      if (val&0x8) flags |= MWAWFont::embossBit;
804
0
      if (val&0x10) flags |= MWAWFont::shadowBit;
805
0
      font.setFlags(flags);
806
0
      val &= 0xe0;
807
0
      f << "font=[" << font.getDebugString(fontConverter) << "]";
808
0
      if (val)
809
0
        f << "fl=" << std::hex << val << std::dec << ",";
810
0
      listener->setFont(font);
811
0
      break;
812
0
    }
813
0
    case 3: {
814
0
      val=int(rInput->readULong(2));
815
0
      bool sent=false;
816
0
      if (val>=0xFFF0) {
817
0
        f << "field" << (0xFFFF-val) << ",";
818
0
        sent=true;
819
0
        switch (0xFFFF-val) {
820
0
        case 0: {
821
0
          MWAWField field(MWAWField::Date);
822
0
          field.m_DTFormat="%B %d, %Y";
823
0
          listener->insertField(field);
824
0
          break;
825
0
        }
826
0
        case 1: {
827
0
          MWAWField field(MWAWField::Time);
828
0
          field.m_DTFormat="%I:%M %p";
829
0
          listener->insertField(field);
830
0
          break;
831
0
        }
832
0
        case 2:
833
0
          listener->insertField(MWAWField(MWAWField::PageNumber));
834
0
          break;
835
0
        default:
836
0
          MWAW_DEBUG_MSG(("JazzWriterParser::readPLC: find unknown field\n"));
837
0
          f << "###";
838
0
          sent=false;
839
0
          break;
840
0
        }
841
0
      }
842
0
      else
843
0
        f << "link[id]=" << val << ",";
844
0
      f << "unk=" << int(rInput->readULong(1)) << ","; // 8-1e
845
0
      numChar=int(rInput->readULong(1));
846
0
      f << "len=" << numChar << ",";
847
0
      if (sent) {
848
0
        input->seek(numChar, librevenge::RVNG_SEEK_CUR);
849
0
        numChar=0;
850
0
      }
851
0
      break;
852
0
    }
853
0
    case 5: {
854
0
      f << "pict[link],";
855
0
      if (i+1>=N) {
856
0
        MWAW_DEBUG_MSG(("JazzWriterParser::readPLC: the zone seems too short\n"));
857
0
        f << "###";
858
0
        break;
859
0
      }
860
0
      f << "link[id]=" << int(rInput->readULong(2)) << ",";
861
0
      unsigned pictId=unsigned(rInput->readULong(2));
862
0
      f << "pict[id]=" << pictId << ",";
863
0
      int dim[2];
864
0
      for (auto &d : dim) d=int(rInput->readULong(2));
865
0
      f << "sz=" << MWAWVec2i(dim[1], dim[0]) << ",";
866
0
      int xPos=int(rInput->readLong(2));
867
0
      f << "xPos=" << xPos << ","; // related to baseline?
868
0
      ++i;
869
0
      rPos+=6;
870
0
      MWAWEmbeddedObject obj;
871
0
      if (!getPicture(pictId, obj) || obj.isEmpty()) {
872
0
        f << "##pictId";
873
0
        break;
874
0
      }
875
      // TODO: use xPos
876
0
      MWAWPosition position(MWAWVec2f(0,0), MWAWVec2f(float(dim[1]), float(dim[0])), librevenge::RVNG_POINT);
877
0
      position.setRelativePosition(MWAWPosition::Char);
878
0
      listener->insertPicture(position, obj);
879
0
      listener->insertEOL(); // one picture by line
880
0
      break;
881
0
    }
882
0
    default:
883
0
      MWAW_DEBUG_MSG(("JazzWriterParser::readPLC: find unknown type\n"));
884
0
      f << "###type" << wh << ",";
885
0
      break;
886
0
    }
887
0
    rAscFile.addPos(rPos);
888
0
    rAscFile.addNote(f.str().c_str());
889
0
    if (rInput->tell()!=rPos+6)
890
0
      rAscFile.addDelimiter(rInput->tell(),'|');
891
0
    rInput->seek(rPos+6, librevenge::RVNG_SEEK_SET);
892
893
    // now read/send the character
894
0
    if (numChar<=0) continue;
895
0
    long pos=input->tell();
896
0
    if (!input->checkPosition(pos+numChar)) {
897
0
      MWAW_DEBUG_MSG(("JazzWriterParser::readPLC: can not find some character\n"));
898
0
      break;
899
0
    }
900
0
    for (int c=0; c<numChar; ++c) {
901
0
      auto ch=(unsigned char)input->readULong(1);
902
0
      switch (ch) {
903
0
      case 0x9:
904
0
        listener->insertChar(ch);
905
0
        break;
906
0
      case 0xc:
907
0
        listener->insertBreak(MWAWTextListener::PageBreak);
908
0
        break;
909
0
      case 0xd:
910
0
        listener->insertEOL();
911
0
        break;
912
0
      default:
913
0
        if (ch<=0x1f) {
914
0
          MWAW_DEBUG_MSG(("JazzWriterParser::readPLC: find bad character %d at pos=0x%lx\n", int(c), input->tell()));
915
0
          break;
916
0
        }
917
0
        listener->insertCharacter(ch);
918
0
      }
919
0
    }
920
0
  }
921
0
  rAscFile.addPos(rInput->tell());
922
0
  rAscFile.addNote("PLC-end:");
923
0
  val=int(rInput->readLong(2));
924
0
  if (val)
925
0
    f << "##f0=" << val << ",";
926
0
  return true;
927
0
}
928
929
bool JazzWriterParser::getPicture(unsigned pId, MWAWEmbeddedObject &obj)
930
0
{
931
0
  MWAWRSRCParserPtr rsrcParser = getRSRCParser();
932
0
  if (!rsrcParser) {
933
0
    MWAW_DEBUG_MSG(("JazzWriterParser::getPicture: can not find the rsrc parser\n"));
934
0
    return false;
935
0
  }
936
0
  MWAWInputStreamPtr input = rsrcInput();
937
0
  libmwaw::DebugFile &ascFile = rsrcAscii();
938
939
0
  MWAWEntry entry=rsrcParser->getEntry("PICT", int(pId));
940
0
  if (!entry.valid()) {
941
0
    MWAW_DEBUG_MSG(("JazzWriterParser::getPicture: can not find the %x picture\n", pId));
942
0
    return false;
943
0
  }
944
0
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
945
0
  std::shared_ptr<MWAWPict> pict(MWAWPictData::get(input, int(entry.length())));
946
0
  if (!pict || !pict->getBinary(obj)) {
947
0
    MWAW_DEBUG_MSG(("JazzWriterParser::getPicture: can not read the %x picture\n", pId));
948
0
    return false;
949
0
  }
950
951
0
  ascFile.skipZone(entry.begin(), entry.end());
952
0
  return true;
953
0
}
954
955
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: