/src/libwps/src/lib/LotusSpreadsheet.cpp
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ |
2 | | /* libwps |
3 | | * Version: MPL 2.0 / LGPLv2.1+ |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * Major Contributor(s): |
10 | | * Copyright (C) 2006, 2007 Andrew Ziem |
11 | | * Copyright (C) 2004 Marc Maurer (uwog@uwog.net) |
12 | | * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch) |
13 | | * |
14 | | * For minor contributions see the git repository. |
15 | | * |
16 | | * Alternatively, the contents of this file may be used under the terms |
17 | | * of the GNU Lesser General Public License Version 2.1 or later |
18 | | * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are |
19 | | * applicable instead of those above. |
20 | | */ |
21 | | |
22 | | #include <stdlib.h> |
23 | | #include <string.h> |
24 | | |
25 | | #include <cmath> |
26 | | #include <limits> |
27 | | #include <map> |
28 | | #include <regex> |
29 | | #include <set> |
30 | | #include <stack> |
31 | | #include <sstream> |
32 | | #include <utility> |
33 | | |
34 | | #include <librevenge-stream/librevenge-stream.h> |
35 | | |
36 | | #include "libwps_internal.h" |
37 | | #include "libwps_tools_win.h" |
38 | | |
39 | | #include "WKSContentListener.h" |
40 | | #include "WKSSubDocument.h" |
41 | | #include "WPSEntry.h" |
42 | | #include "WPSFont.h" |
43 | | #include "WPSCell.h" |
44 | | #include "WPSStream.h" |
45 | | #include "WPSTable.h" |
46 | | |
47 | | #include "Lotus.h" |
48 | | #include "LotusStyleManager.h" |
49 | | |
50 | | #include "LotusSpreadsheet.h" |
51 | | |
52 | | static const int MAX_COLUMNS = 255; |
53 | | |
54 | | namespace LotusSpreadsheetInternal |
55 | | { |
56 | | |
57 | | /////////////////////////////////////////////////////////////////// |
58 | | //! a class used to store a style of a cell in LotusSpreadsheet |
59 | | struct Style final : public WPSCellFormat |
60 | | { |
61 | | //! construtor |
62 | | explicit Style(libwps_tools_win::Font::Type type) |
63 | 3.00M | : WPSCellFormat() |
64 | 3.00M | , m_fontType(type) |
65 | 3.00M | , m_extra("") |
66 | 3.00M | { |
67 | 3.00M | m_font.m_size=10; |
68 | 3.00M | } |
69 | 6.72M | Style(Style const &)=default; |
70 | 248k | Style &operator=(Style const &)=default; |
71 | | //! destructor |
72 | | ~Style() final; |
73 | | //! operator<< |
74 | | friend std::ostream &operator<<(std::ostream &o, Style const &style) |
75 | 0 | { |
76 | 0 | o << static_cast<WPSCellFormat const &>(style) << ","; |
77 | 0 | if (!style.m_extra.empty()) o << style.m_extra; |
78 | 0 | return o; |
79 | 0 | } |
80 | | //! operator== |
81 | | bool operator==(Style const &st) const |
82 | 0 | { |
83 | 0 | if (m_fontType!=st.m_fontType || WPSCellFormat::compare(st)!=0) return false; |
84 | 0 | return true; |
85 | 0 | } |
86 | | //! operator!= |
87 | | bool operator!=(Style const &st) const |
88 | 0 | { |
89 | 0 | return !(*this==st); |
90 | 0 | } |
91 | | //! font encoding type |
92 | | libwps_tools_win::Font::Type m_fontType; |
93 | | /** extra data */ |
94 | | std::string m_extra; |
95 | | }; |
96 | | |
97 | | Style::~Style() |
98 | 9.72M | { |
99 | 9.72M | } |
100 | | //! the extra style |
101 | | struct ExtraStyle |
102 | | { |
103 | | //! constructor |
104 | | ExtraStyle() |
105 | 496k | : m_color(WPSColor::black()) |
106 | 496k | , m_backColor(WPSColor::white()) |
107 | 496k | , m_format(0) |
108 | 496k | , m_flag(0) |
109 | 496k | , m_borders(0) |
110 | 496k | { |
111 | 496k | } |
112 | | //! returns true if the style is empty |
113 | | bool empty() const |
114 | 365k | { |
115 | | // find also f[8-c]ffffffXX, which seems to have a different meaning |
116 | 365k | if ((m_format&0xf0)==0xf0) return true; |
117 | 296k | return m_color.isBlack() && m_backColor.isWhite() && (m_format&0x38)==0 && m_borders==0; |
118 | 365k | } |
119 | | //! update the cell style |
120 | | void update(Style &style) const |
121 | 130k | { |
122 | 130k | WPSFont font=style.getFont(); |
123 | 130k | if (m_format&0x38) |
124 | 66.5k | { |
125 | 66.5k | if (m_format&0x8) font.m_attributes |= WPS_BOLD_BIT; |
126 | 66.5k | if (m_format&0x10) font.m_attributes |= WPS_ITALICS_BIT; |
127 | 66.5k | if (m_format&0x20) font.m_attributes |= WPS_UNDERLINE_BIT; |
128 | 66.5k | } |
129 | 130k | font.m_color=m_color; |
130 | 130k | style.setFont(font); |
131 | 130k | style.setBackgroundColor(m_backColor); |
132 | 130k | if (m_borders) |
133 | 93.9k | { |
134 | 469k | for (int i=0,decal=0; i<4; ++i, decal+=2) |
135 | 375k | { |
136 | 375k | int type=(m_borders>>decal)&3; |
137 | 375k | if (type==0) continue; |
138 | 244k | static int const wh[]= {WPSBorder::LeftBit,WPSBorder::RightBit,WPSBorder::TopBit,WPSBorder::BottomBit}; |
139 | 244k | WPSBorder border; |
140 | 244k | if (type==2) border.m_width=2; |
141 | 176k | else if (type==3) border.m_type=WPSBorder::Double; |
142 | 244k | style.setBorders(wh[i],border); |
143 | 244k | } |
144 | 93.9k | } |
145 | 130k | } |
146 | | //! the font color |
147 | | WPSColor m_color; |
148 | | //! the backgroun color |
149 | | WPSColor m_backColor; |
150 | | //! the format |
151 | | int m_format; |
152 | | //! the second flag: graph |
153 | | int m_flag; |
154 | | //! the border |
155 | | int m_borders; |
156 | | }; |
157 | | |
158 | | //! a class used to store the styles of a row in LotusSpreadsheet |
159 | | struct RowStyles |
160 | | { |
161 | | //! constructor |
162 | | RowStyles() |
163 | 39.2k | : m_colsToStyleMap() |
164 | 39.2k | { |
165 | 39.2k | } |
166 | | //! a map Vec2i(minCol,maxCol) to style |
167 | | std::map<Vec2i, Style> m_colsToStyleMap; |
168 | | }; |
169 | | |
170 | | //! a class used to store the extra style of a row in LotusSpreadsheet |
171 | | struct ExtraRowStyles |
172 | | { |
173 | | //! constructor |
174 | | ExtraRowStyles() |
175 | 47.7k | : m_colsToStyleMap() |
176 | 47.7k | { |
177 | 47.7k | } |
178 | | |
179 | | //! returns true if all style are empty |
180 | | bool empty() const |
181 | 3.98k | { |
182 | 3.98k | for (auto const &it : m_colsToStyleMap) |
183 | 8.57k | { |
184 | 8.57k | if (!it.second.empty()) return false; |
185 | 8.57k | } |
186 | 313 | return true; |
187 | 3.98k | } |
188 | | //! a map Vec2i(minCol,maxCol) to style |
189 | | std::map<Vec2i, ExtraStyle> m_colsToStyleMap; |
190 | | }; |
191 | | |
192 | | //! a list of position of a Lotus spreadsheet |
193 | | struct CellsList |
194 | | { |
195 | | //! constructor |
196 | | CellsList() |
197 | 19.1k | : m_positions() |
198 | 19.1k | { |
199 | 38.3k | for (auto &id : m_ids) id=0; |
200 | 19.1k | } |
201 | | //! operator<< |
202 | | friend std::ostream &operator<<(std::ostream &o, CellsList const &pos) |
203 | 0 | { |
204 | 0 | o << pos.m_positions; |
205 | 0 | for (int i=0; i<2; ++i) |
206 | 0 | { |
207 | 0 | if (pos.m_ids[i]) o << "[sheet" << i << "=" << pos.m_ids[i] << "]"; |
208 | 0 | } |
209 | 0 | o << ","; |
210 | 0 | return o; |
211 | 0 | } |
212 | | //! the sheets id |
213 | | int m_ids[2]; |
214 | | //! the first and last position |
215 | | WPSBox2i m_positions; |
216 | | }; |
217 | | |
218 | | //! a cellule of a Lotus spreadsheet |
219 | | class Cell final : public WPSCell |
220 | | { |
221 | | public: |
222 | | /// constructor |
223 | | Cell() |
224 | 1.22M | : m_input() |
225 | 1.22M | , m_styleId(-1) |
226 | 1.22M | , m_hAlignement(WPSCellFormat::HALIGN_DEFAULT) |
227 | 1.22M | , m_content() |
228 | 1.22M | , m_comment() { } |
229 | | /// constructor |
230 | | explicit Cell(RVNGInputStreamPtr const &input) |
231 | 136k | : m_input(input) |
232 | 136k | , m_styleId(-1) |
233 | 136k | , m_hAlignement(WPSCellFormat::HALIGN_DEFAULT) |
234 | 136k | , m_content() |
235 | 136k | , m_comment() { } |
236 | | //! operator<< |
237 | | friend std::ostream &operator<<(std::ostream &o, Cell const &cell); |
238 | | |
239 | | //! call when a cell must be send |
240 | | bool send(WPSListenerPtr &/*listener*/) final |
241 | 0 | { |
242 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Cell::send: must not be called\n")); |
243 | 0 | return false; |
244 | 0 | } |
245 | | |
246 | | //! call when the content of a cell must be send |
247 | | bool sendContent(WPSListenerPtr &/*listener*/) final; |
248 | | |
249 | | //! the input |
250 | | RVNGInputStreamPtr m_input; |
251 | | //! the style |
252 | | int m_styleId; |
253 | | //! the horizontal align (in dos file) |
254 | | WPSCellFormat::HorizontalAlignment m_hAlignement; |
255 | | //! the content |
256 | | WKSContentListener::CellContent m_content; |
257 | | //! the comment entry |
258 | | WPSEntry m_comment; |
259 | | }; |
260 | | bool Cell::sendContent(WPSListenerPtr &/*listener*/) |
261 | 0 | { |
262 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Cell::sendContent: must not be called\n")); |
263 | 0 | return false; |
264 | 0 | } |
265 | | |
266 | | //! operator<< |
267 | | std::ostream &operator<<(std::ostream &o, Cell const &cell) |
268 | 0 | { |
269 | 0 | o << reinterpret_cast<WPSCell const &>(cell) << cell.m_content << ","; |
270 | 0 | if (cell.m_styleId>=0) o << "style=" << cell.m_styleId << ","; |
271 | 0 | switch (cell.m_hAlignement) |
272 | 0 | { |
273 | 0 | case WPSCellFormat::HALIGN_LEFT: |
274 | 0 | o << "left,"; |
275 | 0 | break; |
276 | 0 | case WPSCellFormat::HALIGN_CENTER: |
277 | 0 | o << "centered,"; |
278 | 0 | break; |
279 | 0 | case WPSCellFormat::HALIGN_RIGHT: |
280 | 0 | o << "right,"; |
281 | 0 | break; |
282 | 0 | case WPSCellFormat::HALIGN_FULL: |
283 | 0 | o << "full,"; |
284 | 0 | break; |
285 | 0 | case WPSCellFormat::HALIGN_DEFAULT: |
286 | 0 | default: |
287 | 0 | break; // default |
288 | 0 | } |
289 | 0 | return o; |
290 | 0 | } |
291 | | |
292 | | /////////////////////////////////////////////////////////////////// |
293 | | //! the spreadsheet of a LotusSpreadsheet |
294 | | class Spreadsheet |
295 | | { |
296 | | public: |
297 | | //! a constructor |
298 | | Spreadsheet() |
299 | 5.30M | : m_name("") |
300 | 5.30M | , m_numCols(0) |
301 | 5.30M | , m_numRows(0) |
302 | 5.30M | , m_boundsColsMap() |
303 | 5.30M | , m_widthCols() |
304 | 5.30M | , m_rowHeightMap() |
305 | 5.30M | , m_heightDefault(16) |
306 | 5.30M | , m_rowPageBreaksList() |
307 | 5.30M | , m_positionToCellMap() |
308 | 5.30M | , m_rowToStyleIdMap() |
309 | 5.30M | , m_rowToExtraStyleMap() {} |
310 | | //! return a cell corresponding to a spreadsheet, create one if needed |
311 | | Cell &getCell(RVNGInputStreamPtr input, Vec2i const &pos) |
312 | 196k | { |
313 | 196k | if (m_positionToCellMap.find(pos)==m_positionToCellMap.end()) |
314 | 136k | { |
315 | 136k | Cell cell(input); |
316 | 136k | cell.setPosition(pos); |
317 | 136k | m_positionToCellMap[pos]=cell; |
318 | 136k | } |
319 | 196k | return m_positionToCellMap.find(pos)->second; |
320 | 196k | } |
321 | | //! set the columns size |
322 | | void setColumnWidth(int col, WPSColumnFormat const &format) |
323 | 1.38G | { |
324 | 1.38G | if (col >= int(m_widthCols.size())) |
325 | 543k | { |
326 | | // sanity check |
327 | 543k | if (col>MAX_COLUMNS || (!m_boundsColsMap.empty() && col >= int(m_widthCols.size())+10 && |
328 | 6.15k | m_boundsColsMap.find(col)==m_boundsColsMap.end())) |
329 | 6.10k | { |
330 | 6.10k | WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Spreadsheet::setColumnWidth: the column %d seems bad\n", col)); |
331 | 6.10k | return; |
332 | 6.10k | } |
333 | 537k | WPSColumnFormat defCol; |
334 | 537k | defCol.m_useOptimalWidth=true; |
335 | 537k | m_widthCols.resize(size_t(col)+1, defCol); |
336 | 537k | } |
337 | 1.38G | m_widthCols[size_t(col)] = format; |
338 | 1.38G | if (col >= m_numCols) m_numCols=col+1; |
339 | 1.38G | } |
340 | | //! returns the row size in point |
341 | | WPSRowFormat getRowHeight(int row) const |
342 | 1.08M | { |
343 | 1.08M | auto rIt=m_rowHeightMap.lower_bound(Vec2i(-1,row)); |
344 | 1.08M | if (rIt!=m_rowHeightMap.end() && rIt->first[0]<=row && rIt->first[1]>=row) |
345 | 54.4k | return rIt->second; |
346 | 1.03M | WPSRowFormat format(m_heightDefault); |
347 | 1.03M | format.m_isMinimalHeight=true; |
348 | 1.03M | return format; |
349 | 1.08M | } |
350 | | //! set the rows size |
351 | | void setRowHeight(int row, WPSRowFormat const &format) |
352 | 243M | { |
353 | 243M | m_rowHeightMap[Vec2i(row,row)]=format; |
354 | 243M | } |
355 | | //! returns the position corresponding to a cell |
356 | | Vec2f getPosition(Vec2i const &cell) const |
357 | 22.4k | { |
358 | | // first compute the height |
359 | 22.4k | int lastRow=0; |
360 | 22.4k | float h=0; |
361 | 22.4k | auto rIt=m_rowHeightMap.begin(); |
362 | 36.0k | while (rIt!=m_rowHeightMap.end() && rIt->first[1]<cell[1]) |
363 | 13.6k | { |
364 | 13.6k | if (rIt->first[0]>lastRow) |
365 | 9.80k | { |
366 | 9.80k | h+=float(rIt->first[0]-lastRow)*m_heightDefault; |
367 | 9.80k | lastRow=rIt->first[0]; |
368 | 9.80k | } |
369 | 13.6k | float rHeight=rIt->second.m_height>=0 ? rIt->second.m_height : m_heightDefault; |
370 | 13.6k | h+=float(rIt->first[1]+1-lastRow)*rHeight; |
371 | 13.6k | lastRow=rIt->first[1]+1; |
372 | 13.6k | ++rIt; |
373 | 13.6k | } |
374 | 22.4k | if (lastRow<cell[1]) |
375 | 9.71k | { |
376 | 9.71k | if (rIt!=m_rowHeightMap.end() && rIt->first[0]<cell[1] && rIt->second.m_height>=0) |
377 | 0 | h+=float(cell[1]-lastRow)*rIt->second.m_height; |
378 | 9.71k | else |
379 | 9.71k | h+=float(cell[1]-lastRow)*m_heightDefault; |
380 | | |
381 | 9.71k | } |
382 | | // now compute the width |
383 | 22.4k | size_t const numCols = m_widthCols.size(); |
384 | 22.4k | float w=0; |
385 | 45.4k | for (size_t i = 0; i < numCols && i<size_t(cell[0]); i++) |
386 | 23.0k | w+=m_widthCols[i].m_width>=0 ? m_widthCols[i].m_width : 72; |
387 | 22.4k | if (numCols<size_t(cell[0])) |
388 | 14.5k | w+=72*float(size_t(cell[0])-numCols); |
389 | 22.4k | return Vec2f(w, h); |
390 | 22.4k | } |
391 | | //! try to compress the list of row height |
392 | | void compressRowHeights() |
393 | 2.27M | { |
394 | 2.27M | auto oldMap=m_rowHeightMap; |
395 | 2.27M | m_rowHeightMap.clear(); |
396 | 2.27M | auto rIt=oldMap.begin(); |
397 | 2.27M | WPSRowFormat actHeight; |
398 | 2.27M | WPSRowFormat defHeight(m_heightDefault); |
399 | 2.27M | defHeight.m_isMinimalHeight=true; |
400 | 2.27M | Vec2i actPos(0,-1); |
401 | 3.15M | while (rIt!=oldMap.end()) |
402 | 880k | { |
403 | | // first check for not filled row |
404 | 880k | if (rIt->first[0]!=actPos[1]+1) |
405 | 20.6k | { |
406 | 20.6k | if (actHeight==defHeight) |
407 | 0 | actPos[1]=rIt->first[0]-1; |
408 | 20.6k | else |
409 | 20.6k | { |
410 | 20.6k | if (actPos[1]>=actPos[0]) |
411 | 15.9k | m_rowHeightMap[actPos]=actHeight; |
412 | 20.6k | actHeight=defHeight; |
413 | 20.6k | actPos=Vec2i(actPos[1]+1, rIt->first[0]-1); |
414 | 20.6k | } |
415 | 20.6k | } |
416 | 880k | if (rIt->second!=actHeight) |
417 | 31.1k | { |
418 | 31.1k | if (actPos[1]>=actPos[0]) |
419 | 25.1k | m_rowHeightMap[actPos]=actHeight; |
420 | 31.1k | actPos[0]=rIt->first[0]; |
421 | 31.1k | actHeight=rIt->second; |
422 | 31.1k | } |
423 | 880k | actPos[1]=rIt->first[1]; |
424 | 880k | ++rIt; |
425 | 880k | } |
426 | 2.27M | if (actPos[1]>=actPos[0]) |
427 | 10.6k | m_rowHeightMap[actPos]=actHeight; |
428 | 2.27M | } |
429 | | //! convert the m_widthColsInChar in a vector of of point size |
430 | | std::vector<WPSColumnFormat> getWidths() const |
431 | 2.27M | { |
432 | 2.27M | std::vector<WPSColumnFormat> widths; |
433 | 2.27M | WPSColumnFormat actWidth; |
434 | 2.27M | int repeat=0; |
435 | 2.27M | for (auto const &newWidth : m_widthCols) |
436 | 432k | { |
437 | 432k | if (repeat && newWidth!=actWidth) |
438 | 17.3k | { |
439 | 17.3k | actWidth.m_numRepeat=repeat; |
440 | 17.3k | widths.push_back(actWidth); |
441 | 17.3k | repeat=0; |
442 | 17.3k | } |
443 | 432k | if (repeat==0) |
444 | 22.0k | actWidth=newWidth; |
445 | 432k | ++repeat; |
446 | 432k | } |
447 | 2.27M | if (repeat) |
448 | 4.75k | { |
449 | 4.75k | actWidth.m_numRepeat=repeat; |
450 | 4.75k | widths.push_back(actWidth); |
451 | 4.75k | } |
452 | 2.27M | return widths; |
453 | 2.27M | } |
454 | | //! returns the row style id corresponding to a sheetId (or -1) |
455 | | int getRowStyleId(int row) const |
456 | 1.08M | { |
457 | 1.08M | auto it=m_rowToStyleIdMap.lower_bound(Vec2i(-1, row)); |
458 | 1.08M | if (it!=m_rowToStyleIdMap.end() && it->first[0]<=row && row<=it->first[1]) |
459 | 16.4k | return int(it->second); |
460 | 1.06M | return -1; |
461 | 1.08M | } |
462 | | |
463 | | //! returns true if the spreedsheet is empty |
464 | | bool empty() const |
465 | 2.40M | { |
466 | 2.40M | return m_positionToCellMap.empty() && m_rowToStyleIdMap.empty() && m_name.empty(); |
467 | 2.40M | } |
468 | | /** the sheet name */ |
469 | | librevenge::RVNGString m_name; |
470 | | /** the number of columns */ |
471 | | int m_numCols; |
472 | | /** the number of rows */ |
473 | | int m_numRows; |
474 | | /** a map used to stored the min/max row of each columns */ |
475 | | std::map<int, Vec2i> m_boundsColsMap; |
476 | | /** the column size */ |
477 | | std::vector<WPSColumnFormat> m_widthCols; |
478 | | /** the map Vec2i(min row, max row) to size in points */ |
479 | | std::map<Vec2i,WPSRowFormat> m_rowHeightMap; |
480 | | /** the default row size in point */ |
481 | | float m_heightDefault; |
482 | | /** the list of row page break */ |
483 | | std::vector<int> m_rowPageBreaksList; |
484 | | /** a map cell to not empty cells */ |
485 | | std::map<Vec2i, Cell> m_positionToCellMap; |
486 | | //! map Vec2i(min row, max row) to state row style id |
487 | | std::map<Vec2i,size_t> m_rowToStyleIdMap; |
488 | | //! map row to extra style |
489 | | std::map<int,ExtraRowStyles> m_rowToExtraStyleMap; |
490 | | }; |
491 | | |
492 | | // ------------------------------------------------------------ |
493 | | // Lotus 123 |
494 | | // ------------------------------------------------------------ |
495 | | //! the format style for lotus 123 |
496 | | struct Format123Style final : public WPSCellFormat |
497 | | { |
498 | | //! constructor |
499 | | Format123Style() |
500 | 405k | : m_alignAcrossColumn(false) |
501 | 405k | { |
502 | 405k | } |
503 | 2.74M | Format123Style(Format123Style const &)=default; |
504 | 208k | Format123Style &operator=(Format123Style const &)=default; |
505 | | //! destructor |
506 | | ~Format123Style() final; |
507 | | //! update the cell style |
508 | | void update(Style &style) const |
509 | 656k | { |
510 | 656k | style.setDTFormat(getFormat(), getDTFormat()); |
511 | 656k | style.setFormat(getFormat(), getSubFormat()); |
512 | 656k | style.setDigits(digits()); |
513 | 656k | } |
514 | | //! operator== |
515 | | bool operator==(Format123Style const &f) const |
516 | 96.4k | { |
517 | 96.4k | return m_alignAcrossColumn==f.m_alignAcrossColumn && compare(f)==0; |
518 | 96.4k | } |
519 | | //! flag to know if we must align across column |
520 | | bool m_alignAcrossColumn; |
521 | | }; |
522 | | |
523 | | Format123Style::~Format123Style() |
524 | 3.15M | { |
525 | 3.15M | } |
526 | | |
527 | | //! the extra style for lotus 123 |
528 | | struct Extra123Style |
529 | | { |
530 | | //! constructor |
531 | | Extra123Style() |
532 | 270k | { |
533 | 270k | for (auto &border : m_borders) |
534 | 541k | border.m_style=WPSBorder::None; |
535 | 270k | } |
536 | | //! returns true if the style is empty |
537 | | bool empty() const |
538 | 0 | { |
539 | 0 | for (const auto &border : m_borders) |
540 | 0 | { |
541 | 0 | if (!border.isEmpty()) return false; |
542 | 0 | } |
543 | 0 | return true; |
544 | 0 | } |
545 | | //! operator== |
546 | | bool operator==(Extra123Style const &f) const |
547 | 41.6k | { |
548 | 83.6k | for (int i=0; i<2; ++i) |
549 | 65.2k | { |
550 | 65.2k | if (m_borders[i]!=f.m_borders[i]) |
551 | 23.3k | return false; |
552 | 65.2k | } |
553 | 18.3k | return true; |
554 | 41.6k | } |
555 | | //! update the cell style |
556 | | void update(Style &style) const |
557 | 245k | { |
558 | 737k | for (int i=0; i<2; ++i) |
559 | 491k | { |
560 | 491k | if (m_borders[i].isEmpty()) continue; |
561 | 391k | style.setBorders(i==0 ? WPSBorder::TopBit : WPSBorder::LeftBit,m_borders[i]); |
562 | 391k | } |
563 | 245k | } |
564 | | //! the top/left border |
565 | | WPSBorder m_borders[2]; |
566 | | }; |
567 | | |
568 | | //! a class used to store the styles of a table in LotusSpreadsheet in a lotus 123 files |
569 | | struct Table123Styles |
570 | | { |
571 | | //! constructor |
572 | | Table123Styles() |
573 | 249k | : m_defaultCellId(-1) |
574 | 249k | , m_rowsToColsToCellIdMap() |
575 | 249k | , m_rowsToColsToExtraStyleMap() |
576 | 249k | , m_rowsToColsToFormatStyleMap() |
577 | 249k | { |
578 | 249k | } |
579 | | //! add a style to a list of cell |
580 | | void addCellStyle(Vec2i const &cols, Vec2i const &rows, int cellId) |
581 | 152k | { |
582 | 152k | if (m_rowsToColsToCellIdMap.find(rows)==m_rowsToColsToCellIdMap.end()) |
583 | 33.3k | m_rowsToColsToCellIdMap[rows]= std::map<Vec2i,int>(); |
584 | 152k | auto &map=m_rowsToColsToCellIdMap.find(rows)->second; |
585 | 152k | if (map.find(cols)!=map.end()) |
586 | 26.1k | { |
587 | 26.1k | WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Table123Styles::addCellStyle: find dupplicated cell\n")); |
588 | 26.1k | } |
589 | 152k | auto it=map.lower_bound(cols); |
590 | 152k | if (!map.empty() && it!=map.begin()) --it; |
591 | 152k | if (it!=map.end() && it->first[1]+1==cols[0] && it->second==cellId) |
592 | 70.6k | { |
593 | 70.6k | Vec2i newCols=it->first; |
594 | 70.6k | map.erase(newCols); |
595 | 70.6k | newCols[1]=cols[1]; |
596 | 70.6k | map[newCols]=cellId; |
597 | 70.6k | } |
598 | 81.3k | else |
599 | 81.3k | map[cols]=cellId; |
600 | 152k | } |
601 | | //! add a extra style to a list of cell |
602 | | void addCellStyle(Vec2i const &cols, Vec2i const &rows, Extra123Style const &extra) |
603 | 164k | { |
604 | 164k | if (m_rowsToColsToExtraStyleMap.find(rows)==m_rowsToColsToExtraStyleMap.end()) |
605 | 41.1k | m_rowsToColsToExtraStyleMap[rows]= std::map<Vec2i,Extra123Style>(); |
606 | 164k | auto &map=m_rowsToColsToExtraStyleMap.find(rows)->second; |
607 | | // checkme: sometimes, we can retrieve the same cells again |
608 | 164k | auto it=map.lower_bound(cols); |
609 | 164k | if (!map.empty() && it!=map.begin()) --it; |
610 | 164k | if (it!=map.end() && it->first[1]+1==cols[0] && it->second==extra) |
611 | 18.3k | { |
612 | 18.3k | Vec2i newCols=it->first; |
613 | 18.3k | map.erase(newCols); |
614 | 18.3k | newCols[1]=cols[1]; |
615 | 18.3k | map[newCols]=extra; |
616 | 18.3k | } |
617 | 146k | else |
618 | 146k | map[cols]=extra; |
619 | 164k | } |
620 | | //! add a extra style to a list of cell |
621 | | void addCellStyle(Vec2i const &cols, Vec2i const &rows, Format123Style const &format) |
622 | 207k | { |
623 | 207k | if (m_rowsToColsToFormatStyleMap.find(rows)==m_rowsToColsToFormatStyleMap.end()) |
624 | 42.7k | m_rowsToColsToFormatStyleMap[rows]= std::map<Vec2i,Format123Style>(); |
625 | 207k | auto &map=m_rowsToColsToFormatStyleMap.find(rows)->second; |
626 | 207k | if (map.find(cols)!=map.end()) |
627 | 55.5k | { |
628 | 55.5k | WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Table123Styles::addCellStyle: find dupplicated cell\n")); |
629 | 55.5k | } |
630 | 207k | auto it=map.lower_bound(cols); |
631 | 207k | if (!map.empty() && it!=map.begin()) --it; |
632 | 207k | if (it!=map.end() && it->first[1]+1==cols[0] && it->second==format) |
633 | 69.4k | { |
634 | 69.4k | Vec2i newCols=it->first; |
635 | 69.4k | map.erase(newCols); |
636 | 69.4k | newCols[1]=cols[1]; |
637 | 69.4k | map[newCols]=format; |
638 | 69.4k | } |
639 | 137k | else |
640 | 137k | map[cols]=format; |
641 | 207k | } |
642 | | //! the default cell style |
643 | | int m_defaultCellId; |
644 | | //! map rows to cols to cell id |
645 | | std::map<Vec2i, std::map<Vec2i,int> > m_rowsToColsToCellIdMap; |
646 | | //! map rows to cols to extra style |
647 | | std::map<Vec2i, std::map<Vec2i,Extra123Style> > m_rowsToColsToExtraStyleMap; |
648 | | //! map rows to cols to format style |
649 | | std::map<Vec2i, std::map<Vec2i,Format123Style> > m_rowsToColsToFormatStyleMap; |
650 | | }; |
651 | | |
652 | | //! the state of LotusSpreadsheet |
653 | | struct State |
654 | | { |
655 | | //! constructor |
656 | | State() |
657 | 121k | : m_version(-1) |
658 | 121k | , m_spreadsheetList() |
659 | 121k | , m_nameToCellsMap() |
660 | 121k | , m_rowStylesList() |
661 | 121k | , m_rowSheetIdToStyleIdMap() |
662 | 121k | , m_rowSheetIdToChildRowIdMap() |
663 | 121k | , m_sheetIdToTableStyleMap() |
664 | 121k | , m_sheetCurrentId(-1) |
665 | 121k | { |
666 | 121k | m_spreadsheetList.resize(1); |
667 | 121k | } |
668 | | //! returns the number of spreadsheet |
669 | | int getNumSheet() const |
670 | 2.35M | { |
671 | 2.35M | return int(m_spreadsheetList.size()); |
672 | 2.35M | } |
673 | | //! returns the ith spreadsheet |
674 | | Spreadsheet &getSheet(int id) |
675 | 1.03G | { |
676 | 1.03G | if (id<0||id>=int(m_spreadsheetList.size())) |
677 | 1.03G | { |
678 | 1.03G | WPS_DEBUG_MSG(("LotusSpreadsheetInternal::State::getSheet: can not find spreadsheet %d\n", id)); |
679 | 1.03G | static Spreadsheet empty; |
680 | 1.03G | return empty; |
681 | 1.03G | } |
682 | 2.43M | return m_spreadsheetList[size_t(id)]; |
683 | 1.03G | } |
684 | | //! returns a table style for a sheet(if it exists) |
685 | | Table123Styles const *getTableStyle(int id) const |
686 | 2.27M | { |
687 | 2.27M | Vec2i pos(-1,id); |
688 | 2.27M | auto cIt=m_sheetIdToTableStyleMap.lower_bound(pos); |
689 | 2.27M | if (cIt==m_sheetIdToTableStyleMap.end() || cIt->first[0]>id || cIt->first[1]<id) |
690 | 1.84M | return nullptr; |
691 | 435k | return &cIt->second; |
692 | 2.27M | } |
693 | | //! returns a table style for a sheet zone, create it if needed |
694 | | Table123Styles *getTablesStyle(Vec2i pos) |
695 | 597k | { |
696 | 597k | auto cIt=m_sheetIdToTableStyleMap.lower_bound(pos); |
697 | 597k | if (cIt==m_sheetIdToTableStyleMap.end() || cIt->first[0]>pos[1] || cIt->first[1]<pos[0]) |
698 | 47.3k | { |
699 | 47.3k | m_sheetIdToTableStyleMap[pos]=Table123Styles(); |
700 | 47.3k | return &m_sheetIdToTableStyleMap.find(pos)->second; |
701 | 47.3k | } |
702 | 550k | if (cIt->first==pos) |
703 | 476k | return &cIt->second; |
704 | 73.9k | Vec2i actPos=cIt->first; |
705 | 73.9k | if (actPos[0]>pos[0] || actPos[1]<pos[1]) |
706 | 17.1k | { |
707 | 17.1k | WPS_DEBUG_MSG(("LotusSpreadsheetInternal::State::getTablesStyle: problem when creating spreadsheet %d,%d\n", pos[0], pos[1])); |
708 | 17.1k | return nullptr; |
709 | 17.1k | } |
710 | 56.8k | auto const &table=cIt->second; |
711 | 56.8k | if (actPos[0]<pos[0]) |
712 | 48.6k | { |
713 | 48.6k | Vec2i newPos(actPos[0],pos[0]); |
714 | 48.6k | m_sheetIdToTableStyleMap[newPos]=table; |
715 | 48.6k | } |
716 | 56.8k | m_sheetIdToTableStyleMap[pos]=table; |
717 | 56.8k | if (actPos[1]>pos[1]) |
718 | 56.8k | { |
719 | 56.8k | Vec2i newPos(pos[1],actPos[1]); |
720 | 56.8k | m_sheetIdToTableStyleMap[newPos]=table; |
721 | 56.8k | } |
722 | 56.8k | m_sheetIdToTableStyleMap.erase(actPos); |
723 | 56.8k | return &m_sheetIdToTableStyleMap.find(pos)->second; |
724 | 73.9k | } |
725 | | //! returns the ith spreadsheet name |
726 | | librevenge::RVNGString getSheetName(int id) const |
727 | 2.37M | { |
728 | 2.37M | if (id>=0 && id<int(m_spreadsheetList.size()) && !m_spreadsheetList[size_t(id)].m_name.empty()) |
729 | 18.4k | return m_spreadsheetList[size_t(id)].m_name; |
730 | 2.35M | librevenge::RVNGString name; |
731 | 2.35M | name.sprintf("Sheet%d", id+1); |
732 | 2.35M | return name; |
733 | 2.37M | } |
734 | | //! the file version |
735 | | int m_version; |
736 | | //! the list of spreadsheet ( first: main spreadsheet, other report spreadsheet ) |
737 | | std::vector<Spreadsheet> m_spreadsheetList; |
738 | | //! map name to position |
739 | | std::map<std::string, CellsList> m_nameToCellsMap; |
740 | | //! the list of row styles |
741 | | std::vector<RowStyles> m_rowStylesList; |
742 | | //! map Vec2i(row, sheetId) to row style id |
743 | | std::map<Vec2i,size_t> m_rowSheetIdToStyleIdMap; |
744 | | //! map Vec2i(row, sheetId) to child style |
745 | | std::multimap<Vec2i,Vec2i> m_rowSheetIdToChildRowIdMap; |
746 | | //! map Vec2i(sheetMin, sheetMax) to table style |
747 | | std::map<Vec2i,Table123Styles> m_sheetIdToTableStyleMap; |
748 | | //! the sheet id |
749 | | int m_sheetCurrentId; |
750 | | }; |
751 | | |
752 | | //! Internal: the subdocument of a LotusSpreadsheet |
753 | | class SubDocument final : public WKSSubDocument |
754 | | { |
755 | | public: |
756 | | //! constructor for a text entry |
757 | | SubDocument(RVNGInputStreamPtr const &input, LotusSpreadsheet &sheetParser, WPSEntry const &entry) |
758 | 2.23k | : WKSSubDocument(input, nullptr) |
759 | 2.23k | , m_sheetParser(&sheetParser) |
760 | 2.23k | , m_entry(entry) {} |
761 | | //! destructor |
762 | 2.23k | ~SubDocument() final {} |
763 | | |
764 | | //! operator== |
765 | | bool operator==(std::shared_ptr<WPSSubDocument> const &doc) const final |
766 | 0 | { |
767 | 0 | if (!doc || !WKSSubDocument::operator==(doc)) |
768 | 0 | return false; |
769 | 0 | auto const *sDoc = dynamic_cast<SubDocument const *>(doc.get()); |
770 | 0 | if (!sDoc) return false; |
771 | 0 | if (m_sheetParser != sDoc->m_sheetParser) return false; |
772 | 0 | if (m_entry != sDoc->m_entry) return false; |
773 | 0 | return true; |
774 | 0 | } |
775 | | |
776 | | //! the parser function |
777 | | void parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType subDocumentType) final; |
778 | | //! the spreadsheet parse |
779 | | LotusSpreadsheet *m_sheetParser; |
780 | | //! a text zone entry |
781 | | WPSEntry m_entry; |
782 | | private: |
783 | | SubDocument(SubDocument const &) = delete; |
784 | | SubDocument &operator=(SubDocument const &) = delete; |
785 | | }; |
786 | | |
787 | | void SubDocument::parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType) |
788 | 2.23k | { |
789 | 2.23k | if (!m_sheetParser) |
790 | 0 | { |
791 | 0 | listener->insertCharacter(' '); |
792 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheetInternal::SubDocument::parse: bad parser\n")); |
793 | 0 | return; |
794 | 0 | } |
795 | 2.23k | m_sheetParser->sendTextNote(m_input, m_entry); |
796 | 2.23k | } |
797 | | } |
798 | | |
799 | | // constructor, destructor |
800 | | LotusSpreadsheet::LotusSpreadsheet(LotusParser &parser) |
801 | 60.7k | : m_listener() |
802 | 60.7k | , m_mainParser(parser) |
803 | 60.7k | , m_styleManager(parser.m_styleManager) |
804 | 60.7k | , m_state(new LotusSpreadsheetInternal::State) |
805 | 60.7k | { |
806 | 60.7k | } |
807 | | |
808 | | LotusSpreadsheet::~LotusSpreadsheet() |
809 | 60.7k | { |
810 | 60.7k | } |
811 | | |
812 | | void LotusSpreadsheet::cleanState() |
813 | 60.6k | { |
814 | 60.6k | m_state.reset(new LotusSpreadsheetInternal::State); |
815 | 60.6k | } |
816 | | |
817 | | bool LotusSpreadsheet::getLeftTopPosition(Vec2i const &cell, int sheetId, Vec2f &pos) |
818 | 22.4k | { |
819 | | // set to default |
820 | 22.4k | pos=Vec2f(float(cell[0]>=0 ? cell[0]*72 : 0), float(cell[1]>=0 ? cell[1]*16 : 0)); |
821 | 22.4k | if (sheetId<0||sheetId>=m_state->getNumSheet() || cell[0]<0 || cell[1]<0) |
822 | 0 | { |
823 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::getLeftTopPosition: the sheet %d seems bad\n", sheetId)); |
824 | 0 | return true; |
825 | 0 | } |
826 | 22.4k | auto const &sheet = m_state->getSheet(sheetId); |
827 | 22.4k | pos=sheet.getPosition(cell); |
828 | 22.4k | return true; |
829 | 22.4k | } |
830 | | |
831 | | librevenge::RVNGString LotusSpreadsheet::getSheetName(int id) const |
832 | 2.37M | { |
833 | 2.37M | return m_state->getSheetName(id); |
834 | 2.37M | } |
835 | | |
836 | | void LotusSpreadsheet::updateState() |
837 | 28.1k | { |
838 | | // update the state correspondance between row and row's styles |
839 | 28.1k | if (!m_state->m_rowSheetIdToChildRowIdMap.empty()) |
840 | 1.33k | { |
841 | 1.33k | std::set<Vec2i> seens; |
842 | 1.33k | std::stack<Vec2i> toDo; |
843 | 1.33k | for (auto const &it : m_state->m_rowSheetIdToStyleIdMap) |
844 | 9.41k | toDo.push(it.first); |
845 | 168k | while (!toDo.empty()) |
846 | 166k | { |
847 | 166k | Vec2i pos=toDo.top(); |
848 | 166k | toDo.pop(); |
849 | 166k | if (seens.find(pos)!=seens.end()) |
850 | 98.5k | { |
851 | 98.5k | WPS_DEBUG_MSG(("LotusSpreadsheet::updateState: dupplicated position, something is bad\n")); |
852 | 98.5k | continue; |
853 | 98.5k | } |
854 | 68.3k | seens.insert(pos); |
855 | 68.3k | auto cIt=m_state->m_rowSheetIdToChildRowIdMap.lower_bound(pos); |
856 | 68.3k | if (cIt==m_state->m_rowSheetIdToChildRowIdMap.end() || cIt->first!=pos) |
857 | 11.3k | continue; |
858 | 56.9k | if (m_state->m_rowSheetIdToStyleIdMap.find(pos)==m_state->m_rowSheetIdToStyleIdMap.end()) |
859 | 0 | { |
860 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::updateState: something is bad\n")); |
861 | 0 | continue; |
862 | 0 | } |
863 | 56.9k | size_t finalPos=m_state->m_rowSheetIdToStyleIdMap.find(pos)->second; |
864 | 214k | while (cIt!=m_state->m_rowSheetIdToChildRowIdMap.end() && cIt->first==pos) |
865 | 157k | { |
866 | 157k | Vec2i const &cPos=cIt++->second; |
867 | 157k | m_state->m_rowSheetIdToStyleIdMap[cPos]=finalPos; |
868 | 157k | toDo.push(cPos); |
869 | 157k | } |
870 | 56.9k | } |
871 | 1.33k | } |
872 | | |
873 | | // time to update each sheet rows style map |
874 | 33.7k | for (auto it=m_state->m_rowSheetIdToStyleIdMap.begin(); it!=m_state->m_rowSheetIdToStyleIdMap.end();) |
875 | 5.54k | { |
876 | 5.54k | int sheetId=it->first[1]; |
877 | 5.54k | LotusSpreadsheetInternal::Spreadsheet *sheet=nullptr; |
878 | 5.54k | if (sheetId>=0 && sheetId<int(m_state->m_spreadsheetList.size())) |
879 | 4.51k | sheet=&m_state->m_spreadsheetList[size_t(sheetId)]; |
880 | 1.02k | else |
881 | 1.02k | { |
882 | 1.02k | WPS_DEBUG_MSG(("LotusSpreadsheet::updateState: can not find sheet %d\n", sheetId)); |
883 | 1.02k | } |
884 | 5.54k | int lastStyleId=-1; |
885 | 5.54k | Vec2i rows(0,-1); |
886 | 77.6k | while (it!=m_state->m_rowSheetIdToStyleIdMap.end() && it->first[1]==sheetId) |
887 | 72.1k | { |
888 | 72.1k | if (lastStyleId!=int(it->second) || it->first[0]!=rows[1]+1) |
889 | 17.3k | { |
890 | 17.3k | if (lastStyleId>=0 && sheet) |
891 | 11.4k | sheet->m_rowToStyleIdMap[rows]=size_t(lastStyleId); |
892 | 17.3k | lastStyleId=int(it->second); |
893 | 17.3k | rows=Vec2i(it->first[0], it->first[0]); |
894 | 17.3k | } |
895 | 54.7k | else |
896 | 54.7k | ++rows[1]; |
897 | 72.1k | ++it; |
898 | 72.1k | } |
899 | 5.54k | if (lastStyleId>=0 && sheet) |
900 | 4.51k | sheet->m_rowToStyleIdMap[rows]=size_t(lastStyleId); |
901 | 5.54k | } |
902 | 28.1k | } |
903 | | |
904 | | void LotusSpreadsheet::setLastSpreadsheetId(int id) |
905 | 75.7k | { |
906 | 75.7k | if (id<0) |
907 | 0 | { |
908 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::setLastSpreadsheetId: the id:%d seems bad\n", id)); |
909 | 0 | return; |
910 | 0 | } |
911 | 75.7k | m_state->m_spreadsheetList.resize(size_t(id+1)); |
912 | 75.7k | } |
913 | | |
914 | | int LotusSpreadsheet::version() const |
915 | 1.43M | { |
916 | 1.43M | if (m_state->m_version<0) |
917 | 25.5k | m_state->m_version=m_mainParser.version(); |
918 | 1.43M | return m_state->m_version; |
919 | 1.43M | } |
920 | | |
921 | | bool LotusSpreadsheet::hasSomeSpreadsheetData() const |
922 | 45.6k | { |
923 | 45.6k | for (auto const &sheet : m_state->m_spreadsheetList) |
924 | 2.40M | { |
925 | 2.40M | if (!sheet.empty()) |
926 | 13.4k | return true; |
927 | 2.40M | } |
928 | 32.2k | return false; |
929 | 45.6k | } |
930 | | |
931 | | //////////////////////////////////////////////////////////// |
932 | | // low level |
933 | | |
934 | | //////////////////////////////////////////////////////////// |
935 | | // parse sheet data |
936 | | //////////////////////////////////////////////////////////// |
937 | | bool LotusSpreadsheet::readColumnDefinition(std::shared_ptr<WPSStream> stream) |
938 | 46.8k | { |
939 | 46.8k | if (!stream) return false; |
940 | 46.8k | RVNGInputStreamPtr &input=stream->m_input; |
941 | 46.8k | libwps::DebugFile &ascFile=stream->m_ascii; |
942 | 46.8k | libwps::DebugStream f; |
943 | | |
944 | 46.8k | long pos = input->tell(); |
945 | 46.8k | auto type = long(libwps::read16(input)); |
946 | 46.8k | if (type != 0x1f) |
947 | 0 | { |
948 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnDefinition: not a column definition\n")); |
949 | 0 | return false; |
950 | 0 | } |
951 | 46.8k | auto sz = long(libwps::readU16(input)); |
952 | 46.8k | f << "Entries(ColDef):"; |
953 | 46.8k | if (sz<8 || (sz%4)) |
954 | 1.85k | { |
955 | 1.85k | WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnDefinition: the zone is too short\n")); |
956 | 1.85k | f << "###"; |
957 | 1.85k | ascFile.addPos(pos); |
958 | 1.85k | ascFile.addNote(f.str().c_str()); |
959 | 1.85k | return true; |
960 | 1.85k | } |
961 | 44.9k | auto sheetId=int(libwps::readU8(input)); |
962 | 44.9k | f << "sheet[id]=" << sheetId << ","; |
963 | 44.9k | auto col=int(libwps::readU8(input)); |
964 | 44.9k | f << "col=" << col << ","; |
965 | 44.9k | auto N=int(libwps::readU8(input)); |
966 | 44.9k | if (N!=1) f << "N=" << N << ","; |
967 | 44.9k | auto val=int(libwps::readU8(input)); // between 0 and 94 |
968 | 44.9k | if (val) f << "f0=" << val << ","; |
969 | 44.9k | if (sz!=4+4*N) |
970 | 2.87k | { |
971 | 2.87k | WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnDefinition: the number of columns seems bad\n")); |
972 | 2.87k | f << "###N,"; |
973 | 2.87k | if (sz==8) |
974 | 2.70k | N=1; |
975 | 173 | else |
976 | 173 | { |
977 | 173 | ascFile.addPos(pos); |
978 | 173 | ascFile.addNote(f.str().c_str()); |
979 | 173 | return true; |
980 | 173 | } |
981 | 2.87k | } |
982 | 44.8k | Vec2i bound; |
983 | 92.7k | for (int n=0; n<N; ++n) |
984 | 47.9k | { |
985 | 47.9k | int rowPos[2]; |
986 | 95.8k | for (int &i : rowPos) i=int(libwps::readU16(input)); |
987 | 47.9k | if (n==0) |
988 | 44.8k | bound=Vec2i(rowPos[0], rowPos[1]); |
989 | 3.13k | else |
990 | 3.13k | { |
991 | 3.13k | if (rowPos[0]<bound[0]) |
992 | 214 | bound[0]=rowPos[0]; |
993 | 3.13k | if (rowPos[1]>bound[1]) |
994 | 548 | bound[1]=rowPos[1]; |
995 | 3.13k | } |
996 | 47.9k | f << "row" << n << "[bound]=" << Vec2i(rowPos[0], rowPos[1]) << ","; |
997 | 47.9k | } |
998 | 44.8k | if (sheetId>=m_state->getNumSheet()) |
999 | 2.00k | { |
1000 | 2.00k | WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnDefinition the zone id seems bad\n")); |
1001 | 2.00k | f << "##id"; |
1002 | 2.00k | } |
1003 | 42.7k | else |
1004 | 42.7k | { |
1005 | 42.7k | auto &sheet=m_state->getSheet(sheetId); |
1006 | 42.7k | if (sheet.m_boundsColsMap.find(col)!=sheet.m_boundsColsMap.end()) |
1007 | 9.39k | { |
1008 | 9.39k | WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnDefinition the zone col seems bad\n")); |
1009 | 9.39k | f << "##col"; |
1010 | 9.39k | } |
1011 | 33.4k | else |
1012 | 33.4k | sheet.m_boundsColsMap[col]=bound; |
1013 | 42.7k | } |
1014 | 44.8k | ascFile.addPos(pos); |
1015 | 44.8k | ascFile.addNote(f.str().c_str()); |
1016 | 44.8k | return true; |
1017 | 44.9k | } |
1018 | | |
1019 | | bool LotusSpreadsheet::readColumnSizes(std::shared_ptr<WPSStream> stream) |
1020 | 3.19k | { |
1021 | 3.19k | if (!stream) return false; |
1022 | 3.19k | RVNGInputStreamPtr &input=stream->m_input; |
1023 | 3.19k | libwps::DebugFile &ascFile=stream->m_ascii; |
1024 | 3.19k | libwps::DebugStream f; |
1025 | | |
1026 | 3.19k | long pos = input->tell(); |
1027 | 3.19k | auto type = long(libwps::read16(input)); |
1028 | 3.19k | if (type != 0x7) |
1029 | 0 | { |
1030 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnSizes: not a column size name\n")); |
1031 | 0 | return false; |
1032 | 0 | } |
1033 | 3.19k | auto sz = long(libwps::readU16(input)); |
1034 | 3.19k | f << "Entries(ColSize):"; |
1035 | 3.19k | if (sz < 4 || (sz%2)) |
1036 | 1.20k | { |
1037 | 1.20k | WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnSizes: the zone is too odd\n")); |
1038 | 1.20k | f << "###"; |
1039 | 1.20k | ascFile.addPos(pos); |
1040 | 1.20k | ascFile.addNote(f.str().c_str()); |
1041 | 1.20k | return true; |
1042 | 1.20k | } |
1043 | 1.98k | auto sheetId=int(libwps::readU8(input)); |
1044 | 1.98k | f << "id[sheet]=" << sheetId << ","; |
1045 | 1.98k | LotusSpreadsheetInternal::Spreadsheet empty, *sheet=nullptr; |
1046 | 1.98k | if (sheetId>=int(m_state->m_spreadsheetList.size())) |
1047 | 336 | { |
1048 | 336 | WPS_DEBUG_MSG(("LotusSpreadsheet::readColumnSizes: can find spreadsheet %d\n", sheetId)); |
1049 | 336 | sheet=∅ |
1050 | 336 | f << "###"; |
1051 | 336 | } |
1052 | 1.65k | else |
1053 | 1.65k | sheet=&m_state->m_spreadsheetList[size_t(sheetId)]; |
1054 | 1.98k | auto val=int(libwps::readU8(input)); // always 0? |
1055 | 1.98k | if (val) f << "f0=" << val << ","; |
1056 | 1.98k | f << "f1=" << std::hex << libwps::readU16(input) << std::dec << ","; // big number |
1057 | 1.98k | auto N=int((sz-4)/2); |
1058 | 1.98k | f << "widths=["; |
1059 | 92.6k | for (int i=0; i<N; ++i) |
1060 | 90.6k | { |
1061 | 90.6k | auto col=int(libwps::readU8(input)); |
1062 | 90.6k | auto width=int(libwps::readU8(input)); // width in char, default 12... |
1063 | 90.6k | sheet->setColumnWidth(col, WPSColumnFormat(float(7*width))); |
1064 | 90.6k | f << width << "C:col" << col << ","; |
1065 | 90.6k | } |
1066 | 1.98k | f << "],"; |
1067 | 1.98k | ascFile.addPos(pos); |
1068 | 1.98k | ascFile.addNote(f.str().c_str()); |
1069 | | |
1070 | 1.98k | return true; |
1071 | 3.19k | } |
1072 | | |
1073 | | bool LotusSpreadsheet::readRowFormats(std::shared_ptr<WPSStream> stream) |
1074 | 256k | { |
1075 | 256k | if (!stream) return false; |
1076 | 256k | RVNGInputStreamPtr &input=stream->m_input; |
1077 | 256k | libwps::DebugFile &ascFile=stream->m_ascii; |
1078 | 256k | libwps::DebugStream f; |
1079 | | |
1080 | 256k | long pos = input->tell(); |
1081 | 256k | auto type = long(libwps::read16(input)); |
1082 | 256k | if (type != 0x13) |
1083 | 0 | { |
1084 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: not a row definition\n")); |
1085 | 0 | return false; |
1086 | 0 | } |
1087 | 256k | auto sz = long(libwps::readU16(input)); |
1088 | 256k | long endPos = pos+4+sz; |
1089 | 256k | f << "Entries(RowFormat):"; |
1090 | 256k | if (sz<8) |
1091 | 1.68k | { |
1092 | 1.68k | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: the zone is too short\n")); |
1093 | 1.68k | f << "###"; |
1094 | 1.68k | ascFile.addPos(pos); |
1095 | 1.68k | ascFile.addNote(f.str().c_str()); |
1096 | 1.68k | return true; |
1097 | 1.68k | } |
1098 | 255k | auto sheetId=int(libwps::readU8(input)); |
1099 | 255k | auto rowType=int(libwps::readU8(input)); |
1100 | 255k | auto row=int(libwps::readU16(input)); |
1101 | 255k | int val; |
1102 | 255k | f << "sheet[id]=" << sheetId << ","; |
1103 | 255k | if (row) f << "row=" << row << ","; |
1104 | 255k | switch (rowType) |
1105 | 255k | { |
1106 | 39.2k | case 0: |
1107 | 39.2k | { |
1108 | 39.2k | f << "def,"; |
1109 | 39.2k | size_t rowStyleId=m_state->m_rowStylesList.size(); |
1110 | 39.2k | m_state->m_rowStylesList.resize(rowStyleId+1); |
1111 | | |
1112 | 39.2k | int actCell=0; |
1113 | 39.2k | auto &stylesList=m_state->m_rowStylesList.back(); |
1114 | 39.2k | f << "["; |
1115 | 752k | while (input->tell()<endPos) |
1116 | 722k | { |
1117 | 722k | int numCell; |
1118 | 722k | LotusSpreadsheetInternal::Style style(m_mainParser.getDefaultFontType()); |
1119 | 722k | if (!readRowFormat(stream, style, numCell, endPos)) |
1120 | 9.42k | { |
1121 | 9.42k | f << "###"; |
1122 | 9.42k | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: find extra data\n")); |
1123 | 9.42k | break; |
1124 | 9.42k | } |
1125 | 713k | if (numCell>=1) |
1126 | 713k | stylesList.m_colsToStyleMap.insert |
1127 | 713k | (std::map<Vec2i,LotusSpreadsheetInternal::Style>::value_type |
1128 | 713k | (Vec2i(actCell,actCell+numCell-1),style)); |
1129 | 713k | f << "[" << style << "]"; |
1130 | 713k | if (numCell>1) |
1131 | 129k | f << "x" << numCell; |
1132 | 713k | f << ","; |
1133 | 713k | actCell+=numCell; |
1134 | 713k | } |
1135 | 39.2k | f << "],"; |
1136 | 39.2k | m_state->m_rowSheetIdToStyleIdMap[Vec2i(row,sheetId)]=rowStyleId; |
1137 | 39.2k | if (actCell>256) |
1138 | 2.33k | { |
1139 | 2.33k | f << "###"; |
1140 | 2.33k | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: find too much cells\n")); |
1141 | 2.33k | } |
1142 | 39.2k | break; |
1143 | 0 | } |
1144 | 1.74k | case 1: // the last row definition, maybe the actual row style ? |
1145 | 1.74k | f << "last,"; |
1146 | 1.74k | if (sz<12) |
1147 | 299 | { |
1148 | 299 | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: the size seems bad\n")); |
1149 | 299 | f << "###sz,"; |
1150 | 299 | break; |
1151 | 299 | } |
1152 | 13.0k | for (int i=0; i<8; ++i) // f0=0|32|41|71|7e|fe,f1=0|1|40|50|c0, f2=0|4|5|b|41, f3=0|40|54|5c, f4=27, other 0 |
1153 | 11.5k | { |
1154 | 11.5k | val=int(libwps::readU8(input)); |
1155 | 11.5k | if (val) |
1156 | 4.05k | f << "f" << i << "=" << std::hex << val << std::dec << ","; |
1157 | 11.5k | } |
1158 | 1.44k | break; |
1159 | 206k | case 2: |
1160 | 206k | { |
1161 | 206k | f << "dup,"; |
1162 | 206k | if (sz!=8) |
1163 | 54 | { |
1164 | 54 | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: the size seems bad\n")); |
1165 | 54 | f << "###sz,"; |
1166 | 54 | break; |
1167 | 54 | } |
1168 | 206k | auto sheetId2=int(libwps::readU8(input)); |
1169 | 206k | if (sheetId2!=sheetId) |
1170 | 3.35k | f << "#sheetId2=" << sheetId2 << ","; |
1171 | 206k | val=int(libwps::readU8(input)); // always 0? |
1172 | 206k | if (val) |
1173 | 3.72k | f << "f0=" << val << ","; |
1174 | 206k | val=int (libwps::readU16(input)); |
1175 | 206k | if (val>=row) |
1176 | 5.03k | { |
1177 | 5.03k | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: the original row seems bad\n")); |
1178 | 5.03k | f << "#"; |
1179 | 5.03k | } |
1180 | 206k | m_state->m_rowSheetIdToChildRowIdMap.insert |
1181 | 206k | (std::multimap<Vec2i,Vec2i>::value_type(Vec2i(val,sheetId2),Vec2i(row,sheetId))); |
1182 | 206k | f << "orig[row]=" << val << ","; |
1183 | 206k | break; |
1184 | 206k | } |
1185 | 7.48k | default: |
1186 | 7.48k | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: find unknown row type\n")); |
1187 | 7.48k | f << "###type=" << rowType << ","; |
1188 | 7.48k | break; |
1189 | 255k | } |
1190 | 255k | if (input->tell()!=endPos) |
1191 | 17.3k | { |
1192 | 17.3k | ascFile.addDelimiter(input->tell(),'|'); |
1193 | 17.3k | input->seek(endPos, librevenge::RVNG_SEEK_SET); |
1194 | 17.3k | } |
1195 | 255k | ascFile.addPos(pos); |
1196 | 255k | ascFile.addNote(f.str().c_str()); |
1197 | 255k | return true; |
1198 | 255k | } |
1199 | | |
1200 | | bool LotusSpreadsheet::readRowFormat(std::shared_ptr<WPSStream> stream, LotusSpreadsheetInternal::Style &style, int &numCell, long endPos) |
1201 | 722k | { |
1202 | 722k | if (!stream) return false; |
1203 | 722k | numCell=1; |
1204 | | |
1205 | 722k | RVNGInputStreamPtr &input=stream->m_input; |
1206 | 722k | libwps::DebugStream f; |
1207 | 722k | long actPos=input->tell(); |
1208 | 722k | if (endPos-actPos<4) |
1209 | 2.67k | { |
1210 | 2.67k | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: the zone seems too short\n")); |
1211 | 2.67k | return false; |
1212 | 2.67k | } |
1213 | | |
1214 | 720k | int value[3]; |
1215 | 2.88M | for (int i=0; i<3; ++i) |
1216 | 2.16M | value[i]=(i==1) ? int(libwps::readU16(input)) : int(libwps::readU8(input)); |
1217 | 720k | WPSFont font; |
1218 | 720k | if (value[2]&0x80) |
1219 | 145k | { |
1220 | 145k | if (actPos+5>endPos) |
1221 | 6.74k | { |
1222 | 6.74k | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormats: the zone seems too short\n")); |
1223 | 6.74k | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
1224 | 6.74k | return false; |
1225 | 6.74k | } |
1226 | 139k | value[2]&=0x7F; |
1227 | 139k | numCell=1+int(libwps::readU8(input)); |
1228 | 139k | } |
1229 | 713k | if ((value[0]&0x80)==0) |
1230 | 536k | f << "protected?,"; |
1231 | 713k | switch ((value[0]>>4)&7) |
1232 | 713k | { |
1233 | 358k | case 0: // fixed |
1234 | 358k | f << "fixed,"; |
1235 | 358k | style.setFormat(WPSCellFormat::F_NUMBER, 1); |
1236 | 358k | style.setDigits(value[0]&0xF); |
1237 | 358k | break; |
1238 | 37.6k | case 1: // scientific |
1239 | 37.6k | style.setFormat(WPSCellFormat::F_NUMBER, 2); |
1240 | 37.6k | style.setDigits(value[0]&0xF); |
1241 | 37.6k | break; |
1242 | 30.9k | case 2: // currency |
1243 | 30.9k | style.setFormat(WPSCellFormat::F_NUMBER, 4); |
1244 | 30.9k | style.setDigits(value[0]&0xF); |
1245 | 30.9k | break; |
1246 | 26.3k | case 3: // percent |
1247 | 26.3k | style.setFormat(WPSCellFormat::F_NUMBER, 3); |
1248 | 26.3k | style.setDigits(value[0]&0xF); |
1249 | 26.3k | break; |
1250 | 41.8k | case 4: // decimal |
1251 | 41.8k | style.setFormat(WPSCellFormat::F_NUMBER, 1); |
1252 | 41.8k | style.setDigits(value[0]&0xF); |
1253 | 41.8k | break; |
1254 | 133k | case 7: |
1255 | 133k | switch (value[0]&0xF) |
1256 | 133k | { |
1257 | 3.76k | case 0: // +/- : kind of bool |
1258 | 3.76k | style.setFormat(WPSCellFormat::F_BOOLEAN); |
1259 | 3.76k | f << "+/-,"; |
1260 | 3.76k | break; |
1261 | 21.9k | case 1: |
1262 | 21.9k | style.setFormat(WPSCellFormat::F_NUMBER, 0); |
1263 | 21.9k | break; |
1264 | 5.39k | case 2: |
1265 | 5.39k | style.setDTFormat(WPSCellFormat::F_DATE, "%d %B %y"); |
1266 | 5.39k | break; |
1267 | 3.77k | case 3: |
1268 | 3.77k | style.setDTFormat(WPSCellFormat::F_DATE, "%d %B"); |
1269 | 3.77k | break; |
1270 | 5.05k | case 4: |
1271 | 5.05k | style.setDTFormat(WPSCellFormat::F_DATE, "%B %y"); |
1272 | 5.05k | break; |
1273 | 3.42k | case 5: |
1274 | 3.42k | style.setFormat(WPSCellFormat::F_TEXT); |
1275 | 3.42k | break; |
1276 | 3.74k | case 6: |
1277 | 3.74k | style.setFormat(WPSCellFormat::F_TEXT); |
1278 | 3.74k | font.m_attributes |= WPS_HIDDEN_BIT; |
1279 | 3.74k | break; |
1280 | 3.04k | case 7: |
1281 | 3.04k | style.setDTFormat(WPSCellFormat::F_TIME, "%I:%M:%S%p"); |
1282 | 3.04k | break; |
1283 | 3.10k | case 8: |
1284 | 3.10k | style.setDTFormat(WPSCellFormat::F_TIME, "%I:%M%p"); |
1285 | 3.10k | break; |
1286 | 4.13k | case 9: |
1287 | 4.13k | style.setDTFormat(WPSCellFormat::F_DATE, "%m/%d/%y"); |
1288 | 4.13k | break; |
1289 | 2.82k | case 0xa: |
1290 | 2.82k | style.setDTFormat(WPSCellFormat::F_DATE, "%m/%d"); |
1291 | 2.82k | break; |
1292 | 2.45k | case 0xb: |
1293 | 2.45k | style.setDTFormat(WPSCellFormat::F_TIME, "%H:%M:%S"); |
1294 | 2.45k | break; |
1295 | 2.51k | case 0xc: |
1296 | 2.51k | style.setDTFormat(WPSCellFormat::F_TIME, "%H:%M"); |
1297 | 2.51k | break; |
1298 | 2.21k | case 0xd: |
1299 | 2.21k | style.setFormat(WPSCellFormat::F_TEXT); |
1300 | 2.21k | f << "label,"; |
1301 | 2.21k | break; |
1302 | 63.0k | case 0xf: // automatic |
1303 | 63.0k | break; |
1304 | 2.85k | default: |
1305 | 2.85k | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormat: find unknown 7e format\n")); |
1306 | 2.85k | f << "Fo=##7e,"; |
1307 | 2.85k | break; |
1308 | 133k | } |
1309 | 133k | break; |
1310 | 133k | default: |
1311 | 84.7k | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowFormat: find unknown %x format\n", static_cast<unsigned int>(value[0]&0x7F))); |
1312 | 84.7k | f << "##Fo=" << std::hex << (value[0]&0x7F) << std::dec << ","; |
1313 | 84.7k | break; |
1314 | 713k | } |
1315 | | |
1316 | 713k | switch (value[2]&3) |
1317 | 713k | { |
1318 | 79.5k | case 1: |
1319 | 79.5k | style.setHAlignment(WPSCellFormat::HALIGN_LEFT); |
1320 | 79.5k | break; |
1321 | 79.5k | case 2: |
1322 | 79.5k | style.setHAlignment(WPSCellFormat::HALIGN_RIGHT); |
1323 | 79.5k | break; |
1324 | 107k | case 3: |
1325 | 107k | style.setHAlignment(WPSCellFormat::HALIGN_CENTER); |
1326 | 107k | break; |
1327 | 446k | default: // general |
1328 | 446k | break; |
1329 | 713k | } |
1330 | | |
1331 | 713k | if (value[1]&1) |
1332 | 196k | f << "red[neg],"; |
1333 | 713k | if (value[1]&2) |
1334 | 192k | f << "add[parenthesis],"; |
1335 | | /* |
1336 | | Now can either find some font definitions or a type id. I am not |
1337 | | sure how to distinguish these two cases ; this code does not |
1338 | | seem robust and may fail on some files... |
1339 | | */ |
1340 | 713k | int wh=(value[2]>>2); |
1341 | 713k | int const vers=version(); |
1342 | 713k | if (vers==1 && (wh&0x10)) |
1343 | 86.6k | { |
1344 | 86.6k | int fId=(value[1]>>6)&0x3F; |
1345 | 86.6k | if (fId==0) |
1346 | 10.7k | ; |
1347 | 75.9k | else if ((wh&0xf)==5) |
1348 | 12.0k | { |
1349 | | /* unsure about this code, seems to work for Lotus123 mac, |
1350 | | but I never find a cell style in Lotus123 pc and this |
1351 | | part can be called*/ |
1352 | 12.0k | if (!m_styleManager->updateCellStyle(fId, style, font, style.m_fontType)) |
1353 | 3.88k | f << "#"; |
1354 | 12.0k | f << "Ce" << fId << ","; |
1355 | 12.0k | wh &= 0xE0; |
1356 | 12.0k | } |
1357 | 63.8k | else if ((wh&0xf)==0) |
1358 | 7.30k | { |
1359 | 7.30k | if (!m_styleManager->updateFontStyle(fId, font, style.m_fontType)) |
1360 | 5.61k | f << "#"; |
1361 | 7.30k | f << "FS" << fId << ","; |
1362 | 7.30k | wh &= 0xE0; |
1363 | 7.30k | } |
1364 | 56.5k | else |
1365 | 56.5k | f << "#fId=" << fId << ","; |
1366 | 86.6k | value[1] &= 0xF03C; |
1367 | 86.6k | } |
1368 | 626k | else if (wh&0x10) |
1369 | 103k | { |
1370 | 103k | int fId=(value[1]>>6); |
1371 | 103k | if (fId==0) |
1372 | 8.74k | ; |
1373 | 94.5k | else if ((wh&0xf)==0) |
1374 | 21.5k | { |
1375 | 21.5k | if (!m_styleManager->updateCellStyle(fId, style, font, style.m_fontType)) |
1376 | 9.15k | f << "#"; |
1377 | 21.5k | f << "Ce" << fId << ","; |
1378 | 21.5k | wh &= 0xE0; |
1379 | 21.5k | } |
1380 | 72.9k | else |
1381 | 72.9k | f << "#fId=" << fId << ","; |
1382 | 103k | value[1] &= 0x3C; |
1383 | 103k | } |
1384 | 523k | else |
1385 | 523k | { |
1386 | 523k | if (value[1]&0x40) |
1387 | 50.5k | font.m_attributes |= WPS_BOLD_BIT; |
1388 | 523k | if (value[1]&0x80) |
1389 | 43.0k | font.m_attributes |= WPS_ITALICS_BIT; |
1390 | 523k | if (value[1]>>11) |
1391 | 179k | font.m_size=(value[1]>>11); |
1392 | | // values[1]&0x20 is often set in this case... |
1393 | 523k | value[1] &= 0x033c; |
1394 | 523k | } |
1395 | 713k | if (value[1]) |
1396 | 392k | f << "f1=" << std::hex << value[1] << std::dec << ","; |
1397 | 713k | if (wh) |
1398 | 276k | f << "wh=" << std::hex << wh << std::dec << ","; |
1399 | 713k | if (font.m_size<=0) |
1400 | 522k | font.m_size=10; // if the size is not defined, set it to default |
1401 | 713k | style.setFont(font); |
1402 | 713k | style.m_extra=f.str(); |
1403 | 713k | return true; |
1404 | 713k | } |
1405 | | |
1406 | | bool LotusSpreadsheet::readCellsFormat801(std::shared_ptr<WPSStream> stream, WPSVec3i const &minC, WPSVec3i const &maxC, int subZoneId) |
1407 | 674k | { |
1408 | 674k | if (!stream) return false; |
1409 | 674k | RVNGInputStreamPtr &input=stream->m_input; |
1410 | 674k | libwps::DebugFile &ascFile=stream->m_ascii; |
1411 | 674k | libwps::DebugStream f; |
1412 | | |
1413 | 674k | long pos = input->tell(); |
1414 | 674k | auto type = long(libwps::read16(input)); |
1415 | 674k | if (type != 0x801) |
1416 | 0 | { |
1417 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: not a cells formats definition\n")); |
1418 | 0 | return false; |
1419 | 0 | } |
1420 | 674k | auto sz = long(libwps::readU16(input)); |
1421 | 674k | long endPos = pos+4+sz; |
1422 | 674k | if (!stream->checkFilePosition(endPos)) |
1423 | 0 | { |
1424 | 0 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1425 | 0 | return false; |
1426 | 0 | } |
1427 | 674k | f << "Entries(Zone8):"; |
1428 | 674k | if (minC==maxC) |
1429 | 178k | f << "setStyle[" << minC << "],"; |
1430 | 495k | else |
1431 | 495k | f << "setStyle[" << minC << "<->" << maxC << "],"; |
1432 | 674k | LotusSpreadsheetInternal::Table123Styles *tableStyle=nullptr; |
1433 | 674k | int const vers=version(); |
1434 | 674k | if (minC[0]<=maxC[0] && minC[0]>=0) |
1435 | 597k | tableStyle=m_state->getTablesStyle(Vec2i(minC[0],maxC[0])); |
1436 | | |
1437 | 674k | int val; |
1438 | 674k | switch (sz) |
1439 | 674k | { |
1440 | 198k | case 2: // in general the second one |
1441 | 198k | val=int(libwps::readU16(input)); |
1442 | 198k | if ((val>>8)==0x50) |
1443 | 169k | { |
1444 | 169k | if (tableStyle) tableStyle->addCellStyle(Vec2i(minC[1],maxC[1]),Vec2i(minC[2],maxC[2]),(val&0xFF)); |
1445 | 169k | f << "Ce" << (val&0xFF); |
1446 | 169k | } |
1447 | 29.2k | else |
1448 | 29.2k | { |
1449 | 29.2k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: find unexpected format type\n")); |
1450 | 29.2k | f << "##" << std::hex << val << std::dec << ","; |
1451 | 29.2k | } |
1452 | 198k | break; |
1453 | 244k | case 4: // in general the first one |
1454 | 244k | { |
1455 | 244k | LotusSpreadsheetInternal::Format123Style format; |
1456 | 244k | int values[4]; |
1457 | 977k | for (int &value : values) value=int(libwps::readU8(input)); |
1458 | 244k | if ((values[0]&0x80)==0) |
1459 | 164k | f << "protected[no],"; |
1460 | 79.6k | else |
1461 | 79.6k | values[0] &= 0x7f; |
1462 | 244k | if ((values[3]&0x80) && (values[0]>>4)==0) |
1463 | 120k | { |
1464 | 120k | if (values[2]>=0x7a) |
1465 | 2.33k | format.setDTFormat(WPSCellFormat::F_TIME, "%I:%M:%S%p"); |
1466 | 117k | else if (values[2]>=0x63) |
1467 | 3.36k | format.setDTFormat(WPSCellFormat::F_DATE, "%m/%d/%y"); |
1468 | 114k | else |
1469 | 114k | { |
1470 | 114k | format.setFormat(WPSCellFormat::F_NUMBER, 4); |
1471 | 114k | format.setDigits(values[0]&0xF); |
1472 | 114k | } |
1473 | 120k | values[3]&=0x7f; |
1474 | 120k | } |
1475 | 124k | else if (values[0]!=0x7f) |
1476 | 113k | { |
1477 | 113k | switch ((values[0]>>4)) |
1478 | 113k | { |
1479 | 58.7k | case 0: // fixed |
1480 | 58.7k | f << "fixed,"; |
1481 | 58.7k | format.setFormat(WPSCellFormat::F_NUMBER, 1); |
1482 | 58.7k | format.setDigits(values[0]&0xF); |
1483 | 58.7k | break; |
1484 | 2.75k | case 1: // scientific |
1485 | 2.75k | format.setFormat(WPSCellFormat::F_NUMBER, 2); |
1486 | 2.75k | format.setDigits(values[0]&0xF); |
1487 | 2.75k | break; |
1488 | 1.90k | case 2: // currency |
1489 | 1.90k | format.setFormat(WPSCellFormat::F_NUMBER, 4); |
1490 | 1.90k | format.setDigits(values[0]&0xF); |
1491 | 1.90k | break; |
1492 | 1.49k | case 3: // percent |
1493 | 1.49k | format.setFormat(WPSCellFormat::F_NUMBER, 3); |
1494 | 1.49k | format.setDigits(values[0]&0xF); |
1495 | 1.49k | break; |
1496 | 13.2k | case 4: // decimal |
1497 | 13.2k | format.setFormat(WPSCellFormat::F_NUMBER, 1); |
1498 | 13.2k | format.setDigits(values[0]&0xF); |
1499 | 13.2k | break; |
1500 | 31.4k | case 7: // checkme |
1501 | 31.4k | switch (values[0]&0xF) |
1502 | 31.4k | { |
1503 | 92 | case 0: // +/- : kind of bool |
1504 | 92 | format.setFormat(WPSCellFormat::F_BOOLEAN); |
1505 | 92 | f << "+/-,"; |
1506 | 92 | break; |
1507 | 4.88k | case 1: |
1508 | 4.88k | format.setFormat(WPSCellFormat::F_NUMBER, 0); |
1509 | 4.88k | break; |
1510 | 230 | case 2: |
1511 | 230 | format.setDTFormat(WPSCellFormat::F_DATE, "%d %B %y"); |
1512 | 230 | break; |
1513 | 451 | case 3: |
1514 | 451 | format.setDTFormat(WPSCellFormat::F_DATE, "%d %B"); |
1515 | 451 | break; |
1516 | 746 | case 4: |
1517 | 746 | format.setDTFormat(WPSCellFormat::F_DATE, "%B %y"); |
1518 | 746 | break; |
1519 | 4.67k | case 5: |
1520 | 4.67k | format.setFormat(WPSCellFormat::F_TEXT); |
1521 | 4.67k | break; |
1522 | 2.30k | case 6: |
1523 | 2.30k | format.setFormat(WPSCellFormat::F_TEXT); |
1524 | | // font.m_attributes |= WPS_HIDDEN_BIT; |
1525 | 2.30k | break; |
1526 | 625 | case 7: |
1527 | 625 | format.setDTFormat(WPSCellFormat::F_TIME, "%I:%M:%S%p"); |
1528 | 625 | break; |
1529 | 2.93k | case 8: |
1530 | 2.93k | format.setDTFormat(WPSCellFormat::F_TIME, "%I:%M%p"); |
1531 | 2.93k | break; |
1532 | 3.53k | case 9: |
1533 | 3.53k | format.setDTFormat(WPSCellFormat::F_DATE, "%m/%d/%y"); |
1534 | 3.53k | break; |
1535 | 843 | case 0xa: |
1536 | 843 | format.setDTFormat(WPSCellFormat::F_DATE, "%m/%d"); |
1537 | 843 | break; |
1538 | 947 | case 0xb: |
1539 | 947 | format.setDTFormat(WPSCellFormat::F_TIME, "%H:%M:%S"); |
1540 | 947 | break; |
1541 | 2.92k | case 0xc: |
1542 | 2.92k | format.setDTFormat(WPSCellFormat::F_TIME, "%H:%M"); |
1543 | 2.92k | break; |
1544 | 4.73k | case 0xd: |
1545 | 4.73k | format.setFormat(WPSCellFormat::F_TEXT); |
1546 | 4.73k | f << "label,"; |
1547 | 4.73k | break; |
1548 | 1.59k | default: |
1549 | 1.59k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: find unknown 7e format\n")); |
1550 | 1.59k | f << "Fo=##7e,"; |
1551 | 1.59k | break; |
1552 | 31.4k | } |
1553 | 31.4k | break; |
1554 | 31.4k | default: |
1555 | 3.89k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: find unknown %x format\n", static_cast<unsigned int>(values[0]))); |
1556 | 3.89k | f << "##Fo=" << std::hex << values[0] << std::dec << ","; |
1557 | 3.89k | break; |
1558 | 113k | } |
1559 | 113k | } |
1560 | 244k | f << format << ","; |
1561 | | |
1562 | 244k | if (values[1]&1) f << "neg[value,red],"; |
1563 | 244k | if (values[1]&2) f << "add[parenthesis],"; |
1564 | 244k | if (values[1]&0x10) |
1565 | 27.5k | { |
1566 | 27.5k | format.m_alignAcrossColumn=true; |
1567 | 27.5k | f << "align[across,column],"; |
1568 | 27.5k | } |
1569 | 244k | if (values[1]&0x20) f << "hidden,"; |
1570 | 244k | values[1] &=0xCC; |
1571 | 977k | for (int i=1; i<4; ++i) |
1572 | 733k | { |
1573 | 733k | if (values[i]) f << "f" << i << "=" << std::hex << values[i] << std::dec << ","; |
1574 | 733k | } |
1575 | 244k | if (tableStyle) tableStyle->addCellStyle(Vec2i(minC[1],maxC[1]),Vec2i(minC[2],maxC[2]),format); |
1576 | 244k | break; |
1577 | 244k | } |
1578 | 188k | case 8: // in general the third one, this define border, ... |
1579 | 188k | { |
1580 | 188k | LotusSpreadsheetInternal::Extra123Style style; |
1581 | 565k | for (int i=0; i<2; ++i) |
1582 | 376k | { |
1583 | 376k | auto col=int(libwps::readU8(input)); |
1584 | 376k | val=int(libwps::readU8(input)); |
1585 | 376k | if ((val&0xF)==0xF) continue; |
1586 | 367k | f << (i==0 ? "bordT" : "bordL") << "=["; |
1587 | 367k | WPSBorder border; |
1588 | 367k | switch (val&0xF) |
1589 | 367k | { |
1590 | 57.7k | case 0: |
1591 | 57.7k | border.m_style=WPSBorder::None; |
1592 | 57.7k | break; |
1593 | 264k | case 1: // single |
1594 | 264k | break; |
1595 | 1.29k | case 2: |
1596 | 1.29k | border.m_type=WPSBorder::Double; |
1597 | 1.29k | break; |
1598 | 5.87k | case 3: |
1599 | 5.87k | border.m_width=2; |
1600 | 5.87k | break; |
1601 | 6.25k | case 4: // dot[1x1] |
1602 | 6.25k | border.m_style=WPSBorder::Dot; |
1603 | 6.25k | break; |
1604 | 2.54k | case 5: |
1605 | 2.54k | border.m_style=WPSBorder::Dash; |
1606 | 2.54k | f << "dash[1x3],"; |
1607 | 2.54k | break; |
1608 | 3.38k | case 6: |
1609 | 3.38k | border.m_style=WPSBorder::Dash; |
1610 | 3.38k | f << "dash[3x1],"; |
1611 | 3.38k | break; |
1612 | 2.67k | case 7: |
1613 | 2.67k | border.m_style=WPSBorder::Dash; |
1614 | 2.67k | f << "dash[1x1,3x1],"; |
1615 | 2.67k | break; |
1616 | 15.5k | case 8: |
1617 | 15.5k | border.m_style=WPSBorder::Dash; |
1618 | 15.5k | f << "dash[1x1,1x1,2x1],"; |
1619 | 15.5k | break; |
1620 | 7.46k | default: |
1621 | 7.46k | f << "##type=" << (val&0xF) << ","; |
1622 | 7.46k | break; |
1623 | 367k | } |
1624 | 367k | if (!m_styleManager->getColor256(col,border.m_color)) |
1625 | 0 | f << "##colId=" << col << ","; |
1626 | 367k | f << border; |
1627 | 367k | f << "],"; |
1628 | 367k | style.m_borders[i]=border; |
1629 | 367k | } |
1630 | 188k | f << "unk0=["; |
1631 | 942k | for (int i=0; i<4; ++i) |
1632 | 753k | { |
1633 | 753k | val=int(libwps::readU8(input)); |
1634 | 753k | if (val) |
1635 | 139k | f <<std::hex << val << std::dec << ","; |
1636 | 614k | else |
1637 | 614k | f << "_,"; |
1638 | 753k | } |
1639 | 188k | f << "],"; |
1640 | 188k | if (tableStyle) tableStyle->addCellStyle(Vec2i(minC[1],maxC[1]),Vec2i(minC[2],maxC[2]),style); |
1641 | 188k | break; |
1642 | 188k | } |
1643 | 25.3k | case 12: // column data, in general the fourst one |
1644 | 25.3k | { |
1645 | 25.3k | if (subZoneId==0) f << "col,"; |
1646 | 21.9k | else if (subZoneId==1) f << "row,"; |
1647 | 10.1k | else if (subZoneId!=-1) |
1648 | 0 | { |
1649 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: the zone8 id seems bad\n")); |
1650 | 0 | f << "###zone15=" << subZoneId << ","; |
1651 | 0 | } |
1652 | 25.3k | int width=-1; |
1653 | 25.3k | bool isDefault=false; |
1654 | 25.3k | bool isWidthDef=true; |
1655 | 25.3k | f << "colUnkn=["; |
1656 | 202k | for (int i=0; i<7; ++i) // f1=[01][04][01], f2: big number, f3=0|1, f4=0|2d|3c|240 |
1657 | 177k | { |
1658 | 177k | if (i==2 && vers>=5) continue; |
1659 | 171k | val=(vers<5 && (i==2||i==4)) ? int(libwps::readU8(input)) : int(libwps::readU16(input)); |
1660 | 171k | if (i==1) |
1661 | 25.3k | { |
1662 | 25.3k | if ((val&1)==0) |
1663 | 3.32k | { |
1664 | 3.32k | isDefault=true; |
1665 | 3.32k | f << "no[w],"; |
1666 | 3.32k | } |
1667 | 25.3k | if (val&2) f << "hidden,"; |
1668 | 25.3k | if (val&0x20) f << "page[break],"; |
1669 | 25.3k | if (val&0x40) |
1670 | 4.81k | { |
1671 | 4.81k | isWidthDef=false; |
1672 | 4.81k | f << "w[def],"; |
1673 | 4.81k | } |
1674 | 25.3k | if (val&0x100) |
1675 | 13.3k | f << "fl100,"; |
1676 | 25.3k | val &= 0xFE9C; |
1677 | 25.3k | if (val) |
1678 | 5.91k | f << "##fl=" << std::hex << val << std::dec << ","; |
1679 | 25.3k | } |
1680 | 146k | else if (i==3) |
1681 | 25.3k | { |
1682 | 25.3k | width=val; |
1683 | 25.3k | if (!isDefault) |
1684 | 22.0k | f << "w=" << width << ","; |
1685 | 25.3k | } |
1686 | 120k | else if (val) |
1687 | 37.0k | f <<std::hex << val << std::dec << ","; |
1688 | 83.6k | else |
1689 | 83.6k | f << "_,"; |
1690 | 171k | } |
1691 | 25.3k | f << "],"; |
1692 | 25.3k | if (isDefault || minC[1]<0 || width<0) break; |
1693 | 18.0k | if (subZoneId<0 || subZoneId>1) break; |
1694 | 1.03G | for (int i=minC[0]; i<=maxC[0]; ++i) |
1695 | 1.03G | { |
1696 | 1.03G | auto &sheet=m_state->getSheet(i); |
1697 | 2.66G | for (int c=minC[1]; c<=std::min(maxC[1], MAX_COLUMNS); ++c) |
1698 | 1.63G | { |
1699 | 1.63G | if (subZoneId==0) |
1700 | 1.38G | { |
1701 | 1.38G | WPSColumnFormat format(!isWidthDef ? 72 : vers>=5 ? float(width)/16.f : float(width)); |
1702 | 1.38G | sheet.setColumnWidth(c, format); |
1703 | 1.38G | } |
1704 | 243M | else |
1705 | 243M | { |
1706 | 243M | WPSRowFormat format(vers>=5 ? float(width)/16.f : float(width)); |
1707 | 243M | format.m_useOptimalHeight=!isWidthDef; |
1708 | 243M | sheet.setRowHeight(c, format); |
1709 | 243M | } |
1710 | 1.63G | } |
1711 | 1.03G | } |
1712 | 10.1k | break; |
1713 | 18.0k | } |
1714 | 983 | case 30: // table data, rare, the last one |
1715 | 2.94k | for (int i=0; i<2; ++i) // f0=71|7e |
1716 | 1.96k | { |
1717 | 1.96k | val=int(libwps::readU16(input)); |
1718 | 1.96k | if (val) |
1719 | 1.80k | f << "f" << i << "=" << val << ","; |
1720 | 1.96k | } |
1721 | 983 | f << "],"; |
1722 | 983 | val=int(libwps::readU16(input)); |
1723 | 983 | if ((val>>8)==0x50) |
1724 | 346 | { |
1725 | 346 | if (tableStyle) tableStyle->m_defaultCellId=(val&0xFF); |
1726 | 346 | f << "Ce" << (val&0xFF) << ","; |
1727 | 346 | } |
1728 | 637 | else |
1729 | 637 | { |
1730 | 637 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: find unexpected format type\n")); |
1731 | 637 | f << "##" << std::hex << val << std::dec << ","; |
1732 | 637 | } |
1733 | 983 | f << "tableUnk=["; |
1734 | 12.7k | for (int i=0; i<12; ++i) |
1735 | 11.7k | { |
1736 | | // f0=168|438|3000|3600, f2=f0|14a|a008|c00,f4=[02]0[04c]0,f6=0|3 |
1737 | | // f8=[37]5d4,f10=0|4,f11=1003(L3?) |
1738 | 11.7k | val=int(libwps::readU16(input)); |
1739 | 11.7k | if (val) |
1740 | 9.67k | f <<std::hex << val << std::dec << ","; |
1741 | 2.11k | else |
1742 | 2.11k | f << "_,"; |
1743 | 11.7k | } |
1744 | 983 | f << "],"; |
1745 | 983 | break; |
1746 | 16.9k | default: |
1747 | 16.9k | break; |
1748 | 674k | } |
1749 | 674k | ascFile.addPos(pos); |
1750 | 674k | ascFile.addNote(f.str().c_str()); |
1751 | 674k | if (input->tell()!=endPos) |
1752 | 5.58k | { |
1753 | 5.58k | ascFile.addDelimiter(input->tell(),'|'); |
1754 | 5.58k | input->seek(endPos, librevenge::RVNG_SEEK_SET); |
1755 | 5.58k | } |
1756 | 674k | return true; |
1757 | 674k | } |
1758 | | |
1759 | | bool LotusSpreadsheet::readRowSizes(std::shared_ptr<WPSStream> stream, long endPos) |
1760 | 1.24k | { |
1761 | 1.24k | if (!stream) return false; |
1762 | 1.24k | RVNGInputStreamPtr &input=stream->m_input; |
1763 | 1.24k | libwps::DebugFile &ascFile=stream->m_ascii; |
1764 | 1.24k | libwps::DebugStream f; |
1765 | | |
1766 | 1.24k | long pos = input->tell(); |
1767 | 1.24k | long sz=endPos-pos; |
1768 | 1.24k | f << "Entries(RowSize):"; |
1769 | 1.24k | if (sz<10 || (sz%8)!=2) |
1770 | 164 | { |
1771 | 164 | WPS_DEBUG_MSG(("LotusParser::readRowSizes: the zone size seems bad\n")); |
1772 | 164 | f << "###"; |
1773 | 164 | ascFile.addPos(pos-6); |
1774 | 164 | ascFile.addNote(f.str().c_str()); |
1775 | 164 | return true; |
1776 | 164 | } |
1777 | | |
1778 | 1.07k | auto sheetId=int(libwps::readU8(input)); |
1779 | 1.07k | f << "id[sheet]=" << sheetId << ","; |
1780 | 1.07k | LotusSpreadsheetInternal::Spreadsheet empty, *sheet=nullptr; |
1781 | 1.07k | if (sheetId>=int(m_state->m_spreadsheetList.size())) |
1782 | 160 | { |
1783 | 160 | WPS_DEBUG_MSG(("LotusSpreadsheet::readRowSizes: can find spreadsheet %d\n", sheetId)); |
1784 | 160 | sheet=∅ |
1785 | 160 | f << "###"; |
1786 | 160 | } |
1787 | 918 | else |
1788 | 918 | sheet=&m_state->m_spreadsheetList[size_t(sheetId)]; |
1789 | 1.07k | auto val=int(libwps::readU8(input)); // always 0 |
1790 | 1.07k | if (val) f << "f0=" << val << ","; |
1791 | 1.07k | ascFile.addPos(pos-6); |
1792 | 1.07k | ascFile.addNote(f.str().c_str()); |
1793 | 1.07k | auto N=int(sz/8); |
1794 | 33.4k | for (int i=0; i<N; ++i) |
1795 | 32.3k | { |
1796 | 32.3k | pos=input->tell(); |
1797 | 32.3k | f.str(""); |
1798 | 32.3k | f << "RowSize-" << i << ":"; |
1799 | 32.3k | auto row=int(libwps::readU16(input)); |
1800 | 32.3k | f << "row=" << row << ","; |
1801 | 32.3k | val=int(libwps::readU16(input)); |
1802 | 32.3k | if (val!=0xFFFF) |
1803 | 29.3k | { |
1804 | 29.3k | f << "dim=" << float(val+31)/32.f << ","; |
1805 | 29.3k | sheet->setRowHeight(row, WPSRowFormat(float(val+31)/32.f)); |
1806 | 29.3k | } |
1807 | 97.1k | for (int j=0; j<2; ++j) |
1808 | 64.7k | { |
1809 | 64.7k | val=int(libwps::read16(input)); |
1810 | 64.7k | if (val!=j-1) |
1811 | 56.3k | f << "f" << j << "=" << val <<","; |
1812 | 64.7k | } |
1813 | 32.3k | input->seek(pos+8, librevenge::RVNG_SEEK_SET); |
1814 | 32.3k | ascFile.addPos(pos); |
1815 | 32.3k | ascFile.addNote(f.str().c_str()); |
1816 | 32.3k | } |
1817 | | |
1818 | 1.07k | return true; |
1819 | 1.24k | } |
1820 | | |
1821 | | bool LotusSpreadsheet::readSheetName(std::shared_ptr<WPSStream> stream) |
1822 | 8.23k | { |
1823 | 8.23k | if (!stream) return false; |
1824 | 8.23k | RVNGInputStreamPtr &input=stream->m_input; |
1825 | 8.23k | libwps::DebugFile &ascFile=stream->m_ascii; |
1826 | 8.23k | libwps::DebugStream f; |
1827 | | |
1828 | 8.23k | long pos = input->tell(); |
1829 | 8.23k | auto type = long(libwps::read16(input)); |
1830 | 8.23k | if (type != 0x23) |
1831 | 0 | { |
1832 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetName: not a sheet name\n")); |
1833 | 0 | return false; |
1834 | 0 | } |
1835 | 8.23k | auto sz = long(libwps::readU16(input)); |
1836 | 8.23k | f << "Entries(SheetName):"; |
1837 | 8.23k | if (sz < 5) |
1838 | 2.00k | { |
1839 | 2.00k | WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetName: sheet name is too short\n")); |
1840 | 2.00k | f << "###"; |
1841 | 2.00k | ascFile.addPos(pos); |
1842 | 2.00k | ascFile.addNote(f.str().c_str()); |
1843 | 2.00k | return true; |
1844 | 2.00k | } |
1845 | 6.23k | auto val=int(libwps::read16(input)); // always 14000 |
1846 | 6.23k | if (val!=14000) |
1847 | 5.68k | f << "f0=" << std::hex << val << std::dec << ","; |
1848 | 6.23k | auto sheetId=int(libwps::readU8(input)); |
1849 | 6.23k | f << "id[sheet]=" << sheetId << ","; |
1850 | 6.23k | val=int(libwps::readU8(input)); // always 0 |
1851 | 6.23k | if (val) |
1852 | 4.68k | f << "f1=" << val << ","; |
1853 | 6.23k | std::string name; |
1854 | 53.5k | for (long i = 0; i < sz-4; i++) |
1855 | 53.0k | { |
1856 | 53.0k | char c = char(libwps::readU8(input)); |
1857 | 53.0k | if (c == '\0') break; |
1858 | 47.2k | name.push_back(c); |
1859 | 47.2k | } |
1860 | 6.23k | f << name << ","; |
1861 | 6.23k | if (input->tell()!=pos+4+sz && input->tell()+1!=pos+4+sz) |
1862 | 3.82k | { |
1863 | 3.82k | WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetName: the zone seems too short\n")); |
1864 | 3.82k | f << "##"; |
1865 | 3.82k | ascFile.addDelimiter(input->tell(), '|'); |
1866 | 3.82k | } |
1867 | 6.23k | if (sheetId>=m_state->getNumSheet()) |
1868 | 1.59k | { |
1869 | 1.59k | WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetName: the zone id seems bad\n")); |
1870 | 1.59k | f << "##id"; |
1871 | 1.59k | } |
1872 | 4.63k | else if (!name.empty()) |
1873 | 3.91k | m_state->getSheet(sheetId).m_name=libwps_tools_win::Font::unicodeString(name, m_mainParser.getDefaultFontType()); |
1874 | 6.23k | ascFile.addPos(pos); |
1875 | 6.23k | ascFile.addNote(f.str().c_str()); |
1876 | 6.23k | return true; |
1877 | 8.23k | } |
1878 | | |
1879 | | bool LotusSpreadsheet::readSheetHeader(std::shared_ptr<WPSStream> stream) |
1880 | 6.90k | { |
1881 | 6.90k | if (!stream) return false; |
1882 | 6.90k | RVNGInputStreamPtr &input=stream->m_input; |
1883 | 6.90k | libwps::DebugFile &ascFile=stream->m_ascii; |
1884 | 6.90k | libwps::DebugStream f; |
1885 | | |
1886 | 6.90k | long pos = input->tell(); |
1887 | 6.90k | auto type = long(libwps::read16(input)); |
1888 | 6.90k | if (type!=0xc3) |
1889 | 0 | { |
1890 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetHeader: not a sheet header\n")); |
1891 | 0 | return false; |
1892 | 0 | } |
1893 | 6.90k | auto sz = long(libwps::readU16(input)); |
1894 | 6.90k | f << "Entries(FMTSheetBegin):"; |
1895 | 6.90k | if (sz != 0x22) |
1896 | 300 | { |
1897 | 300 | WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetHeader: the zone size seems bad\n")); |
1898 | 300 | f << "###"; |
1899 | 300 | ascFile.addPos(pos); |
1900 | 300 | ascFile.addNote(f.str().c_str()); |
1901 | 300 | return true; |
1902 | 300 | } |
1903 | 6.60k | auto id=int(libwps::read16(input)); |
1904 | 6.60k | if (id<0) |
1905 | 214 | { |
1906 | 214 | WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetHeader: the id seems bad\n")); |
1907 | 214 | f << "###"; |
1908 | 214 | m_state->m_sheetCurrentId=-1; |
1909 | 214 | } |
1910 | 6.39k | else |
1911 | 6.39k | m_state->m_sheetCurrentId=id; |
1912 | 6.60k | f << "id=" << id << ","; |
1913 | 112k | for (int i=0; i<16; ++i) // always 0 |
1914 | 105k | { |
1915 | 105k | auto val=int(libwps::read16(input)); |
1916 | 105k | if (val) f << "f" << i << "=" << val << ","; |
1917 | 105k | } |
1918 | 6.60k | ascFile.addPos(pos); |
1919 | 6.60k | ascFile.addNote(f.str().c_str()); |
1920 | 6.60k | return true; |
1921 | 6.90k | } |
1922 | | |
1923 | | bool LotusSpreadsheet::readExtraRowFormats(std::shared_ptr<WPSStream> stream) |
1924 | 24.1k | { |
1925 | 24.1k | if (!stream) return false; |
1926 | 24.1k | RVNGInputStreamPtr &input=stream->m_input; |
1927 | 24.1k | libwps::DebugFile &ascFile=stream->m_ascii; |
1928 | 24.1k | libwps::DebugStream f; |
1929 | | |
1930 | 24.1k | long pos = input->tell(); |
1931 | 24.1k | auto type = long(libwps::read16(input)); |
1932 | 24.1k | if (type!=0xc5) |
1933 | 0 | { |
1934 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readExtraRowFormats: not a sheet header\n")); |
1935 | 0 | return false; |
1936 | 0 | } |
1937 | 24.1k | auto sz = long(libwps::readU16(input)); |
1938 | 24.1k | f << "Entries(FMTRowForm):"; |
1939 | 24.1k | if (sz < 9 || (sz%5)!=4) |
1940 | 1.42k | { |
1941 | 1.42k | WPS_DEBUG_MSG(("LotusSpreadsheet::readExtraRowFormats: the zone size seems bad\n")); |
1942 | 1.42k | f << "###"; |
1943 | 1.42k | ascFile.addPos(pos); |
1944 | 1.42k | ascFile.addNote(f.str().c_str()); |
1945 | 1.42k | return true; |
1946 | 1.42k | } |
1947 | 22.7k | auto row=int(libwps::readU16(input)); |
1948 | 22.7k | f << "row=" << row << ","; |
1949 | | |
1950 | 22.7k | auto &sheet=m_state->getSheet(m_state->m_sheetCurrentId); |
1951 | 22.7k | auto val=int(libwps::readU8(input)); |
1952 | 22.7k | sheet.setRowHeight(row, WPSRowFormat(float(val))); |
1953 | 22.7k | if (val!=14) f << "height=" << val << ","; |
1954 | 22.7k | val=int(libwps::readU8(input)); // 10|80 |
1955 | 22.7k | if (val) f << "f0=" << std::hex << val << std::dec << ","; |
1956 | 22.7k | ascFile.addPos(pos); |
1957 | 22.7k | ascFile.addNote(f.str().c_str()); |
1958 | | |
1959 | 22.7k | LotusSpreadsheetInternal::ExtraRowStyles badRow; |
1960 | 22.7k | auto *rowStyles=&badRow; |
1961 | 22.7k | if (sheet.m_rowToExtraStyleMap.find(row)==sheet.m_rowToExtraStyleMap.end()) |
1962 | 12.5k | { |
1963 | 12.5k | sheet.m_rowToExtraStyleMap[row]=LotusSpreadsheetInternal::ExtraRowStyles(); |
1964 | 12.5k | rowStyles=&sheet.m_rowToExtraStyleMap.find(row)->second; |
1965 | 12.5k | } |
1966 | 10.1k | else |
1967 | 10.1k | { |
1968 | 10.1k | WPS_DEBUG_MSG(("LotusSpreadsheet::readExtraRowFormats: row %d is already defined\n", row)); |
1969 | 10.1k | } |
1970 | 22.7k | auto N=int(sz/5); |
1971 | 22.7k | int begPos=0; |
1972 | 271k | for (int i=0; i<N; ++i) |
1973 | 248k | { |
1974 | 248k | pos=input->tell(); |
1975 | 248k | f.str(""); |
1976 | 248k | f << "FMTRowForm-" << i << ":"; |
1977 | 248k | LotusSpreadsheetInternal::ExtraStyle style; |
1978 | 248k | val=style.m_format=int(libwps::readU8(input)); |
1979 | | // find also f[8-c]ffffffXX, which seems to have a different meaning |
1980 | 248k | if ((val>>4)==0xf) f << "#"; |
1981 | 248k | if (val&0x7) f << "font[id]=" << (val&0x7) << ","; // useMe |
1982 | 248k | if (val&0x8) f << "bold,"; |
1983 | 248k | if (val&0x10) f << "italic,"; |
1984 | 248k | if (val&0x20) f << "underline,"; |
1985 | 248k | val &= 0xC0; |
1986 | 248k | if (val) f << "fl=" << std::hex << val << std::dec << ","; |
1987 | 248k | style.m_flag=val=int(libwps::readU8(input)); |
1988 | 248k | if (val&0x20) f << "special,"; |
1989 | 248k | if (!m_styleManager->getColor8(val&7, style.m_color)) |
1990 | 0 | { |
1991 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readExtraRowFormats: can not read a color\n")); |
1992 | 0 | f << "##colId=" << (val&7) << ","; |
1993 | 0 | } |
1994 | 248k | else if (!style.m_color.isBlack()) f << "col=" << style.m_color << ","; |
1995 | 248k | val &= 0xD8; |
1996 | 248k | if (val) f << "fl1=" << std::hex << val << std::dec << ","; |
1997 | 248k | val=int(libwps::readU8(input)); |
1998 | 248k | if (val&7) |
1999 | 143k | { |
2000 | 143k | if ((val&7)==7) // checkMe |
2001 | 69.6k | style.m_backColor=WPSColor::black(); |
2002 | 74.3k | else if (!m_styleManager->getColor8(val&7, style.m_backColor)) |
2003 | 0 | { |
2004 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readExtraRowFormats: can not read a color\n")); |
2005 | 0 | f << "##colId=" << (val&7) << ","; |
2006 | 0 | } |
2007 | 74.3k | else |
2008 | 74.3k | f << "col[back]=" << style.m_backColor << ","; |
2009 | 143k | } |
2010 | 248k | if (val&0x10) f << "shadow2"; |
2011 | 248k | if (val&0x20) f << "shadow,"; |
2012 | 248k | val &= 0xD8; |
2013 | 248k | if (val) f << "f0=" << std::hex << val << std::dec << ","; |
2014 | 248k | style.m_borders=val=int(libwps::readU8(input)); |
2015 | 248k | if (val) f << "border=" << std::hex << val << std::dec << ","; |
2016 | 248k | val=int(libwps::readU8(input)); |
2017 | 248k | if (val) f << "dup=" << val << ","; |
2018 | 248k | rowStyles->m_colsToStyleMap[Vec2i(begPos, begPos+val)]=style; |
2019 | 248k | begPos+=1+val; |
2020 | 248k | ascFile.addPos(pos); |
2021 | 248k | ascFile.addNote(f.str().c_str()); |
2022 | 248k | input->seek(pos+5, librevenge::RVNG_SEEK_SET); |
2023 | 248k | } |
2024 | 22.7k | if (begPos!=256) |
2025 | 22.5k | { |
2026 | 22.5k | WPS_DEBUG_MSG(("LotusSpreadsheet::readExtraRowFormats: the number of columns for row %d seems bad\n", row)); |
2027 | 22.5k | } |
2028 | 22.7k | return true; |
2029 | 24.1k | } |
2030 | | |
2031 | | //////////////////////////////////////////////////////////// |
2032 | | // Cell |
2033 | | //////////////////////////////////////////////////////////// |
2034 | | bool LotusSpreadsheet::readCellName(std::shared_ptr<WPSStream> stream) |
2035 | 13.1k | { |
2036 | 13.1k | if (!stream) return false; |
2037 | 13.1k | RVNGInputStreamPtr &input=stream->m_input; |
2038 | 13.1k | libwps::DebugFile &ascFile=stream->m_ascii; |
2039 | 13.1k | libwps::DebugStream f; |
2040 | | |
2041 | 13.1k | long pos = input->tell(); |
2042 | 13.1k | auto type = long(libwps::read16(input)); |
2043 | 13.1k | if (type!=9) |
2044 | 0 | { |
2045 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCellName: not a cell name cell\n")); |
2046 | 0 | return false; |
2047 | 0 | } |
2048 | 13.1k | auto sz = long(libwps::readU16(input)); |
2049 | 13.1k | long endPos=pos+4+sz; |
2050 | 13.1k | f << "Entries(CellName):"; |
2051 | 13.1k | if (sz < 0x1a) |
2052 | 1.32k | { |
2053 | 1.32k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCellName: the zone is too short\n")); |
2054 | 1.32k | f << "###"; |
2055 | 1.32k | ascFile.addPos(pos); |
2056 | 1.32k | ascFile.addNote(f.str().c_str()); |
2057 | 1.32k | return true; |
2058 | 1.32k | } |
2059 | 11.8k | auto val=int(libwps::read16(input)); // 0 or 1 |
2060 | 11.8k | if (val) |
2061 | 6.62k | f << "f0=" << val << ","; |
2062 | 11.8k | std::string name(""); |
2063 | 75.7k | for (int i=0; i<16; ++i) |
2064 | 75.1k | { |
2065 | 75.1k | auto c=char(libwps::readU8(input)); |
2066 | 75.1k | if (!c) break; |
2067 | 63.9k | name += c; |
2068 | 63.9k | } |
2069 | 11.8k | f << name << ","; |
2070 | 11.8k | input->seek(pos+4+18, librevenge::RVNG_SEEK_SET); |
2071 | 11.8k | LotusSpreadsheetInternal::CellsList cells; |
2072 | 35.4k | for (int i=0; i<2; ++i) |
2073 | 23.6k | { |
2074 | 23.6k | auto row=int(libwps::readU16(input)); |
2075 | 23.6k | auto sheetId=int(libwps::readU8(input)); |
2076 | 23.6k | auto col=int(libwps::readU8(input)); |
2077 | 23.6k | if (i==0) |
2078 | 11.8k | cells.m_positions.setMin(Vec2i(col,row)); |
2079 | 11.8k | else |
2080 | 11.8k | cells.m_positions.setMax(Vec2i(col,row)); |
2081 | 23.6k | cells.m_ids[i]=sheetId; |
2082 | 23.6k | } |
2083 | 11.8k | f << cells << ","; |
2084 | 11.8k | if (m_state->m_nameToCellsMap.find(name)!=m_state->m_nameToCellsMap.end()) |
2085 | 4.50k | { |
2086 | 4.50k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCellName: cell with name %s already exists\n", name.c_str())); |
2087 | 4.50k | f << "##name=" << name << ","; |
2088 | 4.50k | } |
2089 | 7.32k | else |
2090 | 7.32k | m_state->m_nameToCellsMap[name]=cells; |
2091 | 11.8k | std::string note(""); |
2092 | 11.8k | auto remain=int(endPos-input->tell()); |
2093 | 22.7k | for (int i=0; i<remain; ++i) |
2094 | 15.0k | { |
2095 | 15.0k | auto c=char(libwps::readU8(input)); |
2096 | 15.0k | if (!c) break; |
2097 | 10.8k | note+=c; |
2098 | 10.8k | } |
2099 | 11.8k | if (!note.empty()) |
2100 | 3.54k | f << "note=" << note << ","; |
2101 | 11.8k | if (input->tell()!=endPos) |
2102 | 4.00k | ascFile.addDelimiter(input->tell(),'|'); |
2103 | | |
2104 | 11.8k | ascFile.addPos(pos); |
2105 | 11.8k | ascFile.addNote(f.str().c_str()); |
2106 | 11.8k | return true; |
2107 | 13.1k | } |
2108 | | |
2109 | | bool LotusSpreadsheet::readCell(std::shared_ptr<WPSStream> stream) |
2110 | 211k | { |
2111 | 211k | if (!stream) return false; |
2112 | 211k | RVNGInputStreamPtr &input=stream->m_input; |
2113 | 211k | libwps::DebugFile &ascFile=stream->m_ascii; |
2114 | 211k | libwps::DebugStream f; |
2115 | | |
2116 | 211k | long pos = input->tell(); |
2117 | 211k | auto type = long(libwps::read16(input)); |
2118 | 211k | std::string what(""); |
2119 | 211k | if (type == 0x16) |
2120 | 46.5k | what="TextCell"; |
2121 | 165k | else if (type == 0x17) |
2122 | 9.18k | what="Doub10Cell"; |
2123 | 155k | else if (type == 0x18) |
2124 | 13.1k | what="DoubU16Cell"; |
2125 | 142k | else if (type == 0x19) |
2126 | 14.7k | what="Doub10FormCell"; |
2127 | 127k | else if (type == 0x1a) // checkme |
2128 | 19.0k | what="TextFormCell"; |
2129 | 108k | else if (type == 0x25) |
2130 | 59.8k | what="DoubU32Cell"; |
2131 | 49.0k | else if (type == 0x26) |
2132 | 4.70k | what="CommentCell"; |
2133 | 44.3k | else if (type == 0x27) |
2134 | 4.02k | what="Doub8Cell"; |
2135 | 40.2k | else if (type == 0x28) |
2136 | 40.2k | what="Doub8FormCell"; |
2137 | 0 | else |
2138 | 0 | { |
2139 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: not a cell's cell\n")); |
2140 | 0 | return false; |
2141 | 0 | } |
2142 | | |
2143 | 211k | auto sz = long(libwps::readU16(input)); |
2144 | 211k | if (sz < 5) |
2145 | 15.2k | { |
2146 | 15.2k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the zone is too short\n")); |
2147 | 15.2k | f << "Entries(" << what << "):###"; |
2148 | 15.2k | ascFile.addPos(pos); |
2149 | 15.2k | ascFile.addNote(f.str().c_str()); |
2150 | 15.2k | return true; |
2151 | 15.2k | } |
2152 | 196k | long endPos=pos+4+sz; |
2153 | 196k | auto row=int(libwps::readU16(input)); |
2154 | 196k | auto sheetId=int(libwps::readU8(input)); |
2155 | 196k | auto col=int(libwps::readU8(input)); |
2156 | 196k | if (sheetId) f << "sheet[id]=" << sheetId << ","; |
2157 | | |
2158 | 196k | LotusSpreadsheetInternal::Spreadsheet empty, *sheet=nullptr; |
2159 | 196k | if (sheetId>=int(m_state->m_spreadsheetList.size())) |
2160 | 22.6k | { |
2161 | 22.6k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can find spreadsheet %d\n", sheetId)); |
2162 | 22.6k | sheet=∅ |
2163 | 22.6k | f << "###"; |
2164 | 22.6k | } |
2165 | 173k | else |
2166 | 173k | sheet=&m_state->m_spreadsheetList[size_t(sheetId)]; |
2167 | | |
2168 | 196k | auto &cell=sheet->getCell(stream->m_input, Vec2i(col, row)); |
2169 | 196k | switch (type) |
2170 | 196k | { |
2171 | 45.5k | case 0x16: |
2172 | 58.4k | case 0x1a: |
2173 | 62.2k | case 0x26: // comment |
2174 | 62.2k | { |
2175 | 62.2k | std::string text(""); |
2176 | 62.2k | long begText=input->tell(); |
2177 | 580k | for (long i=4; i<sz; ++i) |
2178 | 574k | { |
2179 | 574k | auto c=char(libwps::readU8(input)); |
2180 | 574k | if (!c) break; |
2181 | 518k | if (i==4) |
2182 | 55.3k | { |
2183 | 55.3k | bool done=true; |
2184 | 55.3k | if (c=='\'') cell.m_hAlignement=WPSCellFormat::HALIGN_DEFAULT; // sometimes followed by 0x7C |
2185 | 34.1k | else if (c=='\\') cell.m_hAlignement=WPSCellFormat::HALIGN_LEFT; |
2186 | 33.7k | else if (c=='^') cell.m_hAlignement=WPSCellFormat::HALIGN_CENTER; |
2187 | 25.7k | else if (c=='\"') cell.m_hAlignement=WPSCellFormat::HALIGN_RIGHT; |
2188 | 15.1k | else done=false; |
2189 | 55.3k | if (done) |
2190 | 40.2k | { |
2191 | 40.2k | ++begText; |
2192 | 40.2k | continue; |
2193 | 40.2k | } |
2194 | 55.3k | } |
2195 | 478k | text += c; |
2196 | 478k | } |
2197 | 62.2k | f << "\"" << getDebugStringForText(text) << "\","; |
2198 | 62.2k | WPSEntry entry; |
2199 | 62.2k | entry.setBegin(begText); |
2200 | 62.2k | entry.setEnd(endPos); |
2201 | 62.2k | if (type==0x16) |
2202 | 45.5k | { |
2203 | 45.5k | cell.m_content.m_contentType=WKSContentListener::CellContent::C_TEXT; |
2204 | 45.5k | cell.m_content.m_textEntry=entry; |
2205 | 45.5k | } |
2206 | 16.6k | else if (type==0x1a) |
2207 | 12.8k | { |
2208 | 12.8k | if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_FORMULA) |
2209 | 11.1k | cell.m_content.m_contentType=WKSContentListener::CellContent::C_TEXT; |
2210 | 12.8k | cell.m_content.m_textEntry=entry; |
2211 | 12.8k | } |
2212 | 3.81k | else if (type==0x26) |
2213 | 3.81k | cell.m_comment=entry; |
2214 | 0 | else |
2215 | 0 | { |
2216 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: find unexpected type %x\n", unsigned(type))); |
2217 | 0 | f << "###type"; |
2218 | 0 | } |
2219 | | |
2220 | 62.2k | if (input->tell()!=endPos && input->tell()+1!=endPos) |
2221 | 8.53k | { |
2222 | 8.53k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the string zone seems too short\n")); |
2223 | 8.53k | f << "###"; |
2224 | 8.53k | } |
2225 | 62.2k | break; |
2226 | 58.4k | } |
2227 | 8.73k | case 0x17: |
2228 | 8.73k | { |
2229 | 8.73k | if (sz!=14) |
2230 | 7.90k | { |
2231 | 7.90k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the double10 zone seems too short\n")); |
2232 | 7.90k | f << "###"; |
2233 | 7.90k | } |
2234 | 8.73k | double res; |
2235 | 8.73k | bool isNaN; |
2236 | 8.73k | if (!libwps::readDouble10(input, res, isNaN)) |
2237 | 424 | { |
2238 | 424 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can read a double10 zone\n")); |
2239 | 424 | f << "###"; |
2240 | 424 | break; |
2241 | 424 | } |
2242 | 8.31k | if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_FORMULA) |
2243 | 8.27k | cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER; |
2244 | 8.31k | cell.m_content.setValue(res); |
2245 | 8.31k | break; |
2246 | 8.73k | } |
2247 | 12.7k | case 0x18: |
2248 | 12.7k | { |
2249 | 12.7k | if (sz!=6) |
2250 | 2.47k | { |
2251 | 2.47k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the uint16 zone seems too short\n")); |
2252 | 2.47k | f << "###"; |
2253 | 2.47k | } |
2254 | 12.7k | double res; |
2255 | 12.7k | bool isNaN; |
2256 | 12.7k | if (!libwps::readDouble2Inv(input, res, isNaN)) |
2257 | 5 | { |
2258 | 5 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can read a uint16 zone\n")); |
2259 | 5 | f << "###"; |
2260 | 5 | break; |
2261 | 5 | } |
2262 | 12.7k | if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_FORMULA) |
2263 | 11.8k | cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER; |
2264 | 12.7k | cell.m_content.setValue(res); |
2265 | 12.7k | break; |
2266 | 12.7k | } |
2267 | 14.3k | case 0x19: |
2268 | 14.3k | { |
2269 | 14.3k | if (sz<=14) |
2270 | 409 | { |
2271 | 409 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the double10+formula zone seems too short\n")); |
2272 | 409 | f << "###"; |
2273 | 409 | } |
2274 | 14.3k | double res; |
2275 | 14.3k | bool isNaN; |
2276 | 14.3k | if (!libwps::readDouble10(input, res, isNaN)) |
2277 | 563 | { |
2278 | 563 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can read double10+formula for zone\n")); |
2279 | 563 | f << "###"; |
2280 | 563 | break; |
2281 | 563 | } |
2282 | 13.7k | cell.m_content.m_contentType=WKSContentListener::CellContent::C_FORMULA; |
2283 | 13.7k | cell.m_content.setValue(res); |
2284 | 13.7k | ascFile.addDelimiter(input->tell(),'|'); |
2285 | 13.7k | std::string error; |
2286 | 13.7k | if (!readFormula(*stream, endPos, sheetId, false, cell.m_content.m_formula, error)) |
2287 | 7.58k | { |
2288 | 7.58k | cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER; |
2289 | 7.58k | ascFile.addDelimiter(input->tell()-1, '#'); |
2290 | 7.58k | if (error.length()) f << error; |
2291 | 7.58k | break; |
2292 | 7.58k | } |
2293 | 6.16k | if (error.length()) f << error; |
2294 | 6.16k | if (input->tell()+1>=endPos) |
2295 | 5.69k | break; |
2296 | 472 | static bool first=true; |
2297 | 472 | if (first) |
2298 | 4 | { |
2299 | 4 | first=false; |
2300 | 4 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: find err message for double10+formula\n")); |
2301 | 4 | } |
2302 | | // find in one file "Formula failed to convert" |
2303 | 472 | error=""; |
2304 | 472 | auto remain=int(endPos-input->tell()); |
2305 | 14.7k | for (int i=0; i<remain; ++i) error += char(libwps::readU8(input)); |
2306 | 472 | f << "#err[msg]=" << error << ","; |
2307 | 472 | break; |
2308 | 6.16k | } |
2309 | 58.5k | case 0x25: |
2310 | 58.5k | { |
2311 | 58.5k | if (sz!=8) |
2312 | 1.31k | { |
2313 | 1.31k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the uint32 zone seems too short\n")); |
2314 | 1.31k | f << "###"; |
2315 | 1.31k | } |
2316 | 58.5k | double res; |
2317 | 58.5k | bool isNaN; |
2318 | 58.5k | if (!libwps::readDouble4Inv(input, res, isNaN)) |
2319 | 19 | { |
2320 | 19 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can read a uint32 zone\n")); |
2321 | 19 | f << "###"; |
2322 | 19 | break; |
2323 | 19 | } |
2324 | 58.5k | if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_FORMULA) |
2325 | 58.0k | cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER; |
2326 | 58.5k | cell.m_content.setValue(res); |
2327 | 58.5k | break; |
2328 | 58.5k | } |
2329 | 2.79k | case 0x27: |
2330 | 2.79k | { |
2331 | 2.79k | if (sz!=12) |
2332 | 2.48k | { |
2333 | 2.48k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the double8 zone seems too short\n")); |
2334 | 2.48k | f << "###"; |
2335 | 2.48k | } |
2336 | 2.79k | double res; |
2337 | 2.79k | bool isNaN; |
2338 | 2.79k | if (!libwps::readDouble8(input, res, isNaN)) |
2339 | 1.65k | { |
2340 | 1.65k | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can read a double8 zone\n")); |
2341 | 1.65k | f << "###"; |
2342 | 1.65k | break; |
2343 | 1.65k | } |
2344 | 1.14k | if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_FORMULA) |
2345 | 1.08k | cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER; |
2346 | 1.14k | cell.m_content.setValue(res); |
2347 | 1.14k | break; |
2348 | 2.79k | } |
2349 | 36.9k | case 0x28: |
2350 | 36.9k | { |
2351 | 36.9k | if (sz<=12) |
2352 | 726 | { |
2353 | 726 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: the double8 formula zone seems too short\n")); |
2354 | 726 | f << "###"; |
2355 | 726 | } |
2356 | 36.9k | double res; |
2357 | 36.9k | bool isNaN; |
2358 | 36.9k | if (!libwps::readDouble8(input, res, isNaN)) |
2359 | 642 | { |
2360 | 642 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: can read a double8 formula zone\n")); |
2361 | 642 | f << "###"; |
2362 | 642 | break; |
2363 | 642 | } |
2364 | 36.3k | cell.m_content.m_contentType=WKSContentListener::CellContent::C_FORMULA; |
2365 | 36.3k | cell.m_content.setValue(res); |
2366 | 36.3k | ascFile.addDelimiter(input->tell(),'|'); |
2367 | 36.3k | std::string error; |
2368 | 36.3k | if (!readFormula(*stream, endPos, sheetId, true, cell.m_content.m_formula, error)) |
2369 | 17.9k | { |
2370 | 17.9k | cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER; |
2371 | 17.9k | ascFile.addDelimiter(input->tell()-1, '#'); |
2372 | 17.9k | } |
2373 | 18.4k | else if (input->tell()+1<endPos) |
2374 | 489 | { |
2375 | | // often end with another bytes 03, probably for alignement |
2376 | 489 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: find extra data for double8 formula zone\n")); |
2377 | 489 | f << "###extra"; |
2378 | 489 | } |
2379 | 36.3k | if (error.length()) f << error; |
2380 | 36.3k | break; |
2381 | 36.9k | } |
2382 | 0 | default: |
2383 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::readCell: oops find unimplemented type\n")); |
2384 | 0 | break; |
2385 | 196k | } |
2386 | 196k | std::string extra=f.str(); |
2387 | 196k | f.str(""); |
2388 | 196k | f << "Entries(" << what << "):" << cell << "," << extra; |
2389 | 196k | if (input->tell()!=endPos) |
2390 | 49.9k | ascFile.addDelimiter(input->tell(),'|'); |
2391 | 196k | ascFile.addPos(pos); |
2392 | 196k | ascFile.addNote(f.str().c_str()); |
2393 | | |
2394 | 196k | return true; |
2395 | 196k | } |
2396 | | |
2397 | | //////////////////////////////////////////////////////////// |
2398 | | // filter |
2399 | | //////////////////////////////////////////////////////////// |
2400 | | |
2401 | | //////////////////////////////////////////////////////////// |
2402 | | // report |
2403 | | //////////////////////////////////////////////////////////// |
2404 | | |
2405 | | //////////////////////////////////////////////////////////// |
2406 | | // send data |
2407 | | //////////////////////////////////////////////////////////// |
2408 | | void LotusSpreadsheet::sendSpreadsheet(int sheetId) |
2409 | 2.27M | { |
2410 | 2.27M | if (!m_listener) |
2411 | 0 | { |
2412 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::sendSpreadsheet: I can not find the listener\n")); |
2413 | 0 | return; |
2414 | 0 | } |
2415 | 2.27M | if (sheetId<0||sheetId>=m_state->getNumSheet()) |
2416 | 0 | { |
2417 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::sendSpreadsheet: the sheet %d seems bad\n", sheetId)); |
2418 | 0 | return; |
2419 | 0 | } |
2420 | 2.27M | auto &sheet = m_state->getSheet(sheetId); |
2421 | 2.27M | m_listener->openSheet(sheet.getWidths(), getSheetName(sheetId)); |
2422 | 2.27M | m_mainParser.sendGraphics(sheetId); |
2423 | 2.27M | sheet.compressRowHeights(); |
2424 | | /* create a set to know which row needed to be send, each value of |
2425 | | the set corresponding to a position where the rows change |
2426 | | excepted the last position */ |
2427 | 2.27M | std::set<int> newRowSet; |
2428 | 2.27M | newRowSet.insert(0); |
2429 | 2.27M | std::map<Vec2i, LotusSpreadsheetInternal::Cell>::const_iterator cIt; |
2430 | 2.27M | int prevRow=-1; |
2431 | 2.39M | for (cIt=sheet.m_positionToCellMap.begin(); cIt!=sheet.m_positionToCellMap.end(); ++cIt) |
2432 | 114k | { |
2433 | 114k | if (prevRow==cIt->first[1]) |
2434 | 78.9k | continue; |
2435 | 35.1k | prevRow=cIt->first[1]; |
2436 | 35.1k | newRowSet.insert(prevRow); |
2437 | 35.1k | newRowSet.insert(prevRow+1); |
2438 | 35.1k | } |
2439 | 2.27M | size_t numRowStyle=m_state->m_rowStylesList.size(); |
2440 | 2.27M | for (auto const &rIt : sheet.m_rowToStyleIdMap) |
2441 | 16.0k | { |
2442 | 16.0k | Vec2i const &rows=rIt.first; |
2443 | 16.0k | size_t listId=rIt.second; |
2444 | 16.0k | if (listId>=numRowStyle) |
2445 | 0 | { |
2446 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::sendSpreadsheet: can not find list %d\n", int(listId))); |
2447 | 0 | continue; |
2448 | 0 | } |
2449 | 16.0k | newRowSet.insert(rows[0]); |
2450 | 16.0k | newRowSet.insert(rows[1]+1); |
2451 | 16.0k | } |
2452 | 2.27M | for (auto const &rIt : sheet.m_rowHeightMap) |
2453 | 51.7k | { |
2454 | 51.7k | Vec2i const &rows=rIt.first; |
2455 | 51.7k | newRowSet.insert(rows[0]); |
2456 | 51.7k | newRowSet.insert(rows[1]+1); |
2457 | 51.7k | } |
2458 | 2.27M | for (auto const &rIt : sheet.m_rowToExtraStyleMap) |
2459 | 3.98k | { |
2460 | 3.98k | if (rIt.second.empty()) continue; |
2461 | 3.67k | newRowSet.insert(rIt.first); |
2462 | 3.67k | newRowSet.insert(rIt.first+1); |
2463 | 3.67k | } |
2464 | | |
2465 | 2.27M | LotusSpreadsheetInternal::Table123Styles const *table123Styles=m_state->getTableStyle(sheetId); |
2466 | 2.27M | if (table123Styles) |
2467 | 435k | { |
2468 | 435k | for (auto const &rIt : table123Styles->m_rowsToColsToCellIdMap) |
2469 | 621k | { |
2470 | 621k | Vec2i const &rows=rIt.first; |
2471 | 621k | newRowSet.insert(rows[0]); |
2472 | 621k | newRowSet.insert(rows[1]+1); |
2473 | 621k | } |
2474 | 435k | for (auto const &rIt : table123Styles->m_rowsToColsToExtraStyleMap) |
2475 | 344k | { |
2476 | 344k | Vec2i const &rows=rIt.first; |
2477 | 344k | newRowSet.insert(rows[0]); |
2478 | 344k | newRowSet.insert(rows[1]+1); |
2479 | 344k | } |
2480 | 435k | for (auto const &rIt : table123Styles->m_rowsToColsToFormatStyleMap) |
2481 | 804k | { |
2482 | 804k | Vec2i const &rows = rIt.first; |
2483 | 804k | newRowSet.insert(rows[0]); |
2484 | 804k | newRowSet.insert(rows[1]+1); |
2485 | 804k | } |
2486 | 435k | } |
2487 | 3.61M | for (auto sIt=newRowSet.begin(); sIt!=newRowSet.end();) |
2488 | 3.61M | { |
2489 | 3.61M | int row=*(sIt++); |
2490 | 3.61M | if (row<0) |
2491 | 248k | { |
2492 | 248k | WPS_DEBUG_MSG(("LotusSpreadsheet::sendSpreadsheet: find a negative row %d\n", row)); |
2493 | 248k | continue; |
2494 | 248k | } |
2495 | 3.36M | if (sIt==newRowSet.end()) |
2496 | 2.27M | break; |
2497 | 1.08M | m_listener->openSheetRow(sheet.getRowHeight(row), *sIt-row); |
2498 | 1.08M | sendRowContent(sheet, row, table123Styles); |
2499 | 1.08M | m_listener->closeSheetRow(); |
2500 | 1.08M | } |
2501 | 2.27M | m_listener->closeSheet(); |
2502 | 2.27M | } |
2503 | | |
2504 | | void LotusSpreadsheet::sendRowContent(LotusSpreadsheetInternal::Spreadsheet const &sheet, int row, LotusSpreadsheetInternal::Table123Styles const *table123Styles) |
2505 | 1.08M | { |
2506 | 1.08M | if (m_listener.get() == nullptr) |
2507 | 0 | { |
2508 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::sendRowContent: I can not find the listener\n")); |
2509 | 0 | return; |
2510 | 0 | } |
2511 | | |
2512 | | // create a set of column we need to generate |
2513 | 1.08M | std::set<int> newColSet; |
2514 | 1.08M | newColSet.insert(0); |
2515 | | |
2516 | 1.08M | auto cIt=sheet.m_positionToCellMap.lower_bound(Vec2i(-1, row)); |
2517 | 1.08M | bool checkCell=cIt!=sheet.m_positionToCellMap.end() && cIt->first[1]==row; |
2518 | 1.08M | if (checkCell) |
2519 | 35.1k | { |
2520 | 149k | for (; cIt!=sheet.m_positionToCellMap.end() && cIt->first[1]==row ; ++cIt) |
2521 | 114k | { |
2522 | 114k | newColSet.insert(cIt->first[0]); |
2523 | 114k | newColSet.insert(cIt->first[0]+1); |
2524 | 114k | } |
2525 | 35.1k | cIt=sheet.m_positionToCellMap.lower_bound(Vec2i(-1, row)); |
2526 | 35.1k | } |
2527 | | |
2528 | 1.08M | LotusSpreadsheetInternal::RowStyles const *styles=nullptr; |
2529 | 1.08M | int styleId=sheet.getRowStyleId(row); |
2530 | 1.08M | if (styleId>=int(m_state->m_rowStylesList.size())) |
2531 | 0 | { |
2532 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::sendRowContent: I can not row style %d\n", styleId)); |
2533 | 0 | } |
2534 | 1.08M | else if (styleId>=0) |
2535 | 16.4k | styles=&m_state->m_rowStylesList[size_t(styleId)]; |
2536 | 1.08M | std::map<Vec2i, LotusSpreadsheetInternal::Style>::const_iterator sIt; |
2537 | 1.08M | if (styles) |
2538 | 16.4k | { |
2539 | 353k | for (sIt=styles->m_colsToStyleMap.begin(); sIt!=styles->m_colsToStyleMap.end(); ++sIt) |
2540 | 337k | { |
2541 | 337k | newColSet.insert(sIt->first[0]); |
2542 | 337k | newColSet.insert(sIt->first[1]+1); |
2543 | 337k | } |
2544 | 16.4k | sIt=styles->m_colsToStyleMap.begin(); |
2545 | 16.4k | if (sIt==styles->m_colsToStyleMap.end()) |
2546 | 926 | styles=nullptr; |
2547 | 16.4k | } |
2548 | | |
2549 | 1.08M | LotusSpreadsheetInternal::ExtraRowStyles const *extraStyles=nullptr; |
2550 | 1.08M | if (sheet.m_rowToExtraStyleMap.find(row)!=sheet.m_rowToExtraStyleMap.end()) |
2551 | 3.97k | extraStyles=&sheet.m_rowToExtraStyleMap.find(row)->second; |
2552 | 1.08M | std::map<Vec2i, LotusSpreadsheetInternal::ExtraStyle>::const_iterator eIt; |
2553 | 1.08M | if (extraStyles) |
2554 | 3.97k | { |
2555 | 208k | for (eIt=extraStyles->m_colsToStyleMap.begin(); eIt!=extraStyles->m_colsToStyleMap.end(); ++eIt) |
2556 | 204k | { |
2557 | 204k | if (eIt->second.empty()) continue; |
2558 | 120k | newColSet.insert(eIt->first[0]); |
2559 | 120k | newColSet.insert(eIt->first[1]+1); |
2560 | 120k | } |
2561 | 3.97k | eIt=extraStyles->m_colsToStyleMap.begin(); |
2562 | 3.97k | if (eIt==extraStyles->m_colsToStyleMap.end()) |
2563 | 0 | extraStyles=nullptr; |
2564 | 3.97k | } |
2565 | | |
2566 | 1.08M | auto const defFontType=m_mainParser.getDefaultFontType(); |
2567 | 1.08M | LotusSpreadsheetInternal::Style defaultStyle(defFontType); |
2568 | | |
2569 | 1.08M | std::map<Vec2i, LotusSpreadsheetInternal::Style> colToCellIdMap; |
2570 | 1.08M | std::map<Vec2i, LotusSpreadsheetInternal::Style>::const_iterator c123It; |
2571 | 1.08M | std::map<Vec2i, LotusSpreadsheetInternal::Extra123Style> colToExtraStyleMap; |
2572 | 1.08M | std::map<Vec2i, LotusSpreadsheetInternal::Extra123Style>::const_iterator e123It; |
2573 | 1.08M | std::map<Vec2i, LotusSpreadsheetInternal::Format123Style> colToFormatStyleMap; |
2574 | 1.08M | std::map<Vec2i, LotusSpreadsheetInternal::Format123Style>::const_iterator f123It; |
2575 | | |
2576 | 1.08M | std::map<int,int> potentialMergeMap; |
2577 | 1.08M | if (table123Styles) |
2578 | 989k | { |
2579 | 989k | for (auto const &rIt : table123Styles->m_rowsToColsToCellIdMap) |
2580 | 9.63M | { |
2581 | 9.63M | if (rIt.first[0]>row || rIt.first[1]<row) continue; |
2582 | 715k | for (auto const &colIt : rIt.second) |
2583 | 1.19M | { |
2584 | 1.19M | LotusSpreadsheetInternal::Style style(defFontType); |
2585 | 1.19M | WPSFont font; |
2586 | 1.19M | if (!m_styleManager->updateCellStyle(colIt.second, style, font, style.m_fontType)) |
2587 | 577k | continue; |
2588 | 615k | style.setFont(font); |
2589 | 615k | colToCellIdMap.insert(std::map<Vec2i, LotusSpreadsheetInternal::Style>::value_type(colIt.first,style)); |
2590 | 615k | newColSet.insert(colIt.first[0]); |
2591 | 615k | newColSet.insert(colIt.first[1]+1); |
2592 | 615k | } |
2593 | 715k | } |
2594 | 989k | for (auto const &rIt : table123Styles->m_rowsToColsToExtraStyleMap) |
2595 | 8.37M | { |
2596 | 8.37M | if (rIt.first[0]>row || rIt.first[1]<row) continue; |
2597 | 410k | for (auto const &colIt : rIt.second) |
2598 | 454k | { |
2599 | 454k | colToExtraStyleMap.insert(std::map<Vec2i, LotusSpreadsheetInternal::Extra123Style>::value_type(colIt.first,colIt.second)); |
2600 | 454k | newColSet.insert(colIt.first[0]); |
2601 | 454k | newColSet.insert(colIt.first[1]+1); |
2602 | 454k | } |
2603 | 410k | } |
2604 | 989k | for (auto const &rIt : table123Styles->m_rowsToColsToFormatStyleMap) |
2605 | 13.2M | { |
2606 | 13.2M | if (rIt.first[0]>row || rIt.first[1]<row) continue; |
2607 | 895k | for (auto const &colIt : rIt.second) |
2608 | 1.39M | { |
2609 | 1.39M | colToFormatStyleMap.insert(std::map<Vec2i, LotusSpreadsheetInternal::Format123Style>::value_type(colIt.first,colIt.second)); |
2610 | 1.39M | newColSet.insert(colIt.first[0]); |
2611 | 1.39M | newColSet.insert(colIt.first[1]+1); |
2612 | 1.39M | if (colIt.first[0]!=colIt.first[1] && colIt.second.m_alignAcrossColumn) |
2613 | 755k | potentialMergeMap[colIt.first[0]]=colIt.first[1]+1; |
2614 | 1.39M | } |
2615 | 895k | } |
2616 | 989k | c123It=colToCellIdMap.begin(); |
2617 | 989k | e123It=colToExtraStyleMap.begin(); |
2618 | 989k | f123It=colToFormatStyleMap.begin(); |
2619 | 989k | if (table123Styles->m_defaultCellId>=0) |
2620 | 5.57k | { |
2621 | 5.57k | WPSFont font; |
2622 | 5.57k | if (m_styleManager->updateCellStyle(table123Styles->m_defaultCellId, defaultStyle, font, defaultStyle.m_fontType)) |
2623 | 4.06k | defaultStyle.setFont(font); |
2624 | 5.57k | } |
2625 | 989k | if (colToCellIdMap.empty() && colToExtraStyleMap.empty() && colToFormatStyleMap.empty()) |
2626 | 150k | table123Styles=nullptr; |
2627 | 989k | } |
2628 | | |
2629 | 1.08M | LotusSpreadsheetInternal::Cell emptyCell; |
2630 | 3.70M | for (auto colIt=newColSet.begin(); colIt!=newColSet.end();) |
2631 | 3.70M | { |
2632 | 3.70M | int const col=*colIt; |
2633 | 3.70M | ++colIt; |
2634 | 3.70M | if (colIt==newColSet.end()) |
2635 | 1.08M | break; |
2636 | 2.61M | int const endCol=*colIt; |
2637 | 2.61M | if (styles) |
2638 | 340k | { |
2639 | 652k | while (sIt->first[1] < col) |
2640 | 313k | { |
2641 | 313k | ++sIt; |
2642 | 313k | if (sIt==styles->m_colsToStyleMap.end()) |
2643 | 588 | { |
2644 | 588 | styles=nullptr; |
2645 | 588 | break; |
2646 | 588 | } |
2647 | 313k | } |
2648 | 340k | } |
2649 | 2.61M | if (extraStyles) |
2650 | 153k | { |
2651 | 351k | while (eIt->first[1] < col) |
2652 | 198k | { |
2653 | 198k | ++eIt; |
2654 | 198k | if (eIt==extraStyles->m_colsToStyleMap.end()) |
2655 | 664 | { |
2656 | 664 | extraStyles=nullptr; |
2657 | 664 | break; |
2658 | 664 | } |
2659 | 198k | } |
2660 | 153k | } |
2661 | 2.61M | auto style=styles ? sIt->second : defaultStyle; |
2662 | 2.61M | bool hasStyle=styles!=nullptr; |
2663 | 2.61M | if (table123Styles) |
2664 | 1.92M | { |
2665 | 2.30M | while (c123It!=colToCellIdMap.end() && c123It->first[1] < col) |
2666 | 379k | ++c123It; |
2667 | 1.92M | if (c123It!=colToCellIdMap.end() && c123It->first[0] <= col && c123It->first[1] >= col) |
2668 | 248k | { |
2669 | 248k | style=c123It->second; |
2670 | 248k | hasStyle=true; |
2671 | 248k | } |
2672 | | |
2673 | 2.05M | while (e123It!=colToExtraStyleMap.end() && e123It->first[1] < col) |
2674 | 130k | ++e123It; |
2675 | 1.92M | if (e123It!=colToExtraStyleMap.end() && e123It->first[0] <= col && e123It->first[1] >= col) |
2676 | 245k | { |
2677 | 245k | e123It->second.update(style); |
2678 | 245k | hasStyle=true; |
2679 | 245k | } |
2680 | 2.68M | while (f123It!=colToFormatStyleMap.end() && f123It->first[1] < col) |
2681 | 758k | ++f123It; |
2682 | 1.92M | if (f123It!=colToFormatStyleMap.end() && f123It->first[0] <= col && f123It->first[1] >= col) |
2683 | 656k | { |
2684 | 656k | f123It->second.update(style); |
2685 | 656k | hasStyle=true; |
2686 | 656k | } |
2687 | 1.92M | if (c123It==colToCellIdMap.end() && e123It==colToExtraStyleMap.end() && f123It==colToFormatStyleMap.end()) |
2688 | 59.9k | table123Styles=nullptr; |
2689 | 1.92M | } |
2690 | | |
2691 | 2.61M | if (extraStyles && !eIt->second.empty()) |
2692 | 130k | { |
2693 | 130k | eIt->second.update(style); |
2694 | 130k | hasStyle=true; |
2695 | 130k | } |
2696 | 2.61M | bool hasCell=false; |
2697 | 2.61M | if (checkCell) |
2698 | 152k | { |
2699 | 234k | while (cIt->first[1]==row && cIt->first[0]<col) |
2700 | 82.8k | { |
2701 | 82.8k | ++cIt; |
2702 | 82.8k | if (cIt==sheet.m_positionToCellMap.end()) |
2703 | 957 | { |
2704 | 957 | checkCell=false; |
2705 | 957 | break; |
2706 | 957 | } |
2707 | 82.8k | } |
2708 | 152k | if (checkCell && cIt->first[1]!=row) checkCell=false; |
2709 | 152k | hasCell=checkCell && cIt->first[0]==col; |
2710 | 152k | } |
2711 | 2.61M | if (!hasCell && !hasStyle) continue; |
2712 | 1.46M | if (!hasCell) emptyCell.setPosition(Vec2i(col, row)); |
2713 | 1.46M | bool canMerge=false; |
2714 | 1.46M | if (hasCell && potentialMergeMap.find(col)!=potentialMergeMap.end()) |
2715 | 1.82k | { |
2716 | 1.82k | int newEndCol=potentialMergeMap.find(col)->second; |
2717 | 1.82k | auto newCIt=cIt; |
2718 | 1.82k | if (newCIt!=sheet.m_positionToCellMap.end()) ++newCIt; |
2719 | 1.82k | canMerge=(newCIt==sheet.m_positionToCellMap.end() || newCIt->first[1]!=row || newCIt->first[0]>=newEndCol); |
2720 | 1.82k | } |
2721 | 1.46M | if (canMerge) |
2722 | 1.28k | { |
2723 | 1.28k | int newEndCol=potentialMergeMap.find(col)->second; |
2724 | 11.8k | while (colIt!=newColSet.end() && *colIt<newEndCol) |
2725 | 10.5k | ++colIt; |
2726 | 1.28k | auto cell=cIt->second; |
2727 | 1.28k | cell.setNumSpannedCells(Vec2i(newEndCol-col,1)); |
2728 | 1.28k | sendCellContent(cell, style, 1); |
2729 | 1.28k | } |
2730 | 1.46M | else |
2731 | 1.46M | sendCellContent(hasCell ? cIt->second : emptyCell, style, endCol-col); |
2732 | 1.46M | } |
2733 | 1.08M | } |
2734 | | |
2735 | | void LotusSpreadsheet::sendCellContent(LotusSpreadsheetInternal::Cell const &cell, |
2736 | | LotusSpreadsheetInternal::Style const &style, |
2737 | | int numRepeated) |
2738 | 1.46M | { |
2739 | 1.46M | if (m_listener.get() == nullptr) |
2740 | 0 | { |
2741 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::sendCellContent: I can not find the listener\n")); |
2742 | 0 | return; |
2743 | 0 | } |
2744 | | |
2745 | 1.46M | LotusSpreadsheetInternal::Style cellStyle(style); |
2746 | 1.46M | if (cell.m_hAlignement!=WPSCellFormat::HALIGN_DEFAULT) |
2747 | 12.7k | cellStyle.setHAlignment(cell.m_hAlignement); |
2748 | | |
2749 | 1.46M | auto fontType = cellStyle.m_fontType; |
2750 | | |
2751 | 1.46M | m_listener->setFont(cellStyle.getFont()); |
2752 | | |
2753 | 1.46M | LotusSpreadsheetInternal::Cell finalCell(cell); |
2754 | 1.46M | finalCell.WPSCellFormat::operator=(cellStyle); |
2755 | 1.46M | WKSContentListener::CellContent content(cell.m_content); |
2756 | 1.46M | for (auto &f : content.m_formula) |
2757 | 137k | { |
2758 | 137k | if (f.m_type!=WKSContentListener::FormulaInstruction::F_Text) |
2759 | 133k | continue; |
2760 | 3.69k | std::string &text=f.m_content; |
2761 | 3.69k | librevenge::RVNGString finalString=libwps_tools_win::Font::unicodeString(text, fontType); |
2762 | 3.69k | text=finalString.cstr(); |
2763 | 3.69k | } |
2764 | 1.46M | m_listener->openSheetCell(finalCell, content, numRepeated); |
2765 | | |
2766 | 1.46M | if (cell.m_input && cell.m_content.m_textEntry.valid()) |
2767 | 37.8k | { |
2768 | 37.8k | RVNGInputStreamPtr input=cell.m_input; |
2769 | 37.8k | input->seek(cell.m_content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET); |
2770 | 37.8k | sendText(input, cell.m_content.m_textEntry.end(), cellStyle); |
2771 | 37.8k | } |
2772 | 1.46M | if (cell.m_comment.valid()) |
2773 | 2.23k | { |
2774 | 2.23k | WPSSubDocumentPtr subdoc(new LotusSpreadsheetInternal::SubDocument |
2775 | 2.23k | (cell.m_input, *this, cell.m_comment)); |
2776 | 2.23k | m_listener->insertComment(subdoc); |
2777 | 2.23k | } |
2778 | 1.46M | m_listener->closeSheetCell(); |
2779 | 1.46M | } |
2780 | | |
2781 | | //////////////////////////////////////////////////////////// |
2782 | | // formula |
2783 | | //////////////////////////////////////////////////////////// |
2784 | | bool LotusSpreadsheet::parseVariable(std::string const &variable, WKSContentListener::FormulaInstruction &instr) |
2785 | 35.3k | { |
2786 | 35.3k | std::regex exp("<<([^>]+)>>([^:]+):([A-Z]+)([0-9]+)"); |
2787 | 35.3k | std::regex exp2("<<([^>]+)>>([^:]+):([A-Z]+)([0-9]+)\\.\\.([^:]+):([A-Z]+)([0-9]+)"); |
2788 | 35.3k | std::smatch base_match; |
2789 | 35.3k | size_t dim=0; |
2790 | 35.3k | if (std::regex_match(variable,base_match,exp) && base_match.size() == 5) |
2791 | 1.03k | dim=1; |
2792 | 34.3k | else if (std::regex_match(variable,base_match,exp2) && base_match.size() == 8) |
2793 | 853 | { |
2794 | 853 | if (base_match[2].str()!=base_match[5].str()) |
2795 | 852 | instr.m_sheetName[1]=base_match[5].str().c_str(); |
2796 | 853 | dim=2; |
2797 | 853 | } |
2798 | 33.4k | else // can also be variable, <<File>>variable, db.field ? |
2799 | 33.4k | return false; |
2800 | 1.88k | instr.m_fileName=base_match[1].str().c_str(); |
2801 | 1.88k | instr.m_sheetName[0]=base_match[2].str().c_str(); |
2802 | 1.88k | instr.m_type=dim==1 ? WKSContentListener::FormulaInstruction::F_Cell : WKSContentListener::FormulaInstruction::F_CellList; |
2803 | 4.26k | for (size_t d=0; d<dim; ++d) |
2804 | 2.72k | { |
2805 | 2.72k | int col=0; |
2806 | 2.72k | std::string sCol=base_match[3+3*d].str(); |
2807 | 2.72k | for (auto c:sCol) |
2808 | 7.32k | { |
2809 | 7.32k | if (col > (std::numeric_limits<int>::max() - int(c-'A')) / 26) |
2810 | 195 | { |
2811 | 195 | WPS_DEBUG_MSG(("LotusSpreadsheet::parseVariable: oops the column seems bad\n")); |
2812 | 195 | return false; |
2813 | 195 | } |
2814 | 7.12k | col=26*col+int(c-'A'); |
2815 | 7.12k | } |
2816 | 2.53k | int row=0; |
2817 | 2.53k | std::string sRow=base_match[4+3*d].str(); |
2818 | 2.53k | for (auto c:sRow) |
2819 | 7.99k | { |
2820 | 7.99k | if (row > (std::numeric_limits<int>::max() - int(c-'0')) / 10) |
2821 | 150 | { |
2822 | 150 | WPS_DEBUG_MSG(("LotusSpreadsheet::parseVariable: oops the row seems bad\n")); |
2823 | 150 | return false; |
2824 | 150 | } |
2825 | 7.84k | row=10*row+int(c-'0'); |
2826 | 7.84k | } |
2827 | 2.38k | instr.m_position[d]=Vec2i(col,row-1); |
2828 | 2.38k | instr.m_positionRelative[d]=Vec2b(true,true); |
2829 | 2.38k | } |
2830 | 1.54k | return true; |
2831 | 1.88k | } |
2832 | | |
2833 | | bool LotusSpreadsheet::readCell(WPSStream &stream, int sId, bool isList, WKSContentListener::FormulaInstruction &instr) |
2834 | 46.5k | { |
2835 | 46.5k | RVNGInputStreamPtr &input=stream.m_input; |
2836 | 46.5k | instr=WKSContentListener::FormulaInstruction(); |
2837 | 46.5k | instr.m_type=isList ? WKSContentListener::FormulaInstruction::F_CellList : |
2838 | 46.5k | WKSContentListener::FormulaInstruction::F_Cell; |
2839 | 46.5k | auto flags=int(libwps::readU8(input)); |
2840 | 65.5k | for (int i=0; i<2; ++i) |
2841 | 56.0k | { |
2842 | 56.0k | auto row=int(libwps::readU16(input)); |
2843 | 56.0k | auto sheetId=int(libwps::readU8(input)); |
2844 | 56.0k | auto col=int(libwps::readU8(input)); |
2845 | 56.0k | instr.m_position[i]=Vec2i(col, row); |
2846 | 56.0k | int wh=(i==0) ? (flags&0xF) : (flags>>4); |
2847 | 56.0k | instr.m_positionRelative[i]=Vec2b((wh&1)!=0, (wh&2)!=0); |
2848 | 56.0k | if (sheetId!=sId) |
2849 | 32.4k | instr.m_sheetName[i]=getSheetName(sheetId); |
2850 | 56.0k | if (!isList) break; |
2851 | 56.0k | } |
2852 | 46.5k | return true; |
2853 | 46.5k | } |
2854 | | |
2855 | | namespace LotusSpreadsheetInternal |
2856 | | { |
2857 | | struct Functions |
2858 | | { |
2859 | | char const *m_name; |
2860 | | int m_arity; |
2861 | | }; |
2862 | | |
2863 | | static Functions const s_listFunctions[] = |
2864 | | { |
2865 | | { "", 0} /*SPEC: number*/, {"", 0}/*SPEC: cell*/, {"", 0}/*SPEC: cells*/, {"=", 1} /*SPEC: end of formula*/, |
2866 | | { "(", 1} /* SPEC: () */, {"", 0}/*SPEC: number*/, { "", 0} /*SPEC: text*/, {"", 0}/*name reference*/, |
2867 | | { "", 0}/* SPEC: abs name ref*/, {"", 0}/* SPEC: err range ref*/, { "", 0}/* SPEC: err cell ref*/, {"", 0}/* SPEC: err constant*/, |
2868 | | { "", -2} /* unused*/, { "", -2} /*unused*/, {"-", 1}, {"+", 2}, |
2869 | | |
2870 | | { "-", 2},{ "*", 2},{ "/", 2},{ "^", 2}, |
2871 | | { "=", 2},{ "<>", 2},{ "<=", 2},{ ">=", 2}, |
2872 | | { "<", 2},{ ">", 2},{ "And", 2},{ "Or", 2}, |
2873 | | { "Not", 1}, { "+", 1}, { "&", 2}, { "NA", 0} /* not applicable*/, |
2874 | | |
2875 | | { "NA", 0} /* Error*/,{ "Abs", 1},{ "Int", 1},{ "Sqrt", 1}, |
2876 | | { "Log10", 1},{ "Ln", 1},{ "Pi", 0},{ "Sin", 1}, |
2877 | | { "Cos", 1},{ "Tan", 1},{ "Atan2", 2},{ "Atan", 1}, |
2878 | | { "Asin", 1},{ "Acos", 1},{ "Exp", 1},{ "Mod", 2}, |
2879 | | |
2880 | | { "Choose", -1},{ "IsNa", 1},{ "IsError", 1},{ "False", 0}, |
2881 | | { "True", 0},{ "Rand", 0},{ "Date", 3},{ "Now", 0}, |
2882 | | { "PMT", 3} /*BAD*/,{ "PV", 3} /*BAD*/,{ "FV", 3} /*BAD*/,{ "IF", 3}, |
2883 | | { "Day", 1},{ "Month", 1},{ "Year", 1},{ "Round", 2}, |
2884 | | |
2885 | | { "Time", 3},{ "Hour", 1},{ "Minute", 1},{ "Second", 1}, |
2886 | | { "IsNumber", 1},{ "IsText", 1},{ "Len", 1},{ "Value", 1}, |
2887 | | { "Text", 2}/* or fixed*/, { "Mid", 3}, { "Char", 1},{ "Ascii", 1}, |
2888 | | { "Find", 3},{ "DateValue", 1} /*checkme*/,{ "TimeValue", 1} /*checkme*/,{ "CellPointer", 1} /*checkme*/, |
2889 | | |
2890 | | { "Sum", -1},{ "Average", -1},{ "COUNT", -1},{ "Min", -1}, |
2891 | | { "Max", -1},{ "VLookUp", 3},{ "NPV", 2}, { "Var", -1}, |
2892 | | { "StDev", -1},{ "IRR", 2} /*BAD*/, { "HLookup", 3},{ "DSum", 3}, |
2893 | | { "DAvg", 3},{ "DCount", 3},{ "DMin", 3},{ "DMax", 3}, |
2894 | | |
2895 | | { "DVar", 3},{ "DStd", 3},{ "Index", 3}, { "Columns", 1}, |
2896 | | { "Rows", 1},{ "Rept", 2},{ "Upper", 1},{ "Lower", 1}, |
2897 | | { "Left", 2},{ "Right", 2},{ "Replace", 4}, { "Proper", 1}, |
2898 | | { "Cell", 2} /*checkme*/,{ "Trim", 1},{ "Clean", 1} /*UNKN*/,{ "T", 1}, |
2899 | | |
2900 | | { "IsNonText", 1},{ "Exact", 2},{ "", -2} /*App not implemented*/,{ "", 3} /*UNKN*/, |
2901 | | { "Rate", 3} /*BAD*/,{ "TERM", 3}, { "CTERM", 3}, { "SLN", 3}, |
2902 | | { "SYD", 4},{ "DDB", 4},{ "SplFunc", -1} /*SplFunc*/,{ "Sheets", 1}, |
2903 | | { "Info", 1},{ "SumProduct", -1},{ "IsRange", 1},{ "DGet", -1}, |
2904 | | |
2905 | | { "DQuery", -1},{ "Coord", 4}, { "", -2} /*reserved*/, { "Today", 0}, |
2906 | | { "Vdb", -1},{ "Dvars", -1},{ "Dstds", -1},{ "Vars", -1}, |
2907 | | { "Stds", -1},{ "D360", 2},{ "", -2} /*reserved*/,{ "IsApp", 0}, |
2908 | | { "IsAaf", -1},{ "Weekday", 1},{ "DateDiff", 3},{ "Rank", -1}, |
2909 | | |
2910 | | { "NumberString", 2},{ "DateString", 1}, { "Decimal", 1}, { "Hex", 1}, |
2911 | | { "Db", 4},{ "PMTI", 4},{ "SPI", 4},{ "Fullp", 1}, |
2912 | | { "Halfp", 1},{ "PureAVG", -1},{ "PureCount", -1},{ "PureMax", -1}, |
2913 | | { "PureMin", -1},{ "PureSTD", -1},{ "PureVar", -1},{ "PureSTDS", -1}, |
2914 | | |
2915 | | { "PureVars", -1},{ "PMT2", 3}, { "PV2", 3}, { "FV2", 3}, |
2916 | | { "TERM2", 3},{ "", -2} /*UNKN*/,{ "D360", 2},{ "", -2} /*UNKN*/, |
2917 | | { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/, { "", -2} /*UNKN*/, |
2918 | | { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/, |
2919 | | }; |
2920 | | } |
2921 | | |
2922 | | bool LotusSpreadsheet::readFormula(WPSStream &stream, long endPos, int sheetId, bool newFormula, |
2923 | | std::vector<WKSContentListener::FormulaInstruction> &formula, std::string &error) |
2924 | 50.0k | { |
2925 | 50.0k | int const vers=version(); |
2926 | 50.0k | RVNGInputStreamPtr &input=stream.m_input; |
2927 | 50.0k | formula.resize(0); |
2928 | 50.0k | error = ""; |
2929 | 50.0k | long pos = input->tell(); |
2930 | 50.0k | if (endPos - pos < 1) return false; |
2931 | | |
2932 | 49.4k | std::stringstream f; |
2933 | 49.4k | std::vector<std::vector<WKSContentListener::FormulaInstruction> > stack; |
2934 | 49.4k | bool ok = true; |
2935 | 345k | while (long(input->tell()) != endPos) |
2936 | 344k | { |
2937 | 344k | double val = 0.0; |
2938 | 344k | bool isNaN; |
2939 | 344k | pos = input->tell(); |
2940 | 344k | if (pos > endPos) return false; |
2941 | 343k | auto wh = int(libwps::readU8(input)); |
2942 | 343k | int arity = 0; |
2943 | 343k | WKSContentListener::FormulaInstruction instr; |
2944 | 343k | switch (wh) |
2945 | 343k | { |
2946 | 30.5k | case 0x0: |
2947 | 30.5k | if ((!newFormula && (endPos-pos<11 || !libwps::readDouble10(input, val, isNaN))) || |
2948 | 28.9k | (newFormula && (endPos-pos<9 || !libwps::readDouble8(input, val, isNaN)))) |
2949 | 7.38k | { |
2950 | 7.38k | f.str(""); |
2951 | 7.38k | f << "###number"; |
2952 | 7.38k | error=f.str(); |
2953 | 7.38k | ok = false; |
2954 | 7.38k | break; |
2955 | 7.38k | } |
2956 | 23.1k | instr.m_type=WKSContentListener::FormulaInstruction::F_Double; |
2957 | 23.1k | instr.m_doubleValue=val; |
2958 | 23.1k | break; |
2959 | 38.0k | case 0x1: |
2960 | 38.0k | { |
2961 | 38.0k | if (endPos-pos<6 || !readCell(stream, sheetId, false, instr)) |
2962 | 949 | { |
2963 | 949 | f.str(""); |
2964 | 949 | f << "###cell short"; |
2965 | 949 | error=f.str(); |
2966 | 949 | ok = false; |
2967 | 949 | break; |
2968 | 949 | } |
2969 | 37.0k | break; |
2970 | 38.0k | } |
2971 | 37.0k | case 0x2: |
2972 | 9.82k | { |
2973 | 9.82k | if (endPos-pos<10 || !readCell(stream, sheetId, true, instr)) |
2974 | 331 | { |
2975 | 331 | f.str(""); |
2976 | 331 | f << "###list cell short"; |
2977 | 331 | error=f.str(); |
2978 | 331 | ok = false; |
2979 | 331 | break; |
2980 | 331 | } |
2981 | 9.48k | break; |
2982 | 9.82k | } |
2983 | 25.3k | case 0x5: |
2984 | 25.3k | instr.m_type=WKSContentListener::FormulaInstruction::F_Double; |
2985 | 25.3k | if ((!newFormula && (endPos-pos<3 || !libwps::readDouble2Inv(input, val, isNaN))) || |
2986 | 25.2k | (newFormula && (endPos-pos<5 || !libwps::readDouble4Inv(input, val, isNaN)))) |
2987 | 446 | { |
2988 | 446 | WPS_DEBUG_MSG(("LotusSpreadsheet::readFormula: can read a uint16/32 zone\n")); |
2989 | 446 | f << "###uint16/32"; |
2990 | 446 | error=f.str(); |
2991 | 446 | break; |
2992 | 446 | } |
2993 | 24.9k | instr.m_doubleValue=val; |
2994 | 24.9k | break; |
2995 | 5.47k | case 0x6: |
2996 | 5.47k | instr.m_type=WKSContentListener::FormulaInstruction::F_Text; |
2997 | 25.4k | while (!input->isEnd()) |
2998 | 25.4k | { |
2999 | 25.4k | if (input->tell() >= endPos) |
3000 | 78 | { |
3001 | 78 | ok=false; |
3002 | 78 | break; |
3003 | 78 | } |
3004 | 25.3k | auto c = char(libwps::readU8(input)); |
3005 | 25.3k | if (c==0) break; |
3006 | 19.9k | instr.m_content += c; |
3007 | 19.9k | } |
3008 | 5.47k | break; |
3009 | 22.0k | case 0x7: |
3010 | 45.7k | case 0x8: |
3011 | 45.7k | { |
3012 | 45.7k | std::string variable(""); |
3013 | 280k | while (!input->isEnd()) |
3014 | 280k | { |
3015 | 280k | if (input->tell() >= endPos) |
3016 | 232 | { |
3017 | 232 | ok=false; |
3018 | 232 | break; |
3019 | 232 | } |
3020 | 280k | auto c = char(libwps::readU8(input)); |
3021 | 280k | if (c==0) break; |
3022 | 234k | variable += c; |
3023 | 234k | } |
3024 | 45.7k | if (!ok) |
3025 | 232 | break; |
3026 | 45.5k | if (m_state->m_nameToCellsMap.find(variable)==m_state->m_nameToCellsMap.end()) |
3027 | 35.3k | { |
3028 | 35.3k | if (parseVariable(variable, instr)) break; |
3029 | | // can also be a database field, ... |
3030 | 33.8k | WPS_DEBUG_MSG(("LotusSpreadsheet::readFormula: can not find variable %s\n", variable.c_str())); |
3031 | 33.8k | f << "##variable=" << variable << ","; |
3032 | 33.8k | error=f.str(); |
3033 | 33.8k | instr.m_type=WKSContentListener::FormulaInstruction::F_Text; |
3034 | 33.8k | instr.m_content = variable; |
3035 | 33.8k | break; |
3036 | 35.3k | } |
3037 | 10.2k | auto const &cells=m_state->m_nameToCellsMap.find(variable)->second; |
3038 | 10.2k | instr.m_position[0]=cells.m_positions[0]; |
3039 | 10.2k | instr.m_position[1]=cells.m_positions[1]; |
3040 | 10.2k | instr.m_positionRelative[0]=instr.m_positionRelative[1]=Vec2b(wh==7,wh==7); |
3041 | 30.6k | for (int i=0; i<2; ++i) |
3042 | 20.4k | { |
3043 | 20.4k | if (cells.m_ids[i] != sheetId) |
3044 | 17.3k | instr.m_sheetName[i]=getSheetName(cells.m_ids[i]); |
3045 | 20.4k | } |
3046 | 10.2k | instr.m_type=cells.m_positions[0]==cells.m_positions[1] ? |
3047 | 1.47k | WKSContentListener::FormulaInstruction::F_Cell : |
3048 | 10.2k | WKSContentListener::FormulaInstruction::F_CellList; |
3049 | 10.2k | break; |
3050 | 45.5k | } |
3051 | 188k | default: |
3052 | 188k | if (wh >= 0xb0 || LotusSpreadsheetInternal::s_listFunctions[wh].m_arity == -2) |
3053 | 6.72k | { |
3054 | 6.72k | f.str(""); |
3055 | 6.72k | f << "##Funct" << std::hex << wh; |
3056 | 6.72k | error=f.str(); |
3057 | 6.72k | ok = false; |
3058 | 6.72k | break; |
3059 | 6.72k | } |
3060 | 182k | instr.m_type=WKSContentListener::FormulaInstruction::F_Function; |
3061 | 182k | instr.m_content=LotusSpreadsheetInternal::s_listFunctions[wh].m_name; |
3062 | 182k | ok=!instr.m_content.empty(); |
3063 | 182k | arity = LotusSpreadsheetInternal::s_listFunctions[wh].m_arity; |
3064 | 182k | if (arity == -1) |
3065 | 32.8k | arity = int(libwps::read8(input)); |
3066 | 182k | if (wh==0x7a) // special Spell function |
3067 | 963 | { |
3068 | 963 | auto sSz=int(libwps::readU16(input)); |
3069 | 963 | if (input->tell()+sSz>endPos || (vers>=3 && sSz<2)) |
3070 | 306 | { |
3071 | 306 | WPS_DEBUG_MSG(("LotusSpreadsheet::readFormula: can not find spell function length\n")); |
3072 | 306 | f << "###spell[length]=" << sSz << ","; |
3073 | 306 | error = f.str(); |
3074 | 306 | ok=false; |
3075 | 306 | } |
3076 | 963 | if (vers>=3) // 1801 |
3077 | 770 | { |
3078 | 770 | f << "f0=" << std::dec << libwps::readU16(input) << std::dec << ","; |
3079 | 770 | sSz-=2; |
3080 | 770 | } |
3081 | 963 | WKSContentListener::FormulaInstruction lastArg; |
3082 | 963 | lastArg.m_type=WKSContentListener::FormulaInstruction::F_Text; |
3083 | 74.5k | for (int i=0; i<sSz; ++i) |
3084 | 74.3k | { |
3085 | 74.3k | auto c = char(libwps::readU8(input)); |
3086 | 74.3k | if (c==0) break; |
3087 | 73.6k | lastArg.m_content += c; |
3088 | 73.6k | } |
3089 | 963 | std::vector<WKSContentListener::FormulaInstruction> child; |
3090 | 963 | child.push_back(lastArg); |
3091 | 963 | stack.push_back(child); |
3092 | 963 | ++arity; |
3093 | 963 | break; |
3094 | 963 | } |
3095 | 181k | break; |
3096 | 343k | } |
3097 | | |
3098 | 343k | if (!ok) break; |
3099 | 327k | std::vector<WKSContentListener::FormulaInstruction> child; |
3100 | 327k | if (instr.m_type!=WKSContentListener::FormulaInstruction::F_Function) |
3101 | 145k | { |
3102 | 145k | child.push_back(instr); |
3103 | 145k | stack.push_back(child); |
3104 | 145k | continue; |
3105 | 145k | } |
3106 | 181k | size_t numElt = stack.size(); |
3107 | 181k | if (int(numElt) < arity) |
3108 | 5.70k | { |
3109 | 5.70k | f.str(""); |
3110 | 5.70k | f << instr.m_content << "[##" << arity << "]"; |
3111 | 5.70k | error=f.str(); |
3112 | 5.70k | ok = false; |
3113 | 5.70k | break; |
3114 | 5.70k | } |
3115 | | // |
3116 | | // first treat the special cases |
3117 | | // |
3118 | 175k | if (arity==3 && instr.m_type==WKSContentListener::FormulaInstruction::F_Function && instr.m_content=="TERM") |
3119 | 3.77k | { |
3120 | | // @TERM(pmt,pint,fv) -> NPER(pint,-pmt,pv=0,fv) |
3121 | 3.77k | auto pmt=stack[size_t(int(numElt)-3)]; |
3122 | 3.77k | auto pint=stack[size_t(int(numElt)-2)]; |
3123 | 3.77k | auto fv=stack[size_t(int(numElt)-1)]; |
3124 | | |
3125 | 3.77k | stack.resize(size_t(++numElt)); |
3126 | | // pint |
3127 | 3.77k | stack[size_t(int(numElt)-4)]=pint; |
3128 | | //-pmt |
3129 | 3.77k | auto &node=stack[size_t(int(numElt)-3)]; |
3130 | 3.77k | instr.m_type=WKSContentListener::FormulaInstruction::F_Operator; |
3131 | 3.77k | instr.m_content="-"; |
3132 | 3.77k | node.resize(0); |
3133 | 3.77k | node.push_back(instr); |
3134 | 3.77k | instr.m_content="("; |
3135 | 3.77k | node.push_back(instr); |
3136 | 3.77k | node.insert(node.end(), pmt.begin(), pmt.end()); |
3137 | 3.77k | instr.m_content=")"; |
3138 | 3.77k | node.push_back(instr); |
3139 | | //pv=zero |
3140 | 3.77k | instr.m_type=WKSContentListener::FormulaInstruction::F_Long; |
3141 | 3.77k | instr.m_longValue=0; |
3142 | 3.77k | stack[size_t(int(numElt)-2)].resize(0); |
3143 | 3.77k | stack[size_t(int(numElt)-2)].push_back(instr); |
3144 | | //fv |
3145 | 3.77k | stack[size_t(int(numElt)-1)]=fv; |
3146 | 3.77k | arity=4; |
3147 | 3.77k | instr.m_type=WKSContentListener::FormulaInstruction::F_Function; |
3148 | 3.77k | instr.m_content="NPER"; |
3149 | 3.77k | } |
3150 | 172k | else if (arity==3 && instr.m_type==WKSContentListener::FormulaInstruction::F_Function && instr.m_content=="CTERM") |
3151 | 4.47k | { |
3152 | | // @CTERM(pint,fv,pv) -> NPER(pint,pmt=0,-pv,fv) |
3153 | 4.47k | auto pint=stack[size_t(int(numElt)-3)]; |
3154 | 4.47k | auto fv=stack[size_t(int(numElt)-2)]; |
3155 | 4.47k | auto pv=stack[size_t(int(numElt)-1)]; |
3156 | 4.47k | stack.resize(size_t(++numElt)); |
3157 | | // pint |
3158 | 4.47k | stack[size_t(int(numElt)-4)]=pint; |
3159 | | // pmt=0 |
3160 | 4.47k | instr.m_type=WKSContentListener::FormulaInstruction::F_Long; |
3161 | 4.47k | instr.m_longValue=0; |
3162 | 4.47k | stack[size_t(int(numElt)-3)].resize(0); |
3163 | 4.47k | stack[size_t(int(numElt)-3)].push_back(instr); |
3164 | | // -pv |
3165 | 4.47k | auto &node=stack[size_t(int(numElt)-2)]; |
3166 | 4.47k | instr.m_type=WKSContentListener::FormulaInstruction::F_Operator; |
3167 | 4.47k | instr.m_content="-"; |
3168 | 4.47k | node.resize(0); |
3169 | 4.47k | node.push_back(instr); |
3170 | 4.47k | instr.m_content="("; |
3171 | 4.47k | node.push_back(instr); |
3172 | 4.47k | node.insert(node.end(), pv.begin(), pv.end()); |
3173 | 4.47k | instr.m_content=")"; |
3174 | 4.47k | node.push_back(instr); |
3175 | | |
3176 | | //fv |
3177 | 4.47k | stack[size_t(int(numElt)-1)]=fv; |
3178 | 4.47k | arity=4; |
3179 | 4.47k | instr.m_type=WKSContentListener::FormulaInstruction::F_Function; |
3180 | 4.47k | instr.m_content="NPER"; |
3181 | 4.47k | } |
3182 | | |
3183 | 175k | if ((instr.m_content[0] >= 'A' && instr.m_content[0] <= 'Z') || instr.m_content[0] == '(') |
3184 | 113k | { |
3185 | 113k | if (instr.m_content[0] != '(') |
3186 | 111k | child.push_back(instr); |
3187 | | |
3188 | 113k | instr.m_type=WKSContentListener::FormulaInstruction::F_Operator; |
3189 | 113k | instr.m_content="("; |
3190 | 113k | child.push_back(instr); |
3191 | 690k | for (int i = 0; i < arity; i++) |
3192 | 577k | { |
3193 | 577k | if (i) |
3194 | 497k | { |
3195 | 497k | instr.m_content=";"; |
3196 | 497k | child.push_back(instr); |
3197 | 497k | } |
3198 | 577k | auto const &node=stack[size_t(int(numElt)-arity+i)]; |
3199 | 577k | child.insert(child.end(), node.begin(), node.end()); |
3200 | 577k | } |
3201 | 113k | instr.m_content=")"; |
3202 | 113k | child.push_back(instr); |
3203 | | |
3204 | 113k | stack.resize(size_t(int(numElt)-arity+1)); |
3205 | 113k | stack[size_t(int(numElt)-arity)] = child; |
3206 | 113k | continue; |
3207 | 113k | } |
3208 | 62.5k | if (arity==1) |
3209 | 29.8k | { |
3210 | 29.8k | instr.m_type=WKSContentListener::FormulaInstruction::F_Operator; |
3211 | 29.8k | stack[numElt-1].insert(stack[numElt-1].begin(), instr); |
3212 | 29.8k | if (wh==3) break; |
3213 | 4.41k | continue; |
3214 | 29.8k | } |
3215 | 32.6k | if (arity==2) |
3216 | 32.6k | { |
3217 | 32.6k | instr.m_type=WKSContentListener::FormulaInstruction::F_Operator; |
3218 | 32.6k | stack[numElt-2].push_back(instr); |
3219 | 32.6k | stack[numElt-2].insert(stack[numElt-2].end(), stack[numElt-1].begin(), stack[numElt-1].end()); |
3220 | 32.6k | stack.resize(numElt-1); |
3221 | 32.6k | continue; |
3222 | 32.6k | } |
3223 | 0 | ok=false; |
3224 | 0 | error = "### unexpected arity"; |
3225 | 0 | break; |
3226 | 32.6k | } |
3227 | | |
3228 | 49.2k | if (!ok) ; |
3229 | 27.1k | else if (stack.size()==1 && stack[0].size()>1 && stack[0][0].m_content=="=") |
3230 | 24.5k | { |
3231 | 24.5k | formula.insert(formula.begin(),stack[0].begin()+1,stack[0].end()); |
3232 | 24.5k | return true; |
3233 | 24.5k | } |
3234 | 2.57k | else |
3235 | 2.57k | error = "###stack problem"; |
3236 | | |
3237 | 24.6k | static bool first = true; |
3238 | 24.6k | if (first) |
3239 | 5 | { |
3240 | 5 | WPS_DEBUG_MSG(("LotusSpreadsheet::readFormula: I can not read some formula\n")); |
3241 | 5 | first = false; |
3242 | 5 | } |
3243 | | |
3244 | 24.6k | f.str(""); |
3245 | 1.97M | for (size_t i = 0; i < stack.size(); ++i) |
3246 | 1.95M | { |
3247 | 1.95M | if (i) f << "##"; |
3248 | 1.95M | for (auto const &j : stack[i]) |
3249 | 852k | f << j << ","; |
3250 | 1.95M | } |
3251 | 24.6k | f << error; |
3252 | 24.6k | error = f.str(); |
3253 | 24.6k | return false; |
3254 | 49.2k | } |
3255 | | |
3256 | | // ------------------------------------------------------------ |
3257 | | // zone 1b |
3258 | | // ------------------------------------------------------------ |
3259 | | bool LotusSpreadsheet::readSheetName1B(std::shared_ptr<WPSStream> stream, long endPos) |
3260 | 543 | { |
3261 | 543 | if (!stream) return false; |
3262 | 543 | RVNGInputStreamPtr &input=stream->m_input; |
3263 | 543 | libwps::DebugFile &ascFile=stream->m_ascii; |
3264 | 543 | libwps::DebugStream f; |
3265 | | |
3266 | 543 | long pos = input->tell(); |
3267 | 543 | long sz=endPos-pos; |
3268 | 543 | f << "Entries(SheetName):"; |
3269 | 543 | if (sz<3) |
3270 | 57 | { |
3271 | 57 | WPS_DEBUG_MSG(("LotusParser::readSheetName1B: the zone size seems bad\n")); |
3272 | 57 | f << "###"; |
3273 | 57 | ascFile.addPos(pos-6); |
3274 | 57 | ascFile.addNote(f.str().c_str()); |
3275 | 57 | return true; |
3276 | 57 | } |
3277 | 486 | auto sheetId=int(libwps::readU16(input)); |
3278 | 486 | f << "id=" << sheetId << ","; |
3279 | 486 | std::string name; |
3280 | 4.08k | for (long i=2; i<sz; ++i) |
3281 | 4.00k | { |
3282 | 4.00k | char c = char(libwps::readU8(input)); |
3283 | 4.00k | if (c == '\0') break; |
3284 | 3.60k | name.push_back(c); |
3285 | 3.60k | } |
3286 | 486 | f << name << ","; |
3287 | 486 | if (sheetId<0||sheetId>=m_state->getNumSheet()) |
3288 | 211 | { |
3289 | 211 | WPS_DEBUG_MSG(("LotusSpreadsheet::readSheetName: the zone id seems bad\n")); |
3290 | 211 | f << "##id"; |
3291 | 211 | } |
3292 | 275 | else if (!name.empty()) |
3293 | 232 | m_state->getSheet(sheetId).m_name=libwps_tools_win::Font::unicodeString(name, m_mainParser.getDefaultFontType()); |
3294 | 486 | ascFile.addPos(pos-6); |
3295 | 486 | ascFile.addNote(f.str().c_str()); |
3296 | 486 | return true; |
3297 | 543 | } |
3298 | | |
3299 | | bool LotusSpreadsheet::readNote(std::shared_ptr<WPSStream> stream, long endPos) |
3300 | 340 | { |
3301 | 340 | if (!stream) return false; |
3302 | 340 | RVNGInputStreamPtr &input=stream->m_input; |
3303 | 340 | libwps::DebugFile &ascFile=stream->m_ascii; |
3304 | 340 | libwps::DebugStream f; |
3305 | | |
3306 | 340 | long pos = input->tell(); |
3307 | 340 | long sz=endPos-pos; |
3308 | 340 | f << "Entries(Note):"; |
3309 | 340 | if (sz<4) |
3310 | 151 | { |
3311 | 151 | WPS_DEBUG_MSG(("LotusParser::readNote: the zone size seems bad\n")); |
3312 | 151 | f << "###"; |
3313 | 151 | ascFile.addPos(pos-6); |
3314 | 151 | ascFile.addNote(f.str().c_str()); |
3315 | 151 | return true; |
3316 | 151 | } |
3317 | 189 | static bool first=true; |
3318 | 189 | if (first) |
3319 | 4 | { |
3320 | 4 | first=false; |
3321 | 4 | WPS_DEBUG_MSG(("LotusParser::readNote: this spreadsheet contains some notes, but there is no code to retrieve them\n")); |
3322 | 4 | } |
3323 | 189 | f << "id=" << int(libwps::readU8(input)) << ","; |
3324 | 567 | for (int i=0; i<2; ++i) // f0=1, f1=2|4 |
3325 | 378 | { |
3326 | 378 | auto val=int(libwps::readU8(input)); |
3327 | 378 | if (val!=i+1) f << "f" << i << "=" << val << ","; |
3328 | 378 | } |
3329 | 189 | std::string text; |
3330 | 8.17k | for (long i=0; i<sz-3; ++i) text+=char(libwps::readU8(input)); |
3331 | 189 | f << getDebugStringForText(text) << ","; |
3332 | 189 | ascFile.addPos(pos-6); |
3333 | 189 | ascFile.addNote(f.str().c_str()); |
3334 | 189 | return true; |
3335 | 340 | } |
3336 | | |
3337 | | ////////////////////////////////////////////////////////////////////// |
3338 | | // formatted text |
3339 | | ////////////////////////////////////////////////////////////////////// |
3340 | | void LotusSpreadsheet::sendText(RVNGInputStreamPtr &input, long endPos, LotusSpreadsheetInternal::Style const &style) const |
3341 | 37.8k | { |
3342 | 37.8k | if (!input || !m_listener) |
3343 | 0 | { |
3344 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: can not find the listener\n")); |
3345 | 0 | return; |
3346 | 0 | } |
3347 | 37.8k | libwps_tools_win::Font::Type fontType = style.m_fontType; |
3348 | 37.8k | WPSFont font=style.getFont(); |
3349 | 37.8k | m_listener->setFont(font); |
3350 | 37.8k | bool prevEOL=false; |
3351 | 37.8k | std::string text; |
3352 | 1.28M | while (!input->isEnd()) |
3353 | 1.28M | { |
3354 | 1.28M | long pos=input->tell(); |
3355 | 1.28M | auto c=pos>=endPos ? '\0' : char(libwps::readU8(input)); |
3356 | 1.28M | if ((c==0 || c==1 || c==0xa || c==0xd) && !text.empty()) |
3357 | 165k | { |
3358 | 165k | m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(text, fontType)); |
3359 | 165k | text.clear(); |
3360 | 165k | } |
3361 | 1.28M | if (pos>=endPos) break; |
3362 | 1.24M | switch (c) |
3363 | 1.24M | { |
3364 | 43.8k | case 0x1: |
3365 | 43.8k | if (pos+1>=endPos) |
3366 | 176 | { |
3367 | 176 | WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: can not read the escape value\n")); |
3368 | 176 | break; |
3369 | 176 | } |
3370 | 43.6k | c=char(libwps::readU8(input)); |
3371 | 43.6k | switch (c) |
3372 | 43.6k | { |
3373 | 3.25k | case 0x1e: |
3374 | 3.25k | if (pos+2>=endPos) |
3375 | 39 | { |
3376 | 39 | WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: can not read the escape value\n")); |
3377 | 39 | break; |
3378 | 39 | } |
3379 | 3.21k | c=char(libwps::readU8(input)); |
3380 | 3.21k | switch (c) |
3381 | 3.21k | { |
3382 | 328 | case 'b': // bold |
3383 | 328 | font.m_attributes |= WPS_BOLD_BIT; |
3384 | 328 | m_listener->setFont(font); |
3385 | 328 | break; |
3386 | 394 | case 'i': // italic |
3387 | 394 | font.m_attributes |= WPS_ITALICS_BIT; |
3388 | 394 | m_listener->setFont(font); |
3389 | 394 | break; |
3390 | 2.49k | default: |
3391 | 2.49k | if (c>='0' && c<='7') |
3392 | 983 | { |
3393 | 983 | if (pos+3>=endPos) |
3394 | 18 | { |
3395 | 18 | WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: can not read the escape value\n")); |
3396 | 18 | break; |
3397 | 18 | } |
3398 | 965 | auto c2=char(libwps::readU8(input)); |
3399 | 965 | if (c2=='c' && m_styleManager->getColor8(c-'0', font.m_color)) |
3400 | 427 | m_listener->setFont(font); |
3401 | 538 | else if (c2!='F') // F for font? |
3402 | 509 | { |
3403 | 509 | WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: unknown int sequence\n")); |
3404 | 509 | break; |
3405 | 509 | } |
3406 | 456 | break; |
3407 | 965 | } |
3408 | 1.51k | WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: unknown sequence\n")); |
3409 | 1.51k | break; |
3410 | 3.21k | } |
3411 | 3.21k | break; |
3412 | 3.21k | case 0x1f: // end |
3413 | 448 | font=style.getFont(); |
3414 | 448 | m_listener->setFont(font); |
3415 | 448 | break; |
3416 | 613 | case ';': // unknown, ie. the text can begin with 27013b in some mac file |
3417 | 613 | break; |
3418 | 39.3k | default: |
3419 | 39.3k | WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: unknown debut sequence\n")); |
3420 | 39.3k | break; |
3421 | 43.6k | } |
3422 | 43.6k | break; |
3423 | 43.6k | case 0xd: |
3424 | 8.25k | m_listener->insertEOL(); |
3425 | 8.25k | prevEOL=true; |
3426 | 8.25k | break; |
3427 | 3.95k | case 0xa: |
3428 | 3.95k | if (!prevEOL) |
3429 | 3.41k | { |
3430 | 3.41k | WPS_DEBUG_MSG(("LotusSpreadsheet::sendText: find 0xa without 0xd\n")); |
3431 | 3.41k | } |
3432 | 3.95k | prevEOL=false; |
3433 | 3.95k | break; |
3434 | 1.18M | default: |
3435 | 1.18M | if (c) |
3436 | 892k | text.push_back(c); |
3437 | 1.18M | break; |
3438 | 1.24M | } |
3439 | 1.24M | } |
3440 | 37.8k | } |
3441 | | |
3442 | | void LotusSpreadsheet::sendTextNote(RVNGInputStreamPtr &input, WPSEntry const &entry) const |
3443 | 2.23k | { |
3444 | 2.23k | if (!input || !m_listener) |
3445 | 0 | { |
3446 | 0 | WPS_DEBUG_MSG(("LotusSpreadsheet::sendTextNote: can not find the listener\n")); |
3447 | 0 | return; |
3448 | 0 | } |
3449 | 2.23k | bool prevEOL=false; |
3450 | 2.23k | auto fontType=m_mainParser.getDefaultFontType(); |
3451 | 2.23k | input->seek(entry.begin(), librevenge::RVNG_SEEK_SET); |
3452 | 2.23k | long endPos=entry.end(); |
3453 | 2.23k | std::string text; |
3454 | 75.3k | while (!input->isEnd()) |
3455 | 75.2k | { |
3456 | 75.2k | long pos=input->tell(); |
3457 | 75.2k | auto c=pos>=endPos ? '\0' : char(libwps::readU8(input)); |
3458 | 75.2k | if ((c==0 || c==0xa || c==0xd) && !text.empty()) |
3459 | 10.5k | { |
3460 | 10.5k | m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(text, fontType)); |
3461 | 10.5k | text.clear(); |
3462 | 10.5k | } |
3463 | 75.2k | if (pos>=endPos) break; |
3464 | 73.1k | switch (c) |
3465 | 73.1k | { |
3466 | 3.31k | case 0xd: |
3467 | 3.31k | m_listener->insertEOL(); |
3468 | 3.31k | prevEOL=true; |
3469 | 3.31k | break; |
3470 | 1.52k | case 0xa: |
3471 | 1.52k | if (!prevEOL) |
3472 | 1.38k | { |
3473 | 1.38k | WPS_DEBUG_MSG(("LotusSpreadsheet::sendTextNote: find 0xa without 0xd\n")); |
3474 | 1.38k | } |
3475 | 1.52k | prevEOL=false; |
3476 | 1.52k | break; |
3477 | 68.2k | default: |
3478 | 68.2k | if (c) |
3479 | 45.8k | text.push_back(c); |
3480 | 68.2k | break; |
3481 | 73.1k | } |
3482 | 73.1k | } |
3483 | 2.23k | } |
3484 | | |
3485 | | std::string LotusSpreadsheet::getDebugStringForText(std::string const &text) |
3486 | 62.4k | { |
3487 | 62.4k | std::string res; |
3488 | 62.4k | size_t len=text.length(); |
3489 | 538k | for (size_t i=0; i<len; ++i) |
3490 | 476k | { |
3491 | 476k | char c=text[i]; |
3492 | 476k | switch (c) |
3493 | 476k | { |
3494 | 8.06k | case 0x1: |
3495 | 8.06k | if (i+1>=len) |
3496 | 661 | { |
3497 | 661 | WPS_DEBUG_MSG(("LotusSpreadsheet::getDebugStringForText: can not read the escape value\n")); |
3498 | 661 | res+= "[##escape]"; |
3499 | 661 | break; |
3500 | 661 | } |
3501 | 7.40k | c=text[++i]; |
3502 | 7.40k | switch (c) |
3503 | 7.40k | { |
3504 | 3.55k | case 0x1e: |
3505 | 3.55k | if (i+1>=len) |
3506 | 1.34k | { |
3507 | 1.34k | WPS_DEBUG_MSG(("LotusSpreadsheet::getDebugStringForText: can not read the escape value\n")); |
3508 | 1.34k | res+= "[##escape1]"; |
3509 | 1.34k | break; |
3510 | 1.34k | } |
3511 | 2.20k | c=text[++i]; |
3512 | 2.20k | switch (c) |
3513 | 2.20k | { |
3514 | 200 | case 'b': // bold |
3515 | 358 | case 'i': // italic |
3516 | 358 | res+= std::string("[") + c + "]"; |
3517 | 358 | break; |
3518 | 1.84k | default: |
3519 | 1.84k | if (c>='0' && c<='8') |
3520 | 710 | { |
3521 | 710 | if (i+1>=len) |
3522 | 54 | { |
3523 | 54 | WPS_DEBUG_MSG(("LotusSpreadsheet::getDebugStringForText: can not read the escape value\n")); |
3524 | 54 | res+= "[##escape1]"; |
3525 | 54 | break; |
3526 | 54 | } |
3527 | 656 | char c2=text[++i]; |
3528 | 656 | if (c2!='c' && c2!='F') // color and F for font? |
3529 | 350 | { |
3530 | 350 | WPS_DEBUG_MSG(("LotusSpreadsheet::getDebugStringForText: unknown int sequence\n")); |
3531 | 350 | res+= std::string("[##") + c + c2 + "]"; |
3532 | 350 | break; |
3533 | 350 | } |
3534 | 306 | res += std::string("[") + c + c2 + "]"; |
3535 | 306 | break; |
3536 | 656 | } |
3537 | 1.13k | WPS_DEBUG_MSG(("LotusSpreadsheet::getDebugStringForText: unknown sequence\n")); |
3538 | 1.13k | res+= std::string("[##") + c + "]"; |
3539 | 1.13k | break; |
3540 | 2.20k | } |
3541 | 2.20k | break; |
3542 | 2.20k | case 0x1f: // end |
3543 | 177 | res+= "[^]"; |
3544 | 177 | break; |
3545 | | // case ';': ? |
3546 | 3.67k | default: |
3547 | 3.67k | WPS_DEBUG_MSG(("LotusSpreadsheet::getDebugStringForText: unknown debut sequence\n")); |
3548 | 3.67k | res+= std::string("[##") +c+ "]"; |
3549 | 3.67k | break; |
3550 | 7.40k | } |
3551 | 7.40k | break; |
3552 | 7.40k | case 0xd: |
3553 | 3.78k | res+="\\n"; |
3554 | 3.78k | break; |
3555 | 464k | default: |
3556 | 464k | res+=c; |
3557 | 464k | break; |
3558 | 476k | } |
3559 | 476k | } |
3560 | 62.4k | return res; |
3561 | 62.4k | } |
3562 | | |
3563 | | /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */ |