com.itextpdf.kernel.pdf.xobject.PdfFormXObject Maven / Gradle / Ivy
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2024 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
package com.itextpdf.kernel.pdf.xobject;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.geom.Matrix;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.geom.Vector;
import com.itextpdf.kernel.pdf.PdfArray;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfObject;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfResources;
import com.itextpdf.kernel.pdf.PdfStream;
import com.itextpdf.kernel.pdf.PdfString;
import com.itextpdf.kernel.pdf.canvas.wmf.WmfImageData;
import com.itextpdf.kernel.pdf.canvas.wmf.WmfImageHelper;
/**
* A wrapper for Form XObject. ISO 32000-1, 8.10 FormXObjects.
*/
public class PdfFormXObject extends PdfXObject {
protected PdfResources resources = null;
/**
* Creates a new instance of Form XObject.
*
* @param bBox the form XObject’s bounding box.
*/
public PdfFormXObject(Rectangle bBox) {
super(new PdfStream());
getPdfObject().put(PdfName.Type, PdfName.XObject);
getPdfObject().put(PdfName.Subtype, PdfName.Form);
if (bBox != null) {
getPdfObject().put(PdfName.BBox, new PdfArray(bBox));
}
}
/**
* Create {@link PdfFormXObject} instance by {@link PdfStream}.
* Note, this constructor doesn't perform any additional checks
*
* @param pdfStream {@link PdfStream} with Form XObject.
* @see PdfXObject#makeXObject(PdfStream)
*/
public PdfFormXObject(PdfStream pdfStream) {
super(pdfStream);
if (!getPdfObject().containsKey(PdfName.Subtype)) {
getPdfObject().put(PdfName.Subtype, PdfName.Form);
}
}
/**
* Creates form XObject from page content.
* The page shall be from the document, to which FormXObject will be added.
*
* @param page an instance of {@link PdfPage}
*/
public PdfFormXObject(PdfPage page) {
this(page.getCropBox());
getPdfObject().getOutputStream().writeBytes(page.getContentBytes());
resources = new PdfResources((PdfDictionary) page.getResources().getPdfObject().clone());
getPdfObject().put(PdfName.Resources, resources.getPdfObject());
}
/**
* Creates a form XObject from {@link com.itextpdf.kernel.pdf.canvas.wmf.WmfImageData}.
* Unlike other images, {@link com.itextpdf.kernel.pdf.canvas.wmf.WmfImageData} images are represented as {@link PdfFormXObject}, not as
* {@link PdfImageXObject}.
*
* @param image image to create form object from
* @param pdfDocument document instance which is needed for writing form stream contents
*/
public PdfFormXObject(WmfImageData image, PdfDocument pdfDocument) {
this(new WmfImageHelper(image).createFormXObject(pdfDocument).getPdfObject());
}
/**
* Calculates the coordinates of the xObject BBox multiplied by the Matrix field.
*
*
* For mor information see paragraph 8.10.1 in ISO-32000-1.
*
* @param form the object for which calculate the coordinates of the bBox
* @return the bBox {@link Rectangle}
*/
public static Rectangle calculateBBoxMultipliedByMatrix(PdfFormXObject form) {
PdfArray pdfArrayBBox = form.getPdfObject().getAsArray(PdfName.BBox);
if (pdfArrayBBox == null) {
throw new PdfException(KernelExceptionMessageConstant.PDF_FORM_XOBJECT_HAS_INVALID_BBOX);
}
float[] bBoxArray = pdfArrayBBox.toFloatArray();
PdfArray pdfArrayMatrix = form.getPdfObject().getAsArray(PdfName.Matrix);
float[] matrixArray;
if (pdfArrayMatrix == null) {
matrixArray = new float[] {1, 0, 0, 1, 0, 0};
} else {
matrixArray = pdfArrayMatrix.toFloatArray();
}
Matrix matrix = new Matrix(matrixArray[0], matrixArray[1], matrixArray[2], matrixArray[3], matrixArray[4], matrixArray[5]);
Vector bBoxMin = new Vector(bBoxArray[0], bBoxArray[1], 1);
Vector bBoxMax = new Vector(bBoxArray[2], bBoxArray[3], 1);
Vector bBoxMinByMatrix = bBoxMin.cross(matrix);
Vector bBoxMaxByMatrix = bBoxMax.cross(matrix);
float width = bBoxMaxByMatrix.get(Vector.I1) - bBoxMinByMatrix.get(Vector.I1);
float height = bBoxMaxByMatrix.get(Vector.I2) - bBoxMinByMatrix.get(Vector.I2);
return new Rectangle(bBoxMinByMatrix.get(Vector.I1), bBoxMinByMatrix.get(Vector.I2), width, height);
}
/**
* Gets {@link PdfResources} of the Form XObject.
* Note, if there is no resources, a new instance will be created.
*
* @return not null instance of {@link PdfResources}.
*/
public PdfResources getResources() {
if (this.resources == null) {
PdfDictionary resourcesDict = getPdfObject().getAsDictionary(PdfName.Resources);
if (resourcesDict == null) {
resourcesDict = new PdfDictionary();
getPdfObject().put(PdfName.Resources, resourcesDict);
}
this.resources = new PdfResources(resourcesDict);
}
return resources;
}
/**
* Gets Form XObject's BBox, {@link PdfName#BBox} key.
*
* @return a {@link PdfArray}, that represents {@link Rectangle}.
*/
public PdfArray getBBox() {
return getPdfObject().getAsArray(PdfName.BBox);
}
/**
* Sets Form XObject's BBox, {@link PdfName#BBox} key.
*
* @param bBox a {@link PdfArray}, that represents {@link Rectangle}.
* @return object itself.
*/
public PdfFormXObject setBBox(PdfArray bBox) {
return put(PdfName.BBox, bBox);
}
/**
* Sets a group attributes dictionary indicating that the contents of the form XObject
* shall be treated as a group and specifying the attributes of that group.
* {@link PdfName#Group} key.
*
* @param transparency instance of {@link PdfTransparencyGroup}.
* @return object itself.
* @see PdfTransparencyGroup
*/
public PdfFormXObject setGroup(PdfTransparencyGroup transparency) {
return put(PdfName.Group, transparency.getPdfObject());
}
/**
* Gets width based on XObject's BBox.
*
* @return float value.
*/
@Override
public float getWidth() {
return getBBox() == null ? 0 : getBBox().getAsNumber(2).floatValue() - getBBox().getAsNumber(0).floatValue();
}
/**
* Gets height based on XObject's BBox.
*
* @return float value.
*/
@Override
public float getHeight() {
return getBBox() == null ? 0 : getBBox().getAsNumber(3).floatValue() - getBBox().getAsNumber(1).floatValue();
}
/**
* To manually flush a {@code PdfObject} behind this wrapper, you have to ensure
* that this object is added to the document, i.e. it has an indirect reference.
* Basically this means that before flushing you need to explicitly call {@link #makeIndirect(PdfDocument)}.
* For example: wrapperInstance.makeIndirect(document).flush();
* Note that not every wrapper require this, only those that have such warning in documentation.
*/
@Override
public void flush() {
resources = null;
if (getPdfObject().get(PdfName.BBox) == null) {
throw new PdfException(KernelExceptionMessageConstant.FORM_XOBJECT_MUST_HAVE_BBOX);
}
super.flush();
}
//-----Additional entries in form dictionary for Trap Network annotation
/**
* Sets process color model for trap network appearance, {@link PdfName#PCM} key.
*
* @param model shall be one of the valid values: {@link PdfName#DeviceGray},
* {@link PdfName#DeviceRGB}, {@link PdfName#DeviceCMYK}, {@link PdfName#DeviceCMY},
* {@link PdfName#DeviceRGBK}, and {@link PdfName#DeviceN}.
* @return object itself.
*/
public PdfFormXObject setProcessColorModel(PdfName model) {
return put(PdfName.PCM, model);
}
/**
* Gets process color model of trap network appearance, {@link PdfName#PCM} key.
*
* @return a {@link PdfName} instance, possible values: {@link PdfName#DeviceGray},
* {@link PdfName#DeviceRGB}, {@link PdfName#DeviceCMYK}, {@link PdfName#DeviceCMY},
* {@link PdfName#DeviceRGBK}, and {@link PdfName#DeviceN}.
*/
public PdfName getProcessColorModel() {
return getPdfObject().getAsName(PdfName.PCM);
}
/**
* Sets separation color names for the trap network appearance, {@link PdfName#SeparationColorNames} key.
*
* @param colorNames an array of names identifying the colorants that were assumed
* when the trap network appearance was created.
* @return object itself.
*/
public PdfFormXObject setSeparationColorNames(PdfArray colorNames) {
return put(PdfName.SeparationColorNames, colorNames);
}
/**
* Gets separation color names of trap network appearance, {@link PdfName#SeparationColorNames} key.
*
* @return an {@link PdfArray} of names identifying the colorants.
*/
public PdfArray getSeparationColorNames() {
return getPdfObject().getAsArray(PdfName.SeparationColorNames);
}
/**
* Sets an array of TrapRegion objects defining the page’s trapping zones
* and the associated trapping parameters, as described in Adobe Technical Note #5620,
* Portable Job Ticket Format. {@link PdfName#TrapRegions} key.
*
* @param regions A {@link PdfArray} of indirect references to TrapRegion objects.
* @return object itself.
*/
public PdfFormXObject setTrapRegions(PdfArray regions) {
return put(PdfName.TrapRegions, regions);
}
/**
* Gets an array of TrapRegion objects defining the page’s trapping zones
* and the associated trapping parameters, as described in Adobe Technical Note #5620,
* Portable Job Ticket Format. {@link PdfName#TrapRegions} key.
*
* @return A {@link PdfArray} of indirect references to TrapRegion objects.
*/
public PdfArray getTrapRegions() {
return getPdfObject().getAsArray(PdfName.TrapRegions);
}
/**
* Sets a human-readable text string that described this trap network to the user.
* {@link PdfName#TrapStyles} key.
*
* @param trapStyles a {@link PdfString} value.
* @return object itself.
*/
public PdfFormXObject setTrapStyles(PdfString trapStyles) {
return put(PdfName.TrapStyles, trapStyles);
}
/**
* Gets a human-readable text string that described this trap network to the user.
* {@link PdfName#TrapStyles} key.
*
* @return a {@link PdfString} value.
*/
public PdfString getTrapStyles() {
return getPdfObject().getAsString(PdfName.TrapStyles);
}
//-----Additional entries in form dictionary for Printer Mark annotation
/**
* Sets a text string representing the printer’s mark in human-readable form.
*
* @param markStyle a string value.
* @return object itself.
*/
public PdfFormXObject setMarkStyle(PdfString markStyle) {
return put(PdfName.MarkStyle, markStyle);
}
/**
* Gets a text string representing the printer’s mark in human-readable form.
*
* @return a string value.
*/
public PdfString getMarkStyle() {
return getPdfObject().getAsString(PdfName.MarkStyle);
}
/**
* Puts the value into Image XObject dictionary and associates it with the specified key.
* If the key is already present, it will override the old value with the specified one.
*
* @param key key to insert or to override
* @param value the value to associate with the specified key
* @return object itself.
*/
public PdfFormXObject put(PdfName key, PdfObject value) {
getPdfObject().put(key, value);
setModified();
return this;
}
}