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

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

/*
 * 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 java.util.Iterator;
import java.util.List;

import de.intarsys.pdf.cds.CDSMatrix;
import de.intarsys.pdf.cds.CDSRectangle;
import de.intarsys.pdf.cos.COSDictionary;
import de.intarsys.pdf.cos.COSName;
import de.intarsys.pdf.st.STDocument;

/**
 * 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 doc = annotation.getDoc().cosGetDoc().stGetDoc();
			boolean wasDirty = doc.isDirty();
			annotation.setRectangle(rect);
			if (!wasDirty) {
				doc.setDirty(false);
			}
		}

	}

	/**
	 * 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 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 annotation on the same
	 * page. Returns null if no annotation following
	 * annotation could be found or annotation is the
	 * last one on the page.
	 * 
	 * @param annotation
	 * 
	 * @return the next annotation or null
	 */
	static public PDAnnotation getNextAnnotation(PDAnnotation annotation) {
		PDPage page = getPage(annotation);
		if (page == null) {
			return null;
		}
		return page.getNextAnnotation(annotation);
	}

	/**
	 * Returns the next annotation following annotation in the
	 * whole document. Returns null if no annotation following
	 * annotation could be found in the document.
	 * 
	 * @param annotation
	 * 
	 * @return the next annotation or null
	 */
	static public 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;
	}

	static public 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 annotation on the same
	 * page. Returns null if no annotation preceding
	 * annotation could be found or annotation is the
	 * first one on the page.
	 * 
	 * @param annotation
	 * 
	 * @return the preceding annotation or null
	 */
	static public PDAnnotation getPreviousAnnotation(PDAnnotation annotation) {
		PDPage page = getPage(annotation);
		if (page == null) {
			return null;
		}
		return page.getPreviousAnnotation(annotation);
	}

	/**
	 * Returns the annotation preceding annotation in the whole
	 * document. Returns null if no annotation preceding
	 * annotation could be found in the document.
	 * 
	 * @param annotation
	 * 
	 * @return the previous annotation or null
	 */
	static public 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
	 * annotation.
	 * 
	 * @param annotation
	 *            The annotation to inspect for its "not off" state.
	 * @return Lookup the state that is used to represent "not off" in
	 *         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 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;
		}
		if (subtype.equals(COSName.constant("TrapNet"))) {
			return true;
		}
		return false;
	}

	/**
	 * true if 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 true if 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 = new float[] { 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
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy