Coverage Report

Created: 2026-09-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/WPS8.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* libwps
3
 * Version: MPL 2.0 / LGPLv2.1+
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * Major Contributor(s):
10
 * Copyright (C) 2006, 2007 Andrew Ziem
11
 * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
12
 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13
 *
14
 * For minor contributions see the git repository.
15
 *
16
 * Alternatively, the contents of this file may be used under the terms
17
 * of the GNU Lesser General Public License Version 2.1 or later
18
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
19
 * applicable instead of those above.
20
 */
21
22
#include <algorithm>
23
#include <stdlib.h>
24
#include <stdio.h>
25
#include <string.h>
26
27
#include <librevenge-stream/librevenge-stream.h>
28
29
#include "libwps_internal.h"
30
#include "libwps_tools_win.h"
31
32
#include "WPSContentListener.h"
33
#include "WPSEntry.h"
34
#include "WPSHeader.h"
35
#include "WPSOLEParser.h"
36
#include "WPSPageSpan.h"
37
#include "WPSPosition.h"
38
#include "WPSTextSubDocument.h"
39
40
#include "WPS8Graph.h"
41
#include "WPS8Struct.h"
42
#include "WPS8Table.h"
43
#include "WPS8Text.h"
44
45
#include "WPS8.h"
46
47
namespace WPS8ParserInternal
48
{
49
//! Internal: the subdocument of a WPS8Parser
50
class SubDocument final : public WPSTextSubDocument
51
{
52
public:
53
  //! type of an entry
54
  enum Type { Unknown, TEXT };
55
  //! constructor for a text entry
56
  SubDocument(RVNGInputStreamPtr const &input, WPS8Parser &pars, WPSEntry const &entry)
57
12.6k
    : WPSTextSubDocument(input, &pars)
58
12.6k
    , m_entry(entry) {}
59
  //! destructor
60
12.6k
  ~SubDocument() final {}
61
62
  //! operator==
63
  bool operator==(std::shared_ptr<WPSSubDocument> const &doc) const final
64
0
  {
65
0
    if (!doc || !WPSTextSubDocument::operator==(doc))
66
0
      return false;
67
0
    auto const *sDoc = dynamic_cast<SubDocument const *>(doc.get());
68
0
    if (!sDoc) return false;
69
0
    return m_entry == sDoc->m_entry;
70
0
  }
71
72
  //! the parser function
73
  void parse(std::shared_ptr<WPSContentListener> &listener, libwps::SubDocumentType subDocumentType) final;
74
  //! the entry
75
  WPSEntry m_entry;
76
};
77
78
void SubDocument::parse(std::shared_ptr<WPSContentListener> &listener, libwps::SubDocumentType subDocumentType)
79
16.5k
{
80
16.5k
  if (!listener.get())
81
0
  {
82
0
    WPS_DEBUG_MSG(("WPS8ParserInternal::SubDocument::parse: no listener\n"));
83
0
    return;
84
0
  }
85
16.5k
  if (!dynamic_cast<WPSContentListener *>(listener.get()))
86
0
  {
87
0
    WPS_DEBUG_MSG(("WPS8ParserInternal::SubDocument::parse: bad listener\n"));
88
0
    return;
89
0
  }
90
91
16.5k
  auto &listen =  static_cast<WPSContentListenerPtr &>(listener);
92
16.5k
  if (!m_parser)
93
0
  {
94
0
    listen->insertCharacter(' ');
95
0
    WPS_DEBUG_MSG(("WPS8ParserInternal::SubDocument::parse: bad parser\n"));
96
0
    return;
97
0
  }
98
99
16.5k
  if (m_entry.isParsed() && subDocumentType != libwps::DOC_HEADER_FOOTER)
100
20
  {
101
20
    listen->insertCharacter(' ');
102
20
    WPS_DEBUG_MSG(("WPS8ParserInternal::SubDocument::parse: this zone is already parsed\n"));
103
20
    return;
104
20
  }
105
16.5k
  m_entry.setParsed(true);
106
16.5k
  if (m_entry.type() != "Text")
107
11.8k
  {
108
11.8k
    listen->insertCharacter(' ');
109
11.8k
    WPS_DEBUG_MSG(("WPS8ParserInternal::SubDocument::parse: send not Text entry is not implemented\n"));
110
11.8k
    return;
111
11.8k
  }
112
113
4.72k
  if (!m_entry.valid())
114
7
  {
115
7
    WPS_DEBUG_MSG(("WPS8ParserInternal::SubDocument::parse: empty document found...\n"));
116
7
    listen->insertCharacter(' ');
117
7
    return;
118
7
  }
119
120
121
4.71k
  auto *mnParser = dynamic_cast<WPS8Parser *>(m_parser);
122
4.71k
  if (!mnParser)
123
0
  {
124
0
    WPS_DEBUG_MSG(("WPS8ParserInternal::SubDocument::parse: can not find a WPS8Parser...\n"));
125
0
    listen->insertCharacter(' ');
126
0
    return;
127
0
  }
128
4.71k
  mnParser->send(m_entry);
129
4.71k
}
130
131
/** Internal: a frame, a zone which can contain text, picture, ... and have some borders */
132
struct Frame
133
{
134
  //! constructor
135
290k
  Frame() : m_parsed(false), m_type(UNKNOWN), m_pos(),
136
290k
    m_idStrs(-1), m_idObject(-1), m_idTable(-1), m_idOle(-1), m_columns(1), m_idBorder(),
137
290k
    m_backgroundColor(WPSColor::white()), m_error("")
138
290k
  {
139
290k
    m_pos.setRelativePosition(WPSPosition::Page);
140
290k
    m_pos.setPage(1);
141
290k
  }
142
143
  //! operator <<
144
  friend std::ostream &operator<<(std::ostream &o, Frame const &ft);
145
146
  /** The frame type
147
   *
148
   * - Header/Footer: the header and footer frame
149
   * - Table : a table
150
   * - Object : a picture, ...
151
   * - Text : a text zone */
152
  enum { UNKNOWN = 0, DbField, Header, Footer, Table, Object, Text };
153
154
  //! a flag to know if the frame is already sent to the listener
155
  mutable bool m_parsed;
156
  //! the frame type
157
  int m_type;
158
  //! the frame position
159
  WPSPosition m_pos;
160
  int m_idStrs /** identifier corresponding to a text zone (STRS)*/,
161
      m_idObject /** identifier corresponding to an object zone (EOBJ)*/,
162
      m_idTable /** identifier corresponding to a table (MCLD)*/,
163
      m_idOle /** identifier corresponding to an ole*/;
164
  //! the number of columns for a textbox, ...
165
  int m_columns;
166
  //! the border: an entry to some complex border (if sets)
167
  WPSEntry m_idBorder;
168
  //! the border's color
169
  WPSColor m_backgroundColor;
170
  //! a string used to store the parsing errors
171
  std::string m_error;
172
};
173
174
//! operator<< for a Frame
175
std::ostream &operator<<(std::ostream &o, Frame const &ft)
176
0
{
177
0
  switch (ft.m_type)
178
0
  {
179
0
  case Frame::DbField:
180
0
    o<< "db[field]";
181
0
    break;
182
0
  case Frame::Header:
183
0
    o<< "header";
184
0
    break;
185
0
  case Frame::Footer:
186
0
    o<< "footer";
187
0
    break;
188
0
  case Frame::Table:
189
0
    o<< "table";
190
0
    break;
191
0
  case Frame::Text:
192
0
    o<< "textbox";
193
0
    break;
194
0
  case Frame::Object:
195
0
    o<< "object";
196
0
    break;
197
0
  default:
198
0
    o << "###type=unknown";
199
0
    break;
200
0
  }
201
0
  o << "(";
202
0
  if (ft.m_idStrs >= 0) o << "STRS" << ft.m_idStrs << ",";
203
0
  if (ft.m_idObject >= 0) o << "EOBJ" << ft.m_idObject << ",";
204
0
  if (ft.m_idTable >= 0) o << "MCLD/Table" << ft.m_idTable << ",";
205
0
  if (ft.m_idOle >= 0) o << "oleId=" << ft.m_idOle << ",";
206
0
  o << "),";
207
208
0
  o << ft.m_pos << ",";
209
0
  switch (ft.m_pos.page())
210
0
  {
211
0
  case -1:
212
0
    o << "allpages,";
213
0
    break;
214
0
  case -2:
215
0
    break; // undef
216
0
  default:
217
0
    if (ft.m_pos.page() < 0) o << "###page=" << ft.m_pos.page() << ",";
218
0
  }
219
220
0
  if (ft.m_columns != 1) o << ft.m_columns << "columns,";
221
0
  if (ft.m_idBorder.valid())
222
0
    o << "border='" << ft.m_idBorder.name() << "':" << ft.m_idBorder.id() << ",";
223
0
  if (!ft.m_backgroundColor.isWhite())
224
0
    o << "backColor=" << ft.m_backgroundColor << ",";
225
226
0
  if (!ft.m_error.empty()) o << "errors=(" << ft.m_error << ")";
227
0
  return o;
228
0
}
229
230
//! the state of WPS8
231
struct State
232
{
233
  State()
234
7.96k
    : m_eof(-1)
235
7.96k
    , m_pageSpan()
236
7.96k
    , m_localeLanguage("")
237
7.96k
    , m_background()
238
7.96k
    , m_frameList()
239
7.96k
    , m_object2FrameMap()
240
7.96k
    , m_table2FrameMap()
241
7.96k
    , m_docPropertyTypes()
242
7.96k
    , m_frameTypes()
243
7.96k
    , m_numColumns(1)
244
7.96k
    , m_actPage(0)
245
7.96k
    , m_numPages(0)
246
7.96k
  {
247
7.96k
    initTypeMaps();
248
7.96k
  }
249
  //! initializes the type map
250
  void initTypeMaps();
251
  //! the end of file
252
  long m_eof;
253
  //! the actual document size
254
  WPSPageSpan m_pageSpan;
255
  //! the language
256
  std::string m_localeLanguage;
257
  //! an identificator to design a background picture
258
  WPSEntry m_background;
259
260
  //! the frame's list
261
  std::vector<Frame> m_frameList;
262
  //! a map m_idObject -> frame
263
  std::map<int, int> m_object2FrameMap;
264
  //! a map m_idTable -> frame
265
  std::map<int, int> m_table2FrameMap;
266
267
  //! the document property type
268
  std::map<int, int> m_docPropertyTypes;
269
  //! the frame type
270
  std::map<int, int> m_frameTypes;
271
  int m_numColumns /** the number of columns */;
272
  int m_actPage /** the actual page*/, m_numPages /* the number of pages */;
273
};
274
275
void State::initTypeMaps()
276
7.96k
{
277
7.96k
  static int const docTypes[] =
278
7.96k
  {
279
7.96k
    0, 0x22, 1, 0x22, 2, 0x22, 3, 0x22, 4, 0x22, 5, 0x22, 6, 0x22, 7, 0x22,
280
7.96k
    8, 0x1a, 0xa, 0x2,
281
7.96k
    0x13, 0x2a, 0x15, 0x2a,
282
7.96k
    0x18, 0x12, 0x19, 0x2, 0x1b, 0x12, 0x1c, 0x22, 0x1e, 0x22,
283
7.96k
    0x26, 0x22, 0x27, 0x22,
284
7.96k
    0x28, 0x22, 0x29, 0x22, 0x2a, 0x22, 0x2b, 0x22, 0x2c, 0x82, 0x2d, 0x82, 0x2e, 0x82
285
7.96k
  };
286
215k
  for (int i = 0; i+1 < int(WPS_N_ELEMENTS(docTypes)); i+=2)
287
207k
    m_docPropertyTypes[docTypes[i]] = docTypes[i+1];
288
7.96k
  static int const frameTypes[] =
289
7.96k
  {
290
7.96k
    0, 0x1a, 1, 0x12, 2, 0x12, 3, 0x2, 4, 0x22, 5, 0x22, 6, 0x22, 7, 0x22,
291
7.96k
    8, 0x22, 9, 0x22, 0xa, 0x22,
292
7.96k
    0x10, 0x2a, 0x11, 0x82, 0x13, 0x12, 0x14, 0x12, 0x16, 0x2a, 0x17, 0x2,
293
7.96k
    0x18, 0x22, 0x19, 0x22, 0x1a, 0x12, 0x1b, 0x22, 0x1d, 0x22, 0x1e, 0x22, 0x1f, 0x22,
294
7.96k
    0x20, 0x22, 0x26, 0x22,
295
7.96k
    0x2a, 0x22, 0x2c, 0x1a, 0x2d, 0x1a, 0x2e, 0x22, 0x2f, 0x2,
296
7.96k
    0x30, 0x22
297
7.96k
  };
298
262k
  for (int i = 0; i+1 < int(WPS_N_ELEMENTS(frameTypes)); i+=2)
299
255k
    m_frameTypes[frameTypes[i]] = frameTypes[i+1];
300
301
7.96k
}
302
303
class TableRecursionGuard
304
{
305
  // disable copying
306
  TableRecursionGuard(const TableRecursionGuard &) = delete;
307
  TableRecursionGuard &operator=(const TableRecursionGuard &) = delete;
308
309
public:
310
  TableRecursionGuard(std::deque<int> &stack, const int id)
311
350
    : m_stack(stack)
312
350
  {
313
350
    m_stack.push_front(id);
314
350
  }
315
316
  ~TableRecursionGuard()
317
350
  {
318
350
    m_stack.pop_front();
319
350
  }
320
321
private:
322
  std::deque<int> &m_stack;
323
};
324
325
326
}
327
328
// constructor, destructor
329
WPS8Parser::WPS8Parser(RVNGInputStreamPtr &input, WPSHeaderPtr &header)
330
7.96k
  : WPSParser(input, header)
331
7.96k
  , m_listener()
332
7.96k
  , m_graphParser()
333
7.96k
  , m_tableParser()
334
7.96k
  , m_textParser()
335
7.96k
  , m_state()
336
7.96k
  , m_sendingTables()
337
7.96k
{
338
7.96k
  if (version()<5) setVersion(5);
339
7.96k
  m_state.reset(new WPS8ParserInternal::State);
340
7.96k
  m_graphParser.reset(new WPS8Graph(*this));
341
7.96k
  m_tableParser.reset(new WPS8Table(*this));
342
7.96k
  m_textParser.reset(new WPS8Text(*this));
343
7.96k
}
344
345
WPS8Parser::~WPS8Parser()
346
7.96k
{
347
7.96k
}
348
349
// small funtion ( dimension, ...)
350
float WPS8Parser::pageHeight() const
351
0
{
352
0
  return float(m_state->m_pageSpan.getFormLength()-m_state->m_pageSpan.getMarginTop()-m_state->m_pageSpan.getMarginBottom());
353
0
}
354
355
float WPS8Parser::pageWidth() const
356
10
{
357
10
  return float(m_state->m_pageSpan.getFormWidth()-m_state->m_pageSpan.getMarginLeft()-m_state->m_pageSpan.getMarginRight());
358
10
}
359
360
int WPS8Parser::numColumns() const
361
6.45k
{
362
6.45k
  return m_state->m_numColumns;
363
6.45k
}
364
365
bool WPS8Parser::checkInFile(long pos)
366
0
{
367
0
  if (pos <= m_state->m_eof)
368
0
    return true;
369
0
  RVNGInputStreamPtr input = getInput();
370
0
  long actPos = input->tell();
371
0
  input->seek(pos, librevenge::RVNG_SEEK_SET);
372
0
  bool ok = input->tell() == pos;
373
0
  if (ok) m_state->m_eof = pos;
374
0
  input->seek(actPos, librevenge::RVNG_SEEK_SET);
375
0
  return ok;
376
0
}
377
378
// listener, new page
379
void WPS8Parser::setListener(std::shared_ptr<WPSContentListener> const &listener)
380
7.01k
{
381
7.01k
  m_listener = listener;
382
7.01k
  m_graphParser->setListener(m_listener);
383
7.01k
  m_tableParser->setListener(m_listener);
384
7.01k
  m_textParser->setListener(m_listener);
385
7.01k
}
386
387
std::shared_ptr<WPSContentListener> WPS8Parser::createListener(librevenge::RVNGTextInterface *interface)
388
7.01k
{
389
7.01k
  std::vector<WPSPageSpan> pageList;
390
7.01k
  WPSPageSpan ps(m_state->m_pageSpan);
391
392
7.01k
  int numPages = 1;
393
7.01k
  for (auto const &frame : m_state->m_frameList)
394
290k
  {
395
290k
    if (frame.m_pos.page() > numPages)
396
57
      numPages = frame.m_pos.page();
397
290k
  }
398
399
7.01k
  int textPages = m_textParser->numPages();
400
7.01k
  if (textPages > numPages) numPages = textPages;
401
7.01k
  int tablePages = m_tableParser->numPages();
402
7.01k
  if (tablePages > numPages) numPages = tablePages;
403
7.01k
  int graphPages = m_graphParser->numPages();
404
7.01k
  if (graphPages>=numPages) numPages = graphPages;
405
406
7.01k
  WPSEntry entry = m_textParser->getHeaderEntry();
407
7.01k
  if (entry.valid())
408
326
  {
409
326
    WPSSubDocumentPtr subdoc(new WPS8ParserInternal::SubDocument
410
326
                             (getInput(), *this, entry));
411
326
    ps.setHeaderFooter(WPSPageSpan::HEADER, WPSPageSpan::ALL, subdoc);
412
326
  }
413
414
7.01k
  entry = m_textParser->getFooterEntry();
415
7.01k
  if (entry.valid())
416
400
  {
417
400
    WPSSubDocumentPtr subdoc(new WPS8ParserInternal::SubDocument
418
400
                             (getInput(), *this, entry));
419
400
    ps.setHeaderFooter(WPSPageSpan::FOOTER, WPSPageSpan::ALL, subdoc);
420
400
  }
421
#ifdef DEBUG
422
  // create all the pages + an empty page, if we have some remaining data...
423
  numPages++;
424
#endif
425
426
7.01k
  pageList.push_back(ps);
427
34.6k
  for (int i = 1; i < numPages; i++) pageList.push_back(ps);
428
7.01k
  m_state->m_numPages=numPages;
429
7.01k
  return std::shared_ptr<WPSContentListener>
430
7.01k
         (new WPSContentListener(pageList, interface));
431
7.01k
}
432
433
void WPS8Parser::newPage(int number)
434
24.9k
{
435
24.9k
  if (number <= m_state->m_actPage || number > m_state->m_numPages)
436
75
    return;
437
438
50.6k
  while (m_state->m_actPage < number)
439
25.7k
  {
440
25.7k
    m_state->m_actPage++;
441
442
25.7k
    if (m_listener.get() == nullptr || m_state->m_actPage == 1)
443
891
      continue;
444
24.9k
    m_listener->insertBreak(WPS_PAGE_BREAK);
445
24.9k
    m_graphParser->sendObjects(m_state->m_actPage);
446
24.9k
  }
447
24.9k
}
448
449
////////////////////////////////////////////////////////////
450
// interface with the graph/text parser
451
void WPS8Parser::send(WPSEntry const &entry)
452
4.71k
{
453
4.71k
  RVNGInputStreamPtr input = getInput();
454
4.71k
  long actPos = input->tell();
455
4.71k
  m_textParser->readText(entry);
456
4.71k
  input->seek(actPos, librevenge::RVNG_SEEK_SET);
457
4.71k
}
458
459
void WPS8Parser::send(int strsId)
460
0
{
461
0
  send(m_textParser->getEntry(strsId));
462
0
}
463
464
void WPS8Parser::sendTextInCell(int strsId, int cellId)
465
835
{
466
835
  RVNGInputStreamPtr input = getInput();
467
835
  long actPos = input->tell();
468
835
  m_textParser->readTextInCell(strsId,cellId);
469
835
  input->seek(actPos, librevenge::RVNG_SEEK_SET);
470
835
}
471
472
void WPS8Parser::sendTextBox(WPSPosition const &pos, int strsid, librevenge::RVNGPropertyList frameExtras)
473
11.9k
{
474
11.9k
  if (!m_listener)
475
0
  {
476
0
    WPS_DEBUG_MSG(("WPS8Parser::sendTextBox can not find the listener\n"));
477
0
    return;
478
0
  }
479
11.9k
  WPSEntry entry = m_textParser->getEntry(strsid);
480
11.9k
  WPSSubDocumentPtr subdoc(new WPS8ParserInternal::SubDocument
481
11.9k
                           (getInput(), *this, entry));
482
11.9k
  m_listener->insertTextBox(pos, subdoc, frameExtras);
483
11.9k
}
484
485
bool WPS8Parser::sendObject(Vec2f const &size, int objectId, bool ole)
486
211
{
487
211
  WPSPosition posi(Vec2f(),size);
488
211
  posi.setRelativePosition(WPSPosition::CharBaseLine);
489
211
  posi.m_wrapping = WPSPosition::WDynamic;
490
211
  m_graphParser->sendObject(posi,objectId,ole);
491
211
  return true;
492
211
}
493
494
bool WPS8Parser::sendTable(Vec2f const &size, int objectId)
495
780
{
496
780
  if (find(m_sendingTables.begin(), m_sendingTables.end(), objectId) != m_sendingTables.end())
497
250
  {
498
250
    WPS_DEBUG_MSG(("WPS8Parser::sendTable table %d is contained in itself\n", objectId));
499
250
    return false;
500
250
  }
501
502
530
  auto pos = m_state->m_object2FrameMap.find(objectId);
503
530
  if (pos == m_state->m_object2FrameMap.end())
504
121
  {
505
121
    WPS_DEBUG_MSG(("WPS8Parser::sendTable can not find the table %d \n", objectId));
506
121
    return false;
507
121
  }
508
509
409
  auto const &frame = m_state->m_frameList[size_t(pos->second)];
510
409
  if (frame.m_idStrs < 0)
511
43
  {
512
43
    WPS_DEBUG_MSG(("WPS8Parser:sendTable can not find the text zone \n"));
513
43
    return false;
514
43
  }
515
516
366
  if (frame.m_idTable < 0)
517
16
  {
518
16
    WPSPosition position(Vec2f(), size);
519
16
    position.m_anchorTo = WPSPosition::CharBaseLine; // CHECKME
520
16
    position.m_wrapping = WPSPosition::WDynamic;
521
522
16
    WPS_DEBUG_MSG(("WPS8Parser:sendTable can not find the table zone \n"));
523
16
    sendTextBox(position, frame.m_idStrs);
524
16
    return true;
525
16
  }
526
350
  frame.m_parsed = true;
527
350
  WPS8ParserInternal::TableRecursionGuard guard(m_sendingTables, objectId);
528
350
  return m_tableParser->sendTable(size, frame.m_idTable, frame.m_idStrs);
529
366
}
530
531
int WPS8Parser::getTableSTRSId(int tableId) const
532
0
{
533
0
  auto pos = m_state->m_table2FrameMap.find(tableId);
534
  // probably ok: checkme
535
0
  if (pos == m_state->m_table2FrameMap.end())
536
0
    return -1;
537
0
  auto const &frame = m_state->m_frameList[size_t(pos->second)];
538
0
  if (frame.m_idStrs < 0)
539
0
  {
540
0
    WPS_DEBUG_MSG(("WPS8Parser:getTableSTRSId can not find the text zone \n"));
541
0
  }
542
0
  return frame.m_idStrs;
543
0
}
544
545
////////////////////////////////////////////////////////////
546
// main function to parse the document
547
void WPS8Parser::parse(librevenge::RVNGTextInterface *documentInterface)
548
7.96k
{
549
7.96k
  RVNGInputStreamPtr input=getInput();
550
7.96k
  if (!input)
551
0
  {
552
0
    WPS_DEBUG_MSG(("WPS8Parser::parse: does not find main ole\n"));
553
0
    throw (libwps::ParseException());
554
0
  }
555
556
7.96k
  try
557
7.96k
  {
558
7.96k
    createOLEStructures();
559
7.96k
  }
560
7.96k
  catch (...)
561
7.96k
  {
562
0
    WPS_DEBUG_MSG(("WPS8Parser::parse: exception catched when parsing secondary OLEs\n"));
563
0
  }
564
565
7.96k
  ascii().setStream(input);
566
7.96k
  ascii().open("CONTENTS");
567
7.96k
  try
568
7.96k
  {
569
7.96k
    if (!createStructures()) throw(libwps::ParseException());
570
7.96k
  }
571
7.96k
  catch (...)
572
7.96k
  {
573
958
    WPS_DEBUG_MSG(("WPS8Parser::parse: exception catched when parsing MN0\n"));
574
958
    throw (libwps::ParseException());
575
958
  }
576
7.01k
  setListener(createListener(documentInterface));
577
7.01k
  if (!m_listener)
578
0
  {
579
0
    WPS_DEBUG_MSG(("WPS8Parser::parse: can not create the listener\n"));
580
0
    throw (libwps::ParseException());
581
0
  }
582
7.01k
  m_listener->startDocument();
583
7.01k
  sendPageFrames();
584
7.01k
  WPSEntry ent = m_textParser->getTextEntry();
585
7.01k
  if (ent.valid())
586
6.31k
    m_textParser->readText(ent);
587
693
  else
588
693
  {
589
693
    WPS_DEBUG_MSG(("WPS8Parser::parse: can not find main text entry\n"));
590
693
    throw (libwps::ParseException());
591
693
  }
592
593
#ifdef DEBUG
594
  m_tableParser->flushExtra();
595
#endif
596
6.31k
  m_textParser->flushExtra();
597
#ifdef DEBUG
598
  m_graphParser->sendObjects(-1);
599
#endif
600
601
6.31k
  m_listener->endDocument();
602
6.31k
  m_listener.reset();
603
604
6.31k
  ascii().reset();
605
6.31k
}
606
607
// find and create all the zones ( normal/ole )
608
bool WPS8Parser::createStructures()
609
7.96k
{
610
7.96k
  if (!getInput()) return false;
611
612
7.96k
  RVNGInputStreamPtr input = getInput();
613
7.96k
  parseHeaderIndex();
614
615
  // initialize the text, table, ..
616
7.96k
  if (!m_textParser->readStructures())
617
810
    return false;
618
7.15k
  m_graphParser->readStructures(input);
619
7.15k
  m_tableParser->readStructures(input);
620
621
  // read DOP zone (document properties)
622
7.15k
  auto pos = getNameEntryMap().lower_bound("DOP ");
623
647k
  while (getNameEntryMap().end() != pos)
624
647k
  {
625
647k
    WPSEntry const &entry = pos++->second;
626
647k
    if (!entry.hasName("DOP ")) break;
627
640k
    if (!entry.hasType("DOP ")) continue;
628
629
606k
    WPSPageSpan page;
630
606k
    if (readDocProperties(entry, page)) m_state->m_pageSpan = page;
631
606k
  }
632
633
  // printer data
634
7.15k
  pos = getNameEntryMap().lower_bound("PRNT");
635
418k
  while (getNameEntryMap().end() != pos)
636
418k
  {
637
418k
    WPSEntry const &entry = pos++->second;
638
418k
    if (!entry.hasName("PRNT")) break;
639
411k
    if (!entry.hasType("WNPR")) continue;
640
641
329k
    readWNPR(entry);
642
329k
  }
643
644
  // SYID
645
7.15k
  pos = getNameEntryMap().lower_bound("SYID");
646
174k
  while (getNameEntryMap().end() != pos)
647
174k
  {
648
174k
    WPSEntry const &entry = pos++->second;
649
174k
    if (!entry.hasName("SYID")) break;
650
167k
    if (!entry.hasType("SYID")) continue;
651
652
106k
    std::vector<int> listId;
653
106k
    readSYID(entry, listId);
654
106k
  }
655
656
  // document title
657
7.15k
  pos = getNameEntryMap().find("TITL");
658
7.15k
  librevenge::RVNGString title;
659
7.15k
  if (getNameEntryMap().end() != pos && pos->second.hasType("TITL"))
660
1.30k
  {
661
1.30k
    pos->second.setParsed(true);
662
1.30k
    input->seek(pos->second.begin(), librevenge::RVNG_SEEK_SET);
663
1.30k
    m_textParser->readString(input, pos->second.length(), title);
664
1.30k
    ascii().addPos(pos->second.begin());
665
1.30k
    ascii().addNote(title.cstr());
666
1.30k
  }
667
668
  // ok, we can now read the frame
669
7.15k
  pos = getNameEntryMap().lower_bound("FRAM");
670
801k
  while (getNameEntryMap().end() != pos)
671
801k
  {
672
801k
    WPSEntry const &entry = pos++->second;
673
801k
    if (!entry.hasName("FRAM")) break;
674
794k
    if (!entry.hasType("FRAM")) continue;
675
676
695k
    readFRAM(entry);
677
695k
  }
678
  // FRCD
679
7.15k
  pos = getNameEntryMap().lower_bound("FRCD");
680
448k
  while (getNameEntryMap().end() != pos)
681
447k
  {
682
447k
    WPSEntry const &entry = pos++->second;
683
447k
    if (!entry.hasName("FRCD")) break;
684
440k
    if (!entry.hasType("FRCD")) continue;
685
263k
    readFRCD(entry);
686
263k
  }
687
688
  // creates the correspondance between the eobj and the frame
689
7.15k
  size_t numFrames = m_state->m_frameList.size();
690
297k
  for (size_t i = 0; i < numFrames; i++)
691
290k
  {
692
290k
    WPS8ParserInternal::Frame const &frame = m_state->m_frameList[i];
693
290k
    if (frame.m_idObject < 0) continue;
694
367
    m_state->m_object2FrameMap[frame.m_idObject] = int(i);
695
367
    m_state->m_table2FrameMap[frame.m_idTable] = int(i);
696
367
  }
697
698
7.15k
  m_graphParser->computePositions();
699
700
7.15k
  return true;
701
7.96k
}
702
703
bool WPS8Parser::createOLEStructures()
704
7.96k
{
705
7.96k
  RVNGInputStreamPtr input=getFileInput();
706
7.96k
  if (!input) return false;
707
708
7.96k
  if (!input->isStructured()) return true;
709
7.96k
  WPSOLEParser oleParser("CONTENTS", libwps_tools_win::Font::WIN3_WEUROPE);
710
7.96k
  if (!oleParser.parse(input)) return false;
711
712
7.96k
  m_graphParser->storeObjects(oleParser.getObjectsMap());
713
#ifdef DEBUG
714
  // there can remain some embedded Works subdocument ( WKS, ... )
715
  // with name MN0 and some unknown picture ole
716
  auto unparsed = oleParser.getNotParse();
717
718
  for (std::string const &name : unparsed)
719
  {
720
    if (name == "CONTENTS")
721
      continue;
722
    if (name== "SPELLING")
723
    {
724
      RVNGInputStreamPtr ole(input->getSubStreamByName(name.c_str()));
725
      if (ole && readSPELLING(ole, name))
726
        continue;
727
    }
728
    WPS_DEBUG_MSG(("WPS8Parser::createOLEStructures:: Find unparsed ole: %s\n", name.c_str()));
729
730
#ifdef DEBUG_WITH_FILES
731
    RVNGInputStreamPtr ole(input->getSubStreamByName(name.c_str()));
732
    if (!ole.get())
733
    {
734
      WPS_DEBUG_MSG(("WPS8Parser::createOLEStructures: error: can find OLE part: \"%s\"\n", name.c_str()));
735
      continue;
736
    }
737
738
    librevenge::RVNGBinaryData data;
739
    if (libwps::readDataToEnd(ole,data))
740
      libwps::Debug::dumpFile(data, name.c_str());
741
#endif
742
  }
743
#endif
744
7.96k
  return true;
745
7.96k
}
746
747
////////////////////////////////////////////////////////////
748
// send the frames which correspond to a page
749
////////////////////////////////////////////////////////////
750
void WPS8Parser::sendPageFrames()
751
7.01k
{
752
7.01k
  if (!m_listener) return;
753
754
7.01k
  RVNGInputStreamPtr input = getInput();
755
7.01k
  long actPos = input->tell();
756
757
7.01k
  for (auto const &frame : m_state->m_frameList)
758
290k
  {
759
290k
    if (frame.m_parsed) continue;
760
290k
    if (frame.m_pos.page() < 0 || frame.m_idObject != -1) continue;
761
268k
    if (frame.m_pos.size()[0] <= 0 || frame.m_pos.size()[1] <= 0) continue;
762
763
62.5k
    WPSPosition pos(frame.m_pos);
764
62.5k
    if (pos.origin().y() < 0 || pos.origin().x() < 0)
765
39.8k
    {
766
39.8k
      if (frame.m_type == WPS8ParserInternal::Frame::DbField) continue;
767
      // FIXME: the
768
30.9k
      WPS_DEBUG_MSG(("WPS8Parser::insertPageFrames: origin is too small\n"));
769
30.9k
      Vec2f orig = pos.origin();
770
30.9k
      orig += Vec2f(float(m_state->m_pageSpan.getMarginLeft()),
771
30.9k
                    float(m_state->m_pageSpan.getMarginTop()));
772
30.9k
      pos.setOrigin(orig);
773
30.9k
    }
774
53.7k
    pos.m_anchorTo = WPSPosition::Page;
775
53.7k
    pos.m_wrapping = WPSPosition::WDynamic;
776
53.7k
    frame.m_parsed = true;
777
53.7k
    if (frame.m_type == WPS8ParserInternal::Frame::Object)
778
6.46k
      m_graphParser->sendObject(pos, frame.m_idOle, true);
779
47.2k
    else if (frame.m_type == WPS8ParserInternal::Frame::Text)
780
11.8k
    {
781
11.8k
      librevenge::RVNGPropertyList frameExtras;
782
11.8k
      if (!frame.m_backgroundColor.isWhite())
783
2.80k
        frameExtras.insert("fo:background-color", frame.m_backgroundColor.str().c_str());
784
11.8k
      sendTextBox(pos, frame.m_idStrs, frameExtras);
785
11.8k
    }
786
35.3k
    else if (frame.m_type != WPS8ParserInternal::Frame::Header && frame.m_type != WPS8ParserInternal::Frame::Footer)
787
26.0k
    {
788
26.0k
      WPS_DEBUG_MSG(("WPS8Parser::insertPageFrames: not implemented type%d\n", int(frame.m_type)));
789
26.0k
    }
790
53.7k
  }
791
792
7.01k
  input->seek(actPos,librevenge::RVNG_SEEK_SET);
793
7.01k
}
794
795
////////////////////////////////////////////////////////////
796
// read the index
797
bool WPS8Parser::parseHeaderIndexEntry()
798
37.1M
{
799
37.1M
  RVNGInputStreamPtr input = getInput();
800
37.1M
  long pos = input->tell();
801
37.1M
  ascii().addPos(pos);
802
803
37.1M
  libwps::DebugStream f;
804
805
37.1M
  uint16_t cch = libwps::readU16(input);
806
807
  // check if the entry can be read
808
37.1M
  input->seek(pos + cch, librevenge::RVNG_SEEK_SET);
809
37.1M
  if (input->tell() != pos+cch)
810
2.10k
  {
811
2.10k
    WPS_DEBUG_MSG(("WPS8Parser::parseHeaderIndexEntry: error: incomplete entry\n"));
812
2.10k
    ascii().addNote("###IndexEntry incomplete (ignored)");
813
2.10k
    return false;
814
2.10k
  }
815
37.1M
  input->seek(pos + 2, librevenge::RVNG_SEEK_SET);
816
817
37.1M
  if (0x18 != cch)
818
1.05M
  {
819
1.05M
    if (cch < 0x18)
820
114k
    {
821
114k
      input->seek(pos + cch, librevenge::RVNG_SEEK_SET);
822
114k
      ascii().addNote("###IndexEntry too short(ignored)");
823
114k
      if (cch < 10) throw libwps::ParseException();
824
114k
      return true;
825
114k
    }
826
1.05M
  }
827
828
37.0M
  std::string name;
829
830
  // sanity check
831
37.0M
  int i;
832
175M
  for (i =0; i < 4; i++)
833
141M
  {
834
141M
    uint8_t c = libwps::readU8(input);
835
141M
    name.append(1, char(c));
836
837
141M
    if (c != 0 && c != 0x20 && (41 > c || c > 90))
838
3.78M
    {
839
3.78M
      WPS_DEBUG_MSG(("WPS8Parser::parseHeaderIndexEntry: error: bad character=%u (0x%02x) in name in header index\n", unsigned(c), unsigned(c)));
840
3.78M
      ascii().addNote("###IndexEntry bad name(ignored)");
841
842
3.78M
      input->seek(pos + cch, librevenge::RVNG_SEEK_SET);
843
3.78M
      return true;
844
3.78M
    }
845
141M
  }
846
847
33.2M
  f << "Entries("<<name<<")";
848
33.2M
  if (cch != 24) f << ", #size=" << int(cch);
849
33.2M
  auto id = int(libwps::readU16(input));
850
33.2M
  f << ", id=" << id << ", (";
851
99.8M
  for (i = 0; i < 2; i ++)
852
66.5M
  {
853
66.5M
    auto val = int(libwps::read16(input));
854
66.5M
    f << val << ",";
855
66.5M
  }
856
857
33.2M
  std::string name2;
858
166M
  for (i =0; i < 4; i++)
859
133M
    name2.append(1, char(libwps::readU8(input)));
860
33.2M
  f << "), " << name2;
861
862
33.2M
  WPSEntry hie;
863
33.2M
  hie.setName(name);
864
33.2M
  hie.setType(name2);
865
33.2M
  hie.setId(id);
866
33.2M
  hie.setBegin(long(libwps::readU32(input)));
867
33.2M
  hie.setLength(long(libwps::readU32(input)));
868
869
33.2M
  f << ", offset=" << std::hex << hie.begin() << ", length=" << hie.length();
870
871
33.2M
  std::string mess;
872
33.2M
  if (cch != 0x18 && parseHeaderIndexEntryEnd(pos+cch, hie, mess))
873
599k
    f << "," << mess;
874
875
33.2M
  input->seek(hie.end(), librevenge::RVNG_SEEK_SET);
876
33.2M
  if (input->tell() != hie.end())
877
11.7M
  {
878
11.7M
    f << ", ###ignored";
879
11.7M
    ascii().addNote(f.str().c_str());
880
11.7M
    input->seek(pos + cch, librevenge::RVNG_SEEK_SET);
881
11.7M
    return true;
882
11.7M
  }
883
884
21.5M
  getNameEntryMap().insert(NameMultiMap::value_type(name, hie));
885
886
21.5M
  ascii().addPos(pos);
887
21.5M
  ascii().addNote(f.str().c_str());
888
889
21.5M
  ascii().addPos(hie.begin());
890
21.5M
  f.str("");
891
21.5M
  f << name;
892
21.5M
  if (name != name2) f << "/" << name2;
893
21.5M
  f << ":" << std::dec << id;
894
21.5M
  ascii().addNote(f.str().c_str());
895
896
21.5M
  ascii().addPos(hie.end());
897
21.5M
  ascii().addNote("_");
898
899
21.5M
  input->seek(pos + cch, librevenge::RVNG_SEEK_SET);
900
21.5M
  return true;
901
33.2M
}
902
903
//
904
// read the end the entry index: normally a string name
905
//
906
bool WPS8Parser::parseHeaderIndexEntryEnd(long endPos, WPSEntry &hie, std::string &mess)
907
599k
{
908
599k
  RVNGInputStreamPtr input = getInput();
909
910
599k
  long pos = input->tell();
911
599k
  long len = endPos-pos;
912
599k
  libwps::DebugStream f;
913
914
599k
  auto size = int(libwps::read16(input));
915
599k
  librevenge::RVNGString str;
916
599k
  if (2*(size+1) != len || !m_textParser->readString(input, 2*size, str))
917
557k
  {
918
557k
    WPS_DEBUG_MSG(("WPS8Parser::parseHeaderIndexEntryEnd: unknown data\n"));
919
557k
    ascii().addPos(pos);
920
557k
    ascii().addNote("Entry(end): ###ignored");
921
557k
  }
922
41.9k
  else
923
41.9k
  {
924
41.9k
    hie.setExtra(str.cstr());
925
41.9k
    f << "'" << str.cstr() << "'";
926
41.9k
    mess = f.str();
927
41.9k
  }
928
599k
  return true;
929
599k
}
930
931
/**
932
 * In the header, parse the index to the different sections of
933
 * the CONTENTS stream.
934
 *
935
 */
936
bool WPS8Parser::parseHeaderIndex()
937
7.96k
{
938
7.96k
  RVNGInputStreamPtr input = getInput();
939
7.96k
  getNameEntryMap().clear();
940
7.96k
  input->seek(0x08, librevenge::RVNG_SEEK_SET);
941
942
7.96k
  long pos = input->tell();
943
7.96k
  auto i0 = int(libwps::read16(input));
944
7.96k
  auto i1 = int(libwps::read16(input));
945
7.96k
  uint16_t n_entries = libwps::readU16(input);
946
  // fixme: sanity check n_entries
947
948
7.96k
  libwps::DebugStream f;
949
7.96k
  f << "Entries(HeaderZ): N=" << n_entries << ", " << i0 << ", " << i1 << "(";
950
951
39.8k
  for (int i = 0; i < 4; i++)
952
31.8k
    f << std::hex << libwps::read16(input) << std::dec << ",";
953
7.96k
  f << "), ";
954
7.96k
  f << "unk=" << std::hex <<libwps::read16(input) << std::dec << ",";
955
956
7.96k
  ascii().addPos(pos);
957
7.96k
  ascii().addNote(f.str().c_str());
958
959
7.96k
  input->seek(0x18, librevenge::RVNG_SEEK_SET);
960
7.96k
  bool readSome = false;
961
7.96k
  do
962
3.98M
  {
963
3.98M
    if (input->isEnd()) return readSome;
964
965
3.98M
    pos = input->tell();
966
3.98M
    f.str("");
967
3.98M
    uint16_t unknown1 = libwps::readU16(input); // function of the size of the entries ?
968
969
3.98M
    uint16_t n_entries_local = libwps::readU16(input);
970
3.98M
    f << "HeaderZ("<<std::hex << unknown1<<"): N=" << std::dec << n_entries_local;
971
972
3.98M
    if (n_entries_local > 0x20)
973
529
    {
974
529
      WPS_DEBUG_MSG(("WPS8Parser::parseHeaderIndex: error: n_entries_local=%i\n", n_entries_local));
975
529
      return readSome;
976
529
    }
977
978
3.98M
    uint32_t next_index_table = libwps::readU32(input);
979
3.98M
    f << std::hex << ", nextHeader=" << next_index_table;
980
3.98M
    if (next_index_table != 0xFFFFFFFF && long(next_index_table) < pos)
981
212
    {
982
212
      WPS_DEBUG_MSG(("WPS8Parser::parseHeaderIndex: error: next_index_table=%x decreasing !!!\n", next_index_table));
983
212
      return readSome;
984
212
    }
985
986
3.98M
    ascii().addPos(pos);
987
3.98M
    ascii().addNote(f.str().c_str());
988
989
3.98M
    do
990
37.1M
    {
991
37.1M
      if (!parseHeaderIndexEntry()) return readSome;
992
993
37.1M
      readSome=true;
994
37.1M
      n_entries--;
995
37.1M
      n_entries_local--;
996
37.1M
    }
997
37.1M
    while (n_entries > 0 && n_entries_local);
998
999
3.98M
    if (0xFFFFFFFF == next_index_table && n_entries > 0)
1000
134
    {
1001
134
      WPS_DEBUG_MSG(("WPS8Parser::parseHeaderIndex: error: expected more header index entries\n"));
1002
134
      return true;
1003
134
    }
1004
1005
3.98M
    if (0xFFFFFFFF == next_index_table)  break;
1006
1007
3.98M
    if (input->seek(long(next_index_table), librevenge::RVNG_SEEK_SET) != 0) return readSome;
1008
3.98M
  }
1009
3.98M
  while (n_entries > 0);
1010
1011
4.58k
  return true;
1012
7.96k
}
1013
1014
////////////////////////////////////////////////////////////
1015
// low level
1016
////////////////////////////////////////////////////////////
1017
// read DOP zone: the document properties
1018
bool WPS8Parser::readDocProperties(WPSEntry const &entry, WPSPageSpan &page)
1019
606k
{
1020
606k
  if (!entry.hasType(entry.name()))
1021
0
  {
1022
0
    WPS_DEBUG_MSG(("WPS8Parser::readDocProperties: warning: DOP name=%s, type=%s\n",
1023
0
                   entry.name().c_str(), entry.type().c_str()));
1024
0
    return false;
1025
0
  }
1026
1027
606k
  RVNGInputStreamPtr input = getInput();
1028
606k
  page=WPSPageSpan();
1029
1030
606k
  long page_offset = entry.begin();
1031
606k
  long length = entry.length();
1032
606k
  long endPage = entry.end();
1033
1034
606k
  if (length < 2)
1035
74.9k
  {
1036
74.9k
    WPS_DEBUG_MSG(("WPS8Parser::readDocProperties: warning: DOP length=0x%lx\n", static_cast<unsigned long>(length)));
1037
74.9k
    return false;
1038
74.9k
  }
1039
1040
531k
  entry.setParsed();
1041
531k
  input->seek(page_offset, librevenge::RVNG_SEEK_SET);
1042
1043
531k
  libwps::DebugStream f, f2;
1044
531k
  if (libwps::read16(input) != length)
1045
124k
  {
1046
124k
    WPS_DEBUG_MSG(("WPS8Parser::readDocProperties: invalid length=%ld\n", length));
1047
124k
    return false;
1048
124k
  }
1049
1050
406k
  WPS8Struct::FileData mainData;
1051
406k
  std::string error;
1052
406k
  bool readOk = readBlockData(input, endPage,mainData, error);
1053
1054
406k
  double dim[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1055
406k
  bool setVal[8] = { false, false, false, false, false, false, false, false};
1056
406k
  for (auto const &dt : mainData.m_recursData)
1057
2.25M
  {
1058
2.25M
    if (dt.isBad()) continue;
1059
2.25M
    if (m_state->m_docPropertyTypes.find(dt.id())==m_state->m_docPropertyTypes.end())
1060
168k
    {
1061
168k
      f << "##" << dt << ",";
1062
168k
      continue;
1063
168k
    }
1064
2.08M
    if (m_state->m_docPropertyTypes.find(dt.id())->second != dt.type())
1065
196k
    {
1066
196k
      WPS_DEBUG_MSG(("WPS8Parser::readDocProperties: unexpected type for %d=%d\n", dt.id(), dt.type()));
1067
196k
      f << "###" << dt << ",";
1068
196k
      continue;
1069
196k
    }
1070
    // is dt.id()==6 another dimension ?
1071
1.88M
    if (dt.id() >= 0 && (dt.id() < 6 || dt.id()==7))
1072
401k
    {
1073
401k
      dim[dt.id()] = double(dt.m_value)/914400;
1074
401k
      setVal[dt.id()] = true;
1075
401k
      continue;
1076
401k
    }
1077
1.48M
    bool ok = true;
1078
1.48M
    switch (dt.id())
1079
1.48M
    {
1080
7
    case 0x8:
1081
7
      if (!dt.m_value) break;
1082
6
      if (dt.m_value >= 1 && dt.m_value <= 13)
1083
5
        m_state->m_numColumns = int(1+dt.m_value);
1084
1
      else
1085
1
        f2 << "#";
1086
6
      f2 << "numCols=" << dt.m_value+1 << ",";
1087
6
      break;
1088
338k
    case 0x18:
1089
338k
      switch (dt.m_value)
1090
338k
      {
1091
6.24k
      case 1:
1092
6.24k
        break; // portrait
1093
187k
      case 2:
1094
187k
        page.setFormOrientation(WPSPageSpan::LANDSCAPE);
1095
187k
        f2 << "landscape,";
1096
187k
        break;
1097
144k
      default:
1098
144k
        f2 << "#pageOrientation=" << dt.m_value << ",";
1099
144k
        break;
1100
338k
      }
1101
338k
      break;
1102
338k
    case 0x1b:   // [-1,0,1,3,4]
1103
0
      f2 << "f" << dt.id() << "=" << int(int8_t(dt.m_value)) << ",";
1104
0
      break;
1105
0
    case 0x13:   // an entries index, always an IBGF ?
1106
0
      m_state->m_background.setName(dt.m_text);
1107
0
      m_state->m_background.setId(int(dt.m_value));
1108
0
      f2 << "background(entries)='" << dt.m_text << "':" << dt.m_value << ",";
1109
0
      break;
1110
0
    case 0x15:   // another entries index, always an 'BDR '?
1111
0
      WPS_DEBUG_MSG(("WPS8Parser::readDocProperties: find a BDR entry, not implemented\n"));
1112
0
      f2 << "pageBorder(entries)='" << dt.m_text << "':" << dt.m_value << ",";
1113
0
      break;
1114
341k
    case 0xa:
1115
341k
      if (dt.isTrue()) f2 << "colSep(line),";
1116
341k
      break;
1117
326k
    case 0x19: // almost always set
1118
326k
      if (dt.isFalse())
1119
0
        f2 <<  "f" << dt.id() << "=false,";
1120
326k
      else
1121
326k
        f2 <<  "f" << dt.id() << ",";
1122
326k
      break;
1123
7
    case 0x1c:   // 0.1
1124
7
      f2 <<  "colSep=" << float(dt.m_value)/914400.f << "(inch),";
1125
7
      break;
1126
339k
    case 0x28:   // main language ?
1127
339k
      f2 << "lang?=" << libwps_tools_win::Language::name(dt.m_value) << ",";
1128
339k
      m_state->m_localeLanguage = libwps_tools_win::Language::localeName(dt.m_value);
1129
339k
      break;
1130
0
    case 0x1e: // -3, 47 ou 85
1131
168
    case 0x29: // 1 ou 2
1132
168
      f2 <<  "f" << dt.id() << "=" << dt.m_value << ",";
1133
168
      break;
1134
    // 0x2a: an integer = 4|6|b
1135
    // 0x2b: an integer = 0|8|c
1136
0
    case 0x2c:
1137
0
    {
1138
0
      if (dt.isRead() || !dt.isArray())
1139
0
      {
1140
0
        ok = false;
1141
0
        break;
1142
0
      }
1143
0
      auto size = int(dt.end()-dt.begin());
1144
0
      if (size < 2 || size%2 != 0)
1145
0
      {
1146
0
        ok = false;
1147
0
        break;
1148
0
      }
1149
0
      long actPos = input->tell();
1150
0
      int numElt = (size-2)/2;
1151
1152
0
      input->seek(dt.begin()+2, librevenge::RVNG_SEEK_SET);
1153
0
      std::string str;
1154
0
      int elt=0;
1155
0
      while (elt < numElt)
1156
0
      {
1157
0
        long val = libwps::read16(input);
1158
0
        if (val < 30 && val != '\t' && val != '\n')
1159
0
        {
1160
0
          input->seek(-2, librevenge::RVNG_SEEK_CUR);
1161
0
          break;
1162
0
        }
1163
0
        elt++;
1164
0
        str += char(val);
1165
0
      }
1166
0
      if (!str.length())
1167
0
      {
1168
0
        ok = false;
1169
0
        input->seek(actPos, librevenge::RVNG_SEEK_SET);
1170
0
        break;
1171
0
      }
1172
0
      f2 << "filename?=["; // wkthmLET.fmt and wkthmSCH.fmt
1173
0
      if (str.length()) f2 << "\"" << str << "\",";
1174
0
      numElt -= elt;
1175
0
      elt=0;
1176
0
      while (elt++ < numElt)
1177
0
      {
1178
0
        auto val = long(libwps::readU16(input));
1179
0
        if (val) f2 << "f" << elt << "=" << std::hex << val << std::dec << ",";
1180
0
      }
1181
0
      f2 << "],";
1182
0
      input->seek(actPos, librevenge::RVNG_SEEK_SET);
1183
0
      break;
1184
0
    }
1185
0
    case 0x2d:
1186
0
    case 0x2e:
1187
0
    {
1188
      // In one file with unk2d=[f1=20,], unk2e=[f2=20,]
1189
0
      if (dt.isRead() || !dt.isArray())
1190
0
      {
1191
0
        ok = false;
1192
0
        break;
1193
0
      }
1194
0
      auto size = int(dt.end()-dt.begin());
1195
0
      if (size != 0x100)
1196
0
      {
1197
0
        ok = false;
1198
0
        break;
1199
0
      }
1200
0
      long actPos = input->tell();
1201
0
      input->seek(dt.begin(), librevenge::RVNG_SEEK_SET);
1202
0
      f2 << "unk" << std::hex << dt.id() << "=" << "[";
1203
0
      for (int i = 0; i < 128; i++)
1204
0
      {
1205
0
        auto val = long(libwps::readU16(input));
1206
0
        if (val) f2 << "f" << i << "=" << std::hex << val << std::dec << ",";
1207
0
      }
1208
0
      f2 << "]" << std::dec << ",";
1209
0
      input->seek(actPos, librevenge::RVNG_SEEK_SET);
1210
0
      break;
1211
0
    }
1212
1213
141k
    default:
1214
141k
      ok = false;
1215
141k
      break;
1216
1.48M
    }
1217
1.48M
    if (ok) continue;
1218
141k
    f2 << dt << ",";
1219
141k
  }
1220
406k
  if (setVal[0])
1221
10.2k
  {
1222
10.2k
    if (dim[0] < 0.5 || dim[0] > 40) f << "###";
1223
3.15k
    else page.setFormWidth(dim[0]);
1224
10.2k
    f << "width=" << dim[0] << ",";
1225
10.2k
  }
1226
406k
  if (setVal[1])
1227
16.9k
  {
1228
16.9k
    if (dim[1] < 0.5 || dim[1] > 40) f << "###";
1229
4.16k
    else page.setFormLength(dim[1]);
1230
16.9k
    f << "height=" << dim[1] << ",";
1231
16.9k
  }
1232
406k
  f << "margin=[";
1233
2.03M
  for (int i = 2; i < 6; i++)
1234
1.62M
  {
1235
1.62M
    if (!setVal[i])
1236
1.41M
    {
1237
1.41M
      f << "_,";
1238
1.41M
      continue;
1239
1.41M
    }
1240
214k
    int w = i%2;
1241
214k
    double dd = w ? page.getFormWidth() : page.getFormLength();
1242
214k
    bool ok = (dim[i] >= 0) && (2*dim[i] < dd);
1243
214k
    switch (i)
1244
214k
    {
1245
17.3k
    case 2:
1246
17.3k
      if (ok) page.setMarginTop(dim[i]);
1247
17.3k
      break;
1248
13.8k
    case 3:
1249
13.8k
      if (ok) page.setMarginLeft(dim[i]);
1250
13.8k
      break;
1251
17.3k
    case 4:
1252
17.3k
      if (ok) page.setMarginBottom(dim[i]);
1253
17.3k
      break;
1254
166k
    case 5:
1255
166k
      if (ok) page.setMarginRight(dim[i]);
1256
166k
      break;
1257
0
    default:
1258
0
      break;
1259
214k
    }
1260
214k
    if (!ok) f << "###";
1261
214k
    f << dim[i] << ",";
1262
214k
  }
1263
406k
  f << "],";
1264
406k
  if (setVal[7]) f << "bordDim?=" << dim[7] << ",";
1265
406k
  f << f2.str();
1266
1267
406k
  if (!readOk) f << "###or [" << mainData << "]";
1268
1269
406k
  ascii().addPos(page_offset);
1270
406k
  ascii().addNote(f.str().c_str());
1271
1272
406k
  return true;
1273
406k
}
1274
1275
/** Frame zone: ie a zone which can contains text, picture, ...
1276
    and have some border */
1277
bool WPS8Parser::readFRAM(WPSEntry const &entry)
1278
695k
{
1279
695k
  typedef WPS8ParserInternal::Frame Frame;
1280
695k
  libwps::DebugStream f;
1281
695k
  if (!entry.hasType(entry.name()))
1282
0
  {
1283
0
    WPS_DEBUG_MSG(("WPS8Parser::readFRAM warning: name=%s, type=%s\n",
1284
0
                   entry.name().c_str(), entry.type().c_str()));
1285
0
    return false;
1286
0
  }
1287
695k
  RVNGInputStreamPtr input = getInput();
1288
1289
695k
  long page_offset = entry.begin();
1290
695k
  long length = entry.length();
1291
695k
  long endPage = entry.end();
1292
1293
695k
  if (length < 2)
1294
12.8k
  {
1295
12.8k
    WPS_DEBUG_MSG(("WPS8Parser::readFRAM warning: length=0x%lx\n", static_cast<unsigned long>(length)));
1296
12.8k
    return false;
1297
12.8k
  }
1298
1299
682k
  entry.setParsed();
1300
682k
  input->seek(page_offset, librevenge::RVNG_SEEK_SET);
1301
1302
682k
  int numFram = libwps::read16(input);
1303
682k
  if (numFram < 0 || numFram*2 > length)
1304
205k
  {
1305
205k
    WPS_DEBUG_MSG(("WPS8Parser::readFRAM warning: length=0x%lx, num=%d\n", static_cast<unsigned long>(length), numFram));
1306
205k
    return false;
1307
205k
  }
1308
476k
  f << "N=" << numFram;
1309
1310
476k
  ascii().addPos(page_offset);
1311
476k
  ascii().addNote(f.str().c_str());
1312
1313
1314
476k
  bool parsedAll = true, color = false;
1315
476k
  long lastPos=input->tell(); // uneeded initialization but makes clang analyser happier
1316
767k
  for (int i = 0; i < numFram; i++)
1317
451k
  {
1318
451k
    lastPos = input->tell();
1319
451k
    if (lastPos+2 > endPage)
1320
12.7k
    {
1321
12.7k
      parsedAll = false;
1322
12.7k
      break;
1323
12.7k
    }
1324
438k
    int sz = libwps::read16(input);
1325
438k
    if (sz < 2 || lastPos + sz > endPage)
1326
148k
    {
1327
148k
      parsedAll = false;
1328
148k
      break;
1329
148k
    }
1330
1331
290k
    WPS8Struct::FileData mainData;
1332
290k
    std::string error;
1333
290k
    bool readOk = readBlockData(input, lastPos + sz, mainData, error);
1334
1335
290k
    double dim[3] = {0, 0, 0};
1336
290k
    bool setVal[3] = { false, false, false};
1337
1338
290k
    double bDim[4] = {0, 0, 0, 0};
1339
290k
    bool bset = false, bsetVal[4] = { false, false, false, false};
1340
290k
    libwps::DebugStream f2;
1341
1342
290k
    Frame frame;
1343
290k
    int lastPage = 0;
1344
290k
    Vec2f minP, sizeP;
1345
290k
    for (auto const &dt : mainData.m_recursData)
1346
1.10M
    {
1347
1.10M
      if (dt.isBad()) continue;
1348
1.10M
      if (m_state->m_frameTypes.find(dt.id())==m_state->m_frameTypes.end())
1349
82.1k
      {
1350
82.1k
        WPS_DEBUG_MSG(("WPS8Parser::readFRAM: unexpected id for %d\n", dt.id()));
1351
82.1k
        f2 << "##" << dt << ",";
1352
82.1k
        continue;
1353
82.1k
      }
1354
1.02M
      if (m_state->m_frameTypes.find(dt.id())->second != dt.type())
1355
116k
      {
1356
116k
        WPS_DEBUG_MSG(("WPS8Parser::readFRAM: unexpected type for %d=%d\n", dt.id(), dt.type()));
1357
116k
        f2 << "###" << dt << ",";
1358
116k
        continue;
1359
116k
      }
1360
904k
      if (dt.id() >= 4 && dt.id() < 11)
1361
439k
      {
1362
439k
        switch (dt.id())
1363
439k
        {
1364
94.1k
        case 4:
1365
94.1k
          minP.setX(float(dt.m_value)/914400.f);
1366
94.1k
          break;
1367
71.8k
        case 5:
1368
71.8k
          minP.setY(float(dt.m_value)/914400.f);
1369
71.8k
          break;
1370
115k
        case 6:
1371
115k
          sizeP.setX(float(dt.m_value)/914400.f);
1372
115k
          break;
1373
86.9k
        case 7:
1374
86.9k
          sizeP.setY(float(dt.m_value)/914400.f);
1375
86.9k
          break;
1376
70.9k
        default:
1377
70.9k
          dim[dt.id()-8] = double(dt.m_value)/914400;
1378
70.9k
          setVal[dt.id()-8] = true;
1379
70.9k
          break;
1380
439k
        }
1381
439k
        continue;
1382
439k
      }
1383
1384
465k
      bool ok = true;
1385
465k
      switch (dt.id())
1386
465k
      {
1387
153k
      case 0:
1388
153k
      {
1389
153k
        int page = int16_t(dt.m_value);
1390
        // try to avoid creating to many page
1391
153k
        if (page > lastPage+100)
1392
84.9k
        {
1393
84.9k
          ok = false;
1394
84.9k
          break;
1395
84.9k
        }
1396
68.2k
        frame.m_pos.setPage(page > 0 ? page+1 : page);
1397
68.2k
        if (page > lastPage) lastPage = page;
1398
68.2k
        break;
1399
153k
      }
1400
129k
      case 1:
1401
129k
        switch (dt.m_value)
1402
129k
        {
1403
        // associated with mailing database with 0x16
1404
9.31k
        case 4:
1405
9.31k
          frame.m_type = Frame::DbField;
1406
9.31k
          break;
1407
        // associated with 0x13=22:[0|96], 0x18,1b=0x41,0x2e, rarely with 0x1a
1408
9.78k
        case 6:
1409
9.78k
          frame.m_type = Frame::Header;
1410
9.78k
          break;
1411
        // associated with 0x13=22:[0|96],0x18,1b=0x41,0x2e, rarely with 0x1a
1412
8.51k
        case 7:
1413
8.51k
          frame.m_type = Frame::Footer;
1414
8.51k
          break;
1415
1416
        // does the following correspond to page, column, char position ?
1417
1418
        // associated with 0x11=(m_idOle,0,1), 0x2e=?
1419
35.4k
        case 8:
1420
35.4k
          frame.m_type = Frame::Object;
1421
35.4k
          break;
1422
        // associated with 0x18=?, 0x46=? and often with a BDR entry
1423
        // CHECKME: seems also associated to a MCLD, how ?
1424
17.8k
        case 9:
1425
17.8k
          frame.m_type = Frame::Text;
1426
17.8k
          break;
1427
        // associated with f3 and 0x11=(table_id,0,0) and 0x2a=?
1428
39.3k
        case 12:
1429
39.3k
          frame.m_type = Frame::Table;
1430
39.3k
          break;
1431
1432
9.51k
        default:
1433
9.51k
          f2 << "###type=" << dt.m_value << ",";
1434
9.51k
          break;
1435
129k
        }
1436
129k
        break;
1437
129k
      case 0x2:
1438
1
        if (dt.m_value&1) f2 << "noText[right],";
1439
1
        if (dt.m_value&2) f2 << "noText[left],";
1440
1
        if (dt.m_value&0xFC) f2 << "#f2=" << std::hex << (dt.m_value&0xFC) << ",";
1441
1
        break;
1442
542
      case 0x3: // seem to exist iff type=12
1443
542
        if ((frame.m_type == Frame::Table) != dt.isTrue())
1444
44
          f2 <<  "isTable?["
1445
44
             << (dt.isTrue() ? "true" : "false") << "],";
1446
542
        break;
1447
0
      case 0x10:   // an entry index always a BDR ?
1448
0
        frame.m_idBorder.setName(dt.m_text);
1449
0
        frame.m_idBorder.setId(int(dt.m_value));
1450
0
        break;
1451
535
      case 0x11:   // a list of 3 int32 ? Only the first is signifiant ?
1452
535
      {
1453
535
        if (dt.isRead() || !dt.isArray())
1454
9
        {
1455
9
          ok = false;
1456
9
          break;
1457
9
        }
1458
526
        auto size = int(dt.end()-dt.begin());
1459
526
        if (size < 2 || (size+2)%4 != 0)
1460
6
        {
1461
6
          ok = false;
1462
6
          break;
1463
6
        }
1464
520
        int numElt = (size-2)/4;
1465
520
        long actPos = input->tell();
1466
1467
520
        input->seek(dt.begin()+2, librevenge::RVNG_SEEK_SET);
1468
520
        std::vector<long> values;
1469
2.14k
        for (int elt = 0; elt < numElt; elt++)
1470
1.62k
        {
1471
1.62k
          long val = libwps::read32(input);
1472
1.62k
          values.push_back(val);
1473
1.62k
        }
1474
1475
520
        bool canHaveId = frame.m_type==Frame::Table || frame.m_type==Frame::Object;
1476
520
        if (canHaveId && numElt == 3 && values[1] == 0 && values[2] ==0)
1477
367
          frame.m_idObject = int(values[0]);
1478
153
        else if (canHaveId && numElt == 3 && values[1] == 0 && values[2] == 1)
1479
3
          frame.m_idOle = int(values[0]);
1480
150
        else
1481
150
        {
1482
150
          WPS_DEBUG_MSG(("WPS8Parser::readFRAM unknown field 0x11\n"));
1483
150
          f2 << "###f17=(";
1484
660
          for (int j = 0; j < numElt; j++) f2 << values[size_t(j)] << ",";
1485
150
          f2 << "),";
1486
150
        }
1487
1488
520
        input->seek(actPos, librevenge::RVNG_SEEK_SET);
1489
520
        break;
1490
526
      }
1491
0
      case 0x16: // associated with field and database
1492
0
        f2 << "database=" << dt.m_text << ":" << dt.m_value << ",";
1493
0
        break;
1494
      // associated with a tex box and field 0x19
1495
0
      case 0x17:
1496
0
        color = dt.isTrue();
1497
0
        break;
1498
36.9k
      case 0x18:
1499
36.9k
      {
1500
36.9k
        if (dt.m_value < 0 || dt.m_value >= m_textParser->getNumTextZones())
1501
31.0k
        {
1502
31.0k
          ok = false;
1503
31.0k
          break;
1504
31.0k
        }
1505
5.89k
        int type = m_textParser->getTextZoneType(int(dt.m_value));
1506
5.89k
        if ((type == 6 && frame.m_type == Frame::Header) ||
1507
5.61k
          (type == 7 && frame.m_type == Frame::Footer) ||
1508
5.34k
          (type == 5))
1509
883
          frame.m_idStrs = int(dt.m_value);
1510
5.01k
        else
1511
5.01k
        {
1512
5.01k
          WPS_DEBUG_MSG(("WPS8Parser::readFRAM odd id for field 0x18\n"));
1513
5.01k
          ok = false;
1514
5.01k
        }
1515
5.89k
        break;
1516
36.9k
      }
1517
16.3k
      case 0x19: // CHECKME: can we also have a front color and a pattern ?
1518
16.3k
        if (!color)
1519
16.3k
          f2 << "#f23=false,";
1520
16.3k
        frame.m_backgroundColor = dt.getRGBColor();
1521
16.3k
        break;
1522
3.48k
      case 0x1b: // 41
1523
3.48k
        if (frame.m_type!=Frame::Footer || dt.m_value != 0x41)
1524
3.09k
          f2 << "##f" << dt.id() << "=" << int16_t(dt.m_value) << ",";
1525
3.48k
        break;
1526
13.1k
      case 0x26: // rotation in degree from center ( CCW)
1527
13.1k
        f2 << "rot=" << double(dt.m_value)/10. << "deg,";
1528
13.1k
        break;
1529
      //
1530
      // Table specific field ?
1531
      //
1532
23.5k
      case 0x2a:   // link to the m_idTable(ids) number for a table
1533
23.5k
      {
1534
23.5k
        int id = int16_t(dt.m_value);
1535
23.5k
        if (frame.m_type==Frame::Table)
1536
12.9k
          frame.m_idTable = id;
1537
10.6k
        else
1538
10.6k
          f2 << "f" << dt.id() << "=" << id << ",";
1539
23.5k
        break;
1540
36.9k
      }
1541
3.08k
      case 0x2c: // CHEKME
1542
3.08k
        if (frame.m_type==Frame::Table)
1543
394
          frame.m_columns = int(dt.m_value);
1544
2.69k
        else ok = false;
1545
3.08k
        break;
1546
0
      case 0x14: // in relation with 0x30 ? find a small number between 1 and 18
1547
0
        if (frame.m_type==Frame::Table)
1548
0
          f2 << "grpId=" << int16_t(dt.m_value) << ",";
1549
0
        else ok = false;
1550
0
        break;
1551
4
      case 0x30: // seems to link the table together
1552
4
        if (frame.m_type==Frame::Table)
1553
0
          f2 << "tableGrp=" << int16_t(dt.m_value) << ",";
1554
4
        else ok = false;
1555
4
        break;
1556
228
      case 0x2d: // appear 2 time in a table with value 2
1557
228
        f2 << "f" << dt.id() << "=" << int16_t(dt.m_value) << ",";
1558
228
        break;
1559
1560
      // UNKNOWN
1561
0
      case 0x1a: // appear 2 time (in a header and in a footer) with value 16
1562
0
        f2 << "f" << dt.id() << "=" << int(int8_t(dt.m_value)) << ",";
1563
0
        break;
1564
1565
24.8k
      case 0x2e: // in a table: objectId+1, in textbox: m_idTable ?, other = i ?
1566
24.8k
        f2 << "id" << dt.id() << "=" << int16_t(dt.m_value-1) << ",";
1567
24.8k
        break;
1568
22.5k
      case 0x13:
1569
        // table 0x6-??,
1570
        // header/footer 0x16-??,
1571
        // textbox 0x10(means automatic resize)
1572
        // object : no set
1573
22.5k
        f2 << "f" << dt.id();
1574
22.5k
        if (dt.m_value)
1575
22.5k
          f2 << "=" << std::hex << dt.m_value << std::dec;
1576
22.5k
        f2 << ",";
1577
22.5k
        break;
1578
79
      case 0x2f:
1579
79
        if (dt.isTrue())
1580
79
          f2 <<  "f" << dt.id() << ",";
1581
0
        else
1582
0
          f2 <<  "f" << dt.id() << "=false,";
1583
79
        break;
1584
12.8k
      case 0x1D:
1585
22.7k
      case 0x1E:
1586
29.9k
      case 0x1F:
1587
37.0k
      case 0x20: // in inches small value 0/0.05/0.1 -> a border size ?
1588
37.0k
        bset = true;
1589
37.0k
        bsetVal[dt.id()-0x1D] = true;
1590
37.0k
        bDim[dt.id()-0x1D] =  double(dt.m_value)/914400;
1591
37.0k
        break;
1592
1593
0
      default:
1594
0
        ok = false;
1595
465k
      }
1596
1597
465k
      if (ok) continue;
1598
123k
      f2 << "#" << dt << ",";
1599
123k
    }
1600
290k
    frame.m_pos.setOrigin(minP);
1601
290k
    frame.m_pos.setSize(sizeP);
1602
290k
    m_state->m_frameList.push_back(frame);
1603
1604
290k
    f.str("");
1605
290k
    f << entry.name() << "(" << i << "):" << frame << ",";
1606
290k
    if (setVal[0] || setVal[1])
1607
62.6k
    {
1608
62.6k
      f << "f8/9=[";
1609
187k
      for (int j = 0; j < 2; j++)
1610
125k
      {
1611
125k
        if (!setVal[j])
1612
61.2k
        {
1613
61.2k
          f << "_,";
1614
61.2k
          continue;
1615
61.2k
        }
1616
63.9k
        double diff = 25/18.-dim[j];
1617
63.9k
        if (diff >= -1e-3 && diff <= 1e-3) f << "*,";
1618
38.7k
        else f << dim[j] << ",";
1619
63.9k
      }
1620
62.6k
      f << "],";
1621
62.6k
    }
1622
290k
    if (setVal[2]) f << "border[w]=" << dim[2] << ",";
1623
1624
290k
    if (bset)
1625
35.7k
    {
1626
35.7k
      f << "borderMod[w]=["; // L, T, R, B
1627
178k
      for (int j = 0; j < 4; j++)
1628
142k
      {
1629
142k
        if (!bsetVal[j])
1630
105k
        {
1631
105k
          f << "_,";
1632
105k
          continue;
1633
105k
        }
1634
37.0k
        f << bDim[j] << ",";
1635
37.0k
      }
1636
35.7k
      f << "],";
1637
35.7k
    }
1638
290k
    f << f2.str();
1639
290k
    if (!readOk) f << ", ###or" << mainData;
1640
1641
290k
    ascii().addPos(lastPos);
1642
290k
    ascii().addNote(f.str().c_str());
1643
290k
  }
1644
476k
  if (parsedAll) return true;
1645
1646
161k
  ascii().addPos(lastPos);
1647
161k
  f.str("");
1648
161k
  f << "###" << entry.name();
1649
161k
  ascii().addNote(f.str().c_str());
1650
161k
  return false;
1651
476k
}
1652
1653
/** try to read a frame XXX database  */
1654
bool WPS8Parser::readFRCD(WPSEntry const &entry)
1655
263k
{
1656
263k
  libwps::DebugStream f;
1657
263k
  if (!entry.hasType(entry.name()))
1658
0
  {
1659
0
    WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: name=%s, type=%s\n",
1660
0
                   entry.name().c_str(), entry.type().c_str()));
1661
0
    return false;
1662
0
  }
1663
263k
  RVNGInputStreamPtr input = getInput();
1664
1665
263k
  long page_offset = entry.begin();
1666
263k
  long length = entry.length();
1667
263k
  long endPage = entry.end();
1668
1669
263k
  if (length < 4+33+5*8)
1670
35.9k
  {
1671
35.9k
    WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: length=0x%lx\n", static_cast<unsigned long>(length)));
1672
35.9k
    return false;
1673
35.9k
  }
1674
227k
  entry.setParsed();
1675
227k
  input->seek(page_offset, librevenge::RVNG_SEEK_SET);
1676
227k
  auto val=int(libwps::readU16(input)); // 0x30
1677
227k
  if (val!=0x30) f << "f0=" << val << ",";
1678
227k
  auto sSz=int(libwps::readU16(input));
1679
227k
  librevenge::RVNGString str;
1680
227k
  if (4+2*sSz+33+5*8>length || !m_textParser->readString(input, sSz, str))
1681
36.4k
  {
1682
36.4k
    WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: can not read main string\n"));
1683
36.4k
    return false;
1684
36.4k
  }
1685
190k
  f << str.cstr() << ",";
1686
3.24M
  for (int i=0; i<16; ++i)   // f1=10,f2=22,f4=4,f7=256,f11=-1,f12=-256,f13=255,f14=-1
1687
3.05M
  {
1688
3.05M
    val=int(libwps::read16(input));
1689
3.05M
    if (val) f << "f" << i+1 << "=" << val << ",";
1690
3.05M
  }
1691
190k
  val=int(libwps::read8(input));
1692
190k
  if (val) f << "g0=" << val << ",";
1693
190k
  ascii().addPos(page_offset);
1694
190k
  ascii().addNote(f.str().c_str());
1695
679k
  for (int i=0; i<5; ++i)
1696
604k
  {
1697
604k
    long pos=input->tell();
1698
604k
    f.str("");
1699
604k
    f << "FRCD-A" << i << ":";
1700
604k
    if (pos+8>endPage)
1701
10.9k
    {
1702
10.9k
      WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: the zone seems too short\n"));
1703
10.9k
      f << "###";
1704
10.9k
      ascii().addPos(pos);
1705
10.9k
      ascii().addNote(f.str().c_str());
1706
10.9k
      return true;
1707
10.9k
    }
1708
1.59M
    for (int s=0; s<2; ++s)
1709
1.10M
    {
1710
1.10M
      if (pos+4>endPage || libwps::read8(input)!=-1)
1711
66.2k
      {
1712
66.2k
        WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: the zone seems bad\n"));
1713
66.2k
        f << "###";
1714
66.2k
        ascii().addPos(pos);
1715
66.2k
        ascii().addNote(f.str().c_str());
1716
66.2k
        return true;
1717
66.2k
      }
1718
1.04M
      val=int(libwps::read8(input));
1719
1.04M
      if (val==-1)
1720
936k
      {
1721
936k
        val=int(libwps::read16(input));
1722
936k
        if (val!=0x204)
1723
936k
          f << "f" << s << "=" << val << ",";
1724
936k
      }
1725
106k
      else if (val==-2)
1726
80.3k
      {
1727
80.3k
        val=int(libwps::read8(input));
1728
80.3k
        if (val!=-1) f << "g" << s << "=" << val << ",";
1729
80.3k
        sSz=int(libwps::readU8(input));
1730
80.3k
        str.clear();
1731
80.3k
        if (pos+2*sSz>endPage || (sSz && !m_textParser->readString(input, 2*sSz, str)))
1732
11.9k
        {
1733
11.9k
          WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: the zone seems too short\n"));
1734
11.9k
          f << "###";
1735
11.9k
          ascii().addPos(pos);
1736
11.9k
          ascii().addNote(f.str().c_str());
1737
11.9k
          return true;
1738
11.9k
        }
1739
68.3k
        if (!str.empty())
1740
33.0k
          f << str.cstr() << ",";
1741
68.3k
      }
1742
26.4k
      else
1743
26.4k
      {
1744
26.4k
        WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: find unexpected value\n"));
1745
26.4k
        f << "###val=" << val << ",";
1746
26.4k
        ascii().addPos(pos);
1747
26.4k
        ascii().addNote(f.str().c_str());
1748
26.4k
        return true;
1749
26.4k
      }
1750
1.04M
    }
1751
488k
    ascii().addPos(pos);
1752
488k
    ascii().addNote(f.str().c_str());
1753
488k
  }
