0001: /*
0002: * Java HTML Tidy - JTidy
0003: * HTML parser and pretty printer
0004: *
0005: * Copyright (c) 1998-2000 World Wide Web Consortium (Massachusetts
0006: * Institute of Technology, Institut National de Recherche en
0007: * Informatique et en Automatique, Keio University). All Rights
0008: * Reserved.
0009: *
0010: * Contributing Author(s):
0011: *
0012: * Dave Raggett <dsr@w3.org>
0013: * Andy Quick <ac.quick@sympatico.ca> (translation to Java)
0014: * Gary L Peskin <garyp@firstech.com> (Java development)
0015: * Sami Lempinen <sami@lempinen.net> (release management)
0016: * Fabrizio Giustina <fgiust at users.sourceforge.net>
0017: *
0018: * The contributing author(s) would like to thank all those who
0019: * helped with testing, bug fixes, and patience. This wouldn't
0020: * have been possible without all of you.
0021: *
0022: * COPYRIGHT NOTICE:
0023: *
0024: * This software and documentation is provided "as is," and
0025: * the copyright holders and contributing author(s) make no
0026: * representations or warranties, express or implied, including
0027: * but not limited to, warranties of merchantability or fitness
0028: * for any particular purpose or that the use of the software or
0029: * documentation will not infringe any third party patents,
0030: * copyrights, trademarks or other rights.
0031: *
0032: * The copyright holders and contributing author(s) will not be
0033: * liable for any direct, indirect, special or consequential damages
0034: * arising out of any use of the software or documentation, even if
0035: * advised of the possibility of such damage.
0036: *
0037: * Permission is hereby granted to use, copy, modify, and distribute
0038: * this source code, or portions hereof, documentation and executables,
0039: * for any purpose, without fee, subject to the following restrictions:
0040: *
0041: * 1. The origin of this source code must not be misrepresented.
0042: * 2. Altered versions must be plainly marked as such and must
0043: * not be misrepresented as being the original source.
0044: * 3. This Copyright notice may not be removed or altered from any
0045: * source or altered source distribution.
0046: *
0047: * The copyright holders and contributing author(s) specifically
0048: * permit, without fee, and encourage the use of this source code
0049: * as a component for supporting the Hypertext Markup Language in
0050: * commercial products. If you use this source code in a product,
0051: * acknowledgment is not required but would be appreciated.
0052: *
0053: */
0054: package org.w3c.tidy;
0055:
0056: import java.io.File;
0057: import java.io.FileOutputStream;
0058: import java.io.IOException;
0059: import java.text.NumberFormat;
0060:
0061: /**
0062: * Pretty print parse tree. Block-level and unknown elements are printed on new lines and their contents indented 2
0063: * spaces Inline elements are printed inline. Inline content is wrapped on spaces (except in attribute values or
0064: * preformatted text, after start tags and before end tags.
0065: * @author Dave Raggett <a href="mailto:dsr@w3.org">dsr@w3.org </a>
0066: * @author Andy Quick <a href="mailto:ac.quick@sympatico.ca">ac.quick@sympatico.ca </a> (translation to Java)
0067: * @author Fabrizio Giustina
0068: * @version $Revision: 1.60 $ ($Author: fgiust $)
0069: */
0070: public class PPrint {
0071:
0072: /**
0073: * position: normal.
0074: */
0075: private static final short NORMAL = 0;
0076:
0077: /**
0078: * position: preformatted text.
0079: */
0080: private static final short PREFORMATTED = 1;
0081:
0082: /**
0083: * position: comment.
0084: */
0085: private static final short COMMENT = 2;
0086:
0087: /**
0088: * position: attribute value.
0089: */
0090: private static final short ATTRIBVALUE = 4;
0091:
0092: /**
0093: * position: nowrap.
0094: */
0095: private static final short NOWRAP = 8;
0096:
0097: /**
0098: * position: cdata.
0099: */
0100: private static final short CDATA = 16;
0101:
0102: /**
0103: * Start cdata token.
0104: */
0105: private static final String CDATA_START = "<![CDATA[";
0106:
0107: /**
0108: * End cdata token.
0109: */
0110: private static final String CDATA_END = "]]>";
0111:
0112: /**
0113: * Javascript comment start.
0114: */
0115: private static final String JS_COMMENT_START = "//";
0116:
0117: /**
0118: * Javascript comment end.
0119: */
0120: private static final String JS_COMMENT_END = "";
0121:
0122: /**
0123: * VB comment start.
0124: */
0125: private static final String VB_COMMENT_START = "\'";
0126:
0127: /**
0128: * VB comment end.
0129: */
0130: private static final String VB_COMMENT_END = "";
0131:
0132: /**
0133: * CSS comment start.
0134: */
0135: private static final String CSS_COMMENT_START = "/*";
0136:
0137: /**
0138: * CSS comment end.
0139: */
0140: private static final String CSS_COMMENT_END = "*/";
0141:
0142: /**
0143: * Default comment start.
0144: */
0145: private static final String DEFAULT_COMMENT_START = "";
0146:
0147: /**
0148: * Default comment end.
0149: */
0150: private static final String DEFAULT_COMMENT_END = "";
0151:
0152: private int[] linebuf;
0153:
0154: private int lbufsize;
0155:
0156: private int linelen;
0157:
0158: private int wraphere;
0159:
0160: private boolean inAttVal;
0161:
0162: private boolean inString;
0163:
0164: /**
0165: * Current slide number.
0166: */
0167: private int slide;
0168:
0169: /**
0170: * Total slides count.
0171: */
0172: private int count;
0173:
0174: private Node slidecontent;
0175:
0176: /**
0177: * current configuration.
0178: */
0179: private Configuration configuration;
0180:
0181: /**
0182: * Instantiates a new PPrint.
0183: * @param configuration configuration
0184: */
0185: public PPrint(Configuration configuration) {
0186: this .configuration = configuration;
0187: }
0188:
0189: /**
0190: * @param ind
0191: * @return
0192: */
0193: int cWrapLen(int ind) {
0194: /* #431953 - start RJ Wraplen adjusted for smooth international ride */
0195: if ("zh".equals(this .configuration.language)) {
0196: // Chinese characters take two positions on a fixed-width screen
0197: // It would be more accurate to keep a parallel linelen and wraphere incremented by 2 for Chinese characters
0198: // and 1 otherwise, but this is way simpler.
0199: return (ind + ((this .configuration.wraplen - ind) / 2));
0200: }
0201: if ("ja".equals(this .configuration.language)) {
0202: /* average Japanese text is 30% kanji */
0203: return (ind + (((this .configuration.wraplen - ind) * 7) / 10));
0204: }
0205: return (this .configuration.wraplen);
0206: /* #431953 - end RJ */
0207: }
0208:
0209: /**
0210: * return one less than the number of bytes used by the UTF-8 byte sequence. The Unicode char is returned in ch.
0211: * @param str points to the UTF-8 byte sequence
0212: * @param start starting offset in str
0213: * @param ch initialized to 1st byte, passed as an array to allow modification
0214: * @return one less that the number of bytes used by UTF-8 char
0215: */
0216: public static int getUTF8(byte[] str, int start, int[] ch) {
0217:
0218: int[] n = new int[1];
0219:
0220: int[] bytes = new int[] { 0 };
0221:
0222: // first byte "str[0]" is passed in separately from the
0223: // rest of the UTF-8 byte sequence starting at "str[1]"
0224: byte[] successorBytes = str;
0225:
0226: boolean err = EncodingUtils.decodeUTF8BytesToChar(n, TidyUtils
0227: .toUnsigned(str[start]), successorBytes, null, bytes,
0228: start + 1);
0229:
0230: if (err) {
0231: n[0] = 0xFFFD; // replacement char
0232: }
0233: ch[0] = n[0];
0234: return bytes[0] - 1;
0235:
0236: }
0237:
0238: /**
0239: * store char c as UTF-8 encoded byte stream.
0240: * @param buf
0241: * @param start
0242: * @param c
0243: * @return
0244: */
0245: public static int putUTF8(byte[] buf, int start, int c) {
0246: int[] count = new int[] { 0 };
0247:
0248: boolean err = EncodingUtils.encodeCharToUTF8Bytes(c, buf, null,
0249: count);
0250: if (err) {
0251: // replacement char 0xFFFD encoded as UTF-8
0252: buf[0] = (byte) 0xEF;
0253: buf[1] = (byte) 0xBF;
0254: buf[2] = (byte) 0xBD;
0255: count[0] = 3;
0256: }
0257:
0258: start += count[0];
0259:
0260: return start;
0261: }
0262:
0263: private void addC(int c, int index) {
0264: if (index + 1 >= lbufsize) {
0265: while (index + 1 >= lbufsize) {
0266: if (lbufsize == 0) {
0267: lbufsize = 256;
0268: } else {
0269: lbufsize = lbufsize * 2;
0270: }
0271: }
0272:
0273: int[] temp = new int[lbufsize];
0274: if (linebuf != null) {
0275: System.arraycopy(linebuf, 0, temp, 0, index);
0276: }
0277: linebuf = temp;
0278: }
0279:
0280: linebuf[index] = c;
0281: }
0282:
0283: /**
0284: * Adds an ascii String.
0285: * @param str String to be added
0286: * @param index actual line lenght
0287: * @return final line length
0288: */
0289: private int addAsciiString(String str, int index) {
0290:
0291: int len = str.length();
0292: if (index + len >= lbufsize) {
0293: while (index + len >= lbufsize) {
0294: if (lbufsize == 0) {
0295: lbufsize = 256;
0296: } else {
0297: lbufsize = lbufsize * 2;
0298: }
0299: }
0300:
0301: int[] temp = new int[lbufsize];
0302: if (linebuf != null) {
0303: System.arraycopy(linebuf, 0, temp, 0, index);
0304: }
0305: linebuf = temp;
0306: }
0307:
0308: for (int ix = 0; ix < len; ++ix) {
0309: linebuf[index + ix] = str.charAt(ix);
0310: }
0311: return index + len;
0312: }
0313:
0314: /**
0315: * @param fout
0316: * @param indent
0317: */
0318: private void wrapLine(Out fout, int indent) {
0319: int i, p, q;
0320:
0321: if (wraphere == 0) {
0322: return;
0323: }
0324:
0325: for (i = 0; i < indent; ++i) {
0326: fout.outc(' ');
0327: }
0328:
0329: for (i = 0; i < wraphere; ++i) {
0330: fout.outc(linebuf[i]);
0331: }
0332:
0333: if (inString) {
0334: fout.outc(' ');
0335: fout.outc('\\');
0336: }
0337:
0338: fout.newline();
0339:
0340: if (linelen > wraphere) {
0341: p = 0;
0342:
0343: if (linebuf[wraphere] == ' ') {
0344: ++wraphere;
0345: }
0346:
0347: q = wraphere;
0348: addC('\0', linelen);
0349:
0350: while (true) {
0351: linebuf[p] = linebuf[q];
0352: if (linebuf[q] == 0) {
0353: break;
0354: }
0355: p++;
0356: q++;
0357: }
0358: linelen -= wraphere;
0359: } else {
0360: linelen = 0;
0361: }
0362:
0363: wraphere = 0;
0364: }
0365:
0366: /**
0367: * @param fout
0368: * @param indent
0369: * @param inString
0370: */
0371: private void wrapAttrVal(Out fout, int indent, boolean inString) {
0372: int i, p, q;
0373:
0374: for (i = 0; i < indent; ++i) {
0375: fout.outc(' ');
0376: }
0377:
0378: for (i = 0; i < wraphere; ++i) {
0379: fout.outc(linebuf[i]);
0380: }
0381:
0382: fout.outc(' ');
0383:
0384: if (inString) {
0385: fout.outc('\\');
0386: }
0387:
0388: fout.newline();
0389:
0390: if (linelen > wraphere) {
0391: p = 0;
0392:
0393: if (linebuf[wraphere] == ' ') {
0394: ++wraphere;
0395: }
0396:
0397: q = wraphere;
0398: addC('\0', linelen);
0399:
0400: while (true) {
0401: linebuf[p] = linebuf[q];
0402: if (linebuf[q] == 0) {
0403: break;
0404: }
0405: p++;
0406: q++;
0407: }
0408: linelen -= wraphere;
0409: } else {
0410: linelen = 0;
0411: }
0412:
0413: wraphere = 0;
0414: }
0415:
0416: /**
0417: * @param fout
0418: * @param indent
0419: */
0420: public void flushLine(Out fout, int indent) {
0421: int i;
0422:
0423: if (linelen > 0) {
0424: if (indent + linelen >= this .configuration.wraplen) {
0425: wrapLine(fout, indent);
0426: }
0427:
0428: if (!inAttVal || this .configuration.indentAttributes) {
0429: for (i = 0; i < indent; ++i) {
0430: fout.outc(' ');
0431: }
0432: }
0433:
0434: for (i = 0; i < linelen; ++i) {
0435: fout.outc(linebuf[i]);
0436: }
0437: }
0438:
0439: fout.newline();
0440: linelen = 0;
0441: wraphere = 0;
0442: inAttVal = false;
0443: }
0444:
0445: /**
0446: * @param fout
0447: * @param indent
0448: */
0449: public void condFlushLine(Out fout, int indent) {
0450: int i;
0451:
0452: if (linelen > 0) {
0453: if (indent + linelen >= this .configuration.wraplen) {
0454: wrapLine(fout, indent);
0455: }
0456:
0457: if (!inAttVal || this .configuration.indentAttributes) {
0458: for (i = 0; i < indent; ++i) {
0459: fout.outc(' ');
0460: }
0461: }
0462:
0463: for (i = 0; i < linelen; ++i) {
0464: fout.outc(linebuf[i]);
0465: }
0466:
0467: fout.newline();
0468: linelen = 0;
0469: wraphere = 0;
0470: inAttVal = false;
0471: }
0472: }
0473:
0474: /**
0475: * @param c
0476: * @param mode
0477: */
0478: private void printChar(int c, short mode) {
0479: String entity;
0480: boolean breakable = false; // #431953 - RJ
0481:
0482: if (c == ' '
0483: && !TidyUtils
0484: .toBoolean(mode
0485: & (PREFORMATTED | COMMENT | ATTRIBVALUE | CDATA))) {
0486: // coerce a space character to a non-breaking space
0487: if (TidyUtils.toBoolean(mode & NOWRAP)) {
0488: // by default XML doesn't define
0489: if (this .configuration.numEntities
0490: || this .configuration.xmlTags) {
0491: addC('&', linelen++);
0492: addC('#', linelen++);
0493: addC('1', linelen++);
0494: addC('6', linelen++);
0495: addC('0', linelen++);
0496: addC(';', linelen++);
0497: } else {
0498: // otherwise use named entity
0499: addC('&', linelen++);
0500: addC('n', linelen++);
0501: addC('b', linelen++);
0502: addC('s', linelen++);
0503: addC('p', linelen++);
0504: addC(';', linelen++);
0505: }
0506: return;
0507: }
0508: wraphere = linelen;
0509: }
0510:
0511: // comment characters are passed raw
0512: if (TidyUtils.toBoolean(mode & (COMMENT | CDATA))) {
0513: addC(c, linelen++);
0514: return;
0515: }
0516:
0517: // except in CDATA map < to < etc.
0518: if (!TidyUtils.toBoolean(mode & CDATA)) {
0519: if (c == '<') {
0520: addC('&', linelen++);
0521: addC('l', linelen++);
0522: addC('t', linelen++);
0523: addC(';', linelen++);
0524: return;
0525: }
0526:
0527: if (c == '>') {
0528: addC('&', linelen++);
0529: addC('g', linelen++);
0530: addC('t', linelen++);
0531: addC(';', linelen++);
0532: return;
0533: }
0534:
0535: // naked '&' chars can be left alone or quoted as &
0536: // The latter is required for XML where naked '&' are illegal.
0537: if (c == '&' && this .configuration.quoteAmpersand) {
0538: addC('&', linelen++);
0539: addC('a', linelen++);
0540: addC('m', linelen++);
0541: addC('p', linelen++);
0542: addC(';', linelen++);
0543: return;
0544: }
0545:
0546: if (c == '"' && this .configuration.quoteMarks) {
0547: addC('&', linelen++);
0548: addC('q', linelen++);
0549: addC('u', linelen++);
0550: addC('o', linelen++);
0551: addC('t', linelen++);
0552: addC(';', linelen++);
0553: return;
0554: }
0555:
0556: if (c == '\'' && this .configuration.quoteMarks) {
0557: addC('&', linelen++);
0558: addC('#', linelen++);
0559: addC('3', linelen++);
0560: addC('9', linelen++);
0561: addC(';', linelen++);
0562: return;
0563: }
0564:
0565: if (c == 160 && !this .configuration.rawOut) {
0566: if (this .configuration.makeBare) {
0567: addC(' ', linelen++);
0568: } else if (this .configuration.quoteNbsp) {
0569: addC('&', linelen++);
0570:
0571: if (this .configuration.numEntities
0572: || this .configuration.xmlTags) {
0573: addC('#', linelen++);
0574: addC('1', linelen++);
0575: addC('6', linelen++);
0576: addC('0', linelen++);
0577: } else {
0578: addC('n', linelen++);
0579: addC('b', linelen++);
0580: addC('s', linelen++);
0581: addC('p', linelen++);
0582: }
0583:
0584: addC(';', linelen++);
0585: } else {
0586: addC(c, linelen++);
0587: }
0588:
0589: return;
0590: }
0591: }
0592:
0593: // #431953 - start RJ
0594: // Handle encoding-specific issues
0595:
0596: switch (this .configuration.getOutCharEncoding()) {
0597: case Configuration.UTF8:
0598: // Chinese doesn't have spaces, so it needs other kinds of breaks
0599: // This will also help documents using nice Unicode punctuation
0600: // But we leave the ASCII range punctuation untouched
0601:
0602: // Break after any punctuation or spaces characters
0603: if ((c >= 0x2000)
0604: && !TidyUtils.toBoolean(mode & PREFORMATTED)) {
0605: if (((c >= 0x2000) && (c <= 0x2006))
0606: || ((c >= 0x2008) && (c <= 0x2010))
0607: || ((c >= 0x2011) && (c <= 0x2046))
0608: || ((c >= 0x207D) && (c <= 0x207E))
0609: || ((c >= 0x208D) && (c <= 0x208E))
0610: || ((c >= 0x2329) && (c <= 0x232A))
0611: || ((c >= 0x3001) && (c <= 0x3003))
0612: || ((c >= 0x3008) && (c <= 0x3011))
0613: || ((c >= 0x3014) && (c <= 0x301F))
0614: || ((c >= 0xFD3E) && (c <= 0xFD3F))
0615: || ((c >= 0xFE30) && (c <= 0xFE44))
0616: || ((c >= 0xFE49) && (c <= 0xFE52))
0617: || ((c >= 0xFE54) && (c <= 0xFE61))
0618: || ((c >= 0xFE6A) && (c <= 0xFE6B))
0619: || ((c >= 0xFF01) && (c <= 0xFF03))
0620: || ((c >= 0xFF05) && (c <= 0xFF0A))
0621: || ((c >= 0xFF0C) && (c <= 0xFF0F))
0622: || ((c >= 0xFF1A) && (c <= 0xFF1B))
0623: || ((c >= 0xFF1F) && (c <= 0xFF20))
0624: || ((c >= 0xFF3B) && (c <= 0xFF3D))
0625: || ((c >= 0xFF61) && (c <= 0xFF65))) {
0626: wraphere = linelen + 2; // 2, because AddChar is not till later
0627: breakable = true;
0628: } else {
0629: switch (c) {
0630: case 0xFE63:
0631: case 0xFE68:
0632: case 0x3030:
0633: case 0x30FB:
0634: case 0xFF3F:
0635: case 0xFF5B:
0636: case 0xFF5D:
0637: wraphere = linelen + 2;
0638: breakable = true;
0639: }
0640: }
0641: // but break before a left punctuation
0642: if (breakable) {
0643: if (((c >= 0x201A) && (c <= 0x201C))
0644: || ((c >= 0x201E) && (c <= 0x201F))) {
0645: wraphere--;
0646: } else {
0647: switch (c) {
0648: case 0x2018:
0649: case 0x2039:
0650: case 0x2045:
0651: case 0x207D:
0652: case 0x208D:
0653: case 0x2329:
0654: case 0x3008:
0655: case 0x300A:
0656: case 0x300C:
0657: case 0x300E:
0658: case 0x3010:
0659: case 0x3014:
0660: case 0x3016:
0661: case 0x3018:
0662: case 0x301A:
0663: case 0x301D:
0664: case 0xFD3E:
0665: case 0xFE35:
0666: case 0xFE37:
0667: case 0xFE39:
0668: case 0xFE3B:
0669: case 0xFE3D:
0670: case 0xFE3F:
0671: case 0xFE41:
0672: case 0xFE43:
0673: case 0xFE59:
0674: case 0xFE5B:
0675: case 0xFE5D:
0676: case 0xFF08:
0677: case 0xFF3B:
0678: case 0xFF5B:
0679: case 0xFF62:
0680: wraphere--;
0681: }
0682: }
0683: }
0684: }
0685: break;
0686: case Configuration.BIG5:
0687: // Allow linebreak at Chinese punctuation characters
0688: // There are not many spaces in Chinese
0689: addC(c, linelen++);
0690: if (((c & 0xFF00) == 0xA100)
0691: && !TidyUtils.toBoolean(mode & PREFORMATTED)) {
0692: wraphere = linelen;
0693: // opening brackets have odd codes: break before them
0694: if ((c > 0x5C) && (c < 0xAD) && ((c & 1) == 1)) {
0695: wraphere--;
0696: }
0697: }
0698: return;
0699: case Configuration.SHIFTJIS:
0700: case Configuration.ISO2022: // ISO 2022 characters are passed raw
0701: addC(c, linelen++);
0702: return;
0703: default:
0704: if (this .configuration.rawOut) {
0705: addC(c, linelen++);
0706: return;
0707: }
0708: // #431953 - end RJ
0709: }
0710:
0711: // if preformatted text, map to space
0712: if (c == 160 && TidyUtils.toBoolean(mode & PREFORMATTED)) {
0713: addC(' ', linelen++);
0714: return;
0715: }
0716:
0717: // Filters from Word and PowerPoint often use smart quotes resulting in character codes between 128 and 159.
0718: // Unfortunately, the corresponding HTML 4.0 entities for these are not widely supported.
0719: // The following converts dashes and quotation marks to the nearest ASCII equivalent.
0720: // My thanks to Andrzej Novosiolov for his help with this code.
0721:
0722: if (this .configuration.makeClean
0723: && this .configuration.asciiChars
0724: || this .configuration.makeBare) {
0725: if (c >= 0x2013 && c <= 0x201E) {
0726: switch (c) {
0727: case 0x2013: // en dash
0728: case 0x2014: // em dash
0729: c = '-';
0730: break;
0731: case 0x2018: // left single quotation mark
0732: case 0x2019: // right single quotation mark
0733: case 0x201A: // single low-9 quotation mark
0734: c = '\'';
0735: break;
0736: case 0x201C: // left double quotation mark
0737: case 0x201D: // right double quotation mark
0738: case 0x201E: // double low-9 quotation mark
0739: c = '"';
0740: break;
0741: }
0742: }
0743: }
0744:
0745: // don't map latin-1 chars to entities
0746: if (this .configuration.getOutCharEncoding() == Configuration.LATIN1) {
0747: if (c > 255) /* multi byte chars */
0748: {
0749: if (!this .configuration.numEntities) {
0750: entity = EntityTable.getDefaultEntityTable()
0751: .entityName((short) c);
0752: if (entity != null) {
0753: entity = "&" + entity + ";";
0754: } else {
0755: entity = "&#" + c + ";";
0756: }
0757: } else {
0758: entity = "&#" + c + ";";
0759: }
0760:
0761: for (int i = 0; i < entity.length(); i++) {
0762: addC(entity.charAt(i), linelen++);
0763: }
0764:
0765: return;
0766: }
0767:
0768: if (c > 126 && c < 160) {
0769: entity = "&#" + c + ";";
0770:
0771: for (int i = 0; i < entity.length(); i++) {
0772: addC(entity.charAt(i), linelen++);
0773: }
0774:
0775: return;
0776: }
0777:
0778: addC(c, linelen++);
0779: return;
0780: }
0781:
0782: // don't map utf8 or utf16 chars to entities
0783: if (this .configuration.getOutCharEncoding() == Configuration.UTF8
0784: || this .configuration.getOutCharEncoding() == Configuration.UTF16
0785: || this .configuration.getOutCharEncoding() == Configuration.UTF16LE
0786: || this .configuration.getOutCharEncoding() == Configuration.UTF16BE) {
0787: addC(c, linelen++);
0788: return;
0789: }
0790:
0791: // use numeric entities only for XML
0792: if (this .configuration.xmlTags) {
0793: // if ASCII use numeric entities for chars > 127
0794: if (c > 127
0795: && this .configuration.getOutCharEncoding() == Configuration.ASCII) {
0796: entity = "&#" + c + ";";
0797:
0798: for (int i = 0; i < entity.length(); i++) {
0799: addC(entity.charAt(i), linelen++);
0800: }
0801:
0802: return;
0803: }
0804:
0805: // otherwise output char raw
0806: addC(c, linelen++);
0807: return;
0808: }
0809:
0810: // default treatment for ASCII
0811: if (this .configuration.getOutCharEncoding() == Configuration.ASCII
0812: && (c > 126 || (c < ' ' && c != '\t'))) {
0813: if (!this .configuration.numEntities) {
0814: entity = EntityTable.getDefaultEntityTable()
0815: .entityName((short) c);
0816: if (entity != null) {
0817: entity = "&" + entity + ";";
0818: } else {
0819: entity = "&#" + c + ";";
0820: }
0821: } else {
0822: entity = "&#" + c + ";";
0823: }
0824:
0825: for (int i = 0; i < entity.length(); i++) {
0826: addC(entity.charAt(i), linelen++);
0827: }
0828:
0829: return;
0830: }
0831:
0832: addC(c, linelen++);
0833: }
0834:
0835: /**
0836: * The line buffer is uint not char so we can hold Unicode values unencoded. The translation to UTF-8 is deferred to
0837: * the outc routine called to flush the line buffer.
0838: * @param fout
0839: * @param mode
0840: * @param indent
0841: * @param textarray
0842: * @param start
0843: * @param end
0844: */
0845: private void printText(Out fout, short mode, int indent,
0846: byte[] textarray, int start, int end) {
0847: int i, c;
0848: int[] ci = new int[1];
0849:
0850: for (i = start; i < end; ++i) {
0851: if (indent + linelen >= this .configuration.wraplen) {
0852: wrapLine(fout, indent);
0853: }
0854:
0855: c = (textarray[i]) & 0xFF; // Convert to unsigned.
0856:
0857: // look for UTF-8 multibyte character
0858: if (c > 0x7F) {
0859: i += getUTF8(textarray, i, ci);
0860: c = ci[0];
0861: }
0862:
0863: if (c == '\n') {
0864: flushLine(fout, indent);
0865: continue;
0866: }
0867:
0868: printChar(c, mode);
0869: }
0870: }
0871:
0872: /**
0873: * @param str
0874: */
0875: private void printString(String str) {
0876: for (int i = 0; i < str.length(); i++) {
0877: addC(str.charAt(i), linelen++);
0878: }
0879: }
0880:
0881: /**
0882: * @param fout
0883: * @param indent
0884: * @param value
0885: * @param delim
0886: * @param wrappable
0887: */
0888: private void printAttrValue(Out fout, int indent, String value,
0889: int delim, boolean wrappable) {
0890: int c;
0891: int[] ci = new int[1];
0892: boolean wasinstring = false;
0893: byte[] valueChars = null;
0894: int i;
0895: short mode = (wrappable ? (short) (NORMAL | ATTRIBVALUE)
0896: : (short) (PREFORMATTED | ATTRIBVALUE));
0897:
0898: if (value != null) {
0899: valueChars = TidyUtils.getBytes(value);
0900: }
0901:
0902: // look for ASP, Tango or PHP instructions for computed attribute value
0903: if (valueChars != null && valueChars.length >= 5
0904: && valueChars[0] == '<') {
0905: if (valueChars[1] == '%' || valueChars[1] == '@'
0906: || (new String(valueChars, 0, 5)).equals("<?php")) {
0907: mode |= CDATA;
0908: }
0909: }
0910:
0911: if (delim == 0) {
0912: delim = '"';
0913: }
0914:
0915: addC('=', linelen++);
0916:
0917: // don't wrap after "=" for xml documents
0918: if (!this .configuration.xmlOut) {
0919:
0920: if (indent + linelen < this .configuration.wraplen) {
0921: wraphere = linelen;
0922: }
0923:
0924: if (indent + linelen >= this .configuration.wraplen) {
0925: wrapLine(fout, indent);
0926: }
0927:
0928: if (indent + linelen < this .configuration.wraplen) {
0929: wraphere = linelen;
0930: } else {
0931: condFlushLine(fout, indent);
0932: }
0933: }
0934:
0935: addC(delim, linelen++);
0936:
0937: if (value != null) {
0938: inString = false;
0939:
0940: i = 0;
0941: while (i < valueChars.length) {
0942: c = (valueChars[i]) & 0xFF; // Convert to unsigned.
0943:
0944: if (wrappable
0945: && c == ' '
0946: && indent + linelen < this .configuration.wraplen) {
0947: wraphere = linelen;
0948: wasinstring = inString;
0949: }
0950:
0951: if (wrappable
0952: && wraphere > 0
0953: && indent + linelen >= this .configuration.wraplen) {
0954: wrapAttrVal(fout, indent, wasinstring);
0955: }
0956:
0957: if (c == delim) {
0958: String entity;
0959:
0960: entity = (c == '"' ? """ : "'");
0961:
0962: for (int j = 0; j < entity.length(); j++) {
0963: addC(entity.charAt(j), linelen++);
0964: }
0965:
0966: ++i;
0967: continue;
0968: } else if (c == '"') {
0969: if (this .configuration.quoteMarks) {
0970: addC('&', linelen++);
0971: addC('q', linelen++);
0972: addC('u', linelen++);
0973: addC('o', linelen++);
0974: addC('t', linelen++);
0975: addC(';', linelen++);
0976: } else {
0977: addC('"', linelen++);
0978: }
0979:
0980: if (delim == '\'') {
0981: inString = !inString;
0982: }
0983:
0984: ++i;
0985: continue;
0986: } else if (c == '\'') {
0987: if (this .configuration.quoteMarks) {
0988: addC('&', linelen++);
0989: addC('#', linelen++);
0990: addC('3', linelen++);
0991: addC('9', linelen++);
0992: addC(';', linelen++);
0993: } else {
0994: addC('\'', linelen++);
0995: }
0996:
0997: if (delim == '"') {
0998: inString = !inString;
0999: }
1000:
1001: ++i;
1002: continue;
1003: }
1004:
1005: // look for UTF-8 multibyte character
1006: if (c > 0x7F) {
1007: i += getUTF8(valueChars, i, ci);
1008: c = ci[0];
1009: }
1010:
1011: ++i;
1012:
1013: if (c == '\n') {
1014: flushLine(fout, indent);
1015: continue;
1016: }
1017:
1018: printChar(c, mode);
1019: }
1020: }
1021:
1022: inString = false;
1023: addC(delim, linelen++);
1024: }
1025:
1026: /**
1027: * @param fout
1028: * @param indent
1029: * @param node
1030: * @param attr
1031: */
1032: private void printAttribute(Out fout, int indent, Node node,
1033: AttVal attr) {
1034: String name;
1035: boolean wrappable = false;
1036:
1037: if (this .configuration.indentAttributes) {
1038: flushLine(fout, indent);
1039: indent += this .configuration.spaces;
1040: }
1041:
1042: name = attr.attribute;
1043:
1044: if (indent + linelen >= this .configuration.wraplen) {
1045: wrapLine(fout, indent);
1046: }
1047:
1048: if (!this .configuration.xmlTags && !this .configuration.xmlOut
1049: && attr.dict != null) {
1050: if (AttributeTable.getDefaultAttributeTable()
1051: .isScript(name)) {
1052: wrappable = this .configuration.wrapScriptlets;
1053: } else if (!attr.dict.isNowrap()
1054: && this .configuration.wrapAttVals) {
1055: wrappable = true;
1056: }
1057: }
1058:
1059: if (indent + linelen < this .configuration.wraplen) {
1060: wraphere = linelen;
1061: addC(' ', linelen++);
1062: } else {
1063: condFlushLine(fout, indent);
1064: addC(' ', linelen++);
1065: }
1066:
1067: for (int i = 0; i < name.length(); i++) {
1068: addC(TidyUtils.foldCase(name.charAt(i),
1069: this .configuration.upperCaseAttrs,
1070: this .configuration.xmlTags), linelen++);
1071: }
1072:
1073: if (indent + linelen >= this .configuration.wraplen) {
1074: wrapLine(fout, indent);
1075: }
1076:
1077: if (attr.value == null) {
1078: if (this .configuration.xmlTags || this .configuration.xmlOut) {
1079: printAttrValue(fout, indent,
1080: (attr.isBoolAttribute() ? attr.attribute : ""),
1081: attr.delim, true);
1082: } else if (!attr.isBoolAttribute() && node != null
1083: && !node.isNewNode()) {
1084: printAttrValue(fout, indent, "", attr.delim, true);
1085: } else if (indent + linelen < this .configuration.wraplen) {
1086: wraphere = linelen;
1087: }
1088:
1089: } else {
1090: printAttrValue(fout, indent, attr.value, attr.delim,
1091: wrappable);
1092: }
1093: }
1094:
1095: /**
1096: * @param fout
1097: * @param indent
1098: * @param node
1099: * @param attr
1100: */
1101: private void printAttrs(Out fout, int indent, Node node, AttVal attr) {
1102: // add xml:space attribute to pre and other elements
1103: if (configuration.xmlOut
1104: && configuration.xmlSpace
1105: && ParserImpl.XMLPreserveWhiteSpace(node,
1106: configuration.tt)
1107: && node.getAttrByName("xml:space") == null) {
1108: node.addAttribute("xml:space", "preserve");
1109: if (attr != null) {
1110: attr = node.attributes;
1111: }
1112: }
1113:
1114: if (attr != null) {
1115: if (attr.next != null) {
1116: printAttrs(fout, indent, node, attr.next);
1117: }
1118:
1119: if (attr.attribute != null) {
1120: Attribute attribute = attr.dict;
1121:
1122: if (!this .configuration.dropProprietaryAttributes
1123: || !(attribute == null || TidyUtils
1124: .toBoolean(attribute.getVersions()
1125: & Dict.VERS_PROPRIETARY))) {
1126: printAttribute(fout, indent, node, attr);
1127: }
1128: } else if (attr.asp != null) {
1129: addC(' ', linelen++);
1130: printAsp(fout, indent, attr.asp);
1131: } else if (attr.php != null) {
1132: addC(' ', linelen++);
1133: printPhp(fout, indent, attr.php);
1134: }
1135: }
1136:
1137: }
1138:
1139: /**
1140: * Line can be wrapped immediately after inline start tag provided if follows a text node ending in a space, or it
1141: * parent is an inline element that that rule applies to. This behaviour was reverse engineered from Netscape 3.0
1142: * @param node current Node
1143: * @return <code>true</code> if the current char follows a space
1144: */
1145: private static boolean afterSpace(Node node) {
1146: Node prev;
1147: int c;
1148:
1149: if (node == null
1150: || node.tag == null
1151: || !TidyUtils
1152: .toBoolean(node.tag.model & Dict.CM_INLINE)) {
1153: return true;
1154: }
1155:
1156: prev = node.prev;
1157:
1158: if (prev != null) {
1159: if (prev.type == Node.TEXT_NODE && prev.end > prev.start) {
1160: c = (prev.textarray[prev.end - 1]) & 0xFF; // Convert to unsigned.
1161:
1162: if (c == 160 || c == ' ' || c == '\n') {
1163: return true;
1164: }
1165: }
1166:
1167: return false;
1168: }
1169:
1170: return afterSpace(node.parent);
1171: }
1172:
1173: /**
1174: * @param lexer
1175: * @param fout
1176: * @param mode
1177: * @param indent
1178: * @param node
1179: */
1180: private void printTag(Lexer lexer, Out fout, short mode,
1181: int indent, Node node) {
1182: String p;
1183: TagTable tt = this .configuration.tt;
1184:
1185: addC('<', linelen++);
1186:
1187: if (node.type == Node.END_TAG) {
1188: addC('/', linelen++);
1189: }
1190:
1191: p = node.element;
1192: for (int i = 0; i < p.length(); i++) {
1193: addC(TidyUtils.foldCase(p.charAt(i),
1194: this .configuration.upperCaseTags,
1195: this .configuration.xmlTags), linelen++);
1196: }
1197:
1198: printAttrs(fout, indent, node, node.attributes);
1199:
1200: if ((this .configuration.xmlOut || this .configuration.xHTML)
1201: && (node.type == Node.START_END_TAG || TidyUtils
1202: .toBoolean(node.tag.model & Dict.CM_EMPTY))) {
1203: addC(' ', linelen++); // Space is NS compatibility hack <br />
1204: addC('/', linelen++); // Required end tag marker
1205: }
1206:
1207: addC('>', linelen++);
1208:
1209: if ((node.type != Node.START_END_TAG || configuration.xHTML)
1210: && !TidyUtils.toBoolean(mode & PREFORMATTED)) {
1211: if (indent + linelen >= this .configuration.wraplen) {
1212: wrapLine(fout, indent);
1213: }
1214:
1215: if (indent + linelen < this .configuration.wraplen) {
1216:
1217: // wrap after start tag if is <br/> or if it's not inline
1218: // fix for [514348]
1219: if (!TidyUtils.toBoolean(mode & NOWRAP)
1220: && (!TidyUtils.toBoolean(node.tag.model
1221: & Dict.CM_INLINE) || (node.tag == tt.tagBr))
1222: && afterSpace(node)) {
1223: wraphere = linelen;
1224: }
1225:
1226: }
1227: } else {
1228: condFlushLine(fout, indent);
1229: }
1230:
1231: }
1232:
1233: /**
1234: * @param mode
1235: * @param indent
1236: * @param node
1237: */
1238: private void printEndTag(short mode, int indent, Node node) {
1239: String p;
1240:
1241: // Netscape ignores SGML standard by not ignoring a line break before </A> or </U> etc.
1242: // To avoid rendering this as an underlined space, I disable line wrapping before inline end tags
1243:
1244: // if (indent + linelen < this.configuration.wraplen && !TidyUtils.toBoolean(mode & NOWRAP))
1245: // {
1246: // wraphere = linelen;
1247: // }
1248:
1249: addC('<', linelen++);
1250: addC('/', linelen++);
1251:
1252: p = node.element;
1253: for (int i = 0; i < p.length(); i++) {
1254: addC(TidyUtils.foldCase(p.charAt(i),
1255: this .configuration.upperCaseTags,
1256: this .configuration.xmlTags), linelen++);
1257: }
1258:
1259: addC('>', linelen++);
1260: }
1261:
1262: /**
1263: * @param fout
1264: * @param indent
1265: * @param node
1266: */
1267: private void printComment(Out fout, int indent, Node node) {
1268: if (this .configuration.hideComments) {
1269: return;
1270: }
1271:
1272: if (indent + linelen < this .configuration.wraplen) {
1273: wraphere = linelen;
1274: }
1275:
1276: addC('<', linelen++);
1277: addC('!', linelen++);
1278: addC('-', linelen++);
1279: addC('-', linelen++);
1280:
1281: printText(fout, COMMENT, indent, node.textarray, node.start,
1282: node.end);
1283:
1284: // See Lexer.java: AQ 8Jul2000
1285: addC('-', linelen++);
1286: addC('-', linelen++);
1287: addC('>', linelen++);
1288:
1289: if (node.linebreak) {
1290: flushLine(fout, indent);
1291: }
1292: }
1293:
1294: /**
1295: * @param fout
1296: * @param indent
1297: * @param lexer
1298: * @param node
1299: */
1300: private void printDocType(Out fout, int indent, Lexer lexer,
1301: Node node) {
1302: int i, c = 0;
1303: short mode = 0;
1304: boolean q = this .configuration.quoteMarks;
1305:
1306: this .configuration.quoteMarks = false;
1307:
1308: if (indent + linelen < this .configuration.wraplen) {
1309: wraphere = linelen;
1310: }
1311:
1312: condFlushLine(fout, indent);
1313:
1314: addC('<', linelen++);
1315: addC('!', linelen++);
1316: addC('D', linelen++);
1317: addC('O', linelen++);
1318: addC('C', linelen++);
1319: addC('T', linelen++);
1320: addC('Y', linelen++);
1321: addC('P', linelen++);
1322: addC('E', linelen++);
1323: addC(' ', linelen++);
1324:
1325: if (indent + linelen < this .configuration.wraplen) {
1326: wraphere = linelen;
1327: }
1328:
1329: for (i = node.start; i < node.end; ++i) {
1330: if (indent + linelen >= this .configuration.wraplen) {
1331: wrapLine(fout, indent);
1332: }
1333:
1334: c = node.textarray[i] & 0xFF; // Convert to unsigned.
1335:
1336: // inDTDSubset?
1337: if (TidyUtils.toBoolean(mode & CDATA)) {
1338: if (c == ']') {
1339: mode &= ~CDATA;
1340: }
1341: } else if (c == '[') {
1342: mode |= CDATA;
1343: }
1344: int[] ci = new int[1];
1345:
1346: // look for UTF-8 multibyte character
1347: if (c > 0x7F) {
1348: i += getUTF8(node.textarray, i, ci);
1349: c = ci[0];
1350: }
1351:
1352: if (c == '\n') {
1353: flushLine(fout, indent);
1354: continue;
1355: }
1356:
1357: printChar(c, mode);
1358: }
1359:
1360: if (linelen < this .configuration.wraplen) {
1361: wraphere = linelen;
1362: }
1363:
1364: addC('>', linelen++);
1365: this .configuration.quoteMarks = q;
1366: condFlushLine(fout, indent);
1367: }
1368:
1369: /**
1370: * @param fout
1371: * @param indent
1372: * @param node
1373: */
1374: private void printPI(Out fout, int indent, Node node) {
1375: if (indent + linelen < this .configuration.wraplen) {
1376: wraphere = linelen;
1377: }
1378:
1379: addC('<', linelen++);
1380: addC('?', linelen++);
1381:
1382: // set CDATA to pass < and > unescaped
1383: printText(fout, CDATA, indent, node.textarray, node.start,
1384: node.end);
1385:
1386: if (node.end <= 0 || node.textarray[node.end - 1] != '?') // #542029 - fix by Terry Teague 10 Apr 02
1387: {
1388: addC('?', linelen++);
1389: }
1390:
1391: addC('>', linelen++);
1392: condFlushLine(fout, indent);
1393: }
1394:
1395: /**
1396: * Pretty print the xml declaration.
1397: * @param fout
1398: * @param indent
1399: * @param node
1400: */
1401: private void printXmlDecl(Out fout, int indent, Node node) {
1402: if (indent + linelen < this .configuration.wraplen) {
1403: wraphere = linelen;
1404: }
1405:
1406: addC('<', linelen++);
1407: addC('?', linelen++);
1408: addC('x', linelen++);
1409: addC('m', linelen++);
1410: addC('l', linelen++);
1411:
1412: printAttrs(fout, indent, node, node.attributes);
1413:
1414: if (node.end <= 0 || node.textarray[node.end - 1] != '?') // #542029 - fix by Terry Teague 10 Apr 02
1415: {
1416: addC('?', linelen++);
1417: }
1418:
1419: addC('>', linelen++);
1420:
1421: condFlushLine(fout, indent);
1422: }
1423:
1424: /**
1425: * note ASP and JSTE share <% ... %> syntax.
1426: * @param fout
1427: * @param indent
1428: * @param node
1429: */
1430: private void printAsp(Out fout, int indent, Node node) {
1431: int savewraplen = this .configuration.wraplen;
1432:
1433: // disable wrapping if so requested
1434:
1435: if (!this .configuration.wrapAsp || !this .configuration.wrapJste) {
1436: this .configuration.wraplen = 0xFFFFFF; // a very large number
1437: }
1438:
1439: addC('<', linelen++);
1440: addC('%', linelen++);
1441:
1442: printText(fout, (this .configuration.wrapAsp ? CDATA : COMMENT),
1443: indent, node.textarray, node.start, node.end);
1444:
1445: addC('%', linelen++);
1446: addC('>', linelen++);
1447: /* condFlushLine(fout, indent); */
1448: this .configuration.wraplen = savewraplen;
1449: }
1450:
1451: /**
1452: * JSTE also supports <# ... #> syntax
1453: * @param fout
1454: * @param indent
1455: * @param node
1456: */
1457: private void printJste(Out fout, int indent, Node node) {
1458: int savewraplen = this .configuration.wraplen;
1459:
1460: // disable wrapping if so requested
1461:
1462: if (!this .configuration.wrapJste) {
1463: this .configuration.wraplen = 0xFFFFFF; // a very large number
1464: }
1465:
1466: addC('<', linelen++);
1467: addC('#', linelen++);
1468:
1469: printText(fout,
1470: (this .configuration.wrapJste ? CDATA : COMMENT),
1471: indent, node.textarray, node.start, node.end);
1472:
1473: addC('#', linelen++);
1474: addC('>', linelen++);
1475: // condFlushLine(fout, indent);
1476: this .configuration.wraplen = savewraplen;
1477: }
1478:
1479: /**
1480: * PHP is based on XML processing instructions.
1481: * @param fout
1482: * @param indent
1483: * @param node
1484: */
1485: private void printPhp(Out fout, int indent, Node node) {
1486: int savewraplen = this .configuration.wraplen;
1487:
1488: // disable wrapping if so requested
1489:
1490: if (!this .configuration.wrapPhp) {
1491: this .configuration.wraplen = 0xFFFFFF; // a very large number
1492: }
1493:
1494: addC('<', linelen++);
1495: addC('?', linelen++);
1496:
1497: printText(fout, (this .configuration.wrapPhp ? CDATA : COMMENT),
1498: indent, node.textarray, node.start, node.end);
1499:
1500: addC('?', linelen++);
1501: addC('>', linelen++);
1502: // PCondFlushLine(fout, indent);
1503: this .configuration.wraplen = savewraplen;
1504: }
1505:
1506: /**
1507: * @param fout
1508: * @param indent
1509: * @param node
1510: */
1511: private void printCDATA(Out fout, int indent, Node node) {
1512: int savewraplen = this .configuration.wraplen;
1513:
1514: if (!this .configuration.indentCdata) {
1515: indent = 0;
1516: }
1517:
1518: condFlushLine(fout, indent);
1519:
1520: // disable wrapping
1521: this .configuration.wraplen = 0xFFFFFF; // a very large number
1522:
1523: addC('<', linelen++);
1524: addC('!', linelen++);
1525: addC('[', linelen++);
1526: addC('C', linelen++);
1527: addC('D', linelen++);
1528: addC('A', linelen++);
1529: addC('T', linelen++);
1530: addC('A', linelen++);
1531: addC('[', linelen++);
1532:
1533: printText(fout, COMMENT, indent, node.textarray, node.start,
1534: node.end);
1535:
1536: addC(']', linelen++);
1537: addC(']', linelen++);
1538: addC('>', linelen++);
1539: condFlushLine(fout, indent);
1540: this .configuration.wraplen = savewraplen;
1541: }
1542:
1543: /**
1544: * @param fout
1545: * @param indent
1546: * @param node
1547: */
1548: private void printSection(Out fout, int indent, Node node) {
1549: int savewraplen = this .configuration.wraplen;
1550:
1551: // disable wrapping if so requested
1552:
1553: if (!this .configuration.wrapSection) {
1554: this .configuration.wraplen = 0xFFFFFF; // a very large number
1555: }
1556:
1557: addC('<', linelen++);
1558: addC('!', linelen++);
1559: addC('[', linelen++);
1560:
1561: printText(fout, (this .configuration.wrapSection ? CDATA
1562: : COMMENT), indent, node.textarray, node.start,
1563: node.end);
1564:
1565: addC(']', linelen++);
1566: addC('>', linelen++);
1567: // PCondFlushLine(fout, indent);
1568: this .configuration.wraplen = savewraplen;
1569: }
1570:
1571: /**
1572: * Is the current node inside HEAD?
1573: * @param node Node
1574: * @return <code>true</code> if node is inside an HEAD tag
1575: */
1576: private boolean insideHead(Node node) {
1577: if (node.tag == this .configuration.tt.tagHead) {
1578: return true;
1579: }
1580:
1581: if (node.parent != null) {
1582: return insideHead(node.parent);
1583: }
1584: return false;
1585: }
1586:
1587: /**
1588: * Is text node and already ends w/ a newline? Used to pretty print CDATA/PRE text content. If it already ends on a
1589: * newline, it is not necessary to print another before printing end tag.
1590: * @param lexer Lexer
1591: * @param node text node
1592: * @return text indent
1593: */
1594: private int textEndsWithNewline(Lexer lexer, Node node) {
1595: if (node.type == Node.TEXT_NODE && node.end > node.start) {
1596: int ch, ix = node.end - 1;
1597: // Skip non-newline whitespace
1598: while (ix >= node.start
1599: && TidyUtils
1600: .toBoolean(ch = (node.textarray[ix] & 0xff))
1601: && (ch == ' ' || ch == '\t' || ch == '\r')) {
1602: --ix;
1603: }
1604:
1605: if (node.textarray[ix] == '\n') {
1606: return node.end - ix - 1; // #543262 tidy eats all memory
1607: }
1608: }
1609: return -1;
1610: }
1611:
1612: /**
1613: * Does the current node contain a CDATA section?
1614: * @param lexer Lexer
1615: * @param node Node
1616: * @return <code>true</code> if node contains a CDATA section
1617: */
1618: static boolean hasCDATA(Lexer lexer, Node node) {
1619: // Scan forward through the textarray. Since the characters we're
1620: // looking for are < 0x7f, we don't have to do any UTF-8 decoding.
1621:
1622: if (node.type != Node.TEXT_NODE) {
1623: return false;
1624: }
1625:
1626: int len = node.end - node.start + 1;
1627: String start = TidyUtils.getString(node.textarray, node.start,
1628: len);
1629:
1630: int indexOfCData = start.indexOf(CDATA_START);
1631: return indexOfCData > -1 && indexOfCData <= len;
1632: }
1633:
1634: /**
1635: * Print script and style elements. For XHTML, wrap the content as follows:
1636: *
1637: * <pre>
1638: * JavaScript:
1639: * //<![CDATA[
1640: * content
1641: * //]]>
1642: * VBScript:
1643: * '<![CDATA[
1644: * content
1645: * ']]>
1646: * CSS:
1647: * /*<![CDATA[* /
1648: * content
1649: * /*]]>* /
1650: * other:
1651: * <![CDATA[
1652: * content
1653: * ]]>
1654: * </pre>
1655: *
1656: * @param fout
1657: * @param mode
1658: * @param indent
1659: * @param lexer
1660: * @param node
1661: */
1662: private void printScriptStyle(Out fout, short mode, int indent,
1663: Lexer lexer, Node node) {
1664: Node content;
1665: String commentStart = DEFAULT_COMMENT_START;
1666: String commentEnd = DEFAULT_COMMENT_END;
1667: boolean hasCData = false;
1668: int contentIndent = -1;
1669:
1670: if (insideHead(node)) {
1671: // flushLine(fout, indent);
1672: }
1673:
1674: indent = 0;
1675:
1676: // start script
1677: printTag(lexer, fout, mode, indent, node);
1678: // flushLine(fout, indent); // extra newline
1679:
1680: if (lexer.configuration.xHTML && node.content != null) {
1681: AttVal type = node.getAttrByName("type");
1682: if (type != null) {
1683: if ("text/javascript".equalsIgnoreCase(type.value)) {
1684: commentStart = JS_COMMENT_START;
1685: commentEnd = JS_COMMENT_END;
1686: } else if ("text/css".equalsIgnoreCase(type.value)) {
1687: commentStart = CSS_COMMENT_START;
1688: commentEnd = CSS_COMMENT_END;
1689: } else if ("text/vbscript".equalsIgnoreCase(type.value)) {
1690: commentStart = VB_COMMENT_START;
1691: commentEnd = VB_COMMENT_END;
1692: }
1693: }
1694:
1695: hasCData = hasCDATA(lexer, node.content);
1696: if (!hasCData) {
1697: // disable wrapping
1698: int savewraplen = lexer.configuration.wraplen;
1699: lexer.configuration.wraplen = 0xFFFFFF; // a very large number
1700:
1701: linelen = addAsciiString(commentStart, linelen);
1702: linelen = addAsciiString(CDATA_START, linelen);
1703: linelen = addAsciiString(commentEnd, linelen);
1704: condFlushLine(fout, indent);
1705:
1706: // restore wrapping
1707: lexer.configuration.wraplen = savewraplen;
1708: }
1709: }
1710:
1711: for (content = node.content; content != null; content = content.next) {
1712: printTree(fout,
1713: (short) (mode | PREFORMATTED | NOWRAP | CDATA), 0,
1714: lexer, content);
1715:
1716: if (content.next == null) {
1717: contentIndent = textEndsWithNewline(lexer, content);
1718: }
1719:
1720: }
1721:
1722: if (contentIndent < 0) {
1723: condFlushLine(fout, indent);
1724: contentIndent = 0;
1725: }
1726:
1727: if (lexer.configuration.xHTML && node.content != null) {
1728: if (!hasCData) {
1729: // disable wrapping
1730: int ix, savewraplen = lexer.configuration.wraplen;
1731: lexer.configuration.wraplen = 0xFFFFFF; // a very large number
1732:
1733: // Add spaces to last text node to align w/ indent
1734: if (contentIndent > 0 && linelen < contentIndent) {
1735: linelen = contentIndent;
1736: }
1737: for (ix = 0; contentIndent < indent
1738: && ix < indent - contentIndent; ++ix) {
1739: addC(' ', linelen++);
1740: }
1741:
1742: linelen = addAsciiString(commentStart, linelen);
1743: linelen = addAsciiString(CDATA_END, linelen);
1744: linelen = addAsciiString(commentEnd, linelen);
1745:
1746: // restore wrapping
1747: lexer.configuration.wraplen = savewraplen;
1748: condFlushLine(fout, 0);
1749: }
1750: }
1751:
1752: printEndTag(mode, indent, node);
1753:
1754: if (!lexer.configuration.indentContent
1755: && node.next != null
1756:
1757: && !((node.tag != null && TidyUtils
1758: .toBoolean(node.tag.model & Dict.CM_INLINE))
1759:
1760: || node.type != Node.TEXT_NODE
1761:
1762: )) {
1763: flushLine(fout, indent);
1764: }
1765:
1766: flushLine(fout, indent);
1767: }
1768:
1769: /**
1770: * Should tidy indent the give tag?
1771: * @param node actual node
1772: * @return <code>true</code> if line should be indented
1773: */
1774: private boolean shouldIndent(Node node) {
1775: TagTable tt = this .configuration.tt;
1776:
1777: if (!this .configuration.indentContent) {
1778: return false;
1779: }
1780:
1781: if (this .configuration.smartIndent) {
1782: if (node.content != null
1783: && TidyUtils.toBoolean(node.tag.model
1784: & Dict.CM_NO_INDENT)) {
1785: for (node = node.content; node != null; node = node.next) {
1786: if (node.tag != null
1787: && TidyUtils.toBoolean(node.tag.model
1788: & Dict.CM_BLOCK)) {
1789: return true;
1790: }
1791: }
1792:
1793: return false;
1794: }
1795:
1796: if (TidyUtils.toBoolean(node.tag.model & Dict.CM_HEADING)) {
1797: return false;
1798: }
1799:
1800: if (node.tag == tt.tagP) {
1801: return false;
1802: }
1803:
1804: if (node.tag == tt.tagTitle) {
1805: return false;
1806: }
1807: }
1808:
1809: if (TidyUtils.toBoolean(node.tag.model
1810: & (Dict.CM_FIELD | Dict.CM_OBJECT))) {
1811: return true;
1812: }
1813:
1814: if (node.tag == tt.tagMap) {
1815: return true;
1816: }
1817:
1818: return !TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE);
1819: }
1820:
1821: /**
1822: * Print just the content of the body element. Useful when you want to reuse material from other documents.
1823: * @param fout
1824: * @param lexer
1825: * @param root
1826: * @param xml
1827: */
1828: void printBody(Out fout, Lexer lexer, Node root, boolean xml) {
1829: if (root == null) {
1830: return;
1831: }
1832:
1833: // Feature request #434940 - fix by Dave Raggett/Ignacio Vazquez-Abrams 21 Jun 01
1834: // Sebastiano Vigna <vigna@dsi.unimi.it>
1835: Node body = root.findBody(lexer.configuration.tt);
1836:
1837: if (body != null) {
1838: Node content;
1839: for (content = body.content; content != null; content = content.next) {
1840: if (xml) {
1841: printXMLTree(fout, (short) 0, 0, lexer, content);
1842: } else {
1843: printTree(fout, (short) 0, 0, lexer, content);
1844: }
1845: }
1846: }
1847: }
1848:
1849: /**
1850: * @param fout
1851: * @param mode
1852: * @param indent
1853: * @param lexer
1854: * @param node
1855: */
1856: public void printTree(Out fout, short mode, int indent,
1857: Lexer lexer, Node node) {
1858: Node content, last;
1859: TagTable tt = this .configuration.tt;
1860:
1861: if (node == null) {
1862: return;
1863: }
1864:
1865: if (node.type == Node.TEXT_NODE
1866: || (node.type == Node.CDATA_TAG && lexer.configuration.escapeCdata)) {
1867: printText(fout, mode, indent, node.textarray, node.start,
1868: node.end);
1869: } else if (node.type == Node.COMMENT_TAG) {
1870: printComment(fout, indent, node);
1871: } else if (node.type == Node.ROOT_NODE) {
1872: for (content = node.content; content != null; content = content.next) {
1873: printTree(fout, mode, indent, lexer, content);
1874: }
1875: } else if (node.type == Node.DOCTYPE_TAG) {
1876: printDocType(fout, indent, lexer, node);
1877: } else if (node.type == Node.PROC_INS_TAG) {
1878: printPI(fout, indent, node);
1879: } else if (node.type == Node.XML_DECL) {
1880: printXmlDecl(fout, indent, node);
1881: } else if (node.type == Node.CDATA_TAG) {
1882: printCDATA(fout, indent, node);
1883: } else if (node.type == Node.SECTION_TAG) {
1884: printSection(fout, indent, node);
1885: } else if (node.type == Node.ASP_TAG) {
1886: printAsp(fout, indent, node);
1887: } else if (node.type == Node.JSTE_TAG) {
1888: printJste(fout, indent, node);
1889: } else if (node.type == Node.PHP_TAG) {
1890: printPhp(fout, indent, node);
1891: } else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_EMPTY)
1892: || (node.type == Node.START_END_TAG && !configuration.xHTML)) {
1893: if (!TidyUtils.toBoolean(node.tag.model & Dict.CM_INLINE)) {
1894: condFlushLine(fout, indent);
1895: }
1896:
1897: if (node.tag == tt.tagBr && node.prev != null
1898: && node.prev.tag != tt.tagBr
1899: && this .configuration.breakBeforeBR) {
1900: flushLine(fout, indent);
1901: }
1902:
1903: if (this .configuration.makeClean && node.tag == tt.tagWbr) {
1904: printString(" ");
1905: } else {
1906: printTag(lexer, fout, mode, indent, node);
1907: }
1908:
1909: if (node.tag == tt.tagParam || node.tag == tt.tagArea) {
1910: condFlushLine(fout, indent);
1911: } else if (node.tag == tt.tagBr || node.tag == tt.tagHr) {
1912: flushLine(fout, indent);
1913: }
1914: } else {
1915: if (node.type == Node.START_END_TAG) {
1916: node.type = Node.START_TAG;
1917: }
1918:
1919: // some kind of container element
1920: if (node.tag != null
1921: && node.tag.getParser() == ParserImpl.PRE) {
1922: condFlushLine(fout, indent);
1923:
1924: indent = 0;
1925: condFlushLine(fout, indent);
1926: printTag(lexer, fout, mode, indent, node);
1927: flushLine(fout, indent);
1928:
1929: for (content = node.content; content != null; content = content.next) {
1930: printTree(fout,
1931: (short) (mode | PREFORMATTED | NOWRAP),
1932: indent, lexer, content);
1933: }
1934:
1935: condFlushLine(fout, indent);
1936: printEndTag(mode, indent, node);
1937: flushLine(fout, indent);
1938:
1939: if (!this .configuration.indentContent
1940: && node.next != null) {
1941: flushLine(fout, indent);
1942: }
1943: } else if (node.tag == tt.tagStyle
1944: || node.tag == tt.tagScript) {
1945: printScriptStyle(fout, (short) (mode | PREFORMATTED
1946: | NOWRAP | CDATA), indent, lexer, node);
1947: } else if (TidyUtils.toBoolean(node.tag.model
1948: & Dict.CM_INLINE)) {
1949: if (this .configuration.makeClean) {
1950: // discards <font> and </font> tags
1951: if (node.tag == tt.tagFont) {
1952: for (content = node.content; content != null; content = content.next) {
1953: printTree(fout, mode, indent, lexer,
1954: content);
1955: }
1956: return;
1957: }
1958:
1959: // replace <nobr> ... </nobr> by or   etc.
1960: if (node.tag == tt.tagNobr) {
1961: for (content = node.content; content != null; content = content.next) {
1962: printTree(fout, (short) (mode | NOWRAP),
1963: indent, lexer, content);
1964: }
1965: return;
1966: }
1967: }
1968:
1969: // otherwise a normal inline element
1970:
1971: printTag(lexer, fout, mode, indent, node);
1972:
1973: // indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET
1974:
1975: if (shouldIndent(node)) {
1976: condFlushLine(fout, indent);
1977: indent += this .configuration.spaces;
1978:
1979: for (content = node.content; content != null; content = content.next) {
1980: printTree(fout, mode, indent, lexer, content);
1981: }
1982:
1983: condFlushLine(fout, indent);
1984: indent -= this .configuration.spaces;
1985: condFlushLine(fout, indent);
1986: } else {
1987:
1988: for (content = node.content; content != null; content = content.next) {
1989: printTree(fout, mode, indent, lexer, content);
1990: }
1991: }
1992:
1993: printEndTag(mode, indent, node);
1994: } else {
1995: // other tags
1996: condFlushLine(fout, indent);
1997:
1998: if (this .configuration.smartIndent && node.prev != null) {
1999: flushLine(fout, indent);
2000: }
2001:
2002: // do not omit elements with attributes
2003: if (!this .configuration.hideEndTags
2004: || !(node.tag != null && TidyUtils
2005: .toBoolean(node.tag.model
2006: & Dict.CM_OMITST))
2007: || node.attributes != null) {
2008: printTag(lexer, fout, mode, indent, node);
2009:
2010: if (shouldIndent(node)) {
2011: condFlushLine(fout, indent);
2012: } else if (TidyUtils.toBoolean(node.tag.model
2013: & Dict.CM_HTML)
2014: || node.tag == tt.tagNoframes
2015: || (TidyUtils.toBoolean(node.tag.model
2016: & Dict.CM_HEAD) && !(node.tag == tt.tagTitle))) {
2017: flushLine(fout, indent);
2018: }
2019: }
2020:
2021: if (node.tag == tt.tagBody
2022: && this .configuration.burstSlides) {
2023: printSlide(fout, mode,
2024: (this .configuration.indentContent ? indent
2025: + this .configuration.spaces
2026: : indent), lexer);
2027: } else {
2028: last = null;
2029:
2030: for (content = node.content; content != null; content = content.next) {
2031: // kludge for naked text before block level tag
2032: if (last != null
2033: && !this .configuration.indentContent
2034: && last.type == Node.TEXT_NODE
2035: && content.tag != null
2036: && !TidyUtils
2037: .toBoolean(content.tag.model
2038: & Dict.CM_INLINE)) {
2039: flushLine(fout, indent);
2040: }
2041:
2042: printTree(fout, mode,
2043: (shouldIndent(node) ? indent
2044: + this .configuration.spaces
2045: : indent), lexer, content);
2046:
2047: last = content;
2048: }
2049: }
2050:
2051: // don't flush line for td and th
2052: if (shouldIndent(node)
2053: || ((TidyUtils.toBoolean(node.tag.model
2054: & Dict.CM_HTML)
2055: || node.tag == tt.tagNoframes || //
2056: (TidyUtils.toBoolean(node.tag.model
2057: & Dict.CM_HEAD) && !(node.tag == tt.tagTitle))) && //
2058: !this .configuration.hideEndTags)) {
2059: condFlushLine(fout,
2060: (this .configuration.indentContent ? indent
2061: + this .configuration.spaces
2062: : indent));
2063:
2064: if (!this .configuration.hideEndTags
2065: || !TidyUtils.toBoolean(node.tag.model
2066: & Dict.CM_OPT)) {
2067: printEndTag(mode, indent, node);
2068:
2069: // #603128 tidy adds newslines after </html> tag
2070: // Fix by Fabrizio Giustina 12-02-2004
2071: // fix is different from the one in original tidy
2072: if (!lexer.seenEndHtml) {
2073: flushLine(fout, indent);
2074: }
2075: }
2076: } else {
2077: if (!this .configuration.hideEndTags
2078: || !TidyUtils.toBoolean(node.tag.model
2079: & Dict.CM_OPT)) {
2080: printEndTag(mode, indent, node);
2081: }
2082:
2083: flushLine(fout, indent);
2084: }
2085:
2086: // FG commented out: double newlines
2087: // if (!this.configuration.indentContent
2088: // && node.next != null
2089: // && !this.configuration.hideEndTags
2090: // && (node.tag.model
2091: // & TidyUtils.toBoolean(Dict.CM_BLOCK | Dict.CM_TABLE | Dict.CM_LIST | Dict.CM_DEFLIST)))
2092: // {
2093: // flushLine(fout, indent);
2094: // }
2095: }
2096: }
2097: }
2098:
2099: /**
2100: * @param fout
2101: * @param mode
2102: * @param indent
2103: * @param lexer
2104: * @param node
2105: */
2106: public void printXMLTree(Out fout, short mode, int indent,
2107: Lexer lexer, Node node) {
2108: TagTable tt = this .configuration.tt;
2109:
2110: if (node == null) {
2111: return;
2112: }
2113:
2114: if (node.type == Node.TEXT_NODE
2115: || (node.type == Node.CDATA_TAG && lexer.configuration.escapeCdata)) {
2116: printText(fout, mode, indent, node.textarray, node.start,
2117: node.end);
2118: } else if (node.type == Node.COMMENT_TAG) {
2119: condFlushLine(fout, indent);
2120: printComment(fout, 0, node);
2121: condFlushLine(fout, 0);
2122: } else if (node.type == Node.ROOT_NODE) {
2123: Node content;
2124:
2125: for (content = node.content; content != null; content = content.next) {
2126: printXMLTree(fout, mode, indent, lexer, content);
2127: }
2128: } else if (node.type == Node.DOCTYPE_TAG) {
2129: printDocType(fout, indent, lexer, node);
2130: } else if (node.type == Node.PROC_INS_TAG) {
2131: printPI(fout, indent, node);
2132: } else if (node.type == Node.XML_DECL) {
2133: printXmlDecl(fout, indent, node);
2134: } else if (node.type == Node.CDATA_TAG) {
2135: printCDATA(fout, indent, node);
2136: } else if (node.type == Node.SECTION_TAG) {
2137: printSection(fout, indent, node);
2138: } else if (node.type == Node.ASP_TAG) {
2139: printAsp(fout, indent, node);
2140: } else if (node.type == Node.JSTE_TAG) {
2141: printJste(fout, indent, node);
2142: } else if (node.type == Node.PHP_TAG) {
2143: printPhp(fout, indent, node);
2144: } else if (TidyUtils.toBoolean(node.tag.model & Dict.CM_EMPTY)
2145: || node.type == Node.START_END_TAG
2146: && !configuration.xHTML) {
2147: condFlushLine(fout, indent);
2148: printTag(lexer, fout, mode, indent, node);
2149: // fgiust: Remove empty lines between tags in XML.
2150: //flushLine(fout, indent);
2151:
2152: // CPR: folks don't want so much vertical spacing in XML
2153: // if (node.next != null) { flushLine(fout, indent); }
2154:
2155: } else {
2156: // some kind of container element
2157: Node content;
2158: boolean mixed = false;
2159: int cindent;
2160:
2161: for (content = node.content; content != null; content = content.next) {
2162: if (content.type == Node.TEXT_NODE) {
2163: mixed = true;
2164: break;
2165: }
2166: }
2167:
2168: condFlushLine(fout, indent);
2169:
2170: if (ParserImpl.XMLPreserveWhiteSpace(node, tt)) {
2171: indent = 0;
2172: cindent = 0;
2173: mixed = false;
2174: } else if (mixed) {
2175: cindent = indent;
2176: } else {
2177: cindent = indent + this .configuration.spaces;
2178: }
2179:
2180: printTag(lexer, fout, mode, indent, node);
2181:
2182: if (!mixed && node.content != null) {
2183: flushLine(fout, indent);
2184: }
2185:
2186: for (content = node.content; content != null; content = content.next) {
2187: printXMLTree(fout, mode, cindent, lexer, content);
2188: }
2189:
2190: if (!mixed && node.content != null) {
2191: condFlushLine(fout, cindent);
2192: }
2193: printEndTag(mode, indent, node);
2194: //condFlushLine(fout, indent);
2195:
2196: // CPR: folks don't want so much vertical spacing in XML
2197: // if (node.next != null) { flushLine(fout, indent); }
2198:
2199: }
2200: }
2201:
2202: /**
2203: * Split parse tree by h2 elements and output to separate files. Counts number of h2 children (if any) belonging to
2204: * node.
2205: * @param node root node
2206: * @return number of slides (number of h2 elements)
2207: */
2208: public int countSlides(Node node) {
2209: // assume minimum of 1 slide
2210: int n = 1;
2211:
2212: TagTable tt = this .configuration.tt;
2213:
2214: //fix for [431716] avoid empty slides
2215: if (node != null && node.content != null
2216: && node.content.tag == tt.tagH2) {
2217: // "first" slide is empty, so ignore it
2218: n--;
2219: }
2220:
2221: if (node != null) {
2222: for (node = node.content; node != null; node = node.next) {
2223: if (node.tag == tt.tagH2) {
2224: ++n;
2225: }
2226: }
2227: }
2228:
2229: return n;
2230: }
2231:
2232: /**
2233: * @param fout
2234: * @param indent
2235: */
2236: private void printNavBar(Out fout, int indent) {
2237: String buf;
2238:
2239: condFlushLine(fout, indent);
2240: printString("<center><small>");
2241:
2242: NumberFormat numberFormat = NumberFormat.getInstance();
2243: numberFormat.setMinimumIntegerDigits(3);
2244:
2245: if (slide > 1) {
2246: buf = "<a href=\"slide" + numberFormat.format(slide - 1)
2247: + ".html\">previous</a> | ";
2248: // #427666 - fix by Eric Rossen 02 Aug 00
2249: printString(buf);
2250: condFlushLine(fout, indent);
2251:
2252: if (slide < count) {
2253: printString("<a href=\"slide001.html\">start</a> | ");
2254: // #427666 - fix by Eric Rossen 02 Aug 00
2255: } else {
2256: printString("<a href=\"slide001.html\">start</a>");
2257: // #427666 - fix by Eric Rossen 02 Aug 00
2258: }
2259:
2260: condFlushLine(fout, indent);
2261: }
2262:
2263: if (slide < count) {
2264: buf = "<a href=\"slide" + numberFormat.format(slide + 1)
2265: + ".html\">next</a>";
2266: // #427666 - fix by Eric Rossen 02 Aug 00
2267: printString(buf);
2268: }
2269:
2270: printString("</small></center>");
2271: condFlushLine(fout, indent);
2272: }
2273:
2274: /**
2275: * Called from printTree to print the content of a slide from the node slidecontent. On return slidecontent points
2276: * to the node starting the next slide or null. The variables slide and count are used to customise the navigation
2277: * bar.
2278: * @param fout
2279: * @param mode
2280: * @param indent
2281: * @param lexer
2282: */
2283: public void printSlide(Out fout, short mode, int indent, Lexer lexer) {
2284: Node content, last;
2285: TagTable tt = this .configuration.tt;
2286:
2287: NumberFormat numberFormat = NumberFormat.getInstance();
2288: numberFormat.setMinimumIntegerDigits(3);
2289:
2290: /* insert div for onclick handler */
2291: String s;
2292: s = "<div onclick=\"document.location='slide"
2293: + numberFormat.format(slide < count ? slide + 1 : 1)
2294: + ".html'\">";
2295: // #427666 - fix by Eric Rossen 02 Aug 00
2296: printString(s);
2297: condFlushLine(fout, indent);
2298:
2299: /* first print the h2 element and navbar */
2300: if (slidecontent != null && slidecontent.tag == tt.tagH2) {
2301: printNavBar(fout, indent);
2302:
2303: /* now print an hr after h2 */
2304:
2305: addC('<', linelen++);
2306:
2307: addC(TidyUtils.foldCase('h',
2308: this .configuration.upperCaseTags,
2309: this .configuration.xmlTags), linelen++);
2310: addC(TidyUtils.foldCase('r',
2311: this .configuration.upperCaseTags,
2312: this .configuration.xmlTags), linelen++);
2313:
2314: if (this .configuration.xmlOut) {
2315: printString(" />");
2316: } else {
2317: addC('>', linelen++);
2318: }
2319:
2320: if (this .configuration.indentContent) {
2321: condFlushLine(fout, indent);
2322: }
2323:
2324: // PrintVertSpacer(fout, indent);
2325:
2326: // condFlushLine(fout, indent);
2327:
2328: // print the h2 element
2329: printTree(fout, mode,
2330: (this .configuration.indentContent ? indent
2331: + this .configuration.spaces : indent),
2332: lexer, slidecontent);
2333:
2334: slidecontent = slidecontent.next;
2335: }
2336:
2337: // now continue until we reach the next h2
2338:
2339: last = null;
2340: content = slidecontent;
2341:
2342: for (; content != null; content = content.next) {
2343: if (content.tag == tt.tagH2) {
2344: break;
2345: }
2346:
2347: // kludge for naked text before block level tag
2348: if (last != null
2349: && !this .configuration.indentContent
2350: && last.type == Node.TEXT_NODE
2351: && content.tag != null
2352: && TidyUtils.toBoolean(content.tag.model
2353: & Dict.CM_BLOCK)) {
2354: flushLine(fout, indent);
2355: flushLine(fout, indent);
2356: }
2357:
2358: printTree(fout, mode,
2359: (this .configuration.indentContent ? indent
2360: + this .configuration.spaces : indent),
2361: lexer, content);
2362:
2363: last = content;
2364: }
2365:
2366: slidecontent = content;
2367:
2368: // now print epilog
2369:
2370: condFlushLine(fout, indent);
2371:
2372: printString("<br clear=\"all\">");
2373: condFlushLine(fout, indent);
2374:
2375: addC('<', linelen++);
2376:
2377: addC(TidyUtils.foldCase('h', this .configuration.upperCaseTags,
2378: this .configuration.xmlTags), linelen++);
2379: addC(TidyUtils.foldCase('r', this .configuration.upperCaseTags,
2380: this .configuration.xmlTags), linelen++);
2381:
2382: if (this .configuration.xmlOut) {
2383: printString(" />");
2384: } else {
2385: addC('>', linelen++);
2386: }
2387:
2388: if (this .configuration.indentContent) {
2389: condFlushLine(fout, indent);
2390: }
2391:
2392: printNavBar(fout, indent);
2393:
2394: // end tag for div
2395: printString("</div>");
2396: condFlushLine(fout, indent);
2397: }
2398:
2399: /**
2400: * Add meta element for page transition effect, this works on IE but not NS.
2401: * @param lexer
2402: * @param root
2403: * @param duration
2404: */
2405: public void addTransitionEffect(Lexer lexer, Node root,
2406: double duration) {
2407: Node head = root.findHEAD(lexer.configuration.tt);
2408: String transition;
2409:
2410: transition = "blendTrans(Duration="
2411: + (new Double(duration)).toString() + ")";
2412:
2413: if (head != null) {
2414: Node meta = lexer.inferredTag("meta");
2415: meta.addAttribute("http-equiv", "Page-Enter");
2416: meta.addAttribute("content", transition);
2417: head.insertNodeAtStart(meta);
2418: }
2419: }
2420:
2421: /**
2422: * Creates slides from h2.
2423: * @param lexer Lexer
2424: * @param root root node
2425: */
2426: public void createSlides(Lexer lexer, Node root) {
2427: Node body;
2428: String buf;
2429:
2430: NumberFormat numberFormat = NumberFormat.getInstance();
2431: numberFormat.setMinimumIntegerDigits(3);
2432:
2433: body = root.findBody(lexer.configuration.tt);
2434: count = countSlides(body);
2435: slidecontent = body.content;
2436:
2437: addTransitionEffect(lexer, root, 3.0);
2438:
2439: for (slide = 1; slide <= count; ++slide) {
2440: buf = "slide" + numberFormat.format(slide) + ".html";
2441:
2442: try {
2443: FileOutputStream fis = new FileOutputStream(buf);
2444: Out out = OutFactory.getOut(configuration, fis);
2445:
2446: printTree(out, (short) 0, 0, lexer, root);
2447: flushLine(out, 0);
2448: out.close();
2449: } catch (IOException e) {
2450: System.err.println(buf + e.toString());
2451: }
2452: }
2453:
2454: // delete superfluous slides by deleting slideN.html for N = count+1, count+2, etc.
2455: // until no such file is found.
2456:
2457: // #427666 - fix by Eric Rossen 02 Aug 00
2458: while ((new File("slide" + numberFormat.format(slide) + ".html"))
2459: .delete()) {
2460: ++slide;
2461: }
2462: }
2463:
2464: }
|