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 <algorithm>
26 #include <cctype>
27 #include <cmath>
28 #include <sstream>
29 #include <limits>
30 #include <stack>
31 
32 #include <librevenge-stream/librevenge-stream.h>
33 
34 #include "libwps_internal.h"
35 #include "libwps_tools_win.h"
36 
37 #include "WPSCell.h"
38 #include "WKSContentListener.h"
39 #include "WPSEntry.h"
40 #include "WPSFont.h"
41 #include "WPSStream.h"
42 #include "WPSTable.h"
43 
44 #include "Quattro.h"
45 #include "QuattroFormula.h"
46 
47 #include "QuattroSpreadsheet.h"
48 
49 namespace QuattroSpreadsheetInternal
50 {
51 //! a class used to store a style of a cell in QuattroSpreadsheet
52 struct Style final : public WPSCellFormat
53 {
54 	//! construtor
StyleQuattroSpreadsheetInternal::Style55 	explicit Style(libwps_tools_win::Font::Type type)
56 		: WPSCellFormat()
57 		, m_fontType(type)
58 		, m_fileFormat(0xFF)
59 		, m_alignAcrossColumn(false)
60 		, m_extra("")
61 	{
62 	}
63 	Style(Style const &)=default;
64 	//! destructor
65 	~Style() final;
66 	//! operator<<
67 	friend std::ostream &operator<<(std::ostream &o, Style const &style);
68 	//! operator==
69 	bool operator==(Style const &st) const;
70 	//! operator!=
operator !=QuattroSpreadsheetInternal::Style71 	bool operator!=(Style const &st) const
72 	{
73 		return !(*this==st);
74 	}
75 	//! font encoding type
76 	libwps_tools_win::Font::Type m_fontType;
77 	//! the file format
78 	int m_fileFormat;
79 	//! flag to know if we must align across column
80 	bool m_alignAcrossColumn;
81 	/** extra data */
82 	std::string m_extra;
83 };
84 
~Style()85 Style::~Style()
86 {
87 }
88 
89 //! operator<<
operator <<(std::ostream & o,Style const & style)90 std::ostream &operator<<(std::ostream &o, Style const &style)
91 {
92 	o << static_cast<WPSCellFormat const &>(style) << ",";
93 	if (style.m_fileFormat!=0xFF)
94 		o << "format=" << std::hex << style.m_fileFormat << std::dec << ",";
95 	if (style.m_extra.length())
96 		o << "extra=[" << style.m_extra << "],";
97 
98 	return o;
99 }
100 
operator ==(Style const & st) const101 bool Style::operator==(Style const &st) const
102 {
103 	if (m_fontType!=st.m_fontType || m_fileFormat!=st.m_fileFormat) return false;
104 	int diff = WPSCellFormat::compare(st);
105 	if (diff) return false;
106 	return m_fileFormat==st.m_fileFormat && m_alignAcrossColumn==st.m_alignAcrossColumn && m_extra==st.m_extra;
107 }
108 
109 //! a cellule of a Quattro spreadsheet
110 class Cell final : public WPSCell
111 {
112 public:
113 	/// constructor
Cell(libwps_tools_win::Font::Type type)114 	explicit Cell(libwps_tools_win::Font::Type type)
115 		: m_fontType(type)
116 		, m_fileFormat(0xFF)
117 		, m_styleId(-1)
118 		, m_alignAcrossColumn(false)
119 		, m_content()
120 		, m_hasGraphic(false)
121 		, m_stream() { }
122 
123 	//! operator<<
124 	friend std::ostream &operator<<(std::ostream &o, Cell const &cell);
125 
126 	//! call when a cell must be send
127 	bool send(WPSListenerPtr &/*listener*/) final;
128 
129 	//! call when the content of a cell must be send
sendContent(WPSListenerPtr &)130 	bool sendContent(WPSListenerPtr &/*listener*/) final
131 	{
132 		WPS_DEBUG_MSG(("QuattroSpreadsheetInternal::Cell::sendContent: must not be called\n"));
133 		return false;
134 	}
135 	//! update the cell format using file format
updateFormat()136 	void updateFormat()
137 	{
138 		if (m_fileFormat==0xFF)
139 			return;
140 		switch ((m_fileFormat>>4)&7)
141 		{
142 		case 0:
143 		case 6: // checkme: date format set by system
144 			switch (m_fileFormat&0xF)
145 			{
146 			case 1: // +/- : kind of bool
147 				setFormat(F_BOOLEAN);
148 				break;
149 			case 2: // default
150 				break;
151 			case 3:
152 				setFormat(F_TEXT);
153 				break;
154 			case 4:
155 				setFormat(F_TEXT);
156 				m_font.m_attributes |= WPS_HIDDEN_BIT;
157 				break;
158 			case 5:
159 				setDTFormat(F_DATE, "%d %b %y");
160 				break;
161 			case 6:
162 				setDTFormat(F_DATE, "%d %b");
163 				break;
164 			case 7:
165 				setDTFormat(F_DATE, "%b-%d");
166 				break;
167 			case 8:
168 				setDTFormat(F_DATE, "%m/%d/%y");
169 				break;
170 			case 9:
171 				setDTFormat(F_DATE, "%m/%d");
172 				break;
173 			case 0xa:
174 				setDTFormat(F_TIME, "%I:%M:%S%p");
175 				break;
176 			case 0xb:
177 				setDTFormat(F_TIME, "%I:%M%p");
178 				break;
179 			case 0xc:
180 				setDTFormat(F_TIME, "%H:%M:%S");
181 				break;
182 			case 0xd:
183 				setDTFormat(F_TIME, "%H:%M");
184 				break;
185 			case 0xe:
186 				setDTFormat(F_TIME, "%y");
187 				break;
188 			case 0xf:
189 				setDTFormat(F_TIME, "%b");
190 				break;
191 			default:
192 				WPS_DEBUG_MSG(("QuattroSpreadsheetInternal::Cell::updateFormat: unknown format %x\n", unsigned(m_fileFormat)));
193 				break;
194 			}
195 			break;
196 		case 1: // fixed
197 			setFormat(F_NUMBER, 1);
198 			setDigits(m_fileFormat&0xF);
199 			break;
200 		case 2: // scientific
201 			setFormat(F_NUMBER, 2);
202 			setDigits(m_fileFormat&0xF);
203 			break;
204 		case 3: // currency
205 			setFormat(F_NUMBER, 4);
206 			setDigits(m_fileFormat&0xF);
207 			break;
208 		case 4: // percent
209 			setFormat(F_NUMBER, 3);
210 			setDigits(m_fileFormat&0xF);
211 			break;
212 		case 5: // decimal
213 			setFormat(F_NUMBER, 1);
214 			setDigits(m_fileFormat&0xF);
215 			break;
216 		case 7:   // fixme use UserFormat (m_fileFormat&0xF) in Quattro.cpp at least to decode date...
217 		{
218 			static bool first=true;
219 			if (first)
220 			{
221 				first=false;
222 				WPS_DEBUG_MSG(("QuattroSpreadsheetInternal::Cell::updateFormat: user defined format is not supported\n"));
223 			}
224 			break;
225 		}
226 		default:
227 			break;
228 		}
229 	}
230 	//! font encoding type
231 	libwps_tools_win::Font::Type m_fontType;
232 	//! the file format
233 	int m_fileFormat;
234 	//! the style id
235 	int m_styleId;
236 	//! flag to know if we must align across column
237 	bool m_alignAcrossColumn;
238 	//! the content
239 	WKSContentListener::CellContent m_content;
240 	//! a flag to know a cell has some graphic
241 	bool m_hasGraphic;
242 	//! the text stream(used to send text's zone)
243 	std::shared_ptr<WPSStream> m_stream;
244 };
send(WPSListenerPtr &)245 bool Cell::send(WPSListenerPtr &/*listener*/)
246 {
247 	WPS_DEBUG_MSG(("QuattroSpreadsheetInternal::Cell::send: must not be called\n"));
248 	return false;
249 }
250 
251 //! operator<<
operator <<(std::ostream & o,Cell const & cell)252 std::ostream &operator<<(std::ostream &o, Cell const &cell)
253 {
254 	o << reinterpret_cast<WPSCell const &>(cell) << cell.m_content << ",";
255 	if (cell.m_fileFormat!=0xFF)
256 		o << "format=" << std::hex << cell.m_fileFormat << std::dec << ",";
257 	return o;
258 }
259 
260 //! the spreadsheet of a Quattro Spreadsheet
261 class Spreadsheet
262 {
263 public:
264 	//! a constructor
Spreadsheet(int id,libwps_tools_win::Font::Type fontType)265 	Spreadsheet(int id, libwps_tools_win::Font::Type fontType)
266 		: m_id(id)
267 		, m_numCols(0)
268 		, m_rowHeightMap()
269 		, m_heightDefault(13) // fixme: use zone d2
270 		, m_widthCols()
271 		, m_widthDefault(54) // fixme: use zone d4
272 		, m_positionToCellMap()
273 		, m_dummyCell(fontType)
274 	{
275 	}
276 	//! return a cell corresponding to a spreadsheet, create one if needed
getCell(Vec2i const & pos,libwps_tools_win::Font::Type type)277 	Cell &getCell(Vec2i const &pos, libwps_tools_win::Font::Type type)
278 	{
279 		if (m_positionToCellMap.find(pos)==m_positionToCellMap.end())
280 		{
281 			Cell cell(type);
282 			cell.setPosition(pos);
283 			if (pos[0]<0 || pos[0]>255)
284 			{
285 				WPS_DEBUG_MSG(("QuattroSpreadsheetInternal::Spreadsheet::getCell: find unexpected col=%d\n", pos[0]));
286 				return m_dummyCell;
287 			}
288 			m_positionToCellMap.insert(std::map<Vec2i, Cell>::value_type(pos,cell));
289 		}
290 		return m_positionToCellMap.find(pos)->second;
291 	}
292 	//! returns true if the spreedsheet is empty
empty() const293 	bool empty() const
294 	{
295 		return m_positionToCellMap.empty();
296 	}
297 	//! set the columns size
setColumnWidth(int col,int w=-1)298 	void setColumnWidth(int col, int w=-1)
299 	{
300 		if (col < 0) return;
301 		if (col >= int(m_widthCols.size())) m_widthCols.resize(size_t(col)+1, -1);
302 		m_widthCols[size_t(col)] = w;
303 		if (col >= m_numCols) m_numCols=col+1;
304 	}
305 
306 	//! return the columns format
getWidths() const307 	std::vector<WPSColumnFormat> getWidths() const
308 	{
309 		std::vector<WPSColumnFormat> widths;
310 		WPSColumnFormat defWidth(m_widthDefault), actWidth;
311 		defWidth.m_useOptimalWidth=true;
312 		int repeat=0;
313 		for (auto const &w : m_widthCols)
314 		{
315 			WPSColumnFormat newWidth;
316 			if (w < 0)
317 				newWidth=defWidth;
318 			else
319 				newWidth=WPSColumnFormat(float(w)/20.f);
320 			if (repeat && newWidth!=actWidth)
321 			{
322 				actWidth.m_numRepeat=repeat;
323 				widths.push_back(actWidth);
324 				repeat=0;
325 			}
326 			if (repeat==0)
327 				actWidth=newWidth;
328 			++repeat;
329 		}
330 		if (repeat)
331 		{
332 			actWidth.m_numRepeat=repeat;
333 			widths.push_back(actWidth);
334 		}
335 		return widths;
336 	}
337 	//! set the rows size in TWIP
setRowHeight(int row,int h)338 	void setRowHeight(int row, int h)
339 	{
340 		auto rIt=m_rowHeightMap.lower_bound(Vec2i(-1,row));
341 		if (rIt!=m_rowHeightMap.end() && rIt->first[0]<=row && rIt->first[1]>=row)
342 		{
343 			WPS_DEBUG_MSG(("QuattroSpreadsheetInternal::Spreadsheet::setRowHeight: oops, row %d is already set\n", row));
344 			return;
345 		}
346 		if (h>=0)
347 			m_rowHeightMap[Vec2i(row,row)]=h;
348 	}
349 	//! set the rows size in TWIP
setRowHeights(int minRow,int maxRow,int h)350 	void setRowHeights(int minRow, int maxRow, int h)
351 	{
352 		auto rIt=m_rowHeightMap.lower_bound(Vec2i(-1,minRow));
353 		while (rIt!=m_rowHeightMap.end())
354 		{
355 			auto const &cells=rIt->first;
356 			if (cells[0]>maxRow) break;
357 			if (cells[1]>=minRow)
358 			{
359 				WPS_DEBUG_MSG(("QuattroSpreadsheetInternal::Spreadsheet::setRowHeight: oops, some rows are already set in %dx%d\n", minRow, maxRow));
360 				return;
361 			}
362 			++rIt;
363 		}
364 		if (h>=0)
365 			m_rowHeightMap[Vec2i(minRow,maxRow)]=h;
366 	}
367 	//! returns the row size in point
getRowHeight(int row) const368 	float getRowHeight(int row) const
369 	{
370 		auto rIt=m_rowHeightMap.lower_bound(Vec2i(-1,row));
371 		if (rIt!=m_rowHeightMap.end() && rIt->first[0]<=row && rIt->first[1]>=row)
372 			return float(rIt->second)/20.f;
373 		return m_heightDefault;
374 	}
375 	//! returns the height of a row in point and updated repeated row
getRowHeight(int row,int & numRepeated) const376 	float getRowHeight(int row, int &numRepeated) const
377 	{
378 		auto rIt=m_rowHeightMap.lower_bound(Vec2i(-1,row));
379 		if (rIt!=m_rowHeightMap.end() && rIt->first[0]<=row && rIt->first[1]>=row)
380 		{
381 			numRepeated=rIt->first[1]-row+1;
382 			return float(rIt->second)/20.f;
383 		}
384 		numRepeated=10000;
385 		return m_heightDefault;
386 	}
387 	//! try to compress the list of row height
compressRowHeights()388 	void compressRowHeights()
389 	{
390 		auto oldMap=m_rowHeightMap;
391 		m_rowHeightMap.clear();
392 		int actHeight=-1;
393 		Vec2i actPos(0,-1);
394 		for (auto rIt : oldMap)
395 		{
396 			// first check for not filled row
397 			if (rIt.first[0]!=actPos[1]+1)
398 			{
399 				if (actHeight==int(m_heightDefault)*20)
400 					actPos[1]=rIt.first[0]-1;
401 				else
402 				{
403 					if (actPos[1]>=actPos[0])
404 						m_rowHeightMap[actPos]=actHeight;
405 					actHeight=int(m_heightDefault)*20;
406 					actPos=Vec2i(actPos[1]+1, rIt.first[0]-1);
407 				}
408 			}
409 			if (rIt.second!=actHeight)
410 			{
411 				if (actPos[1]>=actPos[0])
412 					m_rowHeightMap[actPos]=actHeight;
413 				actPos[0]=rIt.first[0];
414 				actHeight=rIt.second;
415 			}
416 			actPos[1]=rIt.first[1];
417 		}
418 		if (actPos[1]>=actPos[0])
419 			m_rowHeightMap[actPos]=actHeight;
420 	}
421 	//! returns the cell position
getPosition(Vec2i const & cell) const422 	Vec2f getPosition(Vec2i const &cell) const
423 	{
424 		float c=0;
425 		int numWidth=int(m_widthCols.size());
426 		for (int i=0; i<cell[0]; ++i)
427 		{
428 			if (i>=numWidth)
429 			{
430 				c+=float(i+1-numWidth)*m_widthDefault;
431 				break;
432 			}
433 			int w=m_widthCols[size_t(i)];
434 			if (w < 0)
435 				c+=m_widthDefault;
436 			else
437 				c+=float(w)/20.f;
438 		}
439 		int r=0, prevR=0;
440 		for (auto it : m_rowHeightMap)
441 		{
442 			int maxR=std::min(it.first[1],cell[1]-1);
443 			if (prevR<it.first[0])
444 			{
445 				r+=(maxR-prevR)*int(m_heightDefault)*20;
446 				prevR=maxR;
447 			}
448 			if (maxR<it.first[0])
449 				break;
450 			r+=(maxR+1-it.first[0])*it.second;
451 			prevR=maxR;
452 		}
453 		if (prevR<cell[1]) r+=(cell[1]-prevR)*int(m_heightDefault)*20;
454 		return Vec2f(c,float(r/20));
455 	}
456 	//! the spreadsheet id
457 	int m_id;
458 	/** the number of columns */
459 	int m_numCols;
460 
461 	/** the map Vec2i(min row, max row) to size in TWIP */
462 	std::map<Vec2i,int> m_rowHeightMap;
463 	/** the default row size in point */
464 	float m_heightDefault;
465 	/** the column size in TWIP */
466 	std::vector<int> m_widthCols;
467 	/** the default width size in point */
468 	float m_widthDefault;
469 	/** a map cell to not empty cells */
470 	std::map<Vec2i, Cell> m_positionToCellMap;
471 	/** a dummy cell */
472 	mutable Cell m_dummyCell;
473 };
474 
475 //! the state of QuattroSpreadsheet
476 struct State
477 {
478 	//! constructor
StateQuattroSpreadsheetInternal::State479 	explicit State(QuattroFormulaManager::CellReferenceFunction const &readCellReference)
480 		: m_version(-1)
481 		, m_maxDimension(0,0,0)
482 		, m_actSheet(-1)
483 		, m_stylesList()
484 		, m_formulaManager(readCellReference, 1)
485 		, m_spreadsheetMap()
486 		, m_idToSheetNameMap()
487 		, m_idToUserFormatMap()
488 	{
489 	}
490 	//! returns the ith real spreadsheet
getSheetQuattroSpreadsheetInternal::State491 	std::shared_ptr<Spreadsheet> getSheet(int id, libwps_tools_win::Font::Type fontType)
492 	{
493 		auto it=m_spreadsheetMap.find(id);
494 		if (it!=m_spreadsheetMap.end())
495 			return it->second;
496 		std::shared_ptr<Spreadsheet> sheet(new Spreadsheet(id, fontType));
497 		sheet->setColumnWidth(m_maxDimension[0]);
498 		if (id<0 || id>m_maxDimension[2])
499 		{
500 			WPS_DEBUG_MSG(("QuattroSpreadsheetInternal::State::getSheet: find unexpected id=%d\n", id));
501 			if (id<0 || id>255) // too small or too big, return dummy spreadsheet
502 				return sheet;
503 		}
504 		m_spreadsheetMap[id]=sheet;
505 		return sheet;
506 	}
507 	//! returns the ith spreadsheet
getSheetNameQuattroSpreadsheetInternal::State508 	librevenge::RVNGString getSheetName(int id) const
509 	{
510 		auto it = m_idToSheetNameMap.find(id);
511 		if (it!=m_idToSheetNameMap.end() && !it->second.empty())
512 			return it->second;
513 		librevenge::RVNGString name;
514 		name.sprintf("Sheet%d", id+1);
515 		return name;
516 	}
517 	//! the file version
518 	int m_version;
519 	//! the maximum col, row, sheet
520 	WPSVec3i m_maxDimension;
521 	//! the actual sheet
522 	int m_actSheet;
523 	//! the list of styles
524 	std::vector<Style> m_stylesList;
525 	//! the formula manager
526 	QuattroFormulaManager m_formulaManager;
527 
528 	//! the map of spreadsheet
529 	std::map<int, std::shared_ptr<Spreadsheet> > m_spreadsheetMap;
530 	//! the map id to sheet's name
531 	std::map<int, librevenge::RVNGString> m_idToSheetNameMap;
532 	//! map id to user format string
533 	std::map<int, librevenge::RVNGString> m_idToUserFormatMap;
534 };
535 
536 }
537 
538 // constructor, destructor
QuattroSpreadsheet(QuattroParser & parser)539 QuattroSpreadsheet::QuattroSpreadsheet(QuattroParser &parser)
540 	: m_listener()
541 	, m_mainParser(parser)
542 	, m_state()
543 {
544 	m_state.reset(new QuattroSpreadsheetInternal::State(getReadCellReferenceFunction()));
545 }
546 
~QuattroSpreadsheet()547 QuattroSpreadsheet::~QuattroSpreadsheet()
548 {
549 }
550 
cleanState()551 void QuattroSpreadsheet::cleanState()
552 {
553 	m_state.reset(new QuattroSpreadsheetInternal::State(getReadCellReferenceFunction()));
554 }
555 
updateState()556 void QuattroSpreadsheet::updateState()
557 {
558 }
559 
version() const560 int QuattroSpreadsheet::version() const
561 {
562 	if (m_state->m_version<0)
563 		m_state->m_version=m_mainParser.version();
564 	return m_state->m_version;
565 }
566 
getReadCellReferenceFunction()567 QuattroFormulaManager::CellReferenceFunction QuattroSpreadsheet::getReadCellReferenceFunction()
568 {
569 	return [this](std::shared_ptr<WPSStream> const &stream, long endPos,
570 	              QuattroFormulaInternal::CellReference &ref,
571 	              Vec2i const &pos, int sheetId)
572 	{
573 		return this->readCellReference(stream, endPos, ref, pos, sheetId);
574 	};
575 }
576 
getNumSpreadsheets() const577 int QuattroSpreadsheet::getNumSpreadsheets() const
578 {
579 	if (m_state->m_spreadsheetMap.empty())
580 		return m_state->m_maxDimension[2]+1;
581 	auto it=m_state->m_spreadsheetMap.end();
582 	--it;
583 	return std::max(it->first,m_state->m_maxDimension[2])+1;
584 }
585 
getSheetName(int id) const586 librevenge::RVNGString QuattroSpreadsheet::getSheetName(int id) const
587 {
588 	return m_state->getSheetName(id);
589 }
590 
getPosition(int sheetId,Vec2i const & cell) const591 Vec2f QuattroSpreadsheet::getPosition(int sheetId, Vec2i const &cell) const
592 {
593 	auto it=m_state->m_spreadsheetMap.find(sheetId);
594 	if (it==m_state->m_spreadsheetMap.end() || !it->second)
595 	{
596 		WPS_DEBUG_MSG(("QuattroSpreadsheet::getPosition: can not find the sheet %d\n", sheetId));
597 		return Vec2f(float(cell[0]*50), float(cell[1]*13));
598 	}
599 	return it->second->getPosition(cell);
600 }
601 
addDLLIdName(int id,librevenge::RVNGString const & name,bool func1)602 void QuattroSpreadsheet::addDLLIdName(int id, librevenge::RVNGString const &name, bool func1)
603 {
604 	m_state->m_formulaManager.addDLLIdName(id, name, func1);
605 }
606 
addUserFormat(int id,librevenge::RVNGString const & name)607 void QuattroSpreadsheet::addUserFormat(int id, librevenge::RVNGString const &name)
608 {
609 	if (name.empty())
610 	{
611 		WPS_DEBUG_MSG(("QuattroSpreadsheet::addUserFormat: called with empty name for id=%d\n", id));
612 		return;
613 	}
614 	if (m_state->m_idToUserFormatMap.find(id)!=m_state->m_idToUserFormatMap.end())
615 	{
616 		WPS_DEBUG_MSG(("QuattroSpreadsheet::addUserFormat: called with dupplicated id=%d\n", id));
617 	}
618 	else
619 		m_state->m_idToUserFormatMap[id]=name;
620 }
621 
622 ////////////////////////////////////////////////////////////
623 // low level
624 
625 ////////////////////////////////////////////////////////////
626 //   parse sheet data
627 ////////////////////////////////////////////////////////////
readCell(std::shared_ptr<WPSStream> const & stream)628 bool QuattroSpreadsheet::readCell(std::shared_ptr<WPSStream> const &stream)
629 {
630 	RVNGInputStreamPtr input = stream->m_input;
631 	libwps::DebugFile &ascFile=stream->m_ascii;
632 	libwps::DebugStream f;
633 
634 	long pos = input->tell();
635 	auto type = long(libwps::readU16(input)&0x7fff);
636 	if ((type < 0xc || type > 0x10) && (type!=0x33))
637 	{
638 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readCell: not a cell property\n"));
639 		return false;
640 	}
641 	long sz = libwps::readU16(input);
642 	long endPos = pos+4+sz;
643 
644 	if (sz < 5)
645 	{
646 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readCell: cell def is too short\n"));
647 		return false;
648 	}
649 	int cellPos[2];
650 	cellPos[0]=int(libwps::readU8(input));
651 	auto sheetId=int(libwps::readU8(input));
652 	cellPos[1]=int(libwps::read16(input));
653 	if (cellPos[1] < 0)
654 	{
655 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readCell: cell pos is bad\n"));
656 		return false;
657 	}
658 	if (sheetId)
659 		f << "sheet[id]=" << sheetId << ",";
660 
661 	auto defFontType=m_mainParser.getDefaultFontType();
662 	auto sheet = m_state->getSheet(sheetId, defFontType);
663 	auto &cell=sheet->getCell(Vec2i(cellPos[0],cellPos[1]), defFontType);
664 	auto format=int(libwps::readU16(input));
665 	int id=format>>3;
666 	// format&7: reserved
667 	if (id<0 || id>int(m_state->m_stylesList.size()))
668 	{
669 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readCell: can not find cell format\n"));
670 		f << "###Ce" << id << ",";
671 	}
672 	else if (id)
673 	{
674 		auto const &style=m_state->m_stylesList[size_t(id-1)];
675 		if (type!=0x33)
676 		{
677 			cell.m_styleId=id-1;
678 			cell.m_fileFormat=style.m_fileFormat;
679 			cell.m_fontType=style.m_fontType;
680 			static_cast<WPSCellFormat &>(cell)=style;
681 			cell.m_alignAcrossColumn=style.m_alignAcrossColumn;
682 		}
683 		f << "Ce" << id-1 << ",";
684 	}
685 
686 	long dataPos = input->tell();
687 	auto dataSz = int(endPos-dataPos);
688 
689 	bool ok = true;
690 	switch (type)
691 	{
692 	case 12:
693 	{
694 		if (dataSz == 0)
695 		{
696 			cell.m_content.m_contentType=WKSContentListener::CellContent::C_NONE;
697 			break;
698 		}
699 		ok = false;
700 		break;
701 	}
702 	case 13:
703 	{
704 		if (dataSz == 2)
705 		{
706 			cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
707 			cell.m_content.setValue(libwps::read16(input));
708 			break;
709 		}
710 		ok = false;
711 		break;
712 	}
713 	case 14:
714 	{
715 		double val;
716 		bool isNaN;
717 		if (dataSz == 8 && libwps::readDouble8(input, val, isNaN))
718 		{
719 			cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
720 			cell.m_content.setValue(val);
721 			break;
722 		}
723 		ok = false;
724 		break;
725 	}
726 	case 15:
727 		cell.m_content.m_contentType=WKSContentListener::CellContent::C_TEXT;
728 		WPS_FALLTHROUGH;
729 	case 0x33: // formula res
730 	{
731 		long begText=input->tell()+1;
732 		std::string s("");
733 		// align + c string
734 		auto align=char(libwps::readU8(input));
735 		if (align=='\'') cell.setHAlignment(WPSCellFormat::HALIGN_DEFAULT);
736 		else if (align=='^') cell.setHAlignment(WPSCellFormat::HALIGN_CENTER);
737 		else if (align=='\"') cell.setHAlignment(WPSCellFormat::HALIGN_RIGHT);
738 		else if (align=='\\') f << "repeat,"; // USEME
739 		else if (align==0x7c) f << "break,"; // FIXME remove "::" in text
740 		else if (align) f << "#align=" << int(align) << ",";
741 
742 		librevenge::RVNGString text("");
743 		if (!m_mainParser.readCString(stream,text,dataSz-1))
744 			f << "##sSz,";
745 		else
746 		{
747 			if (endPos!=input->tell() && endPos!=input->tell()+1)
748 			{
749 				f << "#extra,";
750 				ascFile.addDelimiter(input->tell(), '|');
751 			}
752 			cell.m_stream=stream;
753 			cell.m_content.m_textEntry.setBegin(begText);
754 			cell.m_content.m_textEntry.setEnd(input->tell()-1);
755 			if (!text.empty())
756 				f << text.cstr() << ",";
757 		}
758 		break;
759 	}
760 	case 16:
761 	{
762 		double val;
763 		bool isNaN;
764 		if (dataSz >= 10 && libwps::readDouble8(input, val, isNaN))
765 		{
766 			cell.m_content.m_contentType=WKSContentListener::CellContent::C_FORMULA;
767 			cell.m_content.setValue(val);
768 			auto state=int(libwps::readU16(input));
769 			if (state)
770 			{
771 				f << "state[";
772 				if (state&0x8) f << "constant,";
773 				if (state==0x10) f << "volatile,";
774 				if (state==0x100) f << "inArray,";
775 				if (state==0x200) f << "useDLL,";
776 				if (state&0xfcf3) f << "#state=" << std::hex << (state&0xfcf3) << std::dec << ",";
777 				f << "],";
778 			}
779 			std::string error;
780 			if (!m_state->m_formulaManager.readFormula(stream, endPos, cell.position(), sheetId, cell.m_content.m_formula, error))
781 			{
782 				cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
783 				ascFile.addDelimiter(input->tell()-1, '#');
784 			}
785 			if (error.length()) f << error;
786 			break;
787 		}
788 		ok = false;
789 		break;
790 	}
791 	default:
792 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readCell: unknown type=%ld\n", type));
793 		ok = false;
794 		break;
795 	}
796 	if (!ok) ascFile.addDelimiter(dataPos, '#');
797 
798 	input->seek(pos+sz, librevenge::RVNG_SEEK_SET);
799 
800 	std::string extra=f.str();
801 	f.str("");
802 	f << cell << "," << extra;
803 
804 	ascFile.addPos(pos);
805 	ascFile.addNote(f.str().c_str());
806 
807 	return true;
808 }
809 
readCellStyle(std::shared_ptr<WPSStream> const & stream)810 bool QuattroSpreadsheet::readCellStyle(std::shared_ptr<WPSStream> const &stream)
811 {
812 	RVNGInputStreamPtr input = stream->m_input;
813 	libwps::DebugFile &ascFile=stream->m_ascii;
814 	libwps::DebugStream f;
815 	long pos = input->tell();
816 	auto type = long(libwps::readU16(input)&0x7fff);
817 	if (type != 0xce)
818 	{
819 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readCellStyle: not a style zone\n"));
820 		return false;
821 	}
822 	long sz = libwps::readU16(input);
823 	f << "[Ce" << m_state->m_stylesList.size() << "],";
824 	QuattroSpreadsheetInternal::Style style(m_mainParser.getDefaultFontType());
825 	if (sz<8)
826 	{
827 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readCellStyle: size seems bad\n"));
828 		f << "###";
829 		ascFile.addPos(pos);
830 		ascFile.addNote(f.str().c_str());
831 		m_state->m_stylesList.push_back(style);
832 		return true;
833 	}
834 	style.m_fileFormat=int(libwps::readU8(input));
835 	if (style.m_fileFormat!=0xFF)
836 		f << "form=" << std::hex << style.m_fileFormat << std::dec << ",";
837 	auto flag=int(libwps::readU8(input));
838 	switch (flag&7)
839 	{
840 	case 1:
841 		style.setHAlignment(WPSCellFormat::HALIGN_LEFT);
842 		f << "left,";
843 		break;
844 	case 2:
845 		style.setHAlignment(WPSCellFormat::HALIGN_CENTER);
846 		f << "center,";
847 		break;
848 	case 3:
849 		style.setHAlignment(WPSCellFormat::HALIGN_RIGHT);
850 		f << "right,";
851 		break;
852 	case 4:
853 		style.setHAlignment(WPSCellFormat::HALIGN_FULL);
854 		f << "block,";
855 		break;
856 	case 6:
857 	{
858 		style.setHAlignment(WPSCellFormat::HALIGN_CENTER);
859 		style.m_alignAcrossColumn=true;
860 		f << "center[across],";
861 		break;
862 	}
863 	default:
864 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readCellStyle: find unexpected alignment\n"));
865 		f << "###align=" << (flag&7) << ",";
866 		break;
867 	case 0: // standart
868 		break;
869 	}
870 	if (sz>=12)
871 	{
872 		switch ((flag>>3)&3)
873 		{
874 		case 0: // default
875 			style.setVAlignment(WPSCellFormat::VALIGN_BOTTOM);
876 			break;
877 		case 1:
878 			style.setVAlignment(WPSCellFormat::VALIGN_CENTER);
879 			f << "vAlign=center,";
880 			break;
881 		case 2:
882 			style.setVAlignment(WPSCellFormat::VALIGN_TOP);
883 			f << "vAlign=top,";
884 			break;
885 		default:
886 			WPS_DEBUG_MSG(("QuattroSpreadsheet::readCellStyle: find unexpected alignment\n"));
887 			f << "###valign=3,";
888 			break;
889 		}
890 		if (flag&0x20)
891 		{
892 			style.setTextRotation(270);
893 			f << "top[down],";
894 		}
895 		if (flag&0x80)
896 		{
897 			style.setWrapping(WPSCellFormat::WRAP_WRAP);
898 			f << "wrap,";
899 		}
900 		flag &= 0x40;
901 	}
902 	else
903 	{
904 		switch ((flag>>6)&3) // checkme, maybe diferent in wb2, ie. I find difference with qprostyle.cpp
905 		{
906 		default:
907 		case 0: // standart
908 			break;
909 		case 1:
910 			f << "label[only],";
911 			break;
912 		case 2:
913 			f << "date[only],";
914 			break;
915 		case 3:
916 			f << "##input=3,";
917 			break;
918 		}
919 		flag&=0x38;
920 	}
921 	if (flag)
922 		f << "#fl=" << std::hex << flag << std::dec << ",";
923 	auto val=int(libwps::readU8(input));
924 	int color[3]= {val>>4, val&0xf, 0}; // col2: shade, col1: shade, textcolor
925 	val=int(libwps::readU8(input));
926 	color[2]=val>>4;
927 	int blend=(val&0x7);
928 	WPSColor colors[]= {WPSColor::white(), WPSColor::black(), WPSColor::black()};
929 	for (int i=0; i<3; ++i)
930 	{
931 		int const expected[]= {0,3,3};
932 		if (color[i]==expected[i]) continue;
933 		if (m_mainParser.getColor(color[i], colors[i]))
934 			f << "color" << i << "=" << colors[i] << ",";
935 		else
936 			f << "##color" << i << "=" << color[i] << ",";
937 	}
938 	if (blend==7)
939 		f << "###blend=7,";
940 	else
941 	{
942 		int const percent[]= {0,6,3,1,2,5,4};
943 		float fPercent=float(percent[blend])/6.f;
944 		if (blend)
945 			f << "blend=" << 100.f *fPercent << "%,";
946 		style.setBackgroundColor(WPSColor::barycenter(fPercent,colors[1],1.f-fPercent,colors[0]));
947 		// percent col2 + (1-percent) col1
948 	}
949 	if (val&8) f << "fl[8],";
950 	auto fId=int(libwps::readU8(input));
951 	WPSFont font;
952 	if (fId)
953 	{
954 		if (!m_mainParser.getFont(fId-1, font, style.m_fontType))
955 			f << "###";
956 		f << "F" << fId-1 << ",";
957 	}
958 	font.m_color=colors[2];
959 	style.setFont(font);
960 	auto bFlags=int(libwps::readU8(input));
961 	// 80:has[textColor], 40:has[protect], 20:has[borders], 10:has[fonts], 8:has[color], 4:[hasAlign], 2:[hasForm], 1:[hasProtect+0x40?]...
962 	val=int(libwps::readU8(input));
963 	val &= 0x41;
964 	if (val==0x41)
965 		f << "protect=no,";
966 	else if (val) f << "fl2=" << std::hex << val << std::dec << ",";
967 	val=int(libwps::readU8(input)); // USEME
968 	if (val) f << "style[id]=" << val << ",";
969 	WPSColor borderColors[4];
970 	for (auto &c : borderColors) c=WPSColor::black();
971 	if (sz>=12)   // sz=12, pre-wb2, wb2?
972 	{
973 		f << "borders[color]=[";
974 		for (int i=0; i<2; ++i)
975 		{
976 			val=int(libwps::readU8(input));
977 			for (int j=0; j<2; ++j)
978 			{
979 				int c=j==1 ? (val>>4) : (val&0xf);
980 				if (!m_mainParser.getColor(c, borderColors[2*i+j]))
981 					f << "##color=" << c << ",";
982 				else if (borderColors[2*i+j].isBlack())
983 					f << "_,";
984 				else
985 					f << borderColors[2*i+j] << ",";
986 			}
987 		}
988 		f << "],";
989 		val=int(libwps::readU16(input));
990 		switch (val&3)
991 		{
992 		default:
993 		case 0: // standart
994 			break;
995 		case 1:
996 			f << "label[only],";
997 			break;
998 		case 2:
999 			f << "date[only],";
1000 			break;
1001 		case 3:
1002 			f << "##input=3,";
1003 			break;
1004 		}
1005 		if (val&4) f << "use[lineColor],"; // or page color
1006 		val&=0xfff8;
1007 		if (val) f << "fl3=" << val << ",";
1008 	}
1009 	if (bFlags)
1010 	{
1011 		f << "borders=[";
1012 		for (int i=0, depl=0; i<4; ++i, depl+=2)   // BRTL
1013 		{
1014 			int bType=(bFlags>>depl)&3;
1015 			if (!bType) continue;
1016 			char const *wh[]= {"L","T","R","B"};
1017 			WPSBorder border;
1018 			switch (bType)
1019 			{
1020 			case 1: // normal
1021 				f << wh[i] << ",";
1022 				break;
1023 			case 2: // double
1024 				border.m_type=WPSBorder::Double;
1025 				f << wh[i] << "=double,";
1026 				break;
1027 			case 3: // width*2
1028 				border.m_width=2;
1029 				f << wh[i] << "=w2,";
1030 				break;
1031 			default: // impossible
1032 				break;
1033 			}
1034 			border.m_color=borderColors[i];
1035 			int const which[]= {WPSBorder::LeftBit, WPSBorder::TopBit, WPSBorder::RightBit, WPSBorder::BottomBit};
1036 			style.setBorders(which[i], border);
1037 		}
1038 		f << "],";
1039 	}
1040 	m_state->m_stylesList.push_back(style);
1041 	ascFile.addPos(pos);
1042 	ascFile.addNote(f.str().c_str());
1043 	return true;
1044 }
1045 
readSheetSize(std::shared_ptr<WPSStream> const & stream)1046 bool QuattroSpreadsheet::readSheetSize(std::shared_ptr<WPSStream> const &stream)
1047 {
1048 	RVNGInputStreamPtr input = stream->m_input;
1049 	libwps::DebugFile &ascFile=stream->m_ascii;
1050 	libwps::DebugStream f;
1051 	long pos = input->tell();
1052 	auto type = long(libwps::readU16(input)&0x7fff);
1053 	if (type != 0x6)
1054 	{
1055 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readSheetSize: not a sheet zone\n"));
1056 		return false;
1057 	}
1058 	long sz = libwps::readU16(input);
1059 	if (sz < 8)
1060 	{
1061 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readSheetSize: block is too short\n"));
1062 		return false;
1063 	}
1064 	bool ok=true;
1065 	for (int i = 0; i < 2; i++)   // min can be invalid if the file is an extract file
1066 	{
1067 		f << (i==0 ? "min": "max") << "=[";
1068 		int nCol = int(libwps::readU8(input))+1;
1069 		f << "col=" << nCol << ",";
1070 		auto nSheet = int(libwps::readU8(input));
1071 		int nRow = libwps::read16(input);
1072 		f << "row=" << nRow << ",";
1073 		if (nSheet)
1074 			f << "sheet=" << nSheet << ",";
1075 		f << "],";
1076 		if (i==0)
1077 			continue;
1078 		m_state->m_maxDimension=WPSVec3i(nCol,nRow,nSheet);
1079 		if (nRow<0)
1080 			ok=(nRow==-1 && nCol==1); // empty spreadsheet
1081 	}
1082 	ascFile.addPos(pos);
1083 	ascFile.addNote(f.str().c_str());
1084 	return ok;
1085 }
1086 
readColumnRowDefaultSize(std::shared_ptr<WPSStream> const & stream)1087 bool QuattroSpreadsheet::readColumnRowDefaultSize(std::shared_ptr<WPSStream> const &stream)
1088 {
1089 	RVNGInputStreamPtr input = stream->m_input;
1090 	libwps::DebugFile &ascFile=stream->m_ascii;
1091 	libwps::DebugStream f;
1092 	long pos = input->tell();
1093 	auto type = long(libwps::readU16(input)&0x7fff);
1094 	if (type < 0xd2 || type > 0xd5)
1095 	{
1096 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readColumnRowDefaultSize: not a column size zone\n"));
1097 		return false;
1098 	}
1099 	long sz = libwps::readU16(input);
1100 	if (sz != 2)
1101 	{
1102 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readColumnRowDefaultSize: block is too short\n"));
1103 		return false;
1104 	}
1105 	int val=int(libwps::readU16(input));
1106 	if (val&0x8000)
1107 	{
1108 		f << "user,";
1109 		val &= 0x7fff;
1110 	}
1111 	f << float(val)/20.f << ",";
1112 	if (type==0xd2 || type==0xd4)
1113 	{
1114 		auto defFontType=m_mainParser.getDefaultFontType();
1115 		auto sheet=m_state->getSheet(m_state->m_actSheet, defFontType);
1116 		if (type==0xd2)
1117 			sheet->m_heightDefault=float(val)/20.f;
1118 		else
1119 			sheet->m_widthDefault=float(val)/20.f;
1120 	}
1121 
1122 	ascFile.addPos(pos);
1123 	ascFile.addNote(f.str().c_str());
1124 	return true;
1125 }
1126 
readColumnSize(std::shared_ptr<WPSStream> const & stream)1127 bool QuattroSpreadsheet::readColumnSize(std::shared_ptr<WPSStream> const &stream)
1128 {
1129 	RVNGInputStreamPtr input = stream->m_input;
1130 	libwps::DebugFile &ascFile=stream->m_ascii;
1131 	libwps::DebugStream f;
1132 	long pos = input->tell();
1133 	auto type = long(libwps::readU16(input)&0x7fff);
1134 	if (type != 0xd8 && type != 0xd9)
1135 	{
1136 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readColumnSize: not a column size zone\n"));
1137 		return false;
1138 	}
1139 	long sz = libwps::readU16(input);
1140 	if (sz < 4)
1141 	{
1142 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readColumnSize: block is too short\n"));
1143 		return false;
1144 	}
1145 
1146 	int col = libwps::read16(input);
1147 	int width = libwps::readU16(input);
1148 
1149 	auto defFontType=m_mainParser.getDefaultFontType();
1150 	auto sheet=m_state->getSheet(m_state->m_actSheet, defFontType);
1151 	bool ok = col >= 0 && col < sheet->m_numCols+10;
1152 	f << "Col" << col << ":";
1153 	if (width&0x8000)
1154 	{
1155 		f << "user,";
1156 		width &= 0x7fff;
1157 	}
1158 	f << "width=" << float(width)/72.f << ",";
1159 	if (ok && type==0xd8)
1160 	{
1161 		if (col >= sheet->m_numCols)
1162 		{
1163 			static bool first = true;
1164 			if (first)
1165 			{
1166 				first = false;
1167 				WPS_DEBUG_MSG(("QuattroSpreadsheet::readColumnSize: I must increase the number of columns\n"));
1168 			}
1169 			f << "#col[inc],";
1170 		}
1171 		sheet->setColumnWidth(col, width);
1172 	}
1173 	else if (col>256 && type==0xd8)
1174 		f << "###,";
1175 	ascFile.addPos(pos);
1176 	ascFile.addNote(f.str().c_str());
1177 
1178 	return true;
1179 }
1180 
readRowSize(std::shared_ptr<WPSStream> const & stream)1181 bool QuattroSpreadsheet::readRowSize(std::shared_ptr<WPSStream> const &stream)
1182 {
1183 	RVNGInputStreamPtr input = stream->m_input;
1184 	libwps::DebugFile &ascFile=stream->m_ascii;
1185 	libwps::DebugStream f;
1186 	long pos = input->tell();
1187 	auto type = long(libwps::readU16(input)&0x7fff);
1188 	if (type != 0xd6 && type != 0xd7)
1189 	{
1190 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readRowSize: not a row size zone\n"));
1191 		return false;
1192 	}
1193 	long sz = libwps::readU16(input);
1194 	if (sz != 4)
1195 	{
1196 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readRowSize: block is too short\n"));
1197 		return false;
1198 	}
1199 
1200 	int row = libwps::read16(input);
1201 	int height = libwps::readU16(input);
1202 
1203 	f << "Row" << row << ",";
1204 	if (height&0x8000)   // maybe set by hand?
1205 	{
1206 		f << "user,";
1207 		height &= 0x7fff;
1208 	}
1209 	f << "h=" << float(height)/20.f << ",";
1210 	if (type==0xd6)
1211 	{
1212 		if (row>=0 && m_state->m_actSheet>=0)
1213 		{
1214 			auto defFontType=m_mainParser.getDefaultFontType();
1215 			m_state->getSheet(m_state->m_actSheet, defFontType)->setRowHeight(row, height);
1216 		}
1217 		else
1218 		{
1219 			WPS_DEBUG_MSG(("QuattroSpreadsheet::readRowSize: can not find the current sheet\n"));
1220 			f << "###";
1221 		}
1222 	}
1223 
1224 	ascFile.addPos(pos);
1225 	ascFile.addNote(f.str().c_str());
1226 
1227 	return true;
1228 }
1229 
readRowRangeSize(std::shared_ptr<WPSStream> const & stream)1230 bool QuattroSpreadsheet::readRowRangeSize(std::shared_ptr<WPSStream> const &stream)
1231 {
1232 	RVNGInputStreamPtr input = stream->m_input;
1233 	libwps::DebugFile &ascFile=stream->m_ascii;
1234 	libwps::DebugStream f;
1235 	long pos = input->tell();
1236 	auto type = long(libwps::readU16(input)&0x7fff);
1237 	if (type != 0x105 && type != 0x106)
1238 	{
1239 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readRowRangeSize: not a row size zone\n"));
1240 		return false;
1241 	}
1242 	long sz = libwps::readU16(input);
1243 	if (sz != 6)
1244 	{
1245 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readRowRangeSize: block is too short\n"));
1246 		return false;
1247 	}
1248 
1249 	int minRow = libwps::read16(input);
1250 	int maxRow = libwps::read16(input);
1251 	int height = libwps::readU16(input);
1252 
1253 	f << "Row" << minRow << "<->R" << maxRow << ",";
1254 	if (height&0x8000)   // maybe set by hand?
1255 	{
1256 		f << "user,";
1257 		height &= 0x7fff;
1258 	}
1259 	f << "h=" << float(height)/20.f << ",";
1260 	if (type==0x105)
1261 	{
1262 		if (minRow>=0 && minRow<=maxRow && m_state->m_actSheet>=0)
1263 		{
1264 			auto defFontType=m_mainParser.getDefaultFontType();
1265 			m_state->getSheet(m_state->m_actSheet, defFontType)->setRowHeights(minRow, maxRow, height);
1266 		}
1267 		else
1268 		{
1269 			WPS_DEBUG_MSG(("QuattroSpreadsheet::readRowSize: can not find the current sheet\n"));
1270 			f << "###";
1271 		}
1272 	}
1273 
1274 	ascFile.addPos(pos);
1275 	ascFile.addNote(f.str().c_str());
1276 
1277 	return true;
1278 }
1279 
1280 ////////////////////////////////////////////////////////////
1281 // general
1282 ////////////////////////////////////////////////////////////
readBeginEndSheet(std::shared_ptr<WPSStream> const & stream,int & sheetId)1283 bool QuattroSpreadsheet::readBeginEndSheet(std::shared_ptr<WPSStream> const &stream, int &sheetId)
1284 {
1285 	RVNGInputStreamPtr input = stream->m_input;
1286 	libwps::DebugFile &ascFile=stream->m_ascii;
1287 	libwps::DebugStream f;
1288 
1289 	long pos = input->tell();
1290 	auto type = long(libwps::readU16(input)&0x7fff);
1291 	if (type != 0xca && type != 0xcb)
1292 	{
1293 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readBeginEndSheet: not a zoneB type\n"));
1294 		return false;
1295 	}
1296 	auto sz = long(libwps::readU16(input));
1297 	if (sz != 1)
1298 	{
1299 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readBeginEndSheet: size seems bad\n"));
1300 		f << "###";
1301 		ascFile.addPos(pos);
1302 		ascFile.addNote(f.str().c_str());
1303 		return true;
1304 	}
1305 	auto sheet=int(libwps::readU8(input));
1306 	f << "sheet=" << sheet << ",";
1307 	if (type==0xca)
1308 	{
1309 		if (m_state->m_actSheet>=0)
1310 		{
1311 			WPS_DEBUG_MSG(("QuattroSpreadsheet::readBeginEndSheet: oops, does not find the previous end\n"));
1312 			f << "###";
1313 		}
1314 		sheetId=m_state->m_actSheet=sheet;
1315 	}
1316 	else
1317 	{
1318 		if (m_state->m_actSheet!=sheet)
1319 		{
1320 			WPS_DEBUG_MSG(("QuattroSpreadsheet::readBeginEndSheet: oops, end sheet id does not match with begin sheet id\n"));
1321 			f << "###";
1322 		}
1323 		sheetId=m_state->m_actSheet=-1;
1324 	}
1325 	ascFile.addPos(pos);
1326 	ascFile.addNote(f.str().c_str());
1327 	return true;
1328 }
1329 
readSheetName(std::shared_ptr<WPSStream> const & stream)1330 bool QuattroSpreadsheet::readSheetName(std::shared_ptr<WPSStream> const &stream)
1331 {
1332 	RVNGInputStreamPtr input = stream->m_input;
1333 	libwps::DebugFile &ascFile=stream->m_ascii;
1334 	libwps::DebugStream f;
1335 
1336 	long pos = input->tell();
1337 	auto type = long(libwps::readU16(input)&0x7fff);
1338 	if (type != 0xcc)
1339 	{
1340 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readSheetName: not a zoneB type\n"));
1341 		return false;
1342 	}
1343 	auto sz = long(libwps::readU16(input));
1344 	if (sz < 1)
1345 	{
1346 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readSheetName: size seems bad\n"));
1347 		f << "###";
1348 		ascFile.addPos(pos);
1349 		ascFile.addNote(f.str().c_str());
1350 		return true;
1351 	}
1352 	librevenge::RVNGString name;
1353 	if (!m_mainParser.readCString(stream,name,sz) || name.empty())
1354 		f << "###";
1355 	else
1356 	{
1357 		f << name.cstr() << ",";
1358 		if (m_state->m_idToSheetNameMap.find(m_state->m_actSheet)!=m_state->m_idToSheetNameMap.end())
1359 		{
1360 			WPS_DEBUG_MSG(("QuattroSpreadsheet::readSheetName: id dupplicated\n"));
1361 			f << "###id";
1362 		}
1363 		else
1364 			m_state->m_idToSheetNameMap[m_state->m_actSheet]=name;
1365 	}
1366 	ascFile.addPos(pos);
1367 	ascFile.addNote(f.str().c_str());
1368 	return true;
1369 }
1370 
readViewInfo(std::shared_ptr<WPSStream> const & stream)1371 bool QuattroSpreadsheet::readViewInfo(std::shared_ptr<WPSStream> const &stream)
1372 {
1373 	RVNGInputStreamPtr input = stream->m_input;
1374 	libwps::DebugFile &ascFile=stream->m_ascii;
1375 	libwps::DebugStream f;
1376 	long pos = input->tell();
1377 	auto type = long(libwps::readU16(input)&0x7fff);
1378 	if (type != 0x197 && type !=0x198)
1379 	{
1380 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readViewInfo: not a sheet zone\n"));
1381 		return false;
1382 	}
1383 	long sz = libwps::readU16(input);
1384 	long endPos=pos+4+sz;
1385 	if (sz < 21)
1386 	{
1387 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readViewInfo: block is too short\n"));
1388 		return false;
1389 	}
1390 	auto id=int(libwps::read8(input));
1391 	f << "id=" << id << ",";
1392 
1393 	int val = libwps::readU16(input); // 0|1|3|f
1394 	f << "show=[";
1395 	if (val&1) f << "rowHeading,";
1396 	if (val&2) f << "colHeading,";
1397 	if (val&4) f << "horiGrid,";
1398 	if (val&8) f << "vertGrid,";
1399 	// val&0x10: reserved
1400 	val &=0xfff0;
1401 	if (val)
1402 		f << "f0=" << std::hex << val << std::dec << ",";
1403 	f << "],";
1404 	f << "range=";
1405 	for (int i=0; i<2; ++i)
1406 	{
1407 		f << "C" << int(libwps::readU8(input));
1408 		f << "S" << int(libwps::readU8(input));
1409 		f << "R" << int(libwps::readU16(input));
1410 		f << (i==0 ? "<->" : ",");
1411 	}
1412 	val = libwps::readU16(input);
1413 	switch (val)
1414 	{
1415 	case 0: // default
1416 		break;
1417 	case 1:
1418 		f << "title[hori],";
1419 		break;
1420 	case 2:
1421 		f << "title[verti],";
1422 		break;
1423 	case 3:
1424 		f << "title[both],";
1425 		break;
1426 	default:
1427 		f << "##title=" << val << ",";
1428 		break;
1429 	}
1430 	f << "cell[TL]=C" << int(libwps::readU8(input));
1431 	f << "S" << int(libwps::readU8(input));
1432 	f << "R" << int(libwps::readU16(input)) << ",";
1433 	f << "num[row]=" << int(libwps::readU16(input)) << ",";
1434 	f << "num[col]=" << int(libwps::readU16(input)) << ",";
1435 	if (input->tell()!=endPos)
1436 		ascFile.addDelimiter(input->tell(),'|');
1437 	ascFile.addPos(pos);
1438 	ascFile.addNote(f.str().c_str());
1439 	return true;
1440 }
1441 
1442 ////////////////////////////////////////////////////////////
1443 // formula
1444 ////////////////////////////////////////////////////////////
readCell(std::shared_ptr<WPSStream> const & stream,Vec2i actPos,WKSContentListener::FormulaInstruction & instr,int sheetId,librevenge::RVNGString const & fName)1445 bool QuattroSpreadsheet::readCell
1446 (std::shared_ptr<WPSStream> const &stream, Vec2i actPos, WKSContentListener::FormulaInstruction &instr, int sheetId, librevenge::RVNGString const &fName)
1447 {
1448 	RVNGInputStreamPtr input = stream->m_input;
1449 	instr=WKSContentListener::FormulaInstruction();
1450 	instr.m_type=WKSContentListener::FormulaInstruction::F_Cell;
1451 	instr.m_fileName=fName;
1452 	bool ok = true;
1453 	int pos[3]; // col, sheet, fl|row
1454 	bool relative[3] = { false, false, false};
1455 	for (int d=0; d<2; ++d) pos[d]=int(libwps::readU8(input));
1456 	pos[2]=int(libwps::readU16(input));
1457 	if (pos[2]&0x8000)
1458 	{
1459 		pos[1] = int8_t(pos[1])+sheetId;
1460 		relative[1] = true;
1461 	}
1462 	if (pos[2]&0x4000)
1463 	{
1464 		pos[0] = int8_t(pos[0])+actPos[0];
1465 		relative[0] = true;
1466 	}
1467 	if (pos[2]&0x2000)
1468 	{
1469 		pos[2] = actPos[1]+(int16_t((pos[2]&0x1fff)<<3)>>3);
1470 		relative[2] = true;
1471 	}
1472 	else
1473 		pos[2] &= 0x1fff;
1474 	if (pos[0] < 0 || pos[0] > 255 || pos[2] < 0)
1475 	{
1476 		if (ok)
1477 		{
1478 			WPS_DEBUG_MSG(("QuattroSpreadsheet::readCell: can not read cell position\n"));
1479 		}
1480 		return false;
1481 	}
1482 	instr.m_position[0]=Vec2i(pos[0],pos[2]);
1483 	instr.m_positionRelative[0]=Vec2b(relative[0],relative[2]);
1484 	if (!fName.empty())   // external file, assume default name
1485 	{
1486 		librevenge::RVNGString name;
1487 		name.sprintf("Sheet%d", pos[1]+1);
1488 		instr.m_sheetName[0]=name;
1489 	}
1490 	else
1491 		instr.m_sheetId[0]=pos[1];
1492 	return ok;
1493 }
1494 
readCellReference(std::shared_ptr<WPSStream> const & stream,long endPos,QuattroFormulaInternal::CellReference & ref,Vec2i const & cPos,int sheetId) const1495 bool QuattroSpreadsheet::readCellReference(std::shared_ptr<WPSStream> const &stream, long endPos,
1496                                            QuattroFormulaInternal::CellReference &ref,
1497                                            Vec2i const &cPos, int sheetId) const
1498 {
1499 	ref.m_cells.clear();
1500 	RVNGInputStreamPtr input = stream->m_input;
1501 	long pos = input->tell();
1502 	if (pos+4>endPos) return false;
1503 	auto type=int(libwps::readU16(input));
1504 	int cellType=type>>12;
1505 	if (cellType>4) return false;
1506 	if (cellType==4)
1507 	{
1508 		// type==4: +6 unused bit then a 233 data field (checkme)
1509 		WPS_DEBUG_MSG(("QuattroSpreadsheet::readCellReference: find a cell collection 4\n"));
1510 		return false;
1511 	}
1512 
1513 	WKSContentListener::FormulaInstruction instr;
1514 	if (cellType==3)
1515 	{
1516 		int dataSize=(type&0x3ff);
1517 		if (pos+2+dataSize>endPos)
1518 		{
1519 			WPS_DEBUG_MSG(("QuattroSpreadsheet::readCellReference: can not read the cell collection data size\n"));
1520 			return false;
1521 		}
1522 		if (type&0xc00) // check for deletion
1523 		{
1524 			input->seek(dataSize, librevenge::RVNG_SEEK_CUR);
1525 			return true;
1526 		}
1527 		endPos=pos+2+dataSize;
1528 		while (input->tell()<endPos)
1529 		{
1530 			QuattroFormulaInternal::CellReference cells;
1531 			if (!readCellReference(stream, endPos, cells, cPos, sheetId))
1532 			{
1533 				WPS_DEBUG_MSG(("QuattroSpreadsheet::readCellReference: can not read a cell\n"));
1534 				return false;
1535 			}
1536 			for (auto const &c : cells.m_cells) ref.addInstruction(c);
1537 		}
1538 		return true;
1539 	}
1540 	int const expectedSize[]= {4,8,2};
1541 	if (pos+2+expectedSize[cellType]>endPos) return false;
1542 	if (type&0xc00)
1543 	{
1544 		input->seek(expectedSize[cellType], librevenge::RVNG_SEEK_CUR);
1545 		return true;
1546 	}
1547 	librevenge::RVNGString fileName;
1548 	if ((type&0x3ff))
1549 	{
1550 		if (!m_mainParser.getExternalFileName((type&0x3ff), fileName))
1551 			return false;
1552 	}
1553 	if (cellType==0 && pos+6<=endPos)
1554 	{
1555 		if (!readCell(stream, cPos, instr, sheetId, fileName))
1556 			return false;
1557 		ref.addInstruction(instr);
1558 		return true;
1559 	}
1560 	else if (cellType==2)
1561 	{
1562 		auto fId=int(libwps::readU16(input));
1563 		librevenge::RVNGString text;
1564 		return m_mainParser.getField(fId, text, ref, fileName);
1565 	}
1566 	else if (cellType==1 && pos+10<=endPos)
1567 	{
1568 		WKSContentListener::FormulaInstruction cell2;
1569 		if (!readCell(stream, cPos, instr, sheetId, fileName) ||
1570 		        !readCell(stream, cPos, cell2, sheetId, fileName))
1571 			return false;
1572 		instr.m_type=WKSContentListener::FormulaInstruction::F_CellList;
1573 		instr.m_position[1]=cell2.m_position[0];
1574 		instr.m_positionRelative[1]=cell2.m_positionRelative[0];
1575 		instr.m_sheetId[1]=cell2.m_sheetId[0];
1576 		instr.m_sheetName[1]=cell2.m_sheetName[0];
1577 		ref.addInstruction(instr);
1578 		return true;
1579 	}
1580 	return false;
1581 }
1582 
1583 ////////////////////////////////////////////////////////////
1584 // send data
1585 ////////////////////////////////////////////////////////////
sendSpreadsheet(int sId,std::vector<Vec2i> const & listGraphicCells)1586 void QuattroSpreadsheet::sendSpreadsheet(int sId, std::vector<Vec2i> const &listGraphicCells)
1587 {
1588 	if (!m_listener)
1589 	{
1590 		WPS_DEBUG_MSG(("QuattroSpreadsheet::sendSpreadsheet: I can not find the listener\n"));
1591 		return;
1592 	}
1593 	auto defFontType=m_mainParser.getDefaultFontType();
1594 	auto sheet = m_state->getSheet(sId, defFontType);
1595 	for (auto c: listGraphicCells)
1596 		sheet->getCell(c, defFontType).m_hasGraphic=true;
1597 	m_listener->openSheet(sheet->getWidths(), m_state->getSheetName(sId));
1598 	m_mainParser.sendPageGraphics(sId);
1599 	sheet->compressRowHeights();
1600 	auto it = sheet->m_positionToCellMap.begin();
1601 	int prevRow = -1;
1602 	while (it != sheet->m_positionToCellMap.end())
1603 	{
1604 		int row=it->first[1];
1605 		auto const &cell=(it++)->second;
1606 		if (row>prevRow+1)
1607 		{
1608 			while (row > prevRow+1)
1609 			{
1610 				if (prevRow != -1) m_listener->closeSheetRow();
1611 				int numRepeat;
1612 				float h=sheet->getRowHeight(prevRow+1, numRepeat);
1613 				if (row<prevRow+1+numRepeat)
1614 					numRepeat=row-1-prevRow;
1615 				m_listener->openSheetRow(WPSRowFormat(h), numRepeat);
1616 				prevRow+=numRepeat;
1617 			}
1618 		}
1619 		if (row!=prevRow)
1620 		{
1621 			if (prevRow != -1) m_listener->closeSheetRow();
1622 			m_listener->openSheetRow(WPSRowFormat(sheet->getRowHeight(++prevRow)));
1623 		}
1624 		if (cell.m_alignAcrossColumn)   // we must look for "merged" cell
1625 		{
1626 			auto firstCol=cell.position()[0], lastCol=firstCol+1;
1627 			auto fIt=it;
1628 			while (fIt!=sheet->m_positionToCellMap.end() && fIt->first==Vec2i(lastCol,row))
1629 			{
1630 				auto const &nextCell=fIt->second;
1631 				if (nextCell.m_styleId!=cell.m_styleId)
1632 					break;
1633 				auto const &nextContent=nextCell.m_content;
1634 				if ((nextContent.m_contentType== nextContent.C_NUMBER && !nextContent.isValueSet()) ||
1635 				        nextContent.empty())
1636 				{
1637 					++fIt;
1638 					++lastCol;
1639 				}
1640 				else
1641 					break;
1642 			}
1643 			if (lastCol!=firstCol+1)
1644 			{
1645 				const_cast<QuattroSpreadsheetInternal::Cell &>(cell).setNumSpannedCells(Vec2i(lastCol-firstCol,1));
1646 				it=fIt;
1647 			}
1648 		}
1649 		sendCellContent(cell, sId);
1650 	}
1651 	if (prevRow!=-1) m_listener->closeSheetRow();
1652 	m_listener->closeSheet();
1653 }
1654 
1655 namespace libwps
1656 {
1657 // basic function which probably does not exist on Windows, so rewrite it
strncasecmp(char const * s1,char const * s2,size_t n)1658 static int strncasecmp(char const *s1, char const *s2, size_t n)
1659 {
1660 	if (n == 0)
1661 		return 0;
1662 
1663 	while (n-- != 0 && std::tolower(*s1) == std::tolower(*s2))
1664 	{
1665 		if (n == 0 || *s1 == '\0' || *s2 == '\0')
1666 			break;
1667 		s1++;
1668 		s2++;
1669 	}
1670 
1671 	return std::tolower(*s1) - std::tolower(*s2);
1672 }
1673 }
1674 
updateCellWithUserFormat(QuattroSpreadsheetInternal::Cell & cell,librevenge::RVNGString const & format)1675 void QuattroSpreadsheet::updateCellWithUserFormat(QuattroSpreadsheetInternal::Cell &cell, librevenge::RVNGString const &format)
1676 {
1677 	if (format.empty())
1678 	{
1679 		WPS_DEBUG_MSG(("QuattroSpreadsheet::updateCellWithUserFormat: called with empty format\n"));
1680 		return;
1681 	}
1682 	char const *ptr=format.cstr();
1683 	auto c=char(std::toupper(*(ptr++)));
1684 	// first N/n: numeric, T/t: date
1685 	// all: *: fill with last data, 'string': strings, \x: x
1686 	if (c=='N')
1687 	{
1688 		bool scientific=false;
1689 		bool hasThousand=false;
1690 		bool percent=false;
1691 		int digits=-1;
1692 		bool end=false;
1693 		// numeric 0:always a digits, 9: potential digit, %: percent, ,:thousand, .:decimal, ;different format pos,equal,neg, [Ee][+-], other string
1694 		while (*ptr)
1695 		{
1696 			c=char(std::toupper(*(ptr++)));
1697 			bool ok=true;
1698 			switch (c)
1699 			{
1700 			case '0':
1701 			case '9':
1702 				if (digits>=0 && !scientific) ++digits;
1703 				break;
1704 			case ',':
1705 				if (digits<0 && !scientific)
1706 					hasThousand=true;
1707 				else
1708 					ok=false;
1709 				break;
1710 			case 'E':
1711 				if (digits<0)
1712 					scientific=true;
1713 				else
1714 					ok=false;
1715 				break;
1716 			case '.':
1717 				if (digits<0 && !scientific)
1718 					digits=0;
1719 				else
1720 					ok=false;
1721 				break;
1722 			case '+':
1723 			case '-':
1724 				ok=scientific;
1725 				break;
1726 			case ';':
1727 				end=true;
1728 				break;
1729 			case '%':
1730 				percent=true;
1731 				break;
1732 			default:
1733 				if (digits || scientific)
1734 					end=true;
1735 				else
1736 					ok=false;
1737 				break;
1738 			}
1739 			if (!ok)
1740 			{
1741 				WPS_DEBUG_MSG(("QuattroSpreadsheet::updateCellWithUserFormat: unsure how to format %s\n", format.cstr()));
1742 				cell.setFormat(cell.F_NUMBER, 0);
1743 				return;
1744 			}
1745 			if (end)
1746 				break;
1747 		}
1748 		if (digits>0)
1749 			cell.setDigits(digits);
1750 		if (scientific)
1751 			cell.setFormat(cell.F_NUMBER,4);
1752 		else if (percent)
1753 			cell.setFormat(cell.F_NUMBER,3);
1754 		else
1755 			cell.setFormat(cell.F_NUMBER, hasThousand ? 5 : 1);
1756 		return;
1757 	}
1758 	if (c!='T')
1759 	{
1760 		WPS_DEBUG_MSG(("QuattroSpreadsheet::updateCellWithUserFormat: unsure how to format %s\n", format.cstr()));
1761 		return;
1762 	}
1763 	// d: day(1-31), dd: day(01-31), wday(sun), weekday(sunday),
1764 	// m: month(1-12) or minute if preceded by h or hh, mm: month(01-12) or..
1765 	// mo: month(1-12), mmo: month(01-12), mon(jan), month(january)
1766 	// yy: year(00-99), yyyy: year(0001-9999), h: hour(0-23) except if ampm, hh: hour(00-23) except if ampm
1767 	// mi: minute, mmi, s: second, ss
1768 	// ampm:
1769 	std::string dtFormat;
1770 	bool hasHour=false;
1771 	bool hasDate=false;
1772 	bool inString=false;
1773 	while (*ptr)
1774 	{
1775 		c=*(ptr++);
1776 		if (inString)
1777 		{
1778 			if (c=='\'')
1779 				inString=false;
1780 			else if (c=='\\')
1781 			{
1782 				if (*ptr)
1783 					dtFormat+=*(ptr++);
1784 			}
1785 			else
1786 				dtFormat+=c;
1787 			continue;
1788 		}
1789 		c=char(std::toupper(c));
1790 		switch (c)
1791 		{
1792 		case 'A':
1793 			if (libwps::strncasecmp(ptr, "mpm",3)==0)
1794 			{
1795 				dtFormat+="%p";
1796 				ptr+=3;
1797 				hasHour=true;
1798 			}
1799 			else
1800 				dtFormat+=c;
1801 			break;
1802 		case 'D':
1803 			if (*ptr=='d' || *ptr=='D')
1804 				++ptr;
1805 			dtFormat+="%d";
1806 			hasDate=true;
1807 			break;
1808 		case 'H':
1809 			if (*ptr=='h' || *ptr=='H')
1810 				++ptr;
1811 			dtFormat+="%H";
1812 			hasHour=true;
1813 			break;
1814 		case 'M':
1815 			if (*ptr=='m' || *ptr=='M')
1816 				++ptr;
1817 			if (*ptr=='o' || *ptr=='O')
1818 			{
1819 				if (libwps::strncasecmp(ptr, "onth",4)==0)
1820 				{
1821 					dtFormat+="%B";
1822 					ptr+=4;
1823 				}
1824 				else if (libwps::strncasecmp(ptr, "on",2)==0)
1825 				{
1826 					dtFormat+="%b";
1827 					ptr+=2;
1828 				}
1829 				else
1830 				{
1831 					dtFormat+="%m";
1832 					++ptr;
1833 				}
1834 				hasDate=true;
1835 			}
1836 			else if (*ptr=='i' || *ptr=='I')
1837 			{
1838 				hasHour=true;
1839 				dtFormat+="%M";
1840 				++ptr;
1841 			}
1842 			else if (hasHour)
1843 				dtFormat+="%M";
1844 			else
1845 				dtFormat+="%m";
1846 			break;
1847 		case 'S':
1848 			if (*ptr=='s' || *ptr=='S')
1849 				++ptr;
1850 			dtFormat+="%S";
1851 			hasHour=true;
1852 			break;
1853 		case 'W':
1854 			if (libwps::strncasecmp(ptr, "day",3)==0)
1855 			{
1856 				dtFormat+="%a";
1857 				ptr+=3;
1858 				hasDate=true;
1859 			}
1860 			else if (libwps::strncasecmp(ptr, "eekday",6)==0)
1861 			{
1862 				dtFormat+="%A";
1863 				ptr+=6;
1864 				hasDate=true;
1865 			}
1866 			else
1867 				dtFormat+=c;
1868 			break;
1869 		case 'Y':
1870 			if (libwps::strncasecmp(ptr, "yyy",3)==0)
1871 			{
1872 				dtFormat+="%Y";
1873 				ptr+=3;
1874 				hasDate=true;
1875 			}
1876 			else if (libwps::strncasecmp(ptr, "y",1)==0)
1877 			{
1878 				dtFormat+="%y";
1879 				ptr+=1;
1880 				hasDate=true;
1881 			}
1882 			else
1883 				dtFormat+=c;
1884 			break;
1885 		case '\'':
1886 			inString=true;
1887 			break;
1888 		case '\\':
1889 			if (*ptr)
1890 				dtFormat+=*(ptr++);
1891 			break;
1892 		default:
1893 			dtFormat+=c;
1894 			break;
1895 		}
1896 	}
1897 	cell.setDTFormat((hasDate||!hasHour) ? cell.F_DATE : cell.F_TIME, dtFormat);
1898 }
1899 
sendCellContent(QuattroSpreadsheetInternal::Cell const & cell,int sheetId)1900 void QuattroSpreadsheet::sendCellContent(QuattroSpreadsheetInternal::Cell const &cell, int sheetId)
1901 {
1902 	if (m_listener.get() == nullptr)
1903 	{
1904 		WPS_DEBUG_MSG(("QuattroSpreadsheet::sendCellContent: I can not find the listener\n"));
1905 		return;
1906 	}
1907 
1908 	libwps_tools_win::Font::Type fontType = cell.m_fontType;
1909 	m_listener->setFont(cell.getFont());
1910 
1911 	QuattroSpreadsheetInternal::Cell finalCell(cell);
1912 	auto &content=finalCell.m_content;
1913 	for (auto &f : content.m_formula)
1914 	{
1915 		if (f.m_type==WKSContentListener::FormulaInstruction::F_Cell ||
1916 		        f.m_type==WKSContentListener::FormulaInstruction::F_CellList)
1917 		{
1918 			int dim=f.m_type==WKSContentListener::FormulaInstruction::F_Cell ? 1 : 2;
1919 			for (int i=0; i<dim; ++i)
1920 			{
1921 				if (f.m_sheetId[i]>=0 && f.m_sheetName[i].empty() && (f.m_sheetId[i]!=sheetId || !f.m_fileName.empty()))
1922 					f.m_sheetName[i]=getSheetName(f.m_sheetId[i]);
1923 			}
1924 			continue;
1925 		}
1926 		if (f.m_type!=WKSContentListener::FormulaInstruction::F_Text)
1927 			continue;
1928 		std::string &text=f.m_content;
1929 		librevenge::RVNGString finalString=libwps_tools_win::Font::unicodeString(text, fontType);
1930 		if (finalString.empty())
1931 			text.clear();
1932 		else
1933 			text=finalString.cstr();
1934 	}
1935 	if ((finalCell.m_fileFormat>>4)==7)
1936 	{
1937 		auto it=m_state->m_idToUserFormatMap.find(finalCell.m_fileFormat&0xf);
1938 		if (it==m_state->m_idToUserFormatMap.end() || it->second.empty())
1939 		{
1940 			WPS_DEBUG_MSG(("QuattroSpreadsheet::sendCellContent: can not find an user format\n"));
1941 		}
1942 		else
1943 			updateCellWithUserFormat(finalCell, it->second);
1944 	}
1945 	else
1946 		finalCell.updateFormat();
1947 	m_listener->openSheetCell(finalCell, content);
1948 	if (cell.m_hasGraphic)
1949 		m_mainParser.sendGraphics(sheetId, cell.position());
1950 	if (cell.m_content.m_textEntry.valid())
1951 	{
1952 		if (!cell.m_stream || !cell.m_stream->m_input)
1953 		{
1954 			WPS_DEBUG_MSG(("QuattroSpreadsheet::sendCellContent: oops can not find the text's stream\n"));
1955 		}
1956 		else
1957 		{
1958 			auto input=cell.m_stream->m_input;
1959 			input->seek(cell.m_content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET);
1960 			bool prevEOL=false;
1961 			std::string text;
1962 			while (input->tell()<=cell.m_content.m_textEntry.end())
1963 			{
1964 				bool last=input->isEnd() || input->tell()>=cell.m_content.m_textEntry.end();
1965 				auto c=last ? '\0' : char(libwps::readU8(input));
1966 				if ((c==0 || c==0xa || c==0xd) && !text.empty())
1967 				{
1968 					m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(text, fontType));
1969 					text.clear();
1970 				}
1971 				if (last) break;
1972 				if (c==0xd)
1973 				{
1974 					m_listener->insertEOL();
1975 					prevEOL=true;
1976 				}
1977 				else if (c==0xa)
1978 				{
1979 					if (!prevEOL)
1980 					{
1981 						WPS_DEBUG_MSG(("QuattroSpreadsheet::sendCellContent: find 0xa without 0xd\n"));
1982 					}
1983 					prevEOL=false;
1984 				}
1985 				else
1986 				{
1987 					if (c)
1988 						text.push_back(c);
1989 					prevEOL=false;
1990 				}
1991 			}
1992 		}
1993 	}
1994 	m_listener->closeSheetCell();
1995 }
1996 
1997 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
1998