1754
75.1k
  long pos=input->tell();
1755
75.1k
  f.str("");
1756
75.1k
  f << "FRCD-B:";
1757
75.1k
  if (pos+4>endPage)
1758
11.8k
  {
1759
11.8k
    WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: can not read the second zone\n"));
1760
11.8k
    f << "###";
1761
11.8k
    ascii().addPos(pos);
1762
11.8k
    ascii().addNote(f.str().c_str());
1763
11.8k
    return true;
1764
11.8k
  }
1765
63.3k
  auto N=int(libwps::readU16(input));
1766
63.3k
  if (pos+4+10*N>endPage)
1767
13.9k
  {
1768
13.9k
    WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: can not read the second zone\n"));
1769
13.9k
    f << "###";
1770
13.9k
    ascii().addPos(pos);
1771
13.9k
    ascii().addNote(f.str().c_str());
1772
13.9k
    return true;
1773
13.9k
  }
1774
49.3k
  val=int(libwps::read16(input)); // 0
1775
49.3k
  if (val) f << "f0=" << val << ",";
1776
49.3k
  ascii().addPos(pos);
1777
49.3k
  ascii().addNote(f.str().c_str());
1778
64.9k
  for (int n=0; n<N; ++n)
1779
51.2k
  {
1780
51.2k
    pos=input->tell();
1781
51.2k
    f.str("");
1782
51.2k
    f << "FRCD-B" << n << ":";
1783
51.2k
    if (pos+10>endPage)
1784
2.82k
    {
1785
2.82k
      WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: the zone seems too short\n"));
1786
2.82k
      f << "###";
1787
2.82k
      ascii().addPos(pos);
1788
2.82k
      ascii().addNote(f.str().c_str());
1789
2.82k
      return true;
1790
2.82k
    }
1791
48.4k
    val=int(libwps::read16(input)); // 0
1792
48.4k
    if (val) f << "f0=" << val << ",";
1793
99.3k
    for (int s=0; s<2; ++s)
1794
83.7k
    {
1795
83.7k
      if (pos+4>endPage || libwps::read8(input)!=-1)
1796
14.0k
      {
1797
14.0k
        WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: the second zone seems bad\n"));
1798
14.0k
        f << "###";
1799
14.0k
        ascii().addPos(pos);
1800
14.0k
        ascii().addNote(f.str().c_str());
1801
14.0k
        return true;
1802
14.0k
      }
1803
69.7k
      val=int(libwps::read8(input));
1804
69.7k
      if (val==-2)
1805
59.0k
      {
1806
59.0k
        val=int(libwps::read8(input));
1807
59.0k
        if (val!=-1) f << "g" << s << "=" << val << ",";
1808
59.0k
        sSz=int(libwps::readU8(input));
1809
59.0k
        str.clear();
1810
59.0k
        if (pos+2*sSz>endPage || (sSz && !m_textParser->readString(input, 2*sSz, str)))
1811
8.09k
        {
1812
8.09k
          WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: the second zone seems too short\n"));
1813
8.09k
          f << "###";
1814
8.09k
          ascii().addPos(pos);
1815
8.09k
          ascii().addNote(f.str().c_str());
1816
8.09k
          return true;
1817
8.09k
        }
1818
50.9k
        if (!str.empty())
1819
27.6k
          f << str.cstr() << ",";
1820
50.9k
      }
1821
10.7k
      else
1822
10.7k
      {
1823
10.7k
        WPS_DEBUG_MSG(("WPS8Parser::readFRCD warning: find unexpected value\n"));
1824
10.7k
        f << "###val=" << val << ",";
1825
10.7k
        ascii().addPos(pos);
1826
10.7k
        ascii().addNote(f.str().c_str());
1827
10.7k
        return true;
1828
10.7k
      }
1829
69.7k
    }
1830
15.6k
    ascii().addPos(pos);
1831
15.6k
    ascii().addNote(f.str().c_str());
1832
15.6k
  }
