/** * Copyright (c) 2009 Juwi MacMillan Group GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * @(#)PPrint.java 1.11 2000/08/16 * */ package org.tizzit.util.tidy; /** * * Pretty print parse tree * * (c) 1998-2000 (W3C) MIT, INRIA, Keio University * See Tidy.java for the copyright notice. * Derived from <a href="http://www.w3.org/People/Raggett/tidy"> * HTML Tidy Release 4 Aug 2000</a> * * @author Dave Raggett <dsr@w3.org> * @author Andy Quick <ac.quick@sympatico.ca> (translation to Java) * @version 1.0, 1999/05/22 * @version 1.0.1, 1999/05/29 * @version 1.1, 1999/06/18 Java Bean * @version 1.2, 1999/07/10 Tidy Release 7 Jul 1999 * @version 1.3, 1999/07/30 Tidy Release 26 Jul 1999 * @version 1.4, 1999/09/04 DOM support * @version 1.5, 1999/10/23 Tidy Release 27 Sep 1999 * @version 1.6, 1999/11/01 Tidy Release 22 Oct 1999 * @version 1.7, 1999/12/06 Tidy Release 30 Nov 1999 * @version 1.8, 2000/01/22 Tidy Release 13 Jan 2000 * @version 1.9, 2000/06/03 Tidy Release 30 Apr 2000 * @version 1.10, 2000/07/22 Tidy Release 8 Jul 2000 * @version 1.11, 2000/08/16 Tidy Release 4 Aug 2000 */ /* 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 */ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; public class PPrint { /* page transition effects */ public static final short EFFECT_BLEND = -1; public static final short EFFECT_BOX_IN = 0; public static final short EFFECT_BOX_OUT = 1; public static final short EFFECT_CIRCLE_IN = 2; public static final short EFFECT_CIRCLE_OUT = 3; public static final short EFFECT_WIPE_UP = 4; public static final short EFFECT_WIPE_DOWN = 5; public static final short EFFECT_WIPE_RIGHT = 6; public static final short EFFECT_WIPE_LEFT = 7; public static final short EFFECT_VERT_BLINDS = 8; public static final short EFFECT_HORZ_BLINDS = 9; public static final short EFFECT_CHK_ACROSS = 10; public static final short EFFECT_CHK_DOWN = 11; public static final short EFFECT_RND_DISSOLVE = 12; public static final short EFFECT_SPLIT_VIRT_IN = 13; public static final short EFFECT_SPLIT_VIRT_OUT = 14; public static final short EFFECT_SPLIT_HORZ_IN = 15; public static final short EFFECT_SPLIT_HORZ_OUT = 16; public static final short EFFECT_STRIPS_LEFT_DOWN = 17; public static final short EFFECT_STRIPS_LEFT_UP = 18; public static final short EFFECT_STRIPS_RIGHT_DOWN = 19; public static final short EFFECT_STRIPS_RIGHT_UP = 20; public static final short EFFECT_RND_BARS_HORZ = 21; public static final short EFFECT_RND_BARS_VERT = 22; public static final short EFFECT_RANDOM = 23; private static final short NORMAL = 0; private static final short PREFORMATTED = 1; private static final short COMMENT = 2; private static final short ATTRIBVALUE = 4; private static final short NOWRAP = 8; private static final short CDATA = 16; private int[] linebuf = null; private int lbufsize = 0; private int linelen = 0; private int wraphere = 0; private boolean inAttVal = false; private boolean InString = false; private int slide = 0; private int count = 0; private Node slidecontent = null; private Configuration configuration; public PPrint(Configuration configuration) { this.configuration = configuration; } /* 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 */ public static int getUTF8(byte[] str, int start, MutableInteger ch) { int c, n, i, bytes; c = ((int) str[start]) & 0xFF; // Convert to unsigned. if ((c & 0xE0) == 0xC0) /* 110X XXXX two bytes */ { n = c & 31; bytes = 2; } else if ((c & 0xF0) == 0xE0) /* 1110 XXXX three bytes */ { n = c & 15; bytes = 3; } else if ((c & 0xF8) == 0xF0) /* 1111 0XXX four bytes */ { n = c & 7; bytes = 4; } else if ((c & 0xFC) == 0xF8) /* 1111 10XX five bytes */ { n = c & 3; bytes = 5; } else if ((c & 0xFE) == 0xFC) /* 1111 110X six bytes */ { n = c & 1; bytes = 6; } else /* 0XXX XXXX one byte */ { ch.value = c; return 0; } /* successor bytes should have the form 10XX XXXX */ for (i = 1; i < bytes; ++i) { c = ((int) str[start + i]) & 0xFF; // Convert to unsigned. n = (n << 6) | (c & 0x3F); } ch.value = n; return bytes - 1; } /* store char c as UTF-8 encoded byte stream */ public static int putUTF8(byte[] buf, int start, int c) { if (c < 128) buf[start++] = (byte) c; else if (c <= 0x7FF) { buf[start++] = (byte) (0xC0 | (c >> 6)); buf[start++] = (byte) (0x80 | (c & 0x3F)); } else if (c <= 0xFFFF) { buf[start++] = (byte) (0xE0 | (c >> 12)); buf[start++] = (byte) (0x80 | ((c >> 6) & 0x3F)); buf[start++] = (byte) (0x80 | (c & 0x3F)); } else if (c <= 0x1FFFFF) { buf[start++] = (byte) (0xF0 | (c >> 18)); buf[start++] = (byte) (0x80 | ((c >> 12) & 0x3F)); buf[start++] = (byte) (0x80 | ((c >> 6) & 0x3F)); buf[start++] = (byte) (0x80 | (c & 0x3F)); } else { buf[start++] = (byte) (0xF8 | (c >> 24)); buf[start++] = (byte) (0x80 | ((c >> 18) & 0x3F)); buf[start++] = (byte) (0x80 | ((c >> 12) & 0x3F)); buf[start++] = (byte) (0x80 | ((c >> 6) & 0x3F)); buf[start++] = (byte) (0x80 | (c & 0x3F)); } return start; } private void addC(int c, int index) { if (index + 1 >= lbufsize) { while (index + 1 >= lbufsize) { if (lbufsize == 0) lbufsize = 256; else lbufsize = lbufsize * 2; } int[] temp = new int[lbufsize]; if (linebuf != null) System.arraycopy(linebuf, 0, temp, 0, index); linebuf = temp; } linebuf[index] = c; } private void wrapLine(Out fout, int indent) { int i, p, q; if (wraphere == 0) return; for (i = 0; i < indent; ++i) fout.outc((int) ' '); for (i = 0; i < wraphere; ++i) fout.outc(linebuf[i]); if (InString) { fout.outc((int) ' '); fout.outc((int) '\\'); } fout.newline(); if (linelen > wraphere) { p = 0; if (linebuf[wraphere] == ' ') ++wraphere; q = wraphere; addC('\0', linelen); while (true) { linebuf[p] = linebuf[q]; if (linebuf[q] == 0) break; p++; q++; } linelen -= wraphere; } else linelen = 0; wraphere = 0; } private void wrapAttrVal(Out fout, int indent, boolean inString) { int i, p, q; for (i = 0; i < indent; ++i) fout.outc((int) ' '); for (i = 0; i < wraphere; ++i) fout.outc(linebuf[i]); fout.outc((int) ' '); if (inString) fout.outc((int) '\\'); fout.newline(); if (linelen > wraphere) { p = 0; if (linebuf[wraphere] == ' ') ++wraphere; q = wraphere; addC('\0', linelen); while (true) { linebuf[p] = linebuf[q]; if (linebuf[q] == 0) break; p++; q++; } linelen -= wraphere; } else linelen = 0; wraphere = 0; } public void flushLine(Out fout, int indent) { int i; if (linelen > 0) { if (indent + linelen >= this.configuration.wraplen) wrapLine(fout, indent); if (!inAttVal || this.configuration.IndentAttributes) { for (i = 0; i < indent; ++i) fout.outc((int) ' '); } for (i = 0; i < linelen; ++i) fout.outc(linebuf[i]); } fout.newline(); linelen = 0; wraphere = 0; inAttVal = false; } public void condFlushLine(Out fout, int indent) { int i; if (linelen > 0) { if (indent + linelen >= this.configuration.wraplen) wrapLine(fout, indent); if (!inAttVal || this.configuration.IndentAttributes) { for (i = 0; i < indent; ++i) fout.outc((int) ' '); } for (i = 0; i < linelen; ++i) fout.outc(linebuf[i]); fout.newline(); linelen = 0; wraphere = 0; inAttVal = false; } } private void printChar(int c, short mode) { String entity; if (c == ' ' && !((mode & (PREFORMATTED | COMMENT | ATTRIBVALUE)) != 0)) { /* coerce a space character to a non-breaking space */ if ((mode & NOWRAP) != 0) { /* by default XML doesn't define   */ if (this.configuration.NumEntities || this.configuration.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) != 0) { addC(c, linelen++); return; } /* except in CDATA map < to < etc. */ if (!((mode & CDATA) != 0)) { 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 == '&' && this.configuration.QuoteAmpersand) { addC('&', linelen++); addC('a', linelen++); addC('m', linelen++); addC('p', linelen++); addC(';', linelen++); return; } if (c == '"' && this.configuration.QuoteMarks) { addC('&', linelen++); addC('q', linelen++); addC('u', linelen++); addC('o', linelen++); addC('t', linelen++); addC(';', linelen++); return; } if (c == '\'' && this.configuration.QuoteMarks) { addC('&', linelen++); addC('#', linelen++); addC('3', linelen++); addC('9', linelen++); addC(';', linelen++); return; } if (c == 160 && this.configuration.CharEncoding != Configuration.RAW) { if (this.configuration.QuoteNbsp) { addC('&', linelen++); if (this.configuration.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++); } else addC(c, linelen++); return; } } /* otherwise ISO 2022 characters are passed raw */ if (this.configuration.CharEncoding == Configuration.ISO2022 || this.configuration.CharEncoding == Configuration.RAW) { addC(c, linelen++); return; } /* if preformatted text, map   to space */ if (c == 160 && ((mode & PREFORMATTED) != 0)) { 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. My thanks to Andrzej Novosiolov for his help with this code. */ if (this.configuration.MakeClean) { if (c >= 0x2013 && c <= 0x201E) { switch (c) { case 0x2013: case 0x2014: c = '-'; break; case 0x2018: case 0x2019: case 0x201A: c = '\''; break; case 0x201C: case 0x201D: case 0x201E: c = '"'; break; } } } /* don't map latin-1 chars to entities */ if (this.configuration.CharEncoding == Configuration.LATIN1) { if (c > 255) /* multi byte chars */ { if (!this.configuration.NumEntities) { entity = EntityTable.getDefaultEntityTable().entityName((short) c); if (entity != null) entity = "&" + entity + ";"; else entity = "&#" + c + ";"; } else entity = "&#" + c + ";"; for (int i = 0; i < entity.length(); i++) addC((int) entity.charAt(i), linelen++); return; } if (c > 126 && c < 160) { entity = "&#" + c + ";"; for (int i = 0; i < entity.length(); i++) addC((int) entity.charAt(i), linelen++); return; } addC(c, linelen++); return; } /* don't map utf8 chars to entities */ if (this.configuration.CharEncoding == Configuration.UTF8) { addC(c, linelen++); return; } /* use numeric entities only for XML */ if (this.configuration.XmlTags) { /* if ASCII use numeric entities for chars > 127 */ if (c > 127 && this.configuration.CharEncoding == Configuration.ASCII) { entity = "&#" + c + ";"; for (int i = 0; i < entity.length(); i++) addC((int) entity.charAt(i), linelen++); return; } /* otherwise output char raw */ addC(c, linelen++); return; } /* default treatment for ASCII */ if (c > 126 || (c < ' ' && c != '\t')) { if (!this.configuration.NumEntities) { entity = EntityTable.getDefaultEntityTable().entityName((short) c); if (entity != null) entity = "&" + entity + ";"; else entity = "&#" + c + ";"; } else entity = "&#" + c + ";"; for (int i = 0; i < entity.length(); i++) addC((int) entity.charAt(i), 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. */ private void printText(Out fout, short mode, int indent, byte[] textarray, int start, int end) { int i, c; MutableInteger ci = new MutableInteger(); for (i = start; i < end; ++i) { if (indent + linelen >= this.configuration.wraplen) wrapLine(fout, indent); c = ((int) textarray[i]) & 0xFF; // Convert to unsigned. /* look for UTF-8 multibyte character */ if (c > 0x7F) { i += getUTF8(textarray, i, ci); c = ci.value; } if (c == '\n') { flushLine(fout, indent); continue; } printChar(c, mode); } } private void printString(Out fout, int indent, String str) { for (int i = 0; i < str.length(); i++) addC((int) str.charAt(i), linelen++); } private void printAttrValue(Out fout, int indent, String value, int delim, boolean wrappable) { int c; MutableInteger ci = new MutableInteger(); boolean wasinstring = false; byte[] valueChars = null; int i; short mode = (wrappable ? (short) (NORMAL | ATTRIBVALUE) : (short) (PREFORMATTED | ATTRIBVALUE)); if (value != null) { valueChars = Lexer.getBytes(value); } /* look for ASP, Tango or PHP instructions for computed attribute value */ if (valueChars != null && valueChars.length >= 5 && valueChars[0] == '<') { if (valueChars[1] == '%' || valueChars[1] == '@' || (new String(valueChars, 0, 5)).equals("<?php")) mode |= CDATA; } if (delim == 0) delim = '"'; addC('=', linelen++); /* don't wrap after "=" for xml documents */ if (!this.configuration.XmlOut) { if (indent + linelen < this.configuration.wraplen) wraphere = linelen; if (indent + linelen >= this.configuration.wraplen) wrapLine(fout, indent); if (indent + linelen < this.configuration.wraplen) wraphere = linelen; else condFlushLine(fout, indent); } addC(delim, linelen++); if (value != null) { InString = false; i = 0; while (i < valueChars.length) { c = ((int) valueChars[i]) & 0xFF; // Convert to unsigned. if (wrappable && c == ' ' && indent + linelen < this.configuration.wraplen) { wraphere = linelen; wasinstring = InString; } if (wrappable && wraphere > 0 && indent + linelen >= this.configuration.wraplen) wrapAttrVal(fout, indent, wasinstring); if (c == delim) { String entity; entity = (c == '"' ? """ : "'"); for (int j = 0; j < entity.length(); j++) addC(entity.charAt(j), linelen++); ++i; continue; } else if (c == '"') { if (this.configuration.QuoteMarks) { addC('&', linelen++); addC('q', linelen++); addC('u', linelen++); addC('o', linelen++); addC('t', linelen++); addC(';', linelen++); } else addC('"', linelen++); if (delim == '\'') InString = !InString; ++i; continue; } else if (c == '\'') { if (this.configuration.QuoteMarks) { addC('&', linelen++); addC('#', linelen++); addC('3', linelen++); addC('9', linelen++); addC(';', linelen++); } else addC('\'', linelen++); if (delim == '"') InString = !InString; ++i; continue; } /* look for UTF-8 multibyte character */ if (c > 0x7F) { i += getUTF8(valueChars, i, ci); c = ci.value; } ++i; if (c == '\n') { flushLine(fout, indent); continue; } printChar(c, mode); } } InString = false; addC(delim, linelen++); } private void printAttribute(Out fout, int indent, Node node, AttVal attr) { String name; boolean wrappable = false; if (this.configuration.IndentAttributes) { flushLine(fout, indent); indent += this.configuration.spaces; } name = attr.attribute; if (indent + linelen >= this.configuration.wraplen) wrapLine(fout, indent); if (!this.configuration.XmlTags && !this.configuration.XmlOut && attr.dict != null) { if (AttributeTable.getDefaultAttributeTable().isScript(name)) wrappable = this.configuration.WrapScriptlets; else if (!attr.dict.nowrap && this.configuration.WrapAttVals) wrappable = true; } if (indent + linelen < this.configuration.wraplen) { wraphere = linelen; addC(' ', linelen++); } else { condFlushLine(fout, indent); addC(' ', linelen++); } for (int i = 0; i < name.length(); i++) addC((int) Lexer.foldCase(name.charAt(i), this.configuration.UpperCaseAttrs, this.configuration.XmlTags), linelen++); if (indent + linelen >= this.configuration.wraplen) wrapLine(fout, indent); if (attr.value == null) { if (this.configuration.XmlTags || this.configuration.XmlOut) printAttrValue(fout, indent, attr.attribute, attr.delim, true); else if (!attr.isBoolAttribute() && !Node.isNewNode(node)) printAttrValue(fout, indent, "", attr.delim, true); else if (indent + linelen < this.configuration.wraplen) wraphere = linelen; } else printAttrValue(fout, indent, attr.value, attr.delim, wrappable); } private void printAttrs(Out fout, int indent, Node node, AttVal attr) { if (attr != null) { if (attr.next != null) printAttrs(fout, indent, node, attr.next); if (attr.attribute != null) printAttribute(fout, indent, node, attr); else if (attr.asp != null) { addC(' ', linelen++); printAsp(fout, indent, attr.asp); } else if (attr.php != null) { addC(' ', linelen++); printPhp(fout, indent, attr.php); } } /* add xml:space attribute to pre and other elements */ if (configuration.XmlOut && configuration.XmlSpace && ParserImpl.XMLPreserveWhiteSpace(node, configuration.tt) && node.getAttrByName("xml:space") == null) printString(fout, indent, " xml:space=\"preserve\""); } /* 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 */ private static boolean afterSpace(Node node) { Node prev; int c; if (node == null || node.tag == null || !((node.tag.model & Dict.CM_INLINE) != 0)) return true; prev = node.prev; if (prev != null) { if (prev.type == Node.TextNode && prev.end > prev.start) { c = ((int) prev.textarray[prev.end - 1]) & 0xFF; // Convert to unsigned. if (c == 160 || c == ' ' || c == '\n') return true; } return false; } return afterSpace(node.parent); } private void printTag(Lexer lexer, Out fout, short mode, int indent, Node node) { char c; String p; TagTable tt = this.configuration.tt; addC('<', linelen++); if (node.type == Node.EndTag) addC('/', linelen++); p = node.element; for (int i = 0; i < p.length(); i++) addC((int) Lexer.foldCase(p.charAt(i), this.configuration.UpperCaseTags, this.configuration.XmlTags), linelen++); printAttrs(fout, indent, node, node.attributes); if ((this.configuration.XmlOut || lexer != null && lexer.isvoyager) && (node.type == Node.StartEndTag || (node.tag.model & Dict.CM_EMPTY) != 0)) { addC(' ', linelen++); /* compatibility hack */ addC('/', linelen++); } addC('>', linelen++); ; if (node.type != Node.StartEndTag && !((mode & PREFORMATTED) != 0)) { if (indent + linelen >= this.configuration.wraplen) wrapLine(fout, indent); if (indent + linelen < this.configuration.wraplen) { /* wrap after start tag if is <br/> or if it's not inline or it is an empty tag followed by </a> */ if (afterSpace(node)) { if (!((mode & NOWRAP) != 0) && (!((node.tag.model & Dict.CM_INLINE) != 0) || (node.tag == tt.tagBr) || (((node.tag.model & Dict.CM_EMPTY) != 0) && node.next == null && node.parent.tag == tt.tagA))) { wraphere = linelen; } } } else condFlushLine(fout, indent); } } private void printEndTag(Out fout, short mode, int indent, Node node) { char c; String p; /* Netscape ignores SGML standard by not ignoring a line break before </A> or </U> etc. To avoid rendering this as an underlined space, I disable line wrapping before inline end tags by the #if 0 ... #endif */ if (false) { if (indent + linelen < this.configuration.wraplen && !((mode & NOWRAP) != 0)) wraphere = linelen; } addC('<', linelen++); addC('/', linelen++); p = node.element; for (int i = 0; i < p.length(); i++) addC((int) Lexer.foldCase(p.charAt(i), this.configuration.UpperCaseTags, this.configuration.XmlTags), linelen++); addC('>', linelen++); } private void printComment(Out fout, int indent, Node node) { if (indent + linelen < this.configuration.wraplen) wraphere = linelen; addC('<', linelen++); addC('!', linelen++); addC('-', linelen++); addC('-', linelen++); if (false) { if (linelen < this.configuration.wraplen) wraphere = linelen; } printText(fout, COMMENT, indent, node.textarray, node.start, node.end); if (false) { if (indent + linelen < this.configuration.wraplen) wraphere = linelen; } // See Lexer.java: AQ 8Jul2000 addC('-', linelen++); addC('-', linelen++); addC('>', linelen++); if (node.linebreak) flushLine(fout, indent); } private void printDocType(Out fout, int indent, Node node) { boolean q = this.configuration.QuoteMarks; this.configuration.QuoteMarks = false; if (indent + linelen < this.configuration.wraplen) wraphere = linelen; condFlushLine(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 < this.configuration.wraplen) wraphere = linelen; printText(fout, (short) 0, indent, node.textarray, node.start, node.end); if (linelen < this.configuration.wraplen) wraphere = linelen; addC('>', linelen++); this.configuration.QuoteMarks = q; condFlushLine(fout, indent); } private void printPI(Out fout, int indent, Node node) { if (indent + linelen < this.configuration.wraplen) wraphere = linelen; addC('<', linelen++); addC('?', linelen++); /* set CDATA to pass < and > unescaped */ printText(fout, CDATA, indent, node.textarray, node.start, node.end); if (node.textarray[node.end - 1] != (byte) '?') addC('?', linelen++); addC('>', linelen++); condFlushLine(fout, indent); } /* note ASP and JSTE share <% ... %> syntax */ private void printAsp(Out fout, int indent, Node node) { int savewraplen = this.configuration.wraplen; /* disable wrapping if so requested */ if (!this.configuration.WrapAsp || !this.configuration.WrapJste) this.configuration.wraplen = 0xFFFFFF; /* a very large number */ if (false) { //#if 0 if (indent + linelen < this.configuration.wraplen) wraphere = linelen; } //#endif addC('<', linelen++); addC('%', linelen++); printText(fout, (this.configuration.WrapAsp ? CDATA : COMMENT), indent, node.textarray, node.start, node.end); addC('%', linelen++); addC('>', linelen++); /* condFlushLine(fout, indent); */ this.configuration.wraplen = savewraplen; } /* JSTE also supports <# ... #> syntax */ private void printJste(Out fout, int indent, Node node) { int savewraplen = this.configuration.wraplen; /* disable wrapping if so requested */ if (!this.configuration.WrapJste) this.configuration.wraplen = 0xFFFFFF; /* a very large number */ addC('<', linelen++); addC('#', linelen++); printText(fout, (this.configuration.WrapJste ? CDATA : COMMENT), indent, node.textarray, node.start, node.end); addC('#', linelen++); addC('>', linelen++); /* condFlushLine(fout, indent); */ this.configuration.wraplen = savewraplen; } /* PHP is based on XML processing instructions */ private void printPhp(Out fout, int indent, Node node) { int savewraplen = this.configuration.wraplen; /* disable wrapping if so requested */ if (!this.configuration.WrapPhp) this.configuration.wraplen = 0xFFFFFF; /* a very large number */ if (false) { //#if 0 if (indent + linelen < this.configuration.wraplen) wraphere = linelen; } //#endif addC('<', linelen++); addC('?', linelen++); printText(fout, (this.configuration.WrapPhp ? CDATA : COMMENT), indent, node.textarray, node.start, node.end); addC('?', linelen++); addC('>', linelen++); /* PCondFlushLine(fout, indent); */ this.configuration.wraplen = savewraplen; } private void printCDATA(Out fout, int indent, Node node) { int savewraplen = this.configuration.wraplen; condFlushLine(fout, indent); /* disable wrapping */ this.configuration.wraplen = 0xFFFFFF; /* a very large number */ addC('<', linelen++); addC('!', linelen++); addC('[', linelen++); addC('C', linelen++); addC('D', linelen++); addC('A', linelen++); addC('T', linelen++); addC('A', linelen++); addC('[', linelen++); printText(fout, COMMENT, indent, node.textarray, node.start, node.end); addC(']', linelen++); addC(']', linelen++); addC('>', linelen++); condFlushLine(fout, indent); this.configuration.wraplen = savewraplen; } private void printSection(Out fout, int indent, Node node) { int savewraplen = this.configuration.wraplen; /* disable wrapping if so requested */ if (!this.configuration.WrapSection) this.configuration.wraplen = 0xFFFFFF; /* a very large number */ if (false) { //#if 0 if (indent + linelen < this.configuration.wraplen) wraphere = linelen; } //#endif addC('<', linelen++); addC('!', linelen++); addC('[', linelen++); printText(fout, (this.configuration.WrapSection ? CDATA : COMMENT), indent, node.textarray, node.start, node.end); addC(']', linelen++); addC('>', linelen++); /* PCondFlushLine(fout, indent); */ this.configuration.wraplen = savewraplen; } private boolean shouldIndent(Node node) { TagTable tt = this.configuration.tt; if (!this.configuration.IndentContent) return false; if (this.configuration.SmartIndent) { if (node.content != null && ((node.tag.model & Dict.CM_NO_INDENT) != 0)) { for (node = node.content; node != null; node = node.next) if (node.tag != null && (node.tag.model & Dict.CM_BLOCK) != 0) return true; return false; } if ((node.tag.model & Dict.CM_HEADING) != 0) return false; if (node.tag == tt.tagP) return false; if (node.tag == tt.tagTitle) return false; } if ((node.tag.model & (Dict.CM_FIELD | Dict.CM_OBJECT)) != 0) return true; if (node.tag == tt.tagMap) return true; return !((node.tag.model & Dict.CM_INLINE) != 0); } public void printTree(Out fout, short mode, int indent, Lexer lexer, Node node) { Node content, last; TagTable tt = this.configuration.tt; if (node == null) return; if (node.type == Node.TextNode) printText(fout, mode, indent, node.textarray, node.start, node.end); else if (node.type == Node.CommentTag) { printComment(fout, indent, node); } else if (node.type == Node.RootNode) { for (content = node.content; content != null; content = content.next) printTree(fout, mode, indent, lexer, content); } else if (node.type == Node.DocTypeTag) printDocType(fout, indent, node); else if (node.type == Node.ProcInsTag) printPI(fout, indent, node); else if (node.type == Node.CDATATag) printCDATA(fout, indent, node); else if (node.type == Node.SectionTag) printSection(fout, indent, node); else if (node.type == Node.AspTag) printAsp(fout, indent, node); else if (node.type == Node.JsteTag) printJste(fout, indent, node); else if (node.type == Node.PhpTag) printPhp(fout, indent, node); else if ((node.tag.model & Dict.CM_EMPTY) != 0 || node.type == Node.StartEndTag) { if (!((node.tag.model & Dict.CM_INLINE) != 0)) condFlushLine(fout, indent); if (node.tag == tt.tagBr && node.prev != null && node.prev.tag != tt.tagBr && this.configuration.BreakBeforeBR) flushLine(fout, indent); if (this.configuration.MakeClean && node.tag == tt.tagWbr) printString(fout, indent, " "); else printTag(lexer, fout, mode, indent, node); if (node.tag == tt.tagParam || node.tag == tt.tagArea) condFlushLine(fout, indent); else if (node.tag == tt.tagBr || node.tag == tt.tagHr) flushLine(fout, indent); } else /* some kind of container element */ { if (node.tag != null && node.tag.parser == ParserImpl.getParsePre()) { condFlushLine(fout, indent); indent = 0; condFlushLine(fout, indent); printTag(lexer, fout, mode, indent, node); flushLine(fout, indent); for (content = node.content; content != null; content = content.next) printTree(fout, (short) (mode | PREFORMATTED | NOWRAP), indent, lexer, content); condFlushLine(fout, indent); printEndTag(fout, mode, indent, node); flushLine(fout, indent); if (this.configuration.IndentContent == false && node.next != null) flushLine(fout, indent); } else if (node.tag == tt.tagStyle || node.tag == tt.tagScript) { condFlushLine(fout, indent); indent = 0; condFlushLine(fout, indent); printTag(lexer, fout, mode, indent, node); flushLine(fout, indent); for (content = node.content; content != null; content = content.next) printTree(fout, (short) (mode | PREFORMATTED | NOWRAP | CDATA), indent, lexer, content); condFlushLine(fout, indent); printEndTag(fout, mode, indent, node); flushLine(fout, indent); if (this.configuration.IndentContent == false && node.next != null) flushLine(fout, indent); } else if ((node.tag.model & Dict.CM_INLINE) != 0) { if (this.configuration.MakeClean) { /* discards <font> and </font> tags */ if (node.tag == tt.tagFont) { for (content = node.content; content != null; content = content.next) printTree(fout, mode, indent, lexer, content); return; } /* replace <nobr>...</nobr> by   or   etc. */ if (node.tag == tt.tagNobr) { for (content = node.content; content != null; content = content.next) printTree(fout, (short) (mode | NOWRAP), indent, lexer, content); return; } } /* otherwise a normal inline element */ printTag(lexer, fout, mode, indent, node); /* indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET */ if (shouldIndent(node)) { condFlushLine(fout, indent); indent += this.configuration.spaces; for (content = node.content; content != null; content = content.next) printTree(fout, mode, indent, lexer, content); condFlushLine(fout, indent); indent -= this.configuration.spaces; condFlushLine(fout, indent); } else { for (content = node.content; content != null; content = content.next) printTree(fout, mode, indent, lexer, content); } printEndTag(fout, mode, indent, node); } else /* other tags */ { condFlushLine(fout, indent); if (this.configuration.SmartIndent && node.prev != null) flushLine(fout, indent); if (this.configuration.HideEndTags == false || !(node.tag != null && ((node.tag.model & Dict.CM_OMITST) != 0))) { printTag(lexer, fout, mode, indent, node); if (shouldIndent(node)) condFlushLine(fout, indent); else if ((node.tag.model & Dict.CM_HTML) != 0 || node.tag == tt.tagNoframes || ((node.tag.model & Dict.CM_HEAD) != 0 && !(node.tag == tt.tagTitle))) flushLine(fout, indent); } if (node.tag == tt.tagBody && this.configuration.BurstSlides) printSlide(fout, mode, (this.configuration.IndentContent ? indent + this.configuration.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 != null && !this.configuration.IndentContent && last.type == Node.TextNode && content.tag != null && (content.tag.model & Dict.CM_BLOCK) != 0) { flushLine(fout, indent); flushLine(fout, indent); } printTree(fout, mode, (shouldIndent(node) ? indent + this.configuration.spaces : indent), lexer, content); last = content; } } /* don't flush line for td and th */ if (shouldIndent(node) || (((node.tag.model & Dict.CM_HTML) != 0 || node.tag == tt.tagNoframes || ((node.tag.model & Dict.CM_HEAD) != 0 && !(node.tag == tt.tagTitle))) && this.configuration.HideEndTags == false)) { condFlushLine(fout, (this.configuration.IndentContent ? indent + this.configuration.spaces : indent)); if (this.configuration.HideEndTags == false || !((node.tag.model & Dict.CM_OPT) != 0)) { printEndTag(fout, mode, indent, node); flushLine(fout, indent); } } else { if (this.configuration.HideEndTags == false || !((node.tag.model & Dict.CM_OPT) != 0)) printEndTag(fout, mode, indent, node); flushLine(fout, indent); } if (this.configuration.IndentContent == false && node.next != null && this.configuration.HideEndTags == false && (node.tag.model & (Dict.CM_BLOCK | Dict.CM_LIST | Dict.CM_DEFLIST | Dict.CM_TABLE)) != 0) { flushLine(fout, indent); } } } } public void printXMLTree(Out fout, short mode, int indent, Lexer lexer, Node node) { TagTable tt = this.configuration.tt; if (node == null) return; if (node.type == Node.TextNode) { printText(fout, mode, indent, node.textarray, node.start, node.end); } else if (node.type == Node.CommentTag) { condFlushLine(fout, indent); printComment(fout, 0, node); condFlushLine(fout, 0); } else if (node.type == Node.RootNode) { Node content; for (content = node.content; content != null; content = content.next) printXMLTree(fout, mode, indent, lexer, content); } else if (node.type == Node.DocTypeTag) printDocType(fout, indent, node); else if (node.type == Node.ProcInsTag) printPI(fout, indent, node); else if (node.type == Node.SectionTag) printSection(fout, indent, node); else if (node.type == Node.AspTag) printAsp(fout, indent, node); else if (node.type == Node.JsteTag) printJste(fout, indent, node); else if (node.type == Node.PhpTag) printPhp(fout, indent, node); else if ((node.tag.model & Dict.CM_EMPTY) != 0 || node.type == Node.StartEndTag) { condFlushLine(fout, indent); printTag(lexer, fout, mode, indent, node); flushLine(fout, indent); if (node.next != null) flushLine(fout, indent); } else /* some kind of container element */ { Node content; boolean mixed = false; int cindent; for (content = node.content; content != null; content = content.next) { if (content.type == Node.TextNode) { mixed = true; break; } } condFlushLine(fout, indent); if (ParserImpl.XMLPreserveWhiteSpace(node, tt)) { indent = 0; cindent = 0; mixed = false; } else if (mixed) cindent = indent; else cindent = indent + this.configuration.spaces; printTag(lexer, fout, mode, indent, node); if (!mixed) flushLine(fout, indent); for (content = node.content; content != null; content = content.next) printXMLTree(fout, mode, cindent, lexer, content); if (!mixed) condFlushLine(fout, cindent); printEndTag(fout, mode, indent, node); condFlushLine(fout, indent); if (node.next != null) flushLine(fout, indent); } } /* split parse tree by h2 elements and output to separate files */ /* counts number of h2 children belonging to node */ public int countSlides(Node node) { int n = 1; TagTable tt = this.configuration.tt; for (node = node.content; node != null; node = node.next) if (node.tag == tt.tagH2) ++n; return n; } /* inserts a space gif called "dot.gif" to ensure that the slide is at least n pixels high */ private void printVertSpacer(Out fout, int indent) { condFlushLine(fout, indent); printString(fout, indent, "<img width=\"0\" height=\"0\" hspace=\"1\" src=\"dot.gif\" vspace=\"%d\" align=\"left\">"); condFlushLine(fout, indent); } private void printNavBar(Out fout, int indent) { String buf; condFlushLine(fout, indent); printString(fout, indent, "<center><small>"); if (slide > 1) { buf = "<a href=\"slide" + (new Integer(slide - 1)).toString() + ".html\">previous</a> | "; printString(fout, indent, buf); condFlushLine(fout, indent); if (slide < count) printString(fout, indent, "<a href=\"slide1.html\">start</a> | "); else printString(fout, indent, "<a href=\"slide1.html\">start</a>"); condFlushLine(fout, indent); } if (slide < count) { buf = "<a href=\"slide" + (new Integer(slide + 1)).toString() + ".html\">next</a>"; printString(fout, indent, buf); } printString(fout, indent, "</small></center>"); condFlushLine(fout, indent); } /* Called from printTree 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. */ public void printSlide(Out fout, short mode, int indent, Lexer lexer) { Node content, last; TagTable tt = this.configuration.tt; /* insert div for onclick handler */ String s; s = "<div onclick=\"document.location='slide" + (new Integer(slide < count ? slide + 1 : 1)).toString() + ".html'\">"; printString(fout, indent, s); condFlushLine(fout, indent); /* first print the h2 element and navbar */ if (slidecontent.tag == tt.tagH2) { printNavBar(fout, indent); /* now print an hr after h2 */ addC('<', linelen++); addC((int) Lexer.foldCase('h', this.configuration.UpperCaseTags, this.configuration.XmlTags), linelen++); addC((int) Lexer.foldCase('r', this.configuration.UpperCaseTags, this.configuration.XmlTags), linelen++); if (this.configuration.XmlOut == true) printString(fout, indent, " />"); else addC('>', linelen++); if (this.configuration.IndentContent == true) condFlushLine(fout, indent); /* PrintVertSpacer(fout, indent); */ /*condFlushLine(fout, indent); */ /* print the h2 element */ printTree(fout, mode, (this.configuration.IndentContent ? indent + this.configuration.spaces : indent), lexer, slidecontent); slidecontent = slidecontent.next; } /* now continue until we reach the next h2 */ last = null; content = slidecontent; for (; content != null; content = content.next) { if (content.tag == tt.tagH2) break; /* kludge for naked text before block level tag */ if (last != null && !this.configuration.IndentContent && last.type == Node.TextNode && content.tag != null && (content.tag.model & Dict.CM_BLOCK) != 0) { flushLine(fout, indent); flushLine(fout, indent); } printTree(fout, mode, (this.configuration.IndentContent ? indent + this.configuration.spaces : indent), lexer, content); last = content; } slidecontent = content; /* now print epilog */ condFlushLine(fout, indent); printString(fout, indent, "<br clear=\"all\">"); condFlushLine(fout, indent); addC('<', linelen++); addC((int) Lexer.foldCase('h', this.configuration.UpperCaseTags, this.configuration.XmlTags), linelen++); addC((int) Lexer.foldCase('r', this.configuration.UpperCaseTags, this.configuration.XmlTags), linelen++); if (this.configuration.XmlOut == true) printString(fout, indent, " />"); else addC('>', linelen++); if (this.configuration.IndentContent == true) condFlushLine(fout, indent); printNavBar(fout, indent); /* end tag for div */ printString(fout, indent, "</div>"); condFlushLine(fout, indent); } /* Add meta element for page transition effect, this works on IE but not NS */ public void addTransitionEffect(Lexer lexer, Node root, short effect, double duration) { Node head = root.findHEAD(lexer.configuration.tt); String transition; if (0 <= effect && effect <= 23) transition = "revealTrans(Duration=" + (new Double(duration)).toString() + ",Transition=" + effect + ")"; else transition = "blendTrans(Duration=" + (new Double(duration)).toString() + ")"; if (head != null) { Node meta = lexer.inferredTag("meta"); meta.addAttribute("http-equiv", "Page-Enter"); meta.addAttribute("content", transition); Node.insertNodeAtStart(head, meta); } } public void createSlides(Lexer lexer, Node root) { Node body; String buf; Out out = new OutImpl(); body = root.findBody(lexer.configuration.tt); count = countSlides(body); slidecontent = body.content; addTransitionEffect(lexer, root, EFFECT_BLEND, 3.0); for (slide = 1; slide <= count; ++slide) { buf = "slide" + slide + ".html"; out.state = StreamIn.FSM_ASCII; out.encoding = this.configuration.CharEncoding; try { out.out = new FileOutputStream(buf); printTree(out, (short) 0, 0, lexer, root); flushLine(out, 0); out.out.close(); } catch (IOException e) { System.err.println(buf + e.toString()); } } /* delete superfluous slides by deleting slideN.html for N = count+1, count+2, etc. until no such file is found. */ for (;;) { buf = "slide" + slide + "html"; if (!(new File(buf)).delete()) break; ++slide; } } }