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