(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.FileOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.FileNotFoundException;
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("= 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
or if it's not
inline or it is an empty tag followed by
*/
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 or 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 and tags */
if (node.tag == tt.tagFont)
{
for (content = node.content;
content != null;
content = content.next)
printTree(fout, mode, indent, lexer, content);
return;
}
/* replace ... 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 ,
"");
condFlushLine(fout, indent);
}
private void printNavBar(Out fout, int indent)
{
String buf;
condFlushLine(fout, indent);
printString(fout, indent , "");
if (slide > 1)
{
buf = "previous | ";
printString(fout, indent , buf);
condFlushLine(fout, indent);
if (slide < count)
printString(fout, indent , "start | ");
else
printString(fout, indent , "start");
condFlushLine(fout, indent);
}
if (slide < count)
{
buf = "next";
printString(fout, indent , buf);
}
printString(fout, indent , "");
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 = "";
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 , "
");
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, "
");
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;
}
}
}