Coverage Report

Created: 2026-04-29 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/WPS4Text.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) 2009, 2011 Alonso Laurent (alonso@loria.fr)
11
 * Copyright (C) 2006, 2007 Andrew Ziem
12
 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13
 * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
14
 * Copyright (C) 2003-2005 William Lachance (william.lachance@sympatico.ca)
15
 *
16
 * For minor contributions see the git repository.
17
 *
18
 * Alternatively, the contents of this file may be used under the terms
19
 * of the GNU Lesser General Public License Version 2.1 or later
20
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
21
 * applicable instead of those above.
22
 *
23
 * For further information visit http://libwps.sourceforge.net
24
 */
25
26
#ifdef DEBUG_WITH_FILES
27
// set to 1 to debug the font property
28
#  define DEBUG_FP 1
29
// set to 1 to debug the paragraph property
30
#  define DEBUG_PP 1
31
// set to 1 to print the plc position
32
#  define DEBUG_PLC_POS 1
33
#else
34
#  define DEBUG_FP 0
35
#  define DEBUG_PP 0
36
#  define DEBUG_PLC_POS 0
37
#endif
38
39
#include <iomanip>
40
#include <iostream>
41
42
#include <map>
43
#include <vector>
44
45
#include <librevenge/librevenge.h>
46
47
#include "libwps_internal.h"
48
#include "libwps_tools_win.h"
49
50
#include "WPSContentListener.h"
51
#include "WPSFont.h"
52
#include "WPSPosition.h"
53
#include "WPSParagraph.h"
54
55
#include "WPS4.h"
56
57
#include "WPS4Text.h"
58
59
/** Internal and low level: the structures of a WPS4Text used to parse PLC*/
60
namespace WPS4PLCInternal
61
{
62
/** Internal and low level: the PLC different types and their structures */
63
struct PLC;
64
65
//! a map of known plc
66
struct KnownPLC
67
{
68
public:
69
  //! constructor
70
  KnownPLC();
71
  //! destructor
72
  ~KnownPLC();
73
  //! returns the PLC corresponding to a name
74
  PLC get(std::string const &name);
75
protected:
76
  //! creates the map of known PLC
77
  void createMapping();
78
  //! map name -> known PLC
79
  std::map<std::string, PLC> m_knowns;
80
};
81
}
82
83
//////////////////////////////////////////////////////////////////////////////
84
// general enum
85
//////////////////////////////////////////////////////////////////////////////
86
namespace WPS4TextInternal
87
{
88
/** a enum used to type a zone */
89
enum ZoneType { Z_String=-1, Z_Header=0, Z_Footer=1, Z_Main=2, Z_Note, Z_Bookmark, Z_DLink, Z_Unknown};
90
/** Internal: class to store a font name: name with encoding type */
91
struct FontName
92
{
93
  //! constructor with file's version to define the default encoding */
94
  explicit FontName(libwps_tools_win::Font::Type type)
95
1.87k
    : m_name("")
96
1.87k
    , m_type(type)
97
1.87k
  {
98
1.87k
  }
99
  //! operator<<
100
  friend std::ostream &operator<<(std::ostream &o, FontName const &ft);
101
  /** returns the default dos name corresponding to \a id th font */
102
  static librevenge::RVNGString getDosName(int id);
103
104
  //! font name
105
  librevenge::RVNGString m_name;
106
  //! font encoding type
107
  libwps_tools_win::Font::Type m_type;
108
};
109
//! operator<< for a font name
110
std::ostream &operator<<(std::ostream &o, FontName const &ft)
111
0
{
112
0
  if (!ft.m_name.empty()) o << "name='" << ft.m_name.cstr() << "'";
113
0
  else o << "name='Unknown'";
114
0
  if (ft.m_type!=libwps_tools_win::Font::WIN3_WEUROPE &&
115
0
          ft.m_type!=libwps_tools_win::Font::DOS_850)
116
0
    o << ",type=" << libwps_tools_win::Font::getTypeName(ft.m_type).cstr() << ",";
117
0
  return o;
118
0
}
119
120
librevenge::RVNGString FontName::getDosName(int id)
121
9.42k
{
122
9.42k
  switch (id)
123
9.42k
  {
124
984
  case 0:
125
984
    return "Courier";
126
514
  case 1:
127
514
    return "Courier PC";
128
222
  case 3:
129
222
    return "Univers_Scale";
130
584
  case 4:
131
584
    return "Universe";
132
1.91k
  case 6:
133
1.91k
    return "LinePrinterPC";
134
607
  case 7:
135
607
    return "LinePrinter";
136
686
  case 16:
137
686
    return "CGTimes_Scale";
138
201
  case 24:
139
201
    return "CGTimes";
140
3.70k
  default:
141
3.70k
    break;
142
9.42k
  }
143
144
3.70k
  WPS_DEBUG_MSG(("WPS4TextInternal::FontName::getDosName: encountered unknown font %i\n", id));
145
3.70k
  return "Courier";
146
9.42k
}
147
/** Internal: class to store font properties */
148
struct Font final : public WPSFont
149
{
150
  //! constructor with file's version to define the default encoding */
151
  explicit Font(libwps_tools_win::Font::Type type)
152
78.9k
    : WPSFont()
153
78.9k
    , m_type(type)
154
78.9k
    , m_backColor(WPSColor::white())
155
78.9k
    , m_special(false)
156
78.9k
    , m_dlinkId(-1)
157
78.9k
  {
158
78.9k
  }
159
99.5k
  Font(Font const &)=default;
160
57.8k
  Font &operator=(Font const &)=default;
161
  //! destructor
162
  ~Font() final;
163
  //! returns a default font (Courier12) with file's version to define the default encoding */
164
  static Font getDefault(libwps_tools_win::Font::Type type, int version)
165
78.9k
  {
166
78.9k
    Font res(type);
167
78.9k
    if (version <= 2)
168
35.8k
      res.m_name="Courier";
169
43.1k
    else
170
43.1k
      res.m_name="Times New Roman";
171
78.9k
    res.m_size=12;
172
78.9k
    return res;
173
78.9k
  }
174
175
  //! operator<<
176
  friend std::ostream &operator<<(std::ostream &o, Font const &ft);
177
178
  //! the font encoding type
179
  libwps_tools_win::Font::Type m_type;
180
  //! background  color index
181
  WPSColor m_backColor;
182
  //! a flag to know if we have a special field (a note), ...
183
  bool m_special;
184
  //! a id to retrieve a file name ( dos )
185
  int m_dlinkId;
186
};
187
188
Font::~Font()
189
178k
{
190
178k
}
191
192
//! operator<< for font properties
193
std::ostream &operator<<(std::ostream &o, Font const &ft)
194
0
{
195
0
  o << static_cast<WPSFont const &>(ft) << ",";
196
197
0
  if (ft.m_special)
198
0
  {
199
0
    if (ft.m_dlinkId >= 0)
200
0
      o << "spec[" << ft.m_dlinkId << "],";
201
0
    else
202
0
      o << "spec,";
203
0
  }
204
205
0
  if (!ft.m_backColor.isWhite())
206
0
    o << "bgCol=" << ft.m_backColor << ",";
207
0
  return o;
208
0
}
209
210
/** Internal: class to store an note type */
211
struct Note final : public WPSEntry
212
{
213
  //! constructor
214
  Note()
215
2.99k
    : WPSEntry()
216
2.99k
    , m_label("")
217
2.99k
    , m_error("") {}
218
2.57k
  Note(Note const &)=default;
219
1.21k
  Note &operator=(Note const &)=default;
220
  //! destructor
221
  ~Note() final;
222
  bool isNumeric() const
223
0
  {
224
0
    return m_label.len()==0;
225
0
  }
226
  //! operator <<
227
  friend std::ostream &operator<<(std::ostream &o, Note const &note)
228
0
  {
229
0
    if (note.m_label.len())
230
0
      o << "lab=" << note.m_label.cstr() << ",";
231
0
    else
232
0
      o << "numeric,";
233
0
    if (!note.m_error.empty()) o << note.m_error << ",";
234
0
    return o;
235
0
  }
236
  //! the label if not numeric
237
  librevenge::RVNGString m_label;
238
  //! a string used to store the parsing errors
239
  std::string m_error;
240
};
241
242
Note::~Note()
243
5.57k
{
244
5.57k
}
245
/** Internal: class to store an object definition */
246
struct Object
247
{
248
  //! constructor
249
  Object()
250
2.37k
    : m_id(-1)
251
2.37k
    , m_page(0)
252
2.37k
    , m_origin()
253
2.37k
    , m_size()
254
2.37k
    , m_pos()
255
2.37k
    , m_unknown(0)
256
2.37k
    , m_extra("") {}
257
  //! operator <<
258
  friend std::ostream &operator<<(std::ostream &o, Object const &obj);
259
260
  //! the object identificator
261
  int m_id;
262
  //! the page
263
  int m_page;
264
  //! the origin
265
  Vec2f m_origin;
266
  //! the object size in the document
267
  Vec2f m_size;
268
  //! an entry which indicates where the object is defined in the file
269
  WPSEntry m_pos;
270
  //! unknown data
271
  long m_unknown;
272
  //! a string used to store the parsing errors
273
  std::string m_extra;
274
};
275
//! operator<< for an object
276
std::ostream &operator<<(std::ostream &o, Object const &obj)
277
0
{
278
0
  if (obj.m_id > -1) o << "ole" << obj.m_id << ",";
279
0
  if (obj.m_page) o << "page=" << obj.m_page << ",";
280
0
  if (obj.m_origin!=Vec2f(0,0)) o << "orig=" << obj.m_origin << ",";
281
0
  o <<"size=" << obj.m_size << ",";
282
0
  if (obj.m_pos.valid()) o << std::hex << "def=(0x" << obj.m_pos.begin() << "->" << obj.m_pos.end() << ")," << std::dec;
283
0
  if (obj.m_unknown) o << std::hex << "unkn=" << obj.m_unknown << std::dec << ",";
284
0
  if (!obj.m_extra.empty()) o << obj.m_extra;
285
0
  return o;
286
0
}
287
288
/** Internal: class to store an object definition */
289
struct DosLink
290
{
291
  //! constructor
292
  DosLink()
293
7.75k
    : m_type(-1)
294
7.75k
    , m_width(-1)
295
7.75k
    , m_size()
296
7.75k
    , m_name("")
297
7.75k
    , m_pos()
298
7.75k
    , m_extra("") {}
299
  //! operator <<
300
  friend std::ostream &operator<<(std::ostream &o, DosLink const &dlink);
301
302
  //! the type
303
  int m_type;
304
  //! the width
305
  float m_width;
306
  //! the object size in the document
307
  Vec2f m_size;
308
  //! the file name
309
  std::string m_name;
310
  //! an entry which indicates where the object is defined in the file
311
  WPSEntry m_pos;
312
  //! a string used to store the parsing errors
313
  std::string m_extra;
314
};
315
//! operator<< for an object
316
std::ostream &operator<<(std::ostream &o, DosLink const &dlink)
317
0
{
318
0
  switch (dlink.m_type)
319
0
  {
320
0
  case -1:
321
0
    break;
322
0
  case 1:
323
0
    o << "chart,";
324
0
    break;
325
0
  case 0x81:
326
0
    o << "pict,";
327
0
    break;
328
0
  case 0x40:
329
0
    o << "spreadsheet,";
330
0
    break;
331
0
  default:
332
0
    o << "#type=" << dlink.m_type << ",";
333
0
    break;
334
0
  }
335
0
  if (dlink.m_width >= 0) o << "width?=" << dlink.m_width << ",";
336
0
  if (dlink.m_size.x() >= 0 && (dlink.m_size.y()<0 || dlink.m_size.y()>0))
337
0
    o <<"size=" << dlink.m_size << ",";
338
0
  if (dlink.m_name.length()) o << "name='" << dlink.m_name << "',";
339
0
  if (!dlink.m_extra.empty()) o << ", err=" << dlink.m_extra;
340
0
  return o;
341
0
}
342
343
/** Internal: class to store a date/time format */
344
struct DateTime
345
{
346
  //! constructor
347
  DateTime()
348
690
    : m_type(-1)
349
690
    , m_extra("") {}
350
  //! returns a format to used with strftime
351
  std::string format() const;
352
  //! operator <<
353
  friend std::ostream &operator<<(std::ostream &o, DateTime const &dtime);
354
355
  //! the type
356
  int m_type;
357
  //! a string used to store the parsing errors
358
  std::string m_extra;
359
};
360
361
std::string DateTime::format() const
362
4.85k
{
363
4.85k
  switch (m_type)
364
4.85k
  {
365
573
  case 0:
366
573
    return "%m/%d/%Y";
367
224
  case 1:
368
224
    return "%m/%Y";
369
261
  case 2:
370
261
    return "%d %B %Y";
371
498
  case 3:
372
498
    return "%A %d %B %Y";
373
84
  case 4:
374
84
    return "%B %Y";
375
212
  case 5:
376
212
    return "%m/%d/%Y %I:%M";
377
249
  case 6:
378
249
    return "%m/%d/%Y %I:%M:%S";
379
355
  case 7:
380
355
    return "%I:%M:%S";
381
308
  case 8:
382
308
    return "%I:%M";
383
355
  case 9:
384
355
    return "%H:%M:%S";
385
871
  case 10:
386
871
    return "%H:%M";
387
864
  default:
388
864
    break;
389
4.85k
  }
390
864
  return "";
391
4.85k
}
392
393
//! operator<< for an object
394
std::ostream &operator<<(std::ostream &o, DateTime const &dtime)
395
0
{
396
0
  switch (dtime.m_type)
397
0
  {
398
0
  case -1:
399
0
    break;
400
0
  case 0:
401
0
  case 1:
402
0
  case 2:
403
0
  case 3:
404
0
  case 4:
405
0
    o << "date[F"<<dtime.m_type<<"],";
406
0
    break;
407
0
  case 5:
408
0
  case 6:
409
0
    o << "date&time[F"<<dtime.m_type-5<<"],";
410
0
    break;
411
0
  case 7:
412
0
  case 8:
413
0
  case 9:
414
0
  case 10:
415
0
    o << "time[F"<<dtime.m_type-7<<"],";
416
0
    break;
417
0
  default:
418
0
    o << "#type=" << dtime.m_type << ",";
419
0
    break;
420
0
  }
421
0
  if (!dtime.m_extra.empty()) o << ", err=" << dtime.m_extra;
422
0
  return o;
423
0
}
424
425
/** different types
426
 *
427
 * - BTE: font/paragraph properties
428
 * - OBJECT: object properties
429
 * - FTNp, FTNd: footnote position in text and footnote content
430
 * - BKMK: comment field
431
 * - DTTM: field type: date/time/..
432
 */
433
enum PLCType { BTE=0, OBJECT, FTNp, FTNd, BKMK, DTTM, Unknown};
434
435
/** Internal: class to store the PLC: Pointer List Content ? */
436
struct DataPLC
437
{
438
  //! constructor
439
  DataPLC()
440
50.6k
    : m_name("")
441
50.6k
    , m_type(Unknown)
442
50.6k
    , m_value(-1)
443
50.6k
    , m_extra() {}
444
  //! operator<<
445
  friend std::ostream &operator<<(std::ostream &o, DataPLC const &plc);
446
  //! the entry field name
447
  std::string m_name;
448
  //! the plc type
449
  PLCType m_type;
450
  //! a potential value
451
  long m_value;
452
  //! a string used to store the parsing errors
453
  std::string m_extra;
454
};
455
//! operator<< for a dataPLC
456
std::ostream &operator<<(std::ostream &o, DataPLC const &plc)
457
0
{
458
0
  o << "type=" << plc.m_name << ",";
459
0
  if (plc.m_value != -1) o << "val=" << std::hex << plc.m_value << std::dec << ", ";
460
0
  if (!plc.m_extra.empty()) o << "errors=(" << plc.m_extra << ")";
461
0
  return o;
462
0
}
463
464
/** Internal: the state of a WPS4Text */
465
struct State
466
{
467
  //! constructor
468
  State()
469
5.20k
    : m_fontNames()
470
5.20k
    , m_fontList()
471
5.20k
    , m_paragraphList()
472
5.20k
    , m_FDPCs()
473
5.20k
    , m_FDPPs()
474
5.20k
    , m_footnoteList()
475
5.20k
    , m_footnoteMap()
476
5.20k
    , m_bookmarkMap()
477
5.20k
    , m_dosLinkList()
478
5.20k
    , m_main()
479
5.20k
    , m_header()
480
5.20k
    , m_footer()
481
5.20k
    , m_otherZones()
482
5.20k
    , m_objectMap()
483
5.20k
    , m_dateTimeMap()
484
5.20k
    , m_plcList()
485
5.20k
    , m_knownPLC()
486
5.20k
  {}
487
488
  //! the list of fonts names
489
  std::map<int,FontName> m_fontNames;
490
  //! the list of all font properties
491
  std::vector<Font> m_fontList;
492
  //! the list of all paragraph properties
493
  std::vector<WPSParagraph> m_paragraphList;
494
495
  //! the list of FDPC entries (ie list to find the font properties lists )
496
  std::vector<WPSEntry> m_FDPCs;
497
  //! the list of FDPP entries (ie list to find the paragraph properties lists )
498
  std::vector<WPSEntry> m_FDPPs;
499
500
  //! the footnote entries
501
  std::vector<Note> m_footnoteList;
502
  //! map: footnote in text -> footnote entry
503
  std::map<long,Note const *> m_footnoteMap;
504
  //! map: bookmark in text -> bookmark
505
  std::map<long, WPSEntry> m_bookmarkMap;
506
  //! the dos file links
507
  std::vector<DosLink> m_dosLinkList;
508
509
  WPSEntry m_main /** the main text zone entry*/,
510
           m_header /** the header text entry*/, m_footer /** the footer text entry*/;
511
512
  //! the entries which are not in main/header/footer text and in the footnotes
513
  std::vector<WPSEntry> m_otherZones;
514
  //! map: object in text -> object
515
  std::map<long, Object> m_objectMap;
516
  //! map: date field in text -> date time format
517
  std::map<long, DateTime> m_dateTimeMap;
518
  //! a list of all PLCs
519
  std::vector<DataPLC> m_plcList;
520
  //! the known plc
521
  WPS4PLCInternal::KnownPLC m_knownPLC;
522
};
523
}
524
525
//////////////////////////////////////////////////////////////////////////////
526
//
527
//   MAIN CODE
528
//
529
//////////////////////////////////////////////////////////////////////////////
530
531
// constructor/destructor
532
WPS4Text::WPS4Text(WPS4Parser &parser, RVNGInputStreamPtr &input)
533
5.20k
  : WPSTextParser(parser, input)
534
5.20k
  , m_listener()
535
5.20k
  , m_state()
536
5.20k
{
537
5.20k
  m_state.reset(new WPS4TextInternal::State);
538
5.20k
}
539
540
WPS4Text::~WPS4Text()
541
5.20k
{
542
5.20k
}
543
544
// number of page
545
int WPS4Text::numPages() const
546
3.09k
{
547
3.09k
  int numPage = 1;
548
3.09k
  m_input->seek(m_textPositions.begin(), librevenge::RVNG_SEEK_SET);
549
5.42M
  while (!m_input->isEnd() && m_input->tell() != m_textPositions.end())
550
5.42M
  {
551
5.42M
    if (libwps::readU8(m_input.get()) == 0x0C) numPage++;
552
5.42M
  }
553
3.09k
  for (auto const &it : m_state->m_objectMap)
554
525
  {
555
525
    if (it.second.m_page<=numPage) continue;
556
188
    if (it.second.m_page<=numPage+10)
557
9
      numPage=it.second.m_page;
558
179
    else
559
179
    {
560
179
      WPS_DEBUG_MSG(("WPS4Text::numPages: the number of pages seems bad\n"));
561
179
    }
562
188
  }
563
3.09k
  return numPage;
564
3.09k
}
565
566
// page object
567
void WPS4Text::sendObjects(int page)
568
3.09k
{
569
3.09k
  for (auto const &it : m_state->m_objectMap)
570
525
  {
571
525
    auto const &obj=it.second;
572
525
    if (obj.m_page<=0 || obj.m_id<0) continue;
573
188
    if (page>=0 && obj.m_page!=page+1) continue;
574
188
    WPSPosition position(obj.m_origin, obj.m_size);
575
188
    if (page<0) position.setPage(obj.m_page);
576
188
    position.setRelativePosition(WPSPosition::Page);
577
188
    position.m_wrapping = WPSPosition::WDynamic;
578
188
    mainParser().sendObject(position, obj.m_id);
579
188
  }
580
3.09k
}
581
582
// return main/header/footer/all entry
583
WPSEntry WPS4Text::getHeaderEntry() const
584
3.09k
{
585
3.09k
  if (m_state->m_header.valid()) return m_state->m_header;
586
2.52k
  auto const &nameMultiMap = getNameEntryMap();
587
2.52k
  auto pos = nameMultiMap.find("SHdr");
588
2.52k
  if (pos == nameMultiMap.end()) return WPSEntry();
589
383
  WPSEntry res = pos->second;
590
383
  res.setType("TEXT");
591
383
  res.setId(WPS4TextInternal::Z_String);
592
383
  return res;
593
2.52k
}
594
595
WPSEntry WPS4Text::getFooterEntry() const
596
3.09k
{
597
3.09k
  if (m_state->m_footer.valid()) return m_state->m_footer;
598
2.86k
  auto const &nameMultiMap = getNameEntryMap();
599
2.86k
  auto pos = nameMultiMap.find("SFtr");
600
2.86k
  if (pos == nameMultiMap.end()) return WPSEntry();
601
484
  WPSEntry res = pos->second;
602
484
  res.setType("TEXT");
603
484
  res.setId(WPS4TextInternal::Z_String);
604
484
  return res;
605
2.86k
}
606
607
WPSEntry WPS4Text::getMainTextEntry() const
608
3.09k
{
609
3.09k
  return m_state->m_main;
610
3.09k
}
611
612
WPS4TextInternal::Font WPS4Text::getDefaultFont() const
613
78.9k
{
614
78.9k
  return WPS4TextInternal::Font::getDefault(mainParser().getDefaultFontType(), version());
615
78.9k
}
616
617
////////////////////////////////////////////////////////////
618
// send the data
619
////////////////////////////////////////////////////////////
620
void WPS4Text::flushExtra()
621
0
{
622
0
  if (!m_listener)
623
0
  {
624
0
    WPS_DEBUG_MSG(("WPS4Text::flushExtra can not find the listener\n"));
625
0
    return;
626
0
  }
627
0
  size_t numExtra = m_state->m_otherZones.size();
628
0
  if (numExtra == 0) return;
629
630
0
  m_listener->setFont(getDefaultFont());
631
0
  m_listener->setParagraph(WPSParagraph());
632
0
  m_listener->insertEOL();
633
#ifdef DEBUG
634
  librevenge::RVNGString message = "--------- extra text zone -------- ";
635
  m_listener->insertUnicodeString(message);
636
#endif
637
0
  for (size_t i = 0; i < numExtra; ++i)
638
0
    readText(m_state->m_otherZones[i]);
639
0
}
640
641
bool WPS4Text::readText(WPSEntry const &zone)
642
63.7k
{
643
63.7k
  bool bookmark = zone.id() == WPS4TextInternal::Z_Bookmark;
644
63.7k
  bool dlink = zone.id() == WPS4TextInternal::Z_DLink;
645
63.7k
  bool simpleString = zone.id() == WPS4TextInternal::Z_String || bookmark || dlink;
646
63.7k
  bool mainZone = zone.id() == WPS4TextInternal::Z_Main;
647
648
63.7k
  if (m_listener.get() == nullptr)
649
0
  {
650
0
    WPS_DEBUG_MSG(("WPS4Text::readText can not find the listener\n"));
651
0
    return false;
652
0
  }
653
63.7k
  if (!zone.valid())
654
0
  {
655
0
    WPS_DEBUG_MSG(("WPS4Text::readText invalid zone, must not happen\n"));
656
0
    m_listener->insertCharacter(' ');
657
0
    return false;
658
0
  }
659
63.7k
  if (mainZone)
660
2.97k
  {
661
2.97k
    int numCols = mainParser().numColumns();
662
2.97k
    if (numCols > 1)
663
139
    {
664
139
      if (m_listener->isSectionOpened())
665
0
      {
666
0
        WPS_DEBUG_MSG(("WPS4Text::readText the section is already open\n"));
667
0
      }
668
139
      else
669
139
      {
670
139
        int w = int(72*mainParser().pageWidth())/numCols;
671
139
        std::vector<int> width;
672
139
        width.resize(size_t(numCols), w);
673
139
        m_listener->openSection(width,librevenge::RVNG_POINT);
674
139
      }
675
139
    }
676
2.97k
  }
677
63.7k
  auto FODs_iter = m_FODList.begin();
678
679
  // update the property to correspond to the text
680
63.7k
  int prevFId = -1, prevPId = -1;
681
63.7k
  if (simpleString) FODs_iter = m_FODList.end();
682
41.4k
  else if (FODs_iter == m_FODList.end() && mainZone)
683
1.34k
  {
684
1.34k
    WPS_DEBUG_MSG(("WPS4Text::readText: CAN NOT FIND any FODs for main zone, REVERT to basic string!!!!!!!!!\n"));
685
1.34k
    simpleString = true;
686
1.34k
  }
687
688
92.7k
  for (; FODs_iter!= m_FODList.end(); ++FODs_iter)
689
35.2k
  {
690
35.2k
    auto const &fod = *(FODs_iter);
691
35.2k
    if (fod.m_pos >= zone.begin()) break;
692
693
28.9k
    int id = (*FODs_iter).m_id;
694
28.9k
    if (fod.m_type == DataFOD::ATTR_TEXT) prevFId = id;
695
22.0k
    else if (fod.m_type == DataFOD::ATTR_PARAG) prevPId = id;
696
28.9k
  }
697
698
63.7k
  WPS4TextInternal::Font defaultFont(getDefaultFont());
699
63.7k
  WPS4TextInternal::Font actFont(defaultFont);
700
63.7k
  if (prevFId != -1)
701
384
    actFont = m_state->m_fontList[size_t(prevFId)];
702
63.7k
  m_listener->setFont(actFont);
703
704
63.7k
  if (prevPId != -1)
705
234
    m_listener->setParagraph(m_state->m_paragraphList[size_t(prevPId)]);
706
63.5k
  else
707
63.5k
    m_listener->setParagraph(WPSParagraph());
708
709
63.7k
  if (dlink)
710
4.61k
  {
711
4.61k
    m_listener->insertUnicodeString("include ");
712
4.61k
    m_listener->insertUnicode(0x226a);
713
4.61k
  }
714
63.7k
  bool first = true;
715
63.7k
  int actPage = 1;
716
265k
  for (; simpleString || FODs_iter!= m_FODList.end(); ++FODs_iter)
717
228k
  {
718
228k
    long actPos;
719
228k
    long lastPos;
720
721
722
228k
    libwps::DebugStream f;
723
228k
    f << "Text";
724
725
228k
    if (simpleString)
726
23.6k
    {
727
23.6k
      actPos = zone.begin();
728
23.6k
      lastPos = zone.end();
729
23.6k
    }
730
205k
    else
731
205k
    {
732
205k
      auto fod = *(FODs_iter);
733
205k
      actPos = first ? zone.begin() : fod.m_pos;
734
205k
      if (long(actPos) >= zone.end()) break;
735
202k
      first = false;
736
737
202k
      if (++FODs_iter!= m_FODList.end())
738
198k
      {
739
198k
        lastPos = (*FODs_iter).m_pos;
740
198k
        if (long(lastPos) >= zone.end()) lastPos = zone.end();
741
198k
      }
742
3.24k
      else
743
3.24k
        lastPos = zone.end();
744
202k
      --FODs_iter;
745
202k
      int fId = fod.m_id;
746
202k
      switch (fod.m_type)
747
202k
      {
748
57.4k
      case DataFOD::ATTR_TEXT:
749
57.4k
        if (fId >= 0)
750
21.2k
          actFont = m_state->m_fontList[size_t(fId)];
751
36.2k
        else
752
36.2k
          actFont = defaultFont;
753
57.4k
        m_listener->setFont(actFont);
754
#if DEBUG_FP
755
        f << "[";
756
        if (fId >= 0) f << "C" << fId << ":" << actFont << "]";
757
        else f << "_]";
758
#endif
759
57.4k
        break;
760
133k
      case DataFOD::ATTR_PARAG:
761
133k
        if (fId >= 0)
762
78.1k
          m_listener->setParagraph(m_state->m_paragraphList[size_t(fId)]);
763
55.2k
        else
764
55.2k
          m_listener->setParagraph(WPSParagraph());
765
#if DEBUG_PP
766
        f << "[";
767
        if (fId >= 0) f << "P" << fId << ":" << m_state->m_paragraphList[size_t(fId)] << "]";
768
        else f << "_]";
769
#endif
770
133k
        break;
771
11.2k
      case DataFOD::ATTR_PLC:
772
11.2k
        if (fId >= 0 && m_state->m_plcList[size_t(fId)].m_type == WPS4TextInternal::BKMK)
773
2.90k
        {
774
2.90k
          WPSEntry bkmk;
775
2.90k
          if (m_state->m_bookmarkMap.find(actPos) == m_state->m_bookmarkMap.end())
776
1.39k
          {
777
1.39k
            WPS_DEBUG_MSG(("WPS4Text::readText: can not find the bookmark entry\n"));
778
1.39k
          }
779
1.50k
          else
780
1.50k
            bkmk = m_state->m_bookmarkMap.find(actPos)->second;
781
2.90k
          bkmk.setType("TEXT");
782
2.90k
          bkmk.setId(WPS4TextInternal::Z_Bookmark);
783
2.90k
          mainParser().createDocument(bkmk, libwps::DOC_COMMENT_ANNOTATION);
784
2.90k
        }
785
#if DEBUG_PLC_POS
786
        f << "[PLC";
787
        if (fId>= 0) f << m_state->m_plcList[size_t(fId)] << "]";
788
        else f << "_]";
789
#endif
790
11.2k
        break;
791
0
      case DataFOD::ATTR_UNKN:
792
0
      default:
793
0
        WPS_DEBUG_MSG(("WPS4Text::readText: find unknown plc\n"));
794
#if DEBUG_PLC_POS
795
        f << "[DataFOD(###Unknown)]";
796
#endif
797
0
        break;
798
202k
      }
799
202k
    }
800
225k
    m_input->seek(actPos, librevenge::RVNG_SEEK_SET);
801
225k
    std::string chaine("");
802
225k
    long len = lastPos-actPos;
803
225k
    std::string text;
804
27.0M
    for (long i = len; i>=0; i--)
805
26.8M
    {
806
26.8M
      long pos = m_input->tell();
807
26.8M
      uint8_t readVal = i==0 ? uint8_t('\0') : libwps::readU8(m_input);
808
26.8M
      bool isSimpleField=false;
809
26.8M
      if (readVal=='&' && simpleString && pos+2 <= lastPos)
810
61.4k
      {
811
61.4k
        int nextVal = libwps::readU8(m_input);
812
61.4k
        m_input->seek(-1, librevenge::RVNG_SEEK_CUR);
813
61.4k
        switch (nextVal)   // check me
814
61.4k
        {
815
3.12k
        case 'p':
816
3.70k
        case 'P':
817
3.98k
        case 'd':
818
4.30k
        case 'D':
819
5.24k
        case 't':
820
6.55k
        case 'T':
821
7.33k
        case 'f':
822
8.57k
        case 'F':
823
8.57k
          isSimpleField=true;
824
8.57k
          break;
825
52.9k
        default:
826
52.9k
          break;
827
61.4k
        }
828
61.4k
      }
829
26.8M
      if ((readVal<0x10 || readVal==0x11 || readVal==0x12 || readVal==0x1f || (version()<=2 && readVal==0xca) || isSimpleField) && !text.empty())
830
2.54M
      {
831
2.54M
        m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(text, actFont.m_type));
832
2.54M
        text.clear();
833
2.54M
      }
834
26.8M
      if (0x00 == readVal)
835
14.6M
      {
836
14.6M
        if (i > 1)
837
14.3M
        {
838
14.3M
          WPS_DEBUG_MSG(("WPS4Text::readText: find some unexpected 0 character\n"));
839
          // probably an error, but we can ignore id
840
14.3M
          chaine += '#';
841
14.3M
        }
842
14.6M
        continue;
843
14.6M
      }
844
12.2M
      chaine += char(readVal);
845
12.2M
      switch (readVal)
846
12.2M
      {
847
341k
      case 0x01: // chart ?
848
401k
      case 0x08: // spreadsheet range
849
434k
      case 0x0e: // picture
850
434k
      {
851
434k
        if (!actFont.m_special || m_state->m_dosLinkList.empty() || actFont.m_dlinkId >= int(m_state->m_dosLinkList.size()))
852
428k
        {
853
428k
          WPS_DEBUG_MSG(("WPS4Text::readText: send DLINK can not find id\n"));
854
428k
          break;
855
428k
        }
856
5.28k
        int id = actFont.m_dlinkId >= 0 ? actFont.m_dlinkId : 0;
857
5.28k
        WPSEntry ent = m_state->m_dosLinkList[size_t(id)].m_pos;
858
5.28k
        ent.setType("TEXT");
859
5.28k
        ent.setId(WPS4TextInternal::Z_DLink);
860
5.28k
        WPSPosition pos_(Vec2f(),Vec2f(3.0f,0.2f));
861
5.28k
        pos_.setRelativePosition(WPSPosition::Paragraph, WPSPosition::XCenter);
862
5.28k
        pos_.m_wrapping = WPSPosition::WNone;
863
5.28k
        librevenge::RVNGPropertyList extras;
864
5.28k
        mainParser().createTextBox(ent, pos_, extras);
865
5.28k
        m_listener->insertEOL();
866
5.28k
        break;
867
434k
      }
868
146k
      case 0x02:
869
146k
        m_listener->insertField(WPSField(WPSField::PageNumber));
870
146k
        break;
871
54.5k
      case 0x03:
872
54.5k
        m_listener->insertField(WPSField(WPSField::Date));
873
54.5k
        break;
874
164k
      case 0x04:
875
164k
        m_listener->insertField(WPSField(WPSField::Time));
876
164k
        break;
877
66.0k
      case 0x05:
878
66.0k
        m_listener->insertField(WPSField(WPSField::Title));
879
66.0k
        break;
880
60.4k
      case 0x07:
881
60.4k
      {
882
60.4k
        if (m_state->m_objectMap.find(actPos) == m_state->m_objectMap.end())
883
58.1k
        {
884
58.1k
          WPS_DEBUG_MSG(("WPS4Text::readText: can not find object for position : %lX\n", static_cast<unsigned long>(actPos)));
885
58.1k
        }
886
2.24k
        else
887
2.24k
        {
888
2.24k
          WPS4TextInternal::Object const &obj = m_state->m_objectMap[actPos];
889
2.24k
          if (obj.m_id < 0 || obj.m_page > 0) break;
890
1.84k
          WPSPosition position(Vec2f(0,0), obj.m_size);
891
1.84k
          position.setRelativePosition(WPSPosition::CharBaseLine);
892
1.84k
          position.m_wrapping = WPSPosition::WDynamic;
893
1.84k
          mainParser().sendObject(position, obj.m_id);
894
1.84k
        }
895
60.0k
        break;
896
60.4k
      }
897
60.0k
      case 0x0f:
898
41.3k
      {
899
41.3k
        if (m_state->m_dateTimeMap.find(actPos) == m_state->m_dateTimeMap.end())
900
36.4k
        {
901
36.4k
          WPS_DEBUG_MSG(("WPS4Text::readText: can not find date/time for position : %lX\n", static_cast<unsigned long>(actPos)));
902
36.4k
        }
903
4.85k
        else
904
4.85k
        {
905
4.85k
          auto const &form = m_state->m_dateTimeMap[actPos];
906
4.85k
          std::string format = form.format();
907
4.85k
          if (format.length())
908
3.99k
          {
909
3.99k
            WPSField field(WPSField::Date);
910
3.99k
            field.m_DTFormat=format;
911
3.99k
            m_listener->insertField(field);
912
3.99k
          }
913
864
          else
914
864
          {
915
864
            WPS_DEBUG_MSG(("WPS4Text::readText: unknown date/time format for position : %lX\n", static_cast<unsigned long>(actPos)));
916
864
          }
917
4.85k
        }
918
41.3k
        break;
919
60.4k
      }
920
70.3k
      case 0x06:   // footnote
921
70.3k
      {
922
        // ok if this is the first character of the footnote definition
923
70.3k
        if (zone.id() == WPS4TextInternal::Z_Note) break;
924
66.7k
        if (m_state->m_footnoteMap.find(pos) == m_state->m_footnoteMap.end() ||
925
1.56k
                m_state->m_footnoteMap[pos] == nullptr)
926
65.1k
        {
927
65.1k
          WPS_DEBUG_MSG(("WPS4Text::readText:do not find the footnote zone\n"));
928
65.1k
          break;
929
65.1k
        }
930
1.56k
        auto const &note = *m_state->m_footnoteMap[pos];
931
1.56k
        mainParser().createNote(note, note.m_label);
932
1.56k
        break;
933
66.7k
      }
934
81.3k
      case 0x09:
935
81.3k
        m_listener->insertTab();
936
81.3k
        break;
937
903k
      case 0x0C:
938
903k
        if (mainZone) mainParser().newPage(++actPage);
939
903k
        break;
940
84.6k
      case 0x0d:
941
84.6k
        break; // 0d0a = end of line
942
204k
      case 0x0a:
943
204k
        m_listener->insertEOL();
944
204k
        break;
945
40.4k
      case 0x0b: // check me
946
40.4k
        m_listener->insertEOL(true);
947
40.4k
        break;
948
21.9k
      case 0x11: // insecable hyphen
949
21.9k
        m_listener->insertUnicode(0x2011);
950
21.9k
        break;
951
87.7k
      case 0x12: // insecable space
952
87.7k
        m_listener->insertUnicode(0xA0);
953
87.7k
        break;
954
16.0k
      case 0x1F: // optional hyphen
955
16.0k
        break;
956
64.1k
      case '&':
957
64.1k
        if (isSimpleField)
958
8.57k
        {
959
8.57k
          int nextVal = libwps::readU8(m_input);
960
8.57k
          bool done = true;
961
8.57k
          switch (nextVal)   // check me
962
8.57k
          {
963
3.12k
          case 'p':
964
3.70k
          case 'P':
965
3.70k
            m_listener->insertField(WPSField(WPSField::PageNumber));
966
3.70k
            break;
967
280
          case 'd':
968
599
          case 'D':
969
599
            m_listener->insertField(WPSField(WPSField::Date));
970
599
            break;
971
939
          case 't':
972
2.24k
          case 'T':
973
2.24k
            m_listener->insertField(WPSField(WPSField::Time));
974
2.24k
            break;
975
785
          case 'f':
976
2.02k
          case 'F':
977
2.02k
            m_listener->insertField(WPSField(WPSField::Title));
978
2.02k
            break;
979
          // case '&': check me does '&&'->'&' ?
980
0
          default:
981
0
            done = false;
982
0
            break;
983
8.57k
          }
984
8.57k
          if (done)
985
8.57k
          {
986
8.57k
            i--;
987
8.57k
            break;
988
8.57k
          }
989
0
          m_input->seek(-1, librevenge::RVNG_SEEK_CUR);
990
0
        }
991
55.6k
        WPS_FALLTHROUGH;
992
9.74M
      default:
993
9.74M
        if (version()<=2)
994
7.19M
        {
995
          // special caracter
996
7.19M
          if (readVal==0xca) // not breaking space
997
5.06k
          {
998
5.06k
            m_listener->insertCharacter(0xA0);
999
5.06k
            break;
1000
5.06k
          }
1001
7.19M
        }
1002
9.74M
        if (readVal)
1003
9.74M
          text.push_back(char(readVal));
1004
9.74M
        break;
1005
12.2M
      }
1006
12.2M
    }
1007
1008
225k
    if (simpleString) break;
1009
1010
202k
    f << "='"<<chaine<<"'";
1011
202k
    ascii().addPos(actPos);
1012
202k
    ascii().addNote(f.str().c_str());
1013
202k
  }
1014
1015
63.7k
  if (dlink)
1016
4.61k
    m_listener->insertUnicode(0x226b);
1017
1018
63.7k
  return true;
1019
63.7k
}
1020
1021
////////////////////////////////////////////////////////////
1022
// find all the text entries
1023
////////////////////////////////////////////////////////////
1024
bool WPS4Text::readEntries()
1025
4.63k
{
1026
4.63k
  auto &nameMultiMap = getNameEntryMap();
1027
1028
4.63k
  libwps::DebugStream f;
1029
4.63k
  long actPos = m_input->tell();
1030
4.63k
  f << "ZZHeader-Text:Limit(";
1031
1032
4.63k
  int textLimits[4];
1033
  // look like begin of text : end of header/end of footer/end text
1034
  // but sometimes the zones overlaps !!!
1035
18.5k
  for (int &textLimit : textLimits) textLimit = libwps::read32(m_input);
1036
1037
4.63k
  bool first = true, ok = true;
1038
4.63k
  long lastPos = textLimits[0] < 0x100 ? 0x100 : textLimits[0];
1039
18.5k
  for (int i = 0; i < 3; ++i)
1040
13.9k
  {
1041
13.9k
    long newPos = textLimits[i+1];
1042
13.9k
    WPSEntry zone;
1043
13.9k
    zone.setBegin(lastPos);
1044
13.9k
    zone.setEnd(newPos);
1045
13.9k
    zone.setType("TEXT");
1046
13.9k
    zone.setId(i);
1047
1048
13.9k
    if (newPos >= lastPos)
1049
7.43k
      lastPos = newPos;
1050
13.9k
    if (!zone.valid() || zone.begin() < 0x100)
1051
9.22k
    {
1052
9.22k
      if (newPos != 0x100 && newPos != -1)
1053
7.44k
      {
1054
7.44k
        WPS_DEBUG_MSG(("WPS4Text::readEntries: find odd text limit\n"));
1055
7.44k
        ok = false;
1056
7.44k
      }
1057
9.22k
      f << "_, ";
1058
9.22k
      continue;
1059
9.22k
    }
1060
1061
4.68k
    if (first)
1062
3.53k
    {
1063
3.53k
      m_textPositions.setBegin(zone.begin());
1064
3.53k
      first = false;
1065
3.53k
    }
1066
1067
4.68k
    m_textPositions.setEnd(zone.end());
1068
4.68k
    nameMultiMap.insert(WPS4Parser::NameMultiMap::value_type(zone.type(), zone));
1069
1070
4.68k
    switch (i)
1071
4.68k
    {
1072
1.13k
    case 0:
1073
1.13k
      m_state->m_header = zone;
1074
1.13k
      break;
1075
1.58k
    case 1:
1076
1.58k
      m_state->m_footer = zone;
1077
1.58k
      break;
1078
1.96k
    case 2:
1079
1.96k
      m_state->m_main = zone;
1080
1.96k
      break;
1081
0
    default:
1082
0
      break;
1083
4.68k
    }
1084
1085
4.68k
    f << "Text"<<i << "=" << std::hex << zone.begin() << "x" << zone.end() << ",";
1086
4.68k
    ascii().addPos(zone.begin());
1087
4.68k
    std::string name = "ZZ";
1088
4.68k
    name+= zone.type();
1089
4.68k
    name+=char('0'+i);
1090
4.68k
    ascii().addNote(name.c_str());
1091
4.68k
    ascii().addPos(zone.end());
1092
4.68k
    ascii().addNote("_");
1093
4.68k
  }
1094
4.63k
  f << ")";
1095
4.63k
  if (!ok)
1096
3.54k
  {
1097
3.54k
    m_state->m_header = m_state->m_footer = WPSEntry();
1098
3.54k
    m_state->m_main = m_textPositions;
1099
3.54k
  }
1100
4.63k
  if (!m_textPositions.valid())
1101
1.10k
  {
1102
1.10k
    WPS_DEBUG_MSG(("WPS4Text::readEntries: textPosition is not valid"));
1103
1.10k
    return false;
1104
1.10k
  }
1105
1106
  /* stream offset to end of file */
1107
3.53k
  auto eof = long(libwps::readU32(m_input));
1108
1109
3.53k
  if (m_textPositions.end() > eof)
1110
117
  {
1111
117
    WPS_DEBUG_MSG(("WPS4Text:readEntries: can not find text positions\n"));
1112
117
    return false;
1113
117
  }
1114
1115
  // check if fPositions.offset_eos
1116
3.41k
  long newPos = m_input->tell();
1117
3.41k
  if (m_input->seek(eof-1, librevenge::RVNG_SEEK_SET) != 0 || m_input->tell() != eof-1)
1118
2.93k
  {
1119
2.93k
    eof = m_input->tell();
1120
2.93k
    WPS_DEBUG_MSG(("WPS4Text:readEntries: incomplete file\n"));
1121
2.93k
    if (eof < m_textPositions.end()) return false;
1122
2.93k
  }
1123
3.22k
  mainParser().setSizeFile(eof);
1124
1125
3.22k
  f << ", endFile=" << eof;
1126
3.22k
  ascii().addPos(actPos);
1127
3.22k
  ascii().addNote(f.str().c_str());
1128
1129
3.22k
  m_input->seek(newPos, librevenge::RVNG_SEEK_SET);
1130
1131
3.22k
  static char const   *zName[] =
1132
3.22k
  { "BTEC", "BTEP", "SHdr", "SFtr", "DLINK", "FTNp", "FTNd", "BKMK", "FONT" };
1133
1134
3.22k
  for (auto &i : zName)
1135
29.0k
    mainParser().parseEntry(i);
1136
1137
3.22k
  return true;
1138
3.41k
}
1139
1140
////////////////////////////////////////////////////////////
1141
// find all the text structures
1142
////////////////////////////////////////////////////////////
1143
bool WPS4Text::readStructures()
1144
3.22k
{
1145
3.22k
  auto &nameMultiMap = getNameEntryMap();
1146
1147
  // first find the font name
1148
3.22k
  auto pos = nameMultiMap.find("FONT");
1149
3.22k
  if (pos != nameMultiMap.end()) readFontNames(pos->second);
1150
1151
  // now find the character and paragraph properties
1152
9.41k
  for (int i = 0; i < 2; ++i)
1153
6.19k
  {
1154
    // we begin by i = 1 to create firsts the fdpc structure
1155
6.19k
    if (findFDPStructures(1-i)) continue;
1156
4.97k
    findFDPStructuresByHand(1-i);
1157
4.97k
  }
1158
1159
  /* read character FODs (FOrmatting Descriptors) */
1160
3.22k
  std::vector<DataFOD> fdps;
1161
3.22k
  for (auto const &fdp : m_state->m_FDPCs)
1162
14.7k
    readFDP(fdp, fdps, static_cast<FDPParser>(&WPS4Text::readFont));
1163
3.22k
  m_FODList = mergeSortedFODLists(fdps, m_FODList);
1164
1165
1166
  /* read paragraphs FODs (FOrmatting Descriptors) */
1167
3.22k
  fdps.resize(0);
1168
3.22k
  for (auto const &fdp : m_state->m_FDPPs)
1169
30.4k
    readFDP(fdp, fdps, static_cast<FDPParser>(&WPS4Text::readParagraph));
1170
3.22k
  m_FODList = mergeSortedFODLists(fdps, m_FODList);
1171
1172
  /* read the object structures */
1173
3.22k
  pos = nameMultiMap.find("EOBJ");
1174
3.22k
  if (pos != nameMultiMap.end())
1175
782
  {
1176
782
    std::vector<long> textPtrs, listValues;
1177
782
    readPLC(pos->second, textPtrs, listValues, &WPS4Text::objectDataParser);
1178
782
  }
1179
1180
  // update the footnote
1181
3.22k
  WPSEntry ftnD, ftnP;
1182
3.22k
  pos = nameMultiMap.find("FTNd");
1183
3.22k
  if (pos != nameMultiMap.end()) ftnD = pos->second;
1184
3.22k
  pos = nameMultiMap.find("FTNp");
1185
3.22k
  if (pos != nameMultiMap.end()) ftnP = pos->second;
1186
3.22k
  readFootNotes(ftnD, ftnP);
1187
1188
  // bookmark
1189
3.22k
  pos = nameMultiMap.find("BKMK");
1190
3.22k
  if (pos != nameMultiMap.end())
1191
420
  {
1192
420
    std::vector<long> textPtrs, listValues;
1193
420
    readPLC(pos->second, textPtrs, listValues, &WPS4Text::bkmkDataParser);
1194
420
  }
1195
1196
  // the list of file
1197
3.22k
  pos = nameMultiMap.find("DLINK");
1198
3.22k
  if (pos != nameMultiMap.end())
1199
299
    readDosLink(pos->second);
1200
1201
  // date/time format
1202
3.22k
  pos = nameMultiMap.find("DTTM");
1203
3.22k
  if (pos != nameMultiMap.end())
1204
340
  {
1205
340
    WPSEntry const &zone = pos->second;
1206
340
    std::vector<long> textPtrs, listValues;
1207
340
    readPLC(zone, textPtrs, listValues, &WPS4Text::dttmDataParser);
1208
340
  }
1209
1210
  // finally, we must remove the footnote of textposition...
1211
3.22k
  long bot = m_state->m_main.begin();
1212
3.22k
  long endPos = m_state->m_main.end();
1213
3.22k
  size_t numFootNotes = m_state->m_footnoteList.size(), actNote = 0;
1214
3.22k
  bool textPosUpdated = false;
1215
8.75k
  while (bot < endPos)
1216
5.52k
  {
1217
5.52k
    if (actNote < numFootNotes &&
1218
2.56k
            m_state->m_footnoteList[actNote].begin()==bot)
1219
1.53k
    {
1220
1.53k
      bot = m_state->m_footnoteList[actNote].end();
1221
1.53k
      actNote++;
1222
1.53k
      continue;
1223
1.53k
    }
1224
3.99k
    long lastPos = actNote < numFootNotes ?
1225
2.96k
                   m_state->m_footnoteList[actNote].begin() : endPos;
1226
3.99k
    if (lastPos > endPos) lastPos = endPos;
1227
3.99k
    WPSEntry mZone;
1228
3.99k
    mZone.setBegin(bot);
1229
3.99k
    mZone.setEnd(lastPos);
1230
3.99k
    mZone.setType("TEXT");
1231
3.99k
    if (!textPosUpdated)
1232
3.01k
    {
1233
3.01k
      mZone.setId(WPS4TextInternal::Z_Main);
1234
3.01k
      m_state->m_main = mZone;
1235
3.01k
      textPosUpdated = true;
1236
3.01k
    }
1237
982
    else
1238
982
    {
1239
982
      if (m_state->m_otherZones.size() == 0)
1240
41
      {
1241
41
        WPS_DEBUG_MSG(("WPS4Text::readStructures: find unknown text zone\n"));
1242
41
      }
1243
982
      mZone.setId(WPS4TextInternal::Z_Unknown);
1244
982
      m_state->m_otherZones.push_back(mZone);
1245
982
    }
1246
3.99k
    bot = lastPos;
1247
3.99k
  }
1248
1249
1250
3.22k
  return true;
1251
3.22k
}
1252
1253
////////////////////////////////////////////////////////////
1254
//  find FDP zones ( normal method followed by another method
1255
//   which may works for some bad files )
1256
////////////////////////////////////////////////////////////
1257
bool WPS4Text::findFDPStructures(int which)
1258
6.19k
{
1259
6.19k
  std::vector<WPSEntry> &zones = which ? m_state->m_FDPCs : m_state->m_FDPPs;
1260
6.19k
  zones.resize(0);
1261
1262
6.19k
  char const *indexName = which ? "BTEC" : "BTEP";
1263
6.19k
  char const *sIndexName = which ? "FDPC" : "FDPP";
1264
1265
6.19k
  auto const &nameMultiMap =getNameEntryMap();
1266
6.19k
  auto pos = nameMultiMap.find(indexName);
1267
6.19k
  if (pos == nameMultiMap.end()) return false;
1268
1269
2.82k
  std::vector<long> textPtrs;
1270
2.82k
  std::vector<long> listValues;
1271
1272
2.82k
  if (!readPLC(pos->second, textPtrs, listValues)) return false;
1273
1274
1.46k
  size_t numV = listValues.size();
1275
1.46k
  if (textPtrs.size() != numV+1) return false;
1276
1277
1.35k
  WPSEntry zone;
1278
1.35k
  zone.setType(sIndexName);
1279
1280
46.0k
  for (size_t i = 0; i < numV; ++i)
1281
44.8k
  {
1282
44.8k
    long bPos = listValues[i];
1283
44.8k
    if (bPos <= 0) return false;
1284
44.7k
    zone.setBegin(bPos);
1285
44.7k
    zone.setLength(0x80);
1286
1287
44.7k
    zones.push_back(zone);
1288
44.7k
  }
1289
1290
1.22k
  return true;
1291
1.35k
}
1292
1293
bool WPS4Text::findFDPStructuresByHand(int which)
1294
4.97k
{
1295
4.97k
  char const *indexName = which ? "FDPC" : "FDPP";
1296
4.97k
  WPS_DEBUG_MSG(("WPS4Text::findFDPStructuresByHand: need to create %s list by hand \n", indexName));
1297
1298
4.97k
  auto &zones = which ? m_state->m_FDPCs : m_state->m_FDPPs;
1299
4.97k
  zones.resize(0);
1300
1301
4.97k
  long debPos;
1302
4.97k
  if (which == 1)
1303
2.65k
  {
1304
    // hack: each fdp block is aligned with 0x80,
1305
    //       and appears consecutively just after the text
1306
2.65k
    auto pnChar = uint32_t((m_textPositions.end()+127)>>7);
1307
    /* sanity check */
1308
2.65k
    if (0 == pnChar)
1309
0
    {
1310
0
      WPS_DEBUG_MSG(("WPS4Text::findFDPStructuresByHand: pnChar is 0, so file may be corrupt\n"));
1311
0
      throw libwps::ParseException();
1312
0
    }
1313
2.65k
    debPos = 0x80 * long(pnChar);
1314
2.65k
  }
1315
2.31k
  else
1316
2.31k
  {
1317
2.31k
    size_t nFDPC = m_state->m_FDPCs.size();
1318
2.31k
    if (!nFDPC)
1319
1.84k
    {
1320
1.84k
      WPS_DEBUG_MSG(("WPS4Text::findFDPStructuresByHand: can not find last fdpc pos\n"));
1321
1.84k
      return false;
1322
1.84k
    }
1323
470
    debPos = m_state->m_FDPCs[nFDPC-1].end();
1324
470
  }
1325
1326
3.12k
  WPSEntry fdp;
1327
3.12k
  fdp.setType(indexName);
1328
1329
3.12k
  long lastPos = m_textPositions.begin();
1330
4.51k
  while (1)
1331
4.51k
  {
1332
4.51k
    m_input->seek(debPos+0x7f, librevenge::RVNG_SEEK_SET);
1333
4.51k
    if (m_input->tell() != debPos+0x7f)
1334
1.43k
    {
1335
1.43k
      WPS_DEBUG_MSG(("WPS4Text: find EOF while parsing the %s\n", indexName));
1336
1.43k
      return false;
1337
1.43k
    }
1338
3.07k
    int nbElt = libwps::readU8(m_input);
1339
3.07k
    if (5*nbElt+4 > 0x80)
1340
584
    {
1341
584
      WPS_DEBUG_MSG(("WPS4Text: find too big number of data while parsing the %s\n", indexName));
1342
584
      return false;
1343
584
    }
1344
2.49k
    m_input->seek(debPos, librevenge::RVNG_SEEK_SET);
1345
2.49k
    if (long(libwps::readU32(m_input)) != lastPos)
1346
991
    {
1347
991
      WPS_DEBUG_MSG(("WPS4Text: find incorrect linking while parsing the %s\n", indexName));
1348
991
      return false;
1349
991
    }
1350
1.50k
    if (nbElt != 1)
1351
1.40k
      m_input->seek(4*nbElt-4, librevenge::RVNG_SEEK_CUR);
1352
1353
1.50k
    auto newPos = long(libwps::readU32(m_input));
1354
1.50k
    if (newPos < lastPos || newPos > m_textPositions.end())
1355
67
    {
1356
67
      WPS_DEBUG_MSG(("WPS4Text: find incorrect linking while parsing the %s\n", indexName));
1357
67
      return false;
1358
67
    }
1359
1.43k
    fdp.setBegin(debPos);
1360
1.43k
    fdp.setLength(0x80);
1361
1.43k
    zones.push_back(fdp);
1362
1363
1.43k
    if (newPos == m_textPositions.end()) break;
1364
1365
1.39k
    lastPos = newPos;
1366
1.39k
    debPos = fdp.end();
1367
1.39k
  }
1368
1369
46
  return true;
1370
3.12k
}
1371
1372
// PLC Data: default parser
1373
bool WPS4Text::defDataParser(long, long, int, long endPos, std::string &mess)
1374
785
{
1375
785
  mess = "";
1376
785
  libwps::DebugStream f;
1377
1378
785
  long actPos = m_input->tell();
1379
785
  long length = endPos+1-actPos;
1380
785
  int sz = (length%4)==0 ? 4 : (length%2)==0 ? 2 : 1;
1381
785
  f << "unk["<< sz << "]=";
1382
7.88k
  while (m_input->tell() <= endPos+1-sz)
1383
7.10k
  {
1384
7.10k
    long val = 0;
1385
7.10k
    switch (sz)
1386
7.10k
    {
1387
2.92k
    case 1:
1388
2.92k
      val = libwps::readU8(m_input);
1389
2.92k
      break;
1390
982
    case 2:
1391
982
      val = libwps::readU16(m_input);
1392
982
      break;
1393
3.20k
    case 4:
1394
3.20k
      val = long(libwps::readU32(m_input));
1395
3.20k
      break;
1396
0
    default:
1397
0
      break;
1398
7.10k
    }
1399
7.10k
    f << std::hex << val << std::dec << ",";
1400
7.10k
  }
1401
785
  mess = f.str();
1402
785
  return true;
1403
785
}
1404
1405
////////////////////////////////////////////////////////////
1406
// the fonts name zone (zone8)
1407
////////////////////////////////////////////////////////////
1408
bool WPS4Text::readFontNames(WPSEntry const &entry)
1409
502
{
1410
502
  if (!entry.valid()) return false;
1411
1412
502
  m_input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1413
1414
502
  long endPos = entry.end();
1415
502
  int nFonts = 0;
1416
502
  auto docType=mainParser().getDefaultFontType();
1417
2.38k
  while (m_input->tell() < endPos)
1418
2.00k
  {
1419
2.00k
    long actPos;
1420
2.00k
    actPos = m_input->tell();
1421
2.00k
    libwps::DebugStream f;
1422
1423
    /* Sometimes the font numbers start at 0 and increment nicely.
1424
       However, other times the font numbers jump around. */
1425
2.00k
    uint8_t font_number = libwps::readU8(m_input);
1426
2.00k
    if (m_state->m_fontNames.find(font_number) != m_state->m_fontNames.end())
1427
105
    {
1428
105
      WPS_DEBUG_MSG(("WPS4Text::readFontNames: at position 0x%lx: font number %i duplicated\n",
1429
105
                     static_cast<unsigned long>(m_input->tell()-2), font_number));
1430
105
      throw libwps::ParseException();
1431
105
    }
1432
1433
1.90k
    f << "Font" << nFonts++ << ": id=" << int(font_number) << ", ";
1434
    //fixme: what is this byte? maybe a font class
1435
1.90k
    uint8_t unknown_byte = libwps::readU8(m_input);
1436
1.90k
    f << "unk=" << int(unknown_byte) << ", ";
1437
1438
1.90k
    librevenge::RVNGString s;
1439
1.90k
    uint8_t nChar = libwps::readU8(m_input);
1440
144k
    for (uint8_t i = nChar; i>0; i--)
1441
142k
    {
1442
142k
      if (m_input->isEnd())
1443
23
      {
1444
23
        WPS_DEBUG_MSG(("WPS4Text::readFontNames: can not read the font number %i (end of file)\n",
1445
23
                       font_number));
1446
23
        throw libwps::ParseException();
1447
23
      }
1448
142k
      unsigned char val = libwps::readU8(m_input);
1449
      // sanity check (because sometimes contains char > 0x80 .. )
1450
142k
      if (val >= ' ' && val <= 'z') s.append(char(val));
1451
92.9k
      else
1452
92.9k
      {
1453
92.9k
        static bool first = true;
1454
92.9k
        if (first)
1455
3
        {
1456
3
          first = false;
1457
3
          WPS_DEBUG_MSG(("WPS4Text:readFontNames find odd caracters in font name : %d\n", int(val)));
1458
3
        }
1459
92.9k
        f << "##oddC=" << int(val) << ", ";
1460
92.9k
      }
1461
142k
    }
1462
1.87k
    auto fType=libwps_tools_win::Font::getFontType(s);
1463
1.87k
    if (fType==libwps_tools_win::Font::UNKNOWN)
1464
1.82k
      fType=docType;
1465
1.87k
    WPS4TextInternal::FontName font(fType);
1466
1.87k
    font.m_name = s;
1467
1.87k
    f << font;
1468
1469
1.87k
    m_state->m_fontNames.insert(std::pair<int,WPS4TextInternal::FontName>(font_number,font));
1470
1471
1.87k
    ascii().addPos(actPos);
1472
1.87k
    ascii().addNote(f.str().c_str());
1473
1.87k
    ascii().addPos(m_input->tell());
1474
1.87k
  }
1475
1476
374
  return true;
1477
502
}
1478
1479
////////////////////////////////////////////////////////////
1480
// the font:
1481
////////////////////////////////////////////////////////////
1482
bool WPS4Text::readFont(long endPos, int &id, std::string &mess)
1483
15.1k
{
1484
15.1k
  WPS4TextInternal::Font font(getDefaultFont());
1485
15.1k
  font.m_size = 12;
1486
1487
15.1k
  libwps::DebugStream f;
1488
1489
15.1k
  int fl[4] = { 0, 0, 0, 0};
1490
15.1k
  if (m_input->tell() < endPos) fl[0] = libwps::readU8(m_input);
1491
1492
  /* set difference from default properties */
1493
15.1k
  uint32_t attributes = 0;
1494
15.1k
  if (fl[0] & 0x01) attributes |= WPS_BOLD_BIT;
1495
15.1k
  if (fl[0] & 0x02) attributes |= WPS_ITALICS_BIT;
1496
15.1k
  if (fl[0] & 0x04) attributes |= WPS_STRIKEOUT_BIT;
1497
15.1k
  fl[0] &= 0xf8;
1498
1499
  // what & 0x01 -> ???
1500
  // what & 0x02 -> note
1501
  // what & 0x04 -> ???
1502
  // what & 0x08 -> fName
1503
  // what & 0x10 -> size
1504
  // what & 0x20 -> underline (fl[2])
1505
  // what & 0x40 -> decalage
1506
  // what & 0x80 -> color
1507
15.1k
  int what = 0;
1508
15.1k
  if (m_input->tell() < endPos) what = libwps::readU8(m_input);
1509
1510
15.1k
  font.m_special = ((what & 2) != 0);
1511
15.1k
  what &= 0xfd;
1512
1513
15.1k
  if (m_input->tell() < endPos)
1514
10.9k
  {
1515
    // the fonts
1516
    // FIXME: find some properties with size=3,
1517
    //        for which this character seems
1518
    //        related to size, not font
1519
10.9k
    uint8_t font_n = libwps::readU8(m_input);
1520
1521
10.9k
    if (m_state->m_fontNames.find(font_n) != m_state->m_fontNames.end())
1522
766
    {
1523
766
      auto const &fontName=m_state->m_fontNames.find(font_n)->second;
1524
766
      font.m_name=fontName.m_name;
1525
766
      font.m_type=fontName.m_type;
1526
766
    }
1527
10.1k
    else if (version() <= 2)
1528
9.42k
    {
1529
9.42k
      font.m_name=WPS4TextInternal::FontName::getDosName(font_n);
1530
9.42k
      font.m_type=mainParser().getDefaultFontType();
1531
9.42k
    }
1532
748
    else
1533
748
    {
1534
748
      WPS_DEBUG_MSG(("WPS4Text: error: encountered font %i which is not indexed\n",
1535
748
                     font_n));
1536
748
    }
1537
1538
10.9k
    if (font.m_name.empty()) f << "###nameId=" << int(font_n) << ",";
1539
10.9k
  }
1540
1541
15.1k
  if (m_input->tell() < endPos)
1542
10.1k
  {
1543
    // underline, ...
1544
10.1k
    int underlinePos = libwps::readU8(m_input);
1545
10.1k
    if (underlinePos)
1546
7.82k
    {
1547
7.82k
      if (!(what & 0x20)) f << "undFl,";
1548
1.74k
      else what &= 0xdf;
1549
7.82k
      attributes |= WPS_UNDERLINE_BIT;
1550
7.82k
    }
1551
10.1k
  }
1552
1553
15.1k
  if (m_input->tell() < endPos)   // font size * 2
1554
9.82k
  {
1555
9.82k
    int fSize = libwps::readU8(m_input);
1556
9.82k
    if (fSize)
1557
7.85k
    {
1558
7.85k
      if (!(what & 0x10)) f << "szFl,";
1559
2.99k
      else what &= 0xef;
1560
7.85k
      font.m_size = (fSize/2);
1561
7.85k
    }
1562
9.82k
  }
1563
1564
15.1k
  if (m_input->tell() < endPos)   // height decalage -> sub/superscript
1565
9.41k
  {
1566
9.41k
    int fDec = libwps::read8(m_input);
1567
9.41k
    if (fDec)
1568
7.08k
    {
1569
7.08k
      if (!(what & 0x40)) f << "sub/supFl(val=" << fDec<<"),";
1570
1.09k
      else what &= 0xbf;
1571
1572
7.08k
      if (fDec > 0) attributes |= WPS_SUPERSCRIPT_BIT;
1573
1.65k
      else attributes |= WPS_SUBSCRIPT_BIT;
1574
7.08k
    }
1575
9.41k
  }
1576
15.1k
  if (m_input->tell()+2 <= endPos)   // color field
1577
7.06k
  {
1578
7.06k
    int bkColor = libwps::readU8(m_input);
1579
7.06k
    int ftColor = libwps::readU8(m_input);
1580
7.06k
    bool setColor = !!(what & 0x80);
1581
7.06k
    what &= 0x7F;
1582
1583
7.06k
    if ((bkColor || ftColor) && !setColor)
1584
4.71k
    {
1585
4.71k
      setColor = true;
1586
4.71k
      f << "colorFl,";
1587
4.71k
    }
1588
7.06k
    if (setColor)
1589
5.78k
    {
1590
5.78k
      WPSColor color;
1591
5.78k
      if (mainParser().getColor(bkColor, color))
1592
2.89k
        font.m_backColor = color;
1593
5.78k
      if (mainParser().getColor(ftColor, color))
1594
3.06k
        font.m_color = color;
1595
5.78k
    }
1596
7.06k
  }
1597
15.1k
  if (m_input->tell() < endPos)
1598
7.48k
    font.m_dlinkId = libwps::readU8(m_input);
1599
15.1k
  if (what)  f << "#what=" << std::hex << what << std::dec << ",";
1600
15.1k
  if (fl[0])  f << "unkn0=" << std::hex << fl[0] << std::dec << ",";
1601
1602
15.1k
  if (m_input->tell() != endPos)
1603
6.32k
  {
1604
6.32k
    f << "#unknEnd=(";
1605
104k
    while (m_input->tell() < endPos) f << std::hex << libwps::readU8(m_input) <<",";
1606
6.32k
    f << ")";
1607
6.32k
  }
1608
1609
15.1k
  font.m_attributes = attributes;
1610
15.1k
  font.m_extra = f.str();
1611
1612
15.1k
  id = int(m_state->m_fontList.size());
1613
15.1k
  m_state->m_fontList.push_back(font);
1614
15.1k
  f.str("");
1615
15.1k
  f << font;
1616
15.1k
  mess = f.str();
1617
1618
15.1k
  return true;
1619
15.1k
}
1620
1621
////////////////////////////////////////////////////////////
1622
// the file list: only in dos3  ?
1623
////////////////////////////////////////////////////////////
1624
bool WPS4Text::readDosLink(WPSEntry const &entry)
1625
299
{
1626
299
  if (!entry.valid()) return false;
1627
1628
299
  long length = entry.length();
1629
299
  if (length%44)
1630
123
  {
1631
123
    WPS_DEBUG_MSG(("WPS4Text::readDosLink: length::=%ld seem odd\n", length));
1632
123
    return false;
1633
123
  }
1634
1635
176
  m_input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1636
176
  libwps::DebugStream f;
1637
176
  long numElt = length/44;
1638
176
  long val;
1639
7.93k
  for (long n = 0; n < numElt; ++n)
1640
7.75k
  {
1641
7.75k
    WPS4TextInternal::DosLink link;
1642
7.75k
    long pos = m_input->tell();
1643
7.75k
    long endPos = pos+44;
1644
7.75k
    f.str("");
1645
23.2k
    for (int i = 0; i < 2; ++i) // always 0, 0
1646
15.5k
    {
1647
15.5k
      val = libwps::readU16(m_input);
1648
15.5k
      if (val) f << "unkn" << i << "=" << std::hex << val << std::dec << ",";
1649
15.5k
    }
1650
7.75k
    link.m_width = float(libwps::readU16(m_input)/1440.);
1651
23.2k
    for (int i = 2; i < 4; ++i) // always f0, f0
1652
15.5k
    {
1653
15.5k
      val = libwps::readU16(m_input);
1654
15.5k
      if (val != 0xf0) f << "unkn" << i << "=" << std::hex << val << std::dec << ",";
1655
15.5k
    }
1656
7.75k
    link.m_type = libwps::readU8(m_input);
1657
7.75k
    val = libwps::readU8(m_input);
1658
7.75k
    if (val) // find 0x18 for a spreadsheet
1659
5.58k
      f << "unk4=" << std::hex << val << std::dec << ",";
1660
7.75k
    switch (link.m_type)
1661
7.75k
    {
1662
299
    case 0x81: // picture ?
1663
299
    {
1664
299
      long dim[2];
1665
598
      for (long &i : dim) i = libwps::readU16(m_input);
1666
299
      link.m_size = Vec2f(float(dim[0])/1440.f, float(dim[1])/1440.f);
1667
299
      val = libwps::readU16(m_input); // always 0
1668
299
      if (val) f << "g0=" << val << ",";
1669
299
      val = libwps::readU16(m_input); // always 4
1670
299
      if (val != 4) f << "g1=" << val << ",";
1671
299
    }
1672
299
    WPS_FALLTHROUGH;
1673
799
    case 0x40: // spreadsheet range
1674
2.19k
    case 0x01: // char ?
1675
2.19k
    {
1676
2.19k
      std::string name("");
1677
2.19k
      link.m_pos.setBegin(m_input->tell());
1678
52.0k
      while (!m_input->isEnd() && long(m_input->tell()) < endPos)
1679
50.5k
      {
1680
50.5k
        auto c = char(libwps::readU8(m_input));
1681
50.5k
        if (!c)
1682
685
        {
1683
685
          m_input->seek(-1, librevenge::RVNG_SEEK_CUR);
1684
685
          break;
1685
685
        }
1686
49.8k
        name += c;
1687
49.8k
      }
1688
2.19k
      link.m_pos.setEnd(m_input->tell());
1689
2.19k
      link.m_pos.setId(WPS4TextInternal::Z_DLink);
1690
2.19k
      link.m_name = name;
1691
2.19k
      break;
1692
799
    }
1693
5.56k
    default:
1694
5.56k
      break;
1695
7.75k
    }
1696
7.75k
    link.m_extra = f.str();
1697
7.75k
    m_state->m_dosLinkList.push_back(link);
1698
7.75k
    f.str("");
1699
7.75k
    f << "ZZDLINK-" << n << ":" << link;
1700
7.75k
    if (long(m_input->tell()) != endPos)
1701
6.24k
      ascii().addDelimiter(m_input->tell(),'|');
1702
7.75k
    ascii().addPos(pos);
1703
7.75k
    ascii().addNote(f.str().c_str());
1704
7.75k
    m_input->seek(endPos, librevenge::RVNG_SEEK_SET);
1705
7.75k
  }
1706
176
  return true;
1707
176
}
1708
1709
////////////////////////////////////////////////////////////
1710
// the paragraph properties:
1711
////////////////////////////////////////////////////////////
1712
bool WPS4Text::readParagraph(long endPos, int &id, std::string &mess)
1713
73.8k
{
1714
73.8k
  long actPos = m_input->tell();
1715
73.8k
  long size = endPos - actPos;
1716
1717
73.8k
  WPSParagraph pp;
1718
73.8k
  if (size && size < 3)
1719
4.58k
  {
1720
4.58k
    WPS_DEBUG_MSG(("WPS4Text:readParagraph:(sz=%ld)\n", size));
1721
4.58k
    return false;
1722
4.58k
  }
1723
1724
69.2k
  libwps::DebugStream f;
1725
277k
  for (int i = 0; i < 3; ++i)
1726
207k
  {
1727
207k
    int v = libwps::readU8(m_input);
1728
207k
    if (v != 0) f << "unkn"<<i<< "=" << v;
1729
207k
  }
1730
1731
217k
  while (m_input->tell() < endPos)
1732
193k
  {
1733
193k
    int v = libwps::readU8(m_input);
1734
193k
    long pos = m_input->tell();
1735
193k
    bool ok = true, done = true;
1736
193k
    int arg = -1;
1737
193k
    switch (v)
1738
193k
    {
1739
3.34k
    case 0x2:
1740
3.34k
    {
1741
3.34k
      if (pos+1 > endPos)
1742
220
      {
1743
220
        ok = false;
1744
220
        break;
1745
220
      }
1746
3.12k
      arg = libwps::readU8(m_input);
1747
3.12k
      f << "f2=" << arg << ",";
1748
3.12k
      break;
1749
3.34k
    }
1750
9.26k
    case 0x5:
1751
9.26k
    {
1752
9.26k
      if (pos+1 > endPos)
1753
670
      {
1754
670
        ok = false;
1755
670
        break;
1756
670
      }
1757
8.59k
      arg = libwps::readU8(m_input);
1758
8.59k
      switch (arg)
1759
8.59k
      {
1760
1.44k
      case 0:
1761
1.44k
        pp.m_justify = libwps::JustificationLeft;
1762
1.44k
        break;
1763
438
      case 1:
1764
438
        pp.m_justify = libwps::JustificationCenter;
1765
438
        break;
1766
280
      case 2:
1767
280
        pp.m_justify = libwps::JustificationRight;
1768
280
        break;
1769
417
      case 3:
1770
417
        pp.m_justify = libwps::JustificationFull;
1771
417
        break;
1772
6.02k
      default:
1773
6.02k
        f << "#just=" << arg << ",";
1774
6.02k
        pp.m_justify = libwps::JustificationLeft;
1775
8.59k
      }
1776
8.59k
      break;
1777
8.59k
    }
1778
8.59k
    case 0x7:   // 1: marked don't break paragraph
1779
14.3k
    case 0x8:   // 1: marked keep paragraph with next
1780
14.3k
    {
1781
14.3k
      if (pos+1 > endPos)
1782
917
      {
1783
917
        ok = false;
1784
917
        break;
1785
917
      }
1786
13.4k
      arg = libwps::readU8(m_input);
1787
13.4k
      if (arg == 0) break;
1788
12.6k
      if (arg == 1) pp.m_breakStatus |= ((v == 7) ? libwps::NoBreakBit : libwps::NoBreakWithNextBit);
1789
11.7k
      else f << "#status=" << arg << ",";
1790
12.6k
      break;
1791
13.4k
    }
1792
1793
    // BORDER
1794
14.1k
    case 0x9:
1795
14.1k
    {
1796
14.1k
      if (pos+1 > endPos)
1797
446
      {
1798
446
        ok = false;
1799
446
        break;
1800
446
      }
1801
13.7k
      arg = libwps::readU8(m_input);
1802
13.7k
      pp.m_borderStyle.m_style = WPSBorder::Simple;
1803
13.7k
      pp.m_borderStyle.m_type = WPSBorder::Single;
1804
13.7k
      pp.m_borderStyle.m_width = 1;
1805
13.7k
      int style = (arg&0xf);
1806
13.7k
      switch (style)
1807
13.7k
      {
1808
681
      case 0:
1809
681
        break;
1810
1.07k
      case 1:
1811
1.07k
        pp.m_borderStyle.m_width = 2;
1812
1.07k
        break;
1813
1.95k
      case 2:
1814
1.95k
        pp.m_borderStyle.m_type = WPSBorder::Double;
1815
1.95k
        break;
1816
790
      case 3:
1817
790
        pp.m_borderStyle.m_style = WPSBorder::Dot;
1818
790
        break;
1819
585
      case 4:
1820
585
        pp.m_borderStyle.m_style = WPSBorder::LargeDot;
1821
585
        break;
1822
577
      case 5:
1823
577
        pp.m_borderStyle.m_style = WPSBorder::Dash;
1824
577
        break;
1825
920
      case 6:
1826
1.58k
      case 7:
1827
2.30k
      case 8:
1828
2.30k
        pp.m_borderStyle.m_width = style-3;
1829
2.30k
        break;
1830
4.54k
      case 9:
1831
4.92k
      case 10:
1832
4.92k
        pp.m_borderStyle.m_width = style-7;
1833
4.92k
        pp.m_borderStyle.m_type = WPSBorder::Double;
1834
4.92k
        break;
1835
832
      default:
1836
832
        f << "#borderStyle=" << style << ",";
1837
832
        WPS_DEBUG_MSG(("WPS4Text:readParagraph: unknown border style\n"));
1838
832
        break;
1839
13.7k
      }
1840
13.7k
      int high = (arg>>4);
1841
13.7k
      if (version() < 3)
1842
4.39k
      {
1843
4.39k
        WPSColor color;
1844
4.39k
        if (high && mainParser().getColor(high, color))
1845
2.43k
          pp.m_borderStyle.m_color = color;
1846
1.95k
        else if (high)
1847
72
          f << "#borderColor=" << high << ",";
1848
4.39k
      }
1849
9.32k
      else
1850
9.32k
      {
1851
9.32k
        switch (high)
1852
9.32k
        {
1853
4.65k
        case 0:
1854
4.65k
          break;
1855
729
        case 4:
1856
729
          pp.m_border = 0xf;
1857
729
          break;
1858
201
        case 8:
1859
201
          pp.m_border = 0xf;
1860
201
          f << "borderShaded,";
1861
201
          break;
1862
3.74k
        default:
1863
3.74k
          f << "#borderStyle[high]=" << high << ",";
1864
3.74k
          break;
1865
9.32k
        }
1866
9.32k
      }
1867
13.7k
      break;
1868
13.7k
    }
1869
13.7k
    case 0xa: // 1: top border
1870
2.44k
    case 0xb: //  : bottom border
1871
31.0k
    case 0xc: //  : left border
1872
37.9k
    case 0xd: //  : right border
1873
37.9k
    {
1874
37.9k
      if (pos+1 > endPos)
1875
1.22k
      {
1876
1.22k
        ok = false;
1877
1.22k
        break;
1878
1.22k
      }
1879
36.6k
      arg = libwps::readU8(m_input);
1880
36.6k
      if (arg == 0) break;
1881
34.2k
      if (arg == 1)
1882
9.38k
      {
1883
9.38k
        switch (v)
1884
9.38k
        {
1885
492
        case 0xa:
1886
492
          pp.m_border |= WPSBorder::TopBit;
1887
492
          break;
1888
262
        case 0xb:
1889
262
          pp.m_border |= WPSBorder::BottomBit;
1890
262
          break;
1891
3.23k
        case 0xc:
1892
3.23k
          pp.m_border |= WPSBorder::LeftBit;
1893
3.23k
          break;
1894
5.40k
        case 0xd:
1895
5.40k
          pp.m_border |= WPSBorder::RightBit;
1896
5.40k
          break;
1897
0
        default:
1898
0
          break;
1899
9.38k
        }
1900
9.38k
      }
1901
24.8k
      else f << "#border=" << arg << ",";
1902
34.2k
      break;
1903
34.2k
    }
1904
34.2k
    case 0x18:   // border color
1905
5.45k
    {
1906
5.45k
      if (long(pos)==endPos)
1907
453
      {
1908
453
        ok = false;
1909
453
        break;
1910
453
      }
1911
5.00k
      int colorId = libwps::readU8(m_input);
1912
5.00k
      WPSColor color;
1913
5.00k
      if (mainParser().getColor(colorId, color))
1914
1.13k
        pp.m_borderStyle.m_color = color;
1915
3.87k
      else
1916
3.87k
        f << "#colorId=" << colorId << ",";
1917
5.00k
      break;
1918
5.45k
    }
1919
17.1k
    case 0xe:   // 1: bullet
1920
17.1k
    {
1921
17.1k
      if (pos+1 > endPos)
1922
804
      {
1923
804
        ok = false;
1924
804
        break;
1925
804
      }
1926
16.3k
      arg = libwps::readU8(m_input);
1927
16.3k
      if (arg == 0) break;
1928
1929
13.3k
      pp.m_listLevelIndex = 1;
1930
13.3k
      pp.m_listLevel.m_type = libwps::BULLET;
1931
13.3k
      static const uint32_t bulletList[]=
1932
13.3k
      {
1933
13.3k
        0x2022, 0x3e, 0x25c6, 0x21d2, 0x25c7, 0x2605, /* 1-6 */
1934
13.3k
        0, 0, 0, 0, 0, 0, /* 7-12 unknown */
1935
13.3k
        0, 0, 0, 0, 0, 0x2750, /* 13-17 unknown and document... */
1936
13.3k
        0x2713, 0x261e, 0x2704, 0x2611, 0x2612, 0x270e /* 18-24 */
1937
13.3k
      };
1938
13.3k
      if (arg <= 24 && bulletList[arg-1])
1939
4.63k
        libwps::appendUnicode(bulletList[arg-1], pp.m_listLevel.m_bullet);
1940
8.73k
      else
1941
8.73k
        libwps::appendUnicode(0x2022, pp.m_listLevel.m_bullet);
1942
13.3k
      break;
1943
16.3k
    }
1944
1.55k
    case 0x1b:
1945
20.9k
    case 0x1a:
1946
23.3k
    case 0x10:   // the bullet char : 0x18
1947
23.3k
    {
1948
23.3k
      if (pos+1 > endPos)
1949
1.04k
      {
1950
1.04k
        ok = false;
1951
1.04k
        break;
1952
1.04k
      }
1953
22.3k
      arg = libwps::readU8(m_input);
1954
22.3k
      done = true;
1955
22.3k
      switch (v)
1956
22.3k
      {
1957
18.7k
      case 0x1a:
1958
18.7k
        if (arg) f << "backPattern=" << arg << ",";
1959
18.7k
        break;
1960
1.40k
      case 0x1b:
1961
1.40k
      {
1962
1.40k
        if (arg==0) break;
1963
710
        WPSColor color;
1964
710
        if (mainParser().getColor(arg>>4, color))
1965
706
          f << "backPatternBackColor=" << color << ";";
1966
4
        else
1967
4
          f << "#backPatternBackColor=" << (arg>>4) << ",";
1968
710
        if (mainParser().getColor(arg&0xf, color))
1969
610
          f << "backPatternFrontColor=" << color << ";";
1970
100
        else
1971
100
          f << "#backPatternFrontColor=" << (arg&0xf) << ",";
1972
710
        break;
1973
1.40k
      }
1974
2.24k
      case 0x10:
1975
2.24k
        if (arg!=0x18) f << "bullet?=" << arg << ",";
1976
2.24k
        break;
1977
0
      default:
1978
0
        done = false;
1979
0
        break;
1980
22.3k
      }
1981
22.3k
      break;
1982
22.3k
    }
1983
22.3k
    case 0xf:   // tabs:
1984
4.94k
    {
1985
4.94k
      if (pos+1 > endPos)
1986
112
      {
1987
112
        ok = false;
1988
112
        break;
1989
112
      }
1990
4.83k
      int nVal = libwps::read8(m_input);
1991
4.83k
      if (nVal < 2 || pos + 1 + nVal > endPos)
1992
1.74k
      {
1993
1.74k
        ok = false;
1994
1.74k
        break;
1995
1.74k
      }
1996
3.08k
      int flag = libwps::readU8(m_input);
1997
3.08k
      if (flag) f << "#tabsFl=" << flag << ",";
1998
3.08k
      size_t nItem = libwps::readU8(m_input);
1999
3.08k
      if (size_t(nVal) != 2 + 3*nItem)
2000
1.21k
      {
2001
1.21k
        ok = false;
2002
1.21k
        break;
2003
1.21k
      }
2004
1.87k
      pp.m_tabs.resize(nItem);
2005
7.28k
      for (size_t i = 0; i < nItem; ++i)
2006
5.40k
        pp.m_tabs[i].m_position = libwps::read16(m_input)/1440.;
2007
7.28k
      for (size_t i = 0; i < nItem; ++i)
2008
5.40k
      {
2009
5.40k
        enum WPSTabStop::Alignment align = WPSTabStop::LEFT;
2010
5.40k
        int val = libwps::readU8(m_input);
2011
5.40k
        switch ((val & 0x3))
2012
5.40k
        {
2013
1.90k
        case 0:
2014
1.90k
          align = WPSTabStop::LEFT;
2015
1.90k
          break;
2016
2.06k
        case 1:
2017
2.06k
          align = WPSTabStop::CENTER;
2018
2.06k
          break;
2019
500
        case 2:
2020
500
          align = WPSTabStop::RIGHT;
2021
500
          break;
2022
947
        case 3:
2023
947
          align = WPSTabStop::DECIMAL;
2024
947
          break;
2025
0
        default:
2026
0
          break;
2027
5.40k
        }
2028
5.40k
        pp.m_tabs[i].m_alignment = align;
2029
2030
5.40k
        if (val&4) f << "#Tabbits3";
2031
5.40k
        val = (val>>3);
2032
2033
5.40k
        switch (val)
2034
5.40k
        {
2035
1.28k
        case 0:
2036
1.28k
          break;
2037
828
        case 1:
2038
828
          pp.m_tabs[i].m_leaderCharacter = '.';
2039
828
          break;
2040
1.84k
        case 2:
2041
1.84k
          pp.m_tabs[i].m_leaderCharacter = '-';
2042
1.84k
          break;
2043
87
        case 3:
2044
87
          pp.m_tabs[i].m_leaderCharacter = '_';
2045
87
          break;
2046
579
        case 4:
2047
579
          pp.m_tabs[i].m_leaderCharacter = '=';
2048
579
          break;
2049
780
        default:
2050
780
          f << "#TabSep=" << val;
2051
5.40k
        }
2052
5.40k
      }
2053
2054
1.87k
      break;
2055
1.87k
    }
2056
2.50k
    case 0x11: // right margin : 1440*inches
2057
10.1k
    case 0x12: // left margin
2058
11.6k
    case 0x13: // another margin ( check me )
2059
15.0k
    case 0x14: // left text indent (relative to left margin)
2060
17.5k
    case 0x15: // line spacing (inter line) 240
2061
23.9k
    case 0x16: // line spacing before 240 = 1 line spacing
2062
27.8k
    case 0x17:   // line spacing after
2063
27.8k
    {
2064
27.8k
      if (pos+2 > endPos)
2065
1.09k
      {
2066
1.09k
        ok = false;
2067
1.09k
        break;
2068
1.09k
      }
2069
2070
26.7k
      arg = libwps::read16(m_input);
2071
26.7k
      switch (v)
2072
26.7k
      {
2073
2.41k
      case 0x11:
2074
2.41k
        pp.m_margins[2] = arg/1440.;
2075
2.41k
        break;
2076
1.40k
      case 0x13: // seems another way to define the left margin
2077
1.40k
        f << "#left,";
2078
1.40k
        WPS_FALLTHROUGH;
2079
9.05k
      case 0x12:
2080
9.05k
        pp.m_margins[1] = arg/1440.;
2081
9.05k
        break;
2082
3.26k
      case 0x14:
2083
3.26k
        pp.m_margins[0] = arg/1440.;
2084
3.26k
        break;
2085
2.42k
      case 0x15:
2086
2.42k
      {
2087
2.42k
        double lines = arg ? arg/240. : 1.0;
2088
2.42k
        if (lines < 1.0 || lines > 2.0)
2089
1.18k
        {
2090
1.18k
          f << "##interLineSpacing=" << lines << ",";
2091
1.18k
          lines = (lines < 1.0) ? 1.0 : 2.0;
2092
1.18k
        }
2093
2.42k
        pp.setInterline(lines, librevenge::RVNG_PERCENT);
2094
2.42k
        break;
2095
1.40k
      }
2096
5.81k
      case 0x16:
2097
5.81k
        pp.m_spacings[1] = arg/240.;
2098
5.81k
        break;
2099
3.78k
      case 0x17:
2100
3.78k
        pp.m_spacings[2] = arg/240.;
2101
3.78k
        break;
2102
0
      default:
2103
0
        done = false;
2104
26.7k
      }
2105
26.7k
      break;
2106
26.7k
    }
2107
36.0k
    default:
2108
36.0k
      ok = false;
2109
193k
    }
2110
193k
    if (!ok)
2111
46.0k
    {
2112
46.0k
      m_input->seek(pos, librevenge::RVNG_SEEK_SET);
2113
46.0k
      f << "###v" << v<<"=" <<std::hex;
2114
494k
      while (m_input->tell() < endPos)
2115
448k
        f << int(libwps::readU8(m_input)) << ",";
2116
46.0k
      break;
2117
46.0k
    }
2118
2119
147k
    if (done) continue;
2120
2121
0
    f << "f" << v << "=" << std::hex << arg << std::dec << ",";
2122
0
  }
2123
69.2k
  if (pp.m_listLevelIndex >= 1)
2124
6.12k
    pp.m_listLevel.m_labelIndent = pp.m_margins[1];
2125
63.1k
  else if (pp.m_margins[0] + pp.m_margins[1] < 0.0)
2126
3.82k
  {
2127
    // sanity check
2128
3.82k
    if (pp.m_margins[1] < 0.0) pp.m_margins[1] = 0.0;
2129
3.82k
    pp.m_margins[0] = -pp.m_margins[1];
2130
3.82k
  }
2131
69.2k
  pp.m_extra = f.str();
2132
2133
69.2k
  id = int(m_state->m_paragraphList.size());
2134
69.2k
  m_state->m_paragraphList.push_back(pp);
2135
2136
69.2k
  f.str("");
2137
69.2k
  f << pp;
2138
69.2k
  mess = f.str();
2139
69.2k
  return true;
2140
69.2k
}
2141
2142
////////////////////////////////////////////////////////////
2143
// the foot note properties:
2144
////////////////////////////////////////////////////////////
2145
bool WPS4Text::readFootNotes(WPSEntry const &ftnD, WPSEntry const &ftnP)
2146
3.09k
{
2147
3.09k
  if (!ftnD.valid() && !ftnP.valid()) return true;
2148
803
  if (!ftnD.valid() || !ftnP.valid())
2149
395
  {
2150
395
    WPS_DEBUG_MSG(("WPS4Text::readFootNotes: one of the two entry is not valid, footnote will be ignored\n"));
2151
395
    return false;
2152
395
  }
2153
2154
408
  std::vector<long> footNotePos,footNoteDef, listValues;
2155
408
  if (!readPLC(ftnP, footNotePos, listValues, &WPS4Text::footNotesDataParser))
2156
172
  {
2157
172
    WPS_DEBUG_MSG(("WPS4Text::readFootNotes: can not read positions\n"));
2158
172
    return false;
2159
172
  }
2160
2161
236
  if (!readPLC(ftnD, footNoteDef, listValues))
2162
37
  {
2163
37
    WPS_DEBUG_MSG(("WPS4Text::readFootNotes: can not read definitions\n"));
2164
37
    return false;
2165
37
  }
2166
2167
199
  int numFootNotes = int(footNotePos.size())-1;
2168
199
  if (numFootNotes <= 0 || int(footNoteDef.size())-1 != numFootNotes)
2169
10
  {
2170
10
    WPS_DEBUG_MSG(("WPS4Text::readFootNotes: no footnotes\n"));
2171
10
    return false;
2172
10
  }
2173
2174
  // save the actual type and create a list of footnote entries
2175
189
  std::vector<WPS4TextInternal::Note> noteTypes=m_state->m_footnoteList;
2176
189
  m_state->m_footnoteList.resize(0);
2177
2178
189
  std::vector<int> corresp;
2179
745
  for (size_t i = 0; i < size_t(numFootNotes); ++i)
2180
556
  {
2181
556
    WPS4TextInternal::Note fZone;
2182
556
    fZone.setBegin(footNoteDef[i]);
2183
556
    fZone.setEnd(footNoteDef[i+1]);
2184
556
    fZone.setType("TEXT");
2185
556
    fZone.setId(WPS4TextInternal::Z_Note);
2186
556
    m_state->m_footnoteList.push_back(fZone);
2187
556
    corresp.push_back(int(i));
2188
2189
    // sort the footnote
2190
556
    for (size_t j = i; j > 0; j--)
2191
367
    {
2192
367
      if (m_state->m_footnoteList[j].begin() >=
2193
367
              m_state->m_footnoteList[j-1].end()) break;
2194
2195
0
      if (m_state->m_footnoteList[j].end() >
2196
0
              m_state->m_footnoteList[j-1].begin())
2197
0
      {
2198
0
        WPS_DEBUG_MSG
2199
0
        (("WPS4Text: error: can not create footnotes zone, found %lx and %lx\n",
2200
0
          static_cast<unsigned long>(m_state->m_footnoteList[j].end()),
2201
0
          static_cast<unsigned long>(m_state->m_footnoteList[j-1].begin())));
2202
2203
0
        m_state->m_footnoteList.resize(0);
2204
0
        return false;
2205
0
      }
2206
2207
0
      std::swap(m_state->m_footnoteList[j], m_state->m_footnoteList[j-1]);
2208
2209
0
      int pos = corresp[j];
2210
0
      corresp[j] = corresp[j-1];
2211
0
      corresp[j-1] = pos;
2212
0
    }
2213
556
  }
2214
  // ok, we can create the map, ...
2215
745
  for (size_t i = 0; i < size_t(numFootNotes); ++i)
2216
556
  {
2217
556
    auto id = size_t(corresp[i]);
2218
556
    WPS4TextInternal::Note &z = m_state->m_footnoteList[id];
2219
556
    if (id < noteTypes.size())
2220
240
    {
2221
240
      z.m_label = noteTypes[id].m_label;
2222
240
      z.m_error = noteTypes[id].m_error;
2223
240
    }
2224
556
    m_state->m_footnoteMap[footNotePos[id]] = &z;
2225
556
  }
2226
189
  return true;
2227
189
}
2228
2229
bool WPS4Text::footNotesDataParser(long /*bot*/, long /*eot*/, int id,
2230
                                   long endPos, std::string &mess)
2231
1.32k
{
2232
1.32k
  mess = "";
2233
2234
1.32k
  long actPos = m_input->tell();
2235
1.32k
  long length = endPos+1-actPos;
2236
1.32k
  if (length != 12)
2237
107
  {
2238
107
    WPS_DEBUG_MSG(("WPS4Text::footNotesDataParser: unknown size %ld for footdata data\n", length));
2239
107
    return false;
2240
107
  }
2241
1.21k
  libwps::DebugStream f;
2242
1.21k
  WPS4TextInternal::Note note;
2243
1.21k
  auto type = libwps::readU16(m_input);
2244
1.21k
  if (type & 1)
2245
493
  {
2246
493
    if (type != 1)
2247
427
      f << "###numeric=" << std::hex << type << std::dec << ",";
2248
493
  }
2249
725
  else if (type == 0 || type > 20)
2250
668
    f << "###char,";
2251
57
  else
2252
57
    note.m_label=libwps_tools_win::Font::unicodeString(m_input.get(), type/2, mainParser().getDefaultFontType());
2253
1.21k
  note.m_error=f.str();
2254
1.21k
  if (id >= int(m_state->m_footnoteList.size()))
2255
1.21k
    m_state->m_footnoteList.resize(size_t(id+1));
2256
1.21k
  m_state->m_footnoteList[size_t(id)]=note;
2257
1.21k
  f.str("");
2258
1.21k
  f << note;
2259
1.21k
  mess = f.str();
2260
1.21k
  m_input->seek(endPos+1, librevenge::RVNG_SEEK_SET);
2261
1.21k
  return true;
2262
1.32k
}
2263
2264
////////////////////////////////////////////////////////////
2265
// the bookmark properties:
2266
////////////////////////////////////////////////////////////
2267
bool WPS4Text::bkmkDataParser(long bot, long /*eot*/, int /*id*/,
2268
                              long endPos, std::string &mess)
2269
155
{
2270
155
  mess = "";
2271
155
  if (m_state->m_bookmarkMap.find(bot) != m_state->m_bookmarkMap.end())
2272
18
  {
2273
18
    WPS_DEBUG_MSG(("WPS4Text:bkmkDataParser: bookmark already exists in this position\n"));
2274
18
    return true;
2275
18
  }
2276
2277
137
  long actPos = m_input->tell();
2278
137
  long length = endPos+1-actPos;
2279
137
  if (length != 16)
2280
14
  {
2281
14
    WPS_DEBUG_MSG(("WPS4Text::bkmkDataParser: unknown size %ld for bkmkdata data\n", length));
2282
14
    return false;
2283
14
  }
2284
2285
977
  for (int i = 0; i < 16; ++i)
2286
948
  {
2287
948
    auto c = char(libwps::readU8(m_input));
2288
948
    if (c == '\0') break;
2289
854
    mess += c;
2290
854
  }
2291
123
  WPSEntry ent;
2292
123
  ent.setBegin(actPos);
2293
123
  ent.setEnd(m_input->tell());
2294
123
  ent.setId(WPS4TextInternal::Z_String);
2295
123
  m_state->m_bookmarkMap[bot] = ent;
2296
123
  m_input->seek(endPos+1, librevenge::RVNG_SEEK_SET);
2297
123
  return true;
2298
137
}
2299
2300
////////////////////////////////////////////////////////////
2301
// the object properties:
2302
////////////////////////////////////////////////////////////
2303
bool WPS4Text::objectDataParser(long bot, long /*eot*/, int id,
2304
                                long endPos, std::string &mess)
