/src/libwps/src/lib/WKS4.cpp
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ |
2 | | /* libwps |
3 | | * Version: MPL 2.0 / LGPLv2.1+ |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * Major Contributor(s): |
10 | | * Copyright (C) 2006, 2007 Andrew Ziem |
11 | | * Copyright (C) 2004 Marc Maurer (uwog@uwog.net) |
12 | | * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch) |
13 | | * |
14 | | * For minor contributions see the git repository. |
15 | | * |
16 | | * Alternatively, the contents of this file may be used under the terms |
17 | | * of the GNU Lesser General Public License Version 2.1 or later |
18 | | * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are |
19 | | * applicable instead of those above. |
20 | | */ |
21 | | |
22 | | #include <stdlib.h> |
23 | | #include <string.h> |
24 | | |
25 | | #include <cmath> |
26 | | #include <sstream> |
27 | | |
28 | | #include <librevenge-stream/librevenge-stream.h> |
29 | | |
30 | | #include "libwps_internal.h" |
31 | | #include "libwps_tools_win.h" |
32 | | |
33 | | #include "WKSContentListener.h" |
34 | | #include "WKSSubDocument.h" |
35 | | |
36 | | #include "WPSEntry.h" |
37 | | #include "WPSFont.h" |
38 | | #include "WPSHeader.h" |
39 | | #include "WPSPageSpan.h" |
40 | | #include "WPSStringStream.h" |
41 | | #include "WPSTable.h" |
42 | | |
43 | | #include "WKS4Chart.h" |
44 | | #include "WKS4Format.h" |
45 | | #include "WKS4Spreadsheet.h" |
46 | | |
47 | | #include "WKS4.h" |
48 | | |
49 | | using namespace libwps; |
50 | | |
51 | | //! Internal: namespace to define internal class of WKS4Parser |
52 | | namespace WKS4ParserInternal |
53 | | { |
54 | | //! the font of a WKS4Parser |
55 | | struct Font final : public WPSFont |
56 | | { |
57 | | //! constructor |
58 | 16.1k | explicit Font(libwps_tools_win::Font::Type type) : WPSFont(), m_type(type) |
59 | 16.1k | { |
60 | 16.1k | } |
61 | 37.9k | Font(Font const &)=default; |
62 | | //! destructor |
63 | | ~Font() final; |
64 | | //! font encoding type |
65 | | libwps_tools_win::Font::Type m_type; |
66 | | }; |
67 | | |
68 | | Font::~Font() |
69 | 54.1k | { |
70 | 54.1k | } |
71 | | |
72 | | //! Internal: the subdocument of a WPS4Parser |
73 | | class SubDocument final : public WKSSubDocument |
74 | | { |
75 | | public: |
76 | | //! constructor for a text entry |
77 | | SubDocument(RVNGInputStreamPtr const &input, WKS4Parser &pars, bool header) : |
78 | 22 | WKSSubDocument(input, &pars), m_header(header) {} |
79 | | //! destructor |
80 | 0 | ~SubDocument() final {} |
81 | | |
82 | | //! operator== |
83 | | virtual bool operator==(std::shared_ptr<WKSSubDocument> const &doc) const final |
84 | 0 | { |
85 | 0 | if (!doc || !WKSSubDocument::operator==(doc)) |
86 | 0 | return false; |
87 | 0 | auto const *sDoc = dynamic_cast<SubDocument const *>(doc.get()); |
88 | 0 | if (!sDoc) return false; |
89 | 0 | return m_header == sDoc->m_header; |
90 | 0 | } |
91 | | |
92 | | //! the parser function |
93 | | void parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType subDocumentType) final; |
94 | | //! a flag to known if we need to send the header or the footer |
95 | | bool m_header; |
96 | | }; |
97 | | |
98 | | void SubDocument::parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType) |
99 | 22 | { |
100 | 22 | if (!listener.get()) |
101 | 0 | { |
102 | 0 | WPS_DEBUG_MSG(("WKS4ParserInternal::SubDocument::parse: no listener\n")); |
103 | 0 | return; |
104 | 0 | } |
105 | 22 | if (!dynamic_cast<WKSContentListener *>(listener.get())) |
106 | 0 | { |
107 | 0 | WPS_DEBUG_MSG(("WKS4ParserInternal::SubDocument::parse: bad listener\n")); |
108 | 0 | return; |
109 | 0 | } |
110 | | |
111 | 22 | WKS4Parser *pser = m_parser ? dynamic_cast<WKS4Parser *>(m_parser) : nullptr; |
112 | 22 | if (!pser) |
113 | 0 | { |
114 | 0 | listener->insertCharacter(' '); |
115 | 0 | WPS_DEBUG_MSG(("WKS4ParserInternal::SubDocument::parse: bad parser\n")); |
116 | 0 | return; |
117 | 0 | } |
118 | 22 | pser->sendHeaderFooter(m_header); |
119 | 22 | } |
120 | | |
121 | | //! the state of WKS4Parser |
122 | | struct State |
123 | | { |
124 | | //! constructor |
125 | | State(libwps_tools_win::Font::Type fontType, char const *password) |
126 | 43.0k | : m_eof(-1) |
127 | 43.0k | , m_creator(libwps::WPS_MSWORKS) |
128 | 43.0k | , m_isSpreadsheet(true) |
129 | 43.0k | , m_fontType(fontType) |
130 | 43.0k | , m_version(-1) |
131 | 43.0k | , m_hasLICSCharacters(false) |
132 | 43.0k | , m_fontsList() |
133 | 43.0k | , m_pageSpan() |
134 | 43.0k | , m_actPage(0) |
135 | 43.0k | , m_numPages(0) |
136 | 43.0k | , m_headerString("") |
137 | 43.0k | , m_footerString("") |
138 | 43.0k | , m_password(password) |
139 | 43.0k | , m_isEncrypted(false) |
140 | 43.0k | , m_isDecoded(false) |
141 | 43.0k | { |
142 | 43.0k | } |
143 | | //! returns a color corresponding to an id |
144 | | bool getColor(int id, WPSColor &color) const; |
145 | | //! return the default font style |
146 | | libwps_tools_win::Font::Type getDefaultFontType() const |
147 | 679k | { |
148 | 679k | if (m_hasLICSCharacters && m_version<=2) |
149 | 52.9k | return libwps_tools_win::Font::LICS; |
150 | 626k | if (m_fontType != libwps_tools_win::Font::UNKNOWN) |
151 | 0 | return m_fontType; |
152 | 626k | if (m_version>2) |
153 | 63.4k | return libwps_tools_win::Font::WIN3_WEUROPE; |
154 | 563k | return m_creator==libwps::WPS_MSWORKS ? libwps_tools_win::Font::DOS_850 : libwps_tools_win::Font::CP_437; |
155 | 626k | } |
156 | | |
157 | | //! returns a default font (Courier12) with file's version to define the default encoding */ |
158 | | WPSFont getDefaultFont() const |
159 | 22 | { |
160 | 22 | WPSFont res; |
161 | 22 | if (m_version <= 2) |
162 | 14 | res.m_name="Courier"; |
163 | 8 | else |
164 | 8 | res.m_name="Times New Roman"; |
165 | 22 | res.m_size=12; |
166 | 22 | return res; |
167 | 22 | } |
168 | | |
169 | | //! the last file position |
170 | | long m_eof; |
171 | | //! the application |
172 | | libwps::WPSCreator m_creator; |
173 | | //! boolean to know if the file is a spreadsheet file or a database file |
174 | | bool m_isSpreadsheet; |
175 | | //! the user font type |
176 | | libwps_tools_win::Font::Type m_fontType; |
177 | | //! the file version |
178 | | int m_version; |
179 | | //! flag to know if the character |
180 | | bool m_hasLICSCharacters; |
181 | | //! the fonts list |
182 | | std::vector<Font> m_fontsList; |
183 | | //! the actual document size |
184 | | WPSPageSpan m_pageSpan; |
185 | | int m_actPage /** the actual page*/, m_numPages /* the number of pages */; |
186 | | //! the header string |
187 | | librevenge::RVNGString m_headerString; |
188 | | //! the footer string |
189 | | librevenge::RVNGString m_footerString; |
190 | | |
191 | | //! the password (if known) |
192 | | char const *m_password; |
193 | | //! true if the file is encrypted |
194 | | bool m_isEncrypted; |
195 | | //! true if the main stream has been decoded |
196 | | bool m_isDecoded; |
197 | | |
198 | | private: |
199 | | State(State const &) = delete; |
200 | | State &operator=(State const &) = delete; |
201 | | }; |
202 | | |
203 | | bool State::getColor(int id, WPSColor &color) const |
204 | 106k | { |
205 | 106k | if (m_version<=2) |
206 | 82.3k | { |
207 | 82.3k | static const uint32_t colorDosMap[]= |
208 | 82.3k | { |
209 | 82.3k | 0x0, 0xFF0000, 0x00FF00, 0x0000FF, |
210 | 82.3k | 0x00FFFF, 0xFF00FF, 0xFFFF00 |
211 | 82.3k | }; |
212 | 82.3k | if (id < 0 || id >= 7) |
213 | 58.2k | { |
214 | 58.2k | WPS_DEBUG_MSG(("WKS4ParserInternal::State::getColor(): unknown Dos color id: %d\n",id)); |
215 | 58.2k | return false; |
216 | 58.2k | } |
217 | 24.0k | color=WPSColor(colorDosMap[id]); |
218 | 24.0k | return true; |
219 | 82.3k | } |
220 | 24.6k | static const uint32_t colorMap[]= |
221 | 24.6k | { |
222 | | // 0x00RRGGBB |
223 | 24.6k | 0, // auto |
224 | 24.6k | 0, |
225 | 24.6k | 0x0000FF, 0x00FFFF, |
226 | 24.6k | 0x00FF00, 0xFF00FF, 0xFF0000, 0xFFFF00, |
227 | 24.6k | 0x808080, 0xFFFFFF, 0x000080, 0x008080, |
228 | 24.6k | 0x008000, 0x800080, 0x808000, 0xC0C0C0 |
229 | 24.6k | }; |
230 | 24.6k | if (id < 0 || id >= 16) |
231 | 0 | { |
232 | 0 | WPS_DEBUG_MSG(("WKS4ParserInternal::State::getColor(): unknown color id: %d\n",id)); |
233 | 0 | return false; |
234 | 0 | } |
235 | 24.6k | color = WPSColor(colorMap[id]); |
236 | 24.6k | return true; |
237 | 24.6k | } |
238 | | |
239 | | } |
240 | | |
241 | | // constructor, destructor |
242 | | WKS4Parser::WKS4Parser(RVNGInputStreamPtr &input, WPSHeaderPtr &header, |
243 | | libwps_tools_win::Font::Type encoding, char const *password) |
244 | 14.4k | : WKSParser(input, header) |
245 | 14.4k | , m_listener() |
246 | 14.4k | , m_state(new WKS4ParserInternal::State(encoding, password)) |
247 | 14.4k | , m_chartParser(new WKS4Chart(*this)) |
248 | 14.4k | , m_spreadsheetParser(new WKS4Spreadsheet(*this)) |
249 | 14.4k | { |
250 | 14.4k | } |
251 | | |
252 | | WKS4Parser::~WKS4Parser() |
253 | 14.4k | { |
254 | 14.4k | } |
255 | | |
256 | | int WKS4Parser::version() const |
257 | 24.2k | { |
258 | 24.2k | return m_state->m_version; |
259 | 24.2k | } |
260 | | |
261 | | libwps::WPSCreator WKS4Parser::creator() const |
262 | 29.0k | { |
263 | 29.0k | return m_state->m_creator; |
264 | 29.0k | } |
265 | | |
266 | | void WKS4Parser::resetMainInput(RVNGInputStreamPtr newInput) |
267 | 1.09k | { |
268 | 1.09k | resetInput(newInput); |
269 | 1.09k | ascii().setStream(newInput); |
270 | 1.09k | m_chartParser->resetInput(newInput); |
271 | 1.09k | m_spreadsheetParser->resetInput(newInput); |
272 | 1.09k | } |
273 | | |
274 | | bool WKS4Parser::checkFilePosition(long pos) |
275 | 2.41M | { |
276 | 2.41M | if (m_state->m_eof < 0) |
277 | 28.5k | { |
278 | 28.5k | RVNGInputStreamPtr input = getInput(); |
279 | 28.5k | long actPos = input->tell(); |
280 | 28.5k | input->seek(0, librevenge::RVNG_SEEK_END); |
281 | 28.5k | m_state->m_eof=input->tell(); |
282 | 28.5k | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
283 | 28.5k | } |
284 | 2.41M | return pos <= m_state->m_eof; |
285 | 2.41M | } |
286 | | |
287 | | libwps_tools_win::Font::Type WKS4Parser::getDefaultFontType() const |
288 | 679k | { |
289 | 679k | return m_state->getDefaultFontType(); |
290 | 679k | } |
291 | | |
292 | | libwps::WPSCreator WKS4Parser::getCreator() const |
293 | 0 | { |
294 | 0 | return m_state->m_creator; |
295 | 0 | } |
296 | | |
297 | | ////////////////////////////////////////////////////////////////////// |
298 | | // interface with WKS4Spreadsheet |
299 | | ////////////////////////////////////////////////////////////////////// |
300 | | bool WKS4Parser::getColor(int id, WPSColor &color) const |
301 | 93.9k | { |
302 | 93.9k | return m_state->getColor(id, color); |
303 | 93.9k | } |
304 | | |
305 | | bool WKS4Parser::getFont(int id, WPSFont &font, libwps_tools_win::Font::Type &type) const |
306 | 47.4k | { |
307 | 47.4k | if (id < 0 || id>=int(m_state->m_fontsList.size())) |
308 | 45.8k | { |
309 | 45.8k | WPS_DEBUG_MSG(("WKS4Parser::getFont: can not find font %d\n", id)); |
310 | 45.8k | return false; |
311 | 45.8k | } |
312 | 1.59k | auto const &ft=m_state->m_fontsList[size_t(id)]; |
313 | 1.59k | font=ft; |
314 | 1.59k | type=ft.m_type; |
315 | 1.59k | return true; |
316 | 47.4k | } |
317 | | |
318 | | librevenge::RVNGString WKS4Parser::getSheetName(int id) const |
319 | 1.04M | { |
320 | 1.04M | return m_spreadsheetParser->getSheetName(id); |
321 | 1.04M | } |
322 | | |
323 | | // main function to parse the document |
324 | | void WKS4Parser::parse(librevenge::RVNGSpreadsheetInterface *documentInterface) |
325 | 14.4k | { |
326 | 14.4k | RVNGInputStreamPtr input=getInput(); |
327 | 14.4k | if (!input) |
328 | 0 | { |
329 | 0 | WPS_DEBUG_MSG(("WKS4Parser::parse: does not find main ole\n")); |
330 | 0 | throw (libwps::ParseException()); |
331 | 0 | } |
332 | | |
333 | 14.4k | if (!checkHeader(nullptr)) throw(libwps::ParseException()); |
334 | | |
335 | 14.1k | bool ok=false; |
336 | 14.1k | try |
337 | 14.1k | { |
338 | 14.1k | ascii().setStream(input); |
339 | 14.1k | ascii().open("MN0"); |
340 | | |
341 | 14.1k | if (checkHeader(nullptr) && readZones()) |
342 | 8.52k | { |
343 | 8.52k | parseFormatStream(); |
344 | 8.52k | m_listener=createListener(documentInterface); |
345 | 8.52k | } |
346 | 14.1k | if (m_listener) |
347 | 8.52k | { |
348 | 8.52k | m_chartParser->setListener(m_listener); |
349 | 8.52k | m_spreadsheetParser->setListener(m_listener); |
350 | | |
351 | 8.52k | m_listener->startDocument(); |
352 | 8.52k | int numSheet=m_spreadsheetParser->getNumSpreadsheets(); |
353 | 8.52k | if (numSheet==0) ++numSheet; |
354 | 17.0k | for (int i=0; i<numSheet; ++i) |
355 | 8.52k | m_spreadsheetParser->sendSpreadsheet(i); |
356 | 8.52k | if (m_state->m_isSpreadsheet && m_chartParser->getNumCharts()) |
357 | 2.28k | { |
358 | 2.28k | std::vector<WPSColumnFormat> widths; |
359 | 2.28k | WPSColumnFormat width(72.); |
360 | 2.28k | width.m_numRepeat=20; |
361 | 2.28k | widths.push_back(width); |
362 | 2.28k | m_listener->openSheet(widths, "Charts"); |
363 | 2.28k | m_chartParser->sendCharts(); |
364 | 2.28k | m_listener->closeSheet(); |
365 | 2.28k | } |
366 | 8.52k | m_listener->endDocument(); |
367 | 8.52k | m_listener.reset(); |
368 | 8.52k | ok = true; |
369 | 8.52k | } |
370 | 14.1k | } |
371 | 14.1k | catch (libwps::PasswordException()) |
372 | 14.1k | { |
373 | 0 | ascii().reset(); |
374 | 0 | WPS_DEBUG_MSG(("WKS4Parser::parse: password exception catched when parsing MN0\n")); |
375 | 0 | throw (libwps::PasswordException()); |
376 | 0 | } |
377 | 14.1k | catch (...) |
378 | 14.1k | { |
379 | 56 | WPS_DEBUG_MSG(("WKS4Parser::parse: exception catched when parsing MN0\n")); |
380 | 56 | throw (libwps::ParseException()); |
381 | 56 | } |
382 | | |
383 | 14.0k | ascii().reset(); |
384 | 14.0k | if (!ok) |
385 | 5.56k | throw(libwps::ParseException()); |
386 | 14.0k | } |
387 | | |
388 | | std::shared_ptr<WKSContentListener> WKS4Parser::createListener(librevenge::RVNGSpreadsheetInterface *interface) |
389 | 8.52k | { |
390 | 8.52k | std::vector<WPSPageSpan> pageList; |
391 | 8.52k | WPSPageSpan ps(m_state->m_pageSpan); |
392 | 8.52k | if (!m_state->m_headerString.empty()) |
393 | 11 | { |
394 | 11 | WPSSubDocumentPtr subdoc(new WKS4ParserInternal::SubDocument |
395 | 11 | (getInput(), *this, true)); |
396 | 11 | ps.setHeaderFooter(WPSPageSpan::HEADER, WPSPageSpan::ALL, subdoc); |
397 | 11 | } |
398 | 8.52k | if (!m_state->m_footerString.empty()) |
399 | 11 | { |
400 | 11 | WPSSubDocumentPtr subdoc(new WKS4ParserInternal::SubDocument |
401 | 11 | (getInput(), *this, false)); |
402 | 11 | ps.setHeaderFooter(WPSPageSpan::FOOTER, WPSPageSpan::ALL, subdoc); |
403 | 11 | } |
404 | 8.52k | pageList.push_back(ps); |
405 | 8.52k | return std::shared_ptr<WKSContentListener>(new WKSContentListener(pageList, interface)); |
406 | 8.52k | } |
407 | | |
408 | | //////////////////////////////////////////////////////////// |
409 | | // low level |
410 | | //////////////////////////////////////////////////////////// |
411 | | // read the header |
412 | | //////////////////////////////////////////////////////////// |
413 | | bool WKS4Parser::checkHeader(WPSHeader *header, bool strict) |
414 | 28.5k | { |
415 | 28.5k | m_state.reset(new WKS4ParserInternal::State(m_state->m_fontType, m_state->m_password)); |
416 | 28.5k | libwps::DebugStream f; |
417 | | |
418 | 28.5k | RVNGInputStreamPtr input = getInput(); |
419 | 28.5k | if (!checkFilePosition(12)) |
420 | 108 | { |
421 | 108 | WPS_DEBUG_MSG(("WKS4Parser::checkHeader: file is too short\n")); |
422 | 108 | return false; |
423 | 108 | } |
424 | | |
425 | 28.4k | input->seek(0,librevenge::RVNG_SEEK_SET); |
426 | 28.4k | auto firstOffset = int(libwps::readU8(input)); |
427 | 28.4k | auto type = int(libwps::read8(input)); |
428 | 28.4k | bool needEncoding=true; |
429 | 28.4k | f << "FileHeader:"; |
430 | 28.4k | if ((firstOffset == 0 && type == 0) || |
431 | 15.3k | (firstOffset == 0x20 && type == 0x54)) |
432 | 22.8k | { |
433 | 22.8k | m_state->m_version=1; |
434 | 22.8k | f << "DOS,"; |
435 | 22.8k | } |
436 | 5.66k | else if (firstOffset == 0xff) |
437 | 5.66k | { |
438 | 5.66k | f << "Windows,"; |
439 | 5.66k | m_state->m_version=3; |
440 | 5.66k | needEncoding=false; |
441 | 5.66k | } |
442 | 2 | else |
443 | 2 | { |
444 | 2 | WPS_DEBUG_MSG(("WKS4Parser::checkHeader: find unexpected first data\n")); |
445 | 2 | return false; |
446 | 2 | } |
447 | 28.4k | auto creatorId=libwps::WPS_MSWORKS; |
448 | 28.4k | auto kind=libwps::WPS_SPREADSHEET; |
449 | 28.4k | bool isSpreadsheet=true; |
450 | 28.4k | if (type == 0x54) |
451 | 12.5k | { |
452 | 12.5k | isSpreadsheet=false; |
453 | 12.5k | kind=libwps::WPS_DATABASE; |
454 | 12.5k | f << "database,"; |
455 | 12.5k | } |
456 | 15.8k | else if (type == 0) |
457 | 15.8k | f << "spreadsheet,"; |
458 | 0 | else |
459 | 0 | { |
460 | 0 | WPS_DEBUG_MSG(("WKS4Parser::checkHeader: find unexpected type file\n")); |
461 | 0 | return false; |
462 | 0 | } |
463 | 28.4k | auto val=int(libwps::read16(input)); |
464 | 28.4k | if (val==2) |
465 | 28.4k | { |
466 | | // version |
467 | 28.4k | val=int(libwps::readU16(input)); |
468 | 28.4k | if (isSpreadsheet) |
469 | 15.8k | { |
470 | 15.8k | if (val==0x404) |
471 | 7.56k | { |
472 | 7.56k | } |
473 | 8.31k | else if (val==0x405) |
474 | 514 | { |
475 | 514 | f << "symphony,"; |
476 | 514 | creatorId=libwps::WPS_SYMPHONY; |
477 | 514 | } |
478 | 7.79k | else if (val==0x406) |
479 | 7.71k | { |
480 | 7.71k | m_state->m_version=1; |
481 | 7.71k | f << "lotus,"; |
482 | 7.71k | creatorId=libwps::WPS_LOTUS; |
483 | 7.71k | } |
484 | 85 | else if (val==0x5120 || val==0x5121) |
485 | 1 | { |
486 | 1 | WPS_DEBUG_MSG(("WKS4Parser::checkHeader: must not be called with a DOS Quattro file\n")); |
487 | 1 | return false; |
488 | 1 | } |
489 | 84 | else if (val==0x8006) |
490 | 0 | { |
491 | 0 | WPS_DEBUG_MSG(("WKS4Parser::checkHeader: find lotus file format, sorry parsing this format is not implemented\n")); |
492 | 0 | return false; |
493 | 0 | } |
494 | 84 | else |
495 | 84 | { |
496 | | #ifdef DEBUG |
497 | | f << "vers=" << std::hex << val << std::dec << ","; |
498 | | #else |
499 | 84 | WPS_DEBUG_MSG(("WKS4Parser::checkHeader: find unknown file version\n")); |
500 | 84 | return false; |
501 | 84 | #endif |
502 | 84 | } |
503 | 15.8k | } |
504 | 12.5k | else if (val) |
505 | 45 | return false; |
506 | 28.4k | } |
507 | 38 | else |
508 | 38 | { |
509 | 38 | WPS_DEBUG_MSG(("WKS4Parser::checkHeader: header contain unexpected size field data\n")); |
510 | 38 | return false; |
511 | 38 | } |
512 | | |
513 | 28.3k | m_state->m_creator=creatorId; |
514 | 28.3k | input->seek(0, librevenge::RVNG_SEEK_SET); |
515 | 28.3k | if (strict && m_state->m_version<1000) |
516 | 0 | { |
517 | 0 | for (int i=0; i < 4; ++i) |
518 | 0 | { |
519 | 0 | if (!readZone()) return false; |
520 | 0 | if (m_state->m_isEncrypted) break; |
521 | 0 | } |
522 | 0 | } |
523 | 28.3k | ascii().addPos(0); |
524 | 28.3k | ascii().addNote(f.str().c_str()); |
525 | | |
526 | 28.3k | m_state->m_isSpreadsheet=isSpreadsheet; |
527 | 28.3k | if (header) |
528 | 0 | { |
529 | 0 | header->setMajorVersion(uint8_t(m_state->m_version)); |
530 | 0 | header->setCreator(creatorId); |
531 | 0 | header->setKind(kind); |
532 | 0 | header->setNeedEncoding(needEncoding); |
533 | 0 | header->setIsEncrypted(m_state->m_isEncrypted); |
534 | 0 | } |
535 | 28.3k | return true; |
536 | 28.3k | } |
537 | | |
538 | | bool WKS4Parser::parseFormatStream() |
539 | 8.52k | { |
540 | 8.52k | RVNGInputStreamPtr file=getFileInput(); |
541 | 8.52k | if (!file || !file->isStructured() || !m_state->m_isSpreadsheet) |
542 | 8.52k | return false; |
543 | | |
544 | 0 | RVNGInputStreamPtr formatInput(file->getSubStreamByName("FMT")); |
545 | 0 | if (!formatInput) |
546 | 0 | { |
547 | 0 | WPS_DEBUG_MSG(("WKS4Parser::parseFormatStream: can not find the format stream\n")); |
548 | 0 | return false; |
549 | 0 | } |
550 | 0 | WKS4Format formatManager(*this, formatInput); |
551 | 0 | return formatManager.parse(); |
552 | 0 | } |
553 | | |
554 | | bool WKS4Parser::readZones() |
555 | 14.1k | { |
556 | 14.1k | RVNGInputStreamPtr input = getInput(); |
557 | 14.1k | input->seek(0, librevenge::RVNG_SEEK_SET); |
558 | 14.1k | if (version()>=1000) |
559 | 0 | { |
560 | | // error ok, we do no known how to parsed this structure |
561 | 0 | while (!input->isEnd()) |
562 | 0 | { |
563 | 0 | if (!readZoneQuattro()) |
564 | 0 | break; |
565 | 0 | } |
566 | |
|
567 | 0 | ascii().addPos(input->tell()); |
568 | 0 | ascii().addNote("Entries(UnknownZone):"); |
569 | 0 | return false; |
570 | 0 | } |
571 | | |
572 | 1.90M | while (readZone()) |
573 | 1.89M | { |
574 | 1.89M | if (m_state->m_isEncrypted && !m_state->m_isDecoded) |
575 | 56 | throw(libwps::PasswordException()); |
576 | 1.89M | } |
577 | | |
578 | | // |
579 | | // look for ending |
580 | | // |
581 | 14.0k | input = getInput(); |
582 | 14.0k | long pos = input->tell(); |
583 | 14.0k | if (!checkFilePosition(pos+4)) |
584 | 5.39k | { |
585 | 5.39k | WPS_DEBUG_MSG(("WKS4Parser::readZones: cell header is too short\n")); |
586 | 5.39k | return m_spreadsheetParser->getNumSpreadsheets()>0; |
587 | 5.39k | } |
588 | 8.70k | auto type = int(libwps::readU16(input)); // 1 |
589 | 8.70k | auto length = int(libwps::readU16(input)); |
590 | 8.70k | if (length) |
591 | 7.42k | { |
592 | 7.42k | WPS_DEBUG_MSG(("WKS4Parser::readZones: parse breaks before ending\n")); |
593 | 7.42k | ascii().addPos(pos); |
594 | 7.42k | ascii().addNote("Entries(BAD):###"); |
595 | 7.42k | return m_spreadsheetParser->getNumSpreadsheets()>0; |
596 | 7.42k | } |
597 | | |
598 | 1.27k | ascii().addPos(pos); |
599 | 1.27k | if (type != 1) |
600 | 1.20k | { |
601 | 1.20k | WPS_DEBUG_MSG(("WKS4Parser::readZones: odd end cell type: %d\n", type)); |
602 | 1.20k | ascii().addNote("Entries(BAD):###"); |
603 | 1.20k | } |
604 | 78 | else |
605 | 78 | ascii().addNote("__End"); |
606 | | |
607 | 1.27k | return true; |
608 | 8.70k | } |
609 | | |
610 | | bool WKS4Parser::readZone() |
611 | 1.90M | { |
612 | 1.90M | libwps::DebugStream f; |
613 | 1.90M | RVNGInputStreamPtr input = getInput(); |
614 | 1.90M | long pos = input->tell(); |
615 | 1.90M | auto id = int(libwps::readU8(input)); |
616 | 1.90M | auto type = int(libwps::read8(input)); |
617 | 1.90M | auto sz = long(libwps::readU16(input)); |
618 | 1.90M | if (sz<0 || !checkFilePosition(pos+4+sz)) |
619 | 12.0k | { |
620 | 12.0k | WPS_DEBUG_MSG(("WKS4Parser::readZone: size is bad\n")); |
621 | 12.0k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
622 | 12.0k | return false; |
623 | 12.0k | } |
624 | | |
625 | 1.89M | f << "Entries(Struct"; |
626 | 1.89M | if (type == 0x54) f << "A"; |
627 | 1.89M | f << std::hex << id << std::dec << "E):"; |
628 | 1.89M | bool ok = true, isParsed = false, needWriteInAscii = false; |
629 | 1.89M | int val; |
630 | 1.89M | input->seek(pos, librevenge::RVNG_SEEK_SET); |
631 | 1.89M | switch (type) |
632 | 1.89M | { |
633 | 1.28M | case 0: |
634 | 1.28M | switch (id) |
635 | 1.28M | { |
636 | | /* also |
637 | | 32: symphony windows settings(144) |
638 | | 37: password checksum(4) |
639 | | 3c: query(127) |
640 | | 3d: query name(16) |
641 | | 3e: symphony print record (679) |
642 | | 3f: printer name(16) |
643 | | 40: symphony graph record (499) |
644 | | 42: zoom(9) |
645 | | 43: number of split windows(2) |
646 | | 44: number of screen row(2) |
647 | | 45: number of screen column(2) |
648 | | 46: name ruler range(25) |
649 | | 47: name sheet range(25) |
650 | | 48: autoload comm(65) |
651 | | 49: autoexecutute macro adress(8) |
652 | | 4a: query parse information |
653 | | */ |
654 | 867k | case 0: |
655 | 867k | if (sz!=2) break; |
656 | 52.2k | f.str(""); |
657 | 52.2k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
658 | 52.2k | f << "version=" << std::hex << libwps::readU16(input) << std::dec << ","; |
659 | 52.2k | isParsed=needWriteInAscii=true; |
660 | 52.2k | break; |
661 | 86 | case 0x1: // EOF |
662 | 86 | ok = false; |
663 | 86 | break; |
664 | | // boolean |
665 | 75.5k | case 0x2: // Calculation mode 0 or FF |
666 | 78.1k | case 0x3: // Calculation order |
667 | 84.0k | case 0x4: // Split window type |
668 | 87.0k | case 0x5: // Split window syn |
669 | 88.0k | case 0x29: // label format 22|27|5e (spreadsheet) |
670 | 88.5k | case 0x30: // formatted/unformatted print 0|ff (spreadsheet) |
671 | 89.3k | case 0x31: // cursor/location 1|2 |
672 | 89.4k | case 0x38: // lock |
673 | 89.4k | f.str(""); |
674 | 89.4k | f << "Entries(Byte" << std::hex << id << std::dec << "Z):"; |
675 | 89.4k | if (sz!=1) break; |
676 | 14.9k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
677 | 14.9k | val=int(libwps::readU8(input)); |
678 | 14.9k | if (id==0x29) |
679 | 464 | f << "val=" << std::hex << val << std::dec << ","; |
680 | 14.4k | else if (id==0x31) |
681 | 439 | { |
682 | 439 | if (val!=1) f << val << ","; |
683 | 439 | } |
684 | 14.0k | else |
685 | 14.0k | { |
686 | 14.0k | if (val==0xFF) f << "true,"; |
687 | 3.85k | else if (val) f << "#val=" << val << ","; |
688 | 14.0k | } |
689 | 14.9k | isParsed=needWriteInAscii=true; |
690 | 14.9k | break; |
691 | 8.76k | case 0x6: // active worksheet range |
692 | 8.76k | ok = m_spreadsheetParser->readSheetSize(); |
693 | 8.76k | isParsed = true; |
694 | 8.76k | break; |
695 | 4.49k | case 0x7: // window 1 record |
696 | 5.09k | case 0x9: // window 2 record |
697 | 5.09k | ok = readWindowRecord(); |
698 | 5.09k | isParsed=true; |
699 | 5.09k | break; |
700 | 4.39k | case 0x8: // col width |
701 | 4.39k | ok = m_spreadsheetParser->readColumnSize(); |
702 | 4.39k | isParsed = true; |
703 | 4.39k | break; |
704 | 869 | case 0xa: // col width (window 2) |
705 | 869 | if (sz!=3) break; |
706 | 29 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
707 | | // varies in this file from 0 to 5 |
708 | 29 | f << "id=" << libwps::read16(input) << ","; |
709 | | // small number 9-13: a dim? |
710 | 29 | f << "dim?=" << libwps::read8(input) << ","; |
711 | 29 | isParsed=needWriteInAscii=true; |
712 | 29 | break; |
713 | 5.51k | case 0xb: // named range |
714 | 5.51k | ok = readFieldName(); |
715 | 5.51k | isParsed=true; |
716 | 5.51k | break; |
717 | 13.9k | case 0xc: // blank cell |
718 | 62.2k | case 0xd: // integer cell |
719 | 113k | case 0xe: // floating cell |
720 | 155k | case 0xf: // label cell |
721 | 180k | case 0x10: // formula cell |
722 | 183k | case 0x36: // continue label |
723 | 183k | ok = m_spreadsheetParser->readCell(); |
724 | 183k | isParsed = true; |
725 | 183k | break; |
726 | 411 | case 0x33: // value of string formula |
727 | 411 | ok = m_spreadsheetParser->readCellFormulaResult(); |
728 | 411 | isParsed = true; |
729 | 411 | break; |
730 | | // some spreadsheet zone ( mainly flags ) |
731 | 454 | case 0x18: // data table range |
732 | 1.34k | case 0x19: // query range |
733 | 2.64k | case 0x20: // distribution range |
734 | 3.99k | case 0x27: // print setup |
735 | 4.67k | case 0x2a: // print borders |
736 | 4.67k | ok = readUnknown1(); |
737 | 4.67k | isParsed=true; |
738 | 4.67k | break; |
739 | 2.29k | case 0x1a: // print range |
740 | 3.69k | case 0x1b: // sort range |
741 | 4.52k | case 0x1c: // fill range |
742 | 5.95k | case 0x1d: // primary sort key range |
743 | 7.31k | case 0x23: // secondary sort key range |
744 | 7.31k | { |
745 | 7.31k | int expectedSz=8; |
746 | 7.31k | f.str(""); |
747 | 7.31k | switch (id) |
748 | 7.31k | { |
749 | 2.29k | case 0x1a: // only in spreadsheet? |
750 | 2.29k | f << "Entries(PrintRange):"; |
751 | 2.29k | break; |
752 | 1.40k | case 0x1b: // a dimension or also some big selection? 31999=infinity?, related to report? |
753 | 1.40k | f << "Entries(SortRange):"; |
754 | 1.40k | break; |
755 | 828 | case 0x1c: // a dimension or also some big selection? only in spreadsheet |
756 | 828 | f << "Entries(FillRange):"; |
757 | 828 | break; |
758 | 1.42k | case 0x1d: |
759 | 1.42k | f << "Entries(PrimSort):"; |
760 | 1.42k | expectedSz=9; |
761 | 1.42k | break; |
762 | 1.36k | case 0x23: |
763 | 1.36k | f << "Entries(SecSort):"; |
764 | 1.36k | expectedSz=9; |
765 | 1.36k | break; |
766 | 0 | default: |
767 | 0 | break; |
768 | 7.31k | } |
769 | 7.31k | if (sz!=expectedSz) break; |
770 | 3.19k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
771 | 3.19k | int dim[4]; |
772 | 12.7k | for (int &i : dim) i=int(libwps::read16(input)); |
773 | | // in a spreadsheet, the cell or the cells corresponding to the field |
774 | | // in a database, col,0,col,0 |
775 | 3.19k | if (dim[0]==-1 && dim[1]==dim[0] && dim[2]==dim[0] && dim[3]==dim[0]) |
776 | 1.18k | { |
777 | 1.18k | } |
778 | 2.01k | else if (m_state->m_isSpreadsheet || dim[1] || dim[0]!= dim[2] || dim[3]) |
779 | 2.00k | { |
780 | 2.00k | f << "cell=" << dim[0] << "x" << dim[1]; |
781 | 2.00k | if (dim[0]!=dim[2] || dim[1]!=dim[3]) |
782 | 1.32k | f << "<->" << dim[2] << "x" << dim[3]; |
783 | 2.00k | f << ","; |
784 | 2.00k | } |
785 | 10 | else |
786 | 10 | f << "col=" << dim[0] << ","; |
787 | 3.19k | if (expectedSz==9) |
788 | 1.27k | { |
789 | 1.27k | val=int(libwps::readU8(input)); // 0|1|ff |
790 | 1.27k | if (val==0xFF) f << "true,"; |
791 | 975 | else if (val) f << "val=" << val << ","; |
792 | 1.27k | } |
793 | 3.19k | isParsed=needWriteInAscii=true; |
794 | 3.19k | break; |
795 | 7.31k | } |
796 | 2.67k | case 0x24: // protection (global) |
797 | 2.67k | f.str(""); |
798 | 2.67k | f << "Entries(Protection):global,"; |
799 | 2.67k | if (sz!=1) break; |
800 | 1.68k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
801 | 1.68k | val=int(libwps::readU8(input)); |
802 | 1.68k | if (val==0) |
803 | 676 | { |
804 | 676 | f.str(""); |
805 | 676 | f << "_"; |
806 | 676 | } |
807 | 1.00k | else if (val==0xFF) f << "protected,"; |
808 | 157 | else f << "#protected=" << val << ","; |
809 | 1.68k | isParsed=needWriteInAscii=true; |
810 | 1.68k | break; |
811 | 1.74k | case 0x25: // footer |
812 | 3.23k | case 0x26: // header |
813 | 3.23k | readHeaderFooter(id==0x26); |
814 | 3.23k | isParsed = true; |
815 | 3.23k | break; |
816 | 1.49k | case 0x28: // print margin |
817 | 1.49k | if (sz!=10) break; |
818 | 123 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
819 | 738 | for (int i=0; i<5; ++i) // f1=4c|96|ac|f0 |
820 | 615 | { |
821 | 615 | static const int expected[]= {4, 0x4c, 0x42, 2, 2}; |
822 | 615 | val=int(libwps::read16(input)); |
823 | 615 | if (val!=expected[i]) f << "f" << i << "=" << val << ","; |
824 | 615 | } |
825 | 123 | isParsed=needWriteInAscii=true; |
826 | 123 | break; |
827 | 40.8k | case 0x2d: // graph setting |
828 | 45.3k | case 0x2e: // named graph setting |
829 | 45.3k | m_chartParser->readChart(); |
830 | 45.3k | isParsed = true; |
831 | 45.3k | break; |
832 | 3.40k | case 0x2f: // iteration count: only in dos file Wk1, Wks(dos), Wq[12] ? |
833 | 3.40k | if (sz!=1) break; |
834 | 2.09k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
835 | 2.09k | f.str(""); |
836 | 2.09k | val=int(libwps::readU8(input)); |
837 | 2.09k | f << "Entries(ItCount):dos"; |
838 | 2.09k | if (val!=1) f << "=" << val << ","; |
839 | 543 | else if (m_state->m_version==2) |
840 | 0 | m_state->m_version=1; |
841 | 2.09k | isParsed = needWriteInAscii = true; |
842 | 2.09k | break; |
843 | 327 | case 0x41: // graph record name |
844 | 327 | m_chartParser->readChartName(); |
845 | 327 | isParsed = true; |
846 | 327 | break; |
847 | 2.39k | case 0x4b: |
848 | 2.39k | if (sz==2 && m_state->m_creator==libwps::WPS_LOTUS) |
849 | 1.60k | { |
850 | 1.60k | m_state->m_isEncrypted=true; |
851 | 1.60k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
852 | 1.60k | f.str(""); |
853 | 1.60k | uint16_t fileKey(libwps::readU16(input)); |
854 | 1.60k | f << "Entries(Password):pass=" << std::hex << fileKey << std::dec << ","; |
855 | 1.60k | isParsed = needWriteInAscii = true; |
856 | 1.60k | if (!m_state->m_isDecoded) |
857 | 1.15k | { |
858 | 1.15k | static uint8_t const defValues[]= |
859 | 1.15k | { |
860 | 1.15k | 0xbb,0xff, 0xff,0xba, 0xff,0xff, 0xb9,0x80, |
861 | 1.15k | 0,0x0be, 0xf,0, 0xbf,0xf, 0,0 |
862 | 1.15k | }; |
863 | 1.15k | uint16_t key; |
864 | 1.15k | std::vector<uint8_t> keys; |
865 | 1.15k | if (m_state->m_password && libwps::encodeLotusPassword(m_state->m_password, key, keys, defValues)) |
866 | 1.15k | { |
867 | 1.15k | RVNGInputStreamPtr newInput; |
868 | 1.15k | if (uint16_t(key<<8|key>>8)==fileKey) |
869 | 1.09k | newInput=decodeStream(input, m_state->m_eof, keys); |
870 | 1.15k | if (newInput) |
871 | 1.09k | { |
872 | | // let's replace the current input by the decoded input |
873 | 1.09k | m_state->m_isDecoded=true; |
874 | 1.09k | input=newInput; |
875 | 1.09k | resetMainInput(newInput); |
876 | 1.09k | } |
877 | 56 | else |
878 | 56 | { |
879 | 56 | WPS_DEBUG_MSG(("WKS4Parser::parse: the password seems bad\n")); |
880 | 56 | } |
881 | | |
882 | 1.15k | } |
883 | 1.15k | } |
884 | 1.60k | break; |
885 | 1.60k | } |
886 | 788 | else |
887 | 788 | { |
888 | 788 | WPS_DEBUG_MSG(("WKS4Parser::parse: find unexpected password field\n")); |
889 | 788 | } |
890 | 788 | break; |
891 | 788 | case 0x64: // hidden column |
892 | 472 | isParsed = m_spreadsheetParser->readHiddenColumns(); |
893 | 472 | break; |
894 | | |
895 | 46.3k | default: |
896 | 46.3k | break; |
897 | 1.28M | } |
898 | 1.28M | break; |
899 | 1.28M | case 0x54: |
900 | 610k | switch (id) |
901 | 610k | { |
902 | | // always empty ? |
903 | 7.67k | case 0x25: |
904 | 7.67k | f.str(""); |
905 | 7.67k | f << "Entries(LICS):"; |
906 | 7.67k | if (sz) |
907 | 435 | { |
908 | 435 | f << "###"; |
909 | 435 | WPS_DEBUG_MSG(("WKS4Parser::readZone: find a not empty LICS encoding zone\n")); |
910 | 435 | break; |
911 | 435 | } |
912 | 7.24k | m_state->m_hasLICSCharacters = true; |
913 | 7.24k | isParsed = needWriteInAscii = true; |
914 | 7.24k | break; |
915 | | // boolean |
916 | 3.65k | case 0x6f: // always 0 |
917 | 3.65k | f.str(""); |
918 | 3.65k | f << "Entries(ByteA" << std::hex << id << std::dec << "Z):"; |
919 | 3.65k | if (sz!=1) break; |
920 | 2.23k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
921 | 2.23k | val=int(libwps::readU8(input)); |
922 | 2.23k | if (val==0xFF) f << "true,"; |
923 | 67 | else if (val) f << "#val=" << val << ","; |
924 | 2.23k | isParsed=needWriteInAscii=true; |
925 | 2.23k | break; |
926 | | // small int zone ? |
927 | 254 | case 0x12: // sometimes in spreadsheet (with 0) |
928 | 418 | case 0x1a: // find at at the end the file, after the reports' definition |
929 | 418 | f.str(""); |
930 | 418 | f << "Entries(IntSmallA" << std::hex << id << std::dec << "Z):"; |
931 | 418 | if (sz!=1) break; |
932 | 104 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
933 | 104 | val=int(libwps::readU8(input)); |
934 | 104 | if (id==0x1a) |
935 | 48 | { |
936 | 48 | f.str(""); |
937 | 48 | f << "Entries(Report):act=" << val << ","; |
938 | 48 | } |
939 | 56 | else |
940 | 56 | { |
941 | 56 | if (val) f << "#val=" << val << ","; |
942 | 56 | } |
943 | 104 | isParsed=needWriteInAscii=true; |
944 | 104 | break; |
945 | | // int zone |
946 | 1.90k | case 0x26: // always with 0 |
947 | 2.25k | case 0x6a: // filter definition? |
948 | 2.25k | f.str(""); |
949 | 2.25k | if (id==0x6a) |
950 | 350 | f << "Entries(Filter)[data1]:"; |
951 | 1.90k | else |
952 | 1.90k | f << "Entries(IntA" << std::hex << id << std::dec << "Z):"; |
953 | 2.25k | if (sz!=2) break; |
954 | 2.08k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
955 | 2.08k | val=int(libwps::read16(input)); |
956 | 2.08k | if (val) f << "f0=" << val << ","; |
957 | 2.08k | isParsed=needWriteInAscii=true; |
958 | 2.08k | break; |
959 | | // zone with 2 ints |
960 | 359 | case 0x32: // find with 00000000 (database) |
961 | 359 | f.str(""); |
962 | 359 | f << "Entries(Int2A" << std::hex << id << std::dec << "Z):"; |
963 | 359 | if (sz!=4) break; |
964 | 113 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
965 | 339 | for (int i=0; i<2; ++i) |
966 | 226 | { |
967 | 226 | val=int(libwps::read16(input)); |
968 | 226 | if (val) f << "f" << i << "=" << val << ","; |
969 | 226 | } |
970 | 113 | isParsed=needWriteInAscii=true; |
971 | 113 | break; |
972 | 8.90k | case 0x1: // the last selected cell |
973 | 8.90k | { |
974 | 8.90k | f.str(""); |
975 | 8.90k | f << "Entries(SelectCells):"; |
976 | 8.90k | if (sz!=0xc) break; |
977 | 8.23k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
978 | 8.23k | val = int(libwps::read16(input)); // always 0? |
979 | 8.23k | if (val) f << "f0=" << val << ","; |
980 | 8.23k | int dim[4]; |
981 | 32.9k | for (int &i : dim) i = int(libwps::read16(input)); |
982 | 8.23k | if (dim[2]==dim[0]+1 && dim[3]==dim[1]+1) // almost always true |
983 | 576 | f << "cell?=" << dim[0] << "x" << dim[1] << ","; |
984 | 7.65k | else |
985 | 7.65k | f << "cells?=" << dim[0] << "x" << dim[1] << "<->" << dim[2] << "x" << dim[3] << ","; |
986 | 8.23k | val = int(libwps::read16(input)); // always 0|2 |
987 | 8.23k | if (val) f << "f1=" << val << ","; |
988 | 8.23k | isParsed = needWriteInAscii = true; |
989 | 8.23k | break; |
990 | 8.90k | } |
991 | 51.4k | case 0x2: |
992 | 51.4k | ok = m_spreadsheetParser->readMsWorksDOSCellProperty(); |
993 | 51.4k | isParsed = true; |
994 | 51.4k | break; |
995 | 7.54k | case 0x5: |
996 | 7.54k | if (sz!=2) break; |
997 | 5.35k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
998 | 5.35k | f.str(""); |
999 | 5.35k | f << "Entries(Version):vers=" << std::hex << libwps::readU16(input) << std::dec; |
1000 | 5.35k | isParsed = needWriteInAscii = true; |
1001 | 5.35k | break; |
1002 | 4.96k | case 0x6: |
1003 | 4.96k | ok = m_spreadsheetParser->readMsWorksDOSFieldProperty(); |
1004 | 4.96k | isParsed = true; |
1005 | 4.96k | break; |
1006 | 830 | case 0x8: // only in database?, checkme: the structure may be different in dosfile |
1007 | 830 | if (sz!=0x18) break; |
1008 | 187 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
1009 | 1.30k | for (int i=0; i<6; ++i) // f0=2|7, f2=0|1|2|4|5|7, f4=0|1|4|5|6|17|19|37|114, f5=3..40 |
1010 | 1.12k | { |
1011 | 1.12k | val=int(libwps::read16(input)); |
1012 | 1.12k | if (val) f << "f" << i << "=" << val << ","; |
1013 | 1.12k | } |
1014 | 935 | for (int i=0; i<4; ++i) // g0=0|12|38|59|71, g1=0|1, g3=1|2|3 |
1015 | 748 | { |
1016 | 748 | int const expected[]= {0,1,0,2}; |
1017 | 748 | val=int(libwps::read8(input)); |
1018 | 748 | if (val!=expected[i]) f << "g" << i << "=" << val << ","; |
1019 | 748 | } |
1020 | 935 | for (int i=0; i<4; ++i) // h0=0|28, h1=0|9 |
1021 | 748 | { |
1022 | 748 | val=int(libwps::read16(input)); |
1023 | 748 | if (val) f << "h" << i << "=" << val << ","; |
1024 | 748 | } |
1025 | 187 | isParsed = needWriteInAscii = true; |
1026 | 187 | break; |
1027 | | /* case 9: 000004002f001e000000bccf000005000f0008000000bccf0000060003000f007404bccf01000600 |
1028 | | 1c0000001b000100010007001c0001001e00010000000900300016007404bccf00000b000f0000000000d6ce |
1029 | | ( database, find one time) |
1030 | | */ |
1031 | | /* case a: (database) |
1032 | | CHECKME: a long structure which seems to contain some text, a list of field? |
1033 | | */ |
1034 | 3.58k | case 0x10: |
1035 | 3.58k | ok = m_spreadsheetParser->readFilterOpen(); |
1036 | 3.58k | isParsed = true; |
1037 | 3.58k | break; |
1038 | 4.39k | case 0x11: |
1039 | 4.39k | ok = m_spreadsheetParser->readFilterClose(); |
1040 | 4.39k | isParsed = true; |
1041 | 4.39k | break; |
1042 | 4.16k | case 0x13: |
1043 | 4.16k | ok = m_spreadsheetParser->readMsWorksPageBreak(); |
1044 | 4.16k | isParsed = true; |
1045 | 4.16k | break; |
1046 | 30.9k | case 0x14: |
1047 | 30.9k | m_chartParser->readChartAxis(); |
1048 | 30.9k | isParsed = true; |
1049 | 30.9k | break; |
1050 | 1.34k | case 0x15: |
1051 | 1.34k | m_chartParser->readChartSeries(); |
1052 | 1.34k | isParsed = true; |
1053 | 1.34k | break; |
1054 | 5.82k | case 0x16: |
1055 | 5.82k | m_chartParser->readChartSeriesStyles(); |
1056 | 5.82k | isParsed = true; |
1057 | 5.82k | break; |
1058 | 290k | case 0x17: |
1059 | 290k | ok=m_spreadsheetParser->readReportOpen(); |
1060 | 290k | isParsed = true; |
1061 | 290k | break; |
1062 | 558 | case 0x18: |
1063 | 558 | ok=m_spreadsheetParser->readReportClose(); |
1064 | 558 | isParsed = true; |
1065 | 558 | break; |
1066 | | |
1067 | 501 | case 0x30: // 30540c00000000000000000000000000 or 30540c00ffff00000000000000000000 |
1068 | 501 | f.str(""); |
1069 | 501 | f << "Entries(ChartUnknA):"; |
1070 | 501 | break; |
1071 | 1.08k | case 0x31: |
1072 | 1.08k | m_chartParser->readChartSeriesColorMap(); |
1073 | 1.08k | isParsed = true; |
1074 | 1.08k | break; |
1075 | 2.23k | case 0x35: |
1076 | 2.23k | m_chartParser->readChartDim(); |
1077 | 2.23k | isParsed = true; |
1078 | 2.23k | break; |
1079 | 254 | case 0x38: // find block of f2 bytes always 0 |
1080 | 521 | case 0x39: // find block of f2 bytes always 0 |
1081 | 521 | f.str(""); |
1082 | 521 | f << "Entries(" << (id==0x38 ? "ChartUnknB" : "ChartUnknC") << "):"; |
1083 | 521 | break; |
1084 | 253 | case 0x41: // 41540600000000000000 |
1085 | 253 | f.str(""); |
1086 | 253 | f << "Entries(ChartUnknD):"; |
1087 | 253 | break; |
1088 | 1.25k | case 0x44: |
1089 | 1.25k | m_chartParser->readChart3D(); |
1090 | 1.25k | isParsed = true; |
1091 | 1.25k | break; |
1092 | | /* find also 83: 835405000[02]ffffffff |
1093 | | 85: block of 6c bytes always 0 |
1094 | | 86: 86540600010001000100 |
1095 | | 87: 87540c00880c00080a00080a00080a00 or 87540c00880c00880a18880a18880a18 |
1096 | | related to chart in Works 4 and Works 2000 */ |
1097 | | |
1098 | 676 | case 0x19: // list id<->unkn, found after the column definition and Struct 66:54 in report, with f0=0|5|8|9 |
1099 | 1.77k | case 0x5e: // some time in can be repeated a spreadsheet often with f0=9000 |
1100 | 1.77k | if (id==0x19) |
1101 | 676 | { |
1102 | 676 | f.str(""); |
1103 | 676 | f << "Report[data1]:"; |
1104 | 676 | } |
1105 | 1.77k | if (sz!=4) break; |
1106 | 234 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
1107 | 234 | f << "id=" << libwps::read16(input) << ","; |
1108 | 234 | val=int(libwps::readU16(input)); |
1109 | 234 | if (val) f << "f0=" << std::hex << val << std::dec << ","; |
1110 | 234 | isParsed=needWriteInAscii=true; |
1111 | 234 | break; |
1112 | | /* case 1b: 000000000000000000000000000000000000010000000000020000000000000000000000000000000000 |
1113 | | database v1 */ |
1114 | 3.62k | case 0x1c: |
1115 | 3.62k | m_spreadsheetParser->readMsWorksDOSCellExtraProperty(); |
1116 | 3.62k | isParsed = true; |
1117 | 3.62k | break; |
1118 | 889 | case 0x1f: |
1119 | 889 | { |
1120 | | // frequent field, near the beginning of the file |
1121 | | // find with 050300000005|058000000005|05000000, so maybe: |
1122 | 889 | if (sz<4 || (sz%2)!=0) break; |
1123 | 671 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
1124 | 671 | val=int(libwps::read8(input)); // always 5? |
1125 | 671 | if (val!=5) f << "f0=" << val << ","; |
1126 | 671 | val=int(libwps::readU8(input)); // 0|80 |
1127 | 671 | if (val) f << "f1=" << std::hex << val << ","; |
1128 | 2.61k | for (long i=1; i<sz/2; ++i) |
1129 | 1.94k | { |
1130 | 1.94k | val=int(libwps::read16(input)); |
1131 | 1.94k | if (val) f << "f" << i+2 << "=" << val << ","; |
1132 | 1.94k | } |
1133 | 671 | isParsed=needWriteInAscii=true; |
1134 | 671 | break; |
1135 | 889 | } |
1136 | 314 | case 0x23: // single page ? |
1137 | 2.04k | case 0x37: // multiple page ? |
1138 | 2.04k | ok = readPrnt(); |
1139 | 2.04k | isParsed = true; |
1140 | 2.04k | break; |
1141 | 1.04k | case 0x24: // font (default) |
1142 | 1.04k | f.str(""); |
1143 | 1.04k | f << "Entries(FontDef):"; |
1144 | 1.04k | if (sz!=4) break; |
1145 | 567 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
1146 | 567 | val=int(libwps::read16(input)); |
1147 | 567 | if (val) f << "fId=" << val << ","; |
1148 | 567 | f << "fSz=" << libwps::read16(input)/2 << ","; |
1149 | 567 | isParsed=needWriteInAscii=true; |
1150 | 567 | break; |
1151 | 1.52k | case 0x27: |
1152 | 1.52k | ok = m_spreadsheetParser->readMsWorksDOSPageBreak(); |
1153 | 1.52k | isParsed = true; |
1154 | 1.52k | break; |
1155 | 625 | case 0x33: |
1156 | 625 | f.str(""); |
1157 | 625 | f << "Entries(Protection)[form]:"; |
1158 | 625 | if (sz!=1) break; |
1159 | 437 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
1160 | 437 | val=int(libwps::readU8(input)); |
1161 | 437 | if (val==0) |
1162 | 350 | { |
1163 | 350 | f.str(""); |
1164 | 350 | f << "_"; |
1165 | 350 | } |
1166 | 87 | else if (val==0xFF) f << "protected,"; |
1167 | 55 | else f << "#protected=" << val << ","; |
1168 | 437 | isParsed=needWriteInAscii=true; |
1169 | 437 | break; |
1170 | 957 | case 0x40: |
1171 | 957 | m_chartParser->readChartFont(); |
1172 | 957 | isParsed = true; |
1173 | 957 | break; |
1174 | | // case 47: big zone, begin by a font name (database) |
1175 | | // case 50: 010000000000000000000000000000000000 |
1176 | | // case 53: CHECKME: looks like b013cc06d00764000000000001000000 ( database v1) |
1177 | 16.4k | case 0x56: |
1178 | 16.4k | ok = readFont(); |
1179 | 16.4k | isParsed = true; |
1180 | 16.4k | break; |
1181 | 78 | case 0x48: // a fontname + 2 ints? (find one time in a spreadsheet file) |
1182 | 2.17k | case 0x57: // int + a fontname + 2 ints? (in database a little after the field name zone) |
1183 | 2.17k | { |
1184 | 2.17k | int const headerSize= id==0x57 ? 2 : 0; |
1185 | 2.17k | f.str(""); |
1186 | 2.17k | f << "Entries(Prefs)[" << std::hex << id << std::dec << "]:"; |
1187 | 2.17k | if (sz!=0x24+headerSize) break; |
1188 | 1.69k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
1189 | 1.69k | if (id==0x57) |
1190 | 1.67k | { |
1191 | 1.67k | val=int(libwps::read16(input)); // always 0? |
1192 | 1.67k | if (val) f << "f0=" << val << ","; |
1193 | 1.67k | } |
1194 | 1.69k | librevenge::RVNGString name; |
1195 | 1.69k | if (!readCString(name,32)) |
1196 | 0 | f << "##name,"; |
1197 | 1.69k | else if (!name.empty()) |
1198 | 514 | f << name.cstr() << ","; |
1199 | 1.69k | input->seek(pos+36+headerSize, librevenge::RVNG_SEEK_SET); |
1200 | 1.69k | val=int(libwps::read16(input)); // 10|20|30|50: some flags? |
1201 | 1.69k | if (val!=0x10) f << "f1=" << std::hex << val << std::dec << ","; |
1202 | 1.69k | val=int(libwps::read16(input)); // 14|18 |
1203 | 1.69k | if (val!=0x18) f << "f2=" << std::hex << val << std::dec << ","; |
1204 | 1.69k | isParsed=needWriteInAscii=true; |
1205 | 1.69k | break; |
1206 | 2.17k | } |
1207 | 377 | case 0x58: |
1208 | 377 | { |
1209 | 377 | f.str(""); |
1210 | 377 | f << "Entries(Filter)[name]:"; |
1211 | 377 | if (sz!=16) break; |
1212 | 229 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
1213 | 229 | librevenge::RVNGString name; |
1214 | 229 | if (!readCString(name,16)) |
1215 | 0 | f << "##name,"; |
1216 | 229 | else if (!name.empty()) |
1217 | 48 | f << name.cstr() << ","; |
1218 | 229 | isParsed=needWriteInAscii=true; |
1219 | 229 | break; |
1220 | 377 | } |
1221 | 47.4k | case 0x5a: |
1222 | 47.4k | ok = m_spreadsheetParser->readMsWorksStyle(); |
1223 | 47.4k | isParsed = true; |
1224 | 47.4k | break; |
1225 | 5.07k | case 0x5b: |
1226 | 5.07k | ok = m_spreadsheetParser->readCell(); |
1227 | 5.07k | isParsed = true; |
1228 | 5.07k | break; |
1229 | | // case 5c: a small number 0-8 (database) |
1230 | 584 | case 0x5d: // checkme |
1231 | 584 | f.str(""); |
1232 | 584 | f << "FldProperties:"; |
1233 | 584 | if (sz!=4) break; |
1234 | 315 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
1235 | 315 | f << "col=" << libwps::read16(input) << ","; |
1236 | 315 | f << "form?=" << std::hex << libwps::readU16(input) << std::dec << ","; |
1237 | 315 | break; |
1238 | 863 | case 0x5f: |
1239 | 863 | { |
1240 | | // fixme: read end of fields |
1241 | 863 | f.str(""); |
1242 | 863 | f << "Entries(FormZones):"; |
1243 | 863 | if (sz<0x4d) break; |
1244 | 218 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
1245 | 218 | int zType=libwps::read16(input); |
1246 | 218 | switch (zType) |
1247 | 218 | { |
1248 | 31 | case 1: |
1249 | 31 | f << "field,"; |
1250 | 31 | break; |
1251 | 180 | case 2: |
1252 | 180 | f << "textbox,"; |
1253 | 180 | break; |
1254 | 0 | case 3: |
1255 | 0 | f << "object,"; |
1256 | 0 | break; |
1257 | 0 | case 4: |
1258 | 0 | f << "rectangle,"; |
1259 | 0 | break; |
1260 | 7 | default: |
1261 | 7 | WPS_DEBUG_MSG(("WKS4Parser::readZone: find unknown zone type\n")); |
1262 | 7 | f << "##type=" << zType << ","; |
1263 | 7 | break; |
1264 | 218 | } |
1265 | 218 | if (input->tell()!=pos+4+sz) |
1266 | 218 | ascii().addDelimiter(input->tell(),'|'); |
1267 | 218 | isParsed=needWriteInAscii=true; |
1268 | 218 | break; |
1269 | 218 | } |
1270 | 1.01k | case 0x64: // present in database (can to store some block: graphic?) |
1271 | 1.01k | { |
1272 | 1.01k | if (sz!=4) break; |
1273 | 755 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
1274 | 755 | auto dataSz=long(libwps::readU32(input)); |
1275 | 755 | if (!checkFilePosition(pos+8+dataSz)) break; |
1276 | 508 | if (dataSz) f << "dSz=" << std::hex << dataSz << std::dec << ","; |
1277 | 508 | ascii().addPos(pos); |
1278 | 508 | ascii().addNote(f.str().c_str()); |
1279 | 508 | if (dataSz) |
1280 | 189 | { |
1281 | 189 | ascii().addPos(pos+8); |
1282 | 189 | ascii().addNote("Entries(StructA64E)[data]:"); |
1283 | 189 | sz += dataSz; |
1284 | 189 | } |
1285 | 508 | isParsed = true; |
1286 | 508 | break; |
1287 | 755 | } |
1288 | 7.29k | case 0x65: |
1289 | 7.29k | ok = m_spreadsheetParser->readMsWorksRowSize(); |
1290 | 7.29k | isParsed = true; |
1291 | 7.29k | break; |
1292 | | // case 66: ff|12c|13B|1d1, dim/flag? (database) |
1293 | 126 | case 0x67: // single page ? |
1294 | 378 | case 0x82: // multiple page ? |
1295 | 378 | ok = readPrn2(); |
1296 | 378 | isParsed = true; |
1297 | 378 | break; |
1298 | 3.32k | case 0x6b: |
1299 | 3.32k | ok = m_spreadsheetParser->readMsWorksColumnSize(); |
1300 | 3.32k | isParsed = true; |
1301 | 3.32k | break; |
1302 | 316 | case 0x6e: // field(series) |
1303 | 316 | f.str(""); |
1304 | 316 | f << "Entries(FldSeries):"; |
1305 | 316 | if (sz!=8) break; |
1306 | 53 | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
1307 | 53 | val=int(libwps::read16(input)); |
1308 | 53 | if (val) f << "col=" << val << ","; |
1309 | 53 | f << "act[val]=" << libwps::read16(input) << ","; |
1310 | 53 | val=int(libwps::read16(input)); // always 0 first? |
1311 | 53 | if (val) f << "first=" << val << ","; |
1312 | 53 | val=int(libwps::read16(input)); |
1313 | 53 | if (val!=1) f << "increm=" << val << ","; |
1314 | 53 | isParsed=needWriteInAscii=true; |
1315 | 53 | break; |
1316 | | // case 70: id? (database) |
1317 | 1.46k | case 0x80: |
1318 | 2.30k | case 0x81: |
1319 | 2.30k | m_chartParser->readChartLimit(); |
1320 | 2.30k | isParsed = true; |
1321 | 2.30k | break; |
1322 | 1.31k | case 0x84: |
1323 | 1.31k | m_chartParser->readChart2Font(); |
1324 | 1.31k | isParsed = true; |
1325 | 1.31k | break; |
1326 | 73.5k | default: |
1327 | 73.5k | break; |
1328 | 610k | } |
1329 | 610k | break; |
1330 | 610k | default: |
1331 | 1.51k | ok = false; |
1332 | 1.51k | break; |
1333 | 1.89M | } |
1334 | | |
1335 | 1.89M | if (!ok) |
1336 | 2.01k | { |
1337 | 2.01k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1338 | 2.01k | return false; |
1339 | 2.01k | } |
1340 | 1.89M | if (isParsed) |
1341 | 861k | { |
1342 | 861k | if (needWriteInAscii) |
1343 | 105k | { |
1344 | 105k | ascii().addPos(pos); |
1345 | 105k | ascii().addNote(f.str().c_str()); |
1346 | 105k | } |
1347 | 861k | input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET); |
1348 | 861k | return true; |
1349 | 861k | } |
1350 | | |
1351 | 1.03M | if (sz && input->tell()!=pos && input->tell()!=pos+4+sz) |
1352 | 151 | ascii().addDelimiter(input->tell(),'|'); |
1353 | 1.03M | input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET); |
1354 | 1.03M | ascii().addPos(pos); |
1355 | 1.03M | ascii().addNote(f.str().c_str()); |
1356 | 1.03M | return true; |
1357 | 1.89M | } |
1358 | | |
1359 | | //////////////////////////////////////////////////////////// |
1360 | | // other formats |
1361 | | //////////////////////////////////////////////////////////// |
1362 | | bool WKS4Parser::readZoneQuattro() |
1363 | 0 | { |
1364 | 0 | libwps::DebugStream f; |
1365 | 0 | RVNGInputStreamPtr input = getInput(); |
1366 | 0 | long pos = input->tell(); |
1367 | 0 | auto id = int(libwps::readU8(input)); |
1368 | 0 | auto type = int(libwps::readU8(input)); |
1369 | 0 | auto sz = long(libwps::readU16(input)); |
1370 | 0 | if (type>5 || sz<0 || !checkFilePosition(pos+4+sz)) |
1371 | 0 | { |
1372 | 0 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1373 | 0 | return false; |
1374 | 0 | } |
1375 | 0 | f << "Entries(Quattro"; |
1376 | 0 | if (type) f << type << "A"; |
1377 | 0 | f << std::hex << id << std::dec << "E):"; |
1378 | 0 | if (sz) ascii().addDelimiter(pos+4,'|'); |
1379 | 0 | input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET); |
1380 | 0 | ascii().addPos(pos); |
1381 | 0 | ascii().addNote(f.str().c_str()); |
1382 | 0 | return true; |
1383 | 0 | } |
1384 | | |
1385 | | //////////////////////////////////////////////////////////// |
1386 | | // generic |
1387 | | //////////////////////////////////////////////////////////// |
1388 | | bool WKS4Parser::readCString(librevenge::RVNGString &string, long maxSize) |
1389 | 462k | { |
1390 | 462k | RVNGInputStreamPtr input = getInput(); |
1391 | 462k | long pos = input->tell(); |
1392 | 462k | string.clear(); |
1393 | 462k | if (!checkFilePosition(pos+maxSize)) |
1394 | 0 | { |
1395 | 0 | WPS_DEBUG_MSG(("WKS4Parser::readCString: string's size seems bad\n")); |
1396 | 0 | return false; |
1397 | 0 | } |
1398 | 462k | std::string text; |
1399 | 2.16M | for (long i=0; i<maxSize; ++i) |
1400 | 2.14M | { |
1401 | 2.14M | auto c = char(libwps::readU8(input)); |
1402 | 2.14M | if (c == '\0') break; |
1403 | 1.69M | text.push_back(c); |
1404 | 1.69M | } |
1405 | 462k | if (!text.empty()) |
1406 | 222k | string=libwps_tools_win::Font::unicodeString(text, getDefaultFontType()); |
1407 | 462k | return true; |
1408 | 462k | } |
1409 | | |
1410 | | bool WKS4Parser::readFont() |
1411 | 16.4k | { |
1412 | 16.4k | libwps::DebugStream f; |
1413 | 16.4k | RVNGInputStreamPtr input = getInput(); |
1414 | 16.4k | long pos = input->tell(); |
1415 | 16.4k | auto type = int(libwps::read16(input)); |
1416 | | |
1417 | 16.4k | if (type != 0x5456) |
1418 | 0 | { |
1419 | 0 | WPS_DEBUG_MSG(("WKS4Parser::readFont: not a font zone\n")); |
1420 | 0 | return false; |
1421 | 0 | } |
1422 | 16.4k | auto sz = long(libwps::readU16(input)); |
1423 | 16.4k | long endPos = pos+4+sz; |
1424 | 16.4k | if (sz < 32) |
1425 | 294 | { |
1426 | 294 | WPS_DEBUG_MSG(("WKS4Parser::readFont: seems very short\n")); |
1427 | 294 | ascii().addPos(pos); |
1428 | 294 | ascii().addNote("Entries(Font)###"); |
1429 | 294 | return true; |
1430 | 294 | } |
1431 | | |
1432 | 16.1k | WKS4ParserInternal::Font font(getDefaultFontType()); |
1433 | 16.1k | auto flags = int(libwps::readU8(input)); |
1434 | 16.1k | uint32_t attributes = 0; |
1435 | 16.1k | if (flags & 1) attributes |= WPS_BOLD_BIT; |
1436 | 16.1k | if (flags & 2) attributes |= WPS_ITALICS_BIT; |
1437 | 16.1k | if (flags & 4) attributes |= WPS_UNDERLINE_BIT; |
1438 | 16.1k | if (flags & 8) attributes |= WPS_STRIKEOUT_BIT; |
1439 | | |
1440 | 16.1k | font.m_attributes=attributes; |
1441 | 16.1k | if (flags & 0xF0) |
1442 | 12.9k | { |
1443 | 12.9k | if (!m_state->getColor((flags >> 4), font.m_color)) |
1444 | 6.35k | { |
1445 | 6.35k | WPS_DEBUG_MSG(("WKS4Parser::readFont: unknown color\n")); |
1446 | 6.35k | f << "##color=" << (flags >> 4) << ","; |
1447 | 6.35k | } |
1448 | 12.9k | } |
1449 | | |
1450 | 16.1k | auto val=int(libwps::readU8(input)); |
1451 | 16.1k | if (val) f << "f0=" << std::hex << val << std::dec << ","; |
1452 | 16.1k | librevenge::RVNGString name(""); |
1453 | 217k | while (int(input->tell()) < endPos-4) |
1454 | 216k | { |
1455 | 216k | auto c = char(libwps::readU8(input)); |
1456 | 216k | if (c == '\0') break; |
1457 | 201k | name.append(c); |
1458 | 201k | } |
1459 | | |
1460 | 16.1k | font.m_type=libwps_tools_win::Font::getFontType(name); |
1461 | 16.1k | if (font.m_type==libwps_tools_win::Font::UNKNOWN) |
1462 | 7.07k | font.m_type=getDefaultFontType(); |
1463 | 16.1k | font.m_name=name; |
1464 | | |
1465 | 16.1k | input->seek(endPos-4, librevenge::RVNG_SEEK_SET); |
1466 | 16.1k | val = int(libwps::readU16(input)); // always 0x20 |
1467 | 16.1k | if (val!=0x20) f << "f1=" << std::hex << val << std::dec << ","; |
1468 | 16.1k | int fSize = libwps::read16(input)/2; |
1469 | 16.1k | if (fSize >= 1 && fSize <= 50) |
1470 | 1.68k | font.m_size=double(fSize); |
1471 | 14.4k | else |
1472 | 14.4k | f << "###fSize=" << fSize << ","; |
1473 | 16.1k | if (name.empty()) |
1474 | 208 | f << "###noName,"; |
1475 | 16.1k | font.m_extra=f.str(); |
1476 | | |
1477 | 16.1k | f.str(""); |
1478 | 16.1k | f << "Entries(Font):"; |
1479 | 16.1k | f << "font" << m_state->m_fontsList.size() << "[" << font << "]"; |
1480 | 16.1k | m_state->m_fontsList.push_back(font); |
1481 | | |
1482 | 16.1k | ascii().addPos(pos); |
1483 | 16.1k | ascii().addNote(f.str().c_str()); |
1484 | | |
1485 | 16.1k | return true; |
1486 | 16.4k | } |
1487 | | |
1488 | | // ---------------------------------------------------------------------- |
1489 | | // Header/Footer |
1490 | | // ---------------------------------------------------------------------- |
1491 | | void WKS4Parser::sendHeaderFooter(bool header) |
1492 | 22 | { |
1493 | 22 | if (!m_listener) |
1494 | 0 | { |
1495 | 0 | WPS_DEBUG_MSG(("WKS4Parser::sendHeaderFooter: can not find the listener\n")); |
1496 | 0 | return; |
1497 | 0 | } |
1498 | | |
1499 | 22 | m_listener->setFont(m_state->getDefaultFont()); |
1500 | 22 | m_listener->insertUnicodeString(header ? m_state->m_headerString : m_state->m_footerString); |
1501 | 22 | } |
1502 | | |
1503 | | bool WKS4Parser::readHeaderFooter(bool header) |
1504 | 3.23k | { |
1505 | 3.23k | libwps::DebugStream f; |
1506 | 3.23k | RVNGInputStreamPtr input = getInput(); |
1507 | 3.23k | long pos = input->tell(); |
1508 | 3.23k | auto type = int(libwps::read16(input)); |
1509 | 3.23k | if (type != 0x0026 && type != 0x0025) |
1510 | 0 | { |
1511 | 0 | WPS_DEBUG_MSG(("WKS4Parser::readHeaderFooter: not a header/footer\n")); |
1512 | 0 | return false; |
1513 | 0 | } |
1514 | 3.23k | auto sz = long(libwps::readU16(input)); |
1515 | 3.23k | long endPos = pos+4+sz; |
1516 | | |
1517 | 3.23k | f << "Entries(" << (header ? "HeaderText" : "FooterText") << "):"; |
1518 | 3.23k | if (sz==1) |
1519 | 422 | { |
1520 | | // followed with 0 |
1521 | 422 | auto val=int(libwps::read8(input)); |
1522 | 422 | if (val) f << "##f0=" << val << ","; |
1523 | 422 | ascii().addPos(pos); |
1524 | 422 | ascii().addNote(f.str().c_str()); |
1525 | 422 | return true; |
1526 | 422 | } |
1527 | 2.81k | if (sz < 0xF2) |
1528 | 2.50k | { |
1529 | 2.50k | WPS_DEBUG_MSG(("WKS4Parser::readHeaderFooter: the header/footer size seeem odds\n")); |
1530 | 2.50k | f << "###"; |
1531 | 2.50k | ascii().addPos(pos); |
1532 | 2.50k | ascii().addNote(f.str().c_str()); |
1533 | 2.50k | return false; |
1534 | 2.50k | } |
1535 | 307 | librevenge::RVNGString text; |
1536 | 307 | if (!readCString(text,sz)) |
1537 | 0 | f << "##name,"; |
1538 | 307 | if (header) |
1539 | 145 | m_state->m_headerString=text; |
1540 | 162 | else |
1541 | 162 | m_state->m_footerString=text; |
1542 | 307 | f << text.cstr(); |
1543 | 307 | if (input->tell()!=endPos) |
1544 | 303 | ascii().addDelimiter(input->tell(), '|'); |
1545 | 307 | ascii().addPos(pos); |
1546 | 307 | ascii().addNote(f.str().c_str()); |
1547 | | |
1548 | 307 | return true; |
1549 | 2.81k | } |
1550 | | |
1551 | | bool WKS4Parser::readPrnt() |
1552 | 2.04k | { |
1553 | 2.04k | libwps::DebugStream f; |
1554 | 2.04k | RVNGInputStreamPtr input = getInput(); |
1555 | 2.04k | long pos = input->tell(); |
1556 | 2.04k | auto type = int(libwps::read16(input)); |
1557 | 2.04k | if (type != 0x5423 && type != 0x5437) |
1558 | 0 | { |
1559 | 0 | WPS_DEBUG_MSG(("WKS4Parser::readPrnt: not a prnt zone\n")); |
1560 | 0 | return false; |
1561 | 0 | } |
1562 | 2.04k | auto sz = long(libwps::readU16(input)); |
1563 | 2.04k | long endPos = pos+4+sz; |
1564 | | |
1565 | 2.04k | f << "Entries(PRNT):"; |
1566 | 2.04k | if (type==0x5437) f << "chart,"; |
1567 | 2.04k | if (sz >= 12) |
1568 | 402 | { |
1569 | 402 | float dim[6]; |
1570 | 402 | for (float &i : dim) |
1571 | 2.41k | i = float(libwps::read16(input))/1440.f; |
1572 | 402 | f << "dim=" << dim[5] << "x" << dim[4] << ","; |
1573 | 402 | f << "margin=[" << dim[0] << "x" << dim[2] << "," |
1574 | 402 | << dim[3] << "x" << dim[1] << "],"; |
1575 | | // check me |
1576 | 402 | if (type==0x5423) |
1577 | 211 | { |
1578 | 211 | m_state->m_pageSpan.setFormWidth(double(dim[5])); |
1579 | 211 | m_state->m_pageSpan.setFormLength(double(dim[4])); |
1580 | 211 | m_state->m_pageSpan.setMarginLeft(double(dim[0])); |
1581 | 211 | m_state->m_pageSpan.setMarginTop(double(dim[2])); |
1582 | 211 | m_state->m_pageSpan.setMarginRight(double(dim[3])); |
1583 | 211 | m_state->m_pageSpan.setMarginBottom(double(dim[1])); |
1584 | 211 | } |
1585 | 402 | } |
1586 | 2.04k | int val = libwps::read16(input); |
1587 | 2.04k | if (val!=1) f << "first[pageNumber]=" << val <<","; |
1588 | 2.04k | long numElt = (endPos-input->tell())/2; |
1589 | 8.59k | for (long i = 0; i < numElt; i++) |
1590 | 6.54k | { |
1591 | | // f2/3=0x2d0 (dim in inches ? ) |
1592 | 6.54k | val = libwps::read16(input); |
1593 | 6.54k | if (!val) continue; |
1594 | 4.80k | f << "f" << i << "=" << std::hex << val << std::dec << ","; |
1595 | 4.80k | } |
1596 | | |
1597 | 2.04k | ascii().addPos(pos); |
1598 | 2.04k | ascii().addNote(f.str().c_str()); |
1599 | 2.04k | return true; |
1600 | 2.04k | } |
1601 | | |
1602 | | bool WKS4Parser::readPrn2() |
1603 | 378 | { |
1604 | 378 | libwps::DebugStream f; |
1605 | 378 | RVNGInputStreamPtr input = getInput(); |
1606 | 378 | long pos = input->tell(); |
1607 | 378 | long type = libwps::read16(input); |
1608 | 378 | if (type != 0x5482 && type != 0x5467) |
1609 | 0 | { |
1610 | 0 | WPS_DEBUG_MSG(("WKS4Parser::readPrn2: not a prn2 zone\n")); |
1611 | 0 | return false; |
1612 | 0 | } |
1613 | 378 | long sz = libwps::readU16(input); |
1614 | 378 | long endPos = pos+4+sz; |
1615 | | |
1616 | 378 | f << "Entries(PRN2):"; |
1617 | 378 | if (sz >= 64) |
1618 | 73 | { |
1619 | 73 | float dim[8]; |
1620 | 219 | for (int st = 0; st < 2; st++) |
1621 | 146 | { |
1622 | 146 | for (float &i : dim) |
1623 | 1.16k | i = float(libwps::read32(input))/1440.f; |
1624 | 146 | f << "dim" << st << "=" << dim[5] << "x" << dim[4] << ","; |
1625 | 146 | f << "margin" << st << "=[" << dim[0] << "x" << dim[2] << "," |
1626 | 146 | << dim[3] << "x" << dim[1] << "],"; |
1627 | 146 | f << "head/foot" << st << "?=" << dim[7] << "x" << dim[6] << ","; |
1628 | 146 | } |
1629 | 73 | } |
1630 | 378 | long numElt = (endPos-input->tell())/4; |
1631 | | /* |
1632 | | in general only f0=1, |
1633 | | but sometime f0=1,f2=1,f8=64,f42=174,f44=1,f46=175,f48=1 |
1634 | | */ |
1635 | 4.73k | for (long i = 0; i < numElt; i++) |
1636 | 4.35k | { |
1637 | 4.35k | auto val = int(libwps::read16(input)); |
1638 | 4.35k | if (!val) continue; |
1639 | 1.02k | f << "f" << i << "=" << std::hex << val << std::dec << ","; |
1640 | 1.02k | } |
1641 | | |
1642 | 378 | ascii().addPos(pos); |
1643 | 378 | ascii().addNote(f.str().c_str()); |
1644 | 378 | return true; |
1645 | 378 | } |
1646 | | |
1647 | | bool WKS4Parser::readFieldName() |
1648 | 5.51k | { |
1649 | 5.51k | libwps::DebugStream f; |
1650 | 5.51k | RVNGInputStreamPtr input = getInput(); |
1651 | | |
1652 | 5.51k | long pos = input->tell(); |
1653 | 5.51k | auto type = long(libwps::readU16(input)); |
1654 | 5.51k | if (type != 0xb) |
1655 | 0 | { |
1656 | 0 | WPS_DEBUG_MSG(("WKS4Parser::readFieldName: not a zoneB type\n")); |
1657 | 0 | return false; |
1658 | 0 | } |
1659 | 5.51k | auto sz = long(libwps::readU16(input)); |
1660 | 5.51k | f << "Entries(FldNames):"; |
1661 | 5.51k | if (sz != 0x18 && sz != 0x1e) |
1662 | 536 | { |
1663 | | // find also 0x85 a zone with 4 fldnames ? |
1664 | 536 | WPS_DEBUG_MSG(("WKS4Parser::readFieldName: size seems bad\n")); |
1665 | 536 | f << "###"; |
1666 | 536 | ascii().addPos(pos); |
1667 | 536 | ascii().addNote(f.str().c_str()); |
1668 | 536 | return true; |
1669 | 536 | } |
1670 | 4.98k | librevenge::RVNGString name; |
1671 | 4.98k | if (!readCString(name,16)) |
1672 | 0 | f << "##name,"; |
1673 | 4.98k | else if (!name.empty()) |
1674 | 4.59k | f << name.cstr() << ","; |
1675 | | |
1676 | 4.98k | input->seek(pos+20, librevenge::RVNG_SEEK_SET); |
1677 | | // the position |
1678 | 4.98k | int dim[4]; |
1679 | 4.98k | if (sz==0x18) |
1680 | 3.77k | { |
1681 | 15.1k | for (int &i : dim) i=int(libwps::read16(input)); |
1682 | 3.77k | } |
1683 | 1.20k | else |
1684 | 1.20k | { |
1685 | 9.63k | for (int i=0; i<7; ++i) |
1686 | 8.42k | { |
1687 | 8.42k | auto val=int(libwps::read16(input)); |
1688 | 8.42k | if (i<2) dim[i]=val; |
1689 | 6.02k | else if (i>=3 && i<5) dim[i-1]=val; |
1690 | 3.61k | else if (val) f << "f" << i << "=" << val << ","; |
1691 | 8.42k | } |
1692 | 1.20k | } |
1693 | | // in a spreadsheet, the cell or the cells corresponding to the field |
1694 | | // in a database, col,0,col,0xFFF |
1695 | 4.98k | if (m_state->m_isSpreadsheet || dim[1] || dim[0]!= dim[2] || dim[3]!=0xFFF) |
1696 | 4.66k | { |
1697 | 4.66k | f << "cell=" << dim[0] << "x" << dim[1]; |
1698 | 4.66k | if (dim[0]!=dim[2] || dim[1]!=dim[3]) |
1699 | 3.11k | f << "<->" << dim[2] << "x" << dim[3]; |
1700 | 4.66k | f << ","; |
1701 | 4.66k | } |
1702 | 315 | else |
1703 | 315 | f << "col=" << dim[0] << ","; |
1704 | 4.98k | ascii().addPos(pos); |
1705 | 4.98k | ascii().addNote(f.str().c_str()); |
1706 | 4.98k | return true; |
1707 | 5.51k | } |
1708 | | |
1709 | | //////////////////////////////////////////////////////////// |
1710 | | // Unknown |
1711 | | //////////////////////////////////////////////////////////// |
1712 | | |
1713 | | |
1714 | | /* the zone 0:7 and 0:9 */ |
1715 | | bool WKS4Parser::readWindowRecord() |
1716 | 5.09k | { |
1717 | 5.09k | libwps::DebugStream f; |
1718 | 5.09k | RVNGInputStreamPtr input = getInput(); |
1719 | | |
1720 | 5.09k | long pos = input->tell(); |
1721 | 5.09k | auto type = long(libwps::read16(input)); |
1722 | 5.09k | if (type != 7 && type != 9) |
1723 | 0 | { |
1724 | 0 | WPS_DEBUG_MSG(("WKS4Parser::readWindowRecord: unknown type\n")); |
1725 | 0 | return false; |
1726 | 0 | } |
1727 | 5.09k | auto sz = long(libwps::readU16(input)); |
1728 | | |
1729 | | // normally size=0x1f but one time 0x1e |
1730 | 5.09k | if (sz < 0x1e) |
1731 | 3.94k | { |
1732 | 3.94k | WPS_DEBUG_MSG(("WKS4Parser::readWindowRecord: zone seems too short\n")); |
1733 | 3.94k | ascii().addPos(pos); |
1734 | 3.94k | ascii().addNote("Entries(WindowRecord):###"); |
1735 | 3.94k | return true; |
1736 | 3.94k | } |
1737 | | |
1738 | 1.15k | f << "Entries(WindowRecord)[" << type << "]:"; |
1739 | | // f0=0-a|1f|21, f1=1-3c|1da, f2=0-6|71|7f|f1, f3=4|9|a|c(size?), |
1740 | | // f4=0|4|6-11, f5=5-2c, f6=0|3|6|10|1f|20, f7=0-3c|1ca (related to f1?) |
1741 | | // f8=0|1, f9=0|2, f10=f11=0 |
1742 | 15.0k | for (int i=0; i<12; ++i) |
1743 | 13.8k | { |
1744 | 13.8k | auto val=int(libwps::read16(input)); |
1745 | 13.8k | if (val) f << "f" << i << "=" << val << ","; |
1746 | 13.8k | } |
1747 | 3.47k | for (int i=0; i<2; ++i) // g0=4, g1=4|a|b|10 |
1748 | 2.31k | { |
1749 | 2.31k | auto val=int(libwps::read16(input)); |
1750 | 2.31k | if (val!=4) f << "g" << i << "=" << val << ","; |
1751 | 2.31k | } |
1752 | 1.15k | auto val=int(libwps::read16(input)); // number between -5 and ad |
1753 | 1.15k | f << "g2=" << val << ","; |
1754 | | |
1755 | 1.15k | if (sz!=0x1e) |
1756 | 1.13k | ascii().addDelimiter(input->tell(),'|'); |
1757 | 1.15k | ascii().addPos(pos); |
1758 | 1.15k | ascii().addNote(f.str().c_str()); |
1759 | | |
1760 | 1.15k | return true; |
1761 | 5.09k | } |
1762 | | |
1763 | | /* some spreadsheet zones 0:18, 0:19, 0:20, 0:27, 0:2a */ |
1764 | | bool WKS4Parser::readUnknown1() |
1765 | 4.67k | { |
1766 | 4.67k | libwps::DebugStream f; |
1767 | 4.67k | RVNGInputStreamPtr input = getInput(); |
1768 | | |
1769 | 4.67k | long pos = input->tell(); |
1770 | 4.67k | auto type = long(libwps::read16(input)); |
1771 | 4.67k | int expectedSize=0, extraSize=0; |
1772 | 4.67k | switch (type) |
1773 | 4.67k | { |
1774 | 454 | case 0x18: |
1775 | 1.34k | case 0x19: |
1776 | 1.34k | expectedSize=0x19; |
1777 | 1.34k | break; |
1778 | 1.29k | case 0x20: |
1779 | 1.96k | case 0x2a: |
1780 | 1.96k | expectedSize=0x10; |
1781 | 1.96k | break; |
1782 | 1.35k | case 0x27: |
1783 | 1.35k | expectedSize=0x19; |
1784 | 1.35k | extraSize=15; |
1785 | 1.35k | break; |
1786 | 0 | default: |
1787 | 0 | WPS_DEBUG_MSG(("WKS4Parser::readUnknown1: unexpected type ???\n")); |
1788 | 0 | return false; |
1789 | 4.67k | } |
1790 | 4.67k | auto sz = long(libwps::readU16(input)); |
1791 | | |
1792 | 4.67k | f << "Entries(Flags" << std::hex << type << std::dec << ")]:"; |
1793 | 4.67k | if (sz != expectedSize+extraSize) |
1794 | 2.96k | { |
1795 | | // find also 270001000[01] |
1796 | 2.96k | if (type==0x27 && sz==1) |
1797 | 170 | { |
1798 | 170 | f << "f0=" << int(libwps::read8(input)) << ","; |
1799 | 170 | ascii().addPos(pos); |
1800 | 170 | ascii().addNote(f.str().c_str()); |
1801 | 170 | return true; |
1802 | 170 | } |
1803 | 2.79k | WPS_DEBUG_MSG(("WKS4Parser::readUnknown1: the zone size seems too bad\n")); |
1804 | 2.79k | f << "###"; |
1805 | 2.79k | ascii().addPos(pos); |
1806 | 2.79k | ascii().addNote(f.str().c_str()); |
1807 | 2.79k | return true; |
1808 | 2.96k | } |
1809 | | |
1810 | | // find always 0xff, excepted for zone 18(f0=0), zone 19(f24=0|3), zone 27(f0=..=f23=0|ff, f24=0|3) |
1811 | 37.4k | for (int i=0; i<expectedSize; ++i) |
1812 | 35.7k | { |
1813 | 35.7k | auto val=int(libwps::read8(input)); |
1814 | 35.7k | if (val!=-1) f << "f" << i << "=" << val << ","; |
1815 | 35.7k | } |
1816 | | |
1817 | 1.71k | if (type==0x27) |
1818 | 381 | { |
1819 | 381 | auto val=int(libwps::read8(input)); // always 0 |
1820 | 381 | if (val) f << "g0=" << val << ","; |
1821 | 3.04k | for (int i=0; i<7; ++i) // g1=0|4, g2=0|72, g4=0|20, g5=0|1|4, g6=0|1, g7=0|-1|205|80d8|e9f |
1822 | 2.66k | { |
1823 | 2.66k | val=int(libwps::read16(input)); |
1824 | 2.66k | if (val) f << "g" << i+1 << "=" << val << ","; |
1825 | 2.66k | } |
1826 | 381 | } |
1827 | 1.71k | ascii().addPos(pos); |
1828 | 1.71k | ascii().addNote(f.str().c_str()); |
1829 | | |
1830 | 1.71k | return true; |
1831 | 4.67k | } |
1832 | | |
1833 | | //////////////////////////////////////////////////////////// |
1834 | | // decode |
1835 | | //////////////////////////////////////////////////////////// |
1836 | | RVNGInputStreamPtr WKS4Parser::decodeStream(RVNGInputStreamPtr input, long endPos, std::vector<uint8_t> const &key) |
1837 | 1.09k | { |
1838 | 1.09k | if (!input || key.size()!=16) |
1839 | 0 | { |
1840 | 0 | WPS_DEBUG_MSG(("WKS4Parser::decodeStream: the arguments seems bad\n")); |
1841 | 0 | return RVNGInputStreamPtr(); |
1842 | 0 | } |
1843 | 1.09k | long actPos=input->tell(); |
1844 | 1.09k | input->seek(0,librevenge::RVNG_SEEK_SET); |
1845 | 1.09k | librevenge::RVNGBinaryData data; |
1846 | 1.09k | if (!libwps::readDataToEnd(input, data) || long(data.size())!=endPos || !data.getDataBuffer()) |
1847 | 0 | { |
1848 | 0 | WPS_DEBUG_MSG(("WKS4Parser::decodeStream: can not read the original input\n")); |
1849 | 0 | return RVNGInputStreamPtr(); |
1850 | 0 | } |
1851 | 1.09k | auto *buf=const_cast<unsigned char *>(data.getDataBuffer()); |
1852 | 1.09k | input->seek(actPos,librevenge::RVNG_SEEK_SET); |
1853 | 1.09k | uint8_t d7=0; |
1854 | 416k | while (!input->isEnd()) |
1855 | 416k | { |
1856 | 416k | long pos=input->tell(); |
1857 | 416k | if (pos+4>endPos) break; |
1858 | 415k | input->seek(2,librevenge::RVNG_SEEK_CUR); |
1859 | 415k | auto sSz=int(libwps::readU16(input)); |
1860 | 415k | if (pos+4+sSz>endPos) |
1861 | 779 | { |
1862 | 779 | input->seek(pos,librevenge::RVNG_SEEK_SET); |
1863 | 779 | break; |
1864 | 779 | } |
1865 | 16.4M | for (int i=0; i<sSz; ++i) |
1866 | 16.0M | { |
1867 | 16.0M | auto c=uint8_t(libwps::readU8(input)); |
1868 | 16.0M | c=uint8_t((c<<1)|(c>>7)); |
1869 | 16.0M | c=(c^key[(d7++)&0xf]); |
1870 | 16.0M | buf[pos+4+i]=uint8_t((c>>6)|(c<<2)); |
1871 | 16.0M | } |
1872 | 415k | } |
1873 | 1.09k | if (input->tell()!=endPos) |
1874 | 997 | { |
1875 | 997 | WPS_DEBUG_MSG(("WKS4Parser::decodeStream: can not decode the end of the file, data may be bad %lx %lx\n", static_cast<unsigned long>(input->tell()), static_cast<unsigned long>(endPos))); |
1876 | 997 | } |
1877 | 1.09k | RVNGInputStreamPtr res(new WPSStringStream(data.getDataBuffer(), static_cast<unsigned int>(endPos))); |
1878 | 1.09k | res->seek(actPos, librevenge::RVNG_SEEK_SET); |
1879 | 1.09k | return res; |
1880 | 1.09k | } |
1881 | | /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */ |