WordPerfectCollector.cpp
1/* WordPerfectCollector: Collects sections and runs of text from a
2 * wordperfect file (and styles to go along with them) and writes them
3 * to a Writer target document
4 *
5 * Copyright (C) 2002-2004 William Lachance (william.lachance@sympatico.ca)
6 * Copyright (C) 2003-2004 Net Integration Technologies (http://www.net-itech.com)
7 * Copyright (C) 2004 Fridrich Strba (fridrich.strba@bluewin.ch)
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 * For further information visit http://libwpd.sourceforge.net
25 *
26 */
27
28/* "This product is not manufactured, approved, or supported by
29 * Corel Corporation or Corel Corporation Limited."
30 */
31
32#include <config.h>
33
34#include <libwpd/libwpd.h>
35#include <string.h> // for strcmp
36
37#include "WordPerfectCollector.h"
38#include "DocumentElement.h"
39#include "TextRunStyle.h"
40#include "FontStyle.h"
41#include "ListStyle.h"
42#include "PageSpan.h"
43#include "SectionStyle.h"
44#include "TableStyle.h"
45#include "FilterInternal.h"
46#include "WriterProperties.h"
47
48_WriterDocumentState::_WriterDocumentState() :
49 mbFirstElement(true),
50 mbInFakeSection(false),
51 mbListElementOpenedAtCurrentLevel(false),
52 mbTableCellOpened(false),
53 mbHeaderRow(false),
54 mbInNote(false)
55{
56}
57
58WordPerfectCollector::WordPerfectCollector(_SH_InputStream *pInput, DocumentHandler *pHandler) :
59 mpInput(pInput),
60 mpHandler(pHandler),
61 mbUsed(false),
62 mfSectionSpaceAfter(0.0f),
63 miNumListStyles(0),
64 mpCurrentContentElements(&mBodyElements),
65 mpCurrentPageSpan(NULL),
66 miNumPageStyles(0),
67 mpCurrentListStyle(NULL),
68 miCurrentListLevel(0),
69 miLastListLevel(0),
70 miLastListNumber(0),
71 mbListContinueNumbering(false),
72 mbListElementOpened(false),
73 mbListElementParagraphOpened(false)
74{
75}
76
77WordPerfectCollector::~WordPerfectCollector()
78{
79}
80
81bool WordPerfectCollector::filter()
82{
83 // The contract for WordPerfectCollector is that it will only be used once after it is
84 // instantiated
85 if (mbUsed)
86 return false;
87
88 mbUsed = true;
89
90 // parse & write
91 // WLACH_REFACTORING: Remove these args..
92 if (!_parseSourceDocument(*mpInput))
93 return false;
94 if (!_writeTargetDocument(*mpHandler))
95 return false;
96
97 // clean up the mess we made
98 WRITER_DEBUG_MSG(("WriterWordPerfect: Cleaning up our mess..\n"));
99
100 WRITER_DEBUG_MSG(("Destroying the body elements\n"));
101 for (std::vector<DocumentElement *>::iterator iterBody = mBodyElements.begin(); iterBody != mBodyElements.end(); iterBody++) {
102 delete((*iterBody));
103 (*iterBody) = NULL;
104 }
105
106 WRITER_DEBUG_MSG(("Destroying the styles elements\n"));
107 for (std::vector<DocumentElement *>::iterator iterStyles = mStylesElements.begin(); iterStyles != mStylesElements.end(); iterStyles++) {
108 delete (*iterStyles);
109 (*iterStyles) = NULL; // we may pass over the same element again (in the case of headers/footers spanning multiple pages)
110 // so make sure we don't do a double del
111 }
112
113 WRITER_DEBUG_MSG(("Destroying the rest of the styles elements\n"));
114 for (std::map<_SH_String, ParagraphStyle *, ltstr>::iterator iterTextStyle = mTextStyleHash.begin(); iterTextStyle != mTextStyleHash.end(); iterTextStyle++) {
115 delete(iterTextStyle->second);
116 }
117 for (std::map<_SH_String, FontStyle *, ltstr>::iterator iterFont = mFontHash.begin(); iterFont != mFontHash.end(); iterFont++) {
118 delete(iterFont->second);
119 }
120
121 for (std::vector<ListStyle *>::iterator iterListStyles = mListStyles.begin(); iterListStyles != mListStyles.end(); iterListStyles++) {
122 delete((*iterListStyles));
123 }
124 for (std::vector<SectionStyle *>::iterator iterSectionStyles = mSectionStyles.begin(); iterSectionStyles != mSectionStyles.end(); iterSectionStyles++) {
125 delete((*iterSectionStyles));
126 }
127 for (std::vector<TableStyle *>::iterator iterTableStyles = mTableStyles.begin(); iterTableStyles != mTableStyles.end(); iterTableStyles++) {
128 delete((*iterTableStyles));
129 }
130
131 for (std::vector<PageSpan *>::iterator iterPageSpans = mPageSpans.begin(); iterPageSpans != mPageSpans.end(); iterPageSpans++) {
132 delete((*iterPageSpans));
133 }
134
135 return true;
136}
137
138bool WordPerfectCollector::_parseSourceDocument(_SH_InputStream &input)
139{
140#if defined(HAVE_LIBWPD_0100)
141 libwpd::WPDResult result = libwpd::WPDocument::parse(&input, static_cast<_SH_DocumentInterface*>(this), NULL);
142#elif defined(HAVE_LIBWPD_090)
143 WPDResult result = WPDocument::parse(&input, static_cast<_SH_DocumentInterface*>(this), NULL);
144#else
145 WPDResult result = WPDocument::parse(&input, static_cast<_SH_DocumentInterface*>(this));
146#endif
147#if defined(HAVE_LIBWPD_0100)
148 if (result != libwpd::WPD_OK)
149#else
150 if (result != WPD_OK)
151#endif
152 return false;
153
154 return true;
155}
156
157void WordPerfectCollector::_writeDefaultStyles(DocumentHandler &xHandler)
158{
159 TagOpenElement stylesOpenElement("office:styles");
160 stylesOpenElement.write(xHandler);
161
162 TagOpenElement defaultParagraphStyleOpenElement("style:default-style");
163 defaultParagraphStyleOpenElement.addAttribute("style:family", "paragraph");
164 defaultParagraphStyleOpenElement.write(xHandler);
165
166 TagOpenElement defaultParagraphStylePropertiesOpenElement("style:properties");
167 defaultParagraphStylePropertiesOpenElement.addAttribute("style:family", "paragraph");
168 defaultParagraphStylePropertiesOpenElement.addAttribute("style:tab-stop-distance", "0.5inch");
169 defaultParagraphStylePropertiesOpenElement.write(xHandler);
170 TagCloseElement defaultParagraphStylePropertiesCloseElement("style:properties");
171 defaultParagraphStylePropertiesCloseElement.write(xHandler);
172
173 TagCloseElement defaultParagraphStyleCloseElement("style:default-style");
174 defaultParagraphStyleCloseElement.write(xHandler);
175
176 TagOpenElement standardStyleOpenElement("style:style");
177 standardStyleOpenElement.addAttribute("style:name", "Standard");
178 standardStyleOpenElement.addAttribute("style:family", "paragraph");
179 standardStyleOpenElement.addAttribute("style:class", "text");
180 standardStyleOpenElement.write(xHandler);
181 TagCloseElement standardStyleCloseElement("style:style");
182 standardStyleCloseElement.write(xHandler);
183
184 TagOpenElement textBodyStyleOpenElement("style:style");
185 textBodyStyleOpenElement.addAttribute("style:name", "Text Body");
186 textBodyStyleOpenElement.addAttribute("style:family", "paragraph");
187 textBodyStyleOpenElement.addAttribute("style:parent-style-name", "Standard");
188 textBodyStyleOpenElement.addAttribute("style:class", "text");
189 textBodyStyleOpenElement.write(xHandler);
190 TagCloseElement textBodyStyleCloseElement("style:style");
191 textBodyStyleCloseElement.write(xHandler);
192
193 TagOpenElement tableContentsStyleOpenElement("style:style");
194 tableContentsStyleOpenElement.addAttribute("style:name", "Table Contents");
195 tableContentsStyleOpenElement.addAttribute("style:family", "paragraph");
196 tableContentsStyleOpenElement.addAttribute("style:parent-style-name", "Text Body");
197 tableContentsStyleOpenElement.addAttribute("style:class", "extra");
198 tableContentsStyleOpenElement.write(xHandler);
199 TagCloseElement tableContentsStyleCloseElement("style:style");
200 tableContentsStyleCloseElement.write(xHandler);
201
202 TagOpenElement tableHeadingStyleOpenElement("style:style");
203 tableHeadingStyleOpenElement.addAttribute("style:name", "Table Heading");
204 tableHeadingStyleOpenElement.addAttribute("style:family", "paragraph");
205 tableHeadingStyleOpenElement.addAttribute("style:parent-style-name", "Table Contents");
206 tableHeadingStyleOpenElement.addAttribute("style:class", "extra");
207 tableHeadingStyleOpenElement.write(xHandler);
208 TagCloseElement tableHeadingStyleCloseElement("style:style");
209 tableHeadingStyleCloseElement.write(xHandler);
210
211 TagCloseElement stylesCloseElement("office:styles");
212 stylesCloseElement.write(xHandler);
213
214}
215
216// writes everything up to the automatic styles declarations..
217void WordPerfectCollector::_writeBegin()
218{
219}
220
221void WordPerfectCollector::_writeMasterPages(DocumentHandler &xHandler)
222{
223 _SH_PropertyList xBlankAttrList;
224
225 xHandler.startElement("office:master-styles", xBlankAttrList);
226 int pageNumber = 1;
227 for (int i=0; i<mPageSpans.size(); i++)
228 {
229 bool bLastPage;
230 (i == (mPageSpans.size() - 1)) ? bLastPage = true : bLastPage = false;
231 mPageSpans[i]->writeMasterPages(pageNumber, i, bLastPage, xHandler);
232 pageNumber += mPageSpans[i]->getSpan();
233 }
234 xHandler.endElement("office:master-styles");
235}
236
237void WordPerfectCollector::_writePageMasters(DocumentHandler &xHandler)
238{
239 int pageNumber = 1;
240 for (int i=0; i<mPageSpans.size(); i++)
241 {
242 mPageSpans[i]->writePageMaster(i, xHandler);
243 }
244}
245
246bool WordPerfectCollector::_writeTargetDocument(DocumentHandler &xHandler)
247{
248 WRITER_DEBUG_MSG(("WriterWordPerfect: Document Body: Printing out the header stuff..\n"));
249 _SH_PropertyList xBlankAttrList;
250
251 WRITER_DEBUG_MSG(("WriterWordPerfect: Document Body: Start Document\n"));
252 mpHandler->startDocument();
253
254 WRITER_DEBUG_MSG(("WriterWordPerfect: Document Body: preamble\n"));
255 _SH_PropertyList docContentPropList;
256 docContentPropList.insert("xmlns:office", "http://openoffice.org/2000/office");
257 docContentPropList.insert("xmlns:style", "http://openoffice.org/2000/style");
258 docContentPropList.insert("xmlns:text", "http://openoffice.org/2000/text");
259 docContentPropList.insert("xmlns:table", "http://openoffice.org/2000/table");
260 docContentPropList.insert("xmlns:draw", "http://openoffice.org/2000/draw");
261 docContentPropList.insert("xmlns:fo", "http://www.w3.org/1999/XSL/Format");
262 docContentPropList.insert("xmlns:xlink", "http://www.w3.org/1999/xlink");
263 docContentPropList.insert("xmlns:number", "http://openoffice.org/2000/datastyle");
264 docContentPropList.insert("xmlns:svg", "http://www.w3.org/2000/svg");
265 docContentPropList.insert("xmlns:chart", "http://openoffice.org/2000/chart");
266 docContentPropList.insert("xmlns:dr3d", "http://openoffice.org/2000/dr3d");
267 docContentPropList.insert("xmlns:math", "http://www.w3.org/1998/Math/MathML");
268 docContentPropList.insert("xmlns:form", "http://openoffice.org/2000/form");
269 docContentPropList.insert("xmlns:script", "http://openoffice.org/2000/script");
270 docContentPropList.insert("office:class", "text");
271 docContentPropList.insert("office:version", "1.0");
272 mpHandler->startElement("office:document-content", docContentPropList);
273
274 // write out the font styles
275 mpHandler->startElement("office:font-decls", xBlankAttrList);
276 for (std::map<_SH_String, FontStyle *, ltstr>::iterator iterFont = mFontHash.begin(); iterFont != mFontHash.end(); iterFont++) {
277 iterFont->second->write(*mpHandler);
278 }
279 TagOpenElement symbolFontOpen("style:font-decl");
280 symbolFontOpen.addAttribute("style:name", "StarSymbol");
281 symbolFontOpen.addAttribute("fo:font-family", "StarSymbol");
282 symbolFontOpen.addAttribute("style:font-charset", "x-symbol");
283 symbolFontOpen.write(*mpHandler);
284 mpHandler->endElement("style:font-decl");
285
286 mpHandler->endElement("office:font-decls");
287
288
289 WRITER_DEBUG_MSG(("WriterWordPerfect: Document Body: Writing out the styles..\n"));
290
291 // write default styles
292 _writeDefaultStyles(*mpHandler);
293
294 mpHandler->startElement("office:automatic-styles", xBlankAttrList);
295
296 for (std::map<_SH_String, ParagraphStyle *, ltstr>::iterator iterTextStyle = mTextStyleHash.begin();
297 iterTextStyle != mTextStyleHash.end(); iterTextStyle++)
298 {
299 // writing out the paragraph styles
300 if (strcmp((iterTextStyle->second)->getName().cstr(), "Standard"))
301 {
302 // don't write standard paragraph "no styles" style
303 (iterTextStyle->second)->write(xHandler);
304 }
305 }
306
307 // span styles..
308 for (std::map<_SH_String, SpanStyle *, ltstr>::iterator iterSpanStyle = mSpanStyleHash.begin();
309 iterSpanStyle != mSpanStyleHash.end(); iterSpanStyle++)
310 {
311 (iterSpanStyle->second)->write(xHandler);
312 }
313
314 // writing out the sections styles
315 for (std::vector<SectionStyle *>::iterator iterSectionStyles = mSectionStyles.begin(); iterSectionStyles != mSectionStyles.end(); iterSectionStyles++) {
316 (*iterSectionStyles)->write(xHandler);
317 }
318
319 // writing out the lists styles
320 for (std::vector<ListStyle *>::iterator iterListStyles = mListStyles.begin(); iterListStyles != mListStyles.end(); iterListStyles++) {
321 (*iterListStyles)->write(xHandler);
322 }
323
324 // writing out the table styles
325 for (std::vector<TableStyle *>::iterator iterTableStyles = mTableStyles.begin(); iterTableStyles != mTableStyles.end(); iterTableStyles++) {
326 (*iterTableStyles)->write(xHandler);
327 }
328
329 // writing out the page masters
330 _writePageMasters(xHandler);
331
332
333 xHandler.endElement("office:automatic-styles");
334
335 _writeMasterPages(xHandler);
336
337 WRITER_DEBUG_MSG(("WriterWordPerfect: Document Body: Writing out the document..\n"));
338 // writing out the document
339 xHandler.startElement("office:body", xBlankAttrList);
340
341 for (std::vector<DocumentElement *>::iterator iterBodyElements = mBodyElements.begin(); iterBodyElements != mBodyElements.end(); iterBodyElements++) {
342 (*iterBodyElements)->write(xHandler);
343 }
344 WRITER_DEBUG_MSG(("WriterWordPerfect: Document Body: Finished writing all doc els..\n"));
345
346 xHandler.endElement("office:body");
347 xHandler.endElement("office:document-content");
348
349 xHandler.endDocument();
350
351 return true;
352}
353
354
355_SH_String propListToStyleKey(const _SH_PropertyList & xPropList)
356{
357 _SH_String sKey;
358 _SH_PropertyList::Iter i(xPropList);
359 for (i.rewind(); i.next(); )
360 {
361 _SH_String sProp;
362 sProp.sprintf("[%s:%s]", i.key(), i()->getStr().cstr());
363 sKey.append(sProp);
364 }
365
366 return sKey;
367}
368
369_SH_String getParagraphStyleKey(const _SH_PropertyList & xPropList, const _SH_PropertyListVector & xTabStops)
370{
371 _SH_String sKey = propListToStyleKey(xPropList);
372
373 _SH_String sTabStops;
374 sTabStops.sprintf("[num-tab-stops:%i]", xTabStops.count());
375 _SH_PropertyListVector::Iter i(xTabStops);
376 for (i.rewind(); i.next();)
377 {
378 sTabStops.append(propListToStyleKey(i()));
379 }
380 sKey.append(sTabStops);
381
382 return sKey;
383}
384
385// _allocateFontName: add a (potentially mapped) font style to the hash if it's not already there, do nothing otherwise
386void WordPerfectCollector::_allocateFontName(const _SH_String & sFontName)
387{
388 if (mFontHash.find(sFontName) == mFontHash.end())
389 {
390 FontStyle *pFontStyle = new FontStyle(sFontName.cstr(), sFontName.cstr());
391 mFontHash[sFontName] = pFontStyle;
392 }
393}
394
395void WordPerfectCollector::openPageSpan(const _SH_PropertyList &propList)
396{
397 PageSpan *pPageSpan = new PageSpan(propList);
398 mPageSpans.push_back(pPageSpan);
399 mpCurrentPageSpan = pPageSpan;
400}
401
402void WordPerfectCollector::openHeader(const _SH_PropertyList &propList)
403{
404 std::vector<DocumentElement *> * pHeaderFooterContentElements = new std::vector<DocumentElement *>;
405
406 if (propList["libwpd:occurence"]->getStr() == "even")
407 mpCurrentPageSpan->setHeaderLeftContent(pHeaderFooterContentElements);
408 else
409 mpCurrentPageSpan->setHeaderContent(pHeaderFooterContentElements);
410
411 mpCurrentContentElements = pHeaderFooterContentElements;
412}
413
414void WordPerfectCollector::closeHeader()
415{
416 mpCurrentContentElements = &mBodyElements;
417}
418
419void WordPerfectCollector::openFooter(const _SH_PropertyList &propList)
420{
421 std::vector<DocumentElement *> * pHeaderFooterContentElements = new std::vector<DocumentElement *>;
422
423 if (propList["libwpd:occurence"]->getStr() == "even")
424 mpCurrentPageSpan->setFooterLeftContent(pHeaderFooterContentElements);
425 else
426 mpCurrentPageSpan->setFooterContent(pHeaderFooterContentElements);
427
428 mpCurrentContentElements = pHeaderFooterContentElements;
429}
430
431void WordPerfectCollector::closeFooter()
432{
433 mpCurrentContentElements = &mBodyElements;
434}
435
436void WordPerfectCollector::openSection(const _SH_PropertyList &propList, const _SH_PropertyListVector &columns)
437{
438 int iNumColumns = columns.count();
439
440 if (iNumColumns > 1)
441 {
442#if defined(HAVE_LIBWPD_090) || defined(HAVE_LIBWPD_0100)
443 mfSectionSpaceAfter = propList["fo:margin-bottom"]->getDouble();
444#else
445 mfSectionSpaceAfter = propList["fo:margin-bottom"]->getFloat();
446#endif
447 _SH_String sSectionName;
448 sSectionName.sprintf("Section%i", mSectionStyles.size());
449
450 SectionStyle *pSectionStyle = new SectionStyle(propList, columns, sSectionName.cstr());
451 mSectionStyles.push_back(pSectionStyle);
452
453 TagOpenElement *pSectionOpenElement = new TagOpenElement("text:section");
454 pSectionOpenElement->addAttribute("text:style-name", pSectionStyle->getName());
455 pSectionOpenElement->addAttribute("text:name", pSectionStyle->getName());
456 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pSectionOpenElement));
457 }
458 else
459 mWriterDocumentState.mbInFakeSection = true;
460}
461
462void WordPerfectCollector::closeSection()
463{
464 if (!mWriterDocumentState.mbInFakeSection)
465 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:section")));
466 else
467 mWriterDocumentState.mbInFakeSection = false;
468
469 // open as many paragraphs as needed to simulate section space after
470 // WLACH_REFACTORING: disable this for now..
471 #if 0
472 for (float f=0.0f; f<mfSectionSpaceAfter; f+=1.0f) {
473 vector<WPXTabStop> dummyTabStops;
474 openParagraph(WPX_PARAGRAPH_JUSTIFICATION_LEFT, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, dummyTabStops, false, false);
475 closeParagraph();
476 }
477 #endif
478 mfSectionSpaceAfter = 0.0f;
479}
480
481void WordPerfectCollector::openParagraph(const _SH_PropertyList &propList, const _SH_PropertyListVector &tabStops)
482{
483 // FIXMENOW: What happens if we open a footnote inside a table? do we then inherit the footnote's style
484 // from "Table Contents"
485
486 _SH_PropertyList *pPersistPropList = new _SH_PropertyList(propList);
487 ParagraphStyle *pStyle = NULL;
488
489 if (mWriterDocumentState.mbFirstElement && mpCurrentContentElements == &mBodyElements)
490 {
491 // we don't have to go through the fuss of determining if the paragraph style is
492 // unique in this case, because if we are the first document element, then we
493 // are singular. Neither do we have to determine what our parent style is-- we can't
494 // be inside a table in this case (the table would be the first document element
495 //in that case)
496 pPersistPropList->insert("style:parent-style-name", "Standard");
497 _SH_String sName;
498 sName.sprintf("FS");
499
500 _SH_String sParagraphHashKey("P|FS");
501 pPersistPropList->insert("style:master-page-name", "Page Style 1");
502 pStyle = new ParagraphStyle(pPersistPropList, tabStops, sName);
503 mTextStyleHash[sParagraphHashKey] = pStyle;
504 mWriterDocumentState.mbFirstElement = false;
505 }
506 else
507 {
508 if (mWriterDocumentState.mbTableCellOpened)
509 {
510 if (mWriterDocumentState.mbHeaderRow)
511 pPersistPropList->insert("style:parent-style-name", "Table Heading");
512 else
513 pPersistPropList->insert("style:parent-style-name", "Table Contents");
514 }
515 else
516 pPersistPropList->insert("style:parent-style-name", "Standard");
517
518 _SH_String sKey = getParagraphStyleKey(*pPersistPropList, tabStops);
519
520 if (mTextStyleHash.find(sKey) == mTextStyleHash.end()) {
521 _SH_String sName;
522 sName.sprintf("S%i", mTextStyleHash.size());
523
524 pStyle = new ParagraphStyle(pPersistPropList, tabStops, sName);
525
526 mTextStyleHash[sKey] = pStyle;
527 }
528 else
529 {
530 pStyle = mTextStyleHash[sKey];
531 delete pPersistPropList;
532 }
533 }
534 // create a document element corresponding to the paragraph, and append it to our list of document elements
535 TagOpenElement *pParagraphOpenElement = new TagOpenElement("text:p");
536 pParagraphOpenElement->addAttribute("text:style-name", pStyle->getName());
537 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pParagraphOpenElement));
538}
539
540void WordPerfectCollector::closeParagraph()
541{
542 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:p")));
543}
544
545void WordPerfectCollector::openSpan(const _SH_PropertyList &propList)
546{
547 if (propList["style:font-name"])
548 _allocateFontName(propList["style:font-name"]->getStr());
549 _SH_String sSpanHashKey = propListToStyleKey(propList);
550 WRITER_DEBUG_MSG(("WriterWordPerfect: Span Hash Key: %s\n", sSpanHashKey.cstr()));
551
552 // Get the style
553 _SH_String sName;
554 if (mSpanStyleHash.find(sSpanHashKey) == mSpanStyleHash.end())
555 {
556 // allocate a new paragraph style
557 sName.sprintf("Span%i", mSpanStyleHash.size());
558 SpanStyle *pStyle = new SpanStyle(sName.cstr(), propList);
559
560 mSpanStyleHash[sSpanHashKey] = pStyle;
561 }
562 else
563 {
564 sName.sprintf("%s", mSpanStyleHash.find(sSpanHashKey)->second->getName().cstr());
565 }
566
567 // create a document element corresponding to the paragraph, and append it to our list of document elements
568 TagOpenElement *pSpanOpenElement = new TagOpenElement("text:span");
569 pSpanOpenElement->addAttribute("text:style-name", sName.cstr());
570 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pSpanOpenElement));
571}
572
573void WordPerfectCollector::closeSpan()
574{
575 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:span")));
576}
577
578void WordPerfectCollector::defineOrderedListLevel(const _SH_PropertyList &propList)
579{
580 int id = 0;
581 if (propList["libwpd:id"])
582 id = propList["libwpd:id"]->getInt();
583
584 OrderedListStyle *pOrderedListStyle = NULL;
585 if (mpCurrentListStyle && mpCurrentListStyle->getListID() == id)
586 pOrderedListStyle = static_cast<OrderedListStyle *>(mpCurrentListStyle); // FIXME: using a dynamic cast here causes oo to crash?!
587
588 // this rather appalling conditional makes sure we only start a new list (rather than continue an old
589 // one) if: (1) we have no prior list OR (2) the prior list is actually definitively different
590 // from the list that is just being defined (listIDs differ) OR (3) we can tell that the user actually
591 // is starting a new list at level 1 (and only level 1)
592 if (pOrderedListStyle == NULL || pOrderedListStyle->getListID() != id ||
593 (propList["libwpd:level"] && propList["libwpd:level"]->getInt()==1 &&
594 (propList["text:start-value"] && propList["text:start-value"]->getInt() != (miLastListNumber+1))))
595 {
596 WRITER_DEBUG_MSG(("Attempting to create a new ordered list style (listid: %i)\n", id));
597 _SH_String sName;
598 sName.sprintf("OL%i", miNumListStyles);
599 miNumListStyles++;
600 pOrderedListStyle = new OrderedListStyle(sName.cstr(), propList["libwpd:id"]->getInt());
601 mListStyles.push_back(static_cast<ListStyle *>(pOrderedListStyle));
602 mpCurrentListStyle = static_cast<ListStyle *>(pOrderedListStyle);
603 mbListContinueNumbering = false;
604 miLastListNumber = 0;
605 }
606 else
607 mbListContinueNumbering = true;
608
609 // Iterate through ALL list styles with the same WordPerfect list id and define a level if it is not already defined
610 // This solves certain problems with lists that start and finish without reaching certain levels and then begin again
611 // and reach those levels. See gradguide0405_PC.wpd in the regression suite
612 for (std::vector<ListStyle *>::iterator iterOrderedListStyles = mListStyles.begin(); iterOrderedListStyles != mListStyles.end(); iterOrderedListStyles++)
613 {
614 if ((* iterOrderedListStyles)->getListID() == propList["libwpd:id"]->getInt())
615 (* iterOrderedListStyles)->updateListLevel((propList["libwpd:level"]->getInt() - 1), propList);
616 }
617}
618
619void WordPerfectCollector::defineUnorderedListLevel(const _SH_PropertyList &propList)
620{
621 int id = 0;
622 if (propList["libwpd:id"])
623 id = propList["libwpd:id"]->getInt();
624
625 UnorderedListStyle *pUnorderedListStyle = NULL;
626 if (mpCurrentListStyle && mpCurrentListStyle->getListID() == id)
627 pUnorderedListStyle = static_cast<UnorderedListStyle *>(mpCurrentListStyle); // FIXME: using a dynamic cast here causes oo to crash?!
628
629 if (pUnorderedListStyle == NULL) {
630 WRITER_DEBUG_MSG(("Attempting to create a new unordered list style (listid: %i)\n", id));
631 _SH_String sName;
632 sName.sprintf("UL%i", miNumListStyles);
633 pUnorderedListStyle = new UnorderedListStyle(sName.cstr(), id);
634 mListStyles.push_back(static_cast<ListStyle *>(pUnorderedListStyle));
635 mpCurrentListStyle = static_cast<ListStyle *>(pUnorderedListStyle);
636 }
637
638 // See comment in WordPerfectCollector::defineOrderedListLevel
639 for (std::vector<ListStyle *>::iterator iterUnorderedListStyles = mListStyles.begin(); iterUnorderedListStyles != mListStyles.end(); iterUnorderedListStyles++)
640 {
641 if ((* iterUnorderedListStyles)->getListID() == propList["libwpd:id"]->getInt())
642 (* iterUnorderedListStyles)->updateListLevel((propList["libwpd:level"]->getInt() - 1), propList);
643 }
644}
645
646void WordPerfectCollector::openOrderedListLevel(const _SH_PropertyList &propList)
647{
648 miCurrentListLevel++;
649 TagOpenElement *pListLevelOpenElement = new TagOpenElement("text:ordered-list");
650 _openListLevel(pListLevelOpenElement);
651
652 if (mbListContinueNumbering) {
653 pListLevelOpenElement->addAttribute("text:continue-numbering", "true");
654 }
655
656 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pListLevelOpenElement));
657}
658
659void WordPerfectCollector::openUnorderedListLevel(const _SH_PropertyList &propList)
660{
661 miCurrentListLevel++;
662 TagOpenElement *pListLevelOpenElement = new TagOpenElement("text:unordered-list");
663 _openListLevel(pListLevelOpenElement);
664
665 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pListLevelOpenElement));
666}
667
668void WordPerfectCollector::_openListLevel(TagOpenElement *pListLevelOpenElement)
669{
670 if (!mbListElementOpened && miCurrentListLevel > 1)
671 {
672 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:list-item")));
673 }
674 else if (mbListElementParagraphOpened)
675 {
676 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:p")));
677 mbListElementParagraphOpened = false;
678 }
679
680 if (miCurrentListLevel==1) {
681 pListLevelOpenElement->addAttribute("text:style-name", mpCurrentListStyle->getName());
682 }
683
684 mbListElementOpened = false;
685}
686
687void WordPerfectCollector::closeOrderedListLevel()
688{
689 _closeListLevel("ordered-list");
690}
691
692void WordPerfectCollector::closeUnorderedListLevel()
693{
694 _closeListLevel("unordered-list");
695}
696
697void WordPerfectCollector::_closeListLevel(const char *szListType)
698{
699 if (mbListElementOpened)
700 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:list-item")));
701
702 miCurrentListLevel--;
703
704 _SH_String sCloseElement;
705 sCloseElement.sprintf("text:%s", szListType);
706 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement(sCloseElement.cstr())));
707
708 if (miCurrentListLevel > 0)
709 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:list-item")));
710 mbListElementOpened = false;
711}
712
713void WordPerfectCollector::openListElement(const _SH_PropertyList &propList, const _SH_PropertyListVector &tabStops)
714{
715 miLastListLevel = miCurrentListLevel;
716 if (miCurrentListLevel == 1)
717 miLastListNumber++;
718
719 if (mbListElementOpened)
720 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:list-item")));
721
722 ParagraphStyle *pStyle = NULL;
723
724 _SH_PropertyList *pPersistPropList = new _SH_PropertyList(propList);
725 pPersistPropList->insert("style:list-style-name", mpCurrentListStyle->getName());
726 pPersistPropList->insert("style:parent-style-name", "Standard");
727
728 _SH_String sKey = getParagraphStyleKey(*pPersistPropList, tabStops);
729
730 if (mTextStyleHash.find(sKey) == mTextStyleHash.end())
731 {
732 _SH_String sName;
733 sName.sprintf("S%i", mTextStyleHash.size());
734
735 pStyle = new ParagraphStyle(pPersistPropList, tabStops, sName);
736
737 mTextStyleHash[sKey] = pStyle;
738 }
739 else
740 {
741 pStyle = mTextStyleHash[sKey];
742 delete pPersistPropList;
743 }
744
745 TagOpenElement *pOpenListElement = new TagOpenElement("text:list-item");
746 TagOpenElement *pOpenListElementParagraph = new TagOpenElement("text:p");
747
748 pOpenListElementParagraph->addAttribute("text:style-name", pStyle->getName());
749
750 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pOpenListElement));
751 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pOpenListElementParagraph));
752
753 mbListElementOpened = true;
754 mbListElementParagraphOpened = true;
755 mbListContinueNumbering = false;
756}
757
758void WordPerfectCollector::closeListElement()
759{
760 // this code is kind of tricky, because we don't actually close the list element (because this list element
761 // could contain another list level in OOo's implementation of lists). that is done in the closeListLevel
762 // code (or when we open another list element)
763
764 if (mbListElementParagraphOpened)
765 {
766 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:p")));
767 mbListElementParagraphOpened = false;
768 }
769}
770
771void WordPerfectCollector::openFootnote(const _SH_PropertyList &propList)
772{
773 TagOpenElement *pOpenFootNote = new TagOpenElement("text:footnote");
774 if (propList["libwpd:number"])
775 {
776 _SH_String tmpString("ftn");
777 tmpString.append(propList["libwpd:number"]->getStr());
778 pOpenFootNote->addAttribute("text:id", tmpString);
779 }
780 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pOpenFootNote));
781
782 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:footnote-citation")));
783 if (propList["libwpd:number"])
784 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new CharDataElement(propList["libwpd:number"]->getStr().cstr())));
785 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:footnote-citation")));
786
787 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:footnote-body")));
788
789 mWriterDocumentState.mbInNote = true;
790}
791
792void WordPerfectCollector::closeFootnote()
793{
794 mWriterDocumentState.mbInNote = false;
795
796 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:footnote-body")));
797 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:footnote")));
798}
799
800void WordPerfectCollector::openEndnote(const _SH_PropertyList &propList)
801{
802 TagOpenElement *pOpenEndNote = new TagOpenElement("text:endnote");
803 if (propList["libwpd:number"])
804 {
805 _SH_String tmpString("edn");
806 tmpString.append(propList["libwpd:number"]->getStr());
807 pOpenEndNote->addAttribute("text:id", tmpString);
808 }
809 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pOpenEndNote));
810
811 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:endnote-citation")));
812 if (propList["libwpd:number"])
813 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new CharDataElement(propList["libwpd:number"]->getStr().cstr())));
814 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:endnote-citation")));
815
816 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:endnote-body")));
817
818 mWriterDocumentState.mbInNote = true;
819}
820
821void WordPerfectCollector::closeEndnote()
822{
823 mWriterDocumentState.mbInNote = false;
824
825 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:endnote-body")));
826 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:endnote")));
827}
828
829void WordPerfectCollector::openTable(const _SH_PropertyList &propList, const _SH_PropertyListVector &columns)
830{
831 if (!mWriterDocumentState.mbInNote)
832 {
833 _SH_String sTableName;
834 sTableName.sprintf("Table%i", mTableStyles.size());
835
836 // FIXME: we base the table style off of the page's margin left, ignoring (potential) wordperfect margin
837 // state which is transmitted inside the page. could this lead to unacceptable behaviour?
838 // WLACH_REFACTORING: characterize this behaviour, probably should nip it at the bud within libwpd
839 TableStyle *pTableStyle = new TableStyle(propList, columns, sTableName.cstr());
840
841 if (mWriterDocumentState.mbFirstElement && mpCurrentContentElements == &mBodyElements)
842 {
843 _SH_String sMasterPageName("Page Style 1");
844 pTableStyle->setMasterPageName(sMasterPageName);
845 mWriterDocumentState.mbFirstElement = false;
846 }
847
848 mTableStyles.push_back(pTableStyle);
849
850 mpCurrentTableStyle = pTableStyle;
851
852 TagOpenElement *pTableOpenElement = new TagOpenElement("table:table");
853
854 pTableOpenElement->addAttribute("table:name", sTableName.cstr());
855 pTableOpenElement->addAttribute("table:style-name", sTableName.cstr());
856 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pTableOpenElement));
857
858 for (int i=0; i<pTableStyle->getNumColumns(); i++)
859 {
860 TagOpenElement *pTableColumnOpenElement = new TagOpenElement("table:table-column");
861 _SH_String sColumnStyleName;
862 sColumnStyleName.sprintf("%s.Column%i", sTableName.cstr(), (i+1));
863 pTableColumnOpenElement->addAttribute("table:style-name", sColumnStyleName.cstr());
864 mpCurrentContentElements->push_back(pTableColumnOpenElement);
865
866 TagCloseElement *pTableColumnCloseElement = new TagCloseElement("table:table-column");
867 mpCurrentContentElements->push_back(pTableColumnCloseElement);
868 }
869 }
870}
871
872void WordPerfectCollector::openTableRow(const _SH_PropertyList &propList)
873{
874 if (!mWriterDocumentState.mbInNote)
875 {
876 if (propList["libwpd:is-header-row"] && (propList["libwpd:is-header-row"]->getInt()))
877 {
878 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("table:table-header-rows")));
879 mWriterDocumentState.mbHeaderRow = true;
880 }
881
882 _SH_String sTableRowStyleName;
883 sTableRowStyleName.sprintf("%s.Row%i", mpCurrentTableStyle->getName().cstr(), mpCurrentTableStyle->getNumTableRowStyles());
884 TableRowStyle *pTableRowStyle = new TableRowStyle(propList, sTableRowStyleName.cstr());
885 mpCurrentTableStyle->addTableRowStyle(pTableRowStyle);
886
887 TagOpenElement *pTableRowOpenElement = new TagOpenElement("table:table-row");
888 pTableRowOpenElement->addAttribute("table:style-name", sTableRowStyleName);
889 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pTableRowOpenElement));
890 }
891}
892
893void WordPerfectCollector::closeTableRow()
894{
895 if (!mWriterDocumentState.mbInNote)
896 {
897 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("table:table-row")));
898 if (mWriterDocumentState.mbHeaderRow)
899 {
900 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("table:table-header-rows")));
901 mWriterDocumentState.mbHeaderRow = false;
902 }
903 }
904}
905
906void WordPerfectCollector::openTableCell(const _SH_PropertyList &propList)
907{
908 if (!mWriterDocumentState.mbInNote)
909 {
910 _SH_String sTableCellStyleName;
911 sTableCellStyleName.sprintf( "%s.Cell%i", mpCurrentTableStyle->getName().cstr(), mpCurrentTableStyle->getNumTableCellStyles());
912 TableCellStyle *pTableCellStyle = new TableCellStyle(propList, sTableCellStyleName.cstr());
913 mpCurrentTableStyle->addTableCellStyle(pTableCellStyle);
914
915 TagOpenElement *pTableCellOpenElement = new TagOpenElement("table:table-cell");
916 pTableCellOpenElement->addAttribute("table:style-name", sTableCellStyleName);
917 if (propList["table:number-columns-spanned"])
918 pTableCellOpenElement->addAttribute("table:number-columns-spanned",
919 propList["table:number-columns-spanned"]->getStr().cstr());
920 if (propList["table:number-rows-spanned"])
921 pTableCellOpenElement->addAttribute("table:number-rows-spanned",
922 propList["table:number-rows-spanned"]->getStr().cstr());
923 pTableCellOpenElement->addAttribute("table:value-type", "string");
924 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(pTableCellOpenElement));
925
926 mWriterDocumentState.mbTableCellOpened = true;
927 }
928}
929
930void WordPerfectCollector::closeTableCell()
931{
932 if (!mWriterDocumentState.mbInNote)
933 {
934 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("table:table-cell")));
935 mWriterDocumentState.mbTableCellOpened = false;
936 }
937}
938
939void WordPerfectCollector::insertCoveredTableCell(const _SH_PropertyList &propList)
940{
941 if (!mWriterDocumentState.mbInNote)
942 {
943 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("table:covered-table-cell")));
944 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("table:covered-table-cell")));
945 }
946}
947
948void WordPerfectCollector::closeTable()
949{
950 if (!mWriterDocumentState.mbInNote)
951 {
952 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("table:table")));
953 }
954}
955
956
957void WordPerfectCollector::insertTab()
958{
959 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:tab-stop")));
960 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:tab-stop")));
961}
962
963void WordPerfectCollector::insertLineBreak()
964{
965 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagOpenElement("text:line-break")));
966 mpCurrentContentElements->push_back(static_cast<DocumentElement *>(new TagCloseElement("text:line-break")));
967}
968
969void WordPerfectCollector::insertText(const _SH_String &text)
970{
971 DocumentElement *pText = new TextElement(text);
972 mpCurrentContentElements->push_back(pText);
973}