使用PDFBox从PDF获取可见签名?
是否可以使用OSS库PDFBox提取签名PDF的可见签名(图像) 工作流程:使用PDFBox从PDF获取可见签名?,pdf,digital-signature,capture,signature,pdfbox,Pdf,Digital Signature,Capture,Signature,Pdfbox,是否可以使用OSS库PDFBox提取签名PDF的可见签名(图像) 工作流程: 列出文件的所有签名 显示包含可见签名的签名 显示哪些是有效的 提取签名图像(需要为每个签名提取正确的图像) 下面这种oop风格的东西会很棒: PDFSignatures [] sigs = document.getPDFSignatures() sig[0].getCN() ... (Buffered)Image visibleSig = sig[0].getVisibleSignature() 找到了类PDSign
PDFSignatures [] sigs = document.getPDFSignatures()
sig[0].getCN()
...
(Buffered)Image visibleSig = sig[0].getVisibleSignature()
找到了类PDSignature和如何签署PDF,但没有将可见签名提取为图像的解决方案。由于没有人来回答,我自己在对您的问题的评论中尝试了我的建议。第一个结果是:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdfviewer.PageDrawer;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.PDGraphicsState;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
public class AnnotationDrawer extends PageDrawer
{
public AnnotationDrawer(int imageType, int resolution) throws IOException
{
super();
this.imageType = imageType;
this.resolution = resolution;
}
public Map<String, BufferedImage> convertToImages(PDPage p) throws IOException
{
page = p;
final Map<String, BufferedImage> result = new HashMap<String, BufferedImage>();
List<PDAnnotation> annotations = page.getAnnotations();
for (PDAnnotation annotation: annotations)
{
String appearanceName = annotation.getAppearanceStream();
PDAppearanceDictionary appearDictionary = annotation.getAppearance();
if( appearDictionary != null )
{
if( appearanceName == null )
{
appearanceName = "default";
}
Map<String, PDAppearanceStream> appearanceMap = appearDictionary.getNormalAppearance();
if (appearanceMap != null)
{
PDAppearanceStream appearance =
(PDAppearanceStream)appearanceMap.get( appearanceName );
if( appearance != null )
{
BufferedImage image = initializeGraphics(annotation);
setTextMatrix(null);
setTextLineMatrix(null);
getGraphicsStack().clear();
processSubStream( page, appearance.getResources(), appearance.getStream() );
String name = annotation.getAnnotationName();
if (name == null || name.length() == 0)
{
name = annotation.getDictionary().getString(COSName.T);
if (name == null || name.length() == 0)
{
name = Long.toHexString(annotation.hashCode());
}
}
result.put(name, image);
}
}
}
}
return result;
}
BufferedImage initializeGraphics(PDAnnotation annotation)
{
PDRectangle rect = annotation.getRectangle();
float widthPt = rect.getWidth();
float heightPt = rect.getHeight();
float scaling = resolution / (float)DEFAULT_USER_SPACE_UNIT_DPI;
int widthPx = Math.round(widthPt * scaling);
int heightPx = Math.round(heightPt * scaling);
//TODO The following reduces accuracy. It should really be a Dimension2D.Float.
Dimension pageDimension = new Dimension( (int)widthPt, (int)heightPt );
BufferedImage retval = new BufferedImage( widthPx, heightPx, imageType );
Graphics2D graphics = (Graphics2D)retval.getGraphics();
graphics.setBackground( TRANSPARENT_WHITE );
graphics.clearRect( 0, 0, retval.getWidth(), retval.getHeight() );
graphics.scale( scaling, scaling );
setGraphics(graphics);
pageSize = pageDimension;
graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
graphics.setRenderingHint( RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON );
setGraphicsState(new PDGraphicsState(new PDRectangle(widthPt, heightPt)));
return retval;
}
void setGraphics(Graphics2D graphics)
{
try {
Field field = PageDrawer.class.getDeclaredField("graphics");
field.setAccessible(true);
field.set(this, graphics);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static final int DEFAULT_USER_SPACE_UNIT_DPI = 72;
private static final Color TRANSPARENT_WHITE = new Color( 255, 255, 255, 0 );
private int imageType;
private int resolution;
}
构造函数参数对应于PDPage.convertToImage(int-imageType,int-resolution)的参数。
当心,这已经发生了
a。已根据PDFBox 1.8.2进行黑客攻击;它可能包含特定版本的代码;
B只是检查了一些我在这里看到的签名注释;它可能不完整,特别是对于其他注释类型,它可能会失败。是的,这是可能的,但不是,它不像仅仅调用一个方法那么简单,但也不是什么神奇的事情。只需研究
PDPage.convertToImage
和PageDrawer.drawPage中发生的情况。
在后一种方法中,您可以看到页面内容之后如何绘制页面批注的外观。基本上,您必须找到签名字段的注释,并仅在各自大小的画布上绘制它们。透明度不受尊重(因此我将在此位置获取可见的捕获+文档)2。两个重叠的签名不可恢复。posion对添加的注释进行匹配并不是很聪明。这就是为什么您应该研究这些方法,并(不幸的是,我没有清楚地阐明)将它们作为自己代码的灵感,只呈现签名注释,并且单独呈现每个注释。感谢您的回复,但实现起来很复杂。在评估一个实现时,我没有发现有可能获得嵌入图像的原始大小(没有DPI),不知道是否真的需要,但我的目标是以原始大小提取图像,而不进行任何缩放。要以原始大小提取图像,这是什么意思?PDF本质上不是光栅化格式(即使它可能包含光栅化图像),但您希望将其渲染为光栅化格式。因此,您可以决定使用哪种分辨率,这意味着可以选择大小。这太棒了!非常感谢,比我想象的简单多了。非常好的示例。获得正常图像的近似分辨率是多少?这里的图像以何种方式不正常?
AnnotationDrawer drawer = new AnnotationDrawer(8, 288);
Map<String, BufferedImage> images = drawer.convertToImages(page);