/src/libmwaw/src/lib/MultiplanParser.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */ |
2 | | |
3 | | /* libmwaw |
4 | | * Version: MPL 2.0 / LGPLv2+ |
5 | | * |
6 | | * The contents of this file are subject to the Mozilla Public License Version |
7 | | * 2.0 (the "License"); you may not use this file except in compliance with |
8 | | * the License or as specified alternatively below. You may obtain a copy of |
9 | | * the License at http://www.mozilla.org/MPL/ |
10 | | * |
11 | | * Software distributed under the License is distributed on an "AS IS" basis, |
12 | | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
13 | | * for the specific language governing rights and limitations under the |
14 | | * License. |
15 | | * |
16 | | * Major Contributor(s): |
17 | | * Copyright (C) 2002 William Lachance (wrlach@gmail.com) |
18 | | * Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net) |
19 | | * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch) |
20 | | * Copyright (C) 2006, 2007 Andrew Ziem |
21 | | * Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr) |
22 | | * |
23 | | * |
24 | | * All Rights Reserved. |
25 | | * |
26 | | * For minor contributions see the git repository. |
27 | | * |
28 | | * Alternatively, the contents of this file may be used under the terms of |
29 | | * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), |
30 | | * in which case the provisions of the LGPLv2+ are applicable |
31 | | * instead of those above. |
32 | | */ |
33 | | |
34 | | #include <cmath> |
35 | | #include <cstring> |
36 | | #include <iomanip> |
37 | | #include <iostream> |
38 | | #include <limits> |
39 | | #include <map> |
40 | | #include <set> |
41 | | #include <sstream> |
42 | | |
43 | | #include <librevenge/librevenge.h> |
44 | | |
45 | | #include "MWAWCell.hxx" |
46 | | #include "MWAWFont.hxx" |
47 | | #include "MWAWFontConverter.hxx" |
48 | | #include "MWAWHeader.hxx" |
49 | | #include "MWAWParagraph.hxx" |
50 | | #include "MWAWPosition.hxx" |
51 | | #include "MWAWSpreadsheetListener.hxx" |
52 | | #include "MWAWSubDocument.hxx" |
53 | | |
54 | | #include "MultiplanParser.hxx" |
55 | | |
56 | | /** Internal: the structures of a MultiplanParser */ |
57 | | namespace MultiplanParserInternal |
58 | | { |
59 | | //////////////////////////////////////// |
60 | | //! Internal: the state of a MultiplanParser |
61 | | struct State { |
62 | | //! constructor |
63 | | State() |
64 | 12.8k | : m_font() |
65 | 12.8k | , m_maximumCell() |
66 | 12.8k | , m_columnPositions() |
67 | 12.8k | , m_cellPositions() |
68 | 12.8k | , m_cellPositionsSet() |
69 | 12.8k | , m_posToLinkMap() |
70 | 12.8k | , m_posToNameMap() |
71 | 12.8k | , m_posToSharedDataSeen() |
72 | 12.8k | { |
73 | 12.8k | } |
74 | | //! returns the column width in point |
75 | | std::vector<float> getColumnsWidth() const; |
76 | | //! the default font |
77 | | MWAWFont m_font; |
78 | | //! the maximumCell |
79 | | MWAWVec2i m_maximumCell; |
80 | | //! the columns begin position in point |
81 | | std::vector<int> m_columnPositions; |
82 | | //! the header/footer/printer message entries |
83 | | MWAWEntry m_hfpEntries[3]; |
84 | | //! the positions of each cell: a vector for each row |
85 | | std::vector<std::vector<int> > m_cellPositions; |
86 | | //! the list of all position (use for checking) |
87 | | std::set<int> m_cellPositionsSet; |
88 | | //! the different main spreadsheet zones |
89 | | MWAWEntry m_entries[9]; |
90 | | //! the list of link instruction |
91 | | std::map<int, MWAWCellContent::FormulaInstruction> m_posToLinkMap; |
92 | | //! the map name's pos to name's cell instruction |
93 | | std::map<int, MWAWCellContent::FormulaInstruction> m_posToNameMap; |
94 | | //! a set a shared data already seen |
95 | | std::set<int> m_posToSharedDataSeen; |
96 | | }; |
97 | | |
98 | | std::vector<float> State::getColumnsWidth() const |
99 | 1.82k | { |
100 | 1.82k | std::vector<float> res; |
101 | 1.82k | bool first=true; |
102 | 1.82k | int lastPos=0; |
103 | 1.82k | float const defWidth=64.f; |
104 | 116k | for (auto p : m_columnPositions) { |
105 | 116k | if (first) { |
106 | 1.82k | first=false; |
107 | 1.82k | continue; |
108 | 1.82k | } |
109 | 114k | if (p<lastPos) |
110 | 17.0k | res.push_back(defWidth); |
111 | 97.5k | else |
112 | 97.5k | res.push_back(float(p-lastPos)); |
113 | 114k | lastPos=p; |
114 | 114k | } |
115 | 1.82k | if (res.size()<64) res.resize(64, defWidth); |
116 | 1.82k | return res; |
117 | 1.82k | } |
118 | | |
119 | | //////////////////////////////////////// |
120 | | //! Internal: the subdocument of a MultiplanParserInternal |
121 | | class SubDocument final : public MWAWSubDocument |
122 | | { |
123 | | public: |
124 | | SubDocument(MultiplanParser &parser, MWAWInputStreamPtr const &input, MWAWEntry const &entry) |
125 | 3.12k | : MWAWSubDocument(&parser, input, entry) |
126 | 3.12k | , m_multiParser(parser) |
127 | 3.12k | { |
128 | 3.12k | } |
129 | | |
130 | | //! destructor |
131 | 0 | ~SubDocument() final {} |
132 | | |
133 | | //! operator!= |
134 | | bool operator!=(MWAWSubDocument const &doc) const final; |
135 | | |
136 | | //! the parser function |
137 | | void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final; |
138 | | |
139 | | protected: |
140 | | /** the main parser */ |
141 | | MultiplanParser &m_multiParser; |
142 | | }; |
143 | | |
144 | | void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/) |
145 | 3.12k | { |
146 | 3.12k | if (!listener.get()) { |
147 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::SubDocument::parse: no listener\n")); |
148 | 0 | return; |
149 | 0 | } |
150 | | |
151 | 3.12k | long pos = m_input->tell(); |
152 | 3.12k | m_multiParser.sendText(m_zone); |
153 | 3.12k | m_input->seek(pos, librevenge::RVNG_SEEK_SET); |
154 | 3.12k | } |
155 | | |
156 | | bool SubDocument::operator!=(MWAWSubDocument const &doc) const |
157 | 0 | { |
158 | 0 | if (MWAWSubDocument::operator!=(doc)) return true; |
159 | 0 | auto const *sDoc = dynamic_cast<SubDocument const *>(&doc); |
160 | 0 | if (!sDoc) return true; |
161 | 0 | if (&m_multiParser != &sDoc->m_multiParser) return true; |
162 | 0 | return false; |
163 | 0 | } |
164 | | } |
165 | | |
166 | | //////////////////////////////////////////////////////////// |
167 | | // constructor/destructor, ... |
168 | | //////////////////////////////////////////////////////////// |
169 | | MultiplanParser::MultiplanParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header) |
170 | 5.49k | : MWAWSpreadsheetParser(input, rsrcParser, header) |
171 | 5.49k | , m_state(new MultiplanParserInternal::State) |
172 | 5.49k | { |
173 | 5.49k | setAsciiName("main-1"); |
174 | | // reduce the margin (in case, the page is not defined) |
175 | 5.49k | getPageSpan().setMargins(0.1); |
176 | 5.49k | } |
177 | | |
178 | | MultiplanParser::~MultiplanParser() |
179 | 5.49k | { |
180 | 5.49k | } |
181 | | |
182 | | //////////////////////////////////////////////////////////// |
183 | | // the parser |
184 | | //////////////////////////////////////////////////////////// |
185 | | void MultiplanParser::parse(librevenge::RVNGSpreadsheetInterface *docInterface) |
186 | 1.82k | { |
187 | 1.82k | if (!getInput().get() || !checkHeader(nullptr)) throw(libmwaw::ParseException()); |
188 | 1.82k | bool ok = true; |
189 | 1.82k | try { |
190 | | // create the asciiFile |
191 | 1.82k | ascii().setStream(getInput()); |
192 | 1.82k | ascii().open(asciiName()); |
193 | 1.82k | checkHeader(nullptr); |
194 | 1.82k | ok = createZones(); |
195 | 1.82k | if (ok) { |
196 | 1.82k | createDocument(docInterface); |
197 | 1.82k | sendSpreadsheet(); |
198 | 1.82k | } |
199 | 1.82k | } |
200 | 1.82k | catch (...) { |
201 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::parse: exception catched when parsing\n")); |
202 | 0 | ok = false; |
203 | 0 | } |
204 | | |
205 | 1.82k | ascii().reset(); |
206 | 1.82k | resetSpreadsheetListener(); |
207 | 1.82k | if (!ok) throw(libmwaw::ParseException()); |
208 | 1.82k | } |
209 | | |
210 | | //////////////////////////////////////////////////////////// |
211 | | // create the document |
212 | | //////////////////////////////////////////////////////////// |
213 | | void MultiplanParser::createDocument(librevenge::RVNGSpreadsheetInterface *documentInterface) |
214 | 1.82k | { |
215 | 1.82k | if (!documentInterface) return; |
216 | 1.82k | if (getSpreadsheetListener()) { |
217 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::createDocument: listener already exist\n")); |
218 | 0 | return; |
219 | 0 | } |
220 | | |
221 | | // create the page list |
222 | 1.82k | MWAWPageSpan ps(getPageSpan()); |
223 | 1.82k | ps.setPageSpan(1); |
224 | 5.46k | for (int i=0; i<2; ++i) { |
225 | 3.64k | if (!m_state->m_hfpEntries[i].valid()) continue; |
226 | 3.12k | MWAWHeaderFooter header(i==0 ? MWAWHeaderFooter::HEADER : MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL); |
227 | 3.12k | header.m_subDocument.reset |
228 | 3.12k | (new MultiplanParserInternal::SubDocument |
229 | 3.12k | (*this, getInput(), m_state->m_hfpEntries[i])); |
230 | 3.12k | ps.setHeaderFooter(header); |
231 | 3.12k | } |
232 | 1.82k | std::vector<MWAWPageSpan> pageList(1,ps); |
233 | | // |
234 | 1.82k | MWAWSpreadsheetListenerPtr listen(new MWAWSpreadsheetListener(*getParserState(), pageList, documentInterface)); |
235 | 1.82k | setSpreadsheetListener(listen); |
236 | 1.82k | listen->startDocument(); |
237 | 1.82k | } |
238 | | |
239 | | |
240 | | //////////////////////////////////////////////////////////// |
241 | | // |
242 | | // Intermediate level |
243 | | // |
244 | | //////////////////////////////////////////////////////////// |
245 | | bool MultiplanParser::createZones() |
246 | 1.82k | { |
247 | 1.82k | if (!readPrinterMessage() || !readZoneB()) return false; |
248 | 1.82k | if (!readColumnsPos() || !readPrinterInfo()) return false; |
249 | 1.82k | if (!readHeaderFooter() || !readZoneC()) return false; |
250 | 1.82k | if (!readZonesList()) return false; |
251 | 1.82k | MWAWInputStreamPtr input = getInput(); |
252 | 1.82k | if (!input->isEnd()) { |
253 | 1.39k | MWAW_DEBUG_MSG(("MultiplanParser::createZones: find extra data\n")); |
254 | 1.39k | ascii().addPos(input->tell()); |
255 | 1.39k | ascii().addNote("Entries(Unknown):###extra"); |
256 | 1.39k | } |
257 | 1.82k | return true; |
258 | 1.82k | } |
259 | | |
260 | | //////////////////////////////////////////////////////////// |
261 | | // |
262 | | // Low level |
263 | | // |
264 | | //////////////////////////////////////////////////////////// |
265 | | |
266 | | //////////////////////////////////////////////////////////// |
267 | | // spreadsheet |
268 | | //////////////////////////////////////////////////////////// |
269 | | bool MultiplanParser::readHeaderFooter() |
270 | 1.82k | { |
271 | 1.82k | MWAWInputStreamPtr input = getInput(); |
272 | 1.82k | long pos = input->tell(); |
273 | 1.82k | if (!input->checkPosition(pos+2*256)) { |
274 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::readHeaderFooter: the zone seems too short\n")); |
275 | 0 | return false; |
276 | 0 | } |
277 | 1.82k | libmwaw::DebugStream f; |
278 | 5.46k | for (int i=0; i<2; ++i) { |
279 | 3.64k | pos = input->tell(); |
280 | 3.64k | f.str(""); |
281 | 3.64k | f << "Entries(HF)[" << (i==0 ? "header" : "footer") << "]:"; |
282 | 3.64k | int sSz=int(input->readULong(1)); |
283 | 3.64k | m_state->m_hfpEntries[i].setBegin(pos+1); |
284 | 3.64k | m_state->m_hfpEntries[i].setLength(sSz); |
285 | 3.64k | std::string name; |
286 | 445k | for (int c=0; c<sSz; ++c) name+=char(input->readULong(1)); |
287 | 3.64k | f << name; |
288 | 3.64k | ascii().addPos(pos); |
289 | 3.64k | ascii().addNote(f.str().c_str()); |
290 | 3.64k | input->seek(pos+256, librevenge::RVNG_SEEK_SET); |
291 | 3.64k | } |
292 | 1.82k | return true; |
293 | 1.82k | } |
294 | | |
295 | | bool MultiplanParser::readPrinterInfo() |
296 | 1.82k | { |
297 | 1.82k | MWAWInputStreamPtr input = getInput(); |
298 | 1.82k | long pos = input->tell(); |
299 | 1.82k | if (!input->checkPosition(pos+0xbc)) { |
300 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::readPrinterInfo: the zone seems too short\n")); |
301 | 0 | return false; |
302 | 0 | } |
303 | 1.82k | libmwaw::DebugStream f; |
304 | 1.82k | f << "Entries(PrinterInfo):"; |
305 | 1.82k | int val=int(input->readULong(2)); |
306 | 1.82k | if (val!=0x7fff) f << "f0=" << val << ","; |
307 | 1.82k | val=int(input->readULong(2)); |
308 | 1.82k | if (val) f << "f1=" << val << ","; |
309 | 1.82k | f << "left[margin]=" << input->readULong(1) << ","; |
310 | 1.82k | f << "width=" << input->readULong(1) << ","; |
311 | 1.82k | f << "right[margin]=" << input->readULong(1) << ","; |
312 | 1.82k | f << "length=" << input->readULong(1) << ","; |
313 | | // then 0 and a string? |
314 | 1.82k | ascii().addDelimiter(input->tell(),'|'); |
315 | 1.82k | ascii().addPos(pos); |
316 | 1.82k | ascii().addNote(f.str().c_str()); |
317 | 1.82k | input->seek(pos+130, librevenge::RVNG_SEEK_SET); |
318 | | |
319 | 1.82k | pos=input->tell(); |
320 | 1.82k | f.str(""); |
321 | 1.82k | f << "PrinterInfo[II]:"; |
322 | 1.82k | f << "row[pbBreak]=["; |
323 | 60.0k | for (int i=0; i<32; ++i) { |
324 | 58.2k | val=int(input->readULong(1)); |
325 | 58.2k | if (!val) continue; |
326 | 276k | for (int d=0, depl=1; d<8; ++d, depl<<=1) { |
327 | 246k | if (val&depl) f << i*8+d << ","; |
328 | 246k | } |
329 | 30.7k | } |
330 | 1.82k | f << "],"; |
331 | 1.82k | f << "col[pbBreak]=["; |
332 | 16.3k | for (int i=0; i<8; ++i) { |
333 | 14.5k | val=int(input->readULong(1)); |
334 | 14.5k | if (!val) continue; |
335 | 54.1k | for (int d=0, depl=1; d<8; ++d, depl<<=1) { |
336 | 48.1k | if (val&depl) f << i*8+d << ","; |
337 | 48.1k | } |
338 | 6.01k | } |
339 | 1.82k | f << "],"; |
340 | 14.5k | for (int i=0; i<7; ++i) { |
341 | 12.7k | val=int(input->readULong(2)); |
342 | 12.7k | int const expected[]= {0x48,0x48,0x36,0x36,1,1,0}; |
343 | 12.7k | if (val==expected[i]) continue; |
344 | 9.23k | if (i==4) { |
345 | 1.38k | if (val==0) |
346 | 550 | f << "print[col,row,number]=no,"; |
347 | 831 | else |
348 | 831 | f << "##print[col,row,number]=" << val << ","; |
349 | 1.38k | } |
350 | 7.85k | else |
351 | 7.85k | f << "g" << i << "=" << val << ","; |
352 | 9.23k | } |
353 | 1.82k | m_state->m_font.setId(int(input->readULong(2))); |
354 | 1.82k | m_state->m_font.setSize(float(input->readULong(2))); |
355 | 1.82k | f << "font=[" << m_state->m_font.getDebugString(getFontConverter()) << "],"; |
356 | 1.82k | ascii().addPos(pos); |
357 | 1.82k | ascii().addNote(f.str().c_str()); |
358 | 1.82k | input->seek(pos+58, librevenge::RVNG_SEEK_SET); |
359 | 1.82k | return true; |
360 | 1.82k | } |
361 | | |
362 | | bool MultiplanParser::readPrinterMessage() |
363 | 1.82k | { |
364 | 1.82k | MWAWInputStreamPtr input = getInput(); |
365 | 1.82k | long pos = input->tell(); |
366 | 1.82k | if (!input->checkPosition(pos+256)) { |
367 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::readPrinterMessage: the zone seems too short\n")); |
368 | 0 | return false; |
369 | 0 | } |
370 | 1.82k | libmwaw::DebugStream f; |
371 | 1.82k | f << "Entries(HF)[printerMessage]:"; |
372 | 1.82k | int sSz=int(input->readULong(1)); |
373 | 1.82k | m_state->m_hfpEntries[2].setBegin(pos+1); |
374 | 1.82k | m_state->m_hfpEntries[2].setLength(sSz); |
375 | 1.82k | std::string name; |
376 | 221k | for (int c=0; c<sSz; ++c) name+=char(input->readULong(1)); |
377 | 1.82k | f << name; |
378 | 1.82k | ascii().addPos(pos); |
379 | 1.82k | ascii().addNote(f.str().c_str()); |
380 | 1.82k | input->seek(pos+256, librevenge::RVNG_SEEK_SET); |
381 | 1.82k | return true; |
382 | 1.82k | } |
383 | | |
384 | | bool MultiplanParser::readColumnsPos() |
385 | 1.82k | { |
386 | 1.82k | MWAWInputStreamPtr input = getInput(); |
387 | 1.82k | long pos = input->tell(); |
388 | 1.82k | if (!input->checkPosition(pos+256)) { |
389 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::readColumnsPos: the zone seems too short\n")); |
390 | 0 | return false; |
391 | 0 | } |
392 | 1.82k | libmwaw::DebugStream f; |
393 | 1.82k | f << "Entries(ColPos):pos=["; |
394 | 118k | for (int i=0; i<64; ++i) { |
395 | 116k | m_state->m_columnPositions.push_back(int(input->readULong(2))); |
396 | 116k | f << m_state->m_columnPositions.back() << ","; |
397 | 116k | } |
398 | 1.82k | f << "],"; |
399 | 1.82k | ascii().addPos(pos); |
400 | 1.82k | ascii().addNote(f.str().c_str()); |
401 | 1.82k | return true; |
402 | 1.82k | } |
403 | | |
404 | | bool MultiplanParser::readZonesList() |
405 | 1.82k | { |
406 | 1.82k | MWAWInputStreamPtr input = getInput(); |
407 | 1.82k | long pos = input->tell(); |
408 | 1.82k | if (!input->checkPosition(pos+20)) { |
409 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::readZonesList: the zone seems too short\n")); |
410 | 0 | return false; |
411 | 0 | } |
412 | 1.82k | libmwaw::DebugStream f; |
413 | 1.82k | f << "Entries(ZonesList):"; |
414 | 1.82k | int lastPos=0; |
415 | 1.82k | f << "zones=["; |
416 | 20.0k | for (int i=0, w=0; i<10; ++i) { |
417 | 18.2k | int newPos=int(input->readULong(2)); |
418 | 18.2k | if (i==6) newPos+=lastPos; // length |
419 | 18.2k | if (i==7) { |
420 | 1.82k | lastPos=newPos; |
421 | 1.82k | continue; |
422 | 1.82k | } |
423 | 16.3k | if (newPos>lastPos) { |
424 | 8.22k | if (!input->checkPosition(pos+20+newPos)) { |
425 | 516 | MWAW_DEBUG_MSG(("MultiplanParser::readZonesList: find a bad position")); |
426 | 516 | f << "###"; |
427 | 516 | } |
428 | 7.70k | else { |
429 | 7.70k | m_state->m_entries[w].setBegin(pos+20+lastPos); |
430 | 7.70k | m_state->m_entries[w].setEnd(pos+20+newPos); |
431 | 7.70k | } |
432 | 8.22k | f << std::hex << lastPos << "<->" << newPos << std::dec << ","; |
433 | 8.22k | lastPos=newPos; |
434 | 8.22k | } |
435 | 8.15k | else |
436 | 8.15k | f << "_,"; |
437 | 16.3k | ++w; |
438 | 16.3k | } |
439 | 1.82k | f << "],"; |
440 | 1.82k | ascii().addPos(pos); |
441 | 1.82k | ascii().addNote(f.str().c_str()); |
442 | 18.2k | for (int i=0; i<9; ++i) { |
443 | 16.3k | if (!m_state->m_entries[i].valid()) continue; |
444 | 7.70k | bool ok=false; |
445 | 7.70k | std::string name; |
446 | 7.70k | switch (i) { |
447 | 624 | case 1: |
448 | 624 | ok=readZone1(m_state->m_entries[i]); |
449 | 624 | break; |
450 | 1.65k | case 3: |
451 | 1.65k | ok=readCellDataPosition(m_state->m_entries[i]); |
452 | 1.65k | break; |
453 | 1.03k | case 4: |
454 | 1.03k | name="Link"; |
455 | 1.03k | break; |
456 | 740 | case 5: |
457 | 740 | name="Link"; |
458 | 740 | break; |
459 | 1.62k | case 6: |
460 | 1.62k | name="DataCell"; |
461 | 1.62k | break; |
462 | 1.03k | case 7: // the data are normally read in zone 6 |
463 | 1.03k | name="SharedData"; |
464 | 1.03k | break; |
465 | 539 | case 8: |
466 | 539 | name="Names"; |
467 | 539 | break; |
468 | 459 | default: |
469 | 459 | ok=false; |
470 | 459 | break; |
471 | 7.70k | } |
472 | 7.70k | if (ok) |
473 | 2.08k | continue; |
474 | 5.62k | f.str(""); |
475 | 5.62k | if (!name.empty()) |
476 | 4.96k | f << "Entries(" << name << "):"; |
477 | 652 | else |
478 | 652 | f << "Entries(Zone" << i << "):"; |
479 | 5.62k | ascii().addPos(m_state->m_entries[i].begin()); |
480 | 5.62k | ascii().addNote(f.str().c_str()); |
481 | 5.62k | ascii().addPos(m_state->m_entries[i].end()); |
482 | 5.62k | ascii().addNote("_"); |
483 | 5.62k | input->seek(m_state->m_entries[i].end(), librevenge::RVNG_SEEK_SET); |
484 | 5.62k | } |
485 | 1.82k | return true; |
486 | 1.82k | } |
487 | | |
488 | | bool MultiplanParser::readZone1(MWAWEntry const &entry) |
489 | 624 | { |
490 | 624 | if (entry.length()%30) { |
491 | 174 | MWAW_DEBUG_MSG(("MultiplanParser::readZone1: the zone size seems bad\n")); |
492 | 174 | return false; |
493 | 174 | } |
494 | 450 | MWAWInputStreamPtr input = getInput(); |
495 | 450 | input->seek(entry.begin(), librevenge::RVNG_SEEK_SET); |
496 | 450 | libmwaw::DebugStream f; |
497 | 450 | f << "Entries(Zone1):"; |
498 | 450 | ascii().addPos(entry.begin()); |
499 | 450 | ascii().addNote(f.str().c_str()); |
500 | 450 | int N=int(entry.length()/30); |
501 | 4.92k | for (int i=0; i<N; ++i) { |
502 | | /* find something like that |
503 | | 0000000000fb01d80012001c00fb01d800000000000d0007ffe4ffeec000 |
504 | | 00000000005500550012001c005500550000000000040001ffe4ffeec000 |
505 | | */ |
506 | 4.47k | long pos=input->tell(); |
507 | 4.47k | f.str(""); |
508 | 4.47k | f << "Zone1-" << i << ":"; |
509 | 4.47k | ascii().addPos(pos); |
510 | 4.47k | ascii().addNote(f.str().c_str()); |
511 | 4.47k | input->seek(pos+30, librevenge::RVNG_SEEK_SET); |
512 | 4.47k | } |
513 | 450 | return true; |
514 | 624 | } |
515 | | |
516 | | bool MultiplanParser::readCellDataPosition(MWAWEntry const &entry) |
517 | 1.65k | { |
518 | 1.65k | if (m_state->m_maximumCell[0]<=0 || m_state->m_maximumCell[1]<=0 || entry.length()/m_state->m_maximumCell[0]/2<m_state->m_maximumCell[1]) { |
519 | 19 | MWAW_DEBUG_MSG(("MultiplanParser::readCellDataPosition: the zone seems bad\n")); |
520 | 19 | return false; |
521 | 19 | } |
522 | 1.63k | MWAWInputStreamPtr input = getInput(); |
523 | 1.63k | input->seek(entry.begin(), librevenge::RVNG_SEEK_SET); |
524 | 1.63k | libmwaw::DebugStream f; |
525 | 1.63k | f << "Entries(DataPos):"; |
526 | 1.63k | m_state->m_cellPositions.resize(size_t(m_state->m_maximumCell[0])); |
527 | 1.63k | auto &posSet=m_state->m_cellPositionsSet; |
528 | 327k | for (int i=0; i<m_state->m_maximumCell[0]; ++i) { |
529 | 326k | f << "[" << std::hex; |
530 | 326k | auto &cellPos=m_state->m_cellPositions[size_t(i)]; |
531 | 7.67M | for (int j=0; j<m_state->m_maximumCell[1]; ++j) { |
532 | 7.34M | cellPos.push_back(int(input->readLong(2))); |
533 | 7.34M | posSet.insert(cellPos.back()); |
534 | 7.34M | if (cellPos.back()) |
535 | 5.91M | f << cellPos.back() << ","; |
536 | 1.43M | else |
537 | 1.43M | f << "_,"; |
538 | 7.34M | } |
539 | 326k | f << std::dec << "],"; |
540 | 326k | } |
541 | 1.63k | if (input->tell()!=entry.end()) { |
542 | 1.20k | MWAW_DEBUG_MSG(("MultiplanParser::readCellDataPosition: find extra data\n")); |
543 | 1.20k | f << "###extra"; |
544 | 1.20k | ascii().addDelimiter(input->tell(),'|'); |
545 | 1.20k | } |
546 | 1.63k | ascii().addPos(entry.begin()); |
547 | 1.63k | ascii().addNote(f.str().c_str()); |
548 | 1.63k | return true; |
549 | 1.65k | } |
550 | | |
551 | | bool MultiplanParser::readLink(int pos, MWAWCellContent::FormulaInstruction &instr) |
552 | 99.4k | { |
553 | 99.4k | auto it=m_state->m_posToLinkMap.find(pos); |
554 | 99.4k | if (it!=m_state->m_posToLinkMap.end()) { |
555 | 16.4k | instr=it->second; |
556 | 16.4k | return true; |
557 | 16.4k | } |
558 | 83.0k | auto const &entry=m_state->m_entries[4]; |
559 | 83.0k | if (!entry.valid() || pos<0 || pos+12>entry.length()) { |
560 | 74.6k | MWAW_DEBUG_MSG(("MultiplanParser::readLink: the pos %d seems bad\n", pos)); |
561 | 74.6k | return false; |
562 | 74.6k | } |
563 | 8.34k | auto input = getInput(); |
564 | 8.34k | long actPos=input->tell(); |
565 | 8.34k | long begPos=entry.begin()+pos; |
566 | 8.34k | input->seek(begPos, librevenge::RVNG_SEEK_SET); |
567 | 8.34k | libmwaw::DebugStream f; |
568 | 8.34k | f << "Link-" << std::hex << pos << std::dec << "[pos]:"; |
569 | 8.34k | int dSz=int(input->readULong(1)); |
570 | 8.34k | if (dSz<0 || pos+12+dSz > entry.end()) { |
571 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::readLink: the pos %d seems bad\n", pos)); |
572 | 0 | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
573 | 0 | return false; |
574 | 0 | } |
575 | 8.34k | int type=int(input->readULong(1)); |
576 | 8.34k | f << "type=" << type << ","; |
577 | 8.34k | int lPos=int(input->readULong(2)); |
578 | 8.34k | if (!readLinkFilename(lPos, instr)) |
579 | 4.05k | f << "###"; |
580 | 8.34k | f << "pos=" << std::hex << lPos << std::dec << ","; |
581 | 8.34k | int val; |
582 | 25.0k | for (int j=0; j<2; ++j) { |
583 | 16.6k | val=int(input->readULong(1)); |
584 | 16.6k | int const expected[]= {0x1a,0x1a}; |
585 | 16.6k | if (val!=expected[j]) f << "f" << j+2 << "=" << val << ","; |
586 | 16.6k | } |
587 | 33.3k | for (int j=0; j<3; ++j) { // f4=1|821 |
588 | 25.0k | val=int(input->readULong(2)); |
589 | 25.0k | if (val) |
590 | 16.0k | f << "f" << j+4 << "=" << std::hex << val << std::dec << ","; |
591 | 25.0k | } |
592 | 8.34k | bool ok=false; |
593 | 8.34k | switch (type) { |
594 | 1.19k | case 0: { |
595 | 1.19k | ok=true; |
596 | 1.19k | auto fontConverter=getFontConverter(); |
597 | 1.19k | auto const fId=m_state->m_font.id(); |
598 | 1.19k | librevenge::RVNGString name=instr.m_fileName; |
599 | 1.19k | name.append(':'); |
600 | 29.0k | for (int i=0; i<dSz; ++i) { |
601 | 27.8k | auto ch=static_cast<unsigned char>(input->readULong(1)); |
602 | 27.8k | int unicode = fontConverter->unicode(fId, static_cast<unsigned char>(ch)); |
603 | 27.8k | if (unicode!=-1) |
604 | 7.02k | libmwaw::appendUnicode(uint32_t(unicode), name); |
605 | 20.7k | else if (ch==0x9 || ch > 0x1f) |
606 | 2.60k | libmwaw::appendUnicode(static_cast<uint32_t>(ch), name); |
607 | 18.1k | else { |
608 | 18.1k | f << "##"; |
609 | 18.1k | MWAW_DEBUG_MSG(("MultiplanParser::readLink: name seems bad\n")); |
610 | 18.1k | } |
611 | 27.8k | } |
612 | 1.19k | instr.m_type=instr.F_Text; |
613 | 1.19k | instr.m_content=name.cstr(); |
614 | 1.19k | break; |
615 | 0 | } |
616 | 1.77k | case 1: { |
617 | 1.77k | if (dSz<4) |
618 | 1.41k | break; |
619 | 363 | ok=true; |
620 | 363 | int rows[2], cols[2]; |
621 | 726 | for (auto &r : rows) r=int(input->readULong(1)); |
622 | 726 | for (auto &c : cols) c=int(input->readULong(1)); |
623 | 1.08k | for (int j=0; j<2; ++j) { |
624 | 726 | instr.m_position[j]=MWAWVec2i(cols[j],rows[j]); |
625 | 726 | instr.m_positionRelative[j]=MWAWVec2b(false,false); |
626 | 726 | } |
627 | 363 | instr.m_type=instr.m_position[0]==instr.m_position[1] ? instr.F_Cell : instr.F_CellList; |
628 | 363 | f << instr << ","; |
629 | 363 | break; |
630 | 1.77k | } |
631 | 5.36k | default: |
632 | 5.36k | MWAW_DEBUG_MSG(("MultiplanParser::readLink: find unknown type %d\n", type)); |
633 | 5.36k | break; |
634 | 8.34k | } |
635 | 8.34k | if (!ok) { |
636 | 6.78k | MWAW_DEBUG_MSG(("MultiplanParser::readLink: can not read link at pos %d\n", pos)); |
637 | 6.78k | f << "###"; |
638 | 6.78k | } |
639 | 1.56k | else |
640 | 1.56k | m_state->m_posToLinkMap[pos]=instr; |
641 | 8.34k | ascii().addPos(begPos); |
642 | 8.34k | ascii().addNote(f.str().c_str()); |
643 | 8.34k | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
644 | 8.34k | return ok; |
645 | 8.34k | } |
646 | | |
647 | | bool MultiplanParser::readLinkFilename(int pos, MWAWCellContent::FormulaInstruction &instr) |
648 | 8.34k | { |
649 | 8.34k | MWAWInputStreamPtr input = getInput(); |
650 | 8.34k | auto const &entry=m_state->m_entries[5]; |
651 | 8.34k | if (!entry.valid() || pos<0 || pos+10>entry.length() || !input->checkPosition(entry.end())) { |
652 | 3.91k | MWAW_DEBUG_MSG(("MultiplanParser::readLinkFilename: the pos %d seems bad\n", pos)); |
653 | 3.91k | return false; |
654 | 3.91k | } |
655 | 4.43k | long actPos=input->tell(); |
656 | 4.43k | long begPos=entry.begin()+pos; |
657 | 4.43k | input->seek(begPos, librevenge::RVNG_SEEK_SET); |
658 | 4.43k | libmwaw::DebugStream f; |
659 | 4.43k | f << "Link-" << std::hex << pos << std::dec << ":"; |
660 | 13.2k | for (int i=0; i<2; ++i) { |
661 | 8.86k | int val=int(input->readLong(2)); |
662 | 8.86k | if (val!=1-i) f << "f" << i << "=" << val << ","; |
663 | 8.86k | } |
664 | 4.43k | f << "unkn=" << std::hex << input->readULong(4) << std::dec << ","; // d66aa996 | d66aab1e maybe dirId, fileId |
665 | 4.43k | int dSz=int(input->readULong(1)); |
666 | 4.43k | if (begPos+9+dSz>entry.end()) { |
667 | 146 | MWAW_DEBUG_MSG(("MultiplanParser::readLinkFilename: the pos %d seems bad\n", pos)); |
668 | 146 | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
669 | 146 | return false; |
670 | 146 | } |
671 | 4.28k | librevenge::RVNGString filename; |
672 | 4.28k | auto fontConverter=getFontConverter(); |
673 | 4.28k | auto const fId=m_state->m_font.id(); |
674 | 153k | for (int i=0; i<dSz; ++i) { |
675 | 149k | auto ch=static_cast<unsigned char>(input->readULong(1)); |
676 | 149k | int unicode = fontConverter->unicode(fId, static_cast<unsigned char>(ch)); |
677 | 149k | if (unicode!=-1) |
678 | 62.0k | libmwaw::appendUnicode(uint32_t(unicode), filename); |
679 | 87.4k | else if (ch==0x9 || ch > 0x1f) |
680 | 7.36k | libmwaw::appendUnicode(static_cast<uint32_t>(ch), filename); |
681 | 80.0k | else { |
682 | 80.0k | f << "##"; |
683 | 80.0k | MWAW_DEBUG_MSG(("MultiplanParser::readLinkFilename: dir seems bad\n")); |
684 | 80.0k | } |
685 | 149k | } |
686 | 4.28k | instr.m_fileName=filename; |
687 | 4.28k | f << instr.m_fileName.cstr() << ","; |
688 | 4.28k | instr.m_sheet[0]="Sheet0"; |
689 | 4.28k | ascii().addPos(begPos); |
690 | 4.28k | ascii().addNote(f.str().c_str()); |
691 | 4.28k | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
692 | 4.28k | return true; |
693 | 4.43k | } |
694 | | |
695 | | bool MultiplanParser::readSharedData(int pos, int cellType, MWAWVec2i const &cellPos, MWAWCellContent &content) |
696 | 73.5k | { |
697 | 73.5k | auto const &entry=m_state->m_entries[7]; |
698 | 73.5k | MWAWInputStreamPtr input = getInput(); |
699 | 73.5k | if (!entry.valid() || pos<0 || pos+3>entry.length() || !input->checkPosition(entry.end())) { |
700 | 9.55k | MWAW_DEBUG_MSG(("MultiplanParser::readSharedData: the pos %d seems bad\n", pos)); |
701 | 9.55k | return false; |
702 | 9.55k | } |
703 | 63.9k | long actPos=input->tell(); |
704 | 63.9k | long begPos=entry.begin()+pos; |
705 | 63.9k | input->seek(begPos, librevenge::RVNG_SEEK_SET); |
706 | 63.9k | libmwaw::DebugStream f; |
707 | 63.9k | f << "SharedData-" << std::hex << pos << std::dec << ":"; |
708 | 63.9k | int type=int(input->readULong(2)); |
709 | 63.9k | f << "type=" << (type&3) << ","; |
710 | 63.9k | int N=(type/4); |
711 | 63.9k | if (N!=2) f << "used=" << N << ","; |
712 | 63.9k | int dSz=int(input->readULong(1)); |
713 | 63.9k | long endPos=begPos+3+dSz; |
714 | 63.9k | if (endPos>entry.end()) { |
715 | 1.63k | MWAW_DEBUG_MSG(("MultiplanParser::readSharedData: the pos %d seems bad\n", pos)); |
716 | 1.63k | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
717 | 1.63k | return false; |
718 | 1.63k | } |
719 | 62.3k | bool ok=true; |
720 | 62.3k | switch (type&3) { |
721 | 43.8k | case 0: |
722 | 43.8k | switch (cellType&3) { |
723 | 16.4k | case 0: { |
724 | 16.4k | double value; |
725 | 16.4k | if (dSz!=8 || !readDouble(value)) |
726 | 13.6k | ok=false; |
727 | 2.84k | else { |
728 | 2.84k | content.m_contentType=content.C_NUMBER; |
729 | 2.84k | content.setValue(value); |
730 | 2.84k | f << value << ","; |
731 | 2.84k | } |
732 | 16.4k | break; |
733 | 0 | } |
734 | 17.7k | case 1: { |
735 | 17.7k | content.m_contentType=content.C_TEXT; |
736 | 17.7k | content.m_textEntry.setBegin(input->tell()); |
737 | 17.7k | content.m_textEntry.setLength(dSz); |
738 | 17.7k | std::string name; |
739 | 209k | for (int c=0; c<dSz; ++c) name+=char(input->readULong(1)); |
740 | 17.7k | f << name << ","; |
741 | 17.7k | break; |
742 | 0 | } |
743 | 4.12k | case 2: |
744 | 4.12k | if (dSz!=8) |
745 | 3.71k | ok=false; |
746 | 412 | else { |
747 | 412 | f << "Nan" << input->readULong(1) << ","; |
748 | 412 | input->seek(7, librevenge::RVNG_SEEK_CUR); |
749 | 412 | content.m_contentType=content.C_NUMBER; |
750 | 412 | content.setValue(std::nan("")); |
751 | 412 | } |
752 | 4.12k | break; |
753 | 5.53k | case 3: |
754 | 5.53k | default: |
755 | 5.53k | if (dSz!=8) |
756 | 5.19k | ok=false; |
757 | 337 | else { |
758 | 337 | int val=int(input->readULong(1)); |
759 | 337 | content.m_contentType=content.C_NUMBER; |
760 | 337 | content.setValue(val); |
761 | 337 | if (val==0) |
762 | 0 | f << "false,"; |
763 | 337 | else if (val==1) |
764 | 0 | f << "true,"; |
765 | 337 | else |
766 | 337 | f << "##bool=" << val << ","; |
767 | 337 | input->seek(7, librevenge::RVNG_SEEK_CUR); |
768 | 337 | } |
769 | 5.53k | break; |
770 | 43.8k | } |
771 | 43.8k | break; |
772 | 43.8k | case 1: { |
773 | 13.4k | std::string err; |
774 | 13.4k | if (!readFormula(cellPos, content.m_formula, endPos, err)) |
775 | 0 | f << "###"; |
776 | 13.4k | else |
777 | 13.4k | content.m_contentType=content.C_FORMULA; |
778 | 26.4k | for (auto const &fo : content.m_formula) f << fo; |
779 | 13.4k | f << ","; |
780 | 13.4k | f << err; |
781 | 13.4k | break; |
782 | 43.8k | } |
783 | 5.07k | default: |
784 | 5.07k | ok=false; |
785 | 5.07k | break; |
786 | 62.3k | } |
787 | 62.3k | if (!ok) { |
788 | 27.5k | MWAW_DEBUG_MSG(("MultiplanParser::readSharedData: can not read data for the pos %d\n", pos)); |
789 | 27.5k | f << "###"; |
790 | 27.5k | } |
791 | 62.3k | if (m_state->m_posToSharedDataSeen.find(pos)==m_state->m_posToSharedDataSeen.end()) { |
792 | 19.9k | m_state->m_posToSharedDataSeen.insert(pos); |
793 | 19.9k | if (input->tell()!=endPos) |
794 | 4.90k | ascii().addDelimiter(input->tell(),'|'); |
795 | 19.9k | ascii().addPos(begPos); |
796 | 19.9k | ascii().addNote(f.str().c_str()); |
797 | 19.9k | } |
798 | 62.3k | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
799 | 62.3k | return true; |
800 | 62.3k | } |
801 | | |
802 | | bool MultiplanParser::readName(int pos, MWAWCellContent::FormulaInstruction &instruction) |
803 | 959 | { |
804 | 959 | auto it=m_state->m_posToNameMap.find(pos); |
805 | 959 | if (it!=m_state->m_posToNameMap.end()) { |
806 | 136 | instruction=it->second; |
807 | 136 | return true; |
808 | 136 | } |
809 | 823 | auto const &entry=m_state->m_entries[8]; // the named entry |
810 | 823 | if (!entry.valid() || pos<0 || pos+10>=entry.length()) { |
811 | 533 | MWAW_DEBUG_MSG(("MultiplanParser::readName: the pos %d seeems bad\n", pos)); |
812 | 533 | return false; |
813 | 533 | } |
814 | 290 | MWAWInputStreamPtr input = getInput(); |
815 | 290 | long actPos=input->tell(); |
816 | 290 | long begPos=entry.begin()+pos; |
817 | 290 | input->seek(begPos, librevenge::RVNG_SEEK_SET); |
818 | 290 | libmwaw::DebugStream f; |
819 | 290 | f << "Names-" << std::hex << pos << std::dec << ":"; |
820 | 290 | int val=int(input->readULong(1)); |
821 | 290 | int dSz=(val>>3); |
822 | 290 | if (dSz<=0 || begPos+10+dSz>entry.end()) { |
823 | 146 | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
824 | 146 | MWAW_DEBUG_MSG(("MultiplanParser::readName: the pos %d seeems bad\n", pos)); |
825 | 146 | return false; |
826 | 146 | } |
827 | 144 | if (val&3) f << "f0=" << val << ","; |
828 | 144 | val=int(input->readULong(1)); // 40|60 |
829 | 144 | if (val) f << "f1=" << std::hex << val << std::dec << ","; |
830 | 144 | int rows[2]; |
831 | 288 | for (auto &r : rows) r=int(input->readULong(1)); |
832 | 144 | val=int(input->readULong(2)); |
833 | 144 | int cols[2]= {(val>>10),(val>>4)&0x3f}; |
834 | 432 | for (int i=0; i<2; ++i) { |
835 | 288 | instruction.m_position[i]=MWAWVec2i(cols[i], rows[i]); |
836 | 288 | instruction.m_positionRelative[i]=MWAWVec2b(false,false); |
837 | 288 | } |
838 | 144 | instruction.m_type=instruction.m_position[0]==instruction.m_position[1] ? instruction.F_Cell : instruction.F_CellList; |
839 | 144 | f << instruction << ","; |
840 | 144 | m_state->m_posToNameMap[pos]=instruction; |
841 | 144 | if (val&0xf) f << "f2=" << (val&0xf) << ","; // 0|2 |
842 | 432 | for (int i=0; i<2; ++i) { // 0 |
843 | 288 | val=int(input->readLong(2)); |
844 | 288 | if (val) f << "f" << i+2 << "=" << val << ","; |
845 | 288 | } |
846 | 144 | std::string name; |
847 | 1.65k | for (int c=0; c<dSz; ++c) name+=char(input->readULong(1)); |
848 | 144 | f << name << ","; |
849 | 144 | ascii().addPos(begPos); |
850 | 144 | ascii().addNote(f.str().c_str()); |
851 | | |
852 | 144 | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
853 | 144 | return true; |
854 | 290 | } |
855 | | |
856 | | bool MultiplanParser::readZoneB() |
857 | 1.82k | { |
858 | 1.82k | MWAWInputStreamPtr input = getInput(); |
859 | 1.82k | long pos = input->tell(); |
860 | 1.82k | if (!input->checkPosition(pos+82)) { |
861 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::readZoneB: the zone seems too short\n")); |
862 | 0 | return false; |
863 | 0 | } |
864 | 1.82k | libmwaw::DebugStream f; |
865 | 1.82k | f << "Entries(ZoneB):"; |
866 | 1.82k | int dim[2]; |
867 | 3.64k | for (auto &d : dim) d=int(input->readULong(2)); |
868 | 1.82k | m_state->m_maximumCell=MWAWVec2i(dim[0],dim[1]); |
869 | 1.82k | f << "cell[max]=" << m_state->m_maximumCell << ","; |
870 | 1.82k | int val; |
871 | 14.5k | for (int i=0; i<7; ++i) { |
872 | 12.7k | val=int(input->readLong(2)); |
873 | 12.7k | int const expected[]= {0,0,0x7fff,0x47,0xc,0x1e7,0x10a}; |
874 | 12.7k | if (val!=expected[i]) |
875 | 9.49k | f << "f" << i << "=" << val << ","; |
876 | 12.7k | } |
877 | 29.1k | for (int i=0; i<15; ++i) { |
878 | 27.3k | val=int(input->readLong(2)); |
879 | 27.3k | if (!val) continue; |
880 | 12.8k | f << "g" << i << "=" << val << ","; |
881 | 12.8k | } |
882 | 1.82k | ascii().addPos(pos); |
883 | 1.82k | ascii().addNote(f.str().c_str()); |
884 | | |
885 | 1.82k | pos=input->tell(); |
886 | 1.82k | f.str(""); |
887 | 1.82k | f << "ZoneB[II]:"; |
888 | 5.46k | for (int i=0; i<2; ++i) { |
889 | 3.64k | val=int(input->readLong(1)); |
890 | 3.64k | if (val!=1-i) f << "f" << i << "=" << val << ","; |
891 | 3.64k | } |
892 | 1.82k | int dim4[4]; |
893 | 7.28k | for (auto &d : dim4) d=int(input->readULong(1)); |
894 | 1.82k | f << "selection=" << MWAWBox2i(MWAWVec2i(dim4[0],dim4[1]),MWAWVec2i(dim4[2],dim4[3])) << ","; |
895 | 36.4k | for (int i=0; i<19; ++i) { // 0 |
896 | 34.5k | val=int(input->readLong(1)); |
897 | 34.5k | if (val) f << "g" << i << "=" << val << ","; |
898 | 34.5k | } |
899 | 1.82k | ascii().addPos(pos); |
900 | 1.82k | ascii().addNote(f.str().c_str()); |
901 | | |
902 | 1.82k | input->seek(pos+82, librevenge::RVNG_SEEK_SET); |
903 | 1.82k | return true; |
904 | 1.82k | } |
905 | | |
906 | | bool MultiplanParser::readZoneC() |
907 | 1.82k | { |
908 | 1.82k | MWAWInputStreamPtr input = getInput(); |
909 | 1.82k | long pos = input->tell(); |
910 | 1.82k | if (!input->checkPosition(pos+22)) { |
911 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::readZoneC: the zone seems too short\n")); |
912 | 0 | return false; |
913 | 0 | } |
914 | 1.82k | libmwaw::DebugStream f; |
915 | 1.82k | f << "Entries(ZoneC):"; |
916 | 1.82k | int val; |
917 | 1.82k | f << "unkn=["; |
918 | 9.10k | for (int i=0; i<4; ++i) { // small number |
919 | 7.28k | val=int(input->readLong(2)); |
920 | 7.28k | if (val) |
921 | 5.37k | f << val << ","; |
922 | 1.90k | else |
923 | 1.90k | f << "_,"; |
924 | 7.28k | } |
925 | 1.82k | f << "],"; |
926 | 1.82k | val=int(input->readLong(2)); |
927 | 1.82k | if (val==1) |
928 | 3 | f << "protected,"; |
929 | 1.81k | else if (val) |
930 | 856 | f << "protected=#" << val << ","; |
931 | 1.82k | val=int(input->readULong(2)); |
932 | 1.82k | if (val) f << "passwd[crypted]=" << std::hex << val << std::dec << ","; |
933 | 10.9k | for (int i=0; i<5; ++i) { |
934 | 9.10k | val=int(input->readLong(2)); |
935 | 9.10k | int const expected[]= {0,0,0,2,1}; |
936 | 9.10k | if (val!=expected[i]) f << "g" << i << "=" << val << ","; |
937 | 9.10k | } |
938 | 1.82k | ascii().addPos(pos); |
939 | 1.82k | ascii().addNote(f.str().c_str()); |
940 | 1.82k | return true; |
941 | 1.82k | } |
942 | | |
943 | | //////////////////////////////////////////////////////////// |
944 | | // double |
945 | | //////////////////////////////////////////////////////////// |
946 | | bool MultiplanParser::readDouble(double &value) |
947 | 31.1k | { |
948 | 31.1k | MWAWInputStreamPtr input = getInput(); |
949 | 31.1k | long pos=input->tell(); |
950 | 31.1k | value=0; |
951 | 31.1k | if (!input->checkPosition(input->tell()+8)) { |
952 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::readDouble: the zone is too short\n")); |
953 | 0 | return false; |
954 | 0 | } |
955 | 31.1k | int exponant=int(input->readULong(1)); |
956 | 31.1k | double sign=1; |
957 | 31.1k | if (exponant&0x80) { |
958 | 5.71k | exponant&=0x7f; |
959 | 5.71k | sign=-1; |
960 | 5.71k | } |
961 | 31.1k | bool ok=true; |
962 | 31.1k | double factor=1; |
963 | 218k | for (int i=0; i<7; ++i) { |
964 | 192k | int val=int(input->readULong(1)); |
965 | 569k | for (int d=0; d<2; ++d) { |
966 | 381k | int v= d==0 ? (val>>4) : (val&0xf); |
967 | 381k | if (v>=10) { |
968 | 5.61k | MWAW_DEBUG_MSG(("MultiplanParser::readDouble: oops find a bad digits\n")); |
969 | 5.61k | ok=false; |
970 | 5.61k | break; |
971 | 5.61k | } |
972 | 376k | factor/=10.; |
973 | 376k | value+=factor*v; |
974 | 376k | } |
975 | 192k | if (!ok) break; |
976 | 192k | } |
977 | 31.1k | value *= sign*std::pow(10.,exponant-0x40); |
978 | 31.1k | input->seek(pos+8, librevenge::RVNG_SEEK_SET); |
979 | 31.1k | return ok; |
980 | 31.1k | } |
981 | | |
982 | | //////////////////////////////////////////////////////////// |
983 | | // formula |
984 | | //////////////////////////////////////////////////////////// |
985 | | namespace MultiplanParserInternal |
986 | | { |
987 | | struct Functions { |
988 | | char const *m_name; |
989 | | int m_arity; |
990 | | }; |
991 | | |
992 | | static Functions const s_listOperators[] = { |
993 | | // 0 |
994 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
995 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
996 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
997 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
998 | | // 1 |
999 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1000 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1001 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1002 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1003 | | // 2 |
1004 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1005 | | { "", -2}, { ":", 2}, { "", -2}, { "", -2}, |
1006 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1007 | | { "", -2}, { ":", 2}, { "", -2}, { "", -2}, |
1008 | | // 3 |
1009 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1010 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1011 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1012 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1013 | | // 4 |
1014 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1015 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1016 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1017 | | { "", -2}, { ":", 2}, { "", -2}, { "", -2}, |
1018 | | // 5 |
1019 | | { "&", 2}, { "", -2}, { "", -2}, { "", -2}, |
1020 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1021 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1022 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1023 | | // 6 |
1024 | | { "<", 2}, { "", -2}, { "<=", 2}, { "", -2}, |
1025 | | { "=", 2}, { "", -2}, { ">=", 2}, { "", -2}, |
1026 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1027 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1028 | | // 7 |
1029 | | { ">", 2}, { "", -2}, { "<>", 2}, { "", -2}, |
1030 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1031 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1032 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1033 | | // 8 |
1034 | | { "", -2}, { "", -2}, { "+", 2}, { "", -2}, |
1035 | | { "-", 2}, { "", -2}, { "*", 2}, { "", -2}, |
1036 | | { "/", 2}, { "", -2}, { "^", 2}, { "", -2}, |
1037 | | { "", -2}, { "", -2}, { "-", 1}, { "", -2}, |
1038 | | // 9 |
1039 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1040 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1041 | | { "%", 1}, { "", -2}, { "", -2}, { "", -2}, |
1042 | | { "", -2}, { "", -2}, { "", -2}, { "", -2}, |
1043 | | }; |
1044 | | |
1045 | | static char const* const s_listFunctions[]= { |
1046 | | // 0 |
1047 | | "Count", "If", "IsNA", "IsError", |
1048 | | "Sum", "Average", "Min", "Max", |
1049 | | "Row", "Column", "NA", "NPV", |
1050 | | "Stdev", "Dollar", "Fixed", "Sin", |
1051 | | // 1 |
1052 | | "Cos", "Tan", "Atan", "Pi", |
1053 | | "Sqrt", "Exp", "Ln", "Log", |
1054 | | "Abs", "Int", "Sign", "Round", |
1055 | | "Lookup", "Index", "Rept", "Mid", |
1056 | | // 2 |
1057 | | "Length", "Value", "True", "False", |
1058 | | "And", "Or", "Not", "Mod", |
1059 | | "IterCnt", "Delta", "PV", "FV", |
1060 | | "NPer", "PMT", "Rate", "MIRR", |
1061 | | // 3 |
1062 | | "Irr", nullptr, nullptr, nullptr, |
1063 | | nullptr, nullptr, nullptr, nullptr, |
1064 | | nullptr, nullptr, nullptr, nullptr, |
1065 | | nullptr, nullptr, nullptr, nullptr, |
1066 | | }; |
1067 | | } |
1068 | | |
1069 | | bool MultiplanParser::readFormula(MWAWVec2i const &cellPos, std::vector<MWAWCellContent::FormulaInstruction> &formula, long endPos, std::string &error) |
1070 | 562k | { |
1071 | 562k | formula.clear(); |
1072 | 562k | MWAWInputStreamPtr input = getInput(); |
1073 | 562k | if (!input || !input->checkPosition(endPos)) { |
1074 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::readFormula: bad position\n")); |
1075 | 0 | error="badPos###"; |
1076 | 0 | return false; |
1077 | 0 | } |
1078 | 562k | std::vector<std::vector<MWAWCellContent::FormulaInstruction> > stack; |
1079 | 562k | auto const &listOperators=MultiplanParserInternal::s_listOperators; |
1080 | 562k | int const numOperators=int(MWAW_N_ELEMENTS(MultiplanParserInternal::s_listOperators)); |
1081 | 562k | auto const &listFunctions=MultiplanParserInternal::s_listFunctions; |
1082 | 562k | int const numFunctions=int(MWAW_N_ELEMENTS(MultiplanParserInternal::s_listFunctions)); |
1083 | 562k | bool ok=true; |
1084 | 562k | int closeDelayed=0; |
1085 | 562k | bool checkForClose=false; |
1086 | 712k | while (input->tell()<=endPos) { |
1087 | 712k | long pos=input->tell(); |
1088 | 712k | int wh=pos==endPos ? -1 : int(input->readULong(1)); |
1089 | 712k | bool needCloseParenthesis=closeDelayed && (checkForClose || pos==endPos); |
1090 | 712k | ok=true; |
1091 | 712k | if (closeDelayed && !needCloseParenthesis && wh!=0x3c) |
1092 | 19.9k | needCloseParenthesis=wh>=numOperators || listOperators[wh].m_arity!=2; |
1093 | 712k | while (needCloseParenthesis && closeDelayed>0) { |
1094 | 20.2k | auto len=stack.size(); |
1095 | 20.2k | if (len<2) { |
1096 | 19.7k | error="##closedParenthesis,"; |
1097 | 19.7k | ok=false; |
1098 | 19.7k | break; |
1099 | 19.7k | } |
1100 | 475 | auto &dParenthesisFunc=stack[len-2]; |
1101 | 475 | if (dParenthesisFunc.size()!=1 || dParenthesisFunc[0].m_type!=dParenthesisFunc[0].F_Operator || |
1102 | 379 | dParenthesisFunc[0].m_content!="(") { |
1103 | 99 | error="##closedParenthesis,"; |
1104 | 99 | ok=false; |
1105 | 99 | break; |
1106 | 99 | } |
1107 | 376 | dParenthesisFunc.insert(dParenthesisFunc.end(),stack.back().begin(), stack.back().end()); |
1108 | 376 | MWAWCellContent::FormulaInstruction instr; |
1109 | 376 | instr.m_type=instr.F_Operator; |
1110 | 376 | instr.m_content=")"; |
1111 | 376 | dParenthesisFunc.push_back(instr); |
1112 | 376 | stack.resize(len-1); |
1113 | 376 | --closeDelayed; |
1114 | 376 | } |
1115 | 712k | if (!ok || pos==endPos) |
1116 | 31.1k | break; |
1117 | 681k | int arity=0; |
1118 | 681k | MWAWCellContent::FormulaInstruction instr; |
1119 | 681k | ok=false; |
1120 | 681k | bool noneInstr=false, closeFunction=false; |
1121 | 681k | switch (wh) { |
1122 | 139k | case 0: |
1123 | 139k | if (pos+3>endPos || !readLink(int(input->readULong(2)), instr)) |
1124 | 121k | break; |
1125 | 17.9k | ok=true; |
1126 | 17.9k | break; |
1127 | 12.1k | case 0x12: { |
1128 | 12.1k | if (pos+2>endPos) |
1129 | 717 | break; |
1130 | 11.4k | ok=true; |
1131 | 11.4k | instr.m_type=instr.F_Function; |
1132 | 11.4k | int id=int(input->readULong(1)); |
1133 | 11.4k | if (id<numFunctions && listFunctions[id]) |
1134 | 10.8k | instr.m_content=listFunctions[id]; |
1135 | 654 | else { |
1136 | 654 | std::stringstream s; |
1137 | 654 | s << "Funct" << std::hex << id << std::dec; |
1138 | 654 | instr.m_content=s.str(); |
1139 | 654 | } |
1140 | 11.4k | std::vector<MWAWCellContent::FormulaInstruction> child; |
1141 | 11.4k | child.push_back(instr); |
1142 | 11.4k | stack.push_back(child); |
1143 | 11.4k | instr.m_type=instr.F_Operator; |
1144 | 11.4k | instr.m_content="("; |
1145 | 11.4k | break; |
1146 | 12.1k | } |
1147 | 992 | case 0x51: |
1148 | 5.42k | case 0x71: |
1149 | 7.43k | case 0x91: |
1150 | 7.82k | case 0xd1: |
1151 | 8.47k | case 0xf1: |
1152 | 8.47k | closeFunction=ok=true; |
1153 | 8.47k | break; |
1154 | 4.96k | case 0x1c: // use before % |
1155 | 8.61k | case 0x1e: // use for <> A 1e B "code <>" |
1156 | 10.1k | case 0x34: // use for <=,>= ... A 34 B "code <=,..." |
1157 | 11.8k | case 0x36: // use before -unary |
1158 | 11.8k | noneInstr=ok=true; |
1159 | 11.8k | break; |
1160 | 6.43k | case 0x3a: |
1161 | 6.43k | ok=true; |
1162 | 6.43k | instr.m_type=instr.F_Operator; |
1163 | 6.43k | instr.m_content=";"; |
1164 | 6.43k | break; |
1165 | 27.5k | case 0x3c: |
1166 | 27.5k | noneInstr=ok=true; |
1167 | 27.5k | ++closeDelayed; |
1168 | 27.5k | break; |
1169 | 20.7k | case 0x3e: |
1170 | 20.7k | ok=true; |
1171 | 20.7k | instr.m_type=instr.F_Operator; |
1172 | 20.7k | instr.m_content="("; |
1173 | 20.7k | break; |
1174 | 12.9k | case 0x56: { |
1175 | 12.9k | int dSz=int(input->readULong(1)); |
1176 | 12.9k | if (pos+2+dSz>endPos) |
1177 | 12.0k | break; |
1178 | 911 | instr.m_type=instr.F_Text; |
1179 | 911 | auto fontConverter=getFontConverter(); |
1180 | 911 | auto const fId=m_state->m_font.id(); |
1181 | 911 | librevenge::RVNGString content; |
1182 | 14.1k | for (int i=0; i<dSz; ++i) { |
1183 | 13.2k | auto ch=static_cast<unsigned char>(input->readULong(1)); |
1184 | 13.2k | int unicode = fontConverter->unicode(fId, static_cast<unsigned char>(ch)); |
1185 | 13.2k | if (unicode!=-1) |
1186 | 8.94k | libmwaw::appendUnicode(uint32_t(unicode), content); |
1187 | 4.28k | else if (ch==0x9 || ch > 0x1f) |
1188 | 993 | libmwaw::appendUnicode(static_cast<uint32_t>(ch), content); |
1189 | 3.29k | else { |
1190 | 3.29k | MWAW_DEBUG_MSG(("MultiplanParser::readFormula: content seen bad seems bad\n")); |
1191 | 3.29k | error="##content"; |
1192 | 3.29k | } |
1193 | 13.2k | } |
1194 | 911 | instr.m_content=content.cstr(); |
1195 | 911 | ok=true; |
1196 | 911 | break; |
1197 | 12.9k | } |
1198 | 32.1k | case 0x21: // double |
1199 | 32.9k | case 0xe1: |
1200 | 33.1k | case 0x8f: // simple |
1201 | 33.3k | case 0xef: |
1202 | 33.3k | if (pos+3>endPos) |
1203 | 580 | break; |
1204 | 32.7k | instr.m_type=instr.F_Cell; |
1205 | 32.7k | instr.m_positionRelative[0]=MWAWVec2b(false,false); |
1206 | 32.7k | instr.m_position[0][1]=int(input->readULong(1)); |
1207 | 32.7k | instr.m_position[0][0]=int(input->readULong(1)); |
1208 | 32.7k | ok=(instr.m_position[0][0]<63) && (instr.m_position[0][1]<255); |
1209 | 32.7k | if (!ok) { |
1210 | 8.44k | error="###RorC"; |
1211 | 8.44k | MWAW_DEBUG_MSG(("MultiplanParser::readFormula: find only row/column reference\n")); |
1212 | 8.44k | } |
1213 | 32.7k | break; |
1214 | 5.09k | case 0x29: // example C2 R1 |
1215 | 5.09k | MWAW_DEBUG_MSG(("MultiplanParser::readFormula: find union operator\n")); |
1216 | 5.09k | error="###union"; |
1217 | 5.09k | ok=false; |
1218 | 5.09k | break; |
1219 | 3.02k | case 0x37: // use for list cell |
1220 | 4.52k | case 0x53: |
1221 | 4.83k | case 0x73: |
1222 | 6.23k | case 0x93: // basic cell |
1223 | 7.61k | case 0xf3: { // difference ? |
1224 | 7.61k | if (pos+3>endPos) |
1225 | 786 | break; |
1226 | 6.83k | instr.m_type=instr.F_Cell; |
1227 | 6.83k | instr.m_positionRelative[0]=MWAWVec2b(true,true); |
1228 | 6.83k | int val=int(input->readULong(2)); |
1229 | 6.83k | auto &newPos=instr.m_position[0]; |
1230 | 6.83k | if (val&0x80) |
1231 | 2.75k | newPos[1]=cellPos[1]-(val>>8); |
1232 | 4.07k | else |
1233 | 4.07k | newPos[1]=cellPos[1]+(val>>8); |
1234 | 6.83k | if (val&0x40) |
1235 | 2.02k | newPos[0]=cellPos[0]-(val&0x3f); |
1236 | 4.81k | else |
1237 | 4.81k | newPos[0]=cellPos[0]+(val&0x3f); |
1238 | 6.83k | ok=newPos[0]>=0 && newPos[1]>=0; |
1239 | 6.83k | break; |
1240 | 7.61k | } |
1241 | 9.58k | case 0x94: |
1242 | 9.58k | if (pos+9>endPos || !readDouble(instr.m_doubleValue)) |
1243 | 646 | break; |
1244 | 8.93k | instr.m_type=instr.F_Double; |
1245 | 8.93k | ok=true; |
1246 | 8.93k | break; |
1247 | 1.04k | case 0xf5: |
1248 | 1.04k | if (pos+3>endPos || !readName(int(input->readULong(2)), instr)) |
1249 | 764 | break; |
1250 | 280 | ok=true; |
1251 | 280 | break; |
1252 | 384k | default: |
1253 | 384k | if (wh<numOperators && listOperators[wh].m_arity!=-2) { |
1254 | 18.0k | instr.m_content=listOperators[wh].m_name; |
1255 | 18.0k | instr.m_type=instr.F_Function; |
1256 | 18.0k | arity=listOperators[wh].m_arity; |
1257 | 18.0k | } |
1258 | 384k | if (instr.m_content.empty()) { |
1259 | 366k | MWAW_DEBUG_MSG(("MultiplanParser::readFormula: find unknown type %x\n", wh)); |
1260 | 366k | std::stringstream s; |
1261 | 366k | s << "##unkn[func]=" << std::hex << wh << std::dec << ","; |
1262 | 366k | error=s.str(); |
1263 | 366k | break; |
1264 | 366k | } |
1265 | 18.0k | ok=true; |
1266 | 18.0k | break; |
1267 | 681k | } |
1268 | 681k | if (!ok) { |
1269 | 518k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1270 | 518k | break; |
1271 | 518k | } |
1272 | 162k | checkForClose=!noneInstr && closeDelayed>0; |
1273 | 162k | if (noneInstr) continue; |
1274 | 123k | if (closeFunction) { |
1275 | 8.47k | ok=false; |
1276 | 8.47k | if (stack.empty()) { |
1277 | 4.76k | error="##closed,"; |
1278 | 4.76k | break; |
1279 | 4.76k | } |
1280 | 3.70k | auto it=stack.end(); |
1281 | 3.70k | --it; |
1282 | 9.07k | for (; it!=stack.begin(); --it) { |
1283 | 8.38k | if (it->size()!=1) continue; |
1284 | 7.53k | auto const &dInstr=(*it)[0]; |
1285 | 7.53k | if (dInstr.m_type!=dInstr.F_Operator || dInstr.m_content!="(") continue; |
1286 | 3.02k | auto fIt=it; |
1287 | 3.02k | --fIt; |
1288 | 3.02k | auto &functionStack=*fIt; |
1289 | 3.02k | if (functionStack.size()!=1 || functionStack[0].m_type!=functionStack[0].F_Function) continue; |
1290 | 3.01k | ok=true; |
1291 | 10.3k | for (; it!=stack.end(); ++it) |
1292 | 7.37k | functionStack.insert(functionStack.end(), it->begin(), it->end()); |
1293 | 3.01k | ++fIt; |
1294 | 3.01k | stack.erase(fIt, stack.end()); |
1295 | 3.01k | break; |
1296 | 3.02k | } |
1297 | 3.70k | if (!ok) { |
1298 | 693 | error="##closed"; |
1299 | 693 | break; |
1300 | 693 | } |
1301 | 3.01k | instr.m_type=instr.F_Operator; |
1302 | 3.01k | instr.m_content=")"; |
1303 | 3.01k | stack.back().push_back(instr); |
1304 | 3.01k | continue; |
1305 | 3.70k | } |
1306 | 115k | if (instr.m_type!=MWAWCellContent::FormulaInstruction::F_Function) { |
1307 | 97.1k | std::vector<MWAWCellContent::FormulaInstruction> child; |
1308 | 97.1k | child.push_back(instr); |
1309 | 97.1k | stack.push_back(child); |
1310 | 97.1k | continue; |
1311 | 97.1k | } |
1312 | 18.0k | size_t numElt = stack.size(); |
1313 | 18.0k | if (static_cast<int>(numElt) < arity) { |
1314 | 7.45k | std::stringstream s; |
1315 | 7.45k | s << instr.m_content << "[##" << arity << "]"; |
1316 | 7.45k | error=s.str(); |
1317 | 7.45k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1318 | 7.45k | ok=false; |
1319 | 7.45k | break; |
1320 | 7.45k | } |
1321 | 10.5k | if (arity==1) { |
1322 | 454 | instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator; |
1323 | 454 | if (instr.m_content=="%") |
1324 | 99 | stack[numElt-1].push_back(instr); |
1325 | 355 | else |
1326 | 355 | stack[numElt-1].insert(stack[numElt-1].begin(), instr); |
1327 | 454 | continue; |
1328 | 454 | } |
1329 | 10.0k | if (arity==2) { |
1330 | 10.0k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator; |
1331 | 10.0k | stack[numElt-2].push_back(instr); |
1332 | 10.0k | stack[numElt-2].insert(stack[numElt-2].end(), stack[numElt-1].begin(), stack[numElt-1].end()); |
1333 | 10.0k | stack.resize(numElt-1); |
1334 | 10.0k | continue; |
1335 | 10.0k | } |
1336 | 0 | ok=false; |
1337 | 0 | error = "### unexpected arity"; |
1338 | 0 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1339 | 0 | break; |
1340 | 10.0k | } |
1341 | 562k | long pos=input->tell(); |
1342 | 562k | if (pos!=endPos || !ok || closeDelayed || stack.size()!=1 || stack[0].empty()) { |
1343 | 555k | MWAW_DEBUG_MSG(("MultiplanParser::readFormula: can not read a formula\n")); |
1344 | 555k | ascii().addDelimiter(pos, '|'); |
1345 | 555k | input->seek(endPos, librevenge::RVNG_SEEK_SET); |
1346 | | |
1347 | 555k | std::stringstream s; |
1348 | 555k | if (!error.empty()) |
1349 | 412k | s << error; |
1350 | 142k | else |
1351 | 142k | s << "##unknownError"; |
1352 | 555k | s << "["; |
1353 | 555k | for (auto const &i : stack) { |
1354 | 83.8k | for (auto const &j : i) |
1355 | 93.9k | s << j << ","; |
1356 | 83.8k | } |
1357 | 555k | s << "],"; |
1358 | 555k | error=s.str(); |
1359 | 555k | return true; |
1360 | 555k | } |
1361 | 6.86k | formula=stack[0]; |
1362 | 6.86k | return true; |
1363 | 562k | } |
1364 | | |
1365 | | //////////////////////////////////////////////////////////// |
1366 | | // read the header |
1367 | | //////////////////////////////////////////////////////////// |
1368 | | bool MultiplanParser::checkHeader(MWAWHeader *header, bool strict) |
1369 | 7.31k | { |
1370 | 7.31k | *m_state = MultiplanParserInternal::State(); |
1371 | | |
1372 | 7.31k | MWAWInputStreamPtr input = getInput(); |
1373 | 7.31k | if (!input || !input->hasDataFork()) |
1374 | 2 | return false; |
1375 | | |
1376 | 7.31k | if (!input->checkPosition(0x778)) { |
1377 | 137 | MWAW_DEBUG_MSG(("MultiplanParser::checkHeader: file is too short\n")); |
1378 | 137 | return false; |
1379 | 137 | } |
1380 | 7.17k | input->seek(0,librevenge::RVNG_SEEK_SET); |
1381 | 7.17k | if (input->readULong(2)!=0x11ab || input->readULong(2)!=0 || |
1382 | 7.04k | input->readULong(2)!=0x13e8 || input->readULong(2)!=0) |
1383 | 248 | return false; |
1384 | 6.92k | libmwaw::DebugStream f; |
1385 | 6.92k | f << "FileHeader:"; |
1386 | 20.7k | for (int i=0; i<2; ++i) { |
1387 | 13.8k | int val=int(input->readLong(2)); |
1388 | 13.8k | if (val) |
1389 | 6.38k | f << "f" << i << "=" << val << ","; |
1390 | 13.8k | } |
1391 | 6.92k | ascii().addPos(0); |
1392 | 6.92k | ascii().addNote(f.str().c_str()); |
1393 | 6.92k | if (strict) { |
1394 | | // read the last zone list position and check that it corresponds to a valid position |
1395 | 2.74k | input->seek(0x758, librevenge::RVNG_SEEK_SET); |
1396 | 2.74k | int val=int(input->readULong(2)); |
1397 | 2.74k | if (val<0x3c || !input->checkPosition(0x75a+val)) { |
1398 | 548 | MWAW_DEBUG_MSG(("MultiplanParser::checkHeader: can not find last spreadsheet position\n")); |
1399 | 548 | return false; |
1400 | 548 | } |
1401 | 2.74k | } |
1402 | | /* checkme: MsMultiplan DOS begins by a list of 8 potential filename |
1403 | | linked to this files (with length 0x1f) maybe something like |
1404 | | that... */ |
1405 | 6.37k | input->seek(0x30,librevenge::RVNG_SEEK_SET); |
1406 | 6.37k | ascii().addPos(0x30); |
1407 | 6.37k | ascii().addNote("Entries(ZoneA):"); |
1408 | 31.8k | for (int i=0; i<4; ++i) { |
1409 | 25.5k | long pos=input->tell(); |
1410 | 25.5k | f.str(""); |
1411 | 25.5k | f << "ZoneA" << i << ":"; |
1412 | 25.5k | ascii().addPos(pos); |
1413 | 25.5k | ascii().addNote(f.str().c_str()); |
1414 | 25.5k | input->seek(pos+0x80,librevenge::RVNG_SEEK_SET); |
1415 | 25.5k | } |
1416 | 6.37k | long pos=input->tell(); |
1417 | 6.37k | ascii().addPos(pos); |
1418 | 6.37k | ascii().addNote("ZoneA4"); |
1419 | 6.37k | input->seek(0x272,librevenge::RVNG_SEEK_SET); |
1420 | 6.37k | if (header) |
1421 | 2.73k | header->reset(MWAWDocument::MWAW_T_MICROSOFTMULTIPLAN, 1, MWAWDocument::MWAW_K_SPREADSHEET); |
1422 | 6.37k | return true; |
1423 | 6.92k | } |
1424 | | |
1425 | | //////////////////////////////////////////////////////////// |
1426 | | // send spreadsheet |
1427 | | //////////////////////////////////////////////////////////// |
1428 | | bool MultiplanParser::sendText(MWAWEntry const &entry) |
1429 | 3.12k | { |
1430 | 3.12k | auto listener=getMainListener(); |
1431 | 3.12k | if (!listener) { |
1432 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::sendText: can not find the listener\n")); |
1433 | 0 | return false; |
1434 | 0 | } |
1435 | 3.12k | listener->setFont(m_state->m_font); |
1436 | 3.12k | auto input=getInput(); |
1437 | 3.12k | input->seek(entry.begin(), librevenge::RVNG_SEEK_SET); |
1438 | 445k | for (long l=0; l<entry.length(); ++l) { |
1439 | 442k | if (input->isEnd()) { |
1440 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::sendText: oops, can not read a character\n")); |
1441 | 0 | break; |
1442 | 0 | } |
1443 | 442k | auto c=static_cast<unsigned char>(input->readULong(1)); |
1444 | 442k | if (c==0x9) |
1445 | 2.64k | listener->insertTab(); |
1446 | 439k | else if (c==0xa || c==0xd) |
1447 | 10.8k | listener->insertEOL(); |
1448 | 428k | else |
1449 | 428k | listener->insertCharacter(c); |
1450 | 442k | } |
1451 | 3.12k | return true; |
1452 | 3.12k | } |
1453 | | |
1454 | | bool MultiplanParser::sendCell(MWAWVec2i const &cellPos, int p) |
1455 | 4.30M | { |
1456 | 4.30M | MWAWSpreadsheetListenerPtr listener=getSpreadsheetListener(); |
1457 | 4.30M | if (!listener) { |
1458 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::sendCell: I can not find the listener\n")); |
1459 | 0 | return false; |
1460 | 0 | } |
1461 | 4.30M | auto const &entry=m_state->m_entries[6]; |
1462 | 4.30M | if (p<=0 || p>entry.length()) { |
1463 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::sendCell: unexpected position %d\n", p)); |
1464 | 0 | return false; |
1465 | 0 | } |
1466 | 4.30M | MWAWCell cell; |
1467 | 4.30M | MWAWCellContent content; |
1468 | 4.30M | MWAWCell::Format format; |
1469 | 4.30M | cell.setPosition(cellPos); |
1470 | 4.30M | cell.setFont(m_state->m_font); |
1471 | 4.30M | libmwaw::DebugStream f; |
1472 | 4.30M | f << "DataCell[C" << cellPos[0]+1 << "R" << cellPos[1]+1 << "]:"; |
1473 | 4.30M | long pos=entry.begin()+p; |
1474 | 4.30M | auto it=m_state->m_cellPositionsSet.find(p); |
1475 | 4.30M | ++it; |
1476 | 4.30M | long endPos=it!=m_state->m_cellPositionsSet.end() ? entry.begin()+*it : entry.end(); |
1477 | 4.30M | auto input=getInput(); |
1478 | 4.30M | if (endPos-pos<4 || !input->checkPosition(endPos)) { |
1479 | 1.85M | MWAW_DEBUG_MSG(("MultiplanParser::sendCell: a cell %d seems to short\n", p)); |
1480 | 1.85M | f << "###"; |
1481 | 1.85M | ascii().addPos(pos); |
1482 | 1.85M | ascii().addNote(f.str().c_str()); |
1483 | 1.85M | return false; |
1484 | 1.85M | } |
1485 | 2.45M | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1486 | 2.45M | int formSize=int(input->readULong(1)); |
1487 | 2.45M | if (formSize) f << "form[size]=" << std::hex << formSize << std::dec << ","; |
1488 | 2.45M | int val=int(input->readULong(1)); |
1489 | 2.45M | int digits=(val&0xf); |
1490 | 2.45M | if (digits) f << "decimal=" << digits << ","; |
1491 | 2.45M | int form=(val>>4)&7; |
1492 | 2.45M | format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC; |
1493 | 2.45M | switch (form) { |
1494 | 442k | case 2: |
1495 | 442k | format.m_numberFormat=MWAWCell::F_NUMBER_SCIENTIFIC; |
1496 | 442k | format.m_digits=digits; |
1497 | 442k | f << "scientific,"; |
1498 | 442k | break; |
1499 | 105k | case 3: |
1500 | 105k | format.m_numberFormat=MWAWCell::F_NUMBER_DECIMAL; |
1501 | 105k | format.m_digits=digits; |
1502 | 105k | f << "decimal,"; |
1503 | 105k | break; |
1504 | 148k | case 4: // default |
1505 | 148k | break; |
1506 | 128k | case 5: |
1507 | 128k | format.m_numberFormat=MWAWCell::F_NUMBER_CURRENCY; |
1508 | 128k | format.m_digits=digits; |
1509 | 128k | f << "currency,"; |
1510 | 128k | break; |
1511 | 121k | case 6: // a bar |
1512 | 121k | f << "bar,"; |
1513 | 121k | break; |
1514 | 103k | case 7: |
1515 | 103k | format.m_numberFormat=MWAWCell::F_NUMBER_PERCENT; |
1516 | 103k | format.m_digits=digits; |
1517 | 103k | f << "percent,"; |
1518 | 103k | break; |
1519 | 1.40M | default: |
1520 | 1.40M | f << "format=" << form << ","; |
1521 | 1.40M | break; |
1522 | 2.45M | } |
1523 | 2.45M | cell.setProtected((val&0x80)!=0); |
1524 | 2.45M | if ((val&0x80)==0) f << "no[protection],"; |
1525 | 2.45M | val=int(input->readULong(1)); |
1526 | 2.45M | int align=(val>>2)&7; |
1527 | 2.45M | switch (align) { |
1528 | 170k | case 1: |
1529 | 170k | cell.setHAlignment(cell.HALIGN_CENTER); |
1530 | 170k | f << "center,"; |
1531 | 170k | break; |
1532 | 1.27M | case 0: // default |
1533 | 1.46M | case 2: // generic |
1534 | 1.46M | break; |
1535 | 395k | case 3: |
1536 | 395k | cell.setHAlignment(cell.HALIGN_LEFT); |
1537 | 395k | f << "left,"; |
1538 | 395k | break; |
1539 | 132k | case 4: |
1540 | 132k | cell.setHAlignment(cell.HALIGN_RIGHT); |
1541 | 132k | f << "right,"; |
1542 | 132k | break; |
1543 | 291k | default: |
1544 | 291k | f << "#align=" << (align) << ","; |
1545 | 291k | break; |
1546 | 2.45M | } |
1547 | 2.45M | switch (val&3) { |
1548 | 1.27M | case 0: |
1549 | 1.27M | f << "double,"; |
1550 | 1.27M | format.m_format=MWAWCell::F_NUMBER; |
1551 | 1.27M | content.m_contentType=content.C_NUMBER; |
1552 | 1.27M | break; |
1553 | 428k | case 1: |
1554 | 428k | format.m_format=MWAWCell::F_TEXT; |
1555 | 428k | content.m_contentType=content.C_TEXT; |
1556 | 428k | f << "text,"; |
1557 | 428k | break; |
1558 | 236k | case 2: |
1559 | 236k | format.m_format=MWAWCell::F_NUMBER; |
1560 | 236k | content.m_contentType=content.C_NUMBER; |
1561 | 236k | f << "nan,"; |
1562 | 236k | break; |
1563 | 514k | case 3: // or nothing |
1564 | 514k | format.m_format=MWAWCell::F_BOOLEAN; |
1565 | 514k | content.m_contentType=content.C_NUMBER; |
1566 | 514k | f << "bool,"; |
1567 | 514k | break; |
1568 | 0 | default: // impossible |
1569 | 0 | break; |
1570 | 2.45M | } |
1571 | 2.45M | cell.setFormat(format); |
1572 | 2.45M | if ((val&0x20)==0) |
1573 | 1.63M | f << "no20[f1],"; |
1574 | 2.45M | if (val&0x40) |
1575 | 520k | f << "shared,"; |
1576 | 2.45M | int type=(val&0xe3); |
1577 | 2.45M | if (val&0x80) f << "80[f1],"; |
1578 | 2.45M | int dSz=int(input->readULong(1)); |
1579 | 2.45M | if (endPos<pos+4+dSz) { |
1580 | 862k | MWAW_DEBUG_MSG(("MultiplanParser::sendCell: a cell seems to short\n")); |
1581 | 862k | f << "###"; |
1582 | 862k | ascii().addPos(pos); |
1583 | 862k | ascii().addNote(f.str().c_str()); |
1584 | 862k | return false; |
1585 | 862k | } |
1586 | 1.59M | if ((type&0x3)==0 && dSz==8) { |
1587 | 18.6k | double value; |
1588 | 18.6k | if (!readDouble(value)) |
1589 | 4.89k | f << "###"; |
1590 | 13.7k | else |
1591 | 13.7k | content.setValue(value); |
1592 | 18.6k | f << value << ","; |
1593 | 18.6k | } |
1594 | 1.57M | else if ((type&0x3)==1 && dSz && pos+4+dSz+((type&0x40) ? 2 : 0) <= endPos) { |
1595 | 133k | content.m_textEntry.setBegin(input->tell()); |
1596 | 133k | content.m_textEntry.setLength(dSz); |
1597 | 133k | std::string name; |
1598 | 4.61M | for (int c=0; c<dSz; ++c) name+=char(input->readULong(1)); |
1599 | 133k | f << name << ","; |
1600 | 133k | } |
1601 | 1.44M | else if ((type&0x3)==2 && dSz==8) { |
1602 | 4.34k | content.setValue(std::nan("")); |
1603 | 4.34k | f << "Nan" << input->readULong(1) << ","; |
1604 | 4.34k | input->seek(7, librevenge::RVNG_SEEK_CUR); |
1605 | 4.34k | } |
1606 | 1.44M | else if ((type&0x3)==3 && dSz==8) { |
1607 | 4.39k | val=int(input->readULong(1)); |
1608 | 4.39k | content.setValue(val); |
1609 | 4.39k | if (val==0) |
1610 | 1.89k | f << "false,"; |
1611 | 2.50k | else if (val==1) |
1612 | 1.49k | f << "true,"; |
1613 | 1.01k | else |
1614 | 1.01k | f << "##bool=" << val << ","; |
1615 | 4.39k | input->seek(7, librevenge::RVNG_SEEK_CUR); |
1616 | 4.39k | } |
1617 | 1.59M | if ((type&0x40) && input->tell()+2<=endPos && (formSize==0 || formSize==2)) { |
1618 | 73.5k | if ((input->tell()-pos)%2) |
1619 | 3.46k | input->seek(1, librevenge::RVNG_SEEK_CUR); |
1620 | 73.5k | int nPos=int(input->readULong(2)); |
1621 | 73.5k | if (!readSharedData(nPos, type, cellPos, content)) |
1622 | 11.1k | f << "###"; |
1623 | 73.5k | f << "sharedData-" << std::hex << nPos << std::dec << ","; |
1624 | 73.5k | } |
1625 | 1.52M | else if (!(type&0x40) && formSize && input->tell()+formSize<=endPos) { |
1626 | 548k | auto endFPos=input->tell()+formSize; |
1627 | 548k | std::string err; |
1628 | 548k | if (!readFormula(cellPos, content.m_formula, endFPos, err)) { |
1629 | 0 | ascii().addDelimiter(input->tell(),'|'); |
1630 | 0 | f << "###"; |
1631 | 0 | } |
1632 | 548k | else |
1633 | 548k | content.m_contentType=content.C_FORMULA; |
1634 | | |
1635 | 548k | for (auto const &fo : content.m_formula) f << fo; |
1636 | 548k | f << ","; |
1637 | 548k | f << err; |
1638 | 548k | input->seek(endFPos, librevenge::RVNG_SEEK_SET); |
1639 | 548k | } |
1640 | 973k | else if (formSize) { |
1641 | 400k | MWAW_DEBUG_MSG(("MultiplanParser::sendCell: can not read a formula\n")); |
1642 | 400k | f << "###form"; |
1643 | 400k | } |
1644 | 1.59M | listener->openSheetCell(cell, content); |
1645 | 1.59M | if (content.m_textEntry.valid()) { |
1646 | 122k | listener->setFont(cell.getFont()); |
1647 | 122k | input->seek(content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET); |
1648 | 3.70M | while (!input->isEnd() && input->tell()<content.m_textEntry.end()) { |
1649 | 3.58M | auto c=static_cast<unsigned char>(input->readULong(1)); |
1650 | 3.58M | if (c==0x9) |
1651 | 33.6k | listener->insertTab(); |
1652 | 3.54M | else if (c==0xa || c==0xd) |
1653 | 59.6k | listener->insertEOL(); |
1654 | 3.48M | else |
1655 | 3.48M | listener->insertCharacter(c); |
1656 | 3.58M | } |
1657 | 122k | } |
1658 | 1.59M | listener->closeSheetCell(); |
1659 | 1.59M | if (input->tell()!=endPos) |
1660 | 1.50M | ascii().addDelimiter(input->tell(),'|'); |
1661 | 1.59M | ascii().addPos(pos); |
1662 | 1.59M | ascii().addNote(f.str().c_str()); |
1663 | 1.59M | return true; |
1664 | 2.45M | } |
1665 | | |
1666 | | bool MultiplanParser::sendSpreadsheet() |
1667 | 1.82k | { |
1668 | 1.82k | MWAWSpreadsheetListenerPtr listener=getSpreadsheetListener(); |
1669 | 1.82k | if (!listener) { |
1670 | 0 | MWAW_DEBUG_MSG(("MultiplanParser::sendSpreadsheet: I can not find the listener\n")); |
1671 | 0 | return false; |
1672 | 0 | } |
1673 | 1.82k | listener->openSheet(m_state->getColumnsWidth(), librevenge::RVNG_POINT, std::vector<int>(), "Sheet0"); |
1674 | 1.82k | auto const &dataEntry=m_state->m_entries[6]; |
1675 | 1.82k | m_state->m_cellPositionsSet.insert(int(dataEntry.length())); |
1676 | 327k | for (size_t r=0; r<m_state->m_cellPositions.size(); ++r) { |
1677 | 326k | auto const &row = m_state->m_cellPositions[r]; |
1678 | 326k | listener->openSheetRow(-16.f, librevenge::RVNG_POINT); |
1679 | 7.67M | for (size_t col=0; col<row.size(); ++col) { |
1680 | 7.34M | auto p=row[col]; |
1681 | 7.34M | if (p<0 || p>dataEntry.length()) { |
1682 | 1.62M | MWAW_DEBUG_MSG(("MultiplanParser::sendSpreadsheet: find some bad data\n")); |
1683 | 1.62M | continue; |
1684 | 1.62M | } |
1685 | 5.72M | if (!p) continue; |
1686 | 4.30M | MWAWVec2i cellPos(static_cast<int>(col), static_cast<int>(r)); |
1687 | 4.30M | sendCell(cellPos, p); |
1688 | 4.30M | } |
1689 | 326k | listener->closeSheetRow(); |
1690 | 326k | } |
1691 | 1.82k | listener->closeSheet(); |
1692 | 1.82k | return true; |
1693 | 1.82k | } |
1694 | | // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: |