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