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 &para)
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 &currentPage = *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 &currentPage = *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