/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */ /* libmwaw * Version: MPL 2.0 / LGPLv2+ * * The contents of this file are subject to the Mozilla Public License Version * 2.0 (the "License"); you may not use this file except in compliance with * the License or as specified alternatively below. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * Major Contributor(s): * Copyright (C) 2002 William Lachance (wrlach@gmail.com) * Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net) * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch) * Copyright (C) 2006, 2007 Andrew Ziem * Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr) * * * All Rights Reserved. * * For minor contributions see the git repository. * * Alternatively, the contents of this file may be used under the terms of * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), * in which case the provisions of the LGPLv2+ are applicable * instead of those above. */ /** \file MWAWTextListener.cxx * Implements MWAWTextListener: the libmwaw word processor listener * * \note this class is the only class which does the interface with * the librevenge::RVNGTextInterface */ #include #include #include #include #include #include "libmwaw_internal.hxx" #include "MWAWCell.hxx" #include "MWAWFont.hxx" #include "MWAWFontConverter.hxx" #include "MWAWGraphicEncoder.hxx" #include "MWAWGraphicListener.hxx" #include "MWAWGraphicStyle.hxx" #include "MWAWGraphicShape.hxx" #include "MWAWInputStream.hxx" #include "MWAWList.hxx" #include "MWAWPageSpan.hxx" #include "MWAWParagraph.hxx" #include "MWAWParser.hxx" #include "MWAWPosition.hxx" #include "MWAWSection.hxx" #include "MWAWSubDocument.hxx" #include "MWAWTable.hxx" #include "MWAWTextListener.hxx" //! Internal and low level namespace to define the states of MWAWTextListener namespace MWAWTextListenerInternal { //! a enum to define basic break bit enum { PageBreakBit=0x1, ColumnBreakBit=0x2 }; //! a class to store the document state of a MWAWTextListener struct DocumentState { //! constructor explicit DocumentState(std::vector const &pageList) : m_pageList(pageList) , m_pageSpan() , m_metaData() , m_footNoteNumber(0) , m_endNoteNumber(0) , m_smallPictureNumber(0) , m_isDocumentStarted(false) , m_isHeaderFooterStarted(false) , m_sentListMarkers() , m_subDocuments() { } //! destructor ~DocumentState() { } //! the pages definition std::vector m_pageList; //! the current page span MWAWPageSpan m_pageSpan; //! the document meta data librevenge::RVNGPropertyList m_metaData; int m_footNoteNumber /** footnote number*/, m_endNoteNumber /** endnote number*/; int m_smallPictureNumber /** number of small picture */; bool m_isDocumentStarted /** a flag to know if the document is open */, m_isHeaderFooterStarted /** a flag to know if the header footer is started */; /// the list of marker corresponding to sent list std::vector m_sentListMarkers; std::vector m_subDocuments; /** list of document actually open */ private: DocumentState(const DocumentState &) = delete; DocumentState &operator=(const DocumentState &) = delete; }; /** the state of a MWAWTextListener */ struct State { //! constructor State(); //! destructor ~State() { } //! a buffer to stored the text librevenge::RVNGString m_textBuffer; //! the number of tabs to add int m_numDeferredTabs; //! the font MWAWFont m_font; //! the paragraph MWAWParagraph m_paragraph; //! a sequence of bit used to know if we need page/column break int m_paragraphNeedBreak; std::shared_ptr m_list; bool m_isPageSpanOpened; bool m_isSectionOpened; bool m_isFrameOpened; bool m_isPageSpanBreakDeferred; bool m_isHeaderFooterWithoutParagraph; //! a flag to know if openGroup was called bool m_isGroupOpened; bool m_isSpanOpened; bool m_isParagraphOpened; bool m_isListElementOpened; bool m_firstParagraphInPageSpan; bool m_isTableOpened; bool m_isTableRowOpened; bool m_isTableColumnOpened; bool m_isTableCellOpened; unsigned m_currentPage; int m_numPagesRemainingInSpan; int m_currentPageNumber; bool m_sectionAttributesChanged; //! the section MWAWSection m_section; std::vector m_listOrderedLevels; //! a stack used to know what is open bool m_inSubDocument; bool m_isNote; bool m_inLink; libmwaw::SubDocumentType m_subDocumentType; private: State(const State &) = delete; State &operator=(const State &) = delete; }; State::State() : m_textBuffer("") , m_numDeferredTabs(0) , m_font(20,12) // default time 12 , m_paragraph() , m_paragraphNeedBreak(0) , m_list() , m_isPageSpanOpened(false) , m_isSectionOpened(false) , m_isFrameOpened(false) , m_isPageSpanBreakDeferred(false) , m_isHeaderFooterWithoutParagraph(false) , m_isGroupOpened(false) , m_isSpanOpened(false) , m_isParagraphOpened(false) , m_isListElementOpened(false) , m_firstParagraphInPageSpan(true) , m_isTableOpened(false) , m_isTableRowOpened(false) , m_isTableColumnOpened(false) , m_isTableCellOpened(false) , m_currentPage(0) , m_numPagesRemainingInSpan(0) , m_currentPageNumber(1) , m_sectionAttributesChanged(false) , m_section() , m_listOrderedLevels() , m_inSubDocument(false) , m_isNote(false) , m_inLink(false) , m_subDocumentType(libmwaw::DOC_NONE) { } } MWAWTextListener::MWAWTextListener(MWAWParserState &parserState, std::vector const &pageList, librevenge::RVNGTextInterface *documentInterface) : MWAWListener() , m_ds(new MWAWTextListenerInternal::DocumentState(pageList)) , m_ps(new MWAWTextListenerInternal::State), m_psStack() , m_parserState(parserState) , m_documentInterface(documentInterface) { } MWAWTextListener::~MWAWTextListener() { } /////////////////// // text data /////////////////// void MWAWTextListener::insertChar(uint8_t character) { if (character >= 0x80) { MWAWTextListener::insertUnicode(character); return; } _flushDeferredTabs(); if (!m_ps->m_isSpanOpened) _openSpan(); m_ps->m_textBuffer.append(char(character)); } void MWAWTextListener::insertCharacter(unsigned char c) { int unicode = m_parserState.m_fontConverter->unicode(m_ps->m_font.id(), c); if (unicode == -1) { if (c < 0x20) { MWAW_DEBUG_MSG(("MWAWTextListener::insertCharacter: Find odd char %x\n", static_cast(c))); } else MWAWTextListener::insertChar(static_cast(c)); } else MWAWTextListener::insertUnicode(static_cast(unicode)); } int MWAWTextListener::insertCharacter(unsigned char c, MWAWInputStreamPtr &input, long endPos) { if (!input || !m_parserState.m_fontConverter) { MWAW_DEBUG_MSG(("MWAWTextListener::insertCharacter: input or font converter does not exist!!!!\n")); return 0; } long debPos=input->tell(); int fId = m_ps->m_font.id(); int unicode = endPos==debPos ? m_parserState.m_fontConverter->unicode(fId, c) : m_parserState.m_fontConverter->unicode(fId, c, input); long pos=input->tell(); if (endPos > 0 && pos > endPos) { MWAW_DEBUG_MSG(("MWAWTextListener::insertCharacter: problem reading a character\n")); pos = debPos; input->seek(pos, librevenge::RVNG_SEEK_SET); unicode = m_parserState.m_fontConverter->unicode(fId, c); } if (unicode == -1) { if (c < 0x20) { MWAW_DEBUG_MSG(("MWAWTextListener::insertCharacter: Find odd char %x\n", static_cast(c))); } else MWAWTextListener::insertChar(static_cast(c)); } else MWAWTextListener::insertUnicode(static_cast(unicode)); return int(pos-debPos); } void MWAWTextListener::insertUnicode(uint32_t val) { // undef character, we skip it if (val == 0xfffd) return; _flushDeferredTabs(); if (!m_ps->m_isSpanOpened) _openSpan(); libmwaw::appendUnicode(val, m_ps->m_textBuffer); } void MWAWTextListener::insertUnicodeString(librevenge::RVNGString const &str) { _flushDeferredTabs(); if (!m_ps->m_isSpanOpened) _openSpan(); m_ps->m_textBuffer.append(str); } void MWAWTextListener::insertEOL(bool soft) { if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened) _openSpan(); _flushDeferredTabs(); if (soft) { if (m_ps->m_isSpanOpened) _flushText(); m_documentInterface->insertLineBreak(); } else if (m_ps->m_isParagraphOpened) _closeParagraph(); // sub/superscript must not survive a new line m_ps->m_font.set(MWAWFont::Script()); } void MWAWTextListener::insertTab() { if (!m_ps->m_isParagraphOpened) { m_ps->m_numDeferredTabs++; return; } if (m_ps->m_isSpanOpened) _flushText(); m_ps->m_numDeferredTabs++; _flushDeferredTabs(); } void MWAWTextListener::insertBreak(MWAWTextListener::BreakType breakType) { switch (breakType) { case ColumnBreak: if (!m_ps->m_isPageSpanOpened && !m_ps->m_inSubDocument) _openSpan(); if (m_ps->m_isParagraphOpened) _closeParagraph(); m_ps->m_paragraphNeedBreak |= MWAWTextListenerInternal::ColumnBreakBit; break; case PageBreak: if (!m_ps->m_isPageSpanOpened && !m_ps->m_inSubDocument) _openSpan(); if (m_ps->m_isParagraphOpened) _closeParagraph(); m_ps->m_paragraphNeedBreak |= MWAWTextListenerInternal::PageBreakBit; break; case SoftPageBreak: #if !defined(__clang__) default: #endif break; } if (m_ps->m_inSubDocument) return; switch (breakType) { case PageBreak: case SoftPageBreak: if (m_ps->m_numPagesRemainingInSpan > 0) m_ps->m_numPagesRemainingInSpan--; else { if (!m_ps->m_isTableOpened && !m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened) _closePageSpan(); else m_ps->m_isPageSpanBreakDeferred = true; } m_ps->m_currentPageNumber++; break; case ColumnBreak: #if !defined(__clang__) default: #endif break; } } void MWAWTextListener::_insertBreakIfNecessary(librevenge::RVNGPropertyList &propList) { if (!m_ps->m_paragraphNeedBreak) return; if ((m_ps->m_paragraphNeedBreak&MWAWTextListenerInternal::PageBreakBit) || m_ps->m_section.numColumns() <= 1) { if (m_ps->m_inSubDocument) { MWAW_DEBUG_MSG(("MWAWTextListener::_insertBreakIfNecessary: can not add page break in subdocument\n")); } else propList.insert("fo:break-before", "page"); } else if (m_ps->m_paragraphNeedBreak&MWAWTextListenerInternal::ColumnBreakBit) propList.insert("fo:break-before", "column"); m_ps->m_paragraphNeedBreak=0; } /////////////////// // font/paragraph function /////////////////// void MWAWTextListener::setFont(MWAWFont const &font) { if (font == m_ps->m_font) return; // check if id and size are defined, if not used the previous fields MWAWFont finalFont(font); if (font.id() == -1) finalFont.setId(m_ps->m_font.id()); if (font.size() <= 0) finalFont.setSize(m_ps->m_font.size()); if (finalFont == m_ps->m_font) return; _closeSpan(); m_ps->m_font = finalFont; } MWAWFont const &MWAWTextListener::getFont() const { return m_ps->m_font; } bool MWAWTextListener::isParagraphOpened() const { return m_ps->m_isParagraphOpened; } void MWAWTextListener::setParagraph(MWAWParagraph const ¶) { if (para==m_ps->m_paragraph) return; m_ps->m_paragraph=para; } MWAWParagraph const &MWAWTextListener::getParagraph() const { return m_ps->m_paragraph; } /////////////////// // field/link : /////////////////// void MWAWTextListener::insertField(MWAWField const &field) { librevenge::RVNGPropertyList propList; if (field.addTo(propList)) { _flushDeferredTabs(); _flushText(); _openSpan(); m_documentInterface->insertField(propList); return; } librevenge::RVNGString text=field.getString(); if (!text.empty()) MWAWTextListener::insertUnicodeString(text); else { MWAW_DEBUG_MSG(("MWAWTextListener::insertField: must not be called with type=%d\n", int(field.m_type))); } } void MWAWTextListener::openLink(MWAWLink const &link) { if (m_ps->m_inLink) { MWAW_DEBUG_MSG(("MWAWTextListener:openLink: a link is already opened\n")); return; } if (!m_ps->m_isSpanOpened) _openSpan(); librevenge::RVNGPropertyList propList; link.addTo(propList); m_documentInterface->openLink(propList); _pushParsingState(); m_ps->m_inLink=true; // we do not want any close open paragraph in a link m_ps->m_isParagraphOpened=true; } void MWAWTextListener::closeLink() { if (!m_ps->m_inLink) { MWAW_DEBUG_MSG(("MWAWTextListener:closeLink: can not close a link\n")); return; } if (m_ps->m_isSpanOpened) _closeSpan(); m_documentInterface->closeLink(); _popParsingState(); } /////////////////// // document /////////////////// void MWAWTextListener::setDocumentMetaData(librevenge::RVNGPropertyList const &meta) { librevenge::RVNGPropertyList::Iter i(meta); for (i.rewind(); i.next();) m_ds->m_metaData.insert(i.key(), i()->getStr()); } void MWAWTextListener::setDocumentLanguage(std::string const &locale) { if (!locale.length()) return; m_ds->m_metaData.insert("librevenge:language", locale.c_str()); } bool MWAWTextListener::isDocumentStarted() const { return m_ds->m_isDocumentStarted; } void MWAWTextListener::startDocument() { if (m_ds->m_isDocumentStarted) { MWAW_DEBUG_MSG(("MWAWTextListener::startDocument: the document is already started\n")); return; } m_documentInterface->startDocument(librevenge::RVNGPropertyList()); m_ds->m_isDocumentStarted = true; m_documentInterface->setDocumentMetaData(m_ds->m_metaData); } void MWAWTextListener::endDocument(bool sendDelayedSubDoc) { if (!m_ds->m_isDocumentStarted) { MWAW_DEBUG_MSG(("MWAWTextListener::endDocument: the document is not started\n")); return; } if (!m_ps->m_isPageSpanOpened) { // we must call by hand openPageSpan to avoid sending any header/footer documents if (!sendDelayedSubDoc) _openPageSpan(false); _openSpan(); } if (m_ps->m_isTableOpened) closeTable(); if (m_ps->m_isParagraphOpened) _closeParagraph(); m_ps->m_paragraph.m_listLevelIndex = 0; _changeList(); // flush the list exterior // close the document nice and tight _closeSection(); _closePageSpan(); m_documentInterface->endDocument(); m_ds->m_isDocumentStarted = false; } /////////////////// // page /////////////////// bool MWAWTextListener::isPageSpanOpened() const { return m_ps->m_isPageSpanOpened; } MWAWPageSpan const &MWAWTextListener::getPageSpan() { if (!m_ps->m_isPageSpanOpened) _openPageSpan(); return m_ds->m_pageSpan; } void MWAWTextListener::_openPageSpan(bool sendHeaderFooters) { if (m_ps->m_isPageSpanOpened) return; if (!m_ds->m_isDocumentStarted) startDocument(); if (m_ds->m_pageList.size()==0) { MWAW_DEBUG_MSG(("MWAWTextListener::_openPageSpan: can not find any page\n")); throw libmwaw::ParseException(); } unsigned actPage = 0; auto it = m_ds->m_pageList.begin(); ++m_ps->m_currentPage; while (true) { actPage+=static_cast(it->getPageSpan()); if (actPage>=m_ps->m_currentPage) break; if (++it == m_ds->m_pageList.end()) { MWAW_DEBUG_MSG(("MWAWTextListener::_openPageSpan: can not find current page, use last page\n")); --it; break; } } MWAWPageSpan ¤tPage = *it; librevenge::RVNGPropertyList propList; currentPage.getPageProperty(propList); propList.insert("librevenge:is-last-page-span", ++it == m_ds->m_pageList.end()); if (!m_ps->m_isPageSpanOpened) m_documentInterface->openPageSpan(propList); m_ps->m_isPageSpanOpened = true; m_ds->m_pageSpan = currentPage; // we insert the header footer if (sendHeaderFooters) currentPage.sendHeaderFooters(this); // first paragraph in span (necessary for resetting page number) m_ps->m_firstParagraphInPageSpan = true; m_ps->m_numPagesRemainingInSpan = (currentPage.getPageSpan() - 1); } void MWAWTextListener::_closePageSpan() { if (!m_ps->m_isPageSpanOpened) return; if (m_ps->m_isSectionOpened) _closeSection(); m_documentInterface->closePageSpan(); m_ps->m_isPageSpanOpened = m_ps->m_isPageSpanBreakDeferred = false; } /////////////////// // header/footer /////////////////// bool MWAWTextListener::isHeaderFooterOpened() const { return m_ds->m_isHeaderFooterStarted; } bool MWAWTextListener::insertHeader(MWAWSubDocumentPtr const &subDocument, librevenge::RVNGPropertyList const &extras) { if (m_ds->m_isHeaderFooterStarted) { MWAW_DEBUG_MSG(("MWAWTextListener::insertHeader: Oops a header/footer is already opened\n")); return false; } librevenge::RVNGPropertyList propList(extras); m_documentInterface->openHeader(propList); handleSubDocument(subDocument, libmwaw::DOC_HEADER_FOOTER); m_documentInterface->closeHeader(); return true; } bool MWAWTextListener::insertFooter(MWAWSubDocumentPtr const &subDocument, librevenge::RVNGPropertyList const &extras) { if (m_ds->m_isHeaderFooterStarted) { MWAW_DEBUG_MSG(("MWAWTextListener::insertFooter: Oops a header/footer is already opened\n")); return false; } librevenge::RVNGPropertyList propList(extras); m_documentInterface->openFooter(propList); handleSubDocument(subDocument, libmwaw::DOC_HEADER_FOOTER); m_documentInterface->closeFooter(); return true; } /////////////////// // section /////////////////// bool MWAWTextListener::isSectionOpened() const { return m_ps->m_isSectionOpened; } MWAWSection const &MWAWTextListener::getSection() const { return m_ps->m_section; } bool MWAWTextListener::canOpenSectionAddBreak() const { return !m_ps->m_isTableOpened && (!m_ps->m_inSubDocument || m_ps->m_subDocumentType == libmwaw::DOC_TEXT_BOX); } bool MWAWTextListener::openSection(MWAWSection const §ion) { if (m_ps->m_isSectionOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::openSection: a section is already opened\n")); return false; } if (m_ps->m_isTableOpened || (m_ps->m_inSubDocument && m_ps->m_subDocumentType != libmwaw::DOC_TEXT_BOX)) { MWAW_DEBUG_MSG(("MWAWTextListener::openSection: impossible to open a section\n")); return false; } m_ps->m_section=section; _openSection(); return true; } bool MWAWTextListener::closeSection() { if (!m_ps->m_isSectionOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::closeSection: no section are already opened\n")); return false; } if (m_ps->m_isTableOpened || (m_ps->m_inSubDocument && m_ps->m_subDocumentType != libmwaw::DOC_TEXT_BOX)) { MWAW_DEBUG_MSG(("MWAWTextListener::closeSection: impossible to close a section\n")); return false; } _closeSection(); return true; } void MWAWTextListener::_openSection() { if (m_ps->m_isSectionOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::_openSection: a section is already opened\n")); return; } if (!m_ps->m_isPageSpanOpened) _openPageSpan(); librevenge::RVNGPropertyList propList; m_ps->m_section.addTo(propList); librevenge::RVNGPropertyListVector columns; m_ps->m_section.addColumnsTo(columns); if (columns.count()) propList.insert("style:columns", columns); m_documentInterface->openSection(propList); m_ps->m_sectionAttributesChanged = false; m_ps->m_isSectionOpened = true; } void MWAWTextListener::_closeSection() { if (!m_ps->m_isSectionOpened ||m_ps->m_isTableOpened) return; if (m_ps->m_isParagraphOpened) _closeParagraph(); m_ps->m_paragraph.m_listLevelIndex=0; _changeList(); m_documentInterface->closeSection(); m_ps->m_section = MWAWSection(); m_ps->m_sectionAttributesChanged = false; m_ps->m_isSectionOpened = false; } /////////////////// // paragraph /////////////////// void MWAWTextListener::_openParagraph() { if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened) return; if (m_ps->m_isParagraphOpened || m_ps->m_isListElementOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::_openParagraph: a paragraph (or a list) is already opened")); return; } if (!m_ps->m_isTableOpened && (!m_ps->m_inSubDocument || m_ps->m_subDocumentType == libmwaw::DOC_TEXT_BOX)) { if (m_ps->m_sectionAttributesChanged) _closeSection(); if (!m_ps->m_isSectionOpened) _openSection(); } librevenge::RVNGPropertyList propList; _appendParagraphProperties(propList); if (!m_ps->m_isParagraphOpened) m_documentInterface->openParagraph(propList); _resetParagraphState(); m_ps->m_firstParagraphInPageSpan = false; } void MWAWTextListener::_closeParagraph() { // we can not close a paragraph in a link if (m_ps->m_inLink) return; if (m_ps->m_isListElementOpened) { _closeListElement(); return; } if (m_ps->m_isParagraphOpened) { if (m_ps->m_isSpanOpened) _closeSpan(); m_documentInterface->closeParagraph(); } m_ps->m_isParagraphOpened = false; m_ps->m_paragraph.m_listLevelIndex = 0; if (!m_ps->m_isTableOpened && m_ps->m_isPageSpanBreakDeferred && !m_ps->m_inSubDocument) _closePageSpan(); } void MWAWTextListener::_resetParagraphState(const bool isListElement) { m_ps->m_paragraphNeedBreak = 0; m_ps->m_isListElementOpened = isListElement; m_ps->m_isParagraphOpened = true; m_ps->m_isHeaderFooterWithoutParagraph = false; } void MWAWTextListener::_appendParagraphProperties(librevenge::RVNGPropertyList &propList, const bool /*isListElement*/) { m_ps->m_paragraph.addTo(propList,m_ps->m_isTableOpened); if (!m_ps->m_inSubDocument && m_ps->m_firstParagraphInPageSpan && m_ds->m_pageSpan.getPageNumber() >= 0) propList.insert("style:page-number", m_ds->m_pageSpan.getPageNumber()); _insertBreakIfNecessary(propList); } /////////////////// // list /////////////////// void MWAWTextListener::_openListElement() { if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened) return; if (m_ps->m_isParagraphOpened || m_ps->m_isListElementOpened) return; if (!m_ps->m_isTableOpened && (!m_ps->m_inSubDocument || m_ps->m_subDocumentType == libmwaw::DOC_TEXT_BOX)) { if (m_ps->m_sectionAttributesChanged) _closeSection(); if (!m_ps->m_isSectionOpened) _openSection(); } librevenge::RVNGPropertyList propList; _appendParagraphProperties(propList, true); // check if we must change the start value int startValue=m_ps->m_paragraph.m_listStartValue.get(); if (startValue > 0 && m_ps->m_list && m_ps->m_list->getStartValueForNextElement() != startValue) { propList.insert("text:start-value", startValue); m_ps->m_list->setStartValueForNextElement(startValue); } if (m_ps->m_list) m_ps->m_list->openElement(); m_documentInterface->openListElement(propList); _resetParagraphState(true); } void MWAWTextListener::_closeListElement() { if (m_ps->m_isListElementOpened) { if (m_ps->m_isSpanOpened) _closeSpan(); if (m_ps->m_list) m_ps->m_list->closeElement(); m_documentInterface->closeListElement(); } m_ps->m_isListElementOpened = m_ps->m_isParagraphOpened = false; if (!m_ps->m_isTableOpened && m_ps->m_isPageSpanBreakDeferred && !m_ps->m_inSubDocument) _closePageSpan(); } int MWAWTextListener::_getListId() const { auto newLevel= size_t(m_ps->m_paragraph.m_listLevelIndex.get()); if (newLevel == 0) return -1; int newListId = m_ps->m_paragraph.m_listId.get(); if (newListId > 0) return newListId; static bool first = true; if (first) { MWAW_DEBUG_MSG(("MWAWTextListener::_getListId: the list id is not set, try to find a new one\n")); first = false; } auto list=m_parserState.m_listManager->getNewList(m_ps->m_list, int(newLevel), *m_ps->m_paragraph.m_listLevel); if (!list) return -1; return list->getId(); } void MWAWTextListener::_changeList() { if (m_ps->m_isParagraphOpened) _closeParagraph(); size_t actualLevel = m_ps->m_listOrderedLevels.size(); auto newLevel= size_t(m_ps->m_paragraph.m_listLevelIndex.get() > 0 ? m_ps->m_paragraph.m_listLevelIndex.get() : 0); if (newLevel>100) { MWAW_DEBUG_MSG(("MWAWTextListener::_changeList: find level=%d, set it to 100\n", static_cast(newLevel))); newLevel=100; } if (!m_ps->m_isSectionOpened && newLevel && !m_ps->m_isTableOpened && (!m_ps->m_inSubDocument || m_ps->m_subDocumentType == libmwaw::DOC_TEXT_BOX)) _openSection(); int newListId = newLevel>0 ? _getListId() : -1; bool changeList = newLevel && (m_ps->m_list && m_ps->m_list->getId()!=newListId); size_t minLevel = changeList ? 0 : newLevel; while (actualLevel > minLevel) { if (m_ps->m_listOrderedLevels[--actualLevel]) m_documentInterface->closeOrderedListLevel(); else m_documentInterface->closeUnorderedListLevel(); } if (newLevel) { std::shared_ptr theList; theList=m_parserState.m_listManager->getList(newListId); if (!theList) { MWAW_DEBUG_MSG(("MWAWTextListener::_changeList: can not find any list\n")); m_ps->m_listOrderedLevels.resize(actualLevel); return; } m_parserState.m_listManager->needToSend(newListId, m_ds->m_sentListMarkers); m_ps->m_list = theList; m_ps->m_list->setLevel(static_cast(newLevel)); } m_ps->m_listOrderedLevels.resize(newLevel, false); if (actualLevel == newLevel) return; for (size_t i=actualLevel+1; i<= newLevel; i++) { bool ordered = m_ps->m_list->isNumeric(int(i)); m_ps->m_listOrderedLevels[i-1] = ordered; librevenge::RVNGPropertyList level; m_ps->m_list->addTo(int(i), level, m_parserState.m_fontManager); if (ordered) m_documentInterface->openOrderedListLevel(level); else m_documentInterface->openUnorderedListLevel(level); } } /////////////////// // span /////////////////// void MWAWTextListener::_openSpan() { if (m_ps->m_isSpanOpened) return; if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened) return; if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened) { _changeList(); if (*m_ps->m_paragraph.m_listLevelIndex == 0) _openParagraph(); else _openListElement(); } librevenge::RVNGPropertyList propList; m_ps->m_font.addTo(propList, m_parserState.m_fontConverter); m_documentInterface->openSpan(propList); m_ps->m_isSpanOpened = true; } void MWAWTextListener::_closeSpan() { // better not to close a link... if (!m_ps->m_isSpanOpened) return; _flushText(); m_documentInterface->closeSpan(); m_ps->m_isSpanOpened = false; } /////////////////// // text (send data) /////////////////// void MWAWTextListener::_flushDeferredTabs() { if (m_ps->m_numDeferredTabs == 0) return; if (!m_ps->m_font.hasDecorationLines()) { if (!m_ps->m_isSpanOpened) _openSpan(); for (; m_ps->m_numDeferredTabs > 0; m_ps->m_numDeferredTabs--) m_documentInterface->insertTab(); return; } MWAWFont oldFont(m_ps->m_font); m_ps->m_font.resetDecorationLines(); _closeSpan(); _openSpan(); for (; m_ps->m_numDeferredTabs > 0; m_ps->m_numDeferredTabs--) m_documentInterface->insertTab(); setFont(oldFont); } void MWAWTextListener::_flushText() { if (m_ps->m_textBuffer.len() == 0) return; // when some many ' ' follows each other, call insertSpace librevenge::RVNGString tmpText; int numConsecutiveSpaces = 0; librevenge::RVNGString::Iter i(m_ps->m_textBuffer); for (i.rewind(); i.next();) { if (*(i()) == 0x20) // this test is compatible with unicode format numConsecutiveSpaces++; else numConsecutiveSpaces = 0; if (numConsecutiveSpaces > 1) { if (tmpText.len() > 0) { m_documentInterface->insertText(tmpText); tmpText.clear(); } m_documentInterface->insertSpace(); } else tmpText.append(i()); } m_documentInterface->insertText(tmpText); m_ps->m_textBuffer.clear(); } /////////////////// // Note/Comment/picture/textbox /////////////////// void MWAWTextListener::insertNote(MWAWNote const ¬e, MWAWSubDocumentPtr &subDocument) { if (m_ps->m_isNote) { MWAW_DEBUG_MSG(("MWAWTextListener::insertNote try to insert a note recursively (ignored)\n")); return; } m_ps->m_isNote = true; if (m_ds->m_isHeaderFooterStarted) { MWAW_DEBUG_MSG(("MWAWTextListener::insertNote try to insert a note in a header/footer\n")); /** Must not happen excepted in corrupted document, so we do the minimum. Note that we have no choice, either we begin by closing the paragraph, ... or we reprogram handleSubDocument. */ if (m_ps->m_isParagraphOpened) _closeParagraph(); int prevListLevel = *m_ps->m_paragraph.m_listLevelIndex; m_ps->m_paragraph.m_listLevelIndex = 0; _changeList(); // flush the list exterior handleSubDocument(subDocument, libmwaw::DOC_NOTE); m_ps->m_paragraph.m_listLevelIndex = prevListLevel; } else { if (!m_ps->m_isParagraphOpened) _openParagraph(); else { _flushText(); _closeSpan(); } librevenge::RVNGPropertyList propList; if (note.m_label.len()) propList.insert("text:label", librevenge::RVNGPropertyFactory::newStringProp(note.m_label)); if (note.m_type == MWAWNote::FootNote) { if (note.m_number >= 0) m_ds->m_footNoteNumber = note.m_number; else m_ds->m_footNoteNumber++; propList.insert("librevenge:number", m_ds->m_footNoteNumber); m_documentInterface->openFootnote(propList); handleSubDocument(subDocument, libmwaw::DOC_NOTE); m_documentInterface->closeFootnote(); } else { if (note.m_number >= 0) m_ds->m_endNoteNumber = note.m_number; else m_ds->m_endNoteNumber++; propList.insert("librevenge:number", m_ds->m_endNoteNumber); m_documentInterface->openEndnote(propList); handleSubDocument(subDocument, libmwaw::DOC_NOTE); m_documentInterface->closeEndnote(); } } m_ps->m_isNote = false; } void MWAWTextListener::insertComment(MWAWSubDocumentPtr &subDocument) { if (m_ps->m_isNote) { MWAW_DEBUG_MSG(("MWAWTextListener::insertComment try to insert a note recursively (ignored)\n")); return; } if (!m_ps->m_isParagraphOpened) _openParagraph(); else { _flushText(); _closeSpan(); } librevenge::RVNGPropertyList propList; m_documentInterface->openComment(propList); m_ps->m_isNote = true; handleSubDocument(subDocument, libmwaw::DOC_COMMENT_ANNOTATION); m_documentInterface->closeComment(); m_ps->m_isNote = false; } void MWAWTextListener::insertTextBox (MWAWPosition const &pos, MWAWSubDocumentPtr const &subDocument, MWAWGraphicStyle const &frameStyle) { if (!openFrame(pos, frameStyle)) return; librevenge::RVNGPropertyList propList; if (!frameStyle.m_frameNextName.empty()) propList.insert("librevenge:next-frame-name",frameStyle.m_frameNextName.c_str()); m_documentInterface->openTextBox(propList); handleSubDocument(subDocument, libmwaw::DOC_TEXT_BOX); m_documentInterface->closeTextBox(); closeFrame(); } void MWAWTextListener::insertShape (MWAWPosition const &pos, MWAWGraphicShape const &shape, MWAWGraphicStyle const &style) { // sanity check: avoid to send to many small pict float factor=pos.getScaleFactor(pos.unit(), librevenge::RVNG_POINT); if (pos.size()[0]*factor <= 8 && pos.size()[1]*factor <= 8 && m_ds->m_smallPictureNumber++ > 200) { static bool first = true; if (first) { first = false; MWAW_DEBUG_MSG(("MWAWTextListener::insertShape: find too much small pictures, skip them from now\n")); } return; } // now check that the anchor is coherent with the actual state switch (pos.m_anchorTo) { case MWAWPosition::Page: break; case MWAWPosition::Paragraph: if (m_ps->m_isParagraphOpened) _flushText(); else _openParagraph(); break; case MWAWPosition::Unknown: #if !defined(__clang__) default: #endif MWAW_DEBUG_MSG(("MWAWTextListener::insertShape: UNKNOWN position, insert as char position\n")); MWAW_FALLTHROUGH; case MWAWPosition::CharBaseLine: case MWAWPosition::Char: if (m_ps->m_isSpanOpened) _flushText(); else _openSpan(); break; case MWAWPosition::Cell: case MWAWPosition::Frame: break; } librevenge::RVNGPropertyList shapePList; _handleFrameParameters(shapePList, pos); shapePList.remove("svg:x"); shapePList.remove("svg:y"); librevenge::RVNGPropertyList list; style.addTo(list, shape.getType()==MWAWGraphicShape::Line); MWAWVec2f decal = factor*pos.origin(); switch (shape.addTo(decal, style.hasSurface(), shapePList)) { case MWAWGraphicShape::C_Ellipse: m_documentInterface->defineGraphicStyle(list); m_documentInterface->drawEllipse(shapePList); break; case MWAWGraphicShape::C_Path: { // odt seems to have some problem displaying path so // first create the picture, reset origin (if it is bad) MWAWBox2f bdbox = shape.getBdBox(style,true); MWAWGraphicEncoder graphicEncoder; MWAWGraphicListener graphicListener(m_parserState, MWAWBox2f(MWAWVec2f(0,0),bdbox.size()), &graphicEncoder); graphicListener.startDocument(); MWAWPosition pathPos(-1.f*bdbox[0],bdbox.size(),librevenge::RVNG_POINT); pathPos.m_anchorTo=MWAWPosition::Page; graphicListener.insertShape(pathPos, shape, style); graphicListener.endDocument(); MWAWEmbeddedObject picture; if (!graphicEncoder.getBinaryResult(picture) || !openFrame(pos)) break; librevenge::RVNGPropertyList propList; if (picture.addTo(propList)) m_documentInterface->insertBinaryObject(propList); closeFrame(); break; } case MWAWGraphicShape::C_Polyline: m_documentInterface->defineGraphicStyle(list); m_documentInterface->drawPolyline(shapePList); break; case MWAWGraphicShape::C_Polygon: m_documentInterface->defineGraphicStyle(list); m_documentInterface->drawPolygon(shapePList); break; case MWAWGraphicShape::C_Rectangle: m_documentInterface->defineGraphicStyle(list); m_documentInterface->drawRectangle(shapePList); break; case MWAWGraphicShape::C_Bad: break; #if !defined(__clang__) default: MWAW_DEBUG_MSG(("MWAWTextListener::insertShape: unexpected shape\n")); break; #endif } } void MWAWTextListener::insertPicture(MWAWPosition const &pos, MWAWEmbeddedObject const &picture, MWAWGraphicStyle const &style) { // sanity check: avoid to send to many small pict float factor=pos.getScaleFactor(pos.unit(), librevenge::RVNG_POINT); if (pos.size()[0]*factor <= 8 && pos.size()[1]*factor <= 8 && m_ds->m_smallPictureNumber++ > 200) { static bool first = true; if (first) { first = false; MWAW_DEBUG_MSG(("MWAWTextListener::insertPicture: find too much small pictures, skip them from now\n")); } return; } if (!openFrame(pos, style)) return; librevenge::RVNGPropertyList propList; if (picture.addTo(propList)) m_documentInterface->insertBinaryObject(propList); closeFrame(); } /////////////////// // frame /////////////////// bool MWAWTextListener::openFrame(MWAWPosition const &pos, MWAWGraphicStyle const &style) { if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::openFrame: called in table but cell is not opened\n")); return false; } if (m_ps->m_isFrameOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::openFrame: called but a frame is already opened\n")); return false; } MWAWPosition fPos(pos); switch (pos.m_anchorTo) { case MWAWPosition::Page: break; case MWAWPosition::Paragraph: if (m_ps->m_isParagraphOpened) _flushText(); else _openParagraph(); break; case MWAWPosition::Unknown: MWAW_DEBUG_MSG(("MWAWTextListener::openFrame: UNKNOWN position, insert as char position\n")); MWAW_FALLTHROUGH; case MWAWPosition::CharBaseLine: case MWAWPosition::Char: if (m_ps->m_isSpanOpened) _flushText(); else _openSpan(); break; case MWAWPosition::Frame: if (!m_ds->m_subDocuments.size()) { MWAW_DEBUG_MSG(("MWAWTextListener::openFrame: can not determine the frame\n")); return false; } if (m_ps->m_subDocumentType==libmwaw::DOC_HEADER_FOOTER) { MWAW_DEBUG_MSG(("MWAWTextListener::openFrame: called with Frame position in header footer, switch to paragraph\n")); if (m_ps->m_isParagraphOpened) _flushText(); else _openParagraph(); fPos.m_anchorTo=MWAWPosition::Paragraph; } break; case MWAWPosition::Cell: if (!m_ps->m_isTableCellOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::openFrame: called with Cell position not in a table cell\n")); return false; } if (pos.m_anchorCellName.empty()) { MWAW_DEBUG_MSG(("MWAWTextListener::openFrame: can not find the cell name\n")); return false; } break; #if !defined(__clang__) default: MWAW_DEBUG_MSG(("MWAWTextListener::openFrame: can not determine the anchor\n")); return false; #endif } librevenge::RVNGPropertyList propList; style.addFrameTo(propList); _handleFrameParameters(propList, fPos); m_documentInterface->openFrame(propList); m_ps->m_isFrameOpened = true; return true; } void MWAWTextListener::closeFrame() { if (!m_ps->m_isFrameOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::closeFrame: called but no frame is already opened\n")); return; } m_documentInterface->closeFrame(); m_ps->m_isFrameOpened = false; } bool MWAWTextListener::openGroup(MWAWPosition const &pos) { if (!m_ds->m_isDocumentStarted) { MWAW_DEBUG_MSG(("MWAWTextListener::openGroup: the document is not started\n")); return false; } if (m_ps->m_isTableOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::openGroup: called in table or in a text zone\n")); return false; } // now check that the anchor is coherent with the actual state switch (pos.m_anchorTo) { case MWAWPosition::Page: break; case MWAWPosition::Paragraph: if (m_ps->m_isParagraphOpened) _flushText(); else _openParagraph(); break; case MWAWPosition::Unknown: #if !defined(__clang__) default: #endif MWAW_DEBUG_MSG(("MWAWTextListener::openGroup: UNKNOWN position, insert as char position\n")); MWAW_FALLTHROUGH; case MWAWPosition::CharBaseLine: case MWAWPosition::Char: if (m_ps->m_isSpanOpened) _flushText(); else _openSpan(); break; case MWAWPosition::Frame: case MWAWPosition::Cell: break; } librevenge::RVNGPropertyList propList; _handleFrameParameters(propList, pos); _pushParsingState(); _startSubDocument(); m_ps->m_isGroupOpened = true; m_documentInterface->openGroup(propList); return true; } void MWAWTextListener::closeGroup() { if (!m_ps->m_isGroupOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::closeGroup: called but no group is already opened\n")); return; } _endSubDocument(); _popParsingState(); m_documentInterface->closeGroup(); } void MWAWTextListener::_handleFrameParameters (librevenge::RVNGPropertyList &propList, MWAWPosition const &pos) { MWAWVec2f origin = pos.origin(); librevenge::RVNGUnit unit = pos.unit(); float inchFactor=pos.getInvUnitScale(librevenge::RVNG_INCH); float pointFactor = pos.getInvUnitScale(librevenge::RVNG_POINT); if (pos.size()[0]>0) propList.insert("svg:width", double(pos.size()[0]), unit); else if (pos.size()[0]<0) propList.insert("fo:min-width", double(-pos.size()[0]), unit); if (pos.size()[1]>0) propList.insert("svg:height", double(pos.size()[1]), unit); else if (pos.size()[1]<0) propList.insert("fo:min-height", double(-pos.size()[1]), unit); if (pos.order() > 0) propList.insert("draw:z-index", pos.order()); if (pos.naturalSize().x() > 4*pointFactor && pos.naturalSize().y() > 4*pointFactor) { propList.insert("librevenge:naturalWidth", double(pos.naturalSize().x()), pos.unit()); propList.insert("librevenge:naturalHeight", double(pos.naturalSize().y()), pos.unit()); } MWAWVec2f TLClip = (1.f/pointFactor)*pos.leftTopClipping(); MWAWVec2f RBClip = (1.f/pointFactor)*pos.rightBottomClipping(); if (TLClip[0] > 0 || TLClip[1] > 0 || RBClip[0] > 0 || RBClip[1] > 0) { // in ODF1.2 we need to separate the value with , std::stringstream s; s << "rect(" << TLClip[1] << "pt " << RBClip[0] << "pt " << RBClip[1] << "pt " << TLClip[0] << "pt)"; propList.insert("fo:clip", s.str().c_str()); } if (pos.m_wrapping == MWAWPosition::WDynamic) propList.insert("style:wrap", "dynamic"); else if (pos.m_wrapping == MWAWPosition::WBackground) { propList.insert("style:wrap", "run-through"); propList.insert("style:run-through", "background"); } else if (pos.m_wrapping == MWAWPosition::WForeground) { propList.insert("style:wrap", "run-through"); propList.insert("style:run-through", "foreground"); } else if (pos.m_wrapping == MWAWPosition::WParallel) { propList.insert("style:wrap", "parallel"); propList.insert("style:run-through", "foreground"); } else if (pos.m_wrapping == MWAWPosition::WRunThrough) propList.insert("style:wrap", "run-through"); else propList.insert("style:wrap", "none"); if (pos.m_anchorTo == MWAWPosition::Paragraph || pos.m_anchorTo == MWAWPosition::Frame) { std::string what= pos.m_anchorTo == MWAWPosition::Paragraph ? "paragraph" : "frame"; propList.insert("text:anchor-type", what.c_str()); propList.insert("style:vertical-rel", what.c_str()); propList.insert("style:horizontal-rel", what.c_str()); double w = m_ds->m_pageSpan.getPageWidth() - m_ps->m_paragraph.getMarginsWidth(); w *= double(inchFactor); switch (pos.m_xPos) { case MWAWPosition::XRight: if (origin[0] < 0 || origin[0] > 0) { propList.insert("style:horizontal-pos", "from-left"); propList.insert("svg:x", double(origin[0]) - double(pos.size()[0]) + w, unit); } else propList.insert("style:horizontal-pos", "right"); break; case MWAWPosition::XCenter: if (origin[0] < 0 || origin[0] > 0) { propList.insert("style:horizontal-pos", "from-left"); propList.insert("svg:x", double(origin[0]) - double(pos.size()[0])/2.0 + w/2.0, unit); } else propList.insert("style:horizontal-pos", "center"); break; case MWAWPosition::XLeft: case MWAWPosition::XFull: #if !defined(__clang__) default: #endif if (origin[0] < 0 || origin[0] > 0) { propList.insert("style:horizontal-pos", "from-left"); propList.insert("svg:x", double(origin[0]), unit); } else propList.insert("style:horizontal-pos", "left"); break; } if (origin[1] < 0 || origin[1] > 0) { propList.insert("style:vertical-pos", "from-top"); propList.insert("svg:y", double(origin[1]), unit); } else propList.insert("style:vertical-pos", "top"); return; } if (pos.m_anchorTo == MWAWPosition::Page) { // Page position seems to do not use the page margin... propList.insert("text:anchor-type", "page"); if (pos.page() > 0) propList.insert("text:anchor-page-number", pos.page()); double w = m_ds->m_pageSpan.getFormWidth(); double h = m_ds->m_pageSpan.getFormLength(); w *= double(inchFactor); h *= double(inchFactor); propList.insert("style:vertical-rel", "page"); propList.insert("style:horizontal-rel", "page"); double newPosition; switch (pos.m_yPos) { case MWAWPosition::YFull: propList.insert("svg:height", double(h), unit); MWAW_FALLTHROUGH; case MWAWPosition::YTop: if (origin[1] < 0 || origin[1] > 0) { propList.insert("style:vertical-pos", "from-top"); newPosition = double(origin[1]); if (newPosition > h -double(pos.size()[1])) newPosition = h - double(pos.size()[1]); propList.insert("svg:y", double(newPosition), unit); } else propList.insert("style:vertical-pos", "top"); break; case MWAWPosition::YCenter: if (origin[1] < 0 || origin[1] > 0) { propList.insert("style:vertical-pos", "from-top"); newPosition = (h - double(pos.size()[1]))/2.0; if (newPosition > h -double(pos.size()[1])) newPosition = h - double(pos.size()[1]); propList.insert("svg:y", double(newPosition), unit); } else propList.insert("style:vertical-pos", "middle"); break; case MWAWPosition::YBottom: if (origin[1] < 0 || origin[1] > 0) { propList.insert("style:vertical-pos", "from-top"); newPosition = h - double(pos.size()[1])-double(origin[1]); if (newPosition > h -double(pos.size()[1])) newPosition = h -double(pos.size()[1]); else if (newPosition < 0) newPosition = 0; propList.insert("svg:y", double(newPosition), unit); } else propList.insert("style:vertical-pos", "bottom"); break; #if !defined(__clang__) default: break; #endif } switch (pos.m_xPos) { case MWAWPosition::XFull: propList.insert("svg:width", double(w), unit); MWAW_FALLTHROUGH; case MWAWPosition::XLeft: if (origin[0] < 0 || origin[0] > 0) { propList.insert("style:horizontal-pos", "from-left"); propList.insert("svg:x", double(origin[0]), unit); } else propList.insert("style:horizontal-pos", "left"); break; case MWAWPosition::XRight: if (origin[0] < 0 || origin[0] > 0) { propList.insert("style:horizontal-pos", "from-left"); propList.insert("svg:x",w - double(pos.size()[0]) + double(origin[0]), unit); } else propList.insert("style:horizontal-pos", "right"); break; case MWAWPosition::XCenter: if (origin[0] < 0 || origin[0] > 0) { propList.insert("style:horizontal-pos", "from-left"); propList.insert("svg:x", (w - double(pos.size()[0]))/2. + double(origin[0]), unit); } else propList.insert("style:horizontal-pos", "center"); break; #if !defined(__clang__) default: break; #endif } return; } if (pos.m_anchorTo == MWAWPosition::Cell) { if (!pos.m_anchorCellName.empty()) propList.insert("table:end-cell-address", pos.m_anchorCellName); // todo: implement also different m_xPos and m_yPos if (origin[0] < 0 || origin[0] > 0) propList.insert("svg:x", double(origin[0]), unit); if (origin[1] < 0 || origin[1] > 0) propList.insert("svg:y", double(origin[1]), unit); return; } if (pos.m_anchorTo != MWAWPosition::Char && pos.m_anchorTo != MWAWPosition::CharBaseLine && pos.m_anchorTo != MWAWPosition::Unknown) return; propList.insert("text:anchor-type", "as-char"); if (pos.m_anchorTo == MWAWPosition::CharBaseLine) propList.insert("style:vertical-rel", "baseline"); else propList.insert("style:vertical-rel", "line"); switch (pos.m_yPos) { case MWAWPosition::YFull: case MWAWPosition::YTop: if (origin[1] < 0 || origin[1] > 0) { propList.insert("style:vertical-pos", "from-top"); propList.insert("svg:y", double(origin[1]), unit); } else propList.insert("style:vertical-pos", "top"); break; case MWAWPosition::YCenter: if (origin[1] < 0 || origin[1] > 0) { propList.insert("style:vertical-pos", "from-top"); propList.insert("svg:y", double(origin[1] - pos.size()[1]/2.0f), unit); } else propList.insert("style:vertical-pos", "middle"); break; case MWAWPosition::YBottom: #if !defined(__clang__) default: #endif if (origin[1] < 0 || origin[1] > 0) { propList.insert("style:vertical-pos", "from-top"); propList.insert("svg:y", double(origin[1] - pos.size()[1]), unit); } else propList.insert("style:vertical-pos", "bottom"); break; } } /////////////////// // subdocument /////////////////// void MWAWTextListener::handleSubDocument(MWAWSubDocumentPtr const &subDocument, libmwaw::SubDocumentType subDocumentType) { _pushParsingState(); _startSubDocument(); m_ps->m_subDocumentType = subDocumentType; m_ps->m_isPageSpanOpened = true; m_ps->m_list.reset(); switch (subDocumentType) { case libmwaw::DOC_TEXT_BOX: m_ds->m_pageSpan.setMargins(0.0); m_ps->m_sectionAttributesChanged = true; break; case libmwaw::DOC_HEADER_FOOTER: m_ps->m_isHeaderFooterWithoutParagraph = true; m_ds->m_isHeaderFooterStarted = true; break; case libmwaw::DOC_NONE: case libmwaw::DOC_CHART: case libmwaw::DOC_CHART_ZONE: case libmwaw::DOC_NOTE: case libmwaw::DOC_SHEET: case libmwaw::DOC_TABLE: case libmwaw::DOC_COMMENT_ANNOTATION: case libmwaw::DOC_GRAPHIC_GROUP: #if !defined(__clang__) default: #endif break; } // Check whether the document is calling itself bool sendDoc = true; for (auto doc : m_ds->m_subDocuments) { if (!subDocument) break; if (!doc) continue; if (*subDocument == *doc) { MWAW_DEBUG_MSG(("MWAWTextListener::handleSubDocument: recursif call, stop...\n")); sendDoc = false; break; } } if (sendDoc) { if (subDocument) { m_ds->m_subDocuments.push_back(subDocument); std::shared_ptr listen(this, MWAW_shared_ptr_noop_deleter()); try { subDocument->parse(listen, subDocumentType); } catch (...) { MWAW_DEBUG_MSG(("Works: MWAWTextListener::handleSubDocument exception catched \n")); } m_ds->m_subDocuments.pop_back(); } if (m_ps->m_isHeaderFooterWithoutParagraph) _openSpan(); } switch (m_ps->m_subDocumentType) { case libmwaw::DOC_TEXT_BOX: _closeSection(); break; case libmwaw::DOC_HEADER_FOOTER: m_ds->m_isHeaderFooterStarted = false; case libmwaw::DOC_NONE: case libmwaw::DOC_CHART: case libmwaw::DOC_CHART_ZONE: case libmwaw::DOC_NOTE: case libmwaw::DOC_SHEET: case libmwaw::DOC_TABLE: case libmwaw::DOC_COMMENT_ANNOTATION: case libmwaw::DOC_GRAPHIC_GROUP: #if !defined(__clang__) default: #endif break; } _endSubDocument(); _popParsingState(); } bool MWAWTextListener::isSubDocumentOpened(libmwaw::SubDocumentType &subdocType) const { if (!m_ps->m_inSubDocument) return false; subdocType = m_ps->m_subDocumentType; return true; } void MWAWTextListener::_startSubDocument() { m_ds->m_isDocumentStarted = true; m_ps->m_inSubDocument = true; } void MWAWTextListener::_endSubDocument() { if (m_ps->m_isTableOpened) closeTable(); if (m_ps->m_isParagraphOpened) _closeParagraph(); m_ps->m_paragraph.m_listLevelIndex=0; _changeList(); // flush the list exterior } /////////////////// // table /////////////////// void MWAWTextListener::openTable(MWAWTable const &table) { if (m_ps->m_isTableOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::openTable: called with m_isTableOpened=true\n")); return; } if (m_ps->m_isParagraphOpened) _closeParagraph(); // default value: which can be redefined by table librevenge::RVNGPropertyList propList; propList.insert("table:align", "left"); propList.insert("fo:margin-left", *m_ps->m_paragraph.m_margins[1], *m_ps->m_paragraph.m_marginsUnit); _pushParsingState(); _startSubDocument(); m_ps->m_subDocumentType = libmwaw::DOC_TABLE; table.addTablePropertiesTo(propList); m_documentInterface->openTable(propList); m_ps->m_isTableOpened = true; } void MWAWTextListener::closeTable() { if (!m_ps->m_isTableOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::closeTable: called with m_isTableOpened=false\n")); return; } m_ps->m_isTableOpened = false; _endSubDocument(); m_documentInterface->closeTable(); _popParsingState(); } void MWAWTextListener::openTableRow(float h, librevenge::RVNGUnit unit, bool headerRow) { if (m_ps->m_isTableRowOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::openTableRow: called with m_isTableRowOpened=true\n")); return; } if (!m_ps->m_isTableOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::openTableRow: called with m_isTableOpened=false\n")); return; } librevenge::RVNGPropertyList propList; propList.insert("librevenge:is-header-row", headerRow); if (h > 0) propList.insert("style:row-height", double(h), unit); else if (h < 0) propList.insert("style:min-row-height", double(-h), unit); m_documentInterface->openTableRow(propList); m_ps->m_isTableRowOpened = true; } void MWAWTextListener::closeTableRow() { if (!m_ps->m_isTableRowOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::openTableRow: called with m_isTableRowOpened=false\n")); return; } m_ps->m_isTableRowOpened = false; m_documentInterface->closeTableRow(); } void MWAWTextListener::addEmptyTableCell(MWAWVec2i const &pos, MWAWVec2i span) { if (!m_ps->m_isTableRowOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::addEmptyTableCell: called with m_isTableRowOpened=false\n")); return; } if (m_ps->m_isTableCellOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::addEmptyTableCell: called with m_isTableCellOpened=true\n")); closeTableCell(); } librevenge::RVNGPropertyList propList; propList.insert("librevenge:column", pos[0]); propList.insert("librevenge:row", pos[1]); propList.insert("table:number-columns-spanned", span[0]); propList.insert("table:number-rows-spanned", span[1]); m_documentInterface->openTableCell(propList); m_documentInterface->closeTableCell(); } void MWAWTextListener::openTableCell(MWAWCell const &cell) { if (!m_ps->m_isTableRowOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::openTableCell: called with m_isTableRowOpened=false\n")); return; } if (m_ps->m_isTableCellOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::openTableCell: called with m_isTableCellOpened=true\n")); closeTableCell(); } librevenge::RVNGPropertyList propList; cell.addTo(propList, m_parserState.m_fontConverter); m_ps->m_isTableCellOpened = true; m_documentInterface->openTableCell(propList); } void MWAWTextListener::closeTableCell() { if (!m_ps->m_isTableCellOpened) { MWAW_DEBUG_MSG(("MWAWTextListener::closeTableCell: called with m_isTableCellOpened=false\n")); return; } _closeParagraph(); m_ps->m_paragraph.m_listLevelIndex=0; _changeList(); // flush the list exterior m_ps->m_isTableCellOpened = false; m_documentInterface->closeTableCell(); } /////////////////// // others /////////////////// // ---------- state stack ------------------ std::shared_ptr MWAWTextListener::_pushParsingState() { auto actual = m_ps; m_psStack.push_back(actual); m_ps.reset(new MWAWTextListenerInternal::State); m_ps->m_isNote = actual->m_isNote; return actual; } void MWAWTextListener::_popParsingState() { if (m_psStack.size()==0) { MWAW_DEBUG_MSG(("MWAWTextListener::_popParsingState: psStack is empty()\n")); throw libmwaw::ParseException(); } m_ps = m_psStack.back(); m_psStack.pop_back(); } // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: