/src/libmwaw/src/lib/ClarisWksDbaseContent.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 <time.h> |
35 | | |
36 | | #include <cmath> |
37 | | #include <cstring> |
38 | | #include <ctime> |
39 | | #include <iomanip> |
40 | | #include <iostream> |
41 | | #include <set> |
42 | | #include <sstream> |
43 | | #include <limits> |
44 | | |
45 | | #include <librevenge/librevenge.h> |
46 | | |
47 | | #include "MWAWDebug.hxx" |
48 | | #include "MWAWHeader.hxx" |
49 | | #include "MWAWInputStream.hxx" |
50 | | #include "MWAWListener.hxx" |
51 | | #include "MWAWParagraph.hxx" |
52 | | #include "MWAWParser.hxx" |
53 | | |
54 | | #include "ClarisWksDocument.hxx" |
55 | | #include "ClarisWksStyleManager.hxx" |
56 | | |
57 | | #include "ClarisWksDbaseContent.hxx" |
58 | | |
59 | | ClarisWksDbaseContent::ClarisWksDbaseContent(ClarisWksDocument &document, bool spreadsheet) |
60 | 586k | : m_version(0) |
61 | 586k | , m_isSpreadsheet(spreadsheet) |
62 | 586k | , m_document(document) |
63 | 586k | , m_parserState(document.m_parserState) |
64 | 586k | , m_idColumnMap() |
65 | 586k | , m_positionSet() |
66 | 586k | , m_dbFormatList() |
67 | 586k | { |
68 | 586k | if (!m_parserState || !m_parserState->m_header) { |
69 | 0 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::ClarisWksDbaseContent: can not find the file header\n")); |
70 | 0 | return; |
71 | 0 | } |
72 | 586k | m_version = m_parserState->m_header->getMajorVersion(); |
73 | 586k | } |
74 | | |
75 | | ClarisWksDbaseContent::~ClarisWksDbaseContent() |
76 | 586k | { |
77 | 586k | } |
78 | | |
79 | | void ClarisWksDbaseContent::setDatabaseFormats(std::vector<ClarisWksStyleManager::CellFormat> const &format) |
80 | 1.96k | { |
81 | 1.96k | if (m_isSpreadsheet) { |
82 | 0 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::setDatabaseFormats: called with spreadsheet\n")); |
83 | 0 | return; |
84 | 0 | } |
85 | 1.96k | m_dbFormatList=format; |
86 | 1.96k | } |
87 | | |
88 | | bool ClarisWksDbaseContent::getExtrema(MWAWVec2i &min, MWAWVec2i &max) const |
89 | 154k | { |
90 | 154k | if (m_idColumnMap.empty()) |
91 | 15.0k | return false; |
92 | 139k | bool first=true; |
93 | 236k | for (auto const &it : m_idColumnMap) { |
94 | 236k | int col=it.first; |
95 | 236k | Column const &column=it.second; |
96 | 236k | if (column.m_idRecordMap.empty()) |
97 | 0 | continue; |
98 | 236k | max[0]=col; |
99 | 10.4M | for (auto const &rIt : column.m_idRecordMap) { |
100 | 10.4M | int row=rIt.first; |
101 | 10.4M | if (first) { |
102 | 139k | min[0]=col; |
103 | 139k | min[1]=max[1]=row; |
104 | 139k | first=false; |
105 | 139k | } |
106 | 10.2M | else if (row < min[1]) |
107 | 4.66k | min[1]=row; |
108 | 10.2M | else if (row > max[1]) |
109 | 5.95M | max[1]=row; |
110 | 10.4M | } |
111 | 236k | } |
112 | 139k | return !first; |
113 | 154k | } |
114 | | |
115 | | void ClarisWksDbaseContent::updateCellPositionsSet() const |
116 | 5.70M | { |
117 | 5.70M | if (!m_positionSet.empty() || m_idColumnMap.empty()) |
118 | 5.69M | return; |
119 | 21.1k | for (auto const &it : m_idColumnMap) { |
120 | 21.1k | int col=it.first; |
121 | 21.1k | Column const &column=it.second; |
122 | 952k | for (auto const &rIt : column.m_idRecordMap) { |
123 | 952k | int row=rIt.first; |
124 | 952k | m_positionSet.insert(MWAWVec2i(col,row)); |
125 | 952k | } |
126 | 21.1k | } |
127 | 12.0k | } |
128 | | |
129 | | bool ClarisWksDbaseContent::getRecordList(std::vector<int> &list) const |
130 | 113k | { |
131 | 113k | list.resize(0); |
132 | 113k | if (m_idColumnMap.empty()) |
133 | 0 | return false; |
134 | 113k | std::set<int> set; |
135 | 189k | for (auto const &it : m_idColumnMap) { |
136 | 189k | Column const &column=it.second; |
137 | 189k | for (auto const &rIt : column.m_idRecordMap) |
138 | 8.73M | set.insert(rIt.first); |
139 | 189k | } |
140 | 113k | if (set.empty()) |
141 | 0 | return false; |
142 | 113k | list = std::vector<int>(set.begin(), set.end()); |
143 | | |
144 | 113k | return true; |
145 | 113k | } |
146 | | |
147 | | bool ClarisWksDbaseContent::getColumnList(int row, std::vector<int> &list) const |
148 | 5.70M | { |
149 | 5.70M | list.resize(0); |
150 | 5.70M | if (m_idColumnMap.empty()) |
151 | 0 | return false; |
152 | | |
153 | 5.70M | updateCellPositionsSet(); |
154 | 5.70M | auto rIt=m_positionSet.lower_bound(MWAWVec2i(-1,row)); |
155 | 14.3M | while (rIt!=m_positionSet.end()) { |
156 | 14.2M | MWAWVec2i const &pos=*(rIt++); |
157 | 14.2M | if (pos[1]!=row) |
158 | 5.59M | break; |
159 | 8.68M | list.push_back(pos[0]); |
160 | 8.68M | } |
161 | | |
162 | 5.70M | return !list.empty(); |
163 | 5.70M | } |
164 | | |
165 | | bool ClarisWksDbaseContent::readContent() |
166 | 531k | { |
167 | 531k | if (!m_parserState) return false; |
168 | 531k | MWAWInputStreamPtr &input= m_parserState->m_input; |
169 | 531k | long pos = input->tell(); |
170 | 531k | auto sz = long(input->readULong(4)); |
171 | | /** ARGHH: this zone is almost the only zone which count the header in sz ... */ |
172 | 531k | long endPos = pos+sz; |
173 | 531k | std::string zoneName(m_isSpreadsheet ? "spread" : "dbase"); |
174 | 531k | input->seek(endPos, librevenge::RVNG_SEEK_SET); |
175 | 531k | if (long(input->tell()) != endPos || sz < 6) { |
176 | 90.2k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
177 | 90.2k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readContent: file is too short\n")); |
178 | 90.2k | return false; |
179 | 90.2k | } |
180 | | |
181 | 441k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
182 | 441k | libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile; |
183 | 441k | libmwaw::DebugStream f; |
184 | 441k | f << "Entries(DBHeader)[" << zoneName << "]:"; |
185 | 441k | auto N = static_cast<int>(input->readULong(2)); |
186 | 441k | f << "N=" << N << ","; |
187 | 441k | ascFile.addPos(pos); |
188 | 441k | ascFile.addNote(f.str().c_str()); |
189 | | |
190 | 441k | input->pushLimit(endPos); |
191 | 441k | readColumnList(); |
192 | | |
193 | 441k | if (input->tell() == endPos) { |
194 | 140k | input->popLimit(); |
195 | 140k | return true; |
196 | 140k | } |
197 | | /* can we have more than one sheet ? If so, going into the while |
198 | | loop may be ok, we will not read the data...*/ |
199 | 300k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readContent: find extra data\n")); |
200 | 300k | bool ok=true; |
201 | 2.02M | while (input->tell() < endPos) { |
202 | 1.91M | pos = input->tell(); |
203 | 1.91M | sz = long(input->readULong(4)); |
204 | 1.91M | long zoneEnd=pos+4+sz; |
205 | 1.91M | if (zoneEnd > endPos || (sz && sz < 12)) { |
206 | 186k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
207 | 186k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readContent: find a odd content field\n")); |
208 | 186k | ok=false; |
209 | 186k | break; |
210 | 186k | } |
211 | 1.72M | if (!sz) { |
212 | 1.61M | ascFile.addPos(pos); |
213 | 1.61M | ascFile.addNote("Nop"); |
214 | 1.61M | continue; |
215 | 1.61M | } |
216 | 109k | std::string name(""); |
217 | 548k | for (int i = 0; i < 4; i++) |
218 | 438k | name+=char(input->readULong(1)); |
219 | 109k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readContent: find unexpected content field\n")); |
220 | 109k | f << "DBHeader[" << zoneName << "]:###" << name; |
221 | 109k | ascFile.addDelimiter(input->tell(),'|'); |
222 | 109k | ascFile.addPos(pos); |
223 | 109k | ascFile.addNote(f.str().c_str()); |
224 | 109k | input->seek(zoneEnd, librevenge::RVNG_SEEK_SET); |
225 | 109k | } |
226 | 300k | input->popLimit(); |
227 | 300k | return ok; |
228 | 441k | } |
229 | | |
230 | | bool ClarisWksDbaseContent::readColumnList() |
231 | 441k | { |
232 | 441k | if (!m_parserState) return false; |
233 | 441k | MWAWInputStreamPtr &input= m_parserState->m_input; |
234 | 441k | long pos=input->tell(); |
235 | 441k | long sz=input->readLong(4); |
236 | 441k | std::string hName(""); |
237 | 2.20M | for (int i=0; i < 4; ++i) hName+=char(input->readULong(1)); |
238 | 441k | if (sz!=0x408 || hName!="CTAB" || !input->checkPosition(pos+4+sz)) { |
239 | 238k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readCOLM: the entry seems bad\n")); |
240 | 238k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
241 | 238k | return false; |
242 | 238k | } |
243 | | |
244 | 202k | libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile; |
245 | 202k | libmwaw::DebugStream f; |
246 | 202k | if (m_isSpreadsheet) |
247 | 126k | f << "Entries(DBCTAB)[spread]:"; |
248 | 75.5k | else |
249 | 75.5k | f << "Entries(DBCTAB)[dbase]:"; |
250 | 202k | auto N=static_cast<int>(input->readLong(2)); |
251 | 202k | if (N) f << "Ncols=" << N << ","; |
252 | 202k | auto val=static_cast<int>(input->readLong(2)); |
253 | 202k | if (val) f << "Nrows=" << val << ","; |
254 | 202k | if (N<0 || N>255) { |
255 | 1.71k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readColumnList: the entries number of elements seems bad\n")); |
256 | 1.71k | f << "####"; |
257 | 1.71k | ascFile.addPos(pos); |
258 | 1.71k | ascFile.addNote(f.str().c_str()); |
259 | 1.71k | return false; |
260 | 1.71k | } |
261 | 200k | f << "ptr=["; |
262 | 200k | long ptr; |
263 | 200k | std::vector<long> listIds; |
264 | 5.38M | for (int i=0; i <= N; i++) { |
265 | 5.18M | ptr=long(input->readULong(4)); |
266 | 5.18M | listIds.push_back(ptr); |
267 | 5.18M | if (ptr) |
268 | 1.43M | f << std::hex << ptr << std::dec << ","; |
269 | 3.74M | else |
270 | 3.74M | f << "_,"; |
271 | 5.18M | } |
272 | 200k | f << "],"; |
273 | 46.3M | for (int i=N+1; i<256; i++) { // always 0 |
274 | 46.1M | ptr=long(input->readULong(4)); |
275 | 46.1M | if (!ptr) continue; |
276 | 4.09M | static bool first=true; |
277 | 4.09M | if (first) { |
278 | 37 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readColumnList: find some extra values\n")); |
279 | 37 | first=false; |
280 | 37 | } |
281 | 4.09M | f << "#g" << i << "=" << ptr << ","; |
282 | 4.09M | } |
283 | 200k | ascFile.addPos(pos); |
284 | 200k | ascFile.addNote(f.str().c_str()); |
285 | 580k | for (size_t c=0; c<listIds.size(); c++) { |
286 | 551k | if (!listIds[c]) continue; |
287 | 402k | pos=input->tell(); |
288 | 402k | if (readColumn(int(c))) |
289 | 231k | continue; |
290 | 171k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
291 | 171k | return false; |
292 | 402k | } |
293 | 29.1k | return true; |
294 | 200k | } |
295 | | |
296 | | bool ClarisWksDbaseContent::readColumn(int c) |
297 | 402k | { |
298 | 402k | if (!m_parserState) return false; |
299 | 402k | MWAWInputStreamPtr &input= m_parserState->m_input; |
300 | 402k | long pos=input->tell(); |
301 | 402k | long sz=input->readLong(4); |
302 | 402k | std::string hName(""); |
303 | 2.01M | for (int i=0; i < 4; ++i) hName+=char(input->readULong(1)); |
304 | 402k | int cPos[2]; |
305 | 402k | for (int &i : cPos) |
306 | 805k | i=static_cast<int>(input->readLong(2)); |
307 | 402k | if (sz!=8+4*(cPos[1]-cPos[0]+1) || hName!="COLM" || !input->checkPosition(pos+4+sz)) { |
308 | 139k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readCOLM: the entry seems bad\n")); |
309 | 139k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
310 | 139k | return false; |
311 | 139k | } |
312 | 263k | libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile; |
313 | 263k | libmwaw::DebugStream f; |
314 | 263k | if (m_isSpreadsheet) |
315 | 160k | f << "Entries(DBCOLM)[spread]:"; |
316 | 102k | else |
317 | 102k | f << "Entries(DBCOLM)[dbase]:"; |
318 | 263k | f << "ptr[" << cPos[0] << "<=>" << cPos[1] << "]=["; |
319 | 263k | std::vector<long> listIds; |
320 | 263k | listIds.resize(size_t(cPos[0]),0); |
321 | 526k | for (int i=cPos[0]; i <= cPos[1]; i++) { |
322 | 263k | auto ptr=long(input->readULong(4)); |
323 | 263k | listIds.push_back(ptr); |
324 | 263k | if (ptr) |
325 | 262k | f << std::hex << ptr << std::dec << ","; |
326 | 420 | else |
327 | 420 | f << "_,"; |
328 | 263k | } |
329 | 263k | f << "],"; |
330 | 263k | ascFile.addPos(pos); |
331 | 263k | ascFile.addNote(f.str().c_str()); |
332 | | |
333 | | // now read the chnk |
334 | 263k | ClarisWksDbaseContent::Column col; |
335 | 263k | bool ok=true; |
336 | 1.76M | for (size_t i=0; i < listIds.size(); ++i) { |
337 | 1.53M | pos=input->tell(); |
338 | 1.53M | if (!listIds[i] || readRecordList(MWAWVec2i(c,64*int(i)), col)) |
339 | 1.49M | continue; |
340 | 31.5k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
341 | 31.5k | ok=false; |
342 | 31.5k | break; |
343 | 1.53M | } |
344 | 263k | if (!col.m_idRecordMap.empty()) |
345 | 230k | m_idColumnMap[c]=col; |
346 | 263k | return ok; |
347 | 402k | } |
348 | | |
349 | | bool ClarisWksDbaseContent::readRecordList(MWAWVec2i const &where, Column &col) |
350 | 262k | { |
351 | 262k | if (!m_parserState) return false; |
352 | 262k | MWAWInputStreamPtr &input= m_parserState->m_input; |
353 | 262k | long pos=input->tell(); |
354 | 262k | long sz=input->readLong(4); |
355 | 262k | long endPos=pos+4+sz; |
356 | 262k | std::string hName(""); |
357 | 1.31M | for (int i=0; i < 4; ++i) hName+=char(input->readULong(1)); |
358 | 262k | auto N=static_cast<int>(input->readULong(2)); |
359 | 262k | if (sz<6+134 || hName!="CHNK" || !input->checkPosition(pos+4+sz) || N>0x40) { |
360 | 19.1k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordList: the entry seems bad\n")); |
361 | 19.1k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
362 | 19.1k | return false; |
363 | 19.1k | } |
364 | | |
365 | 243k | libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile; |
366 | 243k | libmwaw::DebugStream f; |
367 | 243k | std::string zoneName(m_isSpreadsheet ? "spread" : "dbase"); |
368 | 243k | f << "Entries(DBCHNK)[" << zoneName << "]:N=" << N << ","; |
369 | 243k | auto type=static_cast<int>(input->readULong(2)); // often 400, 800... |
370 | 243k | f << "type=" << std::hex << type << std::dec << ","; |
371 | 243k | int dim[2]; // checkme |
372 | 486k | for (auto &d : dim) d=static_cast<int>(input->readLong(2)); |
373 | 243k | f << "dim=" << dim[0] << "x" << dim[1] << ","; |
374 | | |
375 | 243k | f << "depl=["; |
376 | 243k | std::vector<long> ptrLists(64,0); |
377 | 243k | int find=0; |
378 | 15.3M | for (size_t i=0; i < 64; ++i) { |
379 | 15.1M | auto depl=long(input->readLong(2)); |
380 | 15.1M | if (depl==0) { |
381 | 6.70M | f << "_,"; |
382 | 6.70M | continue; |
383 | 6.70M | } |
384 | 8.43M | find++; |
385 | 8.43M | long fPos=pos+4+depl; |
386 | 8.43M | if (fPos > endPos) { |
387 | 12.4k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordList: the %d ptr seems bad\n", static_cast<int>(i))); |
388 | 12.4k | f << "###"; |
389 | 12.4k | ascFile.addPos(pos); |
390 | 12.4k | ascFile.addNote(f.str().c_str()); |
391 | 12.4k | return false; |
392 | 12.4k | } |
393 | 8.42M | f << std::hex << depl << std::dec << ","; |
394 | 8.42M | ptrLists[i]=fPos; |
395 | 8.42M | } |
396 | 230k | f << "],"; |
397 | 230k | if (find!=N) { |
398 | 209k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordList: the number of find data seems bad\n")); |
399 | 209k | f << "###find=" << find << "!=" << N << ","; |
400 | 209k | } |
401 | 230k | ascFile.addPos(pos); |
402 | 230k | ascFile.addNote(f.str().c_str()); |
403 | | |
404 | 15.0M | for (size_t i=0; i<64; ++i) { |
405 | 14.7M | if (!ptrLists[i]) continue; |
406 | 8.20M | Record record; |
407 | 8.20M | MWAWVec2i wh(where[0],where[1]+static_cast<int>(i)); |
408 | 8.20M | if ((m_isSpreadsheet && readRecordSS(wh, ptrLists[i], record)) || |
409 | 7.17M | (!m_isSpreadsheet && readRecordDB(wh, ptrLists[i], record))) { |
410 | 7.17M | col.m_idRecordMap[wh[1]]=record; |
411 | 7.17M | continue; |
412 | 7.17M | } |
413 | | |
414 | 1.02M | f.str(""); |
415 | 1.02M | f << "DBCHNK[" << zoneName << wh << "]:#"; |
416 | 1.02M | input->seek(ptrLists[i], librevenge::RVNG_SEEK_SET); |
417 | 1.02M | auto fType=static_cast<int>(input->readULong(1)); |
418 | 1.02M | f << "type=" << std::hex << fType << std::dec << ","; |
419 | 1.02M | ascFile.addPos(ptrLists[i]); |
420 | 1.02M | ascFile.addNote(f.str().c_str()); |
421 | 1.02M | col.m_idRecordMap[wh[1]]=record; |
422 | 1.02M | } |
423 | | |
424 | 230k | input->seek(endPos, librevenge::RVNG_SEEK_SET); |
425 | 230k | return true; |
426 | 243k | } |
427 | | |
428 | | bool ClarisWksDbaseContent::readRecordSSV1(MWAWVec2i const &id, long pos, ClarisWksDbaseContent::Record &record) |
429 | 3.89M | { |
430 | 3.89M | record=ClarisWksDbaseContent::Record(); |
431 | 3.89M | libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile; |
432 | 3.89M | libmwaw::DebugStream f; |
433 | 3.89M | f << "DBCHNK[spread" << id << "]:"; |
434 | 3.89M | MWAWInputStreamPtr &input= m_parserState->m_input; |
435 | 3.89M | input->seek(pos, librevenge::RVNG_SEEK_SET); |
436 | 3.89M | auto val=static_cast<int>(input->readULong(1)); |
437 | 3.89M | int type=(val>>4); |
438 | 3.89M | int fileFormat=(val&0xF); |
439 | 3.89M | auto &format=record.m_format; |
440 | 3.89M | switch (fileFormat) { |
441 | 1.25M | case 0: // general |
442 | 1.25M | break; |
443 | 277k | case 1: |
444 | 277k | format.m_format=MWAWCell::F_NUMBER; |
445 | 277k | format.m_numberFormat=MWAWCell::F_NUMBER_CURRENCY; |
446 | 277k | break; |
447 | 171k | case 2: |
448 | 171k | format.m_format=MWAWCell::F_NUMBER; |
449 | 171k | format.m_numberFormat=MWAWCell::F_NUMBER_PERCENT; |
450 | 171k | break; |
451 | 459k | case 3: |
452 | 459k | format.m_format=MWAWCell::F_NUMBER; |
453 | 459k | format.m_numberFormat=MWAWCell::F_NUMBER_SCIENTIFIC; |
454 | 459k | break; |
455 | 171k | case 4: |
456 | 171k | format.m_format=MWAWCell::F_NUMBER; |
457 | 171k | format.m_numberFormat=MWAWCell::F_NUMBER_DECIMAL; |
458 | 171k | break; |
459 | 123k | case 5: |
460 | 324k | case 6: |
461 | 382k | case 7: |
462 | 570k | case 8: |
463 | 633k | case 9: { |
464 | 633k | static char const* const wh[]= {"%m/%d/%y", "%B %d, %y", "%B %d, %Y", "%a, %b %d %y", "%A, %B %d %Y" }; |
465 | 633k | format.m_format=MWAWCell::F_DATE; |
466 | 633k | format.m_DTFormat=wh[fileFormat-5]; |
467 | 633k | break; |
468 | 570k | } |
469 | 97.1k | case 10: |
470 | 197k | case 11: |
471 | 565k | case 12: |
472 | 633k | case 13: { |
473 | 633k | static char const* const wh[]= {"%I:%M %p", "%I:%M:%S %p", "%H:%M", "%H:%M:%S" }; |
474 | 633k | format.m_format=MWAWCell::F_TIME; |
475 | 633k | format.m_DTFormat=wh[fileFormat-10]; |
476 | 633k | break; |
477 | 565k | } |
478 | 288k | default: // unknown |
479 | 288k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: find unknown format\n")); |
480 | 288k | f << "format=##" << fileFormat << ","; |
481 | 288k | break; |
482 | 3.89M | } |
483 | | |
484 | 3.89M | bool ok=true; |
485 | 3.89M | auto ord=static_cast<int>(input->readULong(1)); |
486 | 3.89M | if (ord&8) { |
487 | 1.17M | f << "commas[thousand],"; |
488 | 1.17M | ord &= 0xF7; |
489 | 1.17M | } |
490 | 3.89M | if (ord&4) { |
491 | 1.36M | f << "parenthese[negative],"; |
492 | 1.36M | ord &= 0xFB; |
493 | 1.36M | } |
494 | 3.89M | if (ord&2) { |
495 | 1.35M | f << "lock,"; |
496 | 1.35M | ord &= 0xFD; |
497 | 1.35M | } |
498 | 3.89M | if (ord&1) { |
499 | 1.57M | if (!input->checkPosition(pos+8)) { |
500 | 359k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: can not read format\n")); |
501 | 359k | f << "###"; |
502 | 359k | ok = false; |
503 | 359k | } |
504 | 1.21M | else { |
505 | 1.21M | MWAWFont &font = record.m_font; |
506 | 1.21M | font = MWAWFont(); |
507 | 1.21M | auto fId= static_cast<int>(input->readULong(2)); |
508 | 1.21M | if (fId!=0xFFFF) |
509 | 1.12M | font.setId(m_document.getStyleManager()->getFontId(static_cast<int>(fId))); |
510 | 1.21M | font.setSize(float(input->readULong(1))); |
511 | 1.21M | auto flag =static_cast<int>(input->readULong(1)); |
512 | 1.21M | uint32_t flags=0; |
513 | 1.21M | if (flag&0x1) flags |= MWAWFont::boldBit; |
514 | 1.21M | if (flag&0x2) flags |= MWAWFont::italicBit; |
515 | 1.21M | if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple); |
516 | 1.21M | if (flag&0x8) flags |= MWAWFont::embossBit; |
517 | 1.21M | if (flag&0x10) flags |= MWAWFont::shadowBit; |
518 | 1.21M | if (flag&0x20) font.setDeltaLetterSpacing(-1); |
519 | 1.21M | if (flag&0x40) font.setDeltaLetterSpacing(1); |
520 | 1.21M | if (flag&0x80) font.setStrikeOutStyle(MWAWFont::Line::Simple); |
521 | 1.21M | font.setFlags(flags); |
522 | 1.21M | auto colId = static_cast<int>(input->readULong(1)); |
523 | 1.21M | if (colId!=1) { |
524 | 1.16M | MWAWColor col; |
525 | 1.16M | if (m_document.getStyleManager()->getColor(colId, col)) |
526 | 1.05M | font.setColor(col); |
527 | 112k | else { |
528 | 112k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: unknown color %d\n", colId)); |
529 | 112k | } |
530 | 1.16M | } |
531 | 1.21M | f << "font=[" << font.getDebugString(m_parserState->m_fontConverter) << "],"; |
532 | 1.21M | val=static_cast<int>(input->readULong(1)); |
533 | 1.21M | record.m_borders = (val>>4); |
534 | 1.21M | if (record.m_borders) { |
535 | 623k | f << "border="; |
536 | 623k | if (record.m_borders&1) f << "L"; |
537 | 623k | if (record.m_borders&2) f << "T"; |
538 | 623k | if (record.m_borders&4) f << "R"; |
539 | 623k | if (record.m_borders&8) f << "B"; |
540 | 623k | f << ","; |
541 | 623k | } |
542 | 1.21M | val &=0xF; |
543 | 1.21M | switch ((val>>2)) { |
544 | 509k | case 1: |
545 | 509k | record.m_hAlign=MWAWCell::HALIGN_LEFT; |
546 | 509k | f << "left,"; |
547 | 509k | break; |
548 | 118k | case 2: |
549 | 118k | record.m_hAlign=MWAWCell::HALIGN_CENTER; |
550 | 118k | f << "center,"; |
551 | 118k | break; |
552 | 166k | case 3: |
553 | 166k | record.m_hAlign=MWAWCell::HALIGN_RIGHT; |
554 | 166k | f << "right,"; |
555 | 166k | break; |
556 | 417k | default: |
557 | 417k | break; |
558 | 1.21M | } |
559 | 1.21M | val &=0x3; |
560 | 1.21M | if (val) f << "#unk=" << val << ","; |
561 | 1.21M | ascFile.addDelimiter(pos+2,'|'); |
562 | 1.21M | input->seek(pos+8, librevenge::RVNG_SEEK_SET); |
563 | 1.21M | ascFile.addDelimiter(pos+8,'|'); |
564 | 1.21M | ord &= 0xFE; |
565 | 1.21M | } |
566 | 1.57M | } |
567 | 3.89M | format.m_digits=(ord>>4); |
568 | 3.89M | ord &= 0xF; |
569 | 3.89M | if (ok && ord) { |
570 | 0 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: find unexpected order\n")); |
571 | 0 | f << "###ord=" << std::hex << ord << std::dec; |
572 | 0 | ok = false; |
573 | 0 | } |
574 | 3.89M | auto &content=record.m_content; |
575 | 3.89M | if (ok && type==4) { |
576 | 279k | f << "formula,"; |
577 | 279k | long actPos=input->tell(); |
578 | 279k | auto formSz=static_cast<int>(input->readULong(1)); |
579 | 279k | if (!input->checkPosition(actPos+2+formSz)) { |
580 | 488 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: the formula seems bad\n")); |
581 | 488 | f << "###"; |
582 | 488 | ok = false; |
583 | 488 | } |
584 | 278k | else { |
585 | 278k | ascFile.addDelimiter(input->tell(),'|'); |
586 | 278k | std::vector<MWAWCellContent::FormulaInstruction> formula; |
587 | 278k | std::string error; |
588 | 278k | if (readFormula(id, actPos+1+formSz, formula, error)) { |
589 | 21.3k | content.m_contentType=MWAWCellContent::C_FORMULA; |
590 | 21.3k | content.m_formula=formula; |
591 | 21.3k | } |
592 | 257k | else { |
593 | 257k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: can not read a formula\n")); |
594 | 257k | f << "###"; |
595 | 257k | } |
596 | 278k | f << "form="; |
597 | 278k | for (auto const &fo : formula) |
598 | 635k | f << fo; |
599 | 278k | f << error << ","; |
600 | 278k | if ((formSz%2)==0) ++formSz; |
601 | 278k | input->seek(actPos+1+formSz, librevenge::RVNG_SEEK_SET); |
602 | 278k | ascFile.addDelimiter(input->tell(),'|'); |
603 | 278k | val=static_cast<int>(input->readULong(1)); |
604 | 278k | type=(val>>4); |
605 | 278k | if (val&0xF) f << "unkn0=" << (val&0xF) << ","; |
606 | 278k | val=static_cast<int>(input->readULong(1)); |
607 | 278k | if (val) f << "unkn1=" << (val) << ","; |
608 | 278k | } |
609 | 279k | } |
610 | 3.89M | if (ok) { |
611 | 3.53M | long actPos=input->tell(); |
612 | 3.53M | switch (type) { |
613 | 1.74M | case 0: |
614 | 1.84M | case 1: |
615 | 1.84M | if (type==0) |
616 | 1.74M | f << "int,"; |
617 | 92.0k | else |
618 | 92.0k | f << "long,"; |
619 | 1.84M | if (!input->checkPosition(actPos+2+2*type)) { |
620 | 1.56k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: unexpected size for a int\n")); |
621 | 1.56k | f << "###"; |
622 | 1.56k | ok = false; |
623 | 1.56k | break; |
624 | 1.56k | } |
625 | 1.84M | record.m_valueType=MWAWCellContent::C_NUMBER; |
626 | 1.84M | content.setValue(double(input->readLong(2+2*type))); |
627 | 1.84M | f << "val=" << content.m_value << ","; |
628 | 1.84M | break; |
629 | 267k | case 2: { |
630 | 267k | f << "float,"; |
631 | 267k | if (!input->checkPosition(actPos+10)) { |
632 | 2.42k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: unexpected size for a float\n")); |
633 | 2.42k | f << "###"; |
634 | 2.42k | ok=false; |
635 | 2.42k | break; |
636 | 2.42k | } |
637 | 264k | record.m_valueType=MWAWCellContent::C_NUMBER; |
638 | 264k | double value; |
639 | 264k | if (input->readDouble10(value, record.m_hasNaNValue)) { |
640 | 242k | content.setValue(value); |
641 | 242k | f << "val=" << value << ","; |
642 | 242k | } |
643 | 21.8k | else { |
644 | 21.8k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: can not read a float\n")); |
645 | 21.8k | f << "###,"; |
646 | 21.8k | } |
647 | 264k | break; |
648 | 267k | } |
649 | 129k | case 3: { |
650 | 129k | f << "string,"; |
651 | 129k | auto stringSize = static_cast<int>(input->readULong(1)); |
652 | 129k | if (!input->checkPosition(actPos+1+stringSize)) { |
653 | 440 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: the string(II) seems bad\n")); |
654 | 440 | f << "###"; |
655 | 440 | ok=false; |
656 | 440 | break; |
657 | 440 | } |
658 | 129k | record.m_valueType=MWAWCellContent::C_TEXT; |
659 | 129k | content.m_textEntry.setBegin(input->tell()); |
660 | 129k | content.m_textEntry.setLength(long(stringSize)); |
661 | 129k | std::string data(""); |
662 | 9.18M | for (int c=0; c < stringSize; ++c) data+=char(input->readULong(1)); |
663 | 129k | f << "val=" << data << ","; |
664 | 129k | break; |
665 | 129k | } |
666 | | // case 4: the formula is already read |
667 | 160k | case 5: // bool |
668 | 160k | if (input->checkPosition(actPos+1)) { |
669 | 159k | if (!fileFormat) |
670 | 9.26k | format.m_format=MWAWCell::F_BOOLEAN; |
671 | 159k | record.m_valueType=MWAWCellContent::C_NUMBER; |
672 | 159k | content.setValue(double(input->readLong(1))); |
673 | 159k | f << "val=" << content.m_value << ","; |
674 | 159k | break; |
675 | 159k | } |
676 | 714 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: can not read a bool\n")); |
677 | 714 | f << "###bool,"; |
678 | 714 | break; |
679 | 78.1k | case 6: |
680 | 78.1k | if (input->checkPosition(actPos+1)) { |
681 | 75.8k | record.m_valueType=MWAWCellContent::C_NUMBER; |
682 | 75.8k | content.setValue(std::numeric_limits<double>::quiet_NaN()); |
683 | 75.8k | record.m_hasNaNValue = true; |
684 | 75.8k | f << "val=nan" << input->readLong(1) << ","; |
685 | 75.8k | break; |
686 | 75.8k | } |
687 | 2.34k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: can not read a nanc[formula]\n")); |
688 | 2.34k | f << "###nan[res],"; |
689 | 2.34k | break; |
690 | 40.0k | case 7: // empty |
691 | 40.0k | break; |
692 | 357k | case 8: // checkme: does such cell can have data? |
693 | 357k | f << "recovered,"; |
694 | 357k | break; |
695 | 661k | default: |
696 | 661k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: unexpected type\n")); |
697 | 661k | f << "###type=" << type << ","; |
698 | 661k | ok=false; |
699 | 661k | break; |
700 | 3.53M | } |
701 | 3.53M | } |
702 | 3.89M | if (content.m_contentType!=MWAWCellContent::C_FORMULA) |
703 | 3.87M | content.m_contentType=record.m_valueType; |
704 | 3.89M | if (format.m_format==MWAWCell::F_UNKNOWN && content.isValueSet()) { |
705 | 1.24M | format.m_format=MWAWCell::F_NUMBER; |
706 | 1.24M | format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC; |
707 | 1.24M | } |
708 | 3.89M | f << format << ","; |
709 | 3.89M | ascFile.addPos(pos); |
710 | 3.89M | ascFile.addNote(f.str().c_str()); |
711 | 3.89M | if (ok) { |
712 | 2.87M | ascFile.addPos(input->tell()); |
713 | 2.87M | ascFile.addNote("_"); |
714 | 2.87M | } |
715 | 3.89M | return ok; |
716 | 3.89M | } |
717 | | |
718 | | bool ClarisWksDbaseContent::readRecordSS(MWAWVec2i const &id, long pos, ClarisWksDbaseContent::Record &record) |
719 | 6.00M | { |
720 | 6.00M | if (m_version <= 3) |
721 | 3.89M | return readRecordSSV1(id, pos, record); |
722 | 2.11M | record=ClarisWksDbaseContent::Record(); |
723 | 2.11M | libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile; |
724 | 2.11M | libmwaw::DebugStream f; |
725 | 2.11M | f << "DBCHNK[spread" << id << "]:"; |
726 | 2.11M | MWAWInputStreamPtr &input= m_parserState->m_input; |
727 | 2.11M | input->seek(pos, librevenge::RVNG_SEEK_SET); |
728 | 2.11M | auto sz=long(input->readULong(2)); |
729 | 2.11M | long endPos=pos+sz+2; |
730 | 2.11M | if (!input->checkPosition(endPos) || sz < 4) { |
731 | 1.44M | f << "###sz=" << sz; |
732 | 1.44M | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the sz seems bad\n")); |
733 | 1.44M | ascFile.addPos(pos); |
734 | 1.44M | ascFile.addNote(f.str().c_str()); |
735 | 1.44M | return true; |
736 | 1.44M | } |
737 | 665k | auto type=static_cast<int>(input->readULong(1)); |
738 | | // next some format ? |
739 | 665k | auto val= static_cast<int>(input->readULong(1)); // 0-46 |
740 | 665k | if (val) f << "format=" << std::hex << val << std::dec << ","; |
741 | 665k | record.m_style=static_cast<int>(input->readLong(2)); |
742 | 665k | if (record.m_style) f << "Style-" << record.m_style << ","; |
743 | | |
744 | 665k | int fileFormat=0; |
745 | 665k | ClarisWksStyleManager::Style style; |
746 | 665k | ClarisWksStyleManager::CellFormat format; |
747 | 665k | if (m_document.getStyleManager()->get(record.m_style, style)) { |
748 | 3.91k | if (m_document.getStyleManager()->get(style.m_cellFormatId, format)) { |
749 | 3.63k | f << format << ","; |
750 | 3.63k | fileFormat=format.m_fileFormat; |
751 | 3.63k | } |
752 | 3.91k | if (style.m_fontId>=0) |
753 | 3.85k | m_document.getStyleManager()->get(style.m_fontId, record.m_font); |
754 | 3.91k | MWAWGraphicStyle graphStyle; |
755 | 3.91k | if (style.m_graphicId>=0 && m_document.getStyleManager()->get(style.m_graphicId, graphStyle)) { |
756 | 3.68k | if (graphStyle.hasSurfaceColor()) |
757 | 3.61k | record.m_backgroundColor=graphStyle.m_surfaceColor; |
758 | 3.68k | } |
759 | 3.91k | } |
760 | 665k | switch (fileFormat) { |
761 | 664k | case 0: // general |
762 | 664k | break; |
763 | 152 | case 1: |
764 | 152 | format.m_format=MWAWCell::F_NUMBER; |
765 | 152 | format.m_numberFormat=MWAWCell::F_NUMBER_CURRENCY; |
766 | 152 | break; |
767 | 135 | case 2: |
768 | 135 | format.m_format=MWAWCell::F_NUMBER; |
769 | 135 | format.m_numberFormat=MWAWCell::F_NUMBER_PERCENT; |
770 | 135 | break; |
771 | 125 | case 3: |
772 | 125 | format.m_format=MWAWCell::F_NUMBER; |
773 | 125 | format.m_numberFormat=MWAWCell::F_NUMBER_SCIENTIFIC; |
774 | 125 | break; |
775 | 302 | case 4: |
776 | 302 | format.m_format=MWAWCell::F_NUMBER; |
777 | 302 | format.m_numberFormat=MWAWCell::F_NUMBER_DECIMAL; |
778 | 302 | break; |
779 | 131 | case 5: |
780 | 135 | case 6: |
781 | 151 | case 7: |
782 | 264 | case 8: |
783 | 460 | case 9: { |
784 | 460 | static char const* const wh[]= {"%m/%d/%y", "%B %d, %y", "%B %d, %Y", "%a, %b %d %y", "%A, %B %d %Y" }; |
785 | 460 | format.m_format=MWAWCell::F_DATE; |
786 | 460 | format.m_DTFormat=wh[fileFormat-5]; |
787 | 460 | break; |
788 | 264 | } |
789 | 0 | case 10: // unknown |
790 | 14 | case 11: |
791 | 14 | break; |
792 | 1 | case 12: |
793 | 15 | case 13: |
794 | 19 | case 14: |
795 | 129 | case 15: { |
796 | 129 | static char const* const wh[]= {"%H:%M", "%H:%M:%S", "%I:%M %p", "%I:%M:%S %p" }; |
797 | 129 | format.m_format=MWAWCell::F_TIME; |
798 | 129 | format.m_DTFormat=wh[fileFormat-12]; |
799 | 129 | break; |
800 | 19 | } |
801 | 89 | default: // unknown |
802 | 89 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: find unknown format\n")); |
803 | 89 | f << "format=##" << fileFormat << ","; |
804 | 89 | break; |
805 | 665k | } |
806 | | |
807 | 665k | auto &content=record.m_content; |
808 | 665k | bool ok=true; |
809 | 665k | if (type==4) { |
810 | 85.6k | f << "formula,"; |
811 | 85.6k | if (sz<7) { |
812 | 2.05k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the formula seems bad\n")); |
813 | 2.05k | f << "##sz,"; |
814 | 2.05k | ok = false; |
815 | 2.05k | } |
816 | 83.5k | else { |
817 | 83.5k | type=static_cast<int>(input->readULong(1)); |
818 | 83.5k | val=static_cast<int>(input->readLong(1)); |
819 | 83.5k | if (val) f << "unkn=" << val << ","; // 1: maybe an enum stored by value instead of by id |
820 | 83.5k | auto formSz=static_cast<int>(input->readULong(1)); |
821 | 83.5k | long actPos=input->tell(); |
822 | 83.5k | ascFile.addDelimiter(actPos,'|'); |
823 | | |
824 | 83.5k | if (8+formSz > sz) { |
825 | 18.9k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the formula seems bad\n")); |
826 | 18.9k | f << "###"; |
827 | 18.9k | ok=false; |
828 | 18.9k | } |
829 | 64.6k | else { |
830 | 64.6k | std::vector<MWAWCellContent::FormulaInstruction> formula; |
831 | 64.6k | std::string error; |
832 | 64.6k | if (readFormula(id, actPos+1+formSz, formula, error)) { |
833 | 17.8k | content.m_contentType=MWAWCellContent::C_FORMULA; |
834 | 17.8k | content.m_formula=formula; |
835 | 17.8k | } |
836 | 46.8k | else { |
837 | 46.8k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: can not read a formule\n")); |
838 | 46.8k | f << "###"; |
839 | 46.8k | } |
840 | 64.6k | f << "form="; |
841 | 64.6k | for (auto const &fo : formula) |
842 | 120k | f << fo; |
843 | 64.6k | f << error << ","; |
844 | | |
845 | | /** checkme: there does not seem to be alignment, but another |
846 | | variable before the result */ |
847 | 64.6k | input->seek(actPos+formSz+1, librevenge::RVNG_SEEK_SET); |
848 | 64.6k | ascFile.addDelimiter(input->tell(),'|'); |
849 | 64.6k | sz=4+int(endPos-input->tell()); |
850 | 64.6k | } |
851 | 83.5k | } |
852 | 85.6k | } |
853 | 665k | if (ok) { |
854 | 644k | switch (type) { |
855 | 175k | case 0: |
856 | 204k | case 1: |
857 | 204k | if (type==0) |
858 | 175k | f << "int,"; |
859 | 28.7k | else |
860 | 28.7k | f << "long,"; |
861 | 204k | if (sz==4) { |
862 | | // rare, broken file ? AppleWorks seems to display a 0, but show a different number in the formula |
863 | 5.53k | record.m_valueType=MWAWCellContent::C_NUMBER; |
864 | 5.53k | content.setValue(0); |
865 | 5.53k | f << "##val=" << 0 << ","; |
866 | 5.53k | break; |
867 | 5.53k | } |
868 | 198k | if (sz<6+2*type) { |
869 | 6.44k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: unexpected size for a int\n")); |
870 | 6.44k | f << "###"; |
871 | 6.44k | break; |
872 | 6.44k | } |
873 | 192k | record.m_valueType=MWAWCellContent::C_NUMBER; |
874 | 192k | content.setValue(double(input->readLong(2+2*type))); |
875 | 192k | f << "val=" << content.m_value << ","; |
876 | 192k | break; |
877 | 34.8k | case 2: { |
878 | 34.8k | f << "float,"; |
879 | 34.8k | if (sz<0xe) { |
880 | 5.76k | if (sz>=0xc && input->checkPosition(input->tell()+10)) { |
881 | | // rare, broken file ? but as we have the main 8 bytes, let continue |
882 | 2.21k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: unexpected size for a float, try to read it\n")); |
883 | 2.21k | f << "#"; |
884 | 2.21k | } |
885 | 3.54k | else { |
886 | 3.54k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: unexpected size for a float\n")); |
887 | 3.54k | f << "###"; |
888 | 3.54k | break; |
889 | 3.54k | } |
890 | 5.76k | } |
891 | 31.3k | record.m_valueType=MWAWCellContent::C_NUMBER; |
892 | 31.3k | double value; |
893 | 31.3k | if (input->readDouble10(value, record.m_hasNaNValue)) { |
894 | 28.8k | content.setValue(value); |
895 | 28.8k | f << value << ","; |
896 | 28.8k | } |
897 | 2.54k | else { |
898 | 2.54k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: can not read a float\n")); |
899 | 2.54k | f << "###,"; |
900 | 2.54k | } |
901 | 31.3k | break; |
902 | 34.8k | } |
903 | 65.4k | case 3: { |
904 | 65.4k | f << "string,"; |
905 | 65.4k | if (sz<5) { |
906 | 2.38k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the string seems bad\n")); |
907 | 2.38k | f << "###"; |
908 | 2.38k | break; |
909 | 2.38k | } |
910 | 63.0k | auto stringSize = static_cast<int>(input->readULong(1)); |
911 | 63.0k | if (stringSize+5>sz) { |
912 | | // rare, broken file ? let try to read the beginning of the file |
913 | 8.38k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the string(II) seems bad, try to read the beginning\n")); |
914 | 8.38k | f << "###"; |
915 | 8.38k | if (sz==5) |
916 | 6.72k | break; |
917 | 1.66k | stringSize=int(sz)-5; |
918 | 1.66k | } |
919 | 56.3k | record.m_valueType=MWAWCellContent::C_TEXT; |
920 | 56.3k | content.m_textEntry.setBegin(input->tell()); |
921 | 56.3k | content.m_textEntry.setLength(long(stringSize)); |
922 | 56.3k | std::string data(""); |
923 | 946k | for (int c=0; c < stringSize; ++c) data+=char(input->readULong(1)); |
924 | 56.3k | f << data << ","; |
925 | 56.3k | break; |
926 | 63.0k | } |
927 | | // 4: formula |
928 | 18.6k | case 5: // bool |
929 | 18.6k | if (sz>=4+1) { |
930 | 17.9k | if (format.m_format==MWAWCell::F_UNKNOWN) |
931 | 17.9k | format.m_format=MWAWCell::F_BOOLEAN; |
932 | 17.9k | record.m_valueType=MWAWCellContent::C_NUMBER; |
933 | 17.9k | content.setValue(double(input->readLong(1))); |
934 | 17.9k | f << "val=" << content.m_value << ","; |
935 | 17.9k | break; |
936 | 17.9k | } |
937 | 707 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: can not read a bool\n")); |
938 | 707 | f << "###bool,"; |
939 | 707 | break; |
940 | 14.0k | case 6: |
941 | 14.0k | if (sz>=4+1) { |
942 | 9.48k | record.m_valueType=MWAWCellContent::C_NUMBER; |
943 | 9.48k | content.setValue(std::numeric_limits<double>::quiet_NaN()); |
944 | 9.48k | record.m_hasNaNValue = true; |
945 | 9.48k | f << "val=nan" << input->readLong(1) << ","; |
946 | 9.48k | break; |
947 | 9.48k | } |
948 | 4.59k | break; |
949 | 4.59k | case 7: // link/anchor/goto |
950 | 3.17k | if (sz<4) { |
951 | 0 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the mark size seems bad\n")); |
952 | 0 | f << "###mark"; |
953 | 0 | break; |
954 | 0 | } |
955 | 3.17k | f << "mark,"; |
956 | 3.17k | break; |
957 | 22.1k | case 8: |
958 | 25.6k | case 9: |
959 | 25.6k | f << "type" << type << ","; |
960 | 25.6k | if (sz<4) { |
961 | 0 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the type%d seems bad\n", type)); |
962 | 0 | f << "###"; |
963 | 0 | break; |
964 | 0 | } |
965 | 25.6k | break; |
966 | 278k | default: |
967 | 278k | f << "#type=" << type << ","; |
968 | 644k | } |
969 | 644k | } |
970 | 665k | if (content.m_contentType!=MWAWCellContent::C_FORMULA) |
971 | 647k | content.m_contentType=record.m_valueType; |
972 | 665k | record.m_format=format; |
973 | 665k | record.m_hAlign=format.m_hAlign; |
974 | 665k | record.m_borders=format.m_borders; |
975 | 665k | ascFile.addPos(pos); |
976 | 665k | ascFile.addNote(f.str().c_str()); |
977 | 665k | ascFile.addPos(endPos); |
978 | 665k | ascFile.addNote("_"); |
979 | 665k | return true; |
980 | 665k | } |
981 | | |
982 | | bool ClarisWksDbaseContent::readRecordDB(MWAWVec2i const &id, long pos, ClarisWksDbaseContent::Record &record) |
983 | 2.19M | { |
984 | 2.19M | record=ClarisWksDbaseContent::Record(); |
985 | 2.19M | libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile; |
986 | 2.19M | libmwaw::DebugStream f; |
987 | 2.19M | f << "DBCHNK[dbase" << id << "]:"; |
988 | 2.19M | MWAWInputStreamPtr &input= m_parserState->m_input; |
989 | 2.19M | input->seek(pos, librevenge::RVNG_SEEK_SET); |
990 | 2.19M | long sz=0; |
991 | 2.19M | long endPos=-1; |
992 | 2.19M | if (m_version>3) { |
993 | 910k | sz=long(input->readULong(2)); |
994 | 910k | endPos=pos+sz+2; |
995 | 910k | if (!input->checkPosition(endPos) || sz < 2) { |
996 | 406k | f << "###sz=" << sz; |
997 | 406k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: the sz seems bad\n")); |
998 | 406k | ascFile.addPos(pos); |
999 | 406k | ascFile.addNote(f.str().c_str()); |
1000 | 406k | return true; |
1001 | 406k | } |
1002 | 910k | } |
1003 | 1.78M | auto val=static_cast<int>(input->readULong(2)); |
1004 | 1.78M | int type=(val>>12); |
1005 | 1.78M | val = int(val&0xFFF); |
1006 | 1.78M | MWAWCellContent &content=record.m_content; |
1007 | 1.78M | switch (type) { |
1008 | 720k | case 0: { |
1009 | 720k | f << "string,"; |
1010 | 720k | if ((m_version<=3&&!input->checkPosition(pos+2+val)) || |
1011 | 669k | (m_version>3 && (val+2>sz || val+4<sz))) { |
1012 | 139k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: the string(II) seems bad\n")); |
1013 | 139k | f << "###"; |
1014 | 139k | break; |
1015 | 139k | } |
1016 | 580k | content.m_contentType=MWAWCellContent::C_TEXT; |
1017 | 580k | content.m_textEntry.setBegin(input->tell()); |
1018 | 580k | content.m_textEntry.setLength(long(val)); |
1019 | 580k | std::string data(""); |
1020 | 54.2M | for (int c=0; c < val; ++c) data+=char(input->readULong(1)); |
1021 | 580k | f << data << ","; |
1022 | 580k | break; |
1023 | 720k | } |
1024 | 313k | case 2: { |
1025 | 313k | if ((m_version<=3&&!input->checkPosition(pos+2+val+2)) || |
1026 | 278k | (m_version>3 && val+2>sz)) { |
1027 | 160k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: can not read a text and style field\n")); |
1028 | 160k | f << "##string2" << type << "[" << val << "],"; |
1029 | 160k | break; |
1030 | 160k | } |
1031 | 153k | content.m_contentType=MWAWCellContent::C_TEXT; |
1032 | 153k | content.m_textEntry.setBegin(input->tell()); |
1033 | 153k | content.m_textEntry.setLength(long(val)); |
1034 | 153k | std::string data(""); |
1035 | 194M | for (int c=0; c < val; ++c) data+=char(input->readULong(1)); |
1036 | 153k | f << "string2," << data << ","; |
1037 | 153k | if (val&1) input->seek(1, librevenge::RVNG_SEEK_CUR); |
1038 | 153k | auto N=int(input->readULong(2)); |
1039 | 153k | f << "N=" << N << ","; |
1040 | 153k | int fontSize = m_version<=3 ? 10 : m_version<=5 ? 12 : 18; |
1041 | 153k | if ((m_version<=3 && !input->checkPosition(input->tell()+fontSize*N)) || |
1042 | 115k | (m_version>3 && input->tell()+fontSize*N>endPos)) { |
1043 | 52.5k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: can not read the text's style\n")); |
1044 | 52.5k | f << "##N"; |
1045 | 52.5k | break; |
1046 | 52.5k | } |
1047 | 8.78M | for (int i=0; i<N; ++i) { |
1048 | 8.68M | MWAWFont font; |
1049 | 8.68M | int posChar; |
1050 | 8.68M | if (!m_document.getStyleManager()->readFontAndPos(i, posChar, font)) { |
1051 | 0 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: can not read the text's style\n")); |
1052 | 0 | f << "##font"; |
1053 | 0 | break; |
1054 | 0 | } |
1055 | 8.68M | record.m_posToFontMap[posChar]=font; |
1056 | 8.68M | } |
1057 | 100k | break; |
1058 | 153k | } |
1059 | 105k | case 4: |
1060 | | // find also some string here when val is not null so let test |
1061 | 105k | if (val && ((m_version>3 && val+2<=sz && val+4>=sz) || (m_version<=3 && input->checkPosition(pos+2+val)))) { |
1062 | 74.6k | content.m_contentType=MWAWCellContent::C_TEXT; |
1063 | 74.6k | content.m_textEntry.setBegin(input->tell()); |
1064 | 74.6k | content.m_textEntry.setLength(long(val)); |
1065 | 74.6k | std::string data(""); |
1066 | 97.7M | for (int c=0; c < val; ++c) data+=char(input->readULong(1)); |
1067 | 74.6k | f << "string4," << data << ","; |
1068 | 74.6k | break; |
1069 | 74.6k | } |
1070 | 30.8k | f << "intDB,"; |
1071 | 30.8k | if ((m_version<=3&&!input->checkPosition(pos+2)) || (m_version>3 && sz!=2)) { |
1072 | 11.7k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: unexpected size for a int\n")); |
1073 | 11.7k | f << "###"; |
1074 | 11.7k | break; |
1075 | 11.7k | } |
1076 | 19.0k | content.m_contentType=MWAWCellContent::C_NUMBER; |
1077 | 19.0k | content.setValue(double(input->readLong(1))); |
1078 | 19.0k | break; |
1079 | 156k | case 8: |
1080 | 177k | case 9: { |
1081 | 177k | if (val) f << "unkn=" << std::hex << val << std::dec << ","; |
1082 | 177k | f << "float" << type << ","; |
1083 | 177k | if ((m_version<=3&&!input->checkPosition(pos+12)) || (m_version>3 && sz!=12)) { |
1084 | 8.48k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: unexpected size for a float\n")); |
1085 | 8.48k | f << "###"; |
1086 | 8.48k | break; |
1087 | 8.48k | } |
1088 | 169k | double value; |
1089 | 169k | if (input->readDouble10(value, record.m_hasNaNValue)) { |
1090 | 162k | content.m_contentType=MWAWCellContent::C_NUMBER; |
1091 | 162k | content.setValue(value); |
1092 | 162k | f << value << ","; |
1093 | 162k | } |
1094 | 6.74k | else { |
1095 | 6.74k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: can not read a float\n")); |
1096 | 6.74k | f << "###,"; |
1097 | 6.74k | } |
1098 | 169k | break; |
1099 | 177k | } |
1100 | 471k | default: |
1101 | 471k | if (val) f << "unkn=" << std::hex << val << std::dec << ","; |
1102 | 471k | f << "#type=" << type << ","; |
1103 | 1.78M | } |
1104 | 1.78M | ascFile.addPos(pos); |
1105 | 1.78M | ascFile.addNote(f.str().c_str()); |
1106 | 1.78M | if (m_version>3) { |
1107 | 503k | ascFile.addPos(endPos); |
1108 | 503k | ascFile.addNote("_"); |
1109 | 503k | } |
1110 | 1.78M | return true; |
1111 | 1.78M | } |
1112 | | |
1113 | | bool ClarisWksDbaseContent::get(MWAWVec2i const &pos, ClarisWksDbaseContent::Record &record) const |
1114 | 14.1M | { |
1115 | 14.1M | auto it=m_idColumnMap.find(pos[0]); |
1116 | 14.1M | if (it==m_idColumnMap.end()) return false; |
1117 | 11.3M | Column const &col=it->second; |
1118 | 11.3M | auto rIt=col.m_idRecordMap.find(pos[1]); |
1119 | 11.3M | if (rIt==col.m_idRecordMap.end()) return false; |
1120 | 10.4M | record=rIt->second; |
1121 | 10.4M | if (m_isSpreadsheet) return true; |
1122 | | |
1123 | 47.5k | static bool first=true; |
1124 | 47.5k | if (pos[0]>=0&&pos[0]<int(m_dbFormatList.size())) { |
1125 | 47.5k | auto const &format=m_dbFormatList[size_t(pos[0])]; |
1126 | 47.5k | record.m_format=format; |
1127 | 47.5k | record.m_fileFormat=format.m_fileFormat; |
1128 | 47.5k | record.m_hAlign=format.m_hAlign; |
1129 | 47.5k | } |
1130 | 0 | else if (first) { |
1131 | 0 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::get: can not find format for field %d\n", pos[0])); |
1132 | 0 | first=false; |
1133 | 0 | } |
1134 | 47.5k | return true; |
1135 | 10.4M | } |
1136 | | |
1137 | | bool ClarisWksDbaseContent::send(MWAWVec2i const &pos) |
1138 | 5.31M | { |
1139 | 5.31M | MWAWListenerPtr listener=m_parserState->getMainListener(); |
1140 | 5.31M | if (!listener) { |
1141 | 0 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::send: can not find the listener\n")); |
1142 | 0 | return false; |
1143 | 0 | } |
1144 | 5.31M | Record record; |
1145 | 5.31M | if (!get(pos, record)) return true; |
1146 | 1.66M | auto const &content=record.m_content; |
1147 | 1.66M | listener->setFont(record.m_font); |
1148 | 1.66M | auto contentType= content.m_contentType==MWAWCellContent::C_FORMULA ? record.m_valueType : content.m_contentType; |
1149 | 1.66M | MWAWParagraph para; |
1150 | 1.66M | para.m_justify = |
1151 | 1.66M | record.m_hAlign==MWAWCell::HALIGN_LEFT ? MWAWParagraph::JustificationLeft : |
1152 | 1.66M | record.m_hAlign==MWAWCell::HALIGN_CENTER ? MWAWParagraph::JustificationCenter : |
1153 | 1.56M | record.m_hAlign==MWAWCell::HALIGN_RIGHT ? MWAWParagraph::JustificationRight : |
1154 | 1.52M | contentType==MWAWCellContent::C_TEXT ? MWAWParagraph::JustificationLeft : |
1155 | 1.43M | MWAWParagraph::JustificationRight; |
1156 | 1.66M | listener->setParagraph(para); |
1157 | 1.66M | switch (contentType) { |
1158 | 1.13M | case MWAWCellContent::C_NUMBER: |
1159 | 1.13M | if (record.m_fileFormat) |
1160 | 0 | send(content.m_value, record.m_hasNaNValue, ClarisWksStyleManager::CellFormat(record.m_format)); |
1161 | 1.13M | else { |
1162 | 1.13M | std::stringstream s; |
1163 | 1.13M | s << content.m_value; |
1164 | 1.13M | listener->insertUnicodeString(librevenge::RVNGString(s.str().c_str())); |
1165 | 1.13M | } |
1166 | 1.13M | break; |
1167 | 67.5k | case MWAWCellContent::C_TEXT: |
1168 | 67.5k | if (content.m_textEntry.valid()) { |
1169 | 61.3k | MWAWInputStreamPtr &input= m_parserState->m_input; |
1170 | 61.3k | long fPos = input->tell(); |
1171 | 61.3k | input->seek(content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET); |
1172 | 61.3k | long endPos = content.m_textEntry.end(); |
1173 | 6.93M | while (!input->isEnd() && input->tell() < endPos) { |
1174 | 6.87M | auto c=static_cast<unsigned char>(input->readULong(1)); |
1175 | 6.87M | if (c==0x9) |
1176 | 50.7k | listener->insertTab(); |
1177 | 6.82M | else if (c==0xd || c==0xa) |
1178 | 93.8k | listener->insertEOL(); |
1179 | 6.73M | else |
1180 | 6.73M | listener->insertCharacter(c, input, endPos); |
1181 | 6.87M | } |
1182 | 61.3k | input->seek(fPos,librevenge::RVNG_SEEK_SET); |
1183 | 61.3k | } |
1184 | 67.5k | break; |
1185 | 0 | case MWAWCellContent::C_FORMULA: |
1186 | 0 | case MWAWCellContent::C_NONE: |
1187 | 468k | case MWAWCellContent::C_UNKNOWN: |
1188 | | #if !defined(__clang__) |
1189 | | default: |
1190 | | #endif |
1191 | 468k | break; |
1192 | 1.66M | } |
1193 | 1.66M | return true; |
1194 | 1.66M | } |
1195 | | |
1196 | | void ClarisWksDbaseContent::send(double val, bool isNotANumber, ClarisWksStyleManager::CellFormat const &format) |
1197 | 0 | { |
1198 | 0 | MWAWListenerPtr listener=m_parserState->getMainListener(); |
1199 | 0 | if (!listener) |
1200 | 0 | return; |
1201 | 0 | std::stringstream s; |
1202 | 0 | int const &type=format.m_fileFormat; |
1203 | | // note: if val*0!=0, val is a NaN so better so simply print NaN |
1204 | 0 | if (type <= 0 || type >=16 || type==10 || type==11 || isNotANumber) { |
1205 | 0 | s << val; |
1206 | 0 | listener->insertUnicodeString(librevenge::RVNGString(s.str().c_str())); |
1207 | 0 | return; |
1208 | 0 | } |
1209 | 0 | std::string value(""); |
1210 | | // FIXME: must not be here, change the reference date from 1/1/1904 to 1/1/1900 |
1211 | 0 | if (MWAWCellContent::double2String(format.m_format==MWAWCell::F_DATE ? val+1460 : val, format, value)) |
1212 | 0 | s << value; |
1213 | 0 | else { |
1214 | 0 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::send: can not convert the actual value\n")); |
1215 | 0 | s << val; |
1216 | 0 | } |
1217 | 0 | listener->insertUnicodeString(librevenge::RVNGString(s.str().c_str())); |
1218 | 0 | } |
1219 | | |
1220 | | //////////////////////////////////////////////////////////// |
1221 | | // formula |
1222 | | //////////////////////////////////////////////////////////// |
1223 | | bool ClarisWksDbaseContent::readCellInFormula(MWAWVec2i const &pos, MWAWCellContent::FormulaInstruction &instr) |
1224 | 97.7k | { |
1225 | 97.7k | MWAWInputStreamPtr input=m_parserState->m_input; |
1226 | 97.7k | instr=MWAWCellContent::FormulaInstruction(); |
1227 | 97.7k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Cell; |
1228 | 97.7k | bool absolute[2] = { true, true}; |
1229 | 97.7k | int cPos[2]= {0,0}; |
1230 | 293k | for (int i=0; i<2; ++i) { |
1231 | 195k | auto val = static_cast<int>(input->readULong(2)); |
1232 | 195k | if (val & 0x8000) { |
1233 | 39.6k | absolute[1-i]=false; |
1234 | 39.6k | if (val&0x4000) |
1235 | 20.4k | cPos[1-i] = pos[1-i]-1+(val-0xFFFF); |
1236 | 19.1k | else |
1237 | 19.1k | cPos[1-i] = pos[1-i]-1+(val-0x7FFF); |
1238 | 39.6k | } |
1239 | 155k | else |
1240 | 155k | cPos[1-i]=val; |
1241 | 195k | } |
1242 | 97.7k | if (m_version==6) { |
1243 | | // checkme: what is this number |
1244 | 8.01k | auto val=static_cast<int>(input->readLong(2)); |
1245 | 8.01k | static bool first = true; |
1246 | 8.01k | if (val!=-1 && first) { |
1247 | 36 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readCellInFormula: ARGHHH value after cell is %d\n", val)); |
1248 | 36 | first=false; |
1249 | 36 | } |
1250 | 8.01k | } |
1251 | | |
1252 | 97.7k | if (cPos[0] < 0 || cPos[1] < 0) { |
1253 | 13.4k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readCellInFormula: can not read cell position\n")); |
1254 | 13.4k | return false; |
1255 | 13.4k | } |
1256 | 84.3k | instr.m_position[0]=MWAWVec2i(cPos[0],cPos[1]); |
1257 | 84.3k | instr.m_positionRelative[0]=MWAWVec2b(!absolute[0],!absolute[1]); |
1258 | 84.3k | return true; |
1259 | 97.7k | } |
1260 | | |
1261 | | bool ClarisWksDbaseContent::readString(long endPos, std::string &res) |
1262 | 34.4k | { |
1263 | 34.4k | res=""; |
1264 | 34.4k | MWAWInputStreamPtr input=m_parserState->m_input; |
1265 | 34.4k | long pos=input->tell(); |
1266 | 34.4k | auto stringSize=static_cast<int>(input->readULong(1)); |
1267 | 34.4k | if (pos+1+stringSize>endPos || !input->checkPosition(pos+1+stringSize)) { |
1268 | 3.24k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readString: can not read string size\n")); |
1269 | 3.24k | return false; |
1270 | 3.24k | } |
1271 | 803k | for (int i=0; i<stringSize; ++i) |
1272 | 772k | res += char(input->readULong(1)); |
1273 | 31.2k | return true; |
1274 | 34.4k | } |
1275 | | |
1276 | | bool ClarisWksDbaseContent::readNumber(long endPos, double &res, bool &isNan) |
1277 | 25.5k | { |
1278 | 25.5k | MWAWInputStreamPtr input=m_parserState->m_input; |
1279 | 25.5k | long pos=input->tell(); |
1280 | 25.5k | if (pos+10>endPos) { |
1281 | 6.79k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readNumber: can not read a number\n")); |
1282 | 6.79k | return false; |
1283 | 6.79k | } |
1284 | 18.7k | return input->readDouble10(res, isNan); |
1285 | 25.5k | } |
1286 | | |
1287 | | namespace ClarisWksDbaseContentInternal |
1288 | | { |
1289 | | struct Operators { |
1290 | | char const *m_name; |
1291 | | int m_arity; |
1292 | | }; |
1293 | | |
1294 | | static Operators const s_listOperators[] = { |
1295 | | { "<", 2}, { ">", 2}, { "=", 2}, { "<=", 2}, |
1296 | | { ">=", 2}, { "<>", 2}, { "&", 2},{ "+", 2}, |
1297 | | |
1298 | | { "-", 2}, { "*", 2}, { "/", 2}, { "^", 2}, |
1299 | | { "%", 1}/*percent*/, { "-", 1}, { "(", 1}, { "", -2} /*UNKN*/, |
1300 | | }; |
1301 | | } |
1302 | | |
1303 | | bool ClarisWksDbaseContent::readFormula(MWAWVec2i const &cPos, long endPos, std::vector<MWAWCellContent::FormulaInstruction> &formula, std::string &error) |
1304 | 6.48M | { |
1305 | 6.48M | MWAWInputStreamPtr input=m_parserState->m_input; |
1306 | 6.48M | libmwaw::DebugStream f; |
1307 | 6.48M | bool ok=true; |
1308 | 6.48M | long pos=input->tell(); |
1309 | 6.48M | if (input->isEnd() || pos >= endPos) |
1310 | 72.1k | return false; |
1311 | 6.41M | int arity=0, type=static_cast<int>(input->readULong(1)); |
1312 | 6.41M | bool isFunction=false, isOperator=false; |
1313 | 6.41M | MWAWCellContent::FormulaInstruction instr; |
1314 | 6.41M | switch (type) { |
1315 | 20.0k | case 0x10: |
1316 | 20.0k | if (pos+1+2>endPos) { |
1317 | 833 | ok=false; |
1318 | 833 | break; |
1319 | 833 | } |
1320 | 19.2k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Long; |
1321 | 19.2k | instr.m_longValue=double(input->readLong(2)); |
1322 | 19.2k | break; |
1323 | 26.4k | case 0x11: |
1324 | 26.4k | if (pos+1+4>endPos) { |
1325 | 4.92k | ok=false; |
1326 | 4.92k | break; |
1327 | 4.92k | } |
1328 | 21.4k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Long; |
1329 | 21.4k | instr.m_longValue=double(input->readLong(4)); |
1330 | 21.4k | break; |
1331 | 25.5k | case 0x12: { |
1332 | 25.5k | double value; |
1333 | 25.5k | bool isNan; |
1334 | 25.5k | ok=readNumber(endPos, value, isNan); |
1335 | 25.5k | if (!ok) break; |
1336 | 18.5k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Double; |
1337 | 18.5k | instr.m_doubleValue=value; |
1338 | 18.5k | break; |
1339 | 25.5k | } |
1340 | 30.2k | case 0x13: { |
1341 | 30.2k | ok=readCellInFormula(cPos,instr); |
1342 | 30.2k | break; |
1343 | 25.5k | } |
1344 | 8.97k | case 0x1b: |
1345 | 8.97k | if (pos+1+8 > endPos) { |
1346 | 2.10k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readFormula: find instruction 0x1b, unknown size\n")); |
1347 | 2.10k | f << "##[code=1b,short],"; |
1348 | 2.10k | ok = false; |
1349 | 2.10k | break; |
1350 | 2.10k | } |
1351 | | /* found in some web files followed by a bad cell list */ |
1352 | 6.86k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readFormula: find instruction 0x1b\n")); |
1353 | 6.86k | f << "##[code=1b],"; |
1354 | 6.86k | instr.m_type=MWAWCellContent::FormulaInstruction::F_CellList; |
1355 | 6.86k | input->seek(8, librevenge::RVNG_SEEK_SET); |
1356 | 6.86k | break; |
1357 | 36.7k | case 0x14: { |
1358 | 36.7k | if (pos+1+8 > endPos || !readCellInFormula(cPos, instr)) { |
1359 | 4.04k | f << "###list cell short"; |
1360 | 4.04k | ok = false; |
1361 | 4.04k | break; |
1362 | 4.04k | } |
1363 | 32.7k | MWAWCellContent::FormulaInstruction instr2; |
1364 | 32.7k | if (!readCellInFormula(cPos, instr2)) { |
1365 | 10.0k | f << "###list cell short2"; |
1366 | 10.0k | ok = false; |
1367 | 10.0k | break; |
1368 | 10.0k | } |
1369 | 22.6k | instr.m_type=MWAWCellContent::FormulaInstruction::F_CellList; |
1370 | 22.6k | instr.m_position[1]=instr2.m_position[0]; |
1371 | 22.6k | instr.m_positionRelative[1]=instr2.m_positionRelative[0]; |
1372 | 22.6k | break; |
1373 | 32.7k | } |
1374 | 34.4k | case 0x15: { |
1375 | 34.4k | std::string text; |
1376 | 34.4k | ok=readString(endPos, text); |
1377 | 34.4k | if (!ok) break; |
1378 | 31.2k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Text; |
1379 | 31.2k | instr.m_content=text; |
1380 | 31.2k | break; |
1381 | 34.4k | } |
1382 | 841k | case 0x16: { |
1383 | 841k | if (pos+1+2>endPos) { |
1384 | 1.79k | ok=false; |
1385 | 1.79k | break; |
1386 | 1.79k | } |
1387 | 839k | isFunction=true; |
1388 | 839k | auto val=static_cast<int>(input->readULong(1)); |
1389 | 839k | arity=static_cast<int>(input->readULong(1)); |
1390 | 839k | std::string name(""); |
1391 | 839k | if (val<0x70) { |
1392 | 834k | static char const* const wh[] = { |
1393 | 834k | "Abs", "Acos", "Alert", "And", "Code", "Asin", "Atan", "Atan2", |
1394 | 834k | "Average", "Choose", "Char", "Concatenate", "Cos", "Count", "Date", "DateToText", |
1395 | | |
1396 | 834k | "DateValue", "Day", "DayName", "DayOfYear", "Degrees", "NA"/* error*/, "Exact", "Exp", |
1397 | 834k | "Frac", "FV", "HLookup", "Hour", "If", "Index", "Int", "IRR", |
1398 | | |
1399 | 834k | "IsBlank", "IsError", "IsNA", "IsNumber", "IsText", "Left", "Ln", "Log", |
1400 | 834k | "Log10", "LookUp", "Lower", "Match", "Max", "Mid", "Min", "Minute", |
1401 | | |
1402 | 834k | "MIRR", "Mod", "MonthName", "NA", "Not", "Now", "NPER", "NPV", |
1403 | 834k | "N" /*NumToText*/, "Or", "Pi", "PMT", "Product", "Proper", "PV", "Radians", |
1404 | | |
1405 | 834k | "Rand", "Rate", "Replace", "Right", "Round", "Second", "Sign", "Sqrt", |
1406 | 834k | "StDev", "Sum", "Tan", "Rept", "Value"/*TextToNum*/, "Time", "TimeToText", "TimeValue", |
1407 | | |
1408 | 834k | "", "Trim", "Type", "Upper", "Var", "VLookUp", "WeekDay", "WeekOfYear", |
1409 | 834k | "Year", "Find", "Column", "Row", "Fact", "Len", "Sin", "Month", |
1410 | | |
1411 | 834k | "Trunc", "Count2", "Macro", "Beep", "", "", "", "", |
1412 | 834k | "", "", "", "", "", "", "", "", |
1413 | 834k | }; |
1414 | 834k | name=wh[val]; |
1415 | 834k | } |
1416 | 839k | if (name.empty()) { |
1417 | 6.21k | f << "###"; |
1418 | 6.21k | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readFormula: find unknown function\n")); |
1419 | 6.21k | std::stringstream s; |
1420 | 6.21k | s << "Funct" << std::hex << val << std::dec; |
1421 | 6.21k | name=s.str(); |
1422 | 6.21k | } |
1423 | 839k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Function; |
1424 | 839k | instr.m_content=name; |
1425 | 839k | formula.push_back(instr); |
1426 | 839k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator; |
1427 | 839k | instr.m_content="("; |
1428 | 839k | break; |
1429 | 841k | } |
1430 | 24.7k | case 0x18: // bool |
1431 | 24.7k | if (pos+1+1>endPos) { |
1432 | 1.43k | ok=false; |
1433 | 1.43k | break; |
1434 | 1.43k | } |
1435 | 23.3k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Long; |
1436 | 23.3k | instr.m_longValue=static_cast<int>(input->readLong(1)); |
1437 | 23.3k | break; |
1438 | 26.7k | case 0x19: // database, cell |
1439 | 26.7k | if (pos+1+1>endPos) { |
1440 | 2.95k | ok = false; |
1441 | 2.95k | break; |
1442 | 2.95k | } |
1443 | 23.8k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Cell; |
1444 | 23.8k | instr.m_position[0]=MWAWVec2i(int(input->readULong(1)),0); |
1445 | 23.8k | instr.m_positionRelative[0]=MWAWVec2b(true, false); |
1446 | 23.8k | break; |
1447 | 485k | case 0x1f: // final equal sign |
1448 | 485k | return true; |
1449 | 4.85M | default: { |
1450 | 4.85M | std::string op(""); |
1451 | 4.85M | if (type<0x10) { |
1452 | 4.60M | op=ClarisWksDbaseContentInternal::s_listOperators[type].m_name; |
1453 | 4.60M | arity=ClarisWksDbaseContentInternal::s_listOperators[type].m_arity; |
1454 | 4.60M | } |
1455 | 4.85M | if (!op.empty()&&arity>0) { |
1456 | 4.60M | instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator; |
1457 | 4.60M | instr.m_content=op; |
1458 | 4.60M | isOperator=true; |
1459 | 4.60M | break; |
1460 | 4.60M | } |
1461 | 244k | f << "##type=" << std::hex << type << std::dec << ","; |
1462 | 244k | ok=false; |
1463 | 244k | break; |
1464 | 4.85M | } |
1465 | 6.41M | } |
1466 | 5.92M | error+=f.str(); |
1467 | 5.92M | if (!ok) { |
1468 | 284k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1469 | 284k | return false; |
1470 | 284k | } |
1471 | 5.64M | if (isOperator && instr.m_content=="%") { |
1472 | | // special case, percentage %2 => 2% |
1473 | 79.1k | if (!readFormula(cPos, endPos, formula, error)) |
1474 | 74.4k | return false; |
1475 | 4.66k | formula.push_back(instr); |
1476 | 4.66k | arity=0; |
1477 | 4.66k | } |
1478 | 5.56M | else if (!isOperator || arity==1) |
1479 | 1.06M | formula.push_back(instr); |
1480 | 5.56M | if (isFunction && arity>1) { |
1481 | 813k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator; |
1482 | 813k | instr.m_content=";"; |
1483 | 813k | } |
1484 | 6.23M | for (int i=0; i < arity; ++i) { |
1485 | 6.00M | if (!readFormula(cPos, endPos, formula, error)) |
1486 | 5.17M | return false; |
1487 | 833k | if (i+1==arity) break; |
1488 | 663k | formula.push_back(instr); |
1489 | 663k | } |
1490 | 395k | if (isFunction || instr.m_content=="(") { |
1491 | 46.5k | instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator; |
1492 | 46.5k | instr.m_content=")"; |
1493 | 46.5k | formula.push_back(instr); |
1494 | 46.5k | } |
1495 | | |
1496 | 395k | return true; |
1497 | 5.56M | } |
1498 | | |
1499 | | //////////////////////////////////////////////////////////// |
1500 | | // |
1501 | | //////////////////////////////////////////////////////////// |
1502 | | void ClarisWksDbaseContent::Record::updateFormulaCells(MWAWVec2i const &removeDelta) |
1503 | 276k | { |
1504 | 276k | if (m_content.m_contentType!=MWAWCellContent::C_FORMULA) |
1505 | 261k | return; |
1506 | 14.9k | for (auto &instr : m_content.m_formula) { |
1507 | 14.9k | int numCell=instr.m_type==MWAWCellContent::FormulaInstruction::F_Cell ? 1 : |
1508 | 14.9k | instr.m_type==MWAWCellContent::FormulaInstruction::F_CellList ? 2 : 0; |
1509 | 21.5k | for (int c=0; c<numCell; ++c) { |
1510 | 8.76k | instr.m_position[c]-=removeDelta; |
1511 | 8.76k | if (instr.m_position[c][0]<0 || instr.m_position[c][1]<0) { |
1512 | 2.12k | static bool first=true; |
1513 | 2.12k | if (first) { |
1514 | 7 | MWAW_DEBUG_MSG(("ClarisWksDbaseContent::Record::updateFormulaCells: some cell's positions are bad, remove formula\n")); |
1515 | 7 | first=false; |
1516 | 7 | } |
1517 | | // revert to the basic cell type |
1518 | 2.12k | m_content.m_contentType=m_valueType; |
1519 | 2.12k | return; |
1520 | 2.12k | } |
1521 | 8.76k | } |
1522 | 14.9k | } |
1523 | 14.5k | } |
1524 | | |
1525 | | // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: |