2305
2.01k
{
2306
2.01k
  mess = "";
2307
2.01k
  if (m_state->m_objectMap.find(bot) != m_state->m_objectMap.end())
2308
150
  {
2309
150
    WPS_DEBUG_MSG(("WPS4Text:objectDataParser: object already exists in this position\n"));
2310
150
    return true;
2311
150
  }
2312
2313
1.86k
  libwps::DebugStream f;
2314
2315
1.86k
  long actPos = m_input->tell();
2316
1.86k
  long length = endPos+1-actPos;
2317
1.86k
  if (length != 36)
2318
15
  {
2319
15
    WPS_DEBUG_MSG(("WPS4Text:objectDataParser unknown size %ld for object data\n", length));
2320
15
    return false;
2321
15
  }
2322
  // 3->08 4->4f4d or 68->list?
2323
1.85k
  f << "type?=" << int(libwps::read16(m_input)) << ",";
2324
5.55k
  for (int i = 0; i < 2; ++i)
2325
3.70k
  {
2326
3.70k
    int v =libwps::read16(m_input);
2327
3.70k
    if (v) f << "unkn1:" << i << "=" << v << ",";
2328
3.70k
  }
2329
1.85k
  float dim[4];
2330
1.85k
  for (float &i : dim)
2331
7.41k
    i =float(libwps::read16(m_input)/1440.);
2332
2333
  // CHECKME: the next two sizes are often simillar,
2334
  //         maybe the first one is the original size and the second
2335
  //         size in the document...
2336
1.85k
  f << "origSz?=[" << dim[0] << "," << dim[1] << "],";
2337
2338
1.85k
  WPS4TextInternal::Object obj;
2339
1.85k
  obj.m_size = Vec2f(dim[2], dim[3]); // CHECKME: unit
2340
2341
1.85k
  auto size = long(libwps::readU32(m_input));
2342
1.85k
  auto pos = long(libwps::readU32(m_input));
2343
2344
1.85k
  actPos = m_input->tell();
2345
1.85k
  if (pos >= 0 && size > 0 && (long)((unsigned long) pos+(unsigned long) size)>pos && mainParser().checkFilePosition(pos+size))
2346
826
  {
2347
826
    obj.m_pos.setBegin(pos);
2348
826
    obj.m_pos.setLength(size);
2349
826
    obj.m_pos.setId(id);
2350
2351
826
    int objectId = mainParser().readObject(m_input, obj.m_pos);
2352
826
    if (objectId == -1)
2353
301
    {
2354
301
      WPS_DEBUG_MSG(("WPS4Text::objectDataParser: can not find the object %d\n", id));
2355
301
    }
2356
826
    obj.m_id = objectId;
2357
826
  }
2358
1.02k
  else
2359
1.02k
  {
2360
1.02k
    WPS_DEBUG_MSG(("WPS4Text::objectDataParser: bad object position\n"));
2361
1.02k
  }
2362
2363
1.85k
  m_input->seek(actPos, librevenge::RVNG_SEEK_SET);
2364
1.85k
  auto val =int(libwps::read16(m_input)); // small number, probably an id
2365
1.85k
  if (val) f << "id=" << val << ",";
2366
5.55k
  for (int i = 0; i < 2; ++i)
2367
3.70k
  {
2368
3.70k
    val =int(libwps::read16(m_input));
2369
3.70k
    if (val) f << "f" << i << "=" << val << ",";
2370
3.70k
  }
2371
1.85k
  obj.m_page =int(libwps::read16(m_input));
2372
1.85k
  int origin[2];
2373
3.70k
  for (int &i : origin) i =int(libwps::read16(m_input));
2374
1.85k
  obj.m_origin=Vec2f(float(origin[0]/1440.),float(origin[1]/1440.));
2375
1.85k
  val =int(libwps::read16(m_input)); // can be big
2376
1.85k
  if (val) f << "f2=" << val << ",";
2377
1.85k
  obj.m_extra = f.str();
2378
1.85k
  if (obj.m_id>=0)
2379
525
    m_state->m_objectMap[bot] = obj;
2380
1.85k
  f.str("");
2381
1.85k
  f << obj;
2382
2383
1.85k
  mess = f.str();
2384
1.85k
  return true;
2385
1.86k
}
2386
2387
////////////////////////////////////////////////////////////
2388
// the dttm properties:
2389
////////////////////////////////////////////////////////////
2390
bool WPS4Text::dttmDataParser(long bot, long /*eot*/, int /*id*/,
2391
                              long endPos, std::string &mess)
2392
680
{
2393
680
  mess = "";
2394
680
  if (m_state->m_dateTimeMap.find(bot) != m_state->m_dateTimeMap.end())
2395
328
  {
2396
328
    WPS_DEBUG_MSG(("WPS4Text:dttmDataParser: dttm already exists in this position\n"));
2397
328
    return true;
2398
328
  }
2399
2400
352
  libwps::DebugStream f;
2401
2402
352
  long actPos = m_input->tell();
2403
352
  long length = endPos+1-actPos;
2404
352
  if (length != 42)
2405
7
  {
2406
7
    WPS_DEBUG_MSG(("WPS4Text:dttmDataParser unknown size %ld for dttm data\n", length));
2407
7
    return false;
2408
7
  }
2409
2410
345
  WPS4TextInternal::DateTime form;
2411
345
  int val;
2412
1.38k
  for (int i = 0; i < 3; ++i) // always 0, 0, 0 ?
2413
1.03k
  {
2414
1.03k
    val =libwps::read16(m_input);
2415
1.03k
    if (val) f << "f" << i << "=" << val << ",";
2416
1.03k
  }
2417
345
  form.m_type=libwps::read16(m_input);
2418
345
  val =libwps::read16(m_input); // alway 0 ?
2419
345
  if (val) f << "f3=" << val << ",";
2420
  // end unknown
2421
5.86k
  for (int i = 0; i < 16; ++i)
2422
5.52k
  {
2423
5.52k
    val =libwps::readU16(m_input);
2424
5.52k
    if (val) f << "g" << i << "=" << std::hex << val << std::dec << ",";
2425
5.52k
  }
2426
345
  form.m_extra = f.str();
2427
345
  m_state->m_dateTimeMap[bot] = form;
2428
345
  f.str("");
2429
345
  f << form;
2430
345
  mess = f.str();
2431
345
  return true;
2432
352
}
2433
2434
////////////////////////////////////////
2435
// VERY LOW LEVEL ( plc )
2436
////////////////////////////////////////
2437
/** Internal and low level: the structures of a WPS4Text used to parse PLC*/
2438
namespace WPS4PLCInternal
2439
{
2440
/** Internal and low level: the PLC different types and their structures */
2441
struct PLC
2442
{
2443
  /** the PLC types */
2444
  typedef enum WPS4TextInternal::PLCType PLCType;
2445
  /** the way to define the text positions
2446
   *
2447
   * - P_ABS: absolute position,
2448
   * - P_REL: position are relative to the beginning text offset */
2449
  typedef enum { P_ABS=0, P_REL, P_UNKNOWN} Position;
2450
  /** the type of the content
2451
   *
2452
   * - T_CST: size is constant
2453
   * - T_STRUCT: a structured type ( which unknown size) */
2454
  typedef enum { T_CST=0, T_COMPLEX, T_UNKNOWN} Type;
2455
2456
  //! constructor
2457
  explicit PLC(PLCType w= WPS4TextInternal::Unknown, Position p=P_UNKNOWN, Type t=T_UNKNOWN, unsigned char tChar='\0', int f=1) :
2458
72.8k
    m_type(w), m_pos(p), m_contentType(t), m_textChar(tChar), m_cstFactor(f) {}
2459
  PLC(PLC const &orig)=default;
2460
  PLC(PLC &&orig)=default;
2461
  PLC &operator=(PLC const &orig)=default;
2462
  PLC &operator=(PLC &&orig)=default;
2463
  //! PLC type
2464
  PLCType m_type;
2465
  //! the way to define the text positions
2466
  Position m_pos;
2467
  //! the type of the content
2468
  Type m_contentType;
2469
  /** the character which appears in the text when this PLC is found
2470
   *
2471
   * '\\0' means that there is not default character */
2472
  unsigned char m_textChar;
2473
  //! some data are stored divided by some unit
2474
  int m_cstFactor;
2475
};
2476
2477
KnownPLC::KnownPLC()
2478
5.20k
  : m_knowns()
2479
5.20k
{
2480
5.20k
  createMapping();
2481
5.20k
}
2482
2483
KnownPLC::~KnownPLC()
2484
5.20k
{
2485
5.20k
}
2486
2487
PLC KnownPLC::get(std::string const &name)
2488
4.67k
{
2489
4.67k
  auto pos = m_knowns.find(name);
2490
4.67k
  if (pos == m_knowns.end()) return PLC();
2491
4.67k
  return pos->second;
2492
4.67k
}
2493
2494
void KnownPLC::createMapping()
2495
5.20k
{
2496
5.20k
  m_knowns["BTEP"] =
2497
5.20k
      PLC(WPS4TextInternal::BTE, PLC::P_ABS, PLC::T_CST, '\0', 0x80);
2498
5.20k
  m_knowns["BTEC"] =
2499
5.20k
      PLC(WPS4TextInternal::BTE,PLC::P_ABS, PLC::T_CST, '\0', 0x80);
2500
5.20k
  m_knowns["EOBJ"] =
2501
5.20k
      PLC(WPS4TextInternal::OBJECT,PLC::P_UNKNOWN, PLC::T_COMPLEX, 0x7);
2502
5.20k
  m_knowns["FTNp"] =
2503
5.20k
      PLC(WPS4TextInternal::FTNp,PLC::P_REL, PLC::T_CST, 0x6);
2504
5.20k
  m_knowns["FTNd"] =
2505
5.20k
      PLC(WPS4TextInternal::FTNd,PLC::P_REL, PLC::T_COMPLEX, 0x6);
2506
5.20k
  m_knowns["BKMK"] =
2507
5.20k
      PLC(WPS4TextInternal::BKMK,PLC::P_REL, PLC::T_COMPLEX);
2508
5.20k
  m_knowns["DTTM"] =
2509
5.20k
      PLC(WPS4TextInternal::DTTM,PLC::P_REL, PLC::T_COMPLEX, 0xf);
2510
5.20k
}
2511
}
2512
2513
bool WPS4Text::readPLC
2514
(WPSEntry const &zone,
2515
 std::vector<long> &textPtrs, std::vector<long> &listValues, WPS4Text::DataParser parser)
2516
5.01k
{
2517
5.01k
  textPtrs.resize(0);
2518
5.01k
  listValues.resize(0);
2519
5.01k
  long size = zone.length();
2520
5.01k
  if (zone.begin() <= 0 || size < 8) return false;
2521
4.67k
  auto plcType = m_state->m_knownPLC.get(zone.type());
2522
2523
4.67k
  libwps::DebugStream f;
2524
4.67k
  ascii().addPos(zone.begin());
2525
4.67k
  m_input->seek(zone.begin(), librevenge::RVNG_SEEK_SET);
2526
2527
4.67k
  long lastPos = 0;
2528
4.67k
  std::vector<DataFOD> fods;
2529
4.67k
  unsigned numElt = 0;
2530
4.67k
  f << "pos=(";
2531
183k
  while (numElt*4+4 <= unsigned(size))
2532
183k
  {
2533
183k
    auto newPos = long(libwps::readU32(m_input));
2534
183k
    if (plcType.m_pos == WPS4PLCInternal::PLC::P_UNKNOWN)
2535
743
    {
2536
743
      if (newPos < m_textPositions.begin())
2537
430
        plcType.m_pos = WPS4PLCInternal::PLC::P_REL;
2538
313
      else if (newPos+m_textPositions.begin() > m_textPositions.end())
2539
249
        plcType.m_pos = WPS4PLCInternal::PLC::P_ABS;
2540
64
      else if (plcType.m_textChar=='\0')
2541
0
      {
2542
0
        WPS_DEBUG_MSG(("WPS4Text:readPLC Can not decide position for PLC: %s\n", zone.type().c_str()));
2543
0
        plcType.m_pos = WPS4PLCInternal::PLC::P_REL;
2544
0
      }
2545
64
      else
2546
64
      {
2547
64
        long actPos = m_input->tell();
2548
64
        m_input->seek(newPos, librevenge::RVNG_SEEK_SET);
2549
64
        if (libwps::readU8(m_input) == plcType.m_textChar)
2550
3
          plcType.m_pos = WPS4PLCInternal::PLC::P_ABS;
2551
61
        else plcType.m_pos = WPS4PLCInternal::PLC::P_REL;
2552
64
        m_input->seek(actPos, librevenge::RVNG_SEEK_SET);
2553
64
      }
2554
743
    }
2555
2556
183k
    if (plcType.m_pos == WPS4PLCInternal::PLC::P_REL)
2557
67.1k
      newPos += m_textPositions.begin();
2558
2559
183k
    if (newPos < lastPos ||
2560
183k
            newPos > m_textPositions.end())
2561
2.58k
    {
2562
      // sometimes the convertissor do not their jobs correctly
2563
      // for the last element
2564
2.58k
      if (plcType.m_pos == WPS4PLCInternal::PLC::P_REL &&
2565
1.16k
              newPos == m_textPositions.end()+m_textPositions.begin())
2566
632
        newPos = m_textPositions.end();
2567
1.95k
      else
2568
1.95k
        return false;
2569
2.58k
    }
2570
2571
181k
    textPtrs.push_back(newPos);
2572
2573
181k
    DataFOD fod;
2574
181k
    fod.m_type = DataFOD::ATTR_PLC;
2575
181k
    fod.m_pos = newPos;
2576
2577
181k
    f << std::hex << newPos << ", ";
2578
181k
    if (newPos == m_textPositions.end()) break;
2579
2580
178k
    numElt++;
2581
178k
    lastPos = newPos;
2582
178k
    fods.push_back(fod);
2583
178k
  }
2584
2.72k
  f << ")";
2585
2586
2.72k
  if (long(numElt) < 1) return false;
2587
2588
2.69k
  long dataSize = (size-4*long(numElt)-4)/long(numElt);
2589
2.69k
  if (dataSize > 100) return false;
2590
2.68k
  if (size!= long(numElt)*(4+dataSize)+4) return false;
2591
2592
2.54k
  ascii().addNote(f.str().c_str());
2593
2594
2.54k
  if (!dataSize)
2595
109
  {
2596
811
    for (size_t i = 0; i < numElt; ++i)
2597
702
    {
2598
702
      listValues.push_back(-1);
2599
702
      fods[i].m_id = int(m_state->m_plcList.size());
2600
702
    }
2601
109
    WPS4TextInternal::DataPLC plc;
2602
109
    plc.m_name = zone.type();
2603
109
    plc.m_type = plcType.m_type;
2604
109
    m_state->m_plcList.push_back(plc);
2605
109
    m_FODList = mergeSortedFODLists(fods, m_FODList);
2606
109
    return true;
2607
109
  }
2608
2609
  // ok we have some data
2610
2.43k
  bool ok = true;
2611
2.43k
  long pos = m_input->tell();
2612
2.43k
  auto pars = parser;
2613
2.43k
  if ((dataSize == 3 || dataSize > 4) && !pars)
2614
129
    pars = &WPS4Text::defDataParser;
2615
2616
52.8k
  for (size_t i = 0; i < numElt; ++i)
2617
50.5k
  {
2618
50.5k
    WPS4TextInternal::DataPLC plc;
2619
2620
50.5k
    if (!pars && dataSize <= 4)
2621
45.6k
    {
2622
45.6k
      switch (dataSize)
2623
45.6k
      {
2624
43.7k
      case 1:
2625
43.7k
        plc.m_value = libwps::readU8(m_input);
2626
43.7k
        break;
2627
1.02k
      case 2:
2628
1.02k
        plc.m_value = libwps::readU16(m_input);
2629
1.02k
        break;
2630
485
      case 4:
2631
485
        plc.m_value = long(libwps::readU32(m_input));
2632
485
        break;
2633
375
      default:
2634
375
        WPS_DEBUG_MSG(("WPS4Text:readPLC: unexpected PLC size\n"));
2635
375
        WPS_FALLTHROUGH;
2636
375
      case 0:
2637
375
        plc.m_value = 0;
2638
45.6k
      }
2639
45.6k
      plc.m_value *=plcType.m_cstFactor;
2640
45.6k
    }
2641
4.96k
    else if (pars)
2642
4.96k
    {
2643
4.96k
      std::string mess;
2644
4.96k
      if (!(this->*pars)(textPtrs[i], textPtrs[i+1], int(i), pos+dataSize-1, mess))
2645
143
      {
2646
143
        ok = false;
2647
143
        break;
2648
143
      }
2649
4.82k
      plc.m_extra = mess;
2650
4.82k
      m_input->seek(pos+dataSize, librevenge::RVNG_SEEK_SET);
2651
4.82k
    }
2652
2653
50.4k
    listValues.push_back(plc.m_value);
2654
2655
50.4k
    fods[i].m_id = int(m_state->m_plcList.size());
2656
50.4k
    fods[i].m_defPos = pos;
2657
2658
50.4k
    plc.m_name = zone.type();
2659
50.4k
    plc.m_type = plcType.m_type;
2660
50.4k
    m_state->m_plcList.push_back(plc);
2661
2662
50.4k
    f.str("");
2663
50.4k
    f << "ZZ" << zone.type() << i << ":" << plc;
2664
50.4k
    ascii().addPos(pos);
2665
50.4k
    ascii().addNote(f.str().c_str());
2666
2667
50.4k
    pos += dataSize;
2668
50.4k
  }
2669
2670
2.43k
  if (ok) m_FODList = mergeSortedFODLists(fods, m_FODList);
2671
2.43k
  return true;
2672
2.43k
}
2673
2674
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */