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

com.aowagie.text.pdf.PdfCopy Maven / Gradle / Ivy

/*
 * $Id: PdfCopy.java 3912 2009-04-26 08:38:15Z blowagie $
 *
 * Copyright (C) 2002 Mark Thompson
 *
 * 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.aowagie.text.pdf;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;

import com.aowagie.text.Document;
import com.aowagie.text.DocumentException;
import com.aowagie.text.ExceptionConverter;
import com.aowagie.text.Rectangle;

/**
 * Make copies of PDF documents. Documents can be edited after reading and
 * before writing them out.
 * @author Mark Thompson
 */

class PdfCopy extends PdfWriter {
    /**
     * This class holds information about indirect references, since they are
     * renumbered by iText.
     */
    private static class IndirectReferences {
        private final PdfIndirectReference theRef;
        private boolean hasCopied;
        private IndirectReferences(final PdfIndirectReference ref) {
            this.theRef = ref;
            this.hasCopied = false;
        }
        private void setCopied() { this.hasCopied = true; }
        private boolean getCopied() { return this.hasCopied; }
        private PdfIndirectReference getRef() { return this.theRef; }
    };
    private HashMap indirects;
    private final HashMap indirectMap;

    private PdfReader reader;
    private PdfIndirectReference acroForm;
    private final int[] namePtr = {0};
    /** Holds value of property rotateContents. */
    private boolean rotateContents = true;
    private PdfArray fieldArray;
    private HashMap fieldTemplates;

    /**
     * A key to allow us to hash indirect references
     */
    private static class RefKey {
        private final int num;
        private final int gen;


        private RefKey(final PRIndirectReference ref) {
            this.num = ref.getNumber();
            this.gen = ref.getGeneration();
        }
        @Override
		public int hashCode() {
            return (this.gen<<16)+this.num;
        }
        @Override
		public boolean equals(final Object o) {
            if (!(o instanceof RefKey)) {
				return false;
			}
            final RefKey other = (RefKey)o;
            return this.gen == other.gen && this.num == other.num;
        }
        @Override
		public String toString() {
            return Integer.toString(this.num) + ' ' + this.gen;
        }
    }

    /**
     * Constructor
     * @param document
     * @param os outputstream
     * @param globalDate
     */
    PdfCopy(final Document document, final OutputStream os, final Calendar globalDate) throws DocumentException {
        super(new PdfDocument(globalDate), os);
        document.addDocListener(this.pdf);
        this.pdf.addWriter(this);
        this.indirectMap = new HashMap();
    }

    /** Getter for property rotateContents.
     * @return Value of property rotateContents.
     *
     */
    public boolean isRotateContents() {
        return this.rotateContents;
    }

    /** Setter for property rotateContents.
     * @param rotateContents New value of property rotateContents.
     *
     */
    public void setRotateContents(final boolean rotateContents) {
        this.rotateContents = rotateContents;
    }

    /**
     * Grabs a page from the input document
     * @param reader the reader of the document
     * @param pageNumber which page to get
     * @return the page
     */
    @Override
	public PdfImportedPage getImportedPage(final PdfReader reader, final int pageNumber) {
        if (this.currentPdfReaderInstance != null) {
            if (this.currentPdfReaderInstance.getReader() != reader) {
                try {
                    this.currentPdfReaderInstance.getReader().close();
                    this.currentPdfReaderInstance.getReaderFile().close();
                }
                catch (final IOException ioe) {
                    // empty on purpose
                }
                this.currentPdfReaderInstance = reader.getPdfReaderInstance(this);
            }
        }
        else {
            this.currentPdfReaderInstance = reader.getPdfReaderInstance(this);
        }
        return this.currentPdfReaderInstance.getImportedPage(pageNumber);
    }


    /**
     * Translate a PRIndirectReference to a PdfIndirectReference
     * In addition, translates the object numbers, and copies the
     * referenced object to the output file.
     * NB: PRIndirectReferences (and PRIndirectObjects) really need to know what
     * file they came from, because each file has its own namespace. The translation
     * we do from their namespace to ours is *at best* heuristic, and guaranteed to
     * fail under some circumstances.
     */
    private PdfIndirectReference copyIndirect(final PRIndirectReference in) throws IOException, BadPdfFormatException {
        PdfIndirectReference theRef;
        final RefKey key = new RefKey(in);
        IndirectReferences iRef = (IndirectReferences)this.indirects.get(key);
        if (iRef != null) {
            theRef = iRef.getRef();
            if (iRef.getCopied()) {
                return theRef;
            }
        }
        else {
            theRef = this.body.getPdfIndirectReference();
            iRef = new IndirectReferences(theRef);
            this.indirects.put(key, iRef);
        }
        PdfObject obj = PdfReader.getPdfObjectRelease(in);
        if (obj != null && obj.isDictionary()) {
            final PdfObject type = PdfReader.getPdfObjectRelease(((PdfDictionary)obj).get(PdfName.TYPE));
            if (type != null && PdfName.PAGE.equals(type)) {
                return theRef;
            }
        }
        iRef.setCopied();
        obj = copyObject(obj);
        addToBody(obj, theRef);
        return theRef;
    }

    /**
     * Translate a PRDictionary to a PdfDictionary. Also translate all of the
     * objects contained in it.
     */
    private PdfDictionary copyDictionary(final PdfDictionary in)
    throws IOException, BadPdfFormatException {
        final PdfDictionary out = new PdfDictionary();
        final PdfObject type = PdfReader.getPdfObjectRelease(in.get(PdfName.TYPE));

        for (final Object element : in.getKeys()) {
            final PdfName key = (PdfName)element;
            final PdfObject value = in.get(key);
            //	    System.out.println("Copy " + key);
            if (type != null && PdfName.PAGE.equals(type)) {
                if (!key.equals(PdfName.B) && !key.equals(PdfName.PARENT)) {
					out.put(key, copyObject(value));
				}
            } else {
				out.put(key, copyObject(value));
			}
        }
        return out;
    }

    /**
     * Translate a PRStream to a PdfStream. The data part copies itself.
     */
    private PdfStream copyStream(final PRStream in) throws IOException, BadPdfFormatException {
        final PRStream out = new PRStream(in, null);

        for (final Object element : in.getKeys()) {
            final PdfName key = (PdfName) element;
            final PdfObject value = in.get(key);
            out.put(key, copyObject(value));
        }

        return out;
    }


    /**
     * Translate a PRArray to a PdfArray. Also translate all of the objects contained
     * in it
     */
    private PdfArray copyArray(final PdfArray in) throws IOException, BadPdfFormatException {
        final PdfArray out = new PdfArray();

        for (final Iterator i = in.listIterator(); i.hasNext();) {
            final PdfObject value = (PdfObject)i.next();
            out.add(copyObject(value));
        }
        return out;
    }

    /**
     * Translate a PR-object to a Pdf-object
     */
    private PdfObject copyObject(final PdfObject in) throws IOException,BadPdfFormatException {
        if (in == null) {
			return PdfNull.PDFNULL;
		}
        switch (in.type) {
            case PdfObject.DICTIONARY:
                //	        System.out.println("Dictionary: " + in.toString());
                return copyDictionary((PdfDictionary)in);
            case PdfObject.INDIRECT:
                return copyIndirect((PRIndirectReference)in);
            case PdfObject.ARRAY:
                return copyArray((PdfArray)in);
            case PdfObject.NUMBER:
            case PdfObject.NAME:
            case PdfObject.STRING:
            case PdfObject.NULL:
            case PdfObject.BOOLEAN:
            case 0:
                return in;
            case PdfObject.STREAM:
                return copyStream((PRStream)in);
                //                return in;
            default:
                if (in.type < 0) {
                    final String lit = ((PdfLiteral)in).toString();
                    if (lit.equals("true") || lit.equals("false")) {
                        return new PdfBoolean(lit);
                    }
                    return new PdfLiteral(lit);
                }
                System.out.println("CANNOT COPY type " + in.type);
                return null;
        }
    }

    /**
     * convenience method. Given an imported page, set our "globals"
     */
    private int setFromIPage(final PdfImportedPage iPage) {
        final int pageNum = iPage.getPageNumber();
        final PdfReaderInstance inst = this.currentPdfReaderInstance = iPage.getPdfReaderInstance();
        this.reader = inst.getReader();
        setFromReader(this.reader);
        return pageNum;
    }

    /**
     * convenience method. Given a reader, set our "globals"
     */
    private void setFromReader(final PdfReader reader) {
        this.reader = reader;
        this.indirects = (HashMap)this.indirectMap.get(reader);
        if (this.indirects == null) {
            this.indirects = new HashMap();
            this.indirectMap.put(reader,this.indirects);
            final PdfDictionary catalog = reader.getCatalog();
            PRIndirectReference ref = null;
            final PdfObject o = catalog.get(PdfName.ACROFORM);
            if (o == null || o.type() != PdfObject.INDIRECT) {
				return;
			}
            ref = (PRIndirectReference)o;
            if (this.acroForm == null) {
				this.acroForm = this.body.getPdfIndirectReference();
			}
            this.indirects.put(new RefKey(ref), new IndirectReferences(this.acroForm));
        }
    }






    /*
     * the getCatalog method is part of PdfWriter.
     * we wrap this so that we can extend it
     */
    @Override
	protected PdfDictionary getCatalog(final PdfIndirectReference rootObj) {
        try {
            final PdfDictionary theCat = this.pdf.getCatalog(rootObj);
            if (this.fieldArray == null) {
                if (this.acroForm != null) {
					theCat.put(PdfName.ACROFORM, this.acroForm);
				}
            } else {
				addFieldResources(theCat);
			}
            return theCat;
        }
        catch (final IOException e) {
            throw new ExceptionConverter(e);
        }
    }

    private void addFieldResources(final PdfDictionary catalog) throws IOException {
        if (this.fieldArray == null) {
			return;
		}
        final PdfDictionary acroForm = new PdfDictionary();
        catalog.put(PdfName.ACROFORM, acroForm);
        acroForm.put(PdfName.FIELDS, this.fieldArray);
        acroForm.put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g "));
        if (this.fieldTemplates.isEmpty()) {
			return;
		}
        final PdfDictionary dr = new PdfDictionary();
        acroForm.put(PdfName.DR, dr);
        for (final Iterator it = this.fieldTemplates.keySet().iterator(); it.hasNext();) {
            final PdfTemplate template = (PdfTemplate)it.next();
            PdfFormField.mergeResources(dr, (PdfDictionary)template.getResources());
        }
        // if (dr.get(PdfName.ENCODING) == null) dr.put(PdfName.ENCODING, PdfName.WIN_ANSI_ENCODING);
        PdfDictionary fonts = dr.getAsDict(PdfName.FONT);
        if (fonts == null) {
            fonts = new PdfDictionary();
            dr.put(PdfName.FONT, fonts);
        }
        if (!fonts.contains(PdfName.HELV)) {
            final PdfDictionary dic = new PdfDictionary(PdfName.FONT);
            dic.put(PdfName.BASEFONT, PdfName.HELVETICA);
            dic.put(PdfName.ENCODING, PdfName.WIN_ANSI_ENCODING);
            dic.put(PdfName.NAME, PdfName.HELV);
            dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
            fonts.put(PdfName.HELV, addToBody(dic).getIndirectReference());
        }
        if (!fonts.contains(PdfName.ZADB)) {
            final PdfDictionary dic = new PdfDictionary(PdfName.FONT);
            dic.put(PdfName.BASEFONT, PdfName.ZAPFDINGBATS);
            dic.put(PdfName.NAME, PdfName.ZADB);
            dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
            fonts.put(PdfName.ZADB, addToBody(dic).getIndirectReference());
        }
    }

    /**
     * Signals that the Document was closed and that no other
     * Elements will be added.
     * 

* The pages-tree is built and written to the outputstream. * A Catalog is constructed, as well as an Info-object, * the reference table is composed and everything is written * to the outputstream embedded in a Trailer. */ @Override public void close() { if (this.open) { final PdfReaderInstance ri = this.currentPdfReaderInstance; this.pdf.close(); super.close(); if (ri != null) { try { ri.getReader().close(); ri.getReaderFile().close(); } catch (final IOException ioe) { // empty on purpose } } } } @Override public void addAnnotation(final PdfAnnotation annot) { } @Override PdfIndirectReference add(final PdfPage page, final PdfContents contents) throws PdfException { return null; } @Override public void freeReader(final PdfReader reader) throws IOException { this.indirectMap.remove(reader); if (this.currentPdfReaderInstance != null) { if (this.currentPdfReaderInstance.getReader() == reader) { try { this.currentPdfReaderInstance.getReader().close(); this.currentPdfReaderInstance.getReaderFile().close(); } catch (final IOException ioe) { // empty on purpose } this.currentPdfReaderInstance = null; } } } private static class PageStamp { private final PdfDictionary pageN; private PdfCopy.StampContent under; private PdfCopy.StampContent over; private PageResources pageResources; private final PdfReader reader; private final PdfCopy cstp; private PageStamp(final PdfReader reader, final PdfDictionary pageN, final PdfCopy cstp) { this.pageN = pageN; this.reader = reader; this.cstp = cstp; } public PdfContentByte getUnderContent(){ if (this.under == null) { if (this.pageResources == null) { this.pageResources = new PageResources(); final PdfDictionary resources = this.pageN.getAsDict(PdfName.RESOURCES); this.pageResources.setOriginalResources(resources, this.cstp.namePtr); } this.under = new PdfCopy.StampContent(this.cstp, this.pageResources); } return this.under; } public PdfContentByte getOverContent(){ if (this.over == null) { if (this.pageResources == null) { this.pageResources = new PageResources(); final PdfDictionary resources = this.pageN.getAsDict(PdfName.RESOURCES); this.pageResources.setOriginalResources(resources, this.cstp.namePtr); } this.over = new PdfCopy.StampContent(this.cstp, this.pageResources); } return this.over; } private void applyRotation(final PdfDictionary pageN, final ByteBuffer out) { if (!this.cstp.rotateContents) { return; } final Rectangle page = this.reader.getPageSizeWithRotation(pageN); final int rotation = page.getRotation(); switch (rotation) { case 90: out.append(PdfContents.ROTATE90); out.append(page.getTop()); out.append(' ').append('0').append(PdfContents.ROTATEFINAL); break; case 180: out.append(PdfContents.ROTATE180); out.append(page.getRight()); out.append(' '); out.append(page.getTop()); out.append(PdfContents.ROTATEFINAL); break; case 270: out.append(PdfContents.ROTATE270); out.append('0').append(' '); out.append(page.getRight()); out.append(PdfContents.ROTATEFINAL); break; } } private void addDocumentField(final PdfIndirectReference ref) { if (this.cstp.fieldArray == null) { this.cstp.fieldArray = new PdfArray(); } this.cstp.fieldArray.add(ref); } private void expandFields(final PdfFormField field, final ArrayList allAnnots) { allAnnots.add(field); final ArrayList kids = field.getKids(); if (kids != null) { for (int k = 0; k < kids.size(); ++k) { expandFields((PdfFormField)kids.get(k), allAnnots); } } } public void addAnnotation(PdfAnnotation annot) { try { final ArrayList allAnnots = new ArrayList(); if (annot.isForm()) { final PdfFormField field = (PdfFormField)annot; if (field.getParent() != null) { return; } expandFields(field, allAnnots); if (this.cstp.fieldTemplates == null) { this.cstp.fieldTemplates = new LinkedHashMap(); } } else { allAnnots.add(annot); } for (int k = 0; k < allAnnots.size(); ++k) { annot = (PdfAnnotation)allAnnots.get(k); if (annot.isForm()) { if (!annot.isUsed()) { final HashMap templates = annot.getTemplates(); if (templates != null) { this.cstp.fieldTemplates.putAll(templates); } } final PdfFormField field = (PdfFormField)annot; if (field.getParent() == null) { addDocumentField(field.getIndirectReference()); } } if (annot.isAnnotation()) { final PdfObject pdfobj = PdfReader.getPdfObject(this.pageN.get(PdfName.ANNOTS), this.pageN); PdfArray annots = null; if (pdfobj == null || !pdfobj.isArray()) { annots = new PdfArray(); this.pageN.put(PdfName.ANNOTS, annots); } else { annots = (PdfArray)pdfobj; } annots.add(annot.getIndirectReference()); if (!annot.isUsed()) { final PdfRectangle rect = (PdfRectangle)annot.get(PdfName.RECT); if (rect != null && (rect.left() != 0 || rect.right() != 0 || rect.top() != 0 || rect.bottom() != 0)) { final int rotation = this.reader.getPageRotation(this.pageN); final Rectangle pageSize = this.reader.getPageSizeWithRotation(this.pageN); switch (rotation) { case 90: annot.put(PdfName.RECT, new PdfRectangle( pageSize.getTop() - rect.bottom(), rect.left(), pageSize.getTop() - rect.top(), rect.right())); break; case 180: annot.put(PdfName.RECT, new PdfRectangle( pageSize.getRight() - rect.left(), pageSize.getTop() - rect.bottom(), pageSize.getRight() - rect.right(), pageSize.getTop() - rect.top())); break; case 270: annot.put(PdfName.RECT, new PdfRectangle( rect.bottom(), pageSize.getRight() - rect.left(), rect.top(), pageSize.getRight() - rect.right())); break; } } } } if (!annot.isUsed()) { annot.setUsed(); this.cstp.addToBody(annot, annot.getIndirectReference()); } } } catch (final IOException e) { throw new ExceptionConverter(e); } } } private static class StampContent extends PdfContentByte { private final PageResources pageResources; /** Creates a new instance of StampContent */ private StampContent(final PdfWriter writer, final PageResources pageResources) { super(writer); this.pageResources = pageResources; } /** * Gets a duplicate of this PdfContentByte. All * the members are copied by reference but the buffer stays different. * * @return a copy of this PdfContentByte */ @Override public PdfContentByte getDuplicate() { return new PdfCopy.StampContent(this.writer, this.pageResources); } @Override PageResources getPageResources() { return this.pageResources; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy