From 6025b6016517c6d898d8957d1d7e03ba71431912 Mon Sep 17 00:00:00 2001 From: tknall Date: Fri, 1 Dec 2006 12:20:24 +0000 Subject: Initial import of release 2.2. git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@4 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- src/main/java/com/lowagie/text/rtf/RtfWriter.java | 2299 +++++++++++++++++++++ 1 file changed, 2299 insertions(+) create mode 100644 src/main/java/com/lowagie/text/rtf/RtfWriter.java (limited to 'src/main/java/com/lowagie/text/rtf/RtfWriter.java') diff --git a/src/main/java/com/lowagie/text/rtf/RtfWriter.java b/src/main/java/com/lowagie/text/rtf/RtfWriter.java new file mode 100644 index 0000000..43b47da --- /dev/null +++ b/src/main/java/com/lowagie/text/rtf/RtfWriter.java @@ -0,0 +1,2299 @@ +/* + * $Id: RtfWriter.java,v 1.70 2006/02/09 17:25:25 hallm Exp $ + * $Name: $ + * + * Copyright 2001, 2002 by Mark Hall + * + * 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.rtf; + +import com.lowagie.text.*; + +import java.io.*; +import java.util.ArrayList; +import java.util.ListIterator; +import java.util.Iterator; +import java.util.Calendar; +import java.util.Date; +import java.awt.Color; +import java.text.SimpleDateFormat; +import java.text.ParsePosition; +import com.lowagie.text.pdf.codec.wmf.MetaDo; + +/** + * If you are creating a new project using the rtf part of iText, please + * consider using the new RtfWriter2. The RtfWriter is in bug-fix-only mode, + * will be deprecated end of 2005 and removed end of 2007. + * + * A DocWriter class for Rich Text Files (RTF). + *

+ * A RtfWriter can be added as a DocListener + * to a certain Document by getting an instance. + * Every Element added to the original Document + * will be written to the OutputStream of this RtfWriter. + *

+ * Example: + *

+ * // 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 RTF to the Standard OutputStream
+ *    RtfWriter.getInstance(document, System.out);
+ *    // this will write Rtf to a file called text.rtf
+ *    RtfWriter.getInstance(document, new FileOutputStream("text.rtf"));
+ *    // this will write Rtf to for instance the OutputStream of a HttpServletResponse-object
+ *    RtfWriter.getInstance(document, response.getOutputStream());
+ * }
+ * catch(DocumentException de) {
+ *    System.err.println(de.getMessage());
+ * }
+ * // this will close the document and all the OutputStreams listening to it
+ * document.close();
+ * 
+ *

+ * LIMITATIONS
+ * There are currently still a few limitations on what the RTF Writer can do: + *

+ *
+ * + * @author Mark.Hall@myrealbox.com + * @author Steffen Stundzig + * @author Eric Mattes + * @author Raul Wegmann + * @deprecated The RtfWriter is deprecated and will be removed from the iText library end of 2007 + */ +public class RtfWriter extends DocWriter implements DocListener { + /** + * Static Constants + */ + + /** + * General + */ + + /** This is the escape character which introduces RTF tags. */ + public static final byte escape = (byte) '\\'; + + /** This is another escape character which introduces RTF tags. */ + private static final byte[] extendedEscape = "\\*\\".getBytes(); + + /** This is the delimiter between RTF tags and normal text. */ + protected static final byte delimiter = (byte) ' '; + + /** This is another delimiter between RTF tags and normal text. */ + private static final byte commaDelimiter = (byte) ';'; + + /** This is the character for beginning a new group. */ + public static final byte openGroup = (byte) '{'; + + /** This is the character for closing a group. */ + public static final byte closeGroup = (byte) '}'; + + /** + * RTF Information + */ + + /** RTF begin and version. */ + private static final byte[] docBegin = "rtf1".getBytes(); + + /** RTF encoding. */ + private static final byte[] ansi = "ansi".getBytes(); + + /** RTF encoding codepage. */ + private static final byte[] ansiCodepage = "ansicpg".getBytes(); + + /** + *Font Data + */ + + /** Begin the font table tag. */ + private static final byte[] fontTable = "fonttbl".getBytes(); + + /** Font number tag. */ + protected static final byte fontNumber = (byte) 'f'; + + /** Font size tag. */ + protected static final byte[] fontSize = "fs".getBytes(); + + /** Font color tag. */ + protected static final byte[] fontColor = "cf".getBytes(); + + /** Modern font tag. */ + private static final byte[] fontModern = "fmodern".getBytes(); + + /** Swiss font tag. */ + private static final byte[] fontSwiss = "fswiss".getBytes(); + + /** Roman font tag. */ + private static final byte[] fontRoman = "froman".getBytes(); + + /** Tech font tag. */ + private static final byte[] fontTech = "ftech".getBytes(); + + /** Font charset tag. */ + private static final byte[] fontCharset = "fcharset".getBytes(); + + /** Font Courier tag. */ + private static final byte[] fontCourier = "Courier".getBytes(); + + /** Font Arial tag. */ + private static final byte[] fontArial = "Arial".getBytes(); + + /** Font Symbol tag. */ + private static final byte[] fontSymbol = "Symbol".getBytes(); + + /** Font Times New Roman tag. */ + private static final byte[] fontTimesNewRoman = "Times New Roman".getBytes(); + + /** Font Windings tag. */ + private static final byte[] fontWindings = "Windings".getBytes(); + + /** Default Font. */ + private static final byte[] defaultFont = "deff".getBytes(); + + /** First indent tag. */ + private static final byte[] firstIndent = "fi".getBytes(); + + /** Left indent tag. */ + private static final byte[] listIndent = "li".getBytes(); + + /** Right indent tag. */ + private static final byte[] rightIndent = "ri".getBytes(); + + /** + * Sections / Paragraphs + */ + + /** Reset section defaults tag. */ + private static final byte[] sectionDefaults = "sectd".getBytes(); + + /** Begin new section tag. */ + private static final byte[] section = "sect".getBytes(); + + /** Reset paragraph defaults tag. */ + public static final byte[] paragraphDefaults = "pard".getBytes(); + + /** Begin new paragraph tag. */ + public static final byte[] paragraph = "par".getBytes(); + + /** Page width of a section. */ + public static final byte[] sectionPageWidth = "pgwsxn".getBytes(); + + /** Page height of a section. */ + public static final byte[] sectionPageHeight = "pghsxn".getBytes(); + + /** + * Lists + */ + + /** Begin the List Table */ + private static final byte[] listtableGroup = "listtable".getBytes(); + + /** Begin the List Override Table */ + private static final byte[] listoverridetableGroup = "listoverridetable".getBytes(); + + /** Begin a List definition */ + private static final byte[] listDefinition = "list".getBytes(); + + /** List Template ID */ + private static final byte[] listTemplateID = "listtemplateid".getBytes(); + + /** RTF Writer outputs hybrid lists */ + private static final byte[] hybridList = "hybrid".getBytes(); + + /** Current List level */ + private static final byte[] listLevelDefinition = "listlevel".getBytes(); + + /** Level numbering (old) */ + private static final byte[] listLevelTypeOld = "levelnfc".getBytes(); + + /** Level numbering (new) */ + private static final byte[] listLevelTypeNew = "levelnfcn".getBytes(); + + /** Level alignment (old) */ + private static final byte[] listLevelAlignOld = "leveljc".getBytes(); + + /** Level alignment (new) */ + private static final byte[] listLevelAlignNew = "leveljcn".getBytes(); + + /** Level starting number */ + private static final byte[] listLevelStartAt = "levelstartat".getBytes(); + + /** Level text group */ + private static final byte[] listLevelTextDefinition = "leveltext".getBytes(); + + /** Filler for Level Text Length */ + private static final byte[] listLevelTextLength = "\'0".getBytes(); + + /** Level Text Numbering Style */ + private static final byte[] listLevelTextStyleNumbers = "\'00.".getBytes(); + + /** Level Text Bullet Style */ + private static final byte[] listLevelTextStyleBullet = "u-3913 ?".getBytes(); + + /** Level Numbers Definition */ + private static final byte[] listLevelNumbersDefinition = "levelnumbers".getBytes(); + + /** Filler for Level Numbers */ + private static final byte[] listLevelNumbers = "\\'0".getBytes(); + + /** Tab Stop */ + private static final byte[] tabStop = "tx".getBytes(); + + /** Actual list begin */ + private static final byte[] listBegin = "ls".getBytes(); + + /** Current list level */ + private static final byte[] listCurrentLevel = "ilvl".getBytes(); + + /** List text group for older browsers */ + private static final byte[] listTextOld = "listtext".getBytes(); + + /** Tab */ + private static final byte[] tab = "tab".getBytes(); + + /** Old Bullet Style */ + private static final byte[] listBulletOld = "\'b7".getBytes(); + + /** Current List ID */ + private static final byte[] listID = "listid".getBytes(); + + /** List override */ + private static final byte[] listOverride = "listoverride".getBytes(); + + /** Number of overrides */ + private static final byte[] listOverrideCount = "listoverridecount".getBytes(); + + /** + * Text Style + */ + + /** Bold tag. */ + protected static final byte bold = (byte) 'b'; + + /** Italic tag. */ + protected static final byte italic = (byte) 'i'; + + /** Underline tag. */ + protected static final byte[] underline = "ul".getBytes(); + + /** Strikethrough tag. */ + protected static final byte[] strikethrough = "strike".getBytes(); + + /** Text alignment left tag. */ + public static final byte[] alignLeft = "ql".getBytes(); + + /** Text alignment center tag. */ + public static final byte[] alignCenter = "qc".getBytes(); + + /** Text alignment right tag. */ + public static final byte[] alignRight = "qr".getBytes(); + + /** Text alignment justify tag. */ + public static final byte[] alignJustify = "qj".getBytes(); + + /** + * Colors + */ + + /** Begin colour table tag. */ + private static final byte[] colorTable = "colortbl".getBytes(); + + /** Red value tag. */ + private static final byte[] colorRed = "red".getBytes(); + + /** Green value tag. */ + private static final byte[] colorGreen = "green".getBytes(); + + /** Blue value tag. */ + private static final byte[] colorBlue = "blue".getBytes(); + + /** + * Information Group + */ + + /** Begin the info group tag.*/ + private static final byte[] infoBegin = "info".getBytes(); + + /** Author tag. */ + private static final byte[] metaAuthor = "author".getBytes(); + + /** Subject tag. */ + private static final byte[] metaSubject = "subject".getBytes(); + + /** Keywords tag. */ + private static final byte[] metaKeywords = "keywords".getBytes(); + + /** Title tag. */ + private static final byte[] metaTitle = "title".getBytes(); + + /** Producer tag. */ + private static final byte[] metaProducer = "operator".getBytes(); + + /** Creation Date tag. */ + private static final byte[] metaCreationDate = "creationdate".getBytes(); + + /** Year tag. */ + private static final byte[] year = "yr".getBytes(); + + /** Month tag. */ + private static final byte[] month = "mo".getBytes(); + + /** Day tag. */ + private static final byte[] day = "dy".getBytes(); + + /** Hour tag. */ + private static final byte[] hour = "hr".getBytes(); + + /** Minute tag. */ + private static final byte[] minute = "min".getBytes(); + + /** Second tag. */ + private static final byte[] second = "sec".getBytes(); + + /** Start superscript. */ + private static final byte[] startSuper = "super".getBytes(); + + /** Start subscript. */ + private static final byte[] startSub = "sub".getBytes(); + + /** End super/sub script. */ + private static final byte[] endSuperSub = "nosupersub".getBytes(); + + /** + * Header / Footer + */ + + /** Title Page tag */ + private static final byte[] titlePage = "titlepg".getBytes(); + + /** Facing pages tag */ + private static final byte[] facingPages = "facingp".getBytes(); + + /** Begin header group tag. */ + private static final byte[] headerBegin = "header".getBytes(); + + /** Begin footer group tag. */ + private static final byte[] footerBegin = "footer".getBytes(); + + // header footer 'left', 'right', 'first' + private static final byte[] headerlBegin = "headerl".getBytes(); + + private static final byte[] footerlBegin = "footerl".getBytes(); + + private static final byte[] headerrBegin = "headerr".getBytes(); + + private static final byte[] footerrBegin = "footerr".getBytes(); + + private static final byte[] headerfBegin = "headerf".getBytes(); + + private static final byte[] footerfBegin = "footerf".getBytes(); + + /** + * Paper Properties + */ + + /** Paper width tag. */ + private static final byte[] rtfPaperWidth = "paperw".getBytes(); + + /** Paper height tag. */ + private static final byte[] rtfPaperHeight = "paperh".getBytes(); + + /** Margin left tag. */ + private static final byte[] rtfMarginLeft = "margl".getBytes(); + + /** Margin right tag. */ + private static final byte[] rtfMarginRight = "margr".getBytes(); + + /** Margin top tag. */ + private static final byte[] rtfMarginTop = "margt".getBytes(); + + /** Margin bottom tag. */ + private static final byte[] rtfMarginBottom = "margb".getBytes(); + + /** New Page tag. */ + private static final byte[] newPage = "page".getBytes(); + + /** Document Landscape tag 1. */ + private static final byte[] landscapeTag1 = "landscape".getBytes(); + + /** Document Landscape tag 2. */ + private static final byte[] landscapeTag2 = "lndscpsxn".getBytes(); + + /** + * Annotations + */ + + /** Annotation ID tag. */ + private static final byte[] annotationID = "atnid".getBytes(); + + /** Annotation Author tag. */ + private static final byte[] annotationAuthor = "atnauthor".getBytes(); + + /** Annotation text tag. */ + private static final byte[] annotation = "annotation".getBytes(); + + /** + * Images + */ + + /** Begin the main Picture group tag */ + private static final byte[] pictureGroup = "shppict".getBytes(); + + /** Begin the picture tag */ + private static final byte[] picture = "pict".getBytes(); + + /** PNG Image */ + private static final byte[] picturePNG = "pngblip".getBytes(); + + /** JPEG Image */ + private static final byte[] pictureJPEG = "jpegblip".getBytes(); + + /** BMP Image */ + private static final byte[] pictureBMP = "dibitmap0".getBytes(); + + /** WMF Image */ + private static final byte[] pictureWMF = "wmetafile8".getBytes(); + + /** Picture width */ + private static final byte[] pictureWidth = "picw".getBytes(); + + /** Picture height */ + private static final byte[] pictureHeight = "pich".getBytes(); + + /** Picture scale horizontal percent */ + private static final byte[] pictureScaleX = "picscalex".getBytes(); + + /** Picture scale vertical percent */ + private static final byte[] pictureScaleY = "picscaley".getBytes(); + + /** + * Fields (for page numbering) + */ + + /** Begin field tag */ + protected static final byte[] field = "field".getBytes(); + + /** Content fo the field */ + protected static final byte[] fieldContent = "fldinst".getBytes(); + + /** PAGE numbers */ + protected static final byte[] fieldPage = "PAGE".getBytes(); + + /** HYPERLINK field */ + protected static final byte[] fieldHyperlink = "HYPERLINK".getBytes(); + + /** Last page number (not used) */ + protected static final byte[] fieldDisplay = "fldrslt".getBytes(); + + + /** Class variables */ + + /** + * Because of the way RTF works and the way itext works, the text has to be + * stored and is only written to the actual OutputStream at the end. + */ + + /** This ArrayList contains all fonts used in the document. */ + private ArrayList fontList = new ArrayList(); + + /** This ArrayList contains all colours used in the document. */ + private ArrayList colorList = new ArrayList(); + + /** This ByteArrayOutputStream contains the main body of the document. */ + private ByteArrayOutputStream content = null; + + /** This ByteArrayOutputStream contains the information group. */ + private ByteArrayOutputStream info = null; + + /** This ByteArrayOutputStream contains the list table. */ + private ByteArrayOutputStream listtable = null; + + /** This ByteArrayOutputStream contains the list override table. */ + private ByteArrayOutputStream listoverride = null; + + /** Document header. */ + private HeaderFooter header = null; + + /** Document footer. */ + private HeaderFooter footer = null; + + /** Left margin. */ + private int marginLeft = 1800; + + /** Right margin. */ + private int marginRight = 1800; + + /** Top margin. */ + private int marginTop = 1440; + + /** Bottom margin. */ + private int marginBottom = 1440; + + /** Page width. */ + private int pageWidth = 11906; + + /** Page height. */ + private int pageHeight = 16838; + + /** Factor to use when converting. */ + public final static double TWIPSFACTOR = 20;//20.57140; + + /** Current list ID. */ + private int currentListID = 1; + + /** List of current Lists. */ + private ArrayList listIds = null; + + /** Current List Level. */ + private int listLevel = 0; + + /** Current maximum List Level. */ + private int maxListLevel = 0; + + /** Write a TOC */ + private boolean writeTOC = false; + + /** Special title page */ + private boolean hasTitlePage = false; + + /** Currently writing either Header or Footer */ + private boolean inHeaderFooter = false; + + /** Currently writing a Table */ + private boolean inTable = false; + + /** Landscape or Portrait Document */ + private boolean landscape = false; + + /** Protected Constructor */ + + /** + * Constructs a RtfWriter. + * + * @param doc The Document that is to be written as RTF + * @param os The OutputStream the writer has to write to. + */ + + protected RtfWriter(Document doc, OutputStream os) { + super(doc, os); + document.addDocListener(this); + initDefaults(); + } + + /** Public functions special to the RtfWriter */ + + /** + * This method controls whether TOC entries are automatically generated + * + * @param writeTOC boolean value indicating whether a TOC is to be generated + */ + public void setGenerateTOCEntries(boolean writeTOC) { + this.writeTOC = writeTOC; + } + + /** + * Gets the current setting of writeTOC + * + * @return boolean value indicating whether a TOC is being generated + */ + public boolean getGeneratingTOCEntries() { + return writeTOC; + } + + /** + * This method controls whether the first page is a title page + * + * @param hasTitlePage boolean value indicating whether the first page is a title page + */ + public void setHasTitlePage(boolean hasTitlePage) { + this.hasTitlePage = hasTitlePage; + } + + /** + * Gets the current setting of hasTitlePage + * + * @return boolean value indicating whether the first page is a title page + */ + public boolean getHasTitlePage() { + return hasTitlePage; + } + + /** + * Explicitly sets the page format to use. + * Otherwise the RtfWriter will try to guess the format by comparing pagewidth and pageheight + * + * @param landscape boolean value indicating whether we are using landscape format or not + */ + public void setLandscape(boolean landscape) { + this.landscape = landscape; + } + + /** + * Returns the current landscape setting + * + * @return boolean value indicating the current page format + */ + public boolean getLandscape() { + return landscape; + } + + /** Public functions from the DocWriter Interface */ + + /** + * Gets an instance of the RtfWriter. + * + * @param document The Document that has to be written + * @param os The OutputStream the writer has to write to. + * @return a new RtfWriter + */ + public static RtfWriter getInstance(Document document, OutputStream os) { + return (new RtfWriter(document, os)); + } + + /** + * Signals that the Document has been opened and that + * Elements can be added. + */ + public void open() { + super.open(); + } + + /** + * Signals that the Document was closed and that no other + * Elements will be added. + *

+ * The content of the font table, color table, information group, content, header, footer are merged into the final + * OutputStream + */ + public void close() { + writeDocument(); + super.close(); + } + + /** + * Adds the footer to the bottom of the Document. + * @param footer + */ + public void setFooter(HeaderFooter footer) { + this.footer = footer; + processHeaderFooter(this.footer); + } + + /** + * Adds the header to the top of the Document. + * @param header + */ + public void setHeader(HeaderFooter header) { + this.header = header; + processHeaderFooter(this.header); + } + + /** + * Resets the footer. + */ + public void resetFooter() { + setFooter(null); + } + + /** + * Resets the header. + */ + public void resetHeader() { + setHeader(null); + } + + /** + * Tells the RtfWriter that a new page is to be begun. + * + * @return true if a new Page was begun. + * @throws DocumentException if the Document was not open or had been closed. + */ + public boolean newPage() throws DocumentException { + try { + content.write(escape); + content.write(newPage); + content.write(escape); + content.write(paragraph); + } catch (IOException e) { + return false; + } + return true; + } + + /** + * Sets the page margins + * + * @param marginLeft The left margin + * @param marginRight The right margin + * @param marginTop The top margin + * @param marginBottom The bottom margin + * + * @return true if the page margins were set. + */ + public boolean setMargins(float marginLeft, float marginRight, float marginTop, float marginBottom) { + this.marginLeft = (int) (marginLeft * TWIPSFACTOR); + this.marginRight = (int) (marginRight * TWIPSFACTOR); + this.marginTop = (int) (marginTop * TWIPSFACTOR); + this.marginBottom = (int) (marginBottom * TWIPSFACTOR); + return true; + } + + /** + * Sets the page size + * + * @param pageSize A Rectangle specifying the page size + * + * @return true if the page size was set + */ + public boolean setPageSize(Rectangle pageSize) { + if (!parseFormat(pageSize, false)) { + pageWidth = (int) (pageSize.width() * TWIPSFACTOR); + pageHeight = (int) (pageSize.height() * TWIPSFACTOR); + landscape = pageWidth > pageHeight; + } + return true; + } + + /** + * Write the table of contents. + * + * @param tocTitle The title that will be displayed above the TOC + * @param titleFont The Font that will be used for the tocTitle + * @param showTOCasEntry Set this to true if you want the TOC to appear as an entry in the TOC + * @param showTOCEntryFont Use this Font to specify what Font to use when showTOCasEntry is true + * + * @return true if the TOC was added. + */ + public boolean writeTOC(String tocTitle, Font titleFont, boolean showTOCasEntry, Font showTOCEntryFont) { + try { + RtfTOC toc = new RtfTOC(tocTitle, titleFont); + if (showTOCasEntry) { + toc.addTOCAsTOCEntry(tocTitle, showTOCEntryFont); + } + add(new Paragraph(toc)); + } catch (DocumentException de) { + return false; + } + return true; + } + + /** + * Signals that an Element was added to the Document. + * + * @param element A high level object to add + * @return true if the element was added, false if not. + * @throws DocumentException if a document isn't open yet, or has been closed + */ + public boolean add(Element element) throws DocumentException { + if (pause) { + return false; + } + return addElement(element, content); + } + + + /** Private functions */ + + /** + * Adds an Element to the Document. + * @param element the high level element to add + * @param out the outputstream to which the RTF data is sent + * @return true if the element was added, false if not. + * @throws DocumentException if a document isn't open yet, or has been closed + */ + protected boolean addElement(Element element, ByteArrayOutputStream out) throws DocumentException { + try { + switch (element.type()) { + case Element.CHUNK: + writeChunk((Chunk) element, out); + break; + case Element.PARAGRAPH: + writeParagraph((Paragraph) element, out); + break; + case Element.ANCHOR: + writeAnchor((Anchor) element, out); + break; + case Element.PHRASE: + writePhrase((Phrase) element, out); + break; + case Element.CHAPTER: + case Element.SECTION: + writeSection((Section) element, out); + break; + case Element.LIST: + writeList((com.lowagie.text.List) element, out); + break; + case Element.TABLE: + try { + writeTable((Table) element, out); + } + catch(ClassCastException cce) { + writeTable(((SimpleTable)element).createTable(), out); + } + break; + case Element.ANNOTATION: + writeAnnotation((Annotation) element, out); + break; + case Element.IMGRAW: + case Element.IMGTEMPLATE: + case Element.JPEG: + Image img = (Image)element; + writeImage(img, out); + break; + + case Element.AUTHOR: + writeMeta(metaAuthor, (Meta) element); + break; + case Element.SUBJECT: + writeMeta(metaSubject, (Meta) element); + break; + case Element.KEYWORDS: + writeMeta(metaKeywords, (Meta) element); + break; + case Element.TITLE: + writeMeta(metaTitle, (Meta) element); + break; + case Element.PRODUCER: + writeMeta(metaProducer, (Meta) element); + break; + case Element.CREATIONDATE: + writeMeta(metaCreationDate, (Meta) element); + break; + } + } catch (IOException e) { + return false; + } + return true; + } + + /** + * Write the beginning of a new Section + * + * @param sectionElement The Section be written + * @param out The ByteArrayOutputStream to write to + * + * @throws IOException + * @throws DocumentException + */ + private void writeSection(Section sectionElement, ByteArrayOutputStream out) throws IOException, DocumentException { + if (sectionElement.type() == Element.CHAPTER) { + out.write(escape); + out.write(sectionDefaults); + writeSectionDefaults(out); + } + if (sectionElement.title() != null) { + if (writeTOC) { + StringBuffer title = new StringBuffer(""); + for (ListIterator li = sectionElement.title().getChunks().listIterator(); li.hasNext();) { + title.append(((Chunk) li.next()).content()); + } + add(new RtfTOCEntry(title.toString(), sectionElement.title().font())); + } else { + add(sectionElement.title()); + } + out.write(escape); + out.write(paragraph); + } + sectionElement.process(this); + if (sectionElement.type() == Element.CHAPTER) { + out.write(escape); + out.write(section); + } + if (sectionElement.type() == Element.SECTION) { + out.write(escape); + out.write(paragraph); + } + } + + /** + * Write the beginning of a new Paragraph + * + * @param paragraphElement The Paragraph to be written + * @param out The ByteArrayOutputStream to write to + * + * @throws IOException + */ + private void writeParagraph(Paragraph paragraphElement, ByteArrayOutputStream out) throws IOException { + out.write(escape); + out.write(paragraphDefaults); + if (inTable) { + out.write(escape); + out.write(RtfCell.cellInTable); + } + switch (paragraphElement.alignment()) { + case Element.ALIGN_LEFT: + out.write(escape); + out.write(alignLeft); + break; + case Element.ALIGN_RIGHT: + out.write(escape); + out.write(alignRight); + break; + case Element.ALIGN_CENTER: + out.write(escape); + out.write(alignCenter); + break; + case Element.ALIGN_JUSTIFIED: + case Element.ALIGN_JUSTIFIED_ALL: + out.write(escape); + out.write(alignJustify); + break; + } + out.write(escape); + out.write(listIndent); + writeInt(out, (int) (paragraphElement.indentationLeft() * TWIPSFACTOR)); + out.write(escape); + out.write(rightIndent); + writeInt(out, (int) (paragraphElement.indentationRight() * TWIPSFACTOR)); + Iterator chunks = paragraphElement.getChunks().iterator(); + while (chunks.hasNext()) { + Chunk ch = (Chunk) chunks.next(); + ch.setFont(paragraphElement.font().difference(ch.font())); + } + ByteArrayOutputStream save = content; + content = out; + paragraphElement.process(this); + content = save; + if (!inTable) { + out.write(escape); + out.write(paragraph); + } + } + + /** + * Write a Phrase. + * + * @param phrase The Phrase item to be written + * @param out The ByteArrayOutputStream to write to + * + * @throws IOException + */ + private void writePhrase(Phrase phrase, ByteArrayOutputStream out) throws IOException { + out.write(escape); + out.write(paragraphDefaults); + if (inTable) { + out.write(escape); + out.write(RtfCell.cellInTable); + } + Iterator chunks = phrase.getChunks().iterator(); + while (chunks.hasNext()) { + Chunk ch = (Chunk) chunks.next(); + ch.setFont(phrase.font().difference(ch.font())); + } + ByteArrayOutputStream save = content; + content = out; + phrase.process(this); + content = save; + } + + /** + * Write an Anchor. Anchors are treated like Phrases. + * + * @param anchor The Chunk item to be written + * @param out The ByteArrayOutputStream to write to + * + * @throws IOException + */ + private void writeAnchor(Anchor anchor, ByteArrayOutputStream out) throws IOException { + if (anchor.url() != null) { + out.write(openGroup); + out.write(escape); + out.write(field); + out.write(openGroup); + out.write(extendedEscape); + out.write(fieldContent); + out.write(openGroup); + out.write(fieldHyperlink); + out.write(delimiter); + out.write(anchor.url().toString().getBytes()); + out.write(closeGroup); + out.write(closeGroup); + out.write(openGroup); + out.write(escape); + out.write(fieldDisplay); + out.write(delimiter); + writePhrase(anchor, out); + out.write(closeGroup); + out.write(closeGroup); + } else { + writePhrase(anchor, out); + } + } + + /** + * Write a Chunk and all its font properties. + * + * @param chunk The Chunk item to be written + * @param out The ByteArrayOutputStream to write to + * + * @throws IOException + * @throws DocumentException + */ + private void writeChunk(Chunk chunk, ByteArrayOutputStream out) throws IOException, DocumentException { + if (chunk instanceof RtfField) { + ((RtfField) chunk).write(this, out); + } else { + if (chunk.getImage() != null) { + writeImage(chunk.getImage(), out); + } else { + writeInitialFontSignature(out, chunk); + out.write(filterSpecialChar(chunk.content(), false).getBytes()); + writeFinishingFontSignature(out, chunk); + } + } + } + + + protected void writeInitialFontSignature(OutputStream out, Chunk chunk) throws IOException { + Font font = chunk.font(); + + out.write(escape); + out.write(fontNumber); + if (!font.getFamilyname().equalsIgnoreCase("unknown")) { + writeInt(out, addFont(font)); + } else { + writeInt(out, 0); + } + out.write(escape); + out.write(fontSize); + if (font.size() > 0) { + writeInt(out, (int) (font.size() * 2)); + } else { + writeInt(out, 20); + } + out.write(escape); + out.write(fontColor); + writeInt(out, addColor(font.color())); + if (font.isBold()) { + out.write(escape); + out.write(bold); + } + if (font.isItalic()) { + out.write(escape); + out.write(italic); + } + if (font.isUnderlined()) { + out.write(escape); + out.write(underline); + } + if (font.isStrikethru()) { + out.write(escape); + out.write(strikethrough); + } + + /* + * Superscript / Subscript added by Scott Dietrich (sdietrich@emlab.com) + */ + if (chunk.getAttributes() != null) { + Float f = (Float) chunk.getAttributes().get(Chunk.SUBSUPSCRIPT); + if (f != null) + if (f.floatValue() > 0) { + out.write(escape); + out.write(startSuper); + } else if (f.floatValue() < 0) { + out.write(escape); + out.write(startSub); + } + } + + out.write(delimiter); + } + + + protected void writeFinishingFontSignature(OutputStream out, Chunk chunk) throws IOException { + Font font = chunk.font(); + + if (font.isBold()) { + out.write(escape); + out.write(bold); + writeInt(out, 0); + } + if (font.isItalic()) { + out.write(escape); + out.write(italic); + writeInt(out, 0); + } + if (font.isUnderlined()) { + out.write(escape); + out.write(underline); + writeInt(out, 0); + } + if (font.isStrikethru()) { + out.write(escape); + out.write(strikethrough); + writeInt(out, 0); + } + + /* + * Superscript / Subscript added by Scott Dietrich (sdietrich@emlab.com) + */ + if (chunk.getAttributes() != null) { + Float f = (Float) chunk.getAttributes().get(Chunk.SUBSUPSCRIPT); + if (f != null) + if (f.floatValue() != 0) { + out.write(escape); + out.write(endSuperSub); + } + } + } + + /** + * Write a ListItem + * + * @param listItem The ListItem to be written + * @param out The ByteArrayOutputStream to write to + * + * @throws IOException + * @throws DocumentException + */ + private void writeListElement(ListItem listItem, ByteArrayOutputStream out) throws IOException, DocumentException { + Iterator chunks = listItem.getChunks().iterator(); + while (chunks.hasNext()) { + Chunk ch = (Chunk) chunks.next(); + addElement(ch, out); + } + out.write(escape); + out.write(paragraph); + } + + /** + * Write a List + * + * @param list The List to be written + * @param out The ByteArrayOutputStream to write to + * + * @throws IOException + * @throws DocumentException + */ + private void writeList(com.lowagie.text.List list, ByteArrayOutputStream out) throws IOException, DocumentException { + int type = 0; + int align = 0; + int fontNr = addFont(new Font(Font.SYMBOL, 10, Font.NORMAL, new Color(0, 0, 0))); + if (!list.isNumbered()) type = 23; + if (listLevel == 0) { + maxListLevel = 0; + listtable.write(openGroup); + listtable.write(escape); + listtable.write(listDefinition); + int i = getRandomInt(); + listtable.write(escape); + listtable.write(listTemplateID); + writeInt(listtable, i); + listtable.write(escape); + listtable.write(hybridList); + listtable.write((byte) '\n'); + } + if (listLevel >= maxListLevel) { + maxListLevel++; + listtable.write(openGroup); + listtable.write(escape); + listtable.write(listLevelDefinition); + listtable.write(escape); + listtable.write(listLevelTypeOld); + writeInt(listtable, type); + listtable.write(escape); + listtable.write(listLevelTypeNew); + writeInt(listtable, type); + listtable.write(escape); + listtable.write(listLevelAlignOld); + writeInt(listtable, align); + listtable.write(escape); + listtable.write(listLevelAlignNew); + writeInt(listtable, align); + listtable.write(escape); + listtable.write(listLevelStartAt); + writeInt(listtable, 1); + listtable.write(openGroup); + listtable.write(escape); + listtable.write(listLevelTextDefinition); + listtable.write(escape); + listtable.write(listLevelTextLength); + if (list.isNumbered()) { + writeInt(listtable, 2); + } else { + writeInt(listtable, 1); + } + listtable.write(escape); + if (list.isNumbered()) { + listtable.write(listLevelTextStyleNumbers); + } else { + listtable.write(listLevelTextStyleBullet); + } + listtable.write(commaDelimiter); + listtable.write(closeGroup); + listtable.write(openGroup); + listtable.write(escape); + listtable.write(listLevelNumbersDefinition); + if (list.isNumbered()) { + listtable.write(delimiter); + listtable.write(listLevelNumbers); + writeInt(listtable, listLevel + 1); + } + listtable.write(commaDelimiter); + listtable.write(closeGroup); + if (!list.isNumbered()) { + listtable.write(escape); + listtable.write(fontNumber); + writeInt(listtable, fontNr); + } + listtable.write(escape); + listtable.write(firstIndent); + writeInt(listtable, (int) (list.indentationLeft() * TWIPSFACTOR * -1)); + listtable.write(escape); + listtable.write(listIndent); + writeInt(listtable, (int) ((list.indentationLeft() + list.symbolIndent()) * TWIPSFACTOR)); + listtable.write(escape); + listtable.write(rightIndent); + writeInt(listtable, (int) (list.indentationRight() * TWIPSFACTOR)); + listtable.write(escape); + listtable.write(tabStop); + writeInt(listtable, (int) (list.symbolIndent() * TWIPSFACTOR)); + listtable.write(closeGroup); + listtable.write((byte) '\n'); + } + // Actual List Begin in Content + out.write(escape); + out.write(paragraphDefaults); + out.write(escape); + out.write(alignLeft); + out.write(escape); + out.write(firstIndent); + writeInt(out, (int) (list.indentationLeft() * TWIPSFACTOR * -1)); + out.write(escape); + out.write(listIndent); + writeInt(out, (int) ((list.indentationLeft() + list.symbolIndent()) * TWIPSFACTOR)); + out.write(escape); + out.write(rightIndent); + writeInt(out, (int) (list.indentationRight() * TWIPSFACTOR)); + out.write(escape); + out.write(fontSize); + writeInt(out, 20); + out.write(escape); + out.write(listBegin); + writeInt(out, currentListID); + if (listLevel > 0) { + out.write(escape); + out.write(listCurrentLevel); + writeInt(out, listLevel); + } + out.write(openGroup); + ListIterator listItems = list.getItems().listIterator(); + Element listElem; + int count = 1; + while (listItems.hasNext()) { + listElem = (Element) listItems.next(); + if (listElem.type() == Element.CHUNK) { + listElem = new ListItem((Chunk) listElem); + } + if (listElem.type() == Element.LISTITEM) { + out.write(openGroup); + out.write(escape); + out.write(listTextOld); + out.write(escape); + out.write(paragraphDefaults); + out.write(escape); + out.write(fontNumber); + if (list.isNumbered()) { + writeInt(out, addFont(new Font(Font.TIMES_ROMAN, Font.NORMAL, 10, new Color(0, 0, 0)))); + } else { + writeInt(out, fontNr); + } + out.write(escape); + out.write(firstIndent); + writeInt(out, (int) (list.indentationLeft() * TWIPSFACTOR * -1)); + out.write(escape); + out.write(listIndent); + writeInt(out, (int) ((list.indentationLeft() + list.symbolIndent()) * TWIPSFACTOR)); + out.write(escape); + out.write(rightIndent); + writeInt(out, (int) (list.indentationRight() * TWIPSFACTOR)); + out.write(delimiter); + if (list.isNumbered()) { + writeInt(out, count); + out.write(".".getBytes()); + } else { + out.write(escape); + out.write(listBulletOld); + } + out.write(escape); + out.write(tab); + out.write(closeGroup); + writeListElement((ListItem) listElem, out); + count++; + } else if (listElem.type() == Element.LIST) { + listLevel++; + writeList((com.lowagie.text.List) listElem, out); + listLevel--; + out.write(escape); + out.write(paragraphDefaults); + out.write(escape); + out.write(alignLeft); + out.write(escape); + out.write(firstIndent); + writeInt(out, (int) (list.indentationLeft() * TWIPSFACTOR * -1)); + out.write(escape); + out.write(listIndent); + writeInt(out, (int) ((list.indentationLeft() + list.symbolIndent()) * TWIPSFACTOR)); + out.write(escape); + out.write(rightIndent); + writeInt(out, (int) (list.indentationRight() * TWIPSFACTOR)); + out.write(escape); + out.write(fontSize); + writeInt(out, 20); + out.write(escape); + out.write(listBegin); + writeInt(out, currentListID); + if (listLevel > 0) { + out.write(escape); + out.write(listCurrentLevel); + writeInt(out, listLevel); + } + } + out.write((byte) '\n'); + } + out.write(closeGroup); + if (listLevel == 0) { + int i = getRandomInt(); + listtable.write(escape); + listtable.write(listID); + writeInt(listtable, i); + listtable.write(closeGroup); + listtable.write((byte) '\n'); + listoverride.write(openGroup); + listoverride.write(escape); + listoverride.write(listOverride); + listoverride.write(escape); + listoverride.write(listID); + writeInt(listoverride, i); + listoverride.write(escape); + listoverride.write(listOverrideCount); + writeInt(listoverride, 0); + listoverride.write(escape); + listoverride.write(listBegin); + writeInt(listoverride, currentListID); + currentListID++; + listoverride.write(closeGroup); + listoverride.write((byte) '\n'); + } + out.write(escape); + out.write(paragraphDefaults); + } + + /** + * Write a Table. + * + * @param table The table to be written + * @param out The ByteArrayOutputStream to write to + * + * Currently no nesting of tables is supported. If a cell contains anything but a Cell Object it is ignored. + * + * @throws IOException + * @throws DocumentException + */ + private void writeTable(Table table, ByteArrayOutputStream out) throws IOException, DocumentException { + inTable = true; + table.complete(); + RtfTable rtfTable = new RtfTable(this); + rtfTable.importTable(table, pageWidth - marginLeft - marginRight); + rtfTable.writeTable(out); + inTable = false; + } + + + /** + * Write an Image. + * + * @param image The image to be written + * @param out The ByteArrayOutputStream to write to + * + * At the moment only PNG and JPEG Images are supported. + * + * @throws IOException + * @throws DocumentException + */ + private void writeImage(Image image, ByteArrayOutputStream out) throws IOException, DocumentException { + int type = image.getOriginalType(); + if (!(type == Image.ORIGINAL_JPEG || type == Image.ORIGINAL_BMP + || type == Image.ORIGINAL_PNG || type == Image.ORIGINAL_WMF)) + throw new DocumentException("Only BMP, PNG, WMF and JPEG images are supported by the RTF Writer"); + switch (image.alignment()) { + case Element.ALIGN_LEFT: + out.write(escape); + out.write(alignLeft); + break; + case Element.ALIGN_RIGHT: + out.write(escape); + out.write(alignRight); + break; + case Element.ALIGN_CENTER: + out.write(escape); + out.write(alignCenter); + break; + case Element.ALIGN_JUSTIFIED: + out.write(escape); + out.write(alignJustify); + break; + } + out.write(openGroup); + out.write(extendedEscape); + out.write(pictureGroup); + out.write(openGroup); + out.write(escape); + out.write(picture); + out.write(escape); + switch (type) { + case Image.ORIGINAL_JPEG: + out.write(pictureJPEG); + break; + case Image.ORIGINAL_PNG: + out.write(picturePNG); + break; + case Image.ORIGINAL_WMF: + case Image.ORIGINAL_BMP: + out.write(pictureWMF); + break; + } + out.write(escape); + out.write(pictureWidth); + writeInt(out, (int) (image.plainWidth() * TWIPSFACTOR)); + out.write(escape); + out.write(pictureHeight); + writeInt(out, (int) (image.plainHeight() * TWIPSFACTOR)); + + +// For some reason this messes up the intended image size. It makes it too big. Weird +// +// out.write(escape); +// out.write(pictureIntendedWidth); +// writeInt(out, (int) (image.plainWidth() * twipsFactor)); +// out.write(escape); +// out.write(pictureIntendedHeight); +// writeInt(out, (int) (image.plainHeight() * twipsFactor)); + + + if (image.width() > 0) { + out.write(escape); + out.write(pictureScaleX); + writeInt(out, (int) (100 / image.width() * image.plainWidth())); + } + if (image.height() > 0) { + out.write(escape); + out.write(pictureScaleY); + writeInt(out, (int) (100 / image.height() * image.plainHeight())); + } + out.write(delimiter); + InputStream imgIn; + if (type == Image.ORIGINAL_BMP) { + imgIn = new ByteArrayInputStream(MetaDo.wrapBMP(image)); + } + else { + if (image.getOriginalData() == null) { + imgIn = image.url().openStream(); + } else { + imgIn = new ByteArrayInputStream(image.getOriginalData()); + } + if (type == Image.ORIGINAL_WMF) { //remove the placeable header + long skipLength = 22; + while(skipLength > 0) { + skipLength = skipLength - imgIn.skip(skipLength); + } + } + } + int buffer = -1; + int count = 0; + out.write((byte) '\n'); + while ((buffer = imgIn.read()) != -1) { + String helperStr = Integer.toHexString(buffer); + if (helperStr.length() < 2) helperStr = "0" + helperStr; + out.write(helperStr.getBytes()); + count++; + if (count == 64) { + out.write((byte) '\n'); + count = 0; + } + } + imgIn.close(); + out.write(closeGroup); + out.write(closeGroup); + out.write((byte) '\n'); + } + + /** + * Write an Annotation + * + * @param annotationElement The Annotation to be written + * @param out The ByteArrayOutputStream to write to + * + * @throws IOException + */ + private void writeAnnotation(Annotation annotationElement, ByteArrayOutputStream out) throws IOException { + int id = getRandomInt(); + out.write(openGroup); + out.write(extendedEscape); + out.write(annotationID); + out.write(delimiter); + writeInt(out, id); + out.write(closeGroup); + out.write(openGroup); + out.write(extendedEscape); + out.write(annotationAuthor); + out.write(delimiter); + out.write(annotationElement.title().getBytes()); + out.write(closeGroup); + out.write(openGroup); + out.write(extendedEscape); + out.write(annotation); + out.write(escape); + out.write(paragraphDefaults); + out.write(delimiter); + out.write(annotationElement.content().getBytes()); + out.write(closeGroup); + } + + /** + * Add a Meta element. It is written to the Inforamtion Group + * and merged with the main ByteArrayOutputStream when the + * Document is closed. + * + * @param metaName The type of Meta element to be added + * @param meta The Meta element to be added + * + * Currently only the Meta Elements Author, Subject, Keywords, Title, Producer and CreationDate are supported. + * + * @throws IOException + */ + private void writeMeta(byte[] metaName, Meta meta) throws IOException { + info.write(openGroup); + try { + info.write(escape); + info.write(metaName); + info.write(delimiter); + if (meta.type() == Meta.CREATIONDATE) { + writeFormatedDateTime(meta.content()); + } else { + info.write(meta.content().getBytes()); + } + } finally { + info.write(closeGroup); + } + } + + /** + * Writes a date. The date is formated Year, Month, Day, Hour, Minute, Second + * + * @param date The date to be written + * + * @throws IOException + */ + private void writeFormatedDateTime(String date) throws IOException { + Calendar cal = Calendar.getInstance(); + SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy"); + ParsePosition pp = new ParsePosition(0); + Date d = sdf.parse(date, pp); + if (d == null) { + d = new Date(); + } + cal.setTime(d); + info.write(escape); + info.write(year); + writeInt(info, cal.get(Calendar.YEAR)); + info.write(escape); + info.write(month); + writeInt(info, cal.get(Calendar.MONTH)); + info.write(escape); + info.write(day); + writeInt(info, cal.get(Calendar.DAY_OF_MONTH)); + info.write(escape); + info.write(hour); + writeInt(info, cal.get(Calendar.HOUR_OF_DAY)); + info.write(escape); + info.write(minute); + writeInt(info, cal.get(Calendar.MINUTE)); + info.write(escape); + info.write(second); + writeInt(info, cal.get(Calendar.SECOND)); + } + + /** + * Add a new Font to the list of fonts. If the Font + * already exists in the list of fonts, then it is not added again. + * + * @param newFont The Font to be added + * + * @return The index of the Font in the font list + */ + protected int addFont(Font newFont) { + int fn = -1; + + for (int i = 0; i < fontList.size(); i++) { + if (newFont.getFamilyname().equals(((Font) fontList.get(i)).getFamilyname())) { + fn = i; + } + } + if (fn == -1) { + fontList.add(newFont); + return fontList.size() - 1; + } + return fn; + } + + /** + * Add a new Color to the list of colours. If the Color + * already exists in the list of colours, then it is not added again. + * + * @param newColor The Color to be added + * + * @return The index of the color in the colour list + */ + protected int addColor(Color newColor) { + int cn = 0; + if (newColor == null) { + return cn; + } + cn = colorList.indexOf(newColor); + if (cn == -1) { + colorList.add(newColor); + return colorList.size() - 1; + } + return cn; + } + + /** + * Merge all the different ArrayLists and ByteArrayOutputStreams + * to the final ByteArrayOutputStream + * + * @return true if all information was sucessfully written to the ByteArrayOutputStream + */ + private boolean writeDocument() { + try { + writeDocumentIntro(); + writeFontList(); + os.write((byte) '\n'); + writeColorList(); + os.write((byte) '\n'); + writeList(); + os.write((byte) '\n'); + writeInfoGroup(); + os.write((byte) '\n'); + writeDocumentFormat(); + os.write((byte) '\n'); + ByteArrayOutputStream hf = new ByteArrayOutputStream(); + writeSectionDefaults(hf); + hf.writeTo(os); + content.writeTo(os); + os.write(closeGroup); + return true; + } catch (IOException e) { + System.err.println(e.getMessage()); + return false; + } + + } + + /** Write the Rich Text file settings + * @throws IOException + */ + private void writeDocumentIntro() throws IOException { + os.write(openGroup); + os.write(escape); + os.write(docBegin); + os.write(escape); + os.write(ansi); + os.write(escape); + os.write(ansiCodepage); + writeInt(os, 1252); + os.write((byte)'\n'); + os.write(escape); + os.write(defaultFont); + writeInt(os, 0); + } + + /** + * Write the font list to the final ByteArrayOutputStream + * @throws IOException + */ + private void writeFontList() throws IOException { + Font fnt; + + os.write(openGroup); + os.write(escape); + os.write(fontTable); + for (int i = 0; i < fontList.size(); i++) { + fnt = (Font) fontList.get(i); + os.write(openGroup); + os.write(escape); + os.write(fontNumber); + writeInt(os, i); + os.write(escape); + switch (Font.getFamilyIndex(fnt.getFamilyname())) { + case Font.COURIER: + os.write(fontModern); + os.write(escape); + os.write(fontCharset); + writeInt(os, 0); + os.write(delimiter); + os.write(fontCourier); + break; + case Font.HELVETICA: + os.write(fontSwiss); + os.write(escape); + os.write(fontCharset); + writeInt(os, 0); + os.write(delimiter); + os.write(fontArial); + break; + case Font.SYMBOL: + os.write(fontRoman); + os.write(escape); + os.write(fontCharset); + writeInt(os, 2); + os.write(delimiter); + os.write(fontSymbol); + break; + case Font.TIMES_ROMAN: + os.write(fontRoman); + os.write(escape); + os.write(fontCharset); + writeInt(os, 0); + os.write(delimiter); + os.write(fontTimesNewRoman); + break; + case Font.ZAPFDINGBATS: + os.write(fontTech); + os.write(escape); + os.write(fontCharset); + writeInt(os, 0); + os.write(delimiter); + os.write(fontWindings); + break; + default: + os.write(fontRoman); + os.write(escape); + os.write(fontCharset); + writeInt(os, 0); + os.write(delimiter); + os.write(filterSpecialChar(fnt.getFamilyname(), true).getBytes()); + } + os.write(commaDelimiter); + os.write(closeGroup); + } + os.write(closeGroup); + } + + /** + * Write the colour list to the final ByteArrayOutputStream + * @throws IOException + */ + private void writeColorList() throws IOException { + Color color = null; + + os.write(openGroup); + os.write(escape); + os.write(colorTable); + for (int i = 0; i < colorList.size(); i++) { + color = (Color) colorList.get(i); + os.write(escape); + os.write(colorRed); + writeInt(os, color.getRed()); + os.write(escape); + os.write(colorGreen); + writeInt(os, color.getGreen()); + os.write(escape); + os.write(colorBlue); + writeInt(os, color.getBlue()); + os.write(commaDelimiter); + } + os.write(closeGroup); + } + + /** + * Write the Information Group to the final ByteArrayOutputStream + * @throws IOException + */ + private void writeInfoGroup() throws IOException { + os.write(openGroup); + os.write(escape); + os.write(infoBegin); + info.writeTo(os); + os.write(closeGroup); + } + + /** + * Write the listtable and listoverridetable to the final ByteArrayOutputStream + * @throws IOException + */ + private void writeList() throws IOException { + listtable.write(closeGroup); + listoverride.write(closeGroup); + listtable.writeTo(os); + os.write((byte) '\n'); + listoverride.writeTo(os); + } + + /** + * Write an integer + * + * @param out The OuputStream to which the int value is to be written + * @param i The int value to be written + * @throws IOException + */ + public final static void writeInt(OutputStream out, int i) throws IOException { + out.write(Integer.toString(i).getBytes()); + } + + /** + * Get a random integer. + * This returns a unique random integer to be used with listids. + * + * @return Random int value. + */ + private int getRandomInt() { + boolean ok = false; + Integer newInt = null; + Integer oldInt = null; + while (!ok) { + newInt = new Integer((int) (Math.random() * Integer.MAX_VALUE)); + ok = true; + for (int i = 0; i < listIds.size(); i++) { + oldInt = (Integer) listIds.get(i); + if (oldInt.equals(newInt)) { + ok = true; + } + } + } + listIds.add(newInt); + return newInt.intValue(); + } + + /** + * Write the current header and footer to a ByteArrayOutputStream + * + * @param os The ByteArrayOutputStream to which the header and footer will be written. + * @throws IOException + */ + public void writeHeadersFooters(ByteArrayOutputStream os) throws IOException { + if (this.footer instanceof RtfHeaderFooters) { + RtfHeaderFooters rtfHf = (RtfHeaderFooters) this.footer; + HeaderFooter hf = rtfHf.get(RtfHeaderFooters.ALL_PAGES); + if (hf != null) { + writeHeaderFooter(hf, footerBegin, os); + } + hf = rtfHf.get(RtfHeaderFooters.LEFT_PAGES); + if (hf != null) { + writeHeaderFooter(hf, footerlBegin, os); + } + hf = rtfHf.get(RtfHeaderFooters.RIGHT_PAGES); + if (hf != null) { + writeHeaderFooter(hf, footerrBegin, os); + } + hf = rtfHf.get(RtfHeaderFooters.FIRST_PAGE); + if (hf != null) { + writeHeaderFooter(hf, footerfBegin, os); + } + } else { + writeHeaderFooter(this.footer, footerBegin, os); + } + if (this.header instanceof RtfHeaderFooters) { + RtfHeaderFooters rtfHf = (RtfHeaderFooters) this.header; + HeaderFooter hf = rtfHf.get(RtfHeaderFooters.ALL_PAGES); + if (hf != null) { + writeHeaderFooter(hf, headerBegin, os); + } + hf = rtfHf.get(RtfHeaderFooters.LEFT_PAGES); + if (hf != null) { + writeHeaderFooter(hf, headerlBegin, os); + } + hf = rtfHf.get(RtfHeaderFooters.RIGHT_PAGES); + if (hf != null) { + writeHeaderFooter(hf, headerrBegin, os); + } + hf = rtfHf.get(RtfHeaderFooters.FIRST_PAGE); + if (hf != null) { + writeHeaderFooter(hf, headerfBegin, os); + } + } else { + writeHeaderFooter(this.header, headerBegin, os); + } + } + + /** + * Write a HeaderFooter to a ByteArrayOutputStream + * + * @param headerFooter The HeaderFooter object to be written. + * @param hfType The type of header or footer to be added. + * @param target The ByteArrayOutputStream to which the HeaderFooter will be written. + * @throws IOException + */ + private void writeHeaderFooter(HeaderFooter headerFooter, byte[] hfType, ByteArrayOutputStream target) throws IOException { + inHeaderFooter = true; + try { + target.write(openGroup); + target.write(escape); + target.write(hfType); + target.write(delimiter); + if (headerFooter != null) { + if (headerFooter instanceof RtfHeaderFooter && ((RtfHeaderFooter) headerFooter).content() != null) { + this.addElement(((RtfHeaderFooter) headerFooter).content(), target); + } else { + Paragraph par = new Paragraph(); + par.setAlignment(headerFooter.alignment()); + if (headerFooter.getBefore() != null) { + par.add(headerFooter.getBefore()); + } + if (headerFooter.isNumbered()) { + par.add(new RtfPageNumber("", headerFooter.getBefore().font())); + } + if (headerFooter.getAfter() != null) { + par.add(headerFooter.getAfter()); + } + this.addElement(par, target); + } + } + target.write(closeGroup); + } catch (DocumentException e) { + throw new IOException("DocumentException - " + e.getMessage()); + } + inHeaderFooter = false; + } + + /** + * Write the Document's Paper and Margin Size + * to the final ByteArrayOutputStream + * @throws IOException + */ + private void writeDocumentFormat() throws IOException { +// os.write(openGroup); + os.write(escape); + os.write(rtfPaperWidth); + writeInt(os, pageWidth); + os.write(escape); + os.write(rtfPaperHeight); + writeInt(os, pageHeight); + os.write(escape); + os.write(rtfMarginLeft); + writeInt(os, marginLeft); + os.write(escape); + os.write(rtfMarginRight); + writeInt(os, marginRight); + os.write(escape); + os.write(rtfMarginTop); + writeInt(os, marginTop); + os.write(escape); + os.write(rtfMarginBottom); + writeInt(os, marginBottom); +// os.write(closeGroup); + } + + /** + * Initialise all helper classes. + * Clears alls lists, creates new ByteArrayOutputStream's + */ + private void initDefaults() { + fontList.clear(); + colorList.clear(); + info = new ByteArrayOutputStream(); + content = new ByteArrayOutputStream(); + listtable = new ByteArrayOutputStream(); + listoverride = new ByteArrayOutputStream(); + document.addProducer(); + document.addCreationDate(); + addFont(new Font(Font.TIMES_ROMAN, 10, Font.NORMAL)); + addColor(new Color(0, 0, 0)); + addColor(new Color(255, 255, 255)); + listIds = new ArrayList(); + try { + listtable.write(openGroup); + listtable.write(extendedEscape); + listtable.write(listtableGroup); + listtable.write((byte) '\n'); + listoverride.write(openGroup); + listoverride.write(extendedEscape); + listoverride.write(listoverridetableGroup); + listoverride.write((byte) '\n'); + } catch (IOException e) { + System.err.println("InitDefaultsError" + e); + } + } + + /** + * Writes the default values for the current Section + * + * @param out The ByteArrayOutputStream to be written to + * @throws IOException + */ + private void writeSectionDefaults(ByteArrayOutputStream out) throws IOException { + if (header instanceof RtfHeaderFooters || footer instanceof RtfHeaderFooters) { + RtfHeaderFooters rtfHeader = (RtfHeaderFooters) header; + RtfHeaderFooters rtfFooter = (RtfHeaderFooters) footer; + if ((rtfHeader != null && (rtfHeader.get(RtfHeaderFooters.LEFT_PAGES) != null || rtfHeader.get(RtfHeaderFooters.RIGHT_PAGES) != null)) || (rtfFooter != null && (rtfFooter.get(RtfHeaderFooters.LEFT_PAGES) != null || rtfFooter.get(RtfHeaderFooters.RIGHT_PAGES) != null))) { + out.write(escape); + out.write(facingPages); + } + } + if (hasTitlePage) { + out.write(escape); + out.write(titlePage); + } + writeHeadersFooters(out); + if (landscape) { + //out.write(escape); + //out.write(landscapeTag1); + out.write(escape); + out.write(landscapeTag2); + out.write(escape); + out.write(sectionPageWidth); + writeInt(out, pageWidth); + out.write(escape); + out.write(sectionPageHeight); + writeInt(out, pageHeight); + } else { + out.write(escape); + out.write(sectionPageWidth); + writeInt(out, pageWidth); + out.write(escape); + out.write(sectionPageHeight); + writeInt(out, pageHeight); + } + } + + /** + * This method tries to fit the Rectangle pageSize to one of the predefined PageSize rectangles. + * If a match is found the pageWidth and pageHeight will be set according to values determined from files + * generated by MS Word2000 and OpenOffice 641. If no match is found the method will try to match the rotated + * Rectangle by calling itself with the parameter rotate set to true. + * @param pageSize a rectangle defining the size of the page + * @param rotate portrait or lanscape? + * @return true if the format parsing succeeded + */ + private boolean parseFormat(Rectangle pageSize, boolean rotate) { + if (rotate) { + pageSize = pageSize.rotate(); + } + if (rectEquals(pageSize, PageSize.A3)) { + pageWidth = 16837; + pageHeight = 23811; + landscape = rotate; + return true; + } + if (rectEquals(pageSize, PageSize.A4)) { + pageWidth = 11907; + pageHeight = 16840; + landscape = rotate; + return true; + } + if (rectEquals(pageSize, PageSize.A5)) { + pageWidth = 8391; + pageHeight = 11907; + landscape = rotate; + return true; + } + if (rectEquals(pageSize, PageSize.A6)) { + pageWidth = 5959; + pageHeight = 8420; + landscape = rotate; + return true; + } + if (rectEquals(pageSize, PageSize.B4)) { + pageWidth = 14570; + pageHeight = 20636; + landscape = rotate; + return true; + } + if (rectEquals(pageSize, PageSize.B5)) { + pageWidth = 10319; + pageHeight = 14572; + landscape = rotate; + return true; + } + if (rectEquals(pageSize, PageSize.HALFLETTER)) { + pageWidth = 7927; + pageHeight = 12247; + landscape = rotate; + return true; + } + if (rectEquals(pageSize, PageSize.LETTER)) { + pageWidth = 12242; + pageHeight = 15842; + landscape = rotate; + return true; + } + if (rectEquals(pageSize, PageSize.LEGAL)) { + pageWidth = 12252; + pageHeight = 20163; + landscape = rotate; + return true; + } + if (!rotate && parseFormat(pageSize, true)) { + int x = pageWidth; + pageWidth = pageHeight; + pageHeight = x; + return true; + } + return false; + } + + /** + * This method compares to Rectangles. They are considered equal if width and height are the same + * @param rect1 + * @param rect2 + * @return true if rect1 and rect2 represent the same rectangle + */ + private boolean rectEquals(Rectangle rect1, Rectangle rect2) { + return (rect1.width() == rect2.width()) && (rect1.height() == rect2.height()); + } + + /** + * Returns whether we are currently writing a header or footer + * + * @return the value of inHeaderFooter + */ + public boolean writingHeaderFooter() { + return inHeaderFooter; + } + + /** + * Replaces special characters with their unicode values + * + * @param str The original String + * @param useHex + * @return The converted String + */ + public final static String filterSpecialChar(String str, boolean useHex) { + int length = str.length(); + int z = (int) 'z'; + StringBuffer ret = new StringBuffer(length); + for (int i = 0; i < length; i++) { + char ch = str.charAt(i); + + if (ch == '\\') { + ret.append("\\\\"); + } else if (ch == '\n') { + ret.append("\\par "); + } else if (((int) ch) > z) { + if(useHex) { + ret.append("\\\'").append(Long.toHexString((long) ch)); + } else { + ret.append("\\u").append((long) ch).append('?'); + } + } else { + ret.append(ch); + } + } + String s = ret.toString(); + if(s.indexOf("$newpage$") >= 0) { + String before = s.substring(0, s.indexOf("$newpage$")); + String after = s.substring(s.indexOf("$newpage$") + 9); + ret = new StringBuffer(before); + ret.append("\\page\\par "); + ret.append(after); + return ret.toString(); + } + return s; + } + + private void addHeaderFooterFontColor(HeaderFooter hf) { + if(hf instanceof RtfHeaderFooter) { + RtfHeaderFooter rhf = (RtfHeaderFooter) hf; + if(rhf.content() instanceof Chunk) { + addFont(((Chunk) rhf.content()).font()); + addColor(((Chunk) rhf.content()).font().color()); + } else if(rhf.content() instanceof Phrase) { + addFont(((Phrase) rhf.content()).font()); + addColor(((Phrase) rhf.content()).font().color()); + } + } + if(hf.getBefore() != null) { + addFont(hf.getBefore().font()); + addColor(hf.getBefore().font().color()); + } + if(hf.getAfter() != null) { + addFont(hf.getAfter().font()); + addColor(hf.getAfter().font().color()); + } + } + + private void processHeaderFooter(HeaderFooter hf) { + if(hf != null) { + if(hf instanceof RtfHeaderFooters) { + RtfHeaderFooters rhf = (RtfHeaderFooters) hf; + if(rhf.get(RtfHeaderFooters.ALL_PAGES) != null) { + addHeaderFooterFontColor(rhf.get(RtfHeaderFooters.ALL_PAGES)); + } + if(rhf.get(RtfHeaderFooters.LEFT_PAGES) != null) { + addHeaderFooterFontColor(rhf.get(RtfHeaderFooters.LEFT_PAGES)); + } + if(rhf.get(RtfHeaderFooters.RIGHT_PAGES) != null) { + addHeaderFooterFontColor(rhf.get(RtfHeaderFooters.RIGHT_PAGES)); + } + if(rhf.get(RtfHeaderFooters.FIRST_PAGE) != null) { + addHeaderFooterFontColor(rhf.get(RtfHeaderFooters.FIRST_PAGE)); + } + } else { + addHeaderFooterFontColor(hf); + } + } + } + + /** + * @see com.lowagie.text.DocListener#setMarginMirroring(boolean) + */ + public boolean setMarginMirroring(boolean MarginMirroring) { + return false; + } + +} + -- cgit v1.2.3