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