de.intarsys.pdf.pd.PDPage Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jpod Show documentation
Show all versions of jpod Show documentation
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.
/*
* 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);
}
}