From 2b381dee7015e89e9dfcaa31bf3f28af1c7b42d9 Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Thu, 27 Mar 2003 10:14:24 +0000 Subject: [PATCH] Abstract base class for all PDF streams. It supports writing stream on-the-fly (Length dict entry is a forward reference). This can be disabled in PDFDocument, the old method still works. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@196149 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/pdf/AbstractPDFStream.java | 260 ++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 src/java/org/apache/fop/pdf/AbstractPDFStream.java diff --git a/src/java/org/apache/fop/pdf/AbstractPDFStream.java b/src/java/org/apache/fop/pdf/AbstractPDFStream.java new file mode 100644 index 000000000..083e3d2eb --- /dev/null +++ b/src/java/org/apache/fop/pdf/AbstractPDFStream.java @@ -0,0 +1,260 @@ +/* + * $Id$ + * ============================================================================ + * The Apache Software License, Version 1.1 + * ============================================================================ + * + * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: "This product includes software + * developed by the Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "FOP" and "Apache Software Foundation" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", nor may + * "Apache" appear in their name, without prior written permission of the + * Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * James Tauber . For more information on the Apache + * Software Foundation, please see . + */ +package org.apache.fop.pdf; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.commons.io.output.CountingOutputStream; +import org.apache.fop.util.CloseBlockerOutputStream; + +/** + * This is an abstract base class for PDF streams. + */ +public abstract class AbstractPDFStream extends PDFObject { + + /** The filters that should be applied */ + private PDFFilterList filters; + + /** + * Constructor for AbstractPDFStream. + */ + public AbstractPDFStream() { + super(); + } + + /** + * Sets up the default filters for this stream if they haven't been set + * from outside. + */ + protected void setupFilterList() { + if (!getFilterList().isInitialized()) { + getFilterList().addDefaultFilters( + getDocumentSafely().getFilterMap(), + PDFFilterList.DEFAULT_FILTER); + } + prepareImplicitFilters(); + getDocument().applyEncryption(this); + } + + /** + * Returns the associated filter list. + * @return the filter list + */ + public PDFFilterList getFilterList() { + if (this.filters == null) { + if (getDocument() == null) { + this.filters = new PDFFilterList(); + } else { + this.filters = new PDFFilterList(getDocument().isEncryptionActive()); + //this.filters = new PDFFilterList(false); + } + } + return this.filters; + } + + /** + * Returns a value that hints at the size of the encoded stream. This is + * used to optimize buffer allocation so fewer buffer reallocations are + * necessary. + * @return an estimated size (0 if no hint can be given) + * @throws IOException in case of an I/O problem + */ + protected abstract int getSizeHint() throws IOException; + + /** + * Sends the raw stream data to the target OutputStream. + * @param out OutputStream to write to + * @throws IOException In case of an I/O problem + */ + protected abstract void outputRawStreamData(OutputStream out) + throws IOException; + + /** + * Output just the stream data enclosed by stream/endstream markers + * @param encodedStream already encoded/filtered stream to write + * @param out OutputStream to write to + * @return int number of bytes written + * @throws IOException in case of an I/O problem + */ + protected int outputStreamData(StreamCache encodedStream, OutputStream out) throws IOException { + int length = 0; + byte[] p = encode("stream\n"); + out.write(p); + length += p.length; + + encodedStream.outputContents(out); + length += encodedStream.getSize(); + + p = encode("\nendstream"); + out.write(p); + length += p.length; + return length; + } + + /** + * Encodes the raw data stream for output to a PDF file. + * @return the encoded stream + * @throws IOException in case of an I/O problem + */ + protected StreamCache encodeStream() throws IOException { + //Allocate a temporary buffer to find out the size of the encoded stream + final StreamCache encodedStream = StreamCacheFactory.getInstance(). + createStreamCache(getSizeHint()); + OutputStream filteredOutput = + getFilterList().applyFilters(encodedStream.getOutputStream()); + outputRawStreamData(filteredOutput); + filteredOutput.flush(); + filteredOutput.close(); + return encodedStream; + } + + /** + * Encodes and writes a stream directly to an OutputStream. The length of + * the stream, in this case, is set on a PDFNumber object that has to be + * prepared beforehand. + * @param out OutputStream to write to + * @param refLength PDFNumber object to receive the stream length + * @return number of bytes written (header and trailer included) + * @throws IOException in case of an I/O problem + */ + protected int encodeAndWriteStream(OutputStream out, PDFNumber refLength) + throws IOException { + int bytesWritten = 0; + //Stream header + byte[] buf = encode("stream\n"); + out.write(buf); + bytesWritten += buf.length; + + //Stream contents + CloseBlockerOutputStream cbout = new CloseBlockerOutputStream(out); + CountingOutputStream cout = new CountingOutputStream(cbout); + OutputStream filteredOutput = + getFilterList().applyFilters(cout); + outputRawStreamData(filteredOutput); + filteredOutput.close(); + refLength.setNumber(new Integer(cout.getCount())); + bytesWritten += cout.getCount(); + + //Stream trailer + buf = encode("\nendstream"); + out.write(buf); + bytesWritten += buf.length; + + return bytesWritten; + } + + /** + * Overload the base object method so we don't have to copy + * byte arrays around so much + * @see org.apache.fop.pdf.PDFObject#output(OutputStream) + */ + protected int output(OutputStream stream) throws IOException { + int length = 0; + setupFilterList(); + + StreamCache encodedStream = null; + PDFNumber refLength = null; + final String lengthEntry; + if (getDocument().isEncodingOnTheFly()) { + refLength = new PDFNumber(); + getDocumentSafely().registerObject(refLength); + lengthEntry = refLength.referencePDF(); + } else { + encodedStream = encodeStream(); + lengthEntry = Integer.toString(encodedStream.getSize() + 1); + } + + String filterEntry = getFilterList().buildFilterDictEntries(); + byte[] p = encode(buildStreamDict(lengthEntry)); + + stream.write(p); + length += p.length; + + //Send encoded stream to target OutputStream + if (encodedStream == null) { + int bytesWritten = encodeAndWriteStream(stream, refLength); + length += bytesWritten; + } else { + length += outputStreamData(encodedStream, stream); + encodedStream.clear(); //Encoded stream can now be discarded + } + + p = encode("\nendobj\n"); + stream.write(p); + length += p.length; + return length; + } + + /** + * Constructs the dictionary for the stream. Override this method if you + * need additional entries. + * @param lengthEntry value for the /Length entry + * @return the newly constructed dictionary + */ + protected String buildStreamDict(String lengthEntry) { + final String filterEntry = getFilterList().buildFilterDictEntries(); + return (getObjectID() + + "<< /Length " + lengthEntry + "\n" + + filterEntry + + "\n>>\n"); + } + + /** + * Prepares implicit filters (such as the DCTFilter for JPEG images). You + * must make sure that the appropriate filters are in the filter list at + * the right places. + */ + protected void prepareImplicitFilters() { + //nop: No default implicit filters + } + +} -- 2.39.5