001: /*
002: * $Id: PDFOutput.java,v 1.1.1.1 2001/10/29 19:51:08 ezb Exp $
003: *
004: * $Date: 2001/10/29 19:51:08 $
005: *
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: */
022: package gnu.jpdf;
023:
024: import java.io.*;
025: import java.util.*;
026:
027: /**
028: * This class is used to write a PDF document. It acts as a wrapper
029: * to a real OutputStream, but is necessary for certain internal PDF
030: * structures to be built correctly.
031: *
032: * @author Peter T. Mount
033: * @author Eric Z. Beard, ericzbeard@hotmail.com
034: * @author $Author: ezb $
035: * @version $Revision: 1.1.1.1 $, $Date: 2001/10/29 19:51:08 $
036: */
037: public class PDFOutput {
038: /**
039: * This is the actual OutputStream used to write to.
040: */
041: protected OutputStream os;
042:
043: /**
044: * This is the OutputStream used to write each object to.
045: *
046: * <p>We use a separate stream, because we need to keep track of how
047: * many bytes have been written for each object for the xref table to
048: * work correctly.
049: */
050: protected ByteArrayOutputStream baos;
051:
052: /**
053: * This is the current position within the stream
054: */
055: protected int offset;
056:
057: /**
058: * This vector contains offsets of each object
059: */
060: protected Vector offsets;
061:
062: /**
063: * This is used to track the /Root object (catalog)
064: */
065: protected PDFObject rootID;
066:
067: /**
068: * This is used to track the /Info object (info)
069: */
070: protected PDFObject infoID;
071:
072: /**
073: * This creates a PDF OutputStream
074: *
075: * @param os The output stream to write the PDF file to.
076: */
077: public PDFOutput(OutputStream os) throws IOException {
078: this .os = os;
079: offset = 0;
080: offsets = new Vector();
081: baos = new ByteArrayOutputStream();
082:
083: // Now write the PDF header
084: //
085: // Note: As the encoding is fixed here, we use getBytes().
086: //
087: baos.write("%PDF-1.2\n".getBytes());
088:
089: // This second comment is advised in the PDF Reference manual
090: // page 61
091: baos.write("%\342\343\317\323\n".getBytes());
092:
093: offset = baos.size();
094: baos.writeTo(os);
095: }
096:
097: /**
098: * This method writes a PDFObject to the stream.
099: *
100: * @param ob PDFObject Obeject to write
101: * @exception IOException on error
102: */
103: protected void write(PDFObject ob) throws IOException {
104: // Check the object to see if it's one that is needed in the trailer
105: // object
106: if (ob instanceof PDFCatalog)
107: rootID = ob;
108: if (ob instanceof PDFInfo)
109: infoID = ob;
110:
111: offsets.addElement(new PDFXref(ob.getSerialID(), offset));
112: baos.reset();
113: ob.write(baos);
114: offset += baos.size();
115: baos.writeTo(os);
116: }
117:
118: /**
119: * This closes the Stream, writing the xref table
120: */
121: protected void close() throws IOException {
122: // Make sure everything is written
123: os.flush();
124:
125: // we use baos to speed things up a little.
126: // Also, offset is preserved, and marks the begining of this block.
127: // This is required by PDF at the end of the PDF file.
128: baos.reset();
129: baos.write("xref\n".getBytes());
130:
131: // Now a single subsection for object 0
132: //baos.write("0 1\n0000000000 65535 f \n".getBytes());
133:
134: // Now scan through the offsets list. The should be in sequence,
135: // but just in case:
136: int firstid = 0; // First id in block
137: int lastid = -1; // The last id used
138: Vector block = new Vector(); // xrefs in this block
139:
140: // We need block 0 to exist
141: block.addElement(new PDFXref(0, 0, 65535));
142:
143: for (Enumeration en = offsets.elements(); en.hasMoreElements();) {
144: PDFXref x = (PDFXref) en.nextElement();
145:
146: if (firstid == -1)
147: firstid = x.id;
148:
149: // check to see if block is in range (-1 means empty)
150: if (lastid > -1 && x.id != (lastid + 1)) {
151: // no, so write this block, and reset
152: writeblock(firstid, block);
153: block.removeAllElements();
154: firstid = -1;
155: }
156:
157: // now add to block
158: block.addElement(x);
159: lastid = x.id;
160: }
161:
162: // now write the last block
163: if (firstid > -1)
164: writeblock(firstid, block);
165:
166: // now the trailer object
167: baos.write("trailer\n<<\n".getBytes());
168:
169: // the number of entries (REQUIRED)
170: baos.write("/Size ".getBytes());
171: baos.write(Integer.toString(offsets.size() + 1).getBytes());
172: baos.write("\n".getBytes());
173:
174: // the /Root catalog indirect reference (REQUIRED)
175: if (rootID != null) {
176: baos.write("/Root ".getBytes());
177: baos.write(rootID.toString().getBytes());
178: baos.write("\n".getBytes());
179: } else
180: throw new IOException(
181: "Root object is not present in document");
182:
183: // the /Info reference (OPTIONAL)
184: if (infoID != null) {
185: baos.write("/Info ".getBytes());
186: baos.write(infoID.toString().getBytes());
187: baos.write("\n".getBytes());
188: }
189:
190: // end the trailer object
191: baos.write(">>\nstartxref\n".getBytes());
192: baos.write(Integer.toString(offset).getBytes());
193: baos.write("\n%%EOF\n".getBytes());
194:
195: // now flush the stream
196: baos.writeTo(os);
197: os.flush();
198: }
199:
200: /**
201: * Writes a block of references to the PDF file
202: * @param firstid ID of the first reference in this block
203: * @param block Vector containing the references in this block
204: * @exception IOException on write error
205: */
206: protected void writeblock(int firstid, Vector block)
207: throws IOException {
208: baos.write(Integer.toString(firstid).getBytes());
209: baos.write(" ".getBytes());
210: baos.write(Integer.toString(block.size()).getBytes());
211: baos.write("\n".getBytes());
212: //baos.write("\n0000000000 65535 f\n".getBytes());
213:
214: for (Enumeration en = block.elements(); en.hasMoreElements();) {
215: baos.write(en.nextElement().toString().getBytes());
216: baos.write("\n".getBytes());
217: }
218: }
219: } // end class PDFOutput
|