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 ¶)
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 ¤tPage = *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