0001: /*
0002: * $Id: HtmlWriter.java 2752 2007-05-15 14:58:33Z blowagie $
0003: * $Name$
0004: *
0005: * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
0006: *
0007: * The contents of this file are subject to the Mozilla Public License Version 1.1
0008: * (the "License"); you may not use this file except in compliance with the License.
0009: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the License.
0014: *
0015: * The Original Code is 'iText, a free JAVA-PDF library'.
0016: *
0017: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
0018: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
0019: * All Rights Reserved.
0020: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
0021: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
0022: *
0023: * Contributor(s): all the names of the contributors are added in the source code
0024: * where applicable.
0025: *
0026: * Alternatively, the contents of this file may be used under the terms of the
0027: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
0028: * provisions of LGPL are applicable instead of those above. If you wish to
0029: * allow use of your version of this file only under the terms of the LGPL
0030: * License and not to allow others to use your version of this file under
0031: * the MPL, indicate your decision by deleting the provisions above and
0032: * replace them with the notice and other provisions required by the LGPL.
0033: * If you do not delete the provisions above, a recipient may use your version
0034: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE
0035: *
0036: * This library is free software; you can redistribute it and/or modify it
0037: * under the terms of the MPL as stated above or under the terms of the GNU
0038: * Library General Public License as published by the Free Software Foundation;
0039: * either version 2 of the License, or any later version.
0040: *
0041: * This library is distributed in the hope that it will be useful, but WITHOUT
0042: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
0043: * FOR A PARTICULAR PURPOSE. See the GNU LIBRARY GENERAL PUBLIC LICENSE for more
0044: * details.
0045: *
0046: * If you didn't download this code from the following link, you should check if
0047: * you aren't using an obsolete version:
0048: * http://www.lowagie.com/iText/
0049: */
0050:
0051: package com.lowagie.text.html;
0052:
0053: import java.io.IOException;
0054: import java.io.OutputStream;
0055: import java.util.Date;
0056: import java.util.EmptyStackException;
0057: import java.util.Enumeration;
0058: import java.util.HashMap;
0059: import java.util.Iterator;
0060: import java.util.Properties;
0061: import java.util.Stack;
0062:
0063: import com.lowagie.text.Anchor;
0064: import com.lowagie.text.Annotation;
0065: import com.lowagie.text.BadElementException;
0066: import com.lowagie.text.Cell;
0067: import com.lowagie.text.Chunk;
0068: import com.lowagie.text.DocWriter;
0069: import com.lowagie.text.Document;
0070: import com.lowagie.text.DocumentException;
0071: import com.lowagie.text.Element;
0072: import com.lowagie.text.ExceptionConverter;
0073: import com.lowagie.text.Font;
0074: import com.lowagie.text.Header;
0075: import com.lowagie.text.HeaderFooter;
0076: import com.lowagie.text.Image;
0077: import com.lowagie.text.List;
0078: import com.lowagie.text.ListItem;
0079: import com.lowagie.text.MarkedObject;
0080: import com.lowagie.text.MarkedSection;
0081: import com.lowagie.text.Meta;
0082: import com.lowagie.text.Paragraph;
0083: import com.lowagie.text.Phrase;
0084: import com.lowagie.text.Rectangle;
0085: import com.lowagie.text.Row;
0086: import com.lowagie.text.Section;
0087: import com.lowagie.text.SimpleTable;
0088: import com.lowagie.text.Table;
0089: import com.lowagie.text.pdf.BaseFont;
0090:
0091: /**
0092: * A <CODE>DocWriter</CODE> class for HTML.
0093: * <P>
0094: * An <CODE>HtmlWriter</CODE> can be added as a <CODE>DocListener</CODE>
0095: * to a certain <CODE>Document</CODE> by getting an instance.
0096: * Every <CODE>Element</CODE> added to the original <CODE>Document</CODE>
0097: * will be written to the <CODE>OutputStream</CODE> of this <CODE>HtmlWriter</CODE>.
0098: * <P>
0099: * Example:
0100: * <BLOCKQUOTE><PRE>
0101: * // creation of the document with a certain size and certain margins
0102: * Document document = new Document(PageSize.A4, 50, 50, 50, 50);
0103: * try {
0104: * // this will write HTML to the Standard OutputStream
0105: * <STRONG>HtmlWriter.getInstance(document, System.out);</STRONG>
0106: * // this will write HTML to a file called text.html
0107: * <STRONG>HtmlWriter.getInstance(document, new FileOutputStream("text.html"));</STRONG>
0108: * // this will write HTML to for instance the OutputStream of a HttpServletResponse-object
0109: * <STRONG>HtmlWriter.getInstance(document, response.getOutputStream());</STRONG>
0110: * }
0111: * catch(DocumentException de) {
0112: * System.err.println(de.getMessage());
0113: * }
0114: * // this will close the document and all the OutputStreams listening to it
0115: * <STRONG>document.close();</CODE>
0116: * </PRE></BLOCKQUOTE>
0117: */
0118:
0119: public class HtmlWriter extends DocWriter {
0120:
0121: // static membervariables (tags)
0122:
0123: /** This is a possible HTML-tag. */
0124: public static final byte[] BEGINCOMMENT = getISOBytes("<!-- ");
0125:
0126: /** This is a possible HTML-tag. */
0127: public static final byte[] ENDCOMMENT = getISOBytes(" -->");
0128:
0129: /** This is a possible HTML-tag. */
0130: public static final String NBSP = " ";
0131:
0132: // membervariables
0133:
0134: /** This is the current font of the HTML. */
0135: protected Stack currentfont = new Stack();
0136:
0137: /** This is the standard font of the HTML. */
0138: protected Font standardfont = new Font();
0139:
0140: /** This is a path for images. */
0141: protected String imagepath = null;
0142:
0143: /** Stores the page number. */
0144: protected int pageN = 0;
0145:
0146: /** This is the textual part of a header */
0147: protected HeaderFooter header = null;
0148:
0149: /** This is the textual part of the footer */
0150: protected HeaderFooter footer = null;
0151:
0152: /** Store the markup properties of a MarkedObject. */
0153: protected Properties markup = new Properties();
0154:
0155: // constructor
0156:
0157: /**
0158: * Constructs a <CODE>HtmlWriter</CODE>.
0159: *
0160: * @param doc The <CODE>Document</CODE> that has to be written as HTML
0161: * @param os The <CODE>OutputStream</CODE> the writer has to write to.
0162: */
0163:
0164: protected HtmlWriter(Document doc, OutputStream os) {
0165: super (doc, os);
0166:
0167: document.addDocListener(this );
0168: this .pageN = document.getPageNumber();
0169: try {
0170: os.write(LT);
0171: os.write(getISOBytes(HtmlTags.HTML));
0172: os.write(GT);
0173: os.write(NEWLINE);
0174: os.write(TAB);
0175: os.write(LT);
0176: os.write(getISOBytes(HtmlTags.HEAD));
0177: os.write(GT);
0178: } catch (IOException ioe) {
0179: throw new ExceptionConverter(ioe);
0180: }
0181: }
0182:
0183: // get an instance of the HtmlWriter
0184:
0185: /**
0186: * Gets an instance of the <CODE>HtmlWriter</CODE>.
0187: *
0188: * @param document The <CODE>Document</CODE> that has to be written
0189: * @param os The <CODE>OutputStream</CODE> the writer has to write to.
0190: * @return a new <CODE>HtmlWriter</CODE>
0191: */
0192:
0193: public static HtmlWriter getInstance(Document document,
0194: OutputStream os) {
0195: return new HtmlWriter(document, os);
0196: }
0197:
0198: // implementation of the DocListener methods
0199:
0200: /**
0201: * Signals that an new page has to be started.
0202: *
0203: * @return <CODE>true</CODE> if this action succeeded, <CODE>false</CODE> if not.
0204: * @throws DocumentException when a document isn't open yet, or has been closed
0205: */
0206:
0207: public boolean newPage() {
0208: try {
0209: writeStart(HtmlTags.DIV);
0210: write(" ");
0211: write(HtmlTags.STYLE);
0212: write("=\"");
0213: writeCssProperty(Markup.CSS_KEY_PAGE_BREAK_BEFORE,
0214: Markup.CSS_VALUE_ALWAYS);
0215: write("\" /");
0216: os.write(GT);
0217: } catch (IOException ioe) {
0218: throw new ExceptionConverter(ioe);
0219: }
0220: return true;
0221: }
0222:
0223: /**
0224: * Signals that an <CODE>Element</CODE> was added to the <CODE>Document</CODE>.
0225: *
0226: * @param element a high level object that has to be translated to HTML
0227: * @return <CODE>true</CODE> if the element was added, <CODE>false</CODE> if not.
0228: * @throws DocumentException when a document isn't open yet, or has been closed
0229: */
0230:
0231: public boolean add(Element element) throws DocumentException {
0232: if (pause) {
0233: return false;
0234: }
0235: try {
0236: switch (element.type()) {
0237: case Element.HEADER:
0238: try {
0239: Header h = (Header) element;
0240: if (HtmlTags.STYLESHEET.equals(h.getName())) {
0241: writeLink(h);
0242: } else if (HtmlTags.JAVASCRIPT.equals(h.getName())) {
0243: writeJavaScript(h);
0244: } else {
0245: writeHeader(h);
0246: }
0247: } catch (ClassCastException cce) {
0248: }
0249: return true;
0250: case Element.SUBJECT:
0251: case Element.KEYWORDS:
0252: case Element.AUTHOR:
0253: Meta meta = (Meta) element;
0254: writeHeader(meta);
0255: return true;
0256: case Element.TITLE:
0257: addTabs(2);
0258: writeStart(HtmlTags.TITLE);
0259: os.write(GT);
0260: addTabs(3);
0261: write(HtmlEncoder.encode(((Meta) element).getContent()));
0262: addTabs(2);
0263: writeEnd(HtmlTags.TITLE);
0264: return true;
0265: case Element.CREATOR:
0266: writeComment("Creator: "
0267: + HtmlEncoder.encode(((Meta) element)
0268: .getContent()));
0269: return true;
0270: case Element.PRODUCER:
0271: writeComment("Producer: "
0272: + HtmlEncoder.encode(((Meta) element)
0273: .getContent()));
0274: return true;
0275: case Element.CREATIONDATE:
0276: writeComment("Creationdate: "
0277: + HtmlEncoder.encode(((Meta) element)
0278: .getContent()));
0279: return true;
0280: case Element.MARKED:
0281: if (element instanceof MarkedSection) {
0282: MarkedSection ms = (MarkedSection) element;
0283: addTabs(1);
0284: writeStart(HtmlTags.DIV);
0285: writeMarkupAttributes(ms.getMarkupAttributes());
0286: os.write(GT);
0287: MarkedObject mo = ((MarkedSection) element).title();
0288: if (mo != null) {
0289: markup = mo.getMarkupAttributes();
0290: mo.process(this );
0291: }
0292: ms.process(this );
0293: writeEnd(HtmlTags.DIV);
0294: return true;
0295: } else {
0296: MarkedObject mo = (MarkedObject) element;
0297: markup = mo.getMarkupAttributes();
0298: return mo.process(this );
0299: }
0300: default:
0301: write(element, 2);
0302: return true;
0303: }
0304: } catch (IOException ioe) {
0305: throw new ExceptionConverter(ioe);
0306: }
0307: }
0308:
0309: /**
0310: * Signals that the <CODE>Document</CODE> has been opened and that
0311: * <CODE>Elements</CODE> can be added.
0312: * <P>
0313: * The <CODE>HEAD</CODE>-section of the HTML-document is written.
0314: */
0315:
0316: public void open() {
0317: super .open();
0318: try {
0319: writeComment(Document.getVersion());
0320: writeComment("CreationDate: " + new Date().toString());
0321: addTabs(1);
0322: writeEnd(HtmlTags.HEAD);
0323: addTabs(1);
0324: writeStart(HtmlTags.BODY);
0325: if (document.leftMargin() > 0) {
0326: write(HtmlTags.LEFTMARGIN, String.valueOf(document
0327: .leftMargin()));
0328: }
0329: if (document.rightMargin() > 0) {
0330: write(HtmlTags.RIGHTMARGIN, String.valueOf(document
0331: .rightMargin()));
0332: }
0333: if (document.topMargin() > 0) {
0334: write(HtmlTags.TOPMARGIN, String.valueOf(document
0335: .topMargin()));
0336: }
0337: if (document.bottomMargin() > 0) {
0338: write(HtmlTags.BOTTOMMARGIN, String.valueOf(document
0339: .bottomMargin()));
0340: }
0341: if (pageSize.getBackgroundColor() != null) {
0342: write(HtmlTags.BACKGROUNDCOLOR, HtmlEncoder
0343: .encode(pageSize.getBackgroundColor()));
0344: }
0345: if (document.getJavaScript_onLoad() != null) {
0346: write(HtmlTags.JAVASCRIPT_ONLOAD, HtmlEncoder
0347: .encode(document.getJavaScript_onLoad()));
0348: }
0349: if (document.getJavaScript_onUnLoad() != null) {
0350: write(HtmlTags.JAVASCRIPT_ONUNLOAD, HtmlEncoder
0351: .encode(document.getJavaScript_onUnLoad()));
0352: }
0353: if (document.getHtmlStyleClass() != null) {
0354: write(Markup.HTML_ATTR_CSS_CLASS, document
0355: .getHtmlStyleClass());
0356: }
0357: os.write(GT);
0358: initHeader(); // line added by David Freels
0359: } catch (IOException ioe) {
0360: throw new ExceptionConverter(ioe);
0361: }
0362: }
0363:
0364: /**
0365: * Signals that the <CODE>Document</CODE> was closed and that no other
0366: * <CODE>Elements</CODE> will be added.
0367: */
0368:
0369: public void close() {
0370: try {
0371: initFooter(); // line added by David Freels
0372: addTabs(1);
0373: writeEnd(HtmlTags.BODY);
0374: os.write(NEWLINE);
0375: writeEnd(HtmlTags.HTML);
0376: super .close();
0377: } catch (IOException ioe) {
0378: throw new ExceptionConverter(ioe);
0379: }
0380: }
0381:
0382: // some protected methods
0383:
0384: /**
0385: * Adds the header to the top of the </CODE>Document</CODE>
0386: */
0387:
0388: protected void initHeader() {
0389: if (header != null) {
0390: try {
0391: add(header.paragraph());
0392: } catch (Exception e) {
0393: throw new ExceptionConverter(e);
0394: }
0395: }
0396: }
0397:
0398: /**
0399: * Adds the header to the top of the </CODE>Document</CODE>
0400: */
0401:
0402: protected void initFooter() {
0403: if (footer != null) {
0404: try {
0405: // Set the page number. HTML has no notion of a page, so it should always
0406: // add up to 1
0407: footer.setPageNumber(pageN + 1);
0408: add(footer.paragraph());
0409: } catch (Exception e) {
0410: throw new ExceptionConverter(e);
0411: }
0412: }
0413: }
0414:
0415: /**
0416: * Writes a Metatag in the header.
0417: *
0418: * @param meta the element that has to be written
0419: * @throws IOException
0420: */
0421:
0422: protected void writeHeader(Meta meta) throws IOException {
0423: addTabs(2);
0424: writeStart(HtmlTags.META);
0425: switch (meta.type()) {
0426: case Element.HEADER:
0427: write(HtmlTags.NAME, ((Header) meta).getName());
0428: break;
0429: case Element.SUBJECT:
0430: write(HtmlTags.NAME, HtmlTags.SUBJECT);
0431: break;
0432: case Element.KEYWORDS:
0433: write(HtmlTags.NAME, HtmlTags.KEYWORDS);
0434: break;
0435: case Element.AUTHOR:
0436: write(HtmlTags.NAME, HtmlTags.AUTHOR);
0437: break;
0438: }
0439: write(HtmlTags.CONTENT, HtmlEncoder.encode(meta.getContent()));
0440: writeEnd();
0441: }
0442:
0443: /**
0444: * Writes a link in the header.
0445: *
0446: * @param header the element that has to be written
0447: * @throws IOException
0448: */
0449:
0450: protected void writeLink(Header header) throws IOException {
0451: addTabs(2);
0452: writeStart(HtmlTags.LINK);
0453: write(HtmlTags.REL, header.getName());
0454: write(HtmlTags.TYPE, HtmlTags.TEXT_CSS);
0455: write(HtmlTags.REFERENCE, header.getContent());
0456: writeEnd();
0457: }
0458:
0459: /**
0460: * Writes a JavaScript section or, if the markup attribute HtmlTags.URL is set, a JavaScript reference in the header.
0461: *
0462: * @param header the element that has to be written
0463: * @throws IOException
0464: */
0465:
0466: protected void writeJavaScript(Header header) throws IOException {
0467: addTabs(2);
0468: writeStart(HtmlTags.SCRIPT);
0469: write(HtmlTags.LANGUAGE, HtmlTags.JAVASCRIPT);
0470: if (markup.size() > 0) {
0471: /* JavaScript reference example:
0472: *
0473: * <script language="JavaScript" src="/myPath/MyFunctions.js"/>
0474: */
0475: writeMarkupAttributes(markup);
0476: os.write(GT);
0477: writeEnd(HtmlTags.SCRIPT);
0478: } else {
0479: /* JavaScript coding convention:
0480: *
0481: * <script language="JavaScript" type="text/javascript">
0482: * <!--
0483: * // ... JavaScript methods ...
0484: * //-->
0485: * </script>
0486: */
0487: write(HtmlTags.TYPE, Markup.HTML_VALUE_JAVASCRIPT);
0488: os.write(GT);
0489: addTabs(2);
0490: write(new String(BEGINCOMMENT) + "\n");
0491: write(header.getContent());
0492: addTabs(2);
0493: write("//" + new String(ENDCOMMENT));
0494: addTabs(2);
0495: writeEnd(HtmlTags.SCRIPT);
0496: }
0497: }
0498:
0499: /**
0500: * Writes some comment.
0501: * <P>
0502: * This method writes some comment.
0503: *
0504: * @param comment the comment that has to be written
0505: * @throws IOException
0506: */
0507:
0508: protected void writeComment(String comment) throws IOException {
0509: addTabs(2);
0510: os.write(BEGINCOMMENT);
0511: write(comment);
0512: os.write(ENDCOMMENT);
0513: }
0514:
0515: // public methods
0516:
0517: /**
0518: * Changes the standardfont.
0519: *
0520: * @param standardfont The font
0521: */
0522:
0523: public void setStandardFont(Font standardfont) {
0524: this .standardfont = standardfont;
0525: }
0526:
0527: /**
0528: * Checks if a given font is the same as the font that was last used.
0529: *
0530: * @param font the font of an object
0531: * @return true if the font differs
0532: */
0533:
0534: public boolean isOtherFont(Font font) {
0535: try {
0536: Font cFont = (Font) currentfont.peek();
0537: if (cFont.compareTo(font) == 0)
0538: return false;
0539: return true;
0540: } catch (EmptyStackException ese) {
0541: if (standardfont.compareTo(font) == 0)
0542: return false;
0543: return true;
0544: }
0545: }
0546:
0547: /**
0548: * Sets the basepath for images.
0549: * <P>
0550: * This is especially useful if you add images using a file,
0551: * rather than an URL. In PDF there is no problem, since
0552: * the images are added inline, but in HTML it is sometimes
0553: * necessary to use a relative path or a special path to some
0554: * images directory.
0555: *
0556: * @param imagepath the new imagepath
0557: */
0558:
0559: public void setImagepath(String imagepath) {
0560: this .imagepath = imagepath;
0561: }
0562:
0563: /**
0564: * Resets the imagepath.
0565: */
0566:
0567: public void resetImagepath() {
0568: imagepath = null;
0569: }
0570:
0571: /**
0572: * Changes the header of this document.
0573: *
0574: * @param header the new header
0575: */
0576:
0577: public void setHeader(HeaderFooter header) {
0578: this .header = header;
0579: }
0580:
0581: /**
0582: * Changes the footer of this document.
0583: *
0584: * @param footer the new footer
0585: */
0586:
0587: public void setFooter(HeaderFooter footer) {
0588: this .footer = footer;
0589: }
0590:
0591: /**
0592: * Signals that a <CODE>String</CODE> was added to the <CODE>Document</CODE>.
0593: *
0594: * @param string a String to add to the HTML
0595: * @return <CODE>true</CODE> if the string was added, <CODE>false</CODE> if not.
0596: * @throws DocumentException when a document isn't open yet, or has been closed
0597: */
0598:
0599: public boolean add(String string) {
0600: if (pause) {
0601: return false;
0602: }
0603: try {
0604: write(string);
0605: return true;
0606: } catch (IOException ioe) {
0607: throw new ExceptionConverter(ioe);
0608: }
0609: }
0610:
0611: /**
0612: * Writes the HTML representation of an element.
0613: *
0614: * @param element the element
0615: * @param indent the indentation
0616: * @throws IOException
0617: */
0618:
0619: protected void write(Element element, int indent)
0620: throws IOException {
0621: Properties styleAttributes = null;
0622: switch (element.type()) {
0623: case Element.MARKED: {
0624: try {
0625: add(element);
0626: } catch (DocumentException e) {
0627: e.printStackTrace();
0628: }
0629: return;
0630: }
0631: case Element.CHUNK: {
0632: Chunk chunk = (Chunk) element;
0633: // if the chunk contains an image, return the image representation
0634: Image image = chunk.getImage();
0635: if (image != null) {
0636: write(image, indent);
0637: return;
0638: }
0639:
0640: if (chunk.isEmpty())
0641: return;
0642: HashMap attributes = chunk.getAttributes();
0643: if (attributes != null
0644: && attributes.get(Chunk.NEWPAGE) != null) {
0645: return;
0646: }
0647: boolean tag = isOtherFont(chunk.getFont())
0648: || markup.size() > 0;
0649: if (tag) {
0650: // start span tag
0651: addTabs(indent);
0652: writeStart(HtmlTags.SPAN);
0653: if (isOtherFont(chunk.getFont())) {
0654: write(chunk.getFont(), null);
0655: }
0656: writeMarkupAttributes(markup);
0657: os.write(GT);
0658: }
0659: if (attributes != null
0660: && attributes.get(Chunk.SUBSUPSCRIPT) != null) {
0661: // start sup or sub tag
0662: if (((Float) attributes.get(Chunk.SUBSUPSCRIPT))
0663: .floatValue() > 0) {
0664: writeStart(HtmlTags.SUP);
0665: } else {
0666: writeStart(HtmlTags.SUB);
0667: }
0668: os.write(GT);
0669: }
0670: // contents
0671: write(HtmlEncoder.encode(chunk.getContent()));
0672: if (attributes != null
0673: && attributes.get(Chunk.SUBSUPSCRIPT) != null) {
0674: // end sup or sub tag
0675: os.write(LT);
0676: os.write(FORWARD);
0677: if (((Float) attributes.get(Chunk.SUBSUPSCRIPT))
0678: .floatValue() > 0) {
0679: write(HtmlTags.SUP);
0680: } else {
0681: write(HtmlTags.SUB);
0682: }
0683: os.write(GT);
0684: }
0685: if (tag) {
0686: // end tag
0687: writeEnd(Markup.HTML_TAG_SPAN);
0688: }
0689: return;
0690: }
0691: case Element.PHRASE: {
0692: Phrase phrase = (Phrase) element;
0693: styleAttributes = new Properties();
0694: if (phrase.hasLeading())
0695: styleAttributes.setProperty(Markup.CSS_KEY_LINEHEIGHT,
0696: phrase.getLeading() + "pt");
0697:
0698: // start tag
0699: addTabs(indent);
0700: writeStart(Markup.HTML_TAG_SPAN);
0701: writeMarkupAttributes(markup);
0702: write(phrase.getFont(), styleAttributes);
0703: os.write(GT);
0704: currentfont.push(phrase.getFont());
0705: // contents
0706: for (Iterator i = phrase.iterator(); i.hasNext();) {
0707: write((Element) i.next(), indent + 1);
0708: }
0709: // end tag
0710: addTabs(indent);
0711: writeEnd(Markup.HTML_TAG_SPAN);
0712: currentfont.pop();
0713: return;
0714: }
0715: case Element.ANCHOR: {
0716: Anchor anchor = (Anchor) element;
0717: styleAttributes = new Properties();
0718: if (anchor.hasLeading())
0719: styleAttributes.setProperty(Markup.CSS_KEY_LINEHEIGHT,
0720: anchor.getLeading() + "pt");
0721:
0722: // start tag
0723: addTabs(indent);
0724: writeStart(HtmlTags.ANCHOR);
0725: if (anchor.getName() != null) {
0726: write(HtmlTags.NAME, anchor.getName());
0727: }
0728: if (anchor.getReference() != null) {
0729: write(HtmlTags.REFERENCE, anchor.getReference());
0730: }
0731: writeMarkupAttributes(markup);
0732: write(anchor.getFont(), styleAttributes);
0733: os.write(GT);
0734: currentfont.push(anchor.getFont());
0735: // contents
0736: for (Iterator i = anchor.iterator(); i.hasNext();) {
0737: write((Element) i.next(), indent + 1);
0738: }
0739: // end tag
0740: addTabs(indent);
0741: writeEnd(HtmlTags.ANCHOR);
0742: currentfont.pop();
0743: return;
0744: }
0745: case Element.PARAGRAPH: {
0746: Paragraph paragraph = (Paragraph) element;
0747: styleAttributes = new Properties();
0748: if (paragraph.hasLeading())
0749: styleAttributes.setProperty(Markup.CSS_KEY_LINEHEIGHT,
0750: paragraph.getTotalLeading() + "pt");
0751: // start tag
0752: addTabs(indent);
0753: writeStart(HtmlTags.DIV);
0754: writeMarkupAttributes(markup);
0755: String alignment = HtmlEncoder.getAlignment(paragraph
0756: .getAlignment());
0757: if (!"".equals(alignment)) {
0758: write(HtmlTags.ALIGN, alignment);
0759: }
0760: write(paragraph.getFont(), styleAttributes);
0761: os.write(GT);
0762: currentfont.push(paragraph.getFont());
0763: // contents
0764: for (Iterator i = paragraph.iterator(); i.hasNext();) {
0765: write((Element) i.next(), indent + 1);
0766: }
0767: // end tag
0768: addTabs(indent);
0769: writeEnd(HtmlTags.DIV);
0770: currentfont.pop();
0771: return;
0772: }
0773: case Element.SECTION:
0774: case Element.CHAPTER: {
0775: // part of the start tag + contents
0776: writeSection((Section) element, indent);
0777: return;
0778: }
0779: case Element.LIST: {
0780: List list = (List) element;
0781: // start tag
0782: addTabs(indent);
0783: if (list.isNumbered()) {
0784: writeStart(HtmlTags.ORDEREDLIST);
0785: } else {
0786: writeStart(HtmlTags.UNORDEREDLIST);
0787: }
0788: writeMarkupAttributes(markup);
0789: os.write(GT);
0790: // contents
0791: for (Iterator i = list.getItems().iterator(); i.hasNext();) {
0792: write((Element) i.next(), indent + 1);
0793: }
0794: // end tag
0795: addTabs(indent);
0796: if (list.isNumbered()) {
0797: writeEnd(HtmlTags.ORDEREDLIST);
0798: } else {
0799: writeEnd(HtmlTags.UNORDEREDLIST);
0800: }
0801: return;
0802: }
0803: case Element.LISTITEM: {
0804: ListItem listItem = (ListItem) element;
0805: styleAttributes = new Properties();
0806: if (listItem.hasLeading())
0807: styleAttributes.setProperty(Markup.CSS_KEY_LINEHEIGHT,
0808: listItem.getTotalLeading() + "pt");
0809:
0810: // start tag
0811: addTabs(indent);
0812: writeStart(HtmlTags.LISTITEM);
0813: writeMarkupAttributes(markup);
0814: write(listItem.getFont(), styleAttributes);
0815: os.write(GT);
0816: currentfont.push(listItem.getFont());
0817: // contents
0818: for (Iterator i = listItem.iterator(); i.hasNext();) {
0819: write((Element) i.next(), indent + 1);
0820: }
0821: // end tag
0822: addTabs(indent);
0823: writeEnd(HtmlTags.LISTITEM);
0824: currentfont.pop();
0825: return;
0826: }
0827: case Element.CELL: {
0828: Cell cell = (Cell) element;
0829:
0830: // start tag
0831: addTabs(indent);
0832: if (cell.isHeader()) {
0833: writeStart(HtmlTags.HEADERCELL);
0834: } else {
0835: writeStart(HtmlTags.CELL);
0836: }
0837: writeMarkupAttributes(markup);
0838: if (cell.getBorderWidth() != Rectangle.UNDEFINED) {
0839: write(HtmlTags.BORDERWIDTH, String.valueOf(cell
0840: .getBorderWidth()));
0841: }
0842: if (cell.getBorderColor() != null) {
0843: write(HtmlTags.BORDERCOLOR, HtmlEncoder.encode(cell
0844: .getBorderColor()));
0845: }
0846: if (cell.getBackgroundColor() != null) {
0847: write(HtmlTags.BACKGROUNDCOLOR, HtmlEncoder.encode(cell
0848: .getBackgroundColor()));
0849: }
0850: String alignment = HtmlEncoder.getAlignment(cell
0851: .getHorizontalAlignment());
0852: if (!"".equals(alignment)) {
0853: write(HtmlTags.HORIZONTALALIGN, alignment);
0854: }
0855: alignment = HtmlEncoder.getAlignment(cell
0856: .getVerticalAlignment());
0857: if (!"".equals(alignment)) {
0858: write(HtmlTags.VERTICALALIGN, alignment);
0859: }
0860: if (cell.getWidthAsString() != null) {
0861: write(HtmlTags.WIDTH, cell.getWidthAsString());
0862: }
0863: if (cell.getColspan() != 1) {
0864: write(HtmlTags.COLSPAN, String.valueOf(cell
0865: .getColspan()));
0866: }
0867: if (cell.getRowspan() != 1) {
0868: write(HtmlTags.ROWSPAN, String.valueOf(cell
0869: .getRowspan()));
0870: }
0871: if (cell.getMaxLines() == 1) {
0872: write(HtmlTags.NOWRAP, String.valueOf(true));
0873: }
0874: os.write(GT);
0875: // contents
0876: if (cell.isEmpty()) {
0877: write(NBSP);
0878: } else {
0879: for (Iterator i = cell.getElements(); i.hasNext();) {
0880: write((Element) i.next(), indent + 1);
0881: }
0882: }
0883: // end tag
0884: addTabs(indent);
0885: if (cell.isHeader()) {
0886: writeEnd(HtmlTags.HEADERCELL);
0887: } else {
0888: writeEnd(HtmlTags.CELL);
0889: }
0890: return;
0891: }
0892: case Element.ROW: {
0893: Row row = (Row) element;
0894:
0895: // start tag
0896: addTabs(indent);
0897: writeStart(HtmlTags.ROW);
0898: writeMarkupAttributes(markup);
0899: os.write(GT);
0900: // contents
0901: Element cell;
0902: for (int i = 0; i < row.getColumns(); i++) {
0903: if ((cell = (Element) row.getCell(i)) != null) {
0904: write(cell, indent + 1);
0905: }
0906: }
0907: // end tag
0908: addTabs(indent);
0909: writeEnd(HtmlTags.ROW);
0910: return;
0911: }
0912: case Element.TABLE: {
0913: Table table;
0914: try {
0915: table = (Table) element;
0916: } catch (ClassCastException cce) {
0917: try {
0918: table = ((SimpleTable) element).createTable();
0919: } catch (BadElementException e) {
0920: throw new ExceptionConverter(e);
0921: }
0922: }
0923: table.complete();
0924: // start tag
0925: addTabs(indent);
0926: writeStart(HtmlTags.TABLE);
0927: writeMarkupAttributes(markup);
0928: os.write(SPACE);
0929: write(HtmlTags.WIDTH);
0930: os.write(EQUALS);
0931: os.write(QUOTE);
0932: write(String.valueOf(table.getWidth()));
0933: if (!table.isLocked()) {
0934: write("%");
0935: }
0936: os.write(QUOTE);
0937: String alignment = HtmlEncoder.getAlignment(table
0938: .getAlignment());
0939: if (!"".equals(alignment)) {
0940: write(HtmlTags.ALIGN, alignment);
0941: }
0942: write(HtmlTags.CELLPADDING, String.valueOf(table
0943: .getPadding()));
0944: write(HtmlTags.CELLSPACING, String.valueOf(table
0945: .getSpacing()));
0946: if (table.getBorderWidth() != Rectangle.UNDEFINED) {
0947: write(HtmlTags.BORDERWIDTH, String.valueOf(table
0948: .getBorderWidth()));
0949: }
0950: if (table.getBorderColor() != null) {
0951: write(HtmlTags.BORDERCOLOR, HtmlEncoder.encode(table
0952: .getBorderColor()));
0953: }
0954: if (table.getBackgroundColor() != null) {
0955: write(HtmlTags.BACKGROUNDCOLOR, HtmlEncoder
0956: .encode(table.getBackgroundColor()));
0957: }
0958: os.write(GT);
0959: // contents
0960: Row row;
0961: for (Iterator iterator = table.iterator(); iterator
0962: .hasNext();) {
0963: row = (Row) iterator.next();
0964: write(row, indent + 1);
0965: }
0966: // end tag
0967: addTabs(indent);
0968: writeEnd(HtmlTags.TABLE);
0969: return;
0970: }
0971: case Element.ANNOTATION: {
0972: Annotation annotation = (Annotation) element;
0973: writeComment(annotation.title() + ": "
0974: + annotation.content());
0975: return;
0976: }
0977: case Element.IMGRAW:
0978: case Element.JPEG:
0979: case Element.IMGTEMPLATE: {
0980: Image image = (Image) element;
0981: if (image.getUrl() == null) {
0982: return;
0983: }
0984:
0985: // start tag
0986: addTabs(indent);
0987: writeStart(HtmlTags.IMAGE);
0988: String path = image.getUrl().toString();
0989: if (imagepath != null) {
0990: if (path.indexOf('/') > 0) {
0991: path = imagepath
0992: + path.substring(path.lastIndexOf('/') + 1);
0993: } else {
0994: path = imagepath + path;
0995: }
0996: }
0997: write(HtmlTags.URL, path);
0998: if ((image.getAlignment() & Image.RIGHT) > 0) {
0999: write(HtmlTags.ALIGN, HtmlTags.ALIGN_RIGHT);
1000: } else if ((image.getAlignment() & Image.MIDDLE) > 0) {
1001: write(HtmlTags.ALIGN, HtmlTags.ALIGN_MIDDLE);
1002: } else {
1003: write(HtmlTags.ALIGN, HtmlTags.ALIGN_LEFT);
1004: }
1005: if (image.getAlt() != null) {
1006: write(HtmlTags.ALT, image.getAlt());
1007: }
1008: write(HtmlTags.PLAINWIDTH, String.valueOf(image
1009: .getScaledWidth()));
1010: write(HtmlTags.PLAINHEIGHT, String.valueOf(image
1011: .getScaledHeight()));
1012: writeMarkupAttributes(markup);
1013: writeEnd();
1014: return;
1015: }
1016:
1017: default:
1018: return;
1019: }
1020: }
1021:
1022: /**
1023: * Writes the HTML representation of a section.
1024: *
1025: * @param section the section to write
1026: * @param indent the indentation
1027: * @throws IOException
1028: */
1029:
1030: protected void writeSection(Section section, int indent)
1031: throws IOException {
1032: if (section.getTitle() != null) {
1033: int depth = section.getDepth() - 1;
1034: if (depth > 5) {
1035: depth = 5;
1036: }
1037: Properties styleAttributes = new Properties();
1038: if (section.getTitle().hasLeading())
1039: styleAttributes.setProperty(Markup.CSS_KEY_LINEHEIGHT,
1040: section.getTitle().getTotalLeading() + "pt");
1041: // start tag
1042: addTabs(indent);
1043: writeStart(HtmlTags.H[depth]);
1044: write(section.getTitle().getFont(), styleAttributes);
1045: String alignment = HtmlEncoder.getAlignment(section
1046: .getTitle().getAlignment());
1047: if (!"".equals(alignment)) {
1048: write(HtmlTags.ALIGN, alignment);
1049: }
1050: writeMarkupAttributes(markup);
1051: os.write(GT);
1052: currentfont.push(section.getTitle().getFont());
1053: // contents
1054: for (Iterator i = section.getTitle().iterator(); i
1055: .hasNext();) {
1056: write((Element) i.next(), indent + 1);
1057: }
1058: // end tag
1059: addTabs(indent);
1060: writeEnd(HtmlTags.H[depth]);
1061: currentfont.pop();
1062: }
1063: for (Iterator i = section.iterator(); i.hasNext();) {
1064: write((Element) i.next(), indent);
1065: }
1066: }
1067:
1068: /**
1069: * Writes the representation of a <CODE>Font</CODE>.
1070: *
1071: * @param font a <CODE>Font</CODE>
1072: * @param styleAttributes the style of the font
1073: * @throws IOException
1074: */
1075:
1076: protected void write(Font font, Properties styleAttributes)
1077: throws IOException {
1078: if (font == null || !isOtherFont(font) /* || styleAttributes == null*/)
1079: return;
1080: write(" ");
1081: write(HtmlTags.STYLE);
1082: write("=\"");
1083: if (styleAttributes != null) {
1084: String key;
1085: for (Enumeration e = styleAttributes.propertyNames(); e
1086: .hasMoreElements();) {
1087: key = (String) e.nextElement();
1088: writeCssProperty(key, styleAttributes.getProperty(key));
1089: }
1090: }
1091: if (isOtherFont(font)) {
1092: writeCssProperty(Markup.CSS_KEY_FONTFAMILY, font
1093: .getFamilyname());
1094:
1095: if (font.getSize() != Font.UNDEFINED) {
1096: writeCssProperty(Markup.CSS_KEY_FONTSIZE, font
1097: .getSize()
1098: + "pt");
1099: }
1100: if (font.getColor() != null) {
1101: writeCssProperty(Markup.CSS_KEY_COLOR, HtmlEncoder
1102: .encode(font.getColor()));
1103: }
1104:
1105: int fontstyle = font.getStyle();
1106: BaseFont bf = font.getBaseFont();
1107: if (bf != null) {
1108: String ps = bf.getPostscriptFontName().toLowerCase();
1109: if (ps.indexOf("bold") >= 0) {
1110: if (fontstyle == Font.UNDEFINED)
1111: fontstyle = 0;
1112: fontstyle |= Font.BOLD;
1113: }
1114: if (ps.indexOf("italic") >= 0
1115: || ps.indexOf("oblique") >= 0) {
1116: if (fontstyle == Font.UNDEFINED)
1117: fontstyle = 0;
1118: fontstyle |= Font.ITALIC;
1119: }
1120: }
1121: if (fontstyle != Font.UNDEFINED && fontstyle != Font.NORMAL) {
1122: switch (fontstyle & Font.BOLDITALIC) {
1123: case Font.BOLD:
1124: writeCssProperty(Markup.CSS_KEY_FONTWEIGHT,
1125: Markup.CSS_VALUE_BOLD);
1126: break;
1127: case Font.ITALIC:
1128: writeCssProperty(Markup.CSS_KEY_FONTSTYLE,
1129: Markup.CSS_VALUE_ITALIC);
1130: break;
1131: case Font.BOLDITALIC:
1132: writeCssProperty(Markup.CSS_KEY_FONTWEIGHT,
1133: Markup.CSS_VALUE_BOLD);
1134: writeCssProperty(Markup.CSS_KEY_FONTSTYLE,
1135: Markup.CSS_VALUE_ITALIC);
1136: break;
1137: }
1138:
1139: // CSS only supports one decoration tag so if both are specified
1140: // only one of the two will display
1141: if ((fontstyle & Font.UNDERLINE) > 0) {
1142: writeCssProperty(Markup.CSS_KEY_TEXTDECORATION,
1143: Markup.CSS_VALUE_UNDERLINE);
1144: }
1145: if ((fontstyle & Font.STRIKETHRU) > 0) {
1146: writeCssProperty(Markup.CSS_KEY_TEXTDECORATION,
1147: Markup.CSS_VALUE_LINETHROUGH);
1148: }
1149: }
1150: }
1151: write("\"");
1152: }
1153:
1154: /**
1155: * Writes out a CSS property.
1156: * @param prop a CSS property
1157: * @param value the value of the CSS property
1158: * @throws IOException
1159: */
1160: protected void writeCssProperty(String prop, String value)
1161: throws IOException {
1162: write(new StringBuffer(prop).append(": ").append(value).append(
1163: "; ").toString());
1164: }
1165: }
|