2 * @(#)PPrint.java 1.11 2000/08/16
10 * Pretty print parse tree
12 * (c) 1998-2000 (W3C) MIT, INRIA, Keio University
13 * See Tidy.java for the copyright notice.
14 * Derived from <a href="http://www.w3.org/People/Raggett/tidy">
15 * HTML Tidy Release 4 Aug 2000</a>
17 * @author Dave Raggett <dsr@w3.org>
18 * @author Andy Quick <ac.quick@sympatico.ca> (translation to Java)
19 * @version 1.0, 1999/05/22
20 * @version 1.0.1, 1999/05/29
21 * @version 1.1, 1999/06/18 Java Bean
22 * @version 1.2, 1999/07/10 Tidy Release 7 Jul 1999
23 * @version 1.3, 1999/07/30 Tidy Release 26 Jul 1999
24 * @version 1.4, 1999/09/04 DOM support
25 * @version 1.5, 1999/10/23 Tidy Release 27 Sep 1999
26 * @version 1.6, 1999/11/01 Tidy Release 22 Oct 1999
27 * @version 1.7, 1999/12/06 Tidy Release 30 Nov 1999
28 * @version 1.8, 2000/01/22 Tidy Release 13 Jan 2000
29 * @version 1.9, 2000/06/03 Tidy Release 30 Apr 2000
30 * @version 1.10, 2000/07/22 Tidy Release 8 Jul 2000
31 * @version 1.11, 2000/08/16 Tidy Release 4 Aug 2000
35 Block-level and unknown elements are printed on
36 new lines and their contents indented 2 spaces
38 Inline elements are printed inline.
40 Inline content is wrapped on spaces (except in
41 attribute values or preformatted text, after
42 start tags and before end tags
45 import java.io.FileOutputStream;
48 import java.io.IOException;
49 import java.io.FileNotFoundException;
53 /* page transition effects */
55 public static final short EFFECT_BLEND = -1;
56 public static final short EFFECT_BOX_IN = 0;
57 public static final short EFFECT_BOX_OUT = 1;
58 public static final short EFFECT_CIRCLE_IN = 2;
59 public static final short EFFECT_CIRCLE_OUT = 3;
60 public static final short EFFECT_WIPE_UP = 4;
61 public static final short EFFECT_WIPE_DOWN = 5;
62 public static final short EFFECT_WIPE_RIGHT = 6;
63 public static final short EFFECT_WIPE_LEFT = 7;
64 public static final short EFFECT_VERT_BLINDS = 8;
65 public static final short EFFECT_HORZ_BLINDS = 9;
66 public static final short EFFECT_CHK_ACROSS = 10;
67 public static final short EFFECT_CHK_DOWN = 11;
68 public static final short EFFECT_RND_DISSOLVE = 12;
69 public static final short EFFECT_SPLIT_VIRT_IN = 13;
70 public static final short EFFECT_SPLIT_VIRT_OUT = 14;
71 public static final short EFFECT_SPLIT_HORZ_IN = 15;
72 public static final short EFFECT_SPLIT_HORZ_OUT = 16;
73 public static final short EFFECT_STRIPS_LEFT_DOWN = 17;
74 public static final short EFFECT_STRIPS_LEFT_UP = 18;
75 public static final short EFFECT_STRIPS_RIGHT_DOWN = 19;
76 public static final short EFFECT_STRIPS_RIGHT_UP = 20;
77 public static final short EFFECT_RND_BARS_HORZ = 21;
78 public static final short EFFECT_RND_BARS_VERT = 22;
79 public static final short EFFECT_RANDOM = 23;
81 private static final short NORMAL = 0;
82 private static final short PREFORMATTED = 1;
83 private static final short COMMENT = 2;
84 private static final short ATTRIBVALUE = 4;
85 private static final short NOWRAP = 8;
86 private static final short CDATA = 16;
88 private int[] linebuf = null;
89 private int lbufsize = 0;
90 private int linelen = 0;
91 private int wraphere = 0;
92 private boolean inAttVal = false;
93 private boolean InString = false;
95 private int slide = 0;
96 private int count = 0;
97 private Node slidecontent = null;
99 private Configuration configuration;
101 public PPrint(Configuration configuration)
103 this.configuration = configuration;
115 /* return one less that the number of bytes used by UTF-8 char */
116 /* str points to 1st byte, *ch initialized to 1st byte */
117 public static int getUTF8(byte[] str, int start, MutableInteger ch)
121 c = ((int)str[start]) & 0xFF; // Convert to unsigned.
123 if ((c & 0xE0) == 0xC0) /* 110X XXXX two bytes */
128 else if ((c & 0xF0) == 0xE0) /* 1110 XXXX three bytes */
133 else if ((c & 0xF8) == 0xF0) /* 1111 0XXX four bytes */
138 else if ((c & 0xFC) == 0xF8) /* 1111 10XX five bytes */
143 else if ((c & 0xFE) == 0xFC) /* 1111 110X six bytes */
149 else /* 0XXX XXXX one byte */
155 /* successor bytes should have the form 10XX XXXX */
156 for (i = 1; i < bytes; ++i)
158 c = ((int)str[start + i]) & 0xFF; // Convert to unsigned.
159 n = (n << 6) | (c & 0x3F);
166 /* store char c as UTF-8 encoded byte stream */
167 public static int putUTF8(byte[] buf, int start, int c)
170 buf[start++] = (byte)c;
173 buf[start++] = (byte)(0xC0 | (c >> 6));
174 buf[start++] = (byte)(0x80 | (c & 0x3F));
176 else if (c <= 0xFFFF)
178 buf[start++] = (byte)(0xE0 | (c >> 12));
179 buf[start++] = (byte)(0x80 | ((c >> 6) & 0x3F));
180 buf[start++] = (byte)(0x80 | (c & 0x3F));
182 else if (c <= 0x1FFFFF)
184 buf[start++] = (byte)(0xF0 | (c >> 18));
185 buf[start++] = (byte)(0x80 | ((c >> 12) & 0x3F));
186 buf[start++] = (byte)(0x80 | ((c >> 6) & 0x3F));
187 buf[start++] = (byte)(0x80 | (c & 0x3F));
191 buf[start++] = (byte)(0xF8 | (c >> 24));
192 buf[start++] = (byte)(0x80 | ((c >> 18) & 0x3F));
193 buf[start++] = (byte)(0x80 | ((c >> 12) & 0x3F));
194 buf[start++] = (byte)(0x80 | ((c >> 6) & 0x3F));
195 buf[start++] = (byte)(0x80 | (c & 0x3F));
201 private void addC(int c, int index)
203 if (index + 1 >= lbufsize)
205 while (index + 1 >= lbufsize)
210 lbufsize = lbufsize * 2;
213 int[] temp = new int[ lbufsize ];
215 System.arraycopy(linebuf, 0, temp, 0, index);
222 private void wrapLine(Out fout, int indent)
229 for (i = 0; i < indent; ++i)
232 for (i = 0; i < wraphere; ++i)
233 fout.outc(linebuf[i]);
238 fout.outc((int)'\\');
243 if (linelen > wraphere)
247 if (linebuf[wraphere] == ' ')
255 linebuf[p] = linebuf[q];
256 if (linebuf[q] == 0) break;
268 private void wrapAttrVal(Out fout, int indent, boolean inString)
272 for (i = 0; i < indent; ++i)
275 for (i = 0; i < wraphere; ++i)
276 fout.outc(linebuf[i]);
281 fout.outc((int)'\\');
285 if (linelen > wraphere)
289 if (linebuf[wraphere] == ' ')
297 linebuf[p] = linebuf[q];
298 if (linebuf[q] == 0) break;
310 public void flushLine(Out fout, int indent)
316 if (indent + linelen >= this.configuration.wraplen)
317 wrapLine(fout, indent);
319 if (!inAttVal || this.configuration.IndentAttributes)
321 for (i = 0; i < indent; ++i)
325 for (i = 0; i < linelen; ++i)
326 fout.outc(linebuf[i]);
335 public void condFlushLine(Out fout, int indent)
341 if (indent + linelen >= this.configuration.wraplen)
342 wrapLine(fout, indent);
344 if (!inAttVal || this.configuration.IndentAttributes)
346 for (i = 0; i < indent; ++i)
350 for (i = 0; i < linelen; ++i)
351 fout.outc(linebuf[i]);
360 private void printChar(int c, short mode)
364 if (c == ' ' && !((mode & (PREFORMATTED | COMMENT | ATTRIBVALUE)) != 0))
366 /* coerce a space character to a non-breaking space */
367 if ((mode & NOWRAP) != 0)
369 /* by default XML doesn't define */
370 if (this.configuration.NumEntities || this.configuration.XmlTags)
372 addC('&', linelen++);
373 addC('#', linelen++);
374 addC('1', linelen++);
375 addC('6', linelen++);
376 addC('0', linelen++);
377 addC(';', linelen++);
379 else /* otherwise use named entity */
381 addC('&', linelen++);
382 addC('n', linelen++);
383 addC('b', linelen++);
384 addC('s', linelen++);
385 addC('p', linelen++);
386 addC(';', linelen++);
394 /* comment characters are passed raw */
395 if ((mode & COMMENT) != 0)
401 /* except in CDATA map < to < etc. */
402 if (! ((mode & CDATA) != 0) )
406 addC('&', linelen++);
407 addC('l', linelen++);
408 addC('t', linelen++);
409 addC(';', linelen++);
415 addC('&', linelen++);
416 addC('g', linelen++);
417 addC('t', linelen++);
418 addC(';', linelen++);
423 naked '&' chars can be left alone or
424 quoted as & The latter is required
425 for XML where naked '&' are illegal.
427 if (c == '&' && this.configuration.QuoteAmpersand)
429 addC('&', linelen++);
430 addC('a', linelen++);
431 addC('m', linelen++);
432 addC('p', linelen++);
433 addC(';', linelen++);
437 if (c == '"' && this.configuration.QuoteMarks)
439 addC('&', linelen++);
440 addC('q', linelen++);
441 addC('u', linelen++);
442 addC('o', linelen++);
443 addC('t', linelen++);
444 addC(';', linelen++);
448 if (c == '\'' && this.configuration.QuoteMarks)
450 addC('&', linelen++);
451 addC('#', linelen++);
452 addC('3', linelen++);
453 addC('9', linelen++);
454 addC(';', linelen++);
458 if (c == 160 && this.configuration.CharEncoding != Configuration.RAW)
460 if (this.configuration.QuoteNbsp)
462 addC('&', linelen++);
464 if (this.configuration.NumEntities)
466 addC('#', linelen++);
467 addC('1', linelen++);
468 addC('6', linelen++);
469 addC('0', linelen++);
473 addC('n', linelen++);
474 addC('b', linelen++);
475 addC('s', linelen++);
476 addC('p', linelen++);
479 addC(';', linelen++);
488 /* otherwise ISO 2022 characters are passed raw */
489 if (this.configuration.CharEncoding == Configuration.ISO2022 ||
490 this.configuration.CharEncoding == Configuration.RAW)
496 /* if preformatted text, map to space */
497 if (c == 160 && ((mode & PREFORMATTED) != 0))
499 addC(' ', linelen++);
504 Filters from Word and PowerPoint often use smart
505 quotes resulting in character codes between 128
506 and 159. Unfortunately, the corresponding HTML 4.0
507 entities for these are not widely supported. The
508 following converts dashes and quotation marks to
509 the nearest ASCII equivalent. My thanks to
510 Andrzej Novosiolov for his help with this code.
513 if (this.configuration.MakeClean)
515 if (c >= 0x2013 && c <= 0x201E)
536 /* don't map latin-1 chars to entities */
537 if (this.configuration.CharEncoding == Configuration.LATIN1)
539 if (c > 255) /* multi byte chars */
541 if (!this.configuration.NumEntities)
543 entity = EntityTable.getDefaultEntityTable().entityName((short)c);
545 entity = "&" + entity + ";";
547 entity = "&#" + c + ";";
550 entity = "&#" + c + ";";
552 for (int i = 0; i < entity.length(); i++)
553 addC((int)entity.charAt(i), linelen++);
558 if (c > 126 && c < 160)
560 entity = "&#" + c + ";";
562 for (int i = 0; i < entity.length(); i++)
563 addC((int)entity.charAt(i), linelen++);
572 /* don't map utf8 chars to entities */
573 if (this.configuration.CharEncoding == Configuration.UTF8)
579 /* use numeric entities only for XML */
580 if (this.configuration.XmlTags)
582 /* if ASCII use numeric entities for chars > 127 */
583 if (c > 127 && this.configuration.CharEncoding == Configuration.ASCII)
585 entity = "&#" + c + ";";
587 for (int i = 0; i < entity.length(); i++)
588 addC((int)entity.charAt(i), linelen++);
593 /* otherwise output char raw */
598 /* default treatment for ASCII */
599 if (c > 126 || (c < ' ' && c != '\t'))
601 if (!this.configuration.NumEntities)
603 entity = EntityTable.getDefaultEntityTable().entityName((short)c);
605 entity = "&" + entity + ";";
607 entity = "&#" + c + ";";
610 entity = "&#" + c + ";";
612 for (int i = 0; i < entity.length(); i++)
613 addC((int)entity.charAt(i), linelen++);
622 The line buffer is uint not char so we can
623 hold Unicode values unencoded. The translation
624 to UTF-8 is deferred to the outc routine called
625 to flush the line buffer.
627 private void printText(Out fout, short mode, int indent,
628 byte[] textarray, int start, int end)
631 MutableInteger ci = new MutableInteger();
633 for (i = start; i < end; ++i)
635 if (indent + linelen >= this.configuration.wraplen)
636 wrapLine(fout, indent);
638 c = ((int)textarray[i]) & 0xFF; // Convert to unsigned.
640 /* look for UTF-8 multibyte character */
643 i += getUTF8(textarray, i, ci);
649 flushLine(fout, indent);
657 private void printString(Out fout, int indent, String str)
659 for (int i = 0; i < str.length(); i++ )
660 addC((int)str.charAt(i), linelen++);
663 private void printAttrValue(Out fout, int indent, String value, int delim, boolean wrappable)
666 MutableInteger ci = new MutableInteger();
667 boolean wasinstring = false;
668 byte[] valueChars = null;
670 short mode = (wrappable ? (short)(NORMAL | ATTRIBVALUE) :
671 (short)(PREFORMATTED | ATTRIBVALUE));
675 valueChars = Lexer.getBytes(value);
678 /* look for ASP, Tango or PHP instructions for computed attribute value */
679 if (valueChars != null && valueChars.length >= 5 && valueChars[0] == '<')
681 if (valueChars[1] == '%' || valueChars[1] == '@'||
682 (new String(valueChars, 0, 5)).equals("<?php"))
689 addC('=', linelen++);
691 /* don't wrap after "=" for xml documents */
692 if (!this.configuration.XmlOut) {
694 if (indent + linelen < this.configuration.wraplen)
697 if (indent + linelen >= this.configuration.wraplen)
698 wrapLine(fout, indent);
700 if (indent + linelen < this.configuration.wraplen)
703 condFlushLine(fout, indent);
706 addC(delim, linelen++);
713 while (i < valueChars.length)
715 c = ((int)valueChars[i]) & 0xFF; // Convert to unsigned.
717 if (wrappable && c == ' ' && indent + linelen < this.configuration.wraplen)
720 wasinstring = InString;
723 if (wrappable && wraphere > 0 && indent + linelen >= this.configuration.wraplen)
724 wrapAttrVal(fout, indent, wasinstring);
730 entity = (c == '"' ? """ : "'");
732 for (int j = 0; j < entity.length(); j++ )
733 addC(entity.charAt(j), linelen++);
740 if (this.configuration.QuoteMarks)
742 addC('&', linelen++);
743 addC('q', linelen++);
744 addC('u', linelen++);
745 addC('o', linelen++);
746 addC('t', linelen++);
747 addC(';', linelen++);
750 addC('"', linelen++);
753 InString = !InString;
760 if (this.configuration.QuoteMarks)
762 addC('&', linelen++);
763 addC('#', linelen++);
764 addC('3', linelen++);
765 addC('9', linelen++);
766 addC(';', linelen++);
769 addC('\'', linelen++);
772 InString = !InString;
778 /* look for UTF-8 multibyte character */
781 i += getUTF8(valueChars, i, ci);
789 flushLine(fout, indent);
798 addC(delim, linelen++);
801 private void printAttribute(Out fout, int indent, Node node, AttVal attr)
804 boolean wrappable = false;
806 if (this.configuration.IndentAttributes)
808 flushLine(fout, indent);
809 indent += this.configuration.spaces;
812 name = attr.attribute;
814 if (indent + linelen >= this.configuration.wraplen)
815 wrapLine(fout, indent);
817 if (!this.configuration.XmlTags && !this.configuration.XmlOut && attr.dict != null)
819 if (AttributeTable.getDefaultAttributeTable().isScript(name))
820 wrappable = this.configuration.WrapScriptlets;
821 else if (!attr.dict.nowrap && this.configuration.WrapAttVals)
825 if (indent + linelen < this.configuration.wraplen)
828 addC(' ', linelen++);
832 condFlushLine(fout, indent);
833 addC(' ', linelen++);
836 for (int i = 0; i < name.length(); i++ )
837 addC((int)Lexer.foldCase(name.charAt(i),
838 this.configuration.UpperCaseAttrs,
839 this.configuration.XmlTags),
842 if (indent + linelen >= this.configuration.wraplen)
843 wrapLine(fout, indent);
845 if (attr.value == null)
847 if (this.configuration.XmlTags || this.configuration.XmlOut)
848 printAttrValue(fout, indent, attr.attribute, attr.delim, true);
849 else if (!attr.isBoolAttribute() && !Node.isNewNode(node))
850 printAttrValue(fout, indent, "", attr.delim, true);
851 else if (indent + linelen < this.configuration.wraplen)
856 printAttrValue(fout, indent, attr.value, attr.delim, wrappable);
859 private void printAttrs(Out fout, int indent,
860 Node node, AttVal attr)
864 if (attr.next != null)
865 printAttrs(fout, indent, node, attr.next);
867 if (attr.attribute != null)
868 printAttribute(fout, indent, node, attr);
869 else if (attr.asp != null)
871 addC(' ', linelen++);
872 printAsp(fout, indent, attr.asp);
874 else if (attr.php != null)
876 addC(' ', linelen++);
877 printPhp(fout, indent, attr.php);
881 /* add xml:space attribute to pre and other elements */
882 if (configuration.XmlOut &&
883 configuration.XmlSpace &&
884 ParserImpl.XMLPreserveWhiteSpace(node, configuration.tt) &&
885 node.getAttrByName("xml:space") == null)
886 printString(fout, indent, " xml:space=\"preserve\"");
890 Line can be wrapped immediately after inline start tag provided
891 if follows a text node ending in a space, or it parent is an
892 inline element that that rule applies to. This behaviour was
893 reverse engineered from Netscape 3.0
895 private static boolean afterSpace(Node node)
900 if (node == null || node.tag == null || !((node.tag.model & Dict.CM_INLINE) != 0))
907 if (prev.type == Node.TextNode && prev.end > prev.start)
909 c = ((int)prev.textarray[prev.end - 1]) & 0xFF; // Convert to unsigned.
911 if (c == 160 || c == ' ' || c == '\n')
918 return afterSpace(node.parent);
921 private void printTag(Lexer lexer, Out fout, short mode, int indent, Node node)
925 TagTable tt = this.configuration.tt;
927 addC('<', linelen++);
929 if (node.type == Node.EndTag)
930 addC('/', linelen++);
933 for (int i = 0; i < p.length(); i++ )
934 addC((int)Lexer.foldCase(p.charAt(i),
935 this.configuration.UpperCaseTags,
936 this.configuration.XmlTags),
939 printAttrs(fout, indent, node, node.attributes);
941 if ((this.configuration.XmlOut || lexer != null && lexer.isvoyager) &&
942 (node.type == Node.StartEndTag || (node.tag.model & Dict.CM_EMPTY) != 0))
944 addC(' ', linelen++); /* compatibility hack */
945 addC('/', linelen++);
948 addC('>', linelen++);;
950 if (node.type != Node.StartEndTag && !((mode & PREFORMATTED) != 0))
952 if (indent + linelen >= this.configuration.wraplen)
953 wrapLine(fout, indent);
955 if (indent + linelen < this.configuration.wraplen)
958 wrap after start tag if is <br/> or if it's not
959 inline or it is an empty tag followed by </a>
961 if (afterSpace(node))
963 if (!((mode & NOWRAP) != 0) &&
964 (!((node.tag.model & Dict.CM_INLINE) != 0) ||
965 (node.tag == tt.tagBr) ||
966 (((node.tag.model & Dict.CM_EMPTY) != 0) &&
968 node.parent.tag == tt.tagA)))
975 condFlushLine(fout, indent);
979 private void printEndTag(Out fout, short mode, int indent, Node node)
985 Netscape ignores SGML standard by not ignoring a
986 line break before </A> or </U> etc. To avoid rendering
987 this as an underlined space, I disable line wrapping
988 before inline end tags by the #if 0 ... #endif
991 if (indent + linelen < this.configuration.wraplen && !((mode & NOWRAP) != 0))
995 addC('<', linelen++);
996 addC('/', linelen++);
999 for (int i = 0; i < p.length(); i++ )
1000 addC((int)Lexer.foldCase(p.charAt(i),
1001 this.configuration.UpperCaseTags,
1002 this.configuration.XmlTags),
1005 addC('>', linelen++);
1008 private void printComment(Out fout, int indent, Node node)
1010 if (indent + linelen < this.configuration.wraplen)
1013 addC('<', linelen++);
1014 addC('!', linelen++);
1015 addC('-', linelen++);
1016 addC('-', linelen++);
1018 if (linelen < this.configuration.wraplen)
1021 printText(fout, COMMENT, indent,
1022 node.textarray, node.start, node.end);
1024 if (indent + linelen < this.configuration.wraplen)
1027 // See Lexer.java: AQ 8Jul2000
1028 addC('-', linelen++);
1029 addC('-', linelen++);
1030 addC('>', linelen++);
1033 flushLine(fout, indent);
1036 private void printDocType(Out fout, int indent, Node node)
1038 boolean q = this.configuration.QuoteMarks;
1040 this.configuration.QuoteMarks = false;
1042 if (indent + linelen < this.configuration.wraplen)
1045 condFlushLine(fout, indent);
1047 addC('<', linelen++);
1048 addC('!', linelen++);
1049 addC('D', linelen++);
1050 addC('O', linelen++);
1051 addC('C', linelen++);
1052 addC('T', linelen++);
1053 addC('Y', linelen++);
1054 addC('P', linelen++);
1055 addC('E', linelen++);
1056 addC(' ', linelen++);
1058 if (indent + linelen < this.configuration.wraplen)
1061 printText(fout, (short)0, indent,
1062 node.textarray, node.start, node.end);
1064 if (linelen < this.configuration.wraplen)
1067 addC('>', linelen++);
1068 this.configuration.QuoteMarks = q;
1069 condFlushLine(fout, indent);
1072 private void printPI(Out fout, int indent, Node node)
1074 if (indent + linelen < this.configuration.wraplen)
1077 addC('<', linelen++);
1078 addC('?', linelen++);
1080 /* set CDATA to pass < and > unescaped */
1081 printText(fout, CDATA, indent,
1082 node.textarray, node.start, node.end);
1084 if (node.textarray[node.end - 1] != (byte)'?')
1085 addC('?', linelen++);
1087 addC('>', linelen++);
1088 condFlushLine(fout, indent);
1091 /* note ASP and JSTE share <% ... %> syntax */
1092 private void printAsp(Out fout, int indent, Node node)
1094 int savewraplen = this.configuration.wraplen;
1096 /* disable wrapping if so requested */
1098 if (!this.configuration.WrapAsp || !this.configuration.WrapJste)
1099 this.configuration.wraplen = 0xFFFFFF; /* a very large number */
1100 if (false) { //#if 0
1101 if (indent + linelen < this.configuration.wraplen)
1105 addC('<', linelen++);
1106 addC('%', linelen++);
1108 printText(fout, (this.configuration.WrapAsp ? CDATA : COMMENT), indent,
1109 node.textarray, node.start, node.end);
1111 addC('%', linelen++);
1112 addC('>', linelen++);
1113 /* condFlushLine(fout, indent); */
1114 this.configuration.wraplen = savewraplen;
1117 /* JSTE also supports <# ... #> syntax */
1118 private void printJste(Out fout, int indent, Node node)
1120 int savewraplen = this.configuration.wraplen;
1122 /* disable wrapping if so requested */
1124 if (!this.configuration.WrapJste)
1125 this.configuration.wraplen = 0xFFFFFF; /* a very large number */
1127 addC('<', linelen++);
1128 addC('#', linelen++);
1130 printText(fout, (this.configuration.WrapJste ? CDATA : COMMENT), indent,
1131 node.textarray, node.start, node.end);
1133 addC('#', linelen++);
1134 addC('>', linelen++);
1135 /* condFlushLine(fout, indent); */
1136 this.configuration.wraplen = savewraplen;
1139 /* PHP is based on XML processing instructions */
1140 private void printPhp(Out fout, int indent, Node node)
1142 int savewraplen = this.configuration.wraplen;
1144 /* disable wrapping if so requested */
1146 if (!this.configuration.WrapPhp)
1147 this.configuration.wraplen = 0xFFFFFF; /* a very large number */
1149 if (false) { //#if 0
1150 if (indent + linelen < this.configuration.wraplen)
1153 addC('<', linelen++);
1154 addC('?', linelen++);
1156 printText(fout, (this.configuration.WrapPhp ? CDATA : COMMENT), indent,
1157 node.textarray, node.start, node.end);
1159 addC('?', linelen++);
1160 addC('>', linelen++);
1161 /* PCondFlushLine(fout, indent); */
1162 this.configuration.wraplen = savewraplen;
1165 private void printCDATA(Out fout, int indent, Node node)
1167 int savewraplen = this.configuration.wraplen;
1169 condFlushLine(fout, indent);
1171 /* disable wrapping */
1173 this.configuration.wraplen = 0xFFFFFF; /* a very large number */
1175 addC('<', linelen++);
1176 addC('!', linelen++);
1177 addC('[', linelen++);
1178 addC('C', linelen++);
1179 addC('D', linelen++);
1180 addC('A', linelen++);
1181 addC('T', linelen++);
1182 addC('A', linelen++);
1183 addC('[', linelen++);
1185 printText(fout, COMMENT, indent,
1186 node.textarray, node.start, node.end);
1188 addC(']', linelen++);
1189 addC(']', linelen++);
1190 addC('>', linelen++);
1191 condFlushLine(fout, indent);
1192 this.configuration.wraplen = savewraplen;
1195 private void printSection(Out fout, int indent, Node node)
1197 int savewraplen = this.configuration.wraplen;
1199 /* disable wrapping if so requested */
1201 if (!this.configuration.WrapSection)
1202 this.configuration.wraplen = 0xFFFFFF; /* a very large number */
1204 if (false) { //#if 0
1205 if (indent + linelen < this.configuration.wraplen)
1208 addC('<', linelen++);
1209 addC('!', linelen++);
1210 addC('[', linelen++);
1212 printText(fout, (this.configuration.WrapSection ? CDATA : COMMENT), indent,
1213 node.textarray, node.start, node.end);
1215 addC(']', linelen++);
1216 addC('>', linelen++);
1217 /* PCondFlushLine(fout, indent); */
1218 this.configuration.wraplen = savewraplen;
1221 private boolean shouldIndent(Node node)
1223 TagTable tt = this.configuration.tt;
1225 if (!this.configuration.IndentContent)
1228 if (this.configuration.SmartIndent)
1230 if (node.content != null && ((node.tag.model & Dict.CM_NO_INDENT) != 0))
1232 for (node = node.content; node != null; node = node.next)
1233 if (node.tag != null && (node.tag.model & Dict.CM_BLOCK) != 0)
1239 if ((node.tag.model & Dict.CM_HEADING) != 0)
1242 if (node.tag == tt.tagP)
1245 if (node.tag == tt.tagTitle)
1249 if ((node.tag.model & (Dict.CM_FIELD | Dict.CM_OBJECT)) != 0)
1252 if (node.tag == tt.tagMap)
1255 return !((node.tag.model & Dict.CM_INLINE) != 0);
1258 public void printTree(Out fout, short mode, int indent,
1259 Lexer lexer, Node node)
1262 TagTable tt = this.configuration.tt;
1267 if (node.type == Node.TextNode)
1268 printText(fout, mode, indent,
1269 node.textarray, node.start, node.end);
1270 else if (node.type == Node.CommentTag)
1272 printComment(fout, indent, node);
1274 else if (node.type == Node.RootNode)
1276 for (content = node.content;
1278 content = content.next)
1279 printTree(fout, mode, indent, lexer, content);
1281 else if (node.type == Node.DocTypeTag)
1282 printDocType(fout, indent, node);
1283 else if (node.type == Node.ProcInsTag)
1284 printPI(fout, indent, node);
1285 else if (node.type == Node.CDATATag)
1286 printCDATA(fout, indent, node);
1287 else if (node.type == Node.SectionTag)
1288 printSection(fout, indent, node);
1289 else if (node.type == Node.AspTag)
1290 printAsp(fout, indent, node);
1291 else if (node.type == Node.JsteTag)
1292 printJste(fout, indent, node);
1293 else if (node.type == Node.PhpTag)
1294 printPhp(fout, indent, node);
1295 else if ((node.tag.model & Dict.CM_EMPTY) != 0 || node.type == Node.StartEndTag)
1297 if (!((node.tag.model & Dict.CM_INLINE) != 0))
1298 condFlushLine(fout, indent);
1300 if (node.tag == tt.tagBr && node.prev != null &&
1301 node.prev.tag != tt.tagBr && this.configuration.BreakBeforeBR)
1302 flushLine(fout, indent);
1304 if (this.configuration.MakeClean && node.tag == tt.tagWbr)
1305 printString(fout, indent, " ");
1307 printTag(lexer, fout, mode, indent, node);
1309 if (node.tag == tt.tagParam || node.tag == tt.tagArea)
1310 condFlushLine(fout, indent);
1311 else if (node.tag == tt.tagBr || node.tag == tt.tagHr)
1312 flushLine(fout, indent);
1314 else /* some kind of container element */
1316 if (node.tag != null && node.tag.parser == ParserImpl.getParsePre())
1318 condFlushLine(fout, indent);
1321 condFlushLine(fout, indent);
1322 printTag(lexer, fout, mode, indent, node);
1323 flushLine(fout, indent);
1325 for (content = node.content;
1327 content = content.next)
1328 printTree(fout, (short)(mode | PREFORMATTED | NOWRAP), indent, lexer, content);
1330 condFlushLine(fout, indent);
1331 printEndTag(fout, mode, indent, node);
1332 flushLine(fout, indent);
1334 if (this.configuration.IndentContent == false && node.next != null)
1335 flushLine(fout, indent);
1337 else if (node.tag == tt.tagStyle || node.tag == tt.tagScript)
1339 condFlushLine(fout, indent);
1342 condFlushLine(fout, indent);
1343 printTag(lexer, fout, mode, indent, node);
1344 flushLine(fout, indent);
1346 for (content = node.content;
1348 content = content.next)
1349 printTree(fout, (short)(mode | PREFORMATTED | NOWRAP |CDATA), indent, lexer, content);
1351 condFlushLine(fout, indent);
1352 printEndTag(fout, mode, indent, node);
1353 flushLine(fout, indent);
1355 if (this.configuration.IndentContent == false && node.next != null)
1356 flushLine(fout, indent);
1358 else if ((node.tag.model & Dict.CM_INLINE) != 0)
1360 if (this.configuration.MakeClean)
1362 /* discards <font> and </font> tags */
1363 if (node.tag == tt.tagFont)
1365 for (content = node.content;
1367 content = content.next)
1368 printTree(fout, mode, indent, lexer, content);
1372 /* replace <nobr>...</nobr> by or   etc. */
1373 if (node.tag == tt.tagNobr)
1375 for (content = node.content;
1377 content = content.next)
1378 printTree(fout, (short)(mode|NOWRAP), indent, lexer, content);
1383 /* otherwise a normal inline element */
1385 printTag(lexer, fout, mode, indent, node);
1387 /* indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET */
1389 if (shouldIndent(node))
1391 condFlushLine(fout, indent);
1392 indent += this.configuration.spaces;
1394 for (content = node.content;
1396 content = content.next)
1397 printTree(fout, mode, indent, lexer, content);
1399 condFlushLine(fout, indent);
1400 indent -= this.configuration.spaces;
1401 condFlushLine(fout, indent);
1406 for (content = node.content;
1408 content = content.next)
1409 printTree(fout, mode, indent, lexer, content);
1412 printEndTag(fout, mode, indent, node);
1414 else /* other tags */
1416 condFlushLine(fout, indent);
1418 if (this.configuration.SmartIndent && node.prev != null)
1419 flushLine(fout, indent);
1421 if (this.configuration.HideEndTags == false ||
1422 !(node.tag != null && ((node.tag.model & Dict.CM_OMITST) != 0)))
1424 printTag(lexer, fout, mode, indent, node);
1426 if (shouldIndent(node))
1427 condFlushLine(fout, indent);
1428 else if ((node.tag.model & Dict.CM_HTML) != 0 ||
1429 node.tag == tt.tagNoframes ||
1430 ((node.tag.model & Dict.CM_HEAD) != 0 &&
1431 !(node.tag == tt.tagTitle)))
1432 flushLine(fout, indent);
1435 if (node.tag == tt.tagBody && this.configuration.BurstSlides)
1436 printSlide(fout, mode, (this.configuration.IndentContent ? indent+this.configuration.spaces : indent), lexer);
1441 for (content = node.content;
1442 content != null; content = content.next)
1444 /* kludge for naked text before block level tag */
1445 if (last != null && !this.configuration.IndentContent && last.type == Node.TextNode &&
1446 content.tag != null && (content.tag.model & Dict.CM_BLOCK) != 0)
1448 flushLine(fout, indent);
1449 flushLine(fout, indent);
1452 printTree(fout, mode,
1453 (shouldIndent(node) ? indent+this.configuration.spaces : indent), lexer, content);
1459 /* don't flush line for td and th */
1460 if (shouldIndent(node) ||
1461 (((node.tag.model & Dict.CM_HTML) != 0 || node.tag == tt.tagNoframes ||
1462 ((node.tag.model & Dict.CM_HEAD) != 0 && !(node.tag == tt.tagTitle)))
1463 && this.configuration.HideEndTags == false))
1465 condFlushLine(fout, (this.configuration.IndentContent ? indent+this.configuration.spaces : indent));
1467 if (this.configuration.HideEndTags == false || !((node.tag.model & Dict.CM_OPT) != 0))
1469 printEndTag(fout, mode, indent, node);
1470 flushLine(fout, indent);
1475 if (this.configuration.HideEndTags == false || !((node.tag.model & Dict.CM_OPT) != 0))
1476 printEndTag(fout, mode, indent, node);
1478 flushLine(fout, indent);
1481 if (this.configuration.IndentContent == false &&
1482 node.next != null &&
1483 this.configuration.HideEndTags == false &&
1484 (node.tag.model & (Dict.CM_BLOCK|Dict.CM_LIST|Dict.CM_DEFLIST|Dict.CM_TABLE)) != 0)
1486 flushLine(fout, indent);
1492 public void printXMLTree(Out fout, short mode, int indent,
1493 Lexer lexer, Node node)
1495 TagTable tt = this.configuration.tt;
1500 if (node.type == Node.TextNode)
1502 printText(fout, mode, indent,
1503 node.textarray, node.start, node.end);
1505 else if (node.type == Node.CommentTag)
1507 condFlushLine(fout, indent);
1508 printComment(fout, 0, node);
1509 condFlushLine(fout, 0);
1511 else if (node.type == Node.RootNode)
1515 for (content = node.content;
1517 content = content.next)
1518 printXMLTree(fout, mode, indent, lexer, content);
1520 else if (node.type == Node.DocTypeTag)
1521 printDocType(fout, indent, node);
1522 else if (node.type == Node.ProcInsTag)
1523 printPI(fout, indent, node);
1524 else if (node.type == Node.SectionTag)
1525 printSection(fout, indent, node);
1526 else if (node.type == Node.AspTag)
1527 printAsp(fout, indent, node);
1528 else if (node.type == Node.JsteTag)
1529 printJste(fout, indent, node);
1530 else if (node.type == Node.PhpTag)
1531 printPhp(fout, indent, node);
1532 else if ((node.tag.model & Dict.CM_EMPTY) != 0 || node.type == Node.StartEndTag)
1534 condFlushLine(fout, indent);
1535 printTag(lexer, fout, mode, indent, node);
1536 flushLine(fout, indent);
1538 if (node.next != null)
1539 flushLine(fout, indent);
1541 else /* some kind of container element */
1544 boolean mixed = false;
1547 for (content = node.content; content != null; content = content.next)
1549 if (content.type == Node.TextNode)
1556 condFlushLine(fout, indent);
1558 if (ParserImpl.XMLPreserveWhiteSpace(node, tt))
1567 cindent = indent + this.configuration.spaces;
1569 printTag(lexer, fout, mode, indent, node);
1572 flushLine(fout, indent);
1574 for (content = node.content;
1576 content = content.next)
1577 printXMLTree(fout, mode, cindent, lexer, content);
1580 condFlushLine(fout, cindent);
1581 printEndTag(fout, mode, indent, node);
1582 condFlushLine(fout, indent);
1584 if (node.next != null)
1585 flushLine(fout, indent);
1590 /* split parse tree by h2 elements and output to separate files */
1592 /* counts number of h2 children belonging to node */
1593 public int countSlides(Node node)
1596 TagTable tt = this.configuration.tt;
1598 for (node = node.content; node != null; node = node.next)
1599 if (node.tag == tt.tagH2)
1606 inserts a space gif called "dot.gif" to ensure
1607 that the slide is at least n pixels high
1609 private void printVertSpacer(Out fout, int indent)
1611 condFlushLine(fout, indent);
1612 printString(fout, indent ,
1613 "<img width=\"0\" height=\"0\" hspace=\"1\" src=\"dot.gif\" vspace=\"%d\" align=\"left\">");
1614 condFlushLine(fout, indent);
1617 private void printNavBar(Out fout, int indent)
1621 condFlushLine(fout, indent);
1622 printString(fout, indent , "<center><small>");
1626 buf = "<a href=\"slide" +
1627 (new Integer(slide - 1)).toString() +
1628 ".html\">previous</a> | ";
1629 printString(fout, indent , buf);
1630 condFlushLine(fout, indent);
1633 printString(fout, indent , "<a href=\"slide1.html\">start</a> | ");
1635 printString(fout, indent , "<a href=\"slide1.html\">start</a>");
1637 condFlushLine(fout, indent);
1642 buf = "<a href=\"slide" +
1643 (new Integer(slide + 1)).toString() +
1645 printString(fout, indent , buf);
1648 printString(fout, indent , "</small></center>");
1649 condFlushLine(fout, indent);
1653 Called from printTree to print the content of a slide from
1654 the node slidecontent. On return slidecontent points to the
1655 node starting the next slide or null. The variables slide
1656 and count are used to customise the navigation bar.
1658 public void printSlide(Out fout, short mode, int indent, Lexer lexer)
1661 TagTable tt = this.configuration.tt;
1663 /* insert div for onclick handler */
1665 s = "<div onclick=\"document.location='slide" +
1666 (new Integer(slide < count ? slide + 1 : 1)).toString() +
1668 printString(fout, indent, s);
1669 condFlushLine(fout, indent);
1671 /* first print the h2 element and navbar */
1672 if (slidecontent.tag == tt.tagH2)
1674 printNavBar(fout, indent);
1676 /* now print an hr after h2 */
1678 addC('<', linelen++);
1681 addC((int)Lexer.foldCase('h',
1682 this.configuration.UpperCaseTags,
1683 this.configuration.XmlTags),
1685 addC((int)Lexer.foldCase('r',
1686 this.configuration.UpperCaseTags,
1687 this.configuration.XmlTags),
1690 if (this.configuration.XmlOut == true)
1691 printString(fout, indent , " />");
1693 addC('>', linelen++);
1696 if (this.configuration.IndentContent == true)
1697 condFlushLine(fout, indent);
1699 /* PrintVertSpacer(fout, indent); */
1701 /*condFlushLine(fout, indent); */
1703 /* print the h2 element */
1704 printTree(fout, mode,
1705 (this.configuration.IndentContent ? indent+this.configuration.spaces : indent), lexer, slidecontent);
1707 slidecontent = slidecontent.next;
1710 /* now continue until we reach the next h2 */
1713 content = slidecontent;
1715 for (; content != null; content = content.next)
1717 if (content.tag == tt.tagH2)
1720 /* kludge for naked text before block level tag */
1721 if (last != null && !this.configuration.IndentContent && last.type == Node.TextNode &&
1722 content.tag != null && (content.tag.model & Dict.CM_BLOCK) != 0)
1724 flushLine(fout, indent);
1725 flushLine(fout, indent);
1728 printTree(fout, mode,
1729 (this.configuration.IndentContent ? indent+this.configuration.spaces : indent), lexer, content);
1734 slidecontent = content;
1736 /* now print epilog */
1738 condFlushLine(fout, indent);
1740 printString(fout, indent , "<br clear=\"all\">");
1741 condFlushLine(fout, indent);
1743 addC('<', linelen++);
1746 addC((int)Lexer.foldCase('h',
1747 this.configuration.UpperCaseTags,
1748 this.configuration.XmlTags),
1750 addC((int)Lexer.foldCase('r',
1751 this.configuration.UpperCaseTags,
1752 this.configuration.XmlTags),
1755 if (this.configuration.XmlOut == true)
1756 printString(fout, indent , " />");
1758 addC('>', linelen++);
1761 if (this.configuration.IndentContent == true)
1762 condFlushLine(fout, indent);
1764 printNavBar(fout, indent);
1766 /* end tag for div */
1767 printString(fout, indent, "</div>");
1768 condFlushLine(fout, indent);
1773 Add meta element for page transition effect, this works on IE but not NS
1776 public void addTransitionEffect(Lexer lexer, Node root, short effect, double duration)
1778 Node head = root.findHEAD(lexer.configuration.tt);
1781 if (0 <= effect && effect <= 23)
1782 transition = "revealTrans(Duration=" +
1783 (new Double(duration)).toString() +
1784 ",Transition=" + effect + ")";
1786 transition = "blendTrans(Duration=" +
1787 (new Double(duration)).toString() + ")";
1791 Node meta = lexer.inferredTag("meta");
1792 meta.addAttribute("http-equiv", "Page-Enter");
1793 meta.addAttribute("content", transition);
1794 Node.insertNodeAtStart(head, meta);
1798 public void createSlides(Lexer lexer, Node root)
1802 Out out = new OutImpl();
1804 body = root.findBody(lexer.configuration.tt);
1805 count = countSlides(body);
1806 slidecontent = body.content;
1807 addTransitionEffect(lexer, root, EFFECT_BLEND, 3.0);
1809 for (slide = 1; slide <= count; ++slide)
1811 buf = "slide" + slide + ".html";
1812 out.state = StreamIn.FSM_ASCII;
1813 out.encoding = this.configuration.CharEncoding;
1817 out.out = new FileOutputStream(buf);
1818 printTree(out, (short)0, 0, lexer, root);
1822 catch (IOException e)
1824 System.err.println(buf + e.toString() );
1829 delete superfluous slides by deleting slideN.html
1830 for N = count+1, count+2, etc. until no such file
1836 buf = "slide" + slide + "html";
1838 if (!(new File(buf)).delete())