Visual signature with pdfbox 1.8.8

I am trying to create a pdf with visual signature and pdfbox. I have two streams, and it seems that pdfbox can only work with files. I was unable to get it to work without three temporary files. I can see from here that the API has changed, but still it deals with files.

public void signPdf(InputStream originalPdf, OutputStream signedPdf, InputStream image, float x, float y, String name, String location, String reason) { File temp = null; File temp2 = null; File scratchFile = null; RandomAccessFile randomAccessFile = null; OutputStream tempOut = null; InputStream tempIn = null; try { /* Copy original to temporary file */ temp = File.createTempFile("signed1", ".tmp"); tempOut = new FileOutputStream(temp); copyStream(originalPdf, tempOut); tempOut.close(); /* Read temporary file to second temporary file and stream */ tempIn = new FileInputStream(temp); temp2 = File.createTempFile("signed2", ".tmp"); tempOut = new FileOutputStream(temp2); copyStream(tempIn, tempOut); tempIn.close(); tempIn = new FileInputStream(temp2); scratchFile = File.createTempFile("signed3", ".bin"); randomAccessFile = new RandomAccessFile(scratchFile, "rw"); /* Read temporary file */ PDDocument document = PDDocument.load(temp, randomAccessFile); document.getCurrentAccessPermission().setCanModify(false); PDSignature signature = new PDSignature(); signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED); signature.setName(name); signature.setLocation(location); signature.setReason(reason); signature.setSignDate(Calendar.getInstance()); PDVisibleSignDesigner signatureDesigner = new PDVisibleSignDesigner( document, image, document.getNumberOfPages()); signatureDesigner.xAxis(250).yAxis(60).zoom(-90).signatureFieldName("signature"); PDVisibleSigProperties signatureProperties = new PDVisibleSigProperties(); signatureProperties.signerName(name).signerLocation(location) .signatureReason(reason).preferredSize(0).page(1) .visualSignEnabled(true).setPdVisibleSignature(signatureDesigner) .buildSignature(); SignatureOptions options = new SignatureOptions(); options.setVisualSignature(signatureProperties); document.addSignature(signature, dataSigner, options); /* Sign */ document.saveIncremental(tempIn, tempOut); document.close(); tempIn.close(); /* Copy temporary file to an output stream */ tempIn = new FileInputStream(temp2); copyStream(tempIn, signedPdf); } catch (IOException e) { logger.error("PDF signing failure", e); } catch (COSVisitorException e) { logger.error("PDF creation failure", e); } catch (SignatureException e) { logger.error("PDF signing failure", e); } finally { closeStream(originalPdf); closeStream(signedPdf); closeStream(randomAccessFile); closeStream(tempOut); deleteTempFile(temp); deleteTempFile(temp2); deleteTempFile(scratchFile); } } private void deleteTempFile(File tempFile) { if (tempFile != null && tempFile.exists() && !tempFile.delete()) { tempFile.deleteOnExit(); } } private void closeStream(Closeable is) { if (is!= null) { try { is.close(); } catch (IOException e) { logger.error("failure", e); } } } private void copyStream(InputStream is, OutputStream os) throws IOException { byte[] buffer = new byte[1024]; int c; while ((c = is.read(buffer)) != -1) { os.write(buffer, 0, c); } is.close(); } 

Besides file madness, I don't see any text in the signature. Here is the result:

enter image description here

and it looks like this when I do this with the itext library

enter image description here

Why is the name, location, and reason missing from the visual signature? How can i fix this?

+5
source share
2 answers

Why is the name, location, and reason missing from the visual signature?

They are not, because they are not drawn.

By default, iText presents a rendered signature, adding this information to the renderer.

By default, the PDFBox ' PDVisibleSigBuilder for presenting a rendered signature does not have this information.

Wrong or wrong, both are simply the default values.

The canonical place where people should look for such information is, finally, a signature panel.

How can i fix this?

The actual contents of the signature render are created by the PDVisibleSigBuilder instance during signatureProperties.buildSignature() :

 public void buildSignature() throws IOException { PDFTemplateBuilder builder = new PDVisibleSigBuilder(); PDFTemplateCreator creator = new PDFTemplateCreator(builder); setVisibleSignature(creator.buildPDF(getPdVisibleSignature())); } 

Thus replacing

  signatureProperties.signerName(name).signerLocation(location) .signatureReason(reason).preferredSize(0).page(1) .visualSignEnabled(true).setPdVisibleSignature(signatureDesigner) .buildSignature(); 

in your code

  signatureProperties.signerName(name).signerLocation(location) .signatureReason(reason).preferredSize(0).page(1) .visualSignEnabled(true).setPdVisibleSignature(signatureDesigner); PDFTemplateBuilder builder = new ExtSigBuilder(); PDFTemplateCreator creator = new PDFTemplateCreator(builder); signatureProperties.setVisibleSignature(creator.buildPDF(signatureProperties.getPdVisibleSignature())); 

for a custom version of ExtSigBuilder this ExtSigBuilder class, you can draw anything, for example:

 class ExtSigBuilder extends PDVisibleSigBuilder { String fontName; public void createImageForm(PDResources imageFormResources, PDResources innerFormResource, PDStream imageFormStream, PDRectangle formrect, AffineTransform affineTransform, PDJpeg img) throws IOException { super.createImageForm(imageFormResources, innerFormResource, imageFormStream, formrect, affineTransform, img); PDFont font = PDType1Font.HELVETICA; fontName = getStructure().getImageForm().getResources().addFont(font); logger.info("Added font to image form: " + fontName); } public void injectAppearanceStreams(PDStream holderFormStream, PDStream innterFormStream, PDStream imageFormStream, String imageObjectName, String imageName, String innerFormName, PDVisibleSignDesigner properties) throws IOException { super.injectAppearanceStreams(holderFormStream, innterFormStream, imageFormStream, imageObjectName, imageName, innerFormName, properties); String imgFormComment = "q " + 100 + " 0 0 50 0 0 cm /" + imageName + " Do Q\n"; String text = "BT /" + fontName + " 10 Tf (Hello) Tj ET\n"; appendRawCommands(getStructure().getImageFormStream().createOutputStream(), imgFormComment + text); logger.info("Added text commands to image form: " + text); } } 

writes "Hello" in Helvetica 10 in the upper left corner of the image form (the form actually displays something).

PS: In my opinion, the object-oriented structure behind this should be completely redone.

+6
source

Show the text above the signature for a while when it does not look beautiful. For me, I create a new database of images in the text that I want to display. And combine it with the signed image. I can add a background for the signer of the image (company watermark name)

Here is the code creating a new signed image with text and background:

 public class ImageSignatory { public static void main(String[] args) { DateFormat df = new SimpleDateFormat("MM.dd.yyyy"); Date today = Calendar.getInstance().getTime(); String reportDate = df.format(today); String text = "Signature eletronic Company AA DUC NGUYEN - Date " + reportDate; String background = "background.png"; String signImage = "sign.jpg"; String textImage = createImageFromText(text); String imageResultURL = null ; try { String mergedImage = mergeTwoImage(signImage, textImage); imageResultURL = addBackgroundToImage(background, mergedImage); } catch (Exception ex) { } } public static String StringDivider(String s) { StringBuilder sb = new StringBuilder(s); int i = 0; while ((i = sb.indexOf(" ", i + 30)) != -1) { sb.replace(i, i + 1, "\n"); } return sb.toString(); } public static BufferedImage toBufferedImage(Image img) { if (img instanceof BufferedImage) { return (BufferedImage) img; } BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics2D bGr = bimage.createGraphics(); bGr.drawImage(img, 0, 0, null); bGr.dispose(); return bimage; } public static String addBackgroundToImage(String backgroundPATH, String imagePath) { try { String imageResult = "result.jpg"; Image backgroundImage = ImageIO.read(new File(backgroundPATH)); int width = backgroundImage.getWidth(null); int height = backgroundImage.getHeight(null); Image signAndText = ImageIO.read(new File(imagePath)); signAndText = signAndText.getScaledInstance(width, height, Image.SCALE_SMOOTH); signAndText = toBufferedImage(signAndText); BufferedImage combined = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g2 = combined.getGraphics(); g2.drawImage(backgroundImage, 0, 0, null); g2.drawImage(signAndText, 0, 0, null); g2.dispose(); ImageIO.write(combined, "JPG", new File(imageResult)); return imageResult; } catch (IOException ex) { return null; } } public static String mergeTwoImage(String first, String second) { try { String tempFileName = "merged_image.png"; Image signatoryImage = ImageIO.read(new File(first)); Image addtionalTextImage = ImageIO.read(new File(second)); float ratio = (float) signatoryImage.getWidth(null) / (float) addtionalTextImage.getWidth(null); addtionalTextImage = addtionalTextImage.getScaledInstance((int) (ratio * (float) addtionalTextImage.getWidth(null)), (int) (ratio * (float) addtionalTextImage.getHeight(null)), Image.SCALE_SMOOTH); addtionalTextImage = toBufferedImage(addtionalTextImage); BufferedImage combinedTemp = new BufferedImage(signatoryImage.getWidth(null), signatoryImage.getHeight(null) + (int) (ratio * (float) addtionalTextImage.getHeight(null)), BufferedImage.TYPE_INT_ARGB); Graphics g = combinedTemp.getGraphics(); g.drawImage(signatoryImage, 0, 0, null); g.drawImage(addtionalTextImage, 0, signatoryImage.getHeight(null), null); g.dispose(); ImageIO.write(combinedTemp, "PNG", new File(tempFileName)); return tempFileName; } catch (IOException ex) { return null; } } public static String createImageFromText(String text) { String tempFileName = "text.png"; text = StringDivider(text); String[] textParts = text.split("\n"); BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = img.createGraphics(); Font font = new Font("Arial", Font.PLAIN, 48); g2d.setFont(font); FontMetrics fm = g2d.getFontMetrics(); int width = 0; for (String textPart : textParts) { int tempWidth = fm.stringWidth(textPart); if (tempWidth > width) { width = tempWidth; } } width += 10; int oneLineHeight = fm.getHeight(); int height = (oneLineHeight) * textParts.length; g2d.dispose(); img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); g2d = img.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); g2d.setFont(font); fm = g2d.getFontMetrics(); g2d.setColor(Color.BLACK); int index = 0; for (String textPart : textParts) { g2d.drawString(textPart, 5, (oneLineHeight) * index + fm.getAscent()); index++; } g2d.dispose(); try { ImageIO.write(img, "PNG", new File(tempFileName)); } catch (IOException ex) { return null; } return tempFileName; } public static void removeFile(String fileName) { try { File file = new File(fileName); file.delete(); } catch (Exception ex) { } } 

}

+2
source

Source: https://habr.com/ru/post/1211002/


All Articles