Coverage Report

Created: 2026-09-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/XYWrite.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* libwps
3
 * Version: MPL 2.0 / LGPLv2.1+
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * Major Contributor(s):
10
 * Copyright (C) 2006, 2007 Andrew Ziem
11
 * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
12
 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13
 *
14
 * For minor contributions see the git repository.
15
 *
16
 * Alternatively, the contents of this file may be used under the terms
17
 * of the GNU Lesser General Public License Version 2.1 or later
18
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
19
 * applicable instead of those above.
20
 */
21
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include <sstream>
26
27
#include <librevenge-stream/librevenge-stream.h>
28
29
#include "libwps_internal.h"
30
#include "libwps_tools_win.h"
31
32
#include "WPSCell.h"
33
#include "WPSContentListener.h"
34
#include "WPSEntry.h"
35
#include "WPSFont.h"
36
#include "WPSHeader.h"
37
#include "WPSPageSpan.h"
38
#include "WPSParagraph.h"
39
#include "WPSPosition.h"
40
#include "WPSTable.h"
41
#include "WPSTextSubDocument.h"
42
43
#include "XYWrite.h"
44
45
namespace XYWriteParserInternal
46
{
47
/** Internal: class to store a basic cell with borders */
48
struct Cell final : public WPSCell
49
{
50
  //! constructor
51
  explicit Cell(XYWriteParser &parser)
52
125k
    : WPSCell()
53
125k
    , m_parser(parser)
54
125k
    , m_entry()
55
125k
    , m_style()
56
125k
  {
57
125k
  }
58
  //! virtual destructor
59
  ~Cell() final;
60
61
  //! operator<<
62
  friend std::ostream &operator<<(std::ostream &o, Cell const &cell);
63
  //! call when a cell must be send
64
  bool send(WPSListenerPtr &listener) final
65
0
  {
66
0
    if (!listener) return true;
67
0
    auto *listen=dynamic_cast<WPSContentListener *>(listener.get());
68
0
    if (!listen)
69
0
    {
70
0
      WPS_DEBUG_MSG(("XYWriteParserInternal::Cell::send: unexpected listener\n"));
71
0
      return true;
72
0
    }
73
0
    listen->openTableCell(*this);
74
0
    sendContent(listener);
75
0
    listen->closeTableCell();
76
0
    return true;
77
0
  }
78
79
  //! call when the content of a cell must be send
80
  bool sendContent(WPSListenerPtr &) final
81
107k
  {
82
107k
    auto input=m_parser.getInput();
83
107k
    if (!input)
84
0
    {
85
0
      WPS_DEBUG_MSG(("XYWriteParserInternal::Cell::sendContent: can not find the impu\n"));
86
0
      return true;
87
0
    }
88
107k
    long pos=input->tell();
89
107k
    m_parser.parseTextZone(m_entry, m_style);
90
107k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
91
107k
    return true;
92
107k
  }
93
94
  //! the actual parser
95
  XYWriteParser &m_parser;
96
  //! the text entry
97
  WPSEntry m_entry;
98
  //! the cell style
99
  std::string m_style;
100
};
101
Cell::~Cell()
102
410k
{
103
410k
}
104
105
//! a structure used to store a format
106
struct Format
107
{
108
  //! constructor
109
  explicit Format(bool inDosFile=false)
110
6.86M
    : m_inDosFile(inDosFile)
111
6.86M
    , m_string()
112
6.86M
    , m_args()
113
6.86M
    , m_entry()
114
6.86M
    , m_isComplex(false)
115
6.86M
    , m_listCounter(-1)
116
6.86M
    , m_level()
117
6.86M
    , m_children()
118
6.86M
  {
119
6.86M
  }
120
  //! return true if the format is empty
121
  bool empty() const
122
0
  {
123
0
    return m_string.empty() && m_args.empty();
124
0
  }
125
  //! returns the upper case of a string
126
  static std::string upperCase(std::string const &str)
127
9.62M
  {
128
9.62M
    std::string res(str);
129
9.62M
    for (auto &c : res)
130
430M
      c=char(std::toupper(c));
131
9.62M
    return res;
132
9.62M
  }
133
  //! returns the title in uppercase
134
  std::string title() const
135
3.21M
  {
136
3.21M
    return upperCase(m_string);
137
3.21M
  }
138
  //! returns the two/... first character of the main string in uppercase
139
  std::string shortTitle(size_t sz=2) const
140
4.28M
  {
141
4.28M
    if (m_string.size()<=sz)
142
1.54M
      return upperCase(m_string);
143
2.73M
    return upperCase(m_string.substr(0,sz));
144
4.28M
  }
145
  //! update a font if possible
146
  bool updateFont(WPSFont &font) const
147
36.6k
  {
148
36.6k
    bool ok=true;
149
36.6k
    auto str=title();
150
36.6k
    auto sTitle=shortTitle();
151
36.6k
    if (sTitle=="MD")
152
21.0k
    {
153
21.0k
      if (str=="MDNM")
154
645
        font.m_attributes&=(unsigned int)(WPS_SMALL_CAPS_BIT|WPS_ALL_CAPS_BIT);
155
20.3k
      else if (str=="MDBO" || str=="MD+BO")
156
893
        font.m_attributes|=WPS_BOLD_BIT;
157
19.4k
      else if (str=="MD-BO")
158
351
        font.m_attributes&=~(unsigned int)(WPS_BOLD_BIT);
159
19.1k
      else if (str=="MDBR" || str=="MD+BR")
160
1.59k
        font.m_attributes|=(WPS_BOLD_BIT|WPS_ITALICS_BIT);
161
17.5k
      else if (str=="MD-BR")
162
439
        font.m_attributes&=~(unsigned int)(WPS_BOLD_BIT|WPS_ITALICS_BIT);
163
17.1k
      else if (str=="MDBU" || str=="MD+BU")
164
932
        font.m_attributes|=(WPS_BOLD_BIT|WPS_UNDERLINE_BIT);
165
16.1k
      else if (str=="MD-BU")
166
461
        font.m_attributes&=~(unsigned int)(WPS_BOLD_BIT|WPS_UNDERLINE_BIT);
167
15.7k
      else if (str=="MDDN" || str=="MD+DN")
168
1.02k
        font.m_attributes|=WPS_STRIKEOUT_BIT;
169
14.6k
      else if (str=="MD-DN")
170
639
        font.m_attributes&=~(unsigned int)(WPS_STRIKEOUT_BIT);
171
14.0k
      else if (str=="MDRV" || str=="MD+IT")
172
681
        font.m_attributes|=WPS_ITALICS_BIT;
173
13.3k
      else if (str=="MD-IT")
174
348
        font.m_attributes&=~(unsigned int)(WPS_ITALICS_BIT);
175
13.0k
      else if (str=="MD+RV")
176
370
        font.m_attributes|=WPS_REVERSEVIDEO_BIT;
177
12.6k
      else if (str=="MD-RV")
178
183
        font.m_attributes&=~(unsigned int)(WPS_REVERSEVIDEO_BIT);
179
12.4k
      else if (str=="MDSD" || str=="MD+SD")
180
1.50k
        font.m_attributes|=WPS_SUBSCRIPT_BIT;
181
10.9k
      else if (str=="MD-SD")
182
407
        font.m_attributes&=~(unsigned int)(WPS_SUBSCRIPT_BIT);
183
10.5k
      else if (str=="MDSU" || str=="MD+SU")
184
1.13k
        font.m_attributes|=WPS_SUPERSCRIPT_BIT;
185
9.41k
      else if (str=="MD-SU")
186
1.13k
        font.m_attributes&=~(unsigned int)(WPS_SUPERSCRIPT_BIT);
187
8.28k
      else if (str=="MDUL" || str=="MD+UL")
188
703
        font.m_attributes|=WPS_UNDERLINE_BIT;
189
7.58k
      else if (str=="MD-UL")
190
327
        font.m_attributes&=~(unsigned int)(WPS_UNDERLINE_BIT);
191
7.25k
      else
192
7.25k
        ok=false;
193
21.0k
    }
194
15.6k
    else if (sTitle=="RG")
195
10.0k
    {
196
10.0k
      if (str=="RG0")
197
1.27k
        font.m_attributes&=~(unsigned int)(WPS_SMALL_CAPS_BIT|WPS_ALL_CAPS_BIT);
198
8.74k
      else if (str=="RG1")
199
1.76k
      {
200
1.76k
        font.m_attributes&=~(unsigned int)(WPS_SMALL_CAPS_BIT);
201
1.76k
        font.m_attributes|=WPS_ALL_CAPS_BIT;
202
1.76k
      }
203
6.97k
      else if (str=="RG2")
204
576
      {
205
576
        font.m_attributes&=~(unsigned int)(WPS_ALL_CAPS_BIT);
206
576
        font.m_attributes|=WPS_SMALL_CAPS_BIT;
207
576
      }
208
6.40k
      else
209
6.40k
        ok=false;
210
10.0k
    }
211
5.63k
    else if (sTitle=="FG")
212
1.57k
      ok=readColor(font.m_color);
213
4.06k
    else if (sTitle=="SZ")
214
4.06k
    {
215
4.06k
      double value;
216
4.06k
      bool inPoint;
217
4.06k
      std::string extra;
218
4.06k
      if (readUnit(str, 2, m_inDosFile, value, inPoint, extra) && inPoint)
219
1.03k
        font.m_size=value;
220
4.06k
    }
221
222
36.6k
    if (!ok && !m_string.empty())
223
14.7k
    {
224
14.7k
      WPS_DEBUG_MSG(("XYWriteParserInternal::Format::updateFont: unknown format=%s\n", m_string.c_str()));
225
14.7k
      return false;
226
14.7k
    }
227
21.8k
    return true;
228
36.6k
  }
229
  //! update a paragraph if possible
230
  bool updateParagraph(WPSParagraph &para) const
231
65.6k
  {
232
65.6k
    bool ok=true;
233
65.6k
    auto str=title();
234
65.6k
    auto sTitle=shortTitle();
235
65.6k
    double value=0;
236
65.6k
    bool inPoint;
237
65.6k
    std::string tmp;
238
65.6k
    if (str=="FC")
239
238
      para.m_justify=libwps::JustificationCenter;
240
65.4k
    else if (str=="FL")
241
401
      para.m_justify=libwps::JustificationLeft;
242
65.0k
    else if (str=="FR")
243
384
      para.m_justify=libwps::JustificationRight;
244
64.6k
    else if (str=="JU")
245
1.98k
    {
246
1.98k
      if (para.m_justify==libwps::JustificationLeft)
247
1.41k
        para.m_justify=libwps::JustificationFull;
248
1.98k
    }
249
62.6k
    else if (str=="NJ")
250
1.17k
    {
251
1.17k
      if (para.m_justify==libwps::JustificationFull)
252
341
        para.m_justify=libwps::JustificationLeft;
253
1.17k
    }
254
61.4k
    else if (sTitle=="IP" || sTitle=="RM" || sTitle=="LS")
255
10.5k
    {
256
10.5k
      size_t p=2;
257
10.5k
      if (readUnit(str, p, m_inDosFile, value, inPoint, tmp, sTitle!="LS"))
258
8.18k
      {
259
8.18k
        if (sTitle=="LS")
260
3.67k
          para.setInterline(value, (!inPoint || (m_inDosFile && value<=3)) ? librevenge::RVNG_PERCENT : librevenge::RVNG_POINT, WPSParagraph::AtLeast);
261
4.51k
        else if (inPoint)
262
3.75k
        {
263
3.75k
          if (sTitle=="IP")
264
943
            para.m_margins[0]=value/72;
265
2.81k
          else if (!m_inDosFile || value<150) // in dos file, size from left
266
2.43k
            para.m_margins[2]=value/72;
267
3.75k
        }
268
8.18k
        if (!tmp.empty())
269
6.03k
        {
270
6.03k
          WPS_DEBUG_MSG(("XYWriteParserInternal::Paragraph::updateParagraph: find extra data in %s\n", m_string.c_str()));
271
6.03k
        }
272
8.18k
      }
273
10.5k
    }
274
50.9k
    else if (sTitle=="AL")
275
2.31k
    {
276
2.31k
      if (str=="AL0" || str=="AL1")
277
1.14k
        para.setInterline(1, librevenge::RVNG_PERCENT);
278
2.31k
    }
279
48.6k
    else if (sTitle=="NB" || sTitle=="BB")
280
5.77k
    {
281
5.77k
      if (str=="NB0" || str=="NB1" || str=="BB")
282
1.63k
        para.m_breakStatus=0;
283
4.14k
      else if (sTitle=="NB")   // no break
284
2.48k
      {
285
2.48k
        size_t p=2;
286
2.48k
        if (readNumber(str, p, value))
287
1.07k
          para.m_breakStatus=libwps::NoBreakWithNextBit;
288
1.41k
        else
289
1.41k
          ok=false;
290
2.48k
      }
291
1.65k
      else // allow break + data?
292
1.65k
        ok=false;
293
5.77k
    }
294
42.8k
    else if (sTitle=="LL" || sTitle=="EL")
295
6.00k
    {
296
      // some extra spacings
297
15.0k
      for (size_t i=0; i<=(sTitle=="LL" ? std::min<size_t>(1,m_args.size()) : 0); ++i)
298
9.04k
      {
299
9.04k
        size_t p=i==0 ? 2 : 0;
300
9.04k
        if (readUnit(i==0 ? str : m_args[i-1], p, m_inDosFile, value, inPoint, tmp))
301
3.21k
        {
302
3.21k
          if (sTitle=="EL") // after a line
303
420
            continue;
304
          // LL after para
305
2.79k
          if (i==1)
306
1.20k
            para.m_spacings[2]=inPoint ? value/72 : value*12/72;
307
          // FIXME update also para spacings 0 when i==0
308
2.79k
        }
309
9.04k
      }
310
6.00k
    }
311
36.8k
    else if (sTitle=="TS")
312
35.2k
    {
313
1.45M
      for (size_t i=0; i<=m_args.size(); ++i)
314
1.41M
      {
315
1.41M
        size_t p=i==0 ? 2 : 0;
316
1.41M
        WPSTabStop tab;
317
1.41M
        if (readUnit(i==0 ? m_string : m_args[i-1], p, m_inDosFile, value, inPoint, tmp))
318
863k
        {
319
863k
          if (!inPoint)
320
331
            continue;
321
862k
          tmp=upperCase(tmp);
322
862k
          if (tmp=="R")
323
12.9k
            tab.m_alignment=WPSTabStop::RIGHT;
324
850k
          else if (tmp=="C")
325
1.23k
            tab.m_alignment=WPSTabStop::CENTER;
326
848k
          else if (tmp=="D")
327
2.47k
            tab.m_alignment=WPSTabStop::DECIMAL;
328
846k
          else
329
846k
            tab.m_alignment=WPSTabStop::LEFT;
330
862k
          tab.m_position=value/72.;
331
862k
          para.m_tabs.push_back(tab);
332
862k
        }
333
1.41M
      }
334
35.2k
    }
335
1.57k
    else if (sTitle=="BG")
336
1.57k
      ok=readColor(para.m_backgroundColor);
337
0
    else
338
0
      ok=false;
339
65.6k
    if (!ok && !m_string.empty())
340
4.64k
    {
341
4.64k
      WPS_DEBUG_MSG(("XYWriteParserInternal::Paragraph::updateParagraph: unknown format=%s\n", m_string.c_str()));
342
4.64k
      return false;
343
4.64k
    }
344
61.0k
    return true;
345
65.6k
  }
346
  //! operator<<
347
  friend std::ostream &operator<<(std::ostream &o, Format const &format)
348
0
  {
349
0
    o << format.m_string;
350
0
    if (!format.m_children.empty())
351
0
    {
352
0
      o << "\n";
353
0
      for (auto const &child : format.m_children)
354
0
        o << "\t" << child << "\n";
355
0
      return o;
356
0
    }
357
0
    if (format.m_isComplex && format.m_entry.valid())
358
0
      o << "[dt=" << format.m_entry.length() << "]";
359
0
    if (format.m_args.empty())
360
0
      return o;
361
0
    o << "[";
362
0
    for (auto const &arg : format.m_args)
363
0
      o << arg << ",";
364
0
    o << "]";
365
0
    return o;
366
0
  }
367
  //! flag to know if we are in a dos file
368
  bool m_inDosFile;
369
  //! the main part
370
  std::string m_string;
371
  //! the other arguments
372
  std::vector<std::string> m_args;
373
  //! a text zone entry
374
  WPSEntry m_entry;
375
  //! a flag to know if this is a complex entry
376
  bool m_isComplex;
377
  //! the list counter (if known)
378
  mutable int m_listCounter;
379
  //! the list level (if known)
380
  mutable WPSList::Level m_level;
381
  //! the list of child (for style, ...)
382
  std::vector<Format> m_children;
383
384
  ////////////////////////////////
385
  // low level
386
  ////////////////////////////////
387
388
  //! try to read a color
389
  bool readColor(WPSColor &color) const
390
3.14k
  {
391
3.14k
    if (m_args.size()!=2)
392
1.44k
    {
393
1.44k
      WPS_DEBUG_MSG(("XYWriteParserInternal::Format::readColor: bad number of argument\n"));
394
1.44k
      return false;
395
1.44k
    }
396
1.70k
    unsigned char col[3];
397
4.06k
    for (size_t i=0; i<3; ++i)
398
3.62k
    {
399
3.62k
      size_t p=i==0 ? 2 : 0;
400
3.62k
      unsigned int val;
401
3.62k
      std::string tmp;
402
3.62k
      if (!readUInt(i==0 ? m_string : m_args[i-1], p, val, tmp) || val>255)
403
1.26k
      {
404
1.26k
        WPS_DEBUG_MSG(("XYWriteParserInternal::Format::readColor: can not read a component\n"));
405
1.26k
        return false;
406
1.26k
      }
407
2.35k
      col[i]=(unsigned char)(val);
408
2.35k
    }
409
438
    color=WPSColor(col[0],col[1],col[2]);
410
438
    return true;
411
1.70k
  }
412
  //! try to read a box of double in points
413
  static bool readBox2f(std::string const &str, size_t i, bool inDosFile, WPSBox2f &box, std::string &extra)
414
105k
  {
415
105k
    Vec2f vec;
416
105k
    std::string remain;
417
232k
    for (int c=0; c<2; ++c)
418
169k
    {
419
169k
      if ((c==0 && (!readVec2f(str, i, inDosFile, vec, remain) || remain.substr(0,1)!=" ")) ||
420
129k
              (c==1 && (!readVec2f(remain, 1, inDosFile, vec, extra))))
421
42.0k
        return false;
422
127k
      if (c==0)
423
64.6k
        box.setMin(vec);
424
63.1k
      else
425
63.1k
        box.setMax(vec);
426
127k
    }
427
63.1k
    return true;
428
105k
  }
429
  //! try to read a vector of double in points
430
  static bool readVec2f(std::string const &str, size_t i, bool inDosFile, Vec2f &vec, std::string &extra)
431
182k
  {
432
182k
    double value=0;
433
182k
    bool inPoint;
434
182k
    std::string remain;
435
463k
    for (int c=0; c<2; ++c)
436
324k
    {
437
324k
      if ((c==0 && (!readUnit(str, i, inDosFile, value, inPoint, remain) || remain.substr(0,1)!="x")) ||
438
282k
              (c==1 && (!readUnit(remain, 1, inDosFile, value, inPoint, extra))) || !inPoint)
439
43.6k
        return false;
440
280k
      vec[c]=float(value);
441
280k
    }
442
139k
    return true;
443
182k
  }
444
  //! try to read a vector of uint
445
  static bool readVec2i(std::string const &str, size_t i, Vec2i &vec, std::string &extra)
446
3.93k
  {
447
3.93k
    unsigned int value=0;
448
3.93k
    std::string remain;
449
5.59k
    for (int c=0; c<2; ++c)
450
4.81k
    {
451
4.81k
      size_t p=c==0 ? i : 1;
452
4.81k
      if ((c==0 && (!readUInt(str, p, value, remain) || remain.substr(0,1)!="x")) ||
453
1.76k
              (c==1 && (!readUInt(remain, p, value, extra))))
454
3.16k
        return false;
455
1.65k
      vec[c]=int(value);
456
1.65k
    }
457
773
    return true;
458
3.93k
  }
459
  //! try to read a int
460
  static bool readUInt(std::string const &str, size_t &i, unsigned int &value, std::string &extra)
461
225k
  {
462
225k
    auto len=str.size();
463
225k
    value=0;
464
225k
    size_t p=i;
465
512k
    while (p<len && str[p]>='0' && str[p]<='9')
466
286k
      value=value*10+(unsigned int)(str[p++]-'0');
467
225k
    if (p==i)
468
48.7k
      return false;
469
177k
    i=p;
470
177k
    if (i<len)
471
155k
      extra=str.substr(i);
472
177k
    return true;
473
225k
  }
474
  //! try to read a double.
475
  static bool readNumber(std::string const &str, size_t &i, double &value)
476
2.66M
  try
477
2.66M
  {
478
2.66M
    if (str.size()<=i)
479
1.12M
    {
480
1.12M
      if (!str.empty())
481
21.4k
      {
482
21.4k
        WPS_DEBUG_MSG(("XYWriteParserInternal::Format::readNumber: the string %s is too short\n", str.c_str()));
483
21.4k
      }
484
1.12M
      return false;
485
1.12M
    }
486
1.54M
    size_t p;
487
1.54M
    if (i==0)
488
1.11M
      value = std::stod(str,&p);
489
429k
    else
490
429k
      value = std::stod(str.substr(i),&p);
491
1.54M
    i+=p;
492
1.54M
    return true;
493
2.66M
  }
494
2.66M
  catch (...)
495
2.66M
  {
496
284k
    if (!str.empty())
497
284k
    {
498
284k
      WPS_DEBUG_MSG(("XYWriteParserInternal::Format::readNumber: can not extract number in %s\n", str.c_str()));
499
284k
    }
500
284k
    return false;
501
284k
  }
502
  /** try to read an unit, returns a number in Point or in Line.
503
    \note if unit=AUTO set value to 72pt=1in
504
   */
505
  static bool readUnit(std::string const &str, size_t i, bool inDosFile, double &value, bool &inPoint, std::string &extra, bool dosInChar=true)
506
2.66M
  {
507
2.66M
    if (str.size()>=i+4 && str.substr(i,4)=="AUTO")
508
2.99k
    {
509
2.99k
      value=72;
510
2.99k
      inPoint=true;
511
2.99k
      if (str.size()>i+4)
512
2.57k
        extra=str.substr(i+4);
513
2.99k
      return true;
514
2.99k
    }
515
2.66M
    if (!readNumber(str,i,value))
516
1.40M
      return false;
517
1.25M
    auto remain=upperCase(str.substr(i,2));
518
1.25M
    if (i+2<str.size())
519
369k
      extra=str.substr(i+2);
520
1.25M
    inPoint=true;
521
1.25M
    if (inDosFile && remain.empty())
522
836k
    {
523
836k
      if (dosInChar) value*=8;
524
836k
      return true;
525
836k
    }
526
422k
    if (remain=="PT")
527
970
      return true;
528
421k
    if (remain=="IN")
529
565
    {
530
565
      value *= 72;
531
565
      return true;
532
565
    }
533
420k
    if (remain=="CM")
534
1.00k
    {
535
1.00k
      value *= 72/2.54;
536
1.00k
      return true;
537
1.00k
    }
538
419k
    if (remain=="MM")
539
16.4k
    {
540
16.4k
      value *= 72/25.4;
541
16.4k
      return true;
542
16.4k
    }
543
403k
    if (remain=="LI")
544
4.87k
    {
545
4.87k
      inPoint=false;
546
4.87k
      return true;
547
4.87k
    }
548
398k
    if (inDosFile)
549
395k
    {
550
395k
      if (dosInChar) value*=8;
551
395k
      extra=str.substr(i);
552
395k
      return true;
553
395k
    }
554
2.69k
    if (!str.empty())
555
2.69k
    {
556
2.69k
      WPS_DEBUG_MSG(("XYWriteParserInternal::Format::readUnit: can not extract unit in %s\n", str.c_str()));
557
2.69k
    }
558
2.69k
    return false;
559
560
398k
  }
561
};
562
563
//! the state of XYWrite
564
struct State
565
{
566
  //! constructor
567
  explicit State(libwps_tools_win::Font::Type fontType)
568
4.47k
    : m_isDosFile(false)
569
4.47k
    , m_eof(-1)
570
4.47k
    , m_fontType(fontType)
571
4.47k
    , m_metaData()
572
4.47k
    , m_nameToStyleMap()
573
4.47k
    , m_counterToTypeMap()
574
4.47k
    , m_counterToValueMap()
575
4.47k
  {
576
4.47k
  }
577
  //! returns the current font type
578
  libwps_tools_win::Font::Type getFontType() const
579
345k
  {
580
345k
    if (m_fontType!=libwps_tools_win::Font::UNKNOWN)
581
0
      return m_fontType;
582
    // checkme
583
345k
    return m_isDosFile ? libwps_tools_win::Font::CP_437 :
584
345k
           libwps_tools_win::Font::WIN3_WEUROPE;
585
345k
  }
586
  //! flag to know if the file is or not a dos file
587
  bool m_isDosFile;
588
  //! the last file position
589
  long m_eof;
590
  //! the user font type
591
  libwps_tools_win::Font::Type m_fontType;
592
  //! the meta data
593
  librevenge::RVNGPropertyList m_metaData;
594
  //! map name to style
595
  std::map<std::string, Format> m_nameToStyleMap;
596
  //! map counter to type
597
  std::map<int, libwps::NumberingType> m_counterToTypeMap;
598
  //! map counter to value
599
  std::map<int, int> m_counterToValueMap;
600
};
601
602
//! Internal: the subdocument of a XYWriteParser
603
class SubDocument final : public WPSTextSubDocument
604
{
605
public:
606
  //! constructor for a text entry
607
  SubDocument(RVNGInputStreamPtr const &input, XYWriteParser &pars, WPSEntry const &entry, std::string const &style="")
608
132k
    : WPSTextSubDocument(input, &pars), m_entry(entry), m_style(style) {}
609
  //! destructor
610
132k
  ~SubDocument() final {}
611
612
  //! operator==
613
  bool operator==(std::shared_ptr<WPSSubDocument> const &doc) const final
614
3.38M
  {
615
3.38M
    if (!doc || !WPSTextSubDocument::operator==(doc))
616
0
      return false;
617
3.38M
    auto const *sDoc = dynamic_cast<SubDocument const *>(doc.get());
618
3.38M
    if (!sDoc) return false;
619
3.38M
    return m_entry == sDoc->m_entry && m_style == sDoc->m_style;
620
3.38M
  }
621
622
  //! the parser function
623
  void parse(std::shared_ptr<WPSContentListener> &listener, libwps::SubDocumentType subDocumentType) final;
624
  //! the entry
625
  WPSEntry m_entry;
626
  //! the style
627
  std::string m_style;
628
};
629
630
void SubDocument::parse(std::shared_ptr<WPSContentListener> &listener, libwps::SubDocumentType /*subDocumentType*/)
631
216k
{
632
216k
  if (!listener.get())
633
0
  {
634
0
    WPS_DEBUG_MSG(("XYWriteParserInternal::SubDocument::parse: no listener\n"));
635
0
    return;
636
0
  }
637
638
216k
  if (!m_parser)
639
0
  {
640
0
    listener->insertCharacter(' ');
641
0
    WPS_DEBUG_MSG(("XYWriteParserInternal::SubDocument::parse: bad parser\n"));
642
0
    return;
643
0
  }
644
216k
  if (!m_entry.valid() || !m_input)
645
9.55k
  {
646
9.55k
    listener->insertCharacter(' ');
647
9.55k
    return;
648
9.55k
  }
649
650
206k
  auto *mnParser = dynamic_cast<XYWriteParser *>(m_parser);
651
206k
  if (!mnParser)
652
0
  {
653
0
    WPS_DEBUG_MSG(("XYWriteParserInternal::SubDocument::parse: bad parser...\n"));
654
0
    listener->insertCharacter(' ');
655
0
    return;
656
0
  }
657
206k
  long pos=m_input->tell();
658
206k
  mnParser->parseTextZone(m_entry, m_style);
659
206k
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
660
206k
}
661
}
662
663
// constructor, destructor
664
XYWriteParser::XYWriteParser(RVNGInputStreamPtr &input, WPSHeaderPtr &header,
665
                             libwps_tools_win::Font::Type encoding)
