2 * @(#)PPrint.java 1.11 2000/08/16
6 package net.sourceforge.phpdt.tidy;
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
46 import java.io.FileOutputStream;
47 import java.io.IOException;
51 /* page transition effects */
53 public static final short EFFECT_BLEND = -1;
54 public static final short EFFECT_BOX_IN = 0;
55 public static final short EFFECT_BOX_OUT = 1;
56 public static final short EFFECT_CIRCLE_IN = 2;
57 public static final short EFFECT_CIRCLE_OUT = 3;
58 public static final short EFFECT_WIPE_UP = 4;
59 public static final short EFFECT_WIPE_DOWN = 5;
60 public static final short EFFECT_WIPE_RIGHT = 6;
61 public static final short EFFECT_WIPE_LEFT = 7;
62 public static final short EFFECT_VERT_BLINDS = 8;
63 public static final short EFFECT_HORZ_BLINDS = 9;
64 public static final short EFFECT_CHK_ACROSS = 10;
65 public static final short EFFECT_CHK_DOWN = 11;
66 public static final short EFFECT_RND_DISSOLVE = 12;
67 public static final short EFFECT_SPLIT_VIRT_IN = 13;
68 public static final short EFFECT_SPLIT_VIRT_OUT = 14;
69 public static final short EFFECT_SPLIT_HORZ_IN = 15;
70 public static final short EFFECT_SPLIT_HORZ_OUT = 16;
71 public static final short EFFECT_STRIPS_LEFT_DOWN = 17;
72 public static final short EFFECT_STRIPS_LEFT_UP = 18;
73 public static final short EFFECT_STRIPS_RIGHT_DOWN = 19;
74 public static final short EFFECT_STRIPS_RIGHT_UP = 20;
75 public static final short EFFECT_RND_BARS_HORZ = 21;
76 public static final short EFFECT_RND_BARS_VERT = 22;
77 public static final short EFFECT_RANDOM = 23;
79 private static final short NORMAL = 0;
80 private static final short PREFORMATTED = 1;
81 private static final short COMMENT = 2;
82 private static final short ATTRIBVALUE = 4;
83 private static final short NOWRAP = 8;
84 private static final short CDATA = 16;
86 private int[] linebuf = null;
87 private int lbufsize = 0;
88 private int linelen = 0;
89 private int wraphere = 0;
90 private boolean inAttVal = false;
91 private boolean InString = false;
93 private int slide = 0;
94 private int count = 0;
95 private Node slidecontent = null;
97 private Configuration configuration;
99 public PPrint(Configuration configuration)
101 this.configuration = configuration;
113 /* return one less that the number of bytes used by UTF-8 char */
114 /* str points to 1st byte, *ch initialized to 1st byte */
115 public static int getUTF8(byte[] str, int start, MutableInteger ch)
119 c = ((int)str[start]) & 0xFF; // Convert to unsigned.
121 if ((c & 0xE0) == 0xC0) /* 110X XXXX two bytes */
126 else if ((c & 0xF0) == 0xE0) /* 1110 XXXX three bytes */
131 else if ((c & 0xF8) == 0xF0) /* 1111 0XXX four bytes */
136 else if ((c & 0xFC) == 0xF8) /* 1111 10XX five bytes */
141 else if ((c & 0xFE) == 0xFC) /* 1111 110X six bytes */
147 else /* 0XXX XXXX one byte */
153 /* successor bytes should have the form 10XX XXXX */
154 for (i = 1; i < bytes; ++i)
156 c = ((int)str[start + i]) & 0xFF; // Convert to unsigned.
157 n = (n << 6) | (c & 0x3F);
164 /* store char c as UTF-8 encoded byte stream */
165 public static int putUTF8(byte[] buf, int start, int c)
168 buf[start++] = (byte)c;
171 buf[start++] = (byte)(0xC0 | (c >> 6));
172 buf[start++] = (byte)(0x80 | (c & 0x3F));
174 else if (c <= 0xFFFF)
176 buf[start++] = (byte)(0xE0 | (c >> 12));
177 buf[start++] = (byte)(0x80 | ((c >> 6) & 0x3F));
178 buf[start++] = (byte)(0x80 | (c & 0x3F));
180 else if (c <= 0x1FFFFF)
182 buf[start++] = (byte)(0xF0 | (c >> 18));
183 buf[start++] = (byte)(0x80 | ((c >> 12) & 0x3F));
184 buf[start++] = (byte)(0x80 | ((c >> 6) & 0x3F));
185 buf[start++] = (byte)(0x80 | (c & 0x3F));
189 buf[start++] = (byte)(0xF8 | (c >> 24));
190 buf[start++] = (byte)(0x80 | ((c >> 18) & 0x3F));
191 buf[start++] = (byte)(0x80 | ((c >> 12) & 0x3F));
192 buf[start++] = (byte)(0x80 | ((c >> 6) & 0x3F));
193 buf[start++] = (byte)(0x80 | (c & 0x3F));
199 private void addC(int c, int index)
201 if (index + 1 >= lbufsize)
203 while (index + 1 >= lbufsize)
208 lbufsize = lbufsize * 2;
211 int[] temp = new int[ lbufsize ];
213 System.arraycopy(linebuf, 0, temp, 0, index);
220 private void wrapLine(Out fout, int indent)
227 for (i = 0; i < indent; ++i)
230 for (i = 0; i < wraphere; ++i)
231 fout.outc(linebuf[i]);
236 fout.outc((int)'\\');
241 if (linelen > wraphere)
245 if (linebuf[wraphere] == ' ')
253 linebuf[p] = linebuf[q];
254 if (linebuf[q] == 0) break;
266 private void wrapAttrVal(Out fout, int indent, boolean inString)
270 for (i = 0; i < indent; ++i)
273 for (i = 0; i < wraphere; ++i)
274 fout.outc(linebuf[i]);
279 fout.outc((int)'\\');
283 if (linelen > wraphere)
287 if (linebuf[wraphere] == ' ')
295 linebuf[p] = linebuf[q];
296 if (linebuf[q] == 0) break;
308 public void flushLine(Out fout, int indent)
314 if (indent + linelen >= this.configuration.wraplen)
315 wrapLine(fout, indent);
317 if (!inAttVal || this.configuration.IndentAttributes)
319 for (i = 0; i < indent; ++i)
323 for (i = 0; i < linelen; ++i)
324 fout.outc(linebuf[i]);
333 public void condFlushLine(Out fout, int indent)
339 if (indent + linelen >= this.configuration.wraplen)
340 wrapLine(fout, indent);
342 if (!inAttVal || this.configuration.IndentAttributes)
344 for (i = 0; i < indent; ++i)
348 for (i = 0; i < linelen; ++i)
349 fout.outc(linebuf[i]);
358 private void printChar(int c, short mode)
362 if (c == ' ' && !((mode & (PREFORMATTED | COMMENT | ATTRIBVALUE)) != 0))
364 /* coerce a space character to a non-breaking space */
365 if ((mode & NOWRAP) != 0)
367 /* by default XML doesn't define */
368 if (this.configuration.NumEntities || this.configuration.XmlTags)
370 addC('&', linelen++);
371 addC('#', linelen++);
372 addC('1', linelen++);
373 addC('6', linelen++);
374 addC('0', linelen++);
375 addC(';', linelen++);
377 else /* otherwise use named entity */
379 addC('&', linelen++);
380 addC('n', linelen++);
381 addC('b', linelen++);
382 addC('s', linelen++);
383 addC('p', linelen++);
384 addC(';', linelen++);
392 /* comment characters are passed raw */
393 if ((mode & COMMENT) != 0)
399 /* except in CDATA map < to < etc. */
400 if (! ((mode & CDATA) != 0) )
404 addC('&', linelen++);
405 addC('l', linelen++);
406 addC('t', linelen++);
407 addC(';', linelen++);
413 addC('&', linelen++);
414 addC('g', linelen++);
415 addC('t', linelen++);
416 addC(';', linelen++);
421 naked '&' chars can be left alone or
422 quoted as & The latter is required
423 for XML where naked '&' are illegal.
425 if (c == '&' && this.configuration.QuoteAmpersand)
427 addC('&', linelen++);
428 addC('a', linelen++);
429 addC('m', linelen++);
430 addC('p', linelen++);
431 addC(';', linelen++);
435 if (c == '"' && this.configuration.QuoteMarks)
437 addC('&', linelen++);
438 addC('q', linelen++);
439 addC('u', linelen++);
440 addC('o', linelen++);
441 addC('t', linelen++);
442 addC(';', linelen++);
446 if (c == '\'' && this.configuration.QuoteMarks)
448 addC('&', linelen++);
449 addC('#', linelen++);
450 addC('3', linelen++);
451 addC('9', linelen++);
452 addC(';', linelen++);
456 if (c == 160 && this.configuration.CharEncoding != Configuration.RAW)
458 if (this.configuration.QuoteNbsp)
460 addC('&', linelen++);
462 if (this.configuration.NumEntities)
464 addC('#', linelen++);
465 addC('1', linelen++);
466 addC('6', linelen++);
467 addC('0', linelen++);
471 addC('n', linelen++);
472 addC('b', linelen++);
473 addC('s', linelen++);
474 addC('p', linelen++);
477 addC(';', linelen++);
486 /* otherwise ISO 2022 characters are passed raw */
487 if (this.configuration.CharEncoding == Configuration.ISO2022 ||
488 this.configuration.CharEncoding == Configuration.RAW)
494 /* if preformatted text, map to space */
495 if (c == 160 && ((mode & PREFORMATTED) != 0))
497 addC(' ', linelen++);
502 Filters from Word and PowerPoint often use smart
503 quotes resulting in character codes between 128
504 and 159. Unfortunately, the corresponding HTML 4.0
505 entities for these are not widely supported. The
506 following converts dashes and quotation marks to
507 the nearest ASCII equivalent. My thanks to
508 Andrzej Novosiolov for his help with this code.
511 if (this.configuration.MakeClean)
513 if (c >= 0x2013 && c <= 0x201E)
534 /* don't map latin-1 chars to entities */
535 if (this.configuration.CharEncoding == Configuration.LATIN1)
537 if (c > 255) /* multi byte chars */
539 if (!this.configuration.NumEntities)
541 entity = EntityTable.getDefaultEntityTable().entityName((short)c);
543 entity = "&" + entity + ";";
545 entity = "&#" + c + ";";
548 entity = "&#" + c + ";";
550 for (int i = 0; i < entity.length(); i++)
551 addC((int)entity.charAt(i), linelen++);
556 if (c > 126 && c < 160)
558 entity = "&#" + c + ";";
560 for (int i = 0; i < entity.length(); i++)
561 addC((int)entity.charAt(i), linelen++);
570 /* don't map utf8 chars to entities */
571 if (this.configuration.CharEncoding == Configuration.UTF8)
577 /* use numeric entities only for XML */
578 if (this.configuration.XmlTags)
580 /* if ASCII use numeric entities for chars > 127 */
581 if (c > 127 && this.configuration.CharEncoding == Configuration.ASCII)
583 entity = "&#" + c + ";";
585 for (int i = 0; i < entity.length(); i++)
586 addC((int)entity.charAt(i), linelen++);
591 /* otherwise output char raw */
596 /* default treatment for ASCII */
597 if (c > 126 || (c < ' ' && c != '\t'))
599 if (!this.configuration.NumEntities)
601 entity = EntityTable.getDefaultEntityTable().entityName((short)c);
603 entity = "&" + entity + ";";
605 entity = "&#" + c + ";";
608 entity = "&#" + c + ";";
610 for (int i = 0; i < entity.length(); i++)
611 addC((int)entity.charAt(i), linelen++);
620 The line buffer is uint not char so we can
621 hold Unicode values unencoded. The translation
622 to UTF-8 is deferred to the outc routine called
623 to flush the line buffer.
625 private void printText(Out fout, short mode, int indent,
626 byte[] textarray, int start, int end)
629 MutableInteger ci = new MutableInteger();
631 for (i = start; i < end; ++i)
633 if (indent + linelen >= this.configuration.wraplen)
634 wrapLine(fout, indent);
636 c = ((int)textarray[i]) & 0xFF; // Convert to unsigned.
638 /* look for UTF-8 multibyte character */
641 i += getUTF8(textarray, i, ci);
647 flushLine(fout, indent);
655 private void printString(Out fout, int indent, String str)
657 for (int i = 0; i < str.length(); i++ )
658 addC((int)str.charAt(i), linelen++);
661 private void printAttrValue(Out fout, int indent, String value, int delim, boolean wrappable)
664 MutableInteger ci = new MutableInteger();
665 boolean wasinstring = false;
666 byte[] valueChars = null;
668 short mode = (wrappable ? (short)(NORMAL | ATTRIBVALUE) :
669 (short)(PREFORMATTED | ATTRIBVALUE));
673 valueChars = Lexer.getBytes(value);
676 /* look for ASP, Tango or PHP instructions for computed attribute value */
677 if (valueChars != null && valueChars.length >= 5 && valueChars[0] == '<')
679 if (valueChars[1] == '%' || valueChars[1] == '@'||
680 (new String(valueChars, 0, 5)).equals("<?php"))
687 addC('=', linelen++);
689 /* don't wrap after "=" for xml documents */
690 if (!this.configuration.XmlOut) {
692 if (indent + linelen < this.configuration.wraplen)
695 if (indent + linelen >= this.configuration.wraplen)
696 wrapLine(fout, indent);
698 if (indent + linelen < this.configuration.wraplen)
701 condFlushLine(fout, indent);
704 addC(delim, linelen++);
711 while (i < valueChars.length)
713 c = ((int)valueChars[i]) & 0xFF; // Convert to unsigned.
715 if (wrappable && c == ' ' && indent + linelen < this.configuration.wraplen)
718 wasinstring = InString;
721 if (wrappable && wraphere > 0 && indent + linelen >= this.configuration.wraplen)
722 wrapAttrVal(fout, indent, wasinstring);
728 entity = (c == '"' ? """ : "'");
730 for (int j = 0; j < entity.length(); j++ )
731 addC(entity.charAt(j), linelen++);
738 if (this.configuration.QuoteMarks)
740 addC('&', linelen++);
741 addC('q', linelen++);
742 addC('u', linelen++);
743 addC('o', linelen++);
744 addC('t', linelen++);
745 addC(';', linelen++);
748 addC('"', linelen++);
751 InString = !InString;
758 if (this.configuration.QuoteMarks)
760 addC('&', linelen++);
761 addC('#', linelen++);
762 addC('3', linelen++);
763 addC('9', linelen++);
764 addC(';', linelen++);
767 addC('\'', linelen++);
770 InString = !InString;
776 /* look for UTF-8 multibyte character */
779 i += getUTF8(valueChars, i, ci);
787 flushLine(fout, indent);
796 addC(delim, linelen++);
799 private void printAttribute(Out fout, int indent, Node node, AttVal attr)
802 boolean wrappable = false;
804 if (this.configuration.IndentAttributes)
806 flushLine(fout, indent);
807 indent += this.configuration.spaces;
810 name = attr.attribute;
812 if (indent + linelen >= this.configuration.wraplen)
813 wrapLine(fout, indent);
815 if (!this.configuration.XmlTags && !this.configuration.XmlOut && attr.dict != null)
817 if (AttributeTable.getDefaultAttributeTable().isScript(name))
818 wrappable = this.configuration.WrapScriptlets;
819 else if (!attr.dict.nowrap && this.configuration.WrapAttVals)
823 if (indent + linelen < this.configuration.wraplen)
826 addC(' ', linelen++);
830 condFlushLine(fout, indent);
831 addC(' ', linelen++);
834 for (int i = 0; i < name.length(); i++ )
835 addC((int)Lexer.foldCase(name.charAt(i),
836 this.configuration.UpperCaseAttrs,
837 this.configuration.XmlTags),
840 if (indent + linelen >= this.configuration.wraplen)
841 wrapLine(fout, indent);
843 if (attr.value == null)
845 if (this.configuration.XmlTags || this.configuration.XmlOut)
846 printAttrValue(fout, indent, attr.attribute, attr.delim, true);
847 else if (!attr.isBoolAttribute() && !Node.isNewNode(node))
848 printAttrValue(fout, indent, "", attr.delim, true);
849 else if (indent + linelen < this.configuration.wraplen)
854 printAttrValue(fout, indent, attr.value, attr.delim, wrappable);
857 private void printAttrs(Out fout, int indent,
858 Node node, AttVal attr)
862 if (attr.next != null)
863 printAttrs(fout, indent, node, attr.next);
865 if (attr.attribute != null)
866 printAttribute(fout, indent, node, attr);
867 else if (attr.asp != null)
869 addC(' ', linelen++);
870 printAsp(fout, indent, attr.asp);
872 else if (attr.php != null)
874 addC(' ', linelen++);
875 printPhp(fout, indent, attr.php);
879 /* add xml:space attribute to pre and other elements */
880 if (configuration.XmlOut &&
881 configuration.XmlSpace &&
882 ParserImpl.XMLPreserveWhiteSpace(node, configuration.tt) &&
883 node.getAttrByName("xml:space") == null)
884 printString(fout, indent, " xml:space=\"preserve\"");
888 Line can be wrapped immediately after inline start tag provided
889 if follows a text node ending in a space, or it parent is an
890 inline element that that rule applies to. This behaviour was
891 reverse engineered from Netscape 3.0
893 private static boolean afterSpace(Node node)
898 if (node == null || node.tag == null || !((node.tag.model & Dict.CM_INLINE) != 0))
905 if (prev.type == Node.TextNode && prev.end > prev.start)
907 c = ((int)prev.textarray[prev.end - 1]) & 0xFF; // Convert to unsigned.
909 if (c == 160 || c == ' ' || c == '\n')
916 return afterSpace(node.parent);
919 private void printTag(Lexer lexer, Out fout, short mode, int indent, Node node)
923 TagTable tt = this.configuration.tt;
925 addC('<', linelen++);
927 if (node.type == Node.EndTag)
928 addC('/', linelen++);
931 for (int i = 0; i < p.length(); i++ )
932 addC((int)Lexer.foldCase(p.charAt(i),
933 this.configuration.UpperCaseTags,
934 this.configuration.XmlTags),
937 printAttrs(fout, indent, node, node.attributes);
939 if ((this.configuration.XmlOut || lexer != null && lexer.isvoyager) &&
940 (node.type == Node.StartEndTag || (node.tag.model & Dict.CM_EMPTY) != 0))
942 addC(' ', linelen++); /* compatibility hack */
943 addC('/', linelen++);
946 addC('>', linelen++);;
948 if (node.type != Node.StartEndTag && !((mode & PREFORMATTED) != 0))
950 if (indent + linelen >= this.configuration.wraplen)
951 wrapLine(fout, indent);
953 if (indent + linelen < this.configuration.wraplen)
956 wrap after start tag if is <br/> or if it's not
957 inline or it is an empty tag followed by </a>
959 if (afterSpace(node))
961 if (!((mode & NOWRAP) != 0) &&
962 (!((node.tag.model & Dict.CM_INLINE) != 0) ||
963 (node.tag == tt.tagBr) ||
964 (((node.tag.model & Dict.CM_EMPTY) != 0) &&
966 node.parent.tag == tt.tagA)))
973 condFlushLine(fout, indent);
977 private void printEndTag(Out fout, short mode, int indent, Node node)
983 Netscape ignores SGML standard by not ignoring a
984 line break before </A> or </U> etc. To avoid rendering
985 this as an underlined space, I disable line wrapping
986 before inline end tags by the #if 0 ... #endif
989 if (indent + linelen < this.configuration.wraplen && !((mode & NOWRAP) != 0))
993 addC('<', linelen++);
994 addC('/', linelen++);
997 for (int i = 0; i < p.length(); i++ )
998 addC((int)Lexer.foldCase(p.charAt(i),
999 this.configuration.UpperCaseTags,
1000 this.configuration.XmlTags),
1003 addC('>', linelen++);
1006 private void printComment(Out fout, int indent, Node node)
1008 if (indent + linelen < this.configuration.wraplen)
1011 addC('<', linelen++);
1012 addC('!', linelen++);
1013 addC('-', linelen++);
1014 addC('-', linelen++);
1016 if (linelen < this.configuration.wraplen)
1019 printText(fout, COMMENT, indent,
1020 node.textarray, node.start, node.end);
1022 if (indent + linelen < this.configuration.wraplen)
1025 // See Lexer.java: AQ 8Jul2000
1026 addC('-', linelen++);
1027 addC('-', linelen++);
1028 addC('>', linelen++);
1031 flushLine(fout, indent);
1034 private void printDocType(Out fout, int indent, Node node)
1036 boolean q = this.configuration.QuoteMarks;
1038 this.configuration.QuoteMarks = false;
1040 if (indent + linelen < this.configuration.wraplen)
1043 condFlushLine(fout, indent);
1045 addC('<', linelen++);
1046 addC('!', linelen++);
1047 addC('D', linelen++);
1048 addC('O', linelen++);
1049 addC('C', linelen++);
1050 addC('T', linelen++);
1051 addC('Y', linelen++);
1052 addC('P', linelen++);
1053 addC('E', linelen++);
1054 addC(' ', linelen++);
1056 if (indent + linelen < this.configuration.wraplen)
1059 printText(fout, (short)0, indent,
1060 node.textarray, node.start, node.end);
1062 if (linelen < this.configuration.wraplen)
1065 addC('>', linelen++);
1066 this.configuration.QuoteMarks = q;
1067 condFlushLine(fout, indent);
1070 private void printPI(Out fout, int indent, Node node)
1072 if (indent + linelen < this.configuration.wraplen)
1075 addC('<', linelen++);
1076 addC('?', linelen++);
1078 /* set CDATA to pass < and > unescaped */
1079 printText(fout, CDATA, indent,
1080 node.textarray, node.start, node.end);
1082 if (node.textarray[node.end - 1] != (byte)'?')
1083 addC('?', linelen++);
1085 addC('>', linelen++);
1086 condFlushLine(fout, indent);
1089 /* note ASP and JSTE share <% ... %> syntax */
1090 private void printAsp(Out fout, int indent, Node node)
1092 int savewraplen = this.configuration.wraplen;
1094 /* disable wrapping if so requested */
1096 if (!this.configuration.WrapAsp || !this.configuration.WrapJste)
1097 this.configuration.wraplen = 0xFFFFFF; /* a very large number */
1098 if (false) { //#if 0
1099 if (indent + linelen < this.configuration.wraplen)
1103 addC('<', linelen++);
1104 addC('%', linelen++);
1106 printText(fout, (this.configuration.WrapAsp ? CDATA : COMMENT), indent,
1107 node.textarray, node.start, node.end);
1109 addC('%', linelen++);
1110 addC('>', linelen++);
1111 /* condFlushLine(fout, indent); */
1112 this.configuration.wraplen = savewraplen;
1115 /* JSTE also supports <# ... #> syntax */
1116 private void printJste(Out fout, int indent, Node node)
1118 int savewraplen = this.configuration.wraplen;
1120 /* disable wrapping if so requested */
1122 if (!this.configuration.WrapJste)
1123 this.configuration.wraplen = 0xFFFFFF; /* a very large number */
1125 addC('<', linelen++);
1126 addC('#', linelen++);
1128 printText(fout, (this.configuration.WrapJste ? CDATA : COMMENT), indent,
1129 node.textarray, node.start, node.end);
1131 addC('#', linelen++);
1132 addC('>', linelen++);
1133 /* condFlushLine(fout, indent); */
1134 this.configuration.wraplen = savewraplen;
1137 /* PHP is based on XML processing instructions */
1138 private void printPhp(Out fout, int indent, Node node)
1140 int savewraplen = this.configuration.wraplen;
1142 /* disable wrapping if so requested */
1144 if (!this.configuration.WrapPhp)
1145 this.configuration.wraplen = 0xFFFFFF; /* a very large number */
1147 if (false) { //#if 0
1148 if (indent + linelen < this.configuration.wraplen)
1151 addC('<', linelen++);
1152 addC('?', linelen++);
1154 printText(fout, (this.configuration.WrapPhp ? CDATA : COMMENT), indent,
1155 node.textarray, node.start, node.end);
1157 addC('?', linelen++);
1158 addC('>', linelen++);
1159 /* PCondFlushLine(fout, indent); */
1160 this.configuration.wraplen = savewraplen;
1163 private void printCDATA(Out fout, int indent, Node node)
1165 int savewraplen = this.configuration.wraplen;
1167 condFlushLine(fout, indent);
1169 /* disable wrapping */
1171 this.configuration.wraplen = 0xFFFFFF; /* a very large number */
1173 addC('<', linelen++);
1174 addC('!', linelen++);
1175 addC('[', linelen++);
1176 addC('C', linelen++);
1177 addC('D', linelen++);
1178 addC('A', linelen++);
1179 addC('T', linelen++);
1180 addC('A', linelen++);
1181 addC('[', linelen++);
1183 printText(fout, COMMENT, indent,
1184 node.textarray, node.start, node.end);
1186 addC(']', linelen++);
1187 addC(']', linelen++);
1188 addC('>', linelen++);
1189 condFlushLine(fout, indent);
1190 this.configuration.wraplen = savewraplen;
1193 private void printSection(Out fout, int indent, Node node)
1195 int savewraplen = this.configuration.wraplen;
1197 /* disable wrapping if so requested */
1199 if (!this.configuration.WrapSection)
1200 this.configuration.wraplen = 0xFFFFFF; /* a very large number */
1202 if (false) { //#if 0
1203 if (indent + linelen < this.configuration.wraplen)
1206 addC('<', linelen++);
1207 addC('!', linelen++);
1208 addC('[', linelen++);
1210 printText(fout, (this.configuration.WrapSection ? CDATA : COMMENT), indent,
1211 node.textarray, node.start, node.end);
1213 addC(']', linelen++);
1214 addC('>', linelen++);
1215 /* PCondFlushLine(fout, indent); */
1216 this.configuration.wraplen = savewraplen;
1219 private boolean shouldIndent(Node node)
1221 TagTable tt = this.configuration.tt;
1223 if (!this.configuration.IndentContent)
1226 if (this.configuration.SmartIndent)
1228 if (node.content != null && ((node.tag.model & Dict.CM_NO_INDENT) != 0))
1230 for (node = node.content; node != null; node = node.next)
1231 if (node.tag != null && (node.tag.model & Dict.CM_BLOCK) != 0)
1237 if ((node.tag.model & Dict.CM_HEADING) != 0)
1240 if (node.tag == tt.tagP)
1243 if (node.tag == tt.tagTitle)
1247 if ((node.tag.model & (Dict.CM_FIELD | Dict.CM_OBJECT)) != 0)
1250 if (node.tag == tt.tagMap)
1253 return !((node.tag.model & Dict.CM_INLINE) != 0);
1256 public void printTree(Out fout, short mode, int indent,
1257 Lexer lexer, Node node)
1260 TagTable tt = this.configuration.tt;
1265 if (node.type == Node.TextNode)
1266 printText(fout, mode, indent,
1267 node.textarray, node.start, node.end);
1268 else if (node.type == Node.CommentTag)
1270 printComment(fout, indent, node);
1272 else if (node.type == Node.RootNode)
1274 for (content = node.content;
1276 content = content.next)
1277 printTree(fout, mode, indent, lexer, content);
1279 else if (node.type == Node.DocTypeTag)
1280 printDocType(fout, indent, node);
1281 else if (node.type == Node.ProcInsTag)
1282 printPI(fout, indent, node);
1283 else if (node.type == Node.CDATATag)
1284 printCDATA(fout, indent, node);
1285 else if (node.type == Node.SectionTag)
1286 printSection(fout, indent, node);
1287 else if (node.type == Node.AspTag)
1288 printAsp(fout, indent, node);
1289 else if (node.type == Node.JsteTag)
1290 printJste(fout, indent, node);
1291 else if (node.type == Node.PhpTag)
1292 printPhp(fout, indent, node);
1293 else if ((node.tag.model & Dict.CM_EMPTY) != 0 || node.type == Node.StartEndTag)
1295 if (!((node.tag.model & Dict.CM_INLINE) != 0))
1296 condFlushLine(fout, indent);
1298 if (node.tag == tt.tagBr && node.prev != null &&
1299 node.prev.tag != tt.tagBr && this.configuration.BreakBeforeBR)
1300 flushLine(fout, indent);
1302 if (this.configuration.MakeClean && node.tag == tt.tagWbr)
1303 printString(fout, indent, " ");
1305 printTag(lexer, fout, mode, indent, node);
1307 if (node.tag == tt.tagParam || node.tag == tt.tagArea)
1308 condFlushLine(fout, indent);
1309 else if (node.tag == tt.tagBr || node.tag == tt.tagHr)
1310 flushLine(fout, indent);
1312 else /* some kind of container element */
1314 if (node.tag != null && node.tag.parser == ParserImpl.getParsePre())
1316 condFlushLine(fout, indent);
1319 condFlushLine(fout, indent);
1320 printTag(lexer, fout, mode, indent, node);
1321 flushLine(fout, indent);
1323 for (content = node.content;
1325 content = content.next)
1326 printTree(fout, (short)(mode | PREFORMATTED | NOWRAP), indent, lexer, content);
1328 condFlushLine(fout, indent);
1329 printEndTag(fout, mode, indent, node);
1330 flushLine(fout, indent);
1332 if (this.configuration.IndentContent == false && node.next != null)
1333 flushLine(fout, indent);
1335 else if (node.tag == tt.tagStyle || node.tag == tt.tagScript)
1337 condFlushLine(fout, indent);
1340 condFlushLine(fout, indent);
1341 printTag(lexer, fout, mode, indent, node);
1342 flushLine(fout, indent);
1344 for (content = node.content;
1346 content = content.next)
1347 printTree(fout, (short)(mode | PREFORMATTED | NOWRAP |CDATA), indent, lexer, content);
1349 condFlushLine(fout, indent);
1350 printEndTag(fout, mode, indent, node);
1351 flushLine(fout, indent);
1353 if (this.configuration.IndentContent == false && node.next != null)
1354 flushLine(fout, indent);
1356 else if ((node.tag.model & Dict.CM_INLINE) != 0)
1358 if (this.configuration.MakeClean)
1360 /* discards <font> and </font> tags */
1361 if (node.tag == tt.tagFont)
1363 for (content = node.content;
1365 content = content.next)
1366 printTree(fout, mode, indent, lexer, content);
1370 /* replace <nobr>...</nobr> by or   etc. */
1371 if (node.tag == tt.tagNobr)
1373 for (content = node.content;
1375 content = content.next)
1376 printTree(fout, (short)(mode|NOWRAP), indent, lexer, content);
1381 /* otherwise a normal inline element */
1383 printTag(lexer, fout, mode, indent, node);
1385 /* indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET */
1387 if (shouldIndent(node))
1389 condFlushLine(fout, indent);
1390 indent += this.configuration.spaces;
1392 for (content = node.content;
1394 content = content.next)
1395 printTree(fout, mode, indent, lexer, content);
1397 condFlushLine(fout, indent);
1398 indent -= this.configuration.spaces;
1399 condFlushLine(fout, indent);
1404 for (content = node.content;
1406 content = content.next)
1407 printTree(fout, mode, indent, lexer, content);
1410 printEndTag(fout, mode, indent, node);
1412 else /* other tags */
1414 condFlushLine(fout, indent);
1416 if (this.configuration.SmartIndent && node.prev != null)
1417 flushLine(fout, indent);
1419 if (this.configuration.HideEndTags == false ||
1420 !(node.tag != null && ((node.tag.model & Dict.CM_OMITST) != 0)))
1422 printTag(lexer, fout, mode, indent, node);
1424 if (shouldIndent(node))
1425 condFlushLine(fout, indent);
1426 else if ((node.tag.model & Dict.CM_HTML) != 0 ||
1427 node.tag == tt.tagNoframes ||
1428 ((node.tag.model & Dict.CM_HEAD) != 0 &&
1429 !(node.tag == tt.tagTitle)))
1430 flushLine(fout, indent);
1433 if (node.tag == tt.tagBody && this.configuration.BurstSlides)
1434 printSlide(fout, mode, (this.configuration.IndentContent ? indent+this.configuration.spaces : indent), lexer);
1439 for (content = node.content;
1440 content != null; content = content.next)
1442 /* kludge for naked text before block level tag */
1443 if (last != null && !this.configuration.IndentContent && last.type == Node.TextNode &&
1444 content.tag != null && (content.tag.model & Dict.CM_BLOCK) != 0)
1446 flushLine(fout, indent);
1447 flushLine(fout, indent);
1450 printTree(fout, mode,
1451 (shouldIndent(node) ? indent+this.configuration.spaces : indent), lexer, content);
1457 /* don't flush line for td and th */
1458 if (shouldIndent(node) ||
1459 (((node.tag.model & Dict.CM_HTML) != 0 || node.tag == tt.tagNoframes ||
1460 ((node.tag.model & Dict.CM_HEAD) != 0 && !(node.tag == tt.tagTitle)))
1461 && this.configuration.HideEndTags == false))
1463 condFlushLine(fout, (this.configuration.IndentContent ? indent+this.configuration.spaces : indent));
1465 if (this.configuration.HideEndTags == false || !((node.tag.model & Dict.CM_OPT) != 0))
1467 printEndTag(fout, mode, indent, node);
1468 flushLine(fout, indent);
1473 if (this.configuration.HideEndTags == false || !((node.tag.model & Dict.CM_OPT) != 0))
1474 printEndTag(fout, mode, indent, node);
1476 flushLine(fout, indent);
1479 if (this.configuration.IndentContent == false &&
1480 node.next != null &&
1481 this.configuration.HideEndTags == false &&
1482 (node.tag.model & (Dict.CM_BLOCK|Dict.CM_LIST|Dict.CM_DEFLIST|Dict.CM_TABLE)) != 0)
1484 flushLine(fout, indent);
1490 public void printXMLTree(Out fout, short mode, int indent,
1491 Lexer lexer, Node node)
1493 TagTable tt = this.configuration.tt;
1498 if (node.type == Node.TextNode)
1500 printText(fout, mode, indent,
1501 node.textarray, node.start, node.end);
1503 else if (node.type == Node.CommentTag)
1505 condFlushLine(fout, indent);
1506 printComment(fout, 0, node);
1507 condFlushLine(fout, 0);
1509 else if (node.type == Node.RootNode)
1513 for (content = node.content;
1515 content = content.next)
1516 printXMLTree(fout, mode, indent, lexer, content);
1518 else if (node.type == Node.DocTypeTag)
1519 printDocType(fout, indent, node);
1520 else if (node.type == Node.ProcInsTag)
1521 printPI(fout, indent, node);
1522 else if (node.type == Node.SectionTag)
1523 printSection(fout, indent, node);
1524 else if (node.type == Node.AspTag)
1525 printAsp(fout, indent, node);
1526 else if (node.type == Node.JsteTag)
1527 printJste(fout, indent, node);
1528 else if (node.type == Node.PhpTag)
1529 printPhp(fout, indent, node);
1530 else if ((node.tag.model & Dict.CM_EMPTY) != 0 || node.type == Node.StartEndTag)
1532 condFlushLine(fout, indent);
1533 printTag(lexer, fout, mode, indent, node);
1534 flushLine(fout, indent);
1536 if (node.next != null)
1537 flushLine(fout, indent);
1539 else /* some kind of container element */
1542 boolean mixed = false;
1545 for (content = node.content; content != null; content = content.next)
1547 if (content.type == Node.TextNode)
1554 condFlushLine(fout, indent);
1556 if (ParserImpl.XMLPreserveWhiteSpace(node, tt))
1565 cindent = indent + this.configuration.spaces;
1567 printTag(lexer, fout, mode, indent, node);
1570 flushLine(fout, indent);
1572 for (content = node.content;
1574 content = content.next)
1575 printXMLTree(fout, mode, cindent, lexer, content);
1578 condFlushLine(fout, cindent);
1579 printEndTag(fout, mode, indent, node);
1580 condFlushLine(fout, indent);
1582 if (node.next != null)
1583 flushLine(fout, indent);
1588 /* split parse tree by h2 elements and output to separate files */
1590 /* counts number of h2 children belonging to node */
1591 public int countSlides(Node node)
1594 TagTable tt = this.configuration.tt;
1596 for (node = node.content; node != null; node = node.next)
1597 if (node.tag == tt.tagH2)
1604 inserts a space gif called "dot.gif" to ensure
1605 that the slide is at least n pixels high
1607 private void printVertSpacer(Out fout, int indent)
1609 condFlushLine(fout, indent);
1610 printString(fout, indent ,
1611 "<img width=\"0\" height=\"0\" hspace=\"1\" src=\"dot.gif\" vspace=\"%d\" align=\"left\">");
1612 condFlushLine(fout, indent);
1615 private void printNavBar(Out fout, int indent)
1619 condFlushLine(fout, indent);
1620 printString(fout, indent , "<center><small>");
1624 buf = "<a href=\"slide" +
1625 (new Integer(slide - 1)).toString() +
1626 ".html\">previous</a> | ";
1627 printString(fout, indent , buf);
1628 condFlushLine(fout, indent);
1631 printString(fout, indent , "<a href=\"slide1.html\">start</a> | ");
1633 printString(fout, indent , "<a href=\"slide1.html\">start</a>");
1635 condFlushLine(fout, indent);
1640 buf = "<a href=\"slide" +
1641 (new Integer(slide + 1)).toString() +
1643 printString(fout, indent , buf);
1646 printString(fout, indent , "</small></center>");
1647 condFlushLine(fout, indent);
1651 Called from printTree to print the content of a slide from
1652 the node slidecontent. On return slidecontent points to the
1653 node starting the next slide or null. The variables slide
1654 and count are used to customise the navigation bar.
1656 public void printSlide(Out fout, short mode, int indent, Lexer lexer)
1659 TagTable tt = this.configuration.tt;
1661 /* insert div for onclick handler */
1663 s = "<div onclick=\"document.location='slide" +
1664 (new Integer(slide < count ? slide + 1 : 1)).toString() +
1666 printString(fout, indent, s);
1667 condFlushLine(fout, indent);
1669 /* first print the h2 element and navbar */
1670 if (slidecontent.tag == tt.tagH2)
1672 printNavBar(fout, indent);
1674 /* now print an hr after h2 */
1676 addC('<', linelen++);
1679 addC((int)Lexer.foldCase('h',
1680 this.configuration.UpperCaseTags,
1681 this.configuration.XmlTags),
1683 addC((int)Lexer.foldCase('r',
1684 this.configuration.UpperCaseTags,
1685 this.configuration.XmlTags),
1688 if (this.configuration.XmlOut == true)
1689 printString(fout, indent , " />");
1691 addC('>', linelen++);
1694 if (this.configuration.IndentContent == true)
1695 condFlushLine(fout, indent);
1697 /* PrintVertSpacer(fout, indent); */
1699 /*condFlushLine(fout, indent); */
1701 /* print the h2 element */
1702 printTree(fout, mode,
1703 (this.configuration.IndentContent ? indent+this.configuration.spaces : indent), lexer, slidecontent);
1705 slidecontent = slidecontent.next;
1708 /* now continue until we reach the next h2 */
1711 content = slidecontent;
1713 for (; content != null; content = content.next)
1715 if (content.tag == tt.tagH2)
1718 /* kludge for naked text before block level tag */
1719 if (last != null && !this.configuration.IndentContent && last.type == Node.TextNode &&
1720 content.tag != null && (content.tag.model & Dict.CM_BLOCK) != 0)
1722 flushLine(fout, indent);
1723 flushLine(fout, indent);
1726 printTree(fout, mode,
1727 (this.configuration.IndentContent ? indent+this.configuration.spaces : indent), lexer, content);
1732 slidecontent = content;
1734 /* now print epilog */
1736 condFlushLine(fout, indent);
1738 printString(fout, indent , "<br clear=\"all\">");
1739 condFlushLine(fout, indent);
1741 addC('<', linelen++);
1744 addC((int)Lexer.foldCase('h',
1745 this.configuration.UpperCaseTags,
1746 this.configuration.XmlTags),
1748 addC((int)Lexer.foldCase('r',
1749 this.configuration.UpperCaseTags,
1750 this.configuration.XmlTags),
1753 if (this.configuration.XmlOut == true)
1754 printString(fout, indent , " />");
1756 addC('>', linelen++);
1759 if (this.configuration.IndentContent == true)
1760 condFlushLine(fout, indent);
1762 printNavBar(fout, indent);
1764 /* end tag for div */
1765 printString(fout, indent, "</div>");
1766 condFlushLine(fout, indent);
1771 Add meta element for page transition effect, this works on IE but not NS
1774 public void addTransitionEffect(Lexer lexer, Node root, short effect, double duration)
1776 Node head = root.findHEAD(lexer.configuration.tt);
1779 if (0 <= effect && effect <= 23)
1780 transition = "revealTrans(Duration=" +
1781 (new Double(duration)).toString() +
1782 ",Transition=" + effect + ")";
1784 transition = "blendTrans(Duration=" +
1785 (new Double(duration)).toString() + ")";
1789 Node meta = lexer.inferredTag("meta");
1790 meta.addAttribute("http-equiv", "Page-Enter");
1791 meta.addAttribute("content", transition);
1792 Node.insertNodeAtStart(head, meta);
1796 public void createSlides(Lexer lexer, Node root)
1800 Out out = new OutImpl();
1802 body = root.findBody(lexer.configuration.tt);
1803 count = countSlides(body);
1804 slidecontent = body.content;
1805 addTransitionEffect(lexer, root, EFFECT_BLEND, 3.0);
1807 for (slide = 1; slide <= count; ++slide)
1809 buf = "slide" + slide + ".html";
1810 out.state = StreamIn.FSM_ASCII;
1811 out.encoding = this.configuration.CharEncoding;
1815 out.out = new FileOutputStream(buf);
1816 printTree(out, (short)0, 0, lexer, root);
1820 catch (IOException e)
1822 System.err.println(buf + e.toString() );
1827 delete superfluous slides by deleting slideN.html
1828 for N = count+1, count+2, etc. until no such file
1834 buf = "slide" + slide + "html";
1836 if (!(new File(buf)).delete())