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 <sstream>
27 #include <stdio.h>
28
29 #include <librevenge/librevenge.h>
30
31 #include "libwps_internal.h"
32 #include "libwps_tools_win.h"
33
34 #include "WPSContentListener.h"
35
36 #include "WPSCell.h"
37 #include "WPSFont.h"
38 #include "WPSList.h"
39 #include "WPSPageSpan.h"
40 #include "WPSParagraph.h"
41 #include "WPSPosition.h"
42 #include "WPSTextSubDocument.h"
43
44 ////////////////////////////////////////////////////////////
45 //! the document state
46 struct WPSDocumentParsingState
47 {
48 //! constructor
49 explicit WPSDocumentParsingState(std::vector<WPSPageSpan> const &pageList);
50 //! destructor
51 ~WPSDocumentParsingState();
52
53 std::vector<WPSPageSpan> m_pageList;
54 librevenge::RVNGPropertyList m_metaData;
55
56 int m_footNoteNumber /** footnote number*/, m_endNoteNumber /** endnote number*/;
57 int m_newListId; // a new free id
58
59 bool m_isDocumentStarted, m_isHeaderFooterStarted;
60 std::vector<WPSSubDocumentPtr> m_subDocuments; /** list of document actually open */
61
62 private:
63 WPSDocumentParsingState(const WPSDocumentParsingState &) = delete;
64 WPSDocumentParsingState &operator=(const WPSDocumentParsingState &) = delete;
65 };
66
WPSDocumentParsingState(std::vector<WPSPageSpan> const & pageList)67 WPSDocumentParsingState::WPSDocumentParsingState(std::vector<WPSPageSpan> const &pageList)
68 : m_pageList(pageList)
69 , m_metaData()
70 , m_footNoteNumber(0)
71 , m_endNoteNumber(0)
72 , m_newListId(0)
73 , m_isDocumentStarted(false)
74 , m_isHeaderFooterStarted(false)
75 , m_subDocuments()
76 {
77 }
78
~WPSDocumentParsingState()79 WPSDocumentParsingState::~WPSDocumentParsingState()
80 {
81 }
82
83 ////////////////////////////////////////////////////////////
84 //! the content state
85 struct WPSContentParsingState
86 {
87 WPSContentParsingState();
88 ~WPSContentParsingState();
89
90 librevenge::RVNGString m_textBuffer;
91 int m_numDeferredTabs;
92
93 WPSFont m_font;
94 WPSParagraph m_paragraph;
95 std::shared_ptr<WPSList> m_list;
96
97 bool m_isParagraphColumnBreak;
98 bool m_isParagraphPageBreak;
99
100 bool m_isPageSpanOpened;
101 bool m_isSectionOpened;
102 bool m_isFrameOpened;
103 bool m_isPageSpanBreakDeferred;
104 bool m_isHeaderFooterWithoutParagraph;
105 //! a flag to know if openGroup was called
106 bool m_isGroupOpened;
107
108 bool m_isSpanOpened;
109 bool m_isParagraphOpened;
110 bool m_isListElementOpened;
111
112 bool m_firstParagraphInPageSpan;
113
114 std::vector<unsigned int> m_numRowsToSkip;
115 bool m_isTableOpened;
116 bool m_isTableRowOpened;
117 bool m_isTableColumnOpened;
118 bool m_isTableCellOpened;
119
120 unsigned m_currentPage;
121 int m_numPagesRemainingInSpan;
122 int m_currentPageNumber;
123
124 bool m_sectionAttributesChanged;
125 int m_numColumns;
126 std::vector < WPSColumnDefinition > m_textColumns;
127 bool m_isTextColumnWithoutParagraph;
128
129 double m_pageFormLength;
130 double m_pageFormWidth;
131 bool m_pageFormOrientationIsPortrait;
132
133 double m_pageMarginLeft;
134 double m_pageMarginRight;
135 double m_pageMarginTop;
136 double m_pageMarginBottom;
137
138 std::vector<bool> m_listOrderedLevels; //! a stack used to know what is open
139
140 bool m_inSubDocument;
141
142 bool m_isNote;
143 libwps::SubDocumentType m_subDocumentType;
144
145 private:
146 WPSContentParsingState(const WPSContentParsingState &) = delete;
147 WPSContentParsingState &operator=(const WPSContentParsingState &) = delete;
148 };
149
WPSContentParsingState()150 WPSContentParsingState::WPSContentParsingState()
151 : m_textBuffer("")
152 , m_numDeferredTabs(0)
153
154 , m_font()
155 , m_paragraph()
156 , m_list()
157 , m_isParagraphColumnBreak(false)
158 , m_isParagraphPageBreak(false)
159
160 , m_isPageSpanOpened(false)
161 , m_isSectionOpened(false)
162 , m_isFrameOpened(false)
163 , m_isPageSpanBreakDeferred(false)
164 , m_isHeaderFooterWithoutParagraph(false)
165 , m_isGroupOpened(false)
166
167 , m_isSpanOpened(false)
168 , m_isParagraphOpened(false)
169 , m_isListElementOpened(false)
170
171 , m_firstParagraphInPageSpan(true)
172
173 , m_numRowsToSkip()
174 , m_isTableOpened(false)
175 , m_isTableRowOpened(false)
176 , m_isTableColumnOpened(false)
177 , m_isTableCellOpened(false)
178
179 , m_currentPage(0)
180 , m_numPagesRemainingInSpan(0)
181 , m_currentPageNumber(1)
182
183 , m_sectionAttributesChanged(false)
184 , m_numColumns(1)
185 , m_textColumns()
186 , m_isTextColumnWithoutParagraph(false)
187
188 , m_pageFormLength(11.0)
189 , m_pageFormWidth(8.5)
190 , m_pageFormOrientationIsPortrait(true)
191
192 , m_pageMarginLeft(1.0)
193 , m_pageMarginRight(1.0)
194 , m_pageMarginTop(1.0)
195 , m_pageMarginBottom(1.0)
196
197 , m_listOrderedLevels()
198
199 , m_inSubDocument(false)
200 , m_isNote(false)
201 , m_subDocumentType(libwps::DOC_NONE)
202 {
203 m_font.m_size=12.0;
204 m_font.m_name="Times New Roman";
205 }
206
~WPSContentParsingState()207 WPSContentParsingState::~WPSContentParsingState()
208 {
209 }
210
WPSContentListener(std::vector<WPSPageSpan> const & pageList,librevenge::RVNGTextInterface * documentInterface)211 WPSContentListener::WPSContentListener(std::vector<WPSPageSpan> const &pageList, librevenge::RVNGTextInterface *documentInterface)
212 : m_ds(new WPSDocumentParsingState(pageList))
213 , m_ps(new WPSContentParsingState)
214 , m_psStack()
215 , m_documentInterface(documentInterface)
216 {
217 _updatePageSpanDependent(true);
218 }
219
~WPSContentListener()220 WPSContentListener::~WPSContentListener()
221 {
222 }
223
224 ///////////////////
225 // text data
226 ///////////////////
insertCharacter(uint8_t character)227 void WPSContentListener::insertCharacter(uint8_t character)
228 {
229 if (character >= 0x80)
230 {
231 insertUnicode(character);
232 return;
233 }
234 _flushDeferredTabs();
235 if (!m_ps->m_isSpanOpened) _openSpan();
236 m_ps->m_textBuffer.append(char(character));
237 }
238
insertUnicode(uint32_t val)239 void WPSContentListener::insertUnicode(uint32_t val)
240 {
241 // undef character, we skip it
242 if (val == 0xfffd) return;
243 _flushDeferredTabs();
244 if (!m_ps->m_isSpanOpened) _openSpan();
245 libwps::appendUnicode(val, m_ps->m_textBuffer);
246 }
247
insertUnicodeString(librevenge::RVNGString const & str)248 void WPSContentListener::insertUnicodeString(librevenge::RVNGString const &str)
249 {
250 _flushDeferredTabs();
251 if (!m_ps->m_isSpanOpened) _openSpan();
252 m_ps->m_textBuffer.append(str);
253 }
254
insertEOL(bool soft)255 void WPSContentListener::insertEOL(bool soft)
256 {
257 if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
258 _openSpan();
259 _flushDeferredTabs();
260
261 if (soft)
262 {
263 if (m_ps->m_isSpanOpened)
264 _flushText();
265 m_documentInterface->insertLineBreak();
266 }
267 else if (m_ps->m_isParagraphOpened)
268 _closeParagraph();
269
270 // sub/superscript must not survive a new line
271 static const uint32_t s_subsuperBits = WPS_SUBSCRIPT_BIT | WPS_SUPERSCRIPT_BIT;
272 if (m_ps->m_font.m_attributes & s_subsuperBits)
273 m_ps->m_font.m_attributes &= ~s_subsuperBits;
274 }
275
insertTab()276 void WPSContentListener::insertTab()
277 {
278 if (!m_ps->m_isParagraphOpened)
279 {
280 m_ps->m_numDeferredTabs++;
281 return;
282 }
283 if (m_ps->m_isSpanOpened) _flushText();
284 m_ps->m_numDeferredTabs++;
285 _flushDeferredTabs();
286 }
287
insertBreak(const uint8_t breakType)288 void WPSContentListener::insertBreak(const uint8_t breakType)
289 {
290 switch (breakType)
291 {
292 case WPS_COLUMN_BREAK:
293 if (!m_ps->m_isPageSpanOpened && !m_ps->m_inSubDocument)
294 _openSpan();
295 if (m_ps->m_isParagraphOpened)
296 _closeParagraph();
297 m_ps->m_isParagraphColumnBreak = true;
298 m_ps->m_isTextColumnWithoutParagraph = true;
299 break;
300 case WPS_PAGE_BREAK:
301 if (!m_ps->m_isPageSpanOpened && !m_ps->m_inSubDocument)
302 _openSpan();
303 if (m_ps->m_isParagraphOpened)
304 _closeParagraph();
305 m_ps->m_isParagraphPageBreak = true;
306 break;
307 default:
308 break;
309 }
310
311 if (m_ps->m_inSubDocument)
312 return;
313
314 switch (breakType)
315 {
316 case WPS_PAGE_BREAK:
317 case WPS_SOFT_PAGE_BREAK:
318 if (m_ps->m_numPagesRemainingInSpan > 0)
319 m_ps->m_numPagesRemainingInSpan--;
320 else
321 {
322 if (!m_ps->m_isTableOpened && !m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
323 _closePageSpan();
324 else
325 m_ps->m_isPageSpanBreakDeferred = true;
326 }
327 m_ps->m_currentPageNumber++;
328 break;
329 default:
330 break;
331 }
332 }
333
_insertBreakIfNecessary(librevenge::RVNGPropertyList & propList)334 void WPSContentListener::_insertBreakIfNecessary(librevenge::RVNGPropertyList &propList)
335 {
336 if (m_ps->m_isParagraphPageBreak && !m_ps->m_inSubDocument)
337 {
338 // no hard page-breaks in subdocuments
339 propList.insert("fo:break-before", "page");
340 m_ps->m_isParagraphPageBreak = false;
341 }
342 else if (m_ps->m_isParagraphColumnBreak)
343 {
344 if (m_ps->m_numColumns > 1)
345 propList.insert("fo:break-before", "column");
346 else
347 propList.insert("fo:break-before", "page");
348 }
349 }
350
351 ///////////////////
352 // font/character format
353 ///////////////////
setFont(const WPSFont & font)354 void WPSContentListener::setFont(const WPSFont &font)
355 {
356 WPSFont newFont(font);
357 if (font.m_size<=0)
358 newFont.m_size=m_ps->m_font.m_size;
359 if (font.m_name.empty())
360 newFont.m_name=m_ps->m_font.m_name;
361 if (font.m_languageId <= 0)
362 newFont.m_languageId=m_ps->m_font.m_languageId;
363 if (m_ps->m_font==newFont) return;
364 _closeSpan();
365 m_ps->m_font=newFont;
366 }
367
getFont() const368 WPSFont const &WPSContentListener::getFont() const
369 {
370 return m_ps->m_font;
371 }
372
373 ///////////////////
374 // paragraph tabs, tabs/...
375 ///////////////////
isParagraphOpened() const376 bool WPSContentListener::isParagraphOpened() const
377 {
378 return m_ps->m_isParagraphOpened;
379 }
380
getParagraph() const381 WPSParagraph const &WPSContentListener::getParagraph() const
382 {
383 return m_ps->m_paragraph;
384 }
385
setParagraph(const WPSParagraph & para)386 void WPSContentListener::setParagraph(const WPSParagraph ¶)
387 {
388 // check if we need to update the list
389 if (para.m_listLevelIndex >= 1)
390 {
391 auto level = para.m_listLevel;
392 level.m_labelWidth = (para.m_margins[1]-level.m_labelIndent);
393 if (level.m_labelWidth<0.1)
394 level.m_labelWidth = 0.1;
395 level.m_labelIndent = 0;
396
397 auto theList = getCurrentList();
398 if (!theList)
399 {
400 theList = std::make_shared<WPSList>();
401 theList->set(para.m_listLevelIndex, level);
402 setCurrentList(theList);
403 }
404 else
405 theList->set(para.m_listLevelIndex, level);
406 }
407 m_ps->m_paragraph=para;
408 }
409
410 ///////////////////
411 // List: Minimal implementation
412 ///////////////////
setCurrentList(std::shared_ptr<WPSList> list)413 void WPSContentListener::setCurrentList(std::shared_ptr<WPSList> list)
414 {
415 m_ps->m_list=list;
416 if (list && list->getId() <= 0 && list->numLevels())
417 list->setId(++m_ds->m_newListId);
418 }
getCurrentList() const419 std::shared_ptr<WPSList> WPSContentListener::getCurrentList() const
420 {
421 return m_ps->m_list;
422 }
423
424 ///////////////////
425 // field :
426 ///////////////////
insertField(WPSField const & field)427 void WPSContentListener::insertField(WPSField const &field)
428 {
429 librevenge::RVNGPropertyList propList;
430 if (field.addTo(propList))
431 {
432 _flushText();
433 _openSpan();
434 m_documentInterface->insertField(propList);
435 return;
436 }
437 auto text=field.getString();
438 if (!text.empty())
439 WPSContentListener::insertUnicodeString(text);
440 else
441 {
442 WPS_DEBUG_MSG(("WPSContentListener::insertField: must not be called with type=%d\n", int(field.m_type)));
443 }
444 }
445
446 ///////////////////
447 // section
448 ///////////////////
isSectionOpened() const449 bool WPSContentListener::isSectionOpened() const
450 {
451 return m_ps->m_isSectionOpened;
452 }
453
getSectionNumColumns() const454 int WPSContentListener::getSectionNumColumns() const
455 {
456 return m_ps->m_numColumns;
457 }
458
openSection(std::vector<int> colsWidth,librevenge::RVNGUnit unit)459 bool WPSContentListener::openSection(std::vector<int> colsWidth, librevenge::RVNGUnit unit)
460 {
461 if (m_ps->m_isSectionOpened)
462 {
463 WPS_DEBUG_MSG(("WPSContentListener::openSection: a section is already opened\n"));
464 return false;
465 }
466
467 if (m_ps->m_isTableOpened || (m_ps->m_inSubDocument && m_ps->m_subDocumentType != libwps::DOC_TEXT_BOX))
468 {
469 WPS_DEBUG_MSG(("WPSContentListener::openSection: impossible to open a section\n"));
470 return false;
471 }
472
473 size_t numCols = colsWidth.size();
474 if (numCols <= 1)
475 m_ps->m_textColumns.resize(0);
476 else
477 {
478 float factor = 1.0;
479 switch (unit)
480 {
481 case librevenge::RVNG_POINT:
482 case librevenge::RVNG_TWIP:
483 factor = WPSPosition::getScaleFactor(unit, librevenge::RVNG_INCH);
484 break;
485 case librevenge::RVNG_INCH:
486 break;
487 case librevenge::RVNG_PERCENT:
488 case librevenge::RVNG_GENERIC:
489 case librevenge::RVNG_UNIT_ERROR:
490 default:
491 WPS_DEBUG_MSG(("WPSContentListener::openSection: unknown unit\n"));
492 return false;
493 }
494 m_ps->m_textColumns.resize(numCols);
495 m_ps->m_numColumns=int(numCols);
496 for (size_t col = 0; col < numCols; col++)
497 {
498 WPSColumnDefinition column;
499 column.m_width = double(factor)*double(colsWidth[col]);
500 m_ps->m_textColumns[col] = column;
501 }
502 }
503 _openSection();
504 return true;
505 }
506
closeSection()507 bool WPSContentListener::closeSection()
508 {
509 if (!m_ps->m_isSectionOpened)
510 {
511 WPS_DEBUG_MSG(("WPSContentListener::closeSection: no section are already opened\n"));
512 return false;
513 }
514
515 if (m_ps->m_isTableOpened || (m_ps->m_inSubDocument && m_ps->m_subDocumentType != libwps::DOC_TEXT_BOX))
516 {
517 WPS_DEBUG_MSG(("WPSContentListener::closeSection: impossible to close a section\n"));
518 return false;
519 }
520
521 _closeSection();
522 return true;
523 }
524
525 ///////////////////
526 // document
527 ///////////////////
setDocumentLanguage(int lcid)528 void WPSContentListener::setDocumentLanguage(int lcid)
529 {
530 if (lcid <= 0) return;
531 std::string lang = libwps_tools_win::Language::localeName(lcid);
532 if (!lang.length()) return;
533 m_ds->m_metaData.insert("librevenge:language", lang.c_str());
534 }
535
setMetaData(const librevenge::RVNGPropertyList & list)536 void WPSContentListener::setMetaData(const librevenge::RVNGPropertyList &list)
537 {
538 librevenge::RVNGPropertyList::Iter i(list);
539
540 for (i.rewind(); i.next();)
541 {
542 m_ds->m_metaData.insert(i.key(), i()->getStr());
543 }
544 }
545
startDocument()546 void WPSContentListener::startDocument()
547 {
548 if (m_ds->m_isDocumentStarted)
549 {
550 WPS_DEBUG_MSG(("WPSContentListener::startDocument: the document is already started\n"));
551 return;
552 }
553
554 m_documentInterface->startDocument(librevenge::RVNGPropertyList());
555 m_ds->m_isDocumentStarted = true;
556
557 // FIXME: this is stupid, we should store a property list filled with the relevant metadata
558 // and then pass that directly..
559 m_documentInterface->setDocumentMetaData(m_ds->m_metaData);
560 }
561
endDocument()562 void WPSContentListener::endDocument()
563 {
564 if (!m_ds->m_isDocumentStarted)
565 {
566 WPS_DEBUG_MSG(("WPSContentListener::startDocument: the document is not started\n"));
567 return;
568 }
569
570 if (!m_ps->m_isPageSpanOpened)
571 _openSpan();
572
573 if (m_ps->m_isTableOpened)
574 closeTable();
575 if (m_ps->m_isParagraphOpened)
576 _closeParagraph();
577
578 m_ps->m_paragraph.m_listLevelIndex = 0;
579 _changeList(); // flush the list exterior
580
581 // close the document nice and tight
582 _closeSection();
583 _closePageSpan();
584 m_documentInterface->endDocument();
585 m_ds->m_isDocumentStarted = false;
586 }
587
588 ///////////////////
589 // page
590 ///////////////////
_openPageSpan()591 void WPSContentListener::_openPageSpan()
592 {
593 if (m_ps->m_isPageSpanOpened)
594 return;
595
596 if (!m_ds->m_isDocumentStarted)
597 startDocument();
598
599 if (m_ds->m_pageList.size()==0)
600 {
601 WPS_DEBUG_MSG(("WPSContentListener::_openPageSpan: can not find any page\n"));
602 throw libwps::ParseException();
603 }
604 unsigned actPage = 0;
605 auto it = m_ds->m_pageList.begin();
606 while (actPage < m_ps->m_currentPage)
607 {
608 actPage+=unsigned(it++->getPageSpan());
609 if (it == m_ds->m_pageList.end())
610 {
611 WPS_DEBUG_MSG(("WPSContentListener::_openPageSpan: can not find current page, revert to last page\n"));
612 --it;
613 break;
614 }
615 }
616 WPSPageSpan ¤tPage = *it;
617
618 librevenge::RVNGPropertyList propList;
619 currentPage.getPageProperty(propList);
620 propList.insert("librevenge:is-last-page-span", ((m_ps->m_currentPage + 1 == m_ds->m_pageList.size()) ? true : false));
621
622 if (!m_ps->m_isPageSpanOpened)
623 m_documentInterface->openPageSpan(propList);
624
625 m_ps->m_isPageSpanOpened = true;
626
627 _updatePageSpanDependent(false);
628 m_ps->m_pageFormLength = currentPage.getFormLength();
629 m_ps->m_pageFormWidth = currentPage.getFormWidth();
630 m_ps->m_pageMarginLeft = currentPage.getMarginLeft();
631 m_ps->m_pageMarginRight = currentPage.getMarginRight();
632 m_ps->m_pageFormOrientationIsPortrait =
633 currentPage.getFormOrientation() == WPSPageSpan::PORTRAIT;
634 m_ps->m_pageMarginTop = currentPage.getMarginTop();
635 m_ps->m_pageMarginBottom = currentPage.getMarginBottom();
636 _updatePageSpanDependent(true);
637
638 // we insert the header footer
639 currentPage.sendHeaderFooters(this, m_documentInterface);
640
641 // first paragraph in span (necessary for resetting page number)
642 m_ps->m_firstParagraphInPageSpan = true;
643 m_ps->m_numPagesRemainingInSpan = (currentPage.getPageSpan() - 1);
644 m_ps->m_currentPage++;
645 }
646
_closePageSpan()647 void WPSContentListener::_closePageSpan()
648 {
649 if (!m_ps->m_isPageSpanOpened)
650 return;
651
652 if (m_ps->m_isSectionOpened)
653 _closeSection();
654
655 m_documentInterface->closePageSpan();
656 m_ps->m_isPageSpanOpened = m_ps->m_isPageSpanBreakDeferred = false;
657 }
658
_updatePageSpanDependent(bool)659 void WPSContentListener::_updatePageSpanDependent(bool /*set*/)
660 {
661 }
662
663 ///////////////////
664 // section
665 ///////////////////
_openSection()666 void WPSContentListener::_openSection()
667 {
668 if (m_ps->m_isSectionOpened)
669 {
670 WPS_DEBUG_MSG(("WPSContentListener::_openSection: a section is already opened\n"));
671 return;
672 }
673
674 if (!m_ps->m_isPageSpanOpened)
675 _openPageSpan();
676
677 m_ps->m_numColumns = int(m_ps->m_textColumns.size());
678
679 librevenge::RVNGPropertyList propList;
680 propList.insert("fo:margin-left", 0.);
681 propList.insert("fo:margin-right", 0.);
682 if (m_ps->m_numColumns > 1)
683 propList.insert("text:dont-balance-text-columns", false);
684
685 librevenge::RVNGPropertyListVector columns;
686 for (auto const &col : m_ps->m_textColumns)
687 {
688 librevenge::RVNGPropertyList column;
689 // The "style:rel-width" is expressed in twips (1440 twips per inch) and includes the left and right Gutter
690 column.insert("style:rel-width", col.m_width * 1440.0, librevenge::RVNG_TWIP);
691 column.insert("fo:start-indent", col.m_leftGutter);
692 column.insert("fo:end-indent", col.m_rightGutter);
693 columns.append(column);
694 }
695 if (columns.count())
696 propList.insert("style:columns", columns);
697 m_documentInterface->openSection(propList);
698
699 m_ps->m_sectionAttributesChanged = false;
700 m_ps->m_isSectionOpened = true;
701 }
702
_closeSection()703 void WPSContentListener::_closeSection()
704 {
705 if (!m_ps->m_isSectionOpened ||m_ps->m_isTableOpened)
706 return;
707
708 if (m_ps->m_isParagraphOpened)
709 _closeParagraph();
710 _changeList();
711
712 m_documentInterface->closeSection();
713
714 m_ps->m_numColumns = 1;
715 m_ps->m_sectionAttributesChanged = false;
716 m_ps->m_isSectionOpened = false;
717 }
718
719 ///////////////////
720 // paragraph
721 ///////////////////
_openParagraph()722 void WPSContentListener::_openParagraph()
723 {
724 if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
725 return;
726
727 if (m_ps->m_isParagraphOpened || m_ps->m_isListElementOpened)
728 {
729 WPS_DEBUG_MSG(("WPSContentListener::_openParagraph: a paragraph (or a list) is already opened"));
730 return;
731 }
732 if (!m_ps->m_isTableOpened && (!m_ps->m_inSubDocument || m_ps->m_subDocumentType == libwps::DOC_TEXT_BOX))
733 {
734 if (m_ps->m_sectionAttributesChanged)
735 _closeSection();
736
737 if (!m_ps->m_isSectionOpened)
738 _openSection();
739 }
740
741 librevenge::RVNGPropertyList propList;
742 _appendParagraphProperties(propList);
743
744 if (!m_ps->m_isParagraphOpened)
745 m_documentInterface->openParagraph(propList);
746
747 _resetParagraphState();
748 m_ps->m_firstParagraphInPageSpan = false;
749 }
750
_closeParagraph()751 void WPSContentListener::_closeParagraph()
752 {
753 if (m_ps->m_isListElementOpened)
754 {
755 _closeListElement();
756 return;
757 }
758
759 if (m_ps->m_isParagraphOpened)
760 {
761 if (m_ps->m_isSpanOpened)
762 _closeSpan();
763
764 m_documentInterface->closeParagraph();
765 }
766
767 m_ps->m_isParagraphOpened = false;
768 m_ps->m_paragraph.m_listLevelIndex = 0;
769
770 if (!m_ps->m_isTableOpened && m_ps->m_isPageSpanBreakDeferred && !m_ps->m_inSubDocument)
771 _closePageSpan();
772 }
773
_resetParagraphState(const bool isListElement)774 void WPSContentListener::_resetParagraphState(const bool isListElement)
775 {
776 m_ps->m_isParagraphColumnBreak = false;
777 m_ps->m_isParagraphPageBreak = false;
778 if (isListElement)
779 {
780 m_ps->m_isListElementOpened = true;
781 m_ps->m_isParagraphOpened = true;
782 }
783 else
784 {
785 m_ps->m_isListElementOpened = false;
786 m_ps->m_isParagraphOpened = true;
787 }
788 m_ps->m_isTextColumnWithoutParagraph = false;
789 m_ps->m_isHeaderFooterWithoutParagraph = false;
790 }
791
_appendParagraphProperties(librevenge::RVNGPropertyList & propList,const bool)792 void WPSContentListener::_appendParagraphProperties
793 (librevenge::RVNGPropertyList &propList, const bool /*isListElement*/)
794 {
795 m_ps->m_paragraph.addTo(propList, m_ps->m_isTableOpened);
796
797 if (!m_ps->m_inSubDocument && m_ps->m_firstParagraphInPageSpan)
798 {
799 unsigned actPage = 1;
800 auto it = m_ds->m_pageList.begin();
801 while (actPage < m_ps->m_currentPage)
802 {
803 if (it == m_ds->m_pageList.end())
804 break;
805
806 actPage+=unsigned(it++->getPageSpan());
807 }
808 if (it != m_ds->m_pageList.end())
809 {
810 WPSPageSpan const ¤tPage = *it;
811 if (currentPage.getPageNumber() >= 0)
812 propList.insert("style:page-number", currentPage.getPageNumber());
813 }
814 }
815
816 _insertBreakIfNecessary(propList);
817 }
818
819 ///////////////////
820 // list
821 ///////////////////
_openListElement()822 void WPSContentListener::_openListElement()
823 {
824 if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
825 return;
826
827 if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
828 {
829 if (!m_ps->m_isTableOpened && (!m_ps->m_inSubDocument || m_ps->m_subDocumentType == libwps::DOC_TEXT_BOX))
830 {
831 if (m_ps->m_sectionAttributesChanged)
832 _closeSection();
833
834 if (!m_ps->m_isSectionOpened)
835 _openSection();
836 }
837
838 librevenge::RVNGPropertyList propList;
839 _appendParagraphProperties(propList, true);
840
841 if (!m_ps->m_isListElementOpened)
842 m_documentInterface->openListElement(propList);
843 _resetParagraphState(true);
844 }
845 }
846
_closeListElement()847 void WPSContentListener::_closeListElement()
848 {
849 if (m_ps->m_isListElementOpened)
850 {
851 if (m_ps->m_isSpanOpened)
852 _closeSpan();
853
854 m_documentInterface->closeListElement();
855 }
856
857 m_ps->m_isListElementOpened = m_ps->m_isParagraphOpened = false;
858 m_ps->m_paragraph.m_listLevelIndex = 0;
859
860 if (!m_ps->m_isTableOpened && m_ps->m_isPageSpanBreakDeferred && !m_ps->m_inSubDocument)
861 _closePageSpan();
862 }
863
_changeList()864 void WPSContentListener::_changeList()
865 {
866 if (m_ps->m_isParagraphOpened)
867 _closeParagraph();
868
869 if (!m_ps->m_isSectionOpened && !m_ps->m_inSubDocument && !m_ps->m_isTableOpened)
870 _openSection();
871
872 // FIXME: even if nobody really care, if we close an ordered or an unordered
873 // elements, we must keep the previous to close this part...
874 size_t actualListLevel = m_ps->m_listOrderedLevels.size();
875 for (size_t i=actualListLevel; int(i) > m_ps->m_paragraph.m_listLevelIndex; i--)
876 {
877 if (m_ps->m_listOrderedLevels[i-1])
878 m_documentInterface->closeOrderedListLevel();
879 else
880 m_documentInterface->closeUnorderedListLevel();
881 }
882
883 if (m_ps->m_paragraph.m_listLevelIndex)
884 {
885 if (!m_ps->m_list.get())
886 {
887 WPS_DEBUG_MSG(("WPSContentListener::_handleListChange: can not find any list\n"));
888 return;
889 }
890 m_ps->m_list->setLevel(m_ps->m_paragraph.m_listLevelIndex);
891 m_ps->m_list->openElement();
892
893 if (m_ps->m_list->mustSendLevel(m_ps->m_paragraph.m_listLevelIndex))
894 {
895 if (actualListLevel == size_t(m_ps->m_paragraph.m_listLevelIndex))
896 {
897 if (m_ps->m_listOrderedLevels[actualListLevel-1])
898 m_documentInterface->closeOrderedListLevel();
899 else
900 m_documentInterface->closeUnorderedListLevel();
901 actualListLevel--;
902 }
903 if (m_ps->m_paragraph.m_listLevelIndex==1)
904 {
905 // we must change the listID for writerperfect
906 int prevId;
907 if ((prevId=m_ps->m_list->getPreviousId()) > 0)
908 m_ps->m_list->setId(prevId);
909 else
910 m_ps->m_list->setId(++m_ds->m_newListId);
911 }
912 }
913
914 m_ps->m_list->closeElement();
915 }
916
917 if (int(actualListLevel) == m_ps->m_paragraph.m_listLevelIndex) return;
918
919 m_ps->m_listOrderedLevels.resize(size_t(m_ps->m_paragraph.m_listLevelIndex), false);
920 for (size_t i=actualListLevel+1; i<= size_t(m_ps->m_paragraph.m_listLevelIndex); i++)
921 {
922 librevenge::RVNGPropertyList propList;
923 m_ps->m_list->addLevelTo(int(i), propList);
924 if (m_ps->m_list->isNumeric(int(i)))
925 {
926 m_ps->m_listOrderedLevels[i-1] = true;
927 m_documentInterface->openOrderedListLevel(propList);
928 }
929 else
930 {
931 m_ps->m_listOrderedLevels[i-1] = false;
932 m_documentInterface->openUnorderedListLevel(propList);
933 }
934 }
935 }
936
937 ///////////////////
938 // span
939 ///////////////////
_openSpan()940 void WPSContentListener::_openSpan()
941 {
942 if (m_ps->m_isSpanOpened)
943 return;
944
945 if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
946 return;
947
948 if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
949 {
950 _changeList();
951 if (m_ps->m_paragraph.m_listLevelIndex == 0)
952 _openParagraph();
953 else
954 _openListElement();
955 }
956
957 librevenge::RVNGPropertyList propList;
958 m_ps->m_font.addTo(propList);
959
960 m_documentInterface->openSpan(propList);
961
962 m_ps->m_isSpanOpened = true;
963 }
964
_closeSpan()965 void WPSContentListener::_closeSpan()
966 {
967 if (!m_ps->m_isSpanOpened)
968 return;
969
970 _flushText();
971 m_documentInterface->closeSpan();
972 m_ps->m_isSpanOpened = false;
973 }
974
975 ///////////////////
976 // text (send data)
977 ///////////////////
_flushDeferredTabs()978 void WPSContentListener::_flushDeferredTabs()
979 {
980 if (m_ps->m_numDeferredTabs == 0) return;
981
982 // CHECKME: the tab are not underline even if the underline bit is set
983 uint32_t oldTextAttributes = m_ps->m_font.m_attributes;
984 uint32_t newAttributes = oldTextAttributes & uint32_t(~WPS_UNDERLINE_BIT) &
985 uint32_t(~WPS_OVERLINE_BIT);
986 if (oldTextAttributes != newAttributes)
987 {
988 _closeSpan();
989 m_ps->m_font.m_attributes=newAttributes;
990 }
991 if (!m_ps->m_isSpanOpened) _openSpan();
992 for (; m_ps->m_numDeferredTabs > 0; m_ps->m_numDeferredTabs--)
993 m_documentInterface->insertTab();
994 if (oldTextAttributes != newAttributes)
995 {
996 _closeSpan();
997 m_ps->m_font.m_attributes=oldTextAttributes;
998 }
999 }
1000
_flushText()1001 void WPSContentListener::_flushText()
1002 {
1003 if (m_ps->m_textBuffer.len() == 0) return;
1004
1005 // when some many ' ' follows each other, call insertSpace
1006 librevenge::RVNGString tmpText;
1007 int numConsecutiveSpaces = 0;
1008 librevenge::RVNGString::Iter i(m_ps->m_textBuffer);
1009 for (i.rewind(); i.next();)
1010 {
1011 if (*(i()) == 0x20) // this test is compatible with unicode format
1012 numConsecutiveSpaces++;
1013 else
1014 numConsecutiveSpaces = 0;
1015
1016 if (numConsecutiveSpaces > 1)
1017 {
1018 if (tmpText.len() > 0)
1019 {
1020 m_documentInterface->insertText(tmpText);
1021 tmpText.clear();
1022 }
1023 m_documentInterface->insertSpace();
1024 }
1025 else
1026 tmpText.append(i());
1027 }
1028 m_documentInterface->insertText(tmpText);
1029 m_ps->m_textBuffer.clear();
1030 }
1031
1032 ///////////////////
1033 // Note/Comment/picture/textbox
1034 ///////////////////
insertNote(const NoteType noteType,WPSSubDocumentPtr & subDocument)1035 void WPSContentListener::insertNote(const NoteType noteType, WPSSubDocumentPtr &subDocument)
1036 {
1037 if (m_ps->m_isNote)
1038 {
1039 WPS_DEBUG_MSG(("WPSContentListener::insertNote try to insert a note recursively (ingnored)\n"));
1040 return;
1041 }
1042 librevenge::RVNGString label("");
1043 insertLabelNote(noteType, label, subDocument);
1044 }
1045
insertLabelNote(const NoteType noteType,librevenge::RVNGString const & label,WPSSubDocumentPtr & subDocument)1046 void WPSContentListener::insertLabelNote(const NoteType noteType, librevenge::RVNGString const &label, WPSSubDocumentPtr &subDocument)
1047 {
1048 if (m_ps->m_isNote)
1049 {
1050 WPS_DEBUG_MSG(("WPSContentListener::insertLabelNote try to insert a note recursively (ingnored)\n"));
1051 return;
1052 }
1053
1054 m_ps->m_isNote = true;
1055 if (m_ds->m_isHeaderFooterStarted)
1056 {
1057 WPS_DEBUG_MSG(("WPSContentListener::insertLabelNote try to insert a note in a header/footer\n"));
1058 /** Must not happen excepted in corrupted document, so we do the minimum.
1059 Note that we have no choice, either we begin by closing the paragraph,
1060 ... or we reprogram handleSubDocument.
1061 */
1062 if (m_ps->m_isParagraphOpened)
1063 _closeParagraph();
1064 int prevListLevel = m_ps->m_paragraph.m_listLevelIndex;
1065 m_ps->m_paragraph.m_listLevelIndex = 0;
1066 _changeList(); // flush the list exterior
1067 handleSubDocument(subDocument, libwps::DOC_NOTE);
1068 m_ps->m_paragraph.m_listLevelIndex = uint8_t(prevListLevel);
1069 }
1070 else
1071 {
1072 if (!m_ps->m_isParagraphOpened)
1073 _openParagraph();
1074 else
1075 {
1076 _flushText();
1077 _closeSpan();
1078 }
1079
1080 librevenge::RVNGPropertyList propList;
1081 if (label.len())
1082 propList.insert("text:label", label);
1083 if (noteType == FOOTNOTE)
1084 {
1085 propList.insert("librevenge:number", ++(m_ds->m_footNoteNumber));
1086 m_documentInterface->openFootnote(propList);
1087 }
1088 else
1089 {
1090 propList.insert("librevenge:number", ++(m_ds->m_endNoteNumber));
1091 m_documentInterface->openEndnote(propList);
1092 }
1093
1094 handleSubDocument(subDocument, libwps::DOC_NOTE);
1095
1096 if (noteType == FOOTNOTE)
1097 m_documentInterface->closeFootnote();
1098 else
1099 m_documentInterface->closeEndnote();
1100 }
1101 m_ps->m_isNote = false;
1102 }
1103
insertComment(WPSSubDocumentPtr & subDocument)1104 void WPSContentListener::insertComment(WPSSubDocumentPtr &subDocument)
1105 {
1106 if (m_ps->m_isNote)
1107 {
1108 WPS_DEBUG_MSG(("WPSContentListener::insertComment try to insert a note recursively (ingnored)"));
1109 return;
1110 }
1111
1112 if (!m_ps->m_isParagraphOpened)
1113 _openParagraph();
1114 else
1115 {
1116 _flushText();
1117 _closeSpan();
1118 }
1119
1120 librevenge::RVNGPropertyList propList;
1121 m_documentInterface->openComment(propList);
1122
1123 m_ps->m_isNote = true;
1124 handleSubDocument(subDocument, libwps::DOC_COMMENT_ANNOTATION);
1125
1126 m_documentInterface->closeComment();
1127 m_ps->m_isNote = false;
1128 }
1129
insertTextBox(WPSPosition const & pos,WPSSubDocumentPtr subDocument,librevenge::RVNGPropertyList frameExtras)1130 void WPSContentListener::insertTextBox
1131 (WPSPosition const &pos, WPSSubDocumentPtr subDocument, librevenge::RVNGPropertyList frameExtras)
1132 {
1133 if (!_openFrame(pos, frameExtras)) return;
1134
1135 librevenge::RVNGPropertyList propList;
1136 m_documentInterface->openTextBox(propList);
1137 handleSubDocument(subDocument, libwps::DOC_TEXT_BOX);
1138 m_documentInterface->closeTextBox();
1139
1140 _closeFrame();
1141 }
1142
insertPicture(WPSPosition const & pos,const librevenge::RVNGBinaryData & binaryData,std::string type,librevenge::RVNGPropertyList frameExtras)1143 void WPSContentListener::insertPicture
1144 (WPSPosition const &pos, const librevenge::RVNGBinaryData &binaryData, std::string type,
1145 librevenge::RVNGPropertyList frameExtras)
1146 {
1147 if (!_openFrame(pos, frameExtras)) return;
1148
1149 librevenge::RVNGPropertyList propList;
1150 propList.insert("librevenge:mime-type", type.c_str());
1151 propList.insert("office:binary-data", binaryData);
1152 m_documentInterface->insertBinaryObject(propList);
1153
1154 _closeFrame();
1155 }
1156
insertObject(WPSPosition const & pos,const WPSEmbeddedObject & obj,librevenge::RVNGPropertyList frameExtras)1157 void WPSContentListener::insertObject(WPSPosition const &pos, const WPSEmbeddedObject &obj,
1158 librevenge::RVNGPropertyList frameExtras)
1159 {
1160 if (!_openFrame(pos, frameExtras)) return;
1161
1162 librevenge::RVNGPropertyList propList;
1163 if (obj.addTo(propList))
1164 m_documentInterface->insertBinaryObject(propList);
1165
1166 _closeFrame();
1167 }
1168
1169
1170 ///////////////////
1171 // frame
1172 ///////////////////
openGroup(WPSPosition const & pos)1173 bool WPSContentListener::openGroup(WPSPosition const &pos)
1174 {
1175 if (!m_ds->m_isDocumentStarted)
1176 {
1177 WPS_DEBUG_MSG(("WPSContentListener::openGroup: the document is not started\n"));
1178 return false;
1179 }
1180 if (m_ps->m_isTableOpened)
1181 {
1182 WPS_DEBUG_MSG(("WPSContentListener::openGroup: called in table or in a text zone\n"));
1183 return false;
1184 }
1185
1186 // now check that the anchor is coherent with the actual state
1187 switch (pos.m_anchorTo)
1188 {
1189 case WPSPosition::Page:
1190 case WPSPosition::PageContent:
1191 break;
1192 case WPSPosition::Paragraph:
1193 case WPSPosition::ParagraphContent:
1194 if (m_ps->m_isParagraphOpened)
1195 _flushText();
1196 else
1197 _openParagraph();
1198 break;
1199 #if !defined(__clang__)
1200 default:
1201 WPS_DEBUG_MSG(("WPSContentListener::openGroup: UNKNOWN position, insert as char position\n"));
1202 WPS_FALLTHROUGH;
1203 #endif
1204 case WPSPosition::CharBaseLine:
1205 case WPSPosition::Char:
1206 if (m_ps->m_isSpanOpened)
1207 _flushText();
1208 else
1209 _openSpan();
1210 break;
1211 case WPSPosition::Cell:
1212 break;
1213 }
1214
1215 librevenge::RVNGPropertyList propList;
1216 _handleFrameParameters(propList, pos);
1217
1218 _pushParsingState();
1219 _startSubDocument();
1220 m_ps->m_isGroupOpened = true;
1221
1222 m_documentInterface->openGroup(propList);
1223
1224 return true;
1225 }
1226
closeGroup()1227 void WPSContentListener::closeGroup()
1228 {
1229 if (!m_ps->m_isGroupOpened)
1230 {
1231 WPS_DEBUG_MSG(("WPSContentListener::closeGroup: called but no group is already opened\n"));
1232 return;
1233 }
1234 _endSubDocument();
1235 _popParsingState();
1236 m_documentInterface->closeGroup();
1237 }
1238
_openFrame(WPSPosition const & pos,librevenge::RVNGPropertyList extras)1239 bool WPSContentListener::_openFrame(WPSPosition const &pos, librevenge::RVNGPropertyList extras)
1240 {
1241 if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
1242 {
1243 WPS_DEBUG_MSG(("WPSContentListener::openFrame: called in table but cell is not opened\n"));
1244 return false;
1245 }
1246 if (m_ps->m_isFrameOpened)
1247 {
1248 WPS_DEBUG_MSG(("WPSContentListener::openFrame: called but a frame is already opened\n"));
1249 return false;
1250 }
1251
1252 switch (pos.m_anchorTo)
1253 {
1254 case WPSPosition::Page:
1255 case WPSPosition::PageContent:
1256 break;
1257 case WPSPosition::Paragraph:
1258 case WPSPosition::ParagraphContent:
1259 if (m_ps->m_isParagraphOpened)
1260 _flushText();
1261 else
1262 _openParagraph();
1263 break;
1264 case WPSPosition::CharBaseLine:
1265 case WPSPosition::Char:
1266 if (m_ps->m_isSpanOpened)
1267 _flushText();
1268 else
1269 _openSpan();
1270 break;
1271 case WPSPosition::Cell:
1272 if (!m_ps->m_isTableCellOpened)
1273 {
1274 WPS_DEBUG_MSG(("WPSTextListener::openFrame: called with Cell position not in a table cell\n"));
1275 return false;
1276 }
1277 if (pos.m_anchorCellName.empty())
1278 {
1279 WPS_DEBUG_MSG(("WPSTextListener::openFrame: can not find the cell name\n"));
1280 return false;
1281 }
1282 break;
1283 default:
1284 WPS_DEBUG_MSG(("WPSContentListener::openFrame: can not determine the anchor\n"));
1285 return false;
1286 }
1287
1288 librevenge::RVNGPropertyList propList(extras);
1289 _handleFrameParameters(propList, pos);
1290 m_documentInterface->openFrame(propList);
1291
1292 m_ps->m_isFrameOpened = true;
1293 return true;
1294 }
1295
_closeFrame()1296 void WPSContentListener::_closeFrame()
1297 {
1298 if (!m_ps->m_isFrameOpened)
1299 {
1300 WPS_DEBUG_MSG(("WPSContentListener::closeFrame: called but no frame is already opened\n"));
1301 return;
1302 }
1303 m_documentInterface->closeFrame();
1304 m_ps->m_isFrameOpened = false;
1305 }
1306
_handleFrameParameters(librevenge::RVNGPropertyList & propList,WPSPosition const & pos)1307 void WPSContentListener::_handleFrameParameters
1308 (librevenge::RVNGPropertyList &propList, WPSPosition const &pos)
1309 {
1310 Vec2f origin = pos.origin();
1311 auto unit = pos.unit();
1312 float inchFactor=pos.getInvUnitScale(librevenge::RVNG_INCH);
1313 float pointFactor = pos.getInvUnitScale(librevenge::RVNG_POINT);
1314
1315 propList.insert("svg:width", double(pos.size()[0]), unit);
1316 propList.insert("svg:height", double(pos.size()[1]), unit);
1317 if (pos.naturalSize().x() > 4*pointFactor && pos.naturalSize().y() > 4*pointFactor)
1318 {
1319 propList.insert("librevenge:naturalWidth", double(pos.naturalSize().x()), pos.unit());
1320 propList.insert("librevenge:naturalHeight", double(pos.naturalSize().y()), pos.unit());
1321 }
1322
1323 if (pos.m_wrapping == WPSPosition::WDynamic)
1324 propList.insert("style:wrap", "dynamic");
1325 else if (pos.m_wrapping == WPSPosition::WRunThrough)
1326 {
1327 propList.insert("style:wrap", "run-through");
1328 propList.insert("style:run-through", "background");
1329 }
1330 else
1331 propList.insert("style:wrap", "none");
1332
1333 if (pos.m_anchorTo == WPSPosition::Paragraph || pos.m_anchorTo == WPSPosition::ParagraphContent)
1334 {
1335 propList.insert("text:anchor-type", "paragraph");
1336 librevenge::RVNGString posRel(pos.m_anchorTo == WPSPosition::Paragraph ?
1337 "paragraph" : "paragraph-content");
1338 propList.insert("style:vertical-rel", posRel);
1339 propList.insert("style:horizontal-rel", posRel);
1340 auto w = float (m_ps->m_pageFormWidth - m_ps->m_pageMarginLeft
1341 - m_ps->m_pageMarginRight - m_ps->m_paragraph.m_margins[1]
1342 - m_ps->m_paragraph.m_margins[2]);
1343 w *= inchFactor;
1344 switch (pos.m_xPos)
1345 {
1346 case WPSPosition::XRight:
1347 if (origin[0] < 0.0f || origin[0] > 0.0f)
1348 {
1349 propList.insert("style:horizontal-pos", "from-left");
1350 propList.insert("svg:x", double(origin[0] - pos.size()[0] + w), unit);
1351 }
1352 else
1353 propList.insert("style:horizontal-pos", "right");
1354 break;
1355 case WPSPosition::XCenter:
1356 if (origin[0] < 0.0f || origin[0] > 0.0f)
1357 {
1358 propList.insert("style:horizontal-pos", "from-left");
1359 propList.insert("svg:x", double(origin[0] - pos.size()[0]/2.0f + w/2.0f), unit);
1360 }
1361 else
1362 propList.insert("style:horizontal-pos", "center");
1363 break;
1364 case WPSPosition::XLeft:
1365 case WPSPosition::XFull:
1366 default:
1367 if (origin[0] < 0.0f || origin[0] > 0.0f)
1368 {
1369 propList.insert("style:horizontal-pos", "from-left");
1370 propList.insert("svg:x", double(origin[0]), unit);
1371 }
1372 else
1373 propList.insert("style:horizontal-pos", "left");
1374 break;
1375 }
1376
1377 if (origin[1] < 0.0f || origin[1] > 0.0f)
1378 {
1379 propList.insert("style:vertical-pos", "from-top");
1380 propList.insert("svg:y", double(origin[1]), unit);
1381 }
1382 else
1383 propList.insert("style:vertical-pos", "top");
1384
1385 return;
1386 }
1387 if (pos.m_anchorTo == WPSPosition::Page || pos.m_anchorTo == WPSPosition::PageContent)
1388 {
1389 // Page position seems to do not use the page margin...
1390 propList.insert("text:anchor-type", "page");
1391 if (pos.page() > 0) propList.insert("text:anchor-page-number", pos.page());
1392 auto w = float(m_ps->m_pageFormWidth);
1393 auto h = float(m_ps->m_pageFormLength);
1394 w *= inchFactor;
1395 h *= inchFactor;
1396
1397 librevenge::RVNGString posRel(pos.m_anchorTo == WPSPosition::Page ? "page" : "page-content");
1398 propList.insert("style:vertical-rel", posRel);
1399 propList.insert("style:horizontal-rel", posRel);
1400
1401 float newPosition;
1402 switch (pos.m_yPos)
1403 {
1404 case WPSPosition::YFull:
1405 propList.insert("svg:height", double(h), unit);
1406 WPS_FALLTHROUGH;
1407 case WPSPosition::YTop:
1408 if (origin[1] < 0.0f || origin[1] > 0.0f)
1409 {
1410 propList.insert("style:vertical-pos", "from-top");
1411 newPosition = origin[1];
1412 if (newPosition > h -pos.size()[1])
1413 newPosition = h - pos.size()[1];
1414 propList.insert("svg:y", double(newPosition), unit);
1415 }
1416 else
1417 propList.insert("style:vertical-pos", "top");
1418 break;
1419 case WPSPosition::YCenter:
1420 if (origin[1] < 0.0f || origin[1] > 0.0f)
1421 {
1422 propList.insert("style:vertical-pos", "from-top");
1423 newPosition = (h - pos.size()[1])/2.0f;
1424 if (newPosition > h -pos.size()[1]) newPosition = h - pos.size()[1];
1425 propList.insert("svg:y", double(newPosition), unit);
1426 }
1427 else
1428 propList.insert("style:vertical-pos", "middle");
1429 break;
1430 case WPSPosition::YBottom:
1431 if (origin[1] < 0.0f || origin[1] > 0.0f)
1432 {
1433 propList.insert("style:vertical-pos", "from-top");
1434 newPosition = h - pos.size()[1]-origin[1];
1435 if (newPosition > h -pos.size()[1]) newPosition = h -pos.size()[1];
1436 else if (newPosition < 0) newPosition = 0;
1437 propList.insert("svg:y", double(newPosition), unit);
1438 }
1439 else
1440 propList.insert("style:vertical-pos", "bottom");
1441 break;
1442 default:
1443 break;
1444 }
1445
1446 switch (pos.m_xPos)
1447 {
1448 case WPSPosition::XFull:
1449 propList.insert("svg:width", double(w), unit);
1450 WPS_FALLTHROUGH;
1451 case WPSPosition::XLeft:
1452 if (origin[0] < 0.0f || origin[0] > 0.0f)
1453 {
1454 propList.insert("style:horizontal-pos", "from-left");
1455 propList.insert("svg:x", double(origin[0]), unit);
1456 }
1457 else
1458 propList.insert("style:horizontal-pos", "left");
1459 break;
1460 case WPSPosition::XRight:
1461 if (origin[0] < 0.0f || origin[0] > 0.0f)
1462 {
1463 propList.insert("style:horizontal-pos", "from-left");
1464 propList.insert("svg:x",double(w - pos.size()[0] + origin[0]), unit);
1465 }
1466 else
1467 propList.insert("style:horizontal-pos", "right");
1468 break;
1469 case WPSPosition::XCenter:
1470 default:
1471 if (origin[0] < 0.0f || origin[0] > 0.0f)
1472 {
1473 propList.insert("style:horizontal-pos", "from-left");
1474 propList.insert("svg:x", double((w - pos.size()[0])/2 + origin[0]), unit);
1475 }
1476 else
1477 propList.insert("style:horizontal-pos", "center");
1478 break;
1479 }
1480 return;
1481 }
1482 if (pos.m_anchorTo == WPSPosition::Cell)
1483 {
1484 if (!pos.m_anchorCellName.empty())
1485 propList.insert("table:end-cell-address", pos.m_anchorCellName);
1486 // todo: implement also different m_xPos and m_yPos
1487 if (origin[0] < 0 || origin[0] > 0)
1488 propList.insert("svg:x", double(origin[0]), unit);
1489 if (origin[1] < 0 || origin[1] > 0)
1490 propList.insert("svg:y", double(origin[1]), unit);
1491 return;
1492 }
1493 if (pos.m_anchorTo != WPSPosition::Char &&
1494 pos.m_anchorTo != WPSPosition::CharBaseLine) return;
1495
1496 propList.insert("text:anchor-type", "as-char");
1497 if (pos.m_anchorTo == WPSPosition::CharBaseLine)
1498 propList.insert("style:vertical-rel", "baseline");
1499 else
1500 propList.insert("style:vertical-rel", "line");
1501 switch (pos.m_yPos)
1502 {
1503 case WPSPosition::YFull:
1504 case WPSPosition::YTop:
1505 if (origin[1] < 0.0f || origin[1] > 0.0f)
1506 {
1507 propList.insert("style:vertical-pos", "from-top");
1508 propList.insert("svg:y", double(origin[1]), unit);
1509 }
1510 else
1511 propList.insert("style:vertical-pos", "top");
1512 break;
1513 case WPSPosition::YCenter:
1514 if (origin[1] < 0.0f || origin[1] > 0.0f)
1515 {
1516 propList.insert("style:vertical-pos", "from-top");
1517 propList.insert("svg:y", double(origin[1] - pos.size()[1]/2.0f), unit);
1518 }
1519 else
1520 propList.insert("style:vertical-pos", "middle");
1521 break;
1522 case WPSPosition::YBottom:
1523 default:
1524 if (origin[1] < 0.0f || origin[1] > 0.0f)
1525 {
1526 propList.insert("style:vertical-pos", "from-top");
1527 propList.insert("svg:y", double(origin[1] - pos.size()[1]), unit);
1528 }
1529 else
1530 propList.insert("style:vertical-pos", "bottom");
1531 break;
1532 }
1533 }
1534
1535 ///////////////////
1536 // subdocument
1537 ///////////////////
handleSubDocument(WPSSubDocumentPtr & subDocument,libwps::SubDocumentType subDocumentType)1538 void WPSContentListener::handleSubDocument(WPSSubDocumentPtr &subDocument, libwps::SubDocumentType subDocumentType)
1539 {
1540 _pushParsingState();
1541 _startSubDocument();
1542 m_ps->m_subDocumentType = subDocumentType;
1543
1544 m_ps->m_isPageSpanOpened = true;
1545 m_ps->m_list.reset();
1546
1547 switch (subDocumentType)
1548 {
1549 case libwps::DOC_TEXT_BOX:
1550 m_ps->m_pageMarginLeft = m_ps->m_pageMarginRight =
1551 m_ps->m_pageMarginTop = m_ps->m_pageMarginBottom = 0.0;
1552 m_ps->m_sectionAttributesChanged = true;
1553 break;
1554 case libwps::DOC_HEADER_FOOTER:
1555 m_ps->m_isHeaderFooterWithoutParagraph = true;
1556 m_ds->m_isHeaderFooterStarted = true;
1557 break;
1558 case libwps::DOC_NONE:
1559 case libwps::DOC_NOTE:
1560 case libwps::DOC_TABLE:
1561 case libwps::DOC_COMMENT_ANNOTATION:
1562 case libwps::DOC_CHART_ZONE:
1563 default:
1564 break;
1565 }
1566
1567 // Check whether the document is calling itself
1568 bool sendDoc = true;
1569 for (const auto &i : m_ds->m_subDocuments)
1570 {
1571 if (!subDocument)
1572 break;
1573 if (*subDocument == i)
1574 {
1575 WPS_DEBUG_MSG(("WPSContentListener::handleSubDocument: recursif call, stop...\n"));
1576 sendDoc = false;
1577 break;
1578 }
1579 }
1580 if (sendDoc)
1581 {
1582 if (subDocument)
1583 {
1584 m_ds->m_subDocuments.push_back(subDocument);
1585 std::shared_ptr<WPSContentListener> listen(this, WPS_shared_ptr_noop_deleter<WPSContentListener>());
1586 try
1587 {
1588 auto *docu=dynamic_cast<WPSTextSubDocument *>(subDocument.get());
1589 if (docu)
1590 docu->parse(listen, subDocumentType);
1591 else
1592 {
1593 WPS_DEBUG_MSG(("Works: WPSContentListener::handleSubDocument bad sub document\n"));
1594 }
1595 }
1596 catch (...)
1597 {
1598 WPS_DEBUG_MSG(("Works: WPSContentListener::handleSubDocument exception catched \n"));
1599 }
1600 m_ds->m_subDocuments.pop_back();
1601 }
1602 if (m_ps->m_isHeaderFooterWithoutParagraph)
1603 _openSpan();
1604 }
1605
1606 switch (m_ps->m_subDocumentType)
1607 {
1608 case libwps::DOC_TEXT_BOX:
1609 _closeSection();
1610 break;
1611 case libwps::DOC_HEADER_FOOTER:
1612 m_ds->m_isHeaderFooterStarted = false;
1613 break;
1614 case libwps::DOC_NONE:
1615 case libwps::DOC_NOTE:
1616 case libwps::DOC_TABLE:
1617 case libwps::DOC_COMMENT_ANNOTATION:
1618 case libwps::DOC_CHART_ZONE:
1619 default:
1620 break;
1621 }
1622 _endSubDocument();
1623 _popParsingState();
1624 }
1625
isHeaderFooterOpened() const1626 bool WPSContentListener::isHeaderFooterOpened() const
1627 {
1628 return m_ds->m_isHeaderFooterStarted;
1629 }
1630
_startSubDocument()1631 void WPSContentListener::_startSubDocument()
1632 {
1633 m_ds->m_isDocumentStarted = true;
1634 m_ps->m_inSubDocument = true;
1635 }
1636
_endSubDocument()1637 void WPSContentListener::_endSubDocument()
1638 {
1639 if (m_ps->m_isTableOpened)
1640 closeTable();
1641 if (m_ps->m_isParagraphOpened)
1642 _closeParagraph();
1643
1644 m_ps->m_paragraph.m_listLevelIndex = 0;
1645 _changeList(); // flush the list exterior
1646 }
1647
1648 ///////////////////
1649 // table
1650 ///////////////////
openTable(std::vector<float> const & colWidth,librevenge::RVNGUnit unit)1651 void WPSContentListener::openTable(std::vector<float> const &colWidth, librevenge::RVNGUnit unit)
1652 {
1653 if (m_ps->m_isTableOpened)
1654 {
1655 WPS_DEBUG_MSG(("WPSContentListener::openTable: called with m_isTableOpened=true\n"));
1656 return;
1657 }
1658
1659 if (m_ps->m_isParagraphOpened)
1660 _closeParagraph();
1661
1662 _pushParsingState();
1663 _startSubDocument();
1664 m_ps->m_subDocumentType = libwps::DOC_TABLE;
1665
1666 librevenge::RVNGPropertyList propList;
1667 propList.insert("table:align", "left");
1668 propList.insert("fo:margin-left", 0.0);
1669
1670 float tableWidth = 0;
1671 librevenge::RVNGPropertyListVector columns;
1672
1673 for (auto const &w : colWidth)
1674 {
1675 librevenge::RVNGPropertyList column;
1676 column.insert("style:column-width", double(w), unit);
1677 columns.append(column);
1678
1679 tableWidth += w;
1680 }
1681 propList.insert("style:width", double(tableWidth), unit);
1682 propList.insert("librevenge:table-columns", columns);
1683 m_documentInterface->openTable(propList);
1684 m_ps->m_isTableOpened = true;
1685 }
1686
closeTable()1687 void WPSContentListener::closeTable()
1688 {
1689 if (!m_ps->m_isTableOpened)
1690 {
1691 WPS_DEBUG_MSG(("WPSContentListener::closeTable: called with m_isTableOpened=false\n"));
1692 return;
1693 }
1694
1695 m_ps->m_isTableOpened = false;
1696 _endSubDocument();
1697 m_documentInterface->closeTable();
1698
1699 _popParsingState();
1700 }
1701
openTableRow(float h,librevenge::RVNGUnit unit,bool headerRow)1702 void WPSContentListener::openTableRow(float h, librevenge::RVNGUnit unit, bool headerRow)
1703 {
1704 if (m_ps->m_isTableRowOpened)
1705 {
1706 WPS_DEBUG_MSG(("WPSContentListener::openTableRow: called with m_isTableRowOpened=true\n"));
1707 return;
1708 }
1709 if (!m_ps->m_isTableOpened)
1710 {
1711 WPS_DEBUG_MSG(("WPSContentListener::openTableRow: called with m_isTableOpened=false\n"));
1712 return;
1713 }
1714 librevenge::RVNGPropertyList propList;
1715 propList.insert("librevenge:is-header-row", headerRow);
1716
1717 if (h > 0)
1718 propList.insert("style:row-height", double(h), unit);
1719 else if (h < 0)
1720 propList.insert("style:min-row-height", -double(h), unit);
1721 m_documentInterface->openTableRow(propList);
1722 m_ps->m_isTableRowOpened = true;
1723 }
1724
closeTableRow()1725 void WPSContentListener::closeTableRow()
1726 {
1727 if (!m_ps->m_isTableRowOpened)
1728 {
1729 WPS_DEBUG_MSG(("WPSContentListener::openTableRow: called with m_isTableRowOpened=false\n"));
1730 return;
1731 }
1732 m_ps->m_isTableRowOpened = false;
1733 m_documentInterface->closeTableRow();
1734 }
1735
addEmptyTableCell(Vec2i const & pos,Vec2i span)1736 void WPSContentListener::addEmptyTableCell(Vec2i const &pos, Vec2i span)
1737 {
1738 if (!m_ps->m_isTableRowOpened)
1739 {
1740 WPS_DEBUG_MSG(("WPSContentListener::addEmptyTableCell: called with m_isTableRowOpened=false\n"));
1741 return;
1742 }
1743 if (m_ps->m_isTableCellOpened)
1744 {
1745 WPS_DEBUG_MSG(("WPSContentListener::addEmptyTableCell: called with m_isTableCellOpened=true\n"));
1746 closeTableCell();
1747 }
1748 librevenge::RVNGPropertyList propList;
1749 propList.insert("librevenge:column", pos[0]);
1750 propList.insert("librevenge:row", pos[1]);
1751 propList.insert("table:number-columns-spanned", span[0]);
1752 propList.insert("table:number-rows-spanned", span[1]);
1753 m_documentInterface->openTableCell(propList);
1754 m_documentInterface->closeTableCell();
1755 }
1756
openTableCell(WPSCell const & cell,librevenge::RVNGPropertyList const & extras)1757 void WPSContentListener::openTableCell(WPSCell const &cell, librevenge::RVNGPropertyList const &extras)
1758 {
1759 if (!m_ps->m_isTableRowOpened)
1760 {
1761 WPS_DEBUG_MSG(("WPSContentListener::openTableCell: called with m_isTableRowOpened=false\n"));
1762 return;
1763 }
1764 if (m_ps->m_isTableCellOpened)
1765 {
1766 WPS_DEBUG_MSG(("WPSContentListener::openTableCell: called with m_isTableCellOpened=true\n"));
1767 closeTableCell();
1768 }
1769
1770 librevenge::RVNGPropertyList propList(extras);
1771 cell.addTo(propList);
1772
1773 m_ps->m_isTableCellOpened = true;
1774 m_documentInterface->openTableCell(propList);
1775 }
1776
closeTableCell()1777 void WPSContentListener::closeTableCell()
1778 {
1779 if (!m_ps->m_isTableCellOpened)
1780 {
1781 WPS_DEBUG_MSG(("WPSContentListener::closeTableCell: called with m_isTableCellOpened=false\n"));
1782 return;
1783 }
1784
1785 _closeParagraph();
1786 m_ps->m_paragraph.m_listLevelIndex = 0;
1787 _changeList(); // flush the list exterior
1788
1789 m_ps->m_isTableCellOpened = false;
1790 m_documentInterface->closeTableCell();
1791 }
1792
1793 ///////////////////
1794 // others
1795 ///////////////////
1796
1797 // ---------- state stack ------------------
_pushParsingState()1798 std::shared_ptr<WPSContentParsingState> WPSContentListener::_pushParsingState()
1799 {
1800 auto actual = m_ps;
1801 m_psStack.push_back(actual);
1802 m_ps.reset(new WPSContentParsingState);
1803
1804 // BEGIN: copy page properties into the new parsing state
1805 m_ps->m_pageFormLength = actual->m_pageFormLength;
1806 m_ps->m_pageFormWidth = actual->m_pageFormWidth;
1807 m_ps->m_pageFormOrientationIsPortrait = actual->m_pageFormOrientationIsPortrait;
1808 m_ps->m_pageMarginLeft = actual->m_pageMarginLeft;
1809 m_ps->m_pageMarginRight = actual->m_pageMarginRight;
1810 m_ps->m_pageMarginTop = actual->m_pageMarginTop;
1811 m_ps->m_pageMarginBottom = actual->m_pageMarginBottom;
1812
1813 m_ps->m_isNote = actual->m_isNote;
1814
1815 return actual;
1816 }
1817
_popParsingState()1818 void WPSContentListener::_popParsingState()
1819 {
1820 if (m_psStack.size()==0)
1821 {
1822 WPS_DEBUG_MSG(("WPSContentListener::_popParsingState: psStack is empty()\n"));
1823 throw libwps::ParseException();
1824 }
1825 m_ps = m_psStack.back();
1826 m_psStack.pop_back();
1827 }
1828
1829 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
1830