experimental jtidy version adapted for phpeclipse
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / tidy / PPrint.java
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/tidy/PPrint.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/tidy/PPrint.java
new file mode 100644 (file)
index 0000000..435e606
--- /dev/null
@@ -0,0 +1,1845 @@
+/*
+ * @(#)PPrint.java   1.11 2000/08/16
+ *
+ */
+
+package net.sourceforge.phpdt.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.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 &nbsp; */
+                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 &lt; 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 &amp; 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 &nbsp; 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 == '"' ? "&quot;" : "&#39;");
+
+                    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 &nbsp; or &#160; 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;
+        }
+    }
+
+}