1 /* AbiWord
2  * Copyright (C) 1998 AbiSource, Inc.
3  * Copyright (C) 2000-2002 Dom Lachowicz
4  * Copyright (C) 2002 Screetch
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301 USA.
20  */
21 
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "ut_string.h"
26 #include "ut_std_string.h"
27 #include "ut_bytebuf.h"
28 #include "ut_base64.h"
29 #include "ut_locale.h"
30 #include "ut_units.h"
31 #include "pt_Types.h"
32 #include "pd_Document.h"
33 #include "ie_impexp_DocBook.h"
34 #include "ie_exp_DocBook.h"
35 #include "pp_AttrProp.h"
36 #include "px_ChangeRecord.h"
37 #include "px_CR_Object.h"
38 #include "px_CR_Span.h"
39 #include "px_CR_Strux.h"
40 #include "xap_App.h"
41 #include "xap_EncodingManager.h"
42 #include "ap_Strings.h"
43 #include "fd_Field.h"
44 
45 #include "ut_path.h"
46 #include "ut_string_class.h"
47 
48 /*****************************************************************/
49 /*****************************************************************/
50 
IE_Exp_DocBook_Sniffer(const char * _name)51 IE_Exp_DocBook_Sniffer::IE_Exp_DocBook_Sniffer (const char * _name) :
52   IE_ExpSniffer(_name)
53 {
54   //
55 }
56 
recognizeSuffix(const char * szSuffix)57 bool IE_Exp_DocBook_Sniffer::recognizeSuffix(const char * szSuffix)
58 {
59 	return (!g_ascii_strcasecmp(szSuffix,".dbk") ||!g_ascii_strcasecmp(szSuffix,".xml"));
60 }
61 
constructExporter(PD_Document * pDocument,IE_Exp ** ppie)62 UT_Error IE_Exp_DocBook_Sniffer::constructExporter(PD_Document * pDocument,
63 						   IE_Exp ** ppie)
64 {
65 	IE_Exp_DocBook * p = new IE_Exp_DocBook(pDocument);
66 	*ppie = p;
67 	return UT_OK;
68 }
69 
getDlgLabels(const char ** pszDesc,const char ** pszSuffixList,IEFileType * ft)70 bool IE_Exp_DocBook_Sniffer::getDlgLabels(const char ** pszDesc,
71 											const char ** pszSuffixList,
72 											IEFileType * ft)
73 {
74 	*pszDesc = "DocBook (.dbk, .xml)";
75 	*pszSuffixList = "*.dbk; *.xml";
76 	*ft = getFileType();
77 	return true;
78 }
79 
80 /*****************************************************************/
81 /*****************************************************************/
82 
IE_Exp_DocBook(PD_Document * pDocument)83 IE_Exp_DocBook::IE_Exp_DocBook(PD_Document * pDocument)
84 	: IE_Exp(pDocument)
85 {
86 	m_error = 0;
87 	s_align = 0;
88 	m_pListener = NULL;
89 }
90 
~IE_Exp_DocBook()91 IE_Exp_DocBook::~IE_Exp_DocBook()
92 {
93 }
94 
indent(void)95 int IE_Exp_DocBook :: indent (void)
96 {
97 	return ++s_align;
98 }
99 
unindent(void)100 int IE_Exp_DocBook :: unindent (void)
101 {
102 	if (--s_align < 0)
103 	{
104 		s_align = 0;
105 		UT_DEBUGMSG(("DocBook: trying to unindent at min indent.\n"));
106 	}
107 	return s_align;
108 }
109 
iwrite(const char * txt)110 void IE_Exp_DocBook :: iwrite (const char *txt)
111 {
112 	if (s_align)
113 	{
114 		char *tmpIndent = new char [s_align + 1];
115 		memset (tmpIndent, '\t', s_align);
116 		tmpIndent [s_align] = '\0';
117 
118 		IE_Exp :: write (tmpIndent);
119 		DELETEPV(tmpIndent);
120 	}
121 
122 	IE_Exp :: write (txt);
123 }
124 
writeln(const char * txt)125 void IE_Exp_DocBook :: writeln (const char *txt)
126 {
127 	iwrite (txt);
128 	IE_Exp :: write ("\n");
129 }
130 
131 /*****************************************************************/
132 /*****************************************************************/
133 
134 /*!
135    removes the suffix from a string by searching backwards for the specified
136    character delimiter. If the delimiter is not found, a copy of the original
137    string is returned
138 
139    eg. _stripSuffix("/home/user/file.png, '.') returns "/home/user/file"
140 	   _stripSuffix("/home/user/foo_bar, '_') returns /home/user/foo
141 	   _stripSuffix("/home/user/file.png, '_') returns /home/user/file.png"
142    TODO: put this in UT_String somehow, it came from ie_exp_HTML.
143 */
_stripSuffix(const char * from,char delimiter)144 static char *_stripSuffix(const char* from, char delimiter)
145 {
146 	char * fremove_s = (char *)g_try_malloc(strlen(from)+1);
147 	strcpy(fremove_s, from);
148 
149 	char * p = fremove_s + strlen(fremove_s);
150 	while ((p >= fremove_s) && (*p != delimiter))
151 		p--;
152 
153 	if (p >= fremove_s)
154 	*p = '\0';
155 
156 	return fremove_s;
157 }
158 
_stripSuffix(const UT_UTF8String & from,char delimiter)159 static char * _stripSuffix(const UT_UTF8String & from, char delimiter)
160 {
161   return _stripSuffix(from.utf8_str(), delimiter);
162 }
163 
164 /*****************************************************************/
165 /*****************************************************************/
166 
167 #define BT_NORMAL		1
168 #define BT_PLAINTEXT	2
169 
_initFile(void)170 bool s_DocBook_Listener::_initFile(void)
171 {
172 	// write out the doctype descriptor
173 	m_pie->writeln("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
174 	m_pie->writeln("<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook XML V4.2//EN\"");
175 	m_pie->writeln("\t\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\">\n");
176 	m_pie->writeln("<!-- ================================================================================ -->");
177 	m_pie->writeln("<!-- This DocBook file was created by AbiWord.                                        -->");
178 	m_pie->writeln("<!-- AbiWord is a free, Open Source word processor.                                   -->");
179 	m_pie->writeln("<!-- You may obtain more information about AbiWord at http://www.abisource.com        -->");
180 	m_pie->writeln("<!-- ================================================================================ -->");
181 	m_pie->write("\n");
182 	_handleDocument();
183 	_handleMetaData();
184 
185 	return true;
186 }
187 
_closeFile(void)188 void s_DocBook_Listener::_closeFile(void)
189 {
190 	if(m_bInTitle && !m_bInSection) //in case the file contains a lone chapter heading
191 	{
192 		_closeChapterTitle();
193 		_tagOpenClose("section",false);
194 		_tagOpenClose("para",false);
195 	}
196 	_closeChapter();  //handles all of the section closing
197 	_handleDataItems();
198 	_tagClose(TT_DOCUMENT,"book");
199 }
200 
_tagClose(UT_uint32 tagID,const UT_UTF8String & content,bool newline,bool indent,bool decrease)201 void s_DocBook_Listener::_tagClose(UT_uint32 tagID, const UT_UTF8String & content, bool newline, bool indent, bool decrease)
202 {
203 	UT_uint32 i = 0;
204 
205 	if(decrease)
206 		m_pie->unindent();
207 
208 	if(indent)
209 		m_pie->iwrite("</");
210 	else
211 		m_pie->write("</");
212 
213 	m_pie->write(content.utf8_str());
214 	m_pie->write(">");
215 
216 	if(newline)
217 		m_pie->write("\n");
218 
219 	m_utnsTagStack.pop((UT_sint32*)&i);
220 	m_iLastClosed = i;
221 	xxx_UT_DEBUGMSG(("Popping %d off of stack\n",i));
222 
223 	if(i != tagID)
224 	{
225 		UT_DEBUGMSG(("DocBook export: possible mismatched tag. Requested: %d, Popped: %d\n",tagID,i));
226 	}
227 }
228 
_tagOpen(UT_uint32 tagID,const UT_UTF8String & content,bool newline,bool indent,bool increase)229 void s_DocBook_Listener::_tagOpen(UT_uint32 tagID, const UT_UTF8String & content, bool newline, bool indent, bool increase)
230 {
231 	if(indent)
232 		m_pie->iwrite("<");
233 	else
234 		m_pie->write("<");
235 
236 	m_pie->write(content.utf8_str());
237 	m_pie->write(">");
238 
239 	if(newline)
240 		m_pie->write("\n");
241 
242 	if(increase)
243 		m_pie->indent();
244 
245 	m_utnsTagStack.push(tagID);
246 	xxx_UT_DEBUGMSG(("Pushing %d onto stack\n",tagID));
247 }
248 
_tagOpenClose(const UT_UTF8String & content,bool suppress,bool newline,bool indent)249 void s_DocBook_Listener::_tagOpenClose(const UT_UTF8String & content, bool suppress, bool newline, bool indent)
250 {
251 	if(indent)
252 		m_pie->iwrite("<");
253 	else
254 		m_pie->write("<");
255 
256 	m_pie->write(content.utf8_str());
257 
258 	if(suppress)
259 		m_pie->write("/>");
260 	else
261 	{
262 		m_pie->write("></");
263 		m_pie->write(content.utf8_str());
264 		m_pie->write(">");
265 	}
266 
267 	if(newline)
268 		m_pie->write("\n");
269 }
270 
_tagTop(void)271 UT_uint32 s_DocBook_Listener::_tagTop(void)
272 {
273 	UT_sint32 i = 0;
274 
275 	if (m_utnsTagStack.viewTop (i))
276 		return (UT_uint32)i;
277 	return 0;
278 }
279 
_closeSection(int sub)280 void s_DocBook_Listener :: _closeSection(int sub)
281 {
282 	_closeParagraph();  // this also prevents section titles from being left open, so keep it above the return
283 
284 	if(_tagTop() == TT_FOOTNOTE)  // triggered by the .doc importer sometimes
285 	{
286 		_tagClose(TT_FOOTNOTE,"footnote",false,false,false);
287 		m_bInNote = false;
288 		_closeParagraph();
289 	}
290 
291 	if((!m_bInSection) || (sub > m_iSectionDepth) || (m_bInTable))
292 		return;
293 
294 	while((sub < m_iSectionDepth) && (m_iSectionDepth > 0))
295 	{
296 		if(_tagTop() == TT_TITLE)
297 			_closeSectionTitle();
298 		if(m_iLastClosed == TT_TITLE)
299 			_tagOpenClose("para",false);  //we can't have empty sections
300 
301 		if(_tagTop() != TT_SECTION)
302 		{
303 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
304 		}
305 
306 		_tagClose(TT_SECTION,"section");
307 		m_iSectionDepth--;
308 	}
309 
310 	if(m_iSectionDepth == 0)
311 		m_bInSection = false;
312 	if(m_bInHdrFtr)
313 		m_bInHdrFtr = false;
314 	m_sLastStyle = "";
315 }
316 
_closeSectionTitle()317 void s_DocBook_Listener :: _closeSectionTitle()
318 {
319 	if(!m_bInTitle)
320 		return;
321 
322 	if(_tagTop() != TT_TITLE)
323 	{
324 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
325 	}
326 
327 	_tagClose(TT_TITLE,"title",true,false);
328 	m_bInTitle = false;
329 }
330 
331 
_closeParagraph(void)332 void s_DocBook_Listener :: _closeParagraph(void)
333 {
334 	if((_tagTop() == TT_FOOTNOTE) || (!m_bInParagraph))
335 		return;
336 
337 	_closeSpan();
338 
339 	if(_tagTop() == TT_LINK)  // don't let links span paragraphs
340 		_tagClose(TT_LINK,"link",false,false,false);
341 	else if(_tagTop() == TT_ULINK)
342 		_tagClose(TT_ULINK,"ulink",false,false,false);
343 
344 	if((m_iBlockType == BT_PLAINTEXT) || (_tagTop() == TT_PLAINTEXT))
345 	{
346 		m_iBlockType = BT_NORMAL;
347 		_tagClose(TT_PLAINTEXT,"literallayout",true,false,false);
348 	}
349 	else if((m_iBlockType == BT_NORMAL) || (_tagTop() == TT_BLOCK))
350 	{
351 		bool deindent = true;
352 		if(m_bInTable)
353 			deindent = false;
354 		else if(m_bInNote)
355 			deindent = false;
356 
357 		_tagClose(TT_BLOCK,"para",((!m_bInTable) && (!m_bInNote)),false,deindent);
358 	}
359 	else
360 	{
361 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
362 	}
363 
364 	if(!m_bInNote)
365 		m_bInParagraph = false;
366 }
367 
_closeSpan(void)368 void s_DocBook_Listener :: _closeSpan(void)
369 {
370 	if(!m_bInSpan)
371 		return;
372 
373 	const PP_AttrProp * pAP = m_pAP_Span;
374 
375 	if (pAP)
376 	{
377 		const gchar * szValue = 0;
378 
379 		if (pAP->getProperty(static_cast<const gchar *>("text-position"), szValue))
380 		{
381 			if (!strcmp("superscript", szValue))
382 			{
383 				_tagClose(TT_SUPERSCRIPT,"superscript",false,false,false);
384 			}
385 			else if (!strcmp("subscript", szValue))
386 			{
387 				_tagClose(TT_SUBSCRIPT,"subscript",false,false,false);
388 			}
389 		}
390 
391 		if ((pAP->getProperty(static_cast<const gchar *>("font-style"), szValue)) && !strcmp(szValue, "italic"))
392 		{
393 			_tagClose(TT_EMPHASIS,"emphasis",false,false,false);
394 		}
395 
396 		_tagClose(TT_PHRASE,"phrase",false,false,false);
397 		m_pAP_Span = NULL;
398 	}
399 
400 	m_bInSpan = false;
401 }
402 
_closeChapter(void)403 void s_DocBook_Listener :: _closeChapter (void)
404 {
405 	if(!m_bInChapter)
406 		return;
407 
408 	if(m_bInTable) // bad .doc import can lead to <section>s being closed in <cell>s; try to export valid XML regardless
409 	{
410 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
411 		_closeTable();
412 	}
413 
414 	_closeSection(0); //close any open sections
415 	_tagClose(TT_CHAPTER,"chapter");
416 	m_bInChapter = false;
417 }
418 
_closeChapterTitle(void)419 void s_DocBook_Listener :: _closeChapterTitle (void)
420 {
421 	if(!m_bInChapter || !m_bInTitle)
422 		return;
423 
424 	if(_tagTop() != TT_TITLE)
425 	{
426 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
427 	}
428 
429 	_tagClose(TT_TITLE,"title",true,false);
430 	m_bInTitle = false;
431 }
432 
_openChapter(PT_AttrPropIndex api)433 void s_DocBook_Listener :: _openChapter (PT_AttrPropIndex api)
434 {
435 	_closeChapter(); // close any open chapters (and sections)
436 
437 	if(_tagTop() != TT_DOCUMENT)
438 	{
439 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
440 	}
441 
442 	_tagOpen(TT_CHAPTER,"chapter");
443 	m_bInChapter = true;
444 	_openChapterTitle(api);
445 }
446 
_openChapterTitle(PT_AttrPropIndex)447 void s_DocBook_Listener :: _openChapterTitle (PT_AttrPropIndex /*api*/)
448 {
449 	if(_tagTop() == TT_CHAPTER)
450 	{
451 		_tagOpen(TT_TITLE,"title",false);
452 		m_bInTitle = true;
453 	}
454 	else
455 	{
456 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
457 	}
458 }
459 
_openList(PT_AttrPropIndex)460 void s_DocBook_Listener :: _openList (PT_AttrPropIndex /*api*/)
461 {
462 /*
463 	if(_tagTop() != TT_ITEMIZEDLIST)
464 	{
465 		m_pie->write("\n");
466 		_tagOpen(TT_ITEMIZEDLIST,"itemizedlist");
467 	}
468 
469 	_tagOpen(TT_LISTITEM,"listitem");
470 */
471 }
472 
_openBlock(bool indent)473 void s_DocBook_Listener :: _openBlock(bool indent)
474 {
475 	if(m_bInTitle)
476 		return;
477 
478 	UT_UTF8String buf = "para";
479 
480 	_closeParagraph();
481 	_tagOpen(TT_BLOCK,buf,false,indent,indent);
482 	m_bInParagraph = true;
483 	m_iBlockType = BT_NORMAL;
484 }
485 
_openPlainBlock()486 void s_DocBook_Listener :: _openPlainBlock()
487 {
488 	if(m_bInTitle)
489 		return;
490 
491 	UT_UTF8String buf = "literallayout";
492 
493 	_closeParagraph();
494 	_tagOpen(TT_PLAINTEXT,buf,true,false,false);
495 	m_bInParagraph = true;
496 	m_iBlockType = BT_PLAINTEXT;
497 }
498 
_openParagraph(PT_AttrPropIndex api)499 void s_DocBook_Listener :: _openParagraph(PT_AttrPropIndex api)
500 {
501 	if((m_iNestedTable == 0) || (m_iNestedTable == 2))
502 		return;  // we can't write before/after a nested table
503 
504 	if(m_bInTable && (_tagTop() == TT_ROW)) //no <entry>, can happen on bad .doc import
505 	{
506 		_openCell();
507 	}
508 
509 	const PP_AttrProp * pAP = NULL;
510 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP), indent = false;
511 	UT_UTF8String buf("");
512 
513 	if (bHaveProp && pAP)
514 	{
515 		const gchar * szValue = 0;
516 
517 		if (pAP->getAttribute(static_cast<const gchar *>(PT_STYLE_ATTRIBUTE_NAME), szValue))
518 		{
519 			if((!strcmp(szValue, "Heading 1")) || (!strcmp(szValue, "Numbered Heading 1")))
520 			{
521 				// <p style="Heading 1"> ...
522 				_closeChapterTitle();
523 
524 				if((!_inSectionStrux()) && !m_bInTitle)
525 					_openSection(api, 1, szValue);
526 				else if(!m_bInTitle)
527 				{
528 					indent = _decideIndent();
529 					_openBlock(indent);
530 				}
531 				return;
532 			}
533 			else if((!strcmp(szValue, "Heading 2")) || (!strcmp(szValue, "Numbered Heading 2")))
534 			{
535 				// <p style="Heading 2"> ...
536 				_closeChapterTitle();
537 
538 				if((!_inSectionStrux()) && !m_bInTitle)
539 				{
540 					_openSection(api, !strcmp(szValue, m_sParentStyle.utf8_str()) ? m_iSectionDepth : 2, szValue);
541 					m_sParentStyle = szValue;  //don't coalesce runs inside of a title
542 				}
543 				else if(!m_bInTitle)
544 				{
545 					indent = _decideIndent();
546 					_openBlock(indent);
547 					m_sParentStyle = szValue;
548 				}
549 				return;
550 			}
551 			else if((!strcmp(szValue, "Heading 3")) || (!strcmp(szValue, "Numbered Heading 3")))
552 			{
553 				// <p style="Heading 3"> ...
554 				_closeChapterTitle();
555 
556 				if(!_inSectionStrux() && !m_bInTitle)
557 				{
558 					_openSection(api, !strcmp(szValue, m_sParentStyle.utf8_str()) ? m_iSectionDepth : 3, szValue);
559 					m_sParentStyle = szValue;
560 				}
561 				else
562 				{
563 					indent = _decideIndent();
564 					_openBlock(indent);
565 					m_sParentStyle = szValue;
566 				}
567 				return;
568 			}
569 			else if(!strcmp(szValue, "Heading 4"))
570 			{
571 				// <p style="Heading 4"> ...
572 				_closeChapterTitle();
573 
574 				if(!_inSectionStrux() && !m_bInTitle)
575 				{
576 					_openSection(api, !strcmp(szValue, m_sParentStyle.utf8_str()) ? m_iSectionDepth : 4, szValue);
577 					m_sParentStyle = szValue;
578 				}
579 				else
580 				{
581 					indent = _decideIndent();
582 					_openBlock(indent);
583 					m_sParentStyle = szValue;
584 				}
585 				return;
586 			}
587 			else if (!strcmp (szValue, "Chapter Heading"))
588 			{
589 				if(!_inSectionStrux() && !m_bInTitle)
590 				{
591 					_openChapter(api);
592 					m_sParentStyle = szValue;
593 				}
594 				else
595 				{
596 					indent = _decideIndent();
597 					_openBlock(indent);
598 					m_sParentStyle = szValue;
599 				}
600 				return;
601 			}
602 			else if (!strcmp (szValue, "Section Heading"))
603 			{
604 				_closeChapterTitle();
605 
606 				if(!_inSectionStrux() && !m_bInTitle)
607 				{
608 					_openSection(api, 1, szValue);
609 					m_sParentStyle = szValue;
610 				}
611 				else
612 				{
613 					indent = _decideIndent();
614 					_openBlock(indent);
615 					m_sParentStyle = szValue;
616 				}
617 				return;
618 			}
619 			else if (!strcmp (szValue, "Plain Text"))
620 			{
621 				_closeChapterTitle();
622 
623 				if (!m_iSectionDepth)
624 				{
625 					_openSection (api,1, szValue);
626 					_closeSectionTitle();  // no title
627 				}
628 
629 				if(m_iLastClosed == TT_SECTION)
630 				{
631 					_openSection(api,m_iSectionDepth,szValue);
632 					_closeSectionTitle(); //no title
633 				}
634 
635 				/* merge all plaintexts into 1 if possible */
636 				if ((!m_bInParagraph) || (!(m_iBlockType == BT_PLAINTEXT)))
637 				{
638 					indent = _decideIndent();
639 					_openPlainBlock();
640 				}
641 				else
642 					m_pie -> write ("\n");
643 
644 				m_sParentStyle = szValue;
645 				return;
646 			}
647 			else if (!strcmp (szValue, "Normal"))
648 			{
649 				_closeChapterTitle();
650 
651 				if (!m_iSectionDepth)
652 					_openSection (api,1, szValue);
653 
654 				_closeSectionTitle();
655 				_closeParagraph();
656 
657 				if(m_iLastClosed == TT_SECTION)
658 				{
659 					_openSection(api,m_iSectionDepth,szValue);
660 					_closeSectionTitle(); //no title
661 				}
662 
663 				buf = "para";
664 
665 				indent = _decideIndent();
666 				_tagOpen(TT_BLOCK,buf,false,indent,indent); //don't indent in tables
667 				m_iBlockType = BT_NORMAL;
668 			}
669 			else
670 			{
671 				/* unhandled style */
672 				_closeChapterTitle();
673 
674 				if(!_inSectionStrux() && !m_bInTitle)
675 				{
676 					buf = "para";
677 
678 					if(strcmp (szValue, m_sLastStyle.utf8_str()))  // not a coalescing run
679 					{
680 						_openSection(api,m_iSectionDepth,szValue);
681 						_closeSectionTitle(); //no title
682 					}
683 
684 					_closeParagraph();
685 					indent = _decideIndent();
686 					_tagOpen(TT_BLOCK,buf,false,indent,indent);
687 					m_bInParagraph = true;
688 					m_iBlockType = BT_NORMAL;
689 					m_sLastStyle = szValue;
690 				}
691 				else
692 				{
693 					indent = _decideIndent();
694 					_openBlock(indent);
695 				}
696 				return;
697 			}
698 		}
699 		else
700 		{
701 			// <p> with no style attribute ...
702 			if (!m_iSectionDepth)
703 				_openSection (api,1, "");
704 
705 			_closeSectionTitle();
706 			_closeParagraph();
707 			m_iBlockType = BT_NORMAL;
708 
709 			if(m_iLastClosed == TT_SECTION)
710 			{
711 				_openSection(api,m_iSectionDepth,szValue);
712 				_closeSectionTitle();
713 			}
714 
715 			buf = "para";
716 			indent = _decideIndent();
717 			_tagOpen(TT_BLOCK,buf,false,indent,indent); //don't indent in table
718 		}
719 	}
720 	else
721 	{
722 		// <p> with no style attribute, and no properties either
723 		if (!m_iSectionDepth)
724 			_openSection (api,1,"");
725 
726 		_closeSectionTitle();
727 		_closeParagraph ();
728 		m_iBlockType = BT_NORMAL;
729 
730 		if(m_iLastClosed == TT_SECTION)
731 		{
732 			_openSection(api,m_iSectionDepth,"");
733 			_closeSectionTitle();
734 		}
735 
736 		indent = _decideIndent();
737 		_tagOpen(TT_BLOCK,"para",false,indent,indent); //don't indent in table
738 	}
739 	m_bInParagraph = true;
740 }
741 
_openSection(PT_AttrPropIndex api,int sub,const UT_UTF8String & content)742 void s_DocBook_Listener :: _openSection (PT_AttrPropIndex api, int sub, const UT_UTF8String & content)
743 {
744 	if((m_bInTable) || (m_bInFrame) || (m_bInHdrFtr))
745 		return;
746 
747 	if(!m_bInChapter)
748 		_openChapter(api);
749 
750 	if(!m_bInSection)
751 		_closeChapterTitle();
752 
753 	_closeSection(sub - 1);
754 
755 	if(_tagTop() == TT_TITLE)
756 		_closeSectionTitle();
757 
758 	UT_UTF8String section = "section", escaped = "";
759 	const PP_AttrProp * pAP = NULL;
760 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
761 	const gchar * szValue = 0;
762 
763 	if(content.length())
764 	{
765 		escaped = content;
766 		escaped.escapeXML();
767 		section += " role=\"";
768 		section += escaped;
769 		section += "\"";
770 	}
771 
772 	_tagOpen(TT_SECTION,section);
773 	m_iSectionDepth++;
774 	m_bInSection = true;
775 	_openSectionTitle();
776 
777 	if(pAP && bHaveProp && (pAP->getAttribute("strux-image-dataid", szValue)))
778 	{
779 		_closeSectionTitle(); // no title
780 		_handlePositionedImage(api);
781 	}
782 }
783 
_openSectionTitle(void)784 void s_DocBook_Listener :: _openSectionTitle(void)
785 {
786 	if((_tagTop() != TT_SECTION) || m_bInTitle)
787 		return;
788 
789 	_tagOpen(TT_TITLE,"title",false);
790 	m_bInTitle = true;
791 }
792 
_openSpan(PT_AttrPropIndex api)793 void s_DocBook_Listener :: _openSpan(PT_AttrPropIndex api)
794 {
795 	if ((!m_bInParagraph && !m_bInTitle))
796 		return;
797 
798 	if(m_bInSpan)
799 		_closeSpan();
800 
801 	UT_UTF8String buf = "phrase";
802 	const PP_AttrProp * pAP = NULL;
803 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
804 
805 	if (bHaveProp && pAP)
806 	{
807 		const gchar * szValue = 0;
808 
809 		if ((pAP->getAttribute(static_cast<const gchar *>("revision"), szValue)))
810 		{
811 			buf += " revision=\"";
812 			buf += szValue;
813 			buf += "\"";
814 		}
815 		if ((pAP->getProperty(static_cast<const gchar *>("lang"), szValue)))
816 		{
817 			buf += " lang=\"";
818 			buf += szValue;
819 			buf += "\"";
820 		}
821 		if ((pAP->getProperty(static_cast<const gchar *>("font-weight"), szValue)) && !strcmp(szValue, "bold"))
822 		{
823 			buf += " role=\"strong\"";
824 		}
825 
826 		_tagOpen(TT_PHRASE,buf,false,false,false);
827 
828 		if ((pAP->getProperty(static_cast<const gchar *>("font-style"), szValue)) && !strcmp(szValue, "italic"))
829 		{
830 			_tagOpen(TT_EMPHASIS,"emphasis",false,false,false);
831 		}
832 		if (pAP->getProperty(static_cast<const gchar *>("text-position"), szValue))
833 		{
834 			if (!strcmp("superscript", szValue))
835 			{
836 				_tagOpen(TT_SUPERSCRIPT,"superscript",false,false,false);
837 			}
838 			else if (!strcmp("subscript", szValue))
839 			{
840 				_tagOpen(TT_SUBSCRIPT,"subscript",false,false,false);
841 			}
842 		}
843 		m_bInSpan = true;
844 		m_pAP_Span = pAP;
845 	}
846 }
847 
_outputData(const UT_UCSChar * data,UT_uint32 length)848 void s_DocBook_Listener::_outputData(const UT_UCSChar * data, UT_uint32 length)
849 {
850 	if ((!m_bInParagraph) && (!m_bInTitle))
851 	{
852 		return;
853 	}
854 	m_bWasSpace = false;
855 
856 	UT_UTF8String sBuf = "";
857 	const UT_UCSChar * pData;
858 
859 	UT_ASSERT(sizeof(UT_Byte) == sizeof(char));
860 
861 	sBuf.reserve(length);
862 	for (pData=data; (pData<data+length); /**/)
863 	{
864 		switch (*pData)
865 		{
866 		case UCS_FF: // page break
867 			if ((!m_bInTitle) && (m_bInParagraph)) // we don't put tags in <title>
868 			{
869 				if(_inFormattedSpan())
870 					_closeSpan();
871 				sBuf += "<beginpage/>";
872 			}
873 			else
874 			{
875 				UT_ASSERT_HARMLESS(UT_TODO);
876 			}
877 
878 			pData++;
879 			break;
880 		case '<':
881 			sBuf += "&lt;";
882 			pData++;
883 			break;
884 
885 		case '>':
886 			sBuf += "&gt;";
887 			pData++;
888 			break;
889 
890 		case '&':
891 			sBuf += "&amp;";
892 			pData++;
893 			break;
894 
895 		case UCS_VTAB:					// column break
896 		case UCS_LF:					// LF -- representing a Forced-Line-Break
897 			if (m_iBlockType == BT_PLAINTEXT)
898 			{
899 				sBuf += "\n";
900 			}
901 			else
902 			{
903 				UT_ASSERT_HARMLESS(UT_TODO); // <br/> isn't valid in docbook
904 			}
905 			pData++;
906 			break;
907 
908 		case ' ':
909 		  // try to honor multiple spaces
910 		  // except in PLAINTEXT
911 		  if (m_iBlockType != BT_PLAINTEXT)
912 		  {
913 		  	if(m_bWasSpace)
914 				{
915 					// not defined in dbk, nor in abw...
916 //					sBuf += "&nbsp;";
917 					pData++;
918 				}
919 			  else
920 				{
921 				  // just tack on a single space to the textrun
922 				  m_bWasSpace = true;
923 				  sBuf += " ";
924 				  pData++;
925 				}
926 			  break;
927 		  }
928 		  else
929 		  {
930 			  // plain text: allowing multiple spaces and so
931 			  sBuf.appendUCS4(pData,1);
932 			  pData++;
933 			  break;
934 		  }
935 		case UCS_TAB:
936 		  // try to honor multiple spaces
937 		  // except in PLAINTEXT
938 		  if (m_iBlockType != BT_PLAINTEXT)
939 		  {
940 		  	if(m_bWasSpace)
941 				{
942 					// not defined in dbk, nor in abw...
943 //					sBuf += "&nbsp;";
944 					pData++;
945 				}
946 			  else
947 				{
948 				  // just tack on a single space to the textrun
949 				  m_bWasSpace = true;
950 				  sBuf += "\t";
951 				  pData++;
952 				}
953 			  break;
954 		  }
955 		  else
956 		  {
957 			  // plain text: allowing multiple spaces and so
958 			  sBuf.appendUCS4(pData,1);
959 			  pData++;
960 			  break;
961 		  }
962 
963 		default:
964 
965 		  // reset this variable
966 			m_bWasSpace = false;
967 			if(*pData < 0x20)  //invalid xml chars
968 				pData++;
969 			else
970 			{
971 				sBuf.appendUCS4(pData, 1);
972 				pData++;
973 			}
974 			break;
975 		}
976 	}
977 
978 	m_pie->write(sBuf.utf8_str(),sBuf.byteLength());
979 }
980 
s_DocBook_Listener(PD_Document * pDocument,IE_Exp_DocBook * pie)981 s_DocBook_Listener::s_DocBook_Listener(PD_Document * pDocument,
982 				       IE_Exp_DocBook * pie)
983  : mTableHelper(pDocument)
984 {
985 	m_pDocument = pDocument;
986 	m_pie = pie;
987 	m_bInParagraph = false;
988 	m_bInSection = false;
989 	m_bInSpan = false;
990 	m_bInChapter = false;
991 	m_bInTable = false;
992 	m_bInTitle = false;
993 	m_bInFrame = false;
994 	m_bInHdrFtr = false;
995 	m_bInNote = false;
996 	m_iBlockType = 0;
997 	m_iNestedTable = -1;
998 	m_iTableDepth = 0;
999 	m_iListDepth = 0;
1000 	m_iPreviousListDepth = 0;
1001 	m_iSectionDepth = 0;
1002 	m_iLastClosed = 0;
1003 	m_sLastStyle = "";  //only used for non-handled styles
1004 	m_sParentStyle = "";
1005 	m_bExternal = false;
1006 }
1007 
~s_DocBook_Listener()1008 s_DocBook_Listener::~s_DocBook_Listener()
1009 {
1010 	UT_VECTOR_FREEALL(char *, m_utvDataIDs);
1011 }
1012 
populate(fl_ContainerLayout *,const PX_ChangeRecord * pcr)1013 bool s_DocBook_Listener::populate(fl_ContainerLayout* /*sfh*/,
1014 								   const PX_ChangeRecord * pcr)
1015 {
1016 	switch (pcr -> getType ())
1017 	{
1018 		case PX_ChangeRecord :: PXT_InsertSpan:
1019 		{
1020 			const PX_ChangeRecord_Span * pcrs = static_cast<const PX_ChangeRecord_Span *> (pcr);
1021 
1022 			PT_AttrPropIndex api = pcr->getIndexAP();
1023 			if (api)
1024 			{
1025 				_openSpan(api);
1026 			}
1027 
1028 			PT_BufIndex bi = pcrs->getBufIndex();
1029 			_outputData(m_pDocument->getPointer(bi),pcrs->getLength());
1030 
1031 			if (api)
1032 				_closeSpan();
1033 			return true;
1034 		}
1035 	case PX_ChangeRecord::PXT_InsertObject:
1036 		{
1037 			const PX_ChangeRecord_Object * pcro = static_cast<const PX_ChangeRecord_Object *> (pcr);
1038 			PT_AttrPropIndex api = pcr->getIndexAP();
1039 
1040 			switch (pcro->getObjectType())
1041 			{
1042 			case PTO_Image:
1043 				_handleImage(api);
1044 				return true;
1045 
1046 			case PTO_Math:
1047 				_handleMath(api);
1048 				return true;
1049 
1050 			case PTO_Embed:
1051 				_handleEmbedded(api);
1052 				return true;
1053 
1054 			case PTO_Field:
1055 				_handleField(pcro, api);
1056 				return true;
1057 
1058 			case PTO_Hyperlink:
1059 				_handleHyperlink(api);
1060 				return true;
1061 
1062 			case PTO_Bookmark:
1063 				_handleBookmark(api);
1064 				return true;
1065 
1066 			default:
1067 				UT_ASSERT_HARMLESS(UT_TODO);
1068 				return true;
1069 			}
1070 			return false;
1071 		}
1072 
1073 		default:
1074 			return true;
1075 	}
1076 }
1077 
1078 /* http://www.docbook.org/tdg/en/html/table.html */
1079 
_openTable(PT_AttrPropIndex api)1080 void s_DocBook_Listener::_openTable(PT_AttrPropIndex api)
1081 {
1082 	if(m_bInTitle)
1083 		_closeSectionTitle();
1084 
1085 	if(m_bInTable)
1086 	{
1087 		_openNestedTable();
1088 		return;
1089 	}
1090 
1091 	UT_UTF8String buf("");
1092 	UT_sint32 nCols = mTableHelper.getNumCols();
1093 
1094 	if(!m_bInSection) //tables as first elements
1095 	{
1096 		_openSection(api,1,"");
1097 		_closeSectionTitle(); //no title in this instance, so just close it
1098 	}
1099 
1100 	if(m_iLastClosed == TT_SECTION)
1101 	{
1102 		_openSection(api,m_iSectionDepth,"");
1103 		_closeSectionTitle(); //no title
1104 	}
1105 
1106 	buf = "informaltable frame=\"all\"";
1107 
1108 	_tagOpen(TT_TABLE,buf);
1109 
1110 	UT_UTF8String tgroup(UT_UTF8String_sprintf("tgroup cols='%d' align='left' colsep='1' rowsep='1'", nCols));
1111 	_tagOpen(TT_TGROUP,tgroup,true,true,false);
1112 
1113 	for (int i = 0; i < nCols; i++)
1114 	{
1115 		UT_UTF8String colspec (UT_UTF8String_sprintf("colspec colname='c%d'", i+1));
1116 		_tagOpenClose(colspec,true);
1117 	}
1118 
1119 	_tagOpen(TT_TBODY,"tbody");
1120 	m_bInTable = true;
1121 }
1122 
_openNestedTable()1123 void s_DocBook_Listener::_openNestedTable()
1124 {
1125 	if(m_iNestedTable != 0) //docbook only allows one level of nesting
1126 		return;
1127 
1128 	if(_tagTop() != TT_ROW)
1129 		_openRow();
1130 
1131 	UT_sint32 nCols = mTableHelper.getNumCols();
1132 
1133 	UT_UTF8String entrytbl(UT_UTF8String_sprintf("entrytbl cols='%d' align='left' colsep='1' rowsep='1'", nCols));
1134 
1135 	_tagOpen(TT_ENTRYTBL,entrytbl);
1136 	_tagOpen(TT_TBODY,"tbody");
1137 	m_iNestedTable = 1;
1138 }
1139 
_openCell()1140 void s_DocBook_Listener::_openCell()
1141 {
1142 	UT_sint32 rowspan = 1, colspan = 1;
1143 	UT_UTF8String entry ("entry");
1144 
1145 	rowspan = mTableHelper.getBot() - mTableHelper.getTop();
1146 	colspan = mTableHelper.getRight() - mTableHelper.getLeft();
1147 
1148 	_openRow();
1149 
1150 	if (rowspan > 1)
1151 		entry += UT_UTF8String_sprintf(" morerows='%d'", rowspan-1);
1152 	if (colspan > 1)
1153 		entry += UT_UTF8String_sprintf(" namest='c%d' nameend='c%d'", mTableHelper.getLeft()+1, mTableHelper.getRight());
1154 
1155 	_tagOpen(TT_ENTRY,entry,false,true,true);
1156 }
1157 
_openRow(void)1158 void s_DocBook_Listener::_openRow(void)
1159 {
1160 	if (mTableHelper.isNewRow())
1161 	{
1162 		_closeCell();
1163 		_closeRow();
1164 		_tagOpen(TT_ROW,"row");
1165 	}
1166 }
1167 
_closeTable(void)1168 void s_DocBook_Listener::_closeTable(void)
1169 {
1170 	if(!m_bInTable)
1171 		return;
1172 
1173 	if(m_iNestedTable == 1)
1174 	{
1175 		_closeNestedTable();
1176 		return;
1177 	}
1178 
1179 	_closeCell();
1180 	_closeRow();
1181 	_tagClose(TT_TBODY,"tbody");
1182 	_tagClose(TT_TGROUP,"tgroup",true,true,false); //keep tgroup and tbody at the same indent level
1183 	_tagClose(TT_TABLE,"informaltable");
1184 	m_bInTable = false;
1185 }
1186 
_closeNestedTable(void)1187 void s_DocBook_Listener::_closeNestedTable(void)
1188 {
1189 	if(m_iNestedTable != 1)
1190 		return;
1191 
1192 	_closeCell();
1193 	_closeRow();
1194 	_tagClose(TT_TBODY,"tbody");
1195 	_tagClose(TT_ENTRYTBL,"entrytbl");
1196 	m_iNestedTable = 2;
1197 }
1198 
_closeCell(void)1199 void s_DocBook_Listener::_closeCell(void)
1200 {
1201 	_closeParagraph();
1202 
1203 	if(_tagTop() == TT_ENTRY)
1204 	{
1205 		_tagClose(TT_ENTRY,"entry",true,false,true);
1206 	}
1207 
1208 }
1209 
_closeRow(void)1210 void s_DocBook_Listener::_closeRow(void)
1211 {
1212 	_closeCell();
1213 
1214 	if (_tagTop() == TT_ROW)
1215 	{
1216 		_tagClose(TT_ROW,"row");
1217 	}
1218 }
1219 
_handleDocument(void)1220 void s_DocBook_Listener::_handleDocument(void)
1221 {
1222 	UT_UTF8String buf("book");
1223 	PT_AttrPropIndex docApi = m_pDocument->getAttrPropIndex();
1224 	const PP_AttrProp * pDAP = NULL;
1225 	const gchar* szValue = 0;
1226 
1227 	m_pDocument->getAttrProp (docApi, &pDAP);
1228 
1229 	if(pDAP && pDAP->getProperty("lang", szValue))
1230 	{
1231 		buf += " lang=\"";
1232 		buf += szValue;
1233 		buf+= "\"";
1234 	}
1235 
1236 	_tagOpen(TT_DOCUMENT,buf);
1237 }
1238 
_handleMetaData(void)1239 void s_DocBook_Listener::_handleMetaData(void)
1240 {
1241 	std::string metaProp, escaped;
1242 
1243 	_tagOpen(TT_BOOKINFO,"bookinfo");
1244 	_tagOpen(TT_BIBLIOMISC,"bibliomisc",false,true,false);
1245 	_tagOpen(TT_APPLICATION,"application class=\"software\"",false,false,false);
1246 	m_pie->write("AbiWord");
1247 	_tagClose(TT_APPLICATION,"application",false,false,false);
1248 	_tagClose(TT_BIBLIOMISC,"bibliomisc",true,false,false);
1249 
1250 	if (m_pDocument->getMetaDataProp (PD_META_KEY_TITLE, metaProp) && metaProp.size())
1251 	{
1252 		escaped = UT_escapeXML(metaProp);
1253 		_tagOpen(TT_TITLE,"title",false,true,false);
1254 		m_pie->write(escaped.c_str());
1255 		_tagClose(TT_TITLE,"title",true,false,false);
1256 	}
1257 	if (m_pDocument->getMetaDataProp (PD_META_KEY_CREATOR, metaProp) && metaProp.size())
1258 	{
1259 		escaped = UT_escapeXML(metaProp);
1260 		_tagOpen(TT_AUTHOR,"author",false,true,false);
1261 		_tagOpen(TT_OTHERNAME,"othername role=\"full\"",false,false,false);
1262 		m_pie->write(metaProp.c_str());
1263 		_tagClose(TT_OTHERNAME,"othername",false,false,false);
1264 		_tagClose(TT_AUTHOR,"author",true,false,false);
1265 	}
1266 	if (m_pDocument->getMetaDataProp (PD_META_KEY_SUBJECT, metaProp) && metaProp.size())
1267 	{
1268 		escaped = UT_escapeXML(metaProp);
1269 		_tagOpen(TT_SUBJECTSET,"subjectset",false,true,false);
1270 		_tagOpen(TT_SUBJECT,"subject",false,false,false);
1271 		_tagOpen(TT_SUBJECTTERM,"subjectterm",false,false,false);
1272 		m_pie->write(escaped.c_str());
1273 		_tagClose(TT_SUBJECTTERM,"subjectterm",false,false,false);
1274 		_tagClose(TT_SUBJECT,"subject",false,false,false);
1275 		_tagClose(TT_SUBJECTSET,"subjectset",true,false,false);
1276 	}
1277 	if (m_pDocument->getMetaDataProp (PD_META_KEY_DESCRIPTION, metaProp) && metaProp.size())
1278 	{
1279 		escaped = UT_escapeXML(metaProp);
1280 		_tagOpen(TT_ABSTRACT,"abstract",false,true,false);
1281 		_tagOpen(TT_BLOCK,"para",false,false,false);
1282 		m_pie->write(escaped.c_str());
1283 		_tagClose(TT_BLOCK,"para",false,false,false);
1284 		_tagClose(TT_ABSTRACT,"abstract",true,false,false);
1285 	}
1286 	if (m_pDocument->getMetaDataProp (PD_META_KEY_PUBLISHER, metaProp) && metaProp.size())
1287 	{
1288 		escaped = UT_escapeXML(metaProp);
1289 		_tagOpen(TT_PUBLISHER,"publisher",false,true,false);
1290 		_tagOpen(TT_PUBLISHERNAME,"publishername",false,false,false);
1291 		m_pie->write(escaped.c_str());
1292 		_tagClose(TT_PUBLISHERNAME,"publishername",false,false,false);
1293 		_tagClose(TT_PUBLISHER,"publisher",true,false,false);
1294 	}
1295 	if (m_pDocument->getMetaDataProp (PD_META_KEY_CONTRIBUTOR, metaProp) && metaProp.size())
1296 	{
1297 		escaped = UT_escapeXML(metaProp);
1298 		_tagOpen(TT_COLLAB,"collab",false,true,false);
1299 		_tagOpen(TT_COLLABNAME,"collabname",false,false,false);
1300 		m_pie->write(escaped.c_str());
1301 		_tagClose(TT_COLLABNAME,"collabname",false,false,false);
1302 		_tagClose(TT_COLLAB,"collab",true,false,false);
1303 	}
1304 	if (m_pDocument->getMetaDataProp (PD_META_KEY_DATE, metaProp) && metaProp.size())
1305 	{
1306 		escaped = UT_escapeXML(metaProp);
1307 		_tagOpen(TT_DATE,"date",false,true,false);
1308 		m_pie->write(escaped.c_str());
1309 		_tagClose(TT_DATE,"date",true,false,false);
1310 	}
1311 	if (m_pDocument->getMetaDataProp (PD_META_KEY_SOURCE, metaProp) && metaProp.size())
1312 	{
1313 		escaped = UT_escapeXML(metaProp);
1314 		_tagOpen(TT_BIBLIOSOURCE,"bibliosource",false,true,false);
1315 		m_pie->write(escaped.c_str());
1316 		_tagClose(TT_BIBLIOSOURCE,"bibliosource",true,false,false);
1317 	}
1318 	if (m_pDocument->getMetaDataProp (PD_META_KEY_RELATION, metaProp) && metaProp.size())
1319 	{
1320 		escaped = UT_escapeXML(metaProp);
1321 		_tagOpen(TT_BIBLIORELATION,"bibliorelation",false,true,false);
1322 		m_pie->write(escaped.c_str());
1323 		_tagClose(TT_BIBLIORELATION,"bibliorelation",true,false,false);
1324 	}
1325 	if (m_pDocument->getMetaDataProp (PD_META_KEY_COVERAGE, metaProp) && metaProp.size())
1326 	{
1327 		escaped = UT_escapeXML(metaProp);
1328 		_tagOpen(TT_BIBLIOCOVERAGE,"bibliocoverage",false,true,false);
1329 		m_pie->write(escaped.c_str());
1330 		_tagClose(TT_BIBLIOCOVERAGE,"bibliocoverage",true,false,false);
1331 	}
1332 	if (m_pDocument->getMetaDataProp (PD_META_KEY_RIGHTS, metaProp) && metaProp.size())
1333 	{
1334 		escaped = UT_escapeXML(metaProp);
1335 		_tagOpen(TT_LEGALNOTICE,"legalnotice",false,true,false);
1336 		_tagOpen(TT_BLOCK,"para",false,false,false);
1337 		m_pie->write(escaped.c_str());
1338 		_tagClose(TT_BLOCK,"para",false,false,false);
1339 		_tagClose(TT_LEGALNOTICE,"legalnotice",true,false,false);
1340 	}
1341 	if (m_pDocument->getMetaDataProp (PD_META_KEY_KEYWORDS, metaProp) && metaProp.size())
1342 	{
1343 		UT_UTF8String buf = "";
1344 		UT_UCS4String keyword(metaProp);
1345 
1346 		for(UT_uint32 i = 0;i < keyword.length(); i++)
1347 		{
1348 			if(keyword[i] != ' ')
1349 			{
1350 				buf += keyword[i];
1351 			}
1352 			else
1353 			{
1354 				if(buf.empty())  //only blank space encountered
1355 				{
1356 					continue;
1357 				}
1358 				if(_tagTop() == TT_BOOKINFO)
1359 					_tagOpen(TT_KEYWORDSET,"keywordset");
1360 
1361 				_tagOpen(TT_KEYWORD,"keyword",false,true,false);
1362 				buf.escapeXML();
1363 				m_pie->write(buf.utf8_str());
1364 				_tagClose(TT_KEYWORD,"keyword",true,false,false);
1365 				buf.clear();
1366 			}
1367 		}
1368 
1369 		if(buf.length())
1370 		{
1371 			if(_tagTop() == TT_BOOKINFO)
1372 				_tagOpen(TT_KEYWORDSET,"keywordset");
1373 
1374 			_tagOpen(TT_KEYWORD,"keyword",false,true,false);
1375 			buf.escapeXML();
1376 			m_pie->write(buf.utf8_str());
1377 			_tagClose(TT_KEYWORD,"keyword",true,false,false);
1378 		}
1379 
1380 		if(_tagTop() == TT_KEYWORDSET)
1381 			_tagClose(TT_KEYWORDSET,"keywordset");
1382 	}
1383 	if (m_pDocument->getMetaDataProp (PD_META_KEY_DATE_LAST_CHANGED, metaProp) && metaProp.size())
1384 	{
1385 		UT_ASSERT_HARMLESS(UT_TODO);
1386 	}
1387 
1388 	_handleRevisions();
1389 	_tagClose(TT_BOOKINFO,"bookinfo");
1390 }
1391 
_handleRevisions(void)1392 void s_DocBook_Listener::_handleRevisions(void)
1393 {
1394 	const AD_Revision * pRev = NULL;
1395 	const UT_GenericVector<AD_Revision*> & vRevisions = m_pDocument->getRevisions();
1396 
1397 	UT_sint32 k = 0;
1398 	for (k=0; k < vRevisions.getItemCount(); k++)
1399 	{
1400 		if(k == 0)
1401 			_tagOpen(TT_REVHISTORY,"revhistory");
1402 
1403 		pRev = vRevisions.getNthItem(k);
1404 		if(!pRev)
1405 			continue;
1406 
1407 		UT_UTF8String s;
1408 		UT_UCS4String s4;
1409 
1410 		UT_UTF8String_sprintf(s, "%d", pRev->getId());
1411 		_tagOpen(TT_REVISION,"revision");
1412 		_tagOpen(TT_REVNUMBER,"revnumber",false);
1413 		m_pie->write(s.utf8_str());
1414 		_tagClose(TT_REVNUMBER,"revnumber",true,false);
1415 		s.clear();
1416 
1417 		UT_UTF8String_sprintf(s, "%d", pRev->getStartTime());
1418 		_tagOpen(TT_DATE,"date",false);
1419 		m_pie->write(s.utf8_str());
1420 		_tagClose(TT_DATE,"date",true,false);
1421 		s4 = pRev->getDescription();
1422 
1423 		if(s4.length())
1424 		{
1425 			_tagOpen(TT_REVREMARK,"revremark",false);
1426 			s.clear();
1427 			s = s4.utf8_str();
1428 			s.escapeXML();
1429 			m_pie->write(s.utf8_str());
1430 			_tagClose(TT_REVREMARK,"revremark",true,false);
1431 		}
1432 		_tagClose(TT_REVISION,"revision");
1433 	}
1434 
1435 	if(_tagTop() == TT_REVHISTORY)
1436 		_tagClose(TT_REVHISTORY,"revhistory");
1437 }
1438 
_handleImage(PT_AttrPropIndex api)1439 void s_DocBook_Listener::_handleImage(PT_AttrPropIndex api)
1440 {
1441 	UT_UTF8String buf(""), escaped("");
1442 	const gchar* szValue = 0;
1443 	const PP_AttrProp * pAP = NULL;
1444 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
1445 	UT_LocaleTransactor t(LC_NUMERIC, "C");
1446 
1447 	if(!m_bInSection) // an image might be in a chapter heading
1448 	{
1449 		_closeChapterTitle();
1450 		_openSection(api,1,"");
1451 	}
1452 	if(!m_bInParagraph) // an image might also be in a section heading
1453 	{
1454 		_closeSectionTitle();
1455 		_openBlock(true);
1456 	}
1457 
1458 	if(bHaveProp && pAP && pAP->getAttribute("dataid", szValue))
1459 	{
1460 		const gchar* dataid = g_strdup(szValue);
1461 		char * temp = _stripSuffix(UT_go_basename(szValue), '_');
1462 		char * fstripped = _stripSuffix(temp, '.');
1463 
1464         std::string mimeType;
1465         const UT_ByteBuf * pByteBuf = 0;
1466         const char * extension = "png";
1467         const char * format = "PNG";
1468         m_pDocument->getDataItemDataByName(szValue, &pByteBuf,
1469                                            &mimeType, NULL);
1470         if(mimeType == "image/jpeg") {
1471             extension = "jpg";
1472             format = "JPEG";
1473         }
1474         else if(mimeType == "image/svg+xml") {
1475             extension = "svg";
1476             format = "SVG";
1477         }
1478 
1479 		UT_UTF8String_sprintf(buf, "%s.%s", fstripped, extension);
1480 		m_utvDataIDs.push_back(dataid);
1481 
1482 		FREEP(temp);
1483 		FREEP(fstripped);
1484 
1485 		_tagOpen(TT_FIGURE,"figure",false,false,false);
1486 		_tagOpen(TT_TITLE,"title",false,false,false);
1487 
1488 		if(pAP->getAttribute("title", szValue))  //use the image's title instead of its file name, if it exists
1489 		{
1490 			escaped = szValue;
1491 			escaped.escapeXML();
1492 			m_pie->write(escaped.utf8_str());
1493 		}
1494 		else  //fall back to the filename
1495 		{
1496 			escaped = buf.escapeXML();
1497 			m_pie->write(escaped.utf8_str());
1498 		}
1499 
1500 		_tagClose(TT_TITLE,"title",false,false,false);
1501 		_tagOpen(TT_MEDIAOBJECT,"mediaobject",false,false,false);
1502 		_tagOpen(TT_IMAGEOBJECT,"imageobject",false,false,false);
1503 
1504 		escaped.clear();
1505 		escaped = "imagedata fileref=\"";
1506 		escaped += UT_go_basename(m_pie->getFileName());
1507 		escaped += "_data/";
1508 		escaped += buf.escapeXML();
1509 		escaped += "\" format=\"";
1510         escaped += format;
1511         escaped += "\"";
1512 
1513 		if(pAP->getProperty("height", szValue))
1514 		{
1515 			escaped += " depth=\"";
1516 			escaped += szValue;
1517 			escaped += "\"";
1518 		}
1519 		if(pAP->getProperty("width", szValue))
1520 		{
1521 			escaped += " width=\"";
1522 			escaped += szValue;
1523 			escaped += "\"";
1524 		}
1525 
1526 		_tagOpenClose(escaped,true,false,false);
1527 		_tagClose(TT_IMAGEOBJECT,"imageobject",false,false,false);
1528 
1529 		if(pAP->getAttribute("alt", szValue))  //use the image's alt
1530 		{
1531 			buf.clear();
1532 			buf = szValue;
1533 			buf.escapeXML();
1534 			_tagOpen(TT_TEXTOBJECT,"textobject",false,false,false);
1535 			_tagOpen(TT_BLOCK,"para",false,false,false);
1536 			m_pie->write(buf.utf8_str());
1537 			_tagClose(TT_BLOCK,"para",false,false,false);
1538 			_tagClose(TT_TEXTOBJECT,"textobject",false,false,false);
1539 		}
1540 		_tagClose(TT_MEDIAOBJECT,"mediaobject",false,false,false);
1541 		_tagClose(TT_FIGURE,"figure",false,false,false);
1542 	}
1543 }
1544 
_handlePositionedImage(PT_AttrPropIndex api)1545 void s_DocBook_Listener::_handlePositionedImage(PT_AttrPropIndex api)
1546 {
1547 	UT_UTF8String buf(""), escaped("");
1548 	const gchar* szValue = 0;
1549 	const PP_AttrProp * pAP = NULL;
1550 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
1551 	UT_LocaleTransactor t(LC_NUMERIC, "C");
1552 
1553 	if(bHaveProp && pAP && pAP->getAttribute("strux-image-dataid", szValue))
1554 	{
1555 		const gchar* dataid = g_strdup(szValue);
1556 		char * temp = _stripSuffix(UT_go_basename(szValue), '_');
1557 		char * fstripped = _stripSuffix(temp, '.');
1558 
1559         std::string mimeType;
1560         const UT_ByteBuf * pByteBuf = 0;
1561         const char * extension = "png";
1562         const char * format = "PNG";
1563         m_pDocument->getDataItemDataByName(szValue, &pByteBuf,
1564                                            &mimeType, NULL);
1565         if(mimeType == "image/jpeg") {
1566             extension = "jpg";
1567             format = "JPEG";
1568         }
1569         else if(mimeType == "image/svg+xml") {
1570             extension = "svg";
1571             format = "SVG";
1572         }
1573 
1574 		UT_UTF8String_sprintf(buf, "%s.%s", fstripped, extension);
1575 		m_utvDataIDs.push_back(dataid);
1576 
1577 		FREEP(temp);
1578 		FREEP(fstripped);
1579 
1580 		_tagOpen(TT_FIGURE,"figure",false,true,false);
1581 		_tagOpen(TT_TITLE,"title",false,false,false);
1582 
1583 		if(pAP->getAttribute("title", szValue))  //use the image's title instead of its file name, if it exists
1584 		{
1585 			escaped = szValue;
1586 			escaped.escapeXML();
1587 			m_pie->write(escaped.utf8_str());
1588 		}
1589 		else  //fall back to the filename
1590 		{
1591 			escaped = buf.escapeXML();
1592 			m_pie->write(escaped.utf8_str());
1593 		}
1594 
1595 		_tagClose(TT_TITLE,"title",false,false,false);
1596 		_tagOpen(TT_MEDIAOBJECT,"mediaobject",false,false,false);
1597 		_tagOpen(TT_IMAGEOBJECT,"imageobject",false,false,false);
1598 
1599 		escaped.clear();
1600 		escaped = "imagedata fileref=\"";
1601 		escaped += UT_go_basename(m_pie->getFileName());
1602 		escaped += "_data/";
1603 		escaped += buf.escapeXML();
1604 		escaped += "\" format=\"";
1605         escaped += format;
1606         escaped += "\"";
1607 
1608 		if(pAP->getProperty("frame-height", szValue))
1609 		{
1610 			escaped += " depth=\"";
1611 			escaped += szValue;
1612 			escaped += "\"";
1613 		}
1614 		if(pAP->getProperty("frame-width", szValue))
1615 		{
1616 			escaped += " width=\"";
1617 			escaped += szValue;
1618 			escaped += "\"";
1619 		}
1620 
1621 		_tagOpenClose(escaped,true,false,false);
1622 		_tagClose(TT_IMAGEOBJECT,"imageobject",false,false,false);
1623 
1624 		if(pAP->getAttribute("alt", szValue))  //use the image's alt
1625 		{
1626 			buf.clear();
1627 			buf = szValue;
1628 			buf.escapeXML();
1629 			_tagOpen(TT_TEXTOBJECT,"textobject",false,false,false);
1630 			_tagOpen(TT_BLOCK,"para",false,false,false);
1631 			m_pie->write(buf.utf8_str());
1632 			_tagClose(TT_BLOCK,"para",false,false,false);
1633 			_tagClose(TT_TEXTOBJECT,"textobject",false,false,false);
1634 		}
1635 		_tagClose(TT_MEDIAOBJECT,"mediaobject",false,false,false);
1636 		_tagClose(TT_FIGURE,"figure",true,false,false);
1637 	}
1638 }
1639 
_handleMath(PT_AttrPropIndex api)1640 void s_DocBook_Listener::_handleMath(PT_AttrPropIndex api)
1641 {
1642 	UT_UTF8String buf(""), escaped("");
1643 	const gchar* szValue = 0;
1644 	const PP_AttrProp * pAP = NULL;
1645 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
1646 	UT_LocaleTransactor t(LC_NUMERIC, "C");
1647 
1648 	if(!m_bInSection) // an equation might be in a chapter heading
1649 	{
1650 		_closeChapterTitle();
1651 		_openSection(api,1,"");
1652 	}
1653 	if(!m_bInParagraph) // an equation might also be in a section heading
1654 	{
1655 		_closeSectionTitle();
1656 		_openBlock(true);
1657 	}
1658 
1659 	if(bHaveProp && pAP && pAP->getAttribute("dataid", szValue))
1660 	{
1661 		buf = "snapshot-png-";
1662 		buf += szValue;
1663 		const gchar* dataid = g_strdup(buf.utf8_str());
1664 		m_utvDataIDs.push_back(dataid);
1665 		buf += ".png";
1666 
1667 		_tagOpen(TT_INLINEEQUATION,"inlineequation",false,false,false);
1668 
1669 		escaped = "graphic fileref=\"";
1670 		escaped += UT_go_basename(m_pie->getFileName());
1671 		escaped += "_data/";
1672 		escaped += buf.escapeXML();
1673 		escaped += "\" format=\"PNG\"";
1674 
1675 		if(pAP->getProperty("height", szValue))
1676 		{
1677 			double dInch = static_cast<double>(atoi(szValue))/UT_LAYOUT_RESOLUTION;
1678 			buf.clear();
1679 			UT_UTF8String_sprintf(buf,"%fin",dInch);
1680 			escaped += " depth=\"";
1681 			escaped += buf;
1682 			escaped += "\"";
1683 		}
1684 		if(pAP->getProperty("width", szValue))
1685 		{
1686 			double dInch = static_cast<double>(atoi(szValue))/UT_LAYOUT_RESOLUTION;
1687 			buf.clear();
1688 			UT_UTF8String_sprintf(buf,"%fin",dInch);
1689 			escaped += " width=\"";
1690 			escaped += buf;
1691 			escaped += "\"";
1692 		}
1693 		if(pAP->getProperty("lang", szValue))
1694 		{
1695 			escaped += " lang=\"";
1696 			escaped += szValue;
1697 			escaped += "\"";
1698 		}
1699 
1700 		_tagOpenClose(escaped,true,false,false);
1701 
1702 /*  TODO: save mathml somehow
1703 
1704 		if(pAP->getAttribute("latexid", szValue))
1705 		{
1706 			escaped.clear();
1707 			escaped = szValue;
1708 			escaped.escapeXML();
1709 			_tagOpen(TT_MATHPHRASE,"mathphrase",false);
1710 			m_pie->write(escaped.utf8_str());	//TODO: investigate mml support in docbook instead
1711 			_tagClose(TT_MATHPHRASE,"mathphrase");
1712 		}
1713 */
1714 		_tagClose(TT_INLINEEQUATION,"inlineequation", false, false, false);
1715 	}
1716 }
1717 
_handleEmbedded(PT_AttrPropIndex api)1718 void s_DocBook_Listener::_handleEmbedded(PT_AttrPropIndex api)
1719 {
1720 	UT_UTF8String buf(""), escaped("");
1721 	const gchar* szValue = 0;
1722 	const PP_AttrProp * pAP = NULL;
1723 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
1724 	UT_LocaleTransactor t(LC_NUMERIC, "C");
1725 
1726 	if(!m_bInSection) // a chart might be in a chapter heading
1727 	{
1728 		_closeChapterTitle();
1729 		_openSection(api,1,"");
1730 	}
1731 	if(!m_bInParagraph) // a chart might also be in a section heading
1732 	{
1733 		_closeSectionTitle();
1734 		_openBlock(true);
1735 	}
1736 
1737 	if(bHaveProp && pAP && pAP->getAttribute("dataid", szValue))
1738 	{
1739 		buf = "snapshot-png-";
1740 		buf += szValue;
1741 		const gchar* dataid = g_strdup(buf.utf8_str());
1742 		m_utvDataIDs.push_back(dataid);
1743 		buf += ".png";
1744 
1745 		_tagOpen(TT_INFORMALFIGURE,"informalfigure",false,false,false);
1746 		_tagOpen(TT_MEDIAOBJECT,"mediaobject",false,false,false);
1747 		_tagOpen(TT_IMAGEOBJECT,"imageobject",false,false,false);
1748 
1749 		escaped = "imagedata fileref=\"";
1750 		escaped += UT_go_basename(m_pie->getFileName());
1751 		escaped += "_data/";
1752 		escaped += buf.escapeXML();
1753 		escaped += "\" format=\"PNG\"";
1754 
1755 		if(pAP->getProperty("height", szValue))
1756 		{
1757 			escaped += " depth=\"";
1758 			escaped += szValue;
1759 			escaped += "\"";
1760 		}
1761 		if(pAP->getProperty("width", szValue))
1762 		{
1763 			escaped += " width=\"";
1764 			escaped += szValue;
1765 			escaped += "\"";
1766 		}
1767 		if(pAP->getProperty("lang", szValue))
1768 		{
1769 			escaped += " lang=\"";
1770 			escaped += szValue;
1771 			escaped += "\"";
1772 		}
1773 
1774 		_tagOpenClose(escaped,true,false,false);
1775 		_tagClose(TT_IMAGEOBJECT,"imageobject",false,false,false);
1776 		_tagClose(TT_MEDIAOBJECT,"mediaobject",false,false,false);
1777 		_tagClose(TT_INFORMALFIGURE,"informalfigure",false,false,false);
1778 	}
1779 }
1780 
_handleField(const PX_ChangeRecord_Object * pcro,PT_AttrPropIndex api)1781 void s_DocBook_Listener::_handleField(const PX_ChangeRecord_Object * pcro, PT_AttrPropIndex api)
1782 {
1783 	UT_UTF8String buf(""), escaped("");
1784 	const PP_AttrProp * pAP = NULL;
1785 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
1786 	const gchar * szValue = 0, * szStyle = 0;
1787 
1788 	if(!m_bInParagraph)
1789 		_openBlock(false);
1790 
1791 	m_pie->populateFields ();
1792 
1793 	if(bHaveProp && pAP && pAP->getAttribute ("type", szValue))
1794 	{
1795 		if (!strcmp (szValue, "list_label"))
1796 		{
1797 			_openList(api);
1798 			return;
1799 		}
1800 
1801 		fd_Field * field = pcro->getField();
1802 		buf = "phrase role=\"";  //save the field type
1803 		buf += szValue;
1804 		buf += "\"";
1805 
1806 		if (!strcmp (szValue, "endnote_anchor"))  //give the endnote <para> a unique id
1807 		{
1808 			if(pAP->getAttribute("endnote-id", szStyle))
1809 			{
1810 				buf += " id=\"endnote-id-";
1811 				buf += szStyle;
1812 				buf += "\"";
1813 			}
1814 		}
1815 
1816 		_tagOpen(TT_PHRASE,buf,false,false,false);
1817 		buf.clear();
1818 
1819 		if (!strcmp (szValue, "footnote_ref"))
1820 		{
1821 			buf = "footnoteref linkend=\"footnote-id-";
1822 
1823 			if(pAP->getAttribute("footnote-id", szValue))
1824 			{
1825 				buf += szValue;
1826 				buf += "\"";
1827 			}
1828 
1829 			_tagOpenClose(buf,true,false,false);
1830 		}
1831 		else if (!strcmp (szValue, "endnote_ref"))
1832 		{
1833 			buf = "xref linkend=\"endnote-id-";
1834 
1835 			if(pAP->getAttribute("endnote-id", szValue))
1836 			{
1837 				buf += szValue;
1838 				buf += "\"";
1839 			}
1840 
1841 			_tagOpenClose(buf,true,false,false);
1842 		}
1843 
1844 		buf.clear();
1845 		buf = field->getValue();
1846 		if(buf.length())
1847 		{
1848 			buf.escapeXML();
1849 			m_pie->write(buf.utf8_str());
1850 		}
1851 
1852 		_tagClose(TT_PHRASE,"phrase",false,false,false);
1853 	}
1854 }
1855 
_handleTOC(PT_AttrPropIndex api)1856 void s_DocBook_Listener::_handleTOC(PT_AttrPropIndex api)
1857 {
1858 	std::string buf;
1859 	UT_UTF8String content("toc");
1860 	const gchar* szValue = 0;
1861 	const PP_AttrProp * pAP = NULL;
1862 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
1863 
1864 	/* TOCs are supposed to be at the beginning or end of a section, so we
1865 	   open a new section to prevent the loss of its position in the text */
1866 
1867 	_closeParagraph();
1868 	_closeSection(m_iSectionDepth);
1869 	_tagOpen(TT_SECTION,"section role=\"abi-toc\"");
1870 
1871 	if(bHaveProp && pAP && pAP->getProperty("toc-heading", szValue)) //user-defined
1872 	{
1873 		buf = UT_escapeXML(szValue ? szValue : "");
1874 	}
1875 	else  // get the default
1876 	{
1877 		XAP_App::getApp()->getStringSet()->getValueUTF8(AP_STRING_ID_TOC_TocHeading, buf);
1878 	}
1879 
1880 	// TODO: populate the TOC
1881 
1882 	_tagOpen(TT_TITLE,"title",false);
1883 	m_pie->write(buf);
1884 	_tagClose(TT_TITLE,"title",true,false);
1885 	_tagOpen(TT_TOC,content,false);
1886 	_tagClose(TT_TOC,"toc",true,false);
1887 	_tagOpenClose("para",false);
1888 	_tagClose(TT_SECTION,"section");
1889 }
1890 
_handleHyperlink(PT_AttrPropIndex api)1891 void s_DocBook_Listener::_handleHyperlink(PT_AttrPropIndex api)
1892 {
1893 	UT_UTF8String buf(""), escaped("");
1894 	const gchar* szValue = 0;
1895 	const PP_AttrProp * pAP = NULL;
1896 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
1897 
1898 	if(bHaveProp && pAP && pAP->getAttribute("xlink:href", szValue))
1899 	{
1900 		if(szValue && (szValue[0] == '#'))
1901 		{
1902 			/* anchor */
1903 			escaped = szValue + 1;  // skip '#'
1904 			escaped.escapeURL();
1905 			buf = "link linkend=\"";
1906 			buf += escaped;
1907 			buf += "\"";
1908 			_tagOpen(TT_LINK,buf,false,false,false);
1909 			m_bExternal = false;
1910 		}
1911 		else if(szValue)
1912 		{
1913 			/* external */
1914 			escaped = szValue;
1915 			escaped.escapeURL();
1916 			buf = "ulink url=\"";
1917 			buf += escaped;
1918 			buf += "\"";
1919 			_tagOpen(TT_ULINK,buf,false,false,false);
1920 			m_bExternal = true;
1921 		}
1922 	}
1923 	else
1924 	{
1925 		if (m_bExternal && (_tagTop() == TT_ULINK))
1926 			_tagClose(TT_ULINK,"ulink",false,false,false);
1927 		else if(!m_bExternal && (_tagTop() == TT_LINK))
1928 			_tagClose(TT_LINK,"link",false,false,false);
1929 	}
1930 }
1931 
_handleBookmark(PT_AttrPropIndex api)1932 void s_DocBook_Listener::_handleBookmark(PT_AttrPropIndex api)
1933 {
1934 	UT_UTF8String buf(""), escaped("");
1935 	const gchar* szValue = 0;
1936 	const PP_AttrProp * pAP = NULL;
1937 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
1938 
1939 	if(bHaveProp && pAP && pAP->getAttribute("type", szValue))
1940 	{
1941 		if (!strcmp (szValue, "start") && pAP->getAttribute("name", szValue))
1942 		{
1943 			buf = "anchor id=\"";
1944 			escaped = szValue;
1945 			escaped.escapeXML();
1946 			buf += escaped;
1947 			buf += "\"";
1948 			_tagOpenClose(buf,true,false,false);
1949 		}
1950 	}
1951 }
1952 
_handleHdrFtr(PT_AttrPropIndex api)1953 void s_DocBook_Listener::_handleHdrFtr(PT_AttrPropIndex api)
1954 {
1955 	UT_UTF8String buf("abi-");  //prefix the type to prevent confusion on import (e.g. if there's a 'footer' style)
1956 	const gchar* szValue = 0;
1957 	const PP_AttrProp * pAP = NULL;
1958 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
1959 
1960 	if(bHaveProp && pAP && pAP->getAttribute("type", szValue))
1961 		buf += szValue;
1962 
1963 	_openSection(api,1,buf);
1964 	_closeSectionTitle(); //no title
1965 }
1966 
_handleFootnote(PT_AttrPropIndex api)1967 void s_DocBook_Listener::_handleFootnote(PT_AttrPropIndex api)
1968 {
1969 	UT_UTF8String buf("footnote id=\"footnote-id-");  //make it an NCNAME to prevent validation errors
1970 	const gchar* szValue = 0;
1971 	const PP_AttrProp * pAP = NULL;
1972 	bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
1973 
1974 	if(bHaveProp && pAP && pAP->getAttribute("footnote-id", szValue))
1975 		buf += szValue;
1976 	buf += "\"";
1977 
1978 	if(m_bInTitle && !m_bInSection) //in a chapter title
1979 	{
1980 		_openSection(api,1,"");
1981 	}
1982 
1983 	if(m_bInTitle)  //in a section or chapter title
1984 	{
1985 		_closeSectionTitle();
1986 		_openBlock(true);
1987 	}
1988 
1989 	_tagOpen(TT_FOOTNOTE,buf,false,false,false);
1990 }
1991 
_decideIndent(void)1992 bool s_DocBook_Listener::_decideIndent(void)
1993 {
1994 	if(m_bInTable)  // don't indent in tables
1995 		return false;
1996 
1997 	if(m_iBlockType == BT_PLAINTEXT)  // <literallayout>
1998 		return false;
1999 
2000 	if((_tagTop() == TT_FOOTNOTE) && ((m_iLastClosed == TT_PHRASE) ||
2001 		(m_iLastClosed == TT_BLOCK) || (m_iLastClosed == TT_TITLE)))  // the <para> right after <footnote>
2002 		return false;
2003 
2004 	if(m_bInNote && ((m_iLastClosed == TT_PHRASE) || (m_iLastClosed == TT_BLOCK)))
2005 		return false;
2006 
2007 	if(m_bInHdrFtr)
2008 		return true;
2009 
2010 	return true;
2011 }
2012 
_inFormattedSpan(void)2013 bool s_DocBook_Listener::_inFormattedSpan(void)
2014 {
2015 	return ((_tagTop() == TT_SUPERSCRIPT) || (_tagTop() == TT_SUBSCRIPT) || (_tagTop() == TT_EMPHASIS));
2016 }
2017 
_inSectionStrux(void)2018 bool s_DocBook_Listener::_inSectionStrux(void)
2019 {
2020 	return ((m_bInTable) || (m_bInFrame) || (m_bInHdrFtr) || (m_bInNote));
2021 }
2022 
populateStrux(pf_Frag_Strux * sdh,const PX_ChangeRecord * pcr,fl_ContainerLayout ** psfh)2023 bool s_DocBook_Listener::populateStrux(pf_Frag_Strux* sdh,
2024 										   const PX_ChangeRecord * pcr,
2025 										   fl_ContainerLayout* * psfh)
2026 {
2027 	UT_ASSERT(pcr->getType() == PX_ChangeRecord::PXT_InsertStrux);
2028 	const PX_ChangeRecord_Strux * pcrx = static_cast<const PX_ChangeRecord_Strux *> (pcr);
2029 	*psfh = 0;							// we don't need it.
2030 
2031 	switch (pcrx->getStruxType())
2032 	{
2033 		case PTX_Section:
2034 		{
2035 			/* open a chapter, _not_ a section; changing this causes the creation of an extra section at the top
2036 			   of every document */
2037 			_openChapter(pcr->getIndexAP());
2038 			return true;
2039 		}
2040 
2041         case PTX_SectionEndnote:
2042 		{
2043 			// don't do anything - handled in _handleField()
2044 			m_bInNote = true;
2045 			return true;
2046 		}
2047 		case PTX_EndEndnote:
2048 		{
2049 			m_bInNote = false;
2050 			return true;
2051 		}
2052 
2053 		case PTX_SectionHdrFtr:
2054 		{
2055 			_closeSection(0);
2056 			_handleHdrFtr(pcr->getIndexAP());
2057 			m_bInHdrFtr = true;
2058 			return true;
2059 		}
2060 
2061 		case PTX_Block:
2062 		{
2063 			_openParagraph (pcr -> getIndexAP ());
2064 			return true;
2065 		}
2066 
2067 		case PTX_SectionTable:
2068 		{
2069 			m_iTableDepth++;
2070 			if(m_iTableDepth <= 2)
2071 			{
2072 				_closeParagraph();
2073 				mTableHelper.OpenTable(sdh,pcr->getIndexAP()) ;
2074 				_openTable(pcr->getIndexAP());
2075 			}
2076 			return true;
2077 		}
2078 
2079 		case PTX_SectionCell:
2080 		{
2081 			if(m_iTableDepth > 2)
2082 				return true;
2083 
2084 			if((m_iNestedTable == 2) && (m_iTableDepth == 1)) //the last cell had a nested table; reset the value
2085 				m_iNestedTable = -1;
2086 
2087 			pf_Frag_Strux* nextTable = NULL, *nextCell = NULL;
2088 			bool bNextTable = m_pDocument->getNextStruxOfType(sdh, PTX_SectionTable, &nextTable);
2089 			bool bEndCell = m_pDocument->getNextStruxOfType(sdh, PTX_EndCell, &nextCell);
2090 
2091 			if(bNextTable && bEndCell && (m_iNestedTable == -1))
2092 			{
2093 				if(m_pDocument->getStruxPosition(nextTable) < m_pDocument->getStruxPosition(nextCell)) //nested table
2094 				{
2095 					_closeParagraph();
2096 					mTableHelper.OpenCell(pcr->getIndexAP());
2097 					m_iNestedTable = 0;  //pending, so don't allow any writing
2098 					return true;
2099 				}
2100 			}
2101 
2102 			if((m_iNestedTable == -1) || (m_iNestedTable == 1))
2103 			{
2104 				// regular cell
2105 				_closeParagraph();
2106 				mTableHelper.OpenCell(pcr->getIndexAP());
2107 				_openCell();
2108 			}
2109 			return true;
2110 		}
2111 
2112 		case PTX_EndTable:
2113 		{
2114 			// m_iTableDepth will be 1 if a nested table was closed
2115 			// or 0 if a regular table was closed
2116 			m_iTableDepth--;
2117 			if(m_iTableDepth > 1)
2118 				return true;
2119 
2120 			_closeParagraph();
2121 			_closeRow();
2122 			_closeTable();
2123 			mTableHelper.CloseTable();
2124 
2125 			if(m_iNestedTable != 2) //don't allow any content after an </entrytbl>
2126 				m_iNestedTable = -1;
2127 			return true;
2128 		}
2129 
2130 		case PTX_EndCell:
2131 		{
2132 			if(m_iTableDepth > 2)
2133 				return true;
2134 
2135 			_closeParagraph();
2136 			_closeCell();
2137 			mTableHelper.CloseCell();
2138 			return true;
2139 		}
2140 
2141 		case PTX_SectionFootnote:
2142 		{
2143 			_handleFootnote(pcr->getIndexAP());
2144 			m_bInNote = true;
2145 			return true;
2146 		}
2147 
2148 		case PTX_EndFootnote:
2149 		{
2150 			_closeParagraph();
2151 			if(m_iLastClosed == TT_PHRASE)  //<footnote> was empty - see bug 9890
2152 				_tagOpenClose("para",false,false,false);
2153 
2154 			if(m_bInNote)  // we might've closed a footnote early to workaround .doc import bugs, so check first
2155 				_tagClose(TT_FOOTNOTE,"footnote",false,false,false);
2156 
2157 			m_bInNote = false;
2158 			return true;
2159 		}
2160 
2161 		case PTX_SectionTOC:
2162 		{
2163 			_handleTOC(pcr->getIndexAP());
2164 			return true;
2165 		}
2166 
2167 		case PTX_EndTOC:
2168 		{
2169 			// don't do anything - already handled in _handleTOC()
2170 			return true;
2171 		}
2172 
2173 		case PTX_SectionFrame:
2174 		{
2175 			_closeSectionTitle();
2176 			_openSection(pcr->getIndexAP(), m_iSectionDepth+1, "abi-frame");
2177 			m_bInFrame = true;  //make sure this remains after the openSection() call
2178 			return true;
2179 		}
2180 		case PTX_EndFrame:
2181 		{
2182 			_closeSection(m_iSectionDepth-1);
2183 			m_bInFrame = false;
2184 			return true;
2185 		}
2186 
2187 		case PTX_EndMarginnote:
2188 		case PTX_SectionMarginnote:
2189 		default:
2190 			UT_ASSERT_HARMLESS(UT_TODO);
2191 			return true;
2192 	}
2193 }
2194 
change(fl_ContainerLayout *,const PX_ChangeRecord *)2195 bool s_DocBook_Listener::change(fl_ContainerLayout* /*sfh*/,
2196 									const PX_ChangeRecord * /*pcr*/)
2197 {
2198 	UT_ASSERT(0);						// this function is not used.
2199 	return false;
2200 }
2201 
insertStrux(fl_ContainerLayout *,const PX_ChangeRecord *,pf_Frag_Strux *,PL_ListenerId,void (*)(pf_Frag_Strux *,PL_ListenerId,fl_ContainerLayout *))2202 bool s_DocBook_Listener::insertStrux(fl_ContainerLayout* /*sfh*/,
2203 									 const PX_ChangeRecord * /*pcr*/,
2204 									 pf_Frag_Strux* /*sdh*/,
2205 									 PL_ListenerId /* lid */,
2206 									 void (* /*pfnBindHandles*/)(pf_Frag_Strux* /* sdhNew */,
2207 																 PL_ListenerId /* lid */,
2208 																 fl_ContainerLayout* /* sfhNew */))
2209 {
2210 	UT_ASSERT(0);						// this function is not used.
2211 	return false;
2212 }
2213 
signal(UT_uint32)2214 bool s_DocBook_Listener::signal(UT_uint32 /* iSignal */)
2215 {
2216 	UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
2217 	return false;
2218 }
2219 
2220 
2221 /*****************************************************************/
2222 /*****************************************************************/
2223 
_writeDocument(void)2224 UT_Error IE_Exp_DocBook::_writeDocument(void)
2225 {
2226 	m_pListener = new s_DocBook_Listener(getDoc(),this);
2227 
2228 	if (!m_pListener)
2229 		return UT_IE_NOMEMORY;
2230 	m_pListener -> _initFile ();
2231 	if (!getDoc()->tellListener(static_cast<PL_Listener *>(m_pListener)))
2232 		return UT_ERROR;
2233 	m_pListener -> _closeFile ();
2234 
2235 	DELETEP(m_pListener);
2236 
2237 	return ((m_error) ? UT_IE_COULDNOTWRITE : UT_OK);
2238 }
2239 
2240 /*****************************************************************/
2241 /*****************************************************************/
2242 
_handleDataItems(void)2243 void s_DocBook_Listener::_handleDataItems(void)
2244 {
2245 	// Lifted from HTML listener
2246  	const char * szName = 0;
2247     std::string mimeType;
2248 	const UT_ByteBuf * pByteBuf;
2249 
2250 	for (UT_uint32 k=0; (m_pDocument->enumDataItems(k,NULL,&szName,&pByteBuf,
2251                                                     &mimeType)); k++)
2252 	{
2253 		UT_sint32 loc = -1;
2254 		for (UT_sint32 i = 0; i < m_utvDataIDs.getItemCount(); i++)
2255 		{
2256 			if(strcmp(const_cast<char*>(reinterpret_cast<const char*>(m_utvDataIDs[i])), szName) == 0)
2257 			{
2258 				loc = i;
2259 				break;
2260 			}
2261 		}
2262 
2263 		if(loc > -1)
2264 		{
2265 			UT_UTF8String fname;
2266 
2267 			UT_UTF8String_sprintf(fname, "%s_data", m_pie->getFileName());
2268 			/* int result = */
2269 			UT_go_directory_create(fname.utf8_str(), 0750, NULL);
2270 
2271 			if (mimeType == "image/svg+xml")
2272 				UT_UTF8String_sprintf(fname, "%s/%s_%d.svg", fname.utf8_str(), szName, loc);
2273 			else if (mimeType == "application/mathml+xml")
2274 				UT_UTF8String_sprintf(fname, "%s/%s_%d.mathml", fname.utf8_str(), szName, loc);
2275 			else // PNG Image
2276 			{
2277 			  char * temp = _stripSuffix(UT_go_basename(szName), '_');
2278 			  char * fstripped = _stripSuffix(temp, '.');
2279 			  FREEP(temp);
2280               const char * extension = "png";
2281               if (mimeType == "image/jpeg") {
2282                   extension = "jpg";
2283               }
2284 			  UT_UTF8String_sprintf(fname, "%s/%s.%s", fname.utf8_str(), fstripped, extension);
2285 			  FREEP(fstripped);
2286 			}
2287 
2288 
2289 			GsfOutput *fp = UT_go_file_create (fname.utf8_str(), NULL);
2290 
2291 			if(!fp)
2292 			  continue;
2293 
2294 			gsf_output_write(fp, pByteBuf->getLength(), (const guint8*)pByteBuf->getPointer(0));
2295 			gsf_output_close(fp);
2296 			g_object_unref (G_OBJECT (fp));
2297 		}
2298 	}
2299 }
2300