1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 package org.w3c.tidy;
55
56 import java.io.File;
57 import java.io.FileOutputStream;
58 import java.io.IOException;
59 import java.text.NumberFormat;
60
61
62 /***
63 * Pretty print parse tree. Block-level and unknown elements are printed on new lines and their contents indented 2
64 * spaces Inline elements are printed inline. Inline content is wrapped on spaces (except in attribute values or
65 * preformatted text, after start tags and before end tags.
66 * @author Dave Raggett <a href="mailto:dsr@w3.org">dsr@w3.org </a>
67 * @author Andy Quick <a href="mailto:ac.quick@sympatico.ca">ac.quick@sympatico.ca </a> (translation to Java)
68 * @author Fabrizio Giustina
69 * @version $Revision: 1.60 $ ($Author: fgiust $)
70 */
71 public class PPrint
72 {
73
74 /***
75 * position: normal.
76 */
77 private static final short NORMAL = 0;
78
79 /***
80 * position: preformatted text.
81 */
82 private static final short PREFORMATTED = 1;
83
84 /***
85 * position: comment.
86 */
87 private static final short COMMENT = 2;
88
89 /***
90 * position: attribute value.
91 */
92 private static final short ATTRIBVALUE = 4;
93
94 /***
95 * position: nowrap.
96 */
97 private static final short NOWRAP = 8;
98
99 /***
100 * position: cdata.
101 */
102 private static final short CDATA = 16;
103
104 /***
105 * Start cdata token.
106 */
107 private static final String CDATA_START = "<![CDATA[";
108
109 /***
110 * End cdata token.
111 */
112 private static final String CDATA_END = "]]>";
113
114 /***
115 * Javascript comment start.
116 */
117 private static final String JS_COMMENT_START = "//";
118
119 /***
120 * Javascript comment end.
121 */
122 private static final String JS_COMMENT_END = "";
123
124 /***
125 * VB comment start.
126 */
127 private static final String VB_COMMENT_START = "\'";
128
129 /***
130 * VB comment end.
131 */
132 private static final String VB_COMMENT_END = "";
133
134 /***
135 * CSS comment start.
136 */
137 private static final String CSS_COMMENT_START = "/*";
138
139 /***
140 * CSS comment end.
141 */
142 private static final String CSS_COMMENT_END = "*/";
143
144 /***
145 * Default comment start.
146 */
147 private static final String DEFAULT_COMMENT_START = "";
148
149 /***
150 * Default comment end.
151 */
152 private static final String DEFAULT_COMMENT_END = "";
153
154 private int[] linebuf;
155
156 private int lbufsize;
157
158 private int linelen;
159
160 private int wraphere;
161
162 private boolean inAttVal;
163
164 private boolean inString;
165
166 /***
167 * Current slide number.
168 */
169 private int slide;
170
171 /***
172 * Total slides count.
173 */
174 private int count;
175
176 private Node slidecontent;
177
178 /***
179 * current configuration.
180 */
181 private Configuration configuration;
182
183 /***
184 * Instantiates a new PPrint.
185 * @param configuration configuration
186 */
187 public PPrint(Configuration configuration)
188 {
189 this.configuration = configuration;
190 }
191
192 /***
193 * @param ind
194 * @return
195 */
196 int cWrapLen(int ind)
197 {
198
199 if ("zh".equals(this.configuration.language))
200 {
201
202
203
204 return (ind + ((this.configuration.wraplen - ind) / 2));
205 }
206 if ("ja".equals(this.configuration.language))
207 {
208
209 return (ind + (((this.configuration.wraplen - ind) * 7) / 10));
210 }
211 return (this.configuration.wraplen);
212
213 }
214
215 /***
216 * return one less than the number of bytes used by the UTF-8 byte sequence. The Unicode char is returned in ch.
217 * @param str points to the UTF-8 byte sequence
218 * @param start starting offset in str
219 * @param ch initialized to 1st byte, passed as an array to allow modification
220 * @return one less that the number of bytes used by UTF-8 char
221 */
222 public static int getUTF8(byte[] str, int start, int[] ch)
223 {
224
225 int[] n = new int[1];
226
227 int[] bytes = new int[]{0};
228
229
230
231 byte[] successorBytes = str;
232
233 boolean err = EncodingUtils.decodeUTF8BytesToChar(
234 n,
235 TidyUtils.toUnsigned(str[start]),
236 successorBytes,
237 null,
238 bytes,
239 start + 1);
240
241 if (err)
242 {
243 n[0] = 0xFFFD;
244 }
245 ch[0] = n[0];
246 return bytes[0] - 1;
247
248 }
249
250 /***
251 * store char c as UTF-8 encoded byte stream.
252 * @param buf
253 * @param start
254 * @param c
255 * @return
256 */
257 public static int putUTF8(byte[] buf, int start, int c)
258 {
259 int[] count = new int[]{0};
260
261 boolean err = EncodingUtils.encodeCharToUTF8Bytes(c, buf, null, count);
262 if (err)
263 {
264
265 buf[0] = (byte) 0xEF;
266 buf[1] = (byte) 0xBF;
267 buf[2] = (byte) 0xBD;
268 count[0] = 3;
269 }
270
271 start += count[0];
272
273 return start;
274 }
275
276 private void addC(int c, int index)
277 {
278 if (index + 1 >= lbufsize)
279 {
280 while (index + 1 >= lbufsize)
281 {
282 if (lbufsize == 0)
283 {
284 lbufsize = 256;
285 }
286 else
287 {
288 lbufsize = lbufsize * 2;
289 }
290 }
291
292 int[] temp = new int[lbufsize];
293 if (linebuf != null)
294 {
295 System.arraycopy(linebuf, 0, temp, 0, index);
296 }
297 linebuf = temp;
298 }
299
300 linebuf[index] = c;
301 }
302
303 /***
304 * Adds an ascii String.
305 * @param str String to be added
306 * @param index actual line lenght
307 * @return final line length
308 */
309 private int addAsciiString(String str, int index)
310 {
311
312 int len = str.length();
313 if (index + len >= lbufsize)
314 {
315 while (index + len >= lbufsize)
316 {
317 if (lbufsize == 0)
318 {
319 lbufsize = 256;
320 }
321 else
322 {
323 lbufsize = lbufsize * 2;
324 }
325 }
326
327 int[] temp = new int[lbufsize];
328 if (linebuf != null)
329 {
330 System.arraycopy(linebuf, 0, temp, 0, index);
331 }
332 linebuf = temp;
333 }
334
335 for (int ix = 0; ix < len; ++ix)
336 {
337 linebuf[index + ix] = str.charAt(ix);
338 }
339 return index + len;
340 }
341
342 /***
343 * @param fout
344 * @param indent
345 */
346 private void wrapLine(Out fout, int indent)
347 {
348 int i, p, q;
349
350 if (wraphere == 0)
351 {
352 return;
353 }
354
355 for (i = 0; i < indent; ++i)
356 {
357 fout.outc(' ');
358 }
359
360 for (i = 0; i < wraphere; ++i)
361 {
362 fout.outc(linebuf[i]);
363 }
364
365 if (inString)
366 {
367 fout.outc(' ');
368 fout.outc('//');
369 }
370
371 fout.newline();
372
373 if (linelen > wraphere)
374 {
375 p = 0;
376
377 if (linebuf[wraphere] == ' ')
378 {
379 ++wraphere;
380 }
381
382 q = wraphere;
383 addC('\0', linelen);
384
385 while (true)
386 {
387 linebuf[p] = linebuf[q];
388 if (linebuf[q] == 0)
389 {
390 break;
391 }
392 p++;
393 q++;
394 }
395 linelen -= wraphere;
396 }
397 else
398 {
399 linelen = 0;
400 }
401
402 wraphere = 0;
403 }
404
405 /***
406 * @param fout
407 * @param indent
408 * @param inString
409 */
410 private void wrapAttrVal(Out fout, int indent, boolean inString)
411 {
412 int i, p, q;
413
414 for (i = 0; i < indent; ++i)
415 {
416 fout.outc(' ');
417 }
418
419 for (i = 0; i < wraphere; ++i)
420 {
421 fout.outc(linebuf[i]);
422 }
423
424 fout.outc(' ');
425
426 if (inString)
427 {
428 fout.outc('//');
429 }
430
431 fout.newline();
432
433 if (linelen > wraphere)
434 {
435 p = 0;
436
437 if (linebuf[wraphere] == ' ')
438 {
439 ++wraphere;
440 }
441
442 q = wraphere;
443 addC('\0', linelen);
444
445 while (true)
446 {
447 linebuf[p] = linebuf[q];
448 if (linebuf[q] == 0)
449 {
450 break;
451 }
452 p++;
453 q++;
454 }
455 linelen -= wraphere;
456 }
457 else
458 {
459 linelen = 0;
460 }
461
462 wraphere = 0;
463 }
464
465 /***
466 * @param fout
467 * @param indent
468 */
469 public void flushLine(Out fout, int indent)
470 {
471 int i;
472
473 if (linelen > 0)
474 {
475 if (indent + linelen >= this.configuration.wraplen)
476 {
477 wrapLine(fout, indent);
478 }
479
480 if (!inAttVal || this.configuration.indentAttributes)
481 {
482 for (i = 0; i < indent; ++i)
483 {
484 fout.outc(' ');
485 }
486 }
487
488 for (i = 0; i < linelen; ++i)
489 {
490 fout.outc(linebuf[i]);
491 }
492 }
493
494 fout.newline();
495 linelen = 0;
496 wraphere = 0;
497 inAttVal = false;
498 }
499
500 /***
501 * @param fout
502 * @param indent
503 */
504 public void condFlushLine(Out fout, int indent)
505 {
506 int i;
507
508 if (linelen > 0)
509 {
510 if (indent + linelen >= this.configuration.wraplen)
511 {
512 wrapLine(fout, indent);
513 }
514
515 if (!inAttVal || this.configuration.indentAttributes)
516 {
517 for (i = 0; i < indent; ++i)
518 {
519 fout.outc(' ');
520 }
521 }
522
523 for (i = 0; i < linelen; ++i)
524 {
525 fout.outc(linebuf[i]);
526 }
527
528 fout.newline();
529 linelen = 0;
530 wraphere = 0;
531 inAttVal = false;
532 }
533 }
534
535 /***
536 * @param c
537 * @param mode
538 */
539 private void printChar(int c, short mode)
540 {
541 String entity;
542 boolean breakable = false;
543
544 if (c == ' ' && !TidyUtils.toBoolean(mode & (PREFORMATTED | COMMENT | ATTRIBVALUE | CDATA)))
545 {
546
547 if (TidyUtils.toBoolean(mode & NOWRAP))
548 {
549
550 if (this.configuration.numEntities || this.configuration.xmlTags)
551 {
552 addC('&', linelen++);
553 addC('#', linelen++);
554 addC('1', linelen++);
555 addC('6', linelen++);
556 addC('0', linelen++);
557 addC(';', linelen++);
558 }
559 else
560 {
561
562 addC('&', linelen++);
563 addC('n', linelen++);
564 addC('b', linelen++);
565 addC('s', linelen++);
566 addC('p', linelen++);
567 addC(';', linelen++);
568 }
569 return;
570 }
571 wraphere = linelen;
572 }
573
574
575 if (TidyUtils.toBoolean(mode & (COMMENT | CDATA)))
576 {
577 addC(c, linelen++);
578 return;
579 }
580
581
582 if (!TidyUtils.toBoolean(mode & CDATA))
583 {
584 if (c == '<')
585 {
586 addC('&', linelen++);
587 addC('l', linelen++);
588 addC('t', linelen++);
589 addC(';', linelen++);
590 return;
591 }
592
593 if (c == '>')
594 {
595 addC('&', linelen++);
596 addC('g', linelen++);
597 addC('t', linelen++);
598 addC(';', linelen++);
599 return;
600 }
601
602
603
604 if (c == '&' && this.configuration.quoteAmpersand)
605 {
606 addC('&', linelen++);
607 addC('a', linelen++);
608 addC('m', linelen++);
609 addC('p', linelen++);
610 addC(';', linelen++);
611 return;
612 }
613
614 if (c == '"' && this.configuration.quoteMarks)
615 {
616 addC('&', linelen++);
617 addC('q', linelen++);
618 addC('u', linelen++);
619 addC('o', linelen++);
620 addC('t', linelen++);
621 addC(';', linelen++);
622 return;
623 }
624
625 if (c == '\'' && this.configuration.quoteMarks)
626 {
627 addC('&', linelen++);
628 addC('#', linelen++);
629 addC('3', linelen++);
630 addC('9', linelen++);
631 addC(';', linelen++);
632 return;
633 }
634
635 if (c == 160 && !this.configuration.rawOut)
636 {
637 if (this.configuration.makeBare)
638 {
639 addC(' ', linelen++);
640 }
641 else if (this.configuration.quoteNbsp)
642 {
643 addC('&', linelen++);
644
645 if (this.configuration.numEntities || this.configuration.xmlTags)
646 {
647 addC('#', linelen++);
648 addC('1', linelen++);
649 addC('6', linelen++);
650 addC('0', linelen++);
651 }
652 else
653 {
654 addC('n', linelen++);
655 addC('b', linelen++);
656 addC('s', linelen++);
657 addC('p', linelen++);
658 }
659
660 addC(';', linelen++);
661 }
662 else
663 {
664 addC(c, linelen++);
665 }
666
667 return;
668 }
669 }
670
671
672
673
674 switch (this.configuration.getOutCharEncoding())
675 {
676 case Configuration.UTF8 :
677
678
679
680
681
682 if ((c >= 0x2000) && !TidyUtils.toBoolean(mode & PREFORMATTED))
683 {
684 if (((c >= 0x2000) && (c <= 0x2006))
685 || ((c >= 0x2008) && (c <= 0x2010))
686 || ((c >= 0x2011) && (c <= 0x2046))
687 || ((c >= 0x207D) && (c <= 0x207E))
688 || ((c >= 0x208D) && (c <= 0x208E))
689 || ((c >= 0x2329) && (c <= 0x232A))
690 || ((c >= 0x3001) && (c <= 0x3003))
691 || ((c >= 0x3008) && (c <= 0x3011))
692 || ((c >= 0x3014) && (c <= 0x301F))
693 || ((c >= 0xFD3E) && (c <= 0xFD3F))
694 || ((c >= 0xFE30) && (c <= 0xFE44))
695 || ((c >= 0xFE49) && (c <= 0xFE52))
696 || ((c >= 0xFE54) && (c <= 0xFE61))
697 || ((c >= 0xFE6A) && (c <= 0xFE6B))
698 || ((c >= 0xFF01) && (c <= 0xFF03))
699 || ((c >= 0xFF05) && (c <= 0xFF0A))
700 || ((c >= 0xFF0C) && (c <= 0xFF0F))
701 || ((c >= 0xFF1A) && (c <= 0xFF1B))
702 || ((c >= 0xFF1F) && (c <= 0xFF20))
703 || ((c >= 0xFF3B) && (c <= 0xFF3D))
704 || ((c >= 0xFF61) && (c <= 0xFF65)))
705 {
706 wraphere = linelen + 2;
707 breakable = true;
708 }
709 else
710 {
711 switch (c)
712 {
713 case 0xFE63 :
714 case 0xFE68 :
715 case 0x3030 :
716 case 0x30FB :
717 case 0xFF3F :
718 case 0xFF5B :
719 case 0xFF5D :
720 wraphere = linelen + 2;
721 breakable = true;
722 }
723 }
724
725 if (breakable)
726 {
727 if (((c >= 0x201A) && (c <= 0x201C)) || ((c >= 0x201E) && (c <= 0x201F)))
728 {
729 wraphere--;
730 }
731 else
732 {
733 switch (c)
734 {
735 case 0x2018 :
736 case 0x2039 :
737 case 0x2045 :
738 case 0x207D :
739 case 0x208D :
740 case 0x2329 :
741 case 0x3008 :
742 case 0x300A :
743 case 0x300C :
744 case 0x300E :
745 case 0x3010 :
746 case 0x3014 :
747 case 0x3016 :
748 case 0x3018 :
749 case 0x301A :
750 case 0x301D :
751 case 0xFD3E :
752 case 0xFE35 :
753 case 0xFE37 :
754 case 0xFE39 :
755 case 0xFE3B :
756 case 0xFE3D :
757 case 0xFE3F :
758 case 0xFE41 :
759 case 0xFE43 :
760 case 0xFE59 :
761 case 0xFE5B :
762 case 0xFE5D :
763 case 0xFF08 :
764 case 0xFF3B :
765 case 0xFF5B :
766 case 0xFF62 :
767 wraphere--;
768 }
769 }
770 }
771 }
772 break;
773 case Configuration.BIG5 :
774
775
776 addC(c, linelen++);
777 if (((c & 0xFF00) == 0xA100) && !TidyUtils.toBoolean(mode & PREFORMATTED))
778 {
779 wraphere = linelen;
780
781 if ((c > 0x5C) && (c < 0xAD) && ((c & 1) == 1))
782 {
783 wraphere--;
784 }
785 }
786 return;
787 case Configuration.SHIFTJIS :
788 case Configuration.ISO2022 :
789 addC(c, linelen++);
790 return;
791 default :
792 if (this.configuration.rawOut)
793 {
794 addC(c, linelen++);
795 return;
796 }
797
798 }
799
800
801 if (c == 160 && TidyUtils.toBoolean(mode & PREFORMATTED))
802 {
803 addC(' ', linelen++);
804 return;
805 }
806
807
808
809
810
811
812 if (this.configuration.makeClean && this.configuration.asciiChars || this.configuration.makeBare)
813 {
814 if (c >= 0x2013 && c <= 0x201E)
815 {
816 switch (c)
817 {
818 case 0x2013 :
819 case 0x2014 :
820 c = '-';
821 break;
822 case 0x2018 :
823 case 0x2019 :
824 case 0x201A :
825 c = '\'';
826 break;
827 case 0x201C :
828 case 0x201D :
829 case 0x201E :
830 c = '"';
831 break;
832 }
833 }
834 }
835
836
837 if (this.configuration.getOutCharEncoding() == Configuration.LATIN1)
838 {
839 if (c > 255)
840 {
841 if (!this.configuration.numEntities)
842 {
843 entity = EntityTable.getDefaultEntityTable().entityName((short) c);
844 if (entity != null)
845 {
846 entity = "&" + entity + ";";
847 }
848 else
849 {
850 entity = "&#" + c + ";";
851 }
852 }
853 else
854 {
855 entity = "&#" + c + ";";
856 }
857
858 for (int i = 0; i < entity.length(); i++)
859 {
860 addC(entity.charAt(i), linelen++);
861 }
862
863 return;
864 }
865
866 if (c > 126 && c < 160)
867 {
868 entity = "&#" + c + ";";
869
870 for (int i = 0; i < entity.length(); i++)
871 {
872 addC(entity.charAt(i), linelen++);
873 }
874
875 return;
876 }
877
878 addC(c, linelen++);
879 return;
880 }
881
882
883 if (this.configuration.getOutCharEncoding() == Configuration.UTF8
884 || this.configuration.getOutCharEncoding() == Configuration.UTF16
885 || this.configuration.getOutCharEncoding() == Configuration.UTF16LE
886 || this.configuration.getOutCharEncoding() == Configuration.UTF16BE)
887 {
888 addC(c, linelen++);
889 return;
890 }
891
892
893 if (this.configuration.xmlTags)
894 {
895
896 if (c > 127 && this.configuration.getOutCharEncoding() == Configuration.ASCII)
897 {
898 entity = "&#" + c + ";";
899
900 for (int i = 0; i < entity.length(); i++)
901 {
902 addC(entity.charAt(i), linelen++);
903 }
904
905 return;
906 }
907
908
909 addC(c, linelen++);
910 return;
911 }
912
913
914 if (this.configuration.getOutCharEncoding() == Configuration.ASCII && (c > 126 || (c < ' ' && c != '\t')))
915 {
916 if (!this.configuration.numEntities)
917 {
918 entity = EntityTable.getDefaultEntityTable().entityName((short) c);
919 if (entity != null)
920 {
921 entity = "&" + entity + ";";
922 }
923 else
924 {
925 entity = "&#" + c + ";";
926 }
927 }
928 else
929 {
930 entity = "&#" + c + ";";
931 }
932
933 for (int i = 0; i < entity.length(); i++)
934 {
935 addC(entity.charAt(i), linelen++);
936 }
937
938 return;
939 }
940
941 addC(c, linelen++);
942 }
943
944 /***
945 * The line buffer is uint not char so we can hold Unicode values unencoded. The translation to UTF-8 is deferred to
946 * the outc routine called to flush the line buffer.
947 * @param fout
948 * @param mode
949 * @param indent
950 * @param textarray
951 * @param start
952 * @param end
953 */
954 private void printText(Out fout, short mode, int indent, byte[] textarray, int start, int end)
955 {
956 int i, c;
957 int[] ci = new int[1];
958
959 for (i = start; i < end; ++i)
960 {
961 if (indent + linelen >= this.configuration.wraplen)
962 {
963 wrapLine(fout, indent);
964 }
965
966 c = (textarray[i]) & 0xFF;
967
968
969 if (c > 0x7F)
970 {
971 i += getUTF8(textarray, i, ci);
972 c = ci[0];
973 }
974
975 if (c == '\n')
976 {
977 flushLine(fout, indent);
978 continue;
979 }
980
981 printChar(c, mode);
982 }
983 }
984
985 /***
986 * @param str
987 */
988 private void printString(String str)
989 {
990 for (int i = 0; i < str.length(); i++)
991 {
992 addC(str.charAt(i), linelen++);
993 }
994 }
995
996 /***
997 * @param fout
998 * @param indent
999 * @param value
1000 * @param delim
1001 * @param wrappable
1002 */
1003 private void printAttrValue(Out fout, int indent, String value, int delim, boolean wrappable)
1004 {
1005 int c;
1006 int[] ci = new int[1];
1007 boolean wasinstring = false;
1008 byte[] valueChars = null;
1009 int i;
1010 short mode = (wrappable ? (short) (NORMAL | ATTRIBVALUE) : (short) (PREFORMATTED | ATTRIBVALUE));
1011
1012 if (value != null)
1013 {
1014 valueChars = TidyUtils.getBytes(value);
1015 }
1016
1017
1018 if (valueChars != null && valueChars.length >= 5 && valueChars[0] == '<')
1019 {
1020 if (valueChars[1] == '%' || valueChars[1] == '@' || (new String(valueChars, 0, 5)).equals("<?php"))
1021 {
1022 mode |= CDATA;
1023 }
1024 }
1025
1026 if (delim == 0)
1027 {
1028 delim = '"';
1029 }
1030
1031 addC('=', linelen++);
1032
1033
1034 if (!this.configuration.xmlOut)
1035 {
1036
1037 if (indent + linelen < this.configuration.wraplen)
1038 {
1039 wraphere = linelen;
1040 }
1041
1042 if (indent + linelen >= this.configuration.wraplen)
1043 {
1044 wrapLine(fout, indent);
1045 }
1046
1047 if (indent + linelen < this.configuration.wraplen)
1048 {
1049 wraphere = linelen;
1050 }
1051 else
1052 {
1053 condFlushLine(fout, indent);
1054 }
1055 }
1056
1057 addC(delim, linelen++);
1058
1059 if (value != null)
1060 {
1061 inString = false;
1062
1063 i = 0;
1064 while (i < valueChars.length)
1065 {
1066 c = (valueChars[i]) & 0xFF;
1067
1068 if (wrappable && c == ' ' && indent + linelen < this.configuration.wraplen)
1069 {
1070 wraphere = linelen;
1071 wasinstring = inString;
1072 }
1073
1074 if (wrappable && wraphere > 0 && indent + linelen >= this.configuration.wraplen)
1075 {
1076 wrapAttrVal(fout, indent, wasinstring);
1077 }
1078
1079 if (c == delim)
1080 {
1081 String entity;
1082
1083 entity = (c == '"' ? """ : "'");
1084
1085 for (int j = 0; j < entity.length(); j++)
1086 {
1087 addC(entity.charAt(j), linelen++);
1088 }
1089
1090 ++i;
1091 continue;
1092 }
1093 else if (c == '"')
1094 {
1095 if (this.configuration.quoteMarks)
1096 {
1097 addC('&', linelen++);
1098 addC('q', linelen++);
1099 addC('u', linelen++);
1100 addC('o', linelen++);
1101 addC('t', linelen++);
1102 addC(';', linelen++);
1103 }
1104 else
1105 {
1106 addC('"', linelen++);
1107 }
1108
1109 if (delim == '\'')
1110 {
1111 inString = !inString;
1112 }
1113
1114 ++i;
1115 continue;
1116 }
1117 else if (c == '\'')
1118 {
1119 if (this.configuration.quoteMarks)
1120 {
1121 addC('&', linelen++);
1122 addC('#', linelen++);
1123 addC('3', linelen++);
1124 addC('9', linelen++);
1125 addC(';', linelen++);
1126 }
1127 else
1128 {
1129 addC('\'', linelen++);
1130 }
1131
1132 if (delim == '"')
1133 {
1134 inString = !inString;
1135 }
1136
1137 ++i;
1138 continue;
1139 }
1140
1141
1142 if (c > 0x7F)
1143 {
1144 i += getUTF8(valueChars, i, ci);
1145 c = ci[0];
1146 }
1147
1148 ++i;
1149
1150 if (c == '\n')
1151 {
1152 flushLine(fout, indent);
1153 continue;
1154 }
1155
1156 printChar(c, mode);
1157 }
1158 }
1159
1160 inString = false;
1161 addC(delim, linelen++);
1162 }
1163
1164 /***
1165 * @param fout
1166 * @param indent
1167 * @param node
1168 * @param attr
1169 */
1170 private void printAttribute(Out fout, int indent, Node node, AttVal attr)
1171 {
1172 String name;
1173 boolean wrappable = false;
1174
1175 if (this.configuration.indentAttributes)
1176 {
1177 flushLine(fout, indent);
1178 indent += this.configuration.spaces;
1179 }
1180
1181 name = attr.attribute;
1182
1183 if (indent + linelen >= this.configuration.wraplen)
1184 {
1185 wrapLine(fout, indent);
1186 }
1187
1188 if (!this.configuration.xmlTags && !this.configuration.xmlOut && attr.dict != null)
1189 {
1190 if (AttributeTable.getDefaultAttributeTable().isScript(name))
1191 {
1192 wrappable = this.configuration.wrapScriptlets;
1193 }
1194 else if (!attr.dict.isNowrap() && this.configuration.wrapAttVals)
1195 {
1196 wrappable = true;
1197 }
1198 }
1199
1200 if (indent + linelen < this.configuration.wraplen)
1201 {
1202 wraphere = linelen;
1203 addC(' ', linelen++);
1204 }
1205 else
1206 {
1207 condFlushLine(fout, indent);
1208 addC(' ', linelen++);
1209 }
1210
1211 for (int i = 0; i < name.length(); i++)
1212 {
1213 addC(
1214 TidyUtils.foldCase(name.charAt(i), this.configuration.upperCaseAttrs, this.configuration.xmlTags),
1215 linelen++);
1216 }
1217
1218 if (indent + linelen >= this.configuration.wraplen)
1219 {
1220 wrapLine(fout, indent);
1221 }
1222
1223 if (attr.value == null)
1224 {
1225 if (this.configuration.xmlTags || this.configuration.xmlOut)
1226 {
1227 printAttrValue(fout, indent, (attr.isBoolAttribute() ? attr.attribute : ""), attr.delim, true);
1228 }
1229 else if (!attr.isBoolAttribute() && node != null && !node.isNewNode())
1230 {
1231 printAttrValue(fout, indent, "", attr.delim, true);
1232 }
1233 else if (indent + linelen < this.configuration.wraplen)
1234 {
1235 wraphere = linelen;
1236 }
1237
1238 }
1239 else
1240 {
1241 printAttrValue(fout, indent, attr.value, attr.delim, wrappable);
1242 }
1243 }
1244
1245 /***
1246 * @param fout
1247 * @param indent
1248 * @param node
1249 * @param attr
1250 */
1251 private void printAttrs(Out fout, int indent, Node node, AttVal attr)
1252 {
1253
1254 if (configuration.xmlOut
1255 && configuration.xmlSpace
1256 && ParserImpl.XMLPreserveWhiteSpace(node, configuration.tt)
1257 && node.getAttrByName("xml:space") == null)
1258 {
1259 node.addAttribute("xml:space", "preserve");
1260 if (attr != null)
1261 {
1262 attr = node.attributes;
1263 }
1264 }
1265
1266 if (attr != null)
1267 {
1268 if (attr.next != null)
1269 {
1270 printAttrs(fout, indent, node, attr.next);
1271 }
1272
1273 if (attr.attribute != null)
1274 {
1275 Attribute attribute = attr.dict;
1276
1277 if (!this.configuration.dropProprietaryAttributes
1278 || !(attribute == null || TidyUtils.toBoolean(attribute.getVersions() & Dict.VERS_PROPRIETARY)))
1279 {
1280 printAttribute(fout, indent, node, attr);
1281 }
1282 }
1283 else if (attr.asp != null)
1284 {
1285 addC(' ', linelen++);
1286 printAsp(fout, indent, attr.asp);
1287 }
1288 else if (attr.php != null)
1289 {
1290 addC(' ', linelen++);
1291 printPhp(fout, indent, attr.php);
1292 }
1293 }
1294
1295 }
1296
1297 /***
1298 * Line can be wrapped immediately after inline start tag provided if follows a text node ending in a space, or it
1299 * parent is an inline element that that rule applies to. This behaviour was reverse engineered from Netscape 3.0
1300 * @param node current Node
1301 * @return <code>true</code> if the current char follows a space
1302 */
1303 private static boolean afterSpace(Node node)
1304 {
1305 Node prev;
1306 int c;
1307
1308 if (node == null || node.tag == null || !TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE))
1309 {
1310 return true;
1311 }
1312
1313 prev = node.prev;
1314
1315 if (prev != null)
1316 {
1317 if (prev.type == Node.TEXT_NODE && prev.end > prev.start)
1318 {
1319 c = (prev.textarray[prev.end - 1]) & 0xFF;
1320
1321 if (c == 160 || c == ' ' || c == '\n')
1322 {
1323 return true;
1324 }
1325 }
1326
1327 return false;
1328 }
1329
1330 return afterSpace(node.parent);
1331 }
1332
1333 /***
1334 * @param lexer
1335 * @param fout
1336 * @param mode
1337 * @param indent
1338 * @param node
1339 */
1340 private void printTag(Lexer lexer, Out fout, short mode, int indent, Node node)
1341 {
1342 String p;
1343 TagTable tt = this.configuration.tt;
1344
1345 addC('<', linelen++);
1346
1347 if (node.type == Node.END_TAG)
1348 {
1349 addC('/', linelen++);
1350 }
1351
1352 p = node.element;
1353 for (int i = 0; i < p.length(); i++)
1354 {
1355 addC(
1356 TidyUtils.foldCase(p.charAt(i), this.configuration.upperCaseTags, this.configuration.xmlTags),
1357 linelen++);
1358 }
1359
1360 printAttrs(fout, indent, node, node.attributes);
1361
1362 if ((this.configuration.xmlOut || this.configuration.xHTML)
1363 && (node.type == Node.START_END_TAG || TidyUtils.toBoolean(node.tag.model & Dict.CM_EMPTY)))
1364 {
1365 addC(' ', linelen++);
1366 addC('/', linelen++);
1367 }
1368
1369 addC('>', linelen++);
1370
1371 if ((node.type != Node.START_END_TAG || configuration.xHTML) && !TidyUtils.toBoolean(mode & PREFORMATTED))
1372 {
1373 if (indent + linelen >= this.configuration.wraplen)
1374 {
1375 wrapLine(fout, indent);
1376 }
1377
1378 if (indent + linelen < this.configuration.wraplen)
1379 {
1380
1381
1382
1383 if (!TidyUtils.toBoolean(mode & NOWRAP)
1384 && (!TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE) || (node.tag == tt.tagBr))
1385 && afterSpace(node))
1386 {
1387 wraphere = linelen;
1388 }
1389
1390 }
1391 }
1392 else
1393 {
1394 condFlushLine(fout, indent);
1395 }
1396
1397 }
1398
1399 /***
1400 * @param mode
1401 * @param indent
1402 * @param node
1403 */
1404 private void printEndTag(short mode, int indent, Node node)
1405 {
1406 String p;
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416 addC('<', linelen++);
1417 addC('/', linelen++);
1418
1419 p = node.element;
1420 for (int i = 0; i < p.length(); i++)
1421 {
1422 addC(
1423 TidyUtils.foldCase(p.charAt(i), this.configuration.upperCaseTags, this.configuration.xmlTags),
1424 linelen++);
1425 }
1426
1427 addC('>', linelen++);
1428 }
1429
1430 /***
1431 * @param fout
1432 * @param indent
1433 * @param node
1434 */
1435 private void printComment(Out fout, int indent, Node node)
1436 {
1437 if (this.configuration.hideComments)
1438 {
1439 return;
1440 }
1441
1442 if (indent + linelen < this.configuration.wraplen)
1443 {
1444 wraphere = linelen;
1445 }
1446
1447 addC('<', linelen++);
1448 addC('!', linelen++);
1449 addC('-', linelen++);
1450 addC('-', linelen++);
1451
1452 printText(fout, COMMENT, indent, node.textarray, node.start, node.end);
1453
1454
1455 addC('-', linelen++);
1456 addC('-', linelen++);
1457 addC('>', linelen++);
1458
1459 if (node.linebreak)
1460 {
1461 flushLine(fout, indent);
1462 }
1463 }
1464
1465 /***
1466 * @param fout
1467 * @param indent
1468 * @param lexer
1469 * @param node
1470 */
1471 private void printDocType(Out fout, int indent, Lexer lexer, Node node)
1472 {
1473 int i, c = 0;
1474 short mode = 0;
1475 boolean q = this.configuration.quoteMarks;
1476
1477 this.configuration.quoteMarks = false;
1478
1479 if (indent + linelen < this.configuration.wraplen)
1480 {
1481 wraphere = linelen;
1482 }
1483
1484 condFlushLine(fout, indent);
1485
1486 addC('<', linelen++);
1487 addC('!', linelen++);
1488 addC('D', linelen++);
1489 addC('O', linelen++);
1490 addC('C', linelen++);
1491 addC('T', linelen++);
1492 addC('Y', linelen++);
1493 addC('P', linelen++);
1494 addC('E', linelen++);
1495 addC(' ', linelen++);
1496
1497 if (indent + linelen < this.configuration.wraplen)
1498 {
1499 wraphere = linelen;
1500 }
1501
1502 for (i = node.start; i < node.end; ++i)
1503 {
1504 if (indent + linelen >= this.configuration.wraplen)
1505 {
1506 wrapLine(fout, indent);
1507 }
1508
1509 c = node.textarray[i] & 0xFF;
1510
1511
1512 if (TidyUtils.toBoolean(mode & CDATA))
1513 {
1514 if (c == ']')
1515 {
1516 mode &= ~CDATA;
1517 }
1518 }
1519 else if (c == '[')
1520 {
1521 mode |= CDATA;
1522 }
1523 int[] ci = new int[1];
1524
1525
1526 if (c > 0x7F)
1527 {
1528 i += getUTF8(node.textarray, i, ci);
1529 c = ci[0];
1530 }
1531
1532 if (c == '\n')
1533 {
1534 flushLine(fout, indent);
1535 continue;
1536 }
1537
1538 printChar(c, mode);
1539 }
1540
1541 if (linelen < this.configuration.wraplen)
1542 {
1543 wraphere = linelen;
1544 }
1545
1546 addC('>', linelen++);
1547 this.configuration.quoteMarks = q;
1548 condFlushLine(fout, indent);
1549 }
1550
1551 /***
1552 * @param fout
1553 * @param indent
1554 * @param node
1555 */
1556 private void printPI(Out fout, int indent, Node node)
1557 {
1558 if (indent + linelen < this.configuration.wraplen)
1559 {
1560 wraphere = linelen;
1561 }
1562
1563 addC('<', linelen++);
1564 addC('?', linelen++);
1565
1566
1567 printText(fout, CDATA, indent, node.textarray, node.start, node.end);
1568
1569 if (node.end <= 0 || node.textarray[node.end - 1] != '?')
1570 {
1571 addC('?', linelen++);
1572 }
1573
1574 addC('>', linelen++);
1575 condFlushLine(fout, indent);
1576 }
1577
1578 /***
1579 * Pretty print the xml declaration.
1580 * @param fout
1581 * @param indent
1582 * @param node
1583 */
1584 private void printXmlDecl(Out fout, int indent, Node node)
1585 {
1586 if (indent + linelen < this.configuration.wraplen)
1587 {
1588 wraphere = linelen;
1589 }
1590
1591 addC('<', linelen++);
1592 addC('?', linelen++);
1593 addC('x', linelen++);
1594 addC('m', linelen++);
1595 addC('l', linelen++);
1596
1597 printAttrs(fout, indent, node, node.attributes);
1598
1599 if (node.end <= 0 || node.textarray[node.end - 1] != '?')
1600 {
1601 addC('?', linelen++);
1602 }
1603
1604 addC('>', linelen++);
1605
1606 condFlushLine(fout, indent);
1607 }
1608
1609 /***
1610 * note ASP and JSTE share <% ... %> syntax.
1611 * @param fout
1612 * @param indent
1613 * @param node
1614 */
1615 private void printAsp(Out fout, int indent, Node node)
1616 {
1617 int savewraplen = this.configuration.wraplen;
1618
1619
1620
1621 if (!this.configuration.wrapAsp || !this.configuration.wrapJste)
1622 {
1623 this.configuration.wraplen = 0xFFFFFF;
1624 }
1625
1626 addC('<', linelen++);
1627 addC('%', linelen++);
1628
1629 printText(fout, (this.configuration.wrapAsp ? CDATA : COMMENT), indent, node.textarray, node.start, node.end);
1630
1631 addC('%', linelen++);
1632 addC('>', linelen++);
1633
1634 this.configuration.wraplen = savewraplen;
1635 }
1636
1637 /***
1638 * JSTE also supports <# ... #> syntax
1639 * @param fout
1640 * @param indent
1641 * @param node
1642 */
1643 private void printJste(Out fout, int indent, Node node)
1644 {
1645 int savewraplen = this.configuration.wraplen;
1646
1647
1648
1649 if (!this.configuration.wrapJste)
1650 {
1651 this.configuration.wraplen = 0xFFFFFF;
1652 }
1653
1654 addC('<', linelen++);
1655 addC('#', linelen++);
1656
1657 printText(fout, (this.configuration.wrapJste ? CDATA : COMMENT), indent, node.textarray, node.start, node.end);
1658
1659 addC('#', linelen++);
1660 addC('>', linelen++);
1661
1662 this.configuration.wraplen = savewraplen;
1663 }
1664
1665 /***
1666 * PHP is based on XML processing instructions.
1667 * @param fout
1668 * @param indent
1669 * @param node
1670 */
1671 private void printPhp(Out fout, int indent, Node node)
1672 {
1673 int savewraplen = this.configuration.wraplen;
1674
1675
1676
1677 if (!this.configuration.wrapPhp)
1678 {
1679 this.configuration.wraplen = 0xFFFFFF;
1680 }
1681
1682 addC('<', linelen++);
1683 addC('?', linelen++);
1684
1685 printText(fout, (this.configuration.wrapPhp ? CDATA : COMMENT), indent, node.textarray, node.start, node.end);
1686
1687 addC('?', linelen++);
1688 addC('>', linelen++);
1689
1690 this.configuration.wraplen = savewraplen;
1691 }
1692
1693 /***
1694 * @param fout
1695 * @param indent
1696 * @param node
1697 */
1698 private void printCDATA(Out fout, int indent, Node node)
1699 {
1700 int savewraplen = this.configuration.wraplen;
1701
1702 if (!this.configuration.indentCdata)
1703 {
1704 indent = 0;
1705 }
1706
1707 condFlushLine(fout, indent);
1708
1709
1710 this.configuration.wraplen = 0xFFFFFF;
1711
1712 addC('<', linelen++);
1713 addC('!', linelen++);
1714 addC('[', linelen++);
1715 addC('C', linelen++);
1716 addC('D', linelen++);
1717 addC('A', linelen++);
1718 addC('T', linelen++);
1719 addC('A', linelen++);
1720 addC('[', linelen++);
1721
1722 printText(fout, COMMENT, indent, node.textarray, node.start, node.end);
1723
1724 addC(']', linelen++);
1725 addC(']', linelen++);
1726 addC('>', linelen++);
1727 condFlushLine(fout, indent);
1728 this.configuration.wraplen = savewraplen;
1729 }
1730
1731 /***
1732 * @param fout
1733 * @param indent
1734 * @param node
1735 */
1736 private void printSection(Out fout, int indent, Node node)
1737 {
1738 int savewraplen = this.configuration.wraplen;
1739
1740
1741
1742 if (!this.configuration.wrapSection)
1743 {
1744 this.configuration.wraplen = 0xFFFFFF;
1745 }
1746
1747 addC('<', linelen++);
1748 addC('!', linelen++);
1749 addC('[', linelen++);
1750
1751 printText(
1752 fout,
1753 (this.configuration.wrapSection ? CDATA : COMMENT),
1754 indent,
1755 node.textarray,
1756 node.start,
1757 node.end);
1758
1759 addC(']', linelen++);
1760 addC('>', linelen++);
1761
1762 this.configuration.wraplen = savewraplen;
1763 }
1764
1765 /***
1766 * Is the current node inside HEAD?
1767 * @param node Node
1768 * @return <code>true</code> if node is inside an HEAD tag
1769 */
1770 private boolean insideHead(Node node)
1771 {
1772 if (node.tag == this.configuration.tt.tagHead)
1773 {
1774 return true;
1775 }
1776
1777 if (node.parent != null)
1778 {
1779 return insideHead(node.parent);
1780 }
1781 return false;
1782 }
1783
1784 /***
1785 * Is text node and already ends w/ a newline? Used to pretty print CDATA/PRE text content. If it already ends on a
1786 * newline, it is not necessary to print another before printing end tag.
1787 * @param lexer Lexer
1788 * @param node text node
1789 * @return text indent
1790 */
1791 private int textEndsWithNewline(Lexer lexer, Node node)
1792 {
1793 if (node.type == Node.TEXT_NODE && node.end > node.start)
1794 {
1795 int ch, ix = node.end - 1;
1796
1797 while (ix >= node.start
1798 && TidyUtils.toBoolean(ch = (node.textarray[ix] & 0xff))
1799 && (ch == ' ' || ch == '\t' || ch == '\r'))
1800 {
1801 --ix;
1802 }
1803
1804 if (node.textarray[ix] == '\n')
1805 {
1806 return node.end - ix - 1;
1807 }
1808 }
1809 return -1;
1810 }
1811
1812 /***
1813 * Does the current node contain a CDATA section?
1814 * @param lexer Lexer
1815 * @param node Node
1816 * @return <code>true</code> if node contains a CDATA section
1817 */
1818 static boolean hasCDATA(Lexer lexer, Node node)
1819 {
1820
1821
1822
1823 if (node.type != Node.TEXT_NODE)
1824 {
1825 return false;
1826 }
1827
1828 int len = node.end - node.start + 1;
1829 String start = TidyUtils.getString(node.textarray, node.start, len);
1830
1831 int indexOfCData = start.indexOf(CDATA_START);
1832 return indexOfCData > -1 && indexOfCData <= len;
1833 }
1834
1835 /***
1836 * Print script and style elements. For XHTML, wrap the content as follows:
1837 *
1838 * <pre>
1839 * JavaScript:
1840 * //<![CDATA[
1841 * content
1842 * //]]>
1843 * VBScript:
1844 * '<![CDATA[
1845 * content
1846 * ']]>
1847 * CSS:
1848 * /*<![CDATA[* /
1849 * content
1850 * /*]]>* /
1851 * other:
1852 * <![CDATA[
1853 * content
1854 * ]]>
1855 * </pre>
1856 *
1857 * @param fout
1858 * @param mode
1859 * @param indent
1860 * @param lexer
1861 * @param node
1862 */
1863 private void printScriptStyle(Out fout, short mode, int indent, Lexer lexer, Node node)
1864 {
1865 Node content;
1866 String commentStart = DEFAULT_COMMENT_START;
1867 String commentEnd = DEFAULT_COMMENT_END;
1868 boolean hasCData = false;
1869 int contentIndent = -1;
1870
1871 if (insideHead(node))
1872 {
1873
1874 }
1875
1876 indent = 0;
1877
1878
1879 printTag(lexer, fout, mode, indent, node);
1880
1881
1882 if (lexer.configuration.xHTML && node.content != null)
1883 {
1884 AttVal type = node.getAttrByName("type");
1885 if (type != null)
1886 {
1887 if ("text/javascript".equalsIgnoreCase(type.value))
1888 {
1889 commentStart = JS_COMMENT_START;
1890 commentEnd = JS_COMMENT_END;
1891 }
1892 else if ("text/css".equalsIgnoreCase(type.value))
1893 {
1894 commentStart = CSS_COMMENT_START;
1895 commentEnd = CSS_COMMENT_END;
1896 }
1897 else if ("text/vbscript".equalsIgnoreCase(type.value))
1898 {
1899 commentStart = VB_COMMENT_START;
1900 commentEnd = VB_COMMENT_END;
1901 }
1902 }
1903
1904 hasCData = hasCDATA(lexer, node.content);
1905 if (!hasCData)
1906 {
1907
1908 int savewraplen = lexer.configuration.wraplen;
1909 lexer.configuration.wraplen = 0xFFFFFF;
1910
1911 linelen = addAsciiString(commentStart, linelen);
1912 linelen = addAsciiString(CDATA_START, linelen);
1913 linelen = addAsciiString(commentEnd, linelen);
1914 condFlushLine(fout, indent);
1915
1916
1917 lexer.configuration.wraplen = savewraplen;
1918 }
1919 }
1920
1921 for (content = node.content; content != null; content = content.next)
1922 {
1923 printTree(fout, (short) (mode | PREFORMATTED | NOWRAP | CDATA), 0, lexer, content);
1924
1925 if (content.next == null)
1926 {
1927 contentIndent = textEndsWithNewline(lexer, content);
1928 }
1929
1930 }
1931
1932 if (contentIndent < 0)
1933 {
1934 condFlushLine(fout, indent);
1935 contentIndent = 0;
1936 }
1937
1938 if (lexer.configuration.xHTML && node.content != null)
1939 {
1940 if (!hasCData)
1941 {
1942
1943 int ix, savewraplen = lexer.configuration.wraplen;
1944 lexer.configuration.wraplen = 0xFFFFFF;
1945
1946
1947 if (contentIndent > 0 && linelen < contentIndent)
1948 {
1949 linelen = contentIndent;
1950 }
1951 for (ix = 0; contentIndent < indent && ix < indent - contentIndent; ++ix)
1952 {
1953 addC(' ', linelen++);
1954 }
1955
1956 linelen = addAsciiString(commentStart, linelen);
1957 linelen = addAsciiString(CDATA_END, linelen);
1958 linelen = addAsciiString(commentEnd, linelen);
1959
1960
1961 lexer.configuration.wraplen = savewraplen;
1962 condFlushLine(fout, 0);
1963 }
1964 }
1965
1966 printEndTag(mode, indent, node);
1967
1968 if (!lexer.configuration.indentContent && node.next != null
1969
1970 && !((node.tag != null && TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE))
1971
1972 || node.type != Node.TEXT_NODE
1973
1974 ))
1975 {
1976 flushLine(fout, indent);
1977 }
1978
1979 flushLine(fout, indent);
1980 }
1981
1982 /***
1983 * Should tidy indent the give tag?
1984 * @param node actual node
1985 * @return <code>true</code> if line should be indented
1986 */
1987 private boolean shouldIndent(Node node)
1988 {
1989 TagTable tt = this.configuration.tt;
1990
1991 if (!this.configuration.indentContent)
1992 {
1993 return false;
1994 }
1995
1996 if (this.configuration.smartIndent)
1997 {
1998 if (node.content != null && TidyUtils.toBoolean(node.tag.model & Dict.CM_NO_INDENT))
1999 {
2000 for (node = node.content; node != null; node = node.next)
2001 {
2002 if (node.tag != null && TidyUtils.toBoolean(node.tag.model & Dict.CM_BLOCK))
2003 {
2004 return true;
2005 }
2006 }
2007
2008 return false;
2009 }
2010
2011 if (TidyUtils.toBoolean(node.tag.model & Dict.CM_HEADING))
2012 {
2013 return false;
2014 }
2015
2016 if (node.tag == tt.tagP)
2017 {
2018 return false;
2019 }
2020
2021 if (node.tag == tt.tagTitle)
2022 {
2023 return false;
2024 }
2025 }
2026
2027 if (TidyUtils.toBoolean(node.tag.model & (Dict.CM_FIELD | Dict.CM_OBJECT)))
2028 {
2029 return true;
2030 }
2031
2032 if (node.tag == tt.tagMap)
2033 {
2034 return true;
2035 }
2036
2037 return !TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE);
2038 }
2039
2040 /***
2041 * Print just the content of the body element. Useful when you want to reuse material from other documents.
2042 * @param fout
2043 * @param lexer
2044 * @param root
2045 * @param xml
2046 */
2047 void printBody(Out fout, Lexer lexer, Node root, boolean xml)
2048 {
2049 if (root == null)
2050 {
2051 return;
2052 }
2053
2054
2055
2056 Node body = root.findBody(lexer.configuration.tt);
2057
2058 if (body != null)
2059 {
2060 Node content;
2061 for (content = body.content; content != null; content = content.next)
2062 {
2063 if (xml)
2064 {
2065 printXMLTree(fout, (short) 0, 0, lexer, content);
2066 }
2067 else
2068 {
2069 printTree(fout, (short) 0, 0, lexer, content);
2070 }
2071 }
2072 }
2073 }
2074
2075 /***
2076 * @param fout
2077 * @param mode
2078 * @param indent
2079 * @param lexer
2080 * @param node
2081 */
2082 public void printTree(Out fout, short mode, int indent, Lexer lexer, Node node)
2083 {
2084 Node content, last;
2085 TagTable tt = this.configuration.tt;
2086
2087 if (node == null)
2088 {
2089 return;
2090 }
2091
2092 if (node.type == Node.TEXT_NODE || (node.type == Node.CDATA_TAG && lexer.configuration.escapeCdata))
2093 {
2094 printText(fout, mode, indent, node.textarray, node.start, node.end);
2095 }
2096 else if (node.type == Node.COMMENT_TAG)
2097 {
2098 printComment(fout, indent, node);
2099 }
2100 else if (node.type == Node.ROOT_NODE)
2101 {
2102 for (content = node.content; content != null; content = content.next)
2103 {
2104 printTree(fout, mode, indent, lexer, content);
2105 }
2106 }
2107 else if (node.type == Node.DOCTYPE_TAG)
2108 {
2109 printDocType(fout, indent, lexer, node);
2110 }
2111 else if (node.type == Node.PROC_INS_TAG)
2112 {
2113 printPI(fout, indent, node);
2114 }
2115 else if (node.type == Node.XML_DECL)
2116 {
2117 printXmlDecl(fout, indent, node);
2118 }
2119 else if (node.type == Node.CDATA_TAG)
2120 {
2121 printCDATA(fout, indent, node);
2122 }
2123 else if (node.type == Node.SECTION_TAG)
2124 {
2125 printSection(fout, indent, node);
2126 }
2127 else if (node.type == Node.ASP_TAG)
2128 {
2129 printAsp(fout, indent, node);
2130 }
2131 else if (node.type == Node.JSTE_TAG)
2132 {
2133 printJste(fout, indent, node);
2134 }
2135 else if (node.type == Node.PHP_TAG)
2136 {
2137 printPhp(fout, indent, node);
2138 }
2139 else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_EMPTY)
2140 || (node.type == Node.START_END_TAG && !configuration.xHTML))
2141 {
2142 if (!TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE))
2143 {
2144 condFlushLine(fout, indent);
2145 }
2146
2147 if (node.tag == tt.tagBr
2148 && node.prev != null
2149 && node.prev.tag != tt.tagBr
2150 && this.configuration.breakBeforeBR)
2151 {
2152 flushLine(fout, indent);
2153 }
2154
2155 if (this.configuration.makeClean && node.tag == tt.tagWbr)
2156 {
2157 printString(" ");
2158 }
2159 else
2160 {
2161 printTag(lexer, fout, mode, indent, node);
2162 }
2163
2164 if (node.tag == tt.tagParam || node.tag == tt.tagArea)
2165 {
2166 condFlushLine(fout, indent);
2167 }
2168 else if (node.tag == tt.tagBr || node.tag == tt.tagHr)
2169 {
2170 flushLine(fout, indent);
2171 }
2172 }
2173 else
2174 {
2175 if (node.type == Node.START_END_TAG)
2176 {
2177 node.type = Node.START_TAG;
2178 }
2179
2180
2181 if (node.tag != null && node.tag.getParser() == ParserImpl.PRE)
2182 {
2183 condFlushLine(fout, indent);
2184
2185 indent = 0;
2186 condFlushLine(fout, indent);
2187 printTag(lexer, fout, mode, indent, node);
2188 flushLine(fout, indent);
2189
2190 for (content = node.content; content != null; content = content.next)
2191 {
2192 printTree(fout, (short) (mode | PREFORMATTED | NOWRAP), indent, lexer, content);
2193 }
2194
2195 condFlushLine(fout, indent);
2196 printEndTag(mode, indent, node);
2197 flushLine(fout, indent);
2198
2199 if (!this.configuration.indentContent && node.next != null)
2200 {
2201 flushLine(fout, indent);
2202 }
2203 }
2204 else if (node.tag == tt.tagStyle || node.tag == tt.tagScript)
2205 {
2206 printScriptStyle(fout, (short) (mode | PREFORMATTED | NOWRAP | CDATA), indent, lexer, node);
2207 }
2208 else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE))
2209 {
2210 if (this.configuration.makeClean)
2211 {
2212
2213 if (node.tag == tt.tagFont)
2214 {
2215 for (content = node.content; content != null; content = content.next)
2216 {
2217 printTree(fout, mode, indent, lexer, content);
2218 }
2219 return;
2220 }
2221
2222
2223 if (node.tag == tt.tagNobr)
2224 {
2225 for (content = node.content; content != null; content = content.next)
2226 {
2227 printTree(fout, (short) (mode | NOWRAP), indent, lexer, content);
2228 }
2229 return;
2230 }
2231 }
2232
2233
2234
2235 printTag(lexer, fout, mode, indent, node);
2236
2237
2238
2239 if (shouldIndent(node))
2240 {
2241 condFlushLine(fout, indent);
2242 indent += this.configuration.spaces;
2243
2244 for (content = node.content; content != null; content = content.next)
2245 {
2246 printTree(fout, mode, indent, lexer, content);
2247 }
2248
2249 condFlushLine(fout, indent);
2250 indent -= this.configuration.spaces;
2251 condFlushLine(fout, indent);
2252 }
2253 else
2254 {
2255
2256 for (content = node.content; content != null; content = content.next)
2257 {
2258 printTree(fout, mode, indent, lexer, content);
2259 }
2260 }
2261
2262 printEndTag(mode, indent, node);
2263 }
2264 else
2265 {
2266
2267 condFlushLine(fout, indent);
2268
2269 if (this.configuration.smartIndent && node.prev != null)
2270 {
2271 flushLine(fout, indent);
2272 }
2273
2274
2275 if (!this.configuration.hideEndTags
2276 || !(node.tag != null && TidyUtils.toBoolean(node.tag.model & Dict.CM_OMITST))
2277 || node.attributes != null)
2278 {
2279 printTag(lexer, fout, mode, indent, node);
2280
2281 if (shouldIndent(node))
2282 {
2283 condFlushLine(fout, indent);
2284 }
2285 else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_HTML)
2286 || node.tag == tt.tagNoframes
2287 || (TidyUtils.toBoolean(node.tag.model & Dict.CM_HEAD) && !(node.tag == tt.tagTitle)))
2288 {
2289 flushLine(fout, indent);
2290 }
2291 }
2292
2293 if (node.tag == tt.tagBody && this.configuration.burstSlides)
2294 {
2295 printSlide(fout, mode, (this.configuration.indentContent
2296 ? indent + this.configuration.spaces
2297 : indent), lexer);
2298 }
2299 else
2300 {
2301 last = null;
2302
2303 for (content = node.content; content != null; content = content.next)
2304 {
2305
2306 if (last != null
2307 && !this.configuration.indentContent
2308 && last.type == Node.TEXT_NODE
2309 && content.tag != null
2310 && !TidyUtils.toBoolean(content.tag.model & Dict.CM_INLINE))
2311 {
2312 flushLine(fout, indent);
2313 }
2314
2315 printTree(
2316 fout,
2317 mode,
2318 (shouldIndent(node) ? indent + this.configuration.spaces : indent),
2319 lexer,
2320 content);
2321
2322 last = content;
2323 }
2324 }
2325
2326
2327 if (shouldIndent(node)
2328 || ((TidyUtils.toBoolean(node.tag.model & Dict.CM_HTML) || node.tag == tt.tagNoframes ||
2329 (TidyUtils.toBoolean(node.tag.model & Dict.CM_HEAD) && !(node.tag == tt.tagTitle))) &&
2330 !this.configuration.hideEndTags))
2331 {
2332 condFlushLine(
2333 fout,
2334 (this.configuration.indentContent ? indent + this.configuration.spaces : indent));
2335
2336 if (!this.configuration.hideEndTags || !TidyUtils.toBoolean(node.tag.model & Dict.CM_OPT))
2337 {
2338 printEndTag(mode, indent, node);
2339
2340
2341
2342
2343 if (!lexer.seenEndHtml)
2344 {
2345 flushLine(fout, indent);
2346 }
2347 }
2348 }
2349 else
2350 {
2351 if (!this.configuration.hideEndTags || !TidyUtils.toBoolean(node.tag.model & Dict.CM_OPT))
2352 {
2353 printEndTag(mode, indent, node);
2354 }
2355
2356 flushLine(fout, indent);
2357 }
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368 }
2369 }
2370 }
2371
2372 /***
2373 * @param fout
2374 * @param mode
2375 * @param indent
2376 * @param lexer
2377 * @param node
2378 */
2379 public void printXMLTree(Out fout, short mode, int indent, Lexer lexer, Node node)
2380 {
2381 TagTable tt = this.configuration.tt;
2382
2383 if (node == null)
2384 {
2385 return;
2386 }
2387
2388 if (node.type == Node.TEXT_NODE || (node.type == Node.CDATA_TAG && lexer.configuration.escapeCdata))
2389 {
2390 printText(fout, mode, indent, node.textarray, node.start, node.end);
2391 }
2392 else if (node.type == Node.COMMENT_TAG)
2393 {
2394 condFlushLine(fout, indent);
2395 printComment(fout, 0, node);
2396 condFlushLine(fout, 0);
2397 }
2398 else if (node.type == Node.ROOT_NODE)
2399 {
2400 Node content;
2401
2402 for (content = node.content; content != null; content = content.next)
2403 {
2404 printXMLTree(fout, mode, indent, lexer, content);
2405 }
2406 }
2407 else if (node.type == Node.DOCTYPE_TAG)
2408 {
2409 printDocType(fout, indent, lexer, node);
2410 }
2411 else if (node.type == Node.PROC_INS_TAG)
2412 {
2413 printPI(fout, indent, node);
2414 }
2415 else if (node.type == Node.XML_DECL)
2416 {
2417 printXmlDecl(fout, indent, node);
2418 }
2419 else if (node.type == Node.CDATA_TAG)
2420 {
2421 printCDATA(fout, indent, node);
2422 }
2423 else if (node.type == Node.SECTION_TAG)
2424 {
2425 printSection(fout, indent, node);
2426 }
2427 else if (node.type == Node.ASP_TAG)
2428 {
2429 printAsp(fout, indent, node);
2430 }
2431 else if (node.type == Node.JSTE_TAG)
2432 {
2433 printJste(fout, indent, node);
2434 }
2435 else if (node.type == Node.PHP_TAG)
2436 {
2437 printPhp(fout, indent, node);
2438 }
2439 else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_EMPTY)
2440 || node.type == Node.START_END_TAG
2441 && !configuration.xHTML)
2442 {
2443 condFlushLine(fout, indent);
2444 printTag(lexer, fout, mode, indent, node);
2445
2446
2447
2448
2449
2450
2451 }
2452 else
2453 {
2454
2455 Node content;
2456 boolean mixed = false;
2457 int cindent;
2458
2459 for (content = node.content; content != null; content = content.next)
2460 {
2461 if (content.type == Node.TEXT_NODE)
2462 {
2463 mixed = true;
2464 break;
2465 }
2466 }
2467
2468 condFlushLine(fout, indent);
2469
2470 if (ParserImpl.XMLPreserveWhiteSpace(node, tt))
2471 {
2472 indent = 0;
2473 cindent = 0;
2474 mixed = false;
2475 }
2476 else if (mixed)
2477 {
2478 cindent = indent;
2479 }
2480 else
2481 {
2482 cindent = indent + this.configuration.spaces;
2483 }
2484
2485 printTag(lexer, fout, mode, indent, node);
2486
2487 if (!mixed && node.content != null)
2488 {
2489 flushLine(fout, indent);
2490 }
2491
2492 for (content = node.content; content != null; content = content.next)
2493 {
2494 printXMLTree(fout, mode, cindent, lexer, content);
2495 }
2496
2497 if (!mixed && node.content != null)
2498 {
2499 condFlushLine(fout, cindent);
2500 }
2501 printEndTag(mode, indent, node);
2502
2503
2504
2505
2506
2507 }
2508 }
2509
2510 /***
2511 * Split parse tree by h2 elements and output to separate files. Counts number of h2 children (if any) belonging to
2512 * node.
2513 * @param node root node
2514 * @return number of slides (number of h2 elements)
2515 */
2516 public int countSlides(Node node)
2517 {
2518
2519 int n = 1;
2520
2521 TagTable tt = this.configuration.tt;
2522
2523
2524 if (node != null && node.content != null && node.content.tag == tt.tagH2)
2525 {
2526
2527 n--;
2528 }
2529
2530 if (node != null)
2531 {
2532 for (node = node.content; node != null; node = node.next)
2533 {
2534 if (node.tag == tt.tagH2)
2535 {
2536 ++n;
2537 }
2538 }
2539 }
2540
2541 return n;
2542 }
2543
2544 /***
2545 * @param fout
2546 * @param indent
2547 */
2548 private void printNavBar(Out fout, int indent)
2549 {
2550 String buf;
2551
2552 condFlushLine(fout, indent);
2553 printString("<center><small>");
2554
2555 NumberFormat numberFormat = NumberFormat.getInstance();
2556 numberFormat.setMinimumIntegerDigits(3);
2557
2558 if (slide > 1)
2559 {
2560 buf = "<a href=\"slide" + numberFormat.format(slide - 1) + ".html\">previous</a> | ";
2561
2562 printString(buf);
2563 condFlushLine(fout, indent);
2564
2565 if (slide < count)
2566 {
2567 printString("<a href=\"slide001.html\">start</a> | ");
2568
2569 }
2570 else
2571 {
2572 printString("<a href=\"slide001.html\">start</a>");
2573
2574 }
2575
2576 condFlushLine(fout, indent);
2577 }
2578
2579 if (slide < count)
2580 {
2581 buf = "<a href=\"slide" + numberFormat.format(slide + 1) + ".html\">next</a>";
2582
2583 printString(buf);
2584 }
2585
2586 printString("</small></center>");
2587 condFlushLine(fout, indent);
2588 }
2589
2590 /***
2591 * Called from printTree to print the content of a slide from the node slidecontent. On return slidecontent points
2592 * to the node starting the next slide or null. The variables slide and count are used to customise the navigation
2593 * bar.
2594 * @param fout
2595 * @param mode
2596 * @param indent
2597 * @param lexer
2598 */
2599 public void printSlide(Out fout, short mode, int indent, Lexer lexer)
2600 {
2601 Node content, last;
2602 TagTable tt = this.configuration.tt;
2603
2604 NumberFormat numberFormat = NumberFormat.getInstance();
2605 numberFormat.setMinimumIntegerDigits(3);
2606
2607
2608 String s;
2609 s = "<div onclick=\"document.location='slide"
2610 + numberFormat.format(slide < count ? slide + 1 : 1)
2611 + ".html'\">";
2612
2613 printString(s);
2614 condFlushLine(fout, indent);
2615
2616
2617 if (slidecontent != null && slidecontent.tag == tt.tagH2)
2618 {
2619 printNavBar(fout, indent);
2620
2621
2622
2623 addC('<', linelen++);
2624
2625 addC(TidyUtils.foldCase('h', this.configuration.upperCaseTags, this.configuration.xmlTags), linelen++);
2626 addC(TidyUtils.foldCase('r', this.configuration.upperCaseTags, this.configuration.xmlTags), linelen++);
2627
2628 if (this.configuration.xmlOut)
2629 {
2630 printString(" />");
2631 }
2632 else
2633 {
2634 addC('>', linelen++);
2635 }
2636
2637 if (this.configuration.indentContent)
2638 {
2639 condFlushLine(fout, indent);
2640 }
2641
2642
2643
2644
2645
2646
2647 printTree(
2648 fout,
2649 mode,
2650 (this.configuration.indentContent ? indent + this.configuration.spaces : indent),
2651 lexer,
2652 slidecontent);
2653
2654 slidecontent = slidecontent.next;
2655 }
2656
2657
2658
2659 last = null;
2660 content = slidecontent;
2661
2662 for (; content != null; content = content.next)
2663 {
2664 if (content.tag == tt.tagH2)
2665 {
2666 break;
2667 }
2668
2669
2670 if (last != null
2671 && !this.configuration.indentContent
2672 && last.type == Node.TEXT_NODE
2673 && content.tag != null
2674 && TidyUtils.toBoolean(content.tag.model & Dict.CM_BLOCK))
2675 {
2676 flushLine(fout, indent);
2677 flushLine(fout, indent);
2678 }
2679
2680 printTree(
2681 fout,
2682 mode,
2683 (this.configuration.indentContent ? indent + this.configuration.spaces : indent),
2684 lexer,
2685 content);
2686
2687 last = content;
2688 }
2689
2690 slidecontent = content;
2691
2692
2693
2694 condFlushLine(fout, indent);
2695
2696 printString("<br clear=\"all\">");
2697 condFlushLine(fout, indent);
2698
2699 addC('<', linelen++);
2700
2701 addC(TidyUtils.foldCase('h', this.configuration.upperCaseTags, this.configuration.xmlTags), linelen++);
2702 addC(TidyUtils.foldCase('r', this.configuration.upperCaseTags, this.configuration.xmlTags), linelen++);
2703
2704 if (this.configuration.xmlOut)
2705 {
2706 printString(" />");
2707 }
2708 else
2709 {
2710 addC('>', linelen++);
2711 }
2712
2713 if (this.configuration.indentContent)
2714 {
2715 condFlushLine(fout, indent);
2716 }
2717
2718 printNavBar(fout, indent);
2719
2720
2721 printString("</div>");
2722 condFlushLine(fout, indent);
2723 }
2724
2725 /***
2726 * Add meta element for page transition effect, this works on IE but not NS.
2727 * @param lexer
2728 * @param root
2729 * @param duration
2730 */
2731 public void addTransitionEffect(Lexer lexer, Node root, double duration)
2732 {
2733 Node head = root.findHEAD(lexer.configuration.tt);
2734 String transition;
2735
2736 transition = "blendTrans(Duration=" + (new Double(duration)).toString() + ")";
2737
2738 if (head != null)
2739 {
2740 Node meta = lexer.inferredTag("meta");
2741 meta.addAttribute("http-equiv", "Page-Enter");
2742 meta.addAttribute("content", transition);
2743 head.insertNodeAtStart(meta);
2744 }
2745 }
2746
2747 /***
2748 * Creates slides from h2.
2749 * @param lexer Lexer
2750 * @param root root node
2751 */
2752 public void createSlides(Lexer lexer, Node root)
2753 {
2754 Node body;
2755 String buf;
2756
2757 NumberFormat numberFormat = NumberFormat.getInstance();
2758 numberFormat.setMinimumIntegerDigits(3);
2759
2760 body = root.findBody(lexer.configuration.tt);
2761 count = countSlides(body);
2762 slidecontent = body.content;
2763
2764 addTransitionEffect(lexer, root, 3.0);
2765
2766 for (slide = 1; slide <= count; ++slide)
2767 {
2768 buf = "slide" + numberFormat.format(slide) + ".html";
2769
2770 try
2771 {
2772 FileOutputStream fis = new FileOutputStream(buf);
2773 Out out = OutFactory.getOut(configuration, fis);
2774
2775 printTree(out, (short) 0, 0, lexer, root);
2776 flushLine(out, 0);
2777 out.close();
2778 }
2779 catch (IOException e)
2780 {
2781 System.err.println(buf + e.toString());
2782 }
2783 }
2784
2785
2786
2787
2788
2789 while ((new File("slide" + numberFormat.format(slide) + ".html")).delete())
2790 {
2791 ++slide;
2792 }
2793 }
2794
2795 }