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 <sstream>
27 #include <limits>
28 #include <stack>
29
30 #include <librevenge-stream/librevenge-stream.h>
31
32 #include "libwps_internal.h"
33 #include "libwps_tools_win.h"
34
35 #include "WPSCell.h"
36 #include "WKSContentListener.h"
37 #include "WPSEntry.h"
38 #include "WPSFont.h"
39 #include "WPSTable.h"
40
41 #include "WKS4.h"
42
43 #include "WKS4Spreadsheet.h"
44
45 namespace WKS4SpreadsheetInternal
46 {
47
48 ///////////////////////////////////////////////////////////////////
49 //! a class used to store a style of a cell in WKS4Spreadsheet
50 struct Style final : public WPSCellFormat
51 {
52 //! construtor
StyleWKS4SpreadsheetInternal::Style53 explicit Style(libwps_tools_win::Font::Type type)
54 : WPSCellFormat()
55 , m_fileFont()
56 , m_fontType(type)
57 , m_extra("")
58 {
59 for (int &unknFlag : m_unknFlags) unknFlag = 0;
60 }
61 Style(Style const &)=default;
62 Style &operator=(Style const &)=default;
63 //! destructor
64 ~Style() final;
65 //! operator<<
66 friend std::ostream &operator<<(std::ostream &o, Style const &style);
67 //! operator==
68 bool operator==(Style const &st) const;
69 //! operator!=
operator !=WKS4SpreadsheetInternal::Style70 bool operator!=(Style const &st) const
71 {
72 return !(*this==st);
73 }
74 /** the font */
75 WPSFont m_fileFont;
76 //! font encoding type
77 libwps_tools_win::Font::Type m_fontType;
78 /** some flag */
79 int m_unknFlags[10];
80 /** extra data */
81 std::string m_extra;
82 };
83
~Style()84 Style::~Style()
85 {
86 }
87
88 //! operator<<
operator <<(std::ostream & o,Style const & style)89 std::ostream &operator<<(std::ostream &o, Style const &style)
90 {
91 o << "font=[" << style.m_fileFont << "],";
92 o << static_cast<WPSCellFormat const &>(style) << ",";
93
94 bool hasUnkn = false;
95 for (int unknFlag : style.m_unknFlags)
96 {
97 if (!unknFlag) continue;
98 hasUnkn=true;
99 break;
100 }
101 if (hasUnkn)
102 {
103 o << "unkn=[" << std::hex;
104 for (int i = 0; i < 10; i++)
105 {
106 if (style.m_unknFlags[i]) o << "fS" << i << "=" << std::hex << style.m_unknFlags[i] << std::dec << ",";
107 }
108 o << std::dec << "]";
109 }
110 if (style.m_extra.length())
111 o << ", extra=[" << style.m_extra << "]";
112
113 return o;
114 }
115
operator ==(Style const & st) const116 bool Style::operator==(Style const &st) const
117 {
118 if (m_fileFont!=st.m_fileFont) return false;
119 if (m_format!=st.m_format || m_subFormat!=st.m_subFormat || m_digits!=st.m_digits || m_protected !=st.m_protected)
120 return false;
121 int diff = WPSCellFormat::compare(st);
122 if (diff) return false;
123 for (int i = 0; i < 10; i++)
124 {
125 if (m_unknFlags[i]!=st.m_unknFlags[i])
126 return false;
127 }
128 return m_extra==st.m_extra;
129 }
130
131 ///////////////////////////////////////////////////////////////////
132 //! the style manager
133 class StyleManager
134 {
135 public:
StyleManager()136 StyleManager()
137 : m_stylesList() {}
138 //! add a new style and returns its id
add(Style const & st,bool dosFile)139 int add(Style const &st, bool dosFile)
140 {
141 if (dosFile)
142 {
143 for (size_t i=0; i < m_stylesList.size(); ++i)
144 {
145 if (m_stylesList[i]==st) return int(i);
146 }
147 }
148 m_stylesList.push_back(st);
149 return int(m_stylesList.size())-1;
150 }
151 //! returns the style with id
get(int id,Style & style) const152 bool get(int id, Style &style) const
153 {
154 if (id<0|| id >= int(m_stylesList.size()))
155 {
156 WPS_DEBUG_MSG(("WKS4ParserInternal::StyleManager::get can not find style %d\n", id));
157 return false;
158 }
159 style=m_stylesList[size_t(id)];
160 return true;
161 }
162 //! returns the number of style
size() const163 int size() const
164 {
165 return int(m_stylesList.size());
166 }
167 //! print a style
print(int id,std::ostream & o) const168 void print(int id, std::ostream &o) const
169 {
170 if (id < 0) return;
171 if (id < int(m_stylesList.size()))
172 o << ", style=" << m_stylesList[size_t(id)];
173 else
174 {
175 WPS_DEBUG_MSG(("WKS4ParserInternal::StyleManager::print: can not find a style\n"));
176 o << ", ###style=" << id;
177 }
178 }
179
180 protected:
181 //! the styles
182 std::vector<Style> m_stylesList;
183 };
184
185 //! a cellule of a WKS4 spreadsheet
186 class Cell final : public WPSCell
187 {
188 public:
189 /// constructor
Cell()190 Cell()
191 : m_styleId(-1)
192 , m_hAlignement(WPSCellFormat::HALIGN_DEFAULT)
193 , m_content()
194 , m_extraTextEntryList() { }
195
196 //! operator<<
197 friend std::ostream &operator<<(std::ostream &o, Cell const &cell);
198
199 //! call when a cell must be send
200 bool send(WPSListenerPtr &/*listener*/) final;
201
202 //! call when the content of a cell must be send
sendContent(WPSListenerPtr &)203 bool sendContent(WPSListenerPtr &/*listener*/) final
204 {
205 WPS_DEBUG_MSG(("WKS4SpreadsheetInternal::Cell::sendContent: must not be called\n"));
206 return false;
207 }
208
209 //! the style
210 int m_styleId;
211 //! the horizontal align (in dos file)
212 WPSCellFormat::HorizontalAlignment m_hAlignement;
213 //! the content
214 WKSContentListener::CellContent m_content;
215 /** As very long text is splitted in zone 0xf and then in zone 0x36,
216 the list of zone36 text entries...
217 */
218 std::vector<WPSEntry> m_extraTextEntryList;
219 };
220
send(WPSListenerPtr &)221 bool Cell::send(WPSListenerPtr &/*listener*/)
222 {
223 WPS_DEBUG_MSG(("WKS4SpreadsheetInternal::Cell::send: must not be called\n"));
224 return false;
225 }
226
227 //! operator<<
operator <<(std::ostream & o,Cell const & cell)228 std::ostream &operator<<(std::ostream &o, Cell const &cell)
229 {
230 o << reinterpret_cast<WPSCell const &>(cell)
231 << cell.m_content << ",style=" << cell.m_styleId << ",";
232 switch (cell.m_hAlignement)
233 {
234 case WPSCellFormat::HALIGN_LEFT:
235 o << "left,";
236 break;
237 case WPSCellFormat::HALIGN_CENTER:
238 o << "centered,";
239 break;
240 case WPSCellFormat::HALIGN_RIGHT:
241 o << "right,";
242 break;
243 case WPSCellFormat::HALIGN_FULL:
244 o << "full,";
245 break;
246 case WPSCellFormat::HALIGN_DEFAULT:
247 default:
248 break; // default
249 }
250 return o;
251 }
252
253 ///////////////////////////////////////////////////////////////////
254 //! the spreadsheet of a WPS4Spreadsheet
255 class Spreadsheet
256 {
257 public:
258 //! the spreadsheet type
259 enum Type { T_Spreadsheet, T_Filter, T_Report };
260
261 //! a constructor
Spreadsheet(Type type=T_Spreadsheet,int id=0)262 Spreadsheet(Type type=T_Spreadsheet, int id=0)
263 : m_type(type)
264 , m_id(id)
265 , m_numCols(0)
266 , m_numRows(0)
267 , m_widthCols()
268 , m_rowHeightMap()
269 , m_heightDefault(16)
270 , m_positionToCellMap()
271 , m_lastCellPos()
272 , m_rowPageBreaksList() {}
273 //! return a cell corresponding to a spreadsheet, create one if needed
getCell(Vec2i const & pos)274 Cell &getCell(Vec2i const &pos)
275 {
276 if (m_positionToCellMap.find(pos)==m_positionToCellMap.end())
277 {
278 Cell cell;
279 cell.setPosition(pos);
280 m_positionToCellMap[pos]=cell;
281 }
282 m_lastCellPos=pos;
283 return m_positionToCellMap.find(pos)->second;
284 }
285 //! returns the last cell
getLastCell()286 Cell *getLastCell()
287 {
288 if (m_positionToCellMap.find(m_lastCellPos)==m_positionToCellMap.end())
289 return nullptr;
290 return &m_positionToCellMap.find(m_lastCellPos)->second;
291 }
292 //! set the columns size
setColumnWidth(int col,int w=-1)293 void setColumnWidth(int col, int w=-1)
294 {
295 if (col < 0) return;
296 if (col >= int(m_widthCols.size())) m_widthCols.resize(size_t(col)+1, -1);
297 m_widthCols[size_t(col)] = w;
298 if (col >= m_numCols) m_numCols=col+1;
299 }
300
301 //! returns the row size in point
getRowHeight(int row) const302 float getRowHeight(int row) const
303 {
304 auto rIt=m_rowHeightMap.lower_bound(Vec2i(-1,row));
305 if (rIt!=m_rowHeightMap.end() && rIt->first[0]<=row && rIt->first[1]>=row)
306 return float(rIt->second)/20.f;
307 return float(m_heightDefault);
308 }
309 /** returns the height of a row in point and updated repeated row
310
311 \note: you must first call compressRowHeigths
312 */
getRowHeight(int row,int & numRepeated) const313 float getRowHeight(int row, int &numRepeated) const
314 {
315 auto rIt=m_rowHeightMap.lower_bound(Vec2i(-1,row));
316 if (rIt!=m_rowHeightMap.end() && rIt->first[0]<=row && rIt->first[1]>=row)
317 {
318 numRepeated=rIt->first[1]-row+1;
319 return float(rIt->second)/20.f;
320 }
321 numRepeated=10000;
322 return float(m_heightDefault);
323 }
324 //! set the rows size
setRowHeight(int row,int h)325 void setRowHeight(int row, int h)
326 {
327 if (h>=0)
328 m_rowHeightMap[Vec2i(row,row)]=h;
329 }
330 //! try to compress the list of row height
compressRowHeights()331 void compressRowHeights()
332 {
333 auto oldMap=m_rowHeightMap;
334 m_rowHeightMap.clear();
335 int actHeight=-1;
336 Vec2i actPos(0,-1);
337 int const defHeight=m_heightDefault*20; // conversion from point to TWIP
338 for (auto rIt : oldMap)
339 {
340 // first check for not filled row
341 if (rIt.first[0]!=actPos[1]+1)
342 {
343 if (actHeight==defHeight)
344 actPos[1]=rIt.first[0]-1;
345 else
346 {
347 if (actPos[1]>=actPos[0])
348 m_rowHeightMap[actPos]=actHeight;
349 actHeight=defHeight;
350 actPos=Vec2i(actPos[1]+1, rIt.first[0]-1);
351 }
352 }
353 if (rIt.second!=actHeight)
354 {
355 if (actPos[1]>=actPos[0])
356 m_rowHeightMap[actPos]=actHeight;
357 actPos[0]=rIt.first[0];
358 actHeight=rIt.second;
359 }
360 actPos[1]=rIt.first[1];
361 }
362 if (actPos[1]>=actPos[0])
363 m_rowHeightMap[actPos]=actHeight;
364 }
365 //! return the columns format
getWidths(float defSize=72) const366 std::vector<WPSColumnFormat> getWidths(float defSize=72) const
367 {
368 std::vector<WPSColumnFormat> widths;
369 WPSColumnFormat defWidth(defSize), actWidth;
370 defWidth.m_useOptimalWidth=true;
371 int repeat=0;
372 for (auto const &c : m_widthCols)
373 {
374 WPSColumnFormat newWidth;
375 if (c < 0)
376 newWidth=defWidth;
377 else
378 newWidth=WPSColumnFormat(float(c)/20.f);
379 if (repeat && newWidth!=actWidth)
380 {
381 actWidth.m_numRepeat=repeat;
382 widths.push_back(actWidth);
383 repeat=0;
384 }
385 if (repeat==0)
386 actWidth=newWidth;
387 ++repeat;
388 }
389 if (repeat)
390 {
391 actWidth.m_numRepeat=repeat;
392 widths.push_back(actWidth);
393 }
394 return widths;
395 }
396 //! returns true if the spreedsheet is empty
empty() const397 bool empty() const
398 {
399 return m_positionToCellMap.empty();
400 }
401 //! the spreadsheet type
402 Type m_type;
403 //! the spreadsheet id
404 int m_id;
405 /** the number of columns */
406 int m_numCols;
407 /** the number of rows */
408 int m_numRows;
409
410 /** the column size in TWIP (?) */
411 std::vector<int> m_widthCols;
412 /** the map Vec2i(min row, max row) to size in TWIP (?) */
413 std::map<Vec2i,int> m_rowHeightMap;
414 /** the default row size in point */
415 int m_heightDefault;
416 /** a map cell to not empty cells */
417 std::map<Vec2i, Cell> m_positionToCellMap;
418 /** the last cell position */
419 Vec2i m_lastCellPos;
420 /** the list of row page break */
421 std::vector<int> m_rowPageBreaksList;
422
423 };
424
425 //! the state of WKS4Spreadsheet
426 struct State
427 {
428 //! constructor
StateWKS4SpreadsheetInternal::State429 State()
430 : m_eof(-1)
431 , m_version(-1)
432 , m_styleManager()
433 , m_spreadsheetList()
434 , m_spreadsheetStack()
435 {
436 pushNewSheet(std::shared_ptr<Spreadsheet>(new Spreadsheet(Spreadsheet::T_Spreadsheet, 0)));
437 }
438 //! returns the maximal spreadsheet
getMaximalSheetWKS4SpreadsheetInternal::State439 int getMaximalSheet(Spreadsheet::Type type=Spreadsheet::T_Spreadsheet) const
440 {
441 int max=-1;
442 for (auto sheet : m_spreadsheetList)
443 {
444 if (!sheet || sheet->m_type != type || sheet->m_id<=max || sheet->empty()) continue;
445 max=sheet->m_id;
446 }
447 return max;
448 }
449 //! returns the ith real spreadsheet
getSheetWKS4SpreadsheetInternal::State450 std::shared_ptr<Spreadsheet> getSheet(Spreadsheet::Type type, int id)
451 {
452 for (auto sheet : m_spreadsheetList)
453 {
454 if (!sheet || sheet->m_type!=type || sheet->m_id!=id)
455 continue;
456 return sheet;
457 }
458 return std::shared_ptr<Spreadsheet>();
459 }
460 //! returns the ith spreadsheet
getSheetNameWKS4SpreadsheetInternal::State461 static librevenge::RVNGString getSheetName(int id)
462 {
463 librevenge::RVNGString name;
464 name.sprintf("Sheet%d", id+1);
465 return name;
466 }
467 //! returns the actual sheet
getActualSheetWKS4SpreadsheetInternal::State468 Spreadsheet &getActualSheet()
469 {
470 return *m_spreadsheetStack.top();
471 }
472 //! create a new sheet and stack id
pushNewSheetWKS4SpreadsheetInternal::State473 void pushNewSheet(std::shared_ptr<Spreadsheet> sheet)
474 {
475 if (!sheet)
476 {
477 WPS_DEBUG_MSG(("WKS4SpreadsheetInternal::State::pushSheet: can not find the sheet\n"));
478 return;
479 }
480 m_spreadsheetStack.push(sheet);
481 m_spreadsheetList.push_back(sheet);
482 }
483 //! try to pop the actual sheet
popSheetWKS4SpreadsheetInternal::State484 bool popSheet()
485 {
486 if (m_spreadsheetStack.size()<=1)
487 {
488 WPS_DEBUG_MSG(("WKS4SpreadsheetInternal::State::popSheet: can pop the main sheet\n"));
489 return false;
490 }
491 m_spreadsheetStack.pop();
492 return true;
493 }
494 //! the last file position
495 long m_eof;
496 //! the file version
497 int m_version;
498 //! the style manager
499 StyleManager m_styleManager;
500
501 //! the list of spreadsheet ( first: main spreadsheet, other report spreadsheet )
502 std::vector<std::shared_ptr<Spreadsheet> > m_spreadsheetList;
503 //! the stack of spreadsheet id
504 std::stack<std::shared_ptr<Spreadsheet> > m_spreadsheetStack;
505 };
506
507 }
508
509 // constructor, destructor
WKS4Spreadsheet(WKS4Parser & parser)510 WKS4Spreadsheet::WKS4Spreadsheet(WKS4Parser &parser)
511 : m_input(parser.getInput())
512 , m_listener()
513 , m_mainParser(parser)
514 , m_state(new WKS4SpreadsheetInternal::State)
515 , m_asciiFile(parser.ascii())
516 {
517 m_state.reset(new WKS4SpreadsheetInternal::State);
518 }
519
~WKS4Spreadsheet()520 WKS4Spreadsheet::~WKS4Spreadsheet()
521 {
522 }
523
resetInput(RVNGInputStreamPtr const & newInput)524 void WKS4Spreadsheet::resetInput(RVNGInputStreamPtr const &newInput)
525 {
526 m_input=newInput;
527 }
528
version() const529 int WKS4Spreadsheet::version() const
530 {
531 if (m_state->m_version<0)
532 m_state->m_version=m_mainParser.version();
533 return m_state->m_version;
534 }
535
checkFilePosition(long pos)536 bool WKS4Spreadsheet::checkFilePosition(long pos)
537 {
538 if (m_state->m_eof < 0)
539 {
540 long actPos = m_input->tell();
541 m_input->seek(0, librevenge::RVNG_SEEK_END);
542 m_state->m_eof=m_input->tell();
543 m_input->seek(actPos, librevenge::RVNG_SEEK_SET);
544 }
545 return pos <= m_state->m_eof;
546 }
547
getNumSpreadsheets() const548 int WKS4Spreadsheet::getNumSpreadsheets() const
549 {
550 return m_state->getMaximalSheet(WKS4SpreadsheetInternal::Spreadsheet::T_Spreadsheet)+1;
551 }
552
getSheetName(int id) const553 librevenge::RVNGString WKS4Spreadsheet::getSheetName(int id) const
554 {
555 return m_state->getSheetName(id);
556 }
557
558 ////////////////////////////////////////////////////////////
559 // low level
560
561 ////////////////////////////////////////////////////////////
562 // parse sheet data
563 ////////////////////////////////////////////////////////////
readSheetSize()564 bool WKS4Spreadsheet::readSheetSize()
565 {
566 libwps::DebugStream f;
567 long pos = m_input->tell();
568 long type = libwps::read16(m_input);
569 if (type != 0x6)
570 {
571 WPS_DEBUG_MSG(("WKS4Spreadsheet::readSheetSize: not a sheet zone\n"));
572 return false;
573 }
574 long sz = libwps::readU16(m_input);
575 if (sz < 8)
576 {
577 WPS_DEBUG_MSG(("WKS4Spreadsheet::readSheetSize: block is too short\n"));
578 return false;
579 }
580 f << "Entries(SheetSize):";
581 for (int i = 0; i < 2; i++) // always 2 zero ?
582 {
583 int val = libwps::read16(m_input);
584 if (!val) continue;
585 f << "f" << i << "=" << std::hex << val << std::dec << ",";
586 }
587 int nCol = libwps::read16(m_input)+1;
588 f << "nCols=" << nCol << ",";
589 int nRow = libwps::read16(m_input);
590 f << "nRow=" << nRow << ",";
591 ascii().addPos(pos);
592 ascii().addNote(f.str().c_str());
593 // empty spreadsheet
594 if (nRow==-1 && nCol==0) return true;
595 if (nRow < 0 || nCol <= 0) return false;
596
597 m_state->getActualSheet().setColumnWidth(nCol-1);
598 return true;
599
600 }
601
readColumnSize()602 bool WKS4Spreadsheet::readColumnSize()
603 {
604 libwps::DebugStream f;
605 long pos = m_input->tell();
606 long type = libwps::read16(m_input);
607 if (type != 0x8)
608 {
609 WPS_DEBUG_MSG(("WKS4Spreadsheet::readColumnSize: not a column size zone\n"));
610 return false;
611 }
612 long sz = libwps::readU16(m_input);
613 if (sz < 3)
614 {
615 WPS_DEBUG_MSG(("WKS4Spreadsheet::readColumnSize: block is too short\n"));
616 return false;
617 }
618
619 int col = libwps::read16(m_input);
620 int width = libwps::readU8(m_input);
621
622 bool ok = col >= 0 && col < m_state->getActualSheet().m_numCols+10;
623 f << "Entries(Column):Col" << col << "";
624 if (!ok) f << "###";
625 f << ":width=" << width << ",";
626
627 if (ok)
628 {
629 if (col >= m_state->getActualSheet().m_numCols)
630 {
631 static bool first = true;
632 if (first)
633 {
634 first = false;
635 WPS_DEBUG_MSG(("WKS4Spreadsheet::readColumnSize: I must increase the number of columns\n"));
636 }
637 f << "#col[inc],";
638 }
639 // checkme: unit in character(?) -> TWIP
640 m_state->getActualSheet().setColumnWidth(col, width*160);
641 }
642 ascii().addPos(pos);
643 ascii().addNote(f.str().c_str());
644
645 return ok;
646 }
647
readHiddenColumns()648 bool WKS4Spreadsheet::readHiddenColumns()
649 {
650 libwps::DebugStream f;
651 long pos = m_input->tell();
652 long type = libwps::read16(m_input);
653 if (type != 0x64)
654 {
655 WPS_DEBUG_MSG(("WKS4Spreadsheet::readHiddenColumns: not a column hidden zone\n"));
656 return false;
657 }
658 long sz = libwps::readU16(m_input);
659 if (sz != 32)
660 {
661 WPS_DEBUG_MSG(("WKS4Spreadsheet::readHiddenColumns: block size seems bad\n"));
662 return false;
663 }
664
665 f << "Entries(HiddenCol):";
666 for (int i=0; i<32; ++i)
667 {
668 auto val=int(libwps::readU8(m_input));
669 if (!val) continue;
670 static bool first=true;
671 if (first)
672 {
673 WPS_DEBUG_MSG(("WKS4Spreadsheet::readHiddenColumns: find some hidden col, ignored\n"));
674 first=false;
675 }
676 // checkme
677 for (int j=0, depl=1; j<8; ++j, depl<<=1)
678 {
679 if (val&depl)
680 f << "col" << j+8*i << "[hidden],";
681 }
682 }
683 ascii().addPos(pos);
684 ascii().addNote(f.str().c_str());
685
686 return true;
687 }
688
689 ////////////////////////////////////////////////////////////
690 // MSWorks
691 ////////////////////////////////////////////////////////////
readMsWorksColumnSize()692 bool WKS4Spreadsheet::readMsWorksColumnSize()
693 {
694 libwps::DebugStream f;
695 long pos = m_input->tell();
696 long type = libwps::read16(m_input);
697 if (type != 0x546b)
698 {
699 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksColumnSize: not a column size zone\n"));
700 return false;
701 }
702 long sz = libwps::readU16(m_input);
703 if (sz != 4)
704 {
705 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksColumnSize: block size is odd\n"));
706 return false;
707 }
708
709 int col = libwps::read16(m_input);
710 int width = libwps::readU16(m_input); // unit? 1point ~ 115
711
712 bool ok = col >= 0 && col < m_state->getActualSheet().m_numCols+10;
713 if (ok)
714 m_state->getActualSheet().setColumnWidth(col, width & 0x7FFF);
715
716 f << "Entries(Colum2):Col" << col << ":";
717 if (!ok) f << "###";
718 if (width & 0x8000)
719 {
720 f << "flag?,";
721 width &= 0x7FFF;
722 }
723 f << "width(unit?)=" << width;
724
725 ascii().addPos(pos);
726 ascii().addNote(f.str().c_str());
727
728 return true;
729 }
730
readMsWorksRowSize()731 bool WKS4Spreadsheet::readMsWorksRowSize()
732 {
733 libwps::DebugStream f;
734 long pos = m_input->tell();
735 long type = libwps::read16(m_input);
736 if (type != 0x5465)
737 {
738 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksRowSize: not a row size zone\n"));
739 return false;
740 }
741 long sz = libwps::readU16(m_input);
742 if (sz != 4)
743 {
744 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksRowSize: block size is odd\n"));
745 return false;
746 }
747
748 int row = libwps::read16(m_input);
749 int width = libwps::readU16(m_input); // unit? 1point ~ 105
750
751 bool ok = row >= 0;
752 if (ok) m_state->getActualSheet().setRowHeight(row, width & 0x7FFF);
753 f << "Entries(Row2):Row" << row << ":";
754 if (!ok) f << "###";
755 if (width & 0x8000)
756 {
757 f << "flag?,";
758 width &= 0x7FFF;
759 }
760 f << "width(unit?)=" << width;
761
762 ascii().addPos(pos);
763 ascii().addNote(f.str().c_str());
764
765 return true;
766 }
767
readMsWorksPageBreak()768 bool WKS4Spreadsheet::readMsWorksPageBreak()
769 {
770 libwps::DebugStream f;
771 long pos = m_input->tell();
772 long type = libwps::read16(m_input);
773
774 if (type != 0x5413)
775 {
776 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksPageBreak: not a pgbreak zone\n"));
777 return false;
778 }
779 long sz = libwps::readU16(m_input);
780 if (sz < 2)
781 {
782 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksPageBreak: seems very short\n"));
783 ascii().addPos(pos);
784 ascii().addNote("Entries(PBRK)###");
785 return true;
786 }
787 int row = libwps::read16(m_input)+1;
788 m_state->getActualSheet().m_rowPageBreaksList.push_back(row);
789
790 f << "Entries(PBRK): row="<< row;
791 if (sz != 2) ascii().addDelimiter(m_input->tell(),'#');
792
793 ascii().addPos(pos);
794 ascii().addNote(f.str().c_str());
795 return true;
796 }
797
readMsWorksStyle()798 bool WKS4Spreadsheet::readMsWorksStyle()
799 {
800 libwps::DebugStream f;
801
802 long pos = m_input->tell();
803 long type = libwps::read16(m_input);
804 if (type != 0x545a)
805 {
806 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksStyle: not a style property\n"));
807 return false;
808 }
809 long sz = libwps::readU16(m_input);
810 long endPos=pos+4+sz;
811 if (sz < 8)
812 {
813 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksStyle: style property is too short\n"));
814 return false;
815 }
816
817 // win3 has 8 bytes while last version is at least 10 bytes
818 int fl[6];
819 for (int i=0; i<2; ++i) fl[i] = libwps::readU8(m_input);
820 for (int i=0; i<3; ++i) fl[2+i] = libwps::readU16(m_input);
821 fl[5]= sz>=10 ? libwps::readU16(m_input) : 0;
822
823 WKS4SpreadsheetInternal::Style style(m_mainParser.getDefaultFontType());
824 if (!m_mainParser.getFont(fl[3], style.m_fileFont, style.m_fontType))
825 {
826 f << ",#fontId = " << fl[3];
827
828 static bool first = true;
829 if (first)
830 {
831 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksStyle: can not find a font\n"));
832 first = false;
833 }
834 }
835 fl[3]=0;
836
837
838 if (fl[1] & 0x1C)
839 {
840 switch ((fl[1] & 0x1C) >> 2)
841 {
842 case 0:
843 break;
844 case 1:
845 case 4: // left rellenar
846 style.setHAlignment(WPSCell::HALIGN_LEFT);
847 break;
848 case 5: // center arround cell
849 f << ",center[between cell]";
850 WPS_FALLTHROUGH;
851 case 2:
852 style.setHAlignment(WPSCell::HALIGN_CENTER);
853 break;
854 case 3:
855 style.setHAlignment(WPSCell::HALIGN_RIGHT);
856 break;
857 default:
858 f << ",#align=" << ((fl[1] & 0x1C) >> 2);
859 break;
860 }
861 fl[1] &= 0xE3;
862 }
863 switch ((fl[0]&0xf))
864 {
865 case 0:
866 style.setFormat(WPSCell::F_NUMBER,1);
867 break;
868 case 1:
869 style.setFormat(WPSCell::F_NUMBER,2);
870 break;
871 case 2:
872 style.setFormat(WPSCell::F_NUMBER,4);
873 break;
874 case 3:
875 style.setFormat(WPSCell::F_NUMBER,3);
876 break;
877 case 4:
878 style.setFormat(WPSCell::F_NUMBER,5);
879 break;
880 case 5:
881 {
882 int wh=(fl[0]>>5);
883 switch (wh)
884 {
885 case 0:
886 style.setFormat(WPSCell::F_TEXT);
887 break;
888 case 1:
889 style.setFormat(WPSCell::F_BOOLEAN);
890 break;
891 case 2:
892 case 3:
893 case 4:
894 case 5:
895 {
896 char const *format[] = { "%H:%M%p", "%I:%M:%S%p", "%H:%M", "%H:%M:%S" };
897 style.setDTFormat(WPSCell::F_TIME, format[wh-2]);
898 break;
899 }
900 default:
901 f << "#type=" << std::hex << fl[0] << std::dec << ",";
902 break;
903 }
904 break;
905 }
906 case 6:
907 {
908 char const *format[] = { "%m/%d/%Y", "%d %B %Y", "%m/%Y", "%B %Y", "%m/%d", "%d %B", "%m/%d/%y:6"/*TODO*/, "%B"};
909 style.setDTFormat(WPSCell::F_DATE, format[int(fl[0]>>5)]);
910 fl[0] &= 0x18;
911 break;
912 }
913 case 0xa:
914 style.setFormat(WPSCell::F_NUMBER, 6);
915 break;
916 case 0xc:
917 style.setFormat(WPSCell::F_NUMBER, 7);
918 break;
919 case 0xd:
920 style.setFormat(WPSCell::F_NUMBER, 4);
921 f << "negative[inRed],";
922 break;
923 default:
924 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksStyle: find unknown format %d\n", (fl[0]&0xF)));
925 f << ", ##type=" << std::hex << (fl[0]&0xf) << std::dec << ",";
926 break;
927 }
928 fl[0] &= 0xF0;
929
930 if (style.getFormat() == WPSCell::F_NUMBER)
931 {
932 int digits = 0;
933 if (fl[1] &= 0x1)
934 {
935 digits = 8;
936 fl[1] &= 2;
937 }
938 digits += (fl[0]>>5);
939 fl[0] &= 0x18;
940 style.setDigits(digits);
941 }
942 if (fl[1]&0x20) f << "ajust[text],";
943 switch (fl[1]>>6)
944 {
945 case 0:
946 style.setVAlignment(WPSCellFormat::VALIGN_BOTTOM);
947 break;
948 case 1:
949 style.setVAlignment(WPSCellFormat::VALIGN_CENTER);
950 break;
951 case 2:
952 style.setVAlignment(WPSCellFormat::VALIGN_TOP);
953 break;
954 default:
955 f << "##vAlign=3,";
956 break;
957 }
958 fl[1] &= 0x10; // 10
959 //
960 // the color
961 //
962
963 WPSColor frontColor=WPSColor::black();
964 int colorId=(fl[4]>>5)&0xF;
965 if (colorId && m_mainParser.getColor(colorId,frontColor)) // primary color
966 f << "primColor=" << std::hex << frontColor << std::dec << ",";
967
968 WPSColor backColor=WPSColor::white();
969 colorId=(fl[4]>>9)&0xF;
970 if (colorId && m_mainParser.getColor(colorId,backColor)) // secondary color
971 f << "secColor=" << std::hex << backColor << std::dec << ",";
972
973 int pat= (fl[4]&0xf);
974 if (pat==15) f << "###pat15,";
975 else if (pat)
976 {
977 static float const patternPercent[]= {0, 1.f, 0.5f, 0.25f, 0.2f, 0.2f, 0.2f, 0.5f, 0.5f, 0.5f, 0.5f, 0.8f, 0.2f, 0.8f, 0.2f};
978 float percent=patternPercent[pat];
979 f << "patPercent=" << percent << ",";
980 style.setBackgroundColor(WPSColor::barycenter(percent,frontColor, 1.0f-percent, backColor));
981 }
982 fl[4]&=0xE010;
983
984 //
985 // Border
986 //
987 for (int b=0, decal=0; b<4; b++, decal+=4)
988 {
989 int const wh[]= {WPSBorder::LeftBit, WPSBorder::TopBit, WPSBorder::RightBit, WPSBorder::BottomBit};
990 int bType=(fl[2]>>decal)&0xf;
991 if (bType==0) continue;
992 WPSBorder border;
993 switch (bType&0x7)
994 {
995 case 1:
996 break;
997 case 2:
998 border.m_width=2;
999 break;
1000 case 3:
1001 border.m_style = WPSBorder::LargeDot;
1002 break;
1003 case 4:
1004 border.m_style = WPSBorder::Dash;
1005 break;
1006 case 5:
1007 border.m_type = WPSBorder::Double;
1008 break;
1009 case 6:
1010 border.m_style = WPSBorder::Dot;
1011 break;
1012 case 7:
1013 border.m_width=3;
1014 break;
1015 default:
1016 break;
1017 }
1018 if ((fl[5]>>decal)&0xf)
1019 m_mainParser.getColor(((fl[5]>>decal)&0xf), border.m_color);
1020 style.setBorders(wh[b], border);
1021 if (bType&0x8) f << "bType[" << b << "]&8,";
1022 }
1023 fl[2] = fl[5] = 0;
1024
1025 for (int i = 0; i < 6; i++) style.m_unknFlags[i] = fl[i];
1026 style.m_extra = f.str();
1027
1028 /* end of parsing */
1029 f.str("");
1030 f << "Entries(Style):Style" << m_state->m_styleManager.size() << "," << style;
1031 m_state->m_styleManager.add(style, false);
1032 ascii().addPos(pos);
1033 ascii().addNote(f.str().c_str());
1034 if (m_input->tell()!=endPos) ascii().addDelimiter(m_input->tell(),'#');
1035 return true;
1036 }
1037
1038 ////////////////////////////////////////////////////////////
1039 // MsWorks (only in dos)
1040 ////////////////////////////////////////////////////////////
1041
readMsWorksDOSCellProperty()1042 bool WKS4Spreadsheet::readMsWorksDOSCellProperty()
1043 {
1044 libwps::DebugStream f;
1045 long pos = m_input->tell();
1046 long type = libwps::read16(m_input);
1047 if (type != 0x5402)
1048 {
1049 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksDOSCellProperty: not a cell property\n"));
1050 return false;
1051 }
1052 long sz = libwps::readU16(m_input);
1053 if (sz < 2)
1054 {
1055 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksDOSCellProperty: cell property is too short\n"));
1056 return false;
1057 }
1058
1059 f << "Entries(CellDosProperty):";
1060 auto *cell = m_state->getActualSheet().getLastCell();
1061 if (!cell)
1062 {
1063 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksDOSCellProperty: can not find the cell\n"));
1064 f << "###";
1065 ascii().addPos(pos);
1066 ascii().addNote(f.str().c_str());
1067 return true;
1068 }
1069 WKS4SpreadsheetInternal::Style style(m_mainParser.getDefaultFontType());
1070 if (cell->m_styleId>=0)
1071 {
1072 if (!m_state->m_styleManager.get(cell->m_styleId, style))
1073 f << ",###style";
1074 }
1075
1076 int fl[2];
1077 for (int &i : fl)
1078 i = libwps::readU8(m_input);
1079
1080 switch ((fl[0] & 0x7))
1081 {
1082 case 0x5:
1083 if (cell->m_content.m_contentType == WKSContentListener::CellContent::C_TEXT) fl[0] &= 0xF8;
1084 break;
1085 case 0x6:
1086 if (cell->m_content.m_contentType == WKSContentListener::CellContent::C_NUMBER) fl[0] &= 0xF8;
1087 break;
1088 case 0x7:
1089 if (cell->m_content.m_contentType == WKSContentListener::CellContent::C_FORMULA) fl[0] &= 0xF8;
1090 break;
1091 default:
1092 break;
1093 }
1094 WPSCell::FormatType newForm = WPSCell::F_UNKNOWN;
1095 int subForm = 0;
1096 switch (fl[0]>>5)
1097 {
1098 case 0:
1099 newForm = WPSCell::F_NUMBER;
1100 subForm=1;
1101 break;
1102 case 1:
1103 newForm = WPSCell::F_NUMBER;
1104 subForm=2;
1105 break;
1106 case 2:
1107 newForm = WPSCell::F_NUMBER;
1108 subForm=4;
1109 break;
1110 case 3:
1111 newForm = WPSCell::F_NUMBER;
1112 subForm=3;
1113 break;
1114 case 4:
1115 newForm = WPSCell::F_NUMBER;
1116 subForm=5;
1117 break;
1118 case 5: // normal (or time)
1119 {
1120 int wh=(fl[1]>>2)&0x7;
1121 if (wh>=2 && wh <=5)
1122 {
1123 newForm = WPSCell::F_TIME;
1124 subForm=wh-2;
1125 fl[1]&=0xE3;
1126 }
1127 break;
1128 }
1129 case 6:
1130 newForm = WPSCell::F_DATE;
1131 subForm=(fl[1]>>2)&0x7;
1132 fl[1]&=0xE3;
1133 break;
1134 default:
1135 f << "fl0[high]=" << (fl[0]>>5) << ",";
1136 break;
1137 }
1138 fl[0] &= 0x1F;
1139 if (newForm != WPSCell::F_UNKNOWN &&
1140 (newForm != style.getFormat() || subForm != style.getSubFormat()))
1141 {
1142 if (style.getFormat() == WPSCell::F_UNKNOWN)
1143 ;
1144 else if (newForm != style.getFormat())
1145 f << "#prevForm = " << int(style.getFormat());
1146 else
1147 f << "#prevSubForm = " << style.getSubFormat();
1148 if (newForm==WPSCell::F_DATE && subForm>=0 && subForm<=7)
1149 {
1150 char const *wh[]= { "%m/%d/%y", "%B %d, %Y", "%m/%y", "%B %Y", "%m/%d", "%B %d", "%m", "%B"};
1151 style.setDTFormat(newForm, wh[subForm]);
1152 }
1153 else if (newForm==WPSCell::F_TIME && subForm>=0 && subForm<=3)
1154 {
1155 char const *wh[]= { "%I:%M%p", "%I:%M:%S%p", "%H:%M", "%H:%M:%S"};
1156 style.setDTFormat(newForm, wh[subForm]);
1157 }
1158 else
1159 style.setFormat(newForm, subForm);
1160 }
1161
1162 uint32_t fflags = 0; // checkme
1163 if (fl[0] & 0x10)
1164 {
1165 fflags |= WPS_ITALICS_BIT;
1166 fl[0] &= 0xEF;
1167 }
1168 if (fl[1] & 0x20)
1169 {
1170 fflags |= WPS_BOLD_BIT;
1171 fl[1] &= 0xDF;
1172 }
1173 if (fl[1] & 0x40)
1174 {
1175 fflags |= WPS_UNDERLINE_BIT;
1176 fl[1] &= 0xBF;
1177 }
1178 style.m_fileFont.m_attributes=fflags;
1179 switch (fl[1]&3)
1180 {
1181 case 1:
1182 style.setHAlignment(WPSCell::HALIGN_LEFT);
1183 break;
1184 case 2:
1185 style.setHAlignment(WPSCell::HALIGN_CENTER);
1186 break;
1187 case 3:
1188 style.setHAlignment(WPSCell::HALIGN_RIGHT);
1189 break;
1190 case 0:
1191 default:
1192 break;
1193 }
1194 fl[1] &= 0xFC;
1195 style.m_unknFlags[0] = fl[0];
1196 style.m_unknFlags[1] = fl[1];
1197 int id = m_state->m_styleManager.add(style, true);
1198 cell->m_styleId=id;
1199
1200 #ifdef DEBUG_WITH_FILES
1201 f << *cell;
1202 m_state->m_styleManager.print(id, f);
1203 #endif
1204 if (sz > 2) ascii().addDelimiter(pos+6, '#');
1205
1206 ascii().addPos(pos);
1207 ascii().addNote(f.str().c_str());
1208 return true;
1209 }
1210
readMsWorksDOSFieldProperty()1211 bool WKS4Spreadsheet::readMsWorksDOSFieldProperty()
1212 {
1213 libwps::DebugStream f;
1214 long pos = m_input->tell();
1215 long type = libwps::read16(m_input);
1216 if (type != 0x5406)
1217 {
1218 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksDOSFieldProperty: not a cell property\n"));
1219 return false;
1220 }
1221 long sz = libwps::readU16(m_input);
1222 if (sz < 4)
1223 {
1224 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksDOSFieldProperty: cell property is too short\n"));
1225 return false;
1226 }
1227
1228 f << "Entries(FieldDosProperty):";
1229 f << "id=" << libwps::readU16(m_input) << ",";
1230 WKS4SpreadsheetInternal::Style style(m_mainParser.getDefaultFontType());
1231 int fl[2];
1232 for (int &i : fl)
1233 i = libwps::readU8(m_input);
1234
1235 switch ((fl[0] & 0x7))
1236 {
1237 case 0x5:
1238 f << "text,";
1239 fl[0] &= 0xF8;
1240 break;
1241 case 0x6:
1242 f << "number,";
1243 fl[0] &= 0xF8;
1244 break;
1245 case 0x7:
1246 f << "formula,";
1247 fl[0] &= 0xF8;
1248 break;
1249 default:
1250 break;
1251 }
1252 if (fl[0]>>5) f << "subForm=" << (fl[0]>>5) << ",";
1253 fl[0] &= 0x1F;
1254 uint32_t fflags = 0; // checkme
1255 if (fl[0] & 0x10)
1256 {
1257 fflags |= WPS_ITALICS_BIT;
1258 fl[0] &= 0xEF;
1259 }
1260 if (fl[1] & 0x20)
1261 {
1262 fflags |= WPS_BOLD_BIT;
1263 fl[1] &= 0xDF;
1264 }
1265 if (fl[1] & 0x40)
1266 {
1267 fflags |= WPS_UNDERLINE_BIT;
1268 fl[1] &= 0xBF;
1269 }
1270 style.m_fileFont.m_attributes=fflags;
1271 switch (fl[1]&3)
1272 {
1273 case 1:
1274 style.setHAlignment(WPSCell::HALIGN_LEFT);
1275 break;
1276 case 2:
1277 style.setHAlignment(WPSCell::HALIGN_CENTER);
1278 break;
1279 case 3:
1280 style.setHAlignment(WPSCell::HALIGN_RIGHT);
1281 break;
1282 case 0:
1283 default:
1284 break;
1285 }
1286 fl[1] &= 0xFC;
1287 style.m_unknFlags[0] = fl[0];
1288 style.m_unknFlags[1] = fl[1];
1289 f << style;
1290 if (sz > 4) ascii().addDelimiter(pos+8, '|');
1291
1292 ascii().addPos(pos);
1293 ascii().addNote(f.str().c_str());
1294 return true;
1295
1296 }
1297
readMsWorksDOSCellExtraProperty()1298 bool WKS4Spreadsheet::readMsWorksDOSCellExtraProperty()
1299 {
1300 libwps::DebugStream f;
1301 long pos = m_input->tell();
1302 long type = libwps::read16(m_input);
1303 if (type != 0x541c)
1304 {
1305 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksDOSCellExtraProperty: not a cell property\n"));
1306 return false;
1307 }
1308 long sz = libwps::readU16(m_input);
1309 if (sz < 8)
1310 {
1311 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksDOSCellExtraProperty: cell property is too short\n"));
1312 return false;
1313 }
1314
1315 f << "Entries(CellDosExtra):";
1316 auto *cell = m_state->getActualSheet().getLastCell();
1317 if (!cell)
1318 {
1319 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksDOSCellExtraProperty: can not find the cell\n"));
1320 f << "###";
1321 ascii().addPos(pos);
1322 ascii().addNote(f.str().c_str());
1323 return true;
1324 }
1325 WKS4SpreadsheetInternal::Style style(m_mainParser.getDefaultFontType());
1326 if (cell->m_styleId>=0)
1327 {
1328 if (!m_state->m_styleManager.get(cell->m_styleId, style))
1329 f << ",###style";
1330 }
1331 int values[8]; // f0: [02468ac][0157], f1=0..9, f2=0..a, f3=[0-6a][0156], f4=0|9|d|40|80|c0, f5=0|ed
1332 for (int &value : values)
1333 value=int(libwps::readU8(m_input));
1334 if (style.getFormat()==WPSCell::F_NUMBER) // not sure
1335 {
1336 if (values[2]==5)
1337 {
1338 style.setFormat(WPSCell::F_NUMBER,7);
1339 values[2]=0;
1340 }
1341 else if (values[2]==0xa)
1342 {
1343 style.setFormat(WPSCell::F_NUMBER,6);
1344 style.setDigits(((values[3]>>3)&0x7)+1);
1345 values[2]=0;
1346 values[3]&=0xC7;
1347 }
1348 }
1349 WPSColor color;
1350 if ((values[6]&0xE0) && m_mainParser.getColor(values[6]>>5,color))
1351 {
1352 style.m_fileFont.m_color=color;
1353 f << "fontColor=" << std::hex << color << std::dec << ",";
1354 values[6] &= 0x1F;
1355 }
1356 for (int i=0; i < 8; ++i)
1357 {
1358 if (values[i])
1359 f << "f" << i << "=" << std::hex << values[i] << std::dec << ",";
1360 }
1361
1362 int id = m_state->m_styleManager.add(style, true);
1363 cell->m_styleId=id;
1364
1365 ascii().addPos(pos);
1366 ascii().addNote(f.str().c_str());
1367 if (m_input->tell()!=pos+4+sz) ascii().addDelimiter(m_input->tell(),'#');
1368
1369 return false;
1370 }
1371
readMsWorksDOSPageBreak()1372 bool WKS4Spreadsheet::readMsWorksDOSPageBreak()
1373 {
1374 libwps::DebugStream f;
1375 long pos = m_input->tell();
1376 long type = libwps::read16(m_input);
1377
1378 if (type != 0x5427)
1379 {
1380 WPS_DEBUG_MSG(("WKS4Spreadsheet::readMsWorksDOSPageBreak: not a pgbreak zone\n"));
1381 return false;
1382 }
1383 long sz = libwps::readU16(m_input);
1384 if (!sz)
1385 {
1386 ascii().addPos(pos);
1387 ascii().addNote("_");
1388 return true;
1389 }
1390 int row = int(libwps::read8(m_input))+1;
1391 m_state->getActualSheet().m_rowPageBreaksList.push_back(row);
1392
1393 f << "Entries(PBRK): row="<< row;
1394 if (sz != 1) ascii().addDelimiter(m_input->tell(),'#');
1395
1396 ascii().addPos(pos);
1397 ascii().addNote(f.str().c_str());
1398 return true;
1399 }
1400
1401 ////////////////////////////////////////////////////////////
1402 // general
1403 ////////////////////////////////////////////////////////////
1404
readCell()1405 bool WKS4Spreadsheet::readCell()
1406 {
1407 libwps::DebugStream f;
1408
1409 long pos = m_input->tell();
1410 long type = libwps::read16(m_input);
1411 if ((type != 0x545b) && (type!=0x36) && (type < 0xc || type > 0x10))
1412 {
1413 WPS_DEBUG_MSG(("WKS4Spreadsheet::readCell: not a cell property\n"));
1414 return false;
1415 }
1416 long sz = libwps::readU16(m_input);
1417 long endPos = pos+4+sz;
1418
1419 if (sz < 5)
1420 {
1421 WPS_DEBUG_MSG(("WKS4Spreadsheet::readCell: cell def is too short\n"));
1422 return false;
1423 }
1424 int const vers=version();
1425 bool dosFile = vers < 3;
1426 int format = 0xFF;
1427 if (dosFile)
1428 format = int(libwps::readU8(m_input));
1429 int cellPos[2];
1430 cellPos[0]=int(libwps::readU8(m_input));
1431 auto sheetId=int(libwps::readU8(m_input)); // maybe a file id?
1432 cellPos[1]=int(libwps::read16(m_input));
1433 if (cellPos[1] < 0)
1434 {
1435 WPS_DEBUG_MSG(("WKS4Spreadsheet::readCell: cell pos is bad\n"));
1436 return false;
1437 }
1438 if (sheetId)
1439 {
1440 WPS_DEBUG_MSG(("WKS4Spreadsheet::readCell: find unexpected sheet id\n"));
1441 f << "###sheet[id]=" << sheetId << ",";
1442 }
1443
1444 auto &cell=m_state->getActualSheet().getCell(Vec2i(cellPos[0],cellPos[1]));
1445 if (!dosFile)
1446 cell.m_styleId = int(libwps::read16(m_input));
1447
1448 if (type & 0xFF00)
1449 {
1450 if (dosFile)
1451 {
1452 static bool first = true;
1453 if (first)
1454 {
1455 first = false;
1456 WPS_DEBUG_MSG(("WKS4Spreadsheet::readCell: find type=%ld in dos version\n", type));
1457 }
1458 f << "###";
1459 }
1460 f << "win[version],";
1461 }
1462
1463 long dataPos = m_input->tell();
1464 auto dataSz = int(endPos-dataPos);
1465
1466 bool ok = true;
1467 switch (type)
1468 {
1469 case 12:
1470 {
1471 if (dataSz == 0)
1472 {
1473 cell.m_content.m_contentType=WKSContentListener::CellContent::C_NONE;
1474 break;
1475 }
1476 ok = false;
1477 break;
1478 }
1479 case 13:
1480 {
1481 if (dataSz == 2)
1482 {
1483 cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
1484 cell.m_content.setValue(libwps::read16(m_input));
1485 break;
1486 }
1487 ok = false;
1488 break;
1489 }
1490 case 14:
1491 {
1492 double val;
1493 bool isNaN;
1494 if (dataSz == 8 && libwps::readDouble8(m_input, val, isNaN))
1495 {
1496 cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
1497 cell.m_content.setValue(val);
1498 break;
1499 }
1500 ok = false;
1501 break;
1502 }
1503 case 15:
1504 case 0x36: // continue text zone
1505 {
1506 if (type==0x36 && cell.m_content.m_contentType!=WKSContentListener::CellContent::C_TEXT)
1507 {
1508 WPS_DEBUG_MSG(("WKS4Spreadsheet::readCell: oops, find zone 0x36 but not zone 15\n"));
1509 f << "###";
1510 break;
1511 }
1512 cell.m_content.m_contentType=WKSContentListener::CellContent::C_TEXT;
1513 long begText=m_input->tell(), endText=begText+dataSz;
1514 std::string s("");
1515 for (int i = 0; i < dataSz; i++)
1516 {
1517 auto c = char(libwps::read8(m_input));
1518 if (c=='\0')
1519 {
1520 endText=m_input->tell()-1;
1521 if (i == dataSz-1) break;
1522 WPS_DEBUG_MSG(("WKS4Spreadsheet::readCell: cell content seems bad\n"));
1523 f << "###";
1524 break;
1525 }
1526 s += c;
1527 }
1528 f << s << ",";
1529 if (dosFile && s.length())
1530 {
1531 if (s[0]=='\'') cell.m_hAlignement=WPSCellFormat::HALIGN_DEFAULT;
1532 else if (s[0]=='\\') cell.m_hAlignement=WPSCellFormat::HALIGN_LEFT;
1533 else if (s[0]=='^') cell.m_hAlignement=WPSCellFormat::HALIGN_CENTER;
1534 else if (s[0]=='\"') cell.m_hAlignement=WPSCellFormat::HALIGN_RIGHT;
1535 else
1536 --begText;
1537 ++begText;
1538 }
1539 if (type==15)
1540 {
1541 cell.m_content.m_textEntry.setBegin(begText);
1542 cell.m_content.m_textEntry.setEnd(endText);
1543 }
1544 else
1545 {
1546 WPSEntry newEntry;
1547 newEntry.setBegin(begText);
1548 newEntry.setEnd(endText);
1549 cell.m_extraTextEntryList.push_back(newEntry);
1550 }
1551 break;
1552 }
1553 case 16:
1554 {
1555 double val;
1556 bool isNaN;
1557 if (dataSz >= 8 && libwps::readDouble8(m_input, val, isNaN))
1558 {
1559 cell.m_content.m_contentType=WKSContentListener::CellContent::C_FORMULA;
1560 cell.m_content.setValue(val);
1561 std::string error;
1562 if (!readFormula(endPos, cell.position(), cell.m_content.m_formula, error))
1563 {
1564 cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
1565 ascii().addDelimiter(m_input->tell()-1, '#');
1566 }
1567 if (error.length()) f << error;
1568 break;
1569 }
1570 ok = false;
1571 break;
1572 }
1573 case 0x545b: // CHECKME: sometime we do not read the good number
1574 {
1575 double val;
1576 bool isNaN;
1577 if (dataSz == 4 && libwps::readDouble4(m_input, val, isNaN))
1578 {
1579 cell.m_content.m_contentType=WKSContentListener::CellContent::C_NUMBER;
1580 cell.m_content.setValue(val);
1581 break;
1582 }
1583 ok = false;
1584 break;
1585 }
1586 default:
1587 WPS_DEBUG_MSG(("WKS4Spreadsheet::readCell: unknown type=%ld\n", type));
1588 ok = false;
1589 break;
1590 }
1591 if (!ok) ascii().addDelimiter(dataPos, '#');
1592
1593 if (dosFile)
1594 {
1595 WKS4SpreadsheetInternal::Style style(m_mainParser.getDefaultFontType());
1596 switch (cell.m_content.m_contentType)
1597 {
1598 case WKSContentListener::CellContent::C_NONE:
1599 break;
1600 case WKSContentListener::CellContent::C_TEXT:
1601 style.setFormat(WPSCell::F_TEXT);
1602 break;
1603 case WKSContentListener::CellContent::C_NUMBER:
1604 case WKSContentListener::CellContent::C_FORMULA:
1605 case WKSContentListener::CellContent::C_UNKNOWN:
1606 default:
1607 style.setFormat(WPSCell::F_NUMBER);
1608 break;
1609 }
1610
1611 int styleID = (format>>4) & 0x7;
1612 switch (styleID)
1613 {
1614 case 0:
1615 case 1:
1616 case 2:
1617 case 3:
1618 case 4:
1619 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1620 break;
1621 if (styleID == 2) styleID = 3;
1622 else if (styleID == 3) styleID = 2;
1623 style.setFormat(WPSCell::F_NUMBER,1+styleID);
1624 break;
1625 // unsure find in some Quattro Pro File
1626 case 5:
1627 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1628 break;
1629 switch (format&0xF)
1630 {
1631 case 4: // a date but no sure which format is good
1632 style.setDTFormat(WPSCell::F_DATE, "%m/%d/%y");
1633 break;
1634 case 5:
1635 style.setDTFormat(WPSCell::F_DATE, "%m/%d");
1636 break;
1637 default:
1638 WPS_DEBUG_MSG(("WKS4Spreadsheet::readCell::updateFormat: unknown format %x\n", unsigned(format)));
1639 break;
1640 }
1641 break;
1642 // LotusSymphony format
1643 case 0x7:
1644 switch (format&0xF)
1645 {
1646 case 0: // +/- : kind of bool
1647 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1648 break;
1649 style.setFormat(WPSCell::F_BOOLEAN);
1650 break;
1651 case 1:
1652 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1653 break;
1654 style.setFormat(WPSCell::F_NUMBER, 0);
1655 break;
1656 case 2:
1657 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1658 break;
1659 style.setDTFormat(WPSCell::F_DATE, "%d %B %y");
1660 break;
1661 case 3:
1662 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1663 break;
1664 style.setDTFormat(WPSCell::F_DATE, "%d %B");
1665 break;
1666 case 4:
1667 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1668 break;
1669 style.setDTFormat(WPSCell::F_DATE, "%B %y");
1670 break;
1671 case 5:
1672 if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_TEXT)
1673 break;
1674 style.setFormat(WPSCell::F_TEXT);
1675 break;
1676 case 6:
1677 if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_TEXT)
1678 break;
1679 style.setFormat(WPSCell::F_TEXT);
1680 style.m_fileFont.m_attributes |= WPS_HIDDEN_BIT;
1681 break;
1682 case 7:
1683 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1684 break;
1685 style.setDTFormat(WPSCell::F_TIME, "%I:%M:%S%p");
1686 break;
1687 case 8:
1688 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1689 break;
1690 style.setDTFormat(WPSCell::F_TIME, "%I:%M%p");
1691 break;
1692 case 9:
1693 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1694 break;
1695 style.setDTFormat(WPSCell::F_DATE, "%m/%d/%y");
1696 break;
1697 case 0xa:
1698 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1699 break;
1700 style.setDTFormat(WPSCell::F_DATE, "%m/%d");
1701 break;
1702 case 0xb:
1703 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1704 break;
1705 style.setDTFormat(WPSCell::F_TIME, "%H:%M:%S");
1706 break;
1707 case 0xc:
1708 if (cell.m_content.m_contentType==WKSContentListener::CellContent::C_TEXT)
1709 break;
1710 style.setDTFormat(WPSCell::F_TIME, "%H:%M");
1711 break;
1712 case 0xd:
1713 if (cell.m_content.m_contentType!=WKSContentListener::CellContent::C_TEXT)
1714 break;
1715 style.setFormat(WPSCell::F_TEXT);
1716 break;
1717 case 0xf: // automatic
1718 break;
1719 default:
1720 break;
1721 }
1722 break;
1723 default:
1724 f << "type=" << styleID << ",";
1725 }
1726 if ((format&0x80)==0) f << "protected=0,";
1727 style.setDigits(format & 0xF);
1728 cell.m_styleId=m_state->m_styleManager.add(style, true);
1729 }
1730 m_input->seek(pos+sz, librevenge::RVNG_SEEK_SET);
1731
1732 std::string extra=f.str();
1733 f.str("");
1734 f << "Entries(CellContent):" << cell << "," << extra;
1735 #ifdef DEBUG_WITH_FILES
1736 m_state->m_styleManager.print(cell.m_styleId, f);
1737 #endif
1738
1739 ascii().addPos(pos);
1740 ascii().addNote(f.str().c_str());
1741
1742 return true;
1743 }
1744
readCellFormulaResult()1745 bool WKS4Spreadsheet::readCellFormulaResult()
1746 {
1747 libwps::DebugStream f;
1748
1749 long pos = m_input->tell();
1750 long type = libwps::read16(m_input);
1751 if (type != 0x33)
1752 {
1753 WPS_DEBUG_MSG(("WKS4Spreadsheet::readCellFormulaResult: not a cell property\n"));
1754 return false;
1755 }
1756 long sz = libwps::readU16(m_input);
1757 if (sz<6)
1758 {
1759 WPS_DEBUG_MSG(("WKS4Spreadsheet::readCellFormulaResult: the zone seems to short\n"));
1760 return false;
1761 }
1762 long endPos = pos+4+sz;
1763
1764 bool dosFile = version() < 3;
1765 // skip format for dosFile
1766 m_input->seek(dosFile ? pos+5 : pos+4, librevenge::RVNG_SEEK_SET);
1767 f << "CellContent[res]:";
1768 int dim[2];
1769 for (int &i : dim) i=int(libwps::readU16(m_input));
1770 f << "C" << dim[0] << "x" << dim[1] << ",";
1771 // skip format for windows file
1772 if (!dosFile) m_input->seek(2, librevenge::RVNG_SEEK_CUR);
1773 auto sSz=int(endPos-m_input->tell());
1774 librevenge::RVNGString text;
1775 if (!m_mainParser.readCString(text,sSz))
1776 f << "##text,";
1777 else if (!text.empty())
1778 f << text.cstr() << ",";
1779 if (m_input->tell()!=endPos)
1780 ascii().addDelimiter(m_input->tell(),'|');
1781 ascii().addPos(pos);
1782 ascii().addNote(f.str().c_str());
1783 m_input->seek(endPos, librevenge::RVNG_SEEK_SET);
1784 return true;
1785 }
1786
1787 ////////////////////////////////////////////////////////////
1788 // Data
1789 ////////////////////////////////////////////////////////////
readCell(Vec2i actPos,WKSContentListener::FormulaInstruction & instr)1790 bool WKS4Spreadsheet::readCell
1791 (Vec2i actPos, WKSContentListener::FormulaInstruction &instr)
1792 {
1793 instr=WKSContentListener::FormulaInstruction();
1794 instr.m_type=WKSContentListener::FormulaInstruction::F_Cell;
1795 bool ok = true;
1796 int pos[2];
1797 bool absolute[2] = { true, true};
1798 for (int dim = 0; dim < 2; dim++)
1799 {
1800 auto val = int(libwps::readU16(m_input));
1801 if ((val & 0x8000) == 0); // absolue value ?
1802 else
1803 {
1804 // relative
1805 // wb1: maximum row=0x2000, maximum col=0x100
1806 // wks dos (v3) maximum row=0x4000, maximum col=0x100
1807 // wdb maximum number of data ?
1808 if (version()==1 && dim==0)
1809 {
1810 val &= 0xFF;
1811 if ((val & 0x80) && val+actPos[dim] >= 0x100)
1812 // sometimes this value is odd, so do not generate errors here
1813 val = val - 0x100;
1814 }
1815 else
1816 {
1817 // 0x400 for old file(unsure), ie. find many problematic files on
1818 // the web, so maybe 0x4000 is ok and these files are
1819 // problematic
1820 int const maxVal= (dim==1 || m_mainParser.creator()==libwps::WPS_LOTUS) ? 0x2000 : version()==1 ? 0x400 : 0x4000;
1821 val &= (2*maxVal-1);
1822 if (val & maxVal) val = val - 2*maxVal;
1823 if (val+actPos[dim]>=maxVal) val-=maxVal;
1824 }
1825 val += actPos[dim];
1826 absolute[dim] = false;
1827 }
1828 pos[dim] = val;
1829 }
1830
1831 if (pos[0] < 0 || pos[1] < 0)
1832 {
1833 std::stringstream f;
1834 f << "###[" << pos[1] << "," << pos[0] << "]";
1835 if (ok)
1836 {
1837 WPS_DEBUG_MSG(("WKS4Spreadsheet::readCell: can not read cell position\n"));
1838 }
1839 return false;
1840 }
1841 instr.m_position[0]=Vec2i(pos[0],pos[1]);
1842 instr.m_positionRelative[0]=Vec2b(!absolute[0],!absolute[1]);
1843 return ok;
1844 }
1845
1846 namespace WKS4SpreadsheetInternal
1847 {
1848 struct Functions
1849 {
1850 char const *m_name;
1851 int m_arity;
1852 };
1853
1854 static Functions const s_listFunctions[] =
1855 {
1856 { "", 0} /*SPEC: number*/, {"", 0}/*SPEC: cell*/, {"", 0}/*SPEC: cells*/, {"=", 1} /*SPEC: end of formula*/,
1857 { "(", 1} /* SPEC: () */, {"", 0}/*SPEC: number*/, { "", -2} /*SPEC: text*/, {"", -2}/*unused*/,
1858 { "-", 1}, {"+", 2}, {"-", 2}, {"*", 2},
1859 { "/", 2}, { "^", 2}, {"=", 2}, {"<>", 2},
1860
1861 { "<=", 2},{ ">=", 2},{ "<", 2},{ ">", 2},
1862 { "And", 2},{ "Or", 2}, { "Not", 1}, { "+", 1},
1863 { "&", 2}, { "", -2} /*unused*/,{ "", -2} /*unused*/,{ "", -2} /*unused*/,
1864 { "", -2} /*unused*/,{ "", -2} /*unused*/,{ "", -2} /*unused*/,{ "NA", 0} /*checkme*/,
1865
1866 { "NA", 0} /* Error*/,{ "Abs", 1},{ "Int", 1},{ "Sqrt", 1},
1867 { "Log10", 1},{ "Ln", 1},{ "Pi", 0},{ "Sin", 1},
1868 { "Cos", 1},{ "Tan", 1},{ "Atan2", 2},{ "Atan", 1},
1869 { "Asin", 1},{ "Acos", 1},{ "Exp", 1},{ "Mod", 2},
1870
1871 { "Choose", -1},{ "IsNa", 1},{ "IsError", 1},{ "False", 0},
1872 { "True", 0},{ "Rand", 0},{ "Date", 3},{ "Now", 0},
1873 { "PMT", 3} /*BAD*/,{ "PV", 3} /*BAD*/,{ "FV", 3} /*BAD*/,{ "IF", 3},
1874 { "Day", 1},{ "Month", 1},{ "Year", 1},{ "Round", 2},
1875
1876 { "Time", 3},{ "Hour", 1},{ "Minute", 1},{ "Second", 1},
1877 { "IsNumber", 1},{ "IsText", 1},{ "Len", 1},{ "Value", 1},
1878 { "Fixed", 2}, { "Mid", 3}, { "Char", 1},{ "Ascii", 1},
1879 { "Find", 3},{ "DateValue", 1} /*checkme*/,{ "TimeValue", 1} /*checkme*/,{ "CellPointer", 1} /*checkme*/,
1880
1881 { "Sum", -1},{ "Average", -1},{ "COUNT", -1},{ "Min", -1},
1882 { "Max", -1},{ "VLookUp", 3},{ "NPV", 2}, { "Var", -1},
1883 { "StDev", -1},{ "IRR", 2} /*BAD*/, { "HLookup", 3},{ "DSum", 3},
1884 { "DAvg", 3},{ "DCount", 3},{ "DMin", 3},{ "DMax", 3},
1885
1886 { "DVar", 3},{ "DStd", 3},{ "Index", 3}, { "Columns", 1},
1887 { "Rows", 1},{ "Rept", 2},{ "Upper", 1},{ "Lower", 1},
1888 { "Left", 2},{ "Right", 2},{ "Replace", 4}, { "Proper", 1},
1889 { "Cell", 1} /*checkme*/,{ "Trim", 1},{ "", -2} /*UNKN*/,{ "T", 1},
1890
1891 { "IsNonText", 1},{ "Exact", 2},{ "", -2} /*UNKN*/,{ "", 3} /*UNKN*/,
1892 { "Rate", 3} /*BAD*/,{ "TERM", 3}, { "CTERM", 3}, { "SLN", 3},
1893 { "SYD", 4},{ "DDB", 4} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
1894 { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
1895
1896 { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/, { "", -2} /*UNKN*/,
1897 { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
1898 { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
1899 { "", -2} /*UNKN*/,{ "And", -1},{ "Or", -1},{ "Not", 1},
1900
1901 };
1902 }
1903
readFormula(long endPos,Vec2i const & position,std::vector<WKSContentListener::FormulaInstruction> & formula,std::string & error)1904 bool WKS4Spreadsheet::readFormula(long endPos, Vec2i const &position,
1905 std::vector<WKSContentListener::FormulaInstruction> &formula, std::string &error)
1906 {
1907 int const vers=version();
1908 formula.resize(0);
1909 error = "";
1910 long pos = m_input->tell();
1911 if (endPos - pos < 2) return false;
1912 auto sz = int(libwps::readU16(m_input));
1913 if (endPos-pos-2 != sz) return false;
1914
1915 std::stringstream f;
1916 std::vector<std::vector<WKSContentListener::FormulaInstruction> > stack;
1917 bool ok = true;
1918 while (long(m_input->tell()) != endPos)
1919 {
1920 double val;
1921 bool isNaN;
1922 pos = m_input->tell();
1923 if (pos > endPos) return false;
1924 auto wh = int(libwps::readU8(m_input));
1925 int arity = 0;
1926 WKSContentListener::FormulaInstruction instr;
1927 switch (wh)
1928 {
1929 case 0x0:
1930 if (endPos-pos<9 || !libwps::readDouble8(m_input, val, isNaN))
1931 {
1932 f.str("");
1933 f << "###number";
1934 error=f.str();
1935 ok = false;
1936 break;
1937 }
1938 instr.m_type=WKSContentListener::FormulaInstruction::F_Double;
1939 instr.m_doubleValue=val;
1940 break;
1941 case 0x1:
1942 if (endPos-pos<5)
1943 {
1944 f.str("");
1945 f << "###cell short";
1946 error=f.str();
1947 ok = false;
1948 break;
1949 }
1950 ok = readCell(position, instr);
1951 break;
1952 case 0x2:
1953 {
1954 if (endPos-pos< (vers<=1 ? 7 : 9) || !readCell(position, instr))
1955 {
1956 f.str("");
1957 f << "###list cell short";
1958 error=f.str();
1959 ok = false;
1960 break;
1961 }
1962 WKSContentListener::FormulaInstruction instr2;
1963 if (!readCell(position, instr2))
1964 {
1965 ok = false;
1966 f.str("");
1967 f << "###list cell short(2)";
1968 error=f.str();
1969 break;
1970 }
1971 instr.m_type=WKSContentListener::FormulaInstruction::F_CellList;
1972 instr.m_position[1]=instr2.m_position[0];
1973 instr.m_positionRelative[1]=instr2.m_positionRelative[0];
1974 break;
1975 }
1976 case 0x5:
1977 instr.m_type=WKSContentListener::FormulaInstruction::F_Long;
1978 instr.m_longValue=long(libwps::read16(m_input));
1979 break;
1980 case 0x6:
1981 instr.m_type=WKSContentListener::FormulaInstruction::F_Text;
1982 while (!m_input->isEnd())
1983 {
1984 if (m_input->tell() >= endPos)
1985 {
1986 ok=false;
1987 break;
1988 }
1989 auto c = char(libwps::readU8(m_input));
1990 if (c==0) break;
1991 instr.m_content += c;
1992 }
1993 break;
1994 default:
1995 if (wh >= 0x90 || WKS4SpreadsheetInternal::s_listFunctions[wh].m_arity == -2)
1996 {
1997 f.str("");
1998 f << "##Funct" << std::hex << wh;
1999 error=f.str();
2000 ok = false;
2001 break;
2002 }
2003 instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
2004 instr.m_content=WKS4SpreadsheetInternal::s_listFunctions[wh].m_name;
2005 ok=!instr.m_content.empty();
2006 arity = WKS4SpreadsheetInternal::s_listFunctions[wh].m_arity;
2007 if (arity == -1) arity = int(libwps::read8(m_input));
2008 break;
2009 }
2010
2011 if (!ok) break;
2012 std::vector<WKSContentListener::FormulaInstruction> child;
2013 if (instr.m_type!=WKSContentListener::FormulaInstruction::F_Function)
2014 {
2015 child.push_back(instr);
2016 stack.push_back(child);
2017 continue;
2018 }
2019 size_t numElt = stack.size();
2020 if (int(numElt) < arity)
2021 {
2022 f.str("");
2023 f << instr.m_content << "[##" << arity << "]";
2024 error=f.str();
2025 ok = false;
2026 break;
2027 }
2028 //
2029 // first treat the special cases
2030 //
2031 if (arity==3 && instr.m_type==WKSContentListener::FormulaInstruction::F_Function && instr.m_content=="TERM")
2032 {
2033 // @TERM(pmt,pint,fv) -> NPER(pint,-pmt,pv=0,fv)
2034 auto pmt=stack[size_t(int(numElt)-3)];
2035 auto pint=stack[size_t(int(numElt)-2)];
2036 auto fv=stack[size_t(int(numElt)-1)];
2037
2038 stack.resize(size_t(++numElt));
2039 // pint
2040 stack[size_t(int(numElt)-4)]=pint;
2041 //-pmt
2042 auto &node=stack[size_t(int(numElt)-3)];
2043 instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
2044 instr.m_content="-";
2045 node.resize(0);
2046 node.push_back(instr);
2047 instr.m_content="(";
2048 node.push_back(instr);
2049 node.insert(node.end(), pmt.begin(), pmt.end());
2050 instr.m_content=")";
2051 node.push_back(instr);
2052 //pv=zero
2053 instr.m_type=WKSContentListener::FormulaInstruction::F_Long;
2054 instr.m_longValue=0;
2055 stack[size_t(int(numElt)-2)].resize(0);
2056 stack[size_t(int(numElt)-2)].push_back(instr);
2057 //fv
2058 stack[size_t(int(numElt)-1)]=fv;
2059 arity=4;
2060 instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
2061 instr.m_content="NPER";
2062 }
2063 else if (arity==3 && instr.m_type==WKSContentListener::FormulaInstruction::F_Function && instr.m_content=="CTERM")
2064 {
2065 // @CTERM(pint,fv,pv) -> NPER(pint,pmt=0,-pv,fv)
2066 auto pint=stack[size_t(int(numElt)-3)];
2067 auto fv=stack[size_t(int(numElt)-2)];
2068 auto pv=stack[size_t(int(numElt)-1)];
2069 stack.resize(size_t(++numElt));
2070 // pint
2071 stack[size_t(int(numElt)-4)]=pint;
2072 // pmt=0
2073 instr.m_type=WKSContentListener::FormulaInstruction::F_Long;
2074 instr.m_longValue=0;
2075 stack[size_t(int(numElt)-3)].resize(0);
2076 stack[size_t(int(numElt)-3)].push_back(instr);
2077 // -pv
2078 auto &node=stack[size_t(int(numElt)-2)];
2079 instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
2080 instr.m_content="-";
2081 node.resize(0);
2082 node.push_back(instr);
2083 instr.m_content="(";
2084 node.push_back(instr);
2085 node.insert(node.end(), pv.begin(), pv.end());
2086 instr.m_content=")";
2087 node.push_back(instr);
2088
2089 //fv
2090 stack[size_t(int(numElt)-1)]=fv;
2091 arity=4;
2092 instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
2093 instr.m_content="NPER";
2094 }
2095
2096 if ((instr.m_content[0] >= 'A' && instr.m_content[0] <= 'Z') || instr.m_content[0] == '(')
2097 {
2098 if (instr.m_content[0] != '(')
2099 child.push_back(instr);
2100
2101 instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
2102 instr.m_content="(";
2103 child.push_back(instr);
2104 for (int i = 0; i < arity; i++)
2105 {
2106 if (i)
2107 {
2108 instr.m_content=";";
2109 child.push_back(instr);
2110 }
2111 auto const &node=stack[size_t(int(numElt)-arity+i)];
2112 child.insert(child.end(), node.begin(), node.end());
2113 }
2114 instr.m_content=")";
2115 child.push_back(instr);
2116
2117 stack.resize(size_t(int(numElt)-arity+1));
2118 stack[size_t(int(numElt)-arity)] = child;
2119 continue;
2120 }
2121 if (arity==1)
2122 {
2123 instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
2124 stack[numElt-1].insert(stack[numElt-1].begin(), instr);
2125 if (wh==3)
2126 break;
2127 continue;
2128 }
2129 if (arity==2)
2130 {
2131 instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
2132 stack[numElt-2].push_back(instr);
2133 stack[numElt-2].insert(stack[numElt-2].end(), stack[numElt-1].begin(), stack[numElt-1].end());
2134 stack.resize(numElt-1);
2135 continue;
2136 }
2137 ok=false;
2138 error = "### unexpected arity";
2139 break;
2140 }
2141
2142 if (!ok) ;
2143 else if (stack.size()==1 && stack[0].size()>1 && stack[0][0].m_content=="=")
2144 {
2145 formula.insert(formula.begin(),stack[0].begin()+1,stack[0].end());
2146 if (m_input->tell()!=endPos)
2147 {
2148 WPS_DEBUG_MSG(("WKS4Spreadsheet::readFormula: find some extra data\n"));
2149 error="##extra data";
2150 ascii().addDelimiter(m_input->tell(),'#');
2151 }
2152 return true;
2153 }
2154 else
2155 error = "###stack problem";
2156
2157 static bool first = true;
2158 if (first)
2159 {
2160 WPS_DEBUG_MSG(("WKS4Spreadsheet::readFormula: I can not read some formula\n"));
2161 first = false;
2162 }
2163
2164 f.str("");
2165 for (auto const &i : stack)
2166 {
2167 for (auto const &j : i)
2168 f << j << ",";
2169 }
2170 f << error << "###";
2171 error = f.str();
2172 return false;
2173 }
2174
2175 ////////////////////////////////////////////////////////////
2176 // filter
2177 ////////////////////////////////////////////////////////////
readFilterOpen()2178 bool WKS4Spreadsheet::readFilterOpen()
2179 {
2180 libwps::DebugStream f;
2181
2182 long pos = m_input->tell();
2183 auto type = long(libwps::readU16(m_input));
2184 if (type != 0x5410)
2185 {
2186 WPS_DEBUG_MSG(("WKS4Spreadsheet::readFilterOpen: not a filter header\n"));
2187 return false;
2188 }
2189 m_state->pushNewSheet(std::shared_ptr<WKS4SpreadsheetInternal::Spreadsheet>
2190 (new WKS4SpreadsheetInternal::Spreadsheet
2191 (WKS4SpreadsheetInternal::Spreadsheet::T_Filter, 0)));
2192 auto sz = long(libwps::readU16(m_input));
2193 f << "Entries(Filter)[beg]:";
2194 if (sz!=0)
2195 {
2196 WPS_DEBUG_MSG(("WKS4Spreadsheet::readFilterOpen: the field size seems odd\n"));
2197 f << "###";
2198 }
2199 ascii().addPos(pos);
2200 ascii().addNote(f.str().c_str());
2201 return true;
2202 }
2203
readFilterClose()2204 bool WKS4Spreadsheet::readFilterClose()
2205 {
2206 libwps::DebugStream f;
2207
2208 long pos = m_input->tell();
2209 auto type = long(libwps::readU16(m_input));
2210 if (type != 0x5411)
2211 {
2212 WPS_DEBUG_MSG(("WKS4Spreadsheet::readFilterClose: not a filter header\n"));
2213 return false;
2214 }
2215 auto sz = long(libwps::readU16(m_input));
2216 f << "Entries(Filter)[end]:";
2217 auto const &sheet=m_state->getActualSheet();
2218 if (sheet.m_type!=WKS4SpreadsheetInternal::Spreadsheet::T_Filter)
2219 {
2220 WPS_DEBUG_MSG(("WKS4Spreadsheet::readFilterClose: can not find filter spreadsheet\n"));
2221 f << "###[noOpen],";
2222 }
2223 else
2224 m_state->popSheet();
2225 if (sz!=0)
2226 {
2227 WPS_DEBUG_MSG(("WKS4Spreadsheet::readFilterClose: the field size seems odd\n"));
2228 f << "###";
2229 }
2230 ascii().addPos(pos);
2231 ascii().addNote(f.str().c_str());
2232 return true;
2233 }
2234
2235 ////////////////////////////////////////////////////////////
2236 // report
2237 ////////////////////////////////////////////////////////////
readReportOpen()2238 bool WKS4Spreadsheet::readReportOpen()
2239 {
2240 libwps::DebugStream f;
2241
2242 long pos = m_input->tell();
2243 auto type = long(libwps::readU16(m_input));
2244 if (type != 0x5417)
2245 {
2246 WPS_DEBUG_MSG(("WKS4Spreadsheet::readReportOpen: not a report header\n"));
2247 return false;
2248 }
2249 m_state->pushNewSheet(std::shared_ptr<WKS4SpreadsheetInternal::Spreadsheet>
2250 (new WKS4SpreadsheetInternal::Spreadsheet
2251 (WKS4SpreadsheetInternal::Spreadsheet::T_Report, 0)));
2252 auto sz = long(libwps::readU16(m_input));
2253 long endPos = pos+4+sz;
2254 f << "Entries(Report)[header]:";
2255 if (sz < 16+10+7 || !checkFilePosition(endPos))
2256 {
2257 WPS_DEBUG_MSG(("WKS4Spreadsheet::readReportOpen: report header too short\n"));
2258 f << "###";
2259 ascii().addPos(pos);
2260 ascii().addNote(f.str().c_str());
2261 return true;
2262 }
2263
2264 librevenge::RVNGString name;
2265 if (!m_mainParser.readCString(name,16))
2266 f << "##name,";
2267 else if (!name.empty())
2268 f << name.cstr() << ",";
2269 m_input->seek(pos+4+16, librevenge::RVNG_SEEK_SET);
2270 auto val=int(libwps::readU8(m_input)); // always 0?
2271 if (val) f << "f0=" << val << ",";
2272 for (int i=0; i<3; ++i) // normally -1:0, but I also find 4:1
2273 {
2274 val=int(libwps::read16(m_input));
2275 auto unkn=int(libwps::readU8(m_input));
2276 if (val==-1 && unkn==0) continue;
2277 f << "unk" << i << "=" << val << ":" << unkn << ",";
2278 }
2279 auto N=int(libwps::readU16(m_input));
2280 if (m_input->tell()+N+7>endPos)
2281 {
2282 WPS_DEBUG_MSG(("WKS4Spreadsheet::readReportOpen: number of fields seems bad\n"));
2283 f << "###N[fields]=" << N << ",";
2284 ascii().addPos(pos);
2285 ascii().addNote(f.str().c_str());
2286 return true;
2287 }
2288 // we must update the number of columns
2289 m_state->getActualSheet().m_numCols=N;
2290 f << "fields=[";
2291 for (int i=0; i<N; ++i) f << int(libwps::readU8(m_input)) << ",";
2292 f << "],";
2293 for (int i=0; i<8; i++)
2294 {
2295 if (m_input->tell()>endPos) break;
2296 static int const expected[]= {0,0x1a,4,0xff, 0,0xa, 0, 1};
2297 val=int(libwps::readU8(m_input));
2298 if (val!=expected[i]) f << "g" << i << "=" << val << ",";
2299 }
2300 ascii().addDelimiter(m_input->tell(), '|');
2301 ascii().addPos(pos);
2302 ascii().addNote(f.str().c_str());
2303 return true;
2304 }
2305
readReportClose()2306 bool WKS4Spreadsheet::readReportClose()
2307 {
2308 libwps::DebugStream f;
2309
2310 long pos = m_input->tell();
2311 auto type = long(libwps::readU16(m_input));
2312 if (type != 0x5418)
2313 {
2314 WPS_DEBUG_MSG(("WKS4Spreadsheet::readReportClose: not a report header\n"));
2315 return false;
2316 }
2317 f << "Report[end]:";
2318 auto const &sheet=m_state->getActualSheet();
2319 if (sheet.m_type!=WKS4SpreadsheetInternal::Spreadsheet::T_Report)
2320 {
2321 WPS_DEBUG_MSG(("WKS4Spreadsheet::readReportClose: can not find report spreadsheet\n"));
2322 f << "###[noOpen],";
2323 }
2324 else
2325 m_state->popSheet();
2326 auto sz = long(libwps::readU16(m_input));
2327 if (sz)
2328 {
2329 WPS_DEBUG_MSG(("WKS4Spreadsheet::readReportClose: find some data\n"));
2330 f << "###[sz],";
2331 }
2332 ascii().addPos(pos);
2333 ascii().addNote(f.str().c_str());
2334 return true;
2335 }
2336
2337 ////////////////////////////////////////////////////////////
2338 // send data
2339 ////////////////////////////////////////////////////////////
sendSpreadsheet(int sId)2340 void WKS4Spreadsheet::sendSpreadsheet(int sId)
2341 {
2342 if (!m_listener)
2343 {
2344 WPS_DEBUG_MSG(("WKS4Spreadsheet::sendSpreadsheet: I can not find the listener\n"));
2345 return;
2346 }
2347 std::shared_ptr<WKS4SpreadsheetInternal::Spreadsheet> sheet =
2348 m_state->getSheet(WKS4SpreadsheetInternal::Spreadsheet::T_Spreadsheet, sId);
2349 if (!sheet)
2350 {
2351 if (sId==0)
2352 {
2353 WPS_DEBUG_MSG(("WKS4Spreadsheet::sendSpreadsheet: oops can not find the actual sheet\n"));
2354 }
2355 sheet.reset(new WKS4SpreadsheetInternal::Spreadsheet);
2356 }
2357
2358 m_listener->openSheet(sheet->getWidths(), m_state->getSheetName(sId));
2359 sheet->compressRowHeights();
2360 auto it = sheet->m_positionToCellMap.begin();
2361 int prevRow = -1;
2362 while (it!=sheet->m_positionToCellMap.end())
2363 {
2364 int row=it->first[1];
2365 auto const &cell=(it++)->second;
2366 if (row>prevRow+1)
2367 {
2368 while (row > prevRow+1)
2369 {
2370 if (prevRow != -1) m_listener->closeSheetRow();
2371 int numRepeat;
2372 float h=sheet->getRowHeight(prevRow+1, numRepeat);
2373 if (row<prevRow+1+numRepeat)
2374 numRepeat=row-1-prevRow;
2375 m_listener->openSheetRow(WPSRowFormat(h), numRepeat);
2376 prevRow+=numRepeat;
2377 }
2378 }
2379 if (row!=prevRow)
2380 {
2381 if (prevRow != -1) m_listener->closeSheetRow();
2382 m_listener->openSheetRow(WPSRowFormat(sheet->getRowHeight(++prevRow)));
2383 }
2384 sendCellContent(cell);
2385 }
2386 if (prevRow!=-1) m_listener->closeSheetRow();
2387 m_listener->closeSheet();
2388 }
2389
sendCellContent(WKS4SpreadsheetInternal::Cell const & cell)2390 void WKS4Spreadsheet::sendCellContent(WKS4SpreadsheetInternal::Cell const &cell)
2391 {
2392 if (m_listener.get() == nullptr)
2393 {
2394 WPS_DEBUG_MSG(("WKS4Spreadsheet::sendCellContent: I can not find the listener\n"));
2395 return;
2396 }
2397
2398 WKS4SpreadsheetInternal::Style cellStyle(m_mainParser.getDefaultFontType());
2399 if (cell.m_styleId<0 || !m_state->m_styleManager.get(cell.m_styleId,cellStyle))
2400 {
2401 WPS_DEBUG_MSG(("WKS4Spreadsheet::sendCellContent: I can not find the cell style\n"));
2402 }
2403 if (version()<=2 && cell.m_hAlignement!=WPSCellFormat::HALIGN_DEFAULT)
2404 cellStyle.setHAlignment(cell.m_hAlignement);
2405
2406 auto fontType = cellStyle.m_fontType;
2407 m_listener->setFont(cellStyle.m_fileFont);
2408
2409 auto finalCell(cell);
2410 finalCell.WPSCellFormat::operator=(cellStyle);
2411 finalCell.setFont(cellStyle.m_fileFont);
2412 auto content(cell.m_content);
2413 for (auto &f : content.m_formula)
2414 {
2415 if (f.m_type!=WKSContentListener::FormulaInstruction::F_Text)
2416 continue;
2417 auto &text=f.m_content;
2418 auto fText=libwps_tools_win::Font::unicodeString(text, fontType);
2419 if (!fText.empty())
2420 text=fText.cstr();
2421 else
2422 text.clear();
2423 }
2424 m_listener->openSheetCell(finalCell, content);
2425
2426 size_t numTextEntry=cell.m_content.m_textEntry.valid() ? 1 : 0;
2427 if (!cell.m_extraTextEntryList.empty())
2428 numTextEntry=1+cell.m_extraTextEntryList.size();
2429 for (size_t t=0; t<numTextEntry; ++t)
2430 {
2431 WPSEntry entry=t==0 ? cell.m_content.m_textEntry : cell.m_extraTextEntryList[t-1];
2432 if (!entry.valid()) continue;
2433 m_input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
2434 bool prevEOL=false;
2435 std::string text;
2436 while (m_input->tell()<=entry.end())
2437 {
2438 bool last=m_input->isEnd() || m_input->tell()>=entry.end();
2439 auto c=last ? '\0' : char(libwps::readU8(m_input));
2440 if ((c==0 || c==0xa || c==0xd || c==0x19) && !text.empty())
2441 {
2442 m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(text, fontType));
2443 text.clear();
2444 }
2445 if (last) break;
2446 if (c==0xd)
2447 {
2448 m_listener->insertEOL();
2449 prevEOL=true;
2450 }
2451 else if (c==0xa)
2452 {
2453 if (!prevEOL)
2454 {
2455 WPS_DEBUG_MSG(("WKS4Spreadsheet::sendCellContent: find 0xa without 0xd\n"));
2456 }
2457 prevEOL=false;
2458 }
2459 else if (c==0x19)
2460 m_listener->insertEOL(true);
2461 else
2462 {
2463 prevEOL=false;
2464 if (c)
2465 text.push_back(c);
2466 }
2467 }
2468 }
2469 m_listener->closeSheetCell();
2470 }
2471
2472 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
2473