de.intarsys.pdf.pd.PDAnnotationTools 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.CDSMatrix;
import de.intarsys.pdf.cds.CDSRectangle;
import de.intarsys.pdf.cos.COSDictionary;
import de.intarsys.pdf.cos.COSDocument;
import de.intarsys.pdf.cos.COSName;
import de.intarsys.pdf.st.STDocument;
import java.util.Iterator;
import java.util.List;
/**
* Tool class for common tasks with {@link PDAnnotation} objects.
*/
public class PDAnnotationTools {
public static final String CAPTION_CHECK = "4";
public static final String CAPTION_CIRCLE = "l";
public static final String CAPTION_CROSS = "8";
public static final String CAPTION_DIAMOND = "u";
public static final String CAPTION_SQUARE = "n";
public static final String CAPTION_STAR = "H";
public static void adjustRectangleToAppearance(PDAnnotation annotation) {
PDAppearance appearance = annotation.getAppearance();
if (appearance == null) {
return;
}
adjustRectangleToAppearance(annotation, appearance);
}
public static void adjustRectangleToAppearance(PDAnnotation annotation, PDAppearance appearance) {
List forms = appearance.getForms();
Float maxWidth = null;
Float maxHeight = null;
for (PDForm form : forms) {
CDSRectangle formRect = form.getBoundingBox();
float width = formRect.getWidth();
float height = formRect.getHeight();
if (maxWidth == null || width > maxWidth) {
maxWidth = width;
}
if (maxHeight == null || height > maxHeight) {
maxHeight = height;
}
}
// adjust annotation rect
boolean changedRect = false;
CDSRectangle rect = annotation.getNormalizedRectangle();
if (maxWidth != null && maxWidth != rect.getWidth()) {
rect.setWidth(maxWidth);
changedRect = true;
}
if (maxHeight != null && maxHeight != rect.getHeight()) {
rect.setHeight(maxHeight);
changedRect = true;
}
// adjust annotation
if (changedRect) {
STDocument stDoc = getSTDocument(annotation);
boolean wasDirty = stDoc == null || stDoc.isDirty();
annotation.setRectangle(rect);
if (!wasDirty) {
stDoc.setDirty(false);
}
}
}
private static STDocument getSTDocument(PDAnnotation annotation) {
COSDocument cosDoc = annotation.cosGetDoc();
return cosDoc == null ? null : cosDoc.stGetDoc();
}
/**
* The {@link PDAppearance} for annotation. If no {@link PDAppearance} is
* yet available, a new one is created but NOT associated with the
* annotation. This behavior is intended to allow for dynamic appearance
* creation when rendering without changing the document itself.
*
* @return The {@link PDAppearance} for {@code annotation}.
*/
public static PDAppearance getAppearance(PDAnnotation annotation) {
PDAppearance appearance = annotation.getAppearance();
if (appearance == null) {
appearance = (PDAppearance) PDAppearance.META.createNew();
}
return appearance;
}
public static float[] getBorderColor(PDWidgetAnnotation annotation) {
PDAppearanceCharacteristics appearanceCharacteristics = annotation.getAppearanceCharacteristics();
if (appearanceCharacteristics == null) {
return null;
}
return appearanceCharacteristics.getBorderColor();
}
public static float[] getBorderColorNegative(PDWidgetAnnotation annotation) {
float[] borderColor = getBorderColor(annotation);
if (borderColor == null) {
return null;
}
float[] borderColorNegative = new float[borderColor.length];
for (int i = 0; i < borderColor.length; i++) {
borderColorNegative[i] = Math.max(Math.min(borderColorNegative[i] + 0.25f, 1), 0);
}
return borderColorNegative;
}
public static float[] getFillColor(PDWidgetAnnotation annotation) {
PDAppearanceCharacteristics appearanceCharacteristics = annotation.getAppearanceCharacteristics();
if (appearanceCharacteristics == null) {
return null;
}
return appearanceCharacteristics.getBackgroundColor();
}
public static float[] getFillColorNegative(PDWidgetAnnotation annotation) {
float[] fillColor = getFillColor(annotation);
if (fillColor == null) {
return null;
}
float[] fillColorNegative = new float[fillColor.length];
for (int i = 0; i < fillColor.length; i++) {
fillColorNegative[i] = Math.max(Math.min(fillColor[i] - 0.25f, 1), 0);
}
return fillColorNegative;
}
/**
* Returns the next annotation following {@code annotation} on the same
* page. Returns {@code null} if no annotation following
* {@code annotation} could be found or {@code annotation} is the
* last one on the page.
*
* @param annotation
* @return the next annotation or {@code null}
*/
public static PDAnnotation getNextAnnotation(PDAnnotation annotation) {
PDPage page = getPage(annotation);
if (page == null) {
return null;
}
return page.getNextAnnotation(annotation);
}
/**
* Returns the next annotation following {@code annotation} in the
* whole document. Returns {@code null} if no annotation following
* {@code annotation} could be found in the document.
*
* @param annotation
* @return the next annotation or {@code null}
*/
public static PDAnnotation getNextAnnotationAllPages(PDAnnotation annotation) {
PDPage page = getPage(annotation);
if (page == null) {
return null;
}
PDAnnotation result = getNextAnnotation(annotation);
while (result == null) {
page = page.getNextPage();
if (page == null) {
return null;
}
result = page.getFirstAnnotation();
}
return result;
}
public static PDPage getPage(PDAnnotation annotation) {
PDPage page = annotation.getPage();
if (page != null) {
return page;
}
PDDocument doc = annotation.getDoc();
if (doc == null) {
return null;
}
for (page = doc.getPageTree().getFirstPage(); page != null; page = page.getNextPage()) {
List pageAnnots = page.getAnnotations();
if (pageAnnots == null) {
continue;
}
for (Iterator i = pageAnnots.iterator(); i.hasNext(); ) {
PDAnnotation current = (PDAnnotation) i.next();
if (current.cosGetObject() == annotation.cosGetObject()) {
return page;
}
}
}
return null;
}
/**
* Returns the annotation preceding {@code annotation} on the same
* page. Returns {@code null} if no annotation preceding
* {@code annotation} could be found or {@code annotation} is the
* first one on the page.
*
* @param annotation
* @return the preceding annotation or {@code null}
*/
public static PDAnnotation getPreviousAnnotation(PDAnnotation annotation) {
PDPage page = getPage(annotation);
if (page == null) {
return null;
}
return page.getPreviousAnnotation(annotation);
}
/**
* Returns the annotation preceding {@code annotation} in the whole
* document. Returns {@code null} if no annotation preceding
* {@code annotation} could be found in the document.
*
* @param annotation
* @return the previous annotation or {@code null}
*/
public static PDAnnotation getPreviousAnnotationAllPages(PDAnnotation annotation) {
PDPage page = getPage(annotation);
if (page == null) {
return null;
}
PDAnnotation result = getPreviousAnnotation(annotation);
while (result == null) {
page = page.getPreviousPage();
if (page == null) {
return null;
}
result = page.getLastAnnotation();
}
return result;
}
/**
* Lookup the state that is used to represent "not off" in
* {@code annotation}.
*
* @param annotation The annotation to inspect for its "not off" state.
* @return Lookup the state that is used to represent "not off" in
* {@code annotation}.
*/
public static COSName getStateChecked(PDWidgetAnnotation annotation) {
for (Iterator i = annotation.getAppearanceStates().iterator(); i.hasNext(); ) {
COSName state = (COSName) i.next();
if (isStateChecked(state)) {
return state;
}
}
return null;
}
/**
* Checks a COSDictionary for being a subtyped known annotation as of Spec
* PDF 1.4
*
* @param dict
* @return {@code true} if known as of Spec PDF 1.4
*/
public static boolean isAnnotationSpec14(COSDictionary dict) {
COSName subtype = dict.get(PDObject.DK_Subtype).asName();
if (subtype == null) {
return false;
}
if (subtype.equals(PDWidgetAnnotation.CN_Subtype_Widget)) {
return true;
}
if (subtype.equals(PDMarkupAnnotation.CN_Subtype_Ink)) {
return true;
}
if (subtype.equals(PDMarkupAnnotation.CN_Subtype_Square)) {
return true;
}
if (subtype.equals(PDMarkupAnnotation.CN_Subtype_Circle)) {
return true;
}
if (subtype.equals(PDMarkupAnnotation.CN_Subtype_Line)) {
return true;
}
if (subtype.equals(PDMarkupAnnotation.CN_Subtype_Polygon)) {
return true;
}
if (subtype.equals(PDMarkupAnnotation.CN_Subtype_PolyLine)) {
return true;
}
if (subtype.equals(PDPopupAnnotation.CN_Subtype_Popup)) {
return true;
}
if (subtype.equals(COSName.constant("Link"))) {
return true;
}
if (subtype.equals(COSName.constant("FreeText"))) {
return true;
}
if (subtype.equals(COSName.constant("Highlight"))) {
return true;
}
if (subtype.equals(COSName.constant("Underline"))) {
return true;
}
if (subtype.equals(COSName.constant("Squiggly"))) {
return true;
}
if (subtype.equals(COSName.constant("StrikeOut"))) {
return true;
}
if (subtype.equals(COSName.constant("Stamp"))) {
return true;
}
if (subtype.equals(COSName.constant("FileAttachment"))) {
return true;
}
if (subtype.equals(COSName.constant("Sound"))) {
return true;
}
if (subtype.equals(COSName.constant("Movie"))) {
return true;
}
if (subtype.equals(COSName.constant("PrinterMark"))) {
return true;
}
return subtype.equals(COSName.constant("TrapNet"));
}
/**
* {@code true} if {@code state} represents a state that is not
* "/Off". "/Off" is the only legal way to switch of a toggle button, so
* anything else is "on".
*
* @param state The state to inspect if it is not "/Off".
* @return {@code true} if {@code state} represents a state that
* is not "/Off".
*/
public static boolean isStateChecked(COSName state) {
return !PDWidgetAnnotation.CN_State_Off.equals(state);
}
public static void transform(CDSRectangle rect, CDSMatrix matrix, int rotation) {
if (rotation == 90) {
matrix.setTransformation(CDSMatrix.MATRIX_90);
} else if (rotation == 180) {
matrix.setTransformation(CDSMatrix.MATRIX_180);
} else if (rotation == 270) {
matrix.setTransformation(CDSMatrix.MATRIX_270);
}
float[] vec = {rect.getWidth(), rect.getHeight()};
float[] tVec = matrix.transform(vec);
if (rotation == 90) {
matrix.setE(rect.getWidth());
} else if (rotation == 180) {
matrix.setE(rect.getWidth());
matrix.setF(rect.getHeight());
} else if (rotation == 270) {
matrix.setF(rect.getHeight());
}
rect.setCorners(0, 0, tVec[0], tVec[1]);
rect.normalize();
}
private PDAnnotationTools() {
// tool class
}
}