0001: /*
0002: * @(#)PPrint.java 1.11 2000/08/16
0003: *
0004: */
0005:
0006: package org.w3c.tidy;
0007:
0008: /**
0009: *
0010: * Pretty print parse tree
0011: *
0012: * (c) 1998-2000 (W3C) MIT, INRIA, Keio University
0013: * See Tidy.java for the copyright notice.
0014: * Derived from <a href="http://www.w3.org/People/Raggett/tidy">
0015: * HTML Tidy Release 4 Aug 2000</a>
0016: *
0017: * @author Dave Raggett <dsr@w3.org>
0018: * @author Andy Quick <ac.quick@sympatico.ca> (translation to Java)
0019: * @version 1.0, 1999/05/22
0020: * @version 1.0.1, 1999/05/29
0021: * @version 1.1, 1999/06/18 Java Bean
0022: * @version 1.2, 1999/07/10 Tidy Release 7 Jul 1999
0023: * @version 1.3, 1999/07/30 Tidy Release 26 Jul 1999
0024: * @version 1.4, 1999/09/04 DOM support
0025: * @version 1.5, 1999/10/23 Tidy Release 27 Sep 1999
0026: * @version 1.6, 1999/11/01 Tidy Release 22 Oct 1999
0027: * @version 1.7, 1999/12/06 Tidy Release 30 Nov 1999
0028: * @version 1.8, 2000/01/22 Tidy Release 13 Jan 2000
0029: * @version 1.9, 2000/06/03 Tidy Release 30 Apr 2000
0030: * @version 1.10, 2000/07/22 Tidy Release 8 Jul 2000
0031: * @version 1.11, 2000/08/16 Tidy Release 4 Aug 2000
0032: */
0033:
0034: /*
0035: Block-level and unknown elements are printed on
0036: new lines and their contents indented 2 spaces
0037:
0038: Inline elements are printed inline.
0039:
0040: Inline content is wrapped on spaces (except in
0041: attribute values or preformatted text, after
0042: start tags and before end tags
0043: */
0044:
0045: import java.io.FileOutputStream;
0046: import java.io.File;
0047:
0048: import java.io.IOException;
0049: import java.io.FileNotFoundException;
0050:
0051: public class PPrint {
0052:
0053: /* page transition effects */
0054:
0055: public static final short EFFECT_BLEND = -1;
0056: public static final short EFFECT_BOX_IN = 0;
0057: public static final short EFFECT_BOX_OUT = 1;
0058: public static final short EFFECT_CIRCLE_IN = 2;
0059: public static final short EFFECT_CIRCLE_OUT = 3;
0060: public static final short EFFECT_WIPE_UP = 4;
0061: public static final short EFFECT_WIPE_DOWN = 5;
0062: public static final short EFFECT_WIPE_RIGHT = 6;
0063: public static final short EFFECT_WIPE_LEFT = 7;
0064: public static final short EFFECT_VERT_BLINDS = 8;
0065: public static final short EFFECT_HORZ_BLINDS = 9;
0066: public static final short EFFECT_CHK_ACROSS = 10;
0067: public static final short EFFECT_CHK_DOWN = 11;
0068: public static final short EFFECT_RND_DISSOLVE = 12;
0069: public static final short EFFECT_SPLIT_VIRT_IN = 13;
0070: public static final short EFFECT_SPLIT_VIRT_OUT = 14;
0071: public static final short EFFECT_SPLIT_HORZ_IN = 15;
0072: public static final short EFFECT_SPLIT_HORZ_OUT = 16;
0073: public static final short EFFECT_STRIPS_LEFT_DOWN = 17;
0074: public static final short EFFECT_STRIPS_LEFT_UP = 18;
0075: public static final short EFFECT_STRIPS_RIGHT_DOWN = 19;
0076: public static final short EFFECT_STRIPS_RIGHT_UP = 20;
0077: public static final short EFFECT_RND_BARS_HORZ = 21;
0078: public static final short EFFECT_RND_BARS_VERT = 22;
0079: public static final short EFFECT_RANDOM = 23;
0080:
0081: private static final short NORMAL = 0;
0082: private static final short PREFORMATTED = 1;
0083: private static final short COMMENT = 2;
0084: private static final short ATTRIBVALUE = 4;
0085: private static final short NOWRAP = 8;
0086: private static final short CDATA = 16;
0087:
0088: private int[] linebuf = null;
0089: private int lbufsize = 0;
0090: private int linelen = 0;
0091: private int wraphere = 0;
0092: private boolean inAttVal = false;
0093: private boolean InString = false;
0094:
0095: private int slide = 0;
0096: private int count = 0;
0097: private Node slidecontent = null;
0098:
0099: private Configuration configuration;
0100:
0101: public PPrint(Configuration configuration) {
0102: this .configuration = configuration;
0103: }
0104:
0105: /*
0106: 1010 A
0107: 1011 B
0108: 1100 C
0109: 1101 D
0110: 1110 E
0111: 1111 F
0112: */
0113:
0114: /* return one less that the number of bytes used by UTF-8 char */
0115: /* str points to 1st byte, *ch initialized to 1st byte */
0116: public static int getUTF8(byte[] str, int start, MutableInteger ch) {
0117: int c, n, i, bytes;
0118:
0119: c = ((int) str[start]) & 0xFF; // Convert to unsigned.
0120:
0121: if ((c & 0xE0) == 0xC0) /* 110X XXXX two bytes */
0122: {
0123: n = c & 31;
0124: bytes = 2;
0125: } else if ((c & 0xF0) == 0xE0) /* 1110 XXXX three bytes */
0126: {
0127: n = c & 15;
0128: bytes = 3;
0129: } else if ((c & 0xF8) == 0xF0) /* 1111 0XXX four bytes */
0130: {
0131: n = c & 7;
0132: bytes = 4;
0133: } else if ((c & 0xFC) == 0xF8) /* 1111 10XX five bytes */
0134: {
0135: n = c & 3;
0136: bytes = 5;
0137: } else if ((c & 0xFE) == 0xFC) /* 1111 110X six bytes */
0138:
0139: {
0140: n = c & 1;
0141: bytes = 6;
0142: } else /* 0XXX XXXX one byte */
0143: {
0144: ch.value = c;
0145: return 0;
0146: }
0147:
0148: /* successor bytes should have the form 10XX XXXX */
0149: for (i = 1; i < bytes; ++i) {
0150: c = ((int) str[start + i]) & 0xFF; // Convert to unsigned.
0151: n = (n << 6) | (c & 0x3F);
0152: }
0153:
0154: ch.value = n;
0155: return bytes - 1;
0156: }
0157:
0158: /* store char c as UTF-8 encoded byte stream */
0159: public static int putUTF8(byte[] buf, int start, int c) {
0160: if (c < 128)
0161: buf[start++] = (byte) c;
0162: else if (c <= 0x7FF) {
0163: buf[start++] = (byte) (0xC0 | (c >> 6));
0164: buf[start++] = (byte) (0x80 | (c & 0x3F));
0165: } else if (c <= 0xFFFF) {
0166: buf[start++] = (byte) (0xE0 | (c >> 12));
0167: buf[start++] = (byte) (0x80 | ((c >> 6) & 0x3F));
0168: buf[start++] = (byte) (0x80 | (c & 0x3F));
0169: } else if (c <= 0x1FFFFF) {
0170: buf[start++] = (byte) (0xF0 | (c >> 18));
0171: buf[start++] = (byte) (0x80 | ((c >> 12) & 0x3F));
0172: buf[start++] = (byte) (0x80 | ((c >> 6) & 0x3F));
0173: buf[start++] = (byte) (0x80 | (c & 0x3F));
0174: } else {
0175: buf[start++] = (byte) (0xF8 | (c >> 24));
0176: buf[start++] = (byte) (0x80 | ((c >> 18) & 0x3F));
0177: buf[start++] = (byte) (0x80 | ((c >> 12) & 0x3F));
0178: buf[start++] = (byte) (0x80 | ((c >> 6) & 0x3F));
0179: buf[start++] = (byte) (0x80 | (c & 0x3F));
0180: }
0181:
0182: return start;
0183: }
0184:
0185: private void addC(int c, int index) {
0186: if (index + 1 >= lbufsize) {
0187: while (index + 1 >= lbufsize) {
0188: if (lbufsize == 0)
0189: lbufsize = 256;
0190: else
0191: lbufsize = lbufsize * 2;
0192: }
0193:
0194: int[] temp = new int[lbufsize];
0195: if (linebuf != null)
0196: System.arraycopy(linebuf, 0, temp, 0, index);
0197: linebuf = temp;
0198: }
0199:
0200: linebuf[index] = c;
0201: }
0202:
0203: private void wrapLine(Out fout, int indent) {
0204: int i, p, q;
0205:
0206: if (wraphere == 0)
0207: return;
0208:
0209: for (i = 0; i < indent; ++i)
0210: fout.outc((int) ' ');
0211:
0212: for (i = 0; i < wraphere; ++i)
0213: fout.outc(linebuf[i]);
0214:
0215: if (InString) {
0216: fout.outc((int) ' ');
0217: fout.outc((int) '\\');
0218: }
0219:
0220: fout.newline();
0221:
0222: if (linelen > wraphere) {
0223: p = 0;
0224:
0225: if (linebuf[wraphere] == ' ')
0226: ++wraphere;
0227:
0228: q = wraphere;
0229: addC('\0', linelen);
0230:
0231: while (true) {
0232: linebuf[p] = linebuf[q];
0233: if (linebuf[q] == 0)
0234: break;
0235: p++;
0236: q++;
0237: }
0238: linelen -= wraphere;
0239: } else
0240: linelen = 0;
0241:
0242: wraphere = 0;
0243: }
0244:
0245: private void wrapAttrVal(Out fout, int indent, boolean inString) {
0246: int i, p, q;
0247:
0248: for (i = 0; i < indent; ++i)
0249: fout.outc((int) ' ');
0250:
0251: for (i = 0; i < wraphere; ++i)
0252: fout.outc(linebuf[i]);
0253:
0254: fout.outc((int) ' ');
0255:
0256: if (inString)
0257: fout.outc((int) '\\');
0258:
0259: fout.newline();
0260:
0261: if (linelen > wraphere) {
0262: p = 0;
0263:
0264: if (linebuf[wraphere] == ' ')
0265: ++wraphere;
0266:
0267: q = wraphere;
0268: addC('\0', linelen);
0269:
0270: while (true) {
0271: linebuf[p] = linebuf[q];
0272: if (linebuf[q] == 0)
0273: break;
0274: p++;
0275: q++;
0276: }
0277: linelen -= wraphere;
0278: } else
0279: linelen = 0;
0280:
0281: wraphere = 0;
0282: }
0283:
0284: public void flushLine(Out fout, int indent) {
0285: int i;
0286:
0287: if (linelen > 0) {
0288: if (indent + linelen >= this .configuration.wraplen)
0289: wrapLine(fout, indent);
0290:
0291: if (!inAttVal || this .configuration.IndentAttributes) {
0292: for (i = 0; i < indent; ++i)
0293: fout.outc((int) ' ');
0294: }
0295:
0296: for (i = 0; i < linelen; ++i)
0297: fout.outc(linebuf[i]);
0298: }
0299:
0300: fout.newline();
0301: linelen = 0;
0302: wraphere = 0;
0303: inAttVal = false;
0304: }
0305:
0306: public void condFlushLine(Out fout, int indent) {
0307: int i;
0308:
0309: if (linelen > 0) {
0310: if (indent + linelen >= this .configuration.wraplen)
0311: wrapLine(fout, indent);
0312:
0313: if (!inAttVal || this .configuration.IndentAttributes) {
0314: for (i = 0; i < indent; ++i)
0315: fout.outc((int) ' ');
0316: }
0317:
0318: for (i = 0; i < linelen; ++i)
0319: fout.outc(linebuf[i]);
0320:
0321: fout.newline();
0322: linelen = 0;
0323: wraphere = 0;
0324: inAttVal = false;
0325: }
0326: }
0327:
0328: private void printChar(int c, short mode) {
0329: String entity;
0330:
0331: if (c == ' '
0332: && !((mode & (PREFORMATTED | COMMENT | ATTRIBVALUE)) != 0)) {
0333: /* coerce a space character to a non-breaking space */
0334: if ((mode & NOWRAP) != 0) {
0335: /* by default XML doesn't define */
0336: if (this .configuration.NumEntities
0337: || this .configuration.XmlTags) {
0338: addC('&', linelen++);
0339: addC('#', linelen++);
0340: addC('1', linelen++);
0341: addC('6', linelen++);
0342: addC('0', linelen++);
0343: addC(';', linelen++);
0344: } else /* otherwise use named entity */
0345: {
0346: addC('&', linelen++);
0347: addC('n', linelen++);
0348: addC('b', linelen++);
0349: addC('s', linelen++);
0350: addC('p', linelen++);
0351: addC(';', linelen++);
0352: }
0353: return;
0354: } else
0355: wraphere = linelen;
0356: }
0357:
0358: /* comment characters are passed raw */
0359: if ((mode & COMMENT) != 0) {
0360: addC(c, linelen++);
0361: return;
0362: }
0363:
0364: /* except in CDATA map < to < etc. */
0365: if (!((mode & CDATA) != 0)) {
0366: if (c == '<') {
0367: addC('&', linelen++);
0368: addC('l', linelen++);
0369: addC('t', linelen++);
0370: addC(';', linelen++);
0371: return;
0372: }
0373:
0374: if (c == '>') {
0375: addC('&', linelen++);
0376: addC('g', linelen++);
0377: addC('t', linelen++);
0378: addC(';', linelen++);
0379: return;
0380: }
0381:
0382: /*
0383: naked '&' chars can be left alone or
0384: quoted as & The latter is required
0385: for XML where naked '&' are illegal.
0386: */
0387: if (c == '&' && this .configuration.QuoteAmpersand) {
0388: addC('&', linelen++);
0389: addC('a', linelen++);
0390: addC('m', linelen++);
0391: addC('p', linelen++);
0392: addC(';', linelen++);
0393: return;
0394: }
0395:
0396: if (c == '"' && this .configuration.QuoteMarks) {
0397: addC('&', linelen++);
0398: addC('q', linelen++);
0399: addC('u', linelen++);
0400: addC('o', linelen++);
0401: addC('t', linelen++);
0402: addC(';', linelen++);
0403: return;
0404: }
0405:
0406: if (c == '\'' && this .configuration.QuoteMarks) {
0407: addC('&', linelen++);
0408: addC('#', linelen++);
0409: addC('3', linelen++);
0410: addC('9', linelen++);
0411: addC(';', linelen++);
0412: return;
0413: }
0414:
0415: if (c == 160
0416: && this .configuration.CharEncoding != Configuration.RAW) {
0417: if (this .configuration.QuoteNbsp) {
0418: addC('&', linelen++);
0419:
0420: if (this .configuration.NumEntities) {
0421: addC('#', linelen++);
0422: addC('1', linelen++);
0423: addC('6', linelen++);
0424: addC('0', linelen++);
0425: } else {
0426: addC('n', linelen++);
0427: addC('b', linelen++);
0428: addC('s', linelen++);
0429: addC('p', linelen++);
0430: }
0431:
0432: addC(';', linelen++);
0433: } else
0434: addC(c, linelen++);
0435:
0436: return;
0437: }
0438: }
0439:
0440: /* otherwise ISO 2022 characters are passed raw */
0441: if (this .configuration.CharEncoding == Configuration.ISO2022
0442: || this .configuration.CharEncoding == Configuration.RAW) {
0443: addC(c, linelen++);
0444: return;
0445: }
0446:
0447: /* if preformatted text, map to space */
0448: if (c == 160 && ((mode & PREFORMATTED) != 0)) {
0449: addC(' ', linelen++);
0450: return;
0451: }
0452:
0453: /*
0454: Filters from Word and PowerPoint often use smart
0455: quotes resulting in character codes between 128
0456: and 159. Unfortunately, the corresponding HTML 4.0
0457: entities for these are not widely supported. The
0458: following converts dashes and quotation marks to
0459: the nearest ASCII equivalent. My thanks to
0460: Andrzej Novosiolov for his help with this code.
0461: */
0462:
0463: if (this .configuration.MakeClean) {
0464: if (c >= 0x2013 && c <= 0x201E) {
0465: switch (c) {
0466: case 0x2013:
0467: case 0x2014:
0468: c = '-';
0469: break;
0470: case 0x2018:
0471: case 0x2019:
0472: case 0x201A:
0473: c = '\'';
0474: break;
0475: case 0x201C:
0476: case 0x201D:
0477: case 0x201E:
0478: c = '"';
0479: break;
0480: }
0481: }
0482: }
0483:
0484: /* don't map latin-1 chars to entities */
0485: if (this .configuration.CharEncoding == Configuration.LATIN1) {
0486: if (c > 255) /* multi byte chars */
0487: {
0488: if (!this .configuration.NumEntities) {
0489: entity = EntityTable.getDefaultEntityTable()
0490: .entityName((short) c);
0491: if (entity != null)
0492: entity = "&" + entity + ";";
0493: else
0494: entity = "&#" + c + ";";
0495: } else
0496: entity = "&#" + c + ";";
0497:
0498: for (int i = 0; i < entity.length(); i++)
0499: addC((int) entity.charAt(i), linelen++);
0500:
0501: return;
0502: }
0503:
0504: if (c > 126 && c < 160) {
0505: entity = "&#" + c + ";";
0506:
0507: for (int i = 0; i < entity.length(); i++)
0508: addC((int) entity.charAt(i), linelen++);
0509:
0510: return;
0511: }
0512:
0513: addC(c, linelen++);
0514: return;
0515: }
0516:
0517: /* don't map utf8 chars to entities */
0518: if (this .configuration.CharEncoding == Configuration.UTF8) {
0519: addC(c, linelen++);
0520: return;
0521: }
0522:
0523: /* use numeric entities only for XML */
0524: if (this .configuration.XmlTags) {
0525: /* if ASCII use numeric entities for chars > 127 */
0526: if (c > 127
0527: && this .configuration.CharEncoding == Configuration.ASCII) {
0528: entity = "&#" + c + ";";
0529:
0530: for (int i = 0; i < entity.length(); i++)
0531: addC((int) entity.charAt(i), linelen++);
0532:
0533: return;
0534: }
0535:
0536: /* otherwise output char raw */
0537: addC(c, linelen++);
0538: return;
0539: }
0540:
0541: /* default treatment for ASCII */
0542: if (c > 126 || (c < ' ' && c != '\t')) {
0543: if (!this .configuration.NumEntities) {
0544: entity = EntityTable.getDefaultEntityTable()
0545: .entityName((short) c);
0546: if (entity != null)
0547: entity = "&" + entity + ";";
0548: else
0549: entity = "&#" + c + ";";
0550: } else
0551: entity = "&#" + c + ";";
0552:
0553: for (int i = 0; i < entity.length(); i++)
0554: addC((int) entity.charAt(i), linelen++);
0555:
0556: return;
0557: }
0558:
0559: addC(c, linelen++);
0560: }
0561:
0562: /*
0563: The line buffer is uint not char so we can
0564: hold Unicode values unencoded. The translation
0565: to UTF-8 is deferred to the outc routine called
0566: to flush the line buffer.
0567: */
0568: private void printText(Out fout, short mode, int indent,
0569: byte[] textarray, int start, int end) {
0570: int i, c;
0571: MutableInteger ci = new MutableInteger();
0572:
0573: for (i = start; i < end; ++i) {
0574: if (indent + linelen >= this .configuration.wraplen)
0575: wrapLine(fout, indent);
0576:
0577: c = ((int) textarray[i]) & 0xFF; // Convert to unsigned.
0578:
0579: /* look for UTF-8 multibyte character */
0580: if (c > 0x7F) {
0581: i += getUTF8(textarray, i, ci);
0582: c = ci.value;
0583: }
0584:
0585: if (c == '\n') {
0586: flushLine(fout, indent);
0587: continue;
0588: }
0589:
0590: printChar(c, mode);
0591: }
0592: }
0593:
0594: private void printString(Out fout, int indent, String str) {
0595: for (int i = 0; i < str.length(); i++)
0596: addC((int) str.charAt(i), linelen++);
0597: }
0598:
0599: private void printAttrValue(Out fout, int indent, String value,
0600: int delim, boolean wrappable) {
0601: int c;
0602: MutableInteger ci = new MutableInteger();
0603: boolean wasinstring = false;
0604: byte[] valueChars = null;
0605: int i;
0606: short mode = (wrappable ? (short) (NORMAL | ATTRIBVALUE)
0607: : (short) (PREFORMATTED | ATTRIBVALUE));
0608:
0609: if (value != null) {
0610: valueChars = Lexer.getBytes(value);
0611: }
0612:
0613: /* look for ASP, Tango or PHP instructions for computed attribute value */
0614: if (valueChars != null && valueChars.length >= 5
0615: && valueChars[0] == '<') {
0616: if (valueChars[1] == '%' || valueChars[1] == '@'
0617: || (new String(valueChars, 0, 5)).equals("<?php"))
0618: mode |= CDATA;
0619: }
0620:
0621: if (delim == 0)
0622: delim = '"';
0623:
0624: addC('=', linelen++);
0625:
0626: /* don't wrap after "=" for xml documents */
0627: if (!this .configuration.XmlOut) {
0628:
0629: if (indent + linelen < this .configuration.wraplen)
0630: wraphere = linelen;
0631:
0632: if (indent + linelen >= this .configuration.wraplen)
0633: wrapLine(fout, indent);
0634:
0635: if (indent + linelen < this .configuration.wraplen)
0636: wraphere = linelen;
0637: else
0638: condFlushLine(fout, indent);
0639: }
0640:
0641: addC(delim, linelen++);
0642:
0643: if (value != null) {
0644: InString = false;
0645:
0646: i = 0;
0647: while (i < valueChars.length) {
0648: c = ((int) valueChars[i]) & 0xFF; // Convert to unsigned.
0649:
0650: if (wrappable
0651: && c == ' '
0652: && indent + linelen < this .configuration.wraplen) {
0653: wraphere = linelen;
0654: wasinstring = InString;
0655: }
0656:
0657: if (wrappable
0658: && wraphere > 0
0659: && indent + linelen >= this .configuration.wraplen)
0660: wrapAttrVal(fout, indent, wasinstring);
0661:
0662: if (c == delim) {
0663: String entity;
0664:
0665: entity = (c == '"' ? """ : "'");
0666:
0667: for (int j = 0; j < entity.length(); j++)
0668: addC(entity.charAt(j), linelen++);
0669:
0670: ++i;
0671: continue;
0672: } else if (c == '"') {
0673: if (this .configuration.QuoteMarks) {
0674: addC('&', linelen++);
0675: addC('q', linelen++);
0676: addC('u', linelen++);
0677: addC('o', linelen++);
0678: addC('t', linelen++);
0679: addC(';', linelen++);
0680: } else
0681: addC('"', linelen++);
0682:
0683: if (delim == '\'')
0684: InString = !InString;
0685:
0686: ++i;
0687: continue;
0688: } else if (c == '\'') {
0689: if (this .configuration.QuoteMarks) {
0690: addC('&', linelen++);
0691: addC('#', linelen++);
0692: addC('3', linelen++);
0693: addC('9', linelen++);
0694: addC(';', linelen++);
0695: } else
0696: addC('\'', linelen++);
0697:
0698: if (delim == '"')
0699: InString = !InString;
0700:
0701: ++i;
0702: continue;
0703: }
0704:
0705: /* look for UTF-8 multibyte character */
0706: if (c > 0x7F) {
0707: i += getUTF8(valueChars, i, ci);
0708: c = ci.value;
0709: }
0710:
0711: ++i;
0712:
0713: if (c == '\n') {
0714: flushLine(fout, indent);
0715: continue;
0716: }
0717:
0718: printChar(c, mode);
0719: }
0720: }
0721:
0722: InString = false;
0723: addC(delim, linelen++);
0724: }
0725:
0726: private void printAttribute(Out fout, int indent, Node node,
0727: AttVal attr) {
0728: String name;
0729: boolean wrappable = false;
0730:
0731: if (this .configuration.IndentAttributes) {
0732: flushLine(fout, indent);
0733: indent += this .configuration.spaces;
0734: }
0735:
0736: name = attr.attribute;
0737:
0738: if (indent + linelen >= this .configuration.wraplen)
0739: wrapLine(fout, indent);
0740:
0741: if (!this .configuration.XmlTags && !this .configuration.XmlOut
0742: && attr.dict != null) {
0743: if (AttributeTable.getDefaultAttributeTable()
0744: .isScript(name))
0745: wrappable = this .configuration.WrapScriptlets;
0746: else if (!attr.dict.nowrap
0747: && this .configuration.WrapAttVals)
0748: wrappable = true;
0749: }
0750:
0751: if (indent + linelen < this .configuration.wraplen) {
0752: wraphere = linelen;
0753: addC(' ', linelen++);
0754: } else {
0755: condFlushLine(fout, indent);
0756: addC(' ', linelen++);
0757: }
0758:
0759: for (int i = 0; i < name.length(); i++)
0760: addC((int) Lexer.foldCase(name.charAt(i),
0761: this .configuration.UpperCaseAttrs,
0762: this .configuration.XmlTags), linelen++);
0763:
0764: if (indent + linelen >= this .configuration.wraplen)
0765: wrapLine(fout, indent);
0766:
0767: if (attr.value == null) {
0768: if (this .configuration.XmlTags || this .configuration.XmlOut)
0769: printAttrValue(fout, indent, attr.attribute,
0770: attr.delim, true);
0771: else if (!attr.isBoolAttribute() && !Node.isNewNode(node))
0772: printAttrValue(fout, indent, "", attr.delim, true);
0773: else if (indent + linelen < this .configuration.wraplen)
0774: wraphere = linelen;
0775:
0776: } else
0777: printAttrValue(fout, indent, attr.value, attr.delim,
0778: wrappable);
0779: }
0780:
0781: private void printAttrs(Out fout, int indent, Node node, AttVal attr) {
0782: if (attr != null) {
0783: if (attr.next != null)
0784: printAttrs(fout, indent, node, attr.next);
0785:
0786: if (attr.attribute != null)
0787: printAttribute(fout, indent, node, attr);
0788: else if (attr.asp != null) {
0789: addC(' ', linelen++);
0790: printAsp(fout, indent, attr.asp);
0791: } else if (attr.php != null) {
0792: addC(' ', linelen++);
0793: printPhp(fout, indent, attr.php);
0794: }
0795: }
0796:
0797: /* add xml:space attribute to pre and other elements */
0798: if (configuration.XmlOut
0799: && configuration.XmlSpace
0800: && ParserImpl.XMLPreserveWhiteSpace(node,
0801: configuration.tt)
0802: && node.getAttrByName("xml:space") == null)
0803: printString(fout, indent, " xml:space=\"preserve\"");
0804: }
0805:
0806: /*
0807: Line can be wrapped immediately after inline start tag provided
0808: if follows a text node ending in a space, or it parent is an
0809: inline element that that rule applies to. This behaviour was
0810: reverse engineered from Netscape 3.0
0811: */
0812: private static boolean afterSpace(Node node) {
0813: Node prev;
0814: int c;
0815:
0816: if (node == null || node.tag == null
0817: || !((node.tag.model & Dict.CM_INLINE) != 0))
0818: return true;
0819:
0820: prev = node.prev;
0821:
0822: if (prev != null) {
0823: if (prev.type == Node.TextNode && prev.end > prev.start) {
0824: c = ((int) prev.textarray[prev.end - 1]) & 0xFF; // Convert to unsigned.
0825:
0826: if (c == 160 || c == ' ' || c == '\n')
0827: return true;
0828: }
0829:
0830: return false;
0831: }
0832:
0833: return afterSpace(node.parent);
0834: }
0835:
0836: private void printTag(Lexer lexer, Out fout, short mode,
0837: int indent, Node node) {
0838: char c;
0839: String p;
0840: TagTable tt = this .configuration.tt;
0841:
0842: addC('<', linelen++);
0843:
0844: if (node.type == Node.EndTag)
0845: addC('/', linelen++);
0846:
0847: p = node.element;
0848: for (int i = 0; i < p.length(); i++)
0849: addC((int) Lexer.foldCase(p.charAt(i),
0850: this .configuration.UpperCaseTags,
0851: this .configuration.XmlTags), linelen++);
0852:
0853: printAttrs(fout, indent, node, node.attributes);
0854:
0855: if ((this .configuration.XmlOut || lexer != null
0856: && lexer.isvoyager)
0857: && (node.type == Node.StartEndTag || (node.tag.model & Dict.CM_EMPTY) != 0)) {
0858: addC(' ', linelen++); /* compatibility hack */
0859: addC('/', linelen++);
0860: }
0861:
0862: addC('>', linelen++);
0863: ;
0864:
0865: if (node.type != Node.StartEndTag
0866: && !((mode & PREFORMATTED) != 0)) {
0867: if (indent + linelen >= this .configuration.wraplen)
0868: wrapLine(fout, indent);
0869:
0870: if (indent + linelen < this .configuration.wraplen) {
0871: /*
0872: wrap after start tag if is <br/> or if it's not
0873: inline or it is an empty tag followed by </a>
0874: */
0875: if (afterSpace(node)) {
0876: if (!((mode & NOWRAP) != 0)
0877: && (!((node.tag.model & Dict.CM_INLINE) != 0)
0878: || (node.tag == tt.tagBr) || (((node.tag.model & Dict.CM_EMPTY) != 0)
0879: && node.next == null && node.parent.tag == tt.tagA))) {
0880: wraphere = linelen;
0881: }
0882: }
0883: } else
0884: condFlushLine(fout, indent);
0885: }
0886: }
0887:
0888: private void printEndTag(Out fout, short mode, int indent, Node node) {
0889: char c;
0890: String p;
0891:
0892: /*
0893: Netscape ignores SGML standard by not ignoring a
0894: line break before </A> or </U> etc. To avoid rendering
0895: this as an underlined space, I disable line wrapping
0896: before inline end tags by the #if 0 ... #endif
0897: */
0898: if (false) {
0899: if (indent + linelen < this .configuration.wraplen
0900: && !((mode & NOWRAP) != 0))
0901: wraphere = linelen;
0902: }
0903:
0904: addC('<', linelen++);
0905: addC('/', linelen++);
0906:
0907: p = node.element;
0908: for (int i = 0; i < p.length(); i++)
0909: addC((int) Lexer.foldCase(p.charAt(i),
0910: this .configuration.UpperCaseTags,
0911: this .configuration.XmlTags), linelen++);
0912:
0913: addC('>', linelen++);
0914: }
0915:
0916: private void printComment(Out fout, int indent, Node node) {
0917: if (indent + linelen < this .configuration.wraplen)
0918: wraphere = linelen;
0919:
0920: addC('<', linelen++);
0921: addC('!', linelen++);
0922: addC('-', linelen++);
0923: addC('-', linelen++);
0924: if (false) {
0925: if (linelen < this .configuration.wraplen)
0926: wraphere = linelen;
0927: }
0928: printText(fout, COMMENT, indent, node.textarray, node.start,
0929: node.end);
0930: if (false) {
0931: if (indent + linelen < this .configuration.wraplen)
0932: wraphere = linelen;
0933: }
0934: // See Lexer.java: AQ 8Jul2000
0935: addC('-', linelen++);
0936: addC('-', linelen++);
0937: addC('>', linelen++);
0938:
0939: if (node.linebreak)
0940: flushLine(fout, indent);
0941: }
0942:
0943: private void printDocType(Out fout, int indent, Node node) {
0944: boolean q = this .configuration.QuoteMarks;
0945:
0946: this .configuration.QuoteMarks = false;
0947:
0948: if (indent + linelen < this .configuration.wraplen)
0949: wraphere = linelen;
0950:
0951: condFlushLine(fout, indent);
0952:
0953: addC('<', linelen++);
0954: addC('!', linelen++);
0955: addC('D', linelen++);
0956: addC('O', linelen++);
0957: addC('C', linelen++);
0958: addC('T', linelen++);
0959: addC('Y', linelen++);
0960: addC('P', linelen++);
0961: addC('E', linelen++);
0962: addC(' ', linelen++);
0963:
0964: if (indent + linelen < this .configuration.wraplen)
0965: wraphere = linelen;
0966:
0967: printText(fout, (short) 0, indent, node.textarray, node.start,
0968: node.end);
0969:
0970: if (linelen < this .configuration.wraplen)
0971: wraphere = linelen;
0972:
0973: addC('>', linelen++);
0974: this .configuration.QuoteMarks = q;
0975: condFlushLine(fout, indent);
0976: }
0977:
0978: private void printPI(Out fout, int indent, Node node) {
0979: if (indent + linelen < this .configuration.wraplen)
0980: wraphere = linelen;
0981:
0982: addC('<', linelen++);
0983: addC('?', linelen++);
0984:
0985: /* set CDATA to pass < and > unescaped */
0986: printText(fout, CDATA, indent, node.textarray, node.start,
0987: node.end);
0988:
0989: if (node.textarray[node.end - 1] != (byte) '?')
0990: addC('?', linelen++);
0991:
0992: addC('>', linelen++);
0993: condFlushLine(fout, indent);
0994: }
0995:
0996: /* note ASP and JSTE share <% ... %> syntax */
0997: private void printAsp(Out fout, int indent, Node node) {
0998: int savewraplen = this .configuration.wraplen;
0999:
1000: /* disable wrapping if so requested */
1001:
1002: if (!this .configuration.WrapAsp || !this .configuration.WrapJste)
1003: this .configuration.wraplen = 0xFFFFFF; /* a very large number */
1004: if (false) { //#if 0
1005: if (indent + linelen < this .configuration.wraplen)
1006: wraphere = linelen;
1007: } //#endif
1008:
1009: addC('<', linelen++);
1010: addC('%', linelen++);
1011:
1012: printText(fout, (this .configuration.WrapAsp ? CDATA : COMMENT),
1013: indent, node.textarray, node.start, node.end);
1014:
1015: addC('%', linelen++);
1016: addC('>', linelen++);
1017: /* condFlushLine(fout, indent); */
1018: this .configuration.wraplen = savewraplen;
1019: }
1020:
1021: /* JSTE also supports <# ... #> syntax */
1022: private void printJste(Out fout, int indent, Node node) {
1023: int savewraplen = this .configuration.wraplen;
1024:
1025: /* disable wrapping if so requested */
1026:
1027: if (!this .configuration.WrapJste)
1028: this .configuration.wraplen = 0xFFFFFF; /* a very large number */
1029:
1030: addC('<', linelen++);
1031: addC('#', linelen++);
1032:
1033: printText(fout,
1034: (this .configuration.WrapJste ? CDATA : COMMENT),
1035: indent, node.textarray, node.start, node.end);
1036:
1037: addC('#', linelen++);
1038: addC('>', linelen++);
1039: /* condFlushLine(fout, indent); */
1040: this .configuration.wraplen = savewraplen;
1041: }
1042:
1043: /* PHP is based on XML processing instructions */
1044: private void printPhp(Out fout, int indent, Node node) {
1045: int savewraplen = this .configuration.wraplen;
1046:
1047: /* disable wrapping if so requested */
1048:
1049: if (!this .configuration.WrapPhp)
1050: this .configuration.wraplen = 0xFFFFFF; /* a very large number */
1051:
1052: if (false) { //#if 0
1053: if (indent + linelen < this .configuration.wraplen)
1054: wraphere = linelen;
1055: } //#endif
1056: addC('<', linelen++);
1057: addC('?', linelen++);
1058:
1059: printText(fout, (this .configuration.WrapPhp ? CDATA : COMMENT),
1060: indent, node.textarray, node.start, node.end);
1061:
1062: addC('?', linelen++);
1063: addC('>', linelen++);
1064: /* PCondFlushLine(fout, indent); */
1065: this .configuration.wraplen = savewraplen;
1066: }
1067:
1068: private void printCDATA(Out fout, int indent, Node node) {
1069: int savewraplen = this .configuration.wraplen;
1070:
1071: condFlushLine(fout, indent);
1072:
1073: /* disable wrapping */
1074:
1075: this .configuration.wraplen = 0xFFFFFF; /* a very large number */
1076:
1077: addC('<', linelen++);
1078: addC('!', linelen++);
1079: addC('[', linelen++);
1080: addC('C', linelen++);
1081: addC('D', linelen++);
1082: addC('A', linelen++);
1083: addC('T', linelen++);
1084: addC('A', linelen++);
1085: addC('[', linelen++);
1086:
1087: printText(fout, COMMENT, indent, node.textarray, node.start,
1088: node.end);
1089:
1090: addC(']', linelen++);
1091: addC(']', linelen++);
1092: addC('>', linelen++);
1093: condFlushLine(fout, indent);
1094: this .configuration.wraplen = savewraplen;
1095: }
1096:
1097: private void printSection(Out fout, int indent, Node node) {
1098: int savewraplen = this .configuration.wraplen;
1099:
1100: /* disable wrapping if so requested */
1101:
1102: if (!this .configuration.WrapSection)
1103: this .configuration.wraplen = 0xFFFFFF; /* a very large number */
1104:
1105: if (false) { //#if 0
1106: if (indent + linelen < this .configuration.wraplen)
1107: wraphere = linelen;
1108: } //#endif
1109: addC('<', linelen++);
1110: addC('!', linelen++);
1111: addC('[', linelen++);
1112:
1113: printText(fout, (this .configuration.WrapSection ? CDATA
1114: : COMMENT), indent, node.textarray, node.start,
1115: node.end);
1116:
1117: addC(']', linelen++);
1118: addC('>', linelen++);
1119: /* PCondFlushLine(fout, indent); */
1120: this .configuration.wraplen = savewraplen;
1121: }
1122:
1123: private boolean shouldIndent(Node node) {
1124: TagTable tt = this .configuration.tt;
1125:
1126: if (!this .configuration.IndentContent)
1127: return false;
1128:
1129: if (this .configuration.SmartIndent) {
1130: if (node.content != null
1131: && ((node.tag.model & Dict.CM_NO_INDENT) != 0)) {
1132: for (node = node.content; node != null; node = node.next)
1133: if (node.tag != null
1134: && (node.tag.model & Dict.CM_BLOCK) != 0)
1135: return true;
1136:
1137: return false;
1138: }
1139:
1140: if ((node.tag.model & Dict.CM_HEADING) != 0)
1141: return false;
1142:
1143: if (node.tag == tt.tagP)
1144: return false;
1145:
1146: if (node.tag == tt.tagTitle)
1147: return false;
1148: }
1149:
1150: if ((node.tag.model & (Dict.CM_FIELD | Dict.CM_OBJECT)) != 0)
1151: return true;
1152:
1153: if (node.tag == tt.tagMap)
1154: return true;
1155:
1156: return !((node.tag.model & Dict.CM_INLINE) != 0);
1157: }
1158:
1159: public void printTree(Out fout, short mode, int indent,
1160: Lexer lexer, Node node) {
1161: Node content, last;
1162: TagTable tt = this .configuration.tt;
1163:
1164: if (node == null)
1165: return;
1166:
1167: if (node.type == Node.TextNode)
1168: printText(fout, mode, indent, node.textarray, node.start,
1169: node.end);
1170: else if (node.type == Node.CommentTag) {
1171: printComment(fout, indent, node);
1172: } else if (node.type == Node.RootNode) {
1173: for (content = node.content; content != null; content = content.next)
1174: printTree(fout, mode, indent, lexer, content);
1175: } else if (node.type == Node.DocTypeTag)
1176: printDocType(fout, indent, node);
1177: else if (node.type == Node.ProcInsTag)
1178: printPI(fout, indent, node);
1179: else if (node.type == Node.CDATATag)
1180: printCDATA(fout, indent, node);
1181: else if (node.type == Node.SectionTag)
1182: printSection(fout, indent, node);
1183: else if (node.type == Node.AspTag)
1184: printAsp(fout, indent, node);
1185: else if (node.type == Node.JsteTag)
1186: printJste(fout, indent, node);
1187: else if (node.type == Node.PhpTag)
1188: printPhp(fout, indent, node);
1189: else if ((node.tag.model & Dict.CM_EMPTY) != 0
1190: || node.type == Node.StartEndTag) {
1191: if (!((node.tag.model & Dict.CM_INLINE) != 0))
1192: condFlushLine(fout, indent);
1193:
1194: if (node.tag == tt.tagBr && node.prev != null
1195: && node.prev.tag != tt.tagBr
1196: && this .configuration.BreakBeforeBR)
1197: flushLine(fout, indent);
1198:
1199: if (this .configuration.MakeClean && node.tag == tt.tagWbr)
1200: printString(fout, indent, " ");
1201: else
1202: printTag(lexer, fout, mode, indent, node);
1203:
1204: if (node.tag == tt.tagParam || node.tag == tt.tagArea)
1205: condFlushLine(fout, indent);
1206: else if (node.tag == tt.tagBr || node.tag == tt.tagHr)
1207: flushLine(fout, indent);
1208: } else /* some kind of container element */
1209: {
1210: if (node.tag != null
1211: && node.tag.parser == ParserImpl.getParsePre()) {
1212: condFlushLine(fout, indent);
1213:
1214: indent = 0;
1215: condFlushLine(fout, indent);
1216: printTag(lexer, fout, mode, indent, node);
1217: flushLine(fout, indent);
1218:
1219: for (content = node.content; content != null; content = content.next)
1220: printTree(fout,
1221: (short) (mode | PREFORMATTED | NOWRAP),
1222: indent, lexer, content);
1223:
1224: condFlushLine(fout, indent);
1225: printEndTag(fout, mode, indent, node);
1226: flushLine(fout, indent);
1227:
1228: if (this .configuration.IndentContent == false
1229: && node.next != null)
1230: flushLine(fout, indent);
1231: } else if (node.tag == tt.tagStyle
1232: || node.tag == tt.tagScript) {
1233: condFlushLine(fout, indent);
1234:
1235: indent = 0;
1236: condFlushLine(fout, indent);
1237: printTag(lexer, fout, mode, indent, node);
1238: flushLine(fout, indent);
1239:
1240: for (content = node.content; content != null; content = content.next)
1241: printTree(fout, (short) (mode | PREFORMATTED
1242: | NOWRAP | CDATA), indent, lexer, content);
1243:
1244: condFlushLine(fout, indent);
1245: printEndTag(fout, mode, indent, node);
1246: flushLine(fout, indent);
1247:
1248: if (this .configuration.IndentContent == false
1249: && node.next != null)
1250: flushLine(fout, indent);
1251: } else if ((node.tag.model & Dict.CM_INLINE) != 0) {
1252: if (this .configuration.MakeClean) {
1253: /* discards <font> and </font> tags */
1254: if (node.tag == tt.tagFont) {
1255: for (content = node.content; content != null; content = content.next)
1256: printTree(fout, mode, indent, lexer,
1257: content);
1258: return;
1259: }
1260:
1261: /* replace <nobr>...</nobr> by or   etc. */
1262: if (node.tag == tt.tagNobr) {
1263: for (content = node.content; content != null; content = content.next)
1264: printTree(fout, (short) (mode | NOWRAP),
1265: indent, lexer, content);
1266: return;
1267: }
1268: }
1269:
1270: /* otherwise a normal inline element */
1271:
1272: printTag(lexer, fout, mode, indent, node);
1273:
1274: /* indent content for SELECT, TEXTAREA, MAP, OBJECT and APPLET */
1275:
1276: if (shouldIndent(node)) {
1277: condFlushLine(fout, indent);
1278: indent += this .configuration.spaces;
1279:
1280: for (content = node.content; content != null; content = content.next)
1281: printTree(fout, mode, indent, lexer, content);
1282:
1283: condFlushLine(fout, indent);
1284: indent -= this .configuration.spaces;
1285: condFlushLine(fout, indent);
1286: } else {
1287:
1288: for (content = node.content; content != null; content = content.next)
1289: printTree(fout, mode, indent, lexer, content);
1290: }
1291:
1292: printEndTag(fout, mode, indent, node);
1293: } else /* other tags */
1294: {
1295: condFlushLine(fout, indent);
1296:
1297: if (this .configuration.SmartIndent && node.prev != null)
1298: flushLine(fout, indent);
1299:
1300: if (this .configuration.HideEndTags == false
1301: || !(node.tag != null && ((node.tag.model & Dict.CM_OMITST) != 0))) {
1302: printTag(lexer, fout, mode, indent, node);
1303:
1304: if (shouldIndent(node))
1305: condFlushLine(fout, indent);
1306: else if ((node.tag.model & Dict.CM_HTML) != 0
1307: || node.tag == tt.tagNoframes
1308: || ((node.tag.model & Dict.CM_HEAD) != 0 && !(node.tag == tt.tagTitle)))
1309: flushLine(fout, indent);
1310: }
1311:
1312: if (node.tag == tt.tagBody
1313: && this .configuration.BurstSlides)
1314: printSlide(fout, mode,
1315: (this .configuration.IndentContent ? indent
1316: + this .configuration.spaces
1317: : indent), lexer);
1318: else {
1319: last = null;
1320:
1321: for (content = node.content; content != null; content = content.next) {
1322: /* kludge for naked text before block level tag */
1323: if (last != null
1324: && !this .configuration.IndentContent
1325: && last.type == Node.TextNode
1326: && content.tag != null
1327: && (content.tag.model & Dict.CM_BLOCK) != 0) {
1328: flushLine(fout, indent);
1329: flushLine(fout, indent);
1330: }
1331:
1332: printTree(fout, mode,
1333: (shouldIndent(node) ? indent
1334: + this .configuration.spaces
1335: : indent), lexer, content);
1336:
1337: last = content;
1338: }
1339: }
1340:
1341: /* don't flush line for td and th */
1342: if (shouldIndent(node)
1343: || (((node.tag.model & Dict.CM_HTML) != 0
1344: || node.tag == tt.tagNoframes || ((node.tag.model & Dict.CM_HEAD) != 0 && !(node.tag == tt.tagTitle))) && this .configuration.HideEndTags == false)) {
1345: condFlushLine(fout,
1346: (this .configuration.IndentContent ? indent
1347: + this .configuration.spaces
1348: : indent));
1349:
1350: if (this .configuration.HideEndTags == false
1351: || !((node.tag.model & Dict.CM_OPT) != 0)) {
1352: printEndTag(fout, mode, indent, node);
1353: flushLine(fout, indent);
1354: }
1355: } else {
1356: if (this .configuration.HideEndTags == false
1357: || !((node.tag.model & Dict.CM_OPT) != 0))
1358: printEndTag(fout, mode, indent, node);
1359:
1360: flushLine(fout, indent);
1361: }
1362:
1363: if (this .configuration.IndentContent == false
1364: && node.next != null
1365: && this .configuration.HideEndTags == false
1366: && (node.tag.model & (Dict.CM_BLOCK
1367: | Dict.CM_LIST | Dict.CM_DEFLIST | Dict.CM_TABLE)) != 0) {
1368: flushLine(fout, indent);
1369: }
1370: }
1371: }
1372: }
1373:
1374: public void printXMLTree(Out fout, short mode, int indent,
1375: Lexer lexer, Node node) {
1376: TagTable tt = this .configuration.tt;
1377:
1378: if (node == null)
1379: return;
1380:
1381: if (node.type == Node.TextNode) {
1382: printText(fout, mode, indent, node.textarray, node.start,
1383: node.end);
1384: } else if (node.type == Node.CommentTag) {
1385: condFlushLine(fout, indent);
1386: printComment(fout, 0, node);
1387: condFlushLine(fout, 0);
1388: } else if (node.type == Node.RootNode) {
1389: Node content;
1390:
1391: for (content = node.content; content != null; content = content.next)
1392: printXMLTree(fout, mode, indent, lexer, content);
1393: } else if (node.type == Node.DocTypeTag)
1394: printDocType(fout, indent, node);
1395: else if (node.type == Node.ProcInsTag)
1396: printPI(fout, indent, node);
1397: else if (node.type == Node.SectionTag)
1398: printSection(fout, indent, node);
1399: else if (node.type == Node.AspTag)
1400: printAsp(fout, indent, node);
1401: else if (node.type == Node.JsteTag)
1402: printJste(fout, indent, node);
1403: else if (node.type == Node.PhpTag)
1404: printPhp(fout, indent, node);
1405: else if ((node.tag.model & Dict.CM_EMPTY) != 0
1406: || node.type == Node.StartEndTag) {
1407: condFlushLine(fout, indent);
1408: printTag(lexer, fout, mode, indent, node);
1409: flushLine(fout, indent);
1410:
1411: if (node.next != null)
1412: flushLine(fout, indent);
1413: } else /* some kind of container element */
1414: {
1415: Node content;
1416: boolean mixed = false;
1417: int cindent;
1418:
1419: for (content = node.content; content != null; content = content.next) {
1420: if (content.type == Node.TextNode) {
1421: mixed = true;
1422: break;
1423: }
1424: }
1425:
1426: condFlushLine(fout, indent);
1427:
1428: if (ParserImpl.XMLPreserveWhiteSpace(node, tt)) {
1429: indent = 0;
1430: cindent = 0;
1431: mixed = false;
1432: } else if (mixed)
1433: cindent = indent;
1434: else
1435: cindent = indent + this .configuration.spaces;
1436:
1437: printTag(lexer, fout, mode, indent, node);
1438:
1439: if (!mixed)
1440: flushLine(fout, indent);
1441:
1442: for (content = node.content; content != null; content = content.next)
1443: printXMLTree(fout, mode, cindent, lexer, content);
1444:
1445: if (!mixed)
1446: condFlushLine(fout, cindent);
1447: printEndTag(fout, mode, indent, node);
1448: condFlushLine(fout, indent);
1449:
1450: if (node.next != null)
1451: flushLine(fout, indent);
1452: }
1453: }
1454:
1455: /* split parse tree by h2 elements and output to separate files */
1456:
1457: /* counts number of h2 children belonging to node */
1458: public int countSlides(Node node) {
1459: int n = 1;
1460: TagTable tt = this .configuration.tt;
1461:
1462: for (node = node.content; node != null; node = node.next)
1463: if (node.tag == tt.tagH2)
1464: ++n;
1465:
1466: return n;
1467: }
1468:
1469: /*
1470: inserts a space gif called "dot.gif" to ensure
1471: that the slide is at least n pixels high
1472: */
1473: private void printVertSpacer(Out fout, int indent) {
1474: condFlushLine(fout, indent);
1475: printString(
1476: fout,
1477: indent,
1478: "<img width=\"0\" height=\"0\" hspace=\"1\" src=\"dot.gif\" vspace=\"%d\" align=\"left\">");
1479: condFlushLine(fout, indent);
1480: }
1481:
1482: private void printNavBar(Out fout, int indent) {
1483: String buf;
1484:
1485: condFlushLine(fout, indent);
1486: printString(fout, indent, "<center><small>");
1487:
1488: if (slide > 1) {
1489: buf = "<a href=\"slide"
1490: + (new Integer(slide - 1)).toString()
1491: + ".html\">previous</a> | ";
1492: printString(fout, indent, buf);
1493: condFlushLine(fout, indent);
1494:
1495: if (slide < count)
1496: printString(fout, indent,
1497: "<a href=\"slide1.html\">start</a> | ");
1498: else
1499: printString(fout, indent,
1500: "<a href=\"slide1.html\">start</a>");
1501:
1502: condFlushLine(fout, indent);
1503: }
1504:
1505: if (slide < count) {
1506: buf = "<a href=\"slide"
1507: + (new Integer(slide + 1)).toString()
1508: + ".html\">next</a>";
1509: printString(fout, indent, buf);
1510: }
1511:
1512: printString(fout, indent, "</small></center>");
1513: condFlushLine(fout, indent);
1514: }
1515:
1516: /*
1517: Called from printTree to print the content of a slide from
1518: the node slidecontent. On return slidecontent points to the
1519: node starting the next slide or null. The variables slide
1520: and count are used to customise the navigation bar.
1521: */
1522: public void printSlide(Out fout, short mode, int indent, Lexer lexer) {
1523: Node content, last;
1524: TagTable tt = this .configuration.tt;
1525:
1526: /* insert div for onclick handler */
1527: String s;
1528: s = "<div onclick=\"document.location='slide"
1529: + (new Integer(slide < count ? slide + 1 : 1))
1530: .toString() + ".html'\">";
1531: printString(fout, indent, s);
1532: condFlushLine(fout, indent);
1533:
1534: /* first print the h2 element and navbar */
1535: if (slidecontent.tag == tt.tagH2) {
1536: printNavBar(fout, indent);
1537:
1538: /* now print an hr after h2 */
1539:
1540: addC('<', linelen++);
1541:
1542: addC((int) Lexer.foldCase('h',
1543: this .configuration.UpperCaseTags,
1544: this .configuration.XmlTags), linelen++);
1545: addC((int) Lexer.foldCase('r',
1546: this .configuration.UpperCaseTags,
1547: this .configuration.XmlTags), linelen++);
1548:
1549: if (this .configuration.XmlOut == true)
1550: printString(fout, indent, " />");
1551: else
1552: addC('>', linelen++);
1553:
1554: if (this .configuration.IndentContent == true)
1555: condFlushLine(fout, indent);
1556:
1557: /* PrintVertSpacer(fout, indent); */
1558:
1559: /*condFlushLine(fout, indent); */
1560:
1561: /* print the h2 element */
1562: printTree(fout, mode,
1563: (this .configuration.IndentContent ? indent
1564: + this .configuration.spaces : indent),
1565: lexer, slidecontent);
1566:
1567: slidecontent = slidecontent.next;
1568: }
1569:
1570: /* now continue until we reach the next h2 */
1571:
1572: last = null;
1573: content = slidecontent;
1574:
1575: for (; content != null; content = content.next) {
1576: if (content.tag == tt.tagH2)
1577: break;
1578:
1579: /* kludge for naked text before block level tag */
1580: if (last != null && !this .configuration.IndentContent
1581: && last.type == Node.TextNode
1582: && content.tag != null
1583: && (content.tag.model & Dict.CM_BLOCK) != 0) {
1584: flushLine(fout, indent);
1585: flushLine(fout, indent);
1586: }
1587:
1588: printTree(fout, mode,
1589: (this .configuration.IndentContent ? indent
1590: + this .configuration.spaces : indent),
1591: lexer, content);
1592:
1593: last = content;
1594: }
1595:
1596: slidecontent = content;
1597:
1598: /* now print epilog */
1599:
1600: condFlushLine(fout, indent);
1601:
1602: printString(fout, indent, "<br clear=\"all\">");
1603: condFlushLine(fout, indent);
1604:
1605: addC('<', linelen++);
1606:
1607: addC((int) Lexer.foldCase('h',
1608: this .configuration.UpperCaseTags,
1609: this .configuration.XmlTags), linelen++);
1610: addC((int) Lexer.foldCase('r',
1611: this .configuration.UpperCaseTags,
1612: this .configuration.XmlTags), linelen++);
1613:
1614: if (this .configuration.XmlOut == true)
1615: printString(fout, indent, " />");
1616: else
1617: addC('>', linelen++);
1618:
1619: if (this .configuration.IndentContent == true)
1620: condFlushLine(fout, indent);
1621:
1622: printNavBar(fout, indent);
1623:
1624: /* end tag for div */
1625: printString(fout, indent, "</div>");
1626: condFlushLine(fout, indent);
1627: }
1628:
1629: /*
1630: Add meta element for page transition effect, this works on IE but not NS
1631: */
1632:
1633: public void addTransitionEffect(Lexer lexer, Node root,
1634: short effect, double duration) {
1635: Node head = root.findHEAD(lexer.configuration.tt);
1636: String transition;
1637:
1638: if (0 <= effect && effect <= 23)
1639: transition = "revealTrans(Duration="
1640: + (new Double(duration)).toString()
1641: + ",Transition=" + effect + ")";
1642: else
1643: transition = "blendTrans(Duration="
1644: + (new Double(duration)).toString() + ")";
1645:
1646: if (head != null) {
1647: Node meta = lexer.inferredTag("meta");
1648: meta.addAttribute("http-equiv", "Page-Enter");
1649: meta.addAttribute("content", transition);
1650: Node.insertNodeAtStart(head, meta);
1651: }
1652: }
1653:
1654: public void createSlides(Lexer lexer, Node root) {
1655: Node body;
1656: String buf;
1657: Out out = new OutImpl();
1658:
1659: body = root.findBody(lexer.configuration.tt);
1660: count = countSlides(body);
1661: slidecontent = body.content;
1662: addTransitionEffect(lexer, root, EFFECT_BLEND, 3.0);
1663:
1664: for (slide = 1; slide <= count; ++slide) {
1665: buf = "slide" + slide + ".html";
1666: out.state = StreamIn.FSM_ASCII;
1667: out.encoding = this .configuration.CharEncoding;
1668:
1669: try {
1670: out.out = new FileOutputStream(buf);
1671: printTree(out, (short) 0, 0, lexer, root);
1672: flushLine(out, 0);
1673: out.out.close();
1674: } catch (IOException e) {
1675: System.err.println(buf + e.toString());
1676: }
1677: }
1678:
1679: /*
1680: delete superfluous slides by deleting slideN.html
1681: for N = count+1, count+2, etc. until no such file
1682: is found.
1683: */
1684:
1685: for (;;) {
1686: buf = "slide" + slide + "html";
1687:
1688: if (!(new File(buf)).delete())
1689: break;
1690:
1691: ++slide;
1692: }
1693: }
1694:
1695: }
|