1833
13.6k
  return true;
1834
49.3k
}
1835
1836
// reads SYID zone: an unknown zone which seems to have the
1837
//    same number of entries than the text zone
1838
// also related to the MCLD ?
1839
bool WPS8Parser::readSYID(WPSEntry const &entry, std::vector<int> &listId)
1840
106k
{
1841
106k
  RVNGInputStreamPtr input = getInput();
1842
106k
  listId.resize(0);
1843
106k
  libwps::DebugStream f;
1844
106k
  if (!entry.hasType(entry.name()))
1845
0
  {
1846
0
    WPS_DEBUG_MSG(("WPS8Parser::readSYID: warning: SYID name=%s, type=%s\n",
1847
0
                   entry.name().c_str(), entry.type().c_str()));
1848
0
    return false;
1849
0
  }
1850
1851
106k
  long page_offset = entry.begin();
1852
106k
  long length = entry.length();
1853
1854
106k
  if (length < 4)
1855
33.5k
  {
1856
33.5k
    WPS_DEBUG_MSG(("WPS8Parser::readSYID: warning: SYID length=0x%lx\n", static_cast<unsigned long>(length)));
1857
33.5k
    return false;
1858
33.5k
  }
1859
1860
72.8k
  input->seek(page_offset, librevenge::RVNG_SEEK_SET);
1861
1862
72.8k
  int unk = libwps::read32(input);
1863
72.8k
  int numID = libwps::read32(input);
1864
72.8k
  if (numID < 0 || 4*(long(numID)+2) != length)
1865
70.7k
  {
1866
70.7k
    WPS_DEBUG_MSG(("WPS8Parser::readSYID: invalid length=%ld, num=%d\n", length, numID));
1867
70.7k
    return false;
1868
70.7k
  }
1869
1870
2.11k
  f << "N=" << numID << ", unkn=" << unk << ", (";
1871
5.17k
  for (int i = 0; i < numID; i++)
1872
3.05k
  {
1873
3.05k
    int val = libwps::read32(input);
1874
3.05k
    listId.push_back(val);
1875
3.05k
    f << val << ",";
1876
3.05k
  }
1877
2.11k
  f << ")";
1878
1879
2.11k
  ascii().addPos(page_offset);
1880
2.11k
  ascii().addNote(f.str().c_str());
1881
1882
2.11k
  entry.setParsed();
1883
2.11k
  return true;
1884
72.8k
}
1885
1886
// reads the WNPR zone : a zone which seems to contain the printer preferences
1887
// Warning: read data are not used
1888
bool WPS8Parser::readWNPR(WPSEntry const &entry)
1889
329k
{
1890
329k
  if (!entry.hasType("WNPR"))
1891
0
  {
1892
0
    WPS_DEBUG_MSG(("WPS8Parser::readWNPR: warning: WNPR name=%s, type=%s\n",
1893
0
                   entry.name().c_str(), entry.type().c_str()));
1894
0
    return false;
1895
0
  }
1896
329k
  RVNGInputStreamPtr input = getInput();
1897
329k
  long page_offset = entry.begin();
1898
329k
  long length = entry.length();
1899
329k
  long endPage = entry.end();
1900
1901
329k
  if (length < 40)
1902
22.9k
  {
1903
22.9k
    WPS_DEBUG_MSG(("WPS8Parser::readWNPR: warning: WNPR length=0x%lx\n", static_cast<unsigned long>(length)));
1904
22.9k
    return false;
1905
22.9k
  }
1906
1907
306k
  entry.setParsed();
1908
306k
  input->seek(page_offset, librevenge::RVNG_SEEK_SET);
1909
1910
306k
  libwps::DebugStream f;
1911
1912
306k
  f << std::hex << libwps::readU32(input) << std::dec << ",";
1913
306k
  long dim[4];
1914
306k
  for (long &i : dim)
1915
1.01M
  {
1916
1.01M
    i = libwps::read32(input);
1917
1.01M
    if (i <= 0) return false;
1918
1.01M
  }
1919
1920
188k
  if (dim[2] && dim[3])
1921
188k
  {
1922
188k
    f << "width=" << float(dim[0])/float(dim[2]) << ",";
1923
188k
    f << "height=" << float(dim[1])/float(dim[3]) << ",";
1924
188k
  }
1925
0
  else
1926
0
  {
1927
0
    f << "###width=" << dim[0] << ":" << dim[3] << ",";
1928
0
    f << "###height=" << dim[1] << ":" << dim[3] << ",";
1929
0
  }
1930
1931
188k
  f << "printmargins?=(";
1932
944k
  for (int i = 0; i < 4; i++)
1933
755k
  {
1934
755k
    auto val = long(libwps::readU32(input));
1935
755k
    long sz = dim[2+(i%2)];
1936
755k
    if (sz)
1937
755k
      f << float(val)/float(sz) << ",";
1938
0
    else
1939
0
      f << "###" << val << ",";
1940
755k
  }
1941
188k
  f << "),";
1942
1943
566k
  for (int i = 0; i < 2; i++)
1944
377k
    f << std::hex << libwps::readU16(input) << std::dec <<",";
1945
1946
1947
188k
  ascii().addPos(page_offset);
1948
188k
  ascii().addNote(f.str().c_str());
1949
1950
188k
  long actPos = input->tell();
1951
188k
  if (actPos + 32*2+38+22 > endPage)
1952
26.6k
  {
1953
26.6k
    WPS_DEBUG_MSG(("WPS8Parser::readWNPR: length=0x%lx seems too short\n", static_cast<unsigned long>(length)));
1954
26.6k
    return false;
1955
26.6k
  }
1956
1957
  // DEVMODEA
1958
162k
  std::string st;
1959
5.35M
  for (int i = 0; i < 32; i++)
1960
5.18M
  {
1961
5.18M
    unsigned char c = libwps::readU8(input);
1962
5.18M
    if (!c) continue; // normally end by c, check me
1963
3.04M
    st+= char(c);
1964
3.04M
  }
1965
162k
  f.str("");
1966
162k
  f << "PRNT(DevMode):";
1967
162k
  f << "devName='" << st << "'," << std::hex;
1968
162k
  f << "specVersion=" << libwps::readU16(input) << ",";
1969
162k
  f << "driverVersion=" << libwps::readU16(input) << ",";
1970
162k
  int dmSize = libwps::readU16(input);
1971
162k
  if (actPos + dmSize > endPage || dmSize < 124) return false;
1972
26.7k
  f << "dmSize=" << dmSize << ",";
1973
26.7k
  f << "driverExtras=" << libwps::readU16(input) << ",";
1974
26.7k
  f << "dmFields=" << libwps::readU32(input) << ",";
1975
26.7k
  f << "orientation=" << libwps::readU16(input) << ","; // 1: portrait, 2: landscape
1976
26.7k
  f << "paperSize=" << libwps::readU16(input) << ","; // 1: letter, 2:letter small, ..
1977
1978
26.7k
  long dim2[3];
1979
26.7k
  for (long &i : dim2)
1980
80.3k
    i = libwps::read16(input);
1981
26.7k
  f << std::dec;
1982
26.7k
  if (dim2[2] > 0)
1983
10.8k
  {
1984
10.8k
    f << "paperLength=" << std::dec << float(dim2[0])/float(dim2[2]) << ",";
1985
10.8k
    f << "paperWidth=" << float(dim2[1])/float(dim2[2]) << ",";
1986
10.8k
  }
1987
15.8k
  else
1988
15.8k
  {
1989
15.8k
    f << "paperLength=" << dim2[0] << ",";
1990
15.8k
    f << "paperWidth=" << dim2[1] << ",";
1991
15.8k
    f << "dmScale=" << dim2[2] << ",";
1992
15.8k
  }
1993
26.7k
  f << "dmCopies=" << libwps::readU16(input) << ",";
1994
26.7k
  f << "dmDefaultSource=" << libwps::readU16(input) << ",";
1995
26.7k
  f << "dmPrintQuality=" << libwps::read16(input) << ",";
1996
26.7k
  f << std::hex;
1997
26.7k
  f << "dmColor=" << libwps::readU16(input) << ",";
1998
26.7k
  f << "dmDuplex=" << libwps::readU16(input) << ",";
1999
26.7k
  f << "dmYResolution=" << libwps::readU16(input) << ",";
2000
26.7k
  f << "dmTTOption=" << libwps::readU16(input) << ",";
2001
26.7k
  f << "dmCollate=" << libwps::readU16(input) << ",";
2002
2003
26.7k
  st="";
2004
883k
  for (int i = 0; i < 32; i++)
2005
856k
  {
2006
856k
    unsigned char c = libwps::readU8(input);
2007
856k
    if (!c) continue; // normally end by c, check me
2008
359k
    st+= char(c);
2009
359k
  }
2010
26.7k
  f << "formName='" << st << "',";
2011
26.7k
  f << "dmLogPixels=" << libwps::readU16(input) << ",";
2012
26.7k
  f << "dmBitsPerPel=" << libwps::readU32(input) << ",";
2013
26.7k
  f << "dmPelsWidth=" << libwps::readU32(input) << ",";
2014
26.7k
  f << "dmPelsHeight=" << libwps::readU32(input) << ",";
2015
26.7k
  f << "dmDisplayFlags=" << libwps::readU32(input) << ",";
2016
26.7k
  f << "dmDisplayFrequency=" << libwps::readU32(input) << ",";
2017
2018
26.7k
  ascii().addPos(actPos);
2019
26.7k
  ascii().addNote(f.str().c_str());
2020
2021
26.7k
  long devEndPos = actPos+dmSize;
2022
26.7k
  actPos = input->tell();
2023
26.7k
  if (actPos != devEndPos)
2024
26.7k
  {
2025
26.7k
    ascii().addPos(actPos);
2026
26.7k
    ascii().addNote("PRNT(DevMode-End)");
2027
26.7k
  }
2028
2029
26.7k
  ascii().addPos(devEndPos);
2030
26.7k
  ascii().addNote("PRNT(Unknown)");
2031
2032
26.7k
  return true;
2033
162k
}
2034
2035
////////////////////////////////////////////////////////////////
2036
//
2037
// ? Syllabes seperator to read the text
2038
// it is really related to spell check or to speech
2039
// Can surely be safety ignored in normal version.
2040
//
2041
////////////////////////////////////////////////////////////////
2042
#ifndef DEBUG
2043
bool WPS8Parser::readSPELLING(RVNGInputStreamPtr, std::string const &)
2044
0
{
2045
0
  return true;
2046
0
}
2047
#else
2048
bool WPS8Parser::readSPELLING(RVNGInputStreamPtr input, std::string const &oleName)
2049
{
2050
  // SPELLING
2051
  input->seek(0, librevenge::RVNG_SEEK_SET);
2052
  auto vers = int(libwps::read32(input)); // always 6 ?
2053
  if (vers < 0 || input->isEnd()) return false;
2054
2055
  std::string fName = libwps::Debug::flattenFileName(oleName);
2056
  libwps::DebugFile asciiFile(input);
2057
  asciiFile.open(fName);
2058
2059
2060
  int actId = 0;
2061
  std::map<uint32_t, int> listIds;
2062
2063
  libwps::DebugStream f;
2064
  int num = 0;
2065
  while (!input->isEnd())
2066
  {
2067
    long pos = input->tell();
2068
    auto numVal = int(libwps::read32(input));
2069
2070
    if (numVal < 0 || input->seek(8*numVal, librevenge::RVNG_SEEK_CUR) || input->tell() != pos+4+8*numVal)
2071
    {
2072
      input->seek(pos, librevenge::RVNG_SEEK_SET);
2073
      break;
2074
    }
2075
2076
    f.str("");
2077
    f << "SPELLING" << num++ << ": "; // linked to STRSj by num = numSTRS-j?
2078
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
2079
    for (int j = 0; j < numVal; j++)
2080
    {
2081
      uint32_t ptr = libwps::readU32(input);
2082
      int id;
2083
      if (listIds.find(ptr) == listIds.end())
2084
      {
2085
        id = actId++;
2086
        listIds[ptr] = id;
2087
      }
2088
      else
2089
        id = listIds[ptr];
2090
      auto sPos = long(libwps::read32(input));
2091
      f << std::hex << 2*sPos << ":SP" <<  std::dec << id << ",";
2092
    }
2093
    asciiFile.addPos(pos);
2094
    asciiFile.addNote(f.str().c_str());
2095
  }
2096
  f.str("");
2097
  f << "SPELLING:";
2098
  if (vers != 6) f << "version = " << vers << ", ";
2099
  f << "list=[";
2100
  for (auto const &it : listIds)
2101
  {
2102
    uint32_t val = it.first;
2103
    f << "SP" << std::dec << it.second << "(" << (val&0xFF) << ":"
2104
      << std::hex << (val>>8) << std::dec << "),";
2105
2106
    // (val&0xFF) :121-123, 129, 133, 139, 145
2107
    // (val>>8) : 0, e6, d9a3b, 3b75f4, 474555, 548bbb, 609daa,748cce, ba32b9, c068cd, d39bea
2108
    //          seems constant in a SPELLING Ole
2109
  }
2110
  f << "]";
2111
  asciiFile.addPos(0);
2112
  asciiFile.addNote(f.str().c_str());
2113
  if (!input->isEnd())
2114
  {
2115
    asciiFile.addPos(input->tell());
2116
    asciiFile.addNote("###SPELLING");
2117
  }
2118
  return true;
2119
}
2120
#endif
2121
2122
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */