All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itextpdf.kernel.pdf.PdfStream Maven / Gradle / Ivy

There is a newer version: 9.0.0
Show newest version
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2024 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see .
 */
package com.itextpdf.kernel.pdf;

import com.itextpdf.io.source.ByteArrayOutputStream;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
import com.itextpdf.kernel.utils.ICopyFilter;
import com.itextpdf.kernel.utils.NullCopyFilter;

import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;

/**
 * Representation of a stream as described in the PDF Specification.
 */
public class PdfStream extends PdfDictionary {


    protected int compressionLevel;
    // Output stream associated with PDF stream.
    protected PdfOutputStream outputStream;
    private InputStream inputStream;
    private long offset;
    private int length = -1;

    /**
     * Constructs a {@code PdfStream}-object.
     *
     * @param bytes            initial content of {@link PdfOutputStream}.
     * @param compressionLevel the compression level (0 = best speed, 9 = best compression, -1 is default)
     */
    public PdfStream(byte[] bytes, int compressionLevel) {
        super();
        setState(MUST_BE_INDIRECT);
        this.compressionLevel = compressionLevel;
        if (bytes != null && bytes.length > 0) {
            this.outputStream = new PdfOutputStream(new ByteArrayOutputStream(bytes.length));
            this.outputStream.writeBytes(bytes);
        } else {
            this.outputStream = new PdfOutputStream(new ByteArrayOutputStream());
        }
    }

    /**
     * Creates a PdfStream instance.
     *
     * @param bytes bytes to write to the PdfStream
     */
    public PdfStream(byte[] bytes) {
        this(bytes, CompressionConstants.UNDEFINED_COMPRESSION);
    }

    /**
     * Creates an efficient stream. No temporary array is ever created. The {@code InputStream}
     * is totally consumed but is not closed. The general usage is:
     * 
*
     * PdfDocument document = ?;
     * InputStream in = ?;
     * PdfStream stream = new PdfStream(document, in, PdfOutputStream.DEFAULT_COMPRESSION);
     * ?
     * stream.flush();
     * in.close();
     * 
* * @param doc the {@link PdfDocument pdf document} in which this stream lies * @param inputStream the data to write to this stream * @param compressionLevel the compression level (0 = best speed, 9 = best compression, -1 is default) */ public PdfStream(PdfDocument doc, InputStream inputStream, int compressionLevel) { super(); if (doc == null) { throw new PdfException( KernelExceptionMessageConstant.CANNOT_CREATE_PDFSTREAM_BY_INPUT_STREAM_WITHOUT_PDF_DOCUMENT); } makeIndirect(doc); if (inputStream == null) { throw new IllegalArgumentException("The input stream in PdfStream constructor can not be null."); } this.inputStream = inputStream; this.compressionLevel = compressionLevel; put(PdfName.Length, new PdfNumber(-1).makeIndirect(doc)); } /** * Creates an efficient stream. No temporary array is ever created. The {@code InputStream} * is totally consumed but is not closed. The general usage is: *
*
     * PdfDocument document = ?;
     * InputStream in = ?;
     * PdfStream stream = new PdfStream(document, in);
     * stream.flush();
     * in.close();
     * 
* * @param doc the {@link PdfDocument pdf document} in which this stream lies * @param inputStream the data to write to this stream */ public PdfStream(PdfDocument doc, InputStream inputStream) { this(doc, inputStream, CompressionConstants.UNDEFINED_COMPRESSION); } /** * Constructs a {@code PdfStream}-object. * * @param compressionLevel the compression level (0 = best speed, 9 = best compression, -1 is default) */ public PdfStream(int compressionLevel) { this(null, compressionLevel); } /** * Creates an empty PdfStream instance. */ public PdfStream() { this((byte[]) null); } protected PdfStream(java.io.OutputStream outputStream) { this.outputStream = new PdfOutputStream(outputStream); this.compressionLevel = CompressionConstants.UNDEFINED_COMPRESSION; setState(MUST_BE_INDIRECT); } //NOTE This constructor only for PdfReader. PdfStream(long offset, PdfDictionary keys) { super(); this.compressionLevel = CompressionConstants.UNDEFINED_COMPRESSION; this.offset = offset; putAll(keys); PdfNumber length = getAsNumber(PdfName.Length); if (length == null) { this.length = 0; } else { this.length = length.intValue(); } } /** * Gets output stream. * * @return output stream */ public PdfOutputStream getOutputStream() { return outputStream; } /** * Gets compression level of this PdfStream. * For more details @see {@link com.itextpdf.io.source.DeflaterOutputStream}. * * @return compression level. */ public int getCompressionLevel() { return compressionLevel; } /** * Sets compression level of this PdfStream. * For more details @see {@link com.itextpdf.io.source.DeflaterOutputStream}. * * @param compressionLevel the compression level (0 = best speed, 9 = best compression, -1 is default) */ public void setCompressionLevel(int compressionLevel) { this.compressionLevel = compressionLevel; } @Override public byte getType() { return STREAM; } public int getLength() { return length; } /** * Gets decoded stream bytes. * Note, {@link PdfName#DCTDecode} and {@link PdfName#JPXDecode} filters will be ignored. * * @return byte content of the {@code PdfStream}. Byte content will be {@code null}, * if the {@code PdfStream} was created by {@code InputStream}. */ public byte[] getBytes() { return getBytes(true); } /** * Gets stream bytes. * Note, {@link PdfName#DCTDecode} and {@link PdfName#JPXDecode} filters will be ignored. * * @param decoded true if to get decoded stream bytes, otherwise false. * @return byte content of the {@code PdfStream}. Byte content will be {@code null}, * if the {@code PdfStream} was created by {@code InputStream}. */ public byte[] getBytes(boolean decoded) { if (isFlushed()) { throw new PdfException(KernelExceptionMessageConstant.CANNOT_OPERATE_WITH_FLUSHED_PDF_STREAM); } if (inputStream != null) { LoggerFactory.getLogger(PdfStream.class).warn("PdfStream was created by InputStream." + "getBytes() always returns null in this case"); return null; } byte[] bytes = null; if (outputStream != null && outputStream.getOutputStream() != null) { assert outputStream.getOutputStream() instanceof ByteArrayOutputStream : "Invalid OutputStream: ByteArrayByteArrayOutputStream expected"; try { outputStream.getOutputStream().flush(); bytes = ((ByteArrayOutputStream) outputStream.getOutputStream()).toByteArray(); if (decoded && containsKey(PdfName.Filter)) { bytes = PdfReader.decodeBytes(bytes, this); } } catch (IOException ioe) { throw new PdfException(KernelExceptionMessageConstant.CANNOT_GET_PDF_STREAM_BYTES, ioe, this); } } else if (getIndirectReference() != null) { // This logic makes sense only for the case when PdfStream was created by reader and in this // case PdfStream instance always has indirect reference and is never in the MustBeIndirect state PdfReader reader = getIndirectReference().getReader(); if (reader != null) { try { bytes = reader.readStreamBytes(this, decoded); } catch (IOException ioe) { throw new PdfException(KernelExceptionMessageConstant.CANNOT_GET_PDF_STREAM_BYTES, ioe, this); } } } return bytes; } /** * Sets bytes as stream's content. * Could not be used with streams which were created by InputStream. * * @param bytes new content for stream; if null then stream's content will be discarded */ public void setData(byte[] bytes) { setData(bytes, false); } /** * Sets or appends bytes to stream content. * Could not be used with streams which were created by InputStream. * * @param bytes New content for stream. These bytes are considered to be a raw data (i.e. not encoded/compressed/encrypted) * and if it's not true, the corresponding filters shall be set to the PdfStream object manually. * Data compression generally should be configured via {@link PdfStream#setCompressionLevel} and * is handled on stream writing to the output document. * If null and append is false then stream's content will be discarded. * @param append If set to true then bytes will be appended to the end, * rather then replace original content. The original content will be decoded if needed. */ public void setData(byte[] bytes, boolean append) { if (isFlushed()) { throw new PdfException(KernelExceptionMessageConstant.CANNOT_OPERATE_WITH_FLUSHED_PDF_STREAM); } if (inputStream != null) { throw new PdfException( KernelExceptionMessageConstant.CANNOT_SET_DATA_TO_PDF_STREAM_WHICH_WAS_CREATED_BY_INPUT_STREAM); } boolean outputStreamIsUninitialized = outputStream == null; if (outputStreamIsUninitialized) { outputStream = new PdfOutputStream(new ByteArrayOutputStream()); } if (append) { if (outputStreamIsUninitialized && getIndirectReference() != null && getIndirectReference().getReader() != null || !outputStreamIsUninitialized && containsKey(PdfName.Filter)) { // here is the same as in the getBytes() method: this logic makes sense only when stream is created // by reader and in this case indirect reference won't be null and stream is not in the MustBeIndirect state. byte[] oldBytes; try { oldBytes = getBytes(); } catch (PdfException ex) { throw new PdfException( KernelExceptionMessageConstant.CANNOT_READ_A_STREAM_IN_ORDER_TO_APPEND_NEW_BYTES, ex); } outputStream.assignBytes(oldBytes, oldBytes.length); } if (bytes != null) { outputStream.writeBytes(bytes); } } else { if (bytes != null) { outputStream.assignBytes(bytes, bytes.length); } else { outputStream.reset(); } } offset = 0; // Bytes that are set shall be not encoded, and moreover the existing bytes in cases of the appending are decoded, // therefore all filters shall be removed. Compression will be handled on stream flushing. remove(PdfName.Filter); remove(PdfName.DecodeParms); } @Override protected PdfObject newInstance() { return new PdfStream(); } protected long getOffset() { return offset; } /** * Update length manually in case its correction. * * @param length the new length * @see com.itextpdf.kernel.pdf.PdfReader#checkPdfStreamLength(PdfStream) */ protected void updateLength(int length) { this.length = length; } @Override protected void copyContent(PdfObject from, PdfDocument document) { copyContent(from, document, NullCopyFilter.getInstance()); } @Override protected void copyContent(PdfObject from, PdfDocument document, ICopyFilter copyFilter) { super.copyContent(from, document, copyFilter); PdfStream stream = (PdfStream) from; assert inputStream == null : "Try to copy the PdfStream that has been just created."; byte[] bytes = stream.getBytes(false); try { outputStream.write(bytes); } catch (IOException ioe) { throw new PdfException(KernelExceptionMessageConstant.CANNOT_COPY_OBJECT_CONTENT, ioe, stream); } } protected void initOutputStream(java.io.OutputStream stream) { if (getOutputStream() == null && inputStream == null) outputStream = new PdfOutputStream(stream != null ? stream : new ByteArrayOutputStream()); } /** * Release content of PdfStream. */ protected void releaseContent() { super.releaseContent(); try { if (outputStream != null) { outputStream.close(); outputStream = null; } } catch (IOException e) { throw new PdfException(KernelExceptionMessageConstant.IO_EXCEPTION, e); } } protected InputStream getInputStream() { return inputStream; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy