/src/libmwaw/src/lib/GreatWksDBParser.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 <algorithm> |
35 | | #include <iomanip> |
36 | | #include <iostream> |
37 | | #include <cmath> |
38 | | #include <limits> |
39 | | #include <set> |
40 | | #include <sstream> |
41 | | |
42 | | #include <librevenge/librevenge.h> |
43 | | |
44 | | #include "MWAWFont.hxx" |
45 | | #include "MWAWFontConverter.hxx" |
46 | | #include "MWAWGraphicStyle.hxx" |
47 | | #include "MWAWHeader.hxx" |
48 | | #include "MWAWParagraph.hxx" |
49 | | #include "MWAWPosition.hxx" |
50 | | #include "MWAWPrinter.hxx" |
51 | | #include "MWAWRSRCParser.hxx" |
52 | | #include "MWAWSection.hxx" |
53 | | #include "MWAWSpreadsheetListener.hxx" |
54 | | #include "MWAWSubDocument.hxx" |
55 | | |
56 | | #include "GreatWksDocument.hxx" |
57 | | #include "GreatWksGraph.hxx" |
58 | | #include "GreatWksText.hxx" |
59 | | |
60 | | #include "GreatWksDBParser.hxx" |
61 | | |
62 | | /** Internal: the structures of a GreatWksDBParser */ |
63 | | namespace GreatWksDBParserInternal |
64 | | { |
65 | | /**a big zone header of a GreatWksDBParser */ |
66 | | struct BlockHeader { |
67 | | /** constructor */ |
68 | | BlockHeader() |
69 | 64.9M | : m_name("") |
70 | 64.9M | { |
71 | 194M | for (auto &ptr : m_ptr) ptr=0; |
72 | 64.9M | } |
73 | | /** returns true if the block is empty */ |
74 | | bool isEmpty() const |
75 | 35.9M | { |
76 | 103M | for (auto ptr : m_ptr) if (ptr) return false; |
77 | 33.4M | return true; |
78 | 35.9M | } |
79 | | /** returns true if the entry has many block */ |
80 | | bool isComplex() const |
81 | 12.6k | { |
82 | 12.6k | return m_ptr[0]!=m_ptr[1] || m_ptr[0]!=m_ptr[2]; |
83 | 12.6k | } |
84 | | //! operator<< |
85 | | friend std::ostream &operator<<(std::ostream &o, BlockHeader const &entry) |
86 | 0 | { |
87 | 0 | o << std::hex << entry.m_ptr[0]; |
88 | 0 | if (entry.isComplex()) o << "[" << entry.m_ptr[1] << "," << entry.m_ptr[2] << "]"; |
89 | 0 | o << std::dec; |
90 | 0 | if (!entry.m_name.empty()) o << ":" << entry.m_name; |
91 | 0 | return o; |
92 | 0 | } |
93 | | |
94 | | /** the list of pointer: first, next, end? */ |
95 | | long m_ptr[3]; |
96 | | /** the block name */ |
97 | | std::string m_name; |
98 | | }; |
99 | | |
100 | | /** a big block of a GreatWksDBParser */ |
101 | | struct Block { |
102 | | //! a small block of a GreatWksDBParserInternal::Block |
103 | | struct Zone { |
104 | | /** constructor */ |
105 | | Zone() |
106 | 52.7k | : m_ptr(0) |
107 | 52.7k | , m_N(0) |
108 | 52.7k | , m_dataSize(0) |
109 | 52.7k | { |
110 | 52.7k | } |
111 | | /** the begin zone pointer */ |
112 | | long m_ptr; |
113 | | /** the number of sub data */ |
114 | | int m_N; |
115 | | /** the data size */ |
116 | | long m_dataSize; |
117 | | }; |
118 | | //! constructor |
119 | | explicit Block(BlockHeader const &header) |
120 | 51.9k | : m_header(header) |
121 | 51.9k | , m_zoneList() |
122 | 51.9k | { |
123 | 51.9k | } |
124 | | //! constructor given a zone |
125 | | Block(BlockHeader const &header, Zone &zone) |
126 | | : m_header(header) |
127 | | , m_zoneList() |
128 | 0 | { |
129 | 0 | m_zoneList.push_back(zone); |
130 | 0 | } |
131 | | //! destructor |
132 | | ~Block() |
133 | 51.9k | { |
134 | 51.9k | } |
135 | | //! returns true if the zone list is empty |
136 | | bool isEmpty() const |
137 | 0 | { |
138 | 0 | return m_zoneList.empty(); |
139 | 0 | } |
140 | | //! returns the number of zone |
141 | | size_t getNumZones() const |
142 | 102k | { |
143 | 102k | return m_zoneList.size(); |
144 | 102k | } |
145 | | //! returns the ith zone |
146 | | Zone const &getZone(size_t i) const |
147 | 50.3k | { |
148 | 50.3k | if (i>=m_zoneList.size()) { |
149 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParserInternal::Block: can not find zone %d\n", int(i))); |
150 | 0 | static Zone empty; |
151 | 0 | return empty; |
152 | 0 | } |
153 | 50.3k | return m_zoneList[i]; |
154 | 50.3k | } |
155 | | //! the corresponding entry header |
156 | | BlockHeader m_header; |
157 | | //! the zone list |
158 | | std::vector<Zone> m_zoneList; |
159 | | private: |
160 | | Block(Block const &orig) = delete; |
161 | | Block &operator=(Block const &orig) = delete; |
162 | | }; |
163 | | |
164 | | /** a cell of a GreatWksDBParser */ |
165 | | class Cell final : public MWAWCell |
166 | | { |
167 | | public: |
168 | | /// constructor |
169 | | Cell() |
170 | 400M | : m_content() |
171 | 400M | , m_pictEntry() { } |
172 | 0 | Cell(Cell const &)=default; |
173 | 198M | Cell &operator=(Cell const &)=default; |
174 | | //! destructor |
175 | | ~Cell() final; |
176 | | //! returns true if the cell do contain any content |
177 | | bool isEmpty() const |
178 | 197M | { |
179 | 197M | return m_content.empty() && !hasBorders() && !m_pictEntry.valid(); |
180 | 197M | } |
181 | | |
182 | | //! the cell content |
183 | | MWAWCellContent m_content; |
184 | | //! picture |
185 | | MWAWEntry m_pictEntry; |
186 | | }; |
187 | | |
188 | | Cell::~Cell() |
189 | 400M | { |
190 | 400M | } |
191 | | |
192 | | /** a field of a GreatWksDBParser */ |
193 | | struct Field { |
194 | | //! the file type |
195 | | enum Type { F_Unknown, F_Text, F_Number, F_Date, F_Time, F_Memo, F_Picture, F_Formula, F_Summary }; |
196 | | //! constructor |
197 | | Field() |
198 | 64.9M | : m_type(F_Unknown) |
199 | 64.9M | , m_id(-1) |
200 | 64.9M | , m_name("") |
201 | 64.9M | , m_format() |
202 | 64.9M | , m_linkZone(0) |
203 | 64.9M | , m_recordBlock() |
204 | 64.9M | , m_formula() |
205 | 64.9M | , m_summaryType(0) |
206 | 64.9M | , m_summaryField(0) |
207 | 64.9M | , m_isSequence(false) |
208 | 64.9M | , m_firstNumber(1) |
209 | 64.9M | , m_incrementNumber(1) |
210 | 64.9M | , m_extra("") |
211 | 64.9M | { |
212 | 64.9M | } |
213 | | //! update the cell to correspond to the final data |
214 | | bool updateCell(int row, int numRow, Cell &cell) const; |
215 | | //! operator<< |
216 | | friend std::ostream &operator<<(std::ostream &o, Field const &field); |
217 | | //! the field type |
218 | | Type m_type; |
219 | | //! the field id |
220 | | int m_id; |
221 | | //! the field name |
222 | | std::string m_name; |
223 | | //! the field format |
224 | | MWAWCell::Format m_format; |
225 | | //! the file position which stores the position link to record zone |
226 | | long m_linkZone; |
227 | | //! the block file position which stores the position of the field's record |
228 | | BlockHeader m_recordBlock; |
229 | | |
230 | | // formula |
231 | | |
232 | | //! the formula |
233 | | std::vector<MWAWCellContent::FormulaInstruction> m_formula; |
234 | | |
235 | | //! the summary type: 1:average, 2:count, 3:total, 4:minimum, 5:maximum |
236 | | int m_summaryType; |
237 | | //! the summary field |
238 | | int m_summaryField; |
239 | | |
240 | | // sequence (or unique val) |
241 | | |
242 | | //! true if the number is a sequence |
243 | | bool m_isSequence; |
244 | | //! the first number (in case of progression sequence) |
245 | | int m_firstNumber; |
246 | | //! the increment number (in case of progression sequence) |
247 | | int m_incrementNumber; |
248 | | |
249 | | //! extra data |
250 | | std::string m_extra; |
251 | | }; |
252 | | |
253 | | bool Field::updateCell(int row, int numRow, Cell &cell) const |
254 | 197M | { |
255 | 197M | std::vector<MWAWCellContent::FormulaInstruction> &formula=cell.m_content.m_formula; |
256 | 197M | if (m_type==F_Formula) { |
257 | 183k | if (m_formula.size()==0) |
258 | 178k | return false; |
259 | 5.18k | cell.m_content.m_contentType=MWAWCellContent::C_FORMULA; |
260 | 5.18k | formula=m_formula; |
261 | 5.18k | } |
262 | 197M | else if (m_isSequence && !cell.m_content.isValueSet()) { |
263 | 445k | cell.m_content.m_contentType=MWAWCellContent::C_NUMBER; |
264 | 445k | cell.m_content.setValue(double(m_firstNumber+row *m_incrementNumber)); |
265 | 445k | } |
266 | 196M | else if (m_summaryType>0 && m_summaryType<6) { |
267 | 8.38k | cell.m_content.m_contentType=MWAWCellContent::C_FORMULA; |
268 | 8.38k | MWAWCellContent::FormulaInstruction instr; |
269 | 8.38k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Function; |
270 | 8.38k | char const *wh[]= {"Average", "Count", "Sum", "Min", "Max"}; |
271 | 8.38k | instr.m_content=wh[m_summaryType-1]; |
272 | 8.38k | formula.push_back(instr); |
273 | 8.38k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator; |
274 | 8.38k | instr.m_content="("; |
275 | 8.38k | formula.push_back(instr); |
276 | 8.38k | instr.m_type=MWAWCellContent::FormulaInstruction::F_CellList; |
277 | 8.38k | instr.m_position[0][0]=instr.m_position[1][0]=m_summaryField; |
278 | 8.38k | instr.m_position[0][1]=0; |
279 | 8.38k | instr.m_position[1][1]=numRow-1; |
280 | 8.38k | instr.m_positionRelative[0]=instr.m_positionRelative[1]=MWAWVec2b(false,false); |
281 | 8.38k | formula.push_back(instr); |
282 | 8.38k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator; |
283 | 8.38k | instr.m_content=")"; |
284 | 8.38k | formula.push_back(instr); |
285 | 8.38k | return true; |
286 | 8.38k | } |
287 | | // change the reference date from 1/1/1904 to 1/1/1900 |
288 | 197M | if (m_type==F_Date && cell.m_content.isValueSet()) |
289 | 2.93k | cell.m_content.setValue(cell.m_content.m_value+1460.); |
290 | | // and try to update the 1D formula in 2D |
291 | 197M | for (auto &instr : formula) { |
292 | 66.9k | if (instr.m_type==MWAWCellContent::FormulaInstruction::F_Cell) |
293 | 0 | instr.m_position[0][1]=row; |
294 | 66.9k | else if (instr.m_type==MWAWCellContent::FormulaInstruction::F_CellList) |
295 | 0 | instr.m_position[0][1]=instr.m_position[1][1]=row; |
296 | 66.9k | } |
297 | 197M | return true; |
298 | 197M | } |
299 | | |
300 | | std::ostream &operator<<(std::ostream &o, Field const &field) |
301 | 0 | { |
302 | 0 | switch (field.m_type) { |
303 | 0 | case Field::F_Text: |
304 | 0 | o << "text,"; |
305 | 0 | break; |
306 | 0 | case Field::F_Number: |
307 | 0 | o << "number,"; |
308 | 0 | break; |
309 | 0 | case Field::F_Date: |
310 | 0 | o << "date,"; |
311 | 0 | break; |
312 | 0 | case Field::F_Time: |
313 | 0 | o << "time,"; |
314 | 0 | break; |
315 | 0 | case Field::F_Picture: |
316 | 0 | o << "picture,"; |
317 | 0 | break; |
318 | 0 | case Field::F_Memo: |
319 | 0 | o << "memo,"; |
320 | 0 | break; |
321 | 0 | case Field::F_Formula: |
322 | 0 | o << "formula,"; |
323 | 0 | break; |
324 | 0 | case Field::F_Summary: |
325 | 0 | o << "summary,"; |
326 | 0 | break; |
327 | 0 | case Field::F_Unknown: |
328 | 0 | break; |
329 | | #if !defined(__clang__) |
330 | | default: |
331 | | MWAW_DEBUG_MSG(("GreatWksDBParser::Field:operator<<: find unexpected type\n")); |
332 | | break; |
333 | | #endif |
334 | 0 | } |
335 | 0 | if (field.m_id>=0) o << "id=" << field.m_id << ","; |
336 | 0 | if (!field.m_name.empty()) o << "name=" << field.m_name << ","; |
337 | 0 | if (field.m_linkZone>0) o << "zone[link]=" << std::hex << field.m_linkZone << std::dec << ","; |
338 | 0 | if (!field.m_recordBlock.isEmpty()) o << "zone[record]=" << field.m_recordBlock << ","; |
339 | 0 | o << field.m_extra; |
340 | 0 | return o; |
341 | 0 | } |
342 | | |
343 | | /** the database of a GreatWksDBParser */ |
344 | | class Database |
345 | | { |
346 | | public: |
347 | | //! constructor |
348 | | Database() |
349 | 29.8k | : m_numRecords(0) |
350 | 29.8k | , m_rowList() |
351 | 29.8k | , m_fieldList() |
352 | 29.8k | , m_widthDefault(75) |
353 | 29.8k | , m_widthCols() |
354 | 29.8k | , m_heightDefault(13) |
355 | 29.8k | , m_heightRows() |
356 | 29.8k | , m_rowCellsMap() |
357 | 29.8k | , m_name("Sheet0") |
358 | 29.8k | { |
359 | 29.8k | } |
360 | | //! add a cell data in one given position |
361 | | bool addCell(MWAWVec2i const &pos, Cell const &cell) |
362 | 5.13M | { |
363 | 5.13M | if (pos[0]<0 || pos[0]>=int(m_fieldList.size()) || pos[1]<0) { |
364 | 4.51M | MWAW_DEBUG_MSG(("GreatWksDBParserInternal::Database::addCell: the cell position seems bad\n")); |
365 | 4.51M | return false; |
366 | 4.51M | } |
367 | 620k | if (m_rowCellsMap.find(pos[1])==m_rowCellsMap.end()) |
368 | 127k | m_rowCellsMap[pos[1]]=std::vector<Cell>(); |
369 | 620k | auto &cells=m_rowCellsMap.find(pos[1])->second; |
370 | 620k | if (pos[0]>=static_cast<int>(cells.size())) |
371 | 127k | cells.resize(m_fieldList.size()); |
372 | 620k | cells[size_t(pos[0])]=cell; |
373 | 620k | return true; |
374 | 5.13M | } |
375 | | //! returns the row size in point |
376 | | int getRowHeight(int row) const |
377 | 0 | { |
378 | 0 | if (row>=0&&row<static_cast<int>(m_heightRows.size())) |
379 | 0 | return m_heightRows[size_t(row)]; |
380 | 0 | return m_heightDefault; |
381 | 0 | } |
382 | | //! convert the m_widthCols in a vector of of point size |
383 | | std::vector<float> convertInPoint(std::vector<int> const &list) const |
384 | 0 | { |
385 | 0 | size_t numCols=m_fieldList.size(); |
386 | 0 | std::vector<float> res; |
387 | 0 | res.resize(numCols); |
388 | 0 | for (size_t i = 0; i < numCols; i++) { |
389 | 0 | if (i>=list.size() || list[i] < 0) res[i] = float(m_widthDefault); |
390 | 0 | else res[i] = float(list[i]); |
391 | 0 | } |
392 | 0 | return res; |
393 | 0 | } |
394 | | //! the number of records |
395 | | int m_numRecords; |
396 | | //! the list of rows data |
397 | | std::vector<MWAWEntry> m_rowList; |
398 | | //! the list of field |
399 | | std::vector<Field> m_fieldList; |
400 | | /** the default column width */ |
401 | | int m_widthDefault; |
402 | | /** the column size in points */ |
403 | | std::vector<int> m_widthCols; |
404 | | /** the default row height */ |
405 | | int m_heightDefault; |
406 | | /** the row height in points */ |
407 | | std::vector<int> m_heightRows; |
408 | | /** the map row -> list of cells */ |
409 | | std::map<int, std::vector<Cell> > m_rowCellsMap; |
410 | | /** the database name */ |
411 | | std::string m_name; |
412 | | protected: |
413 | | }; |
414 | | |
415 | | //////////////////////////////////////// |
416 | | //! Internal: the state of a GreatWksDBParser |
417 | | struct State { |
418 | | //! constructor |
419 | | State() |
420 | 29.8k | : m_database() |
421 | 29.8k | , m_idZonesMap() |
422 | 29.8k | , m_blocks() |
423 | 29.8k | , m_actPage(0) |
424 | 29.8k | , m_numPages(0) |
425 | 29.8k | , m_headerBlockHeader() |
426 | 29.8k | , m_footerBlockHeader() |
427 | 29.8k | , m_headerPrint(false) |
428 | 29.8k | , m_footerPrint(false) |
429 | 29.8k | , m_headerHeight(0) |
430 | 29.8k | , m_footerHeight(0) |
431 | 29.8k | { |
432 | 29.8k | } |
433 | | /** the database */ |
434 | | Database m_database; |
435 | | /** a map id to zone used to stored the small zones */ |
436 | | std::map<int, MWAWEntry> m_idZonesMap; |
437 | | /** the list of big blocks */ |
438 | | std::vector<BlockHeader> m_blocks; |
439 | | int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */; |
440 | | |
441 | | MWAWEntry m_headerBlockHeader /** the header entry (in v2)*/, m_footerBlockHeader/**the footer entry (in v2)*/; |
442 | | bool m_headerPrint /** the header is printed */, m_footerPrint /* the footer is printed */; |
443 | | int m_headerHeight /** the header height if known */, |
444 | | m_footerHeight /** the footer height if known */; |
445 | | }; |
446 | | |
447 | | //////////////////////////////////////// |
448 | | //! Internal: the subdocument of a GreatWksDBParser |
449 | | class SubDocument final : public MWAWSubDocument |
450 | | { |
451 | | public: |
452 | | SubDocument(GreatWksDBParser &pars, MWAWInputStreamPtr const &input, int zoneId) |
453 | 0 | : MWAWSubDocument(&pars, input, MWAWEntry()) |
454 | 0 | , m_id(zoneId) {} |
455 | | |
456 | | //! destructor |
457 | 0 | ~SubDocument() final {} |
458 | | |
459 | | //! operator!= |
460 | | bool operator!=(MWAWSubDocument const &doc) const final; |
461 | | |
462 | | //! the parser function |
463 | | void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final; |
464 | | |
465 | | protected: |
466 | | //! the subdocument id |
467 | | int m_id; |
468 | | }; |
469 | | |
470 | | void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) |
471 | 0 | { |
472 | 0 | if (!listener.get()) { |
473 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParserInternal::SubDocument::parse: no listener\n")); |
474 | 0 | return; |
475 | 0 | } |
476 | 0 | if (type!=libmwaw::DOC_HEADER_FOOTER) { |
477 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParserInternal::SubDocument::parse: unknown type\n")); |
478 | 0 | return; |
479 | 0 | } |
480 | | |
481 | 0 | auto *parser=dynamic_cast<GreatWksDBParser *>(m_parser); |
482 | 0 | if (!parser) { |
483 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParserInternal::SubDocument::parse: no parser\n")); |
484 | 0 | return; |
485 | 0 | } |
486 | 0 | long pos = m_input->tell(); |
487 | 0 | parser->sendHF(m_id); |
488 | 0 | m_input->seek(pos, librevenge::RVNG_SEEK_SET); |
489 | 0 | } |
490 | | |
491 | | bool SubDocument::operator!=(MWAWSubDocument const &doc) const |
492 | 0 | { |
493 | 0 | if (MWAWSubDocument::operator!=(doc)) return true; |
494 | 0 | auto const *sDoc = dynamic_cast<SubDocument const *>(&doc); |
495 | 0 | if (!sDoc) return true; |
496 | 0 | if (m_id != sDoc->m_id) return true; |
497 | 0 | return false; |
498 | 0 | } |
499 | | } |
500 | | |
501 | | //////////////////////////////////////////////////////////// |
502 | | // constructor/destructor, ... |
503 | | //////////////////////////////////////////////////////////// |
504 | | GreatWksDBParser::GreatWksDBParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header) |
505 | 12.8k | : MWAWSpreadsheetParser(input, rsrcParser, header) |
506 | 12.8k | , m_state() |
507 | 12.8k | , m_document() |
508 | 12.8k | { |
509 | 12.8k | init(); |
510 | 12.8k | } |
511 | | |
512 | | GreatWksDBParser::~GreatWksDBParser() |
513 | 12.8k | { |
514 | 12.8k | } |
515 | | |
516 | | void GreatWksDBParser::init() |
517 | 12.8k | { |
518 | 12.8k | resetSpreadsheetListener(); |
519 | 12.8k | setAsciiName("main-1"); |
520 | | |
521 | 12.8k | m_state.reset(new GreatWksDBParserInternal::State); |
522 | | |
523 | | // reduce the margin (in case, the page is not defined) |
524 | 12.8k | getPageSpan().setMargins(0.1); |
525 | | |
526 | 12.8k | m_document.reset(new GreatWksDocument(*this)); |
527 | 12.8k | } |
528 | | |
529 | | //////////////////////////////////////////////////////////// |
530 | | // interface with the text parser |
531 | | //////////////////////////////////////////////////////////// |
532 | | bool GreatWksDBParser::sendHF(int id) |
533 | 0 | { |
534 | 0 | return m_document->getTextParser()->sendTextbox(id==0 ? m_state->m_headerBlockHeader : m_state->m_footerBlockHeader); |
535 | 0 | } |
536 | | |
537 | | //////////////////////////////////////////////////////////// |
538 | | // the parser |
539 | | //////////////////////////////////////////////////////////// |
540 | | void GreatWksDBParser::parse(librevenge::RVNGSpreadsheetInterface *docInterface) |
541 | 4.18k | { |
542 | 4.18k | if (!getInput().get() || !checkHeader(nullptr)) throw(libmwaw::ParseException()); |
543 | 4.18k | bool ok = false; |
544 | 4.18k | try { |
545 | | // create the asciiFile |
546 | 4.18k | ascii().setStream(getInput()); |
547 | 4.18k | ascii().open(asciiName()); |
548 | | |
549 | 4.18k | checkHeader(nullptr); |
550 | 4.18k | ok = createZones(); |
551 | 4.18k | if (ok) { |
552 | 1.77k | createDocument(docInterface); |
553 | 1.77k | sendDatabase(); |
554 | 1.77k | } |
555 | 4.18k | ascii().reset(); |
556 | 4.18k | } |
557 | 4.18k | catch (...) { |
558 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParser::parse: exception catched when parsing\n")); |
559 | 0 | ok = false; |
560 | 0 | } |
561 | | |
562 | 4.18k | resetSpreadsheetListener(); |
563 | 4.18k | if (!ok) throw(libmwaw::ParseException()); |
564 | 4.18k | } |
565 | | |
566 | | //////////////////////////////////////////////////////////// |
567 | | // create the document |
568 | | //////////////////////////////////////////////////////////// |
569 | | void GreatWksDBParser::createDocument(librevenge::RVNGSpreadsheetInterface *documentInterface) |
570 | 1.77k | { |
571 | 1.77k | if (!documentInterface) return; |
572 | 1.77k | if (getSpreadsheetListener()) { |
573 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParser::createDocument: listener already exist\n")); |
574 | 0 | return; |
575 | 0 | } |
576 | | |
577 | | // update the page |
578 | 1.77k | m_state->m_actPage = 0; |
579 | | |
580 | | // create the page list |
581 | 1.77k | int numPages = 1; |
582 | 1.77k | if (m_document->getGraphParser()->numPages() > numPages) |
583 | 0 | numPages = m_document->getGraphParser()->numPages(); |
584 | 1.77k | if (m_document->getTextParser()->numPages() > numPages) |
585 | 0 | numPages = m_document->getTextParser()->numPages(); |
586 | 1.77k | m_state->m_numPages = numPages; |
587 | | |
588 | 1.77k | MWAWPageSpan ps(getPageSpan()); |
589 | 1.77k | std::vector<MWAWPageSpan> pageList; |
590 | 1.77k | if (m_state->m_headerBlockHeader.valid()) { |
591 | 0 | MWAWHeaderFooter header(MWAWHeaderFooter::HEADER, MWAWHeaderFooter::ALL); |
592 | 0 | header.m_subDocument.reset(new GreatWksDBParserInternal::SubDocument(*this, getInput(), 0)); |
593 | 0 | ps.setHeaderFooter(header); |
594 | 0 | } |
595 | 1.77k | if (m_state->m_footerBlockHeader.valid()) { |
596 | 0 | MWAWHeaderFooter footer(MWAWHeaderFooter::FOOTER, MWAWHeaderFooter::ALL); |
597 | 0 | footer.m_subDocument.reset(new GreatWksDBParserInternal::SubDocument(*this, getInput(), 1)); |
598 | 0 | ps.setHeaderFooter(footer); |
599 | 0 | } |
600 | 1.77k | ps.setPageSpan(numPages); |
601 | 1.77k | pageList.push_back(ps); |
602 | 1.77k | MWAWSpreadsheetListenerPtr listen(new MWAWSpreadsheetListener(*getParserState(), pageList, documentInterface)); |
603 | 1.77k | setSpreadsheetListener(listen); |
604 | 1.77k | listen->startDocument(); |
605 | 1.77k | } |
606 | | |
607 | | //////////////////////////////////////////////////////////// |
608 | | // |
609 | | // Intermediate level |
610 | | // |
611 | | //////////////////////////////////////////////////////////// |
612 | | bool GreatWksDBParser::createZones() |
613 | 4.18k | { |
614 | 4.18k | m_document->readRSRCZones(); |
615 | | |
616 | 4.18k | if (!readHeader()) return false; |
617 | | |
618 | 4.17k | MWAWInputStreamPtr input = getInput(); |
619 | 4.17k | libmwaw::DebugStream f; |
620 | | |
621 | 20.8k | for (size_t i=0; i< m_state->m_blocks.size() ; ++i) { |
622 | 16.6k | auto &header = m_state->m_blocks[i]; |
623 | 16.6k | if (header.isEmpty()) continue; |
624 | 16.6k | auto block=createBlock(header); |
625 | 16.6k | if (block) { |
626 | 3.93k | bool ok=false; |
627 | 3.93k | switch (i) { |
628 | 664 | case 0: // unknown maybe some flag |
629 | 664 | ok = readFreeList(*block); |
630 | 664 | break; |
631 | 2.18k | case 1: // a list of record<->id |
632 | 2.18k | ok = readRowLinks(*block); |
633 | 2.18k | break; |
634 | 401 | case 2: // a list of 0<->id |
635 | 401 | ok = readBlockHeader2(*block); |
636 | 401 | break; |
637 | 685 | case 3: // the record list |
638 | 685 | ok = readRecordList(*block); |
639 | 685 | break; |
640 | 0 | default: |
641 | 0 | break; |
642 | 3.93k | } |
643 | 3.93k | if (ok) |
644 | 3.93k | continue; |
645 | 3.93k | } |
646 | 12.6k | f.str(""); |
647 | 12.6k | f << "Entries(" << header.m_name << ")[" << header << "]:"; |
648 | 12.6k | ascii().addPos(input->tell()); |
649 | 12.6k | ascii().addNote(f.str().c_str()); |
650 | 12.6k | if (!header.isComplex()) continue; |
651 | 34.7k | for (int j=1; j<3; ++j) { |
652 | 23.1k | if (!header.m_ptr[j]) continue; |
653 | 21.4k | ascii().addPos(header.m_ptr[j]); |
654 | 21.4k | ascii().addNote(f.str().c_str()); |
655 | 21.4k | } |
656 | 11.5k | } |
657 | | |
658 | 38.5k | for (auto const &it : m_state->m_idZonesMap) { |
659 | 38.5k | MWAWEntry const &entry=it.second; |
660 | 38.5k | if (!entry.valid() || entry.isParsed()) continue; |
661 | 38.5k | bool ok=false; |
662 | 38.5k | switch (entry.id()) { |
663 | | // zones which will be parsed in readDatabase |
664 | 3.81k | case 1: // the list of fields |
665 | 4.03k | case 10: // the link between field and a zone record |
666 | 4.41k | case 13: // a version 2 zone which seems to contains added data for each fields |
667 | 4.41k | ok=true; |
668 | 4.41k | break; |
669 | 841 | case 2: |
670 | 841 | ok=readFormLinks(it.second); |
671 | 841 | break; |
672 | 616 | case 5: // one time with list=[19, 20], list of computed fields ? |
673 | 1.14k | case 6: // one time with list=[21, 22], list of summary fields ? |
674 | 1.21k | case 14: { // only in v2, probably linked to Zone2 zones, find [1,1,1] or [2,2] |
675 | 1.21k | std::vector<int> list; |
676 | 1.21k | ok=readIntList(it.second, list); |
677 | 1.21k | if (!ok) break; |
678 | 926 | f.str(""); |
679 | 926 | f << "Entries(" << entry.name() << "):"; |
680 | 926 | if (list.size()) { |
681 | 33 | f << "lists=["; |
682 | 66 | for (int i : list) f << i << ","; |
683 | 33 | f << "],"; |
684 | 33 | } |
685 | 926 | ascii().addPos(entry.begin()); |
686 | 926 | ascii().addNote(f.str().c_str()); |
687 | 926 | break; |
688 | 1.21k | } |
689 | 561 | case 7: // always find with size=0 |
690 | 561 | if (entry.length()!=6) break; |
691 | 484 | ok=true; |
692 | 484 | f.str(""); |
693 | 484 | f << "Entries(" << entry.name() << "):"; |
694 | 484 | ascii().addPos(entry.begin()); |
695 | 484 | ascii().addNote(f.str().c_str()); |
696 | 484 | ascii().addPos(entry.end()); |
697 | 484 | ascii().addNote("_"); |
698 | 484 | break; |
699 | 499 | case 12: |
700 | 499 | ok=readZone12(entry); |
701 | 499 | break; |
702 | | // zone3: never find with data, dSz=3C in v1 and dSz=3e in v2 |
703 | | // zone4: never find with data, dSz=112, seems one time in v2 |
704 | | // zone8: never seems |
705 | 31.0k | default: |
706 | 31.0k | ok=readSmallZone(entry); |
707 | 31.0k | break; |
708 | 38.5k | } |
709 | 38.5k | if (ok || entry.isParsed()) continue; |
710 | 3.15k | f.str(""); |
711 | 3.15k | f << "Entries(" << entry.name() << "):#"; |
712 | 3.15k | ascii().addPos(entry.begin()); |
713 | 3.15k | ascii().addNote(f.str().c_str()); |
714 | 3.15k | ascii().addPos(entry.end()); |
715 | 3.15k | ascii().addNote("_"); |
716 | 3.15k | } |
717 | | |
718 | 4.17k | if (!readDatabase()) |
719 | 2.39k | return false; |
720 | | |
721 | 1.77k | return true; |
722 | 4.17k | } |
723 | | |
724 | | //////////////////////////////////////////////////////////// |
725 | | // read a database |
726 | | //////////////////////////////////////////////////////////// |
727 | | bool GreatWksDBParser::readDatabase() |
728 | 4.17k | { |
729 | 4.17k | MWAWInputStreamPtr input = getInput(); |
730 | 4.17k | auto it=m_state->m_idZonesMap.find(1); |
731 | 4.17k | if (it==m_state->m_idZonesMap.end() || !readFields(it->second)) { |
732 | 436 | MWAW_DEBUG_MSG(("GreatWksDBParser::readDatabase: can not find the list of fields\n")); |
733 | 436 | return false; |
734 | 436 | } |
735 | 3.73k | if (version()==2) { |
736 | | // zone13: a v2 zone with dSz=46, content seems constant in a file |
737 | 3.27k | it=m_state->m_idZonesMap.find(13); |
738 | 3.27k | if (it!=m_state->m_idZonesMap.end()) { |
739 | 381 | if (!readFieldAuxis(it->second)) |
740 | 37 | readSmallZone(it->second); |
741 | 381 | } |
742 | 3.27k | } |
743 | 3.73k | auto &database=m_state->m_database; |
744 | | /* First read the fields' records then the rows' records. As the |
745 | | rows' records contain more information than the fields' records, |
746 | | there will replace the field data if we success to read them |
747 | | (and if not, we will keep the fields' records). |
748 | | */ |
749 | 22.7M | for (auto &field : database.m_fieldList) { |
750 | 22.7M | if (!field.m_linkZone) continue; |
751 | 1.07M | readFieldLinks(field); |
752 | 1.07M | } |
753 | 3.73k | for (auto const &row : database.m_rowList) |
754 | 6.66M | readRowRecords(row); |
755 | 22.7M | for (auto &field : database.m_fieldList) { |
756 | 22.7M | if (field.m_recordBlock.isEmpty()) continue; |
757 | 973k | readFieldRecords(field); |
758 | 973k | } |
759 | 3.73k | if (!database.m_rowCellsMap.empty()) |
760 | 1.50k | return true; |
761 | | // let check if we can reconstruct something |
762 | 12.2M | for (auto &field : database.m_fieldList) { |
763 | 12.2M | if (field.m_recordBlock.isEmpty()) continue; |
764 | 553k | if (field.m_isSequence) return true; |
765 | 552k | if (field.m_type==GreatWksDBParserInternal::Field::F_Summary) return true; |
766 | 552k | } |
767 | 1.96k | MWAW_DEBUG_MSG(("GreatWksDBParser::readDatabase: can not find any cellule\n")); |
768 | 1.96k | return false; |
769 | 2.23k | } |
770 | | |
771 | | //////////////////////////////////////////////////////////// |
772 | | // read the file header |
773 | | //////////////////////////////////////////////////////////// |
774 | | bool GreatWksDBParser::readHeader() |
775 | 4.18k | { |
776 | 4.18k | MWAWInputStreamPtr input = getInput(); |
777 | 4.18k | if (!input->checkPosition(0x200)) |
778 | 16 | return false; |
779 | 4.17k | int const vers=version(); |
780 | 4.17k | libmwaw::DebugStream f; |
781 | | |
782 | 4.17k | f << "Entries(HeaderZone):"; |
783 | 4.17k | long pos=16; |
784 | 4.17k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
785 | 16.6k | for (int i=0; i<3; ++i) { |
786 | 12.5k | GreatWksDBParserInternal::BlockHeader block; |
787 | 12.5k | if (!readBlockHeader(block)) { |
788 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParser::readHeader: can not read a entry\n")); |
789 | 0 | f << "###"; |
790 | 0 | ascii().addPos(pos); |
791 | 0 | ascii().addNote(f.str().c_str()); |
792 | 0 | return false; |
793 | 0 | } |
794 | 12.5k | static char const* const wh[]= {"Free", "RecLink", "Block2"}; |
795 | 12.5k | block.m_name=wh[i]; |
796 | 12.5k | m_state->m_blocks.push_back(block); |
797 | 12.5k | f << block << ","; |
798 | 62.5k | for (int j=0; j<4; ++j) { |
799 | 50.0k | auto val=static_cast<int>(input->readULong(2)); |
800 | 50.0k | if (val) |
801 | 34.1k | f << "f" << j << "[" << i << "]=" << val << ","; |
802 | 50.0k | } |
803 | 12.5k | } |
804 | | // an odd entry block (only 2 ptr), does this means that this zone has a size less than 512? |
805 | 4.17k | GreatWksDBParserInternal::BlockHeader block; |
806 | 12.5k | for (int i=0; i<2; ++i) block.m_ptr[i]=long(input->readULong(4)); |
807 | 4.17k | block.m_ptr[2]=block.m_ptr[1]; |
808 | 4.17k | block.m_name="RecList"; |
809 | 4.17k | m_state->m_blocks.push_back(block); |
810 | 4.17k | f << block << ","; |
811 | | |
812 | 4.17k | auto val=static_cast<int>(input->readLong(2)); // always 0 |
813 | 4.17k | if (val) f << "f4=" << val << ","; |
814 | 4.17k | auto numRecords=static_cast<int>(input->readLong(2)); |
815 | 4.17k | m_state->m_database.m_numRecords=numRecords; |
816 | 4.17k | f << "num[records]=" << numRecords << ","; |
817 | 70.8k | for (int i=0; i<16; ++i) { // find h1=h9=h11=h13=numRecords, h3=h7=1|11, h14=2, h15=1 |
818 | 66.7k | val=static_cast<int>(input->readLong(2)); |
819 | 66.7k | if (i==1 || i==9 || i==11 || i==13) { |
820 | 16.6k | if (val==numRecords) continue; |
821 | 14.2k | if (i==9) f << "act[record]=" << val << ","; |
822 | 10.3k | else f << "h" << i << "=" << val << ","; |
823 | 14.2k | } |
824 | 50.0k | else if (val) f << "h" << i << "=" << val << ","; |
825 | 66.7k | } |
826 | 4.17k | ascii().addPos(pos); |
827 | 4.17k | ascii().addNote(f.str().c_str()); |
828 | | |
829 | 4.17k | pos=input->tell(); |
830 | 4.17k | f.str(""); |
831 | 4.17k | f << "Entries(Zones):"; |
832 | 4.17k | std::vector<MWAWEntry> zones; |
833 | 41.7k | for (int i=0; i<9; ++i) { |
834 | 37.5k | val=static_cast<int>(input->readLong(2)); |
835 | 37.5k | if (val) f << "f" << i << "=" << val << ","; |
836 | 37.5k | auto ptr=long(input->readULong(2)); |
837 | 37.5k | MWAWEntry entry; |
838 | 37.5k | entry.setBegin(ptr); |
839 | 37.5k | zones.push_back(entry); |
840 | 37.5k | if (!ptr) continue; |
841 | | // checkme: does the zone type can be deduced from i? |
842 | 31.3k | f << "Zone" << i << "A=" << std::hex << ptr << std::dec << ","; |
843 | 31.3k | } |
844 | 4.17k | if (vers==1) { |
845 | 21.6k | for (int i=0; i<40; ++i) { |
846 | 21.1k | val=static_cast<int>(input->readLong(2)); |
847 | 21.1k | if (val!=-1) f << "g" << i << "=" << val << ","; |
848 | 21.1k | } |
849 | 529 | } |
850 | 3.64k | else { |
851 | | // no sure what is the maximal number of potential added zones |
852 | 76.4k | for (int i=0; i<20; ++i) { |
853 | 72.8k | auto ptr=long(input->readULong(2)); |
854 | 72.8k | val=static_cast<int>(input->readLong(2)); |
855 | 72.8k | if (val) f << "g" << i << "=" << val << ","; |
856 | 72.8k | MWAWEntry entry; |
857 | 72.8k | entry.setBegin(ptr); |
858 | 72.8k | zones.push_back(entry); |
859 | 72.8k | if (!ptr || ptr==0xFFFF) continue; |
860 | 57.8k | f << "Zone" << 9+i << "A=" << std::hex << ptr << std::dec << ","; |
861 | 57.8k | } |
862 | 3.64k | } |
863 | 4.17k | ascii().addPos(pos); |
864 | 4.17k | ascii().addNote(f.str().c_str()); |
865 | | |
866 | | // from here, probably only junk in v1 and list of 0 + unkn number in v2 |
867 | 4.17k | ascii().addPos(input->tell()); |
868 | 4.17k | ascii().addNote("HeaderZone:end"); |
869 | | |
870 | 110k | for (auto &entry : zones) { |
871 | 110k | if (!entry.begin() || entry.begin()==0xFFFF) continue; |
872 | 83.8k | if (!checkSmallZone(entry)) { |
873 | 25.6k | MWAW_DEBUG_MSG(("GreatWksDBParser::readHeader: find a bad zone %d\n", static_cast<int>(entry.id()))); |
874 | 25.6k | entry.setLength(0); |
875 | 25.6k | continue; |
876 | 25.6k | } |
877 | 58.2k | int const id=entry.id(); |
878 | 58.2k | if (m_state->m_idZonesMap.find(id)==m_state->m_idZonesMap.end()) |
879 | 38.5k | m_state->m_idZonesMap[id]=entry; |
880 | 19.6k | else { |
881 | 19.6k | MWAW_DEBUG_MSG(("GreatWksDBParser::readHeader: the zone entry %d already exists\n", id)); |
882 | 19.6k | f.str(""); |
883 | 19.6k | f << "Entries(" << entry.name() << "):###"; |
884 | 19.6k | ascii().addPos(entry.begin()); |
885 | 19.6k | ascii().addNote(f.str().c_str()); |
886 | 19.6k | ascii().addPos(entry.end()); |
887 | 19.6k | ascii().addNote("_"); |
888 | 19.6k | } |
889 | 58.2k | } |
890 | | |
891 | 4.17k | return true; |
892 | 4.17k | } |
893 | | |
894 | | //////////////////////////////////////////////////////////// |
895 | | // |
896 | | // Low level |
897 | | // |
898 | | //////////////////////////////////////////////////////////// |
899 | | |
900 | | //////////////////////////////////////////////////////////// |
901 | | // block gestion |
902 | | //////////////////////////////////////////////////////////// |
903 | | bool GreatWksDBParser::readBlockHeader(GreatWksDBParserInternal::BlockHeader &entry) |
904 | 1.05M | { |
905 | 1.05M | MWAWInputStreamPtr input = getInput(); |
906 | 1.05M | if (!input->checkPosition(input->tell()+12)) return false; |
907 | 3.16M | for (auto &ptr : entry.m_ptr) ptr=long(input->readULong(4)); |
908 | 1.05M | return true; |
909 | 1.05M | } |
910 | | |
911 | | std::shared_ptr<GreatWksDBParserInternal::Block> GreatWksDBParser::createBlock(GreatWksDBParserInternal::BlockHeader &entry) |
912 | 990k | { |
913 | 990k | std::shared_ptr<GreatWksDBParserInternal::Block> res; |
914 | | |
915 | 990k | MWAWInputStreamPtr input = getInput(); |
916 | 990k | long pos=entry.m_ptr[0]; |
917 | 990k | if (!pos || !input->checkPosition(pos+10)) return res; |
918 | | |
919 | 222k | libmwaw::DebugStream f; |
920 | 222k | std::set<long> seens; |
921 | 275k | while (pos) { |
922 | 227k | if (!input->checkPosition(pos+256) || seens.find(pos)!=seens.end()) { |
923 | 2.14k | MWAW_DEBUG_MSG(("GreatWksDBParser::createBlock: find an old position\n")); |
924 | 2.14k | ascii().addPos(pos); |
925 | 2.14k | ascii().addNote("Entries(BlockHeader):###"); |
926 | 2.14k | return res; |
927 | 2.14k | } |
928 | 225k | seens.insert(pos); |
929 | 225k | f.str(""); |
930 | 225k | f << "Entries(BlockHeader):"; |
931 | 225k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
932 | 225k | auto type=static_cast<int>(input->readULong(1)); |
933 | 225k | f << "type=" << std::hex << type << std::dec << ","; |
934 | 225k | if ((type&0xF0) != 0x40) { |
935 | 172k | MWAW_DEBUG_MSG(("GreatWksDBParser::createBlock: find old block type\n")); |
936 | 172k | f << "####"; |
937 | 172k | ascii().addPos(pos); |
938 | 172k | ascii().addNote(f.str().c_str()); |
939 | 172k | return res; |
940 | 172k | } |
941 | 52.7k | auto val=static_cast<int>(input->readULong(1)); |
942 | 52.7k | if (val!=0x7F) // find fl=0 with type=44 |
943 | 51.2k | f << "fl=" << std::hex << val << std::dec << ","; |
944 | 52.7k | GreatWksDBParserInternal::Block::Zone zone; |
945 | 52.7k | zone.m_N = static_cast<int>(input->readULong(2)); |
946 | 52.7k | if (zone.m_N) |
947 | 49.6k | f << "N=" << zone.m_N << ","; |
948 | 52.7k | if ((type&0x4)==0) |
949 | 35.8k | zone.m_dataSize = static_cast<int>(input->readULong(2)); |
950 | 52.7k | if (zone.m_dataSize) |
951 | 24.5k | f << "dataSize=" << zone.m_dataSize << ","; |
952 | 52.7k | if (type==0x41 || type==0x44) { // link to type or to flag |
953 | 13.5k | val=static_cast<int>(input->readULong(4)); |
954 | 13.5k | if (val) f << "prev=" << std::hex << val << std::dec << ","; |
955 | 13.5k | } |
956 | 52.7k | long next=static_cast<int>(input->readULong(4)); |
957 | 52.7k | if (next) { |
958 | 32.4k | f << "next=" << std::hex << next << std::dec << ","; |
959 | 32.4k | if ((next&0x1FF)) { |
960 | 22.8k | MWAW_DEBUG_MSG(("GreatWksDBParser::createBlock: the next zone seems bad(ignored it)\n")); |
961 | 22.8k | f << "###"; |
962 | 22.8k | next=0; |
963 | 22.8k | } |
964 | 32.4k | } |
965 | | |
966 | 52.7k | zone.m_ptr=input->tell(); |
967 | 52.7k | if (!res) res.reset(new GreatWksDBParserInternal::Block(entry)); |
968 | 52.7k | switch (type) { |
969 | 2.38k | case 0x40: // a root with no data? |
970 | 2.38k | ascii().addPos(input->tell()); |
971 | 2.38k | ascii().addNote("_"); |
972 | 2.38k | break; |
973 | 2.60k | case 0x41: // ok |
974 | 13.5k | case 0x44: // no dataSize |
975 | 13.5k | res->m_zoneList.push_back(zone); |
976 | 13.5k | break; |
977 | 36.8k | default: |
978 | 36.8k | res->m_zoneList.push_back(zone); |
979 | 36.8k | MWAW_DEBUG_MSG(("GreatWksDBParser::createBlock: find some unexpected type %d, try to continue\n", type)); |
980 | 36.8k | f << "###"; |
981 | | // let's add this zone, but ignored potential follower |
982 | 36.8k | next=0; |
983 | 36.8k | break; |
984 | 52.7k | } |
985 | 52.7k | ascii().addPos(pos); |
986 | 52.7k | ascii().addNote(f.str().c_str()); |
987 | 52.7k | pos=next; |
988 | 52.7k | } |
989 | 47.7k | return res; |
990 | 222k | } |
991 | | |
992 | | //////////////////////////////////////////////////////////// |
993 | | // functions to read the known block structure |
994 | | //////////////////////////////////////////////////////////// |
995 | | bool GreatWksDBParser::readRecordList(GreatWksDBParserInternal::Block const &block) |
996 | 685 | { |
997 | 685 | MWAWInputStreamPtr input = getInput(); |
998 | 685 | auto const &header=block.m_header; |
999 | 685 | libmwaw::DebugStream f; |
1000 | 1.35k | for (size_t z=0; z<block.getNumZones(); ++z) { |
1001 | 671 | auto const &zone=block.getZone(z); |
1002 | 671 | long pos=zone.m_ptr; |
1003 | 671 | if (!pos || !input->checkPosition(pos+zone.m_N*4)) { |
1004 | 60 | MWAW_DEBUG_MSG(("GreatWksDBParser::readRecordList: the zone seems too short\n")); |
1005 | 60 | f.str(""); |
1006 | 60 | f << "Entries(" << header.m_name << ")[" << header << "]:###"; |
1007 | 60 | ascii().addPos(pos); |
1008 | 60 | ascii().addNote(f.str().c_str()); |
1009 | 60 | continue; |
1010 | 60 | } |
1011 | 611 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1012 | 611 | f.str(""); |
1013 | 611 | f << "Entries(" << header.m_name << "):list=["; |
1014 | 176k | for (int i=0; i< zone.m_N; ++i) f << input->readLong(4) << ","; |
1015 | 611 | f << "]"; |
1016 | 611 | ascii().addPos(pos); |
1017 | 611 | ascii().addNote(f.str().c_str()); |
1018 | 611 | ascii().addPos(input->tell()); |
1019 | 611 | ascii().addNote("_"); |
1020 | 611 | } |
1021 | 685 | return true; |
1022 | 685 | } |
1023 | | |
1024 | | bool GreatWksDBParser::readFreeList(GreatWksDBParserInternal::Block &block) |
1025 | 664 | { |
1026 | 664 | MWAWInputStreamPtr input = getInput(); |
1027 | 664 | auto const &header=block.m_header; |
1028 | 664 | libmwaw::DebugStream f; |
1029 | 1.30k | for (size_t z=0; z<block.getNumZones(); ++z) { |
1030 | 642 | auto const &zone=block.getZone(z); |
1031 | 642 | long pos=zone.m_ptr; |
1032 | 642 | if (!pos || !input->checkPosition(pos+zone.m_N*8)) { |
1033 | 142 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFreeList: the zone seems too short\n")); |
1034 | 142 | f.str(""); |
1035 | 142 | f << "Entries(" << header.m_name << ")[" << header << "]:###"; |
1036 | 142 | ascii().addPos(pos); |
1037 | 142 | ascii().addNote(f.str().c_str()); |
1038 | 142 | continue; |
1039 | 142 | } |
1040 | 500 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1041 | 500 | f.str(""); |
1042 | 500 | f << "Entries(" << header.m_name << "):zones=["; |
1043 | 397k | for (int i=0; i<zone.m_N; ++i) { |
1044 | | /* a ptr followed by 400000000|size or the oposite |
1045 | | normally the content of a free block seems to be 0x8000, dataSz |
1046 | | excepted on time where it is 8, dataSz |
1047 | | */ |
1048 | 396k | unsigned long values[2]; |
1049 | 793k | for (auto &value : values) value=static_cast<unsigned long>(input->readULong(4)); |
1050 | 396k | if ((values[0]&0xFF000000)==0x40000000) |
1051 | 1.49k | std::swap(values[0],values[1]); |
1052 | 396k | if ((values[0]&0xFF000000)==0 && (values[1]&0xFF000000)==0x40000000 && |
1053 | 832 | input->checkPosition(long(values[0]+(values[1]&0xFFFFFF)))) { |
1054 | 318 | ascii().addPos(long(values[0])); |
1055 | 318 | ascii().addNote("Free"); |
1056 | 318 | ascii().addPos(long(values[0]+(values[1]&0xFFFFFF))); |
1057 | 318 | ascii().addNote("_"); |
1058 | 318 | f << std::hex << values[0] << ":" << (values[1]&0xFFFFFF) << std::dec << ","; |
1059 | 318 | } |
1060 | 396k | else { |
1061 | 396k | MWAW_DEBUG_MSG(("GreatWksDBParser::readFreeList: find an odd free zone\n")); |
1062 | 396k | f << "###" << std::hex << values[0] << ":" << values[1] << std::dec << ","; |
1063 | 396k | } |
1064 | 396k | } |
1065 | 500 | f << "]"; |
1066 | 500 | ascii().addPos(pos); |
1067 | 500 | ascii().addNote(f.str().c_str()); |
1068 | 500 | ascii().addPos(input->tell()); |
1069 | 500 | ascii().addNote("_"); |
1070 | 500 | } |
1071 | 664 | return true; |
1072 | 664 | } |
1073 | | |
1074 | | bool GreatWksDBParser::readRowLinks(GreatWksDBParserInternal::Block &block) |
1075 | 2.18k | { |
1076 | 2.18k | MWAWInputStreamPtr input = getInput(); |
1077 | 2.18k | auto const &header=block.m_header; |
1078 | 2.18k | auto &database=m_state->m_database; |
1079 | 2.18k | libmwaw::DebugStream f; |
1080 | 4.35k | for (size_t z=0; z<block.getNumZones(); ++z) { |
1081 | 2.17k | auto const &zone=block.getZone(z); |
1082 | 2.17k | long pos=zone.m_ptr; |
1083 | 2.17k | if (!pos || !input->checkPosition(pos+zone.m_N*8)) { |
1084 | 246 | MWAW_DEBUG_MSG(("GreatWksDBParser::readRowLinks: the zone seems too short\n")); |
1085 | 246 | f.str(""); |
1086 | 246 | f << "Entries(" << header.m_name << ")[" << header << "]:###"; |
1087 | 246 | ascii().addPos(pos); |
1088 | 246 | ascii().addNote(f.str().c_str()); |
1089 | 246 | continue; |
1090 | 246 | } |
1091 | 1.92k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1092 | 1.92k | f.str(""); |
1093 | 1.92k | f << "Entries(" << header.m_name << "):ptrs=["; |
1094 | 8.40M | for (int i=0; i<zone.m_N; ++i) { |
1095 | 8.40M | auto ptr=long(input->readULong(4)); |
1096 | 8.40M | auto id=static_cast<int>(input->readLong(4)); |
1097 | 8.40M | if (ptr) { |
1098 | 6.75M | MWAWEntry entry; |
1099 | 6.75M | entry.setBegin(ptr); |
1100 | 6.75M | entry.setId(id); |
1101 | 6.75M | database.m_rowList.push_back(entry); |
1102 | 6.75M | } |
1103 | 1.65M | else { |
1104 | 1.65M | MWAW_DEBUG_MSG(("GreatWksDBParser::readRowLinks: find an empty record zone\n")); |
1105 | 1.65M | f << "###"; |
1106 | 1.65M | } |
1107 | 8.40M | f << std::hex << ptr << std::dec << ":" << id << ","; |
1108 | 8.40M | } |
1109 | 1.92k | f << "]"; |
1110 | 1.92k | ascii().addPos(pos); |
1111 | 1.92k | ascii().addNote(f.str().c_str()); |
1112 | 1.92k | ascii().addPos(input->tell()); |
1113 | 1.92k | ascii().addNote("_"); |
1114 | 1.92k | } |
1115 | 2.18k | return true; |
1116 | 2.18k | } |
1117 | | |
1118 | | bool GreatWksDBParser::readBlockHeader2(GreatWksDBParserInternal::Block const &block) |
1119 | 401 | { |
1120 | 401 | MWAWInputStreamPtr input = getInput(); |
1121 | 401 | auto const &header=block.m_header; |
1122 | 401 | libmwaw::DebugStream f; |
1123 | 795 | for (size_t z=0; z<block.getNumZones(); ++z) { |
1124 | 394 | auto const &zone=block.getZone(z); |
1125 | 394 | long pos=zone.m_ptr; |
1126 | 394 | if (!pos || !input->checkPosition(pos+zone.m_N*8)) { |
1127 | 13 | MWAW_DEBUG_MSG(("GreatWksDBParser::readBlockHeader2: the zone seems too short\n")); |
1128 | 13 | f.str(""); |
1129 | 13 | f << "Entries(" << header.m_name << ")[" << header << "]:###"; |
1130 | 13 | ascii().addPos(pos); |
1131 | 13 | ascii().addNote(f.str().c_str()); |
1132 | 13 | continue; |
1133 | 13 | } |
1134 | 381 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1135 | 381 | f.str(""); |
1136 | 381 | f << "Entries(" << header.m_name << "):unkn=["; |
1137 | 4.33k | for (int i=0; i<zone.m_N; ++i) { |
1138 | 3.95k | f << input->readULong(4) << ":"; // always 0? |
1139 | 3.95k | f << input->readLong(4) << ","; // ids |
1140 | 3.95k | } |
1141 | 381 | f << "]"; |
1142 | 381 | ascii().addPos(pos); |
1143 | 381 | ascii().addNote(f.str().c_str()); |
1144 | 381 | ascii().addPos(input->tell()); |
1145 | 381 | ascii().addNote("_"); |
1146 | 381 | } |
1147 | 401 | return true; |
1148 | 401 | } |
1149 | | |
1150 | | bool GreatWksDBParser::readBlock(GreatWksDBParserInternal::Block const &block, int fieldSize) |
1151 | 0 | { |
1152 | 0 | MWAWInputStreamPtr input = getInput(); |
1153 | 0 | auto const &header=block.m_header; |
1154 | 0 | libmwaw::DebugStream f; |
1155 | 0 | for (size_t z=0; z<block.getNumZones(); ++z) { |
1156 | 0 | auto const &zone=block.getZone(z); |
1157 | 0 | long pos=zone.m_ptr; |
1158 | 0 | if (fieldSize<=0 || !pos || !input->checkPosition(pos+zone.m_N*fieldSize)) { |
1159 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParser::readBlock: the fieldSize seems bad\n")); |
1160 | 0 | f.str(""); |
1161 | 0 | f << "Entries(" << header.m_name << ")[" << header << "]:###"; |
1162 | 0 | ascii().addPos(pos); |
1163 | 0 | ascii().addNote(f.str().c_str()); |
1164 | 0 | continue; |
1165 | 0 | } |
1166 | 0 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1167 | 0 | for (int i=0; i<zone.m_N; ++i) { |
1168 | 0 | pos=input->tell(); |
1169 | 0 | f.str(""); |
1170 | 0 | f << "Entries(" << header.m_name << ")[" << i << "]:"; |
1171 | 0 | input->seek(pos+fieldSize, librevenge::RVNG_SEEK_SET); |
1172 | 0 | ascii().addPos(pos); |
1173 | 0 | ascii().addNote(f.str().c_str()); |
1174 | 0 | } |
1175 | 0 | ascii().addPos(input->tell()); |
1176 | 0 | ascii().addNote("_"); |
1177 | 0 | } |
1178 | 0 | return true; |
1179 | 0 | } |
1180 | | |
1181 | | //////////////////////////////////////////////////////////// |
1182 | | // check if entry.begin() corresponds or not to a small zone |
1183 | | //////////////////////////////////////////////////////////// |
1184 | | bool GreatWksDBParser::checkSmallZone(MWAWEntry &entry) |
1185 | 84.8k | { |
1186 | 84.8k | if (entry.begin()<=0) return false; |
1187 | 84.8k | MWAWInputStreamPtr input = getInput(); |
1188 | 84.8k | if (!input->checkPosition(entry.begin()+6)) return false; |
1189 | | |
1190 | 59.1k | input->seek(entry.begin(), librevenge::RVNG_SEEK_SET); |
1191 | 59.1k | auto id=static_cast<int>(input->readLong(2)); |
1192 | 59.1k | entry.setId(id); |
1193 | | /* the zone with id=10 is in fact a list of zone with id=10, so |
1194 | | its length will be underestimate */ |
1195 | 59.1k | entry.setLength(6+long(input->readULong(4))); |
1196 | 59.1k | if (id>=0 && id<15) { |
1197 | 25.3k | static char const* const names[]= { |
1198 | 25.3k | "Zone0A", "Field", "FrmLink", "Zone3A", "Zone4A", "ListFrmula", "ListSummary", "Zone7A", |
1199 | 25.3k | "Zone8A", "Form", "FldLink", "Zone11A", "Zone12A", "FldAuxi", "Zone14A" |
1200 | 25.3k | }; |
1201 | 25.3k | entry.setName(names[id]); |
1202 | 25.3k | } |
1203 | 33.8k | else { |
1204 | 33.8k | std::stringstream s; |
1205 | 33.8k | s << "Zone" << id << "A"; |
1206 | 33.8k | entry.setName(s.str()); |
1207 | 33.8k | } |
1208 | 59.1k | return input->checkPosition(entry.begin()+6); |
1209 | 84.8k | } |
1210 | | |
1211 | | //////////////////////////////////////////////////////////// |
1212 | | // read the row zone |
1213 | | //////////////////////////////////////////////////////////// |
1214 | | bool GreatWksDBParser::readRowRecords(MWAWEntry const &entry) |
1215 | 6.66M | { |
1216 | 6.66M | MWAWInputStreamPtr input = getInput(); |
1217 | 6.66M | long pos=entry.begin(); |
1218 | 6.66M | if (!pos || !input->checkPosition(pos+6)) { |
1219 | 5.27M | MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: can not find a row position\n")); |
1220 | 5.27M | if (pos) { |
1221 | 5.27M | ascii().addPos(pos); |
1222 | 5.27M | ascii().addNote("Entries(RowRec):###"); |
1223 | 5.27M | } |
1224 | 5.27M | return false; |
1225 | 5.27M | } |
1226 | | |
1227 | 1.38M | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1228 | 1.38M | libmwaw::DebugStream f; |
1229 | 1.38M | f << "Entries(RowRec):"; |
1230 | | // probably type=0 followed by ? |
1231 | 1.38M | auto val=static_cast<int>(input->readULong(2)); |
1232 | 1.38M | if (val!=0xFF) f << "f0=" << std::hex << val << std::dec << ","; |
1233 | 1.38M | auto dSz=long(input->readULong(4)); |
1234 | 1.38M | long endPos=pos+6+dSz; |
1235 | 1.38M | if (dSz<0 || !input->checkPosition(endPos)) { |
1236 | 899k | MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: the size seems bad\n")); |
1237 | 899k | f << "###"; |
1238 | 899k | ascii().addPos(pos); |
1239 | 899k | ascii().addNote(f.str().c_str()); |
1240 | 899k | return false; |
1241 | 899k | } |
1242 | 488k | ascii().addPos(pos); |
1243 | 488k | ascii().addNote(f.str().c_str()); |
1244 | 488k | ascii().addPos(endPos); |
1245 | 488k | ascii().addNote("_"); |
1246 | | |
1247 | 488k | auto &database=m_state->m_database; |
1248 | 1.71M | for (size_t fl=0; fl<database.m_fieldList.size(); ++fl) { |
1249 | 1.71M | pos=input->tell(); |
1250 | 1.71M | f.str(""); |
1251 | 1.71M | f << "RowRec-" << fl << ":"; |
1252 | 1.71M | if (pos >= endPos) { |
1253 | 288k | MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: actual pos seems bad\n")); |
1254 | 288k | f << "###"; |
1255 | 288k | ascii().addPos(pos); |
1256 | 288k | ascii().addNote(f.str().c_str()); |
1257 | 288k | return true; |
1258 | 288k | } |
1259 | 1.42M | auto const &field=database.m_fieldList[fl]; |
1260 | 1.42M | bool ok=true; |
1261 | | |
1262 | 1.42M | GreatWksDBParserInternal::Cell cell; |
1263 | 1.42M | cell.setFormat(field.m_format); |
1264 | 1.42M | switch (field.m_type) { |
1265 | 6.06k | case GreatWksDBParserInternal::Field::F_Text: { |
1266 | 6.06k | auto sSz=static_cast<int>(input->readULong(1)); |
1267 | 6.06k | if (pos+1+sSz>endPos) { |
1268 | 813 | MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: the string length seems bad\n")); |
1269 | 813 | f << "###"; |
1270 | 813 | ascii().addPos(pos); |
1271 | 813 | ascii().addNote(f.str().c_str()); |
1272 | 813 | } |
1273 | 6.06k | cell.m_content.m_contentType=MWAWCellContent::C_TEXT; |
1274 | 6.06k | cell.m_content.m_textEntry.setBegin(input->tell()); |
1275 | 6.06k | cell.m_content.m_textEntry.setLength(sSz); |
1276 | 6.06k | database.addCell(MWAWVec2i(field.m_id, entry.id()), cell); |
1277 | | |
1278 | 6.06k | std::string text(""); |
1279 | 234k | for (int i=0; i<sSz; ++i) text+=char(input->readULong(1)); |
1280 | 6.06k | f << text << ","; |
1281 | | // realign to a multiple of 2 |
1282 | 6.06k | if ((sSz%2)==0) input->seek(1,librevenge::RVNG_SEEK_CUR); |
1283 | 6.06k | break; |
1284 | 0 | } |
1285 | 221k | case GreatWksDBParserInternal::Field::F_Summary: { |
1286 | 221k | if (pos+10>endPos) { |
1287 | 1.54k | MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: can not read a summary zone\n")); |
1288 | 1.54k | f << "###"; |
1289 | 1.54k | ascii().addPos(pos); |
1290 | 1.54k | ascii().addNote(f.str().c_str()); |
1291 | 1.54k | return false; |
1292 | 1.54k | } |
1293 | | // formula will be will later |
1294 | 219k | cell.m_content.m_contentType=MWAWCellContent::C_FORMULA; |
1295 | 219k | database.addCell(MWAWVec2i(field.m_id, entry.id()), cell); |
1296 | 219k | f << "summary,"; |
1297 | 219k | input->seek(pos+10, librevenge::RVNG_SEEK_SET); |
1298 | 219k | break; |
1299 | 221k | } |
1300 | 834k | case GreatWksDBParserInternal::Field::F_Number: |
1301 | 837k | case GreatWksDBParserInternal::Field::F_Date: |
1302 | 838k | case GreatWksDBParserInternal::Field::F_Time: { |
1303 | 838k | if (pos+10>endPos) { |
1304 | 76.6k | MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: can not read a number zone\n")); |
1305 | 76.6k | f << "###"; |
1306 | 76.6k | ascii().addPos(pos); |
1307 | 76.6k | ascii().addNote(f.str().c_str()); |
1308 | 76.6k | return false; |
1309 | 76.6k | } |
1310 | 762k | double value; |
1311 | 762k | bool isNan; |
1312 | | /*fixme: this does not work for time's field as value is a very |
1313 | | big number and the double precision is too small, ie. we do not |
1314 | | retrieve the decimal part -> time is always 0:0:0 */ |
1315 | 762k | if (!input->readDouble10(value, isNan)) { |
1316 | 36.5k | static bool first=true; |
1317 | 36.5k | if (first) { |
1318 | | // can be normal, ie. 7fff403200000000000000 means no value which generates an error |
1319 | 5 | MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: can not read a number, maybe a undef number\n")); |
1320 | 5 | first=false; |
1321 | 5 | } |
1322 | 36.5k | f << "#"; |
1323 | 36.5k | } |
1324 | 725k | else { |
1325 | 725k | cell.m_content.m_contentType=MWAWCellContent::C_NUMBER; |
1326 | 725k | cell.m_content.setValue(value); |
1327 | 725k | database.addCell(MWAWVec2i(field.m_id, entry.id()), cell); |
1328 | 725k | f << value; |
1329 | 725k | } |
1330 | 762k | input->seek(pos+10, librevenge::RVNG_SEEK_SET); |
1331 | 762k | break; |
1332 | 838k | } |
1333 | 25.7k | case GreatWksDBParserInternal::Field::F_Picture: { |
1334 | 25.7k | long pSz=static_cast<int>(input->readULong(4)); |
1335 | 25.7k | if (pos+4+pSz>endPos) { |
1336 | 12.3k | MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: the picture length seems bad\n")); |
1337 | 12.3k | f << "###"; |
1338 | 12.3k | ascii().addPos(pos); |
1339 | 12.3k | ascii().addNote(f.str().c_str()); |
1340 | 12.3k | } |
1341 | 25.7k | if (pSz) { |
1342 | 21.1k | cell.m_pictEntry.setBegin(pos+4); |
1343 | 21.1k | cell.m_pictEntry.setLength(pSz); |
1344 | 21.1k | database.addCell(MWAWVec2i(field.m_id, entry.id()), cell); |
1345 | | #ifdef DEBUG_WITH_FILES |
1346 | | ascii().skipZone(pos+4,pos+4+pSz-1); |
1347 | | librevenge::RVNGBinaryData file; |
1348 | | input->seek(pos+4,librevenge::RVNG_SEEK_SET); |
1349 | | input->readDataBlock(pSz, file); |
1350 | | |
1351 | | static int volatile pictName = 0; |
1352 | | libmwaw::DebugStream f2; |
1353 | | f2 << "DATA-" << ++pictName << ".pct"; |
1354 | | libmwaw::Debug::dumpFile(file, f2.str().c_str()); |
1355 | | #endif |
1356 | 21.1k | } |
1357 | 25.7k | input->seek(pos+4+pSz, librevenge::RVNG_SEEK_SET); |
1358 | 25.7k | break; |
1359 | 838k | } |
1360 | 202k | case GreatWksDBParserInternal::Field::F_Memo: { |
1361 | 202k | long mSz=static_cast<int>(input->readULong(4)); |
1362 | 202k | if (pos+4+mSz>endPos) { |
1363 | 139k | MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: the memo length seems bad\n")); |
1364 | 139k | f << "###"; |
1365 | 139k | ascii().addPos(pos); |
1366 | 139k | ascii().addNote(f.str().c_str()); |
1367 | 139k | } |
1368 | 202k | cell.m_content.m_contentType=MWAWCellContent::C_TEXT; |
1369 | 202k | cell.m_content.m_textEntry.setBegin(input->tell()); |
1370 | 202k | cell.m_content.m_textEntry.setLength(mSz); |
1371 | 202k | database.addCell(MWAWVec2i(field.m_id, entry.id()), cell); |
1372 | | // probably a simple textbox ( see sendSimpleTextbox ) |
1373 | 202k | if (mSz) f << "memo,"; |
1374 | 202k | input->seek(pos+4+mSz, librevenge::RVNG_SEEK_SET); |
1375 | 202k | break; |
1376 | 838k | } |
1377 | 37.4k | case GreatWksDBParserInternal::Field::F_Formula: { |
1378 | 37.4k | std::string extra(""); |
1379 | 37.4k | ok=readFormulaResult(endPos, cell, extra); |
1380 | 37.4k | if (ok) |
1381 | 15.3k | database.addCell(MWAWVec2i(field.m_id, entry.id()), cell); |
1382 | 37.4k | f << extra; |
1383 | 37.4k | break; |
1384 | 838k | } |
1385 | 95.3k | case GreatWksDBParserInternal::Field::F_Unknown: |
1386 | | #if !defined(__clang__) |
1387 | | default: |
1388 | | #endif |
1389 | 95.3k | ok=false; |
1390 | 95.3k | break; |
1391 | 1.42M | } |
1392 | 1.34M | if (!ok) |
1393 | 117k | break; |
1394 | 1.23M | ascii().addPos(pos); |
1395 | 1.23M | ascii().addNote(f.str().c_str()); |
1396 | 1.23M | } |
1397 | 122k | pos=input->tell(); |
1398 | 122k | if (pos+2==endPos) { |
1399 | | // seems ok to find 0 here |
1400 | 1.52k | val=static_cast<int>(input->readLong(2)); |
1401 | 1.52k | if (val) { |
1402 | 713 | f.str(""); |
1403 | 713 | f << "RowRec[end]:" << val << ","; |
1404 | 713 | ascii().addPos(pos); |
1405 | 713 | ascii().addNote(f.str().c_str()); |
1406 | 713 | } |
1407 | 813 | else { |
1408 | 813 | ascii().addPos(pos); |
1409 | 813 | ascii().addNote("_"); |
1410 | 813 | } |
1411 | 1.52k | } |
1412 | 120k | else if (pos!=endPos) { |
1413 | 118k | MWAW_DEBUG_MSG(("GreatWksDBParser::readRowRecords: find some extra data\n")); |
1414 | 118k | ascii().addPos(pos); |
1415 | 118k | ascii().addNote("RowRec[end]:###"); |
1416 | 118k | } |
1417 | | |
1418 | 122k | return true; |
1419 | 488k | } |
1420 | | |
1421 | | bool GreatWksDBParser::readFormula(long endPos, std::vector<MWAWCellContent::FormulaInstruction> &formula) |
1422 | 2.08k | { |
1423 | 2.08k | formula.resize(0); |
1424 | 2.08k | MWAWInputStreamPtr input = getInput(); |
1425 | 2.08k | long pos=input->tell(); |
1426 | 2.08k | auto dSz=long(input->readULong(2)); |
1427 | 2.08k | long endHeader=pos+2+dSz; |
1428 | 2.08k | if (dSz<2 || endHeader > endPos) { |
1429 | 1.08k | MWAW_DEBUG_MSG(("GreatWksDBParser::readFormula: can not read a formula\n")); |
1430 | 1.08k | return false; |
1431 | 1.08k | } |
1432 | 996 | libmwaw::DebugStream f; |
1433 | 996 | f << "Entries(Formula):"; |
1434 | 996 | std::string error(""); |
1435 | 996 | if (!m_document->readFormula(MWAWVec2i(0,0),endHeader,formula, error)) |
1436 | 960 | f << "###"; |
1437 | 996 | f << "["; |
1438 | 996 | for (const auto &l : formula) |
1439 | 372 | f << l; |
1440 | 996 | f << "]" << error << ","; |
1441 | | |
1442 | 996 | input->seek(endHeader, librevenge::RVNG_SEEK_SET); |
1443 | 996 | auto N=static_cast<int>(input->readULong(2)); |
1444 | 996 | if (endHeader+2*(N+1)>endPos) { |
1445 | 1 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFormula: can not read a formula field(II)\n")); |
1446 | 1 | return false; |
1447 | 1 | } |
1448 | 995 | auto val=static_cast<int>(input->readLong(2)); // Here I find always 2, the field size ? |
1449 | 995 | if (val!=2) f << "g0=" << val << ","; |
1450 | 995 | std::vector<int> varList; |
1451 | 995 | if (N) { |
1452 | 664 | f << "list[varId]=["; |
1453 | 5.18M | for (int i=0; i<N; ++i) { |
1454 | 5.18M | val=static_cast<int>(input->readLong(2)); |
1455 | 5.18M | varList.push_back(val); |
1456 | 5.18M | f << val << ","; |
1457 | 5.18M | } |
1458 | 664 | f << "],"; |
1459 | 664 | } |
1460 | | // time to update the formula |
1461 | 995 | int id=0; |
1462 | 995 | for (auto &instr : formula) { |
1463 | 372 | if (instr.m_type!=MWAWCellContent::FormulaInstruction::F_Cell) |
1464 | 372 | continue; |
1465 | 0 | if (id>=static_cast<int>(varList.size()) || varList[size_t(id)]<=0) { |
1466 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFormula: can not associated a cell with field\n")); |
1467 | 0 | f << "###"; |
1468 | 0 | formula.resize(0); |
1469 | 0 | break; |
1470 | 0 | } |
1471 | 0 | instr.m_position[0][0]=varList[size_t(id++)]-1; |
1472 | 0 | } |
1473 | 995 | ascii().addPos(pos); |
1474 | 995 | ascii().addNote(f.str().c_str()); |
1475 | 995 | return true; |
1476 | 996 | } |
1477 | | |
1478 | | /* read the result of a formula. |
1479 | | |
1480 | | note: we fill the cell content, which can overidden later if we find a formula to associate with the cell */ |
1481 | | bool GreatWksDBParser::readFormulaResult(long endPos, GreatWksDBParserInternal::Cell &cell, std::string &extra) |
1482 | 37.4k | { |
1483 | 37.4k | libmwaw::DebugStream f; |
1484 | 37.4k | MWAWInputStreamPtr input = getInput(); |
1485 | 37.4k | long pos=input->tell(); |
1486 | 37.4k | if (pos+2>endPos || !input->checkPosition(endPos)) return false; |
1487 | 34.8k | auto type=static_cast<int>(input->readLong(2)); |
1488 | 34.8k | switch (type) { |
1489 | 12.2k | case 0: // average |
1490 | 13.9k | case 1: // summary |
1491 | 14.5k | case 5: // number |
1492 | 14.8k | case 9: // date |
1493 | 15.2k | case 0xa: { // time |
1494 | 15.2k | if (pos+12>endPos) return false; |
1495 | 13.7k | double value; |
1496 | 13.7k | bool isNan; |
1497 | 13.7k | if (!input->readDouble10(value, isNan)) |
1498 | 1.63k | f << "#double,"; |
1499 | 12.1k | else { |
1500 | 12.1k | cell.m_content.m_contentType=MWAWCellContent::C_NUMBER; |
1501 | 12.1k | cell.m_content.setValue(value); |
1502 | 12.1k | f << value << ","; |
1503 | 12.1k | } |
1504 | 13.7k | input->seek(pos+12, librevenge::RVNG_SEEK_SET); |
1505 | 13.7k | break; |
1506 | 15.2k | } |
1507 | 862 | case 7: { |
1508 | | // checkme |
1509 | 862 | auto sSz=static_cast<int>(input->readULong(1)); |
1510 | 862 | if (pos+3+sSz>endPos) { |
1511 | 84 | MWAW_DEBUG_MSG(("GreatWksSSParser::readFormulaResult: can not read a string value\n")); |
1512 | 84 | return false; |
1513 | 84 | } |
1514 | 778 | cell.m_content.m_contentType=MWAWCellContent::C_TEXT; |
1515 | 778 | cell.m_content.m_textEntry.setBegin(input->tell()); |
1516 | 778 | cell.m_content.m_textEntry.setLength(sSz); |
1517 | | // also modify the cell format in text |
1518 | 778 | auto format=cell.getFormat(); |
1519 | 778 | format.m_format=MWAWCell::F_TEXT; |
1520 | 778 | cell.setFormat(format); |
1521 | 778 | std::string text(""); |
1522 | 134k | for (int i=0; i<sSz; ++i) |
1523 | 133k | text += char(input->readULong(1)); |
1524 | 778 | f << "\"" << text << "\","; |
1525 | 778 | if ((sSz%2)==0) |
1526 | 235 | input->seek(1, librevenge::RVNG_SEEK_CUR); |
1527 | 778 | break; |
1528 | 862 | } |
1529 | 822 | case 8: { // bool or long |
1530 | 822 | if (pos+4>endPos) return false; |
1531 | 645 | auto val=static_cast<int>(input->readLong(2)); |
1532 | 645 | cell.m_content.m_contentType=MWAWCellContent::C_NUMBER; |
1533 | 645 | cell.m_content.setValue(double(val)); |
1534 | 645 | f << input->readLong(2) << ","; |
1535 | 645 | break; |
1536 | 822 | } |
1537 | 557 | case 0xf: // nan |
1538 | 557 | if (pos+6>endPos) return false; |
1539 | 132 | cell.m_content.m_contentType=MWAWCellContent::C_NUMBER; |
1540 | 132 | cell.m_content.setValue(std::numeric_limits<double>::quiet_NaN()); |
1541 | 132 | f << "nan" << input->readLong(4) << ","; |
1542 | 132 | break; |
1543 | 17.3k | default: |
1544 | 17.3k | MWAW_DEBUG_MSG(("GreatWksDBParser::readFormulaResult: find unknown type %d\n", type)); |
1545 | 17.3k | f << "#type=" << type << ","; |
1546 | 17.3k | extra=f.str(); |
1547 | 17.3k | return false; |
1548 | 34.8k | } |
1549 | 15.3k | extra=f.str(); |
1550 | 15.3k | return true; |
1551 | 34.8k | } |
1552 | | |
1553 | | //////////////////////////////////////////////////////////// |
1554 | | // read the fields zone |
1555 | | //////////////////////////////////////////////////////////// |
1556 | | bool GreatWksDBParser::readFields(MWAWEntry const &entry) |
1557 | 3.81k | { |
1558 | 3.81k | if (!entry.valid() || entry.length()<10) { |
1559 | 10 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFields: the entry length seems bad\n")); |
1560 | 10 | return false; |
1561 | 10 | } |
1562 | 3.80k | int const vers=version(); |
1563 | 3.80k | entry.setParsed(true); |
1564 | 3.80k | ascii().addPos(entry.end()); |
1565 | 3.80k | ascii().addNote("_"); |
1566 | 3.80k | MWAWInputStreamPtr input = getInput(); |
1567 | 3.80k | libmwaw::DebugStream f; |
1568 | 3.80k | f << "Entries(Field):"; |
1569 | 3.80k | input->seek(entry.begin()+6, librevenge::RVNG_SEEK_SET); |
1570 | 3.80k | auto N=static_cast<int>(input->readULong(2)); |
1571 | 3.80k | f << "N=" << N << ","; |
1572 | 3.80k | auto dSz=static_cast<int>(input->readULong(2)); |
1573 | 3.80k | if ((vers==1 && dSz<0x3c) || (vers==2 && dSz<0x3e) || entry.length()<10+long(N)*dSz) { |
1574 | 75 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFields: the number of data seems bad\n")); |
1575 | 75 | f << "###"; |
1576 | 75 | ascii().addPos(entry.begin()); |
1577 | 75 | ascii().addNote(f.str().c_str()); |
1578 | 75 | return false; |
1579 | 75 | } |
1580 | 3.73k | ascii().addPos(entry.begin()); |
1581 | 3.73k | ascii().addNote(f.str().c_str()); |
1582 | 3.73k | long pos; |
1583 | 21.8M | for (int i=0; i<N; ++i) { |
1584 | 21.8M | pos=input->tell(); |
1585 | 21.8M | f.str(""); |
1586 | 21.8M | f << "Field-" << i << ":"; |
1587 | 21.8M | GreatWksDBParserInternal::Field field; |
1588 | 21.8M | if (readField(field)) |
1589 | 1.55M | f << field; |
1590 | 20.3M | else { |
1591 | 20.3M | field = GreatWksDBParserInternal::Field(); |
1592 | 20.3M | MWAW_DEBUG_MSG(("GreatWksDBParser::readFields: can not read a field\n")); |
1593 | 20.3M | f << "###"; |
1594 | 20.3M | } |
1595 | 21.8M | m_state->m_database.m_fieldList.push_back(field); |
1596 | 21.8M | ascii().addPos(pos); |
1597 | 21.8M | ascii().addNote(f.str().c_str()); |
1598 | 21.8M | input->seek(pos+dSz, librevenge::RVNG_SEEK_SET); |
1599 | 21.8M | } |
1600 | 13.8M | for (auto &field : m_state->m_database.m_fieldList) { |
1601 | 13.8M | pos=input->tell(); |
1602 | 13.8M | if (field.m_type==GreatWksDBParserInternal::Field::F_Summary) { |
1603 | 1.14k | if (pos+18>entry.end()) { |
1604 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFields: can not read a summary field\n")); |
1605 | 0 | break; |
1606 | 0 | } |
1607 | 1.14k | f.str(""); |
1608 | 1.14k | f << "Field[summary]:"; |
1609 | 1.14k | auto val=static_cast<int>(input->readULong(2)); // 10a or 180 |
1610 | 1.14k | if (val!=0x10a) f << "fl=" << std::hex << val << std::dec << ","; |
1611 | 1.14k | field.m_summaryType=static_cast<int>(input->readULong(2)); |
1612 | 1.14k | if (field.m_summaryType > 0 && field.m_summaryType < 6) { |
1613 | 47 | static char const* const wh[] = { "", "average", "count", "total", "minimum", "maximum" }; |
1614 | 47 | f << wh[field.m_summaryType] << ","; |
1615 | 47 | } |
1616 | 1.09k | else |
1617 | 1.09k | f << "#type=" << field.m_summaryType << ","; |
1618 | 1.14k | field.m_summaryField=static_cast<int>(input->readLong(2))-1; |
1619 | 1.14k | f << "field=" << field.m_summaryField << ","; |
1620 | 1.14k | val=static_cast<int>(input->readULong(2)); // 1e0 or 14 |
1621 | 1.14k | if (val!=0x1e0) f << "f0=" << std::hex << val << std::dec << ","; |
1622 | 6.87k | for (int i=0; i<5; ++i) { // always 0 |
1623 | 5.73k | val=static_cast<int>(input->readULong(2)); |
1624 | 5.73k | if (val) f << "f" << i+1 << "=" << val << ","; |
1625 | 5.73k | } |
1626 | | |
1627 | 1.14k | ascii().addPos(pos); |
1628 | 1.14k | ascii().addNote(f.str().c_str()); |
1629 | 1.14k | input->seek(pos+18, librevenge::RVNG_SEEK_SET); |
1630 | 1.14k | continue; |
1631 | 1.14k | } |
1632 | 13.8M | if (field.m_type!=GreatWksDBParserInternal::Field::F_Formula) |
1633 | 13.8M | continue; |
1634 | 2.08k | if (!readFormula(entry.end(), field.m_formula)) { |
1635 | 1.09k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1636 | 1.09k | break; |
1637 | 1.09k | } |
1638 | 2.08k | } |
1639 | 3.73k | pos=input->tell(); |
1640 | 3.73k | if (input->tell()!=entry.end()) { // now some string or formula? |
1641 | 3.41k | ascii().addPos(pos); |
1642 | 3.41k | ascii().addNote("Field:end"); |
1643 | 3.41k | } |
1644 | | |
1645 | 3.73k | return true; |
1646 | 3.80k | } |
1647 | | |
1648 | | bool GreatWksDBParser::readField(GreatWksDBParserInternal::Field &field) |
1649 | 21.8M | { |
1650 | 21.8M | field=GreatWksDBParserInternal::Field(); |
1651 | 21.8M | MWAWInputStreamPtr input = getInput(); |
1652 | 21.8M | long pos=input->tell(); |
1653 | 21.8M | if (!input->checkPosition(pos+0x3c)) return false; |
1654 | 1.55M | auto type=static_cast<int>(input->readLong(2)); |
1655 | 1.55M | libmwaw::DebugStream f; |
1656 | 1.55M | auto &format = field.m_format; |
1657 | 1.55M | switch (type) { |
1658 | 10.7k | case 5: |
1659 | 10.7k | field.m_type=GreatWksDBParserInternal::Field::F_Number; |
1660 | 10.7k | format.m_format=MWAWCell::F_NUMBER; |
1661 | 10.7k | format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC; |
1662 | 10.7k | break; |
1663 | 5.78k | case 7: |
1664 | 5.78k | field.m_type=GreatWksDBParserInternal::Field::F_Text; |
1665 | 5.78k | format.m_format=MWAWCell::F_TEXT; |
1666 | 5.78k | break; |
1667 | 2.90k | case 9: |
1668 | 2.90k | field.m_type=GreatWksDBParserInternal::Field::F_Date; |
1669 | 2.90k | format.m_format=MWAWCell::F_DATE; |
1670 | 2.90k | break; |
1671 | 4.91k | case 0xa: |
1672 | 4.91k | field.m_type=GreatWksDBParserInternal::Field::F_Time; |
1673 | 4.91k | format.m_format=MWAWCell::F_TIME; |
1674 | 4.91k | break; |
1675 | 3.40k | case 0xc: |
1676 | 3.40k | field.m_type=GreatWksDBParserInternal::Field::F_Memo; |
1677 | 3.40k | format.m_format=MWAWCell::F_TEXT; |
1678 | 3.40k | break; |
1679 | 2.07k | case 0xd: |
1680 | 2.07k | field.m_type=GreatWksDBParserInternal::Field::F_Picture; |
1681 | 2.07k | break; |
1682 | 5.12k | case 0xFF: |
1683 | 5.12k | field.m_type=GreatWksDBParserInternal::Field::F_Formula; |
1684 | 5.12k | format.m_format=MWAWCell::F_NUMBER; |
1685 | 5.12k | format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC; |
1686 | 5.12k | break; |
1687 | 1.61k | case 0xFE: |
1688 | 1.61k | field.m_type=GreatWksDBParserInternal::Field::F_Summary; |
1689 | 1.61k | format.m_format=MWAWCell::F_NUMBER; |
1690 | 1.61k | format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC; |
1691 | 1.61k | break; |
1692 | 1.51M | default: |
1693 | 1.51M | MWAW_DEBUG_MSG(("GreatWksDBParser::readField: find unknown file type\n")); |
1694 | 1.51M | f << "#type=" << type << ","; |
1695 | 1.55M | } |
1696 | 1.55M | int val; |
1697 | 1.55M | if (version()==2) { // always 5? |
1698 | 1.53M | val=static_cast<int>(input->readLong(2)); |
1699 | 1.53M | if (val!=5) f << "f0=" << val << ","; |
1700 | 1.53M | } |
1701 | 13.9M | for (int i=0; i<8; ++i) { // fl0-6=0|1, fl7=2c|38|ee|c4 |
1702 | 12.4M | val=static_cast<int>(input->readULong(1)); |
1703 | 12.4M | if (val) f << "fl" << i << "=" << std::hex << val << std::dec << ","; |
1704 | 12.4M | } |
1705 | 1.55M | val=static_cast<int>(input->readLong(2)); // always 0 |
1706 | 1.55M | if (val) f << "f1=" << val << ","; |
1707 | | /* f2=f3=f4=0 excepted |
1708 | | for formula find 8,5bb2b4,1b or 4,5bb200,5 |
1709 | | for summary find 0,0,05bb254 and 0,0,5bb268 |
1710 | | */ |
1711 | 1.55M | val=static_cast<int>(input->readLong(2)); |
1712 | 1.55M | if (val) f << "f2=" << val << ","; |
1713 | 4.65M | for (int i=0; i<2; ++i) { |
1714 | 3.10M | val=static_cast<int>(input->readULong(4)); |
1715 | 3.10M | if (val) f << "f" << 3+i << "=" << std::hex << val << std::dec << ","; |
1716 | 3.10M | } |
1717 | 1.55M | val=static_cast<int>(input->readLong(2)); // always 0 |
1718 | 1.55M | if (val) f << "f5=" << val << ","; |
1719 | 1.55M | field.m_linkZone=long(input->readULong(2)); |
1720 | 1.55M | field.m_id=static_cast<int>(input->readLong(2))-1; |
1721 | 1.55M | auto fSz=static_cast<int>(input->readULong(1)); |
1722 | 1.55M | if (fSz>31) { |
1723 | 541k | MWAW_DEBUG_MSG(("GreatWksDBParser::readField: the name field size seems to big\n")); |
1724 | 541k | f << "###"; |
1725 | 541k | ascii().addDelimiter(input->tell(),'|'); |
1726 | 541k | } |
1727 | 1.01M | else { |
1728 | 1.01M | std::string name(""); |
1729 | 3.61M | for (int i=0; i<fSz; ++i) name+=char(input->readULong(1)); |
1730 | 1.01M | field.m_name=name; |
1731 | 1.01M | } |
1732 | 1.55M | field.m_extra=f.str(); |
1733 | 1.55M | return true; |
1734 | 1.55M | } |
1735 | | |
1736 | | bool GreatWksDBParser::readFieldAuxis(MWAWEntry const &entry) |
1737 | 381 | { |
1738 | 381 | if (!entry.valid() || entry.length()<10) { |
1739 | 8 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldAuxis: the entry length seems bad\n")); |
1740 | 8 | return false; |
1741 | 8 | } |
1742 | 373 | int const vers=version(); |
1743 | 373 | entry.setParsed(true); |
1744 | 373 | ascii().addPos(entry.end()); |
1745 | 373 | ascii().addNote("_"); |
1746 | 373 | MWAWInputStreamPtr input = getInput(); |
1747 | 373 | libmwaw::DebugStream f; |
1748 | 373 | f << "Entries(Field):"; |
1749 | 373 | input->seek(entry.begin()+6, librevenge::RVNG_SEEK_SET); |
1750 | 373 | auto N=static_cast<int>(input->readULong(2)); |
1751 | 373 | f << "N=" << N << ","; |
1752 | 373 | auto dSz=static_cast<int>(input->readULong(2)); |
1753 | 373 | if (vers==1 || (vers==2 && dSz<0x46) || entry.length()<10+long(N)*dSz) { |
1754 | 29 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldAuxis: the number of data seems bad\n")); |
1755 | 29 | f << "###"; |
1756 | 29 | ascii().addPos(entry.begin()); |
1757 | 29 | ascii().addNote(f.str().c_str()); |
1758 | 29 | return false; |
1759 | 29 | } |
1760 | 344 | ascii().addPos(entry.begin()); |
1761 | 344 | ascii().addNote(f.str().c_str()); |
1762 | 344 | auto &database=m_state->m_database; |
1763 | 344 | if (N>static_cast<int>(database.m_fieldList.size())) { |
1764 | 76 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldAuxis: the field list seems too short\n")); |
1765 | 76 | database.m_fieldList.resize(size_t(N)); |
1766 | 76 | } |
1767 | 344 | int n=0; |
1768 | 2.70M | for (auto &field : database.m_fieldList) { |
1769 | 2.70M | long pos=input->tell(); |
1770 | 2.70M | f.str(""); |
1771 | 2.70M | f << "FldAuxi-" << n++ << ":"; |
1772 | 2.70M | auto val=static_cast<int>(input->readLong(2)); |
1773 | 2.70M | if (val) { |
1774 | | // checkme: find val=10 for a sequence with increment=10, so... |
1775 | 45.2k | f << "type[increment]=" << val << ","; |
1776 | 45.2k | field.m_isSequence=true; |
1777 | 45.2k | } |
1778 | 2.70M | val=static_cast<int>(input->readLong(2)); |
1779 | 2.70M | if (val==1) { |
1780 | 1.57k | f << "isUnique[number],"; |
1781 | | // let use a sequence |
1782 | 1.57k | field.m_isSequence=true; |
1783 | 1.57k | } |
1784 | 2.70M | else if (val) |
1785 | 43.6k | f << "isUnique[number]=" << val << ","; |
1786 | 8.11M | for (int j=0; j<2; ++j) { // always 0? |
1787 | 5.40M | val=static_cast<int>(input->readLong(2)); |
1788 | 5.40M | if (val) f << "f" << j << "=" << val << ","; |
1789 | 5.40M | } |
1790 | 2.70M | field.m_firstNumber=static_cast<int>(input->readLong(2)); |
1791 | 2.70M | if (field.m_firstNumber!=1) |
1792 | 2.70M | f << "first[sequence]=" << field.m_firstNumber << ","; |
1793 | 2.70M | val=static_cast<int>(input->readLong(2)); // always 0 |
1794 | 2.70M | if (val) f << "f2=" << val << ","; |
1795 | 2.70M | field.m_incrementNumber=static_cast<int>(input->readLong(2)); |
1796 | 2.70M | if (field.m_incrementNumber!=1) |
1797 | 2.70M | f << "increment[sequence]=" << field.m_incrementNumber << ","; |
1798 | 78.4M | for (int j=0; j < 28; ++j) { // always 0 |
1799 | 75.7M | val=static_cast<int>(input->readLong(2)); |
1800 | 75.7M | if (val) f << "f" << j << "=" << val << ","; |
1801 | 75.7M | } |
1802 | 2.70M | input->seek(pos+dSz, librevenge::RVNG_SEEK_SET); |
1803 | 2.70M | ascii().addPos(pos); |
1804 | 2.70M | ascii().addNote(f.str().c_str()); |
1805 | 2.70M | } |
1806 | 344 | return true; |
1807 | 373 | } |
1808 | | //////////////////////////////////////////////////////////// |
1809 | | // read a list of records corresponding to a field |
1810 | | //////////////////////////////////////////////////////////// |
1811 | | bool GreatWksDBParser::readFieldRecords(GreatWksDBParserInternal::Field &field) |
1812 | 973k | { |
1813 | 973k | if (field.m_recordBlock.isEmpty()) { |
1814 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: can not read a record zone\n")); |
1815 | 0 | return false; |
1816 | 0 | } |
1817 | 973k | auto block=createBlock(field.m_recordBlock); |
1818 | 973k | if (!block) { |
1819 | 925k | MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: can not find the records input\n")); |
1820 | 925k | return false; |
1821 | 925k | } |
1822 | 48.0k | auto &database=m_state->m_database; |
1823 | 48.0k | MWAWInputStreamPtr input = getInput(); |
1824 | 48.0k | libmwaw::DebugStream f; |
1825 | 94.5k | for (size_t z=0; z<block->getNumZones(); ++z) { |
1826 | 46.4k | auto const &zone=block->getZone(z); |
1827 | 46.4k | long pos=zone.m_ptr; |
1828 | 46.4k | f.str(""); |
1829 | 46.4k | f << "Entries(FldRec)[" << field.m_id << "]:type=" << int(field.m_type) << ","; |
1830 | 46.4k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1831 | 46.4k | int const N=zone.m_N; |
1832 | 46.4k | auto const dSz=static_cast<int>(zone.m_dataSize); |
1833 | 46.4k | if (field.m_type==GreatWksDBParserInternal::Field::F_Text) { |
1834 | 1.90k | if (!input->checkPosition(pos+dSz+N*5)) { |
1835 | 252 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: can not read a text zone\n")); |
1836 | 252 | f << "###"; |
1837 | 252 | ascii().addPos(pos); |
1838 | 252 | ascii().addNote(f.str().c_str()); |
1839 | 252 | continue; |
1840 | 252 | } |
1841 | 11.5k | for (int i=0; i<6; ++i) { // always 0? |
1842 | 9.91k | auto val=static_cast<int>(input->readLong(2)); |
1843 | 9.91k | if (val) f << "f" << i << "=" << val << ","; |
1844 | 9.91k | } |
1845 | 1.65k | std::vector<int> positions, rows; |
1846 | | // first read the list of positions |
1847 | 1.65k | input->seek(pos+dSz, librevenge::RVNG_SEEK_SET); |
1848 | 2.61M | for (int i=0; i<N; ++i) |
1849 | 2.61M | positions.push_back(static_cast<int>(input->readULong(1))); |
1850 | | // now read a list of small int: the row number |
1851 | 2.61M | for (int i=0; i<N; ++i) |
1852 | 2.61M | rows.push_back(static_cast<int>(input->readULong(4))); |
1853 | 1.65k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1854 | 1.65k | int actPos=0; |
1855 | 13.5k | for (size_t i=0; i<size_t(N); ++i) { |
1856 | 13.2k | int nextPos= i+1==size_t(N) ? dSz : positions[i+1]; |
1857 | 13.2k | if (actPos!=positions[i] || nextPos>dSz) { |
1858 | 1.35k | MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: can not read a text zone, pb with a position\n")); |
1859 | 1.35k | f << "###"; |
1860 | 1.35k | break; |
1861 | 1.35k | } |
1862 | 11.8k | GreatWksDBParserInternal::Cell cell; |
1863 | 11.8k | cell.setFormat(field.m_format); |
1864 | 11.8k | cell.m_content.m_contentType=MWAWCellContent::C_TEXT; |
1865 | 11.8k | cell.m_content.m_textEntry.setBegin(input->tell()); |
1866 | 11.8k | std::string text(""); |
1867 | 11.8k | bool findEmptyChar=false; |
1868 | 340k | while (actPos<nextPos) { |
1869 | 328k | auto c=char(input->readULong(1)); |
1870 | | // c==0 can indicate an empty field |
1871 | 328k | if (c) text += c; |
1872 | 86.9k | else findEmptyChar=true; |
1873 | 328k | ++actPos; |
1874 | 328k | } |
1875 | 11.8k | cell.m_content.m_textEntry.setEnd(input->tell()-(findEmptyChar ? 1 : 0)); |
1876 | 11.8k | database.addCell(MWAWVec2i(field.m_id, rows[i]), cell); |
1877 | 11.8k | f << "\"" << text << "\":" << rows[i] << ","; |
1878 | 11.8k | } |
1879 | 1.65k | input->seek(pos+12+dSz+5*N, librevenge::RVNG_SEEK_SET); |
1880 | 1.65k | } |
1881 | 44.5k | else if (field.m_type==GreatWksDBParserInternal::Field::F_Number || |
1882 | 44.0k | field.m_type==GreatWksDBParserInternal::Field::F_Date || |
1883 | 43.7k | field.m_type==GreatWksDBParserInternal::Field::F_Time) { |
1884 | 1.77k | if (!input->checkPosition(pos+14*N)) { |
1885 | 790 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: can not read a number zone\n")); |
1886 | 790 | f << "###"; |
1887 | 790 | ascii().addPos(pos); |
1888 | 790 | ascii().addNote(f.str().c_str()); |
1889 | 790 | continue; |
1890 | 790 | } |
1891 | 4.13M | for (int i=0; i<N; ++i) { |
1892 | 4.13M | long fPos=input->tell(); |
1893 | 4.13M | auto row=static_cast<int>(input->readLong(4)); |
1894 | 4.13M | double value; |
1895 | 4.13M | bool isNan; |
1896 | 4.13M | if (!input->readDouble10(value, isNan)) { |
1897 | 198k | static bool first=true; |
1898 | 198k | if (first) { |
1899 | | // can be normal, ie. 7fff403200000000000000 means no value which generates an error |
1900 | 6 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: can not read a number, maybe a undef number\n")); |
1901 | 6 | first=false; |
1902 | 6 | } |
1903 | 198k | f << "#"; |
1904 | 198k | input->seek(fPos+14, librevenge::RVNG_SEEK_SET); |
1905 | 198k | } |
1906 | 3.93M | else { |
1907 | 3.93M | GreatWksDBParserInternal::Cell cell; |
1908 | 3.93M | cell.setFormat(field.m_format); |
1909 | 3.93M | cell.m_content.m_contentType=MWAWCellContent::C_NUMBER; |
1910 | 3.93M | cell.m_content.setValue(value); |
1911 | 3.93M | database.addCell(MWAWVec2i(field.m_id, row), cell); |
1912 | 3.93M | f << value; |
1913 | 3.93M | } |
1914 | 4.13M | f << ":" << row << ","; |
1915 | 4.13M | } |
1916 | 986 | } |
1917 | 42.8k | else { |
1918 | 42.8k | MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldRecords: does not know how to read list for type=%d\n", int(field.m_type))); |
1919 | 42.8k | f << "###"; |
1920 | 42.8k | } |
1921 | 45.4k | ascii().addPos(pos); |
1922 | 45.4k | ascii().addNote(f.str().c_str()); |
1923 | 45.4k | ascii().addPos(input->tell()); |
1924 | 45.4k | ascii().addNote("_"); |
1925 | 45.4k | } |
1926 | 48.0k | return true; |
1927 | 973k | } |
1928 | | |
1929 | | //////////////////////////////////////////////////////////// |
1930 | | // read the links between fields and record zone |
1931 | | //////////////////////////////////////////////////////////// |
1932 | | bool GreatWksDBParser::readFieldLinks(GreatWksDBParserInternal::Field &field) |
1933 | 1.07M | { |
1934 | 1.07M | MWAWInputStreamPtr input = getInput(); |
1935 | 1.07M | if (field.m_linkZone<=0 || !input->checkPosition(field.m_linkZone+32)) { |
1936 | 44.1k | MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldLinks: can not read a link between field to records\n")); |
1937 | 44.1k | return false; |
1938 | 44.1k | } |
1939 | 1.02M | input->seek(field.m_linkZone, librevenge::RVNG_SEEK_SET); |
1940 | 1.02M | libmwaw::DebugStream f; |
1941 | 1.02M | f << "Entries(FldLink)[" << field.m_id << "]:"; |
1942 | 1.02M | auto val=static_cast<int>(input->readLong(2)); |
1943 | 1.02M | if (val!=0xa) f << "#type=" << val << ","; |
1944 | 1.02M | val=static_cast<int>(input->readLong(2)); // always 0 |
1945 | 1.02M | if (val) f << "f0=" << val << ","; |
1946 | 1.02M | val=static_cast<int>(input->readLong(2)); |
1947 | 1.02M | if (val!=0x14) f << "f1=" << val << ","; |
1948 | 1.02M | if (!readBlockHeader(field.m_recordBlock)) { |
1949 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFieldLinks: the zone position seems bad\n")); |
1950 | 0 | f << "###block,"; |
1951 | 0 | } |
1952 | 1.02M | f << "block=" << field.m_recordBlock << ","; |
1953 | 1.02M | ascii().addDelimiter(input->tell(),'|'); |
1954 | 1.02M | ascii().addPos(field.m_linkZone); |
1955 | 1.02M | ascii().addNote(f.str().c_str()); |
1956 | 1.02M | ascii().addPos(field.m_linkZone+32); |
1957 | 1.02M | ascii().addNote("_"); |
1958 | 1.02M | return true; |
1959 | 1.07M | } |
1960 | | |
1961 | | //////////////////////////////////////////////////////////// |
1962 | | // read a list of int |
1963 | | //////////////////////////////////////////////////////////// |
1964 | | bool GreatWksDBParser::readIntList(MWAWEntry const &entry, std::vector<int> &list) |
1965 | 1.21k | { |
1966 | 1.21k | list.resize(0); |
1967 | 1.21k | if (!entry.valid() || entry.length()<10) { |
1968 | 33 | MWAW_DEBUG_MSG(("GreatWksDBParser::readIntList: the entry length seems bad\n")); |
1969 | 33 | return false; |
1970 | 33 | } |
1971 | 1.18k | MWAWInputStreamPtr input = getInput(); |
1972 | 1.18k | input->seek(entry.begin()+6, librevenge::RVNG_SEEK_SET); |
1973 | 1.18k | auto N=static_cast<int>(input->readULong(2)); |
1974 | 1.18k | auto dSz=static_cast<int>(input->readULong(2)); |
1975 | 1.18k | if (dSz!=2 || entry.length()!=10+N*dSz) { |
1976 | 257 | MWAW_DEBUG_MSG(("GreatWksDBParser::readIntList: the N or dSz value seems bad\n")); |
1977 | 257 | return false; |
1978 | 257 | } |
1979 | | |
1980 | 926 | entry.setParsed(true); |
1981 | 926 | ascii().addPos(entry.end()); |
1982 | 926 | ascii().addNote("_"); |
1983 | | |
1984 | 992 | for (int i=0; i<N; ++i) |
1985 | 66 | list.push_back(static_cast<int>(input->readLong(2))); |
1986 | | |
1987 | 926 | return true; |
1988 | 1.18k | } |
1989 | | |
1990 | | //////////////////////////////////////////////////////////// |
1991 | | // read a list of form |
1992 | | //////////////////////////////////////////////////////////// |
1993 | | bool GreatWksDBParser::readFormLinks(MWAWEntry const &entry) |
1994 | 841 | { |
1995 | 841 | if (!entry.valid() || entry.length()<10) { |
1996 | 47 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFormLinks: the entry length seems bad\n")); |
1997 | 47 | return false; |
1998 | 47 | } |
1999 | 794 | entry.setParsed(true); |
2000 | 794 | ascii().addPos(entry.end()); |
2001 | 794 | ascii().addNote("_"); |
2002 | 794 | MWAWInputStreamPtr input = getInput(); |
2003 | 794 | libmwaw::DebugStream f; |
2004 | 794 | f << "Entries(" << entry.name() << "):"; |
2005 | 794 | input->seek(entry.begin()+6, librevenge::RVNG_SEEK_SET); |
2006 | 794 | auto N=static_cast<int>(input->readULong(2)); |
2007 | 794 | f << "N=" << N << ","; |
2008 | 794 | auto dSz=static_cast<int>(input->readULong(2)); |
2009 | 794 | if (entry.length()!=10+long(N)*dSz || dSz<4) { |
2010 | 314 | MWAW_DEBUG_MSG(("GreatWksDBParser::readFormLinks: the number of data seems bad\n")); |
2011 | 314 | f << "###"; |
2012 | 314 | ascii().addPos(entry.begin()); |
2013 | 314 | ascii().addNote(f.str().c_str()); |
2014 | 314 | return false; |
2015 | 314 | } |
2016 | 480 | ascii().addPos(entry.begin()); |
2017 | 480 | ascii().addNote(f.str().c_str()); |
2018 | 480 | std::vector<MWAWEntry> listZones; |
2019 | 1.43k | for (int i=0; i<N; ++i) { |
2020 | 956 | long pos=input->tell(); |
2021 | 956 | f.str(""); |
2022 | 956 | f << entry.name() << "-" << i << ":"; |
2023 | 956 | auto val=static_cast<int>(input->readLong(2)); // always 0? |
2024 | 956 | if (val) f << "f0=" << val << ","; |
2025 | 956 | auto ptr=static_cast<int>(input->readULong(2)); |
2026 | 956 | if (ptr) { |
2027 | 953 | MWAWEntry zone; |
2028 | 953 | zone.setBegin(ptr); |
2029 | 953 | if (checkSmallZone(zone)) |
2030 | 942 | listZones.push_back(zone); |
2031 | 11 | else |
2032 | 11 | f << "###"; |
2033 | 953 | f << "ptr" << i << "=" << std::hex << ptr << std::dec << ","; |
2034 | 953 | } |
2035 | 956 | input->seek(pos+dSz, librevenge::RVNG_SEEK_SET); |
2036 | 956 | ascii().addPos(pos); |
2037 | 956 | ascii().addNote(f.str().c_str()); |
2038 | 956 | } |
2039 | 480 | for (auto const &zone : listZones) |
2040 | 942 | readForm(zone); |
2041 | 480 | return true; |
2042 | 794 | } |
2043 | | |
2044 | | //////////////////////////////////////////////////////////// |
2045 | | // read zone 2 link zone |
2046 | | //////////////////////////////////////////////////////////// |
2047 | | bool GreatWksDBParser::readForm(MWAWEntry const &entry) |
2048 | 942 | { |
2049 | 942 | int const vers=version(); |
2050 | | /* not really sure what may be the header size, ie. |
2051 | | in v1: 264 seems ok, |
2052 | | in v2: 276 seems ok, |
2053 | | but some v1 file converted in v2 seems to keep intact the v1 structure :-~ |
2054 | | |
2055 | | This must be fixed if we want to export the form... |
2056 | | */ |
2057 | 942 | int const headerSize=vers==1 ? 264 : 276; |
2058 | 942 | MWAWInputStreamPtr input = getInput(); |
2059 | 942 | long pos=entry.begin(); |
2060 | 942 | libmwaw::DebugStream f; |
2061 | 942 | f << "Entries(" << entry.name() << "):"; |
2062 | 942 | ascii().addPos(entry.end()); |
2063 | 942 | ascii().addNote("_"); |
2064 | 942 | if (entry.id()!=9 || entry.length() < 6+headerSize) { |
2065 | 484 | f << "###"; |
2066 | 484 | MWAW_DEBUG_MSG(("GreatWksDBParser::readForm: the entry seems bad\n")); |
2067 | 484 | ascii().addPos(pos); |
2068 | 484 | ascii().addNote(f.str().c_str()); |
2069 | 484 | return false; |
2070 | 484 | } |
2071 | 458 | input->seek(pos+6, librevenge::RVNG_SEEK_SET); |
2072 | | // a big number 5bafac : an id? |
2073 | 458 | f << "f0=" << std::hex << input->readULong(4) << std::dec << ","; |
2074 | | // small number between 1 and 4 |
2075 | 458 | f << "id=" << input->readLong(2) << ","; |
2076 | 458 | auto sSz=static_cast<int>(input->readULong(1)); |
2077 | 458 | if (sSz>32) { |
2078 | 5 | f << "###"; |
2079 | 5 | MWAW_DEBUG_MSG(("GreatWksDBParser::readForm: the string size seems bad\n")); |
2080 | 5 | ascii().addPos(pos); |
2081 | 5 | ascii().addNote(f.str().c_str()); |
2082 | 5 | return false; |
2083 | 5 | } |
2084 | 453 | std::string text(""); |
2085 | 5.76k | for (int i=0; i<sSz; ++i) text+=char(input->readULong(1)); |
2086 | 453 | f << text << ","; |
2087 | 453 | input->seek(pos+44, librevenge::RVNG_SEEK_SET); |
2088 | 453 | ascii().addDelimiter(input->tell(),'|'); |
2089 | 453 | ascii().addPos(pos); |
2090 | 453 | ascii().addNote(f.str().c_str()); |
2091 | | |
2092 | 453 | input->seek(pos+headerSize, librevenge::RVNG_SEEK_SET); |
2093 | 453 | pos=input->tell(); |
2094 | 453 | if (!m_document->getGraphParser()->readPageFrames()) |
2095 | 306 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
2096 | 453 | pos=input->tell(); |
2097 | 453 | if (pos!=entry.end()) { |
2098 | 364 | ascii().addPos(pos); |
2099 | 364 | ascii().addNote("Form:end"); |
2100 | 364 | } |
2101 | 453 | return true; |
2102 | 458 | } |
2103 | | |
2104 | | //////////////////////////////////////////////////////////// |
2105 | | // read a unknown zone of zone |
2106 | | //////////////////////////////////////////////////////////// |
2107 | | bool GreatWksDBParser::readZone12(MWAWEntry const &entry) |
2108 | 499 | { |
2109 | 499 | int const vers=version(); |
2110 | 499 | if (!entry.valid() || entry.length()!=10+2*vers) { |
2111 | 118 | MWAW_DEBUG_MSG(("GreatWksDBParser::readZone12: the entry length seems bad\n")); |
2112 | 118 | return false; |
2113 | 118 | } |
2114 | 381 | MWAWInputStreamPtr input = getInput(); |
2115 | 381 | libmwaw::DebugStream f; |
2116 | 381 | f << "Entries(" << entry.name() << "):"; |
2117 | 381 | input->seek(entry.begin()+6, librevenge::RVNG_SEEK_SET); |
2118 | | |
2119 | 381 | entry.setParsed(true); |
2120 | 381 | ascii().addPos(entry.end()); |
2121 | 381 | ascii().addNote("_"); |
2122 | | |
2123 | 381 | int val; |
2124 | 1.90k | for (int i=0; i<4; ++i) { // f0=f1=0, f2=f3=1 |
2125 | 1.52k | val=static_cast<int>(input->readLong(1)); |
2126 | 1.52k | if (val) f << "f" << i << "=" << val << ","; |
2127 | 1.52k | } |
2128 | 381 | if (vers==2) // b0|2a |
2129 | 37 | f << "g0=" << input->readLong(2) << ","; |
2130 | 381 | val=static_cast<int>(input->readLong(2)); |
2131 | 381 | if (val) // 66|67|7a |
2132 | 379 | f << "g1=" << val << ","; |
2133 | 381 | ascii().addPos(entry.begin()); |
2134 | 381 | ascii().addNote(f.str().c_str()); |
2135 | | |
2136 | 381 | return true; |
2137 | 499 | } |
2138 | | |
2139 | | //////////////////////////////////////////////////////////// |
2140 | | // read unknown small zone |
2141 | | //////////////////////////////////////////////////////////// |
2142 | | bool GreatWksDBParser::readSmallZone(MWAWEntry const &entry) |
2143 | 31.0k | { |
2144 | 31.0k | if (!entry.valid() || entry.length()<10) { |
2145 | 2.62k | MWAW_DEBUG_MSG(("GreatWksDBParser::readSmallZone: the entry length seems bad\n")); |
2146 | 2.62k | return false; |
2147 | 2.62k | } |
2148 | 28.4k | entry.setParsed(true); |
2149 | 28.4k | ascii().addPos(entry.end()); |
2150 | 28.4k | ascii().addNote("_"); |
2151 | 28.4k | MWAWInputStreamPtr input = getInput(); |
2152 | 28.4k | libmwaw::DebugStream f; |
2153 | 28.4k | f << "Entries(" << entry.name() << "):"; |
2154 | 28.4k | input->seek(entry.begin()+6, librevenge::RVNG_SEEK_SET); |
2155 | 28.4k | auto N=static_cast<int>(input->readULong(2)); |
2156 | 28.4k | f << "N=" << N << ","; |
2157 | 28.4k | long dSz=static_cast<int>(input->readULong(2)); |
2158 | 28.4k | if (entry.length()!=10+long(N)*dSz) { |
2159 | 27.4k | MWAW_DEBUG_MSG(("GreatWksDBParser::readSmallZone: the number of data seems bad\n")); |
2160 | 27.4k | f << "###"; |
2161 | 27.4k | ascii().addPos(entry.begin()); |
2162 | 27.4k | ascii().addNote(f.str().c_str()); |
2163 | 27.4k | return false; |
2164 | 27.4k | } |
2165 | 1.01k | ascii().addPos(entry.begin()); |
2166 | 1.01k | ascii().addNote(f.str().c_str()); |
2167 | 201k | for (int i=0; i<N; ++i) { |
2168 | 200k | long pos=input->tell(); |
2169 | 200k | f.str(""); |
2170 | 200k | f << entry.name() << "-" << i << ":"; |
2171 | 200k | ascii().addPos(pos); |
2172 | 200k | ascii().addNote(f.str().c_str()); |
2173 | 200k | input->seek(pos+dSz, librevenge::RVNG_SEEK_SET); |
2174 | 200k | } |
2175 | | |
2176 | 1.01k | return true; |
2177 | 28.4k | } |
2178 | | |
2179 | | //////////////////////////////////////////////////////////// |
2180 | | //////////////////////////////////////////////////////////// |
2181 | | bool GreatWksDBParser::sendDatabase() |
2182 | 1.77k | { |
2183 | 1.77k | MWAWSpreadsheetListenerPtr listener=getSpreadsheetListener(); |
2184 | 1.77k | if (!listener) { |
2185 | 0 | MWAW_DEBUG_MSG(("GreatWksDBParser::sendDatabase: I can not find the listener\n")); |
2186 | 0 | return false; |
2187 | 0 | } |
2188 | 1.77k | MWAWInputStreamPtr input=getInput(); |
2189 | 1.77k | auto const &database=m_state->m_database; |
2190 | 1.77k | auto const &fields = database.m_fieldList; |
2191 | 1.77k | size_t numFields=fields.size(); |
2192 | | // fixme: use first layout colWidth here |
2193 | 1.77k | listener->openSheet(std::vector<float>(1,76), librevenge::RVNG_POINT, std::vector<int>(1,static_cast<int>(numFields)), "Sheet0"); |
2194 | 1.77k | int r=-1, numRows=static_cast<int>(database.m_rowCellsMap.size()); |
2195 | 127k | for (auto const &rIt : database.m_rowCellsMap) { |
2196 | 127k | auto const &row=rIt.second; |
2197 | 127k | listener->openSheetRow(12, librevenge::RVNG_POINT); |
2198 | 127k | ++r; |
2199 | 197M | for (size_t c=0; c< numFields; ++c) { |
2200 | 197M | auto const &field=fields[c]; |
2201 | 197M | GreatWksDBParserInternal::Cell cell; |
2202 | 197M | if (c<row.size()) cell=row[c]; |
2203 | 197M | field.updateCell(int(r), numRows, cell); |
2204 | 197M | if (cell.isEmpty()) continue; |
2205 | | |
2206 | 621k | auto const &content=cell.m_content; |
2207 | 621k | cell.setPosition(MWAWVec2i(int(c),r)); |
2208 | 621k | listener->openSheetCell(cell, content); |
2209 | 621k | if (field.m_type==GreatWksDBParserInternal::Field::F_Text && content.m_textEntry.valid()) { |
2210 | 1.48k | input->seek(content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET); |
2211 | 35.0k | while (!input->isEnd() && input->tell()<content.m_textEntry.end()) { |
2212 | 33.8k | auto ch=static_cast<unsigned char>(input->readULong(1)); |
2213 | 33.8k | if (ch==0xd) |
2214 | 24 | listener->insertEOL(); |
2215 | 33.8k | else if (ch<30) { |
2216 | 257 | MWAW_DEBUG_MSG(("GreatWksDBParser::sendDatabase: find some odd character\n")); |
2217 | 257 | break; |
2218 | 257 | } |
2219 | 33.5k | else |
2220 | 33.5k | listener->insertCharacter(ch); |
2221 | 33.8k | } |
2222 | 1.48k | } |
2223 | 620k | else if (field.m_type==GreatWksDBParserInternal::Field::F_Memo && content.m_textEntry.valid()) |
2224 | 45.3k | m_document->getTextParser()->sendTextbox(content.m_textEntry, listener); |
2225 | 574k | else if (field.m_type==GreatWksDBParserInternal::Field::F_Picture && cell.m_pictEntry.valid()) { |
2226 | 1.44k | librevenge::RVNGBinaryData pict; |
2227 | 1.44k | input->seek(cell.m_pictEntry.begin(),librevenge::RVNG_SEEK_SET); |
2228 | 1.44k | input->readDataBlock(cell.m_pictEntry.length(), pict); |
2229 | | |
2230 | 1.44k | std::string cellName("Sheet0."); |
2231 | 1.44k | cellName+=MWAWCell::getBasicCellName(cell.position()+MWAWVec2i(1,1)); |
2232 | 1.44k | MWAWPosition position(MWAWVec2f(0,0), MWAWVec2f(76,12), librevenge::RVNG_POINT); // changeme |
2233 | 1.44k | position.setAnchorToCell(librevenge::RVNGString(cellName.c_str())); |
2234 | 1.44k | listener->insertPicture(position, MWAWEmbeddedObject(pict)); |
2235 | 1.44k | } |
2236 | 621k | listener->closeSheetCell(); |
2237 | 621k | } |
2238 | 127k | listener->closeSheetRow(); |
2239 | 127k | } |
2240 | 1.77k | listener->closeSheet(); |
2241 | 1.77k | return true; |
2242 | 1.77k | } |
2243 | | |
2244 | | //////////////////////////////////////////////////////////// |
2245 | | // read the header |
2246 | | //////////////////////////////////////////////////////////// |
2247 | | bool GreatWksDBParser::checkHeader(MWAWHeader *header, bool strict) |
2248 | 17.0k | { |
2249 | 17.0k | MWAWInputStreamPtr input = getInput(); |
2250 | 17.0k | *m_state = GreatWksDBParserInternal::State(); |
2251 | 17.0k | if (!m_document->checkHeader(header, strict) || !input) return false; |
2252 | 16.6k | if (getParserState()->m_kind!=MWAWDocument::MWAW_K_DATABASE) |
2253 | 0 | return false; |
2254 | 16.6k | if (!strict) return true; |
2255 | | |
2256 | | // let check that the 3 header are defined |
2257 | 6.27k | input->seek(16, librevenge::RVNG_SEEK_SET); |
2258 | 19.4k | for (int i=0; i<3; ++i) { |
2259 | 15.2k | GreatWksDBParserInternal::BlockHeader block; |
2260 | 15.2k | if (!readBlockHeader(block) || block.m_ptr[0]==0 || (block.m_ptr[0]&0xFF)) return false; |
2261 | 13.1k | input->seek(8, librevenge::RVNG_SEEK_CUR); |
2262 | 13.1k | } |
2263 | 4.21k | return true; |
2264 | 6.27k | } |
2265 | | |
2266 | | // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: |