/*
* $Id: RtfWriter.java 2849 2007-06-20 19:31:09Z xlv $
* $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.google.code.appengine.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.ListIterator;
import com.lowagie.text.Anchor;
import com.lowagie.text.Annotation;
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.HeaderFooter;
import com.lowagie.text.Image;
import com.lowagie.text.ListItem;
import com.lowagie.text.Meta;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.Rectangle;
import com.lowagie.text.Section;
import com.lowagie.text.SimpleTable;
import com.lowagie.text.Table;
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 <CODE>DocWriter</CODE> class for Rich Text Files (RTF).
* <P>
* A <CODE>RtfWriter</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>RtfWriter</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 RTF to the Standard OutputStream
* <STRONG>RtfWriter.getInstance(document, System.out);</STRONG>
* // this will write Rtf to a file called text.rtf
* <STRONG>RtfWriter.getInstance(document, new FileOutputStream("text.rtf"));</STRONG>
* // this will write Rtf to for instance the OutputStream of a HttpServletResponse-object
* <STRONG>RtfWriter.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>
* <P>
* <STRONG>LIMITATIONS</STRONG><BR>
* There are currently still a few limitations on what the RTF Writer can do:
* <ul>
* <li>Watermarks</li>
* <li>Viewer preferences</li>
* <li>Encryption</li>
* <li>Embedded fonts</li>
* <li>Phrases with a leading</li>
* <li>Lists with non-bullet symbols</li>
* <li>Nested tables</li>
* <li>Images other than JPEG and PNG</li>
* <li>Rotated images</li>
* </ul>
* <br />
*
* @author Mark Hall (mhall@edu.uni-klu.ac.at)
* @author Steffen Stundzig (Steffen.Stundzig@smb-tec.com)
* @author Eric Mattes (ericmattes@yahoo.com)
* @author Raul Wegmann (raul.wegmann@uam.es)
* @deprecated The RtfWriter is deprecated and will be removed from the iText library end of 2007
*/
public class RtfWriter extends DocWriter {
/**
* 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 <code>ArrayList</code> contains all fonts used in the document. */
private ArrayList fontList = new ArrayList();
/** This <code>ArrayList</code> contains all colours used in the document. */
private ArrayList colorList = new ArrayList();
/** This <code>ByteArrayOutputStream</code> contains the main body of the document. */
private ByteArrayOutputStream content = null;
/** This <code>ByteArrayOutputStream</code> contains the information group. */
private ByteArrayOutputStream info = null;
/** This <code>ByteArrayOutputStream</code> contains the list table. */
private ByteArrayOutputStream listtable = null;
/** This <code>ByteArrayOutputStream</code> 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 <CODE>RtfWriter</CODE>.
*
* @param doc The <CODE>Document</CODE> that is to be written as RTF
* @param os The <CODE>OutputStream</CODE> 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 <CODE>RtfWriter</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>RtfWriter</CODE>
*/
public static RtfWriter getInstance(Document document, OutputStream os) {
return new RtfWriter(document, os);
}
/**
* Signals that the <CODE>Document</CODE> was closed and that no other
* <CODE>Elements</CODE> will be added.
* <p>
* The content of the font table, color table, information group, content, header, footer are merged into the final
* <code>OutputStream</code>
*/
public void close() {
if (open) {
writeDocument();
super.close();
}
}
/**
* Adds the footer to the bottom of the <CODE>Document</CODE>.
* @param footer
*/
public void setFooter(HeaderFooter footer) {
this.footer = footer;
processHeaderFooter(this.footer);
}
/**
* Adds the header to the top of the <CODE>Document</CODE>.
* @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 <code>RtfWriter</code> that a new page is to be begun.
*
* @return <code>true</code> if a new Page was begun.
* @throws DocumentException if the Document was not open or had been closed.
*/
public boolean newPage() {
try {
content.write(escape);
content.write(newPage);
content.write(escape);
content.write(paragraph);
} catch (IOException e) {
throw new ExceptionConverter(e);
}
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 <code>true</code> 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 <code>Rectangle</code> specifying the page size
*
* @return <code>true</code> if the page size was set
*/
public boolean setPageSize(Rectangle pageSize) {
if (!parseFormat(pageSize, false)) {
pageWidth = (int) (pageSize.getWidth() * TWIPSFACTOR);
pageHeight = (int) (pageSize.getHeight() * 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 <code>Font</code> 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 <code>Font</code> to specify what Font to use when showTOCasEntry is true
*
* @return <code>true</code> 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 <CODE>Element</CODE> was added to the <CODE>Document</CODE>.
*
* @param element A high level object to add
* @return <CODE>true</CODE> if the element was added, <CODE>false</CODE> 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 <CODE>Element</CODE> to the <CODE>Document</CODE>.
* @param element the high level element to add
* @param out the outputstream to which the RTF data is sent
* @return <CODE>true</CODE> if the element was added, <CODE>false</CODE> 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 <code>Section</code>
*
* @param sectionElement The <code>Section</code> be written
* @param out The <code>ByteArrayOutputStream</code> 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.getTitle() != null) {
if (writeTOC) {
StringBuffer title = new StringBuffer("");
for (ListIterator li = sectionElement.getTitle().getChunks().listIterator(); li.hasNext();) {
title.append(((Chunk) li.next()).getContent());
}
add(new RtfTOCEntry(title.toString(), sectionElement.getTitle().getFont()));
} else {
add(sectionElement.getTitle());
}
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 <code>Paragraph</code>
*
* @param paragraphElement The <code>Paragraph</code> to be written
* @param out The <code>ByteArrayOutputStream</code> 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.getAlignment()) {
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.getIndentationLeft() * TWIPSFACTOR));
out.write(escape);
out.write(rightIndent);
writeInt(out, (int) (paragraphElement.getIndentationRight() * TWIPSFACTOR));
Iterator chunks = paragraphElement.getChunks().iterator();
while (chunks.hasNext()) {
Chunk ch = (Chunk) chunks.next();
ch.setFont(paragraphElement.getFont().difference(ch.getFont()));
}
ByteArrayOutputStream save = content;
content = out;
paragraphElement.process(this);
content = save;
if (!inTable) {
out.write(escape);
out.write(paragraph);
}
}
/**
* Write a <code>Phrase</code>.
*
* @param phrase The <code>Phrase</code> item to be written
* @param out The <code>ByteArrayOutputStream</code> 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.getFont().difference(ch.getFont()));
}
ByteArrayOutputStream save = content;
content = out;
phrase.process(this);
content = save;
}
/**
* Write an <code>Anchor</code>. Anchors are treated like Phrases.
*
* @param anchor The <code>Chunk</code> item to be written
* @param out The <code>ByteArrayOutputStream</code> to write to
*
* @throws IOException
*/
private void writeAnchor(Anchor anchor, ByteArrayOutputStream out) throws IOException {
if (anchor.getUrl() != 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.getUrl().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 <code>Chunk</code> and all its font properties.
*
* @param chunk The <code>Chunk</code> item to be written
* @param out The <code>ByteArrayOutputStream</code> 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.getContent(), false).getBytes());
writeFinishingFontSignature(out, chunk);
}
}
}
protected void writeInitialFontSignature(OutputStream out, Chunk chunk) throws IOException {
Font font = chunk.getFont();
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.getSize() > 0) {
writeInt(out, (int) (font.getSize() * 2));
} else {
writeInt(out, 20);
}
out.write(escape);
out.write(fontColor);
writeInt(out, addColor(font.getColor()));
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.getFont();
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 <code>ListItem</code>
*
* @param listItem The <code>ListItem</code> to be written
* @param out The <code>ByteArrayOutputStream</code> 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 <code>List</code>
*
* @param list The <code>List</code> to be written
* @param out The <code>ByteArrayOutputStream</code> 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.getIndentationLeft() * TWIPSFACTOR * -1));
listtable.write(escape);
listtable.write(listIndent);
writeInt(listtable, (int) ((list.getIndentationLeft() + list.getSymbolIndent()) * TWIPSFACTOR));
listtable.write(escape);
listtable.write(rightIndent);
writeInt(listtable, (int) (list.getIndentationRight() * TWIPSFACTOR));
listtable.write(escape);
listtable.write(tabStop);
writeInt(listtable, (int) (list.getSymbolIndent() * 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.getIndentationLeft() * TWIPSFACTOR * -1));
out.write(escape);
out.write(listIndent);
writeInt(out, (int) ((list.getIndentationLeft() + list.getSymbolIndent()) * TWIPSFACTOR));
out.write(escape);
out.write(rightIndent);
writeInt(out, (int) (list.getIndentationRight() * 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.getIndentationLeft() * TWIPSFACTOR * -1));
out.write(escape);
out.write(listIndent);
writeInt(out, (int) ((list.getIndentationLeft() + list.getSymbolIndent()) * TWIPSFACTOR));
out.write(escape);
out.write(rightIndent);
writeInt(out, (int) (list.getIndentationRight() * 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.getIndentationLeft() * TWIPSFACTOR * -1));
out.write(escape);
out.write(listIndent);
writeInt(out, (int) ((list.getIndentationLeft() + list.getSymbolIndent()) * TWIPSFACTOR));
out.write(escape);
out.write(rightIndent);
writeInt(out, (int) (list.getIndentationRight() * 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 <code>Table</code>.
*
* @param table The <code>table</code> to be written
* @param out The <code>ByteArrayOutputStream</code> 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 <code>Image</code>.
*
* @param image The <code>image</code> to be written
* @param out The <code>ByteArrayOutputStream</code> 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.getAlignment()) {
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.getPlainWidth() * TWIPSFACTOR));
out.write(escape);
out.write(pictureHeight);
writeInt(out, (int) (image.getPlainHeight() * 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.getWidth() > 0) {
out.write(escape);
out.write(pictureScaleX);
writeInt(out, (int) (100 / image.getWidth() * image.getPlainWidth()));
}
if (image.getHeight() > 0) {
out.write(escape);
out.write(pictureScaleY);
writeInt(out, (int) (100 / image.getHeight() * image.getPlainHeight()));
}
out.write(delimiter);
InputStream imgIn;
if (type == Image.ORIGINAL_BMP) {
imgIn = new ByteArrayInputStream(MetaDo.wrapBMP(image));
}
else {
if (image.getOriginalData() == null) {
imgIn = image.getUrl().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 <code>Annotation</code>
*
* @param annotationElement The <code>Annotation</code> to be written
* @param out The <code>ByteArrayOutputStream</code> 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 <code>Meta</code> element. It is written to the Inforamtion Group
* and merged with the main <code>ByteArrayOutputStream</code> when the
* Document is closed.
*
* @param metaName The type of <code>Meta</code> element to be added
* @param meta The <code>Meta</code> 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.getContent());
} else {
info.write(meta.getContent().getBytes());
}
} finally {
info.write(closeGroup);
}
}
/**
* Writes a date. The date is formated <strong>Year, Month, Day, Hour, Minute, Second</strong>
*
* @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 <code>Font</code> to the list of fonts. If the <code>Font</code>
* already exists in the list of fonts, then it is not added again.
*
* @param newFont The <code>Font</code> to be added
*
* @return The index of the <code>Font</code> 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 <code>Color</code> to the list of colours. If the <code>Color</code>
* already exists in the list of colours, then it is not added again.
*
* @param newColor The <code>Color</code> to be added
*
* @return The index of the <code>color</code> 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 <code>ArrayList</code>s and <code>ByteArrayOutputStream</code>s
* to the final <code>ByteArrayOutputStream</code>
*
* @return <code>true</code> if all information was sucessfully written to the <code>ByteArrayOutputStream</code>
*/
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 <code>ByteArrayOutputStream</code>
* @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 <code>ByteArrayOutputStream</code>
* @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 <code>ByteArrayOutputStream</code>
* @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 <code>ByteArrayOutputStream</code>
* @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 <code>OuputStream</code> to which the <code>int</code> value is to be written
* @param i The <code>int</code> 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 <b>unique</b> random integer to be used with listids.
*
* @return Random <code>int</code> 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 <code>ByteArrayOutputStream</code>
*
* @param os The <code>ByteArrayOutputStream</code> 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 <code>HeaderFooter</code> to a <code>ByteArrayOutputStream</code>
*
* @param headerFooter The <code>HeaderFooter</code> object to be written.
* @param hfType The type of header or footer to be added.
* @param target The <code>ByteArrayOutputStream</code> to which the <code>HeaderFooter</code> 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().getFont()));
}
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 <code>Document</code>'s Paper and Margin Size
* to the final <code>ByteArrayOutputStream</code>
* @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 <code>ByteArrayOutputStream</code>'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 <code>ByteArrayOutputStream</code> 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 <code>Rectangle pageSize</code> 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.getWidth() == rect2.getWidth()) && (rect1.getHeight() == rect2.getHeight());
}
/**
* 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 <code>String</code>
* @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()).getFont());
addColor(((Chunk) rhf.content()).getFont().getColor());
} else if(rhf.content() instanceof Phrase) {
addFont(((Phrase) rhf.content()).getFont());
addColor(((Phrase) rhf.content()).getFont().getColor());
}
}
if(hf.getBefore() != null) {
addFont(hf.getBefore().getFont());
addColor(hf.getBefore().getFont().getColor());
}
if(hf.getAfter() != null) {
addFont(hf.getAfter().getFont());
addColor(hf.getAfter().getFont().getColor());
}
}
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;
}
}