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

de.intarsys.pdf.pd.PDPage Maven / Gradle / Ivy

Go to download

This is a fork of http://sourceforge.net/projects/jpodlib/ as development seems to be frozen. We're providing some bug fixes along with deployments to maven.

There is a newer version: 2.0
Show newest version
/*
 * Copyright (c) 2007, intarsys consulting GmbH
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - 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.
 *
 * - Neither the name of intarsys nor the names of its contributors may be used
 *   to endorse or promote products derived from this software without specific
 *   prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS 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 COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, 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.
 */
package de.intarsys.pdf.pd;

import de.intarsys.pdf.cds.CDSRectangle;
import de.intarsys.pdf.content.CSContent;
import de.intarsys.pdf.content.IContentStreamProvider;
import de.intarsys.pdf.cos.COSArray;
import de.intarsys.pdf.cos.COSBasedObject;
import de.intarsys.pdf.cos.COSDictionary;
import de.intarsys.pdf.cos.COSName;
import de.intarsys.pdf.cos.COSObject;
import de.intarsys.pdf.cos.COSStream;

import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * A single concrete page in a PDF document.
 */
public class PDPage extends PDPageNode implements IAdditionalActionSupport, IContentStreamProvider {
    /**
     * The meta class implementation
     */
    public static class MetaClass extends PDPageNode.MetaClass {
        protected MetaClass(Class instanceClass) {
            super(instanceClass);
        }

        @Override
        protected COSBasedObject doCreateCOSBasedObject(COSObject object) {
            return new PDPage(object);
        }
    }

    public static String COPY_SUFFIX = "_copy"; //$NON-NLS-1$

    public static final COSName DK_Annots = COSName.constant("Annots"); //$NON-NLS-1$

    public static final COSName DK_Contents = COSName.constant("Contents"); //$NON-NLS-1$

    public static final COSName DK_MediaBox = COSName.constant("MediaBox"); //$NON-NLS-1$

    public static final COSName DK_CropBox = COSName.constant("CropBox"); //$NON-NLS-1$

    public static final COSName DK_BleedBox = COSName.constant("BleedBox"); //$NON-NLS-1$

    public static final COSName DK_TrimBox = COSName.constant("TrimBox"); //$NON-NLS-1$

    public static final COSName DK_ArtBox = COSName.constant("ArtBox"); //$NON-NLS-1$

    public static final COSName DK_Metadata = COSName.constant("Metadata"); //$NON-NLS-1$

    public static final COSName DK_PieceInfo = COSName.constant("PieceInfo"); //$NON-NLS-1$

    public static final COSName DK_Resources = COSName.constant("Resources"); //$NON-NLS-1$

    public static final COSName DK_TemplateInstantiated = COSName.constant("TemplateInstantiated"); //$NON-NLS-1$

    /**
     * The meta class instance
     */
    public static final MetaClass META = new MetaClass(MetaClass.class.getDeclaringClass());

    private static final List NULL = new ArrayList();

    /**
     * supported additional action triggers
     */
    public static final Set PAGE_ACTION_TRIGGERS;

    static {
        PAGE_ACTION_TRIGGERS = new HashSet(3);
        PAGE_ACTION_TRIGGERS.add("O"); //$NON-NLS-1$
        PAGE_ACTION_TRIGGERS.add("C"); //$NON-NLS-1$
    }

    private SoftReference cachedAnnotations = null;

    private SoftReference cachedContentStream = null;

    /**
     * Create the receiver class from an already defined {@link COSDictionary}.
     * NEVER use the constructor directly.
     *
     * @param object the PDDocument containing the new object
     */
    protected PDPage(COSObject object) {
        super(object);
    }

    /**
     * Add a {@link PDAnnotation} to the collection of annotations on the
     * receiver page.
     *
     * @param annot The PDAnnotation to add to the page.
     */
    public void addAnnotation(PDAnnotation annot) {
        COSArray cosAnnots = cosGetField(DK_Annots).asArray();
        if (cosAnnots == null) {
            cosAnnots = COSArray.create();
            cosAnnots.beIndirect();
            cosSetField(DK_Annots, cosAnnots);
            cachedAnnotations = null;
        }
        cosAnnots.add(annot.cosGetDict());
        annot.setPage(this);
    }

