diff options
Diffstat (limited to 'filters/kword/wordperfect/import/WordPerfectCollector.cpp')
-rw-r--r-- | filters/kword/wordperfect/import/WordPerfectCollector.cpp | 973 |
1 files changed, 973 insertions, 0 deletions
diff --git a/filters/kword/wordperfect/import/WordPerfectCollector.cpp b/filters/kword/wordperfect/import/WordPerfectCollector.cpp new file mode 100644 index 000000000..76fe6fc65 --- /dev/null +++ b/filters/kword/wordperfect/import/WordPerfectCollector.cpp @@ -0,0 +1,973 @@ +/* WordPerfectCollector: Collects sections and runs of text from a + * wordperfect file (and styles to go along with them) and writes them + * to a Writer target document + * + * Copyright (C) 2002-2004 William Lachance (william.lachance@sympatico.ca) + * Copyright (C) 2003-2004 Net Integration Technologies (http://www.net-itech.com) + * Copyright (C) 2004 Fridrich Strba (fridrich.strba@bluewin.ch) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * For further information visit http://libwpd.sourceforge.net + * + */ + +/* "This product is not manufactured, approved, or supported by + * Corel Corporation or Corel Corporation Limited." + */ + +#include <config.h> + +#include <libwpd/libwpd.h> +#include <string.h> // for strcmp + +#include "WordPerfectCollector.h" +#include "DocumentElement.h" +#include "TextRunStyle.h" +#include "FontStyle.h" +#include "ListStyle.h" +#include "PageSpan.h" +#include "SectionStyle.h" +#include "TableStyle.h" +#include "FilterInternal.h" +#include "WriterProperties.h" + +_WriterDocumentState::_WriterDocumentState() : + mbFirstElement(true), + mbInFakeSection(false), + mbListElementOpenedAtCurrentLevel(false), + mbTableCellOpened(false), + mbHeaderRow(false), + mbInNote(false) +{ +} + +WordPerfectCollector::WordPerfectCollector(_SH_InputStream *pInput, DocumentHandler *pHandler) : + mpInput(pInput), + mpHandler(pHandler), + mbUsed(false), + mfSectionSpaceAfter(0.0f), + miNumListStyles(0), + mpCurrentContentElements(&mBodyElements), + mpCurrentPageSpan(NULL), + miNumPageStyles(0), + mpCurrentListStyle(NULL), + miCurrentListLevel(0), + miLastListLevel(0), + miLastListNumber(0), + mbListContinueNumbering(false), + mbListElementOpened(false), + mbListElementParagraphOpened(false) +{ +} + +WordPerfectCollector::~WordPerfectCollector() +{ +} + +bool WordPerfectCollector::filter() +{ + // The contract for WordPerfectCollector is that it will only be used once after it is + // instantiated + if (mbUsed) + return false; + + mbUsed = true; + + // parse & write + // WLACH_REFACTORING: Remove these args.. + if (!_parseSourceDocument(*mpInput)) + return false; + if (!_writeTargetDocument(*mpHandler)) + return false; + + // clean up the mess we made + WRITER_DEBUG_MSG(("WriterWordPerfect: Cleaning up our mess..\n")); + + WRITER_DEBUG_MSG(("Destroying the body elements\n")); + for (std::vector<DocumentElement *>::iterator iterBody = mBodyElements.begin(); iterBody != mBodyElements.end(); iterBody++) { + delete((*iterBody)); + (*iterBody) = NULL; + } + + WRITER_DEBUG_MSG(("Destroying the styles elements\n")); + for (std::vector<DocumentElement *>::iterator iterStyles = mStylesElements.begin(); iterStyles != mStylesElements.end(); iterStyles++) { + delete (*iterStyles); + (*iterStyles) = NULL; // we may pass over the same element again (in the case of headers/footers spanning multiple pages) + // so make sure we don't do a double del + } + + WRITER_DEBUG_MSG(("Destroying the rest of the styles elements\n")); + for (std::map<_SH_String, ParagraphStyle *, ltstr>::iterator iterTextStyle = mTextStyleHash.begin(); iterTextStyle != mTextStyleHash.end(); iterTextStyle++) { + delete(iterTextStyle->second); + } + for (std::map<_SH_String, FontStyle *, ltstr>::iterator iterFont = mFontHash.begin(); iterFont != mFontHash.end(); iterFont++) { + delete(iterFont->second); + } + + for (std::vector<ListStyle *>::iterator iterListStyles = mListStyles.begin(); iterListStyles != mListStyles.end(); iterListStyles++) { + delete((*iterListStyles)); + } + for (std::vector<SectionStyle *>::iterator iterSectionStyles = mSectionStyles.begin(); iterSectionStyles != mSectionStyles.end(); iterSectionStyles++) { + delete((*iterSectionStyles)); + } + for (std::vector<TableStyle *>::iterator iterTableStyles = mTableStyles.begin(); iterTableStyles != mTableStyles.end(); iterTableStyles++) { + delete((*iterTableStyles)); + } + + for (std::vector<PageSpan *>::iterator iterPageSpans = mPageSpans.begin(); iterPageSpans != mPageSpans.end(); iterPageSpans++) { + delete((*iterPageSpans)); + } + + return true; +} + +bool WordPerfectCollector::_parseSourceDocument(_SH_InputStream &input) +{ +#if defined(HAVE_LIBWPD_0100) + libwpd::WPDResult result = libwpd::WPDocument::parse(&input, static_cast<_SH_DocumentInterface*>(this), NULL); +#elif defined(HAVE_LIBWPD_090) + WPDResult result = WPDocument::parse(&input, static_cast<_SH_DocumentInterface*>(this), NULL); +#else + WPDResult result = WPDocument::parse(&input, static_cast<_SH_DocumentInterface*>(this)); +#endif +#if defined(HAVE_LIBWPD_0100) + if (result != libwpd::WPD_OK) +#else + if (result != WPD_OK) +#endif + return false; + + return true; +} + +void WordPerfectCollector::_writeDefaultStyles(DocumentHandler &xHandler) +{ + TagOpenElement stylesOpenElement("office:styles"); + stylesOpenElement.write(xHandler); + + TagOpenElement defaultParagraphStyleOpenElement("style:default-style"); + defaultParagraphStyleOpenElement.addAttribute("style:family", "paragraph"); + defaultParagraphStyleOpenElement.write(xHandler); + + TagOpenElement defaultParagraphStylePropertiesOpenElement("style:properties"); + defaultParagraphStylePropertiesOpenElement.addAttribute("style:family", "paragraph"); + defaultParagraphStylePropertiesOpenElement.addAttribute("style:tab-stop-distance", "0.5inch"); + defaultParagraphStylePropertiesOpenElement.write(xHandler); + TagCloseElement defaultParagraphStylePropertiesCloseElement("style:properties"); + defaultParagraphStylePropertiesCloseElement.write(xHandler); + + TagCloseElement defaultParagraphStyleCloseElement("style:default-style"); + defaultParagraphStyleCloseElement.write(xHandler); + + TagOpenElement standardStyleOpenElement("style:style"); + standardStyleOpenElement.addAttribute("style:name", "Standard"); + standardStyleOpenElement.addAttribute("style:family", "paragraph"); + standardStyleOpenElement.addAttribute("style:class", "text"); + standardStyleOpenElement.write(xHandler); + TagCloseElement standardStyleCloseElement("style:style"); + standardStyleCloseElement.write(xHandler); + + TagOpenElement textBodyStyleOpenElement("style:style"); + textBodyStyleOpenElement.addAttribute("style:name", "Text Body"); + textBodyStyleOpenElement.addAttribute("style:family", "paragraph"); + textBodyStyleOpenElement.addAttribute("style:parent-style-name", "Standard"); + textBodyStyleOpenElement.addAttribute("style:class", "text"); + textBodyStyleOpenElement.write(xHandler); + TagCloseElement textBodyStyleCloseElement("style:style"); + textBodyStyleCloseElement.write(xHandler); + + TagOpenElement tableContentsStyleOpenElement("style:style"); + tableContentsStyleOpenElement.addAttribute("style:name", "Table Contents"); + tableContentsStyleOpenElement.addAttribute("style:family", "paragraph"); + tableContentsStyleOpenElement.addAttribute("style:parent-style-name", "Text Body"); + tableContentsStyleOpenElement.addAttribute("style:class", "extra"); + tableContentsStyleOpenElement.write(xHandler); + TagCloseElement tableContentsStyleCloseElement("style:style"); + tableContentsStyleCloseElement.write(xHandler); + + TagOpenElement tableHeadingStyleOpenElement("style:style"); + tableHeadingStyleOpenElement.addAttribute("style:name", "Table Heading"); + tableHeadingStyleOpenElement.addAttribute("style:family", "paragraph"); + tableHeadingStyleOpenElement.addAttribute("style:parent-style-name", "Table Contents"); + tableHeadingStyleOpenElement.addAttribute("style:class", "extra"); + tableHeadingStyleOpenElement.write(xHandler); + TagCloseElement tableHeadingStyleCloseElement("style:style"); + tableHeadingStyleCloseElement.write(xHandler); + + TagCloseElement stylesCloseElement("office:styles"); + stylesCloseElement.write(xHandler); + +} + +// writes everything up to the automatic styles declarations.. +void WordPerfectCollector::_writeBegin() +{ +} + +void WordPerfectCollector::_writeMasterPages(DocumentHandler &xHandler) +{ + _SH_PropertyList xBlankAttrList; + + xHandler.startElement("office:master-styles", xBlankAttrList); + int pageNumber = 1; + for (int i=0; i<mPageSpans.size(); i++) + { + bool bLastPage; + (i == (mPageSpans.size() - 1)) ? bLastPage = true : bLastPage = false; + mPageSpans[i]->writeMasterPages(pageNumber, i, bLastPage, xHandler); + pageNumber += mPageSpans[i]->getSpan(); + } + xHandler.endElement("office:master-styles"); +} + +void WordPerfectCollector::_writePageMasters(DocumentHandler &xHandler) +{ + int pageNumber = 1; + for (int i=0; i<mPageSpans.size(); i++) + { + mPageSpans[i]->writePageMaster(i, xHandler); + } +} + +bool WordPerfectCollector::_writeTargetDocument(DocumentHandler &xHandler) +{ + WRITER_DEBUG_MSG(("WriterWordPerfect: Document Body: Printing out the header stuff..\n")); + _SH_PropertyList xBlankAttrList; + + WRITER_DEBUG_MSG(("WriterWordPerfect: Document Body: Start Document\n")); + mpHandler->startDocument(); + + WRITER_DEBUG_MSG(("WriterWordPerfect: Document Body: preamble\n")); + _SH_PropertyList docContentPropList; + docContentPropList.insert("xmlns:office", "http://openoffice.org/2000/office"); + docContentPropList.insert("xmlns:style", "http://openoffice.org/2000/style"); + docContentPropList.insert("xmlns:text", "http://openoffice.org/2000/text"); + docContentPropList.insert("xmlns:table", "http://openoffice.org/2000/table"); + docContentPropList.insert("xmlns:draw", "http://openoffice.org/2000/draw"); + docContentPropList.insert("xmlns:fo", "http://www.w3.org/1999/XSL/Format"); + docContentPropList.insert("xmlns:xlink", "http://www.w3.org/1999/xlink"); + docContentPropList.insert("xmlns:number", "http://openoffice.org/2000/datastyle"); + docContentPropList.insert("xmlns:svg", "http://www.w3.org/2000/svg"); + docContentPropList.insert("xmlns:chart", "http://openoffice.org/2000/chart"); + docContentPropList.insert("xmlns:dr3d", "http://openoffice.org/2000/dr3d"); + docContentPropList.insert("xmlns:math", "http://www.w3.org/1998/Math/MathML"); + docContentPropList.insert("xmlns:form", "http://openoffice.org/2000/form"); + docContentPropList.insert("xmlns:script", "http://openoffice.org/2000/script"); + docContentPropList.insert("office:class", "text"); + docContentPropList.insert("office:version", "1.0"); + mpHandler->startElement("office:document-content", docContentPropList); + + // write out the font styles + mpHandler->startElement("office:font-decls", xBlankAttrList); + for (std::map<_SH_String, FontStyle *, ltstr>::iterator iterFont = mFontHash.begin(); iterFont != mFontHash.end(); iterFont++) { + iterFont->second->write(*mpHandler); + } + TagOpenElement symbolFontOpen("style:font-decl"); + symbolFontOpen.addAttribute("style:name", "StarSymbol"); + symbolFontOpen.addAttribute("fo:font-family", "StarSymbol"); + symbolFontOpen.addAttribute("style:font-charset", "x-symbol"); + symbolFontOpen.write(*mpHandler); + mpHandler->endElement("style:font-decl"); + + mpHandler->endElement("office:font-decls"); + + + WRITER_DEBUG_MSG(("WriterWordPerfect: Document Body: Writing out the styles..\n")); + + // write default styles + _writeDefaultStyles(*mpHandler); + + mpHandler->startElement("office:automatic-styles", xBlankAttrList); + + for (std::map<_SH_String, ParagraphStyle *, ltstr>::iterator iterTextStyle = mTextStyleHash.begin(); + iterTextStyle != mTextStyleHash.end(); iterTextStyle++) + { + // writing out the paragraph styles + if (strcmp((iterTextStyle->second)->getName().cstr(), "Standard")) + { + // don't write standard paragraph "no styles" style + (iterTextStyle->second)->write(xHandler); + } + } + + // span styles.. + for (std::map<_SH_String, SpanStyle *, ltstr>::iterator iterSpanStyle = mSpanStyleHash.begin(); + iterSpanStyle != mSpanStyleHash.end(); iterSpanStyle++) + { + (iterSpanStyle->second)->write(xHandler); + } + + // writing out the sections styles + for (std::vector<SectionStyle *>::iterator iterSectionStyles = mSectionStyles.begin(); iterSectionStyles != mSectionStyles.end(); iterSectionStyles++) { + (*iterSectionStyles)->write(xHandler); + } + + // writing out the lists styles + for (std::vector<ListStyle *>::iterator iterListStyles = mListStyles.begin(); iterListStyles != mListStyles.end(); iterListStyles++) { + (*iterListStyles)->write(xHandler); + } + + // writing out the table styles + for (std::vector<TableStyle *>::iterator iterTableStyles = mTableStyles.begin(); iterTableStyles != mTableStyles.end(); iterTableStyles++) { + (*iterTableStyles)->write(xHandler); + } + + // writing out the page masters + _writePageMasters(xHandler); + + + xHandler.endElement("office:automatic-styles"); + + _writeMasterPages(xHandler); + + WRITER_DEBUG_MSG(("WriterWordPerfect: Document Body: Writing out the document..\n")); + // writing out the document + xHandler.startElement("office:body", xBlankAttrList); + + for (std::vector<DocumentElement *>::iterator iterBodyElements = mBodyElements.begin(); iterBodyElements != mBodyElements.end(); iterBodyElements++) { + (*iterBodyElements)->write(xHandler); + } + WRITER_DEBUG_MSG(("WriterWordPerfect: Document Body: Finished writing all doc els..\n")); + + xHandler.endElement("office:body"); + xHandler.endElement("office:document-content"); + + xHandler.endDocument(); + + return true; +} + + +_SH_String propListToStyleKey(const _SH_PropertyList & xPropList) +{ + _SH_String sKey; + _SH_PropertyList::Iter i(xPropList); + for (i.rewind(); i.next(); ) + { + _SH_String sProp; + sProp.sprintf("[%s:%s]", i.key(), i()->getStr().cstr()); + sKey.append(sProp); + } + + return sKey; +} + +_SH_String getParagraphStyleKey(const _SH_PropertyList & xPropList, const _SH_PropertyListVector & xTabStops) +{ + _SH_String sKey = propListToStyleKey(xPropList); + + _SH_String sTabStops; + sTabStops.sprintf("[num-tab-stops:%i]", xTabStops.count()); + _SH_PropertyListVector::Iter i(xTabStops); + for (i.rewind(); i.next();) + { + sTabStops.append(propListToStyleKey(i())); + } + sKey.append(sTabStops); + + return sKey; +} + +// _allocateFontName: add a (potentially mapped) font style to the hash if it's not already there, do nothing otherwise +void WordPerfectCollector::_allocateFontName(const _SH_String & sFontName) +{ + if (mFontHash.find(sFontName) == mFontHash.end()) + { + FontStyle *pFontStyle = new FontStyle(sFontName.cstr(), sFontName.cstr()); + mFontHash[sFontName] = pFontStyle; + } +} + +void WordPerfectCollector::openPageSpan(const _SH_PropertyList &propList) +{ + PageSpan *pPageSpan = new PageSpan(propList); + mPageSpans.push_back(pPageSpan); + mpCurrentPageSpan = pPageSpan; +} + +void WordPerfectCollector::openHeader(const _SH_PropertyList &propList) +{ + std::vector<DocumentElement *> * pHeaderFooterContentElements = new std::vector<DocumentElement *>; + + if (propList["libwpd:occurence"]->getStr() == "even") + mpCurrentPageSpan->setHeaderLeftContent(pHeaderFooterContentElements); + else + mpCurrentPageSpan->setHeaderContent(pHeaderFooterContentElements); + + mpCurrentContentElements = pHeaderFooterContentElements; +} + +void WordPerfectCollector::closeHeader() +{ + mpCurrentContentElements = &mBodyElements; +} + +void WordPerfectCollector::openFooter(const _SH_PropertyList &propList) +{ + std::vector<DocumentElement *> * pHeaderFooterContentElements = new std::vector<DocumentElement *>; + + if (propList["libwpd:occurence"]->getStr() == "even") + mpCurrentPageSpan->setFooterLeftContent(pHeaderFooterContentElements); + else + mpCurrentPageSpan->setFooterContent(pHeaderFooterContentElements); + + mpCurrentContentElements = pHeaderFooterContentElements; +} + +void WordPerfectCollector::closeFooter() +{ + mpCurrentContentElements = &mBodyElements; +} + +void WordPerfectCollector::openSection(const _SH_PropertyList &propList, const _SH_PropertyListVector &columns) +{ + int iNumColumns = columns.count(); + + if (iNumColumns > 1) + { +#if defined(HAVE_LIBWPD_090) || defined(HAVE_LIBWPD_0100) + mfSectionSpaceAfter = propList["fo:margin-bottom"]->getDouble(); +#else + mfSectionSpaceAfter = propList["fo:margin-bottom"]->getFloat(); +#endif + _SH_String sSectionName; + sSectionName.sprintf("Section%i", mSectionStyles.size()); + + SectionStyle *pSectionStyle = new SectionStyle(propList, columns, sSectionName.cstr()); + mSectionStyles.push_back(pSectionStyle); + + TagOpenElement *pSectionOpenElement = new TagOpenElement("text:section"); + pSectionOpenElement->addAttribute("text:style-name", pSectionStyle->getName()); + pSectionOpenElement->addAttribute("text:name", pSectionStyle->getName()); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pSectionOpenElement)); + } + else + mWriterDocumentState.mbInFakeSection = true; +} + +void WordPerfectCollector::closeSection() +{ + if (!mWriterDocumentState.mbInFakeSection) + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:section"))); + else + mWriterDocumentState.mbInFakeSection = false; + + // open as many paragraphs as needed to simulate section space after + // WLACH_REFACTORING: disable this for now.. + #if 0 + for (float f=0.0f; f<mfSectionSpaceAfter; f+=1.0f) { + vector<WPXTabStop> dummyTabStops; + openParagraph(WPX_PARAGRAPH_JUSTIFICATION_LEFT, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, dummyTabStops, false, false); + closeParagraph(); + } + #endif + mfSectionSpaceAfter = 0.0f; +} + +void WordPerfectCollector::openParagraph(const _SH_PropertyList &propList, const _SH_PropertyListVector &tabStops) +{ + // FIXMENOW: What happens if we open a footnote inside a table? do we then inherit the footnote's style + // from "Table Contents" + + _SH_PropertyList *pPersistPropList = new _SH_PropertyList(propList); + ParagraphStyle *pStyle = NULL; + + if (mWriterDocumentState.mbFirstElement && mpCurrentContentElements == &mBodyElements) + { + // we don't have to go through the fuss of determining if the paragraph style is + // unique in this case, because if we are the first document element, then we + // are singular. Neither do we have to determine what our parent style is-- we can't + // be inside a table in this case (the table would be the first document element + //in that case) + pPersistPropList->insert("style:parent-style-name", "Standard"); + _SH_String sName; + sName.sprintf("FS"); + + _SH_String sParagraphHashKey("P|FS"); + pPersistPropList->insert("style:master-page-name", "Page Style 1"); + pStyle = new ParagraphStyle(pPersistPropList, tabStops, sName); + mTextStyleHash[sParagraphHashKey] = pStyle; + mWriterDocumentState.mbFirstElement = false; + } + else + { + if (mWriterDocumentState.mbTableCellOpened) + { + if (mWriterDocumentState.mbHeaderRow) + pPersistPropList->insert("style:parent-style-name", "Table Heading"); + else + pPersistPropList->insert("style:parent-style-name", "Table Contents"); + } + else + pPersistPropList->insert("style:parent-style-name", "Standard"); + + _SH_String sKey = getParagraphStyleKey(*pPersistPropList, tabStops); + + if (mTextStyleHash.find(sKey) == mTextStyleHash.end()) { + _SH_String sName; + sName.sprintf("S%i", mTextStyleHash.size()); + + pStyle = new ParagraphStyle(pPersistPropList, tabStops, sName); + + mTextStyleHash[sKey] = pStyle; + } + else + { + pStyle = mTextStyleHash[sKey]; + delete pPersistPropList; + } + } + // create a document element corresponding to the paragraph, and append it to our list of document elements + TagOpenElement *pParagraphOpenElement = new TagOpenElement("text:p"); + pParagraphOpenElement->addAttribute("text:style-name", pStyle->getName()); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pParagraphOpenElement)); +} + +void WordPerfectCollector::closeParagraph() +{ + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:p"))); +} + +void WordPerfectCollector::openSpan(const _SH_PropertyList &propList) +{ + if (propList["style:font-name"]) + _allocateFontName(propList["style:font-name"]->getStr()); + _SH_String sSpanHashKey = propListToStyleKey(propList); + WRITER_DEBUG_MSG(("WriterWordPerfect: Span Hash Key: %s\n", sSpanHashKey.cstr())); + + // Get the style + _SH_String sName; + if (mSpanStyleHash.find(sSpanHashKey) == mSpanStyleHash.end()) + { + // allocate a new paragraph style + sName.sprintf("Span%i", mSpanStyleHash.size()); + SpanStyle *pStyle = new SpanStyle(sName.cstr(), propList); + + mSpanStyleHash[sSpanHashKey] = pStyle; + } + else + { + sName.sprintf("%s", mSpanStyleHash.find(sSpanHashKey)->second->getName().cstr()); + } + + // create a document element corresponding to the paragraph, and append it to our list of document elements + TagOpenElement *pSpanOpenElement = new TagOpenElement("text:span"); + pSpanOpenElement->addAttribute("text:style-name", sName.cstr()); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pSpanOpenElement)); +} + +void WordPerfectCollector::closeSpan() +{ + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:span"))); +} + +void WordPerfectCollector::defineOrderedListLevel(const _SH_PropertyList &propList) +{ + int id = 0; + if (propList["libwpd:id"]) + id = propList["libwpd:id"]->getInt(); + + OrderedListStyle *pOrderedListStyle = NULL; + if (mpCurrentListStyle && mpCurrentListStyle->getListID() == id) + pOrderedListStyle = static_cast<OrderedListStyle *>(mpCurrentListStyle); // FIXME: using a dynamic cast here causes oo to crash?! + + // this rather appalling conditional makes sure we only start a new list (rather than continue an old + // one) if: (1) we have no prior list OR (2) the prior list is actually definitively different + // from the list that is just being defined (listIDs differ) OR (3) we can tell that the user actually + // is starting a new list at level 1 (and only level 1) + if (pOrderedListStyle == NULL || pOrderedListStyle->getListID() != id || + (propList["libwpd:level"] && propList["libwpd:level"]->getInt()==1 && + (propList["text:start-value"] && propList["text:start-value"]->getInt() != (miLastListNumber+1)))) + { + WRITER_DEBUG_MSG(("Attempting to create a new ordered list style (listid: %i)\n", id)); + _SH_String sName; + sName.sprintf("OL%i", miNumListStyles); + miNumListStyles++; + pOrderedListStyle = new OrderedListStyle(sName.cstr(), propList["libwpd:id"]->getInt()); + mListStyles.push_back(static_cast<ListStyle *>(pOrderedListStyle)); + mpCurrentListStyle = static_cast<ListStyle *>(pOrderedListStyle); + mbListContinueNumbering = false; + miLastListNumber = 0; + } + else + mbListContinueNumbering = true; + + // Iterate through ALL list styles with the same WordPerfect list id and define a level if it is not already defined + // This solves certain problems with lists that start and finish without reaching certain levels and then begin again + // and reach those levels. See gradguide0405_PC.wpd in the regression suite + for (std::vector<ListStyle *>::iterator iterOrderedListStyles = mListStyles.begin(); iterOrderedListStyles != mListStyles.end(); iterOrderedListStyles++) + { + if ((* iterOrderedListStyles)->getListID() == propList["libwpd:id"]->getInt()) + (* iterOrderedListStyles)->updateListLevel((propList["libwpd:level"]->getInt() - 1), propList); + } +} + +void WordPerfectCollector::defineUnorderedListLevel(const _SH_PropertyList &propList) +{ + int id = 0; + if (propList["libwpd:id"]) + id = propList["libwpd:id"]->getInt(); + + UnorderedListStyle *pUnorderedListStyle = NULL; + if (mpCurrentListStyle && mpCurrentListStyle->getListID() == id) + pUnorderedListStyle = static_cast<UnorderedListStyle *>(mpCurrentListStyle); // FIXME: using a dynamic cast here causes oo to crash?! + + if (pUnorderedListStyle == NULL) { + WRITER_DEBUG_MSG(("Attempting to create a new unordered list style (listid: %i)\n", id)); + _SH_String sName; + sName.sprintf("UL%i", miNumListStyles); + pUnorderedListStyle = new UnorderedListStyle(sName.cstr(), id); + mListStyles.push_back(static_cast<ListStyle *>(pUnorderedListStyle)); + mpCurrentListStyle = static_cast<ListStyle *>(pUnorderedListStyle); + } + + // See comment in WordPerfectCollector::defineOrderedListLevel + for (std::vector<ListStyle *>::iterator iterUnorderedListStyles = mListStyles.begin(); iterUnorderedListStyles != mListStyles.end(); iterUnorderedListStyles++) + { + if ((* iterUnorderedListStyles)->getListID() == propList["libwpd:id"]->getInt()) + (* iterUnorderedListStyles)->updateListLevel((propList["libwpd:level"]->getInt() - 1), propList); + } +} + +void WordPerfectCollector::openOrderedListLevel(const _SH_PropertyList &propList) +{ + miCurrentListLevel++; + TagOpenElement *pListLevelOpenElement = new TagOpenElement("text:ordered-list"); + _openListLevel(pListLevelOpenElement); + + if (mbListContinueNumbering) { + pListLevelOpenElement->addAttribute("text:continue-numbering", "true"); + } + + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pListLevelOpenElement)); +} + +void WordPerfectCollector::openUnorderedListLevel(const _SH_PropertyList &propList) +{ + miCurrentListLevel++; + TagOpenElement *pListLevelOpenElement = new TagOpenElement("text:unordered-list"); + _openListLevel(pListLevelOpenElement); + + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pListLevelOpenElement)); +} + +void WordPerfectCollector::_openListLevel(TagOpenElement *pListLevelOpenElement) +{ + if (!mbListElementOpened && miCurrentListLevel > 1) + { + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:list-item"))); + } + else if (mbListElementParagraphOpened) + { + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:p"))); + mbListElementParagraphOpened = false; + } + + if (miCurrentListLevel==1) { + pListLevelOpenElement->addAttribute("text:style-name", mpCurrentListStyle->getName()); + } + + mbListElementOpened = false; +} + +void WordPerfectCollector::closeOrderedListLevel() +{ + _closeListLevel("ordered-list"); +} + +void WordPerfectCollector::closeUnorderedListLevel() +{ + _closeListLevel("unordered-list"); +} + +void WordPerfectCollector::_closeListLevel(const char *szListType) +{ + if (mbListElementOpened) + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:list-item"))); + + miCurrentListLevel--; + + _SH_String sCloseElement; + sCloseElement.sprintf("text:%s", szListType); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement(sCloseElement.cstr()))); + + if (miCurrentListLevel > 0) + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:list-item"))); + mbListElementOpened = false; +} + +void WordPerfectCollector::openListElement(const _SH_PropertyList &propList, const _SH_PropertyListVector &tabStops) +{ + miLastListLevel = miCurrentListLevel; + if (miCurrentListLevel == 1) + miLastListNumber++; + + if (mbListElementOpened) + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:list-item"))); + + ParagraphStyle *pStyle = NULL; + + _SH_PropertyList *pPersistPropList = new _SH_PropertyList(propList); + pPersistPropList->insert("style:list-style-name", mpCurrentListStyle->getName()); + pPersistPropList->insert("style:parent-style-name", "Standard"); + + _SH_String sKey = getParagraphStyleKey(*pPersistPropList, tabStops); + + if (mTextStyleHash.find(sKey) == mTextStyleHash.end()) + { + _SH_String sName; + sName.sprintf("S%i", mTextStyleHash.size()); + + pStyle = new ParagraphStyle(pPersistPropList, tabStops, sName); + + mTextStyleHash[sKey] = pStyle; + } + else + { + pStyle = mTextStyleHash[sKey]; + delete pPersistPropList; + } + + TagOpenElement *pOpenListElement = new TagOpenElement("text:list-item"); + TagOpenElement *pOpenListElementParagraph = new TagOpenElement("text:p"); + + pOpenListElementParagraph->addAttribute("text:style-name", pStyle->getName()); + + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pOpenListElement)); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pOpenListElementParagraph)); + + mbListElementOpened = true; + mbListElementParagraphOpened = true; + mbListContinueNumbering = false; +} + +void WordPerfectCollector::closeListElement() +{ + // this code is kind of tricky, because we don't actually close the list element (because this list element + // could contain another list level in OOo's implementation of lists). that is done in the closeListLevel + // code (or when we open another list element) + + if (mbListElementParagraphOpened) + { + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:p"))); + mbListElementParagraphOpened = false; + } +} + +void WordPerfectCollector::openFootnote(const _SH_PropertyList &propList) +{ + TagOpenElement *pOpenFootNote = new TagOpenElement("text:footnote"); + if (propList["libwpd:number"]) + { + _SH_String tmpString("ftn"); + tmpString.append(propList["libwpd:number"]->getStr()); + pOpenFootNote->addAttribute("text:id", tmpString); + } + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pOpenFootNote)); + + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:footnote-citation"))); + if (propList["libwpd:number"]) + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new CharDataElement(propList["libwpd:number"]->getStr().cstr()))); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:footnote-citation"))); + + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:footnote-body"))); + + mWriterDocumentState.mbInNote = true; +} + +void WordPerfectCollector::closeFootnote() +{ + mWriterDocumentState.mbInNote = false; + + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:footnote-body"))); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:footnote"))); +} + +void WordPerfectCollector::openEndnote(const _SH_PropertyList &propList) +{ + TagOpenElement *pOpenEndNote = new TagOpenElement("text:endnote"); + if (propList["libwpd:number"]) + { + _SH_String tmpString("edn"); + tmpString.append(propList["libwpd:number"]->getStr()); + pOpenEndNote->addAttribute("text:id", tmpString); + } + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pOpenEndNote)); + + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:endnote-citation"))); + if (propList["libwpd:number"]) + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new CharDataElement(propList["libwpd:number"]->getStr().cstr()))); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:endnote-citation"))); + + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:endnote-body"))); + + mWriterDocumentState.mbInNote = true; +} + +void WordPerfectCollector::closeEndnote() +{ + mWriterDocumentState.mbInNote = false; + + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:endnote-body"))); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:endnote"))); +} + +void WordPerfectCollector::openTable(const _SH_PropertyList &propList, const _SH_PropertyListVector &columns) +{ + if (!mWriterDocumentState.mbInNote) + { + _SH_String sTableName; + sTableName.sprintf("Table%i", mTableStyles.size()); + + // FIXME: we base the table style off of the page's margin left, ignoring (potential) wordperfect margin + // state which is transmitted inside the page. could this lead to unacceptable behaviour? + // WLACH_REFACTORING: characterize this behaviour, probably should nip it at the bud within libwpd + TableStyle *pTableStyle = new TableStyle(propList, columns, sTableName.cstr()); + + if (mWriterDocumentState.mbFirstElement && mpCurrentContentElements == &mBodyElements) + { + _SH_String sMasterPageName("Page Style 1"); + pTableStyle->setMasterPageName(sMasterPageName); + mWriterDocumentState.mbFirstElement = false; + } + + mTableStyles.push_back(pTableStyle); + + mpCurrentTableStyle = pTableStyle; + + TagOpenElement *pTableOpenElement = new TagOpenElement("table:table"); + + pTableOpenElement->addAttribute("table:name", sTableName.cstr()); + pTableOpenElement->addAttribute("table:style-name", sTableName.cstr()); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pTableOpenElement)); + + for (int i=0; i<pTableStyle->getNumColumns(); i++) + { + TagOpenElement *pTableColumnOpenElement = new TagOpenElement("table:table-column"); + _SH_String sColumnStyleName; + sColumnStyleName.sprintf("%s.Column%i", sTableName.cstr(), (i+1)); + pTableColumnOpenElement->addAttribute("table:style-name", sColumnStyleName.cstr()); + mpCurrentContentElements->push_back(pTableColumnOpenElement); + + TagCloseElement *pTableColumnCloseElement = new TagCloseElement("table:table-column"); + mpCurrentContentElements->push_back(pTableColumnCloseElement); + } + } +} + +void WordPerfectCollector::openTableRow(const _SH_PropertyList &propList) +{ + if (!mWriterDocumentState.mbInNote) + { + if (propList["libwpd:is-header-row"] && (propList["libwpd:is-header-row"]->getInt())) + { + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("table:table-header-rows"))); + mWriterDocumentState.mbHeaderRow = true; + } + + _SH_String sTableRowStyleName; + sTableRowStyleName.sprintf("%s.Row%i", mpCurrentTableStyle->getName().cstr(), mpCurrentTableStyle->getNumTableRowStyles()); + TableRowStyle *pTableRowStyle = new TableRowStyle(propList, sTableRowStyleName.cstr()); + mpCurrentTableStyle->addTableRowStyle(pTableRowStyle); + + TagOpenElement *pTableRowOpenElement = new TagOpenElement("table:table-row"); + pTableRowOpenElement->addAttribute("table:style-name", sTableRowStyleName); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pTableRowOpenElement)); + } +} + +void WordPerfectCollector::closeTableRow() +{ + if (!mWriterDocumentState.mbInNote) + { + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("table:table-row"))); + if (mWriterDocumentState.mbHeaderRow) + { + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("table:table-header-rows"))); + mWriterDocumentState.mbHeaderRow = false; + } + } +} + +void WordPerfectCollector::openTableCell(const _SH_PropertyList &propList) +{ + if (!mWriterDocumentState.mbInNote) + { + _SH_String sTableCellStyleName; + sTableCellStyleName.sprintf( "%s.Cell%i", mpCurrentTableStyle->getName().cstr(), mpCurrentTableStyle->getNumTableCellStyles()); + TableCellStyle *pTableCellStyle = new TableCellStyle(propList, sTableCellStyleName.cstr()); + mpCurrentTableStyle->addTableCellStyle(pTableCellStyle); + + TagOpenElement *pTableCellOpenElement = new TagOpenElement("table:table-cell"); + pTableCellOpenElement->addAttribute("table:style-name", sTableCellStyleName); + if (propList["table:number-columns-spanned"]) + pTableCellOpenElement->addAttribute("table:number-columns-spanned", + propList["table:number-columns-spanned"]->getStr().cstr()); + if (propList["table:number-rows-spanned"]) + pTableCellOpenElement->addAttribute("table:number-rows-spanned", + propList["table:number-rows-spanned"]->getStr().cstr()); + pTableCellOpenElement->addAttribute("table:value-type", "string"); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pTableCellOpenElement)); + + mWriterDocumentState.mbTableCellOpened = true; + } +} + +void WordPerfectCollector::closeTableCell() +{ + if (!mWriterDocumentState.mbInNote) + { + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("table:table-cell"))); + mWriterDocumentState.mbTableCellOpened = false; + } +} + +void WordPerfectCollector::insertCoveredTableCell(const _SH_PropertyList &propList) +{ + if (!mWriterDocumentState.mbInNote) + { + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("table:covered-table-cell"))); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("table:covered-table-cell"))); + } +} + +void WordPerfectCollector::closeTable() +{ + if (!mWriterDocumentState.mbInNote) + { + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("table:table"))); + } +} + + +void WordPerfectCollector::insertTab() +{ + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:tab-stop"))); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:tab-stop"))); +} + +void WordPerfectCollector::insertLineBreak() +{ + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:line-break"))); + mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:line-break"))); +} + +void WordPerfectCollector::insertText(const _SH_String &text) +{ + DocumentElement *pText = new TextElement(text); + mpCurrentContentElements->push_back(pText); +} |