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: 807 $ ($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 if ("UTF8".equals(this.configuration.getOutCharEncodingName()))
675 {
676
677
678
679
680
681 if ((c >= 0x2000) && !TidyUtils.toBoolean(mode & PREFORMATTED))
682 {
683 if (((c >= 0x2000) && (c <= 0x2006))
684 || ((c >= 0x2008) && (c <= 0x2010))
685 || ((c >= 0x2011) && (c <= 0x2046))
686 || ((c >= 0x207D) && (c <= 0x207E))
687 || ((c >= 0x208D) && (c <= 0x208E))
688 || ((c >= 0x2329) && (c <= 0x232A))
689 || ((c >= 0x3001) && (c <= 0x3003))
690 || ((c >= 0x3008) && (c <= 0x3011))
691 || ((c >= 0x3014) && (c <= 0x301F))
692 || ((c >= 0xFD3E) && (c <= 0xFD3F))
693 || ((c >= 0xFE30) && (c <= 0xFE44))
694 || ((c >= 0xFE49) && (c <= 0xFE52))
695 || ((c >= 0xFE54) && (c <= 0xFE61))
696 || ((c >= 0xFE6A) && (c <= 0xFE6B))
697 || ((c >= 0xFF01) && (c <= 0xFF03))
698 || ((c >= 0xFF05) && (c <= 0xFF0A))
699 || ((c >= 0xFF0C) && (c <= 0xFF0F))
700 || ((c >= 0xFF1A) && (c <= 0xFF1B))
701 || ((c >= 0xFF1F) && (c <= 0xFF20))
702 || ((c >= 0xFF3B) && (c <= 0xFF3D))
703 || ((c >= 0xFF61) && (c <= 0xFF65)))
704 {
705 wraphere = linelen + 2;
706 breakable = true;
707 }
708 else
709 {
710 switch (c)
711 {
712 case 0xFE63 :
713 case 0xFE68 :
714 case 0x3030 :
715 case 0x30FB :
716 case 0xFF3F :
717 case 0xFF5B :
718 case 0xFF5D :
719 wraphere = linelen + 2;
720 breakable = true;
721 }
722 }
723
724 if (breakable)
725 {
726 if (((c >= 0x201A) && (c <= 0x201C)) || ((c >= 0x201E) && (c <= 0x201F)))
727 {
728 wraphere--;
729 }
730 else
731 {
732 switch (c)
733 {
734 case 0x2018 :
735 case 0x2039 :
736 case 0x2045 :
737 case 0x207D :
738 case 0x208D :
739 case 0x2329 :
740 case 0x3008 :
741 case 0x300A :
742 case 0x300C :
743 case 0x300E :
744 case 0x3010 :
745 case 0x3014 :
746 case 0x3016 :
747 case 0x3018 :
748 case 0x301A :
749 case 0x301D :
750 case 0xFD3E :
751 case 0xFE35 :
752 case 0xFE37 :
753 case 0xFE39 :
754 case 0xFE3B :
755 case 0xFE3D :
756 case 0xFE3F :
757 case 0xFE41 :
758 case 0xFE43 :
759 case 0xFE59 :
760 case 0xFE5B :
761 case 0xFE5D :
762 case 0xFF08 :
763 case 0xFF3B :
764 case 0xFF5B :
765 case 0xFF62 :
766 wraphere--;
767 }
768 }
769 }
770 }
771 else if ("BIG5".equals(this.configuration.getOutCharEncodingName()))
772 {
773
774
775 addC(c, linelen++);
776 if (((c & 0xFF00) == 0xA100) && !TidyUtils.toBoolean(mode & PREFORMATTED))
777 {
778 wraphere = linelen;
779
780 if ((c > 0x5C) && (c < 0xAD) && ((c & 1) == 1))
781 {
782 wraphere--;
783 }
784 }
785 return;
786 }
787 else if ("SHIFTJIS".equals(this.configuration.getOutCharEncodingName())
788 || "ISO2022".equals(this.configuration.getOutCharEncodingName()))
789 {
790
791 addC(c, linelen++);
792 return;
793 }
794 else
795 {
796 if (this.configuration.rawOut)
797 {
798 addC(c, linelen++);
799 return;
800 }
801 }
802
803 }
804
805
806 if (c == 160 && TidyUtils.toBoolean(mode & PREFORMATTED))
807 {
808 addC(' ', linelen++);
809 return;
810 }
811
812
813
814
815
816
817 if (this.configuration.makeClean && this.configuration.asciiChars || this.configuration.makeBare)
818 {
819 if (c >= 0x2013 && c <= 0x201E)
820 {
821 switch (c)
822 {
823 case 0x2013 :
824 case 0x2014 :
825 c = '-';
826 break;
827 case 0x2018 :
828 case 0x2019 :
829 case 0x201A :
830 c = '\'';
831 break;
832 case 0x201C :
833 case 0x201D :
834 case 0x201E :
835 c = '"';
836 break;
837 }
838 }
839 }
840
841
842 if ("ISO8859_1".equals(this.configuration.getOutCharEncodingName()))
843 {
844 if (c > 255)
845 {
846 if (!this.configuration.numEntities)
847 {
848 entity = EntityTable.getDefaultEntityTable().entityName((short) c);
849 if (entity != null)
850 {
851 entity = "&" + entity + ";";
852 }
853 else
854 {
855 entity = "&#" + c + ";";
856 }
857 }
858 else
859 {
860 entity = "&#" + c + ";";
861 }
862
863 for (int i = 0; i < entity.length(); i++)
864 {
865 addC(entity.charAt(i), linelen++);
866 }
867
868 return;
869 }
870
871 if (c > 126 && c < 160)
872 {
873 entity = "&#" + c + ";";
874
875 for (int i = 0; i < entity.length(); i++)
876 {
877 addC(entity.charAt(i), linelen++);
878 }
879
880 return;
881 }
882
883 addC(c, linelen++);
884 return;
885 }
886
887
888 if (this.configuration.getOutCharEncodingName().startsWith("UTF"))
889 {
890 addC(c, linelen++);
891 return;
892 }
893
894
895 if (this.configuration.xmlTags)
896 {
897
898 if (c > 127 && "ASCII".equals(this.configuration.getOutCharEncodingName()))
899 {
900 entity = "&#" + c + ";";
901
902 for (int i = 0; i < entity.length(); i++)
903 {
904 addC(entity.charAt(i), linelen++);
905 }
906
907 return;
908 }
909
910
911 addC(c, linelen++);
912 return;
913 }
914
915
916 if ("ASCII".equals(this.configuration.getOutCharEncodingName()) && (c > 126 || (c < ' ' && c != '\t')))
917 {
918 if (!this.configuration.numEntities)
919 {
920 entity = EntityTable.getDefaultEntityTable().entityName((short) c);
921 if (entity != null)
922 {
923 entity = "&" + entity + ";";
924 }
925 else
926 {
927 entity = "&#" + c + ";";
928 }
929 }
930 else
931 {
932 entity = "&#" + c + ";";
933 }
934
935 for (int i = 0; i < entity.length(); i++)
936 {
937 addC(entity.charAt(i), linelen++);
938 }
939
940 return;
941 }
942
943 addC(c, linelen++);
944 }
945
946 /**
947 * The line buffer is uint not char so we can hold Unicode values unencoded. The translation to UTF-8 is deferred to
948 * the outc routine called to flush the line buffer.
949 * @param fout
950 * @param mode
951 * @param indent
952 * @param textarray
953 * @param start
954 * @param end
955 */
956 private void printText(Out fout, short mode, int indent, byte[] textarray, int start, int end)
957 {
958 int i, c;
959 int[] ci = new int[1];
960
961 for (i = start; i < end; ++i)
962 {
963 if (indent + linelen >= this.configuration.wraplen)
964 {
965 wrapLine(fout, indent);
966 }
967
968 c = (textarray[i]) & 0xFF;
969
970
971 if (c > 0x7F)
972 {
973 i += getUTF8(textarray, i, ci);
974 c = ci[0];
975 }
976
977 if (c == '\n')
978 {
979 flushLine(fout, indent);
980 continue;
981 }
982
983 printChar(c, mode);
984 }
985 }
986
987 /**
988 * @param str
989 */
990 private void printString(String str)
991 {
992 for (int i = 0; i < str.length(); i++)
993 {
994 addC(str.charAt(i), linelen++);
995 }
996 }
997
998 /**
999 * @param fout
1000 * @param indent
1001 * @param value
1002 * @param delim
1003 * @param wrappable
1004 */
1005 private void printAttrValue(Out fout, int indent, String value, int delim, boolean wrappable)
1006 {
1007 int c;
1008 int[] ci = new int[1];
1009 boolean wasinstring = false;
1010 byte[] valueChars = null;
1011 int i;
1012 short mode = (wrappable ? (short) (NORMAL | ATTRIBVALUE) : (short) (PREFORMATTED | ATTRIBVALUE));
1013
1014 if (value != null)
1015 {
1016 valueChars = TidyUtils.getBytes(value);
1017 }
1018
1019
1020 if (valueChars != null && valueChars.length >= 5 && valueChars[0] == '<')
1021 {
1022 if (valueChars[1] == '%' || valueChars[1] == '@' || (new String(valueChars, 0, 5)).equals("<?php"))
1023 {
1024 mode |= CDATA;
1025 }
1026 }
1027
1028 if (delim == 0)
1029 {
1030 delim = '"';
1031 }
1032
1033 addC('=', linelen++);
1034
1035
1036 if (!this.configuration.xmlOut)
1037 {
1038
1039 if (indent + linelen < this.configuration.wraplen)
1040 {
1041 wraphere = linelen;
1042 }
1043
1044 if (indent + linelen >= this.configuration.wraplen)
1045 {
1046 wrapLine(fout, indent);
1047 }
1048
1049 if (indent + linelen < this.configuration.wraplen)
1050 {
1051 wraphere = linelen;
1052 }
1053 else
1054 {
1055 condFlushLine(fout, indent);
1056 }
1057 }
1058
1059 addC(delim, linelen++);
1060
1061 if (value != null)
1062 {
1063 inString = false;
1064
1065 i = 0;
1066 while (i < valueChars.length)
1067 {
1068 c = (valueChars[i]) & 0xFF;
1069
1070 if (wrappable && c == ' ' && indent + linelen < this.configuration.wraplen)
1071 {
1072 wraphere = linelen;
1073 wasinstring = inString;
1074 }
1075
1076 if (wrappable && wraphere > 0 && indent + linelen >= this.configuration.wraplen)
1077 {
1078 wrapAttrVal(fout, indent, wasinstring);
1079 }
1080
1081 if (c == delim)
1082 {
1083 String entity;
1084
1085 entity = (c == '"' ? """ : "'");
1086
1087 for (int j = 0; j < entity.length(); j++)
1088 {
1089 addC(entity.charAt(j), linelen++);
1090 }
1091
1092 ++i;
1093 continue;
1094 }
1095 else if (c == '"')
1096 {
1097 if (this.configuration.quoteMarks)
1098 {
1099 addC('&', linelen++);
1100 addC('q', linelen++);
1101 addC('u', linelen++);
1102 addC('o', linelen++);
1103 addC('t', linelen++);
1104 addC(';', linelen++);
1105 }
1106 else
1107 {
1108 addC('"', linelen++);
1109 }
1110
1111 if (delim == '\'')
1112 {
1113 inString = !inString;
1114 }
1115
1116 ++i;
1117 continue;
1118 }
1119 else if (c == '\'')
1120 {
1121 if (this.configuration.quoteMarks)
1122 {
1123 addC('&', linelen++);
1124 addC('#', linelen++);
1125 addC('3', linelen++);
1126 addC('9', linelen++);
1127 addC(';', linelen++);
1128 }
1129 else
1130 {
1131 addC('\'', linelen++);
1132 }
1133
1134 if (delim == '"')
1135 {
1136 inString = !inString;
1137 }
1138
1139 ++i;
1140 continue;
1141 }
1142
1143
1144 if (c > 0x7F)
1145 {
1146 i += getUTF8(valueChars, i, ci);
1147 c = ci[0];
1148 }
1149
1150 ++i;
1151
1152 if (c == '\n')
1153 {
1154 flushLine(fout, indent);
1155 continue;
1156 }
1157
1158 printChar(c, mode);
1159 }
1160 }
1161
1162 inString = false;
1163 addC(delim, linelen++);
1164 }
1165
1166 /**
1167 * @param fout
1168 * @param indent
1169 * @param node
1170 * @param attr
1171 */
1172 private void printAttribute(Out fout, int indent, Node node, AttVal attr)
1173 {
1174 String name;
1175 boolean wrappable = false;
1176
1177 if (this.configuration.indentAttributes)
1178 {
1179 flushLine(fout, indent);
1180 indent += this.configuration.spaces;
1181 }
1182
1183 name = attr.attribute;
1184
1185 if (indent + linelen >= this.configuration.wraplen)
1186 {
1187 wrapLine(fout, indent);
1188 }
1189
1190 if (!this.configuration.xmlTags && !this.configuration.xmlOut && attr.dict != null)
1191 {
1192 if (AttributeTable.getDefaultAttributeTable().isScript(name))
1193 {
1194 wrappable = this.configuration.wrapScriptlets;
1195 }
1196 else if (!attr.dict.isNowrap() && this.configuration.wrapAttVals)
1197 {
1198 wrappable = true;
1199 }
1200 }
1201
1202 if (indent + linelen < this.configuration.wraplen)
1203 {
1204 wraphere = linelen;
1205 addC(' ', linelen++);
1206 }
1207 else
1208 {
1209 condFlushLine(fout, indent);
1210 addC(' ', linelen++);
1211 }
1212
1213 for (int i = 0; i < name.length(); i++)
1214 {
1215 addC(
1216 TidyUtils.foldCase(name.charAt(i), this.configuration.upperCaseAttrs, this.configuration.xmlTags),
1217 linelen++);
1218 }
1219
1220 if (indent + linelen >= this.configuration.wraplen)
1221 {
1222 wrapLine(fout, indent);
1223 }
1224
1225 if (attr.value == null)
1226 {
1227 if (this.configuration.xmlTags || this.configuration.xmlOut)
1228 {
1229 printAttrValue(fout, indent, (attr.isBoolAttribute() ? attr.attribute : ""), attr.delim, true);
1230 }
1231 else if (!attr.isBoolAttribute() && node != null && !node.isNewNode())
1232 {
1233 printAttrValue(fout, indent, "", attr.delim, true);
1234 }
1235 else if (indent + linelen < this.configuration.wraplen)
1236 {
1237 wraphere = linelen;
1238 }
1239
1240 }
1241 else
1242 {
1243 printAttrValue(fout, indent, attr.value, attr.delim, wrappable);
1244 }
1245 }
1246
1247 /**
1248 * @param fout
1249 * @param indent
1250 * @param node
1251 * @param attr
1252 */
1253 private void printAttrs(Out fout, int indent, Node node, AttVal attr)
1254 {
1255
1256 if (configuration.xmlOut
1257 && configuration.xmlSpace
1258 && ParserImpl.XMLPreserveWhiteSpace(node, configuration.tt)
1259 && node.getAttrByName("xml:space") == null)
1260 {
1261 node.addAttribute("xml:space", "preserve");
1262 if (attr != null)
1263 {
1264 attr = node.attributes;
1265 }
1266 }
1267
1268 if (attr != null)
1269 {
1270 if (attr.next != null)
1271 {
1272 printAttrs(fout, indent, node, attr.next);
1273 }
1274
1275 if (attr.attribute != null)
1276 {
1277 Attribute attribute = attr.dict;
1278
1279 if (!this.configuration.dropProprietaryAttributes
1280 || !(attribute == null || TidyUtils.toBoolean(attribute.getVersions() & Dict.VERS_PROPRIETARY)))
1281 {
1282 printAttribute(fout, indent, node, attr);
1283 }
1284 }
1285 else if (attr.asp != null)
1286 {
1287 addC(' ', linelen++);
1288 printAsp(fout, indent, attr.asp);
1289 }
1290 else if (attr.php != null)
1291 {
1292 addC(' ', linelen++);
1293 printPhp(fout, indent, attr.php);
1294 }
1295 }
1296
1297 }
1298
1299 /**
1300 * Line can be wrapped immediately after inline start tag provided if follows a text node ending in a space, or it
1301 * parent is an inline element that that rule applies to. This behaviour was reverse engineered from Netscape 3.0
1302 * @param node current Node
1303 * @return <code>true</code> if the current char follows a space
1304 */
1305 private static boolean afterSpace(Node node)
1306 {
1307 Node prev;
1308 int c;
1309
1310 if (node == null || node.tag == null || !TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE))
1311 {
1312 return true;
1313 }
1314
1315 prev = node.prev;
1316
1317 if (prev != null)
1318 {
1319 if (prev.type == Node.TEXT_NODE && prev.end > prev.start)
1320 {
1321 c = (prev.textarray[prev.end - 1]) & 0xFF;
1322
1323 if (c == 160 || c == ' ' || c == '\n')
1324 {
1325 return true;
1326 }
1327 }
1328
1329 return false;
1330 }
1331
1332 return afterSpace(node.parent);
1333 }
1334
1335 /**
1336 * @param lexer
1337 * @param fout
1338 * @param mode
1339 * @param indent
1340 * @param node
1341 */
1342 private void printTag(Lexer lexer, Out fout, short mode, int indent, Node node)
1343 {
1344 String p;
1345 TagTable tt = this.configuration.tt;
1346
1347 addC('<', linelen++);
1348
1349 if (node.type == Node.END_TAG)
1350 {
1351 addC('/', linelen++);
1352 }
1353
1354 p = node.element;
1355 for (int i = 0; i < p.length(); i++)
1356 {
1357 addC(
1358 TidyUtils.foldCase(p.charAt(i), this.configuration.upperCaseTags, this.configuration.xmlTags),
1359 linelen++);
1360 }
1361
1362 printAttrs(fout, indent, node, node.attributes);
1363
1364 if ((this.configuration.xmlOut || this.configuration.xHTML)
1365 && (node.type == Node.START_END_TAG || TidyUtils.toBoolean(node.tag.model & Dict.CM_EMPTY)))
1366 {
1367 addC(' ', linelen++);
1368 addC('/', linelen++);
1369 }
1370
1371 addC('>', linelen++);
1372
1373 if ((node.type != Node.START_END_TAG || configuration.xHTML) && !TidyUtils.toBoolean(mode & PREFORMATTED))
1374 {
1375 if (indent + linelen >= this.configuration.wraplen)
1376 {
1377 wrapLine(fout, indent);
1378 }
1379
1380 if (indent + linelen < this.configuration.wraplen)
1381 {
1382
1383
1384
1385 if (!TidyUtils.toBoolean(mode & NOWRAP)
1386 && (!TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE) || (node.tag == tt.tagBr))
1387 && afterSpace(node))
1388 {
1389 wraphere = linelen;
1390 }
1391
1392 }
1393 }
1394 else
1395 {
1396 condFlushLine(fout, indent);
1397 }
1398
1399 }
1400
1401 /**
1402 * @param mode
1403 * @param indent
1404 * @param node
1405 */
1406 private void printEndTag(short mode, int indent, Node node)
1407 {
1408 String p;
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418 addC('<', linelen++);
1419 addC('/', linelen++);
1420
1421 p = node.element;
1422 for (int i = 0; i < p.length(); i++)
1423 {
1424 addC(
1425 TidyUtils.foldCase(p.charAt(i), this.configuration.upperCaseTags, this.configuration.xmlTags),
1426 linelen++);
1427 }
1428
1429 addC('>', linelen++);
1430 }
1431
1432 /**
1433 * @param fout
1434 * @param indent
1435 * @param node
1436 */
1437 private void printComment(Out fout, int indent, Node node)
1438 {
1439 if (this.configuration.hideComments)
1440 {
1441 return;
1442 }
1443
1444 if (indent + linelen < this.configuration.wraplen)
1445 {
1446 wraphere = linelen;
1447 }
1448
1449 addC('<', linelen++);
1450 addC('!', linelen++);
1451 addC('-', linelen++);
1452 addC('-', linelen++);
1453
1454 printText(fout, COMMENT, indent, node.textarray, node.start, node.end);
1455
1456
1457 addC('-', linelen++);
1458 addC('-', linelen++);
1459 addC('>', linelen++);
1460
1461 if (node.linebreak)
1462 {
1463 flushLine(fout, indent);
1464 }
1465 }
1466
1467 /**
1468 * @param fout
1469 * @param indent
1470 * @param lexer
1471 * @param node
1472 */
1473 private void printDocType(Out fout, int indent, Lexer lexer, Node node)
1474 {
1475 int i, c = 0;
1476 short mode = 0;
1477 boolean q = this.configuration.quoteMarks;
1478
1479 this.configuration.quoteMarks = false;
1480
1481 if (indent + linelen < this.configuration.wraplen)
1482 {
1483 wraphere = linelen;
1484 }
1485
1486 condFlushLine(fout, indent);
1487
1488 addC('<', linelen++);
1489 addC('!', linelen++);
1490 addC('D', linelen++);
1491 addC('O', linelen++);
1492 addC('C', linelen++);
1493 addC('T', linelen++);
1494 addC('Y', linelen++);
1495 addC('P', linelen++);
1496 addC('E', linelen++);
1497 addC(' ', linelen++);
1498
1499 if (indent + linelen < this.configuration.wraplen)
1500 {
1501 wraphere = linelen;
1502 }
1503
1504 for (i = node.start; i < node.end; ++i)
1505 {
1506 if (indent + linelen >= this.configuration.wraplen)
1507 {
1508 wrapLine(fout, indent);
1509 }
1510
1511 c = node.textarray[i] & 0xFF;
1512
1513
1514 if (TidyUtils.toBoolean(mode & CDATA))
1515 {
1516 if (c == ']')
1517 {
1518 mode &= ~CDATA;
1519 }
1520 }
1521 else if (c == '[')
1522 {
1523 mode |= CDATA;
1524 }
1525 int[] ci = new int[1];
1526
1527
1528 if (c > 0x7F)
1529 {
1530 i += getUTF8(node.textarray, i, ci);
1531 c = ci[0];
1532 }
1533
1534 if (c == '\n')
1535 {
1536 flushLine(fout, indent);
1537 continue;
1538 }
1539
1540 printChar(c, mode);
1541 }
1542
1543 if (linelen < this.configuration.wraplen)
1544 {
1545 wraphere = linelen;
1546 }
1547
1548 addC('>', linelen++);
1549 this.configuration.quoteMarks = q;
1550 condFlushLine(fout, indent);
1551 }
1552
1553 /**
1554 * @param fout
1555 * @param indent
1556 * @param node
1557 */
1558 private void printPI(Out fout, int indent, Node node)
1559 {
1560 if (indent + linelen < this.configuration.wraplen)
1561 {
1562 wraphere = linelen;
1563 }
1564
1565 addC('<', linelen++);
1566 addC('?', linelen++);
1567
1568
1569 printText(fout, CDATA, indent, node.textarray, node.start, node.end);
1570
1571 if (node.end <= 0 || node.textarray[node.end - 1] != '?')
1572 {
1573 addC('?', linelen++);
1574 }
1575
1576 addC('>', linelen++);
1577 condFlushLine(fout, indent);
1578 }
1579
1580 /**
1581 * Pretty print the xml declaration.
1582 * @param fout
1583 * @param indent
1584 * @param node
1585 */
1586 private void printXmlDecl(Out fout, int indent, Node node)
1587 {
1588 if (indent + linelen < this.configuration.wraplen)
1589 {
1590 wraphere = linelen;
1591 }
1592
1593 addC('<', linelen++);
1594 addC('?', linelen++);
1595 addC('x', linelen++);
1596 addC('m', linelen++);
1597 addC('l', linelen++);
1598
1599 printAttrs(fout, indent, node, node.attributes);
1600
1601 if (node.end <= 0 || node.textarray[node.end - 1] != '?')
1602 {
1603 addC('?', linelen++);
1604 }
1605
1606 addC('>', linelen++);
1607
1608 condFlushLine(fout, indent);
1609 }
1610
1611 /**
1612 * note ASP and JSTE share <% ... %> syntax.
1613 * @param fout
1614 * @param indent
1615 * @param node
1616 */
1617 private void printAsp(Out fout, int indent, Node node)
1618 {
1619 int savewraplen = this.configuration.wraplen;
1620
1621
1622
1623 if (!this.configuration.wrapAsp || !this.configuration.wrapJste)
1624 {
1625 this.configuration.wraplen = 0xFFFFFF;
1626 }
1627
1628 addC('<', linelen++);
1629 addC('%', linelen++);
1630
1631 printText(fout, (this.configuration.wrapAsp ? CDATA : COMMENT), indent, node.textarray, node.start, node.end);
1632
1633 addC('%', linelen++);
1634 addC('>', linelen++);
1635
1636 this.configuration.wraplen = savewraplen;
1637 }
1638
1639 /**
1640 * JSTE also supports <# ... #> syntax
1641 * @param fout
1642 * @param indent
1643 * @param node
1644 */
1645 private void printJste(Out fout, int indent, Node node)
1646 {
1647 int savewraplen = this.configuration.wraplen;
1648
1649
1650
1651 if (!this.configuration.wrapJste)
1652 {
1653 this.configuration.wraplen = 0xFFFFFF;
1654 }
1655
1656 addC('<', linelen++);
1657 addC('#', linelen++);
1658
1659 printText(fout, (this.configuration.wrapJste ? CDATA : COMMENT), indent, node.textarray, node.start, node.end);
1660
1661 addC('#', linelen++);
1662 addC('>', linelen++);
1663
1664 this.configuration.wraplen = savewraplen;
1665 }
1666
1667 /**
1668 * PHP is based on XML processing instructions.
1669 * @param fout
1670 * @param indent
1671 * @param node
1672 */
1673 private void printPhp(Out fout, int indent, Node node)
1674 {
1675 int savewraplen = this.configuration.wraplen;
1676
1677
1678
1679 if (!this.configuration.wrapPhp)
1680 {
1681 this.configuration.wraplen = 0xFFFFFF;
1682 }
1683
1684 addC('<', linelen++);
1685 addC('?', linelen++);
1686
1687 printText(fout, (this.configuration.wrapPhp ? CDATA : COMMENT), indent, node.textarray, node.start, node.end);
1688
1689 addC('?', linelen++);
1690 addC('>', linelen++);
1691
1692 this.configuration.wraplen = savewraplen;
1693 }
1694
1695 /**
1696 * @param fout
1697 * @param indent
1698 * @param node
1699 */
1700 private void printCDATA(Out fout, int indent, Node node)
1701 {
1702 int savewraplen = this.configuration.wraplen;
1703
1704 if (!this.configuration.indentCdata)
1705 {
1706 indent = 0;
1707 }
1708
1709 condFlushLine(fout, indent);
1710
1711
1712 this.configuration.wraplen = 0xFFFFFF;
1713
1714 addC('<', linelen++);
1715 addC('!', linelen++);
1716 addC('[', linelen++);
1717 addC('C', linelen++);
1718 addC('D', linelen++);
1719 addC('A', linelen++);
1720 addC('T', linelen++);
1721 addC('A', linelen++);
1722 addC('[', linelen++);
1723
1724 printText(fout, COMMENT, indent, node.textarray, node.start, node.end);
1725
1726 addC(']', linelen++);
1727 addC(']', linelen++);
1728 addC('>', linelen++);
1729 condFlushLine(fout, indent);
1730 this.configuration.wraplen = savewraplen;
1731 }
1732
1733 /**
1734 * @param fout
1735 * @param indent
1736 * @param node
1737 */
1738 private void printSection(Out fout, int indent, Node node)
1739 {
1740 int savewraplen = this.configuration.wraplen;
1741
1742
1743
1744 if (!this.configuration.wrapSection)
1745 {
1746 this.configuration.wraplen = 0xFFFFFF;
1747 }
1748
1749 addC('<', linelen++);
1750 addC('!', linelen++);
1751 addC('[', linelen++);
1752
1753 printText(
1754 fout,
1755 (this.configuration.wrapSection ? CDATA : COMMENT),
1756 indent,
1757 node.textarray,
1758 node.start,
1759 node.end);
1760
1761 addC(']', linelen++);
1762 addC('>', linelen++);
1763
1764 this.configuration.wraplen = savewraplen;
1765 }
1766
1767 /**
1768 * Is the current node inside HEAD?
1769 * @param node Node
1770 * @return <code>true</code> if node is inside an HEAD tag
1771 */
1772 private boolean insideHead(Node node)
1773 {
1774 if (node.tag == this.configuration.tt.tagHead)
1775 {
1776 return true;
1777 }
1778
1779 if (node.parent != null)
1780 {
1781 return insideHead(node.parent);
1782 }
1783 return false;
1784 }
1785
1786 /**
1787 * Is text node and already ends w/ a newline? Used to pretty print CDATA/PRE text content. If it already ends on a
1788 * newline, it is not necessary to print another before printing end tag.
1789 * @param lexer Lexer
1790 * @param node text node
1791 * @return text indent
1792 */
1793 private int textEndsWithNewline(Lexer lexer, Node node)
1794 {
1795 if (node.type == Node.TEXT_NODE && node.end > node.start)
1796 {
1797 int ch, ix = node.end - 1;
1798
1799 while (ix >= node.start
1800 && TidyUtils.toBoolean(ch = (node.textarray[ix] & 0xff))
1801 && (ch == ' ' || ch == '\t' || ch == '\r'))
1802 {
1803 --ix;
1804 }
1805
1806 if (node.textarray[ix] == '\n')
1807 {
1808 return node.end - ix - 1;
1809 }
1810 }
1811 return -1;
1812 }
1813
1814 /**
1815 * Does the current node contain a CDATA section?
1816 * @param lexer Lexer
1817 * @param node Node
1818 * @return <code>true</code> if node contains a CDATA section
1819 */
1820 static boolean hasCDATA(Lexer lexer, Node node)
1821 {
1822
1823
1824
1825 if (node.type != Node.TEXT_NODE)
1826 {
1827 return false;
1828 }
1829
1830 int len = node.end - node.start + 1;
1831 String start = TidyUtils.getString(node.textarray, node.start, len);
1832
1833 int indexOfCData = start.indexOf(CDATA_START);
1834 return indexOfCData > -1 && indexOfCData <= len;
1835 }
1836
1837 /**
1838 * Print script and style elements. For XHTML, wrap the content as follows:
1839 *
1840 * <pre>
1841 * JavaScript:
1842 * //<![CDATA[
1843 * content
1844 * //]]>
1845 * VBScript:
1846 * '<![CDATA[
1847 * content
1848 * ']]>
1849 * CSS:
1850 * /*<![CDATA[* /
1851 * content
1852 * /*]]>* /
1853 * other:
1854 * <![CDATA[
1855 * content
1856 * ]]>
1857 * </pre>
1858 *
1859 * @param fout
1860 * @param mode
1861 * @param indent
1862 * @param lexer
1863 * @param node
1864 */
1865 private void printScriptStyle(Out fout, short mode, int indent, Lexer lexer, Node node)
1866 {
1867 Node content;
1868 String commentStart = DEFAULT_COMMENT_START;
1869 String commentEnd = DEFAULT_COMMENT_END;
1870 boolean hasCData = false;
1871 int contentIndent = -1;
1872
1873 if (insideHead(node))
1874 {
1875
1876 }
1877
1878 indent = 0;
1879
1880
1881 printTag(lexer, fout, mode, indent, node);
1882
1883
1884 if (lexer.configuration.xHTML && node.content != null)
1885 {
1886 AttVal type = node.getAttrByName("type");
1887 if (type != null)
1888 {
1889 if ("text/javascript".equalsIgnoreCase(type.value))
1890 {
1891 commentStart = JS_COMMENT_START;
1892 commentEnd = JS_COMMENT_END;
1893 }
1894 else if ("text/css".equalsIgnoreCase(type.value))
1895 {
1896 commentStart = CSS_COMMENT_START;
1897 commentEnd = CSS_COMMENT_END;
1898 }
1899 else if ("text/vbscript".equalsIgnoreCase(type.value))
1900 {
1901 commentStart = VB_COMMENT_START;
1902 commentEnd = VB_COMMENT_END;
1903 }
1904 }
1905
1906 hasCData = hasCDATA(lexer, node.content);
1907 if (!hasCData)
1908 {
1909
1910 int savewraplen = lexer.configuration.wraplen;
1911 lexer.configuration.wraplen = 0xFFFFFF;
1912
1913 linelen = addAsciiString(commentStart, linelen);
1914 linelen = addAsciiString(CDATA_START, linelen);
1915 linelen = addAsciiString(commentEnd, linelen);
1916 condFlushLine(fout, indent);
1917
1918
1919 lexer.configuration.wraplen = savewraplen;
1920 }
1921 }
1922
1923 for (content = node.content; content != null; content = content.next)
1924 {
1925 printTree(fout, (short) (mode | PREFORMATTED | NOWRAP | CDATA), 0, lexer, content);
1926
1927 if (content.next == null)
1928 {
1929 contentIndent = textEndsWithNewline(lexer, content);
1930 }
1931
1932 }
1933
1934 if (contentIndent < 0)
1935 {
1936 condFlushLine(fout, indent);
1937 contentIndent = 0;
1938 }
1939
1940 if (lexer.configuration.xHTML && node.content != null)
1941 {
1942 if (!hasCData)
1943 {
1944
1945 int ix, savewraplen = lexer.configuration.wraplen;
1946 lexer.configuration.wraplen = 0xFFFFFF;
1947
1948
1949 if (contentIndent > 0 && linelen < contentIndent)
1950 {
1951 linelen = contentIndent;
1952 }
1953 for (ix = 0; contentIndent < indent && ix < indent - contentIndent; ++ix)
1954 {
1955 addC(' ', linelen++);
1956 }
1957
1958 linelen = addAsciiString(commentStart, linelen);
1959 linelen = addAsciiString(CDATA_END, linelen);
1960 linelen = addAsciiString(commentEnd, linelen);
1961
1962
1963 lexer.configuration.wraplen = savewraplen;
1964 condFlushLine(fout, 0);
1965 }
1966 }
1967
1968 printEndTag(mode, indent, node);
1969
1970 if (!lexer.configuration.indentContent && node.next != null
1971
1972 && !((node.tag != null && TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE))
1973
1974 || node.type != Node.TEXT_NODE
1975
1976 ))
1977 {
1978 flushLine(fout, indent);
1979 }
1980
1981 flushLine(fout, indent);
1982 }
1983
1984 /**
1985 * Should tidy indent the give tag?
1986 * @param node actual node
1987 * @return <code>true</code> if line should be indented
1988 */
1989 private boolean shouldIndent(Node node)
1990 {
1991 TagTable tt = this.configuration.tt;
1992
1993 if (!this.configuration.indentContent)
1994 {
1995 return false;
1996 }
1997
1998 if (this.configuration.smartIndent)
1999 {
2000 if (node.content != null && TidyUtils.toBoolean(node.tag.model & Dict.CM_NO_INDENT))
2001 {
2002 for (node = node.content; node != null; node = node.next)
2003 {
2004 if (node.tag != null && TidyUtils.toBoolean(node.tag.model & Dict.CM_BLOCK))
2005 {
2006 return true;
2007 }
2008 }
2009
2010 return false;
2011 }
2012
2013 if (TidyUtils.toBoolean(node.tag.model & Dict.CM_HEADING))
2014 {
2015 return false;
2016 }
2017
2018 if (node.tag == tt.tagP)
2019 {
2020 return false;
2021 }
2022
2023 if (node.tag == tt.tagTitle)
2024 {
2025 return false;
2026 }
2027 }
2028
2029 if (TidyUtils.toBoolean(node.tag.model & (Dict.CM_FIELD | Dict.CM_OBJECT)))
2030 {
2031 return true;
2032 }
2033
2034 if (node.tag == tt.tagMap)
2035 {
2036 return true;
2037 }
2038
2039 return !TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE);
2040 }
2041
2042 /**
2043 * Print just the content of the body element. Useful when you want to reuse material from other documents.
2044 * @param fout
2045 * @param lexer
2046 * @param root
2047 * @param xml
2048 */
2049 void printBody(Out fout, Lexer lexer, Node root, boolean xml)
2050 {
2051 if (root == null)
2052 {
2053 return;
2054 }
2055
2056
2057
2058 Node body = root.findBody(lexer.configuration.tt);
2059
2060 if (body != null)
2061 {
2062 Node content;
2063 for (content = body.content; content != null; content = content.next)
2064 {
2065 if (xml)
2066 {
2067 printXMLTree(fout, (short) 0, 0, lexer, content);
2068 }
2069 else
2070 {
2071 printTree(fout, (short) 0, 0, lexer, content);
2072 }
2073 }
2074 }
2075 }
2076
2077 /**
2078 * @param fout
2079 * @param mode
2080 * @param indent
2081 * @param lexer
2082 * @param node
2083 */
2084 public void printTree(Out fout, short mode, int indent, Lexer lexer, Node node)
2085 {
2086 Node content, last;
2087 TagTable tt = this.configuration.tt;
2088
2089 if (node == null)
2090 {
2091 return;
2092 }
2093
2094 if (node.type == Node.TEXT_NODE || (node.type == Node.CDATA_TAG && lexer.configuration.escapeCdata))
2095 {
2096 printText(fout, mode, indent, node.textarray, node.start, node.end);
2097 }
2098 else if (node.type == Node.COMMENT_TAG)
2099 {
2100 printComment(fout, indent, node);
2101 }
2102 else if (node.type == Node.ROOT_NODE)
2103 {
2104 for (content = node.content; content != null; content = content.next)
2105 {
2106 printTree(fout, mode, indent, lexer, content);
2107 }
2108 }
2109 else if (node.type == Node.DOCTYPE_TAG)
2110 {
2111 printDocType(fout, indent, lexer, node);
2112 }
2113 else if (node.type == Node.PROC_INS_TAG)
2114 {
2115 printPI(fout, indent, node);
2116 }
2117 else if (node.type == Node.XML_DECL)
2118 {
2119 printXmlDecl(fout, indent, node);
2120 }
2121 else if (node.type == Node.CDATA_TAG)
2122 {
2123 printCDATA(fout, indent, node);
2124 }
2125 else if (node.type == Node.SECTION_TAG)
2126 {
2127 printSection(fout, indent, node);
2128 }
2129 else if (node.type == Node.ASP_TAG)
2130 {
2131 printAsp(fout, indent, node);
2132 }
2133 else if (node.type == Node.JSTE_TAG)
2134 {
2135 printJste(fout, indent, node);
2136 }
2137 else if (node.type == Node.PHP_TAG)
2138 {
2139 printPhp(fout, indent, node);
2140 }
2141 else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_EMPTY)
2142 || (node.type == Node.START_END_TAG && !configuration.xHTML))
2143 {
2144 if (!TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE))
2145 {
2146 condFlushLine(fout, indent);
2147 }
2148
2149 if (node.tag == tt.tagBr
2150 && node.prev != null
2151 && node.prev.tag != tt.tagBr
2152 && this.configuration.breakBeforeBR)
2153 {
2154 flushLine(fout, indent);
2155 }
2156
2157 if (this.configuration.makeClean && node.tag == tt.tagWbr)
2158 {
2159 printString(" ");
2160 }
2161 else
2162 {
2163 printTag(lexer, fout, mode, indent, node);
2164 }
2165
2166 if (node.tag == tt.tagParam || node.tag == tt.tagArea)
2167 {
2168 condFlushLine(fout, indent);
2169 }
2170 else if (node.tag == tt.tagBr || node.tag == tt.tagHr)
2171 {
2172 flushLine(fout, indent);
2173 }
2174 }
2175 else
2176 {
2177 if (node.type == Node.START_END_TAG)
2178 {
2179 node.type = Node.START_TAG;
2180 }
2181
2182
2183 if (node.tag != null && node.tag.getParser() == ParserImpl.PRE)
2184 {
2185 condFlushLine(fout, indent);
2186
2187 indent = 0;
2188 condFlushLine(fout, indent);
2189 printTag(lexer, fout, mode, indent, node);
2190 flushLine(fout, indent);
2191
2192 for (content = node.content; content != null; content = content.next)
2193 {
2194 printTree(fout, (short) (mode | PREFORMATTED | NOWRAP), indent, lexer, content);
2195 }
2196
2197 condFlushLine(fout, indent);
2198 printEndTag(mode, indent, node);
2199 flushLine(fout, indent);
2200
2201 if (!this.configuration.indentContent && node.next != null)
2202 {
2203 flushLine(fout, indent);
2204 }
2205 }
2206 else if (node.tag == tt.tagStyle || node.tag == tt.tagScript)
2207 {
2208 printScriptStyle(fout, (short) (mode | PREFORMATTED | NOWRAP | CDATA), indent, lexer, node);
2209 }
2210 else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE))
2211 {
2212 if (this.configuration.makeClean)
2213 {
2214
2215 if (node.tag == tt.tagFont)
2216 {
2217 for (content = node.content; content != null; content = content.next)
2218 {
2219 printTree(fout, mode, indent, lexer, content);
2220 }
2221 return;
2222 }
2223
2224
2225 if (node.tag == tt.tagNobr)
2226 {
2227 for (content = node.content; content != null; content = content.next)
2228 {
2229 printTree(fout, (short) (mode | NOWRAP), indent, lexer, content);
2230 }
2231 return;
2232 }
2233 }
2234
2235
2236
2237 printTag(lexer, fout, mode, indent, node);
2238
2239
2240
2241 if (shouldIndent(node))
2242 {
2243 condFlushLine(fout, indent);
2244 indent += this.configuration.spaces;
2245
2246 for (content = node.content; content != null; content = content.next)
2247 {
2248 printTree(fout, mode, indent, lexer, content);
2249 }
2250
2251 condFlushLine(fout, indent);
2252 indent -= this.configuration.spaces;
2253 condFlushLine(fout, indent);
2254 }
2255 else
2256 {
2257
2258 for (content = node.content; content != null; content = content.next)
2259 {
2260 printTree(fout, mode, indent, lexer, content);
2261 }
2262 }
2263
2264 printEndTag(mode, indent, node);
2265 }
2266 else
2267 {
2268
2269 condFlushLine(fout, indent);
2270
2271 if (this.configuration.smartIndent && node.prev != null)
2272 {
2273 flushLine(fout, indent);
2274 }
2275
2276
2277 if (!this.configuration.hideEndTags
2278 || !(node.tag != null && TidyUtils.toBoolean(node.tag.model & Dict.CM_OMITST))
2279 || node.attributes != null)
2280 {
2281 printTag(lexer, fout, mode, indent, node);
2282
2283 if (shouldIndent(node))
2284 {
2285 condFlushLine(fout, indent);
2286 }
2287 else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_HTML)
2288 || node.tag == tt.tagNoframes
2289 || (TidyUtils.toBoolean(node.tag.model & Dict.CM_HEAD) && !(node.tag == tt.tagTitle)))
2290 {
2291 flushLine(fout, indent);
2292 }
2293 }
2294
2295 if (node.tag == tt.tagBody && this.configuration.burstSlides)
2296 {
2297 printSlide(fout, mode, (this.configuration.indentContent
2298 ? indent + this.configuration.spaces
2299 : indent), lexer);
2300 }
2301 else
2302 {
2303 last = null;
2304
2305 for (content = node.content; content != null; content = content.next)
2306 {
2307
2308 if (last != null
2309 && !this.configuration.indentContent
2310 && last.type == Node.TEXT_NODE
2311 && content.tag != null
2312 && !TidyUtils.toBoolean(content.tag.model & Dict.CM_INLINE))
2313 {
2314 flushLine(fout, indent);
2315 }
2316
2317 printTree(
2318 fout,
2319 mode,
2320 (shouldIndent(node) ? indent + this.configuration.spaces : indent),
2321 lexer,
2322 content);
2323
2324 last = content;
2325 }
2326 }
2327
2328
2329 if (shouldIndent(node)
2330 || ((TidyUtils.toBoolean(node.tag.model & Dict.CM_HTML) || node.tag == tt.tagNoframes ||
2331 (TidyUtils.toBoolean(node.tag.model & Dict.CM_HEAD) && !(node.tag == tt.tagTitle))) &&
2332 !this.configuration.hideEndTags))
2333 {
2334 condFlushLine(
2335 fout,
2336 (this.configuration.indentContent ? indent + this.configuration.spaces : indent));
2337
2338 if (!this.configuration.hideEndTags || !TidyUtils.toBoolean(node.tag.model & Dict.CM_OPT))
2339 {
2340 printEndTag(mode, indent, node);
2341
2342
2343
2344
2345 if (!lexer.seenEndHtml)
2346 {
2347 flushLine(fout, indent);
2348 }
2349 }
2350 }
2351 else
2352 {
2353 if (!this.configuration.hideEndTags || !TidyUtils.toBoolean(node.tag.model & Dict.CM_OPT))
2354 {
2355 printEndTag(mode, indent, node);
2356 }
2357
2358 flushLine(fout, indent);
2359 }
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370 }
2371 }
2372 }
2373
2374 /**
2375 * @param fout
2376 * @param mode
2377 * @param indent
2378 * @param lexer
2379 * @param node
2380 */
2381 public void printXMLTree(Out fout, short mode, int indent, Lexer lexer, Node node)
2382 {
2383 TagTable tt = this.configuration.tt;
2384
2385 if (node == null)
2386 {
2387 return;
2388 }
2389
2390 if (node.type == Node.TEXT_NODE || (node.type == Node.CDATA_TAG && lexer.configuration.escapeCdata))
2391 {
2392 printText(fout, mode, indent, node.textarray, node.start, node.end);
2393 }
2394 else if (node.type == Node.COMMENT_TAG)
2395 {
2396 condFlushLine(fout, indent);
2397 printComment(fout, 0, node);
2398 condFlushLine(fout, 0);
2399 }
2400 else if (node.type == Node.ROOT_NODE)
2401 {
2402 Node content;
2403
2404 for (content = node.content; content != null; content = content.next)
2405 {
2406 printXMLTree(fout, mode, indent, lexer, content);
2407 }
2408 }
2409 else if (node.type == Node.DOCTYPE_TAG)
2410 {
2411 printDocType(fout, indent, lexer, node);
2412 }
2413 else if (node.type == Node.PROC_INS_TAG)
2414 {
2415 printPI(fout, indent, node);
2416 }
2417 else if (node.type == Node.XML_DECL)
2418 {
2419 printXmlDecl(fout, indent, node);
2420 }
2421 else if (node.type == Node.CDATA_TAG)
2422 {
2423 printCDATA(fout, indent, node);
2424 }
2425 else if (node.type == Node.SECTION_TAG)
2426 {
2427 printSection(fout, indent, node);
2428 }
2429 else if (node.type == Node.ASP_TAG)
2430 {
2431 printAsp(fout, indent, node);
2432 }
2433 else if (node.type == Node.JSTE_TAG)
2434 {
2435 printJste(fout, indent, node);
2436 }
2437 else if (node.type == Node.PHP_TAG)
2438 {
2439 printPhp(fout, indent, node);
2440 }
2441 else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_EMPTY)
2442 || node.type == Node.START_END_TAG
2443 && !configuration.xHTML)
2444 {
2445 condFlushLine(fout, indent);
2446 printTag(lexer, fout, mode, indent, node);
2447
2448
2449
2450
2451
2452
2453 }
2454 else
2455 {
2456
2457 Node content;
2458 boolean mixed = false;
2459 int cindent;
2460
2461 for (content = node.content; content != null; content = content.next)
2462 {
2463 if (content.type == Node.TEXT_NODE)
2464 {
2465 mixed = true;
2466 break;
2467 }
2468 }
2469
2470 condFlushLine(fout, indent);
2471
2472 if (ParserImpl.XMLPreserveWhiteSpace(node, tt))
2473 {
2474 indent = 0;
2475 cindent = 0;
2476 mixed = false;
2477 }
2478 else if (mixed)
2479 {
2480 cindent = indent;
2481 }
2482 else
2483 {
2484 cindent = indent + this.configuration.spaces;
2485 }
2486
2487 printTag(lexer, fout, mode, indent, node);
2488
2489 if (!mixed && node.content != null)
2490 {
2491 flushLine(fout, indent);
2492 }
2493
2494 for (content = node.content; content != null; content = content.next)
2495 {
2496 printXMLTree(fout, mode, cindent, lexer, content);
2497 }
2498
2499 if (!mixed && node.content != null)
2500 {
2501 condFlushLine(fout, cindent);
2502 }
2503 printEndTag(mode, indent, node);
2504
2505
2506
2507
2508
2509 }
2510 }
2511
2512 /**
2513 * Split parse tree by h2 elements and output to separate files. Counts number of h2 children (if any) belonging to
2514 * node.
2515 * @param node root node
2516 * @return number of slides (number of h2 elements)
2517 */
2518 public int countSlides(Node node)
2519 {
2520
2521 int n = 1;
2522
2523 TagTable tt = this.configuration.tt;
2524
2525
2526 if (node != null && node.content != null && node.content.tag == tt.tagH2)
2527 {
2528
2529 n--;
2530 }
2531
2532 if (node != null)
2533 {
2534 for (node = node.content; node != null; node = node.next)
2535 {
2536 if (node.tag == tt.tagH2)
2537 {
2538 ++n;
2539 }
2540 }
2541 }
2542
2543 return n;
2544 }
2545
2546 /**
2547 * @param fout
2548 * @param indent
2549 */
2550 private void printNavBar(Out fout, int indent)
2551 {
2552 String buf;
2553
2554 condFlushLine(fout, indent);
2555 printString("<center><small>");
2556
2557 NumberFormat numberFormat = NumberFormat.getInstance();
2558 numberFormat.setMinimumIntegerDigits(3);
2559
2560 if (slide > 1)
2561 {
2562 buf = "<a href=\"slide" + numberFormat.format(slide - 1) + ".html\">previous</a> | ";
2563
2564 printString(buf);
2565 condFlushLine(fout, indent);
2566
2567 if (slide < count)
2568 {
2569 printString("<a href=\"slide001.html\">start</a> | ");
2570
2571 }
2572 else
2573 {
2574 printString("<a href=\"slide001.html\">start</a>");
2575
2576 }
2577
2578 condFlushLine(fout, indent);
2579 }
2580
2581 if (slide < count)
2582 {
2583 buf = "<a href=\"slide" + numberFormat.format(slide + 1) + ".html\">next</a>";
2584
2585 printString(buf);
2586 }
2587
2588 printString("</small></center>");
2589 condFlushLine(fout, indent);
2590 }
2591
2592 /**
2593 * Called from printTree to print the content of a slide from the node slidecontent. On return slidecontent points
2594 * to the node starting the next slide or null. The variables slide and count are used to customise the navigation
2595 * bar.
2596 * @param fout
2597 * @param mode
2598 * @param indent
2599 * @param lexer
2600 */
2601 public void printSlide(Out fout, short mode, int indent, Lexer lexer)
2602 {
2603 Node content, last;
2604 TagTable tt = this.configuration.tt;
2605
2606 NumberFormat numberFormat = NumberFormat.getInstance();
2607 numberFormat.setMinimumIntegerDigits(3);
2608
2609
2610 String s;
2611 s = "<div onclick=\"document.location='slide"
2612 + numberFormat.format(slide < count ? slide + 1 : 1)
2613 + ".html'\">";
2614
2615 printString(s);
2616 condFlushLine(fout, indent);
2617
2618
2619 if (slidecontent != null && slidecontent.tag == tt.tagH2)
2620 {
2621 printNavBar(fout, indent);
2622
2623
2624
2625 addC('<', linelen++);
2626
2627 addC(TidyUtils.foldCase('h', this.configuration.upperCaseTags, this.configuration.xmlTags), linelen++);
2628 addC(TidyUtils.foldCase('r', this.configuration.upperCaseTags, this.configuration.xmlTags), linelen++);
2629
2630 if (this.configuration.xmlOut)
2631 {
2632 printString(" />");
2633 }
2634 else
2635 {
2636 addC('>', linelen++);
2637 }
2638
2639 if (this.configuration.indentContent)
2640 {
2641 condFlushLine(fout, indent);
2642 }
2643
2644
2645
2646
2647
2648
2649 printTree(
2650 fout,
2651 mode,
2652 (this.configuration.indentContent ? indent + this.configuration.spaces : indent),
2653 lexer,
2654 slidecontent);
2655
2656 slidecontent = slidecontent.next;
2657 }
2658
2659
2660
2661 last = null;
2662 content = slidecontent;
2663
2664 for (; content != null; content = content.next)
2665 {
2666 if (content.tag == tt.tagH2)
2667 {
2668 break;
2669 }
2670
2671
2672 if (last != null
2673 && !this.configuration.indentContent
2674 && last.type == Node.TEXT_NODE
2675 && content.tag != null
2676 && TidyUtils.toBoolean(content.tag.model & Dict.CM_BLOCK))
2677 {
2678 flushLine(fout, indent);
2679 flushLine(fout, indent);
2680 }
2681
2682 printTree(
2683 fout,
2684 mode,
2685 (this.configuration.indentContent ? indent + this.configuration.spaces : indent),
2686 lexer,
2687 content);
2688
2689 last = content;
2690 }
2691
2692 slidecontent = content;
2693
2694
2695
2696 condFlushLine(fout, indent);
2697
2698 printString("<br clear=\"all\">");
2699 condFlushLine(fout, indent);
2700
2701 addC('<', linelen++);
2702
2703 addC(TidyUtils.foldCase('h', this.configuration.upperCaseTags, this.configuration.xmlTags), linelen++);
2704 addC(TidyUtils.foldCase('r', this.configuration.upperCaseTags, this.configuration.xmlTags), linelen++);
2705
2706 if (this.configuration.xmlOut)
2707 {
2708 printString(" />");
2709 }
2710 else
2711 {
2712 addC('>', linelen++);
2713 }
2714
2715 if (this.configuration.indentContent)
2716 {
2717 condFlushLine(fout, indent);
2718 }
2719
2720 printNavBar(fout, indent);
2721
2722
2723 printString("</div>");
2724 condFlushLine(fout, indent);
2725 }
2726
2727 /**
2728 * Add meta element for page transition effect, this works on IE but not NS.
2729 * @param lexer
2730 * @param root
2731 * @param duration
2732 */
2733 public void addTransitionEffect(Lexer lexer, Node root, double duration)
2734 {
2735 Node head = root.findHEAD(lexer.configuration.tt);
2736 String transition;
2737
2738 transition = "blendTrans(Duration=" + (new Double(duration)).toString() + ")";
2739
2740 if (head != null)
2741 {
2742 Node meta = lexer.inferredTag("meta");
2743 meta.addAttribute("http-equiv", "Page-Enter");
2744 meta.addAttribute("content", transition);
2745 head.insertNodeAtStart(meta);
2746 }
2747 }
2748
2749 /**
2750 * Creates slides from h2.
2751 * @param lexer Lexer
2752 * @param root root node
2753 */
2754 public void createSlides(Lexer lexer, Node root)
2755 {
2756 Node body;
2757 String buf;
2758
2759 NumberFormat numberFormat = NumberFormat.getInstance();
2760 numberFormat.setMinimumIntegerDigits(3);
2761
2762 body = root.findBody(lexer.configuration.tt);
2763 count = countSlides(body);
2764 slidecontent = body.content;
2765
2766 addTransitionEffect(lexer, root, 3.0);
2767
2768 for (slide = 1; slide <= count; ++slide)
2769 {
2770 buf = "slide" + numberFormat.format(slide) + ".html";
2771
2772 try
2773 {
2774 FileOutputStream fis = new FileOutputStream(buf);
2775 Out out = OutFactory.getOut(configuration, fis);
2776
2777 printTree(out, (short) 0, 0, lexer, root);
2778 flushLine(out, 0);
2779
2780 fis.close();
2781 }
2782 catch (IOException e)
2783 {
2784 System.err.println(buf + e.toString());
2785 }
2786 }
2787
2788
2789
2790
2791
2792 while ((new File("slide" + numberFormat.format(slide) + ".html")).delete())
2793 {
2794 ++slide;
2795 }
2796 }
2797
2798 }