    /**
     * Add a {@link CSContent} stream to this.
     *
     * @param contentStream The new {@link CSContent}
     */
    public void addContentStream(CSContent contentStream) {
        cosAddContents(contentStream.createStream());
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDPageNode#collectAnnotations(java.util.List)
     */
    @Override
    protected void collectAnnotations(List annotations) {
        if (getAnnotations() != null) {
            annotations.addAll(getAnnotations());
        }
    }

    /**
     * Append {@link COSStream} to the pages content
     *
     * @param content The {@link COSStream} to add to the page
     */
    public void cosAddContents(COSStream content) {
        COSObject contents = cosGetField(DK_Contents);
        if (contents.isNull()) {
            cosSetField(DK_Contents, content);
        }
        if (contents instanceof COSStream) {
            COSArray array = COSArray.create(2);
            array.add(contents);
            array.add(content);
            cosSetField(DK_Contents, array);
        }
        if (contents instanceof COSArray) {
            COSArray array = (COSArray) contents;
            array.add(content);
        }
    }

    /**
     * The /Contents entry
     *
     * @return The /Contents entry
     */
    public COSObject cosGetContents() {
        return cosGetField(DK_Contents);
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDObject#cosGetExpectedType()
     */
    @Override
    protected COSName cosGetExpectedType() {
        return CN_Type_Page;
    }

    /**
     * The piece info dictionary of the document.
     *
     * @return The piece info dictionary of the document.
     */
    public COSDictionary cosGetPieceInfo() {
        return cosGetField(DK_PieceInfo).asDictionary();
    }

    public COSName cosGetTemplateInstantiated() {
        return cosGetField(DK_TemplateInstantiated).asName();
    }

    /**
     * Prepend contents to the pages content.
     *
     * @param content The {@link COSStream} to add to the page
     */
    public void cosPrependContents(COSStream content) {
        COSObject contents = cosGetField(DK_Contents);
        if (contents.isNull()) {
            cosSetField(DK_Contents, content);
        }
        if (contents instanceof COSStream) {
            COSArray array = COSArray.create(2);
            array.add(content);
            array.add(contents);
            cosSetField(DK_Contents, array);
        }
        if (contents instanceof COSArray) {
            COSArray array = (COSArray) contents;
            array.add(0, content);
        }
    }

    /**
     * Set the /Contents for the page
     *
     * @param content the stream defining the page content
     * @return The /Contents entry previously associated with this.
     */
    public COSObject cosSetContents(COSObject content) {
        return cosSetField(DK_Contents, content);
    }

    /**
     * Set the piece info dictionary of the document.
     *
     * @param dict The piece info dictionary of the document.
     * @return The /PieceInfo entry previously associated with this.
     */
    public COSDictionary cosSetPieceInfo(COSDictionary dict) {
        if (dict != null) {
            dict.beIndirect();
        }
        return cosSetField(DK_PieceInfo, dict).asDictionary();
    }

    public void cosSetTemplateInstantiated(COSName templateName) {
        cosSetField(DK_TemplateInstantiated, templateName);
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDPageNode#dispose()
     */
    @Override
    public void dispose() {
        // todo 1 too much logic here...
        if (getAnnotations() != null) {
            for (PDAnnotation annotation : getAnnotations()) {
                annotation.dispose();
            }
        }
        super.dispose();
    }

    /**
     * A collection of all {@link PDAcroFormField}s that have
     * {@link PDAnnotation}s on the receiver that are children of
     * {@code parent}.
     *
     * @param parent The parent {@link PDAcroForm} or {@link PDAcroFormField}.
     * @param result The collection of {@link PDAnnotation}s collected so far.
     */
    protected void getAcroFormFields(PDObject parent, List result) {
        List elements = parent.getGenericChildren();
        List annotations = getAnnotations();
        if (elements == null) {
            return;
        }
        for (Iterator i = elements.iterator(); i.hasNext(); ) {
            PDObject object = (PDObject) i.next();
            if (annotations.contains(object)) {
                result.add(object);
            }
            getAcroFormFields(object, result);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.IAdditionalActionSupport#getAdditionalActions()
     */
    @Override
    public PDAdditionalActions getAdditionalActions() {
        COSDictionary field = cosGetField(DK_AA).asDictionary();
        return (PDAdditionalActions) PDAdditionalActions.META.createFromCos(field);
    }

    /**
     * Get a list of all {@link PDAnnotation} objects that are referenced in
     * this page.
     *
     * @return A list of all {@link PDAnnotation} objects that are referenced in
     * this page or null if none exist.
     */
    public List getAnnotations() {
        List annotations = null;
        if (cachedAnnotations != null) {
            annotations = (List) cachedAnnotations.get();
        }
        if (annotations == null) {
            annotations = getPDObjects(DK_Annots, PDAnnotation.META, true);
            if (annotations == null) {
                // avoid continued lookup when no annotations found
                annotations = NULL;
            }
            cachedAnnotations = new SoftReference(annotations);
        }
        if (annotations == NULL) {
            return null;
        }
        return annotations;
    }

    /**
     * The {@link PDApplicationData} associated with {@code name} on the
     * page.
     *
     * @param name The name of the {@link PDApplicationData} to lookup.
     * @return The {@link PDApplicationData} associated with {@code name}
     * on the page.
     */
    public PDApplicationData getApplicationData(String name) {
        COSDictionary pid = cosGetPieceInfo();
        if (pid == null) {
            return null;
        }
        COSName cosName = COSName.createUTF8(name);
        COSDictionary pi = pid.get(cosName).asDictionary();
        if (pi == null) {
            return null;
        }
        return (PDApplicationData) PDApplicationData.META.createFromCos(pi);
    }

    protected int getContentsSize() {
        COSObject contents = cosGetContents();
        if (contents.isNull()) {
            return 0;
        }
        if (contents instanceof COSStream) {
            return 1;
        }
        return ((COSArray) contents).size();
    }

    /**
     * The {@link CSContent} defining the visual content of the page.
     *
     * @return The {@link CSContent} defining the visual content of the page.
     */
    @Override
    public CSContent getContentStream() {
        CSContent contentStream = null;
        if (cachedContentStream != null) {
            contentStream = (CSContent) cachedContentStream.get();
        }
        if (contentStream == null) {
            COSObject contents = cosGetContents();
            if (!contents.isNull()) {
                if (contents instanceof COSStream) {
                    contentStream = CSContent.createFromCos(contents.asStream());
                } else {
                    contentStream = CSContent.createFromCos(contents.asArray());
                }
                // just to be sure we are not registered before (soft ref!)
                contents.removeObjectListener(this);
                contents.addObjectListener(this);
                // todo add listener to content streams in array...
                cachedContentStream = new SoftReference(contentStream);
            }
        }
        return contentStream;
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDPageNode#getCount()
     */
    @Override
    public int getCount() {
        return 1;
    }

    /**
     * @return The first {@link PDAnnotation} on the page or null
     */
    @Override
    public PDAnnotation getFirstAnnotation() {
        if (getAnnotations() == null) {
            return null;
        }
        if (getAnnotations().isEmpty()) {
            return null;
        }
        return getAnnotations().get(0);
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDPageNode#getFirst()
     */
    @Override
    public PDPageNode getFirstNode() {
        return this;
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDPageNode#getFirstPage()
     */
    @Override
    public PDPage getFirstPage() {
        return this;
    }

    /**
     * @return The last {@link PDAnnotation} on the page or null
     */
    @Override
    public PDAnnotation getLastAnnotation() {
        if (getAnnotations() == null) {
            return null;
        }
        int size = getAnnotations().size();
        if (size == 0) {
            return null;
        }
        return getAnnotations().get(size - 1);
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDPageNode#getLast()
     */
    @Override
    public PDPageNode getLastNode() {
        return this;
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDPageNode#getLastPage()
     */
    @Override
    public PDPage getLastPage() {
        return this;
    }

    /**
     * The {@link PDAnnotation} following the given {@link PDAnnotation} annot
     * or null, if {@code annot} was the last one in the list or does't
     * exist on this page.
     *
     * @param annot a PDAnnotation
     * @return a PDAnnotation or null
     */
    public PDAnnotation getNextAnnotation(PDAnnotation annot) {
        if (getAnnotations() == null) {
            return null;
        }
        int resultIndex = getAnnotations().indexOf(annot);
        if (resultIndex == -1) {
            return null;
        }
        if ((resultIndex + 1) < getAnnotations().size()) {
            return getAnnotations().get(resultIndex + 1);
        }
        return null;
    }

    /**
     * The next page after the receiver.
     *
     * @return The next page after the receiver.
     */
    public PDPage getNextPage() {
        PDPageTree tmpParent = getParent();
        if (tmpParent == null) {
            return null;
        }
        return tmpParent.getNextPage(this);
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDPageNode#getPageAt(int)
     */
    @Override
    public PDPage getPageAt(int index) {
        if (index == 0) {
            return this;
        }
        return super.getPageAt(index);
    }

    /**
     * Returns the {@link PDAnnotation} preceding the given {@link PDAnnotation}
     * annot or null, if annot was the first one in the list or does't exist on
     * this page.
     *
     * @param annot a PDAnnotation
     * @return a PDAnnotation or null
     */
    public PDAnnotation getPreviousAnnotation(PDAnnotation annot) {
        if (getAnnotations() == null) {
            return null;
        }
        int resultIndex = getAnnotations().indexOf(annot);
        if (resultIndex == -1) {
            return null;
        }
        if ((resultIndex - 1) >= 0) {
            return getAnnotations().get(resultIndex - 1);
        }
        return null;
    }

    /**
     * Get the previous page before the receiver.
     *
     * @return Get the previous page before the receiver.
     */
    public PDPage getPreviousPage() {
        PDPageTree tmpParent = getParent();
        if (tmpParent == null) {
            return null;
        }
        return tmpParent.getPreviousPage(this);
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.IResourcesProvider#getResources()
     */
    @Override
    public PDResources getResources() {
        COSDictionary dict = cosGetFieldInheritable(DK_Resources).asDictionary();
        return (PDResources) PDResources.META.createFromCos(dict);
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * de.intarsys.pdf.pd.IAdditionalActionSupport#getSupportedTriggerEvents()
     */
    @Override
    public Set getSupportedTriggerEvents() {
        return PAGE_ACTION_TRIGGERS;
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDComplexBase#initializeFromScratch()
     */
    @Override
    protected void initializeFromScratch() {
        super.initializeFromScratch();
        // todo 3 get default paper size from environment
        setMediaBox(new CDSRectangle(CDSRectangle.SIZE_A4));
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDPageNode#invalidateCaches()
     */
    @Override
    public void invalidateCaches() {
        super.invalidateCaches();
        cachedAnnotations = null;
        cachedContentStream = null;
        COSObject cosAnnotations = cosGetField(DK_Annots);
        cosAnnotations.removeObjectListener(this);
        COSObject cosContents = cosGetField(DK_Contents);
        cosContents.removeObjectListener(this);
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDPageNode#isPage()
     */
    @Override
    public boolean isPage() {
        return true;
    }

    /*
     * (non-Javadoc)
     *
     * @see de.intarsys.pdf.pd.PDPageNode#isValid()
     */
    @Override
    public boolean isValid() {
        PDPageTree tempParent = getParent();
        if (tempParent == null) {
            return false;
        }
        return tempParent.isValid();
    }

    /**
     * Prepend a {@link CSContent} stream to this.
     *
     * @param contentStream The new {@link CSContent}
     */
    public void prependContentStream(CSContent contentStream) {
        cosPrependContents(contentStream.createStream());
    }

    /**
     * Remove a {@link PDAnnotation} from the page.
     *
     * @param annot The {@link PDAnnotation} to remove from the page.
     */
    public void removeAnnotation(PDAnnotation annot) {
        COSArray cosAnnots = cosGetField(DK_Annots).asArray();
        if (cosAnnots != null) {
            cosAnnots.remove(annot.cosGetDict());
            if (cosAnnots.isEmpty()) {
                cosRemoveField(DK_Annots);
            }
        }
    }

    /**
     * Remove the {@link PDApplicationData} associated with {@code name}
     * from this page.
     *
     * @param name The name of the application data object to be removed.
     */
    public void removeApplicationData(String name) {
        COSDictionary pid = cosGetPieceInfo();
        if (pid == null) {
            return;
        }
        COSName cosName = COSName.createUTF8(name);
        pid.remove(cosName);
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * de.intarsys.pdf.pd.IAdditionalActionSupport#setActions(de.intarsys.pdf
     * .pd.PDAdditionalActions)
     */
    @Override
    public void setAdditionalActions(PDAdditionalActions actions) {
        setFieldObject(DK_AA, actions);
    }

    /**
     * Associate a {@link PDApplicationData} instance with this using
     * {@code name}.
     *
     * @param name The name for the {@link PDApplicationData} instance within
     *             this.
     * @param data The {@link PDApplicationData} instance.
     */
    public void setApplicationData(String name, PDApplicationData data) {
        COSDictionary pid = cosGetPieceInfo();
        if (pid == null) {
            pid = COSDictionary.create();
            cosSetPieceInfo(pid);
        }
        COSName cosName = COSName.createUTF8(name);
        pid.put(cosName, data.cosGetDict());
    }

    /**
     * Assign a new visual appearance to the page.
     *
     * @param contentStream The new visual appearance.
     */
    @Override
    public void setContentStream(CSContent contentStream) {
        if (contentStream != null) {
            cosSetContents(contentStream.createStream());
        } else {
            cosSetContents(null);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * de.intarsys.pdf.pd.IResourcesProvider#setResources(de.intarsys.pdf.pd
     * .PDResources)
     */
    @Override
    public void setResources(PDResources resources) {
        cosSetField(DK_Resources, resources.cosGetDict());
    }

    /**
     * Sets the rectangle (in default user space units) that defines the region
     * to which the contents of this page shall be clipped when output in a
     * production environment (PDF 1.3). The default is the value of the crop
     * box.
     *
     * @param rect the rectangle that defines the bleed box
     */
    public void setBleedBox(CDSRectangle rect) {
        setFieldObject(DK_BleedBox, rect);
    }

    /**
     * Returns the rectangle (in default user space units) defines the region to
     * which the contents of this page shall be clipped when output in a
     * production environment (PDF 1.3). The default is the value of the crop
     * box.
     *
     * @return this page's bleed box or its crop box, if no bleed box is
     * defined
     */
    public CDSRectangle getBleedBox() {
        COSArray array = cosGetField(DK_BleedBox).asArray();
        if (array == null) {
            return getCropBox();
        }

        return CDSRectangle.createFromCOS(array);
    }

    /**
     * Sets the rectangle (in default user space units) that defines the
     * intended dimensions of the finished page after trimming (PDF 1.3). The
     * default is the value of the crop box.
     *
     * @param rect the rectangle that defines the trim box
     */
    public void setTrimBox(CDSRectangle rect) {
        setFieldObject(DK_TrimBox, rect);
    }

    /**
     * Returns the rectangle (in default user space units) defines the intended
     * dimensions of the finished page after trimming (PDF 1.3). The default is
     * the value of the crop box.
     *
     * @return this page's trim box or its crop box, if no trim box is defined
     */
    public CDSRectangle getTrimBox() {
        COSArray array = cosGetField(DK_TrimBox).asArray();
        if (array == null) {
            return getCropBox();
        }

        return CDSRectangle.createFromCOS(array);
    }

    /**
     * Sets the rectangle (in default user space units) that defines the extent
     * of the page�s meaningful content (including potential white space) as
     * intended by the page�s creator (PDF 1.3). The default is the value of the
     * crop box.
     *
     * @param rect the rectangle that defines the art box
     */
    public void setArtBox(CDSRectangle rect) {
        setFieldObject(DK_ArtBox, rect);
    }

    /**
     * Returns the rectangle (in default user space units) defines defines the
     * extent of the page�s meaningful content (including potential white space)
     * as intended by the page�s creator (PDF 1.3). The default is the value of
     * the crop box.
     *
     * @return this page's art box or its crop box, if no art box is defined
     */
    public CDSRectangle getArtBox() {
        COSArray array = cosGetField(DK_ArtBox).asArray();
        if (array == null) {
            return getCropBox();
        }

        return CDSRectangle.createFromCOS(array);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy