/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 §ion, 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 §ion) |
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 § : 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: */ |