/* pprint.c -- pretty print parse tree (c) 1998 (W3C) MIT, INRIA, Keio University See tidy.c for the copyright notice. */ #include #include #include #include "platform.h" #include "html.h" /* Block-level and unknown elements are printed on new lines and their contents indented 2 spaces Inline elements are printed inline. Inline content is wrapped on spaces (except in attribute values or preformatted text, after start tags and before end tags */ #define NORMAL 0 #define PREFORMATTED 1 #define COMMENT 2 #define ATTRIBVALUE 4 #define NOWRAP 8 #define CDATA 16 extern int CharEncoding; static uint *linebuf; static uint lbufsize; static uint linelen; static uint wraphere; static Bool InAttVal; static Bool InString; static int slide, count; static Node *slidecontent; int foo; /* debug */ /* 1010 A 1011 B 1100 C 1101 D 1110 E 1111 F */ /* return one less that the number of bytes used by UTF-8 char */ /* str points to 1st byte, *ch initialized to 1st byte */ uint GetUTF8(unsigned char *str, uint *ch) { uint c, n, i, count; c = str[0]; if ((c & 0xE0) == 0xC0) /* 110X XXXX two bytes */ { n = c & 31; count = 1; } else if ((c & 0xF0) == 0xE0) /* 1110 XXXX three bytes */ { n = c & 15; count = 2; } else if ((c & 0xF8) == 0xF0) /* 1111 0XXX four bytes */ { n = c & 7; count = 3; } else if ((c & 0xFC) == 0xF8) /* 1111 10XX five bytes */ { n = c & 3; count = 4; } else if ((c & 0xFE) == 0xFC) /* 1111 110X six bytes */ { n = c & 1; count = 5; } else /* 0XXX XXXX one byte */ { *ch = c; return 0; } /* successor bytes should have the form 10XX XXXX */ for (i = 1; i <= count; ++i) { c = str[i]; n = (n << 6) | (c & 0x3F); } *ch = n; return count; } void AddC(uint c, uint index) { if (index + 1 >= lbufsize) { while (index + 1 >= lbufsize) { if (lbufsize == 0) lbufsize = 256; else lbufsize = lbufsize * 2; } linebuf = (uint *)MemRealloc(linebuf, lbufsize*sizeof(uint)); } linebuf[index] = (uint)c; } void WrapLine(Out *fout, uint indent) { uint i, *p, *q; if (wraphere == 0) return; for (i = 0; i < indent; ++i) outc(' ', fout); for (i = 0; i < wraphere; ++i) outc(linebuf[i], fout); if (InString) { outc(' ', fout); outc('\\', fout); } outc('\n', fout); if (linelen > wraphere) { p = linebuf; if (linebuf[wraphere] == ' ') ++wraphere; q = linebuf + wraphere; AddC('\0', linelen); while (*p++ = *q++); linelen -= wraphere; } else linelen = 0; wraphere = 0; } void WrapAttrVal(Out *fout, uint indent) { uint i, *p, *q; for (i = 0; i < indent; ++i) outc(' ', fout); for (i = 0; i < wraphere; ++i) outc(linebuf[i], fout); outc(' ', fout); outc('\n', fout); if (linelen > wraphere) { p = linebuf; if (linebuf[wraphere] == ' ') ++wraphere; q = linebuf + wraphere; AddC('\0', linelen); while ((*p++ = *q++)); linelen -= wraphere; } else linelen = 0; wraphere = 0; } void PFlushLine(Out *fout, uint indent) { uint i; if (linelen > 0) { if (indent + linelen >= wraplen) WrapLine(fout, indent); if (!InAttVal) { for (i = 0; i < indent; ++i) outc(' ', fout); } for (i = 0; i < linelen; ++i) outc(linebuf[i], fout); } outc('\n', fout); linelen = wraphere = 0; InAttVal = no; } void PCondFlushLine(Out *fout, uint indent) { uint i; if (linelen > 0) { if (indent + linelen >= wraplen) WrapLine(fout, indent); if (!InAttVal) { for (i = 0; i < indent; ++i) outc(' ', fout); } for (i = 0; i < linelen; ++i) outc(linebuf[i], fout); outc('\n', fout); linelen = wraphere = 0; InAttVal = no; } } void PPrintChar(uint c, uint mode) { char *p, entity[128]; if (c == ' ' && !(mode & (PREFORMATTED | COMMENT | ATTRIBVALUE))) { /* coerce a space character to a non-breaking space */ if (mode & NOWRAP) { /* by default XML doesn't define   */ if (NumEntities || XmlTags) { AddC('&', linelen++); AddC('#', linelen++); AddC('1', linelen++); AddC('6', linelen++); AddC('0', linelen++); AddC(';', linelen++); } else /* otherwise use named entity */ { AddC('&', linelen++); AddC('n', linelen++); AddC('b', linelen++); AddC('s', linelen++); AddC('p', linelen++); AddC(';', linelen++); } return; } else wraphere = linelen; } /* comment characters are passed raw */ if (mode & COMMENT) { AddC(c, linelen++); return; } /* except in CDATA map < to < etc. */ if (! (mode & CDATA) ) { if (c == '<') { AddC('&', linelen++); AddC('l', linelen++); AddC('t', linelen++); AddC(';', linelen++); return; } if (c == '>') { AddC('&', linelen++); AddC('g', linelen++); AddC('t', linelen++); AddC(';', linelen++); return; } /* naked '&' chars can be left alone or quoted as & The latter is required for XML where naked '&' are illegal. */ if (c == '&' && (XmlOut || QuoteAmpersand)) { AddC('&', linelen++); AddC('a', linelen++); AddC('m', linelen++); AddC('p', linelen++); AddC(';', linelen++); return; } if (c == '"' && QuoteMarks && (mode & ATTRIBVALUE)) { AddC('&', linelen++); AddC('q', linelen++); AddC('u', linelen++); AddC('o', linelen++); AddC('t', linelen++); AddC(';', linelen++); return; } if (c == 160 && QuoteNbsp) { AddC('&', linelen++); if (NumEntities) { AddC('#', linelen++); AddC('1', linelen++); AddC('6', linelen++); AddC('0', linelen++); } else { AddC('n', linelen++); AddC('b', linelen++); AddC('s', linelen++); AddC('p', linelen++); } AddC(';', linelen++); return; } } /* otherwise ISO 2022 characters are passed raw */ if (CharEncoding == ISO2022 || CharEncoding == RAW) { AddC(c, linelen++); return; } /* if preformatted text, map   to space */ if (c == 160 && (mode & PREFORMATTED)) { AddC(' ', linelen++); return; } /* Filters from Word and PowerPoint often use smart quotes resulting in character codes between 128 and 159. Unfortunately, the corresponding HTML 4.0 entities for these are not widely supported. The following converts dashes and quotation marks to the nearest ASCII equivalent. */ if (MakeClean) { if (c > 126 && c < 160) { if (c == 8211) c = '-'; /* en dash */ else if (c == 8212) c = '-'; /* em dash */ else if (c == 8216) c = '\''; /* single left quotation mark */ else if (c == 8217) c = '\''; /* single right quotation mark */ else if (c == 8220) c = '"'; /* double left quotation mark */ else if (c == 8221) c = '"'; /* double right quotation mark */ } } /* don't map latin-1 chars to entities */ if (CharEncoding == LATIN1) { if (c > 255) /* multi byte chars */ { if (!NumEntities && (p = EntityName(c)) != null) sprintf(entity, "&%s;", p); else sprintf(entity, "&#%u;", c); for (p = entity; *p; ++p) AddC(*p, linelen++); return; } if (c > 126 && c < 160) { sprintf(entity, "&#%d;", c); for (p = entity; *p; ++p) AddC(*p, linelen++); return; } AddC(c, linelen++); return; } /* don't map utf8 chars to entities */ if (CharEncoding == UTF8) { AddC(c, linelen++); return; } /* use numeric entities only for XML */ if (XmlTags) { /* if ASCII use numeric entities for chars > 127 */ if (c > 127 && CharEncoding == ASCII) { sprintf(entity, "&#%u;", c); for (p = entity; *p; ++p) AddC(*p, linelen++); return; } /* otherwise output char raw */ AddC(c, linelen++); return; } /* default treatment for ASCII */ if (c > 126 || (c < ' ' && c != '\t')) { if (!NumEntities && (p = EntityName(c)) != null) sprintf(entity, "&%s;", p); else sprintf(entity, "&#%u;", c); for (p = entity; *p; ++p) AddC(*p, linelen++); return; } AddC(c, linelen++); } /* The line buffer is uint not char so we can hold Unicode values unencoded. The translation to UTF-8 is deferred to the outc routine called to flush the line buffer. */ void PPrintText(Out *fout, uint mode, uint indent, Lexer *lexer, uint start, uint end) { uint i, c; for (i = start; i < end; ++i) { if (indent + linelen >= wraplen) WrapLine(fout, indent); c = (unsigned char)lexer->lexbuf[i]; /* look for UTF-8 multibyte character */ if (c > 0x7F) i += GetUTF8((unsigned char *)lexer->lexbuf + i, &c); if (c == '\n') { PFlushLine(fout, indent); continue; } PPrintChar(c, mode); } } void PPrintString(Out *fout, uint indent, char *str) { while (*str != '\0') AddC(*str++, linelen++); } void PPrintAttrValue(Out *fout, uint indent, char *value, int delim, Bool wrappable) { uint c; if (delim == null) delim = '"'; AddC('=', linelen++); if (indent + linelen < wraplen) wraphere = linelen; if (indent + linelen >= wraplen) WrapLine(fout, indent); if (indent + linelen < wraplen) wraphere = linelen; else PCondFlushLine(fout, indent); AddC(delim, linelen++); if (value) { InString = no; while (*value != '\0') { if (wrappable && wraphere > 0 && indent + linelen >= wraplen) WrapAttrVal(fout, indent); c = (unsigned char)*value; if (c == (uint)delim) { char *entity; entity = (c == '"' ? """ : "'"); while (*entity != '\0') AddC(*entity++, linelen++); } if ((delim == '"' && c == '\'') || (delim == '\'' && c == '"')) InString = !InString; /* look for UTF-8 multibyte character */ if (c > 0x7F) value += GetUTF8((unsigned char *)value, &c); ++value; if (c == '\n') { PFlushLine(fout, indent); /*indent = 0; /* kludge */ /*InAttVal = yes; */ continue; } PPrintChar(c, (wrappable ? (NORMAL | ATTRIBVALUE) : (PREFORMATTED | ATTRIBVALUE))); } } InString = no; AddC(delim, linelen++); } void PPrintAttribute(Out *fout, uint indent, AttVal *attr) { char *name; Bool wrappable = yes; name = attr->attribute; if (indent + linelen >= wraplen) WrapLine(fout, indent); if (!XmlTags && !XmlOut && attr->dict) wrappable = !attr->dict->nowrap; if (indent + linelen < wraplen) { wraphere = linelen; AddC(' ', linelen++); } else PCondFlushLine(fout, indent); while (*name != '\0') AddC(FoldCase(*name++, UpperCaseAttrs), linelen++); if (indent + linelen >= wraplen) WrapLine(fout, indent); if (attr->value == null) { if (XmlTags || XmlOut) PPrintAttrValue(fout, indent, attr->attribute, attr->delim, yes); else if (!IsBoolAttribute(attr)) PPrintAttrValue(fout, indent, "", attr->delim, yes); else if (indent + linelen < wraplen) wraphere = linelen; } else PPrintAttrValue(fout, indent, attr->value, attr->delim, wrappable); } void PPrintAttrs(Out *fout, uint indent, AttVal *attr) { if (attr) { if (attr->next) PPrintAttrs(fout, indent, attr->next); if (attr->attribute != null) PPrintAttribute(fout, indent, attr); } } /* Line can be wrapped immediately after inline start tag provided if follows a text node ending in a space, or it parent is an inline element that that rule applies to. This behaviour was reverse engineered from Netscape 3.0 */ Bool AfterSpace(Lexer *lexer, Node *node) { Node *prev; uint c; if (!node || !node->tag || !(node->tag->model & CM_INLINE)) return yes; prev = node->prev; if (prev) { if (prev->type == TextNode && prev->end > prev->start) { c = (unsigned char)lexer->lexbuf[prev->end - 1]; if (c == 160 || c == ' ' || c == '\n') return yes; } return no; } return AfterSpace(lexer, node->parent); } void PPrintTag(Lexer *lexer, Out *fout, uint mode, uint indent, Node *node) { char c, *p; AddC('<', linelen++); if (node->type == EndTag) AddC('/', linelen++); for (p = node->element; (c = *p); ++p) AddC(FoldCase(c, UpperCaseTags), linelen++); PPrintAttrs(fout, indent, node->attributes); if ((XmlOut == yes || lexer->isvoyager) && (node->type == StartEndTag || node->tag->model & CM_EMPTY )) { AddC(' ', linelen++); /* compatibility hack */ AddC('/', linelen++); } AddC('>', linelen++);; if (node->type != StartEndTag && !(mode & PREFORMATTED)) { if (indent + linelen >= wraplen) WrapLine(fout, indent); if (indent + linelen < wraplen) { /* avoid wrapping after inline start tag unless it or its parent follows a space and its not an empty tag (e.g. IMG) followed by */ if (AfterSpace(lexer, node)) { if (!(mode & NOWRAP) && !((node->tag->model & CM_EMPTY) && node->next == null && node->parent->tag == tag_a)) { wraphere = linelen; } } } else PCondFlushLine(fout, indent); } } void PPrintEndTag(Out *fout, uint mode, uint indent, Node *node) { char c, *p; /* Netscape ignores SGML standard by not ignoring a line break before or etc. To avoid rendering this as an underlined space, I disable line wrapping before inline end tags by the #if 0 ... #endif */ #if 0 if (indent + linelen < wraplen && !(mode & NOWRAP)) wraphere = linelen; #endif AddC('<', linelen++); AddC('/', linelen++); for (p = node->element; (c = *p); ++p) AddC(FoldCase(c, UpperCaseTags), linelen++); AddC('>', linelen++); } void PPrintComment(Out *fout, uint indent, Lexer *lexer, Node *node) { if (indent + linelen < wraplen) wraphere = linelen; AddC('<', linelen++); AddC('!', linelen++); AddC('-', linelen++); AddC('-', linelen++); #if 0 if (linelen < wraplen) wraphere = linelen; #endif PPrintText(fout, COMMENT, indent, lexer, node->start, node->end); #if 0 if (indent + linelen < wraplen) wraphere = linelen; #endif AddC('-', linelen++); AddC('-', linelen++); AddC('>', linelen++); } void PPrintDocType(Out *fout, uint indent, Lexer *lexer, Node *node) { if (indent + linelen < wraplen) wraphere = linelen; PCondFlushLine(fout, indent); AddC('<', linelen++); AddC('!', linelen++); AddC('D', linelen++); AddC('O', linelen++); AddC('C', linelen++); AddC('T', linelen++); AddC('Y', linelen++); AddC('P', linelen++); AddC('E', linelen++); AddC(' ', linelen++); if (indent + linelen < wraplen) wraphere = linelen; PPrintText(fout, null, indent, lexer, node->start, node->end); if (linelen < wraplen) wraphere = linelen; AddC('>', linelen++); PCondFlushLine(fout, indent); } void PPrintPI(Out *fout, uint indent, Lexer *lexer, Node *node) { if (indent + linelen < wraplen) wraphere = linelen; AddC('<', linelen++); AddC('?', linelen++); PPrintText(fout, null, indent, lexer, node->start, node->end); if (lexer->lexbuf[node->end - 1] != '?') AddC('?', linelen++); AddC('>', linelen++); /* PCondFlushLine(fout, indent); */ } void PPrintAsp(Out *fout, uint indent, Lexer *lexer, Node *node) { if (indent + linelen < wraplen) wraphere = linelen; AddC('<', linelen++); AddC('%', linelen++); PPrintText(fout, null, indent, lexer, node->start, node->end); AddC('%', linelen++); AddC('>', linelen++); /* PCondFlushLine(fout, indent); */ } Bool ShouldIndent(Node *node) { if (IndentContent == no) return no; if (node->tag->model & CM_NEW) foo = 1; if (SmartIndent) { if (node->content && (node->tag->model & CM_NO_INDENT)) { for (node = node->content; node; node = node->next) if (node->tag && node->tag->model & CM_BLOCK) return yes; return no; } if (node->tag->model & CM_HEADING) return no; if (node->tag == tag_p) return no; } if (node->tag->model & (CM_FIELD | CM_OBJECT)) return yes; if (node->tag == tag_map) return yes; return !(node->tag->model & CM_INLINE); } void PPrintTree(Out *fout, uint mode, uint indent, Lexer *lexer, Node *node) { Node *content, *last; if (node == null) return; if (node->type == TextNode) PPrintText(fout, mode, indent, lexer, node->start, node->end); else if (node->type == CommentTag) { PPrintComment(fout, indent, lexer, node); } else if (node->type == RootNode) { for (content = node->content; content != null; content = content->next) PPrintTree(fout, mode, indent, lexer, content); } else if (node->type == DocTypeTag) PPrintDocType(fout, indent, lexer, node); else if (node->type == ProcInsTag) PPrintPI(fout, indent, lexer, node); else if (node->type == AspTag) PPrintAsp(fout, indent, lexer, node); else if (node->tag->model & CM_EMPTY || node->type == StartEndTag) { if (!(node->tag->model & CM_INLINE)) PCondFlushLine(fout, indent); if (node->tag == tag_br && node->prev && node->prev->tag != tag_br && BreakBeforeBR) PFlushLine(fout, indent); if (MakeClean && node->tag == tag_wbr) PPrintString(fout, indent, " "); else PPrintTag(lexer, fout, mode, indent, node); if (node->tag == tag_param || node->tag == tag_area) PCondFlushLine(fout, indent); else if (node->tag == tag_br || node->tag == tag_hr) PFlushLine(fout, indent); } else /* some kind of container element */ { if (node->tag == tag_pre) { PCondFlushLine(fout, indent); indent = 0; PCondFlushLine(fout, indent); PPrintTag(lexer, fout, mode, indent, node); PFlushLine(fout, indent); for (content = node->content; content != null; content = content->next) PPrintTree(fout, (mode | PREFORMATTED | NOWRAP), indent, lexer, content); PCondFlushLine(fout, indent); PPrintEndTag(fout, mode, indent, node); PFlushLine(fout, indent); if (IndentContent == no && node->next != null) PFlushLine(fout, indent); } else if (node->tag == tag_style || node->tag == tag_script) { PCondFlushLine(fout, indent); indent = 0; PCondFlushLine(fout, indent); PPrintTag(lexer, fout, mode, indent, node); PFlushLine(fout, indent); for (content = node->content; content != null; content = content->next) PPrintTree(fout, (mode | PREFORMATTED | NOWRAP |CDATA), indent, lexer, content); PCondFlushLine(fout, indent); PPrintEndTag(fout, mode, indent, node); PFlushLine(fout, indent); if (IndentContent == no && node->next != null) PFlushLine(fout, indent); } else if (node->tag->model & CM_INLINE) { if (MakeClean) { /* discards and tags */ if (node->tag == tag_font) { for (content = node->content; content != null; content = content->next) PPrintTree(fout, mode, indent, lexer, content); return; } /* replace ... by   or   etc. */ if (node->tag == tag_nobr) { for (content = node->content; content != null; content = content->next) PPrintTree(fout, mode|NOWRAP, indent, lexer, content); return; } } /* otherwise a normal inline element */ PPrintTag(lexer, fout, mode, indent, node); /* indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET */ if (ShouldIndent(node)) { PCondFlushLine(fout, indent); indent += spaces; for (content = node->content; content != null; content = content->next) PPrintTree(fout, mode, indent, lexer, content); PCondFlushLine(fout, indent); indent -= spaces; PCondFlushLine(fout, indent); } else { for (content = node->content; content != null; content = content->next) PPrintTree(fout, mode, indent, lexer, content); } PPrintEndTag(fout, mode, indent, node); } else /* other tags */ { if (MakeClean && node->tag == tag_center) { for (content = node->content; content != null; content = content->next) PPrintTree(fout, mode, indent, lexer, content); return; } PCondFlushLine(fout, indent); PPrintTag(lexer, fout, mode, indent, node); if (ShouldIndent(node)) PCondFlushLine(fout, indent); else if (node->tag->model & CM_HTML || node->tag == tag_noframes || (node->tag->model & CM_HEAD && !(node->tag == tag_title))) PFlushLine(fout, indent); if (node->tag == tag_body && BurstSlides) PPrintSlide(fout, mode, (IndentContent ? indent+spaces : indent), lexer); else { last = null; for (content = node->content; content != null; content = content->next) { /* kludge for naked text before block level tag */ if (last && !IndentContent && last->type == TextNode && content->tag && content->tag->model & CM_BLOCK) { PFlushLine(fout, indent); PFlushLine(fout, indent); } PPrintTree(fout, mode, (ShouldIndent(node) ? indent+spaces : indent), lexer, content); last = content; } } /* don't flush line for td and th */ if (ShouldIndent(node) || ((node->tag->model & CM_HTML || node->tag == tag_noframes || (node->tag->model & CM_HEAD && !(node->tag == tag_title))) && HideEndTags == no)) { PCondFlushLine(fout, (IndentContent ? indent+spaces : indent)); if (HideEndTags == no || !(node->tag->model & CM_OPT)) { PPrintEndTag(fout, mode, indent, node); PFlushLine(fout, indent); } } else { if (HideEndTags == no || !(node->tag->model & CM_OPT)) PPrintEndTag(fout, mode, indent, node); PFlushLine(fout, indent); } if (IndentContent == no && node->next != null && node->tag->model & (CM_BLOCK|CM_LIST|CM_DEFLIST|CM_TABLE)) { PFlushLine(fout, indent); } } } } void PPrintXMLTree(Out *fout, uint mode, uint indent, Lexer *lexer, Node *node) { if (node == null) return; if (node->type == TextNode) { PPrintText(fout, mode, indent, lexer, node->start, node->end); } else if (node->type == CommentTag) { PPrintComment(fout, indent, lexer, node); } else if (node->type == RootNode) { Node *content; for (content = node->content; content != null; content = content->next) PPrintXMLTree(fout, mode, indent, lexer, content); } else if (node->type == DocTypeTag) PPrintDocType(fout, indent, lexer, node); else if (node->type == ProcInsTag) PPrintPI(fout, indent, lexer, node); else if (node->type == AspTag) PPrintAsp(fout, indent, lexer, node); else if (node->tag->model & CM_EMPTY || node->type == StartEndTag) { PCondFlushLine(fout, indent); PPrintTag(lexer, fout, mode, indent, node); PFlushLine(fout, indent); } else /* some kind of container element */ { Node *content; int cindent; PCondFlushLine(fout, indent); if (XMLPreserveWhiteSpace(node)) { indent = 0; cindent = 0; } else cindent = indent + spaces; PPrintTag(lexer, fout, mode, indent, node); PFlushLine(fout, indent); for (content = node->content; content != null; content = content->next) PPrintXMLTree(fout, mode, cindent, lexer, content); PCondFlushLine(fout, cindent); PPrintEndTag(fout, mode, indent, node); PCondFlushLine(fout, indent); } } Node *FindBody(Node *root) { Node *node; node = root->content; while (node && node->tag != tag_html) node = node->next; if (node == null) return null; node = node->content; while (node && node->tag != tag_body) node = node->next; return node; } /* split parse tree by h2 elements and output to separate files */ /* counts number of h2 children belonging to node */ /* ??? somehow misses last slide ??? */ int CountSlides(Node *node) { int count = 1; for (node = node->content; node; node = node->next) if (node->tag == tag_h2) ++count; return count; } /* inserts a space gif called "dot.gif" to ensure that the slide is at least n pixels high */ void PrintVertSpacer(Out *fout, uint indent) { PCondFlushLine(fout, indent); PPrintString(fout, indent , ""); PCondFlushLine(fout, indent); } void PrintNavBar(Out *fout, uint indent) { char buf[128]; PCondFlushLine(fout, indent); PPrintString(fout, indent , "
"); if (slide > 1) { sprintf(buf, "previous | ", slide-1); PPrintString(fout, indent , buf); PCondFlushLine(fout, indent); if (slide < count) PPrintString(fout, indent , "start | "); else PPrintString(fout, indent , "start"); PCondFlushLine(fout, indent); } if (slide < count) { sprintf(buf, "next", slide+1); PPrintString(fout, indent , buf); } PPrintString(fout, indent , "
"); PCondFlushLine(fout, indent); } /* Called from PPrintTree to print the content of a slide from the node slidecontent. On return slidecontent points to the node starting the next slide or null. The variables slide and count are used to customise the navigation bar. */ void PPrintSlide(Out *fout, uint mode, uint indent, Lexer *lexer) { Node *content, *last; /* first print the h2 element and navbar */ if (slidecontent->tag == tag_h2) { PrintNavBar(fout, indent); /* now print an hr after h2 */ AddC('<', linelen++); AddC(FoldCase('h', UpperCaseTags), linelen++); AddC(FoldCase('r', UpperCaseTags), linelen++); if (XmlOut == yes) PPrintString(fout, indent , " />"); else AddC('>', linelen++); if (IndentContent == yes) PCondFlushLine(fout, indent); /* PrintVertSpacer(fout, indent); */ /*PCondFlushLine(fout, indent); */ /* print the h2 element */ PPrintTree(fout, mode, (IndentContent ? indent+spaces : indent), lexer, slidecontent); slidecontent = slidecontent->next; } /* now continue until we reach the next h2 or hr tag */ last = null; content = slidecontent; for (; content != null; content = content->next) { if (content->tag == tag_h2 || content->tag == tag_hr) break; /* kludge for naked text before block level tag */ if (last && !IndentContent && last->type == TextNode && content->tag && content->tag->model & CM_BLOCK) { PFlushLine(fout, indent); PFlushLine(fout, indent); } PPrintTree(fout, mode, (IndentContent ? indent+spaces : indent), lexer, content); last = content; } slidecontent = content; /* now print epilog */ PCondFlushLine(fout, indent); PPrintString(fout, indent , "
"); PCondFlushLine(fout, indent); AddC('<', linelen++); AddC(FoldCase('h', UpperCaseTags), linelen++); AddC(FoldCase('r', UpperCaseTags), linelen++); if (XmlOut == yes) PPrintString(fout, indent , " />"); else AddC('>', linelen++); if (IndentContent == yes) PCondFlushLine(fout, indent); PrintNavBar(fout, indent); } void CreateSlides(Lexer *lexer, Node *root) { Node *body; char buf[128]; Out out; FILE *fp; body = FindBody(root); count = CountSlides(body); slidecontent = body->content; for (slide = 1; slide <= count; ++slide) { sprintf(buf, "slide%d.html", slide); out.state = FSM_ASCII; out.encoding = CharEncoding; if ((fp = fopen(buf, "w"))) { out.fp = fp; PPrintTree(&out, null, 0, lexer, root); PFlushLine(&out, 0); fclose(fp); } } /* delete superfluous slides by deleting slideN.html for N = count+1, count+2, etc. until no such file is found. */ for (;;) { sprintf(buf, "slide%d.html", slide); if (unlink(buf) != 0) break; ++slide; } }