666
4.47k
  : WPSParser(input, header)
667
4.47k
  , m_listener()
668
4.47k
  , m_state()
669
4.47k
{
670
4.47k
  m_state.reset(new XYWriteParserInternal::State(encoding));
671
4.47k
}
672
673
XYWriteParser::~XYWriteParser()
674
4.47k
{
675
4.47k
}
676
677
bool XYWriteParser::checkFilePosition(long pos) const
678
4.47k
{
679
4.47k
  if (m_state->m_eof < 0)
680
4.47k
  {
681
4.47k
    RVNGInputStreamPtr input = const_cast<XYWriteParser *>(this)->getInput();
682
4.47k
    long actPos = input->tell();
683
4.47k
    input->seek(0, librevenge::RVNG_SEEK_END);
684
4.47k
    m_state->m_eof=input->tell();
685
4.47k
    input->seek(actPos, librevenge::RVNG_SEEK_SET);
686
4.47k
  }
687
4.47k
  return pos>=0 && pos <= m_state->m_eof;
688
4.47k
}
689
690
// listener
691
std::shared_ptr<WPSContentListener> XYWriteParser::createListener(librevenge::RVNGTextInterface *interface)
692
4.44k
{
693
4.44k
  auto input=getInput();
694
4.44k
  if (!input)
695
0
    throw "Can not find input";
696
4.44k
  std::vector<WPSPageSpan> pageList;
697
4.44k
  WPSPageSpan ps;
698
4.44k
  ps.setMarginLeft(0.1);
699
4.44k
  ps.setMarginRight(0.1);
700
4.44k
  ps.setMarginTop(0.1);
701
4.44k
  ps.setMarginBottom(0.1);
702
4.44k
  input->seek(0, librevenge::RVNG_SEEK_SET);
703
11.0M
  while (!input->isEnd() && input->tell()<m_state->m_eof)
704
11.0M
  {
705
11.0M
    uint8_t c=libwps::readU8(input);
706
11.0M
    if (c==0x1a)
707
2.84k
      break;
708
11.0M
    if (c!=0xae)
709
10.4M
      continue;
710
588k
    XYWriteParserInternal::Format format;
711
588k
    if (!parseFormat(format))
712
1.46k
      break;
713
586k
    std::string const str=format.title();
714
586k
    std::string const sTitle=format.shortTitle();
715
586k
    if (str=="PG")
716
129k
      pageList.push_back(ps);
717
456k
    else if (sTitle=="RH" || sTitle=="RF")
718
56.5k
    {
719
56.5k
      if (str.size()==2)
720
6.40k
      {
721
6.40k
        WPSEntry fEntry=format.m_entry;
722
6.40k
        long fEnd=fEntry.end();
723
6.40k
        fEntry.setBegin(fEntry.begin()+2);
724
6.40k
        fEntry.setEnd(fEnd);
725
6.40k
        WPSSubDocumentPtr subdoc(new XYWriteParserInternal::SubDocument
726
6.40k
                                 (getInput(), *this,fEntry));
727
6.40k
        ps.setHeaderFooter(sTitle=="RH" ? WPSPageSpan::HEADER : WPSPageSpan::FOOTER,
728
6.40k
                           WPSPageSpan::ALL, subdoc);
729
6.40k
      }
730
50.1k
      else if (str.size()>=3 && (str[2]=='A' || str[2]=='E' || str[2]=='O'))
731
13.1k
      {
732
13.1k
        WPSEntry fEntry=format.m_entry;
733
13.1k
        long fEnd=fEntry.end();
734
13.1k
        fEntry.setBegin(fEntry.begin()+3);
735
13.1k
        fEntry.setEnd(fEnd);
736
13.1k
        WPSSubDocumentPtr subdoc(new XYWriteParserInternal::SubDocument
737
13.1k
                                 (getInput(), *this,fEntry));
738
13.1k
        ps.setHeaderFooter(sTitle=="RH" ? WPSPageSpan::HEADER : WPSPageSpan::FOOTER,
739
13.1k
                           str[2]=='A' ? WPSPageSpan::ALL : str[2]=='E' ? WPSPageSpan::EVEN : WPSPageSpan::ODD, subdoc);
740
13.1k
      }
741
56.5k
    }
742
400k
    else if (sTitle=="PW" || sTitle=="FD")
743
9.59k
    {
744
      // PB is probably related to the page type A4 paysage==82?
745
9.59k
      double val;
746
9.59k
      bool inPoint;
747
9.59k
      std::string tmp;
748
9.59k
      if (format.readUnit(str, 2, m_state->m_isDosFile, val, inPoint, tmp) && inPoint && val>0 && val<100*72)
749
6.96k
      {
750
6.96k
        if (sTitle=="FD")
751
6.28k
          ps.setFormLength(val/72.);
752
679
        else
753
679
          ps.setFormWidth(val/72.);
754
6.96k
      }
755
2.63k
      else
756
2.63k
      {
757
2.63k
        WPS_DEBUG_MSG(("XYWriteParser::createListener: can not parse %s\n", format.m_string.c_str()));
758
2.63k
      }
759
9.59k
    }
760
390k
    else if (sTitle=="OF" || sTitle=="TP" || sTitle=="BT")
761
4.53k
    {
762
40.0k
      for (size_t j=0; j<=format.m_args.size(); ++j)
763
35.5k
      {
764
35.5k
        size_t p=j==0 ? 2 : 0;
765
35.5k
        double value;
766
35.5k
        bool inPoint;
767
35.5k
        std::string tmp;
768
35.5k
        if (format.readUnit(j==0 ? str : format.m_args[j-1], p, m_state->m_isDosFile, value, inPoint, tmp))
769
8.99k
        {
770
8.99k
          if (sTitle=="OF" && inPoint)
771
2.89k
          {
772
2.89k
            if (j==0)
773
1.25k
              ps.setMarginLeft(value/72);
774
1.64k
            else
775
1.64k
              ps.setMarginRight(value/72);
776
2.89k
          }
777
6.09k
          else if (sTitle=="TP" && inPoint)
778
2.16k
          {
779
2.16k
            if (j==1) // i==0 bef header
780
538
              ps.setMarginTop(value/72);
781
2.16k
          }
782
3.92k
          else if (sTitle=="BT" && inPoint)
783
2.76k
          {
784
2.76k
            if (j==2) // i==0 aft footer, i==1 min, 3 max
785
549
              ps.setMarginBottom(value/72);
786
2.76k
          }
787
8.99k
        }
788
26.5k
        else
789
26.5k
        {
790
26.5k
          WPS_DEBUG_MSG(("XYWriteParser::createListener: can not parse %s\n", format.m_string.c_str()));
791
26.5k
        }
792
35.5k
      }
793
794
4.53k
    }
795
586k
  }
796
4.44k
  pageList.push_back(ps);
797
4.44k
  auto listener=std::make_shared<WPSContentListener>(pageList, interface);
798
4.44k
  listener->setMetaData(m_state->m_metaData);
799
4.44k
  return listener;
800
4.44k
}
801
802
////////////////////////////////////////////////////////////
803
// main funtions to parse a document
804
////////////////////////////////////////////////////////////
805
806
// main function to parse the document
807
void XYWriteParser::parse(librevenge::RVNGTextInterface *documentInterface)
808
4.47k
{
809
4.47k
  RVNGInputStreamPtr input=getInput();
810
4.47k
  if (!input)
811
0
  {
812
0
    WPS_DEBUG_MSG(("XYWriteParser::parse: does not find main input\n"));
813
0
    throw (libwps::ParseException());
814
0
  }
815
4.47k
  if (!checkHeader(nullptr, true))
816
28
    throw (libwps::ParseException());
817
818
4.44k
  ascii().setStream(input);
819
4.44k
  ascii().open("MN0");
820
4.44k
  try
821
4.44k
  {
822
4.44k
    if (!m_state->m_isDosFile && !findAllZones())
823
1
      throw (libwps::ParseException());
824
4.44k
    m_listener=createListener(documentInterface);
825
4.44k
    if (!m_listener)
826
0
    {
827
0
      WPS_DEBUG_MSG(("XYWriteParser::parse: can not create the listener\n"));
828
0
      throw (libwps::ParseException());
829
0
    }
830
4.44k
    m_listener->startDocument();
831
4.44k
    WPSEntry entry;
832
4.44k
    entry.setBegin(0);
833
4.44k
    entry.setEnd(m_state->m_eof);
834
4.44k
    parseTextZone(entry);
835
4.44k
    m_listener->endDocument();
836
4.44k
  }
837
4.44k
  catch (...)
838
4.44k
  {
839
1.46k
    WPS_DEBUG_MSG(("XYWriteParser::parse: exception catched when parsing the main document\n"));
840
1.46k
    throw (libwps::ParseException());
841
1.46k
  }
842
843
2.97k
  m_listener.reset();
844
845
2.97k
  ascii().reset();
846
2.97k
}
847
848
bool XYWriteParser::findAllZones()
849
228
{
850
228
  RVNGInputStreamPtr input = getInput();
851
228
  if (!input)
852
0
  {
853
0
    WPS_DEBUG_MSG(("XYWriteParser::findAllZones: can not find the input\n"));
854
0
    return false;
855
0
  }
856
228
  input->seek(0, librevenge::RVNG_SEEK_SET);
857
228
  bool ok=false;
858
830k
  while (!input->isEnd())
859
830k
  {
860
830k
    if (libwps::readU8(input)==0x1a)
861
227
    {
862
227
      ok=true;
863
227
      break;
864
227
    }
865
830k
  }
866
228
  if (!ok)
867
1
  {
868
1
    WPS_DEBUG_MSG(("XYWriteParser::findAllZones: can not find the end main zone marker\n"));
869
1
    return false;
870
1
  }
871
227
  long endZone1=input->tell();
872
4.82M
  while (!input->isEnd())
873
4.82M
  {
874
4.82M
    if (libwps::readU8(input)==0x1a)
875
199
    {
876
199
      WPSEntry entry;
877
199
      entry.setBegin(endZone1);
878
199
      entry.setEnd(input->tell());
879
199
      parseMetaData(entry);
880
199
      input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
881
199
      break;
882
199
    }
883
4.82M
  }
884
  // now normally 22 02 fe fc fe 01 00
885
227
  if (m_state->m_eof!=input->tell()+7)
886
182
  {
887
182
    WPS_DEBUG_MSG(("XYWriteParser::findAllZones: end of file seems bad\n"));
888
182
  }
889
  // ok, update the end of the main zone and returns
890
227
  m_state->m_eof=endZone1-1;
891
227
  return true;
892
228
}
893
894
bool XYWriteParser::update(XYWriteParserInternal::Format const &format, libwps_tools_win::Font::Type &fontType) const
895
1.11M
{
896
1.11M
  if (!m_listener)
897
0
    throw "no listener";
898
1.11M
  std::string const str=format.title();
899
1.11M
  std::string const sTitle=format.shortTitle();
900
1.11M
  if (sTitle=="MD" || sTitle=="RG" || sTitle=="SZ" ||
901
1.07M
          sTitle=="FG")
902
36.6k
  {
903
36.6k
    auto font=m_listener->getFont();
904
36.6k
    if (!format.updateFont(font))
905
14.7k
      return false;
906
21.8k
    m_listener->setFont(font);
907
21.8k
  }
908
1.07M
  else if (sTitle=="UF" && str.size()>2)
909
42.7k
  {
910
42.7k
    auto font=m_listener->getFont();
911
42.7k
    font.m_name=libwps_tools_win::Font::unicodeString(format.m_string.substr(2), m_state->getFontType());
912
42.7k
    auto newType=libwps_tools_win::Font::getFontType(font.m_name);
913
42.7k
    if (newType!=libwps_tools_win::Font::UNKNOWN)
914
21.2k
      fontType=newType;
915
42.7k
    m_listener->setFont(font);
916
42.7k
  }
917
1.03M
  else if (str=="FC" || str=="FL" || str=="FR" ||
918
1.03M
           str=="JU" || str=="NJ" ||
919
1.02M
           sTitle=="IP" || sTitle=="RM" ||
920
1.02M
           sTitle=="AL" || sTitle=="LS" || sTitle=="BB" || sTitle=="NB" ||
921
1.00M
           sTitle=="EL" || sTitle=="LL" || sTitle=="TS" ||
922
968k
           sTitle=="BG")
923
65.6k
  {
924
65.6k
    auto paragraph=m_listener->getParagraph();
925
65.6k
    if (!format.updateParagraph(paragraph))
926
4.64k
      return false;
927
61.0k
    m_listener->setParagraph(paragraph);
928
61.0k
  }
929
967k
  else if (sTitle=="LM")
930
39.6k
  {
931
39.6k
    size_t p=2;
932
39.6k
    double value;
933
39.6k
    std::string tmp;
934
39.6k
    bool inPoint=false;
935
39.6k
    if (!format.readUnit(str, p, m_state->m_isDosFile, value, inPoint, tmp))
936
2.00k
      return false;
937
37.6k
    auto paragraph=m_listener->getParagraph();
938
37.6k
    if (inPoint)
939
37.1k
      paragraph.m_margins[1]=value/72;
940
37.6k
    if (format.m_listCounter==-1 && !tmp.empty()&&format.m_entry.valid())
941
34.4k
    {
942
34.4k
      format.m_listCounter=-2;
943
      // look for a counter
944
34.4k
      auto THIS=const_cast<XYWriteParser *>(this);
945
34.4k
      auto input=THIS->getInput();
946
34.4k
      if (!input)
947
0
        throw "XYWriteParser::update: no input";
948
34.4k
      long actPos=input->tell();
949
34.4k
      bool ok=false;
950
34.4k
      input->seek(format.m_entry.begin(), librevenge::RVNG_SEEK_SET);
951
6.46M
      while (!input->isEnd() && input->tell()<format.m_entry.end())
952
6.45M
      {
953
6.45M
        if (libwps::readU8(input)==';')
954
29.2k
        {
955
29.2k
          ok=true;
956
29.2k
          break;
957
29.2k
        }
958
6.45M
      }
959
34.4k
      if (ok)
960
29.2k
      {
961
29.2k
        ok=false;
962
29.2k
        auto defType=m_state->getFontType();
963
13.1M
        while (!input->isEnd() && input->tell()<format.m_entry.end())
964
13.1M
        {
965
13.1M
          auto ch=libwps::readU8(input);
966
13.1M
          if (ch!=0xae)
967
13.0M
          {
968
13.0M
            if (ch=='\t') ch=' '; // replace tabulations by space
969
13.0M
            libwps::appendUnicode(uint32_t(libwps_tools_win::Font::unicode(ch, defType)),
970
13.0M
                                  format.m_listCounter >= 0 ? format.m_level.m_suffix :
971
13.0M
                                  format.m_level.m_prefix);
972
13.0M
            continue;
973
13.0M
          }
974
61.7k
          XYWriteParserInternal::Format newFormat;
975
61.7k
          if (!THIS->parseFormat(newFormat))
976
690
            break;
977
61.0k
          p=1;
978
61.0k
          unsigned int val;
979
61.0k
          std::string const newFStr=newFormat.title();
980
61.0k
          std::string extra;
981
61.0k
          if (!newFStr.empty() && newFStr[0]=='C' &&
982
29.3k
                  newFormat.readUInt(newFStr, p, val, extra))
983
28.4k
          {
984
28.4k
            if (format.m_listCounter>=0)
985
4.51k
            {
986
4.51k
              format.m_level.m_prefix.clear();
987
4.51k
              format.m_level.m_suffix.clear();
988
4.51k
            }
989
28.4k
            format.m_listCounter=int(val);
990
28.4k
            auto it=m_state->m_counterToTypeMap.find(format.m_listCounter);
991
28.4k
            if (it!=m_state->m_counterToTypeMap.end())
992
20.3k
            {
993
20.3k
              format.m_level.m_type=it->second;
994
20.3k
            }
995
8.15k
            else
996
8.15k
            {
997
8.15k
              WPS_DEBUG_MSG(("XYWriteParser::update: can not find counter %d\n", format.m_listCounter));
998
8.15k
            }
999
28.4k
            ok=true;
1000
28.4k
          }
1001
32.6k
          else
1002
32.6k
          {
1003
#ifdef DEBUG
1004
            std::cerr << "XYWriteParser::update[LM]: unused\n";
1005
            std::cerr << "\t" << newFormat << "\n";
1006
#endif
1007
32.6k
          }
1008
61.0k
        }
1009
29.2k
      }
1010
34.4k
      if (!ok)
1011
10.4k
      {
1012
#ifdef DEBUG
1013
        std::cerr << "XYWriteParser::update[LM]: unused\n";
1014
        std::cerr << "\t" << format << "\n";
1015
#endif
1016
10.4k
      }
1017
34.4k
      input->seek(actPos, librevenge::RVNG_SEEK_SET);
1018
34.4k
    }
1019
37.6k
    if (!format.m_level.isDefault())
1020
18.9k
    {
1021
18.9k
      format.m_level.m_labelIndent=paragraph.m_margins[1];
1022
18.9k
      paragraph.m_listLevel=format.m_level;
1023
18.9k
      paragraph.m_listLevelIndex=1;
1024
18.9k
      paragraph.m_margins[1]=0;
1025
18.9k
    }
1026
37.6k
    m_listener->setParagraph(paragraph);
1027
37.6k
  }
1028
927k
  else
1029
927k
    return false;
1030
163k
  return true;
1031
1.11M
}
1032
1033
bool XYWriteParser::parseTextZone(WPSEntry const &entry, std::string const &styleName)
1034
318k
{
1035
318k
  RVNGInputStreamPtr input = getInput();
1036
318k
  if (!input || !m_listener)
1037
0
    throw (libwps::ParseException());
1038
318k
  if (!entry.valid())
1039
44.6k
    return true;
1040
273k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1041
273k
  auto fontType=m_state->getFontType();
1042
273k
  WPSFont defFont;
1043
273k
  defFont.m_name="Courier New";
1044
273k
  defFont.m_size=10;
1045
273k
  m_listener->setFont(defFont);
1046
273k
  if (!styleName.empty())
1047
22.9k
  {
1048
22.9k
    auto it=m_state->m_nameToStyleMap.find(styleName);
1049
22.9k
    if (it!=m_state->m_nameToStyleMap.end())
1050
2.33k
    {
1051
2.33k
      for (auto const &child : it->second.m_children)
1052
6.35k
      {
1053
6.35k
        std::string const sTitle=child.shortTitle();
1054
6.35k
        if (!update(child, fontType) && sTitle!="FT" && sTitle!="BF") // checkme: FT and BF related to footnote
1055
4.48k
        {
1056
#ifdef DEBUG
1057
          std::cerr << "XYWriteParser::parseTextZone[child]: unused\n";
1058
          std::cerr << "\t" << child << "\n";
1059
#endif
1060
4.48k
        }
1061
6.35k
      }
1062
2.33k
    }
1063
22.9k
  }
1064
30.2M
  while (!input->isEnd() && input->tell()<entry.end())
1065
29.9M
  {
1066
29.9M
    uint8_t c=libwps::readU8(input);
1067
29.9M
    if (c==0x1a)
1068
6.51k
    {
1069
6.51k
      if (input->tell()<entry.end())
1070
3.96k
      {
1071
3.96k
        WPS_DEBUG_MSG(("XYWriteParser::parseTextZone: find unexpected end zone\n"));
1072
3.96k
      }
1073
6.51k
      break;
1074
6.51k
    }
1075
29.9M
    if (c==0xae)
1076
1.08M
    {
1077
1.08M
      XYWriteParserInternal::Format format;
1078
1.08M
      if (!parseFormat(format))
1079
5.21k
        throw (libwps::ParseException());
1080
1.08M
      std::string const str=format.title();
1081
1.08M
      std::string const sTitle=format.shortTitle();
1082
1.08M
      bool done=true;
1083
1.08M
      if (update(format, fontType))
1084
161k
        ;
1085
919k
      else if (sTitle=="DC" && str.size()>2) // DCxxx=[1iIaA]...
1086
23.2k
      {
1087
23.2k
        size_t p=2;
1088
23.2k
        unsigned int val;
1089
23.2k
        std::string extra;
1090
23.2k
        if (!format.readUInt(format.m_string, p, val, extra) || !format.m_entry.valid() ||
1091
22.0k
                extra.size()<=1 || extra[0]!='=' ||
1092
17.1k
                m_state->m_counterToTypeMap.find(int(val)) != m_state->m_counterToTypeMap.end())
1093
17.8k
          done=false;
1094
5.39k
        else
1095
5.39k
        {
1096
5.39k
          std::map<char, libwps::NumberingType> const cToTypeMap=
1097
5.39k
          {
1098
5.39k
            {'1', libwps::ARABIC}, {'a', libwps::LOWERCASE}, {'A', libwps::UPPERCASE}, {'i', libwps::LOWERCASE_ROMAN}, {'I', libwps::UPPERCASE_ROMAN}
1099
5.39k
          };
1100
5.39k
          auto it=cToTypeMap.find(extra[1]);
1101
5.39k
          if (it==cToTypeMap.end())
1102
4.31k
          {
1103
4.31k
            WPS_DEBUG_MSG(("XYWriteParserInternal::Format::parseTextZone: can not decode counter format in %s\n", extra.c_str()));
1104
4.31k
          }
1105
1.07k
          else
1106
1.07k
            m_state->m_counterToTypeMap[int(val)]=it->second;
1107
5.39k
        }
1108
23.2k
      }
1109
896k
      else if (sTitle=="SS")
1110
43.3k
      {
1111
43.3k
        if (createFormatChildren(format,format.m_string.size()+1) && format.m_string.size()>2)
1112
42.2k
          m_state->m_nameToStyleMap[format.m_string.substr(2)]=format;
1113
1.04k
        else
1114
1.04k
          done=false;
1115
43.3k
      }
1116
853k
      else if (sTitle=="FM" && format.m_string.size()>2 &&
1117
18.6k
               (format.m_string[2]>='1' && format.m_string[2]<='3'))
1118
15.4k
      {
1119
15.4k
        if (createFormatChildren(format,3))
1120
15.1k
        {
1121
15.1k
          std::string name("__");
1122
15.1k
          name+=format.m_string.substr(0,3);
1123
15.1k
          m_state->m_nameToStyleMap[name]=format;
1124
15.1k
        }
1125
306
        else
1126
306
          done=false;
1127
15.4k
      }
1128
837k
      else if (sTitle=="US" && format.m_string.size()>2)
1129
13.2k
      {
1130
13.2k
        auto it=m_state->m_nameToStyleMap.find(format.m_string.substr(2));
1131
13.2k
        if (it==m_state->m_nameToStyleMap.end())
1132
9.76k
        {
1133
9.76k
          WPS_DEBUG_MSG(("XYWriteParser::parseTextZone: can not find style %s\n", format.m_string.c_str()));
1134
9.76k
        }
1135
3.46k
        else
1136
3.46k
        {
1137
3.46k
          if (format.m_string.size()>4)
1138
1.30k
          {
1139
            // unsure, when we need to reset the style
1140
1.30k
            m_listener->setFont(defFont);
1141
1.30k
            m_listener->setParagraph(WPSParagraph());
1142
1.30k
          }
1143
3.46k
          for (auto const &child : it->second.m_children)
1144
24.9k
          {
1145
24.9k
            if (!update(child, fontType))
1146
24.1k
            {
1147
#ifdef DEBUG
1148
              std::cerr << "XYWriteParser::parseTextZone[child]: unused\n";
1149
              std::cerr << "\t" << child << "\n";
1150
#endif
1151
24.1k
            }
1152
24.9k
          }
1153
3.46k
        }
1154
13.2k
      }
1155
824k
      else if (sTitle=="FA")
1156
23.9k
      {
1157
23.9k
        if (createFormatChildren(format))
1158
22.2k
          parseFrameZone(format);
1159
1.65k
        else
1160
1.65k
          done=false;
1161
23.9k
      }
1162
800k
      else if (sTitle=="IG")
1163
84.2k
      {
1164
84.2k
        if (createFormatChildren(format,format.m_string.size()+1))
1165
83.6k
          parsePictureZone(format);
1166
602
        else
1167
602
          done=false;
1168
84.2k
      }
1169
716k
      else if (sTitle=="NT" && format.m_string.size()>2 && format.m_entry.valid())
1170
22.6k
      {
1171
22.6k
        WPSEntry fEntry=format.m_entry;
1172
22.6k
        long fEnd=fEntry.end();
1173
22.6k
        fEntry.setBegin(fEntry.begin()+2);
1174
22.6k
        fEntry.setEnd(fEnd);
1175
22.6k
        WPSSubDocumentPtr subdoc(new XYWriteParserInternal::SubDocument
1176
22.6k
                                 (getInput(), *this,fEntry));
1177
22.6k
        m_listener->insertComment(subdoc);
1178
22.6k
      }
1179
693k
      else if (sTitle=="FN")
1180
20.4k
      {
1181
20.4k
        bool hasId=format.m_string.size()>2 &&
1182
16.0k
                   (format.m_string[2]>='1' && format.m_string[2]<='3');
1183
20.4k
        WPSEntry fEntry=format.m_entry;
1184
20.4k
        long fEnd=fEntry.end();
1185
20.4k
        fEntry.setBegin(fEntry.begin()+(hasId ? 3 : 2));
1186
20.4k
        fEntry.setEnd(fEnd);
1187
20.4k
        std::string sName("__FM");
1188
20.4k
        sName+=format.m_string[2];
1189
20.4k
        WPSSubDocumentPtr subdoc(new XYWriteParserInternal::SubDocument
1190
20.4k
                                 (getInput(), *this, fEntry, hasId ? sName : ""));
1191
20.4k
        m_listener->insertNote(WPSContentListener::FOOTNOTE, subdoc);
1192
20.4k
      }
1193
673k
      else if (str=="PG")
1194
221k
        m_listener->insertBreak(WPS_PAGE_BREAK);
1195
451k
      else if (str=="PN")
1196
1.84k
        m_listener->insertField(WPSField(WPSField::PageNumber));
1197
450k
      else if (str=="DA" || str=="TM") // TM date custom format ?
1198
865
        m_listener->insertField(WPSField(WPSField::Date));
1199
449k
      else if (str=="TI")
1200
739
        m_listener->insertField(WPSField(WPSField::Time));
1201
448k
      else if (sTitle=="CT")
1202
16.8k
      {
1203
16.8k
        long actPos=input->tell();
1204
16.8k
        if (!createTable(format, entry.end()))
1205
11.1k
          input->seek(actPos, librevenge::RVNG_SEEK_SET);
1206
16.8k
      }
1207
431k
      else if ((sTitle=="RH" || sTitle=="RF") && // header/footer already parsed
1208
64.8k
               (str.size()==2 ||
1209
56.3k
                (str.size()>=3 && (str[2]=='A'||str[2]=='E'||str[2]=='O'))))
1210
26.3k
        ;
1211
405k
      else if (sTitle=="PW" || sTitle=="FD" || sTitle=="PB" || sTitle=="OF" || sTitle=="TP" || sTitle=="BT" || // page dimension
1212
383k
               format.shortTitle(3)=="UBN" || sTitle=="GU" || sTitle=="EE" || sTitle=="ET" || // related to table (checkme)
1213
371k
               sTitle=="SY" || // other method to define a font
1214
371k
               sTitle=="NF") // footnote number
1215
33.4k
        ;
1216
371k
      else if (sTitle=="LB" || sTitle=="RE")
1217
1.67k
      {
1218
        // LBname: label
1219
        // RE[PCF]name: ref to page, chapter, reference
1220
1.67k
        static bool first=true;
1221
1.67k
        if (first)
1222
1.67k
        {
1223
1.67k
          WPS_DEBUG_MSG(("XYWriteParser::parseTextZone: retrieving label/cross ref is not implemented\n"));
1224
1.67k
        }
1225
1.67k
      }
1226
370k
      else if (format.shortTitle(1)=="C")
1227
50.1k
      {
1228
50.1k
        unsigned int value;
1229
50.1k
        size_t p=1;
1230
50.1k
        std::string tmp;
1231
50.1k
        if (!format.readUInt(str, p, value, tmp))
1232
42.3k
          done=false;
1233
7.87k
        else
1234
7.87k
        {
1235
7.87k
          int actValue=0;
1236
7.87k
          auto it=m_state->m_counterToValueMap.find(int(value));
1237
7.87k
          if (it!=m_state->m_counterToValueMap.end())
1238
6.81k
            actValue=it->second;
1239
7.87k
          ++actValue;
1240
7.87k
          std::stringstream s;
1241
7.87k
          s << actValue;
1242
7.87k
          for (auto res : s.str())
1243
13.4k
            m_listener->insertCharacter(uint8_t(res));
1244
7.87k
          m_state->m_counterToValueMap[int(value)]=actValue;
1245
7.87k
        }
1246
50.1k
      }
1247
319k
      else
1248
319k
        done=false;
1249
1.08M
      if (!done)
1250
383k
      {
1251
#ifdef DEBUG
1252
        std::cerr << "XYWriteParser::parseTextZone: unused\n";
1253
        std::cerr << "\t" << format << "\n";
1254
#endif
1255
383k
      }
1256
1.08M
      continue;
1257
1.08M
    }
1258
28.8M
    if (c==0xff && input->tell()+2<entry.end())
1259
1.25M
    {
1260
      // special case a char in binary...
1261
1.25M
      long pos=input->tell();
1262
1.25M
      c=0;
1263
1.25M
      bool ok=true;
1264
1.36M
      for (int i=0; i<2; ++i)
1265
1.32M
      {
1266
1.32M
        auto ch=libwps::readU8(input);
1267
1.32M
        if (ch>='A' && ch<='F')
1268
52.2k
          c=(unsigned char)(c*16+(ch-'A'+10));
1269
1.26M
        else if (ch>='0' && ch<='9')
1270
62.1k
          c=(unsigned char)(c*16+(ch-'0'));
1271
1.20M
        else
1272
1.20M
        {
1273
1.20M
          ok=false;
1274
1.20M
          break;
1275
1.20M
        }
1276
1.32M
      }
1277
1.25M
      if (!ok)
1278
1.20M
      {
1279
1.20M
        input->seek(pos, librevenge::RVNG_SEEK_SET);
1280
1.20M
        WPS_DEBUG_MSG(("XYWriteParser::parseTextZone: find bad char FF in pos=%lx\n", (unsigned long)(input->tell())));
1281
1.20M
        continue;
1282
1.20M
      }
1283
1.25M
    }
1284
27.6M
    switch (c)
1285
27.6M
    {
1286
35.4k
    case 0x9:
1287
35.4k
      m_listener->insertTab();
1288
35.4k
      break;
1289
56.9k
    case 0xa:
1290
56.9k
      break;
1291
240k
    case 0xd:
1292
240k
    {
1293
      // we must reset the list level to 0
1294
240k
      if (m_listener->getCurrentList())
1295
20.9k
      {
1296
20.9k
        auto paragraph=m_listener->getParagraph();
1297
20.9k
        if (paragraph.m_listLevelIndex>0)
1298
7.46k
        {
1299
7.46k
          paragraph.m_margins[1]=paragraph.m_listLevel.m_labelIndent;
1300
7.46k
          paragraph.m_listLevelIndex=0;
1301
7.46k
          m_listener->setParagraph(paragraph);
1302
7.46k
        }
1303
20.9k
      }
1304
240k
      m_listener->insertEOL();
1305
240k
      break;
1306
0
    }
1307
27.3M
    default:
1308
27.3M
      if (c<0x1f || ((c==0xaf || c==0xfa) && input->tell() != entry.begin()+1 && input->tell() != entry.end()))
1309
3.03M
      {
1310
3.03M
        WPS_DEBUG_MSG(("XYWriteParser::parseTextZone: find bad char %x in pos=%lx\n", unsigned(c), (unsigned long)(input->tell())));
1311
3.03M
      }
1312
24.3M
      else
1313
24.3M
        m_listener->insertUnicode(uint32_t(libwps_tools_win::Font::unicode(c, fontType)));
1314
27.6M
    }
1315
27.6M
  }
1316
268k
  return true;
1317
273k
}
1318
1319
bool XYWriteParser::parseFrameZone(XYWriteParserInternal::Format const &frameFormat)
1320
22.2k
{
1321
22.2k
  RVNGInputStreamPtr input = getInput();
1322
22.2k
  if (!input || !m_listener ||!frameFormat.m_entry.valid())
1323
0
    throw (libwps::ParseException());
1324
22.2k
  Vec2f dim;
1325
22.2k
  WPSEntry textEntry;
1326
22.2k
  for (auto const &child : frameFormat.m_children)
1327
66.1k
  {
1328
66.1k
    std::string const sTitle=child.shortTitle();
1329
66.1k
    bool done=false;
1330
66.1k
    if (sTitle=="SI")
1331
12.9k
    {
1332
12.9k
      std::string tmp;
1333
12.9k
      done=child.readVec2f(child.m_string, 2, m_state->m_isDosFile, dim, tmp);
1334
12.9k
    }
1335
53.2k
    else if (sTitle=="LB") // label are not implement
1336
317
      done=true;
1337
52.9k
    else if (sTitle=="PO")   // find POTMxPC;data
1338
10.5k
    {
1339
10.5k
      textEntry=child.m_entry;
1340
10.5k
      done=true;
1341
10.5k
    }
1342
66.1k
    if (!done)
1343
45.5k
    {
1344
#ifdef DEBUG
1345
      std::cerr << "XYWriteParser::parseFrameZone: unused\n";
1346
      std::cerr << "\t" << child << "\n";
1347
#endif
1348
45.5k
    }
1349
66.1k
  }
1350
22.2k
  if (dim[0]<=0 || dim[1]<=0 || !textEntry.valid())
1351
13.7k
  {
1352
13.7k
    WPS_DEBUG_MSG(("XYWriteParser::parseFrameZone: can not find frame data\n"));
1353
13.7k
    return false;
1354
13.7k
  }
1355
8.54k
  long begPos=input->tell();
1356
1357
8.54k
  long endPos=textEntry.end();
1358
8.54k
  input->seek(textEntry.begin(), librevenge::RVNG_SEEK_SET);
1359
602k
  while (!input->isEnd() && input->tell()<endPos)
1360
601k
  {
1361
601k
    if (libwps::readU8(input)==';')
1362
7.33k
      break;
1363
601k
  }
1364
8.54k
  textEntry.setBegin(input->tell());
1365
8.54k
  textEntry.setEnd(endPos);
1366
8.54k
  WPSPosition fPos(Vec2f(), dim, librevenge::RVNG_POINT);
1367
8.54k
  fPos.setRelativePosition(WPSPosition::Char);
1368
8.54k
  WPSSubDocumentPtr subdoc(new XYWriteParserInternal::SubDocument
1369
8.54k
                           (getInput(), *this, textEntry));
1370
8.54k
  m_listener->insertTextBox(fPos, subdoc);
1371
8.54k
  input->seek(begPos, librevenge::RVNG_SEEK_SET);
1372
8.54k
  return true;
1373
22.2k
}
1374
1375
bool XYWriteParser::parsePictureZone(XYWriteParserInternal::Format const &pictureFormat)
1376
83.6k
{
1377
83.6k
  RVNGInputStreamPtr input = getInput();
1378
83.6k
  if (!input || !m_listener || !pictureFormat.m_entry.valid())
1379
0
    throw (libwps::ParseException());
1380
83.6k
  WPSBox2f box;
1381
83.6k
  Vec2i scaleArray(100,100);
1382
83.6k
  for (auto const &child : pictureFormat.m_children)
1383
293k
  {
1384
293k
    std::string const sTitle=child.shortTitle();
1385
293k
    bool done=false;
1386
293k
    if (sTitle=="CR")
1387
105k
    {
1388
105k
      std::string tmp;
1389
105k
      done=child.readBox2f(child.m_string, 2, m_state->m_isDosFile, box, tmp);
1390
105k
    }
1391
188k
    else if (sTitle=="TY" || child.m_string=="IML" || sTitle=="RV") // type, unknown, revision?
1392
1.16k
      done=true;
1393
187k
    else if (sTitle=="SC")
1394
3.93k
    {
1395
3.93k
      std::string tmp;
1396
3.93k
      done=child.readVec2i(child.m_string, 2, scaleArray, tmp);
1397
3.93k
    }
1398
293k
    if (!done)
1399
228k
    {
1400
#ifdef DEBUG
1401
      std::cerr << "XYWriteParser::parsePictureZone: unused\n";
1402
      std::cerr << "\t" << child << "\n";
1403
#endif
1404
228k
    }
1405
293k
  }
1406
83.6k
  Vec2f dim(static_cast<float>(scaleArray[0])/100.f*box.size()[0], float(scaleArray[1])/100.f*box.size()[1]);
1407
83.6k
  if (dim[0]<=0 || dim[1]<=0)
1408
21.7k
  {
1409
21.7k
    WPS_DEBUG_MSG(("XYWriteParser::parsePictureZone: can not find picture dimension\n"));
1410
21.7k
    return false;
1411
21.7k
  }
1412
1413
61.8k
  long begPos=input->tell();
1414
61.8k
  long endPos=pictureFormat.m_entry.end();
1415
61.8k
  input->seek(pictureFormat.m_entry.begin()+2, librevenge::RVNG_SEEK_SET);
1416
241M
  while (!input->isEnd() && input->tell()<endPos)
1417
241M
  {
1418
241M
    if (libwps::readU8(input)==',')
1419
49.0k
      break;
1420
241M
  }
1421
61.8k
  WPSEntry textEntry;
1422
61.8k
  textEntry.setBegin(pictureFormat.m_entry.begin()+2);
1423
61.8k
  textEntry.setEnd(input->tell()-1);
1424
61.8k
  WPSPosition fPos(Vec2f(), dim, librevenge::RVNG_POINT);
1425
61.8k
  fPos.setRelativePosition(WPSPosition::Char);
1426
61.8k
  WPSSubDocumentPtr subdoc(new XYWriteParserInternal::SubDocument
1427
61.8k
                           (getInput(), *this, textEntry));
1428
61.8k
  m_listener->insertTextBox(fPos, subdoc);
1429
61.8k
  input->seek(begPos, librevenge::RVNG_SEEK_SET);
1430
61.8k
  return true;
1431
83.6k
}
1432
1433
bool XYWriteParser::createTable(XYWriteParserInternal::Format const &tableFormat, long endPos)
1434
16.8k
{
1435
16.8k
  RVNGInputStreamPtr input = getInput();
1436
16.8k
  if (!input || !m_listener || tableFormat.shortTitle()!="CT")
1437
0
    throw (libwps::ParseException());
1438
16.8k
  long begPos=input->tell();
1439
16.8k
  if (begPos>=endPos)
1440
680
  {
1441
680
    WPS_DEBUG_MSG(("XYWriteParser::createTable: the zone seems too short\n"));
1442
680
    return false;
1443
680
  }
1444
16.1k
  std::vector<float> colWidth;
1445
16.1k
  std::vector<std::string> colStyle;
1446
833k
  for (size_t j=0; j<=tableFormat.m_args.size(); ++j)
1447
817k
  {
1448
817k
    size_t p=j==0 ? 2 : 0;
1449
817k
    double value;
1450
817k
    bool inPoint;
1451
817k
    std::string tmp;
1452
817k
    if (tableFormat.readUnit(j==0 ? tableFormat.m_string : tableFormat.m_args[j-1], p, m_state->m_isDosFile, value, inPoint, tmp) && inPoint)
1453
41.8k
    {
1454
41.8k
      if (j==0) // left pos
1455
3.66k
        ;
1456
38.2k
      else
1457
38.2k
      {
1458
38.2k
        colWidth.push_back(float(value));
1459
38.2k
        if (tmp.size()>1)
1460
5.79k
          colStyle.push_back(tmp.substr(1));
1461
32.4k
        else
1462
32.4k
          colStyle.push_back("");
1463
38.2k
      }
1464
41.8k
    }
1465
775k
    else
1466
775k
    {
1467
775k
      WPS_DEBUG_MSG(("XYWriteParser::createTable: can not read some colum size %s\n", j==0 ? tableFormat.m_string.c_str() : tableFormat.m_args[j-1].c_str()));
1468
775k
      if (j)
1469
763k
      {
1470
763k
        colWidth.push_back(0);
1471
763k
        colStyle.push_back("");
1472
763k
      }
1473
775k
    }
1474
817k
  }
1475
16.1k
  if (colWidth.empty())
1476
2.20k
  {
1477
2.20k
    WPS_DEBUG_MSG(("XYWriteParser::createTable: can not find any columns\n"));
1478
2.20k
    return false;
1479
2.20k
  }
1480
13.9k
  int numColumns=int(colWidth.size());
1481
1482
13.9k
  std::vector<XYWriteParserInternal::Cell> cells;
1483
13.9k
  int cRow=0, cCol=0;
1484
13.9k
  cells.push_back(XYWriteParserInternal::Cell(*this));
1485
13.9k
  cells.back().m_entry.setBegin(input->tell());
1486
13.9k
  cells.back().setPosition(Vec2i(cCol,cRow));
1487
1488
13.9k
  bool ok=false;
1489
3.32M
  while (!input->isEnd())
1490
3.32M
  {
1491
3.32M
    long pos=input->tell();
1492
3.32M
    if (pos>=endPos)
1493
5.10k
      break;
1494
3.32M
    uint8_t c=libwps::readU8(input);
1495
3.32M
    if (c==0x1a)
1496
845
    {
1497
845
      if (input->tell()<endPos)
1498
768
      {
1499
768
        WPS_DEBUG_MSG(("XYWriteParser::createTable: find unexpected end zone\n"));
1500
768
      }
1501
845
      break;
1502
845
    }
1503
3.32M
    if (c!=0xae)
1504
3.05M
      continue;
1505
269k
    XYWriteParserInternal::Format format;
1506
269k
    if (!parseFormat(format))
1507
1.11k
      throw (libwps::ParseException());
1508
268k
    std::string const str=format.title();
1509
268k
    std::string const sTitle=format.shortTitle();
1510
268k
    if (str=="EC")
1511
4.58k
    {
1512
4.58k
      ok=true;
1513
4.58k
      break;
1514
4.58k
    }
1515
263k
    if (sTitle=="CT")
1516
1.23k
      break;
1517
262k
    else if (sTitle=="CO")
1518
114k
    {
1519
114k
      unsigned int value;
1520
114k
      size_t p=2;
1521
114k
      std::string tmp;
1522
114k
      if (!format.readUInt(format.m_string, p, value, tmp))
1523
1.95k
        continue;
1524
112k
      if (numColumns==0 || cells.empty() || value==0) // the table is not created
1525
570
        break;
1526
112k
      cells.back().m_entry.setEnd(pos);
1527
112k
      int newCol=int(value)-1;
1528
112k
      if (newCol>numColumns)
1529
535
        break;
1530
111k
      if (newCol<=cCol)
1531
105k
        ++cRow;
1532
111k
      cCol=newCol;
1533
111k
      cells.push_back(XYWriteParserInternal::Cell(*this));
1534
111k
      cells.back().m_entry.setBegin(input->tell());
1535
111k
      cells.back().setPosition(Vec2i(cCol,cRow));
1536
111k
      if (cCol<int(colStyle.size()))
1537
105k
        cells.back().m_style=colStyle[size_t(cCol)];
1538
111k
    }
1539
263k
  }
1540
12.8k
  if (ok)
1541
4.58k
  {
1542
4.58k
    m_listener->openTable(colWidth, librevenge::RVNG_POINT);
1543
4.58k
    cRow=-1;
1544
4.58k
    WPSListenerPtr listener(m_listener);
1545
4.58k
    for (auto &cell : cells)
1546
107k
    {
1547
107k
      auto const cPos=cell.position();
1548
210k
      while (cRow<cPos[1])
1549
103k
      {
1550
103k
        if (cRow!=-1)
1551
98.5k
          m_listener->closeTableRow();
1552
103k
        m_listener->openTableRow(-10, librevenge::RVNG_POINT);
1553
103k
        ++cRow;
1554
103k
        cCol=0;
1555
103k
      }
1556
107k
      if (cCol<cPos[0])
1557
68.1k
        m_listener->addEmptyTableCell(Vec2i(cCol,cRow),Vec2i(cPos[0]-cCol,1));
1558
107k
      m_listener->openTableCell(cell);
1559
107k
      cell.sendContent(listener);
1560
107k
      m_listener->closeTableCell();
1561
107k
      cCol=cPos[0]+1;
1562
107k
    }
1563
4.58k
    if (cRow!=-1)
1564
4.13k
      m_listener->closeTableRow();
1565
4.58k
    m_listener->closeTable();
1566
4.58k
  }
1567
8.29k
  else
1568
8.29k
    input->seek(begPos, librevenge::RVNG_SEEK_SET);
1569
12.8k
  return ok;
1570
13.9k
}
1571
1572
bool XYWriteParser::parseMetaData(WPSEntry const &entry)
1573
199
{
1574
199
  RVNGInputStreamPtr input = getInput();
1575
199
  if (!input)
1576
0
    throw (libwps::ParseException());
1577
199
  if (!entry.valid())
1578
0
    return true;
1579
199
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1580
199
  XYWriteParserInternal::Format format;
1581
199
  std::string actualString;
1582
199
  auto fontType=m_state->getFontType();
1583
4.06M
  while (!input->isEnd() && input->tell()+1<entry.end())
1584
4.06M
  {
1585
4.06M
    uint8_t c=libwps::readU8(input);
1586
4.06M
    if (c==0x1a)
1587
0
    {
1588
0
      WPS_DEBUG_MSG(("XYWriteParser::parseMetaData: find end of zone\n"));
1589
0
      return false;
1590
0
    }
1591
4.06M
    if (c!=0xae)
1592
4.05M
    {
1593
4.05M
      actualString+=char(c);
1594
4.05M
      continue;
1595
4.05M
    }
1596
10.7k
    const auto strEnd = actualString.find_last_not_of(" ");
1597
10.7k
    actualString=actualString.substr(0, strEnd+1);
1598
10.7k
    if (!actualString.empty())
1599
8.43k
    {
1600
      // also LBLG:40 another author
1601
      // find also LBCD:20, LBCT:15, LBMD:20, LBMT:15 with some checksum?
1602
      //      and LBRP:4, LBPJ:20, LBCM:44, LBKY:~250 empty
1603
8.43k
      auto const finalStr=libwps_tools_win::Font::unicodeString(actualString, fontType);
1604
8.43k
      std::string const str=format.title();
1605
8.43k
      if (str=="LBAU") // sz:40
1606
318
        m_state->m_metaData.insert("dc:creator", finalStr);
1607
8.11k
      else if (str=="LBRV") // revision sz:4
1608
2.54k
        m_state->m_metaData.insert("librevenge:version-number", finalStr);
1609
8.43k
    }
1610
10.7k
    actualString.clear();
1611
10.7k
    if (!parseFormat(format))
1612
79
      return false;
1613
10.7k
  }
1614
120
  return true;
1615
199
}
1616
1617
////////////////////////////////////////////////////////////
1618
// low level
1619
////////////////////////////////////////////////////////////
1620
1621
bool XYWriteParser::parseFormat(XYWriteParserInternal::Format &format)
1622
2.01M
{
1623
2.01M
  RVNGInputStreamPtr input = getInput();
1624
2.01M
  if (!input)
1625
0
    throw (libwps::ParseException());
1626
2.01M
  format=XYWriteParserInternal::Format(m_state->m_isDosFile);
1627
2.01M
  format.m_entry.setBegin(input->tell());
1628
289M
  while (!input->isEnd())
1629
289M
  {
1630
289M
    if (input->tell()>=m_state->m_eof)
1631
118
    {
1632
118
      WPS_DEBUG_MSG(("XYWriteParser::parseFormat: can not find end of format\n"));
1633
118
      return false;
1634
118
    }
1635
289M
    uint8_t c=libwps::readU8(input);
1636
289M
    if (c==0xaf)
1637
1.73M
    {
1638
1.73M
      format.m_entry.setEnd(input->tell()-1);
1639
1.73M
      return true;
1640
1.73M
    }
1641
287M
    if (c==0xfa || c==0xae)
1642
270k
    {
1643
270k
      format.m_isComplex=true;
1644
      // normally ae XXX fa ... af
1645
      // but sometimes I found ae XXX ae ... af fa ... af
1646
270k
      if (format.m_entry.valid())
1647
0
      {
1648
0
        WPS_DEBUG_MSG(("XYWriteParser::parseFormat: oops an entry is already defined\n"));
1649
0
      }
1650
270k
      int depth=c==0xae ? 1 : 0;
1651
573M
      while (!input->isEnd())
1652
573M
      {
1653
573M
        if (input->tell()>=m_state->m_eof)
1654
54
        {
1655
54
          WPS_DEBUG_MSG(("XYWriteParser::parseFormat: can not find end of entry\n"));
1656
54
          return false;
1657
54
        }
1658
573M
        c=libwps::readU8(input);
1659
573M
        if (c==0xae)
1660
21.0M
          ++depth;
1661
552M
        else if (c==0xaf)
1662
21.5M
        {
1663
21.5M
          if (depth==0)
1664
269k
          {
1665
269k
            format.m_entry.setEnd(input->tell()-1);
1666
269k
            return true;
1667
269k
          }
1668
21.2M
          --depth;
1669
21.2M
        }
1670
573M
      }
1671
270k
    }
1672
287M
    if (c==0x1a)
1673
8.35k
    {
1674
8.35k
      WPS_DEBUG_MSG(("XYWriteParser::parseFormat: find end of zone\n"));
1675
8.35k
      return false;
1676
8.35k
    }
1677
287M
    if (c==',')
1678
13.9M
      format.m_args.push_back(std::string());
1679
273M
    else if (format.m_args.empty())
1680
237M
      format.m_string+=char(c);
1681
35.6M
    else
1682
35.6M
      format.m_args.back()+=char(c);
1683
287M
  }
1684
32
  WPS_DEBUG_MSG(("XYWriteParser::parseFormat: find end of file\n"));
1685
32
  return false;
1686
2.01M
}
1687
1688
bool XYWriteParser::createFormatChildren(XYWriteParserInternal::Format &format, size_t fPos)
1689
166k
{
1690
166k
  RVNGInputStreamPtr input = getInput();
1691
166k
  if (!input || !format.m_entry.valid())
1692
0
    throw (libwps::ParseException());
1693
166k
  long endPos=format.m_entry.end();
1694
166k
  if (endPos>m_state->m_eof)
1695
0
  {
1696
0
    WPS_DEBUG_MSG(("XYWriteParser::createFormatChildren: end entry seems bad\n"));
1697
0
    return false;
1698
0
  }
1699
166k
  long prevPos=input->tell();
1700
166k
  input->seek(format.m_entry.begin(), librevenge::RVNG_SEEK_SET);
1701
  // skip header
1702
46.3M
  for (size_t p=0; p<fPos; ++p)
1703
46.2M
  {
1704
46.2M
    if (input->isEnd() || input->tell()>=endPos)
1705
25.0k
      break;
1706
46.2M
    if (libwps::readU8(input)==',')
1707
63.1k
      break;
1708
46.2M
  }
1709
166k
  if (input->tell()>=endPos)
1710
26.5k
  {
1711
26.5k
    input->seek(prevPos, librevenge::RVNG_SEEK_SET);
1712
26.5k
    return true;
1713
26.5k
  }
1714
140k
  std::string actString;
1715
140k
  long begPos=input->tell();
1716
140k
  bool isComplex=false;
1717
23.2M
  while (!input->isEnd() && input->tell()<=endPos)
1718
23.2M
  {
1719
23.2M
    uint8_t c=input->tell()==endPos ? ',' : libwps::readU8(input);
1720
23.2M
    auto *child=format.m_children.empty() ? nullptr : &format.m_children.back();
1721
23.2M
    if (c==0xfa || c==0xae)
1722
79.7k
    {
1723
79.7k
      isComplex=true;
1724
      // normally ae XXX fa ... af
1725
      // but sometimes I found ae XXX ae ... af fa ... af
1726
79.7k
      int depth=c==0xae ? 1 : 0;
1727
460M
      while (!input->isEnd() && input->tell()<endPos)
1728
460M
      {
1729
460M
        c=libwps::readU8(input);
1730
460M
        if (c==0xae)
1731
20.3M
          ++depth;
1732
440M
        else if (c==',' && depth==0)
1733
9.49k
          break;
1734
440M
        else if (c==0xaf)
1735
20.4M
        {
1736
20.4M
          if (depth==0)
1737
17.4k
            break;
1738
20.4M
          --depth;
1739
20.4M
        }
1740
460M
      }
1741
79.7k
      if (c!=',')
1742
70.2k
        continue;
1743
79.7k
    }
1744
23.1M
    if (c==0x1a)
1745
2.91k
    {
1746
2.91k
      WPS_DEBUG_MSG(("XYWriteParser::createFormatChildren: find end of zone\n"));
1747
2.91k
      input->seek(prevPos, librevenge::RVNG_SEEK_SET);
1748
2.91k
      return false;
1749
2.91k
    }
1750
23.1M
    if (c==',')
1751
5.47M
    {
1752
5.47M
      if (!actString.empty())
1753
3.51M
      {
1754
3.51M
        if (actString[0]=='.' || (actString[0]>='0' && actString[0]<='9'))
1755
673k
        {
1756
673k
          if (child)
1757
614k
          {
1758
614k
            child->m_isComplex=isComplex;
1759
614k
            child->m_args.push_back(actString);
1760
614k
          }
1761
673k
        }
1762
2.84M
        else
1763
2.84M
        {
1764
2.84M
          if (child)
1765
2.71M
            child->m_entry.setEnd(begPos-1);
1766
2.84M
          format.m_children.push_back(XYWriteParserInternal::Format());
1767
2.84M
          format.m_children.back().m_entry.setBegin(begPos);
1768
2.84M
          format.m_children.back().m_inDosFile = m_state->m_isDosFile;
1769
2.84M
          format.m_children.back().m_string=actString;
1770
2.84M
          format.m_children.back().m_isComplex=isComplex;
1771
2.84M
          isComplex=false;
1772
2.84M
        }
1773
3.51M
      }
1774
5.47M
      actString.clear();
1775
5.47M
      begPos=input->tell();
1776
5.47M
      if (!format.m_children.empty())
1777
5.28M
        format.m_children.back().m_entry.setEnd(begPos==endPos ? endPos : begPos-1);
1778
5.47M
      if (begPos==endPos)
1779
137k
        break;
1780
5.33M
      continue;
1781
5.47M
    }
1782
17.6M
    if (c!='=')
1783
17.6M
      actString+=char(c);
1784
17.6M
  }
1785
137k
  input->seek(prevPos, librevenge::RVNG_SEEK_SET);
1786
137k
  return true;
1787
140k
}
1788
1789
// basic function to check if the header is ok
1790
bool XYWriteParser::checkHeader(WPSHeader *header, bool /*strict*/)
1791
4.47k
{
1792
4.47k
  RVNGInputStreamPtr input = getInput();
1793
4.47k
  if (!input || !checkFilePosition(10)) // too small for containing any usefull format
1794
0
  {
1795
0
    WPS_DEBUG_MSG(("XYWriteParser::checkHeader: file is too short\n"));
1796
0
    return false;
1797
0
  }
1798
1799
  /* check sequence 0xae ...[,*] 0xaf */
1800
4.47k
  input->seek(0x0, librevenge::RVNG_SEEK_SET);
1801
4.47k
  bool ok=false, inFormat=false;
1802
4.47k
  int depth=0;
1803
4.47k
  int numBadChar=0, numCurrentChar=0;
1804
6.73M
  while (!input->isEnd())
1805
6.73M
  {
1806
6.73M
    if (numBadChar>10)
1807
18
      break;
1808
6.73M
    uint8_t c=libwps::readU8(input);
1809
6.73M
    if (c<=0x1f && c!=0x9 && c!=0xa && c!=0xd && c!=0x1b)
1810
13.9k
      ++numBadChar;
1811
6.71M
    else if (!depth)
1812
893k
    {
1813
893k
      if (c==0xae)
1814
4.45k
      {
1815
4.45k
        inFormat=true;
1816
4.45k
        depth=1;
1817
4.45k
      }
1818
889k
      else if (c==0xaf) // end before begin
1819
0
        break;
1820
893k
    }
1821
5.82M
    else
1822
5.82M
    {
1823
5.82M
      if (c==0xaf)
1824
13.2k
      {
1825
13.2k
        if (--depth==0)   // find the end of a sequence, ok
1826
4.44k
        {
1827
4.44k
          ok=true;
1828
4.44k
          break;
1829
4.44k
        }
1830
8.81k
        inFormat=false;
1831
8.81k
      }
1832
5.80M
      else if (c==0xae)
1833
457k
      {
1834
457k
        ++depth;
1835
457k
        inFormat=true;
1836
457k
      }
1837
5.35M
      else if (c==',')
1838
2.31M
        numCurrentChar=0;
1839
3.03M
      else if (c==0xfa)
1840
593
        inFormat=false;
1841
3.03M
      else if (inFormat)
1842
2.54M
      {
1843
2.54M
        if (++numCurrentChar>256)
1844
1
          break;
1845
2.54M
      }
1846
5.82M
    }
1847
6.73M
  }
1848
4.47k
  if (!ok)
1849
28
  {
1850
28
    WPS_DEBUG_MSG(("XYWriteParser::checkHeader: can not find any sequence\n"));
1851
28
    return false;
1852
28
  }
1853
4.44k
  input->seek(-1, librevenge::RVNG_SEEK_END);
1854
4.44k
  int val=libwps::readU8(input);
1855
4.44k
  if (val==0x1a)
1856
4.21k
    m_state->m_isDosFile=true;
1857
228
  else if (val)
1858
0
  {
1859
0
    WPS_DEBUG_MSG(("XYWriteParser::checkHeader: oops unexpected last character\n"));
1860
0
    return false;
1861
0
  }
1862
4.44k
  if (header)
1863
0
    header->setMajorVersion(m_state->m_isDosFile ? 0 : 1);
1864
4.44k
  return true;
1865
4.44k
}
1866
1867
// read the document structure ...
1868
1869
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */