1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: t -*- */
2 
3 /* AbiWord
4 * Copyright (C) 2007, 2009 Hubert Figuiere
5 * Copyright (C) 2003-2005 Mark Gilbert <mg_abimail@yahoo.com>
6 * Copyright (C) 2002, 2004 Francis James Franklin <fjf@alinameridon.com>
7 * Copyright (C) 2001-2002 AbiSource, Inc.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * 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
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * 02110-1301 USA.
23 */
24 
25 #include "ie_exp_HTML_util.h"
26 
27 #define SEPARATOR "/"
28 
29 const char s_DTD_XHTML_AWML[] = "!DOCTYPE html PUBLIC \"-//ABISOURCE//DTD XHTML plus AWML 2.2//EN\" \"http://www.abisource.com/2004/xhtml-awml/xhtml-awml.mod\"";
30 
31 const char s_DTD_XHTML[] = "!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"";
32 
33 const char s_DTD_HTML4[] = "!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"";
34 
35 const char s_Delimiter[] =
36     "=======================================================";
37 
38 const char * s_Header[2] = {
39 "Created by AbiWord, a free, Open Source wordprocessor. ",
40 "For more information visit http://www.abisource.com.   "
41 };
42 
43 const char s_HeaderCompact[] = "Created by Abiword, www.abisource.com";
44 
45 bool m_bSecondPass = false;
46 bool m_bInAFENote = false;
47 bool m_bInAnnotation = false;
48 #include "MathSVGScript.h"
49 
50 UT_UTF8String sStyleSheet = "#toc,\n"
51 ".toc,\n"
52 ".mw-warning {\n"
53 "	border: 1px solid #aaa;\n"
54 "	background-color: #f9f9f9;\n"
55 "	padding: 5px;\n"
56 "	font-size: 95%;\n"
57 "}\n"
58 "#toc h2,\n"
59 ".toc h2 {\n"
60 "	display: inline;\n"
61 "	border: none;\n"
62 "	padding: 0;\n"
63 "	font-size: 100%;\n"
64 "	font-weight: bold;\n"
65 "}\n"
66 "#toc #toctitle,\n"
67 ".toc #toctitle,\n"
68 "#toc .toctitle,\n"
69 ".toc .toctitle {\n"
70 "	text-align: center;\n"
71 "}\n"
72 "#toc ul,\n"
73 ".toc ul {\n"
74 "	list-style-type: none;\n"
75 "	list-style-image: none;\n"
76 "	margin-left: 0;\n"
77 "	padding-left: 0;\n"
78 "	text-align: left;\n"
79 "}\n"
80 "#toc ul ul,\n"
81 ".toc ul ul {\n"
82 "	margin: 0 0 0 2em;\n"
83 "}\n"
84 "#toc .toctoggle,\n"
85 ".toc .toctoggle {\n"
86 "	font-size: 94%;\n"
87 "}";
88 
s_string_to_url(const UT_String & str)89 UT_UTF8String s_string_to_url (const UT_String & str)
90 {
91 	UT_UTF8String url;
92 
93 	static const char hex[16] = {
94 		'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
95 	};
96 	char buf[4];
97 	buf[0] = '%';
98 	buf[3] = 0;
99 
100 	const char * ptr = str.c_str ();
101 	while (*ptr)
102 	{
103 		bool isValidPunctuation = false;
104 		switch (*ptr)
105 		{
106 			case '-': // TODO: any others?
107 			case '_':
108 			case '.':
109 				isValidPunctuation = true;
110 				break;
111 			default:
112 				break;
113 		}
114 		unsigned char u = (unsigned char) *ptr;
115 		if (!isalnum (static_cast<int>(u)) && !isValidPunctuation)
116 		{
117 			buf[1] = hex[(u >> 4) & 0x0f];
118 			buf[2] = hex[ u       & 0x0f];
119 			url += buf;
120 		}
121 		else
122 		{
123 			buf[2] = (char) *ptr;
124 			url += (buf + 2);
125 		}
126 		ptr++;
127 	}
128 	return url;
129 }
130 
s_string_to_url(const UT_UTF8String & str)131 UT_UTF8String s_string_to_url (const UT_UTF8String & str)
132 {
133 	UT_String s(str.utf8_str());
134 	return s_string_to_url(s);
135 }
136 
137 const char * s_prop_list[] = {
138 	"background-color",	"transparent",
139 	"color",			"",
140 	"font-family",		"",
141 	"font-size",		"medium",
142 	"font-style",		"normal",
143 	"font-variant",		"normal",
144 	"font-weight",		"normal",
145 	"height",			"auto",
146 	"margin-bottom",	"0pt",
147 	"margin-left",		"0pt",
148 	"margin-right",		"0pt",
149 	"margin-top",		"0pt",
150 	"orphans",			"2",
151 	"text-align",		"",
152 	"text-decoration",	"none",
153 	"text-transform",	"none",
154 	"text-indent",		"0in",
155 	"vertical-align",	"baseline",
156 	"widows",			"2",
157 	"width",			"auto",
158 	0, 0
159 };
160 const UT_uint32 s_PropListLen = G_N_ELEMENTS(s_prop_list) - 2; /* don't include the zeros */
161 
162 /*!	This function returns true if the given property is a valid CSS
163   property.  It is based on the list in pp_Property.cpp, and, as such,
164   is quite brittle.
165 
166   prop_default may be zero on return, indicating that the default is not fixed
167 */
is_CSS(const char * prop_name,const char ** prop_default)168 bool is_CSS (const char * prop_name, const char ** prop_default)
169 {
170 	if (prop_name == 0)
171 		return false;
172 	if (*prop_name == 0)
173 		return false;
174 
175 	bool bCSS = false;
176 
177 	for (UT_uint32 i = 0; i < s_PropListLen; i += 2)
178 	{
179 		if (!strcmp (prop_name, s_prop_list[i]))
180 		{
181 			if (prop_default) *prop_default = s_prop_list[i+1];
182 			bCSS = true;
183 			break;
184 		}
185 	}
186 	return bCSS;
187 }
188 
189 /*!	This function copies a string to a new string, removing all the white
190   space in the process.
191 */
s_removeWhiteSpace(const char * text,UT_UTF8String & utf8str,bool bLowerCase)192 char * s_removeWhiteSpace (const char * text, UT_UTF8String & utf8str,
193 								  bool bLowerCase)
194 {
195 	utf8str = "";
196 
197 	if (text)
198 	{
199 		char buf[2]; // ick! [TODO ??]
200 		buf[1] = 0;
201 		const char * ptr = text;
202 		while (*ptr)
203 		{
204 			if (isspace ((int) ((unsigned char) *ptr)))
205 			{
206 				buf[0] = '_';
207 			}
208 			else
209 			{
210 				buf[0] = *ptr;
211 			}
212 			utf8str += buf;
213 			ptr++;
214 		}
215 
216 		if(bLowerCase)
217 			utf8str.lowerCase();
218 	}
219 	return 0;
220 }
221 
ConvertToClean(const UT_UTF8String & str)222 UT_UTF8String ConvertToClean(const UT_UTF8String & str)
223 {
224     UT_UTF8String result = "";
225 
226     UT_UTF8Stringbuf::UTF8Iterator i = str.getIterator();
227     i = i.start();
228 
229 
230     if (i.current())
231     {
232         while (true)
233         {
234             const gchar *pCurrent = i.current();
235 
236             if (*pCurrent == 0)
237             {
238                 break;
239             }
240 
241             if (isalnum(*pCurrent) || (*pCurrent == '-') || (*pCurrent == '_'))
242             {
243                 result += *pCurrent;
244             }
245 
246             i.advance();
247         }
248     }
249     return result;
250 }
251 
getPropertySize(const PP_AttrProp * pAP,const gchar * szWidthProp,const gchar * szHeightProp,const gchar ** szWidth,double & widthPercentage,const gchar ** szHeight,double dPageWidthInches,double dSecLeftMarginInches,double dSecRightMarginInches,double dCellWidthInches,ie_Table & tableHelper)252 bool getPropertySize(const PP_AttrProp * pAP, const gchar* szWidthProp,
253 	const gchar* szHeightProp, const gchar** szWidth, double& widthPercentage,
254 	const gchar** szHeight, double dPageWidthInches, double dSecLeftMarginInches,
255 	double dSecRightMarginInches, double dCellWidthInches,
256 	ie_Table &tableHelper)
257 {
258 	UT_return_val_if_fail(pAP, false);
259 	UT_return_val_if_fail(szWidth, false)
260 	UT_return_val_if_fail(szHeight, false)
261 
262 	// get the object width as displayed in AbiWord
263 	*szWidth = NULL;
264 	pAP->getProperty (szWidthProp, *szWidth);
265 
266 	// get the object height as displayed in AbiWord
267 	*szHeight = NULL;
268 	pAP->getProperty (szHeightProp, *szHeight);
269 
270 	// determine the total width of this object, so we can calculate the object's
271 	// width as a percentage of that
272 	widthPercentage = 100;
273 	if (*szWidth)
274 	{
275 		double total = 0;
276 		if(tableHelper.getNestDepth() > 0)
277 		{
278 			total = dCellWidthInches;
279 		}
280 		else
281 		{
282 			total =  dPageWidthInches - dSecLeftMarginInches - dSecRightMarginInches;
283 		}
284 
285 		double dWidth = UT_convertToInches(*szWidth);
286 		widthPercentage = 100.0 * dWidth / total;
287 		if (widthPercentage > 100.)
288 			widthPercentage = 100.0;
289 	}
290 
291 	return true;
292 }
293 
getStyleSizeString(const gchar * szWidth,double widthPercentage,UT_Dimension widthDim,const gchar * szHeight,UT_Dimension heightDim,bool bUseScale)294 UT_UTF8String getStyleSizeString(const gchar * szWidth, double widthPercentage,
295 	UT_Dimension widthDim, const gchar * szHeight,
296 	UT_Dimension heightDim, bool bUseScale)
297 {
298 	UT_UTF8String props;
299 
300 	if (szWidth)
301 	{
302 		props += "width:";
303 		if (bUseScale)
304 		{
305 			UT_sint32 iPercent = (UT_sint32)(widthPercentage + 0.5);
306 			props += UT_UTF8String_sprintf("%d%%", iPercent);
307 		}
308 		else
309 		{
310 			double d = UT_convertToDimension(szWidth, widthDim);
311 			props += UT_formatDimensionString(widthDim, d);
312 		}
313 	}
314 
315 	if (szHeight)
316 	{
317 		if (props.size() > 0)
318 			props += "; ";
319 		props += "height:";
320 		double d = UT_convertToDimension(szHeight, heightDim);
321 		props += UT_formatDimensionString(heightDim , d);
322 	}
323 
324 	if (props.size() > 0)
325 		return props;
326 
327 	return "";
328 }
329 
IE_Exp_HTML_DataExporter(PD_Document * pDocument,const UT_UTF8String & filename)330 IE_Exp_HTML_DataExporter::IE_Exp_HTML_DataExporter(PD_Document* pDocument,
331             const UT_UTF8String &filename):
332     m_pDocument(pDocument)
333 {
334 	std::string baseName = UT_go_basename_from_uri(filename.utf8_str());
335 	m_fileDirectory = baseName.c_str();
336     m_fileDirectory += FILES_DIR_NAME;
337 
338 	m_baseDirectory = g_path_get_dirname(filename.utf8_str());
339 }
340 
IE_Exp_HTML_FileExporter(PD_Document * pDocument,const UT_UTF8String & baseName)341 IE_Exp_HTML_FileExporter::IE_Exp_HTML_FileExporter(PD_Document* pDocument,
342     const UT_UTF8String& baseName):
343     IE_Exp_HTML_DataExporter(pDocument, baseName),
344     m_bInitialized(false)
345 {
346 
347 }
348 
349 
_init()350 void IE_Exp_HTML_FileExporter::_init()
351 {
352     if (!m_bInitialized)
353     {
354         UT_go_directory_create((m_baseDirectory + SEPARATOR +  m_fileDirectory).utf8_str(),
355                                0644, NULL);
356         UT_DEBUGMSG(("\n\nCREATED DIR: %s\n\n",(m_baseDirectory + SEPARATOR +  m_fileDirectory).utf8_str()));
357         m_bInitialized = true;
358     }
359 }
360 
saveData(const gchar * szDataId,const gchar * extension)361 UT_UTF8String IE_Exp_HTML_FileExporter::saveData(const gchar *szDataId,
362                                                  const gchar* extension)
363 {
364     _init();
365     UT_UTF8String filename = szDataId;
366 
367     if (extension != NULL)
368     {
369         filename += extension;
370     }
371 
372     const UT_ByteBuf * pByteBuf = 0;
373     if (!m_pDocument->getDataItemDataByName(szDataId, &pByteBuf,
374                                             NULL, NULL))
375     {
376         UT_ASSERT("No data item with specified dataid found\n");
377         return "";
378     }
379 
380     pByteBuf->writeToURI((m_baseDirectory + SEPARATOR + m_fileDirectory
381         + SEPARATOR + filename).utf8_str());
382 
383     return m_fileDirectory + SEPARATOR + filename;
384 }
385 
saveData(const UT_UTF8String & name,const UT_UTF8String & data)386 UT_UTF8String IE_Exp_HTML_FileExporter::saveData(const UT_UTF8String &name,
387     const UT_UTF8String &data)
388 {
389     _init();
390 
391     std::map<UT_UTF8String, UT_UTF8String>::iterator i =
392         m_saved.find(name);
393 
394     if (i != m_saved.end())
395     {
396         return i->second;
397     }
398     UT_UTF8String filePath = m_fileDirectory
399         + SEPARATOR  + name;
400 
401     GsfOutput* output =
402         UT_go_file_create((m_baseDirectory + SEPARATOR  +
403         m_fileDirectory + SEPARATOR + name).utf8_str(),
404                              NULL);
405 
406     gsf_output_write(output, data.byteLength(), reinterpret_cast<const guint8*>(data.utf8_str()));
407     gsf_output_close(output);
408     m_saved[name] = filePath;
409     return filePath;
410 }
411 
IE_Exp_HTML_MultipartExporter(PD_Document * pDocument,const UT_UTF8String & baseName,UT_UTF8String & buffer,const UT_UTF8String & title)412 IE_Exp_HTML_MultipartExporter::IE_Exp_HTML_MultipartExporter(
413     PD_Document* pDocument,
414     const UT_UTF8String &baseName, UT_UTF8String &buffer,
415     const UT_UTF8String &title):
416     IE_Exp_HTML_DataExporter(pDocument, baseName),
417     m_buffer(buffer),
418     m_title(title)
419 {
420 
421 }
422 
saveData(const gchar * szDataId,const gchar * extension)423 UT_UTF8String IE_Exp_HTML_MultipartExporter::saveData(const gchar *szDataId,
424     const gchar* extension)
425 {
426     UT_UTF8String filename = szDataId;
427 
428     if (extension != NULL)
429     {
430         filename += extension;
431     }
432 
433     std::string mime;
434     m_pDocument->getDataItemDataByName(szDataId, NULL, &mime, NULL);
435     m_buffer += MULTIPART_FIELD("Content-Type",
436                               (mime).c_str());
437     m_buffer += MULTIPART_FIELD("Content-Transfer-Encoding", "base64");
438     m_buffer += MULTIPART_FIELD("Content-Location", (m_fileDirectory + SEPARATOR + filename).utf8_str());
439 
440     UT_UTF8String contents;
441     encodeDataBase64(szDataId, contents, false);
442     UT_DEBUGMSG(("%lu", (long unsigned)contents.length()));
443     m_buffer += contents;
444     m_buffer += MYEOL;
445     m_buffer += MYEOL;
446     m_buffer += "--";
447     m_buffer += MULTIPART_BOUNDARY;
448 
449     return m_fileDirectory + SEPARATOR + filename;
450 }
451 
saveData(const UT_UTF8String & name,const UT_UTF8String & data)452 UT_UTF8String IE_Exp_HTML_MultipartExporter::saveData(const UT_UTF8String &name,
453     const UT_UTF8String &data)
454 {
455     const gchar *szSuffix = strchr(name.utf8_str(), '.');
456     UT_UTF8String mime;
457 
458     if (!g_ascii_strcasecmp(szSuffix, ".css"))
459     {
460         mime = "text/css";
461     } else
462     {
463         mime = "text/plain";
464     }
465 
466     UT_UTF8String filePath = m_fileDirectory
467         + SEPARATOR + name;
468 
469     m_buffer += MULTIPART_FIELD("Content-Type",
470                               (mime).utf8_str());
471     m_buffer += MULTIPART_FIELD("Content-Transfer-Encoding", "quoted-printable");
472     m_buffer += MULTIPART_FIELD("Content-Location", filePath.utf8_str());
473     m_buffer += MYEOL;
474 
475     UT_UTF8String contents = data;
476     contents.escapeMIME();
477     m_buffer += contents;
478     m_buffer += MYEOL;
479     m_buffer += MULTIPART_BOUNDARY;
480 
481     return filePath;
482 }
483 
generateHeader(const UT_UTF8String & index,const UT_UTF8String & mimetype)484 UT_UTF8String IE_Exp_HTML_MultipartExporter::generateHeader(const UT_UTF8String &index,
485     const UT_UTF8String &mimetype)
486 {
487     UT_UTF8String header;
488     header = MULTIPART_FIELD("From", "<Saved by AbiWord>");;
489     header += MULTIPART_FIELD("Subject", m_title.utf8_str());
490 
491     time_t tim = time (NULL);
492 	struct tm * pTime = localtime (&tim);
493 	char timestr[64];
494 	strftime (timestr, 63, "%a, %d %b %Y %H:%M:%S +0100", pTime); // hmm, hard-code time zone
495 	timestr[63] = 0;
496     header += MULTIPART_FIELD("Date", timestr);
497     header += MULTIPART_FIELD("MIME-Version", "1.0");
498 
499     UT_UTF8String contentType = "multipart/related;\n\tboundary=\"";
500     contentType += MULTIPART_BOUNDARY;
501     contentType += "\";\n\ttype=\"";
502     contentType += mimetype + "\"";
503     header += MULTIPART_FIELD("Content-Type",
504                               contentType.utf8_str());
505     header += MYEOL;
506     header += "--";
507     header += MULTIPART_BOUNDARY;
508     header += MYEOL;
509     header += MULTIPART_FIELD("Content-Type",
510                               (mimetype + ";charset=\"UTF-8\"").utf8_str());
511     header += MULTIPART_FIELD("Content-Transfer-Encoding", "quoted-printable");
512     header += MYEOL;
513     UT_UTF8String contents = index;
514     contents.escapeMIME();
515     header += contents;
516     header += MYEOL;
517     header += "--";
518     header += MULTIPART_BOUNDARY;
519     header += MYEOL;
520     return header;
521 }
522 
encodeDataBase64(const gchar * szDataId,UT_UTF8String & result,bool bAddInfo)523 void IE_Exp_HTML_DataExporter::encodeDataBase64(const gchar* szDataId,
524                                                 UT_UTF8String& result,
525                                                 bool bAddInfo)
526 {
527     std::string mimeType;
528     const UT_ByteBuf * pByteBuf = 0;
529     if (!m_pDocument->getDataItemDataByName(szDataId, &pByteBuf,
530                                             &mimeType, NULL))
531     {
532         UT_ASSERT("No data item with specified dataid found\n");
533         return;
534     }
535 
536 
537 
538     char buffer[75];
539 	char * bufptr = 0;
540 	size_t buflen;
541 	size_t imglen = pByteBuf->getLength ();
542 	const char * imgptr = reinterpret_cast<const char *>(pByteBuf->getPointer (0));
543 
544 	buffer[0] = '\r';
545 	buffer[1] = '\n';
546     result.clear();
547     if (bAddInfo)
548     {
549         result += "data:";
550         result += mimeType.c_str();
551         result += ";base64,";
552     }
553 	while (imglen)
554 	{
555 		buflen = 72;
556 		bufptr = buffer + 2;
557 
558 		UT_UTF8_Base64Encode (bufptr, buflen, imgptr, imglen);
559 
560 		*bufptr = 0;
561 
562 		result += buffer;
563 	}
564 }
565 
566 /*
567  *
568  */
IE_Exp_HTML_FileWriter(GsfOutput * output)569 IE_Exp_HTML_FileWriter::IE_Exp_HTML_FileWriter(GsfOutput* output):
570 m_output(output)
571 {
572 
573 }
574 
write(const UT_UTF8String & str)575 void IE_Exp_HTML_FileWriter::write(const UT_UTF8String& str)
576 {
577     gsf_output_write(m_output, str.byteLength(),
578                      reinterpret_cast<const guint8*>(str.utf8_str()));
579 }
580 
IE_Exp_HTML_StringWriter()581 IE_Exp_HTML_StringWriter::IE_Exp_HTML_StringWriter()
582 {
583 
584 }
write(const UT_UTF8String & str)585 void IE_Exp_HTML_StringWriter::write(const UT_UTF8String& str)
586 {
587     m_buffer += str;
588 }
589 
IE_Exp_HTML_TagWriter(IE_Exp_HTML_OutputWriter * pOutputWriter)590 IE_Exp_HTML_TagWriter::IE_Exp_HTML_TagWriter(IE_Exp_HTML_OutputWriter* pOutputWriter):
591 m_pOutputWriter(pOutputWriter),
592     m_bXmlModeEnabled(false),
593     m_bCurrentTagIsSingle(false),
594     m_bAttributesWritten(false),
595         m_bDataWritten(false),
596     m_bInComment(false),
597     m_buffer("")
598 {
599 
600 }
601 
602 
openTag(const std::string & tagName,bool isInline,bool isSingle)603 void IE_Exp_HTML_TagWriter::openTag(const std::string& tagName, bool isInline, bool isSingle)
604 {
605     if (m_bInComment)
606     {
607         UT_ASSERT("Trying to open tag inside comment\n");
608         return;
609     }
610 
611     if ((m_tagStack.size() > 0) && m_bCurrentTagIsSingle)
612     {
613         closeTag();
614     } else
615     {
616         _closeAttributes();
617     }
618     m_bCurrentTagIsSingle = isSingle;
619     m_bAttributesWritten = false;
620     m_bDataWritten = false;
621     m_tagStack.push_back(tagName);
622     m_inlineFlagStack.push_back(isInline);
623 
624 
625 
626     if (!isInline)
627     {
628         std::string indent = "";
629         for (size_t i = 0; i < m_tagStack.size() - 1; i++)
630         {
631             indent += "    ";
632         }
633 
634         m_buffer += indent;
635     }
636     m_buffer += "<" + tagName;
637 
638     UT_DEBUGMSG(("Opened tag: %s Depth: %ld\n", tagName.c_str(), (long)m_tagStack.size()));
639 }
640 
addAttribute(const std::string & name,const std::string & value)641 void IE_Exp_HTML_TagWriter::addAttribute(const std::string& name, const std::string& value)
642 {
643     if (m_bInComment)
644     {
645         UT_ASSERT("Trying to add attribute inside comment\n");
646         return;
647     }
648     m_buffer += " " + name + "=\"" + value + "\"";
649 }
650 
_closeAttributes()651 void IE_Exp_HTML_TagWriter::_closeAttributes()
652 {
653     if (m_tagStack.size() ==  0)
654     {
655         return;
656     }
657 
658     if (m_bInComment)
659     {
660         UT_ASSERT("Trying to close attribute list inside comment\n");
661         return;
662     }
663 
664     if (!m_bAttributesWritten)
665     {
666         if (m_bXmlModeEnabled && m_bCurrentTagIsSingle)
667         {
668                 m_buffer += " />";
669         } else
670         {
671             m_buffer += ">";
672         }
673 
674         if (!m_inlineFlagStack.back())
675         {
676             m_buffer += "\n";
677         }
678 
679         m_bAttributesWritten = true;
680     }
681 }
682 
writeData(const std::string & data)683 void IE_Exp_HTML_TagWriter::writeData(const std::string& data)
684 {
685     if (!m_bInComment)
686     {
687         _closeAttributes();
688     }
689     m_bDataWritten = true;
690     m_buffer += data;
691 }
692 
closeTag()693 void IE_Exp_HTML_TagWriter::closeTag()
694 {
695     if (m_bInComment)
696     {
697         UT_ASSERT("Trying to close tag inside comment\n");
698         return;
699     }
700 
701     if (m_tagStack.size() == 0)
702     {
703         UT_ASSERT("Trying to close unopened tag\n");
704         return;
705     }
706     _closeAttributes();
707 
708     if (!m_bCurrentTagIsSingle)
709     {
710 
711         if (m_bDataWritten && !m_inlineFlagStack.back())
712         {
713             std::string indent = "";
714             for (size_t i = 0; i < m_tagStack.size() - 1; i++)
715             {
716                 indent += "    ";
717             }
718             m_buffer += "\n" + indent;
719         }
720 
721         m_buffer += "</" + m_tagStack.back() + ">";
722         if (!m_inlineFlagStack.back())
723         {
724             m_buffer += "\n";
725         }
726     } else
727     {
728         m_bCurrentTagIsSingle = false;
729     }
730 
731     UT_DEBUGMSG(("Closed tag: %s\n", m_tagStack.back().c_str()));
732     m_tagStack.pop_back();
733     m_inlineFlagStack.pop_back();
734 
735     flush();
736 
737 
738 }
739 
openComment()740 void IE_Exp_HTML_TagWriter::openComment()
741 {
742     if (m_bInComment)
743     {
744         UT_ASSERT("Trying to create nested comment\n");
745         return;
746     }
747     _closeAttributes();
748     m_bInComment = true;
749     m_buffer += "<!-- ";
750 }
751 
closeComment()752 void IE_Exp_HTML_TagWriter::closeComment()
753 {
754     if (!m_bInComment)
755     {
756         UT_ASSERT("Trying to close unopened comment\n");
757         return;
758     }
759     m_bInComment = false;
760     m_buffer += " -->";
761 }
762 
flush()763 void IE_Exp_HTML_TagWriter::flush()
764 {
765     if (m_buffer.length() > 0)
766     {
767         m_pOutputWriter->write(m_buffer.c_str());
768         m_buffer = "";
769     }
770 }
771 
enableXmlMode(bool enable)772 void IE_Exp_HTML_TagWriter::enableXmlMode(bool enable)
773 {
774     m_bXmlModeEnabled = enable;
775 }
776