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) 2006 Fridrich Strba (fridrich.strba@bluewin.ch)
12  * Copyright (C) 2003-2005 William Lachance (william.lachance@sympatico.ca)
13  * Copyright (C) 2003 Marc Maurer (uwog@uwog.net)
14  *
15  * For minor contributions see the git repository.
16  *
17  * Alternatively, the contents of this file may be used under the terms
18  * of the GNU Lesser General Public License Version 2.1 or later
19  * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
20  * applicable instead of those above.
21  *
22  * For further information visit http://libwps.sourceforge.net
23  */
24 
25 #include <iomanip>
26 #include <map>
27 #include <sstream>
28 #include <stdio.h>
29 
30 #include <librevenge/librevenge.h>
31 
32 #include "libwps_internal.h"
33 #include "libwps_tools_win.h"
34 
35 #include "WKSContentListener.h"
36 
37 #include "WPSCell.h"
38 #include "WPSFont.h"
39 #include "WPSGraphicShape.h"
40 #include "WPSPageSpan.h"
41 #include "WPSParagraph.h"
42 #include "WPSPosition.h"
43 #include "WPSTable.h"
44 #include "WKSChart.h"
45 #include "WKSSubDocument.h"
46 
47 ////////////////////////////////////////////////////////////
48 //! the document state
49 struct WKSDocumentParsingState
50 {
51 	//! constructor
52 	explicit WKSDocumentParsingState(std::vector<WPSPageSpan> const &pageList);
53 	//! destructor
54 	~WKSDocumentParsingState();
55 
56 	std::vector<WPSPageSpan> m_pageList;
57 	librevenge::RVNGPropertyList m_metaData;
58 
59 	bool m_isDocumentStarted, m_isHeaderFooterStarted;
60 	std::vector<WPSSubDocumentPtr> m_subDocuments; /** list of document actually open */
61 
62 	/** a map cell's format to id */
63 	std::map<WPSCellFormat,int,WPSCellFormat::CompareFormat> m_numberingIdMap;
64 
65 private:
66 	WKSDocumentParsingState(const WKSDocumentParsingState &) = delete;
67 	WKSDocumentParsingState &operator=(const WKSDocumentParsingState &) = delete;
68 };
69 
WKSDocumentParsingState(std::vector<WPSPageSpan> const & pageList)70 WKSDocumentParsingState::WKSDocumentParsingState(std::vector<WPSPageSpan> const &pageList)
71 	: m_pageList(pageList)
72 	, m_metaData()
73 	, m_isDocumentStarted(false)
74 	, m_isHeaderFooterStarted(false)
75 	, m_subDocuments()
76 	, m_numberingIdMap()
77 {
78 }
79 
~WKSDocumentParsingState()80 WKSDocumentParsingState::~WKSDocumentParsingState()
81 {
82 }
83 
84 ////////////////////////////////////////////////////////////
85 //! the spreadsheet state
86 struct WKSContentParsingState
87 {
88 	WKSContentParsingState();
89 	~WKSContentParsingState();
90 
91 	bool m_isPageSpanOpened;
92 	bool m_isFrameOpened;
93 
94 	unsigned m_currentPage;
95 	int m_numPagesRemainingInSpan;
96 	int m_currentPageNumber;
97 
98 	double m_pageFormLength;
99 	double m_pageFormWidth;
100 	bool m_pageFormOrientationIsPortrait;
101 
102 	double m_pageMarginLeft;
103 	double m_pageMarginRight;
104 	double m_pageMarginTop;
105 	double m_pageMarginBottom;
106 
107 	librevenge::RVNGString m_textBuffer;
108 	int m_numDeferredTabs;
109 
110 	WPSFont m_font;
111 	WPSParagraph m_paragraph;
112 	//! a flag to know if openGroup was called
113 	bool m_isGroupOpened;
114 
115 	bool m_isParagraphColumnBreak;
116 	bool m_isParagraphPageBreak;
117 
118 	bool m_isSpanOpened;
119 	bool m_isParagraphOpened;
120 
121 	bool m_isSheetOpened;
122 	bool m_isSheetRowOpened;
123 	bool m_isSheetCellOpened;
124 
125 	bool m_inSubDocument;
126 
127 	bool m_isNote;
128 	libwps::SubDocumentType m_subDocumentType;
129 
130 private:
131 	WKSContentParsingState(const WKSContentParsingState &) = delete;
132 	WKSContentParsingState &operator=(const WKSContentParsingState &) = delete;
133 };
134 
WKSContentParsingState()135 WKSContentParsingState::WKSContentParsingState()
136 	: m_isPageSpanOpened(false)
137 	, m_isFrameOpened(false)
138 	, m_currentPage(0)
139 	, m_numPagesRemainingInSpan(0)
140 	, m_currentPageNumber(1)
141 	, m_pageFormLength(11.0)
142 	, m_pageFormWidth(8.5)
143 	, m_pageFormOrientationIsPortrait(true)
144 	, m_pageMarginLeft(1.0)
145 	, m_pageMarginRight(1.0)
146 	, m_pageMarginTop(1.0)
147 	, m_pageMarginBottom(1.0)
148 
149 	, m_textBuffer("")
150 	, m_numDeferredTabs(0)
151 
152 	, m_font()
153 	, m_paragraph()
154 	, m_isGroupOpened(false)
155 	, m_isParagraphColumnBreak(false)
156 	, m_isParagraphPageBreak(false)
157 
158 	, m_isSpanOpened(false)
159 	, m_isParagraphOpened(false)
160 
161 	, m_isSheetOpened(false)
162 	, m_isSheetRowOpened(false)
163 	, m_isSheetCellOpened(false)
164 
165 	, m_inSubDocument(false)
166 	, m_isNote(false)
167 	, m_subDocumentType(libwps::DOC_NONE)
168 {
169 	m_font.m_size=12.0;
170 	m_font.m_name="Times New Roman";
171 }
172 
~WKSContentParsingState()173 WKSContentParsingState::~WKSContentParsingState()
174 {
175 }
176 
WKSContentListener(std::vector<WPSPageSpan> const & pageList,librevenge::RVNGSpreadsheetInterface * documentInterface)177 WKSContentListener::WKSContentListener(std::vector<WPSPageSpan> const &pageList, librevenge::RVNGSpreadsheetInterface *documentInterface)
178 	: m_ds(new WKSDocumentParsingState(pageList))
179 	, m_ps(new WKSContentParsingState)
180 	, m_psStack()
181 	, m_documentInterface(documentInterface)
182 {
183 }
184 
~WKSContentListener()185 WKSContentListener::~WKSContentListener()
186 {
187 }
188 
189 ///////////////////
190 // text data
191 ///////////////////
insertCharacter(uint8_t character)192 void WKSContentListener::insertCharacter(uint8_t character)
193 {
194 	if (character >= 0x80)
195 	{
196 		insertUnicode(character);
197 		return;
198 	}
199 	_flushDeferredTabs();
200 	if (!m_ps->m_isSpanOpened) _openSpan();
201 	m_ps->m_textBuffer.append(char(character));
202 }
203 
insertUnicode(uint32_t val)204 void WKSContentListener::insertUnicode(uint32_t val)
205 {
206 	// undef character, we skip it
207 	if (val == 0xfffd) return;
208 	_flushDeferredTabs();
209 	if (!m_ps->m_isSpanOpened) _openSpan();
210 	libwps::appendUnicode(val, m_ps->m_textBuffer);
211 }
212 
insertUnicodeString(librevenge::RVNGString const & str)213 void WKSContentListener::insertUnicodeString(librevenge::RVNGString const &str)
214 {
215 	_flushDeferredTabs();
216 	if (!m_ps->m_isSpanOpened) _openSpan();
217 	m_ps->m_textBuffer.append(str);
218 }
219 
insertEOL(bool soft)220 void WKSContentListener::insertEOL(bool soft)
221 {
222 	if (!m_ps->m_isParagraphOpened)
223 		_openSpan();
224 	_flushDeferredTabs();
225 
226 	if (soft)
227 	{
228 		if (m_ps->m_isSpanOpened)
229 			_flushText();
230 		m_documentInterface->insertLineBreak();
231 	}
232 	else if (m_ps->m_isParagraphOpened)
233 		_closeParagraph();
234 
235 	// sub/superscript must not survive a new line
236 	static const uint32_t s_subsuperBits = WPS_SUBSCRIPT_BIT | WPS_SUPERSCRIPT_BIT;
237 	if (m_ps->m_font.m_attributes & s_subsuperBits)
238 		m_ps->m_font.m_attributes &= ~s_subsuperBits;
239 }
240 
insertTab()241 void WKSContentListener::insertTab()
242 {
243 	if (!m_ps->m_isParagraphOpened)
244 	{
245 		m_ps->m_numDeferredTabs++;
246 		return;
247 	}
248 	if (m_ps->m_isSpanOpened) _flushText();
249 	m_ps->m_numDeferredTabs++;
250 	_flushDeferredTabs();
251 }
252 
insertBreak(const uint8_t breakType)253 void WKSContentListener::insertBreak(const uint8_t breakType)
254 {
255 	switch (breakType)
256 	{
257 	case WPS_COLUMN_BREAK:
258 		if (m_ps->m_isParagraphOpened)
259 			_closeParagraph();
260 		m_ps->m_isParagraphColumnBreak = true;
261 		break;
262 	case WPS_PAGE_BREAK:
263 		if (m_ps->m_isParagraphOpened)
264 			_closeParagraph();
265 		m_ps->m_isParagraphPageBreak = true;
266 		break;
267 	default:
268 		break;
269 	}
270 }
271 
_insertBreakIfNecessary(librevenge::RVNGPropertyList & propList)272 void WKSContentListener::_insertBreakIfNecessary(librevenge::RVNGPropertyList &propList)
273 {
274 	if (m_ps->m_isParagraphPageBreak && !m_ps->m_inSubDocument)
275 	{
276 		// no hard page-breaks in subdocuments
277 		propList.insert("fo:break-before", "page");
278 		m_ps->m_isParagraphPageBreak = false;
279 	}
280 }
281 
282 ///////////////////
283 // font/character format
284 ///////////////////
setFont(const WPSFont & font)285 void WKSContentListener::setFont(const WPSFont &font)
286 {
287 	WPSFont newFont(font);
288 	if (font.m_size<=0)
289 		newFont.m_size=m_ps->m_font.m_size;
290 	if (font.m_name.empty())
291 		newFont.m_name=m_ps->m_font.m_name;
292 	if (font.m_languageId <= 0)
293 		newFont.m_languageId=m_ps->m_font.m_languageId;
294 	if (m_ps->m_font==newFont) return;
295 	_closeSpan();
296 	m_ps->m_font=newFont;
297 }
298 
getFont() const299 WPSFont const &WKSContentListener::getFont() const
300 {
301 	return m_ps->m_font;
302 }
303 
304 ///////////////////
305 // paragraph tabs, tabs/...
306 ///////////////////
isParagraphOpened() const307 bool WKSContentListener::isParagraphOpened() const
308 {
309 	return m_ps->m_isParagraphOpened;
310 }
311 
getParagraph() const312 WPSParagraph const &WKSContentListener::getParagraph() const
313 {
314 	return m_ps->m_paragraph;
315 }
316 
setParagraph(const WPSParagraph & para)317 void WKSContentListener::setParagraph(const WPSParagraph &para)
318 {
319 	m_ps->m_paragraph=para;
320 }
321 
322 ///////////////////
323 // field :
324 ///////////////////
insertField(WPSField const & field)325 void WKSContentListener::insertField(WPSField const &field)
326 {
327 	librevenge::RVNGPropertyList propList;
328 	if (field.addTo(propList))
329 	{
330 		_flushText();
331 		_openSpan();
332 		m_documentInterface->insertField(propList);
333 		return;
334 	}
335 	librevenge::RVNGString text=field.getString();
336 	if (!text.empty())
337 		WKSContentListener::insertUnicodeString(text);
338 	else
339 	{
340 		WPS_DEBUG_MSG(("WKSContentListener::insertField: must not be called with type=%d\n", int(field.m_type)));
341 	}
342 }
343 
344 ///////////////////
345 // document
346 ///////////////////
setDocumentLanguage(int lcid)347 void WKSContentListener::setDocumentLanguage(int lcid)
348 {
349 	if (lcid <= 0) return;
350 	std::string lang = libwps_tools_win::Language::localeName(lcid);
351 	if (!lang.length()) return;
352 	m_ds->m_metaData.insert("librevenge:language", lang.c_str());
353 }
354 
setMetaData(const librevenge::RVNGPropertyList & list)355 void WKSContentListener::setMetaData(const librevenge::RVNGPropertyList &list)
356 {
357 	librevenge::RVNGPropertyList::Iter i(list);
358 
359 	for (i.rewind(); i.next();)
360 	{
361 		m_ds->m_metaData.insert(i.key(), i()->getStr());
362 	}
363 }
364 
startDocument()365 void WKSContentListener::startDocument()
366 {
367 	if (m_ds->m_isDocumentStarted)
368 	{
369 		WPS_DEBUG_MSG(("WKSContentListener::startDocument: the document is already started\n"));
370 		return;
371 	}
372 
373 	m_documentInterface->startDocument(librevenge::RVNGPropertyList());
374 	m_ds->m_isDocumentStarted = true;
375 
376 	// FIXME: this is stupid, we should store a property list filled with the relevant metadata
377 	// and then pass that directly..
378 	m_documentInterface->setDocumentMetaData(m_ds->m_metaData);
379 }
380 
endDocument()381 void WKSContentListener::endDocument()
382 {
383 	if (!m_ds->m_isDocumentStarted)
384 	{
385 		WPS_DEBUG_MSG(("WKSContentListener::startDocument: the document is not started\n"));
386 		return;
387 	}
388 
389 	if (m_ps->m_isSheetOpened)
390 		closeSheet();
391 	if (m_ps->m_isParagraphOpened)
392 		_closeParagraph();
393 
394 	// close the document nice and tight
395 	_closePageSpan();
396 	m_documentInterface->endDocument();
397 	m_ds->m_isDocumentStarted = false;
398 }
399 
400 ///////////////////
401 // paragraph
402 ///////////////////
_openParagraph()403 void WKSContentListener::_openParagraph()
404 {
405 	if (m_ps->m_isSheetOpened && !m_ps->m_isSheetCellOpened)
406 		return;
407 
408 	if (!m_ps->m_isPageSpanOpened)
409 		_openPageSpan();
410 
411 	if (m_ps->m_isParagraphOpened)
412 	{
413 		WPS_DEBUG_MSG(("WKSContentListener::_openParagraph: a paragraph (or a list) is already opened"));
414 		return;
415 	}
416 	librevenge::RVNGPropertyList propList;
417 	_appendParagraphProperties(propList);
418 
419 	if (!m_ps->m_isParagraphOpened)
420 		m_documentInterface->openParagraph(propList);
421 
422 	_resetParagraphState();
423 }
424 
_closeParagraph()425 void WKSContentListener::_closeParagraph()
426 {
427 	if (m_ps->m_isParagraphOpened)
428 	{
429 		if (m_ps->m_isSpanOpened)
430 			_closeSpan();
431 
432 		m_documentInterface->closeParagraph();
433 	}
434 
435 	m_ps->m_isParagraphOpened = false;
436 	m_ps->m_paragraph.m_listLevelIndex = 0;
437 }
438 
_resetParagraphState(const bool)439 void WKSContentListener::_resetParagraphState(const bool /*isListElement*/)
440 {
441 	m_ps->m_isParagraphColumnBreak = false;
442 	m_ps->m_isParagraphPageBreak = false;
443 	m_ps->m_isParagraphOpened = true;
444 }
445 
_appendParagraphProperties(librevenge::RVNGPropertyList & propList,const bool)446 void WKSContentListener::_appendParagraphProperties(librevenge::RVNGPropertyList &propList, const bool /*isListElement*/)
447 {
448 	m_ps->m_paragraph.addTo(propList, m_ps->m_isSheetOpened);
449 
450 	_insertBreakIfNecessary(propList);
451 }
452 
453 ///////////////////
454 // span
455 ///////////////////
_openSpan()456 void WKSContentListener::_openSpan()
457 {
458 	if (m_ps->m_isSpanOpened)
459 		return;
460 
461 	if (m_ps->m_isSheetOpened && !m_ps->m_isSheetCellOpened)
462 		return;
463 
464 	if (!m_ps->m_isParagraphOpened)
465 		_openParagraph();
466 
467 	librevenge::RVNGPropertyList propList;
468 	m_ps->m_font.addTo(propList);
469 
470 	m_documentInterface->openSpan(propList);
471 
472 	m_ps->m_isSpanOpened = true;
473 }
474 
_closeSpan()475 void WKSContentListener::_closeSpan()
476 {
477 	if (!m_ps->m_isSpanOpened)
478 		return;
479 
480 	_flushText();
481 	m_documentInterface->closeSpan();
482 	m_ps->m_isSpanOpened = false;
483 }
484 
485 ///////////////////
486 // text (send data)
487 ///////////////////
_flushDeferredTabs()488 void WKSContentListener::_flushDeferredTabs()
489 {
490 	if (m_ps->m_numDeferredTabs == 0) return;
491 
492 	// CHECKME: the tab are not underline even if the underline bit is set
493 	uint32_t oldTextAttributes = m_ps->m_font.m_attributes;
494 	uint32_t newAttributes = oldTextAttributes & uint32_t(~WPS_UNDERLINE_BIT) &
495 	                         uint32_t(~WPS_OVERLINE_BIT);
496 	if (oldTextAttributes != newAttributes)
497 	{
498 		_closeSpan();
499 		m_ps->m_font.m_attributes=newAttributes;
500 	}
501 	if (!m_ps->m_isSpanOpened) _openSpan();
502 	for (; m_ps->m_numDeferredTabs > 0; m_ps->m_numDeferredTabs--)
503 		m_documentInterface->insertTab();
504 	if (oldTextAttributes != newAttributes)
505 	{
506 		_closeSpan();
507 		m_ps->m_font.m_attributes=oldTextAttributes;
508 	}
509 }
510 
_flushText()511 void WKSContentListener::_flushText()
512 {
513 	if (m_ps->m_textBuffer.len() == 0) return;
514 
515 	// when some many ' ' follows each other, call insertSpace
516 	librevenge::RVNGString tmpText;
517 	int numConsecutiveSpaces = 0;
518 	librevenge::RVNGString::Iter i(m_ps->m_textBuffer);
519 	for (i.rewind(); i.next();)
520 	{
521 		if (*(i()) == 0x20) // this test is compatible with unicode format
522 			numConsecutiveSpaces++;
523 		else
524 			numConsecutiveSpaces = 0;
525 
526 		if (numConsecutiveSpaces > 1)
527 		{
528 			if (tmpText.len() > 0)
529 			{
530 				m_documentInterface->insertText(tmpText);
531 				tmpText.clear();
532 			}
533 			m_documentInterface->insertSpace();
534 		}
535 		else
536 			tmpText.append(i());
537 	}
538 	m_documentInterface->insertText(tmpText);
539 	m_ps->m_textBuffer.clear();
540 }
541 
542 ///////////////////
543 // Comment
544 ///////////////////
insertComment(WPSSubDocumentPtr & subDocument)545 void WKSContentListener::insertComment(WPSSubDocumentPtr &subDocument)
546 {
547 	if (m_ps->m_isNote)
548 	{
549 		WPS_DEBUG_MSG(("WKSContentListener::insertComment try to insert a note recursively (ingnored)"));
550 		return;
551 	}
552 
553 	if (!m_ps->m_isSheetCellOpened)
554 	{
555 		if (!m_ps->m_isParagraphOpened)
556 			_openParagraph();
557 		else
558 		{
559 			_flushText();
560 			_closeSpan();
561 		}
562 	}
563 	else if (m_ps->m_isParagraphOpened)
564 		_closeParagraph();
565 
566 	librevenge::RVNGPropertyList propList;
567 	m_documentInterface->openComment(propList);
568 
569 	m_ps->m_isNote = true;
570 	handleSubDocument(subDocument, libwps::DOC_COMMENT_ANNOTATION);
571 
572 	m_documentInterface->closeComment();
573 	m_ps->m_isNote = false;
574 }
575 
576 ///////////////////
577 // chart
578 ///////////////////
insertChart(WPSPosition const & pos,WKSChart const & chart,WPSGraphicStyle const & style)579 void WKSContentListener::insertChart
580 (WPSPosition const &pos, WKSChart const &chart, WPSGraphicStyle const &style)
581 {
582 	WPSGraphicStyle finalStyle(style);
583 	if (!chart.m_name.empty())
584 		finalStyle.m_frameName=chart.m_name;
585 	if (!_openFrame(pos, finalStyle)) return;
586 
587 	_pushParsingState();
588 	_startSubDocument();
589 	m_ps->m_subDocumentType = libwps::DOC_CHART_ZONE;
590 
591 	std::shared_ptr<WKSContentListener> listen(this, WPS_shared_ptr_noop_deleter<WKSContentListener>());
592 	try
593 	{
594 		chart.sendChart(listen, m_documentInterface);
595 	}
596 	catch (...)
597 	{
598 		WPS_DEBUG_MSG(("WKSContentListener::insertChart exception catched \n"));
599 	}
600 	_endSubDocument();
601 	_popParsingState();
602 
603 	_closeFrame();
604 }
605 
insertTextBox(WPSPosition const & pos,WPSSubDocumentPtr subDocument,WPSGraphicStyle const & frameStyle)606 void WKSContentListener::insertTextBox
607 (WPSPosition const &pos, WPSSubDocumentPtr subDocument, WPSGraphicStyle const &frameStyle)
608 {
609 	if (!_openFrame(pos, frameStyle)) return;
610 
611 	librevenge::RVNGPropertyList propList;
612 	m_documentInterface->openTextBox(propList);
613 	handleSubDocument(subDocument, libwps::DOC_TEXT_BOX);
614 	m_documentInterface->closeTextBox();
615 
616 	_closeFrame();
617 }
618 
insertPicture(WPSPosition const & pos,const librevenge::RVNGBinaryData & binaryData,std::string type,WPSGraphicStyle const & style)619 void WKSContentListener::insertPicture
620 (WPSPosition const &pos, const librevenge::RVNGBinaryData &binaryData, std::string type,
621  WPSGraphicStyle const &style)
622 {
623 	if (!_openFrame(pos, style)) return;
624 
625 	librevenge::RVNGPropertyList propList;
626 	propList.insert("librevenge:mime-type", type.c_str());
627 	propList.insert("office:binary-data", binaryData);
628 	m_documentInterface->insertBinaryObject(propList);
629 
630 	_closeFrame();
631 }
632 
insertObject(WPSPosition const & pos,const WPSEmbeddedObject & obj,WPSGraphicStyle const & style)633 void WKSContentListener::insertObject
634 (WPSPosition const &pos, const WPSEmbeddedObject &obj, WPSGraphicStyle const &style)
635 {
636 	if (!_openFrame(pos, style)) return;
637 
638 	librevenge::RVNGPropertyList propList;
639 	if (obj.addTo(propList))
640 		m_documentInterface->insertBinaryObject(propList);
641 
642 	_closeFrame();
643 }
644 
insertPicture(WPSPosition const & pos,const WPSGraphicShape & shape,WPSGraphicStyle const & style)645 void WKSContentListener::insertPicture
646 (WPSPosition const &pos, const WPSGraphicShape &shape, WPSGraphicStyle const &style)
647 {
648 	librevenge::RVNGPropertyList shapePList;
649 	_handleFrameParameters(shapePList, pos);
650 	shapePList.remove("svg:x");
651 	shapePList.remove("svg:y");
652 
653 	librevenge::RVNGPropertyList styleList;
654 	style.addTo(styleList, shape.getType()==WPSGraphicShape::Line);
655 	float factor=WPSPosition::getScaleFactor(pos.unit(), librevenge::RVNG_POINT);
656 	Vec2f decal = factor*pos.origin();
657 	switch (shape.addTo(decal, style.hasSurface(), shapePList))
658 	{
659 	case WPSGraphicShape::C_Ellipse:
660 		m_documentInterface->defineGraphicStyle(styleList);
661 		m_documentInterface->drawEllipse(shapePList);
662 		break;
663 	case WPSGraphicShape::C_Path:
664 		m_documentInterface->defineGraphicStyle(styleList);
665 		m_documentInterface->drawPath(shapePList);
666 		break;
667 	case WPSGraphicShape::C_Polyline:
668 		m_documentInterface->defineGraphicStyle(styleList);
669 		m_documentInterface->drawPolyline(shapePList);
670 		break;
671 	case WPSGraphicShape::C_Polygon:
672 		m_documentInterface->defineGraphicStyle(styleList);
673 		m_documentInterface->drawPolygon(shapePList);
674 		break;
675 	case WPSGraphicShape::C_Rectangle:
676 		m_documentInterface->defineGraphicStyle(styleList);
677 		m_documentInterface->drawRectangle(shapePList);
678 		break;
679 	case WPSGraphicShape::C_Bad:
680 		break;
681 	default:
682 		WPS_DEBUG_MSG(("WKSSpreadsheetListener::insertPicture: unexpected shape\n"));
683 		break;
684 	}
685 }
686 
687 ///////////////////
688 // frame
689 ///////////////////
openGroup(WPSPosition const & pos)690 bool WKSContentListener::openGroup(WPSPosition const &pos)
691 {
692 	if (!m_ds->m_isDocumentStarted)
693 	{
694 		WPS_DEBUG_MSG(("WKSContentListener::openGroup: the document is not started\n"));
695 		return false;
696 	}
697 	if (m_ps->m_isSheetRowOpened)
698 	{
699 		WPS_DEBUG_MSG(("WKSContentListener::openGroup: can not open a group\n"));
700 		return false;
701 	}
702 	librevenge::RVNGPropertyList propList;
703 	_handleFrameParameters(propList, pos);
704 
705 	_pushParsingState();
706 	_startSubDocument();
707 	m_ps->m_isGroupOpened = true;
708 
709 	m_documentInterface->openGroup(propList);
710 
711 	return true;
712 }
713 
closeGroup()714 void WKSContentListener::closeGroup()
715 {
716 	if (!m_ps->m_isGroupOpened)
717 	{
718 		WPS_DEBUG_MSG(("WKSContentListener::closeGroup: called but no group is already opened\n"));
719 		return;
720 	}
721 	_endSubDocument();
722 	_popParsingState();
723 	m_documentInterface->closeGroup();
724 }
725 
_openFrame(WPSPosition const & pos,WPSGraphicStyle const & style)726 bool WKSContentListener::_openFrame(WPSPosition const &pos, WPSGraphicStyle const &style)
727 {
728 	if (m_ps->m_isFrameOpened)
729 	{
730 		WPS_DEBUG_MSG(("WKSContentListener::openFrame: called but a frame is already opened\n"));
731 		return false;
732 	}
733 
734 	switch (pos.m_anchorTo)
735 	{
736 	case WPSPosition::Page:
737 	case WPSPosition::PageContent:
738 		break;
739 	case WPSPosition::Paragraph:
740 	case WPSPosition::ParagraphContent:
741 		if (m_ps->m_isParagraphOpened)
742 			_flushText();
743 		else
744 			_openParagraph();
745 		break;
746 	case WPSPosition::CharBaseLine:
747 	case WPSPosition::Char:
748 		if (m_ps->m_isSpanOpened)
749 			_flushText();
750 		else
751 			_openSpan();
752 		break;
753 	case WPSPosition::Cell:
754 		if (!m_ps->m_isSheetCellOpened)
755 		{
756 			WPS_DEBUG_MSG(("WPSSpreadsheetListener::openFrame: called with Cell position not in a sheet cell\n"));
757 			return false;
758 		}
759 		if (pos.m_anchorCellName.empty())
760 		{
761 			WPS_DEBUG_MSG(("WPSSpreadsheetListener::openFrame: can not find the cell name\n"));
762 			return false;
763 		}
764 		if (m_ps->m_isParagraphOpened)
765 			_closeParagraph();
766 		break;
767 	default:
768 		WPS_DEBUG_MSG(("WKSContentListener::openFrame: can not determine the anchor\n"));
769 		return false;
770 	}
771 
772 	librevenge::RVNGPropertyList propList;
773 	style.addFrameTo(propList);
774 	if (!propList["draw:fill"])
775 		propList.insert("draw:fill","none");
776 	_handleFrameParameters(propList, pos);
777 	m_documentInterface->openFrame(propList);
778 
779 	m_ps->m_isFrameOpened = true;
780 	return true;
781 }
782 
_closeFrame()783 void WKSContentListener::_closeFrame()
784 {
785 	if (!m_ps->m_isFrameOpened)
786 	{
787 		WPS_DEBUG_MSG(("WKSContentListener::closeFrame: called but no frame is already opened\n"));
788 		return;
789 	}
790 	m_documentInterface->closeFrame();
791 	m_ps->m_isFrameOpened = false;
792 }
793 
_handleFrameParameters(librevenge::RVNGPropertyList & propList,WPSPosition const & pos)794 void WKSContentListener::_handleFrameParameters
795 (librevenge::RVNGPropertyList &propList, WPSPosition const &pos)
796 {
797 	Vec2f origin = pos.origin();
798 	librevenge::RVNGUnit unit = pos.unit();
799 	float inchFactor=pos.getInvUnitScale(librevenge::RVNG_INCH);
800 	float pointFactor = pos.getInvUnitScale(librevenge::RVNG_POINT);
801 
802 	propList.insert("svg:width", double(pos.size()[0]), unit);
803 	propList.insert("svg:height", double(pos.size()[1]), unit);
804 	if (pos.naturalSize().x() > 4*pointFactor && pos.naturalSize().y() > 4*pointFactor)
805 	{
806 		propList.insert("librevenge:naturalWidth", double(pos.naturalSize().x()), pos.unit());
807 		propList.insert("librevenge:naturalHeight", double(pos.naturalSize().y()), pos.unit());
808 	}
809 
810 	if (pos.m_wrapping ==  WPSPosition::WDynamic)
811 		propList.insert("style:wrap", "dynamic");
812 	else if (pos.m_wrapping ==  WPSPosition::WRunThrough)
813 	{
814 		propList.insert("style:wrap", "run-through");
815 		propList.insert("style:run-through", "background");
816 	}
817 	else
818 		propList.insert("style:wrap", "none");
819 
820 	if (pos.m_anchorTo == WPSPosition::Cell)
821 	{
822 		if (!pos.m_anchorCellName.empty())
823 			propList.insert("table:end-cell-address", pos.m_anchorCellName);
824 		// todo: implement also different m_xPos and m_yPos
825 		if (origin[0] < 0 || origin[0] > 0)
826 			propList.insert("svg:x", double(origin[0]), unit);
827 		if (origin[1] < 0 || origin[1] > 0)
828 			propList.insert("svg:y", double(origin[1]), unit);
829 		return;
830 	}
831 
832 	if (pos.m_anchorTo != WPSPosition::Page && pos.m_anchorTo != WPSPosition::PageContent)
833 	{
834 		WPS_DEBUG_MSG(("WKSContentListener::openFrame: only implemented for page anchor\n"));
835 		return;
836 	}
837 
838 	// Page position seems to do not use the page margin...
839 	propList.insert("text:anchor-type", "page");
840 	if (pos.page() > 0) propList.insert("text:anchor-page-number", pos.page());
841 	auto w = float(m_ps->m_pageFormWidth);
842 	auto h = float(m_ps->m_pageFormLength);
843 	w *= inchFactor;
844 	h *= inchFactor;
845 
846 	librevenge::RVNGString relPos(pos.m_anchorTo == WPSPosition::Page ? "page" : "page-content");
847 	propList.insert("style:vertical-rel", relPos);
848 	propList.insert("style:horizontal-rel", relPos);
849 
850 	float newPosition;
851 	switch (pos.m_yPos)
852 	{
853 	case WPSPosition::YFull:
854 		propList.insert("svg:height", double(h), unit);
855 		WPS_FALLTHROUGH;
856 	case WPSPosition::YTop:
857 		if (origin[1] < 0.0f || origin[1] > 0.0f)
858 		{
859 			propList.insert("style:vertical-pos", "from-top");
860 			newPosition = origin[1];
861 			propList.insert("svg:y", double(newPosition), unit);
862 		}
863 		else
864 			propList.insert("style:vertical-pos", "top");
865 		break;
866 	case WPSPosition::YCenter:
867 		if (origin[1] < 0.0f || origin[1] > 0.0f)
868 		{
869 			propList.insert("style:vertical-pos", "from-top");
870 			newPosition = (h - pos.size()[1])/2.0f;
871 			if (newPosition > h -pos.size()[1]) newPosition = h - pos.size()[1];
872 			propList.insert("svg:y", double(newPosition), unit);
873 		}
874 		else
875 			propList.insert("style:vertical-pos", "middle");
876 		break;
877 	case WPSPosition::YBottom:
878 		if (origin[1] < 0.0f || origin[1] > 0.0f)
879 		{
880 			propList.insert("style:vertical-pos", "from-top");
881 			newPosition = h - pos.size()[1]-origin[1];
882 			if (newPosition > h -pos.size()[1]) newPosition = h -pos.size()[1];
883 			else if (newPosition < 0) newPosition = 0;
884 			propList.insert("svg:y", double(newPosition), unit);
885 		}
886 		else
887 			propList.insert("style:vertical-pos", "bottom");
888 		break;
889 	default:
890 		break;
891 	}
892 
893 	switch (pos.m_xPos)
894 	{
895 	case WPSPosition::XFull:
896 		propList.insert("svg:width", double(w), unit);
897 		WPS_FALLTHROUGH;
898 	case WPSPosition::XLeft:
899 		if (origin[0] < 0.0f || origin[0] > 0.0f)
900 		{
901 			propList.insert("style:horizontal-pos", "from-left");
902 			propList.insert("svg:x", double(origin[0]), unit);
903 		}
904 		else
905 			propList.insert("style:horizontal-pos", "left");
906 		break;
907 	case WPSPosition::XRight:
908 		if (origin[0] < 0.0f || origin[0] > 0.0f)
909 		{
910 			propList.insert("style:horizontal-pos", "from-left");
911 			propList.insert("svg:x",double(w - pos.size()[0] + origin[0]), unit);
912 		}
913 		else
914 			propList.insert("style:horizontal-pos", "right");
915 		break;
916 	case WPSPosition::XCenter:
917 	default:
918 		if (origin[0] < 0.0f || origin[0] > 0.0f)
919 		{
920 			propList.insert("style:horizontal-pos", "from-left");
921 			propList.insert("svg:x", double((w - pos.size()[0])/2.f + origin[0]), unit);
922 		}
923 		else
924 			propList.insert("style:horizontal-pos", "center");
925 		break;
926 	}
927 }
928 
929 ///////////////////
930 // subdocument
931 ///////////////////
handleSubDocument(WPSSubDocumentPtr & subDocument,libwps::SubDocumentType subDocumentType)932 void WKSContentListener::handleSubDocument(WPSSubDocumentPtr &subDocument, libwps::SubDocumentType subDocumentType)
933 {
934 	_pushParsingState();
935 	_startSubDocument();
936 
937 	m_ps->m_subDocumentType = subDocumentType;
938 	m_ps->m_isPageSpanOpened = true;
939 
940 	// Check whether the document is calling itself
941 	bool sendDoc = true;
942 	for (auto subDoc : m_ds->m_subDocuments)
943 	{
944 		if (!subDocument)
945 			break;
946 		if (!subDoc)
947 			continue;
948 		if (*subDocument == subDoc)
949 		{
950 			WPS_DEBUG_MSG(("WKSContentListener::handleSubDocument: recursif call, stop...\n"));
951 			sendDoc = false;
952 			break;
953 		}
954 	}
955 	if (sendDoc)
956 	{
957 		if (subDocument)
958 		{
959 			m_ds->m_subDocuments.push_back(subDocument);
960 			std::shared_ptr<WKSContentListener> listen(this, WPS_shared_ptr_noop_deleter<WKSContentListener>());
961 			try
962 			{
963 				auto *subDoc=dynamic_cast<WKSSubDocument *>(subDocument.get());
964 				if (subDoc)
965 					subDoc->parse(listen, subDocumentType);
966 				else
967 				{
968 					WPS_DEBUG_MSG(("Works: WKSContentListener::handleSubDocument bad subdocument\n"));
969 				}
970 			}
971 			catch (...)
972 			{
973 				WPS_DEBUG_MSG(("Works: WKSContentListener::handleSubDocument exception catched \n"));
974 			}
975 			m_ds->m_subDocuments.pop_back();
976 		}
977 	}
978 
979 	_endSubDocument();
980 	_popParsingState();
981 }
982 
_startSubDocument()983 void WKSContentListener::_startSubDocument()
984 {
985 	m_ds->m_isDocumentStarted = true;
986 	m_ps->m_inSubDocument = true;
987 }
988 
_endSubDocument()989 void WKSContentListener::_endSubDocument()
990 {
991 	if (m_ps->m_isSheetOpened)
992 		closeSheet();
993 	if (m_ps->m_isParagraphOpened)
994 		_closeParagraph();
995 }
996 
997 ///////////////////
998 // sheet
999 ///////////////////
openSheet(std::vector<WPSColumnFormat> const & colList,librevenge::RVNGString const & name)1000 void WKSContentListener::openSheet(std::vector<WPSColumnFormat> const &colList, librevenge::RVNGString const &name)
1001 {
1002 	if (m_ps->m_isSheetOpened)
1003 	{
1004 		WPS_DEBUG_MSG(("WKSContentListener::openSheet: called with m_isSheetOpened=true\n"));
1005 		return;
1006 	}
1007 	if (!m_ps->m_isPageSpanOpened)
1008 		_openPageSpan();
1009 	if (m_ps->m_isParagraphOpened)
1010 		_closeParagraph();
1011 
1012 	_pushParsingState();
1013 	_startSubDocument();
1014 	m_ps->m_subDocumentType = libwps::DOC_TABLE;
1015 	m_ps->m_isPageSpanOpened = true;
1016 
1017 	librevenge::RVNGPropertyList propList;
1018 
1019 	librevenge::RVNGPropertyListVector columns;
1020 
1021 	for (auto const &col : colList)
1022 	{
1023 		librevenge::RVNGPropertyList column;
1024 		col.addTo(column);
1025 		columns.append(column);
1026 	}
1027 	propList.insert("librevenge:columns", columns);
1028 	if (!name.empty())
1029 		propList.insert("librevenge:sheet-name", name);
1030 	m_documentInterface->openSheet(propList);
1031 	m_ps->m_isSheetOpened = true;
1032 }
1033 
closeSheet()1034 void WKSContentListener::closeSheet()
1035 {
1036 	if (!m_ps->m_isSheetOpened)
1037 	{
1038 		WPS_DEBUG_MSG(("WKSContentListener::closeSheet: called with m_isSheetOpened=false\n"));
1039 		return;
1040 	}
1041 
1042 	m_ps->m_isSheetOpened = false;
1043 	_endSubDocument();
1044 	m_documentInterface->closeSheet();
1045 
1046 	_popParsingState();
1047 }
1048 
openSheetRow(WPSRowFormat const & format,int numRepeated)1049 void WKSContentListener::openSheetRow(WPSRowFormat const &format, int numRepeated)
1050 {
1051 	if (m_ps->m_isSheetRowOpened)
1052 	{
1053 		WPS_DEBUG_MSG(("WKSContentListener::openSheetRow: called with m_isSheetRowOpened=true\n"));
1054 		return;
1055 	}
1056 	if (!m_ps->m_isSheetOpened)
1057 	{
1058 		WPS_DEBUG_MSG(("WKSContentListener::openSheetRow: called with m_isSheetOpened=false\n"));
1059 		return;
1060 	}
1061 	librevenge::RVNGPropertyList propList;
1062 	format.addTo(propList);
1063 	if (numRepeated>1)
1064 		propList.insert("table:number-rows-repeated", numRepeated);
1065 	m_documentInterface->openSheetRow(propList);
1066 	m_ps->m_isSheetRowOpened = true;
1067 }
1068 
closeSheetRow()1069 void WKSContentListener::closeSheetRow()
1070 {
1071 	if (!m_ps->m_isSheetRowOpened)
1072 	{
1073 		WPS_DEBUG_MSG(("WKSContentListener::openSheetRow: called with m_isSheetRowOpened=false\n"));
1074 		return;
1075 	}
1076 	m_ps->m_isSheetRowOpened = false;
1077 	m_documentInterface->closeSheetRow();
1078 }
1079 
openSheetCell(WPSCell const & cell,WKSContentListener::CellContent const & content,int numRepeated)1080 void WKSContentListener::openSheetCell(WPSCell const &cell, WKSContentListener::CellContent const &content, int numRepeated)
1081 {
1082 	if (!m_ps->m_isSheetRowOpened)
1083 	{
1084 		WPS_DEBUG_MSG(("WKSContentListener::openSheetCell: called with m_isSheetRowOpened=false\n"));
1085 		return;
1086 	}
1087 	if (m_ps->m_isSheetCellOpened)
1088 	{
1089 		WPS_DEBUG_MSG(("WKSContentListener::openSheetCell: called with m_isSheetCellOpened=true\n"));
1090 		closeSheetCell();
1091 	}
1092 
1093 	librevenge::RVNGPropertyList propList;
1094 	cell.addTo(propList);
1095 	if (numRepeated>1)
1096 		propList.insert("table:number-columns-repeated", numRepeated);
1097 	cell.getFont().addTo(propList);
1098 	if (!cell.hasBasicFormat())
1099 	{
1100 		int numberingId=-1;
1101 		std::stringstream name;
1102 		if (m_ds->m_numberingIdMap.find(cell)!=m_ds->m_numberingIdMap.end())
1103 		{
1104 			numberingId=m_ds->m_numberingIdMap.find(cell)->second;
1105 			name << "Numbering" << numberingId;
1106 		}
1107 		else
1108 		{
1109 			numberingId=int(m_ds->m_numberingIdMap.size());
1110 			name << "Numbering" << numberingId;
1111 
1112 			librevenge::RVNGPropertyList numList;
1113 			if (cell.getNumberingProperties(numList))
1114 			{
1115 				numList.insert("librevenge:name", name.str().c_str());
1116 				m_documentInterface->defineSheetNumberingStyle(numList);
1117 				m_ds->m_numberingIdMap[cell]=numberingId;
1118 			}
1119 			else
1120 				numberingId=-1;
1121 		}
1122 		if (numberingId>=0)
1123 			propList.insert("librevenge:numbering-name", name.str().c_str());
1124 	}
1125 	// formula
1126 	if (content.m_formula.size())
1127 	{
1128 		librevenge::RVNGPropertyListVector formulaVect;
1129 		for (auto const &form : content.m_formula)
1130 			formulaVect.append(form.getPropertyList());
1131 		propList.insert("librevenge:formula", formulaVect);
1132 	}
1133 	bool hasFormula=!content.m_formula.empty();
1134 	if (content.isValueSet() || hasFormula)
1135 	{
1136 		bool hasValue=content.isValueSet();
1137 		if (hasFormula && (content.m_value >= 0 && content.m_value <= 0))
1138 			hasValue=false;
1139 		switch (cell.getFormat())
1140 		{
1141 		case WPSCellFormat::F_TEXT:
1142 			if (!hasValue) break;
1143 			propList.insert("librevenge:value-type", cell.getValueType().c_str());
1144 			propList.insert("librevenge:value", content.m_value, librevenge::RVNG_GENERIC);
1145 			break;
1146 		case WPSCellFormat::F_NUMBER:
1147 			propList.insert("librevenge:value-type", cell.getValueType().c_str());
1148 			if (!hasValue) break;
1149 			propList.insert("librevenge:value", content.m_value, librevenge::RVNG_GENERIC);
1150 			break;
1151 		case WPSCellFormat::F_BOOLEAN:
1152 			propList.insert("librevenge:value-type", "boolean");
1153 			if (!hasValue) break;
1154 			propList.insert("librevenge:value", content.m_value, librevenge::RVNG_GENERIC);
1155 			break;
1156 		case WPSCellFormat::F_DATE:
1157 		{
1158 			propList.insert("librevenge:value-type", "date");
1159 			if (!hasValue) break;
1160 			int Y=0, M=0, D=0;
1161 			if (!CellContent::double2Date(content.m_value, Y, M, D)) break;
1162 			propList.insert("librevenge:year", Y);
1163 			propList.insert("librevenge:month", M);
1164 			propList.insert("librevenge:day", D);
1165 			break;
1166 		}
1167 		case WPSCellFormat::F_TIME:
1168 		{
1169 			propList.insert("librevenge:value-type", "time");
1170 			if (!hasValue) break;
1171 			int H=0, M=0, S=0;
1172 			if (!CellContent::double2Time(content.m_value,H,M,S))
1173 				break;
1174 			propList.insert("librevenge:hours", H);
1175 			propList.insert("librevenge:minutes", M);
1176 			propList.insert("librevenge:seconds", S);
1177 			break;
1178 		}
1179 		case WPSCellFormat::F_UNKNOWN:
1180 			if (!hasValue) break;
1181 			propList.insert("librevenge:value-type", cell.getValueType().c_str());
1182 			propList.insert("librevenge:value", content.m_value, librevenge::RVNG_GENERIC);
1183 			break;
1184 		default:
1185 			break;
1186 		}
1187 	}
1188 
1189 	m_ps->m_isSheetCellOpened = true;
1190 	m_documentInterface->openSheetCell(propList);
1191 }
1192 
closeSheetCell()1193 void WKSContentListener::closeSheetCell()
1194 {
1195 	if (!m_ps->m_isSheetCellOpened)
1196 	{
1197 		WPS_DEBUG_MSG(("WKSContentListener::closeSheetCell: called with m_isSheetCellOpened=false\n"));
1198 		return;
1199 	}
1200 
1201 	_closeParagraph();
1202 
1203 	m_ps->m_isSheetCellOpened = false;
1204 	m_documentInterface->closeSheetCell();
1205 }
1206 
1207 ///////////////////
1208 // page
1209 ///////////////////
_openPageSpan()1210 void WKSContentListener::_openPageSpan()
1211 {
1212 	if (m_ps->m_isPageSpanOpened)
1213 		return;
1214 
1215 	if (!m_ds->m_isDocumentStarted)
1216 		startDocument();
1217 
1218 	if (m_ds->m_pageList.size()==0)
1219 	{
1220 		WPS_DEBUG_MSG(("WKSContentListener::_openPageSpan: can not find any page\n"));
1221 		throw libwps::ParseException();
1222 	}
1223 	unsigned actPage = 0;
1224 	auto it = m_ds->m_pageList.begin();
1225 	while (actPage < m_ps->m_currentPage)
1226 	{
1227 		actPage+=unsigned(it++->getPageSpan());
1228 		if (it == m_ds->m_pageList.end())
1229 		{
1230 			WPS_DEBUG_MSG(("WKSContentListener::_openPageSpan: can not find current page\n"));
1231 			throw libwps::ParseException();
1232 		}
1233 	}
1234 	WPSPageSpan &currentPage = *it;
1235 
1236 	librevenge::RVNGPropertyList propList;
1237 	currentPage.getPageProperty(propList);
1238 	propList.insert("librevenge:is-last-page-span", ((m_ps->m_currentPage + 1 == m_ds->m_pageList.size()) ? true : false));
1239 
1240 	if (!m_ps->m_isPageSpanOpened)
1241 		m_documentInterface->openPageSpan(propList);
1242 
1243 	m_ps->m_isPageSpanOpened = true;
1244 
1245 	m_ps->m_pageFormLength = currentPage.getFormLength();
1246 	m_ps->m_pageFormWidth = currentPage.getFormWidth();
1247 	m_ps->m_pageMarginLeft = currentPage.getMarginLeft();
1248 	m_ps->m_pageMarginRight = currentPage.getMarginRight();
1249 	m_ps->m_pageFormOrientationIsPortrait =
1250 	    currentPage.getFormOrientation() == WPSPageSpan::PORTRAIT;
1251 	m_ps->m_pageMarginTop = currentPage.getMarginTop();
1252 	m_ps->m_pageMarginBottom = currentPage.getMarginBottom();
1253 
1254 	// we insert the header footer
1255 	currentPage.sendHeaderFooters(this, m_documentInterface);
1256 
1257 	// first paragraph in span (necessary for resetting page number)
1258 	m_ps->m_numPagesRemainingInSpan = (currentPage.getPageSpan() - 1);
1259 	m_ps->m_currentPage++;
1260 }
1261 
_closePageSpan()1262 void WKSContentListener::_closePageSpan()
1263 {
1264 	if (!m_ps->m_isPageSpanOpened)
1265 		return;
1266 
1267 	if (m_ps->m_isParagraphOpened)
1268 		_closeParagraph();
1269 
1270 	m_documentInterface->closePageSpan();
1271 	m_ps->m_isPageSpanOpened = false;
1272 }
1273 
1274 ///////////////////
1275 // others
1276 ///////////////////
1277 
1278 // ---------- state stack ------------------
_pushParsingState()1279 std::shared_ptr<WKSContentParsingState> WKSContentListener::_pushParsingState()
1280 {
1281 	auto actual = m_ps;
1282 	m_psStack.push_back(actual);
1283 	m_ps.reset(new WKSContentParsingState);
1284 
1285 	// BEGIN: copy page properties into the new parsing state
1286 	m_ps->m_pageFormLength = actual->m_pageFormLength;
1287 	m_ps->m_pageFormWidth = actual->m_pageFormWidth;
1288 	m_ps->m_pageFormOrientationIsPortrait =	actual->m_pageFormOrientationIsPortrait;
1289 	m_ps->m_pageMarginLeft = actual->m_pageMarginLeft;
1290 	m_ps->m_pageMarginRight = actual->m_pageMarginRight;
1291 	m_ps->m_pageMarginTop = actual->m_pageMarginTop;
1292 	m_ps->m_pageMarginBottom = actual->m_pageMarginBottom;
1293 
1294 	m_ps->m_isNote = actual->m_isNote;
1295 	m_ps->m_isPageSpanOpened = actual->m_isPageSpanOpened;
1296 
1297 	return actual;
1298 }
1299 
_popParsingState()1300 void WKSContentListener::_popParsingState()
1301 {
1302 	if (m_psStack.size()==0)
1303 	{
1304 		WPS_DEBUG_MSG(("WKSContentListener::_popParsingState: psStack is empty()\n"));
1305 		throw libwps::ParseException();
1306 	}
1307 	m_ps = m_psStack.back();
1308 	m_psStack.pop_back();
1309 }
1310 
1311 // ---------- WKSContentListener::FormulaInstruction ------------------
getPropertyList() const1312 librevenge::RVNGPropertyList WKSContentListener::FormulaInstruction::getPropertyList() const
1313 {
1314 	librevenge::RVNGPropertyList pList;
1315 	switch (m_type)
1316 	{
1317 	case F_Operator:
1318 		pList.insert("librevenge:type","librevenge-operator");
1319 		pList.insert("librevenge:operator",m_content.c_str());
1320 		break;
1321 	case F_Function:
1322 		pList.insert("librevenge:type","librevenge-function");
1323 		pList.insert("librevenge:function",m_content.c_str());
1324 		break;
1325 	case F_Text:
1326 		pList.insert("librevenge:type","librevenge-text");
1327 		pList.insert("librevenge:text",m_content.c_str());
1328 		break;
1329 	case F_Double:
1330 		pList.insert("librevenge:type","librevenge-number");
1331 		pList.insert("librevenge:number",m_doubleValue, librevenge::RVNG_GENERIC);
1332 		break;
1333 	case F_Long:
1334 		pList.insert("librevenge:type","librevenge-number");
1335 		pList.insert("librevenge:number",m_longValue, librevenge::RVNG_GENERIC);
1336 		break;
1337 	case F_Cell:
1338 		pList.insert("librevenge:type","librevenge-cell");
1339 		pList.insert("librevenge:column",m_position[0][0]);
1340 		pList.insert("librevenge:row",m_position[0][1]);
1341 		pList.insert("librevenge:column-absolute",!m_positionRelative[0][0]);
1342 		pList.insert("librevenge:row-absolute",!m_positionRelative[0][1]);
1343 		if (!m_sheetName[0].empty())
1344 			pList.insert("librevenge:sheet-name",m_sheetName[0].cstr());
1345 		if (!m_fileName.empty())
1346 			pList.insert("librevenge:file-name",m_fileName.cstr());
1347 		break;
1348 	case F_CellList:
1349 		pList.insert("librevenge:type","librevenge-cells");
1350 		pList.insert("librevenge:start-column",m_position[0][0]);
1351 		pList.insert("librevenge:start-row",m_position[0][1]);
1352 		pList.insert("librevenge:start-column-absolute",!m_positionRelative[0][0]);
1353 		pList.insert("librevenge:start-row-absolute",!m_positionRelative[0][1]);
1354 		pList.insert("librevenge:end-column",m_position[1][0]);
1355 		pList.insert("librevenge:end-row",m_position[1][1]);
1356 		pList.insert("librevenge:end-column-absolute",!m_positionRelative[1][0]);
1357 		pList.insert("librevenge:end-row-absolute",!m_positionRelative[1][1]);
1358 		if (!m_sheetName[0].empty())
1359 			pList.insert("librevenge:sheet-name",m_sheetName[0].cstr());
1360 		if (!m_sheetName[1].empty())
1361 			pList.insert("librevenge:end-sheet-name",m_sheetName[1].cstr());
1362 		if (!m_fileName.empty())
1363 			pList.insert("librevenge:file-name",m_fileName.cstr());
1364 		break;
1365 	default:
1366 		WPS_DEBUG_MSG(("WKSContentListener::FormulaInstruction::getPropertyList: unexpected type\n"));
1367 	}
1368 	return pList;
1369 }
1370 
operator <<(std::ostream & o,WKSContentListener::FormulaInstruction const & inst)1371 std::ostream &operator<<(std::ostream &o, WKSContentListener::FormulaInstruction const &inst)
1372 {
1373 	if (inst.m_type==WKSContentListener::FormulaInstruction::F_Double)
1374 		o << inst.m_doubleValue;
1375 	else if (inst.m_type==WKSContentListener::FormulaInstruction::F_Long)
1376 		o << inst.m_longValue;
1377 	else if (inst.m_type==WKSContentListener::FormulaInstruction::F_Cell)
1378 	{
1379 		o << libwps::getCellName(inst.m_position[0],inst.m_positionRelative[0]);
1380 		if (!inst.m_sheetName[0].empty())
1381 			o << "[" << inst.m_sheetName[0].cstr() << "]";
1382 		else if (inst.m_sheetId[0]>=0)
1383 			o << "[sheet" << inst.m_sheetId[0] << "]";
1384 		if (!inst.m_fileName.empty())
1385 			o << "[file=" << inst.m_fileName.cstr() << "]";
1386 	}
1387 	else if (inst.m_type==WKSContentListener::FormulaInstruction::F_CellList)
1388 	{
1389 		for (int l=0; l<2; ++l)
1390 		{
1391 			o << libwps::getCellName(inst.m_position[l],inst.m_positionRelative[l]);
1392 			if (!inst.m_sheetName[l].empty())
1393 				o << "[" << inst.m_sheetName[l].cstr() << "]";
1394 			else if (inst.m_sheetId[l]>=0)
1395 				o << "[sheet" << inst.m_sheetId[l] << "]";
1396 			if (l==0) o << ":";
1397 		}
1398 		if (!inst.m_fileName.empty())
1399 			o << "[file=" << inst.m_fileName.cstr() << "]";
1400 	}
1401 	else if (inst.m_type==WKSContentListener::FormulaInstruction::F_Text)
1402 		o << "\"" << inst.m_content << "\"";
1403 	else
1404 		o << inst.m_content;
1405 	return o;
1406 }
1407 
1408 // ---------- WKSContentListener::CellContent ------------------
double2Date(double val,int & Y,int & M,int & D)1409 bool WKSContentListener::CellContent::double2Date(double val, int &Y, int &M, int &D)
1410 {
1411 	/* first convert the date in long. Checkme: unsure why -2 is needed...*/
1412 	auto numDaysSinceOrigin=long(val-2+0.4);
1413 	// checkme: do we need to check before for isNan(val) ?
1414 	if (numDaysSinceOrigin<-10000*365 || numDaysSinceOrigin>10000*365)
1415 	{
1416 		/* normally, we can expect documents to contain date between 1904
1417 		   and 2004. So even if such a date can make sense, storing it as
1418 		   a number of days is clearly abnormal */
1419 		WPS_DEBUG_MSG(("WKSContentListener::CellContent::double2Date: using a double to represent the date %ld seems odd\n", numDaysSinceOrigin));
1420 		Y=1904;
1421 		M=D=1;
1422 		return false;
1423 	}
1424 	// find the century
1425 	int century=19;
1426 	while (numDaysSinceOrigin>=36500+24)
1427 	{
1428 		long numDaysInCentury=36500+24+((century%4)?0:1);
1429 		if (numDaysSinceOrigin<numDaysInCentury) break;
1430 		numDaysSinceOrigin-=numDaysInCentury;
1431 		++century;
1432 	}
1433 	while (numDaysSinceOrigin<0)
1434 	{
1435 		--century;
1436 		numDaysSinceOrigin+=36500+24+((century%4)?0:1);
1437 	}
1438 	// now compute the year
1439 	Y=int(numDaysSinceOrigin/365);
1440 	long numDaysToEndY1=Y*365+(Y>0 ? (Y-1)/4+((century%4)?0:1) : 0);
1441 	if (numDaysToEndY1>numDaysSinceOrigin)
1442 	{
1443 		--Y;
1444 		numDaysToEndY1=Y*365+(Y>0 ? (Y-1)/4+((century%4)?0:1) : 0);
1445 	}
1446 	// finish to compute the date
1447 	auto numDaysFrom1Jan=int(numDaysSinceOrigin-numDaysToEndY1);
1448 	Y+=century*100;
1449 	bool isLeap=(Y%4)==0 && ((Y%400)==0 || (Y%100)!=0);
1450 
1451 	for (M=0; M<12; ++M)
1452 	{
1453 		static const int days[2][12] =
1454 		{
1455 			{ 0,31,59,90,120,151,181,212,243,273,304,334},
1456 			{ 0,31,60,91,121,152,182,213,244,274,305,335}
1457 		};
1458 		if (M<11 && days[isLeap ? 1 : 0][M+1]<=numDaysFrom1Jan) continue;
1459 		D=(numDaysFrom1Jan-days[isLeap ? 1 : 0][M++])+1;
1460 		break;
1461 	}
1462 	return true;
1463 }
1464 
double2Time(double val,int & H,int & M,int & S)1465 bool WKSContentListener::CellContent::double2Time(double val, int &H, int &M, int &S)
1466 {
1467 	if (val < 0.0 || val > 1.0) return false;
1468 	double time = 24.*3600.*val+0.5;
1469 	H=int(time/3600.);
1470 	time -= H*3600.;
1471 	M=int(time/60.);
1472 	time -= M*60.;
1473 	S=int(time);
1474 	return true;
1475 }
1476 
operator <<(std::ostream & o,WKSContentListener::CellContent const & cell)1477 std::ostream &operator<<(std::ostream &o, WKSContentListener::CellContent const &cell)
1478 {
1479 
1480 	switch (cell.m_contentType)
1481 	{
1482 	case WKSContentListener::CellContent::C_NONE:
1483 		break;
1484 	case WKSContentListener::CellContent::C_TEXT:
1485 		o << ",text=\"" << cell.m_textEntry << "\"";
1486 		break;
1487 	case WKSContentListener::CellContent::C_NUMBER:
1488 	{
1489 		o << ",val=";
1490 		bool textAndVal = false;
1491 		if (cell.hasText())
1492 		{
1493 			o << "entry=" << cell.m_textEntry;
1494 			textAndVal = cell.isValueSet();
1495 		}
1496 		if (textAndVal) o << "[";
1497 		if (cell.isValueSet()) o << cell.m_value;
1498 		if (textAndVal) o << "]";
1499 	}
1500 	break;
1501 	case WKSContentListener::CellContent::C_FORMULA:
1502 		o << ",formula=";
1503 		for (const auto &l : cell.m_formula)
1504 			o << l;
1505 		if (cell.isValueSet()) o << "[" << cell.m_value << "]";
1506 		break;
1507 	case WKSContentListener::CellContent::C_UNKNOWN:
1508 		break;
1509 	default:
1510 		o << "###unknown type,";
1511 		break;
1512 	}
1513 	return o;
1514 }
1515 
1516 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
1517