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 "WPSPageSpan.h"
40 #include "WPSParagraph.h"
41 #include "WPSPosition.h"
42 #include "WKSSubDocument.h"
43 
44 ////////////////////////////////////////////////////////////
45 //! the document state
46 struct WKSDocumentParsingState
47 {
48 	//! constructor
49 	WKSDocumentParsingState(std::vector<WPSPageSpan> const &pageList);
50 	//! destructor
51 	~WKSDocumentParsingState();
52 
53 	std::vector<WPSPageSpan> m_pageList;
54 	librevenge::RVNGPropertyList m_metaData;
55 
56 	bool m_isDocumentStarted, m_isHeaderFooterStarted;
57 	std::vector<WPSSubDocumentPtr> m_subDocuments; /** list of document actually open */
58 
59 	/** a map cell's format to id */
60 	std::map<WPSCellFormat,int,WPSCellFormat::CompareFormat> m_numberingIdMap;
61 
62 private:
63 	WKSDocumentParsingState(const WKSDocumentParsingState &);
64 	WKSDocumentParsingState &operator=(const WKSDocumentParsingState &);
65 };
66 
WKSDocumentParsingState(std::vector<WPSPageSpan> const & pageList)67 WKSDocumentParsingState::WKSDocumentParsingState(std::vector<WPSPageSpan> const &pageList) :
68 	m_pageList(pageList), m_metaData(), m_isDocumentStarted(false), m_isHeaderFooterStarted(false), m_subDocuments(), m_numberingIdMap()
69 {
70 }
71 
~WKSDocumentParsingState()72 WKSDocumentParsingState::~WKSDocumentParsingState()
73 {
74 }
75 
76 ////////////////////////////////////////////////////////////
77 //! the spreadsheet state
78 struct WKSContentParsingState
79 {
80 	WKSContentParsingState();
81 	~WKSContentParsingState();
82 
83 	bool m_isPageSpanOpened;
84 	unsigned m_currentPage;
85 	int m_numPagesRemainingInSpan;
86 	int m_currentPageNumber;
87 
88 	double m_pageFormLength;
89 	double m_pageFormWidth;
90 	bool m_pageFormOrientationIsPortrait;
91 
92 	double m_pageMarginLeft;
93 	double m_pageMarginRight;
94 	double m_pageMarginTop;
95 	double m_pageMarginBottom;
96 
97 	librevenge::RVNGString m_textBuffer;
98 	int m_numDeferredTabs;
99 
100 	WPSFont m_font;
101 	WPSParagraph m_paragraph;
102 
103 	bool m_isParagraphColumnBreak;
104 	bool m_isParagraphPageBreak;
105 
106 	bool m_isSpanOpened;
107 	bool m_isParagraphOpened;
108 
109 	bool m_isSheetOpened;
110 	bool m_isSheetRowOpened;
111 	bool m_isSheetColumnOpened;
112 	bool m_isSheetCellOpened;
113 
114 	bool m_inSubDocument;
115 
116 	bool m_isNote;
117 	libwps::SubDocumentType m_subDocumentType;
118 
119 private:
120 	WKSContentParsingState(const WKSContentParsingState &);
121 	WKSContentParsingState &operator=(const WKSContentParsingState &);
122 };
123 
WKSContentParsingState()124 WKSContentParsingState::WKSContentParsingState() :
125 	m_isPageSpanOpened(false), m_currentPage(0), m_numPagesRemainingInSpan(0), m_currentPageNumber(1),
126 	m_pageFormLength(11.0),	m_pageFormWidth(8.5f), m_pageFormOrientationIsPortrait(true),
127 	m_pageMarginLeft(1.0), m_pageMarginRight(1.0), m_pageMarginTop(1.0), m_pageMarginBottom(1.0),
128 
129 	m_textBuffer(""), m_numDeferredTabs(0),
130 
131 	m_font(), m_paragraph(),
132 
133 	m_isParagraphColumnBreak(false), m_isParagraphPageBreak(false),
134 
135 	m_isSpanOpened(false), m_isParagraphOpened(false),
136 
137 	m_isSheetOpened(false), m_isSheetRowOpened(false), m_isSheetColumnOpened(false),
138 	m_isSheetCellOpened(false),
139 
140 	m_inSubDocument(false),
141 	m_isNote(false),
142 	m_subDocumentType(libwps::DOC_NONE)
143 {
144 	m_font.m_size=12.0;
145 	m_font.m_name="Times New Roman";
146 }
147 
~WKSContentParsingState()148 WKSContentParsingState::~WKSContentParsingState()
149 {
150 }
151 
WKSContentListener(std::vector<WPSPageSpan> const & pageList,librevenge::RVNGSpreadsheetInterface * documentInterface)152 WKSContentListener::WKSContentListener(std::vector<WPSPageSpan> const &pageList, librevenge::RVNGSpreadsheetInterface *documentInterface) :
153 	m_ds(new WKSDocumentParsingState(pageList)), m_ps(new WKSContentParsingState), m_psStack(),
154 	m_documentInterface(documentInterface)
155 {
156 }
157 
~WKSContentListener()158 WKSContentListener::~WKSContentListener()
159 {
160 }
161 
162 ///////////////////
163 // text data
164 ///////////////////
insertCharacter(uint8_t character)165 void WKSContentListener::insertCharacter(uint8_t character)
166 {
167 	if (character >= 0x80)
168 	{
169 		insertUnicode(character);
170 		return;
171 	}
172 	_flushDeferredTabs();
173 	if (!m_ps->m_isSpanOpened) _openSpan();
174 	m_ps->m_textBuffer.append(char(character));
175 }
176 
insertUnicode(uint32_t val)177 void WKSContentListener::insertUnicode(uint32_t val)
178 {
179 	// undef character, we skip it
180 	if (val == 0xfffd) return;
181 	_flushDeferredTabs();
182 	if (!m_ps->m_isSpanOpened) _openSpan();
183 	appendUnicode(val, m_ps->m_textBuffer);
184 }
185 
insertUnicodeString(librevenge::RVNGString const & str)186 void WKSContentListener::insertUnicodeString(librevenge::RVNGString const &str)
187 {
188 	_flushDeferredTabs();
189 	if (!m_ps->m_isSpanOpened) _openSpan();
190 	m_ps->m_textBuffer.append(str);
191 }
192 
insertEOL(bool soft)193 void WKSContentListener::insertEOL(bool soft)
194 {
195 	if (!m_ps->m_isParagraphOpened)
196 		_openSpan();
197 	_flushDeferredTabs();
198 
199 	if (soft)
200 	{
201 		if (m_ps->m_isSpanOpened)
202 			_flushText();
203 		m_documentInterface->insertLineBreak();
204 	}
205 	else if (m_ps->m_isParagraphOpened)
206 		_closeParagraph();
207 
208 	// sub/superscript must not survive a new line
209 	static const uint32_t s_subsuperBits = WPS_SUBSCRIPT_BIT | WPS_SUPERSCRIPT_BIT;
210 	if (m_ps->m_font.m_attributes & s_subsuperBits)
211 		m_ps->m_font.m_attributes &= ~s_subsuperBits;
212 }
213 
insertTab()214 void WKSContentListener::insertTab()
215 {
216 	if (!m_ps->m_isParagraphOpened)
217 	{
218 		m_ps->m_numDeferredTabs++;
219 		return;
220 	}
221 	if (m_ps->m_isSpanOpened) _flushText();
222 	m_ps->m_numDeferredTabs++;
223 	_flushDeferredTabs();
224 }
225 
insertBreak(const uint8_t breakType)226 void WKSContentListener::insertBreak(const uint8_t breakType)
227 {
228 	switch (breakType)
229 	{
230 	case WPS_COLUMN_BREAK:
231 		if (m_ps->m_isParagraphOpened)
232 			_closeParagraph();
233 		m_ps->m_isParagraphColumnBreak = true;
234 		break;
235 	case WPS_PAGE_BREAK:
236 		if (m_ps->m_isParagraphOpened)
237 			_closeParagraph();
238 		m_ps->m_isParagraphPageBreak = true;
239 		break;
240 	default:
241 		break;
242 	}
243 }
244 
_insertBreakIfNecessary(librevenge::RVNGPropertyList & propList)245 void WKSContentListener::_insertBreakIfNecessary(librevenge::RVNGPropertyList &propList)
246 {
247 	if (m_ps->m_isParagraphPageBreak && !m_ps->m_inSubDocument)
248 	{
249 		// no hard page-breaks in subdocuments
250 		propList.insert("fo:break-before", "page");
251 		m_ps->m_isParagraphPageBreak = false;
252 	}
253 }
254 
255 ///////////////////
256 // font/character format
257 ///////////////////
setFont(const WPSFont & font)258 void WKSContentListener::setFont(const WPSFont &font)
259 {
260 	WPSFont newFont(font);
261 	if (font.m_size<=0)
262 		newFont.m_size=m_ps->m_font.m_size;
263 	if (font.m_name.empty())
264 		newFont.m_name=m_ps->m_font.m_name;
265 	if (font.m_languageId <= 0)
266 		newFont.m_languageId=m_ps->m_font.m_languageId;
267 	if (m_ps->m_font==newFont) return;
268 	_closeSpan();
269 	m_ps->m_font=newFont;
270 }
271 
getFont() const272 WPSFont const &WKSContentListener::getFont() const
273 {
274 	return m_ps->m_font;
275 }
276 
277 ///////////////////
278 // paragraph tabs, tabs/...
279 ///////////////////
isParagraphOpened() const280 bool WKSContentListener::isParagraphOpened() const
281 {
282 	return m_ps->m_isParagraphOpened;
283 }
284 
getParagraph() const285 WPSParagraph const &WKSContentListener::getParagraph() const
286 {
287 	return m_ps->m_paragraph;
288 }
289 
setParagraph(const WPSParagraph & para)290 void WKSContentListener::setParagraph(const WPSParagraph &para)
291 {
292 	m_ps->m_paragraph=para;
293 }
294 
295 ///////////////////
296 // field :
297 ///////////////////
298 #include <time.h>
insertField(WKSContentListener::FieldType type)299 void WKSContentListener::insertField(WKSContentListener::FieldType type)
300 {
301 	switch (type)
302 	{
303 	case None:
304 		break;
305 	case PageNumber:
306 	{
307 		_flushText();
308 		_openSpan();
309 		librevenge::RVNGPropertyList propList;
310 		propList.insert("style:num-format", libwps::numberingTypeToString(libwps::ARABIC).c_str());
311 		propList.insert("librevenge:field-type", "text:page-number");
312 		m_documentInterface->insertField(propList);
313 		break;
314 	}
315 	case Database:
316 	{
317 		librevenge::RVNGString tmp("#DATAFIELD#");
318 		insertUnicodeString(tmp);
319 		break;
320 	}
321 	case Title:
322 	{
323 		librevenge::RVNGString tmp("#TITLE#");
324 		insertUnicodeString(tmp);
325 		break;
326 	}
327 	case Date:
328 		insertDateTimeField("%m/%d/%y");
329 		break;
330 	case Time:
331 		insertDateTimeField("%I:%M:%S %p");
332 		break;
333 	case Link:
334 	default:
335 		WPS_DEBUG_MSG(("WKSContentListener::insertField: must not be called with type=%d\n", int(type)));
336 		break;
337 	}
338 }
339 
insertDateTimeField(char const * format)340 void WKSContentListener::insertDateTimeField(char const *format)
341 {
342 	if (!format)
343 	{
344 		WPS_DEBUG_MSG(("WKSContentListener::insertDateTimeField: oops, can not find the format\n"));
345 		return;
346 	}
347 	time_t now = time(0L);
348 	struct tm timeinfo;
349 	if (localtime_r(&now, &timeinfo))
350 	{
351 		char buf[256];
352 		strftime(buf, 256, format, &timeinfo);
353 		insertUnicodeString(librevenge::RVNGString(buf));
354 	}
355 }
356 
357 ///////////////////
358 // document
359 ///////////////////
setDocumentLanguage(int lcid)360 void WKSContentListener::setDocumentLanguage(int lcid)
361 {
362 	if (lcid <= 0) return;
363 	std::string lang = libwps_tools_win::Language::localeName(lcid);
364 	if (!lang.length()) return;
365 	m_ds->m_metaData.insert("librevenge:language", lang.c_str());
366 }
367 
startDocument()368 void WKSContentListener::startDocument()
369 {
370 	if (m_ds->m_isDocumentStarted)
371 	{
372 		WPS_DEBUG_MSG(("WKSContentListener::startDocument: the document is already started\n"));
373 		return;
374 	}
375 
376 	m_documentInterface->startDocument(librevenge::RVNGPropertyList());
377 	m_ds->m_isDocumentStarted = true;
378 
379 	// FIXME: this is stupid, we should store a property list filled with the relevant metadata
380 	// and then pass that directly..
381 	m_documentInterface->setDocumentMetaData(m_ds->m_metaData);
382 }
383 
endDocument()384 void WKSContentListener::endDocument()
385 {
386 	if (!m_ds->m_isDocumentStarted)
387 	{
388 		WPS_DEBUG_MSG(("WKSContentListener::startDocument: the document is not started\n"));
389 		return;
390 	}
391 
392 	if (m_ps->m_isSheetOpened)
393 		closeSheet();
394 	if (m_ps->m_isParagraphOpened)
395 		_closeParagraph();
396 
397 	// close the document nice and tight
398 	_closePageSpan();
399 	m_documentInterface->endDocument();
400 	m_ds->m_isDocumentStarted = false;
401 }
402 
403 ///////////////////
404 // paragraph
405 ///////////////////
_openParagraph()406 void WKSContentListener::_openParagraph()
407 {
408 	if (m_ps->m_isSheetOpened && !m_ps->m_isSheetCellOpened)
409 		return;
410 
411 	if (!m_ps->m_isPageSpanOpened)
412 		_openPageSpan();
413 
414 	if (m_ps->m_isParagraphOpened)
415 	{
416 		WPS_DEBUG_MSG(("WKSContentListener::_openParagraph: a paragraph (or a list) is already opened"));
417 		return;
418 	}
419 	librevenge::RVNGPropertyList propList;
420 	_appendParagraphProperties(propList);
421 
422 	if (!m_ps->m_isParagraphOpened)
423 		m_documentInterface->openParagraph(propList);
424 
425 	_resetParagraphState();
426 }
427 
_closeParagraph()428 void WKSContentListener::_closeParagraph()
429 {
430 	if (m_ps->m_isParagraphOpened)
431 	{
432 		if (m_ps->m_isSpanOpened)
433 			_closeSpan();
434 
435 		m_documentInterface->closeParagraph();
436 	}
437 
438 	m_ps->m_isParagraphOpened = false;
439 	m_ps->m_paragraph.m_listLevelIndex = 0;
440 }
441 
_resetParagraphState(const bool)442 void WKSContentListener::_resetParagraphState(const bool /*isListElement*/)
443 {
444 	m_ps->m_isParagraphColumnBreak = false;
445 	m_ps->m_isParagraphPageBreak = false;
446 	m_ps->m_isParagraphOpened = true;
447 }
448 
_appendParagraphProperties(librevenge::RVNGPropertyList & propList,const bool)449 void WKSContentListener::_appendParagraphProperties(librevenge::RVNGPropertyList &propList, const bool /*isListElement*/)
450 {
451 	m_ps->m_paragraph.addTo(propList, m_ps->m_isSheetOpened);
452 
453 	_insertBreakIfNecessary(propList);
454 }
455 
456 ///////////////////
457 // span
458 ///////////////////
_openSpan()459 void WKSContentListener::_openSpan()
460 {
461 	if (m_ps->m_isSpanOpened)
462 		return;
463 
464 	if (m_ps->m_isSheetOpened && !m_ps->m_isSheetCellOpened)
465 		return;
466 
467 	if (!m_ps->m_isParagraphOpened)
468 		_openParagraph();
469 
470 	librevenge::RVNGPropertyList propList;
471 	m_ps->m_font.addTo(propList);
472 
473 	m_documentInterface->openSpan(propList);
474 
475 	m_ps->m_isSpanOpened = true;
476 }
477 
_closeSpan()478 void WKSContentListener::_closeSpan()
479 {
480 	if (!m_ps->m_isSpanOpened)
481 		return;
482 
483 	_flushText();
484 	m_documentInterface->closeSpan();
485 	m_ps->m_isSpanOpened = false;
486 }
487 
488 ///////////////////
489 // text (send data)
490 ///////////////////
_flushDeferredTabs()491 void WKSContentListener::_flushDeferredTabs()
492 {
493 	if (m_ps->m_numDeferredTabs == 0) return;
494 
495 	// CHECKME: the tab are not underline even if the underline bit is set
496 	uint32_t oldTextAttributes = m_ps->m_font.m_attributes;
497 	uint32_t newAttributes = oldTextAttributes & uint32_t(~WPS_UNDERLINE_BIT) &
498 	                         uint32_t(~WPS_OVERLINE_BIT);
499 	if (oldTextAttributes != newAttributes)
500 	{
501 		_closeSpan();
502 		m_ps->m_font.m_attributes=newAttributes;
503 	}
504 	if (!m_ps->m_isSpanOpened) _openSpan();
505 	for (; m_ps->m_numDeferredTabs > 0; m_ps->m_numDeferredTabs--)
506 		m_documentInterface->insertTab();
507 	if (oldTextAttributes != newAttributes)
508 	{
509 		_closeSpan();
510 		m_ps->m_font.m_attributes=oldTextAttributes;
511 	}
512 }
513 
_flushText()514 void WKSContentListener::_flushText()
515 {
516 	if (m_ps->m_textBuffer.len() == 0) return;
517 
518 	// when some many ' ' follows each other, call insertSpace
519 	librevenge::RVNGString tmpText;
520 	int numConsecutiveSpaces = 0;
521 	librevenge::RVNGString::Iter i(m_ps->m_textBuffer);
522 	for (i.rewind(); i.next();)
523 	{
524 		if (*(i()) == 0x20) // this test is compatible with unicode format
525 			numConsecutiveSpaces++;
526 		else
527 			numConsecutiveSpaces = 0;
528 
529 		if (numConsecutiveSpaces > 1)
530 		{
531 			if (tmpText.len() > 0)
532 			{
533 				m_documentInterface->insertText(tmpText);
534 				tmpText.clear();
535 			}
536 			m_documentInterface->insertSpace();
537 		}
538 		else
539 			tmpText.append(i());
540 	}
541 	m_documentInterface->insertText(tmpText);
542 	m_ps->m_textBuffer.clear();
543 }
544 
545 ///////////////////
546 // Comment
547 ///////////////////
insertComment(WPSSubDocumentPtr & subDocument)548 void WKSContentListener::insertComment(WPSSubDocumentPtr &subDocument)
549 {
550 	if (m_ps->m_isNote)
551 	{
552 		WPS_DEBUG_MSG(("WKSContentListener::insertComment try to insert a note recursively (ingnored)"));
553 		return;
554 	}
555 
556 	if (!m_ps->m_isParagraphOpened)
557 		_openParagraph();
558 	else
559 	{
560 		_flushText();
561 		_closeSpan();
562 	}
563 
564 	librevenge::RVNGPropertyList propList;
565 	m_documentInterface->openComment(propList);
566 
567 	m_ps->m_isNote = true;
568 	handleSubDocument(subDocument, libwps::DOC_COMMENT_ANNOTATION);
569 
570 	m_documentInterface->closeComment();
571 	m_ps->m_isNote = false;
572 }
573 
574 ///////////////////
575 // subdocument
576 ///////////////////
handleSubDocument(WPSSubDocumentPtr & subDocument,libwps::SubDocumentType subDocumentType)577 void WKSContentListener::handleSubDocument(WPSSubDocumentPtr &subDocument, libwps::SubDocumentType subDocumentType)
578 {
579 	_pushParsingState();
580 	_startSubDocument();
581 
582 	m_ps->m_subDocumentType = subDocumentType;
583 	m_ps->m_isPageSpanOpened = true;
584 
585 	// Check whether the document is calling itself
586 	bool sendDoc = true;
587 	for (size_t i = 0; i < m_ds->m_subDocuments.size(); i++)
588 	{
589 		if (!subDocument)
590 			break;
591 		if (subDocument == m_ds->m_subDocuments[i])
592 		{
593 			WPS_DEBUG_MSG(("WKSContentListener::handleSubDocument: recursif call, stop...\n"));
594 			sendDoc = false;
595 			break;
596 		}
597 	}
598 	if (sendDoc)
599 	{
600 		if (subDocument)
601 		{
602 			m_ds->m_subDocuments.push_back(subDocument);
603 			shared_ptr<WKSContentListener> listen(this, WPS_shared_ptr_noop_deleter<WKSContentListener>());
604 			try
605 			{
606 				WKSSubDocument *subDoc=dynamic_cast<WKSSubDocument *>(subDocument.get());
607 				if (subDoc)
608 					subDoc->parse(listen, subDocumentType);
609 				else
610 				{
611 					WPS_DEBUG_MSG(("Works: WKSContentListener::handleSubDocument bad subdocument\n"));
612 				}
613 			}
614 			catch (...)
615 			{
616 				WPS_DEBUG_MSG(("Works: WKSContentListener::handleSubDocument exception catched \n"));
617 			}
618 			m_ds->m_subDocuments.pop_back();
619 		}
620 	}
621 
622 	_endSubDocument();
623 	_popParsingState();
624 }
625 
_startSubDocument()626 void WKSContentListener::_startSubDocument()
627 {
628 	m_ds->m_isDocumentStarted = true;
629 	m_ps->m_inSubDocument = true;
630 }
631 
_endSubDocument()632 void WKSContentListener::_endSubDocument()
633 {
634 	if (m_ps->m_isSheetOpened)
635 		closeSheet();
636 	if (m_ps->m_isParagraphOpened)
637 		_closeParagraph();
638 }
639 
640 ///////////////////
641 // sheet
642 ///////////////////
openSheet(std::vector<float> const & colWidth,librevenge::RVNGUnit unit)643 void WKSContentListener::openSheet(std::vector<float> const &colWidth, librevenge::RVNGUnit unit)
644 {
645 	if (m_ps->m_isSheetOpened)
646 	{
647 		WPS_DEBUG_MSG(("WKSContentListener::openSheet: called with m_isSheetOpened=true\n"));
648 		return;
649 	}
650 	if (!m_ps->m_isPageSpanOpened)
651 		_openPageSpan();
652 	if (m_ps->m_isParagraphOpened)
653 		_closeParagraph();
654 
655 	_pushParsingState();
656 	_startSubDocument();
657 	m_ps->m_subDocumentType = libwps::DOC_TABLE;
658 	m_ps->m_isPageSpanOpened = true;
659 
660 	librevenge::RVNGPropertyList propList;
661 
662 	librevenge::RVNGPropertyListVector columns;
663 
664 	size_t nCols = colWidth.size();
665 	for (size_t c = 0; c < nCols; c++)
666 	{
667 		librevenge::RVNGPropertyList column;
668 		column.insert("style:column-width", colWidth[c], unit);
669 		columns.append(column);
670 	}
671 	propList.insert("librevenge:columns", columns);
672 	m_documentInterface->openSheet(propList);
673 	m_ps->m_isSheetOpened = true;
674 }
675 
closeSheet()676 void WKSContentListener::closeSheet()
677 {
678 	if (!m_ps->m_isSheetOpened)
679 	{
680 		WPS_DEBUG_MSG(("WKSContentListener::closeSheet: called with m_isSheetOpened=false\n"));
681 		return;
682 	}
683 
684 	m_ps->m_isSheetOpened = false;
685 	_endSubDocument();
686 	m_documentInterface->closeSheet();
687 
688 	_popParsingState();
689 }
690 
openSheetRow(float h,librevenge::RVNGUnit unit,bool headerRow)691 void WKSContentListener::openSheetRow(float h, librevenge::RVNGUnit unit, bool headerRow)
692 {
693 	if (m_ps->m_isSheetRowOpened)
694 	{
695 		WPS_DEBUG_MSG(("WKSContentListener::openSheetRow: called with m_isSheetRowOpened=true\n"));
696 		return;
697 	}
698 	if (!m_ps->m_isSheetOpened)
699 	{
700 		WPS_DEBUG_MSG(("WKSContentListener::openSheetRow: called with m_isSheetOpened=false\n"));
701 		return;
702 	}
703 	librevenge::RVNGPropertyList propList;
704 	propList.insert("librevenge:is-header-row", headerRow);
705 
706 	if (h > 0)
707 		propList.insert("style:row-height", h, unit);
708 	else if (h < 0)
709 		propList.insert("style:min-row-height", -h, unit);
710 	m_documentInterface->openSheetRow(propList);
711 	m_ps->m_isSheetRowOpened = true;
712 }
713 
closeSheetRow()714 void WKSContentListener::closeSheetRow()
715 {
716 	if (!m_ps->m_isSheetRowOpened)
717 	{
718 		WPS_DEBUG_MSG(("WKSContentListener::openSheetRow: called with m_isSheetRowOpened=false\n"));
719 		return;
720 	}
721 	m_ps->m_isSheetRowOpened = false;
722 	m_documentInterface->closeSheetRow();
723 }
724 
openSheetCell(WPSCell const & cell,WKSContentListener::CellContent const & content,librevenge::RVNGPropertyList const & extras)725 void WKSContentListener::openSheetCell(WPSCell const &cell, WKSContentListener::CellContent const &content, librevenge::RVNGPropertyList const &extras)
726 {
727 	if (!m_ps->m_isSheetRowOpened)
728 	{
729 		WPS_DEBUG_MSG(("WKSContentListener::openSheetCell: called with m_isSheetRowOpened=false\n"));
730 		return;
731 	}
732 	if (m_ps->m_isSheetCellOpened)
733 	{
734 		WPS_DEBUG_MSG(("WKSContentListener::openSheetCell: called with m_isSheetCellOpened=true\n"));
735 		closeSheetCell();
736 	}
737 
738 	librevenge::RVNGPropertyList propList(extras);
739 	cell.addTo(propList);
740 	cell.getFont().addTo(propList);
741 	if (!cell.hasBasicFormat())
742 	{
743 		int numberingId=-1;
744 		std::stringstream name;
745 		if (m_ds->m_numberingIdMap.find(cell)!=m_ds->m_numberingIdMap.end())
746 		{
747 			numberingId=m_ds->m_numberingIdMap.find(cell)->second;
748 			name << "Numbering" << numberingId;
749 		}
750 		else
751 		{
752 			numberingId=(int) m_ds->m_numberingIdMap.size();
753 			name << "Numbering" << numberingId;
754 
755 			librevenge::RVNGPropertyList numList;
756 			if (cell.getNumberingProperties(numList))
757 			{
758 				numList.insert("librevenge:name", name.str().c_str());
759 				m_documentInterface->defineSheetNumberingStyle(numList);
760 				m_ds->m_numberingIdMap[cell]=numberingId;
761 			}
762 			else
763 				numberingId=-1;
764 		}
765 		if (numberingId>=0)
766 			propList.insert("librevenge:numbering-name", name.str().c_str());
767 	}
768 	// formula
769 	if (content.m_formula.size())
770 	{
771 		librevenge::RVNGPropertyListVector formulaVect;
772 		for (size_t i=0; i < content.m_formula.size(); ++i)
773 			formulaVect.append(content.m_formula[i].getPropertyList());
774 		propList.insert("librevenge:formula", formulaVect);
775 	}
776 	bool hasFormula=!content.m_formula.empty();
777 	if (content.isValueSet() || hasFormula)
778 	{
779 		bool hasValue=content.isValueSet();
780 		if (hasFormula && (content.m_value >= 0 && content.m_value <= 0))
781 			hasValue=false;
782 		switch (cell.getFormat())
783 		{
784 		case WPSCellFormat::F_TEXT:
785 			if (!hasValue) break;
786 			propList.insert("librevenge:value-type", cell.getValueType().c_str());
787 			propList.insert("librevenge:value", content.m_value, librevenge::RVNG_GENERIC);
788 			break;
789 		case WPSCellFormat::F_NUMBER:
790 			propList.insert("librevenge:value-type", cell.getValueType().c_str());
791 			if (!hasValue) break;
792 			propList.insert("librevenge:value", content.m_value, librevenge::RVNG_GENERIC);
793 			break;
794 		case WPSCellFormat::F_BOOLEAN:
795 			propList.insert("librevenge:value-type", "boolean");
796 			if (!hasValue) break;
797 			propList.insert("librevenge:value", content.m_value, librevenge::RVNG_GENERIC);
798 			break;
799 		case WPSCellFormat::F_DATE:
800 		{
801 			propList.insert("librevenge:value-type", "date");
802 			if (!hasValue) break;
803 			int Y=0, M=0, D=0;
804 			if (!CellContent::double2Date(content.m_value, Y, M, D)) break;
805 			propList.insert("librevenge:year", Y);
806 			propList.insert("librevenge:month", M);
807 			propList.insert("librevenge:day", D);
808 			break;
809 		}
810 		case WPSCellFormat::F_TIME:
811 		{
812 			propList.insert("librevenge:value-type", "time");
813 			if (!hasValue) break;
814 			int H=0, M=0, S=0;
815 			if (!CellContent::double2Time(content.m_value,H,M,S))
816 				break;
817 			propList.insert("librevenge:hours", H);
818 			propList.insert("librevenge:minutes", M);
819 			propList.insert("librevenge:seconds", S);
820 			break;
821 		}
822 		case WPSCellFormat::F_UNKNOWN:
823 			if (!hasValue) break;
824 			propList.insert("librevenge:value-type", cell.getValueType().c_str());
825 			propList.insert("librevenge:value", content.m_value, librevenge::RVNG_GENERIC);
826 			break;
827 		default:
828 			break;
829 		}
830 	}
831 
832 	m_ps->m_isSheetCellOpened = true;
833 	m_documentInterface->openSheetCell(propList);
834 }
835 
closeSheetCell()836 void WKSContentListener::closeSheetCell()
837 {
838 	if (!m_ps->m_isSheetCellOpened)
839 	{
840 		WPS_DEBUG_MSG(("WKSContentListener::closeSheetCell: called with m_isSheetCellOpened=false\n"));
841 		return;
842 	}
843 
844 	_closeParagraph();
845 
846 	m_ps->m_isSheetCellOpened = false;
847 	m_documentInterface->closeSheetCell();
848 }
849 
850 ///////////////////
851 // page
852 ///////////////////
_openPageSpan()853 void WKSContentListener::_openPageSpan()
854 {
855 	if (m_ps->m_isPageSpanOpened)
856 		return;
857 
858 	if (!m_ds->m_isDocumentStarted)
859 		startDocument();
860 
861 	if (m_ds->m_pageList.size()==0)
862 	{
863 		WPS_DEBUG_MSG(("WKSContentListener::_openPageSpan: can not find any page\n"));
864 		throw libwps::ParseException();
865 	}
866 	unsigned actPage = 0;
867 	std::vector<WPSPageSpan>::iterator it = m_ds->m_pageList.begin();
868 	while (actPage < m_ps->m_currentPage)
869 	{
870 		actPage+=(unsigned) it++->getPageSpan();
871 		if (it == m_ds->m_pageList.end())
872 		{
873 			WPS_DEBUG_MSG(("WKSContentListener::_openPageSpan: can not find current page\n"));
874 			throw libwps::ParseException();
875 		}
876 	}
877 	WPSPageSpan &currentPage = *it;
878 
879 	librevenge::RVNGPropertyList propList;
880 	currentPage.getPageProperty(propList);
881 	propList.insert("librevenge:is-last-page-span", ((m_ps->m_currentPage + 1 == m_ds->m_pageList.size()) ? true : false));
882 
883 	if (!m_ps->m_isPageSpanOpened)
884 		m_documentInterface->openPageSpan(propList);
885 
886 	m_ps->m_isPageSpanOpened = true;
887 
888 	m_ps->m_pageFormLength = currentPage.getFormLength();
889 	m_ps->m_pageFormWidth = currentPage.getFormWidth();
890 	m_ps->m_pageMarginLeft = currentPage.getMarginLeft();
891 	m_ps->m_pageMarginRight = currentPage.getMarginRight();
892 	m_ps->m_pageFormOrientationIsPortrait =
893 	    currentPage.getFormOrientation() == WPSPageSpan::PORTRAIT;
894 	m_ps->m_pageMarginTop = currentPage.getMarginTop();
895 	m_ps->m_pageMarginBottom = currentPage.getMarginBottom();
896 
897 	// we insert the header footer
898 	currentPage.sendHeaderFooters(this, m_documentInterface);
899 
900 	// first paragraph in span (necessary for resetting page number)
901 	m_ps->m_numPagesRemainingInSpan = (currentPage.getPageSpan() - 1);
902 	m_ps->m_currentPage++;
903 }
904 
_closePageSpan()905 void WKSContentListener::_closePageSpan()
906 {
907 	if (!m_ps->m_isPageSpanOpened)
908 		return;
909 
910 	if (m_ps->m_isParagraphOpened)
911 		_closeParagraph();
912 
913 	m_documentInterface->closePageSpan();
914 	m_ps->m_isPageSpanOpened = false;
915 }
916 
917 ///////////////////
918 // others
919 ///////////////////
920 
921 // ---------- state stack ------------------
_pushParsingState()922 shared_ptr<WKSContentParsingState> WKSContentListener::_pushParsingState()
923 {
924 	shared_ptr<WKSContentParsingState> actual = m_ps;
925 	m_psStack.push_back(actual);
926 	m_ps.reset(new WKSContentParsingState);
927 
928 	// BEGIN: copy page properties into the new parsing state
929 	m_ps->m_pageFormLength = actual->m_pageFormLength;
930 	m_ps->m_pageFormWidth = actual->m_pageFormWidth;
931 	m_ps->m_pageFormOrientationIsPortrait =	actual->m_pageFormOrientationIsPortrait;
932 	m_ps->m_pageMarginLeft = actual->m_pageMarginLeft;
933 	m_ps->m_pageMarginRight = actual->m_pageMarginRight;
934 	m_ps->m_pageMarginTop = actual->m_pageMarginTop;
935 	m_ps->m_pageMarginBottom = actual->m_pageMarginBottom;
936 
937 	m_ps->m_isNote = actual->m_isNote;
938 
939 	return actual;
940 }
941 
_popParsingState()942 void WKSContentListener::_popParsingState()
943 {
944 	if (m_psStack.size()==0)
945 	{
946 		WPS_DEBUG_MSG(("WKSContentListener::_popParsingState: psStack is empty()\n"));
947 		throw libwps::ParseException();
948 	}
949 	m_ps = m_psStack.back();
950 	m_psStack.pop_back();
951 }
952 
953 // ---------- WKSContentListener::FormulaInstruction ------------------
getPropertyList() const954 librevenge::RVNGPropertyList WKSContentListener::FormulaInstruction::getPropertyList() const
955 {
956 	librevenge::RVNGPropertyList pList;
957 	switch (m_type)
958 	{
959 	case F_Operator:
960 		pList.insert("librevenge:type","librevenge-operator");
961 		pList.insert("librevenge:operator",m_content.c_str());
962 		break;
963 	case F_Function:
964 		pList.insert("librevenge:type","librevenge-function");
965 		pList.insert("librevenge:function",m_content.c_str());
966 		break;
967 	case F_Text:
968 		pList.insert("librevenge:type","librevenge-text");
969 		pList.insert("librevenge:text",m_content.c_str());
970 		break;
971 	case F_Double:
972 		pList.insert("librevenge:type","librevenge-number");
973 		pList.insert("librevenge:number",m_doubleValue, librevenge::RVNG_GENERIC);
974 		break;
975 	case F_Long:
976 		pList.insert("librevenge:type","librevenge-number");
977 		pList.insert("librevenge:number",m_longValue, librevenge::RVNG_GENERIC);
978 		break;
979 	case F_Cell:
980 		pList.insert("librevenge:type","librevenge-cell");
981 		pList.insert("librevenge:column",m_position[0][0], librevenge::RVNG_GENERIC);
982 		pList.insert("librevenge:row",m_position[0][1], librevenge::RVNG_GENERIC);
983 		pList.insert("librevenge:column-absolute",!m_positionRelative[0][0]);
984 		pList.insert("librevenge:row-absolute",!m_positionRelative[0][1]);
985 		break;
986 	case F_CellList:
987 		pList.insert("librevenge:type","librevenge-cells");
988 		pList.insert("librevenge:start-column",m_position[0][0], librevenge::RVNG_GENERIC);
989 		pList.insert("librevenge:start-row",m_position[0][1], librevenge::RVNG_GENERIC);
990 		pList.insert("librevenge:start-column-absolute",!m_positionRelative[0][0]);
991 		pList.insert("librevenge:start-row-absolute",!m_positionRelative[0][1]);
992 		pList.insert("librevenge:end-column",m_position[1][0], librevenge::RVNG_GENERIC);
993 		pList.insert("librevenge:end-row",m_position[1][1], librevenge::RVNG_GENERIC);
994 		pList.insert("librevenge:end-column-absolute",!m_positionRelative[1][0]);
995 		pList.insert("librevenge:end-row-absolute",!m_positionRelative[1][1]);
996 		break;
997 	default:
998 		WPS_DEBUG_MSG(("WKSContentListener::FormulaInstruction::getPropertyList: unexpected type\n"));
999 	}
1000 	return pList;
1001 }
1002 
operator <<(std::ostream & o,WKSContentListener::FormulaInstruction const & inst)1003 std::ostream &operator<<(std::ostream &o, WKSContentListener::FormulaInstruction const &inst)
1004 {
1005 	if (inst.m_type==WKSContentListener::FormulaInstruction::F_Double)
1006 		o << inst.m_doubleValue;
1007 	else if (inst.m_type==WKSContentListener::FormulaInstruction::F_Long)
1008 		o << inst.m_longValue;
1009 	else if (inst.m_type==WKSContentListener::FormulaInstruction::F_Cell)
1010 	{
1011 		if (!inst.m_positionRelative[0][0]) o << "$";
1012 		if (inst.m_position[0][0]>=26) o << (char)(inst.m_position[0][0]/26-1 + 'A');
1013 		o << (char)(inst.m_position[0][0]%26+'A');
1014 		if (!inst.m_positionRelative[0][1]) o << "$";
1015 		o << inst.m_position[0][1];
1016 	}
1017 	else if (inst.m_type==WKSContentListener::FormulaInstruction::F_CellList)
1018 	{
1019 		for (int l=0; l<2; ++l)
1020 		{
1021 			if (!inst.m_positionRelative[l][0]) o << "$";
1022 			if (inst.m_position[l][0]>=26) o << (char)(inst.m_position[l][0]/26-1 + 'A');
1023 			o << (char)(inst.m_position[l][0]%26+'A');
1024 			if (!inst.m_positionRelative[l][1]) o << "$";
1025 			o << inst.m_position[l][1];
1026 			if (l==0) o << ":";
1027 		}
1028 	}
1029 	else if (inst.m_type==WKSContentListener::FormulaInstruction::F_Text)
1030 		o << "\"" << inst.m_content << "\"";
1031 	else
1032 		o << inst.m_content;
1033 	return o;
1034 }
1035 
1036 // ---------- WKSContentListener::CellContent ------------------
double2Date(double val,int & Y,int & M,int & D)1037 bool WKSContentListener::CellContent::double2Date(double val, int &Y, int &M, int &D)
1038 {
1039 	// number of day since 1/1/1970
1040 	time_t date= time_t((val-24107-1462+0.4)*24.*3600);
1041 	struct tm dateTm;
1042 	if (!gmtime_r(&date,&dateTm)) return false;
1043 
1044 	Y = dateTm.tm_year+1900;
1045 	M=dateTm.tm_mon+1;
1046 	D=dateTm.tm_mday;
1047 	return true;
1048 }
1049 
double2Time(double val,int & H,int & M,int & S)1050 bool WKSContentListener::CellContent::double2Time(double val, int &H, int &M, int &S)
1051 {
1052 	if (val < 0.0 || val > 1.0) return false;
1053 	double time = 24.*3600.*val+0.5;
1054 	H=int(time/3600.);
1055 	time -= H*3600.;
1056 	M=int(time/60.);
1057 	time -= M*60.;
1058 	S=int(time);
1059 	return true;
1060 }
1061 
operator <<(std::ostream & o,WKSContentListener::CellContent const & cell)1062 std::ostream &operator<<(std::ostream &o, WKSContentListener::CellContent const &cell)
1063 {
1064 
1065 	switch (cell.m_contentType)
1066 	{
1067 	case WKSContentListener::CellContent::C_NONE:
1068 		break;
1069 	case WKSContentListener::CellContent::C_TEXT:
1070 		o << ",text=\"" << cell.m_textEntry << "\"";
1071 		break;
1072 	case WKSContentListener::CellContent::C_NUMBER:
1073 	{
1074 		o << ",val=";
1075 		bool textAndVal = false;
1076 		if (cell.hasText())
1077 		{
1078 			o << "entry=" << cell.m_textEntry;
1079 			textAndVal = cell.isValueSet();
1080 		}
1081 		if (textAndVal) o << "[";
1082 		if (cell.isValueSet()) o << cell.m_value;
1083 		if (textAndVal) o << "]";
1084 	}
1085 	break;
1086 	case WKSContentListener::CellContent::C_FORMULA:
1087 		o << ",formula=";
1088 		for (size_t l=0; l < cell.m_formula.size(); ++l)
1089 			o << cell.m_formula[l];
1090 		if (cell.isValueSet()) o << "[" << cell.m_value << "]";
1091 		break;
1092 	case WKSContentListener::CellContent::C_UNKNOWN:
1093 		break;
1094 	default:
1095 		o << "###unknown type,";
1096 		break;
1097 	}
1098 	return o;
1099 }
1100 
1101 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
1102