/* * $Id$ * * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie. * * The contents of this file are subject to the Mozilla Public License Version 1.1 * (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the License. * * The Original Code is 'iText, a free JAVA-PDF library'. * * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. * All Rights Reserved. * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. * * Contributor(s): all the names of the contributors are added in the source code * where applicable. * * Alternatively, the contents of this file may be used under the terms of the * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the * provisions of LGPL are applicable instead of those above. If you wish to * allow use of your version of this file only under the terms of the LGPL * License and not to allow others to use your version of this file under * the MPL, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the LGPL. * If you do not delete the provisions above, a recipient may use your version * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE * * This library is free software; you can redistribute it and/or modify it * under the terms of the MPL as stated above or under the terms of the GNU * Library General Public License as published by the Free Software Foundation; * either version 2 of the License, or any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU LIBRARY GENERAL PUBLIC LICENSE for more * details. * * If you didn't download this code from the following link, you should check if * you aren't using an obsolete version: * http://www.lowagie.com/iText/ */ package com.lowagie.text.html; import java.io.IOException; import java.io.OutputStream; import java.util.Date; import java.util.EmptyStackException; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Properties; import java.util.Stack; import com.lowagie.text.error_messages.MessageLocalization; import com.lowagie.text.Anchor; import com.lowagie.text.Annotation; import com.lowagie.text.BadElementException; import com.lowagie.text.Cell; import com.lowagie.text.Chunk; import com.lowagie.text.DocWriter; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.Element; import com.lowagie.text.ExceptionConverter; import com.lowagie.text.Font; import com.lowagie.text.Header; import com.lowagie.text.HeaderFooter; import com.lowagie.text.Image; import com.lowagie.text.List; import com.lowagie.text.ListItem; import com.lowagie.text.MarkedObject; import com.lowagie.text.MarkedSection; import com.lowagie.text.Meta; import com.lowagie.text.Paragraph; import com.lowagie.text.Phrase; import com.lowagie.text.Rectangle; import com.lowagie.text.Row; import com.lowagie.text.Section; import com.lowagie.text.SimpleTable; import com.lowagie.text.Table; import com.lowagie.text.pdf.BaseFont; /** * A <CODE>DocWriter</CODE> class for HTML. * <P> * An <CODE>HtmlWriter</CODE> can be added as a <CODE>DocListener</CODE> * to a certain <CODE>Document</CODE> by getting an instance. * Every <CODE>Element</CODE> added to the original <CODE>Document</CODE> * will be written to the <CODE>OutputStream</CODE> of this <CODE>HtmlWriter</CODE>. * <P> * Example: * <BLOCKQUOTE><PRE> * // creation of the document with a certain size and certain margins * Document document = new Document(PageSize.A4, 50, 50, 50, 50); * try { * // this will write HTML to the Standard OutputStream * <STRONG>HtmlWriter.getInstance(document, System.out);</STRONG> * // this will write HTML to a file called text.html * <STRONG>HtmlWriter.getInstance(document, new FileOutputStream("text.html"));</STRONG> * // this will write HTML to for instance the OutputStream of a HttpServletResponse-object * <STRONG>HtmlWriter.getInstance(document, response.getOutputStream());</STRONG> * } * catch(DocumentException de) { * System.err.println(de.getMessage()); * } * // this will close the document and all the OutputStreams listening to it * <STRONG>document.close();</CODE> * </PRE></BLOCKQUOTE> */ public class HtmlWriter extends DocWriter { // static membervariables (tags) /** This is a possible HTML-tag. */ public static final byte[] BEGINCOMMENT = getISOBytes("<!-- "); /** This is a possible HTML-tag. */ public static final byte[] ENDCOMMENT = getISOBytes(" -->"); /** This is a possible HTML-tag. */ public static final String NBSP = " "; // membervariables /** This is the current font of the HTML. */ protected Stack currentfont = new Stack(); /** This is the standard font of the HTML. */ protected Font standardfont = new Font(); /** This is a path for images. */ protected String imagepath = null; /** Stores the page number. */ protected int pageN = 0; /** This is the textual part of a header */ protected HeaderFooter header = null; /** This is the textual part of the footer */ protected HeaderFooter footer = null; /** Store the markup properties of a MarkedObject. */ protected Properties markup = new Properties(); // constructor /** * Constructs a <CODE>HtmlWriter</CODE>. * * @param doc The <CODE>Document</CODE> that has to be written as HTML * @param os The <CODE>OutputStream</CODE> the writer has to write to. */ protected HtmlWriter(Document doc, OutputStream os) { super(doc, os); document.addDocListener(this); this.pageN = document.getPageNumber(); try { os.write(LT); os.write(getISOBytes(HtmlTags.HTML)); os.write(GT); os.write(NEWLINE); os.write(TAB); os.write(LT); os.write(getISOBytes(HtmlTags.HEAD)); os.write(GT); } catch(IOException ioe) { throw new ExceptionConverter(ioe); } } // get an instance of the HtmlWriter /** * Gets an instance of the <CODE>HtmlWriter</CODE>. * * @param document The <CODE>Document</CODE> that has to be written * @param os The <CODE>OutputStream</CODE> the writer has to write to. * @return a new <CODE>HtmlWriter</CODE> */ public static HtmlWriter getInstance(Document document, OutputStream os) { return new HtmlWriter(document, os); } // implementation of the DocListener methods /** * Signals that an new page has to be started. * * @return <CODE>true</CODE> if this action succeeded, <CODE>false</CODE> if not. */ public boolean newPage() { try { writeStart(HtmlTags.DIV); write(" "); write(HtmlTags.STYLE); write("=\""); writeCssProperty(Markup.CSS_KEY_PAGE_BREAK_BEFORE, Markup.CSS_VALUE_ALWAYS); write("\" /"); os.write(GT); } catch(IOException ioe) { throw new ExceptionConverter(ioe); } return true; } /** * Signals that an <CODE>Element</CODE> was added to the <CODE>Document</CODE>. * * @param element a high level object that has to be translated to HTML * @return <CODE>true</CODE> if the element was added, <CODE>false</CODE> if not. * @throws DocumentException when a document isn't open yet, or has been closed */ public boolean add(Element element) throws DocumentException { if (pause) { return false; } if (open && !element.isContent()) { throw new DocumentException(MessageLocalization.getComposedMessage("the.document.is.open.you.can.only.add.elements.with.content")); } try { switch(element.type()) { case Element.HEADER: try { Header h = (Header) element; if (HtmlTags.STYLESHEET.equals(h.getName())) { writeLink(h); } else if (HtmlTags.JAVASCRIPT.equals(h.getName())) { writeJavaScript(h); } else { writeHeader(h); } } catch(ClassCastException cce) { } return true; case Element.SUBJECT: case Element.KEYWORDS: case Element.AUTHOR: Meta meta = (Meta) element; writeHeader(meta); return true; case Element.TITLE: addTabs(2); writeStart(HtmlTags.TITLE); os.write(GT); addTabs(3); write(HtmlEncoder.encode(((Meta)element).getContent())); addTabs(2); writeEnd(HtmlTags.TITLE); return true; case Element.CREATOR: writeComment("Creator: " + HtmlEncoder.encode(((Meta)element).getContent())); return true; case Element.PRODUCER: writeComment("Producer: " + HtmlEncoder.encode(((Meta)element).getContent())); return true; case Element.CREATIONDATE: writeComment("Creationdate: " + HtmlEncoder.encode(((Meta)element).getContent())); return true; case Element.MARKED: if (element instanceof MarkedSection) { MarkedSection ms = (MarkedSection)element; addTabs(1); writeStart(HtmlTags.DIV); writeMarkupAttributes(ms.getMarkupAttributes()); os.write(GT); MarkedObject mo = ((MarkedSection)element).getTitle(); if (mo != null) { markup = mo.getMarkupAttributes(); mo.process(this); } ms.process(this); writeEnd(HtmlTags.DIV); return true; } else { MarkedObject mo = (MarkedObject) element; markup = mo.getMarkupAttributes(); return mo.process(this); } default: write(element, 2); return true; } } catch(IOException ioe) { throw new ExceptionConverter(ioe); } } /** * Signals that the <CODE>Document</CODE> has been opened and that * <CODE>Elements</CODE> can be added. * <P> * The <CODE>HEAD</CODE>-section of the HTML-document is written. */ public void open() { super.open(); try { writeComment(Document.getVersion()); writeComment("CreationDate: " + new Date().toString()); addTabs(1); writeEnd(HtmlTags.HEAD); addTabs(1); writeStart(HtmlTags.BODY); if (document.leftMargin() > 0) { write(HtmlTags.LEFTMARGIN, String.valueOf(document.leftMargin())); } if (document.rightMargin() > 0) { write(HtmlTags.RIGHTMARGIN, String.valueOf(document.rightMargin())); } if (document.topMargin() > 0) { write(HtmlTags.TOPMARGIN, String.valueOf(document.topMargin())); } if (document.bottomMargin() > 0) { write(HtmlTags.BOTTOMMARGIN, String.valueOf(document.bottomMargin())); } if (pageSize.getBackgroundColor() != null) { write(HtmlTags.BACKGROUNDCOLOR, HtmlEncoder.encode(pageSize.getBackgroundColor())); } if (document.getJavaScript_onLoad() != null) { write(HtmlTags.JAVASCRIPT_ONLOAD, HtmlEncoder.encode(document.getJavaScript_onLoad())); } if (document.getJavaScript_onUnLoad() != null) { write(HtmlTags.JAVASCRIPT_ONUNLOAD, HtmlEncoder.encode(document.getJavaScript_onUnLoad())); } if (document.getHtmlStyleClass() != null) { write(Markup.HTML_ATTR_CSS_CLASS, document.getHtmlStyleClass()); } os.write(GT); initHeader(); // line added by David Freels } catch(IOException ioe) { throw new ExceptionConverter(ioe); } } /** * Signals that the <CODE>Document</CODE> was closed and that no other * <CODE>Elements</CODE> will be added. */ public void close() { try { initFooter(); // line added by David Freels addTabs(1); writeEnd(HtmlTags.BODY); os.write(NEWLINE); writeEnd(HtmlTags.HTML); super.close(); } catch(IOException ioe) { throw new ExceptionConverter(ioe); } } // some protected methods /** * Adds the header to the top of the </CODE>Document</CODE> */ protected void initHeader() { if (header != null) { try { add(header.paragraph()); } catch(Exception e) { throw new ExceptionConverter(e); } } } /** * Adds the header to the top of the </CODE>Document</CODE> */ protected void initFooter() { if (footer != null) { try { // Set the page number. HTML has no notion of a page, so it should always // add up to 1 footer.setPageNumber(pageN + 1); add(footer.paragraph()); } catch(Exception e) { throw new ExceptionConverter(e); } } } /** * Writes a Metatag in the header. * * @param meta the element that has to be written * @throws IOException */ protected void writeHeader(Meta meta) throws IOException { addTabs(2); writeStart(HtmlTags.META); switch(meta.type()) { case Element.HEADER: write(HtmlTags.NAME, ((Header) meta).getName()); break; case Element.SUBJECT: write(HtmlTags.NAME, HtmlTags.SUBJECT); break; case Element.KEYWORDS: write(HtmlTags.NAME, HtmlTags.KEYWORDS); break; case Element.AUTHOR: write(HtmlTags.NAME, HtmlTags.AUTHOR); break; } write(HtmlTags.CONTENT, HtmlEncoder.encode(meta.getContent())); writeEnd(); } /** * Writes a link in the header. * * @param header the element that has to be written * @throws IOException */ protected void writeLink(Header header) throws IOException { addTabs(2); writeStart(HtmlTags.LINK); write(HtmlTags.REL, header.getName()); write(HtmlTags.TYPE, HtmlTags.TEXT_CSS); write(HtmlTags.REFERENCE, header.getContent()); writeEnd(); } /** * Writes a JavaScript section or, if the markup attribute HtmlTags.URL is set, a JavaScript reference in the header. * * @param header the element that has to be written * @throws IOException */ protected void writeJavaScript(Header header) throws IOException { addTabs(2); writeStart(HtmlTags.SCRIPT); write(HtmlTags.LANGUAGE, HtmlTags.JAVASCRIPT); if (markup.size() > 0) { /* JavaScript reference example: * * <script language="JavaScript" src="/myPath/MyFunctions.js"/> */ writeMarkupAttributes(markup); os.write(GT); writeEnd(HtmlTags.SCRIPT); } else { /* JavaScript coding convention: * * <script language="JavaScript" type="text/javascript"> * <!-- * // ... JavaScript methods ... * //--> * </script> */ write(HtmlTags.TYPE, Markup.HTML_VALUE_JAVASCRIPT); os.write(GT); addTabs(2); write(new String(BEGINCOMMENT) + "\n"); write(header.getContent()); addTabs(2); write("//" + new String(ENDCOMMENT)); addTabs(2); writeEnd(HtmlTags.SCRIPT); } } /** * Writes some comment. * <P> * This method writes some comment. * * @param comment the comment that has to be written * @throws IOException */ protected void writeComment(String comment) throws IOException { addTabs(2); os.write(BEGINCOMMENT); write(comment); os.write(ENDCOMMENT); } // public methods /** * Changes the standardfont. * * @param standardfont The font */ public void setStandardFont(Font standardfont) { this.standardfont = standardfont; } /** * Checks if a given font is the same as the font that was last used. * * @param font the font of an object * @return true if the font differs */ public boolean isOtherFont(Font font) { try { Font cFont = (Font) currentfont.peek(); if (cFont.compareTo(font) == 0) return false; return true; } catch(EmptyStackException ese) { if (standardfont.compareTo(font) == 0) return false; return true; } } /** * Sets the basepath for images. * <P> * This is especially useful if you add images using a file, * rather than an URL. In PDF there is no problem, since * the images are added inline, but in HTML it is sometimes * necessary to use a relative path or a special path to some * images directory. * * @param imagepath the new imagepath */ public void setImagepath(String imagepath) { this.imagepath = imagepath; } /** * Resets the imagepath. */ public void resetImagepath() { imagepath = null; } /** * Changes the header of this document. * * @param header the new header */ public void setHeader(HeaderFooter header) { this.header = header; } /** * Changes the footer of this document. * * @param footer the new footer */ public void setFooter(HeaderFooter footer) { this.footer = footer; } /** * Signals that a <CODE>String</CODE> was added to the <CODE>Document</CODE>. * * @param string a String to add to the HTML * @return <CODE>true</CODE> if the string was added, <CODE>false</CODE> if not. */ public boolean add(String string) { if (pause) { return false; } try { write(string); return true; } catch(IOException ioe) { throw new ExceptionConverter(ioe); } } /** * Writes the HTML representation of an element. * * @param element the element * @param indent the indentation * @throws IOException */ protected void write(Element element, int indent) throws IOException { Properties styleAttributes = null; switch(element.type()) { case Element.MARKED: { try { add(element); } catch (DocumentException e) { e.printStackTrace(); } return; } case Element.CHUNK: { Chunk chunk = (Chunk) element; // if the chunk contains an image, return the image representation Image image = chunk.getImage(); if (image != null) { write(image, indent); return; } if (chunk.isEmpty()) return; HashMap attributes = chunk.getAttributes(); if (attributes != null && attributes.get(Chunk.NEWPAGE) != null) { return; } boolean tag = isOtherFont(chunk.getFont()) || markup.size() > 0; if (tag) { // start span tag addTabs(indent); writeStart(HtmlTags.SPAN); if (isOtherFont(chunk.getFont())) { write(chunk.getFont(), null); } writeMarkupAttributes(markup); os.write(GT); } if (attributes != null && attributes.get(Chunk.SUBSUPSCRIPT) != null) { // start sup or sub tag if (((Float)attributes.get(Chunk.SUBSUPSCRIPT)).floatValue() > 0) { writeStart(HtmlTags.SUP); } else { writeStart(HtmlTags.SUB); } os.write(GT); } // contents write(HtmlEncoder.encode(chunk.getContent())); if (attributes != null && attributes.get(Chunk.SUBSUPSCRIPT) != null) { // end sup or sub tag os.write(LT); os.write(FORWARD); if (((Float)attributes.get(Chunk.SUBSUPSCRIPT)).floatValue() > 0) { write(HtmlTags.SUP); } else { write(HtmlTags.SUB); } os.write(GT); } if (tag) { // end tag writeEnd(Markup.HTML_TAG_SPAN); } return; } case Element.PHRASE: { Phrase phrase = (Phrase) element; styleAttributes = new Properties(); if (phrase.hasLeading()) styleAttributes.setProperty(Markup.CSS_KEY_LINEHEIGHT, phrase.getLeading() + "pt"); // start tag addTabs(indent); writeStart(Markup.HTML_TAG_SPAN); writeMarkupAttributes(markup); write(phrase.getFont(), styleAttributes); os.write(GT); currentfont.push(phrase.getFont()); // contents for (Iterator i = phrase.iterator(); i.hasNext(); ) { write((Element) i.next(), indent + 1); } // end tag addTabs(indent); writeEnd(Markup.HTML_TAG_SPAN); currentfont.pop(); return; } case Element.ANCHOR: { Anchor anchor = (Anchor) element; styleAttributes = new Properties(); if (anchor.hasLeading()) styleAttributes.setProperty(Markup.CSS_KEY_LINEHEIGHT, anchor.getLeading() + "pt"); // start tag addTabs(indent); writeStart(HtmlTags.ANCHOR); if (anchor.getName() != null) { write(HtmlTags.NAME, anchor.getName()); } if (anchor.getReference() != null) { write(HtmlTags.REFERENCE, anchor.getReference()); } writeMarkupAttributes(markup); write(anchor.getFont(), styleAttributes); os.write(GT); currentfont.push(anchor.getFont()); // contents for (Iterator i = anchor.iterator(); i.hasNext(); ) { write((Element) i.next(), indent + 1); } // end tag addTabs(indent); writeEnd(HtmlTags.ANCHOR); currentfont.pop(); return; } case Element.PARAGRAPH: { Paragraph paragraph = (Paragraph) element; styleAttributes = new Properties(); if (paragraph.hasLeading()) styleAttributes.setProperty(Markup.CSS_KEY_LINEHEIGHT, paragraph.getTotalLeading() + "pt"); // start tag addTabs(indent); writeStart(HtmlTags.DIV); writeMarkupAttributes(markup); String alignment = HtmlEncoder.getAlignment(paragraph.getAlignment()); if (!"".equals(alignment)) { write(HtmlTags.ALIGN, alignment); } write(paragraph.getFont(), styleAttributes); os.write(GT); currentfont.push(paragraph.getFont()); // contents for (Iterator i = paragraph.iterator(); i.hasNext(); ) { write((Element)i.next(), indent + 1); } // end tag addTabs(indent); writeEnd(HtmlTags.DIV); currentfont.pop(); return; } case Element.SECTION: case Element.CHAPTER: { // part of the start tag + contents writeSection((Section) element, indent); return; } case Element.LIST: { List list = (List) element; // start tag addTabs(indent); if (list.isNumbered()) { writeStart(HtmlTags.ORDEREDLIST); } else { writeStart(HtmlTags.UNORDEREDLIST); } writeMarkupAttributes(markup); os.write(GT); // contents for (Iterator i = list.getItems().iterator(); i.hasNext(); ) { write((Element) i.next(), indent + 1); } // end tag addTabs(indent); if (list.isNumbered()) { writeEnd(HtmlTags.ORDEREDLIST); } else { writeEnd(HtmlTags.UNORDEREDLIST); } return; } case Element.LISTITEM: { ListItem listItem = (ListItem) element; styleAttributes = new Properties(); if (listItem.hasLeading()) styleAttributes.setProperty(Markup.CSS_KEY_LINEHEIGHT, listItem.getTotalLeading() + "pt"); // start tag addTabs(indent); writeStart(HtmlTags.LISTITEM); writeMarkupAttributes(markup); write(listItem.getFont(), styleAttributes); os.write(GT); currentfont.push(listItem.getFont()); // contents for (Iterator i = listItem.iterator(); i.hasNext(); ) { write((Element) i.next(), indent + 1); } // end tag addTabs(indent); writeEnd(HtmlTags.LISTITEM); currentfont.pop(); return; } case Element.CELL: { Cell cell = (Cell) element; // start tag addTabs(indent); if (cell.isHeader()) { writeStart(HtmlTags.HEADERCELL); } else { writeStart(HtmlTags.CELL); } writeMarkupAttributes(markup); if (cell.getBorderWidth() != Rectangle.UNDEFINED) { write(HtmlTags.BORDERWIDTH, String.valueOf(cell.getBorderWidth())); } if (cell.getBorderColor() != null) { write(HtmlTags.BORDERCOLOR, HtmlEncoder.encode(cell.getBorderColor())); } if (cell.getBackgroundColor() != null) { write(HtmlTags.BACKGROUNDCOLOR, HtmlEncoder.encode(cell.getBackgroundColor())); } String alignment = HtmlEncoder.getAlignment(cell.getHorizontalAlignment()); if (!"".equals(alignment)) { write(HtmlTags.HORIZONTALALIGN, alignment); } alignment = HtmlEncoder.getAlignment(cell.getVerticalAlignment()); if (!"".equals(alignment)) { write(HtmlTags.VERTICALALIGN, alignment); } if (cell.getWidthAsString() != null) { write(HtmlTags.WIDTH, cell.getWidthAsString()); } if (cell.getColspan() != 1) { write(HtmlTags.COLSPAN, String.valueOf(cell.getColspan())); } if (cell.getRowspan() != 1) { write(HtmlTags.ROWSPAN, String.valueOf(cell.getRowspan())); } if (cell.getMaxLines() == 1) { write(HtmlTags.STYLE, "white-space: nowrap;"); } os.write(GT); // contents if (cell.isEmpty()) { write(NBSP); } else { for (Iterator i = cell.getElements(); i.hasNext(); ) { write((Element) i.next(), indent + 1); } } // end tag addTabs(indent); if (cell.isHeader()) { writeEnd(HtmlTags.HEADERCELL); } else { writeEnd(HtmlTags.CELL); } return; } case Element.ROW: { Row row = (Row) element; // start tag addTabs(indent); writeStart(HtmlTags.ROW); writeMarkupAttributes(markup); os.write(GT); // contents Element cell; for (int i = 0; i < row.getColumns(); i++) { if ((cell = (Element)row.getCell(i)) != null) { write(cell, indent + 1); } } // end tag addTabs(indent); writeEnd(HtmlTags.ROW); return; } case Element.TABLE: { Table table; try { table = (Table) element; } catch(ClassCastException cce) { try { table = ((SimpleTable)element).createTable(); } catch (BadElementException e) { throw new ExceptionConverter(e); } } table.complete(); // start tag addTabs(indent); writeStart(HtmlTags.TABLE); writeMarkupAttributes(markup); os.write(SPACE); write(HtmlTags.WIDTH); os.write(EQUALS); os.write(QUOTE); write(String.valueOf(table.getWidth())); if (!table.isLocked()){ write("%"); } os.write(QUOTE); String alignment = HtmlEncoder.getAlignment(table.getAlignment()); if (!"".equals(alignment)) { write(HtmlTags.ALIGN, alignment); } write(HtmlTags.CELLPADDING, String.valueOf(table.getPadding())); write(HtmlTags.CELLSPACING, String.valueOf(table.getSpacing())); if (table.getBorderWidth() != Rectangle.UNDEFINED) { write(HtmlTags.BORDERWIDTH, String.valueOf(table.getBorderWidth())); } if (table.getBorderColor() != null) { write(HtmlTags.BORDERCOLOR, HtmlEncoder.encode(table.getBorderColor())); } if (table.getBackgroundColor() != null) { write(HtmlTags.BACKGROUNDCOLOR, HtmlEncoder.encode(table.getBackgroundColor())); } os.write(GT); // contents Row row; for (Iterator iterator = table.iterator(); iterator.hasNext(); ) { row = (Row) iterator.next(); write(row, indent + 1); } // end tag addTabs(indent); writeEnd(HtmlTags.TABLE); return; } case Element.ANNOTATION: { Annotation annotation = (Annotation) element; writeComment(annotation.title() + ": " + annotation.content()); return; } case Element.IMGRAW: case Element.JPEG: case Element.JPEG2000: case Element.IMGTEMPLATE: { Image image = (Image) element; if (image.getUrl() == null) { return; } // start tag addTabs(indent); writeStart(HtmlTags.IMAGE); String path = image.getUrl().toString(); if (imagepath != null) { if (path.indexOf('/') > 0) { path = imagepath + path.substring(path.lastIndexOf('/') + 1); } else { path = imagepath + path; } } write(HtmlTags.URL, path); if ((image.getAlignment() & Image.RIGHT) > 0) { write(HtmlTags.ALIGN, HtmlTags.ALIGN_RIGHT); } else if ((image.getAlignment() & Image.MIDDLE) > 0) { write(HtmlTags.ALIGN, HtmlTags.ALIGN_MIDDLE); } else { write(HtmlTags.ALIGN, HtmlTags.ALIGN_LEFT); } if (image.getAlt() != null) { write(HtmlTags.ALT, image.getAlt()); } write(HtmlTags.PLAINWIDTH, String.valueOf(image.getScaledWidth())); write(HtmlTags.PLAINHEIGHT, String.valueOf(image.getScaledHeight())); writeMarkupAttributes(markup); writeEnd(); return; } default: return; } } /** * Writes the HTML representation of a section. * * @param section the section to write * @param indent the indentation * @throws IOException */ protected void writeSection(Section section, int indent) throws IOException { if (section.getTitle() != null) { int depth = section.getDepth() - 1; if (depth > 5) { depth = 5; } Properties styleAttributes = new Properties(); if (section.getTitle().hasLeading()) styleAttributes.setProperty(Markup.CSS_KEY_LINEHEIGHT, section.getTitle().getTotalLeading() + "pt"); // start tag addTabs(indent); writeStart(HtmlTags.H[depth]); write(section.getTitle().getFont(), styleAttributes); String alignment = HtmlEncoder.getAlignment(section.getTitle().getAlignment()); if (!"".equals(alignment)) { write(HtmlTags.ALIGN, alignment); } writeMarkupAttributes(markup); os.write(GT); currentfont.push(section.getTitle().getFont()); // contents for (Iterator i = section.getTitle().iterator(); i.hasNext(); ) { write((Element)i.next(), indent + 1); } // end tag addTabs(indent); writeEnd(HtmlTags.H[depth]); currentfont.pop(); } for (Iterator i = section.iterator(); i.hasNext(); ) { write((Element) i.next(), indent); } } /** * Writes the representation of a <CODE>Font</CODE>. * * @param font a <CODE>Font</CODE> * @param styleAttributes the style of the font * @throws IOException */ protected void write(Font font, Properties styleAttributes) throws IOException { if (font == null || !isOtherFont(font) /* || styleAttributes == null*/) return; write(" "); write(HtmlTags.STYLE); write("=\""); if (styleAttributes != null) { String key; for (Enumeration e = styleAttributes.propertyNames(); e.hasMoreElements(); ) { key = (String)e.nextElement(); writeCssProperty(key, styleAttributes.getProperty(key)); } } if (isOtherFont(font)) { writeCssProperty(Markup.CSS_KEY_FONTFAMILY, font.getFamilyname()); if (font.getSize() != Font.UNDEFINED) { writeCssProperty(Markup.CSS_KEY_FONTSIZE, font.getSize() + "pt"); } if (font.getColor() != null) { writeCssProperty(Markup.CSS_KEY_COLOR, HtmlEncoder.encode(font.getColor())); } int fontstyle = font.getStyle(); BaseFont bf = font.getBaseFont(); if (bf != null) { String ps = bf.getPostscriptFontName().toLowerCase(); if (ps.indexOf("bold") >= 0) { if (fontstyle == Font.UNDEFINED) fontstyle = 0; fontstyle |= Font.BOLD; } if (ps.indexOf("italic") >= 0 || ps.indexOf("oblique") >= 0) { if (fontstyle == Font.UNDEFINED) fontstyle = 0; fontstyle |= Font.ITALIC; } } if (fontstyle != Font.UNDEFINED && fontstyle != Font.NORMAL) { switch (fontstyle & Font.BOLDITALIC) { case Font.BOLD: writeCssProperty(Markup.CSS_KEY_FONTWEIGHT, Markup.CSS_VALUE_BOLD); break; case Font.ITALIC: writeCssProperty(Markup.CSS_KEY_FONTSTYLE, Markup.CSS_VALUE_ITALIC); break; case Font.BOLDITALIC: writeCssProperty(Markup.CSS_KEY_FONTWEIGHT, Markup.CSS_VALUE_BOLD); writeCssProperty(Markup.CSS_KEY_FONTSTYLE, Markup.CSS_VALUE_ITALIC); break; } // CSS only supports one decoration tag so if both are specified // only one of the two will display if ((fontstyle & Font.UNDERLINE) > 0) { writeCssProperty(Markup.CSS_KEY_TEXTDECORATION, Markup.CSS_VALUE_UNDERLINE); } if ((fontstyle & Font.STRIKETHRU) > 0) { writeCssProperty(Markup.CSS_KEY_TEXTDECORATION, Markup.CSS_VALUE_LINETHROUGH); } } } write("\""); } /** * Writes out a CSS property. * @param prop a CSS property * @param value the value of the CSS property * @throws IOException */ protected void writeCssProperty(String prop, String value) throws IOException { write(new StringBuffer(prop).append(": ").append(value).append("; ").toString()); } }