Coverage Report

Created: 2026-10-28 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/MSWrite.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) 2015 Sean Young <sean@mess.org>
11
 *
12
 * For minor contributions see the git repository.
13
 *
14
 * Alternatively, the contents of this file may be used under the terms
15
 * of the GNU Lesser General Public License Version 2.1 or later
16
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
17
 * applicable instead of those above.
18
 *
19
 * For further information visit http://libwps.sourceforge.net
20
 */
21
22
#include <librevenge-stream/librevenge-stream.h>
23
#include "libwps_internal.h"
24
25
#include "WPSContentListener.h"
26
#include "WPSEntry.h"
27
#include "WPSPageSpan.h"
28
#include "WPSParagraph.h"
29
#include "WPSPosition.h"
30
#include "WPSFont.h"
31
#include "WPSTextSubDocument.h"
32
33
#include "MSWrite.h"
34
35
#include <algorithm>
36
#include <cstring>
37
38
namespace MSWriteParserInternal
39
{
40
Font::~Font()
41
64.3k
{
42
64.3k
}
43
44
Paragraph::~Paragraph()
45
84.5k
{
46
84.5k
}
47
//! Internal: the subdocument of a MSWriteParser
48
class SubDocument final : public WPSTextSubDocument
49
{
50
public:
51
  //! constructor for a text entry
52
  SubDocument(RVNGInputStreamPtr const &input, MSWriteParser &pars, WPSEntry const &entry, Paragraph::Location location)
53
5.88k
    : WPSTextSubDocument(input, &pars)
54
5.88k
    , m_entry(entry)
55
5.88k
    , m_Location(location) {}
56
  //! destructor
57
5.88k
  ~SubDocument() final {}
58
59
  //! operator==
60
  bool operator==(std::shared_ptr<WPSSubDocument> const &doc) const final
61
0
  {
62
0
    if (!doc || !WPSTextSubDocument::operator==(doc))
63
0
      return false;
64
0
    auto const *sDoc = dynamic_cast<SubDocument const *>(doc.get());
65
0
    if (!sDoc) return false;
66
0
    return m_entry == sDoc->m_entry;
67
0
  }
68
69
  //! the parser function
70
  void parse(std::shared_ptr<WPSContentListener> &listener, libwps::SubDocumentType subDocumentType) final;
71
  //! the entry
72
  WPSEntry m_entry;
73
  Paragraph::Location m_Location;
74
};
75
76
void SubDocument::parse(std::shared_ptr<WPSContentListener> &listener, libwps::SubDocumentType subDocumentType)
77
1.54k
{
78
1.54k
  if (!listener.get())
79
0
  {
80
0
    WPS_DEBUG_MSG(("MSWriteParserInternal::SubDocument::parse: no listener\n"));
81
0
    return;
82
0
  }
83
1.54k
  if (!dynamic_cast<WPSContentListener *>(listener.get()))
84
0
  {
85
0
    WPS_DEBUG_MSG(("MSWriteParserInternal::SubDocument::parse: bad listener\n"));
86
0
    return;
87
0
  }
88
89
1.54k
  auto &listen = reinterpret_cast<WPSContentListenerPtr &>(listener);
90
1.54k
  if (!m_parser)
91
0
  {
92
0
    listen->insertCharacter(' ');
93
0
    WPS_DEBUG_MSG(("MSWriteParserInternal::SubDocument::parse: bad parser\n"));
94
0
    return;
95
0
  }
96
97
1.54k
  if (m_entry.isParsed() && subDocumentType != libwps::DOC_HEADER_FOOTER)
98
0
  {
99
0
    listen->insertCharacter(' ');
100
0
    WPS_DEBUG_MSG(("MSWriteParserInternal::SubDocument::parse: this zone is already parsed\n"));
101
0
    return;
102
0
  }
103
1.54k
  m_entry.setParsed(true);
104
1.54k
  if (m_entry.type() != "TEXT")
105
0
  {
106
0
    listen->insertCharacter(' ');
107
0
    WPS_DEBUG_MSG(("MSWriteParserInternal::SubDocument::parse: send not Text entry is not implemented\n"));
108
0
    return;
109
0
  }
110
111
1.54k
  if (!m_entry.valid() || !m_input)
112
103
  {
113
103
    WPS_DEBUG_MSG(("MSWriteParserInternal::SubDocument::parse: empty document found...\n"));
114
103
    return;
115
103
  }
116
117
1.44k
  auto *mnParser = dynamic_cast<MSWriteParser *>(m_parser);
118
1.44k
  if (!mnParser)
119
0
  {
120
0
    WPS_DEBUG_MSG(("MSWriteParserInternal::SubDocument::parse: bad parser...\n"));
121
0
    listen->insertCharacter(' ');
122
0
    return;
123
0
  }
124
1.44k
  long actPos=m_input->tell();
125
1.44k
  mnParser->readText(m_entry, m_Location);
126
1.44k
  m_input->seek(actPos, librevenge::RVNG_SEEK_SET);
127
1.44k
}
128
129
struct PAP
130
{
131
  //! constructor
132
  PAP()
133
8.42k
    : m_reserved1(0)
134
8.42k
    , m_justification(0)
135
8.42k
    , m_dxaRight(0)
136
8.42k
    , m_dxaLeft(0)
137
8.42k
    , m_dxaLeft1(0)
138
8.42k
    , m_dyaLine(0)
139
8.42k
    , m_dyaBefore(0)
140
8.42k
    , m_dyaAfter(0)
141
8.42k
    , m_rhcPage(0)
142
8.42k
  {
143
8.42k
    m_reserved2[0]=m_reserved2[1]=0;
144
42.1k
    for (unsigned char &i : m_reserved3) i=0;
145
8.42k
  }
146
  uint8_t m_reserved1;
147
  uint8_t m_justification;
148
  uint8_t m_reserved2[2];
149
  uint16_t m_dxaRight, m_dxaLeft, m_dxaLeft1, m_dyaLine;
150
  uint16_t m_dyaBefore, m_dyaAfter;
151
  uint8_t m_rhcPage;
152
  uint8_t m_reserved3[5];
153
  struct TBD
154
  {
155
    TBD()
156
117k
      : m_dxa(0)
157
117k
      , m_jcTab(0)
158
117k
      , m_chAlign(0)
159
117k
    {
160
117k
    }
161
    uint16_t    m_dxa;
162
    uint8_t     m_jcTab;
163
    uint8_t     m_chAlign;
164
  } m_TBD[14];
165
};
166
167
struct CHP
168
{
169
  CHP()
170
7.44k
    : m_reserved1(0)
171
7.44k
    , m_fBold(0)
172
7.44k
    , m_hps(0)
173
7.44k
    , m_fUline(0)
174
7.44k
    , m_ftcXtra(0)
175
7.44k
    , m_hpsPos(0)
176
7.44k
  {
177
7.44k
  }
178
  uint8_t m_reserved1;
179
  uint8_t m_fBold;
180
  uint8_t m_hps;
181
  uint8_t m_fUline;
182
  uint8_t m_ftcXtra;
183
  uint8_t m_hpsPos;
184
};
185
186
// the file header offsets
187
enum HeaderOffset
188
{
189
  HEADER_W_WIDENT = 0,
190
  HEADER_W_DTY = 2,
191
  HEADER_W_WTOOL = 4,
192
  HEADER_D_FCMAC = 14,
193
  HEADER_W_PNPARA = 18,
194
  HEADER_W_PNFNTB = 20,
195
  HEADER_W_PNSEP = 22,
196
  HEADER_W_PNSETB = 24,
197
  HEADER_W_PNBFTB = 26,
198
  HEADER_W_PNFFNTB = 28,
199
  HEADER_W_PNMAC = 96
200
};
201
202
// pictures in Write 3.0, either DDB of WMF
203
enum PicOffset
204
{
205
  PIC_W_MM = 0,
206
  PIC_W_XEXT = 2,
207
  PIC_W_YEXT = 4,
208
  PIC_W_DXAOFFSET = 8,
209
  PIC_W_DXASIZE = 10,
210
  PIC_W_DYASIZE = 12,
211
  PIC_W_BMWIDTH = 18,
212
  PIC_W_BMHEIGHT = 20,
213
  PIC_W_BMWIDTHBYTES = 22,
214
  PIC_B_BMPLANES = 24,
215
  PIC_B_BMBITSPIXEL = 25,
216
  PIC_D_CBSIZE = 32,
217
  PIC_W_MX = 36,
218
  PIC_W_MY = 38
219
};
220
221
// OLE objects in Write 3.1
222
enum OleOffset
223
{
224
  OLE_W_MM = 0,
225
  OLE_W_OBJECTTYPE = 6,
226
  OLE_W_DXAOFFSET = 8,
227
  OLE_W_DXASIZE = 10,
228
  OLE_W_DYASIZE = 12,
229
  OLE_D_DWDATASIZE = 16,
230
  OLE_W_MX = 36,
231
  OLE_W_MY = 38
232
};
233
234
enum BitmapInfoHeaderV3Offset
235
{
236
  BM_INFO_V3_SIZE = 0,
237
  BM_INFO_V3_WIDTH = 4,
238
  BM_INFO_V3_HEIGHT = 8,
239
  BM_INFO_V3_PLANES = 12,
240
  BM_INFO_V3_BITS_PIXEL = 14,
241
  BM_INFO_V3_COMPRESSION = 16,
242
  BM_INFO_V3_BITMAP_SIZE = 20,
243
  BM_INFO_V3_HORZ_RES = 24,
244
  BM_INFO_V3_VERT_RES = 28,
245
  BM_INFO_V3_COLORS_USED = 32,
246
  BM_INFO_V3_COLORS_IMPORTANT = 36,
247
  BM_INFO_V3_STRUCT_SIZE = 40
248
};
249
250
enum BitmapFileHeaderOffset
251
{
252
  BM_FILE_MAGIC = 0,
253
  BM_FILE_SIZE = 2,
254
  BM_FILE_RESERVED = 6,
255
  BM_FILE_OFFSET = 10,
256
  BM_FILE_STRUCT_SIZE = 14
257
};
258
259
enum BitmapInfoHeaderV2Offset
260
{
261
  BM_INFO_V2_SIZE = 0,
262
  BM_INFO_V2_WIDTH = 4,
263
  BM_INFO_V2_HEIGHT = 6,
264
  BM_INFO_V2_PLANES = 8,
265
  BM_INFO_V2_BITS_PIXEL = 10,
266
  BM_INFO_V2_STRUCT_SIZE = 12
267
};
268
269
struct BitmapPalette
270
{
271
  uint8_t m_r, m_g, m_b;
272
};
273
274
static void appendU16(librevenge::RVNGBinaryData &b, uint16_t val)
275
396
{
276
396
  b.append(static_cast<unsigned char>(val));
277
396
  b.append(static_cast<unsigned char>(val >> 8));
278
396
}
279
280
static void appendU32(librevenge::RVNGBinaryData &b, uint32_t val)
281
396
{
282
396
  b.append(static_cast<unsigned char>(val));
283
396
  b.append(static_cast<unsigned char>(val >> 8));
284
396
  b.append(static_cast<unsigned char>(val >> 16));
285
396
  b.append(static_cast<unsigned char>(val >> 24));
286
396
}
287
288
}
289
290
MSWriteParser::MSWriteParser(RVNGInputStreamPtr &input, WPSHeaderPtr &header,
291
                             libwps_tools_win::Font::Type encoding)
