use of com.tom_roush.pdfbox.pdmodel.common.PDRectangle in project PdfBox-Android by TomRoush.
the class PDAcroForm method flatten.
/**
* This will flatten the specified form fields.
*
* <p>Flattening a form field will take the current appearance and make that part
* of the pages content stream. All form fields and annotations associated are removed.</p>
*
* <p>Invisible and hidden fields will be skipped and will not become part of the
* page content stream</p>
*
* @param fields
* @param refreshAppearances if set to true the appearances for the form field widgets will be updated
* @throws IOException
*/
public void flatten(List<PDField> fields, boolean refreshAppearances) throws IOException {
// Nothing to flatten if there are no fields provided
if (fields.isEmpty()) {
return;
}
if (!refreshAppearances && getNeedAppearances()) {
Log.w("PdfBox-Android", "acroForm.getNeedAppearances() returns true, " + "visual field appearances may not have been set");
Log.w("PdfBox-Android", "call acroForm.refreshAppearances() or " + "use the flatten() method with refreshAppearances parameter");
}
// from the XFA content into a static PDF.
if (xfaIsDynamic()) {
Log.w("PdfBox-Android", "Flatten for a dynamix XFA form is not supported");
return;
}
// refresh the appearances if set
if (refreshAppearances) {
refreshAppearances(fields);
}
// the content stream to write to
PDPageContentStream contentStream;
// get the widgets per page
Map<COSDictionary, Set<COSDictionary>> pagesWidgetsMap = buildPagesWidgetsMap(fields);
// preserve all non widget annotations
for (PDPage page : document.getPages()) {
Set<COSDictionary> widgetsForPageMap = pagesWidgetsMap.get(page.getCOSObject());
// indicates if the original content stream
// has been wrapped in a q...Q pair.
boolean isContentStreamWrapped = false;
List<PDAnnotation> annotations = new ArrayList<PDAnnotation>();
for (PDAnnotation annotation : page.getAnnotations()) {
if (widgetsForPageMap != null && !widgetsForPageMap.contains(annotation.getCOSObject())) {
annotations.add(annotation);
} else if (!annotation.isInvisible() && !annotation.isHidden() && annotation.getNormalAppearanceStream() != null && annotation.getNormalAppearanceStream().getBBox() != null) {
contentStream = new PDPageContentStream(document, page, AppendMode.APPEND, true, !isContentStreamWrapped);
isContentStreamWrapped = true;
PDAppearanceStream appearanceStream = annotation.getNormalAppearanceStream();
PDFormXObject fieldObject = new PDFormXObject(appearanceStream.getCOSObject());
contentStream.saveGraphicsState();
// translate the appearance stream to the widget location if there is
// not already a transformation in place
boolean needsTranslation = resolveNeedsTranslation(appearanceStream);
// scale the appearance stream - mainly needed for images
// in buttons and signatures
boolean needsScaling = resolveNeedsScaling(annotation, page.getRotation());
Matrix transformationMatrix = new Matrix();
boolean transformed = false;
if (needsTranslation) {
transformationMatrix.translate(annotation.getRectangle().getLowerLeftX(), annotation.getRectangle().getLowerLeftY());
transformed = true;
}
// PDFBOX-4693: field could have a rotation matrix
Matrix m = appearanceStream.getMatrix();
int angle = (int) Math.round(Math.toDegrees(Math.atan2(m.getShearY(), m.getScaleY())));
int rotation = (angle + 360) % 360;
if (needsScaling) {
PDRectangle bbox = appearanceStream.getBBox();
PDRectangle fieldRect = annotation.getRectangle();
float xScale;
float yScale;
if (rotation == 90 || rotation == 270) {
xScale = fieldRect.getWidth() / bbox.getHeight();
yScale = fieldRect.getHeight() / bbox.getWidth();
} else {
xScale = fieldRect.getWidth() / bbox.getWidth();
yScale = fieldRect.getHeight() / bbox.getHeight();
}
Matrix scalingMatrix = Matrix.getScaleInstance(xScale, yScale);
transformationMatrix.concatenate(scalingMatrix);
transformed = true;
}
if (transformed) {
contentStream.transform(transformationMatrix);
}
contentStream.drawForm(fieldObject);
contentStream.restoreGraphicsState();
contentStream.close();
}
}
page.setAnnotations(annotations);
}
// remove the fields
removeFields(fields);
// remove XFA for hybrid forms
dictionary.removeItem(COSName.XFA);
}
use of com.tom_roush.pdfbox.pdmodel.common.PDRectangle in project PdfBox-Android by TomRoush.
the class PDFTemplateCreator method buildPDF.
/**
* Build a PDF with a visible signature step by step, and return it as a stream.
*
* @param properties
* @return InputStream
* @throws IOException
*/
public InputStream buildPDF(PDVisibleSignDesigner properties) throws IOException {
Log.i("PdfBox-Android", "pdf building has been started");
PDFTemplateStructure pdfStructure = pdfBuilder.getStructure();
// we create array of [Text, ImageB, ImageC, ImageI]
pdfBuilder.createProcSetArray();
// create page
pdfBuilder.createPage(properties);
PDPage page = pdfStructure.getPage();
// create template
pdfBuilder.createTemplate(page);
PDDocument template = pdfStructure.getTemplate();
// create /AcroForm
pdfBuilder.createAcroForm(template);
PDAcroForm acroForm = pdfStructure.getAcroForm();
// AcroForm contains signature fields
pdfBuilder.createSignatureField(acroForm);
PDSignatureField pdSignatureField = pdfStructure.getSignatureField();
// create signature
// TODO
// The line below has no effect with the CreateVisibleSignature example.
// The signature field is needed as a "holder" for the /AP tree,
// but the /P and /V PDSignatureField entries are ignored by PDDocument.addSignature
pdfBuilder.createSignature(pdSignatureField, page, "");
// that is /AcroForm/DR entry
pdfBuilder.createAcroFormDictionary(acroForm, pdSignatureField);
// create AffineTransform
pdfBuilder.createAffineTransform(properties.getTransform());
AffineTransform transform = pdfStructure.getAffineTransform();
// rectangle, formatter, image. /AcroForm/DR/XObject contains that form
pdfBuilder.createSignatureRectangle(pdSignatureField, properties);
pdfBuilder.createFormatterRectangle(properties.getFormatterRectangleParameters());
PDRectangle bbox = pdfStructure.getFormatterRectangle();
pdfBuilder.createSignatureImage(template, properties.getImage());
// create form stream, form and resource.
pdfBuilder.createHolderFormStream(template);
PDStream holderFormStream = pdfStructure.getHolderFormStream();
pdfBuilder.createHolderFormResources();
PDResources holderFormResources = pdfStructure.getHolderFormResources();
pdfBuilder.createHolderForm(holderFormResources, holderFormStream, bbox);
// that is /AP entry the appearance dictionary.
pdfBuilder.createAppearanceDictionary(pdfStructure.getHolderForm(), pdSignatureField);
// inner form stream, form and resource (holder form contains inner form)
pdfBuilder.createInnerFormStream(template);
pdfBuilder.createInnerFormResource();
PDResources innerFormResource = pdfStructure.getInnerFormResources();
pdfBuilder.createInnerForm(innerFormResource, pdfStructure.getInnerFormStream(), bbox);
PDFormXObject innerForm = pdfStructure.getInnerForm();
// inner form must be in the holder form as we wrote
pdfBuilder.insertInnerFormToHolderResources(innerForm, holderFormResources);
// Image form is in this structure: /AcroForm/DR/FRM/Resources/XObject/n2
pdfBuilder.createImageFormStream(template);
PDStream imageFormStream = pdfStructure.getImageFormStream();
pdfBuilder.createImageFormResources();
PDResources imageFormResources = pdfStructure.getImageFormResources();
pdfBuilder.createImageForm(imageFormResources, innerFormResource, imageFormStream, bbox, transform, pdfStructure.getImage());
pdfBuilder.createBackgroundLayerForm(innerFormResource, bbox);
// now inject procSetArray
pdfBuilder.injectProcSetArray(innerForm, page, innerFormResource, imageFormResources, holderFormResources, pdfStructure.getProcSet());
COSName imageFormName = pdfStructure.getImageFormName();
COSName imageName = pdfStructure.getImageName();
COSName innerFormName = pdfStructure.getInnerFormName();
// now create Streams of AP
pdfBuilder.injectAppearanceStreams(holderFormStream, imageFormStream, imageFormStream, imageFormName, imageName, innerFormName, properties);
pdfBuilder.createVisualSignature(template);
pdfBuilder.createWidgetDictionary(pdSignatureField, holderFormResources);
InputStream in = getVisualSignatureAsStream(pdfStructure.getVisualSignature());
Log.i("PdfBox-Android", "stream returning started, size= " + in.available());
// we must close the document
template.close();
// return result of the stream
return in;
}
use of com.tom_roush.pdfbox.pdmodel.common.PDRectangle in project PdfBox-Android by TomRoush.
the class PDVisibleSigBuilder method createFormatterRectangle.
@Override
public void createFormatterRectangle(int[] params) {
PDRectangle formatterRectangle = new PDRectangle();
formatterRectangle.setLowerLeftX(Math.min(params[0], params[2]));
formatterRectangle.setLowerLeftY(Math.min(params[1], params[3]));
formatterRectangle.setUpperRightX(Math.max(params[0], params[2]));
formatterRectangle.setUpperRightY(Math.max(params[1], params[3]));
pdfStructure.setFormatterRectangle(formatterRectangle);
Log.i("PdfBox-Android", "Formatter rectangle has been created");
}
use of com.tom_roush.pdfbox.pdmodel.common.PDRectangle in project PdfBox-Android by TomRoush.
the class AppearanceGeneratorHelper method insertGeneratedAppearance.
/**
* Generate and insert text content and clipping around it.
*/
private void insertGeneratedAppearance(PDAnnotationWidget widget, PDAppearanceStream appearanceStream, OutputStream output) throws IOException {
PDPageContentStream contents = new PDPageContentStream(field.getAcroForm().getDocument(), appearanceStream, output);
PDRectangle bbox = resolveBoundingBox(widget, appearanceStream);
// Acrobat calculates the left and right padding dependent on the offset of the border edge
// This calculation works for forms having been generated by Acrobat.
// The minimum distance is always 1f even if there is no rectangle being drawn around.
float borderWidth = 0;
if (widget.getBorderStyle() != null) {
borderWidth = widget.getBorderStyle().getWidth();
}
PDRectangle clipRect = applyPadding(bbox, Math.max(1f, borderWidth));
PDRectangle contentRect = applyPadding(clipRect, Math.max(1f, borderWidth));
contents.saveGraphicsState();
// Acrobat always adds a clipping path
contents.addRect(clipRect.getLowerLeftX(), clipRect.getLowerLeftY(), clipRect.getWidth(), clipRect.getHeight());
contents.clip();
// get the font
PDFont font = defaultAppearance.getFont();
if (font == null) {
throw new IllegalArgumentException("font is null, check whether /DA entry is incomplete or incorrect");
}
if (font.getName().contains("+")) {
Log.w("PdfBox-Android", "Font '" + defaultAppearance.getFontName().getName() + "' of field '" + field.getFullyQualifiedName() + "' contains subsetted font '" + font.getName() + "'");
Log.w("PdfBox-Android", "This may bring trouble with PDField.setValue(), PDAcroForm.flatten() or " + "PDAcroForm.refreshAppearances()");
Log.w("PdfBox-Android", "You should replace this font with a non-subsetted font:");
Log.w("PdfBox-Android", "PDFont font = PDType0Font.load(doc, new FileInputStream(fontfile), false);");
Log.w("PdfBox-Android", "acroForm.getDefaultResources().put(COSName.getPDFName(\"" + defaultAppearance.getFontName().getName() + "\", font);");
}
// calculate the fontSize (because 0 = autosize)
float fontSize = defaultAppearance.getFontSize();
if (fontSize == 0) {
fontSize = calculateFontSize(font, contentRect);
}
// options
if (field instanceof PDListBox) {
insertGeneratedListboxSelectionHighlight(contents, appearanceStream, font, fontSize);
}
// start the text output
contents.beginText();
// write font and color from the /DA string, with the calculated font size
defaultAppearance.writeTo(contents, fontSize);
// calculate the y-position of the baseline
float y;
// calculate font metrics at font size
float fontScaleY = fontSize / FONTSCALE;
float fontBoundingBoxAtSize = font.getBoundingBox().getHeight() * fontScaleY;
float fontCapAtSize = font.getFontDescriptor().getCapHeight() * fontScaleY;
float fontDescentAtSize = font.getFontDescriptor().getDescent() * fontScaleY;
if (field instanceof PDTextField && ((PDTextField) field).isMultiline()) {
y = contentRect.getUpperRightY() - fontBoundingBoxAtSize;
} else {
// Adobe shows the text 'shiftet up' in case the caps don't fit into the clipping area
if (fontCapAtSize > clipRect.getHeight()) {
y = clipRect.getLowerLeftY() + -fontDescentAtSize;
} else {
// calculate the position based on the content rectangle
y = clipRect.getLowerLeftY() + (clipRect.getHeight() - fontCapAtSize) / 2;
// check to ensure that ascents and descents fit
if (y - clipRect.getLowerLeftY() < -fontDescentAtSize) {
float fontDescentBased = -fontDescentAtSize + contentRect.getLowerLeftY();
float fontCapBased = contentRect.getHeight() - contentRect.getLowerLeftY() - fontCapAtSize;
y = Math.min(fontDescentBased, Math.max(y, fontCapBased));
}
}
}
// show the text
float x = contentRect.getLowerLeftX();
// chars
if (shallComb()) {
insertGeneratedCombAppearance(contents, appearanceStream, font, fontSize);
} else if (field instanceof PDListBox) {
insertGeneratedListboxAppearance(contents, appearanceStream, contentRect, font, fontSize);
} else {
PlainText textContent = new PlainText(value);
AppearanceStyle appearanceStyle = new AppearanceStyle();
appearanceStyle.setFont(font);
appearanceStyle.setFontSize(fontSize);
// Adobe Acrobat uses the font's bounding box for the leading between the lines
appearanceStyle.setLeading(font.getBoundingBox().getHeight() * fontScaleY);
PlainTextFormatter formatter = new PlainTextFormatter.Builder(contents).style(appearanceStyle).text(textContent).width(contentRect.getWidth()).wrapLines(isMultiLine()).initialOffset(x, y).textAlign(field.getQ()).build();
formatter.format();
}
contents.endText();
contents.restoreGraphicsState();
contents.close();
}
use of com.tom_roush.pdfbox.pdmodel.common.PDRectangle in project PdfBox-Android by TomRoush.
the class PDAcroForm method resolveNeedsScaling.
/**
* Check if there needs to be a scaling transformation applied.
*
* @param annotation
* @param rotation
* @return the need for a scaling transformation.
*/
private boolean resolveNeedsScaling(PDAnnotation annotation, int rotation) {
PDAppearanceStream appearanceStream = annotation.getNormalAppearanceStream();
// Check if there is a transformation within the XObjects content
PDResources resources = appearanceStream.getResources();
if (resources != null && resources.getXObjectNames().iterator().hasNext()) {
return true;
}
PDRectangle bbox = appearanceStream.getBBox();
PDRectangle fieldRect = annotation.getRectangle();
if (rotation == 90 || rotation == 270) {
return Float.compare(bbox.getWidth(), fieldRect.getHeight()) != 0 || Float.compare(bbox.getHeight(), fieldRect.getWidth()) != 0;
} else {
return Float.compare(bbox.getWidth(), fieldRect.getWidth()) != 0 || Float.compare(bbox.getHeight(), fieldRect.getHeight()) != 0;
}
}
Aggregations