435e606ed38a78a673f674257ee8b0902c47a503
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / tidy / PPrint.java
1 /*
2  * @(#)PPrint.java   1.11 2000/08/16
3  *
4  */
5
6 package net.sourceforge.phpdt.tidy;
7
8 /**
9  *
10  * Pretty print parse tree
11  *
12  * (c) 1998-2000 (W3C) MIT, INRIA, Keio University
13  * See Tidy.java for the copyright notice.
14  * Derived from <a href="http://www.w3.org/People/Raggett/tidy">
15  * HTML Tidy Release 4 Aug 2000</a>
16  *
17  * @author  Dave Raggett <dsr@w3.org>
18  * @author  Andy Quick <ac.quick@sympatico.ca> (translation to Java)
19  * @version 1.0, 1999/05/22
20  * @version 1.0.1, 1999/05/29
21  * @version 1.1, 1999/06/18 Java Bean
22  * @version 1.2, 1999/07/10 Tidy Release 7 Jul 1999
23  * @version 1.3, 1999/07/30 Tidy Release 26 Jul 1999
24  * @version 1.4, 1999/09/04 DOM support
25  * @version 1.5, 1999/10/23 Tidy Release 27 Sep 1999
26  * @version 1.6, 1999/11/01 Tidy Release 22 Oct 1999
27  * @version 1.7, 1999/12/06 Tidy Release 30 Nov 1999
28  * @version 1.8, 2000/01/22 Tidy Release 13 Jan 2000
29  * @version 1.9, 2000/06/03 Tidy Release 30 Apr 2000
30  * @version 1.10, 2000/07/22 Tidy Release 8 Jul 2000
31  * @version 1.11, 2000/08/16 Tidy Release 4 Aug 2000
32  */
33
34 /*
35   Block-level and unknown elements are printed on
36   new lines and their contents indented 2 spaces
37
38   Inline elements are printed inline.
39
40   Inline content is wrapped on spaces (except in
41   attribute values or preformatted text, after
42   start tags and before end tags
43 */
44
45 import java.io.FileOutputStream;
46 import java.io.File;
47
48 import java.io.IOException;
49 import java.io.FileNotFoundException;
50
51 public class PPrint {
52
53     /* page transition effects */
54
55     public static final short EFFECT_BLEND               = -1;
56     public static final short EFFECT_BOX_IN              = 0;
57     public static final short EFFECT_BOX_OUT             = 1;
58     public static final short EFFECT_CIRCLE_IN           = 2;
59     public static final short EFFECT_CIRCLE_OUT          = 3;
60     public static final short EFFECT_WIPE_UP             = 4;
61     public static final short EFFECT_WIPE_DOWN           = 5;
62     public static final short EFFECT_WIPE_RIGHT          = 6;
63     public static final short EFFECT_WIPE_LEFT           = 7;
64     public static final short EFFECT_VERT_BLINDS         = 8;
65     public static final short EFFECT_HORZ_BLINDS         = 9;
66     public static final short EFFECT_CHK_ACROSS          = 10;
67     public static final short EFFECT_CHK_DOWN            = 11;
68     public static final short EFFECT_RND_DISSOLVE        = 12;
69     public static final short EFFECT_SPLIT_VIRT_IN       = 13;
70     public static final short EFFECT_SPLIT_VIRT_OUT      = 14;
71     public static final short EFFECT_SPLIT_HORZ_IN       = 15;
72     public static final short EFFECT_SPLIT_HORZ_OUT      = 16;
73     public static final short EFFECT_STRIPS_LEFT_DOWN    = 17;
74     public static final short EFFECT_STRIPS_LEFT_UP      = 18;
75     public static final short EFFECT_STRIPS_RIGHT_DOWN   = 19;
76     public static final short EFFECT_STRIPS_RIGHT_UP     = 20;
77     public static final short EFFECT_RND_BARS_HORZ       = 21;
78     public static final short EFFECT_RND_BARS_VERT       = 22;
79     public static final short EFFECT_RANDOM              = 23;
80
81     private static final short NORMAL        = 0;
82     private static final short PREFORMATTED  = 1;
83     private static final short COMMENT       = 2;
84     private static final short ATTRIBVALUE   = 4;
85     private static final short NOWRAP        = 8;
86     private static final short CDATA         = 16;
87
88     private int[] linebuf = null;
89     private int lbufsize = 0;
90     private int linelen = 0;
91     private int wraphere = 0;
92     private boolean inAttVal = false;
93     private boolean InString = false;
94
95     private int slide = 0;
96     private int count = 0;
97     private Node slidecontent = null;
98
99     private Configuration configuration;
100
101     public PPrint(Configuration configuration)
102     {
103         this.configuration = configuration;
104     }
105
106     /*
107       1010  A
108       1011  B
109       1100  C
110       1101  D
111       1110  E
112       1111  F
113     */
114
115     /* return one less that the number of bytes used by UTF-8 char */
116     /* str points to 1st byte, *ch initialized to 1st byte */
117     public static int getUTF8(byte[] str, int start, MutableInteger ch)
118     {
119         int c, n, i, bytes;
120
121         c = ((int)str[start]) & 0xFF;  // Convert to unsigned.
122
123         if ((c & 0xE0) == 0xC0)  /* 110X XXXX  two bytes */
124         {
125             n = c & 31;
126             bytes = 2;
127         }
128         else if ((c & 0xF0) == 0xE0)  /* 1110 XXXX  three bytes */
129         {
130             n = c & 15;
131             bytes = 3;
132         }
133         else if ((c & 0xF8) == 0xF0)  /* 1111 0XXX  four bytes */
134         {
135             n = c & 7;
136             bytes = 4;
137         }
138         else if ((c & 0xFC) == 0xF8)  /* 1111 10XX  five bytes */
139         {
140             n = c & 3;
141             bytes = 5;
142         }
143         else if ((c & 0xFE) == 0xFC)       /* 1111 110X  six bytes */
144
145         {
146             n = c & 1;
147             bytes = 6;
148         }
149         else  /* 0XXX XXXX one byte */
150         {
151             ch.value = c;
152             return 0;
153         }
154
155         /* successor bytes should have the form 10XX XXXX */
156         for (i = 1; i < bytes; ++i)
157         {
158             c = ((int)str[start + i])  & 0xFF;  // Convert to unsigned.
159             n = (n << 6) | (c & 0x3F);
160         }
161
162         ch.value = n;
163         return bytes - 1;
164     }
165
166     /* store char c as UTF-8 encoded byte stream */
167     public static int putUTF8(byte[] buf, int start, int c)
168     {
169         if (c < 128)
170             buf[start++] = (byte)c;
171         else if (c <= 0x7FF)
172         {
173             buf[start++] = (byte)(0xC0 | (c >> 6));
174             buf[start++] = (byte)(0x80 | (c & 0x3F));
175         }
176         else if (c <= 0xFFFF)
177         {
178             buf[start++] =  (byte)(0xE0 | (c >> 12));
179             buf[start++] =  (byte)(0x80 | ((c >> 6) & 0x3F));
180             buf[start++] =  (byte)(0x80 | (c & 0x3F));
181         }
182         else if (c <= 0x1FFFFF)
183         {
184             buf[start++] =  (byte)(0xF0 | (c >> 18));
185             buf[start++] =  (byte)(0x80 | ((c >> 12) & 0x3F));
186             buf[start++] =  (byte)(0x80 | ((c >> 6) & 0x3F));
187             buf[start++] =  (byte)(0x80 | (c & 0x3F));
188         }
189         else
190         {
191             buf[start++] =  (byte)(0xF8 | (c >> 24));
192             buf[start++] =  (byte)(0x80 | ((c >> 18) & 0x3F));
193             buf[start++] =  (byte)(0x80 | ((c >> 12) & 0x3F));
194             buf[start++] =  (byte)(0x80 | ((c >> 6) & 0x3F));
195             buf[start++] =  (byte)(0x80 | (c & 0x3F));
196         }
197
198         return start;
199     }
200
201     private void addC(int c, int index)
202     {
203         if (index + 1 >= lbufsize)
204         {
205             while (index + 1 >= lbufsize)
206             {
207                 if (lbufsize == 0)
208                     lbufsize = 256;
209                 else
210                     lbufsize = lbufsize * 2;
211             }
212
213            int[] temp = new int[ lbufsize ];
214            if (linebuf != null)
215                System.arraycopy(linebuf, 0, temp, 0, index);
216            linebuf = temp;
217         }
218
219         linebuf[index] = c;
220     }
221
222     private void wrapLine(Out fout, int indent)
223     {
224         int i, p, q;
225
226         if (wraphere == 0)
227             return;
228
229         for (i = 0; i < indent; ++i)
230             fout.outc((int)' ');
231
232         for (i = 0; i < wraphere; ++i)
233             fout.outc(linebuf[i]);
234
235         if (InString)
236         {
237             fout.outc((int)' ');
238             fout.outc((int)'\\');
239         }
240
241         fout.newline();
242
243         if (linelen > wraphere)
244         {
245             p = 0;
246
247             if (linebuf[wraphere] == ' ')
248                 ++wraphere;
249
250             q = wraphere;
251             addC('\0', linelen);
252
253             while (true)
254             {
255                 linebuf[p] = linebuf[q];
256                 if (linebuf[q] == 0) break;
257                 p++;
258                 q++;
259             }
260             linelen -= wraphere;
261         }
262         else
263             linelen = 0;
264
265         wraphere = 0;
266     }
267
268     private void wrapAttrVal(Out fout, int indent, boolean inString)
269     {
270         int i, p, q;
271
272         for (i = 0; i < indent; ++i)
273             fout.outc((int)' ');
274
275         for (i = 0; i < wraphere; ++i)
276             fout.outc(linebuf[i]);
277
278         fout.outc((int)' ');
279
280         if (inString)
281             fout.outc((int)'\\');
282
283         fout.newline();
284
285         if (linelen > wraphere)
286         {
287             p = 0;
288
289             if (linebuf[wraphere] == ' ')
290                 ++wraphere;
291
292             q = wraphere;
293             addC('\0', linelen);
294
295             while (true)
296             {
297                 linebuf[p] = linebuf[q];
298                 if (linebuf[q] == 0) break;
299                 p++;
300                 q++;
301             }
302             linelen -= wraphere;
303         }
304         else
305             linelen = 0;
306
307         wraphere = 0;
308     }
309
310     public void flushLine(Out fout, int indent)
311     {
312         int i;
313
314         if (linelen > 0)
315         {
316             if (indent + linelen >= this.configuration.wraplen)
317                 wrapLine(fout, indent);
318
319             if (!inAttVal || this.configuration.IndentAttributes)
320             {
321                 for (i = 0; i < indent; ++i)
322                     fout.outc((int)' ');
323             }
324
325             for (i = 0; i < linelen; ++i)
326                 fout.outc(linebuf[i]);
327         }
328
329         fout.newline();
330         linelen = 0;
331         wraphere = 0;
332         inAttVal = false;
333     }
334
335     public void condFlushLine(Out fout, int indent)
336     {
337         int i;
338
339         if (linelen > 0)
340         {
341             if (indent + linelen >= this.configuration.wraplen)
342                 wrapLine(fout, indent);
343
344             if (!inAttVal || this.configuration.IndentAttributes)
345             {
346                 for (i = 0; i < indent; ++i)
347                     fout.outc((int)' ');
348             }
349
350             for (i = 0; i < linelen; ++i)
351                 fout.outc(linebuf[i]);
352
353             fout.newline();
354             linelen = 0;
355             wraphere = 0;
356             inAttVal = false;
357         }
358     }
359
360     private void printChar(int c, short mode)
361     {
362         String entity;
363
364         if (c == ' ' && !((mode & (PREFORMATTED | COMMENT | ATTRIBVALUE)) != 0))
365         {
366             /* coerce a space character to a non-breaking space */
367             if ((mode & NOWRAP) != 0)
368             {
369                 /* by default XML doesn't define &nbsp; */
370                 if (this.configuration.NumEntities || this.configuration.XmlTags)
371                 {
372                     addC('&', linelen++);
373                     addC('#', linelen++);
374                     addC('1', linelen++);
375                     addC('6', linelen++);
376                     addC('0', linelen++);
377                     addC(';', linelen++);
378                 }
379                 else /* otherwise use named entity */
380                 {
381                     addC('&', linelen++);
382                     addC('n', linelen++);
383                     addC('b', linelen++);
384                     addC('s', linelen++);
385                     addC('p', linelen++);
386                     addC(';', linelen++);
387                 }
388                 return;
389             }
390             else
391                 wraphere = linelen;
392         }
393
394         /* comment characters are passed raw */
395         if ((mode & COMMENT) != 0)
396         {
397             addC(c, linelen++);
398             return;
399         }
400
401         /* except in CDATA map < to &lt; etc. */
402         if (! ((mode & CDATA) != 0) )
403         {
404             if (c == '<')
405             {
406                 addC('&', linelen++);
407                 addC('l', linelen++);
408                 addC('t', linelen++);
409                 addC(';', linelen++);
410                 return;
411             }
412             
413             if (c == '>')
414             {
415                 addC('&', linelen++);
416                 addC('g', linelen++);
417                 addC('t', linelen++);
418                 addC(';', linelen++);
419                 return;
420             }
421
422             /*
423               naked '&' chars can be left alone or
424               quoted as &amp; The latter is required
425               for XML where naked '&' are illegal.
426             */
427             if (c == '&' && this.configuration.QuoteAmpersand)
428             {
429                 addC('&', linelen++);
430                 addC('a', linelen++);
431                 addC('m', linelen++);
432                 addC('p', linelen++);
433                 addC(';', linelen++);
434                 return;
435             }
436
437             if (c == '"' && this.configuration.QuoteMarks)
438             {
439                 addC('&', linelen++);
440                 addC('q', linelen++);
441                 addC('u', linelen++);
442                 addC('o', linelen++);
443                 addC('t', linelen++);
444                 addC(';', linelen++);
445                 return;
446             }
447
448             if (c == '\'' && this.configuration.QuoteMarks)
449             {
450                 addC('&', linelen++);
451                 addC('#', linelen++);
452                 addC('3', linelen++);
453                 addC('9', linelen++);
454                 addC(';', linelen++);
455                 return;
456             }
457
458             if (c == 160 && this.configuration.CharEncoding != Configuration.RAW)
459             {
460                 if (this.configuration.QuoteNbsp)
461                 {
462                     addC('&', linelen++);
463
464                     if (this.configuration.NumEntities)
465                     {
466                         addC('#', linelen++);
467                         addC('1', linelen++);
468                         addC('6', linelen++);
469                         addC('0', linelen++);
470                     }
471                     else
472                     {
473                         addC('n', linelen++);
474                         addC('b', linelen++);
475                         addC('s', linelen++);
476                         addC('p', linelen++);
477                     }
478
479                     addC(';', linelen++);
480                 }
481                 else
482                     addC(c, linelen++);
483
484                 return;
485             }
486         }
487
488         /* otherwise ISO 2022 characters are passed raw */
489         if (this.configuration.CharEncoding == Configuration.ISO2022 ||
490             this.configuration.CharEncoding == Configuration.RAW)
491         {
492             addC(c, linelen++);
493             return;
494         }
495
496         /* if preformatted text, map &nbsp; to space */
497         if (c == 160 && ((mode & PREFORMATTED) != 0))
498         {
499             addC(' ', linelen++);
500             return;
501         }
502
503         /*
504          Filters from Word and PowerPoint often use smart
505          quotes resulting in character codes between 128
506          and 159. Unfortunately, the corresponding HTML 4.0
507          entities for these are not widely supported. The
508          following converts dashes and quotation marks to
509          the nearest ASCII equivalent. My thanks to
510          Andrzej Novosiolov for his help with this code.
511         */
512
513         if (this.configuration.MakeClean)
514         {
515             if (c >= 0x2013 && c <= 0x201E)
516             {
517                 switch (c) {
518                 case 0x2013:
519                 case 0x2014:
520                   c = '-';
521                   break;
522                 case 0x2018:
523                 case 0x2019:
524                 case 0x201A:
525                   c = '\'';
526                   break;
527                 case 0x201C:
528                 case 0x201D:
529                 case 0x201E:
530                   c = '"';
531                   break;
532                 }
533             }
534         }
535
536         /* don't map latin-1 chars to entities */
537         if (this.configuration.CharEncoding == Configuration.LATIN1)
538         {
539             if (c > 255)  /* multi byte chars */
540             {
541                 if (!this.configuration.NumEntities)
542                 {
543                     entity = EntityTable.getDefaultEntityTable().entityName((short)c);
544                     if (entity != null)
545                         entity = "&" + entity + ";";
546                     else
547                         entity = "&#" + c + ";";
548                 }
549                 else
550                     entity = "&#" + c + ";";
551
552                 for (int i = 0; i < entity.length(); i++)
553                     addC((int)entity.charAt(i), linelen++);
554
555                 return;
556             }
557
558             if (c > 126 && c < 160)
559             {
560                 entity = "&#" + c + ";";
561
562                 for (int i = 0; i < entity.length(); i++)
563                     addC((int)entity.charAt(i), linelen++);
564
565                 return;
566             }
567
568             addC(c, linelen++);
569             return;
570         }
571
572         /* don't map utf8 chars to entities */
573         if (this.configuration.CharEncoding == Configuration.UTF8)
574         {
575             addC(c, linelen++);
576             return;
577         }
578
579         /* use numeric entities only  for XML */
580         if (this.configuration.XmlTags)
581         {
582             /* if ASCII use numeric entities for chars > 127 */
583             if (c > 127 && this.configuration.CharEncoding == Configuration.ASCII)
584             {
585                 entity = "&#" + c + ";";
586
587                 for (int i = 0; i < entity.length(); i++)
588                     addC((int)entity.charAt(i), linelen++);
589
590                 return;
591             }
592
593             /* otherwise output char raw */
594             addC(c, linelen++);
595             return;
596         }
597
598         /* default treatment for ASCII */
599         if (c > 126 || (c < ' ' && c != '\t'))
600         {
601             if (!this.configuration.NumEntities)
602             {
603                 entity = EntityTable.getDefaultEntityTable().entityName((short)c);
604                 if (entity != null)
605                     entity = "&" + entity + ";";
606                 else
607                     entity = "&#" + c + ";";
608             }
609             else
610                 entity = "&#" + c + ";";
611
612             for (int i = 0; i < entity.length(); i++)
613                 addC((int)entity.charAt(i), linelen++);
614
615             return;
616         }
617
618         addC(c, linelen++);
619     }
620
621     /* 
622       The line buffer is uint not char so we can
623       hold Unicode values unencoded. The translation
624       to UTF-8 is deferred to the outc routine called
625       to flush the line buffer.
626     */
627     private void printText(Out fout, short mode, int indent,
628                            byte[] textarray, int start, int end)
629     {
630         int i, c;
631         MutableInteger ci = new MutableInteger();
632
633         for (i = start; i < end; ++i)
634         {
635             if (indent + linelen >= this.configuration.wraplen)
636                 wrapLine(fout, indent);
637
638             c = ((int)textarray[i]) & 0xFF;  // Convert to unsigned.
639
640             /* look for UTF-8 multibyte character */
641             if (c > 0x7F)
642             {
643                  i += getUTF8(textarray, i, ci);
644                  c = ci.value;
645             }
646
647             if (c == '\n')
648             {
649                 flushLine(fout, indent);
650                 continue;
651             }
652
653             printChar(c, mode);
654         }
655     }
656
657     private void printString(Out fout, int indent, String str)
658     {
659         for (int i = 0; i < str.length(); i++ )
660             addC((int)str.charAt(i), linelen++);
661     }
662
663     private void printAttrValue(Out fout, int indent, String value, int delim, boolean wrappable)
664     {
665         int c;
666         MutableInteger ci = new MutableInteger();
667         boolean wasinstring = false;
668         byte[] valueChars = null;
669         int i;
670         short mode = (wrappable ? (short)(NORMAL | ATTRIBVALUE) :
671                                   (short)(PREFORMATTED | ATTRIBVALUE));
672
673         if (value != null)
674         {
675             valueChars = Lexer.getBytes(value);
676         }
677
678         /* look for ASP, Tango or PHP instructions for computed attribute value */
679         if (valueChars != null && valueChars.length >= 5 && valueChars[0] == '<')
680         {
681             if (valueChars[1] == '%' || valueChars[1] == '@'||
682                 (new String(valueChars, 0, 5)).equals("<?php"))
683                 mode |= CDATA;
684         }
685
686         if (delim == 0)
687             delim = '"';
688
689         addC('=', linelen++);
690
691         /* don't wrap after "=" for xml documents */
692         if (!this.configuration.XmlOut) {
693
694             if (indent + linelen < this.configuration.wraplen)
695                 wraphere = linelen;
696
697             if (indent + linelen >= this.configuration.wraplen)
698                 wrapLine(fout, indent);
699
700             if (indent + linelen < this.configuration.wraplen)
701                 wraphere = linelen;
702             else
703                 condFlushLine(fout, indent);
704         }
705
706         addC(delim, linelen++);
707
708         if (value != null)
709         {
710             InString = false;
711
712             i = 0;
713             while (i < valueChars.length)
714             {
715                 c = ((int)valueChars[i]) & 0xFF;  // Convert to unsigned.
716
717                 if (wrappable && c == ' ' && indent + linelen < this.configuration.wraplen)
718                 {
719                     wraphere = linelen;
720                     wasinstring = InString;
721                 }
722
723                 if (wrappable && wraphere > 0 && indent + linelen >= this.configuration.wraplen)
724                     wrapAttrVal(fout, indent, wasinstring);
725
726                 if (c == delim)
727                 {
728                     String entity;
729
730                     entity = (c == '"' ? "&quot;" : "&#39;");
731
732                     for (int j = 0; j < entity.length(); j++ )
733                         addC(entity.charAt(j), linelen++);
734
735                     ++i;
736                     continue;
737                 }
738                 else if (c == '"')
739                 {
740                     if (this.configuration.QuoteMarks)
741                     {
742                         addC('&', linelen++);
743                         addC('q', linelen++);
744                         addC('u', linelen++);
745                         addC('o', linelen++);
746                         addC('t', linelen++);
747                         addC(';', linelen++);
748                     }
749                     else
750                         addC('"', linelen++);
751
752                     if (delim == '\'')
753                         InString = !InString;
754
755                     ++i;
756                     continue;
757                 }
758                 else if (c == '\'')
759                 {
760                     if (this.configuration.QuoteMarks)
761                     {
762                         addC('&', linelen++);
763                         addC('#', linelen++);
764                         addC('3', linelen++);
765                         addC('9', linelen++);
766                         addC(';', linelen++);
767                     }
768                     else
769                         addC('\'', linelen++);
770
771                     if (delim == '"')
772                         InString = !InString;
773
774                     ++i;
775                     continue;
776                 }
777
778                 /* look for UTF-8 multibyte character */
779                 if (c > 0x7F)
780                 {
781                      i += getUTF8(valueChars, i, ci);
782                      c = ci.value;
783                 }
784
785                 ++i;
786
787                 if (c == '\n')
788                 {
789                     flushLine(fout, indent);
790                     continue;
791                 }
792
793                 printChar(c, mode);
794             }
795         }
796
797         InString = false;
798         addC(delim, linelen++);
799     }
800
801     private void printAttribute(Out fout, int indent, Node node, AttVal attr)
802     {
803         String name;
804         boolean wrappable = false;
805
806         if (this.configuration.IndentAttributes)
807         {
808             flushLine(fout, indent);
809             indent += this.configuration.spaces;
810         }
811
812         name = attr.attribute;
813
814         if (indent + linelen >= this.configuration.wraplen)
815             wrapLine(fout, indent);
816
817         if (!this.configuration.XmlTags && !this.configuration.XmlOut && attr.dict != null)
818         {
819             if (AttributeTable.getDefaultAttributeTable().isScript(name))
820                 wrappable = this.configuration.WrapScriptlets;
821             else if (!attr.dict.nowrap && this.configuration.WrapAttVals)
822                 wrappable = true;
823         }
824
825         if (indent + linelen < this.configuration.wraplen)
826         {
827             wraphere = linelen;
828             addC(' ', linelen++);
829         }
830         else
831         {
832             condFlushLine(fout, indent);
833             addC(' ', linelen++);
834         }
835
836         for (int i = 0; i < name.length(); i++ )
837             addC((int)Lexer.foldCase(name.charAt(i),
838                                      this.configuration.UpperCaseAttrs,
839                                      this.configuration.XmlTags),
840                  linelen++);
841
842         if (indent + linelen >= this.configuration.wraplen)
843             wrapLine(fout, indent);
844  
845         if (attr.value == null)
846         {
847             if (this.configuration.XmlTags || this.configuration.XmlOut)
848                 printAttrValue(fout, indent, attr.attribute, attr.delim, true);
849             else if (!attr.isBoolAttribute() && !Node.isNewNode(node))
850                 printAttrValue(fout, indent, "", attr.delim, true);
851             else if (indent + linelen < this.configuration.wraplen)
852                 wraphere = linelen;
853
854         }
855         else
856             printAttrValue(fout, indent, attr.value, attr.delim, wrappable);
857     }
858
859     private void printAttrs(Out fout, int indent,
860                             Node node, AttVal attr)
861     {
862         if (attr != null)
863         {
864             if (attr.next != null)
865                 printAttrs(fout, indent, node, attr.next);
866
867             if (attr.attribute != null)
868                 printAttribute(fout, indent, node, attr);
869             else if (attr.asp != null)
870             {
871                 addC(' ', linelen++);
872                 printAsp(fout, indent, attr.asp);
873             }
874             else if (attr.php != null)
875             {
876                 addC(' ', linelen++);
877                 printPhp(fout, indent, attr.php);
878             }
879         }
880
881         /* add xml:space attribute to pre and other elements */
882         if (configuration.XmlOut &&
883                 configuration.XmlSpace &&
884                 ParserImpl.XMLPreserveWhiteSpace(node, configuration.tt) &&
885                 node.getAttrByName("xml:space") == null)
886             printString(fout, indent, " xml:space=\"preserve\"");
887     }
888
889     /*
890      Line can be wrapped immediately after inline start tag provided
891      if follows a text node ending in a space, or it parent is an
892      inline element that that rule applies to. This behaviour was
893      reverse engineered from Netscape 3.0
894     */
895     private static boolean afterSpace(Node node)
896     {
897         Node prev;
898         int c;
899
900         if (node == null || node.tag == null || !((node.tag.model & Dict.CM_INLINE) != 0))
901             return true;
902
903         prev = node.prev;
904
905         if (prev != null)
906         {
907             if (prev.type == Node.TextNode && prev.end > prev.start)
908             {
909                 c = ((int)prev.textarray[prev.end - 1]) & 0xFF;  // Convert to unsigned.
910
911                 if (c == 160 || c == ' ' || c == '\n')
912                     return true;
913             }
914
915             return false;
916         }
917
918         return afterSpace(node.parent);
919     }
920
921     private void printTag(Lexer lexer, Out fout, short mode, int indent, Node node)
922     {
923         char c;
924         String p;
925         TagTable tt = this.configuration.tt;
926
927         addC('<', linelen++);
928
929         if (node.type == Node.EndTag)
930             addC('/', linelen++);
931
932         p = node.element;
933         for (int i = 0; i < p.length(); i++ )
934             addC((int)Lexer.foldCase(p.charAt(i),
935                                      this.configuration.UpperCaseTags,
936                                      this.configuration.XmlTags),
937                  linelen++);
938
939         printAttrs(fout, indent, node, node.attributes);
940
941         if ((this.configuration.XmlOut || lexer != null && lexer.isvoyager) &&
942                 (node.type == Node.StartEndTag || (node.tag.model & Dict.CM_EMPTY) != 0))
943         {
944             addC(' ', linelen++);   /* compatibility hack */
945             addC('/', linelen++);
946         }
947
948         addC('>', linelen++);;
949
950         if (node.type != Node.StartEndTag && !((mode & PREFORMATTED) != 0))
951         {
952             if (indent + linelen >= this.configuration.wraplen)
953                 wrapLine(fout, indent);
954
955             if (indent + linelen < this.configuration.wraplen)
956             {
957                 /*
958                  wrap after start tag if is <br/> or if it's not
959                  inline or it is an empty tag followed by </a>
960                 */
961                 if (afterSpace(node))
962                 {
963                     if (!((mode & NOWRAP) != 0) &&
964                         (!((node.tag.model & Dict.CM_INLINE) != 0) ||
965                           (node.tag == tt.tagBr) ||
966                           (((node.tag.model & Dict.CM_EMPTY) != 0) && 
967                           node.next == null &&
968                           node.parent.tag == tt.tagA)))
969                     {
970                         wraphere = linelen;
971                     }
972                 }
973             }
974             else
975                 condFlushLine(fout, indent);
976         }
977     }
978
979     private void printEndTag(Out fout, short mode, int indent, Node node)
980     {
981         char c;
982         String p;
983
984        /*
985          Netscape ignores SGML standard by not ignoring a
986          line break before </A> or </U> etc. To avoid rendering 
987          this as an underlined space, I disable line wrapping
988          before inline end tags by the #if 0 ... #endif
989        */
990 if (false) {
991         if (indent + linelen < this.configuration.wraplen && !((mode & NOWRAP) != 0))
992             wraphere = linelen;
993 }
994
995         addC('<', linelen++);
996         addC('/', linelen++);
997
998         p = node.element;
999         for (int i = 0; i < p.length(); i++ )
1000             addC((int)Lexer.foldCase(p.charAt(i),
1001                                      this.configuration.UpperCaseTags,
1002                                      this.configuration.XmlTags),
1003                  linelen++);
1004
1005         addC('>', linelen++);
1006     }
1007
1008     private void printComment(Out fout, int indent, Node node)
1009     {
1010         if (indent + linelen < this.configuration.wraplen)
1011             wraphere = linelen;
1012
1013         addC('<', linelen++);
1014         addC('!', linelen++);
1015         addC('-', linelen++);
1016         addC('-', linelen++);
1017 if (false) {
1018         if (linelen < this.configuration.wraplen)
1019             wraphere = linelen;
1020 }
1021         printText(fout, COMMENT, indent,
1022                         node.textarray, node.start, node.end);
1023 if (false) {
1024         if (indent + linelen < this.configuration.wraplen)
1025             wraphere = linelen;
1026 }
1027         // See Lexer.java: AQ 8Jul2000
1028         addC('-', linelen++);
1029         addC('-', linelen++);
1030         addC('>', linelen++);
1031
1032         if (node.linebreak)
1033             flushLine(fout, indent);
1034     }
1035
1036     private void printDocType(Out fout, int indent, Node node)
1037     {
1038         boolean q = this.configuration.QuoteMarks;
1039
1040         this.configuration.QuoteMarks = false;
1041
1042         if (indent + linelen < this.configuration.wraplen)
1043             wraphere = linelen;
1044
1045         condFlushLine(fout, indent);
1046
1047         addC('<', linelen++);
1048         addC('!', linelen++);
1049         addC('D', linelen++);
1050         addC('O', linelen++);
1051         addC('C', linelen++);
1052         addC('T', linelen++);
1053         addC('Y', linelen++);
1054         addC('P', linelen++);
1055         addC('E', linelen++);
1056         addC(' ', linelen++);
1057
1058         if (indent + linelen < this.configuration.wraplen)
1059             wraphere = linelen;
1060
1061         printText(fout, (short)0, indent,
1062                         node.textarray, node.start, node.end);
1063
1064         if (linelen < this.configuration.wraplen)
1065             wraphere = linelen;
1066
1067         addC('>', linelen++);
1068         this.configuration.QuoteMarks = q;
1069         condFlushLine(fout, indent);
1070     }
1071
1072     private void printPI(Out fout, int indent, Node node)
1073     {
1074         if (indent + linelen < this.configuration.wraplen)
1075             wraphere = linelen;
1076
1077         addC('<', linelen++);
1078         addC('?', linelen++);
1079
1080         /* set CDATA to pass < and > unescaped */
1081         printText(fout, CDATA, indent,
1082                     node.textarray, node.start, node.end);
1083
1084         if (node.textarray[node.end - 1] != (byte)'?')
1085             addC('?', linelen++);
1086
1087         addC('>', linelen++);
1088         condFlushLine(fout, indent);
1089     }
1090
1091     /* note ASP and JSTE share <% ... %> syntax */
1092     private void printAsp(Out fout, int indent, Node node)
1093     {
1094         int savewraplen = this.configuration.wraplen;
1095
1096         /* disable wrapping if so requested */
1097
1098         if (!this.configuration.WrapAsp || !this.configuration.WrapJste)
1099             this.configuration.wraplen = 0xFFFFFF;  /* a very large number */
1100 if (false) { //#if 0
1101         if (indent + linelen < this.configuration.wraplen)
1102             wraphere = linelen;
1103 } //#endif
1104
1105         addC('<', linelen++);
1106         addC('%', linelen++);
1107
1108         printText(fout, (this.configuration.WrapAsp ? CDATA : COMMENT), indent,
1109                     node.textarray, node.start, node.end);
1110
1111         addC('%', linelen++);
1112         addC('>', linelen++);
1113         /* condFlushLine(fout, indent); */
1114         this.configuration.wraplen = savewraplen;
1115     }
1116
1117     /* JSTE also supports <# ... #> syntax */
1118     private void printJste(Out fout, int indent, Node node)
1119     {
1120         int savewraplen = this.configuration.wraplen;
1121
1122         /* disable wrapping if so requested */
1123
1124         if (!this.configuration.WrapJste)
1125             this.configuration.wraplen = 0xFFFFFF;  /* a very large number */
1126
1127         addC('<', linelen++);
1128         addC('#', linelen++);
1129
1130         printText(fout, (this.configuration.WrapJste ? CDATA : COMMENT), indent,
1131                     node.textarray, node.start, node.end);
1132
1133         addC('#', linelen++);
1134         addC('>', linelen++);
1135         /* condFlushLine(fout, indent); */
1136         this.configuration.wraplen = savewraplen;
1137     }
1138
1139     /* PHP is based on XML processing instructions */
1140     private void printPhp(Out fout, int indent, Node node)
1141     {
1142         int savewraplen = this.configuration.wraplen;
1143
1144         /* disable wrapping if so requested */
1145
1146         if (!this.configuration.WrapPhp)
1147             this.configuration.wraplen = 0xFFFFFF;  /* a very large number */
1148
1149 if (false) { //#if 0
1150         if (indent + linelen < this.configuration.wraplen)
1151             wraphere = linelen;
1152 } //#endif
1153         addC('<', linelen++);
1154         addC('?', linelen++);
1155
1156         printText(fout, (this.configuration.WrapPhp ? CDATA : COMMENT), indent,
1157                         node.textarray, node.start, node.end);
1158
1159         addC('?', linelen++);
1160         addC('>', linelen++);
1161         /* PCondFlushLine(fout, indent); */
1162         this.configuration.wraplen = savewraplen;
1163     }
1164
1165     private void printCDATA(Out fout, int indent, Node node)
1166     {
1167         int savewraplen = this.configuration.wraplen;
1168
1169         condFlushLine(fout, indent);
1170
1171         /* disable wrapping */
1172
1173         this.configuration.wraplen = 0xFFFFFF;  /* a very large number */
1174
1175         addC('<', linelen++);
1176         addC('!', linelen++);
1177         addC('[', linelen++);
1178         addC('C', linelen++);
1179         addC('D', linelen++);
1180         addC('A', linelen++);
1181         addC('T', linelen++);
1182         addC('A', linelen++);
1183         addC('[', linelen++);
1184
1185         printText(fout, COMMENT, indent,
1186                         node.textarray, node.start, node.end);
1187
1188         addC(']', linelen++);
1189         addC(']', linelen++);
1190         addC('>', linelen++);
1191         condFlushLine(fout, indent);
1192         this.configuration.wraplen = savewraplen;
1193     }
1194
1195     private void printSection(Out fout, int indent, Node node)
1196     {
1197         int savewraplen = this.configuration.wraplen;
1198
1199         /* disable wrapping if so requested */
1200
1201         if (!this.configuration.WrapSection)
1202             this.configuration.wraplen = 0xFFFFFF;  /* a very large number */
1203
1204 if (false) { //#if 0
1205         if (indent + linelen < this.configuration.wraplen)
1206             wraphere = linelen;
1207 } //#endif
1208         addC('<', linelen++);
1209         addC('!', linelen++);
1210         addC('[', linelen++);
1211
1212         printText(fout, (this.configuration.WrapSection ? CDATA : COMMENT), indent,
1213                         node.textarray, node.start, node.end);
1214
1215         addC(']', linelen++);
1216         addC('>', linelen++);
1217         /* PCondFlushLine(fout, indent); */
1218         this.configuration.wraplen = savewraplen;
1219     }
1220
1221     private boolean shouldIndent(Node node)
1222     {
1223         TagTable tt = this.configuration.tt;
1224
1225         if (!this.configuration.IndentContent)
1226             return false;
1227
1228         if (this.configuration.SmartIndent)
1229         {
1230             if (node.content != null && ((node.tag.model & Dict.CM_NO_INDENT) != 0))
1231             {
1232                 for (node = node.content; node != null; node = node.next)
1233                     if (node.tag != null && (node.tag.model & Dict.CM_BLOCK) != 0)
1234                         return true;
1235
1236                 return false;
1237             }
1238
1239             if ((node.tag.model & Dict.CM_HEADING) != 0)
1240                 return false;
1241
1242             if (node.tag == tt.tagP)
1243                 return false;
1244
1245             if (node.tag == tt.tagTitle)
1246                 return false;
1247         }
1248
1249         if ((node.tag.model & (Dict.CM_FIELD | Dict.CM_OBJECT)) != 0)
1250             return true;
1251
1252         if (node.tag == tt.tagMap)
1253             return true;
1254
1255         return !((node.tag.model & Dict.CM_INLINE) != 0);
1256     }
1257
1258     public void printTree(Out fout, short mode, int indent,
1259                           Lexer lexer, Node node)
1260     {
1261         Node content, last;
1262         TagTable tt = this.configuration.tt;
1263
1264         if (node == null)
1265             return;
1266
1267         if (node.type == Node.TextNode)
1268             printText(fout, mode, indent,
1269                         node.textarray, node.start, node.end);
1270         else if (node.type == Node.CommentTag)
1271         {
1272             printComment(fout, indent, node);
1273         }
1274         else if (node.type == Node.RootNode)
1275         {
1276             for (content = node.content;
1277                     content != null;
1278                     content = content.next)
1279                printTree(fout, mode, indent, lexer, content);
1280         }
1281         else if (node.type == Node.DocTypeTag)
1282             printDocType(fout, indent, node);
1283         else if (node.type == Node.ProcInsTag)
1284             printPI(fout, indent, node);
1285         else if (node.type == Node.CDATATag)
1286             printCDATA(fout, indent, node);
1287         else if (node.type == Node.SectionTag)
1288             printSection(fout, indent, node);
1289         else if (node.type == Node.AspTag)
1290             printAsp(fout, indent, node);
1291         else if (node.type == Node.JsteTag)
1292             printJste(fout, indent, node);
1293         else if (node.type == Node.PhpTag)
1294             printPhp(fout, indent, node);
1295         else if ((node.tag.model & Dict.CM_EMPTY) != 0 || node.type == Node.StartEndTag)
1296         {
1297             if (!((node.tag.model & Dict.CM_INLINE) != 0))
1298                 condFlushLine(fout, indent);
1299
1300             if (node.tag == tt.tagBr && node.prev != null &&
1301                 node.prev.tag != tt.tagBr && this.configuration.BreakBeforeBR)
1302                 flushLine(fout, indent);
1303
1304             if (this.configuration.MakeClean && node.tag == tt.tagWbr)
1305                 printString(fout, indent, " ");
1306             else
1307                 printTag(lexer, fout, mode, indent, node);
1308
1309             if (node.tag == tt.tagParam || node.tag == tt.tagArea)
1310                 condFlushLine(fout, indent);
1311             else if (node.tag == tt.tagBr || node.tag == tt.tagHr)
1312                 flushLine(fout, indent);
1313         }
1314         else /* some kind of container element */
1315         {
1316             if (node.tag != null && node.tag.parser == ParserImpl.getParsePre())
1317             {
1318                 condFlushLine(fout, indent);
1319
1320                 indent = 0;
1321                 condFlushLine(fout, indent);
1322                 printTag(lexer, fout, mode, indent, node);
1323                 flushLine(fout, indent);
1324
1325                 for (content = node.content;
1326                         content != null;
1327                         content = content.next)
1328                     printTree(fout, (short)(mode | PREFORMATTED | NOWRAP), indent, lexer, content);
1329
1330                 condFlushLine(fout, indent);
1331                 printEndTag(fout, mode, indent, node);
1332                 flushLine(fout, indent);
1333
1334                 if (this.configuration.IndentContent == false && node.next != null)
1335                     flushLine(fout, indent);
1336             }
1337             else if (node.tag == tt.tagStyle || node.tag == tt.tagScript)
1338             {
1339                 condFlushLine(fout, indent);
1340
1341                 indent = 0;
1342                 condFlushLine(fout, indent);
1343                 printTag(lexer, fout, mode, indent, node);
1344                 flushLine(fout, indent);
1345
1346                 for (content = node.content;
1347                         content != null;
1348                         content = content.next)
1349                     printTree(fout, (short)(mode | PREFORMATTED | NOWRAP |CDATA), indent, lexer, content);
1350
1351                 condFlushLine(fout, indent);
1352                 printEndTag(fout, mode, indent, node);
1353                 flushLine(fout, indent);
1354
1355                 if (this.configuration.IndentContent == false && node.next != null)
1356                     flushLine(fout, indent);
1357             }
1358             else if ((node.tag.model & Dict.CM_INLINE) != 0)
1359             {
1360                 if (this.configuration.MakeClean)
1361                 {
1362                     /* discards <font> and </font> tags */
1363                     if (node.tag == tt.tagFont)
1364                     {
1365                         for (content = node.content;
1366                                 content != null;
1367                                 content = content.next)
1368                             printTree(fout, mode, indent, lexer, content);
1369                         return;
1370                     }
1371
1372                     /* replace <nobr>...</nobr> by &nbsp; or &#160; etc. */
1373                     if (node.tag == tt.tagNobr)
1374                     {
1375                         for (content = node.content;
1376                                 content != null;
1377                                 content = content.next)
1378                             printTree(fout, (short)(mode|NOWRAP), indent, lexer, content);
1379                         return;
1380                     }
1381                 }
1382
1383                 /* otherwise a normal inline element */
1384
1385                 printTag(lexer, fout, mode, indent, node);
1386
1387                 /* indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET */
1388
1389                 if (shouldIndent(node))
1390                 {
1391                     condFlushLine(fout, indent);
1392                     indent += this.configuration.spaces;
1393
1394                     for (content = node.content;
1395                             content != null;
1396                             content = content.next)
1397                         printTree(fout, mode, indent, lexer, content);
1398
1399                     condFlushLine(fout, indent);
1400                     indent -= this.configuration.spaces;
1401                     condFlushLine(fout, indent);
1402                 }
1403                 else
1404                 {
1405
1406                     for (content = node.content;
1407                             content != null;
1408                             content = content.next)
1409                         printTree(fout, mode, indent, lexer, content);
1410                 }
1411
1412                 printEndTag(fout, mode, indent, node);
1413             }
1414             else /* other tags */
1415             {
1416                 condFlushLine(fout, indent);
1417
1418                 if (this.configuration.SmartIndent && node.prev != null)
1419                     flushLine(fout, indent);
1420
1421                 if (this.configuration.HideEndTags == false ||
1422                     !(node.tag != null && ((node.tag.model & Dict.CM_OMITST) != 0)))
1423                 {
1424                     printTag(lexer, fout, mode, indent, node);
1425
1426                     if (shouldIndent(node))
1427                         condFlushLine(fout, indent);
1428                     else if ((node.tag.model & Dict.CM_HTML) != 0 ||
1429                              node.tag == tt.tagNoframes ||
1430                                 ((node.tag.model & Dict.CM_HEAD) != 0 &&
1431                                 !(node.tag == tt.tagTitle)))
1432                         flushLine(fout, indent);
1433                 }
1434
1435                 if (node.tag == tt.tagBody && this.configuration.BurstSlides)
1436                     printSlide(fout, mode, (this.configuration.IndentContent ? indent+this.configuration.spaces : indent), lexer);
1437                 else
1438                 {
1439                     last = null;
1440
1441                     for (content = node.content;
1442                             content != null; content = content.next)
1443                     {
1444                         /* kludge for naked text before block level tag */
1445                         if (last != null && !this.configuration.IndentContent && last.type == Node.TextNode &&
1446                             content.tag != null && (content.tag.model & Dict.CM_BLOCK) != 0)
1447                         {
1448                             flushLine(fout, indent);
1449                             flushLine(fout, indent);
1450                         }
1451
1452                         printTree(fout, mode,
1453                             (shouldIndent(node) ? indent+this.configuration.spaces : indent), lexer, content);
1454
1455                         last = content;
1456                     }
1457                 }
1458
1459                 /* don't flush line for td and th */
1460                 if (shouldIndent(node) ||
1461                     (((node.tag.model & Dict.CM_HTML) != 0 || node.tag == tt.tagNoframes ||
1462                         ((node.tag.model & Dict.CM_HEAD) != 0 && !(node.tag == tt.tagTitle)))
1463                         && this.configuration.HideEndTags == false))
1464                 {
1465                     condFlushLine(fout, (this.configuration.IndentContent ? indent+this.configuration.spaces : indent));
1466
1467                     if (this.configuration.HideEndTags == false || !((node.tag.model & Dict.CM_OPT) != 0))
1468                     {
1469                         printEndTag(fout, mode, indent, node);
1470                         flushLine(fout, indent);
1471                     }
1472                 }
1473                 else
1474                 {
1475                     if (this.configuration.HideEndTags == false || !((node.tag.model & Dict.CM_OPT) != 0))
1476                         printEndTag(fout, mode, indent, node);
1477
1478                     flushLine(fout, indent);
1479                 }
1480
1481                 if (this.configuration.IndentContent == false &&
1482                     node.next != null &&
1483                     this.configuration.HideEndTags == false &&
1484                     (node.tag.model & (Dict.CM_BLOCK|Dict.CM_LIST|Dict.CM_DEFLIST|Dict.CM_TABLE)) != 0)
1485                 {
1486                     flushLine(fout, indent);
1487                 }
1488             }
1489         }
1490     }
1491
1492     public void printXMLTree(Out fout, short mode, int indent,
1493                              Lexer lexer, Node node)
1494     {
1495         TagTable tt = this.configuration.tt;
1496
1497         if (node == null)
1498             return;
1499
1500         if (node.type == Node.TextNode)
1501         {
1502             printText(fout, mode, indent,
1503                         node.textarray, node.start, node.end);
1504         }
1505         else if (node.type == Node.CommentTag)
1506         {
1507             condFlushLine(fout, indent);
1508             printComment(fout, 0, node);
1509             condFlushLine(fout, 0);
1510         }
1511         else if (node.type == Node.RootNode)
1512         {
1513             Node content;
1514
1515             for (content = node.content;
1516                     content != null;
1517                     content = content.next)
1518                printXMLTree(fout, mode, indent, lexer, content);
1519         }
1520         else if (node.type == Node.DocTypeTag)
1521             printDocType(fout, indent, node);
1522         else if (node.type == Node.ProcInsTag)
1523             printPI(fout, indent, node);
1524         else if (node.type == Node.SectionTag)
1525             printSection(fout, indent, node);
1526         else if (node.type == Node.AspTag)
1527             printAsp(fout, indent, node);
1528         else if (node.type == Node.JsteTag)
1529             printJste(fout, indent, node);
1530         else if (node.type == Node.PhpTag)
1531             printPhp(fout, indent, node);
1532         else if ((node.tag.model & Dict.CM_EMPTY) != 0 || node.type == Node.StartEndTag)
1533         {
1534             condFlushLine(fout, indent);
1535             printTag(lexer, fout, mode, indent, node);
1536             flushLine(fout, indent);
1537
1538             if (node.next != null)
1539                 flushLine(fout, indent);
1540         }
1541         else /* some kind of container element */
1542         {
1543             Node content;
1544             boolean mixed = false;
1545             int cindent;
1546
1547             for (content = node.content; content != null; content = content.next)
1548             {
1549                 if (content.type == Node.TextNode)
1550                 {
1551                     mixed = true;
1552                     break;
1553                 }
1554             }
1555
1556             condFlushLine(fout, indent);
1557
1558             if (ParserImpl.XMLPreserveWhiteSpace(node, tt))
1559             {
1560                 indent = 0;
1561                 cindent = 0;
1562                 mixed = false;
1563             }
1564             else if (mixed)
1565                 cindent = indent;
1566             else
1567                 cindent = indent + this.configuration.spaces;
1568
1569             printTag(lexer, fout, mode, indent, node);
1570
1571             if (!mixed)
1572                 flushLine(fout, indent);
1573  
1574             for (content = node.content;
1575                     content != null;
1576                     content = content.next)
1577                 printXMLTree(fout, mode, cindent, lexer, content);
1578
1579             if (!mixed)
1580                 condFlushLine(fout, cindent);
1581             printEndTag(fout, mode, indent, node);
1582             condFlushLine(fout, indent);
1583
1584             if (node.next != null)
1585                 flushLine(fout, indent);
1586         }
1587     }
1588
1589
1590     /* split parse tree by h2 elements and output to separate files */
1591
1592     /* counts number of h2 children belonging to node */
1593     public int countSlides(Node node)
1594     {
1595         int n = 1;
1596         TagTable tt = this.configuration.tt;
1597
1598         for (node = node.content; node != null; node = node.next)
1599             if (node.tag == tt.tagH2)
1600                 ++n;
1601
1602         return n;
1603     }
1604
1605     /*
1606        inserts a space gif called "dot.gif" to ensure
1607        that the  slide is at least n pixels high
1608      */
1609     private void printVertSpacer(Out fout, int indent)
1610     {
1611         condFlushLine(fout, indent);
1612         printString(fout, indent , 
1613         "<img width=\"0\" height=\"0\" hspace=\"1\" src=\"dot.gif\" vspace=\"%d\" align=\"left\">");
1614         condFlushLine(fout, indent);
1615     }
1616
1617     private void printNavBar(Out fout, int indent)
1618     {
1619         String buf;
1620
1621         condFlushLine(fout, indent);
1622         printString(fout, indent , "<center><small>");
1623
1624         if (slide > 1)
1625         {
1626             buf = "<a href=\"slide" +
1627                   (new Integer(slide - 1)).toString() +
1628                   ".html\">previous</a> | ";
1629             printString(fout, indent , buf);
1630             condFlushLine(fout, indent);
1631
1632             if (slide < count)
1633                 printString(fout, indent , "<a href=\"slide1.html\">start</a> | ");
1634             else
1635                 printString(fout, indent , "<a href=\"slide1.html\">start</a>");
1636
1637             condFlushLine(fout, indent);
1638         }
1639
1640         if (slide < count)
1641         {
1642             buf = "<a href=\"slide" +
1643                   (new Integer(slide + 1)).toString() +
1644                   ".html\">next</a>";
1645             printString(fout, indent , buf);
1646         }
1647
1648         printString(fout, indent , "</small></center>");
1649         condFlushLine(fout, indent);
1650     }
1651
1652     /*
1653       Called from printTree to print the content of a slide from
1654       the node slidecontent. On return slidecontent points to the
1655       node starting the next slide or null. The variables slide
1656       and count are used to customise the navigation bar.
1657     */
1658     public void printSlide(Out fout, short mode, int indent, Lexer lexer)
1659     {
1660         Node content, last;
1661         TagTable tt = this.configuration.tt;
1662
1663         /* insert div for onclick handler */
1664         String s;
1665         s = "<div onclick=\"document.location='slide" +
1666             (new Integer(slide < count ? slide + 1 : 1)).toString() +
1667             ".html'\">";
1668         printString(fout, indent, s);
1669         condFlushLine(fout, indent);
1670
1671         /* first print the h2 element and navbar */
1672         if (slidecontent.tag == tt.tagH2)
1673         {
1674             printNavBar(fout, indent);
1675
1676             /* now print an hr after h2 */
1677
1678             addC('<', linelen++);
1679
1680
1681             addC((int)Lexer.foldCase('h',
1682                                      this.configuration.UpperCaseTags,
1683                                      this.configuration.XmlTags),
1684                  linelen++);
1685             addC((int)Lexer.foldCase('r',
1686                                      this.configuration.UpperCaseTags,
1687                                      this.configuration.XmlTags),
1688                  linelen++);
1689
1690             if (this.configuration.XmlOut == true)
1691                 printString(fout, indent , " />");
1692             else
1693                 addC('>', linelen++);
1694
1695
1696             if (this.configuration.IndentContent == true)
1697                 condFlushLine(fout, indent);
1698
1699             /* PrintVertSpacer(fout, indent); */
1700
1701             /*condFlushLine(fout, indent); */
1702
1703             /* print the h2 element */
1704             printTree(fout, mode,
1705                 (this.configuration.IndentContent ? indent+this.configuration.spaces : indent), lexer, slidecontent);
1706
1707             slidecontent = slidecontent.next;
1708         }
1709     
1710         /* now continue until we reach the next h2 */
1711
1712         last = null;
1713         content = slidecontent;
1714
1715         for (; content != null; content = content.next)
1716         {
1717             if (content.tag == tt.tagH2)
1718                 break;
1719
1720             /* kludge for naked text before block level tag */
1721             if (last != null && !this.configuration.IndentContent && last.type == Node.TextNode &&
1722                 content.tag != null && (content.tag.model & Dict.CM_BLOCK) != 0)
1723             {
1724                 flushLine(fout, indent);
1725                 flushLine(fout, indent);
1726             }
1727
1728             printTree(fout, mode,
1729                 (this.configuration.IndentContent ? indent+this.configuration.spaces : indent), lexer, content);
1730
1731             last = content;
1732         }
1733
1734         slidecontent = content;
1735
1736         /* now print epilog */
1737
1738         condFlushLine(fout, indent);
1739
1740         printString(fout, indent , "<br clear=\"all\">");
1741         condFlushLine(fout, indent);
1742
1743         addC('<', linelen++);
1744
1745
1746         addC((int)Lexer.foldCase('h',
1747                                  this.configuration.UpperCaseTags,
1748                                  this.configuration.XmlTags),
1749              linelen++);
1750         addC((int)Lexer.foldCase('r',
1751                                  this.configuration.UpperCaseTags,
1752                                  this.configuration.XmlTags),
1753              linelen++);
1754
1755         if (this.configuration.XmlOut == true)
1756             printString(fout, indent , " />");
1757         else
1758             addC('>', linelen++);
1759
1760
1761         if (this.configuration.IndentContent == true)
1762             condFlushLine(fout, indent);
1763
1764         printNavBar(fout, indent);
1765
1766         /* end tag for div */
1767         printString(fout, indent, "</div>");
1768         condFlushLine(fout, indent);
1769     }
1770
1771
1772     /*
1773     Add meta element for page transition effect, this works on IE but not NS
1774     */
1775
1776     public void addTransitionEffect(Lexer lexer, Node root, short effect, double duration)
1777     {
1778         Node head = root.findHEAD(lexer.configuration.tt);
1779         String transition;
1780
1781         if (0 <= effect && effect <= 23)
1782             transition = "revealTrans(Duration=" +
1783                          (new Double(duration)).toString() +
1784                          ",Transition=" + effect + ")";
1785         else
1786             transition = "blendTrans(Duration=" +
1787                          (new Double(duration)).toString() + ")";
1788
1789         if (head != null)
1790         {
1791             Node meta = lexer.inferredTag("meta");
1792             meta.addAttribute("http-equiv", "Page-Enter");
1793             meta.addAttribute("content", transition);
1794             Node.insertNodeAtStart(head, meta);
1795         }
1796     }
1797
1798     public void createSlides(Lexer lexer, Node root)
1799     {
1800         Node body;
1801         String buf;
1802         Out out = new OutImpl();
1803
1804         body = root.findBody(lexer.configuration.tt);
1805         count = countSlides(body);
1806         slidecontent = body.content;
1807         addTransitionEffect(lexer, root, EFFECT_BLEND, 3.0);
1808
1809         for (slide = 1; slide <= count; ++slide)
1810         {
1811             buf = "slide" + slide + ".html";
1812             out.state = StreamIn.FSM_ASCII;
1813             out.encoding = this.configuration.CharEncoding;
1814
1815             try
1816             {
1817                 out.out = new FileOutputStream(buf);
1818                 printTree(out, (short)0, 0, lexer, root);
1819                 flushLine(out, 0);
1820                 out.out.close();
1821             }
1822             catch (IOException e)
1823             {
1824                 System.err.println(buf + e.toString() );
1825             }
1826         }
1827
1828         /*
1829          delete superfluous slides by deleting slideN.html
1830          for N = count+1, count+2, etc. until no such file
1831          is found.     
1832         */
1833
1834         for (;;)
1835         {
1836             buf = "slide" + slide + "html";
1837
1838             if (!(new File(buf)).delete())
1839                 break;
1840
1841             ++slide;
1842         }
1843     }
1844
1845 }