292
5.14k
  : WPSParser(input, header)
293
5.14k
  , m_fileLength(0)
294
5.14k
  , m_fcMac(0)
295
5.14k
  , m_paragraphList(0)
296
5.14k
  , m_fontList(0)
297
5.14k
  , m_footnotes(0)
298
5.14k
  , m_sections(0)
299
5.14k
  , m_fonts(0)
300
5.14k
  , m_fontType(encoding)
301
5.14k
  , m_listener()
302
5.14k
  , m_metaData()
303
5.14k
{
304
5.14k
  if (!input)
305
0
  {
306
0
    WPS_DEBUG_MSG(("MSWriteParser::MSWriteParser: called without input\n"));
307
0
    throw libwps::ParseException();
308
0
  }
309
310
5.14k
  input->seek(0, librevenge::RVNG_SEEK_END);
311
5.14k
  m_fileLength=uint32_t(input->tell());
312
5.14k
  input->seek(0, librevenge::RVNG_SEEK_SET);
313
5.14k
  m_fontType = getFileEncoding(encoding);
314
5.14k
}
315
316
MSWriteParser::~MSWriteParser()
317
5.14k
{
318
5.14k
}
319
320
libwps_tools_win::Font::Type MSWriteParser::getFileEncoding(libwps_tools_win::Font::Type encoding)
321
5.14k
{
322
5.14k
  if (encoding == libwps_tools_win::Font::UNKNOWN)
323
5.14k
    encoding = libwps_tools_win::Font::WIN3_WEUROPE;
324
325
5.14k
  return encoding;
326
5.14k
}
327
328
void MSWriteParser::readFIB()
329
5.14k
{
330
5.14k
  RVNGInputStreamPtr input = getInput();
331
332
5.14k
  if (!checkFilePosition(0x100))
333
197
  {
334
197
    WPS_DEBUG_MSG(("MSWriteParser::readFIB File is too short to be a MSWrite file\n"));
335
197
    throw (libwps::ParseException());
336
197
  }
337
338
4.94k
  input->seek(MSWriteParserInternal::HEADER_D_FCMAC, librevenge::RVNG_SEEK_SET);
339
4.94k
  m_fcMac = libwps::readU32(input);
340
341
4.94k
  if (m_fcMac <= 0x80)
342
17
  {
343
17
    WPS_DEBUG_MSG(("MSWriteParser::readFIB fcMac %u is impossible\n", m_fcMac));
344
17
    throw (libwps::ParseException());
345
17
  }
346
347
4.93k
  if (!checkFilePosition(m_fcMac))
348
102
  {
349
102
    WPS_DEBUG_MSG(("MSWriteParser::readFIB File is corrupt, position %u beyond end of file\n", m_fcMac));
350
102
    throw (libwps::ParseException());
351
102
  }
352
4.93k
}
353
354
void MSWriteParser::readFFNTB()
355
1.51k
{
356
1.51k
  unsigned font_count = 0, pnFfntb, pnMac;
357
1.51k
  RVNGInputStreamPtr input = getInput();
358
359
1.51k
  input->seek(MSWriteParserInternal::HEADER_W_PNFFNTB, librevenge::RVNG_SEEK_SET);
360
1.51k
  pnFfntb = libwps::readU16(input);
361
362
1.51k
  input->seek(MSWriteParserInternal::HEADER_W_PNMAC, librevenge::RVNG_SEEK_SET);
363
1.51k
  pnMac = libwps::readU16(input);
364
365
1.51k
  if (pnFfntb != 0 && pnFfntb != pnMac)
366
1.33k
  {
367
1.33k
    if (!checkFilePosition(pnFfntb * 0x80 + 2))
368
920
    {
369
920
      WPS_DEBUG_MSG(("MSWriteParser::readFFNTB Font table is missing\n"));
370
920
    }
371
416
    else
372
416
    {
373
416
      input->seek(long(pnFfntb) * 0x80, librevenge::RVNG_SEEK_SET);
374
416
      font_count = libwps::readU16(input);
375
416
    }
376
1.33k
  }
377
378
1.51k
  if (font_count)
379
363
  {
380
    // Ignore font_count, some write files have more
381
363
    unsigned offset = 0;
382
383
363
    for (;;)
384
2.99k
    {
385
2.99k
      offset += 2;
386
2.99k
      if (!checkFilePosition(pnFfntb * 0x80 + offset))
387
4
      {
388
4
        WPS_DEBUG_MSG(("MSWriteParser::readFFNTB Font table is truncated\n"));
389
4
        break;
390
4
      }
391
392
2.99k
      unsigned int cbFfn = libwps::readU16(input);
393
2.99k
      if (cbFfn == 0)
394
78
        break;
395
396
2.91k
      if (cbFfn == 0xffff)
397
813
      {
398
813
        pnFfntb++;
399
400
813
        if (!checkFilePosition(pnFfntb * 0x80 + 2))
401
48
        {
402
48
          WPS_DEBUG_MSG(("MSWriteParser::readFFNTB Font table is truncated\n"));
403
48
          break;
404
48
        }
405
406
765
        offset = 0;
407
765
        input->seek(long(pnFfntb) * 0x80, librevenge::RVNG_SEEK_SET);
408
765
        continue;
409
813
      }
410
411
2.10k
      offset += cbFfn;
412
2.10k
      if (offset > 0x80)
413
221
      {
414
221
        WPS_DEBUG_MSG(("MSWriteParser::readFFNTB Font straddles page, file corrupt\n"));
415
221
        break;
416
221
      }
417
418
1.88k
      if (!checkFilePosition(pnFfntb * 0x80 + offset))
419
9
      {
420
9
        WPS_DEBUG_MSG(("MSWriteParser::readFFNTB Font table is truncated\n"));
421
9
        break;
422
9
      }
423
424
      // We're not interested in the font family
425
1.87k
      input->seek(1, librevenge::RVNG_SEEK_CUR);
426
427
      // Read font name
428
1.87k
      unsigned fnlen = cbFfn - 1;
429
1.87k
      unsigned long read_bytes;
430
1.87k
      const unsigned char *fn = input->read(fnlen, read_bytes);
431
1.87k
      if (read_bytes != fnlen)
432
3
      {
433
3
        WPS_DEBUG_MSG(("MSWriteParser::readFFNTB failed to read font name\n"));
434
3
        throw (libwps::ParseException());
435
3
      }
436
437
1.87k
      librevenge::RVNGString fontname;
438
439
      // Remove trailing 0s
440
2.98k
      while (fnlen > 0 && !fn[fnlen - 1])
441
1.11k
        fnlen--;
442
443
1.87k
      fontname = libwps_tools_win::Font::unicodeString(fn, fnlen, m_fontType);
444
445
1.87k
      m_fonts.push_back(fontname);
446
1.87k
    }
447
363
  }
448
1.51k
  if (m_fonts.empty())
449
1.36k
    m_fonts.push_back("Arial");
450
1.51k
}
451
452
void MSWriteParser::readSED()
453
1.51k
{
454
1.51k
  unsigned pnSetb, pnBftb;
455
1.51k
  RVNGInputStreamPtr input = getInput();
456
457
1.51k
  input->seek(MSWriteParserInternal::HEADER_W_PNSETB, librevenge::RVNG_SEEK_SET);
458
1.51k
  pnSetb = libwps::readU16(input);
459
460
1.51k
  input->seek(MSWriteParserInternal::HEADER_W_PNBFTB, librevenge::RVNG_SEEK_SET);
461
1.51k
  pnBftb = libwps::readU16(input);
462
463
1.51k
  MSWriteParserInternal::Section sep;
464
465
1.51k
  if (pnSetb && pnSetb != pnBftb)
466
1.21k
  {
467
1.21k
    if (!checkFilePosition(pnSetb * 0x80 + 14))
468
117
    {
469
117
      WPS_DEBUG_MSG(("Section is truncated\n"));
470
117
      throw (libwps::ParseException());
471
117
    }
472
473
1.10k
    input->seek(long(pnSetb) * 0x80, librevenge::RVNG_SEEK_SET);
474
1.10k
    uint16_t cset = libwps::readU16(input);
475
476
    // ignore csetMax, cp, fn
477
1.10k
    input->seek(8, librevenge::RVNG_SEEK_CUR);
478
479
1.10k
    uint32_t fcSep = libwps::readU32(input);
480
1.10k
    if (cset > 1 && checkFilePosition(fcSep + 22))
481
377
    {
482
377
      input->seek(long(fcSep), librevenge::RVNG_SEEK_SET);
483
377
      uint8_t headerSize = libwps::readU8(input);
484
377
      if (headerSize<22 || !checkFilePosition(fcSep+2+headerSize))
485
109
      {
486
109
        WPS_DEBUG_MSG(("MSWriteParser::readSECT: can not read the structure, using default\n"));
487
109
      }
488
268
      else
489
268
      {
490
268
        input->seek(2, librevenge::RVNG_SEEK_CUR); // skip reserved 1
491
        // read section
492
268
        sep.m_yaMac=double(libwps::readU16(input))/1440.;
493
268
        sep.m_xaMac=double(libwps::readU16(input))/1440.;
494
268
        sep.m_startPageNumber=libwps::readU16(input);
495
268
        sep.m_yaTop=double(libwps::readU16(input))/1440.;
496
268
        sep.m_dyaText=double(libwps::readU16(input))/1440.;
497
268
        sep.m_xaLeft=double(libwps::readU16(input))/1440.;
498
268
        sep.m_dxaText=double(libwps::readU16(input))/1440.;
499
268
        input->seek(2, librevenge::RVNG_SEEK_CUR); // skip reserved 2
500
268
        sep.m_yaHeader=double(libwps::readU16(input))/1440.;
501
268
        sep.m_yaFooter=double(libwps::readU16(input))/1440.;
502
268
      }
503
377
    }
504
1.10k
  }
505
506
1.39k
  sep.m_fcLim = m_fcMac;
507
1.39k
  m_sections.push_back(sep);
508
1.39k
}
509
510
void MSWriteParser::getPageStyle(MSWriteParserInternal::Section &sep, WPSPageSpan &pageSpan)
511
4.50k
{
512
4.50k
  pageSpan.setFormLength(sep.m_yaMac);
513
4.50k
  pageSpan.setFormWidth(sep.m_xaMac);
514
515
4.50k
  if (sep.m_yaTop < sep.m_yaMac && sep.m_yaMac - sep.m_yaTop - sep.m_dyaText >= 0 &&
516
2.93k
          sep.m_yaMac - sep.m_dyaText < sep.m_yaMac)
517
2.86k
  {
518
2.86k
    pageSpan.setMarginTop(sep.m_yaTop);
519
2.86k
    pageSpan.setMarginBottom(sep.m_yaMac - sep.m_yaTop - sep.m_dyaText);
520
2.86k
  }
521
1.64k
  else
522
1.64k
  {
523
1.64k
    WPS_DEBUG_MSG(("MSWriteParser::readSECT: the top bottom margin seems bad\n"));
524
1.64k
  }
525
526
4.50k
  if (sep.m_columns > 1 && sep.m_xaLeft < sep.m_xaMac)
527
458
  {
528
    // m_dxaText is width of column
529
458
    double dxaText = sep.m_dxaText * sep.m_columns + sep.m_dxaColumns * (sep.m_columns - 1);
530
531
458
    if (sep.m_xaMac - dxaText >= sep.m_xaLeft)
532
290
    {
533
290
      pageSpan.setMarginLeft(sep.m_xaLeft);
534
290
      pageSpan.setMarginRight(sep.m_xaMac - sep.m_xaLeft - dxaText);
535
290
    }
536
458
  }
537
4.04k
  else if (sep.m_xaLeft < sep.m_xaMac && sep.m_xaMac - sep.m_xaLeft - sep.m_dxaText >= 0 &&
538
3.12k
           sep.m_xaMac - sep.m_dxaText < sep.m_xaMac)
539
2.93k
  {
540
2.93k
    pageSpan.setMarginLeft(sep.m_xaLeft);
541
2.93k
    pageSpan.setMarginRight(sep.m_xaMac - sep.m_xaLeft - sep.m_dxaText);
542
2.93k
  }
543
1.11k
  else
544
1.11k
  {
545
1.11k
    WPS_DEBUG_MSG(("MSWriteParser::readSECT: the left right margin seems bad\n"));
546
1.11k
  }
547
548
4.50k
  if (sep.m_startPageNumber != 0xffff)
549
1.62k
    pageSpan.setPageNumber(sep.m_startPageNumber);
550
551
  // FIXME:
552
  // - page numbers
553
  // - line numbers
554
  // - page break style
555
4.50k
}
556
557
void MSWriteParser::readFOD(unsigned page, void (MSWriteParser::*parseFOD)(uint32_t fcFirst, uint32_t fcLim, unsigned size))
558
8.61k
{
559
8.61k
  RVNGInputStreamPtr input = getInput();
560
8.61k
  unsigned fcLim, fc = 0x80;
561
562
8.61k
  for (;;)
563
19.8k
  {
564
19.8k
    auto const pageBegin=long(page * 0x80);
565
566
19.8k
    if (!checkFilePosition(page * 0x80 + 0x7f))
567
1.96k
    {
568
1.96k
      WPS_DEBUG_MSG(("MSWriteParser::readFOD: FOD list is truncated\n"));
569
1.96k
      break;
570
1.96k
    }
571
572
17.8k
    input->seek(pageBegin + 0x7f, librevenge::RVNG_SEEK_SET);
573
17.8k
    uint8_t cfod = libwps::readU8(input);
574
575
17.8k
    if (cfod > 20)
576
5.87k
    {
577
5.87k
      WPS_DEBUG_MSG(("MSWriteParser::readFOD: Too many FODs %d on one page\n", cfod));
578
5.87k
      cfod = 20;
579
5.87k
    }
580
581
53.6k
    for (unsigned fod = 0; fod < cfod; ++fod)
582
42.4k
    {
583
42.4k
      input->seek(pageBegin + int(fod) * 6 + 4, librevenge::RVNG_SEEK_SET);
584
42.4k
      fcLim = libwps::readU32(input);
585
42.4k
      uint16_t bfProp = libwps::readU16(input);
586
42.4k
      unsigned cch = 0;
587
588
42.4k
      if (bfProp < 0x7f - 4)
589
32.3k
      {
590
32.3k
        input->seek(pageBegin + long(bfProp) + 4, librevenge::RVNG_SEEK_SET);
591
32.3k
        cch = libwps::readU8(input);
592
593
        // Does it fit on the page
594
32.3k
        if ((bfProp + cch + 4) >= 0x80)
595
844
        {
596
844
          cch = 0;
597
844
          WPS_DEBUG_MSG(("MSWriteParser::readFOD: entry %d on page %d invalid\n", fod, page));
598
844
        }
599
32.3k
      }
600
601
42.4k
      (this->*parseFOD)(fc, fcLim, cch);
602
603
42.4k
      if (fcLim >= m_fcMac)
604
6.65k
        return;
605
606
35.8k
      fc = fcLim;
607
35.8k
    }
608
11.2k
    page++;
609
11.2k
  }
610
8.61k
}
611
612
void MSWriteParser::readPAP(uint32_t fcFirst, uint32_t fcLim, unsigned cch)
613
8.42k
{
614
8.42k
  RVNGInputStreamPtr input = getInput();
615
616
8.42k
  struct MSWriteParserInternal::PAP pap;
617
618
8.42k
  WPS_LE_PUT_GUINT16(&pap.m_dyaLine, 240);
619
620
8.42k
  if (cch)
621
3.82k
  {
622
3.82k
    if (cch > sizeof(pap))
623
844
      cch = sizeof(pap);
624
625
3.82k
    unsigned long read_bytes;
626
3.82k
    const unsigned char *p = input->read(cch, read_bytes);
627
3.82k
    if (read_bytes != cch)
628
0
    {
629
0
      WPS_DEBUG_MSG(("MSWriteParser::readPAP failed to read PAP\n"));
630
0
      throw (libwps::ParseException());
631
0
    }
632
633
3.82k
    memcpy(&pap, p, cch);
634
3.82k
  }
635
636
8.42k
  auto dxaLeft = int16_t(WPS_LE_GET_GUINT16(&pap.m_dxaLeft));
637
8.42k
  auto dxaLeft1 = int16_t(WPS_LE_GET_GUINT16(&pap.m_dxaLeft1));
638
8.42k
  auto dxaRight = int16_t(WPS_LE_GET_GUINT16(&pap.m_dxaRight));
639
640
8.42k
  MSWriteParserInternal::Paragraph para;
641
8.42k
  int i;
642
643
15.5k
  for (i=0; i<14; i++)
644
15.4k
  {
645
15.4k
    auto pos = WPS_LE_GET_GUINT16(&pap.m_TBD[i].m_dxa);
646
647
15.4k
    if (!pos)
648
8.33k
      break;
649
650
7.07k
    WPSTabStop tab(pos / 1440., (pap.m_TBD[i].m_jcTab & 3) == 3 ?
651
5.30k
                   WPSTabStop::DECIMAL : WPSTabStop::LEFT);
652
653
7.07k
    para.m_tabs.push_back(tab);
654
655
7.07k
    if (dxaLeft + dxaLeft1 == pos)
656
323
      para.m_skiptab = true;
657
7.07k
  }
658
659
8.42k
  switch (pap.m_justification & 3)
660
8.42k
  {
661
0
  default:
662
7.69k
  case 0:
663
7.69k
    para.m_justify = libwps::JustificationLeft;
664
7.69k
    break;
665
290
  case 1:
666
290
    para.m_justify = libwps::JustificationCenter;
667
290
    break;
668
194
  case 2:
669
194
    para.m_justify = libwps::JustificationRight;
670
194
    break;
671
247
  case 3:
672
247
    para.m_justify = libwps::JustificationFull;
673
247
    break;
674
8.42k
  }
675
676
8.42k
  para.m_margins[0] = dxaLeft1 / 1440.0;
677
8.42k
  para.m_margins[1] = dxaLeft / 1440.0;
678
8.42k
  para.m_margins[2] = dxaRight / 1440.0;
679
680
8.42k
  auto dyaLine = WPS_LE_GET_GUINT16(&pap.m_dyaLine);
681
8.42k
  para.m_interLine = dyaLine / 240.0;
682
683
8.42k
  para.m_fcFirst = fcFirst;
684
8.42k
  para.m_fcLim = fcLim;
685
8.42k
  if (pap.m_rhcPage & 0x10)
686
1.81k
  {
687
1.81k
    para.m_graphics = true;
688
    /* MS Write doesn't take first indent into
689
        account for images, but odt does */
690
1.81k
    para.m_margins[0] = 0.0;
691
1.81k
  }
692
6.61k
  else
693
6.61k
  {
694
6.61k
    if (pap.m_rhcPage & 6)
695
392
    {
696
392
      if (pap.m_rhcPage & 1)
697
194
      {
698
194
        para.m_Location = MSWriteParserInternal::Paragraph::FOOTER;
699
194
      }
700
198
      else
701
198
      {
702
198
        para.m_Location = MSWriteParserInternal::Paragraph::HEADER;
703
198
      }
704
392
      para.m_firstpage = (pap.m_rhcPage & 0x08) != 0;
705
392
    }
706
6.61k
  }
707
708
8.42k
  m_paragraphList.push_back(para);
709
8.42k
}
710
711
void MSWriteParser::readCHP(uint32_t fcFirst, uint32_t fcLim, unsigned cch)
712
7.44k
{
713
7.44k
  RVNGInputStreamPtr input = getInput();
714
715
7.44k
  struct MSWriteParserInternal::CHP chp;
716
717
7.44k
  chp.m_hps = 24;
718
719
7.44k
  if (cch)
720
3.08k
  {
721
3.08k
    if (cch > sizeof(chp))
722
2.38k
      cch = sizeof(chp);
723
724
3.08k
    unsigned long read_bytes;
725
3.08k
    const unsigned char *p = input->read(cch, read_bytes);
726
3.08k
    if (read_bytes != cch)
727
0
    {
728
0
      WPS_DEBUG_MSG(("MSWriteParser::readCHP failed to read CHP entry\n"));
729
0
      throw (libwps::ParseException());
730
0
    }
731
732
3.08k
    memcpy(&chp, p, cch);
733
3.08k
  }
734
735
7.44k
  MSWriteParserInternal::Font font;
736
737
7.44k
  unsigned ftc = (chp.m_fBold / 4) | ((chp.m_ftcXtra & 7) * 64);
738
7.44k
  if (ftc >= m_fonts.size()) ftc = 0;
739
740
7.44k
  font.m_name = m_fonts[ftc];
741
7.44k
  font.m_size = chp.m_hps / 2.0;
742
7.44k
  if (chp.m_fBold & 1)
743
485
    font.m_attributes |= WPS_BOLD_BIT;
744
7.44k
  if (chp.m_fBold & 2)
745
344
    font.m_attributes |= WPS_ITALICS_BIT;
746
7.44k
  if (chp.m_fUline & 1)
747
663
    font.m_attributes |= WPS_UNDERLINE_BIT;
748
7.44k
  if (chp.m_fUline & 0x40)
749
487
    font.m_special = true;
750
7.44k
  if (chp.m_hpsPos)
751
1.13k
  {
752
1.13k
    if (chp.m_hpsPos & 0x80)
753
450
      font.m_attributes |= WPS_SUBSCRIPT_BIT;
754
682
    else
755
682
      font.m_attributes |= WPS_SUPERSCRIPT_BIT;
756
1.13k
  }
757
758
7.44k
  font.m_fcFirst = fcFirst;
759
7.44k
  font.m_fcLim = fcLim;
760
7.44k
  font.m_encoding = libwps_tools_win::Font::getFontType(font.m_name);
761
7.44k
  if (font.m_encoding == libwps_tools_win::Font::UNKNOWN)
762
7.16k
    font.m_encoding = m_fontType;
763
764
7.44k
  m_fontList.push_back(font);
765
7.44k
}
766
767
void MSWriteParser::getHeaderFooters(uint32_t first, MSWriteParserInternal::Section &section, WPSPageSpan &ps)
768
4.50k
{
769
4.50k
  bool headerPage1 = true, footerPage1 = true;
770
4.50k
  bool reachedMain = false;
771
4.50k
  WPSEntry empty, footer, header;
772
4.50k
  WPSPageSpan::HeaderFooterOccurrence headerOccurrence = WPSPageSpan::ALL;
773
4.50k
  WPSPageSpan::HeaderFooterOccurrence footerOccurrence = WPSPageSpan::ALL;
774
775
4.50k
  MSWriteParserInternal::Paragraph::Location location = MSWriteParserInternal::Paragraph::MAIN;
776
4.50k
  RVNGInputStreamPtr input = getInput();
777
4.50k
  int numPage = 0; // each section ends with a page break (0x0c)
778
779
4.50k
  size_t firstP = 0;
780
4.50k
  size_t i;
781
782
33.2k
  for (i = 0; i < m_paragraphList.size(); i++)
783
28.9k
  {
784
28.9k
    MSWriteParserInternal::Paragraph &p = m_paragraphList[i];
785
786
28.9k
    if (p.m_fcLim <= first)
787
21.1k
    {
788
21.1k
      firstP = i + 1;
789
21.1k
      continue;
790
21.1k
    }
791
792
7.81k
    if (p.m_fcFirst >= section.m_fcLim)
793
158
      break;
794
795
7.65k
    if (!p.m_graphics)
796
6.76k
    {
797
6.76k
      uint32_t fc = p.m_fcFirst;
798
799
6.76k
      input->seek(long(fc), librevenge::RVNG_SEEK_SET);
800
801
1.82M
      while (fc < p.m_fcLim && fc < m_fcMac)
802
1.81M
      {
803
1.81M
        if (libwps::readU8(input) == 0x0C) numPage++;
804
1.81M
        fc++;
805
1.81M
      }
806
6.76k
    }
807
808
7.65k
    if (!p.m_headerUseMargin && (p.m_Location == MSWriteParserInternal::Paragraph::HEADER ||
809
6.93k
                                 p.m_Location == MSWriteParserInternal::Paragraph::FOOTER))
810
537
    {
811
      // Indents in header/footer are off paper, not margins
812
537
      p.m_margins[1] -= ps.getMarginLeft();
813
537
      p.m_margins[2] -= ps.getMarginRight();
814
537
    }
815
816
817
7.65k
    if (p.m_Location != location && !reachedMain)
818
742
    {
819
742
      if (location == MSWriteParserInternal::Paragraph::HEADER)
820
131
      {
821
131
        headerPage1 = m_paragraphList[firstP].m_firstpage;
822
131
        headerOccurrence = m_paragraphList[firstP].m_HeaderFooterOccurrence;
823
131
        header.setBegin(long(m_paragraphList[firstP].m_fcFirst));
824
131
        header.setEnd(long(m_paragraphList[i - 1].m_fcLim));
825
131
        header.setType("TEXT");
826
131
      }
827
611
      else if (location == MSWriteParserInternal::Paragraph::FOOTER)
828
180
      {
829
180
        footerPage1 = m_paragraphList[firstP].m_firstpage;
830
180
        footerOccurrence = m_paragraphList[firstP].m_HeaderFooterOccurrence;
831
180
        footer.setBegin(long(m_paragraphList[firstP].m_fcFirst));
832
180
        footer.setEnd(long(m_paragraphList[i - 1].m_fcLim));
833
180
        footer.setType("TEXT");
834
180
      }
835
836
742
      location = p.m_Location;
837
742
      firstP = i;
838
742
    }
839
840
7.65k
    if (p.m_Location == MSWriteParserInternal::Paragraph::FOOTNOTE)
841
28
      break;
842
843
7.62k
    if (p.m_Location == MSWriteParserInternal::Paragraph::MAIN)
844
6.84k
      reachedMain = true;
845
7.62k
  }
846
847
4.50k
  if (i <= firstP)
848
66
  {
849
66
    WPS_DEBUG_MSG(("MSWriteParser::getHeaderFooters: missing body text\n"));
850
66
    throw (libwps::ParseException());
851
66
  }
852
853
4.44k
  section.m_Main.setBegin(long(m_paragraphList[firstP].m_fcFirst));
854
4.44k
  section.m_Main.setEnd(long(std::min(m_paragraphList[i - 1].m_fcLim, m_fcMac)));
855
4.44k
  section.m_Main.setType("TEXT");
856
857
4.44k
  empty.setType("TEXT");
858
859
4.44k
  WPSSubDocumentPtr subemptydoc(new MSWriteParserInternal::SubDocument
860
4.44k
                                (getInput(), *this, empty, MSWriteParserInternal::Paragraph::MAIN));
861
862
4.44k
  if (header.valid())
863
76
  {
864
76
    WPSSubDocumentPtr subdoc(new MSWriteParserInternal::SubDocument
865
76
                             (getInput(), *this, header, MSWriteParserInternal::Paragraph::HEADER));
866
867
76
    if (headerOccurrence == WPSPageSpan::NEVER)
868
9
    {
869
9
      if (headerPage1)
870
9
        ps.setHeaderFooter(WPSPageSpan::HEADER, WPSPageSpan::FIRST, subdoc);
871
9
    }
872
67
    else
873
67
    {
874
67
      ps.setHeaderFooter(WPSPageSpan::HEADER, headerOccurrence, subdoc);
875
876
67
      if (!headerPage1)
877
34
        ps.setHeaderFooter(WPSPageSpan::HEADER, WPSPageSpan::FIRST, subemptydoc);
878
67
    }
879
76
  }
880
881
4.44k
  if (footer.valid())
882
112
  {
883
112
    WPSSubDocumentPtr subdoc(new MSWriteParserInternal::SubDocument
884
112
                             (getInput(), *this, footer, MSWriteParserInternal::Paragraph::FOOTER));
885
886
112
    if (footerOccurrence == WPSPageSpan::NEVER)
887
15
    {
888
15
      if (footerPage1)
889
15
        ps.setHeaderFooter(WPSPageSpan::FOOTER, WPSPageSpan::FIRST, subdoc);
890
15
    }
891
97
    else
892
97
    {
893
97
      ps.setHeaderFooter(WPSPageSpan::FOOTER, footerOccurrence, subdoc);
894
895
97
      if (!footerPage1)
896
34
        ps.setHeaderFooter(WPSPageSpan::FOOTER, WPSPageSpan::FIRST, subemptydoc);
897
97
    }
898
112
  }
899
900
4.44k
  if (section.m_bkc == 0)
901
1.14k
    numPage--;
902
903
4.44k
  ps.setPageSpan(numPage);
904
4.44k
}
905
906
std::shared_ptr<WPSContentListener> MSWriteParser::createListener(librevenge::RVNGTextInterface *interface)
907
2.81k
{
908
2.81k
  std::vector<WPSPageSpan> pageList;
909
2.81k
  uint32_t fc = 0x80;
910
2.81k
  std::vector<MSWriteParserInternal::Section>::iterator sections;
911
912
7.32k
  for (sections = m_sections.begin(); sections != m_sections.end();)
913
4.50k
  {
914
4.50k
    WPSPageSpan ps;
915
    // Get margins etc from SEP
916
4.50k
    getPageStyle(*sections, ps);
917
    // Get headers, footers and page count from actual text
918
4.50k
    getHeaderFooters(fc, *sections, ps);
919
4.50k
    fc = sections->m_fcLim;
920
    // Add extra page for last section
921
4.50k
    if (++sections == m_sections.end())
922
2.74k
      ps.setPageSpan(ps.getPageSpan()+1);
923
924
4.50k
    pageList.push_back(ps);
925
4.50k
  }
926
927
2.81k
  return std::shared_ptr<WPSContentListener>
928
2.81k
         (new WPSContentListener(pageList, interface));
929
2.81k
}
930
931
void MSWriteParser::readText(WPSEntry const &e, MSWriteParserInternal::Paragraph::Location location)
932
5.86k
{
933
5.86k
  auto fc = uint32_t(e.begin());
934
5.86k
  std::vector<MSWriteParserInternal::Paragraph>::iterator paps;
935
5.86k
  std::vector<MSWriteParserInternal::Font>::iterator chps;
936
5.86k
  paps = m_paragraphList.begin();
937
5.86k
  chps = m_fontList.begin();
938
5.86k
  RVNGInputStreamPtr input = getInput();
939
5.86k
  float lastObjectHeight = 0.0;
940
941
20.9k
  while (fc < uint32_t(e.end()))
942
15.1k
  {
943
15.1k
    bool skiptab = false;
944
945
45.1k
    while (fc >= paps->m_fcLim)
946
30.0k
    {
947
30.0k
      if (++paps == m_paragraphList.end())
948
42
      {
949
42
        WPS_DEBUG_MSG(("MSWriteParser::readText PAP not found for offset %u\n", fc));
950
42
        throw (libwps::ParseException());
951
42
      }
952
29.9k
      skiptab = paps->m_skiptab;
953
29.9k
    }
954
955
15.0k
    if (paps->m_Location != location)
956
2.17k
    {
957
      // ignore e.g. headers in main body of text
958
2.17k
      fc = paps->m_fcLim;
959
2.17k
      continue;
960
2.17k
    }
961
962
12.9k
    if (paps->m_graphics)
963
1.09k
    {
964
1.09k
      m_listener->setParagraph(*paps);
965
966
      // The last pap can have m_fcLim of greater than m_fcMac
967
1.09k
      unsigned fcLim = std::min(paps->m_fcLim, m_fcMac);
968
969
1.09k
      if (fcLim - fc < 40)
970
104
      {
971
104
        WPS_DEBUG_MSG(("MSWriteParser::readText object too short\n"));
972
104
        fc = paps->m_fcLim;
973
104
        continue;
974
104
      }
975
976
992
      WPSPosition pos;
977
992
      WPSPosition::XPos align;
978
979
992
      switch (paps->m_justify)
980
992
      {
981
66
      case libwps::JustificationFull:
982
66
      case libwps::JustificationFullAllLines:
983
793
      case libwps::JustificationLeft:
984
793
      default:
985
793
        align = WPSPosition::XLeft;
986
793
        break;
987
89
      case libwps::JustificationRight:
988
89
        align = WPSPosition::XRight;
989
89
        break;
990
110
      case libwps::JustificationCenter:
991
110
        align = WPSPosition::XCenter;
992
110
        break;
993
992
      }
994
995
992
      pos.setRelativePosition(WPSPosition::ParagraphContent, align);
996
997
992
      input->seek(long(fc) + 8, librevenge::RVNG_SEEK_SET);
998
992
      uint16_t dxaOffset = libwps::readU16(input);
999
992
      uint16_t dxaSize = libwps::readU16(input);
1000
992
      uint16_t dyaSize = libwps::readU16(input);
1001
1002
992
      input->seek(22, librevenge::RVNG_SEEK_CUR);
1003
992
      uint16_t mx = libwps::readU16(input);
1004
992
      uint16_t my = libwps::readU16(input);
1005
1006
992
      WPS_DEBUG_MSG(("MSWriteParser::readText object found %utwx%utw, offset %utw, mx=%u my=%u\n", dxaSize, dyaSize, dxaOffset, mx, my));
1007
1008
992
      Vec2f size(Vec2f(dxaSize/1440.0f, dyaSize/1440.0f));
1009
992
      if ((mx > 10 && my > 10) && !(mx == 1000 && my == 1000))
1010
482
      {
1011
482
        size[0] *= mx / 1000.0f;
1012
482
        size[1] *= my / 1000.0f;
1013
482
      }
1014
1015
992
      pos.setUnit(librevenge::RVNG_INCH);
1016
992
      pos.setSize(size);
1017
1018
992
      Vec2f origin;
1019
992
      if (dxaOffset && align == WPSPosition::XLeft)
1020
475
        origin[0] = dxaOffset/1440.0f;
1021
992
      origin[1] = lastObjectHeight;
1022
992
      pos.setOrigin(origin);
1023
992
      input->seek(long(fc), librevenge::RVNG_SEEK_SET);
1024
1025
992
      processObject(pos, fcLim);
1026
992
      lastObjectHeight += pos.size()[1];
1027
992
      fc = paps->m_fcLim;
1028
992
      continue;
1029
992
    }
1030
1031
11.8k
    lastObjectHeight = 0.0f;
1032
1033
38.0k
    while (fc >= chps->m_fcLim)
1034
26.2k
    {
1035
26.2k
      if (++chps == m_fontList.end())
1036
23
      {
1037
23
        WPS_DEBUG_MSG(("MSWriteParser::readText CHP not found for offset %u\n", fc));
1038
23
        throw (libwps::ParseException());
1039
23
      }
1040
26.2k
    }
1041
1042
11.8k
    MSWriteParserInternal::Paragraph para = *paps;
1043
11.8k
    if (paps->m_interLine>0)
1044
1.40k
      para.setInterline((paps->m_interLine * chps->m_size)/72., librevenge::RVNG_INCH, WPSParagraph::AtLeast);
1045
1046
11.8k
    m_listener->setParagraph(para);
1047
11.8k
    m_listener->setFont(*chps);
1048
1049
11.8k
    uint32_t lim = std::min(chps->m_fcLim, paps->m_fcLim);
1050
11.8k
    lim = std::min(lim, m_fcMac);
1051
1052
924k
    while (fc < lim)
1053
912k
    {
1054
912k
      unsigned size = lim - fc;
1055
912k
      unsigned long read_bytes;
1056
1057
912k
      input->seek(long(fc), librevenge::RVNG_SEEK_SET);
1058
912k
      const unsigned char *p = input->read(size, read_bytes);
1059
912k
      if (!p || read_bytes != size)
1060
0
      {
1061
0
        WPS_DEBUG_MSG(("MSWriteParser::readText failed to read\n"));
1062
0
        throw (libwps::ParseException());
1063
0
      }
1064
1065
912k
      if (chps->m_special)
1066
172k
      {
1067
172k
        insertSpecial(p[0], fc, paps->m_Location);
1068
172k
        size = 1;
1069
172k
      }
1070
740k
      else if (chps->m_footnote || chps->m_annotation)
1071
1.24k
      {
1072
1.24k
        if (paps->m_Location == MSWriteParserInternal::Paragraph::MAIN)
1073
1.13k
        {
1074
1.13k
          auto label = libwps_tools_win::Font::unicodeString(p, size, chps->m_encoding);
1075
1.13k
          insertNote(chps->m_annotation, fc, label);
1076
1.13k
        }
1077
1.24k
      }
1078
738k
      else
1079
738k
      {
1080
738k
        if (p[0] == 9 && skiptab)
1081
12
        {
1082
12
          size--;
1083
12
          p++;
1084
12
          fc++;
1085
12
        }
1086
738k
        if (size && *p < ' ')
1087
639k
        {
1088
639k
          insertControl(*p, fc);
1089
639k
          size = 1;
1090
639k
        }
1091
99.6k
        else if (size)
1092
99.6k
          size = insertString(p, size, chps->m_encoding);
1093
738k
      }
1094
912k
      fc += size;
1095
1096
912k
      skiptab = false;
1097
912k
    }
1098
11.8k
  }
1099
5.86k
}
1100
1101
void MSWriteParser::insertSpecial(uint8_t val, uint32_t /*fc*/, MSWriteParserInternal::Paragraph::Location /*location*/)
1102
7.82k
{
1103
7.82k
  if (val == 1)
1104
903
    m_listener->insertField(WPSField(WPSField::PageNumber));
1105
7.82k
}
1106
1107
void MSWriteParser::insertControl(uint8_t val, uint32_t /*fc*/)
1108
79.3k
{
1109
79.3k
  switch (val)
1110
79.3k
  {
1111
1.23k
  case 9:
1112
1.23k
    m_listener->insertTab();
1113
1.23k
    break;
1114
1.35k
  case 10:
1115
2.31k
  case 11:
1116
2.31k
    m_listener->insertEOL();
1117
2.31k
    break;
1118
1.28k
  case 12:
1119
1.28k
    m_listener->insertBreak(WPS_PAGE_BREAK);
1120
1.28k
    break;
1121
835
  case 30:
1122
835
    m_listener->insertUnicode(0x20);
1123
835
    break;
1124
984
  case 31: // soft hyphen (ignored by Write)
1125
2.27k
  case 13: // carriage return
1126
2.27k
    break;
1127
71.3k
  default:
1128
    // MS Write displays these as boxes
1129
71.3k
    m_listener->insertUnicode(0x25af);
1130
71.3k
    break;
1131
79.3k
  }
1132
79.3k
}
1133
1134
unsigned MSWriteParser::insertString(const unsigned char *str, unsigned size, libwps_tools_win::Font::Type type)
1135
99.6k
{
1136
99.6k
  unsigned len = 0;
1137
1138
815k
  while (len < size && str[len] >= ' ')
1139
716k
    len++;
1140
99.6k
  m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(str, len, type));
1141
1142
99.6k
  return len;
1143
99.6k
}
1144
1145
bool MSWriteParser::readString(std::string &res, unsigned long lastPos)
1146
378
{
1147
378
  RVNGInputStreamPtr input = getInput();
1148
378
  unsigned size = libwps::readU32(input);
1149
378
  if (static_cast<unsigned long>(input->tell()) + size > lastPos || !checkFilePosition(uint32_t(lastPos)))
1150
120
  {
1151
120
    WPS_DEBUG_MSG(("MSWriteParser::readString too short\n"));
1152
120
    return false;
1153
120
  }
1154
1155
258
  if (size == 0)
1156
184
  {
1157
184
    res = std::string();
1158
184
    return true;
1159
184
  }
1160
1161
74
  unsigned long read_bytes;
1162
74
  const unsigned char *data = input->read(size, read_bytes);
1163
74
  if (read_bytes != size)
1164
0
  {
1165
0
    WPS_DEBUG_MSG(("MSWriteParser::readString failed to read\n"));
1166
0
    throw (libwps::ParseException());
1167
0
  }
1168
1169
  // Must be ascii
1170
717
  for (unsigned i=0; i < size - 1; i++)
1171
667
  {
1172
667
    if (data[i] < ' ' || data[i] >= 0x7f)
1173
24
    {
1174
24
      WPS_DEBUG_MSG(("MSWriteParser::readString non-ascii found\n"));
1175
24
      return false;
1176
24
    }
1177
667
  }
1178
50
  if (data[size - 1])
1179
7
    return false;
1180
1181
43
  res = std::string(reinterpret_cast<const char *>(data), size - 1u);
1182
1183
43
  return true;
1184
50
}
1185
1186
void MSWriteParser::processObject(WPSPosition &pos, unsigned long lastPos)
1187
992
{
1188
992
  if (!checkFilePosition(uint32_t(lastPos)))
1189
0
  {
1190
0
    WPS_DEBUG_MSG(("MSWriteParser::processObject last position is bad\n"));
1191
0
    return;
1192
0
  }
1193
992
  RVNGInputStreamPtr input = getInput();
1194
992
  unsigned mm = libwps::readU16(input);
1195
1196
992
  if (mm == 0x88)   // Windows metafile (.wmf)
1197
84
  {
1198
    // Step over unused fields
1199
84
    input->seek(30, librevenge::RVNG_SEEK_CUR);
1200
1201
84
    unsigned cbSize = libwps::readU32(input);
1202
1203
    // Step over unused fields
1204
84
    input->seek(4, librevenge::RVNG_SEEK_CUR);
1205
1206
84
    if (static_cast<unsigned long>(input->tell()) + cbSize > lastPos)
1207
67
    {
1208
67
      WPS_DEBUG_MSG(("MSWriteParser::processObject bad size for wmf\n"));
1209
67
      return;
1210
67
    }
1211
17
    WPS_DEBUG_MSG(("MSWriteParser::processObject 3.0 WMF object\n"));
1212
1213
17
    librevenge::RVNGBinaryData wmfdata;
1214
17
    if (processWMF(wmfdata, cbSize))
1215
17
    {
1216
17
      m_listener->insertPicture(pos, wmfdata, "application/x-wmf");
1217
17
    }
1218
17
  }
1219
908
  else if (mm == 0xe3)     // this is a picture
1220
229
  {
1221
    // Step over unused fields
1222
229
    input->seek(16, librevenge::RVNG_SEEK_CUR);
1223
1224
    // read BITMAP structure
1225
229
    unsigned width = libwps::readU16(input);
1226
229
    unsigned height = libwps::readU16(input);
1227
229
    unsigned byte_width = libwps::readU16(input);
1228
229
    unsigned planes = libwps::readU8(input);
1229
229
    unsigned bits_pixel = libwps::readU8(input);
1230
1231
    // Step over unused fields
1232
229
    input->seek(6, librevenge::RVNG_SEEK_CUR);
1233
1234
229
    unsigned cbSize = libwps::readU32(input);
1235
1236
229
    input->seek(4, librevenge::RVNG_SEEK_CUR);
1237
1238
229
    if (static_cast<unsigned long>(input->tell()) + cbSize > lastPos)
1239
61
    {
1240
61
      WPS_DEBUG_MSG(("MSWriteParser::processObject bad size for DDB\n"));
1241
61
      return;
1242
61
    }
1243
1244
168
    WPS_DEBUG_MSG(("MSWriteParser::processObject 3.0 DDB object %ux%u\n", width, height));
1245
1246
168
    librevenge::RVNGBinaryData bmpdata;
1247
168
    if (processDDB(bmpdata, pos, width, height, byte_width, planes, bits_pixel, cbSize))
1248
99
    {
1249
99
      m_listener->insertPicture(pos, bmpdata, "image/bmp");
1250
99
    }
1251
168
  }
1252
679
  else if (mm == 0xe4)     // OLE object
1253
279
  {
1254
    // Step over unused fields
1255
279
    input->seek(38, librevenge::RVNG_SEEK_CUR);
1256
1257
279
    unsigned ole_id = libwps::readU32(input);
1258
279
    unsigned type = libwps::readU32(input);
1259
1260
279
    if (ole_id != 0x501)
1261
79
    {
1262
79
      WPS_DEBUG_MSG(("MSWriteParser::processObject Unknown ole identifier %x\n", ole_id));
1263
79
      return;
1264
79
    }
1265
1266
200
    switch (type)
1267
200
    {
1268
39
    case 3: // static OLE
1269
39
    {
1270
39
      librevenge::RVNGBinaryData staticOle;
1271
39
      std::string mimetype;
1272
39
      if (processStaticOLE(staticOle, mimetype, pos, lastPos))
1273
6
      {
1274
6
        m_listener->insertPicture(pos, staticOle, mimetype);
1275
6
      }
1276
39
      return;
1277
0
    }
1278
158
    case 2: // Embedded OLE
1279
158
      processEmbeddedOLE(pos, lastPos);
1280
158
      return;
1281
0
    case 1:
1282
0
      WPS_DEBUG_MSG(("MSWriteParser::processObject:OLE link\n"));
1283
0
      return;
1284
3
    default:
1285
3
      WPS_DEBUG_MSG(("MSWriteParser::processObject:unknown ole type %d\n", type));
1286
3
      return;
1287
200
    }
1288
200
  }
1289
400
  else
1290
400
  {
1291
400
    WPS_DEBUG_MSG(("MSWriteParser::processObject:Unknown object encountered (mm=%02x)\n", mm));
1292
400
  }
1293
992
}
1294
1295
bool MSWriteParser::processDDB(librevenge::RVNGBinaryData &bmpdata, WPSPosition &pos, unsigned width, unsigned height, unsigned byte_width, unsigned planes, unsigned bits_pixel, unsigned size)
1296
168
{
1297
  /* A ddb image has no color palette; it is BMP Version 1, but this format
1298
     is not widely supported. Create BMP version 2 file with a color palette.
1299
   */
1300
168
  static const MSWriteParserInternal::BitmapPalette pal1[2] =
1301
168
  {
1302
168
    { 0,0,0 }, { 255,255,255 }
1303
168
  };
1304
168
  static const MSWriteParserInternal::BitmapPalette pal4[16] =
1305
168
  {
1306
168
    { 0,0,0 }, // black
1307
168
    { 0,0,255 }, // Blue
1308
168
    { 0,128,0 },  // Cyan
1309
168
    { 0,255,0 }, // Green
1310
168
    { 192,0,192 }, // Magenta
1311
168
    { 255,0,0 }, // Red
1312
168
    { 255,255,0 }, // Yellow
1313
168
    { 255,255,255 }, // White
1314
1315
168
    { 0,0,128 }, // Dark blue
1316
168
    { 0,128,128 }, // Dark cyan
1317
168
    { 0,128,0 }, // Dark green
1318
168
    { 128,0,128 }, // Dark magenta
1319
168
    { 128,0,0  }, // Dark red
1320
168
    { 192,192,0 }, // Dark yellow
1321
168
    { 192,192,192 }, // Light gray
1322
168
    { 128,128,128 }, // Dark gray
1323
168
  };
1324
168
  static const MSWriteParserInternal::BitmapPalette pal8[256] =
1325
168
  {
1326
168
    { 0,0,0 }, { 128,0,0 }, { 0,128,0 }, { 128,128,0 },
1327
168
    { 0,0,128 }, { 128,0,128 }, { 0,128,128 }, { 192,192,192 },
1328
168
    { 192,220,192 }, { 166,202,240 }, { 42,63,170 }, { 42,63,255 },
1329
168
    { 42,95,0 }, { 42,95,85 }, { 42,95,170 }, { 42,95,255 },
1330
168
    { 42,127,0 }, { 42,127,85 }, { 42,127,170 }, { 42,127,255 },
1331
168
    { 42,159,0 }, { 42,159,85 }, { 42,159,170 }, { 42,159,255 },
1332
168
    { 42,191,0 }, { 42,191,85 }, { 42,191,170 }, { 42,191,255 },
1333
168
    { 42,223,0 }, { 42,223,85 }, { 42,223,170 }, { 42,223,255 },
1334
168
    { 42,255,0 }, { 42,255,85 }, { 42,255,170 }, { 42,255,255 },
1335
168
    { 85,0,0 }, { 85,0,85 }, { 85,0,170 }, { 85,0,255 },
1336
168
    { 85,31,0 }, { 85,31,85 }, { 85,31,170 }, { 85,31,255 },
1337
168
    { 85,63,0 }, { 85,63,85 }, { 85,63,170 }, { 85,63,255 },
1338
168
    { 85,95,0 }, { 85,95,85 }, { 85,95,170 }, { 85,95,255 },
1339
168
    { 85,127,0 }, { 85,127,85 }, { 85,127,170 }, { 85,127,255 },
1340
168
    { 85,159,0 }, { 85,159,85 }, { 85,159,170 }, { 85,159,255 },
1341
168
    { 85,191,0 }, { 85,191,85 }, { 85,191,170 }, { 85,191,255 },
1342
168
    { 85,223,0 }, { 85,223,85 }, { 85,223,170 }, { 85,223,255 },
1343
168
    { 85,255,0 }, { 85,255,85 }, { 85,255,170 }, { 85,255,255 },
1344
168
    { 127,0,0 }, { 127,0,85 }, { 127,0,170 }, { 127,0,255 },
1345
168
    { 127,31,0 }, { 127,31,85 }, { 127,31,170 }, { 127,31,255 },
1346
168
    { 127,63,0 }, { 127,63,85 }, { 127,63,170 }, { 127,63,255 },
1347
168
    { 127,95,0 }, { 127,95,85 }, { 127,95,170 }, { 127,95,255 },
1348
168
    { 127,127,0 }, { 127,127,85 }, { 127,127,170 }, { 127,127,255 },
1349
168
    { 127,159,0 }, { 127,159,85 }, { 127,159,170 }, { 127,159,255 },
1350
168
    { 127,191,0 }, { 127,191,85 }, { 127,191,170 }, { 127,191,255 },
1351
168
    { 127,223,0 }, { 127,223,85 }, { 127,223,170 }, { 127,223,255 },
1352
168
    { 127,255,0 }, { 127,255,85 }, { 127,255,170 }, { 127,255,255 },
1353
168
    { 170,0,0 }, { 170,0,85 }, { 170,0,170 }, { 170,0,255 },
1354
168
    { 170,31,0 }, { 170,31,85 }, { 170,31,170 }, { 170,31,255 },
1355
168
    { 170,63,0 }, { 170,63,85 }, { 170,63,170 }, { 170,63,255 },
1356
168
    { 170,95,0 }, { 170,95,85 }, { 170,95,170 }, { 170,95,255 },
1357
168
    { 170,127,0 }, { 170,127,85 }, { 170,127,170 }, { 170,127,255 },
1358
168
    { 170,159,0 }, { 170,159,85 }, { 170,159,170 }, { 170,159,255 },
1359
168
    { 170,191,0 }, { 170,191,85 }, { 170,191,170 }, { 170,191,255 },
1360
168
    { 170,223,0 }, { 170,223,85 }, { 170,223,170 }, { 170,223,255 },
1361
168
    { 170,255,0 }, { 170,255,85 }, { 170,255,170 }, { 170,255,255 },
1362
168
    { 212,0,0 }, { 212,0,85 }, { 212,0,170 }, { 212,0,255 },
1363
168
    { 212,31,0 }, { 212,31,85 }, { 212,31,170 }, { 212,31,255 },
1364
168
    { 212,63,0 }, { 212,63,85 }, { 212,63,170 }, { 212,63,255 },
1365
168
    { 212,95,0 }, { 212,95,85 }, { 212,95,170 }, { 212,95,255 },
1366
168
    { 212,127,0 }, { 212,127,85 }, { 212,127,170 }, { 212,127,255 },
1367
168
    { 212,159,0 }, { 212,159,85 }, { 212,159,170 }, { 212,159,255 },
1368
168
    { 212,191,0 }, { 212,191,85 }, { 212,191,170 }, { 212,191,255 },
1369
168
    { 212,223,0 }, { 212,223,85 }, { 212,223,170 }, { 212,223,255 },
1370
168
    { 212,255,0 }, { 212,255,85 }, { 212,255,170 }, { 212,255,255 },
1371
168
    { 255,0,85 }, { 255,0,170 }, { 255,31,0 }, { 255,31,85 },
1372
168
    { 255,31,170 }, { 255,31,255 }, { 255,63,0 }, { 255,63,85 },
1373
168
    { 255,63,170 }, { 255,63,255 }, { 255,95,0 }, { 255,95,85 },
1374
168
    { 255,95,170 }, { 255,95,255 }, { 255,127,0 }, { 255,127,85 },
1375
168
    { 255,127,170 }, { 255,127,255 }, { 255,159,0 }, { 255,159,85 },
1376
168
    { 255,159,170 }, { 255,159,255 }, { 255,191,0 }, { 255,191,85 },
1377
168
    { 255,191,170 }, { 255,191,255 }, { 255,223,0 }, { 255,223,85 },
1378
168
    { 255,223,170 }, { 255,223,255 }, { 255,255,85 }, { 255,255,170 },
1379
168
    { 204,204,255 }, { 255,204,255 }, { 51,255,255 }, { 102,255,255 },
1380
168
    { 153,255,255 }, { 204,255,255 }, { 0,127,0 }, { 0,127,85 },
1381
168
    { 0,127,170 }, { 0,127,255 }, { 0,159,0 }, { 0,159,85 },
1382
168
    { 0,159,170 }, { 0,159,255 }, { 0,191,0 }, { 0,191,85 },
1383
168
    { 0,191,170 }, { 0,191,255 }, { 0,223,0 }, { 0,223,85 },
1384
168
    { 0,223,170 }, { 0,223,255 }, { 0,255,85 }, { 0,255,170 },
1385
168
    { 42,0,0 }, { 42,0,85 }, { 42,0,170 }, { 42,0,255 },
1386
168
    { 42,31,0 }, { 42,31,85 }, { 42,31,170 }, { 42,31,255 },
1387
168
    { 42,63,0 }, { 42,63,85 }, { 255,251,240 }, { 160,160,164 },
1388
168
    { 128,128,128 }, { 255,0,0 }, { 0,255,0 }, { 255,255,0 },
1389
168
    { 0,0,255 }, { 255,0,255 }, { 0,255,255 }, { 255,255,255 }
1390
168
  };
1391
1392
168
  if (size < byte_width * height)
1393
59
  {
1394
59
    WPS_DEBUG_MSG(("MSWriteParser::processDDB DDB image data missing, skipping\n"));
1395
59
    return false;
1396
59
  }
1397
1398
109
  if (planes != 1)
1399
10
  {
1400
10
    WPS_DEBUG_MSG(("MSWriteParser::processDDB DDB image has planes %d, must be 1\n", planes));
1401
10
    return false;
1402
10
  }
1403
1404
99
  WPS_DEBUG_MSG(("MSWriteParser::processDDB DDB size %ux%ux%u, byte width %u\n", width, height,
1405
99
                 bits_pixel, byte_width));
1406
1407
99
  unsigned colors;
1408
99
  switch (bits_pixel)
1409
99
  {
1410
28
  case 1:
1411
28
    colors = 2;
1412
28
    break;
1413
7
  case 4:
1414
7
    colors = 16;
1415
7
    break;
1416
6
  case 8:
1417
6
    colors = 256;
1418
6
    break;
1419
58
  default: // no palette
1420
58
    colors = 0;
1421
58
    break;
1422
99
  }
1423
1424
99
  unsigned offset = MSWriteParserInternal::BM_FILE_STRUCT_SIZE + MSWriteParserInternal::BM_INFO_V2_STRUCT_SIZE + colors * unsigned(sizeof(MSWriteParserInternal::BitmapPalette));
1425
1426
  // File header
1427
99
  bmpdata.append('B');
1428
99
  bmpdata.append('M');
1429
99
  MSWriteParserInternal::appendU32(bmpdata, offset + height * byte_width);
1430
99
  MSWriteParserInternal::appendU32(bmpdata, 0);
1431
99
  MSWriteParserInternal::appendU32(bmpdata, offset);
1432
1433
  // Info v2 header
1434
99
  MSWriteParserInternal::appendU32(bmpdata, 12);
1435
99
  MSWriteParserInternal::appendU16(bmpdata, uint16_t(width));
1436
99
  MSWriteParserInternal::appendU16(bmpdata, uint16_t(height));
1437
99
  MSWriteParserInternal::appendU16(bmpdata, uint16_t(planes));
1438
99
  MSWriteParserInternal::appendU16(bmpdata, uint16_t(bits_pixel));
1439
1440
99
  switch (bits_pixel)
1441
99
  {
1442
28
  case 1:
1443
28
    bmpdata.append(reinterpret_cast<const unsigned char *>(&pal1), sizeof(pal1));
1444
28
    break;
1445
7
  case 4:
1446
7
    bmpdata.append(reinterpret_cast<const unsigned char *>(&pal4), sizeof(pal4));
1447
7
    break;
1448
6
  case 8:
1449
6
    bmpdata.append(reinterpret_cast<const unsigned char *>(&pal8), sizeof(pal8));
1450
6
    break;
1451
58
  default:
1452
58
    break;
1453
99
  }
1454
1455
99
  RVNGInputStreamPtr input = getInput();
1456
99
  unsigned long read_bytes;
1457
99
  const unsigned char *data = input->read(size, read_bytes);
1458
99
  if (read_bytes != size)
1459
0
  {
1460
0
    WPS_DEBUG_MSG(("MSWriteParser::processDDB failed to read object\n"));
1461
0
    throw (libwps::ParseException());
1462
0
  }
1463
1464
  // Scanlines must be on 4-byte boundary, but some DDB images have
1465
  // byte_width which is not on a 4-byte boundary. Also flip the image
1466
  // vertically, DDB are stored upside down
1467
716k
  for (unsigned i=0; i<height; i++)
1468
716k
  {
1469
716k
    bmpdata.append(data + byte_width * (height - i - 1), byte_width);
1470
716k
    if (byte_width % 4)
1471
402
      bmpdata.append(data, 4 - byte_width % 4);
1472
716k
  }
1473
1474
  // default is 96 dpi
1475
99
  pos.setSize(Vec2f(float(width) / 96.0f, float(height) / 96.0f));
1476
1477
99
  return true;
1478
99
}
1479
1480
bool MSWriteParser::processDIB(librevenge::RVNGBinaryData &bmpdata, unsigned size)
1481
0
{
1482
0
  if (size < MSWriteParserInternal::BM_INFO_V3_STRUCT_SIZE)
1483
0
  {
1484
0
    WPS_DEBUG_MSG(("MSWriteParser::processDIB DIB image missing data, skipping\n"));
1485
0
    return false;
1486
0
  }
1487
1488
0
  RVNGInputStreamPtr input = getInput();
1489
0
  unsigned long read_bytes;
1490
0
  const unsigned char *data = input->read(size, read_bytes);
1491
0
  if (read_bytes != size)
1492
0
  {
1493
0
    WPS_DEBUG_MSG(("MSWriteParser::processDIB failed to read object\n"));
1494
0
    throw (libwps::ParseException());
1495
0
  }
1496
1497
0
  if (WPS_LE_GET_GUINT32(data + MSWriteParserInternal::BM_INFO_V3_SIZE) != MSWriteParserInternal::BM_INFO_V3_STRUCT_SIZE)
1498
0
  {
1499
0
    WPS_DEBUG_MSG(("MSWriteParser::processDIB DIB header incorrect size, skipping\n"));
1500
0
    return false;
1501
0
  }
1502
1503
0
  unsigned bits_pixel = WPS_LE_GET_GUINT16(data + MSWriteParserInternal::BM_INFO_V3_BITS_PIXEL);
1504
0
  unsigned colors_used = WPS_LE_GET_GUINT16(data + MSWriteParserInternal::BM_INFO_V3_COLORS_USED);
1505
1506
0
  unsigned colors = 0;
1507
0
  if (bits_pixel && bits_pixel <= 8)
1508
0
  {
1509
0
    colors = colors_used ? colors_used : 1 << bits_pixel;
1510
0
  }
1511
1512
0
  bmpdata.append('B');
1513
0
  bmpdata.append('M');
1514
0
  MSWriteParserInternal::appendU32(bmpdata, size + MSWriteParserInternal::BM_FILE_STRUCT_SIZE);
1515
0
  MSWriteParserInternal::appendU32(bmpdata, 0);
1516
0
  MSWriteParserInternal::appendU32(bmpdata, MSWriteParserInternal::BM_FILE_STRUCT_SIZE + MSWriteParserInternal::BM_INFO_V3_STRUCT_SIZE + 4 * colors);
1517
1518
0
  bmpdata.append(data, size);
1519
1520
0
  return true;
1521
0
}
1522
1523
bool MSWriteParser::processStaticOLE(librevenge::RVNGBinaryData &b, std::string &mimetype, WPSPosition &pos, unsigned long lastPos)
1524
48
{
1525
48
  RVNGInputStreamPtr input = getInput();
1526
48
  std::string objtype;
1527
1528
48
  if (!readString(objtype, lastPos))
1529
31
  {
1530
31
    return false;
1531
31
  }
1532
17
  WPS_DEBUG_MSG(("MSWriteParser::processStaticOLE object %s\n", objtype.c_str()));
1533
1534
  // Step over unused fields
1535
17
  input->seek(8, librevenge::RVNG_SEEK_CUR);
1536
1537
17
  unsigned cbSize = libwps::readU32(input);
1538
1539
17
  if (static_cast<unsigned long>(input->tell()) + cbSize > lastPos)
1540
9
  {
1541
9
    WPS_DEBUG_MSG(("MSWriteParser::processStaticOLE bad size\n"));
1542
9
    return false;
1543
9
  }
1544
1545
8
  if (objtype == "BITMAP")
1546
0
  {
1547
0
    if (cbSize < 10)
1548
0
    {
1549
0
      WPS_DEBUG_MSG(("MSWriteParser::processStaticOLE bad size for DDB\n"));
1550
0
      return false;
1551
0
    }
1552
0
    input->seek(2, librevenge::RVNG_SEEK_CUR);
1553
0
    unsigned width = libwps::readU16(input);
1554
0
    unsigned height = libwps::readU16(input);
1555
0
    unsigned byte_width = libwps::readU16(input);
1556
0
    unsigned planes = libwps::readU8(input);
1557
0
    unsigned bits_pixel = libwps::readU8(input);
1558
1559
0
    mimetype = "image/bmp";
1560
1561
0
    return processDDB(b, pos, width, height, byte_width, planes, bits_pixel, cbSize - 10);
1562
0
  }
1563
8
  else if (objtype == "DIB")
1564
0
  {
1565
0
    mimetype = "image/bmp";
1566
1567
0
    return processDIB(b, cbSize);
1568
0
  }
1569
8
  else if (objtype == "METAFILEPICT" && cbSize>8)
1570
0
  {
1571
    // Step over unused fields
1572
0
    input->seek(8, librevenge::RVNG_SEEK_CUR);
1573
0
    mimetype = "application/x-wmf";
1574
0
    return processWMF(b, cbSize - 8);
1575
0
  }
1576
1577
8
  return true;
1578
8
}
1579
1580
void MSWriteParser::processEmbeddedOLE(WPSPosition &pos, unsigned long lastPos)
1581
158
{
1582
158
  if (!checkFilePosition(uint32_t(lastPos)))
1583
0
  {
1584
0
    WPS_DEBUG_MSG(("MSWriteParser::processEmbeddedOLE last position is bad\n"));
1585
0
    return;
1586
0
  }
1587
158
  RVNGInputStreamPtr input = getInput();
1588
1589
  // First read entire Object including ole_id and type
1590
158
  long startPos = input->tell();
1591
158
  input->seek(-8, librevenge::RVNG_SEEK_CUR);
1592
1593
158
  unsigned long read_bytes;
1594
158
  unsigned long embeddedSize = lastPos - static_cast<unsigned long>(input->tell());
1595
158
  const unsigned char *data = input->read(embeddedSize, read_bytes);
1596
158
  if (read_bytes != embeddedSize)
1597
0
  {
1598
0
    WPS_DEBUG_MSG(("MSWriteParser::processEmbeddedOLE: failed to read object\n"));
1599
0
    throw (libwps::ParseException());
1600
0
  }
1601
1602
158
  librevenge::RVNGBinaryData embeddedOle;
1603
158
  embeddedOle.append(data, embeddedSize);
1604
1605
  // Seek back to beginning; parse in search of replacement object
1606
158
  input->seek(startPos, librevenge::RVNG_SEEK_SET);
1607
1608
158
  std::string strings[3];
1609
158
  unsigned i;
1610
1611
368
  for (i=0; i<3; i++)
1612
330
  {
1613
330
    if (!readString(strings[i], lastPos))
1614
120
    {
1615
120
      return;
1616
120
    }
1617
330
  }
1618
1619
38
  WPS_DEBUG_MSG(("MSWriteParser::processEmbeddedOLE Embedded OLE object %s,filename=%s,parameter=%s\n",
1620
38
                 strings[0].c_str(), strings[1].c_str(), strings[2].c_str()));
1621
38
  unsigned size = libwps::readU32(input);
1622
1623
38
  if (static_cast<unsigned long>(input->tell()) + size > lastPos)
1624
10
  {
1625
10
    WPS_DEBUG_MSG(("MSWriteParser::processEmbeddedOLE: bad size for object\n"));
1626
10
    return;
1627
10
  }
1628
1629
28
  if (strings[0] == "PBrush" || strings[0] == "Paint.Picture")
1630
0
  {
1631
    // fish out the bmp file
1632
0
    data = input->read(size, read_bytes);
1633
0
    if (read_bytes != size)
1634
0
    {
1635
0
      WPS_DEBUG_MSG(("MSWriteParser::processEmbeddedOLE: failed to read %s object\n", strings[0].c_str()));
1636
0
      throw (libwps::ParseException());
1637
0
    }
1638
1639
0
    librevenge::RVNGBinaryData bmpdata(data, size);
1640
0
    m_listener->insertPicture(pos, bmpdata, "image/bmp");
1641
0
    return;
1642
0
  }
1643
1644
28
  input->seek(long(size), librevenge::RVNG_SEEK_CUR);
1645
1646
28
  WPSEmbeddedObject obj(embeddedOle, "object/ole");
1647
28
  unsigned ole_id = libwps::readU32(input);
1648
28
  unsigned type = libwps::readU32(input);
1649
28
  librevenge::RVNGBinaryData replacement;
1650
28
  std::string mimetype;
1651
1652
28
  if (ole_id == 0x501 && type == 5 && processStaticOLE(replacement, mimetype, pos, lastPos))
1653
2
  {
1654
2
    WPS_DEBUG_MSG(("MSWriteParser::processEmbeddedOLE found replacement object of mime %s\n", mimetype.c_str()));
1655
2
    obj.add(replacement, mimetype);
1656
2
  }
1657
28
  m_listener->insertObject(pos, obj);
1658
28
}
1659
1660
bool MSWriteParser::processWMF(librevenge::RVNGBinaryData &wmfdata, unsigned size)
1661
17
{
1662
17
  WPS_DEBUG_MSG(("MSWriteParser::processWMF Windows metafile (wmf)\n"));
1663
1664
1665
17
  RVNGInputStreamPtr input = getInput();
1666
17
  unsigned long read_bytes;
1667
17
  const unsigned char *data = input->read(size, read_bytes);
1668
17
  if (read_bytes != size)
1669
0
  {
1670
0
    WPS_DEBUG_MSG(("MSWriteParser::processWMF failed to read object\n"));
1671
0
    throw (libwps::ParseException());
1672
0
  }
1673
1674
17
  wmfdata.append(data, size);
1675
1676
17
  return true;
1677
17
}
1678
1679
void MSWriteParser::readSUMD()
1680
1.39k
{
1681
  // no metadata stored in MS Write
1682
1.39k
}
1683
1684
void MSWriteParser::readFNTB()
1685
1.39k
{
1686
  // no footnotes supported in MS Write
1687
1.39k
}
1688
1689
void MSWriteParser::readStructures()
1690
5.14k
{
1691
5.14k
  RVNGInputStreamPtr input = getInput();
1692
1693
5.14k
  readFIB();
1694
5.14k
  readFFNTB();
1695
5.14k
  readSED();
1696
5.14k
  readSUMD();
1697
5.14k
  readFNTB();
1698
1699
5.14k
  input->seek(MSWriteParserInternal::HEADER_W_PNPARA, librevenge::RVNG_SEEK_SET);
1700
5.14k
  unsigned pnPara = libwps::readU16(input);
1701
5.14k
  readFOD(pnPara, &MSWriteParser::readPAP);
1702
1703
5.14k
  if (m_paragraphList.empty())
1704
549
  {
1705
549
    WPS_DEBUG_MSG(("MSWriteParser::parse: failed to read any PAP entries\n"));
1706
549
    throw (libwps::ParseException());
1707
549
  }
1708
1709
4.59k
  readFOD((m_fcMac + 127) / 128, &MSWriteParser::readCHP);
1710
4.59k
  if (m_fontList.empty())
1711
1.21k
  {
1712
1.21k
    WPS_DEBUG_MSG(("MSWriteParser::parse: failed to read any CHP entries\n"));
1713
1.21k
    throw (libwps::ParseException());
1714
1.21k
  }
1715
4.59k
}
1716
1717
void MSWriteParser::insertNote(bool annotation, uint32_t fcPos, librevenge::RVNGString const &label)
1718
12.6k
{
1719
12.6k
  std::vector<MSWriteParserInternal::Footnote>::iterator iter;
1720
1721
1.14M
  for (iter = m_footnotes.begin(); iter != m_footnotes.end(); ++iter)
1722
1.13M
  {
1723
1.13M
    if (fcPos == iter->m_fcRef)
1724
2.37k
    {
1725
2.37k
      WPSEntry e;
1726
2.37k
      e.setBegin(long(iter->m_fcFtn));
1727
2.37k
      if (++iter == m_footnotes.end())
1728
40
      {
1729
40
        WPS_DEBUG_MSG(("MSWriteParser::insertNote missing sentinel footnote\n"));
1730
40
        return;
1731
40
      }
1732
2.33k
      e.setEnd(long(iter->m_fcFtn));
1733
2.33k
      e.setType("TEXT");
1734
1735
2.33k
      if (e.valid() && e.end() <= long(m_fcMac))
1736
1.26k
      {
1737
1.26k
        WPSSubDocumentPtr subdoc(new MSWriteParserInternal::SubDocument
1738
1.26k
                                 (getInput(), *this, e, MSWriteParserInternal::Paragraph::FOOTNOTE));
1739
1.26k
        if (annotation)
1740
623
          m_listener->insertComment(subdoc);
1741
637
        else if (label.size())
1742
106
          m_listener->insertLabelNote(WPSContentListener::FOOTNOTE, label, subdoc);
1743
531
        else
1744
531
          m_listener->insertNote(WPSContentListener::FOOTNOTE, subdoc);
1745
1.26k
      }
1746
1747
2.33k
      return;
1748
2.37k
    }
1749
1.13M
  }
1750
1751
10.2k
  WPS_DEBUG_MSG(("MSWriteParser::insertNote note not found!\n"));
1752
10.2k
}
1753
1754
void MSWriteParser::startSection(MSWriteParserInternal::Section const &section)
1755
4.41k
{
1756
4.41k
  std::vector<int> widths;
1757
1758
4.41k
  if (section.m_columns > 1)
1759
854
    widths.resize(section.m_columns, int(section.m_dxaText * 1440.0));
1760
1761
4.41k
  m_listener->openSection(widths, librevenge::RVNG_TWIP);
1762
4.41k
}
1763
1764
void MSWriteParser::parse(librevenge::RVNGTextInterface *document)
1765
5.14k
{
1766
5.14k
  readStructures();
1767
1768
5.14k
  m_listener = createListener(document);
1769
5.14k
  if (!m_listener)
1770
0
  {
1771
0
    WPS_DEBUG_MSG(("MSWriteParser::parse: can not create the listener\n"));
1772
1773
0
    throw (libwps::ParseException());
1774
0
  }
1775
1776
5.14k
  m_listener->setMetaData(m_metaData);
1777
5.14k
  m_listener->startDocument();
1778
1779
5.14k
  for (auto &sect : m_sections)
1780
4.41k
  {
1781
4.41k
    startSection(sect);
1782
1783
4.41k
    readText(sect.m_Main, MSWriteParserInternal::Paragraph::MAIN);
1784
4.41k
    m_listener->closeSection();
1785
4.41k
  }
1786
1787
5.14k
  m_listener->endDocument();
1788
5.14k
  m_listener.reset();
1789
5.14k
}
1790
1791
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */