1 /* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2
3 /* libmwaw
4 * Version: MPL 2.0 / LGPLv2+
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 2.0 (the "License"); you may not use this file except in compliance with
8 * the License or as specified alternatively below. You may obtain a copy of
9 * the License at http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * Major Contributor(s):
17 * Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18 * Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20 * Copyright (C) 2006, 2007 Andrew Ziem
21 * Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22 *
23 *
24 * All Rights Reserved.
25 *
26 * For minor contributions see the git repository.
27 *
28 * Alternatively, the contents of this file may be used under the terms of
29 * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30 * in which case the provisions of the LGPLv2+ are applicable
31 * instead of those above.
32 */
33
34 #include <cstring>
35 #include <iomanip>
36 #include <sstream>
37 #include <time.h>
38
39 #include <librevenge/librevenge.h>
40
41 #include "libmwaw/libmwaw.hxx"
42 #include "libmwaw_internal.hxx"
43
44 #include "MWAWCell.hxx"
45 #include "MWAWFont.hxx"
46 #include "MWAWFontConverter.hxx"
47 #include "MWAWGraphicEncoder.hxx"
48 #include "MWAWGraphicStyle.hxx"
49 #include "MWAWGraphicShape.hxx"
50 #include "MWAWInputStream.hxx"
51 #include "MWAWList.hxx"
52 #include "MWAWParagraph.hxx"
53 #include "MWAWParser.hxx"
54 #include "MWAWPosition.hxx"
55 #include "MWAWSection.hxx"
56 #include "MWAWSubDocument.hxx"
57 #include "MWAWTable.hxx"
58
59 #include "MWAWPresentationListener.hxx"
60
61 //! Internal and low level namespace to define the states of MWAWPresentationListener
62 namespace MWAWPresentationListenerInternal
63 {
64 /** the global graphic state of MWAWPresentationListener */
65 struct GraphicState {
66 //! constructor
GraphicStateMWAWPresentationListenerInternal::GraphicState67 explicit GraphicState(std::vector<MWAWPageSpan> const &pageList)
68 : m_pageList(pageList)
69 , m_metaData()
70 , m_isDocumentStarted(false)
71 , m_isPageSpanOpened(false)
72 , m_isMasterPageSpanOpened(false)
73 , m_isAtLeastOnePageOpened(false)
74 , m_isHeaderFooterStarted(false)
75 , m_pageSpan()
76 , m_sentListMarkers()
77 , m_subDocuments()
78 , m_section()
79 {
80 }
81 GraphicState &operator=(GraphicState const &)=default;
82 GraphicState &operator=(GraphicState &&)=default;
83 //! destructor
~GraphicStateMWAWPresentationListenerInternal::GraphicState84 ~GraphicState()
85 {
86 }
87 //! the pages definition
88 std::vector<MWAWPageSpan> m_pageList;
89 //! the document meta data
90 librevenge::RVNGPropertyList m_metaData;
91 /** a flag to know if the document is open */
92 bool m_isDocumentStarted;
93 //! true if a page is open
94 bool m_isPageSpanOpened;
95 //! true if a masterpage is open
96 bool m_isMasterPageSpanOpened;
97 /** true if the first page has been open */
98 bool m_isAtLeastOnePageOpened;
99 /** a flag to know if the header footer is started */
100 bool m_isHeaderFooterStarted;
101 ///! the current page span
102 MWAWPageSpan m_pageSpan;
103 /// the list of marker corresponding to sent list
104 std::vector<int> m_sentListMarkers;
105 //! the list of actual subdocument
106 std::vector<MWAWSubDocumentPtr> m_subDocuments;
107 //! empty section used to return a section in getSection
108 MWAWSection m_section;
109 };
110
111 /** the state of a MWAWPresentationListener */
112 struct State {
113 //! constructor
114 State();
115 //! destructor
~StateMWAWPresentationListenerInternal::State116 ~State() { }
117
118 //! returns true if we are in a text zone, ie. either in a textbox or a table cell
isInTextZoneMWAWPresentationListenerInternal::State119 bool isInTextZone() const
120 {
121 return m_inNote || m_inLink || m_isTextBoxOpened || m_isTableCellOpened;
122 }
123 //! the origin position
124 MWAWVec2f m_origin;
125 //! a buffer to stored the text
126 librevenge::RVNGString m_textBuffer;
127
128 //! the font
129 MWAWFont m_font;
130 //! the paragraph
131 MWAWParagraph m_paragraph;
132 //! the list of list
133 std::shared_ptr<MWAWList> m_list;
134
135 //! a flag to know if openFrame was called
136 bool m_isFrameOpened;
137 //! the frame position
138 MWAWPosition m_framePosition;
139 //! the frame style
140 MWAWGraphicStyle m_frameStyle;
141
142 //! a flag to know if we are in a textbox
143 bool m_isTextBoxOpened;
144 //! a flag to know if openGroup was called
145 bool m_isGroupOpened;
146 //! a flag to know if openLayer was called
147 bool m_isLayerOpened;
148 bool m_isSpanOpened;
149 bool m_isParagraphOpened;
150 bool m_isListElementOpened;
151
152 bool m_firstParagraphInPageSpan;
153
154 std::vector<bool> m_listOrderedLevels; //! a stack used to know what is open
155
156 bool m_isTableOpened;
157 bool m_isTableRowOpened;
158 bool m_isTableColumnOpened;
159 bool m_isTableCellOpened;
160
161 unsigned m_currentPage;
162 int m_numPagesRemainingInSpan;
163 int m_currentPageNumber;
164
165 bool m_inLink;
166 bool m_inNote;
167 bool m_inSubDocument;
168 libmwaw::SubDocumentType m_subDocumentType;
169
170 private:
171 State(const State &) = delete;
172 State &operator=(const State &) = delete;
173 };
174
State()175 State::State()
176 : m_origin(0,0)
177 , m_textBuffer("")
178 , m_font(20,12)/* default time 12 */
179 , m_paragraph()
180 , m_list()
181 , m_isFrameOpened(false)
182 , m_framePosition()
183 , m_frameStyle()
184 , m_isTextBoxOpened(false)
185 , m_isGroupOpened(false)
186 , m_isLayerOpened(false)
187 , m_isSpanOpened(false)
188 , m_isParagraphOpened(false)
189 , m_isListElementOpened(false)
190 , m_firstParagraphInPageSpan(true)
191 , m_listOrderedLevels()
192 , m_isTableOpened(false)
193 , m_isTableRowOpened(false)
194 , m_isTableColumnOpened(false)
195 , m_isTableCellOpened(false)
196 , m_currentPage(0)
197 , m_numPagesRemainingInSpan(0)
198 , m_currentPageNumber(1)
199 , m_inLink(false)
200 , m_inNote(false)
201 , m_inSubDocument(false)
202 , m_subDocumentType(libmwaw::DOC_NONE)
203 {
204 }
205 }
206
MWAWPresentationListener(MWAWParserState & parserState,std::vector<MWAWPageSpan> const & pageList,librevenge::RVNGPresentationInterface * documentInterface)207 MWAWPresentationListener::MWAWPresentationListener(MWAWParserState &parserState, std::vector<MWAWPageSpan> const &pageList, librevenge::RVNGPresentationInterface *documentInterface)
208 : MWAWListener()
209 , m_ds(new MWAWPresentationListenerInternal::GraphicState(pageList))
210 , m_ps(new MWAWPresentationListenerInternal::State)
211 , m_psStack()
212 , m_parserState(parserState)
213 , m_documentInterface(documentInterface)
214 {
215 }
216
~MWAWPresentationListener()217 MWAWPresentationListener::~MWAWPresentationListener()
218 {
219 }
220
221 ///////////////////
222 // text data
223 ///////////////////
insertChar(uint8_t character)224 void MWAWPresentationListener::insertChar(uint8_t character)
225 {
226 if (!m_ps->isInTextZone()) {
227 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertCharacter: called outside a text zone\n"));
228 return;
229 }
230 if (character >= 0x80) {
231 MWAWPresentationListener::insertUnicode(character);
232 return;
233 }
234 if (!m_ps->m_isSpanOpened) _openSpan();
235 m_ps->m_textBuffer.append(char(character));
236 }
237
insertCharacter(unsigned char c)238 void MWAWPresentationListener::insertCharacter(unsigned char c)
239 {
240 if (!m_ps->isInTextZone()) {
241 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertCharacter: called outside a text zone\n"));
242 return;
243 }
244 int unicode = m_parserState.m_fontConverter->unicode(m_ps->m_font.id(), c);
245 if (unicode == -1) {
246 if (c < 0x20) {
247 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertCharacter: Find odd char %x\n", static_cast<unsigned int>(c)));
248 }
249 else
250 MWAWPresentationListener::insertChar(static_cast<uint8_t>(c));
251 }
252 else
253 MWAWPresentationListener::insertUnicode(static_cast<uint32_t>(unicode));
254 }
255
insertCharacter(unsigned char c,MWAWInputStreamPtr & input,long endPos)256 int MWAWPresentationListener::insertCharacter(unsigned char c, MWAWInputStreamPtr &input, long endPos)
257 {
258 if (!m_ps->isInTextZone()) {
259 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertCharacter: called outside a text zone\n"));
260 return 0;
261 }
262 if (!input || !m_parserState.m_fontConverter) {
263 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertCharacter: input or font converter does not exist!!!!\n"));
264 return 0;
265 }
266 long debPos=input->tell();
267 int fId = m_ps->m_font.id();
268 int unicode = endPos==debPos ?
269 m_parserState.m_fontConverter->unicode(fId, c) :
270 m_parserState.m_fontConverter->unicode(fId, c, input);
271
272 long pos=input->tell();
273 if (endPos > 0 && pos > endPos) {
274 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertCharacter: problem reading a character\n"));
275 pos = debPos;
276 input->seek(pos, librevenge::RVNG_SEEK_SET);
277 unicode = m_parserState.m_fontConverter->unicode(fId, c);
278 }
279 if (unicode == -1) {
280 if (c < 0x20) {
281 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertCharacter: Find odd char %x\n", static_cast<unsigned int>(c)));
282 }
283 else
284 MWAWPresentationListener::insertChar(static_cast<uint8_t>(c));
285 }
286 else
287 MWAWPresentationListener::insertUnicode(static_cast<uint32_t>(unicode));
288
289 return int(pos-debPos);
290 }
291
insertUnicode(uint32_t val)292 void MWAWPresentationListener::insertUnicode(uint32_t val)
293 {
294 if (!m_ps->isInTextZone()) {
295 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertUnicode: called outside a text zone\n"));
296 return;
297 }
298 // undef character, we skip it
299 if (val == 0xfffd) return;
300
301 if (!m_ps->m_isSpanOpened) _openSpan();
302 libmwaw::appendUnicode(val, m_ps->m_textBuffer);
303 }
304
insertUnicodeString(librevenge::RVNGString const & str)305 void MWAWPresentationListener::insertUnicodeString(librevenge::RVNGString const &str)
306 {
307 if (!m_ps->isInTextZone()) {
308 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertUnicodeString: called outside a text zone\n"));
309 return;
310 }
311 if (!m_ps->m_isSpanOpened) _openSpan();
312 m_ps->m_textBuffer.append(str);
313 }
314
insertEOL(bool soft)315 void MWAWPresentationListener::insertEOL(bool soft)
316 {
317 if (!m_ps->isInTextZone()) {
318 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertEOL: called outside a text zone\n"));
319 return;
320 }
321 if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
322 _openSpan();
323 if (soft) {
324 _flushText();
325 m_documentInterface->insertLineBreak();
326 }
327 else if (m_ps->m_isParagraphOpened)
328 _closeParagraph();
329
330 // sub/superscript must not survive a new line
331 m_ps->m_font.set(MWAWFont::Script());
332 }
333
insertTab()334 void MWAWPresentationListener::insertTab()
335 {
336 if (!m_ps->isInTextZone()) {
337 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertTab: called outside a text zone\n"));
338 return;
339 }
340 if (!m_ps->m_isSpanOpened) _openSpan();
341 _flushText();
342 m_documentInterface->insertTab();
343 }
344
345 ///////////////////
346 // font/paragraph function
347 ///////////////////
setFont(MWAWFont const & font)348 void MWAWPresentationListener::setFont(MWAWFont const &font)
349 {
350 if (!m_ps->isInTextZone()) {
351 MWAW_DEBUG_MSG(("MWAWPresentationListener::setFont: called outside a text zone\n"));
352 return;
353 }
354 if (font == m_ps->m_font) return;
355
356 // check if id and size are defined, if not used the previous fields
357 MWAWFont finalFont(font);
358 if (font.id() == -1)
359 finalFont.setId(m_ps->m_font.id());
360 if (font.size() <= 0)
361 finalFont.setSize(m_ps->m_font.size());
362 if (finalFont == m_ps->m_font) return;
363
364 _closeSpan();
365 m_ps->m_font = finalFont;
366 }
367
getFont() const368 MWAWFont const &MWAWPresentationListener::getFont() const
369 {
370 return m_ps->m_font;
371 }
372
isParagraphOpened() const373 bool MWAWPresentationListener::isParagraphOpened() const
374 {
375 return m_ps->m_isParagraphOpened;
376 }
377
setParagraph(MWAWParagraph const & para)378 void MWAWPresentationListener::setParagraph(MWAWParagraph const ¶)
379 {
380 if (!m_ps->isInTextZone()) {
381 MWAW_DEBUG_MSG(("MWAWPresentationListener::setParagraph: called outside a text zone\n"));
382 return;
383 }
384 if (para==m_ps->m_paragraph) return;
385
386 m_ps->m_paragraph=para;
387 }
388
getParagraph() const389 MWAWParagraph const &MWAWPresentationListener::getParagraph() const
390 {
391 return m_ps->m_paragraph;
392 }
393
394 ///////////////////
395 // field/link :
396 ///////////////////
insertField(MWAWField const & field)397 void MWAWPresentationListener::insertField(MWAWField const &field)
398 {
399 if (!m_ps->isInTextZone()) {
400 MWAW_DEBUG_MSG(("MWAWPresentationListener::setParagraph: called outside a text zone\n"));
401 return;
402 }
403 librevenge::RVNGPropertyList propList;
404 if (field.addTo(propList)) {
405 _flushText();
406 _openSpan();
407 m_documentInterface->insertField(propList);
408 return;
409 }
410 librevenge::RVNGString text=field.getString();
411 if (!text.empty())
412 MWAWPresentationListener::insertUnicodeString(text);
413 else {
414 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertField: must not be called with type=%d\n", int(field.m_type)));
415 }
416 }
417
openLink(MWAWLink const & link)418 void MWAWPresentationListener::openLink(MWAWLink const &link)
419 {
420 if (!m_ps->isInTextZone()) {
421 MWAW_DEBUG_MSG(("MWAWPresentationListener::openLink: called outside a textbox\n"));
422 return;
423 }
424 if (m_ps->m_inLink) {
425 MWAW_DEBUG_MSG(("MWAWPresentationListener::openLink: called inside a link\n"));
426 return;
427 }
428 if (!m_ps->m_isSpanOpened) _openSpan();
429 librevenge::RVNGPropertyList propList;
430 link.addTo(propList);
431 m_documentInterface->openLink(propList);
432 _pushParsingState();
433 m_ps->m_inLink=true;
434 // we do not want any close open paragraph in a link
435 m_ps->m_isParagraphOpened=true;
436 }
437
closeLink()438 void MWAWPresentationListener::closeLink()
439 {
440 if (!m_ps->m_inLink) {
441 MWAW_DEBUG_MSG(("MWAWPresentationListener::closeLink: closed outside a link\n"));
442 return;
443 }
444 _flushText();
445 m_documentInterface->closeLink();
446 _popParsingState();
447 }
448
449 ///////////////////
450 // document
451 ///////////////////
startDocument()452 void MWAWPresentationListener::startDocument()
453 {
454 if (m_ds->m_isDocumentStarted) {
455 MWAW_DEBUG_MSG(("MWAWPresentationListener::startDocument: the document is already started\n"));
456 return;
457 }
458 m_ds->m_isDocumentStarted=true;
459 m_documentInterface->startDocument(librevenge::RVNGPropertyList());
460 m_documentInterface->setDocumentMetaData(m_ds->m_metaData);
461 }
462
endDocument(bool)463 void MWAWPresentationListener::endDocument(bool /*delayed*/)
464 {
465 if (!m_ds->m_isDocumentStarted) {
466 MWAW_DEBUG_MSG(("MWAWPresentationListener::endDocument: the document is not started\n"));
467 return;
468 }
469 if (!m_ds->m_isAtLeastOnePageOpened) {
470 MWAW_DEBUG_MSG(("MWAWPresentationListener::endDocument: no data have been send\n"));
471 _openPageSpan();
472 }
473 if (m_ds->m_isPageSpanOpened)
474 _closePageSpan(m_ds->m_isMasterPageSpanOpened);
475 m_documentInterface->endDocument();
476 m_ds->m_isDocumentStarted=false;
477 *m_ds=MWAWPresentationListenerInternal::GraphicState(std::vector<MWAWPageSpan>());
478 }
479
480 ///////////////////
481 // document
482 ///////////////////
setDocumentMetaData(librevenge::RVNGPropertyList const & meta)483 void MWAWPresentationListener::setDocumentMetaData(librevenge::RVNGPropertyList const &meta)
484 {
485 librevenge::RVNGPropertyList::Iter i(meta);
486 for (i.rewind(); i.next();)
487 m_ds->m_metaData.insert(i.key(), i()->getStr());
488 }
489
setDocumentLanguage(std::string const & locale)490 void MWAWPresentationListener::setDocumentLanguage(std::string const &locale)
491 {
492 if (!locale.length()) return;
493 m_ds->m_metaData.insert("librevenge:language", locale.c_str());
494 }
495
isDocumentStarted() const496 bool MWAWPresentationListener::isDocumentStarted() const
497 {
498 return m_ds->m_isDocumentStarted;
499 }
500
canWriteText() const501 bool MWAWPresentationListener::canWriteText() const
502 {
503 return m_ds->m_isPageSpanOpened && m_ps->isInTextZone();
504 }
505
506 ///////////////////
507 // page
508 ///////////////////
isPageSpanOpened() const509 bool MWAWPresentationListener::isPageSpanOpened() const
510 {
511 return m_ds->m_isPageSpanOpened;
512 }
513
getPageSpan()514 MWAWPageSpan const &MWAWPresentationListener::getPageSpan()
515 {
516 if (!m_ds->m_isPageSpanOpened)
517 _openPageSpan();
518 return m_ds->m_pageSpan;
519 }
520
openMasterPage(MWAWPageSpan & masterPage)521 bool MWAWPresentationListener::openMasterPage(MWAWPageSpan &masterPage)
522 {
523 if (m_ds->m_isMasterPageSpanOpened) {
524 MWAW_DEBUG_MSG(("MWAWPresentationListener::openMasterPage: a master page is already opened\n"));
525 return false;
526 }
527 if (!m_ds->m_isDocumentStarted)
528 startDocument();
529 if (m_ds->m_isPageSpanOpened)
530 _closePageSpan();
531
532 librevenge::RVNGPropertyList propList;
533 masterPage.getPageProperty(propList, true);
534 propList.insert("svg:width",72.*masterPage.getFormWidth(), librevenge::RVNG_POINT);
535 propList.insert("svg:height",72.*masterPage.getFormLength(), librevenge::RVNG_POINT);
536
537 m_documentInterface->startMasterSlide(propList);
538 m_ds->m_isPageSpanOpened = m_ds->m_isMasterPageSpanOpened = true;
539
540 // checkme: can we send some header/footer if some exists
541 return true;
542 }
543
_openPageSpan(bool sendHeaderFooters)544 void MWAWPresentationListener::_openPageSpan(bool sendHeaderFooters)
545 {
546 if (m_ds->m_isPageSpanOpened)
547 return;
548
549 if (!m_ds->m_isDocumentStarted)
550 startDocument();
551
552 if (m_ds->m_pageList.size()==0) {
553 MWAW_DEBUG_MSG(("MWAWPresentationListener::_openPageSpan: can not find any page\n"));
554 throw libmwaw::ParseException();
555 }
556 m_ds->m_isAtLeastOnePageOpened=true;
557 unsigned actPage = 0;
558 auto it = m_ds->m_pageList.begin();
559 m_ps->m_currentPage++;
560 while (true) {
561 actPage+=static_cast<unsigned>(it->getPageSpan());
562 if (actPage >= m_ps->m_currentPage) break;
563 if (++it == m_ds->m_pageList.end()) {
564 MWAW_DEBUG_MSG(("MWAWPresentationListener::_openPageSpan: can not find current page, use the previous one\n"));
565 --it;
566 break;
567 }
568 }
569
570 MWAWPageSpan ¤tPage = *it;
571 librevenge::RVNGPropertyList propList;
572 currentPage.getPageProperty(propList, true);
573 propList.insert("librevenge:is-last-page-span", ++it == m_ds->m_pageList.end());
574 // now add data for embedded graph
575 propList.insert("svg:x",double(m_ps->m_origin.x()), librevenge::RVNG_POINT);
576 propList.insert("svg:y",double(m_ps->m_origin.y()), librevenge::RVNG_POINT);
577 propList.insert("svg:width",72.*currentPage.getFormWidth(), librevenge::RVNG_POINT);
578 propList.insert("svg:height",72.*currentPage.getFormLength(), librevenge::RVNG_POINT);
579 propList.insert("librevenge:enforce-frame",true);
580
581 if (!m_ds->m_isPageSpanOpened)
582 m_documentInterface->startSlide(propList);
583 m_ds->m_isPageSpanOpened = true;
584 m_ds->m_pageSpan = currentPage;
585
586 // we insert the header footer
587 if (sendHeaderFooters)
588 currentPage.sendHeaderFooters(this, (m_ps->m_currentPage%2) ? MWAWHeaderFooter::EVEN : MWAWHeaderFooter::ODD);
589
590 // first paragraph in span (necessary for resetting page number)
591 m_ps->m_firstParagraphInPageSpan = true;
592 m_ps->m_numPagesRemainingInSpan = (currentPage.getPageSpan() - 1);
593 }
594
_closePageSpan(bool masterPage)595 void MWAWPresentationListener::_closePageSpan(bool masterPage)
596 {
597 if (!m_ds->m_isPageSpanOpened)
598 return;
599
600 if (masterPage && !m_ds->m_isMasterPageSpanOpened) {
601 MWAW_DEBUG_MSG(("MWAWPresentationListener::endDocument:no master page are opened\n"));
602 return;
603 }
604 if (!masterPage && m_ds->m_isMasterPageSpanOpened) {
605 MWAW_DEBUG_MSG(("MWAWPresentationListener::endDocument:a master page are opened\n"));
606 return;
607 }
608 if (m_ps->m_inSubDocument) {
609 MWAW_DEBUG_MSG(("MWAWPresentationListener::endDocument: we are in a sub document\n"));
610 _endSubDocument();
611 _popParsingState();
612 }
613 if (m_ps->m_isTableOpened) {
614 MWAW_DEBUG_MSG(("MWAWPresentationListener::_closePageSpan: we are in a table zone\n"));
615 closeTable();
616 }
617 if (m_ps->isInTextZone()) {
618 MWAW_DEBUG_MSG(("MWAWPresentationListener::_closePageSpan: we are in a text zone\n"));
619 if (m_ps->m_isParagraphOpened)
620 _closeParagraph();
621 m_ps->m_paragraph.m_listLevelIndex = 0;
622 _changeList(); // flush the list exterior
623 }
624 m_ds->m_isPageSpanOpened = m_ds->m_isMasterPageSpanOpened = false;
625 if (masterPage)
626 m_documentInterface->endMasterSlide();
627 else
628 m_documentInterface->endSlide();
629 }
630
631 ///////////////////
632 // paragraph
633 ///////////////////
_openParagraph()634 void MWAWPresentationListener::_openParagraph()
635 {
636 if (m_ps->m_inNote || (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened))
637 return;
638 if (!m_ps->isInTextZone()) {
639 MWAW_DEBUG_MSG(("MWAWPresentationListener::_openParagraph: called outsize a text zone\n"));
640 return;
641 }
642 if (m_ps->m_isParagraphOpened || m_ps->m_isListElementOpened) {
643 MWAW_DEBUG_MSG(("MWAWPresentationListener::_openParagraph: a paragraph (or a list) is already opened"));
644 return;
645 }
646
647 librevenge::RVNGPropertyList propList;
648 m_ps->m_paragraph.addTo(propList, m_ps->m_isTableCellOpened);
649 m_documentInterface->openParagraph(propList);
650
651 _resetParagraphState();
652 m_ps->m_firstParagraphInPageSpan = false;
653 }
654
_closeParagraph()655 void MWAWPresentationListener::_closeParagraph()
656 {
657 if (!m_ps->isInTextZone()) {
658 MWAW_DEBUG_MSG(("MWAWPresentationListener::_closeParagraph: called outsize a text zone\n"));
659 return;
660 }
661 if (m_ps->m_inLink) return;
662 if (m_ps->m_isListElementOpened) {
663 _closeListElement();
664 return;
665 }
666
667 if (m_ps->m_isParagraphOpened) {
668 if (m_ps->m_isSpanOpened)
669 _closeSpan();
670 m_documentInterface->closeParagraph();
671 }
672
673 m_ps->m_isParagraphOpened = false;
674 m_ps->m_paragraph.m_listLevelIndex = 0;
675 }
676
_resetParagraphState(const bool isListElement)677 void MWAWPresentationListener::_resetParagraphState(const bool isListElement)
678 {
679 m_ps->m_isListElementOpened = isListElement;
680 m_ps->m_isParagraphOpened = true;
681 }
682
683 ///////////////////
684 // list
685 ///////////////////
_openListElement()686 void MWAWPresentationListener::_openListElement()
687 {
688 if (m_ps->m_inNote || (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened))
689 return;
690 if (!m_ps->isInTextZone()) {
691 MWAW_DEBUG_MSG(("MWAWPresentationListener::_openListElement: called outsize a text zone\n"));
692 return;
693 }
694 if (m_ps->m_isParagraphOpened || m_ps->m_isListElementOpened)
695 return;
696
697 librevenge::RVNGPropertyList propList;
698 m_ps->m_paragraph.addTo(propList,m_ps->m_isTableOpened);
699
700 // check if we must change the start value
701 int startValue=m_ps->m_paragraph.m_listStartValue.get();
702 if (startValue > 0 && m_ps->m_list && m_ps->m_list->getStartValueForNextElement() != startValue) {
703 propList.insert("text:start-value", startValue);
704 m_ps->m_list->setStartValueForNextElement(startValue);
705 }
706
707 if (m_ps->m_list) m_ps->m_list->openElement();
708 m_documentInterface->openListElement(propList);
709 _resetParagraphState(true);
710 }
711
_closeListElement()712 void MWAWPresentationListener::_closeListElement()
713 {
714 if (m_ps->m_isListElementOpened) {
715 if (m_ps->m_isSpanOpened)
716 _closeSpan();
717
718 if (m_ps->m_list) m_ps->m_list->closeElement();
719 m_documentInterface->closeListElement();
720 }
721
722 m_ps->m_isListElementOpened = m_ps->m_isParagraphOpened = false;
723 }
724
_getListId() const725 int MWAWPresentationListener::_getListId() const
726 {
727 auto newLevel= size_t(m_ps->m_paragraph.m_listLevelIndex.get());
728 if (newLevel == 0) return -1;
729 int newListId = m_ps->m_paragraph.m_listId.get();
730 if (newListId > 0) return newListId;
731 static bool first = true;
732 if (first) {
733 MWAW_DEBUG_MSG(("MWAWPresentationListener::_getListId: the list id is not set, try to find a new one\n"));
734 first = false;
735 }
736 auto list=m_parserState.m_listManager->getNewList(m_ps->m_list, int(newLevel), *m_ps->m_paragraph.m_listLevel);
737 if (!list) return -1;
738 return list->getId();
739 }
740
_changeList()741 void MWAWPresentationListener::_changeList()
742 {
743 if (m_ps->m_inNote || !m_ps->isInTextZone()) {
744 MWAW_DEBUG_MSG(("MWAWPresentationListener::_changeList: called outsize a text zone\n"));
745 return;
746 }
747 if (m_ps->m_isParagraphOpened)
748 _closeParagraph();
749
750 size_t actualLevel = m_ps->m_listOrderedLevels.size();
751 auto newLevel= size_t(m_ps->m_paragraph.m_listLevelIndex.get());
752 if (newLevel>100) {
753 MWAW_DEBUG_MSG(("MWAWPresentationListener::_changeList: find level=%d, set it to 100\n", static_cast<int>(newLevel)));
754 newLevel=100;
755 }
756 int newListId = newLevel>0 ? _getListId() : -1;
757 bool changeList = newLevel &&
758 (m_ps->m_list && m_ps->m_list->getId()!=newListId);
759 size_t minLevel = changeList ? 0 : newLevel;
760 while (actualLevel > minLevel) {
761 if (m_ps->m_listOrderedLevels[--actualLevel])
762 m_documentInterface->closeOrderedListLevel();
763 else
764 m_documentInterface->closeUnorderedListLevel();
765 }
766
767 if (newLevel) {
768 std::shared_ptr<MWAWList> theList;
769
770 theList=m_parserState.m_listManager->getList(newListId);
771 if (!theList) {
772 MWAW_DEBUG_MSG(("MWAWPresentationListener::_changeList: can not find any list\n"));
773 m_ps->m_listOrderedLevels.resize(actualLevel);
774 return;
775 }
776 m_parserState.m_listManager->needToSend(newListId, m_ds->m_sentListMarkers);
777 m_ps->m_list = theList;
778 m_ps->m_list->setLevel(static_cast<int>(newLevel));
779 }
780
781 m_ps->m_listOrderedLevels.resize(newLevel, false);
782 if (actualLevel == newLevel) return;
783
784 librevenge::RVNGPropertyList propList;
785 propList.insert("librevenge:list-id", m_ps->m_list->getId());
786 for (size_t i=actualLevel+1; i<= newLevel; i++) {
787 bool ordered = m_ps->m_list->isNumeric(int(i));
788 m_ps->m_listOrderedLevels[i-1] = ordered;
789
790 librevenge::RVNGPropertyList level;
791 m_ps->m_list->addTo(int(i), level, m_parserState.m_fontManager);
792 if (ordered)
793 m_documentInterface->openOrderedListLevel(level);
794 else
795 m_documentInterface->openUnorderedListLevel(level);
796 }
797 }
798
799 ///////////////////
800 // span
801 ///////////////////
_openSpan()802 void MWAWPresentationListener::_openSpan()
803 {
804 if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
805 return;
806 if (m_ps->m_inLink)
807 return;
808 if (!m_ps->isInTextZone()) {
809 MWAW_DEBUG_MSG(("MWAWPresentationListener::_openSpan: called outsize a text zone\n"));
810 return;
811 }
812 if (m_ps->m_isSpanOpened)
813 return;
814
815 if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened) {
816 _changeList();
817 if (*m_ps->m_paragraph.m_listLevelIndex == 0)
818 _openParagraph();
819 else
820 _openListElement();
821 }
822
823 librevenge::RVNGPropertyList propList;
824 m_ps->m_font.addTo(propList, m_parserState.m_fontConverter);
825
826 m_documentInterface->openSpan(propList);
827
828 m_ps->m_isSpanOpened = true;
829 }
830
_closeSpan()831 void MWAWPresentationListener::_closeSpan()
832 {
833 if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
834 return;
835 if (!m_ps->isInTextZone()) {
836 MWAW_DEBUG_MSG(("MWAWPresentationListener::_closeSpan: called outsize a text zone\n"));
837 return;
838 }
839 if (!m_ps->m_isSpanOpened)
840 return;
841
842 _flushText();
843 m_documentInterface->closeSpan();
844 m_ps->m_isSpanOpened = false;
845 }
846
847 ///////////////////
848 // text (send data)
849 ///////////////////
_flushText()850 void MWAWPresentationListener::_flushText()
851 {
852 if (m_ps->m_textBuffer.len() == 0) return;
853
854 // when some many ' ' follows each other, call insertSpace
855 librevenge::RVNGString tmpText("");
856 int numConsecutiveSpaces = 0;
857 librevenge::RVNGString::Iter i(m_ps->m_textBuffer);
858 for (i.rewind(); i.next();) {
859 if (*(i()) == 0x20) // this test is compatible with unicode format
860 numConsecutiveSpaces++;
861 else
862 numConsecutiveSpaces = 0;
863
864 if (numConsecutiveSpaces > 1) {
865 if (tmpText.len() > 0) {
866 m_documentInterface->insertText(tmpText);
867 tmpText.clear();
868 }
869 m_documentInterface->insertSpace();
870 }
871 else
872 tmpText.append(i());
873 }
874 m_documentInterface->insertText(tmpText);
875 m_ps->m_textBuffer.clear();
876 }
877
878 ///////////////////
879 // header/footer
880 ///////////////////
isHeaderFooterOpened() const881 bool MWAWPresentationListener::isHeaderFooterOpened() const
882 {
883 return m_ds->m_isHeaderFooterStarted;
884 }
885
insertHeader(MWAWSubDocumentPtr const & subDocument,librevenge::RVNGPropertyList const & extras)886 bool MWAWPresentationListener::insertHeader(MWAWSubDocumentPtr const &subDocument, librevenge::RVNGPropertyList const &extras)
887 {
888 if (m_ds->m_isHeaderFooterStarted) {
889 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertHeader: Oops a header/footer is already opened\n"));
890 return false;
891 }
892 // we do not have any header interface, so mimick it by creating a textbox
893 MWAWPosition pos(MWAWVec2f(20,20), MWAWVec2f(-20,-10), librevenge::RVNG_POINT); // fixme
894 pos.m_anchorTo=MWAWPosition::Page;
895 if (!openFrame(pos))
896 return false;
897 librevenge::RVNGPropertyList propList(extras);
898 _handleFrameParameters(propList, pos, MWAWGraphicStyle::emptyStyle());
899
900 m_documentInterface->startTextObject(propList);
901 handleSubDocument(pos.origin(), subDocument, libmwaw::DOC_HEADER_FOOTER);
902 m_documentInterface->endTextObject();
903 closeFrame();
904 return true;
905 }
906
insertFooter(MWAWSubDocumentPtr const & subDocument,librevenge::RVNGPropertyList const & extras)907 bool MWAWPresentationListener::insertFooter(MWAWSubDocumentPtr const &subDocument, librevenge::RVNGPropertyList const &extras)
908 {
909 if (m_ds->m_isHeaderFooterStarted) {
910 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertFooter: Oops a header/footer is already opened\n"));
911 return false;
912 }
913 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertFooter: inserting footer is very experimental\n"));
914
915 // we do not have any header interface, so mimick it by creating a textbox
916 MWAWPageSpan page(getPageSpan()); // fixme
917 MWAWPosition pos(MWAWVec2f(20,72.f*float(page.getFormLength())-40.f), MWAWVec2f(-20,-10), librevenge::RVNG_POINT);
918 pos.m_anchorTo=MWAWPosition::Page;
919 if (!openFrame(pos))
920 return false;
921 librevenge::RVNGPropertyList propList(extras);
922 _handleFrameParameters(propList, pos, MWAWGraphicStyle::emptyStyle());
923
924 m_documentInterface->startTextObject(propList);
925 handleSubDocument(pos.origin(), subDocument, libmwaw::DOC_HEADER_FOOTER);
926 m_documentInterface->endTextObject();
927 closeFrame();
928 return true;
929 }
930
931 ///////////////////
932 // section
933 ///////////////////
getSection() const934 MWAWSection const &MWAWPresentationListener::getSection() const
935 {
936 MWAW_DEBUG_MSG(("MWAWPresentationListener::getSection: must not be called\n"));
937 return m_ds->m_section;
938 }
939
openSection(MWAWSection const &)940 bool MWAWPresentationListener::openSection(MWAWSection const &)
941 {
942 MWAW_DEBUG_MSG(("MWAWPresentationListener::openSection: must not be called\n"));
943 return false;
944 }
945
insertBreak(BreakType breakType)946 void MWAWPresentationListener::insertBreak(BreakType breakType)
947 {
948 if (m_ps->m_inSubDocument)
949 return;
950
951 switch (breakType) {
952 case ColumnBreak:
953 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertBreak: must not be called on column\n"));
954 break;
955 case SoftPageBreak:
956 case PageBreak:
957 if (m_ds->m_isMasterPageSpanOpened) {
958 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertBreak: can not insert a page break in master page definition\n"));
959 break;
960 }
961 if (!m_ds->m_isPageSpanOpened)
962 _openPageSpan();
963 _closePageSpan();
964 break;
965 #if !defined(__clang__)
966 default:
967 break;
968 #endif
969 }
970 }
971
972 ///////////////////
973 // note/comment ( we inserted them as text between -- -- )
974 ///////////////////
insertComment(MWAWSubDocumentPtr & subDocument)975 void MWAWPresentationListener::insertComment(MWAWSubDocumentPtr &subDocument)
976 {
977 if (!canWriteText() || m_ps->m_inNote) {
978 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertComment try to insert recursively or outside a text zone\n"));
979 return;
980 }
981 // first check that a paragraph is already open
982 if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
983 _openParagraph();
984 insertChar(' ');
985 insertUnicode(0x2014); // -
986 insertChar(' ');
987 handleSubDocument(subDocument, libmwaw::DOC_COMMENT_ANNOTATION);
988 insertChar(' ');
989 insertUnicode(0x2014); // -
990 insertChar(' ');
991 }
992
insertNote(MWAWNote const &,MWAWSubDocumentPtr & subDocument)993 void MWAWPresentationListener::insertNote(MWAWNote const &, MWAWSubDocumentPtr &subDocument)
994 {
995 if (!canWriteText() || m_ps->m_inNote) {
996 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertNote try to insert recursively or outside a text zone\n"));
997 return;
998 }
999 // first check that a paragraph is already open
1000 if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
1001 _openParagraph();
1002 insertChar(' ');
1003 insertUnicode(0x2014); // -
1004 insertChar(' ');
1005 handleSubDocument(subDocument, libmwaw::DOC_NOTE);
1006 insertChar(' ');
1007 insertUnicode(0x2014); // -
1008 insertChar(' ');
1009 }
1010
1011 ///////////////////
1012 // picture/textbox
1013 ///////////////////
1014
insertShape(MWAWPosition const & pos,MWAWGraphicShape const & shape,MWAWGraphicStyle const & style)1015 void MWAWPresentationListener::insertShape
1016 (MWAWPosition const &pos, MWAWGraphicShape const &shape, MWAWGraphicStyle const &style)
1017 {
1018 if (!m_ds->m_isDocumentStarted) {
1019 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertShape: the document is not started\n"));
1020 return;
1021 }
1022 if (!m_ds->m_isPageSpanOpened)
1023 _openPageSpan();
1024 if (m_ps->m_isFrameOpened) {
1025 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertShape: a frame is already open\n"));
1026 return;
1027 }
1028
1029 librevenge::RVNGPropertyList list, shapePList;
1030 style.addTo(list, shape.getType()==MWAWGraphicShape::Line);
1031 m_documentInterface->setStyle(list);
1032 switch (shape.addTo(1.f/pos.getInvUnitScale(librevenge::RVNG_POINT)*pos.origin()-m_ps->m_origin, style.hasSurface(), shapePList)) {
1033 case MWAWGraphicShape::C_Ellipse:
1034 m_documentInterface->drawEllipse(shapePList);
1035 break;
1036 case MWAWGraphicShape::C_Path:
1037 m_documentInterface->drawPath(shapePList);
1038 break;
1039 case MWAWGraphicShape::C_Polyline:
1040 m_documentInterface->drawPolyline(shapePList);
1041 break;
1042 case MWAWGraphicShape::C_Polygon:
1043 m_documentInterface->drawPolygon(shapePList);
1044 break;
1045 case MWAWGraphicShape::C_Rectangle:
1046 m_documentInterface->drawRectangle(shapePList);
1047 break;
1048 case MWAWGraphicShape::C_Bad:
1049 break;
1050 #if !defined(__clang__)
1051 default:
1052 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertShape: unexpected shape\n"));
1053 break;
1054 #endif
1055 }
1056 }
1057
insertPicture(MWAWPosition const & pos,MWAWEmbeddedObject const & picture,MWAWGraphicStyle const & style)1058 void MWAWPresentationListener::insertPicture(MWAWPosition const &pos, MWAWEmbeddedObject const &picture, MWAWGraphicStyle const &style)
1059 {
1060 if (!m_ds->m_isDocumentStarted) {
1061 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertPicture: the document is not started\n"));
1062 return;
1063 }
1064 if (m_ps->m_isFrameOpened) {
1065 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertPicture: a frame is already open\n"));
1066 return;
1067 }
1068 if (!m_ds->m_isPageSpanOpened)
1069 _openPageSpan();
1070 librevenge::RVNGPropertyList list;
1071 style.addTo(list);
1072 m_documentInterface->setStyle(list);
1073
1074 list.clear();
1075 _handleFrameParameters(list, pos, style);
1076 float rotate = style.m_rotate;
1077 if (style.m_flip[0]&&style.m_flip[1]) rotate += 180.f;
1078 if (rotate<0||rotate>0) {
1079 list.insert("librevenge:rotate", double(rotate), librevenge::RVNG_GENERIC);
1080 float pointFactor =1.f/pos.getInvUnitScale(librevenge::RVNG_POINT);
1081 MWAWVec2f size=pointFactor*pos.size();
1082 if (size[0]<0) size[0]=-size[0];
1083 if (size[1]<0) size[1]=-size[1];
1084 MWAWVec2f center=pointFactor*pos.origin()-m_ps->m_origin+0.5f*size;
1085 list.insert("librevenge:rotate-cx",double(center[0]), librevenge::RVNG_POINT);
1086 list.insert("librevenge:rotate-cy",double(center[1]), librevenge::RVNG_POINT);
1087 }
1088 if (picture.addTo(list))
1089 m_documentInterface->drawGraphicObject(list);
1090 }
1091
insertTextBox(MWAWPosition const & pos,MWAWSubDocumentPtr const & subDocument,MWAWGraphicStyle const & style)1092 void MWAWPresentationListener::insertTextBox
1093 (MWAWPosition const &pos, MWAWSubDocumentPtr const &subDocument, MWAWGraphicStyle const &style)
1094 {
1095 if (!m_ds->m_isDocumentStarted) {
1096 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertTextBox: the document is not started\n"));
1097 return;
1098 }
1099 if (!m_ds->m_isPageSpanOpened)
1100 _openPageSpan();
1101 float pointFactor =1.f/pos.getInvUnitScale(librevenge::RVNG_POINT);
1102 if (m_ps->m_isTextBoxOpened) {
1103 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertTextBox: can not insert a textbox in a textbox\n"));
1104 handleSubDocument(pointFactor*pos.origin(), subDocument, libmwaw::DOC_TEXT_BOX);
1105 return;
1106 }
1107 if (!openFrame(pos))
1108 return;
1109 librevenge::RVNGPropertyList propList;
1110 _handleFrameParameters(propList, pos, style);
1111 float rotate = style.m_rotate;
1112 // flip does not works on text, so we ignore it...
1113 if (style.m_flip[0]&&style.m_flip[1]) rotate += 180.f;
1114 if (rotate<0||rotate>0) {
1115 propList.insert("librevenge:rotate", double(rotate), librevenge::RVNG_GENERIC);
1116 MWAWVec2f size=pointFactor*pos.size();
1117 if (size[0]<0) size[0]=-size[0];
1118 if (size[1]<0) size[1]=-size[1];
1119 MWAWVec2f center=pointFactor*pos.origin()-m_ps->m_origin+0.5f*size;
1120 propList.insert("librevenge:rotate-cx", double(center[0]), librevenge::RVNG_POINT);
1121 propList.insert("librevenge:rotate-cy", double(center[1]), librevenge::RVNG_POINT);
1122 }
1123 m_documentInterface->startTextObject(propList);
1124 handleSubDocument(pointFactor*pos.origin(), subDocument, libmwaw::DOC_TEXT_BOX);
1125 m_documentInterface->endTextObject();
1126 closeFrame();
1127 }
1128
insertSlideNote(MWAWPosition const & pos,MWAWSubDocumentPtr & subDocument)1129 void MWAWPresentationListener::insertSlideNote(MWAWPosition const &pos, MWAWSubDocumentPtr &subDocument)
1130 {
1131 if (!m_ds->m_isDocumentStarted) {
1132 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertSlideNote: the document is not started\n"));
1133 return;
1134 }
1135 if (!m_ds->m_isPageSpanOpened)
1136 _openPageSpan();
1137 float pointFactor =1.f/pos.getInvUnitScale(librevenge::RVNG_POINT);
1138 if (m_ps->m_isTextBoxOpened) {
1139 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertSlideNote: can not insert a textbox in a textbox\n"));
1140 handleSubDocument(pointFactor*pos.origin(), subDocument, libmwaw::DOC_TEXT_BOX);
1141 return;
1142 }
1143 if (!openFrame(pos))
1144 return;
1145 librevenge::RVNGPropertyList propList;
1146 _handleFrameParameters(propList, pos, MWAWGraphicStyle::emptyStyle());
1147 m_documentInterface->startNotes(propList);
1148 handleSubDocument(pointFactor*pos.origin(), subDocument, libmwaw::DOC_TEXT_BOX);
1149 m_documentInterface->endNotes();
1150 closeFrame();
1151 }
1152
insertGroup(MWAWBox2f const & bdbox,MWAWSubDocumentPtr const & subDocument)1153 void MWAWPresentationListener::insertGroup(MWAWBox2f const &bdbox, MWAWSubDocumentPtr const &subDocument)
1154 {
1155 if (!m_ds->m_isDocumentStarted || m_ps->isInTextZone()) {
1156 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertGroup: can not insert a group\n"));
1157 return;
1158 }
1159 if (!m_ds->m_isPageSpanOpened)
1160 _openPageSpan();
1161 handleSubDocument(bdbox[0], subDocument, libmwaw::DOC_GRAPHIC_GROUP);
1162 }
1163
1164 ///////////////////
1165 // table
1166 ///////////////////
insertTable(MWAWPosition const & pos,MWAWTable & table,MWAWGraphicStyle const & style)1167 void MWAWPresentationListener::insertTable
1168 (MWAWPosition const &pos, MWAWTable &table, MWAWGraphicStyle const &style)
1169 {
1170 if (!m_ds->m_isDocumentStarted || m_ps->m_inSubDocument) {
1171 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertTable insert a table in a subdocument is not implemented\n"));
1172 return;
1173 }
1174 if (!openFrame(pos, style)) return;
1175
1176 _pushParsingState();
1177 _startSubDocument();
1178 m_ps->m_subDocumentType = libmwaw::DOC_TABLE;
1179
1180 std::shared_ptr<MWAWListener> listen(this, MWAW_shared_ptr_noop_deleter<MWAWPresentationListener>());
1181 try {
1182 table.sendTable(listen);
1183 }
1184 catch (...) {
1185 MWAW_DEBUG_MSG(("MWAWPresentationListener::insertTable exception catched \n"));
1186 }
1187 _endSubDocument();
1188 _popParsingState();
1189
1190 closeFrame();
1191 }
1192
openTable(MWAWTable const & table)1193 void MWAWPresentationListener::openTable(MWAWTable const &table)
1194 {
1195 if (!m_ps->m_isFrameOpened) {
1196 if (m_ps->m_isTextBoxOpened) {
1197 MWAW_DEBUG_MSG(("MWAWPresentationListener::openTable: must not be called inside a textbox\n"));
1198 MWAWPosition pos(m_ps->m_origin, MWAWVec2f(400,100), librevenge::RVNG_POINT);
1199 pos.m_anchorTo=MWAWPosition::Page;
1200 openTable(pos, table, MWAWGraphicStyle::emptyStyle());
1201 return;
1202 }
1203 MWAW_DEBUG_MSG(("MWAWPresentationListener::openTable: called outside openFrame\n"));
1204 return;
1205 }
1206 openTable(m_ps->m_framePosition, table, m_ps->m_frameStyle);
1207 }
1208
openTable(MWAWPosition const & pos,MWAWTable const & table,MWAWGraphicStyle const & style)1209 void MWAWPresentationListener::openTable(MWAWPosition const &pos, MWAWTable const &table, MWAWGraphicStyle const &style)
1210 {
1211 if (!m_ps->m_isFrameOpened || m_ps->m_isTableOpened) {
1212 MWAW_DEBUG_MSG(("MWAWPresentationListener::openTable: no frame is already open...\n"));
1213 return;
1214 }
1215
1216 if (m_ps->m_isParagraphOpened)
1217 _closeParagraph();
1218
1219 librevenge::RVNGPropertyList propList;
1220 // default value: which can be redefined by table
1221 propList.insert("table:align", "left");
1222 propList.insert("fo:margin-left", *m_ps->m_paragraph.m_margins[1], *m_ps->m_paragraph.m_marginsUnit);
1223 _pushParsingState();
1224 _startSubDocument();
1225 m_ps->m_subDocumentType = libmwaw::DOC_TABLE;
1226
1227 _handleFrameParameters(propList, pos, style);
1228 table.addTablePropertiesTo(propList);
1229 m_documentInterface->startTableObject(propList);
1230 m_ps->m_isTableOpened = true;
1231 }
1232
closeTable()1233 void MWAWPresentationListener::closeTable()
1234 {
1235 if (!m_ps->m_isTableOpened) {
1236 MWAW_DEBUG_MSG(("MWAWPresentationListener::closeTable: called with m_isTableOpened=false\n"));
1237 return;
1238 }
1239
1240 m_ps->m_isTableOpened = false;
1241 _endSubDocument();
1242 m_documentInterface->endTableObject();
1243
1244 _popParsingState();
1245 }
1246
openTableRow(float h,librevenge::RVNGUnit unit,bool headerRow)1247 void MWAWPresentationListener::openTableRow(float h, librevenge::RVNGUnit unit, bool headerRow)
1248 {
1249 if (m_ps->m_isTableRowOpened) {
1250 MWAW_DEBUG_MSG(("MWAWPresentationListener::openTableRow: called with m_isTableRowOpened=true\n"));
1251 return;
1252 }
1253 if (!m_ps->m_isTableOpened) {
1254 MWAW_DEBUG_MSG(("MWAWPresentationListener::openTableRow: called with m_isTableOpened=false\n"));
1255 return;
1256 }
1257 librevenge::RVNGPropertyList propList;
1258 propList.insert("librevenge:is-header-row", headerRow);
1259
1260 if (h > 0)
1261 propList.insert("style:row-height", double(h), unit);
1262 else if (h < 0)
1263 propList.insert("style:min-row-height", double(-h), unit);
1264 m_documentInterface->openTableRow(propList);
1265 m_ps->m_isTableRowOpened = true;
1266 }
1267
closeTableRow()1268 void MWAWPresentationListener::closeTableRow()
1269 {
1270 if (!m_ps->m_isTableRowOpened) {
1271 MWAW_DEBUG_MSG(("MWAWPresentationListener::closeTableRow: called with m_isTableRowOpened=false\n"));
1272 return;
1273 }
1274 m_ps->m_isTableRowOpened = false;
1275 m_documentInterface->closeTableRow();
1276 }
1277
addEmptyTableCell(MWAWVec2i const & pos,MWAWVec2i span)1278 void MWAWPresentationListener::addEmptyTableCell(MWAWVec2i const &pos, MWAWVec2i span)
1279 {
1280 if (!m_ps->m_isTableRowOpened) {
1281 MWAW_DEBUG_MSG(("MWAWPresentationListener::addEmptyTableCell: called with m_isTableRowOpened=false\n"));
1282 return;
1283 }
1284 if (m_ps->m_isTableCellOpened) {
1285 MWAW_DEBUG_MSG(("MWAWPresentationListener::addEmptyTableCell: called with m_isTableCellOpened=true\n"));
1286 closeTableCell();
1287 }
1288 librevenge::RVNGPropertyList propList;
1289 propList.insert("librevenge:column", pos[0]);
1290 propList.insert("librevenge:row", pos[1]);
1291 propList.insert("table:number-columns-spanned", span[0]);
1292 propList.insert("table:number-rows-spanned", span[1]);
1293 m_documentInterface->openTableCell(propList);
1294 m_documentInterface->closeTableCell();
1295 }
1296
openTableCell(MWAWCell const & cell)1297 void MWAWPresentationListener::openTableCell(MWAWCell const &cell)
1298 {
1299 if (!m_ps->m_isTableRowOpened) {
1300 MWAW_DEBUG_MSG(("MWAWPresentationListener::openTableCell: called with m_isTableRowOpened=false\n"));
1301 return;
1302 }
1303 if (m_ps->m_isTableCellOpened) {
1304 MWAW_DEBUG_MSG(("MWAWPresentationListener::openTableCell: called with m_isTableCellOpened=true\n"));
1305 closeTableCell();
1306 }
1307
1308 librevenge::RVNGPropertyList propList;
1309 cell.addTo(propList, m_parserState.m_fontConverter);
1310 m_ps->m_isTableCellOpened = true;
1311 m_documentInterface->openTableCell(propList);
1312 }
1313
closeTableCell()1314 void MWAWPresentationListener::closeTableCell()
1315 {
1316 if (!m_ps->m_isTableCellOpened) {
1317 MWAW_DEBUG_MSG(("MWAWPresentationListener::closeTableCell: called with m_isTableCellOpened=false\n"));
1318 return;
1319 }
1320
1321 _closeParagraph();
1322 m_ps->m_paragraph.m_listLevelIndex=0;
1323 _changeList(); // flush the list exterior
1324
1325 m_documentInterface->closeTableCell();
1326 m_ps->m_isTableCellOpened = false;
1327 }
1328
1329 ///////////////////
1330 // frame/group
1331 ///////////////////
openFrame(MWAWPosition const & pos,MWAWGraphicStyle const & style)1332 bool MWAWPresentationListener::openFrame(MWAWPosition const &pos, MWAWGraphicStyle const &style)
1333 {
1334 if (!m_ds->m_isDocumentStarted) {
1335 MWAW_DEBUG_MSG(("MWAWPresentationListener::openFrame: the document is not started\n"));
1336 return false;
1337 }
1338 if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened) {
1339 MWAW_DEBUG_MSG(("MWAWPresentationListener::openFrame: called in table but cell is not opened\n"));
1340 return false;
1341 }
1342 if (m_ps->m_isFrameOpened) {
1343 MWAW_DEBUG_MSG(("MWAWPresentationListener::openFrame: called but a frame is already opened\n"));
1344 return false;
1345 }
1346 if (!m_ds->m_isPageSpanOpened)
1347 _openPageSpan();
1348 m_ps->m_isFrameOpened = true;
1349 m_ps->m_framePosition=pos;
1350 m_ps->m_frameStyle=style;
1351 return true;
1352 }
1353
closeFrame()1354 void MWAWPresentationListener::closeFrame()
1355 {
1356 if (!m_ps->m_isFrameOpened) {
1357 MWAW_DEBUG_MSG(("MWAWPresentationListener::closeFrame: called but no frame is already opened\n"));
1358 return;
1359 }
1360 m_ps->m_isFrameOpened = false;
1361 }
1362
openGroup(MWAWPosition const & pos)1363 bool MWAWPresentationListener::openGroup(MWAWPosition const &pos)
1364 {
1365 if (!m_ds->m_isDocumentStarted) {
1366 MWAW_DEBUG_MSG(("MWAWPresentationListener::openGroup: the document is not started\n"));
1367 return false;
1368 }
1369 if (m_ps->m_isTableOpened || m_ps->isInTextZone()) {
1370 MWAW_DEBUG_MSG(("MWAWPresentationListener::openGroup: called in table or in a text zone\n"));
1371 return false;
1372 }
1373 if (!m_ds->m_isPageSpanOpened)
1374 _openPageSpan();
1375
1376 librevenge::RVNGPropertyList propList;
1377 _handleFrameParameters(propList, pos, MWAWGraphicStyle::emptyStyle());
1378
1379 _pushParsingState();
1380 _startSubDocument();
1381 m_ps->m_isGroupOpened = true;
1382
1383 m_documentInterface->openGroup(propList);
1384
1385 return true;
1386 }
1387
closeGroup()1388 void MWAWPresentationListener::closeGroup()
1389 {
1390 if (!m_ps->m_isGroupOpened) {
1391 MWAW_DEBUG_MSG(("MWAWPresentationListener::closeGroup: called but no group is already opened\n"));
1392 return;
1393 }
1394 _endSubDocument();
1395 _popParsingState();
1396 m_documentInterface->closeGroup();
1397 }
1398
openLayer(librevenge::RVNGString const & layerName)1399 bool MWAWPresentationListener::openLayer(librevenge::RVNGString const &layerName)
1400 {
1401 if (!m_ds->m_isDocumentStarted) {
1402 MWAW_DEBUG_MSG(("MWAWPresentationListener::openLayer: the document is not started\n"));
1403 return false;
1404 }
1405 if (m_ps->m_isTableOpened || m_ps->isInTextZone()) {
1406 MWAW_DEBUG_MSG(("MWAWPresentationListener::openLayer: called in table or in a text zone\n"));
1407 return false;
1408 }
1409 if (m_ps->m_isLayerOpened) {
1410 MWAW_DEBUG_MSG(("MWAWPresentationListener::openLayer: called but layer is already opened\n"));
1411 return false;
1412 }
1413 if (!m_ds->m_isPageSpanOpened)
1414 _openPageSpan();
1415
1416 _pushParsingState();
1417 _startSubDocument();
1418 m_ps->m_isLayerOpened = true;
1419
1420 librevenge::RVNGPropertyList propList;
1421 propList.insert("draw:layer", layerName);
1422 m_documentInterface->startLayer(propList);
1423 return true;
1424 }
1425
closeLayer()1426 void MWAWPresentationListener::closeLayer()
1427 {
1428 if (!m_ps->m_isLayerOpened) {
1429 MWAW_DEBUG_MSG(("MWAWPresentationListener::closeLayer: called but no layer is already opened\n"));
1430 return;
1431 }
1432 m_documentInterface->endLayer();
1433 _endSubDocument();
1434 _popParsingState();
1435 }
1436
_handleFrameParameters(librevenge::RVNGPropertyList & list,MWAWPosition const & pos,MWAWGraphicStyle const & style)1437 void MWAWPresentationListener::_handleFrameParameters(librevenge::RVNGPropertyList &list, MWAWPosition const &pos, MWAWGraphicStyle const &style)
1438 {
1439 if (!m_ds->m_isDocumentStarted)
1440 return;
1441
1442 librevenge::RVNGUnit unit = pos.unit();
1443 float pointFactor = pos.getInvUnitScale(librevenge::RVNG_POINT);
1444 float inchFactor=pos.getInvUnitScale(librevenge::RVNG_INCH);
1445 // first compute the origin ( in given unit and in point)
1446 MWAWVec2f origin = pos.origin()-pointFactor*m_ps->m_origin;
1447 MWAWVec2f originPt = (1.f/pointFactor)*pos.origin()-m_ps->m_origin;
1448 MWAWVec2f size = pos.size();
1449 // checkme: do we still need to do that ?
1450 if (style.hasGradient(true)) {
1451 if (style.m_rotate<0 || style.m_rotate>0) {
1452 MWAW_DEBUG_MSG(("MWAWPresentationListener::_handleFrameParameters: rotation is not implemented\n"));
1453 }
1454 // ok, first send a background rectangle
1455 librevenge::RVNGPropertyList rectList;
1456 m_documentInterface->setStyle(rectList);
1457 rectList.clear();
1458 rectList.insert("svg:x",double(originPt[0]), librevenge::RVNG_POINT);
1459 rectList.insert("svg:y",double(originPt[1]), librevenge::RVNG_POINT);
1460 rectList.insert("svg:width",size.x()>0 ? double(size.x()) : double(-size.x()), unit);
1461 rectList.insert("svg:height",size.y()>0 ? double(size.y()) : double(-size.y()), unit);
1462 m_documentInterface->drawRectangle(rectList);
1463
1464 list.insert("draw:stroke", "none");
1465 list.insert("draw:fill", "none");
1466 }
1467 else
1468 style.addTo(list);
1469
1470 list.insert("svg:x", double(originPt[0]), librevenge::RVNG_POINT);
1471 list.insert("svg:y", double(originPt[1]), librevenge::RVNG_POINT);
1472 if (size.x()>0)
1473 list.insert("svg:width", double(size.x()), unit);
1474 else if (size.x()<0)
1475 list.insert("fo:min-width", double(-size.x()), unit);
1476 if (size.y()>0)
1477 list.insert("svg:height", double(size.y()), unit);
1478 else if (size.y()<0)
1479 list.insert("fo:min-height", double(-size.y()), unit);
1480 if (pos.order() > 0)
1481 list.insert("draw:z-index", pos.order());
1482 if (pos.naturalSize().x() > 4*pointFactor && pos.naturalSize().y() > 4*pointFactor) {
1483 list.insert("librevenge:naturalWidth", double(pos.naturalSize().x()), pos.unit());
1484 list.insert("librevenge:naturalHeight", double(pos.naturalSize().y()), pos.unit());
1485 }
1486 MWAWVec2f TLClip = (1.f/pointFactor)*pos.leftTopClipping();
1487 MWAWVec2f RBClip = (1.f/pointFactor)*pos.rightBottomClipping();
1488 if (TLClip[0] > 0 || TLClip[1] > 0 || RBClip[0] > 0 || RBClip[1] > 0) {
1489 // in ODF1.2 we need to separate the value with ,
1490 std::stringstream s;
1491 s << "rect(" << TLClip[1] << "pt " << RBClip[0] << "pt "
1492 << RBClip[1] << "pt " << TLClip[0] << "pt)";
1493 list.insert("fo:clip", s.str().c_str());
1494 }
1495
1496 if (pos.m_wrapping == MWAWPosition::WDynamic)
1497 list.insert("style:wrap", "dynamic");
1498 else if (pos.m_wrapping == MWAWPosition::WBackground) {
1499 list.insert("style:wrap", "run-through");
1500 list.insert("style:run-through", "background");
1501 }
1502 else if (pos.m_wrapping == MWAWPosition::WForeground) {
1503 list.insert("style:wrap", "run-through");
1504 list.insert("style:run-through", "foreground");
1505 }
1506 else if (pos.m_wrapping == MWAWPosition::WParallel) {
1507 list.insert("style:wrap", "parallel");
1508 list.insert("style:run-through", "foreground");
1509 }
1510 else if (pos.m_wrapping == MWAWPosition::WRunThrough)
1511 list.insert("style:wrap", "run-through");
1512 else
1513 list.insert("style:wrap", "none");
1514 if (pos.m_anchorTo != MWAWPosition::Page) {
1515 MWAW_DEBUG_MSG(("MWAWPresentationListener::_handleFrameParameters: only page anchor is implemented\n"));
1516 }
1517 else {
1518 double w = m_ds->m_pageSpan.getFormWidth();
1519 double h = m_ds->m_pageSpan.getFormLength();
1520 w *= double(inchFactor);
1521 h *= double(inchFactor);
1522 double newPosition;
1523 switch (pos.m_yPos) {
1524 case MWAWPosition::YFull:
1525 list.insert("svg:height", double(h), unit);
1526 MWAW_FALLTHROUGH;
1527 case MWAWPosition::YTop:
1528 if (origin[1] < 0 || origin[1] > 0) {
1529 list.insert("style:vertical-pos", "from-top");
1530 newPosition = double(origin[1]);
1531 if (newPosition > h -double(pos.size()[1]))
1532 newPosition = h - double(pos.size()[1]);
1533 list.insert("svg:y", double(newPosition), unit);
1534 }
1535 else
1536 list.insert("style:vertical-pos", "top");
1537 break;
1538 case MWAWPosition::YCenter:
1539 if (origin[1] < 0 || origin[1] > 0) {
1540 list.insert("style:vertical-pos", "from-top");
1541 newPosition = (h - double(pos.size()[1]))/2.0;
1542 if (newPosition > h -double(pos.size()[1])) newPosition = h - double(pos.size()[1]);
1543 list.insert("svg:y", double(newPosition), unit);
1544 }
1545 else
1546 list.insert("style:vertical-pos", "middle");
1547 break;
1548 case MWAWPosition::YBottom:
1549 if (origin[1] < 0 || origin[1] > 0) {
1550 list.insert("style:vertical-pos", "from-top");
1551 newPosition = h - double(pos.size()[1])-double(origin[1]);
1552 if (newPosition > h -double(pos.size()[1])) newPosition = h -double(pos.size()[1]);
1553 else if (newPosition < 0) newPosition = 0;
1554 list.insert("svg:y", double(newPosition), unit);
1555 }
1556 else
1557 list.insert("style:vertical-pos", "bottom");
1558 break;
1559 #if !defined(__clang__)
1560 default:
1561 break;
1562 #endif
1563 }
1564
1565 switch (pos.m_xPos) {
1566 case MWAWPosition::XFull:
1567 list.insert("svg:width", double(w), unit);
1568 MWAW_FALLTHROUGH;
1569 case MWAWPosition::XLeft:
1570 if (origin[0] < 0 || origin[0] > 0) {
1571 list.insert("style:horizontal-pos", "from-left");
1572 list.insert("svg:x", double(origin[0]), unit);
1573 }
1574 else
1575 list.insert("style:horizontal-pos", "left");
1576 break;
1577 case MWAWPosition::XRight:
1578 if (origin[0] < 0 || origin[0] > 0) {
1579 list.insert("style:horizontal-pos", "from-left");
1580 list.insert("svg:x",w - double(pos.size()[0]) + double(origin[0]), unit);
1581 }
1582 else
1583 list.insert("style:horizontal-pos", "right");
1584 break;
1585 case MWAWPosition::XCenter:
1586 if (origin[0] < 0 || origin[0] > 0) {
1587 list.insert("style:horizontal-pos", "from-left");
1588 list.insert("svg:x", (w - double(pos.size()[0]))/2. + double(origin[0]), unit);
1589 }
1590 else
1591 list.insert("style:horizontal-pos", "center");
1592 break;
1593 #if !defined(__clang__)
1594 default:
1595 break;
1596 #endif
1597 }
1598
1599 }
1600 float const padding = 0; // fillme
1601 list.insert("fo:padding-top", double(padding), librevenge::RVNG_POINT);
1602 list.insert("fo:padding-bottom", double(padding), librevenge::RVNG_POINT);
1603 list.insert("fo:padding-left", double(padding), librevenge::RVNG_POINT);
1604 list.insert("fo:padding-right", double(padding), librevenge::RVNG_POINT);
1605 }
1606
1607 ///////////////////
1608 // subdocument
1609 ///////////////////
handleSubDocument(MWAWVec2f const & orig,MWAWSubDocumentPtr const & subDocument,libmwaw::SubDocumentType subDocumentType)1610 void MWAWPresentationListener::handleSubDocument(MWAWVec2f const &orig, MWAWSubDocumentPtr const &subDocument, libmwaw::SubDocumentType subDocumentType)
1611 {
1612 if (!m_ds->m_isDocumentStarted) {
1613 MWAW_DEBUG_MSG(("MWAWPresentationListener::handleSubDocument: the graphic is not started\n"));
1614 return;
1615 }
1616 if (!m_ds->m_isPageSpanOpened)
1617 _openPageSpan();
1618 MWAWVec2f actOrigin=m_ps->m_origin;
1619 _pushParsingState();
1620 m_ps->m_origin=actOrigin-orig;
1621 _startSubDocument();
1622 m_ps->m_subDocumentType = subDocumentType;
1623
1624 m_ps->m_list.reset();
1625 if (subDocumentType==libmwaw::DOC_TEXT_BOX)
1626 m_ps->m_isTextBoxOpened=true;
1627 else if (subDocumentType==libmwaw::DOC_HEADER_FOOTER) {
1628 m_ps->m_isTextBoxOpened=true;
1629 m_ds->m_isHeaderFooterStarted = true;
1630 }
1631 else if (subDocumentType==libmwaw::DOC_COMMENT_ANNOTATION || subDocumentType==libmwaw::DOC_NOTE)
1632 m_ps->m_inNote=true;
1633 // Check whether the document is calling itself
1634 bool sendDoc = true;
1635 for (auto doc : m_ds->m_subDocuments) {
1636 if (!subDocument)
1637 break;
1638 if (!doc) continue;
1639 if (*subDocument == *doc) {
1640 MWAW_DEBUG_MSG(("MWAWPresentationListener::handleSubDocument: recursif call, stop...\n"));
1641 sendDoc = false;
1642 break;
1643 }
1644 }
1645 if (sendDoc) {
1646 if (subDocument) {
1647 m_ds->m_subDocuments.push_back(subDocument);
1648 std::shared_ptr<MWAWListener> listen(this, MWAW_shared_ptr_noop_deleter<MWAWListener>());
1649 try {
1650 subDocument->parse(listen, subDocumentType);
1651 }
1652 catch (...) {
1653 MWAW_DEBUG_MSG(("MWAWPresentationListener::handleSubDocument exception catched\n"));
1654 }
1655 m_ds->m_subDocuments.pop_back();
1656 }
1657 }
1658
1659 _endSubDocument();
1660 _popParsingState();
1661
1662 if (subDocumentType==libmwaw::DOC_HEADER_FOOTER)
1663 m_ds->m_isHeaderFooterStarted = false;
1664 }
1665
isSubDocumentOpened(libmwaw::SubDocumentType & subdocType) const1666 bool MWAWPresentationListener::isSubDocumentOpened(libmwaw::SubDocumentType &subdocType) const
1667 {
1668 if (!m_ds->m_isDocumentStarted || !m_ps->m_inSubDocument)
1669 return false;
1670 subdocType = m_ps->m_subDocumentType;
1671 return true;
1672 }
1673
_startSubDocument()1674 void MWAWPresentationListener::_startSubDocument()
1675 {
1676 if (!m_ds->m_isDocumentStarted) return;
1677 m_ps->m_inSubDocument = true;
1678 }
1679
_endSubDocument()1680 void MWAWPresentationListener::_endSubDocument()
1681 {
1682 if (!m_ds->m_isDocumentStarted) return;
1683 if (m_ps->m_isTableOpened)
1684 closeTable();
1685 if (m_ps->m_isParagraphOpened)
1686 _closeParagraph();
1687 if (m_ps->isInTextZone()) {
1688 m_ps->m_paragraph.m_listLevelIndex=0;
1689 _changeList(); // flush the list exterior
1690 }
1691 }
1692
1693 ///////////////////
1694 // others
1695 ///////////////////
1696
1697 // ---------- state stack ------------------
_pushParsingState()1698 std::shared_ptr<MWAWPresentationListenerInternal::State> MWAWPresentationListener::_pushParsingState()
1699 {
1700 auto actual = m_ps;
1701 m_psStack.push_back(actual);
1702 m_ps.reset(new MWAWPresentationListenerInternal::State);
1703 m_ps->m_origin=actual->m_origin;
1704 return actual;
1705 }
1706
_popParsingState()1707 void MWAWPresentationListener::_popParsingState()
1708 {
1709 if (m_psStack.size()==0) {
1710 MWAW_DEBUG_MSG(("MWAWPresentationListener::_popParsingState: psStack is empty()\n"));
1711 throw libmwaw::ParseException();
1712 }
1713 m_ps = m_psStack.back();
1714 m_psStack.pop_back();
1715 }
1716 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
1717