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