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=&empty;
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=&empty;
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=&empty;
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