- 场景,将电子发票pdf 文件解析后,再通过google.zxing解析(主要是根据发票二维码<图>)
由于电子发票文件本身并非纯粹的图片 & pdf 文件,所以处理方案为将pdf 按照 page 内容转图片再去验证发票内容;
使用
// 利用PdfBox生成图像
PDDocument pdDocument = PDDocument.load(pdfFile);
PDFRenderer renderer = new PDFRenderer(pdDocument);
BufferedImage image = renderer.renderImageWithDPI(i, DEFAULT_DPI, ImageType.RGB);
将pdf 转图片后
遇到问题:
1、发票中的二维码转换成图片后无法识别; 原因(因为光点干扰、换句话说就是 pdf 转换图片不够保真);
2、极个别电子发票,二维码无法和发票内容 无法整体识别转图片;(也就是转存图片后,二维码丢失了);
以上两种情况 google.zxing 解析时候,矩阵均会抛出 com.google.zxing.NotFoundException: null
此时,只要将pdf 解析后的图片输出; 就可以发现问题所在了;
当然,问题1 的场景比较难,直观看出; 博主也是搜索他人经验后猜测可能是这种情况;(最后证实确实;)
处理方式:
采用使用多种方式结合; 如果 方式1 无法解析,那么采用方式2去重新解析; 最后还解析不能的话,才报错;
针对问题1,
将pdf 读取成黑白格式的图片可以解决;
renderer.renderImageWithDPI(pageIndex, 296, ImageType.GRAY);
方法 pdfToImage(); 代码下面贴上
针对问题 2:
采用
PDPage page = document.getPage(pageIndex);
PDResources resources = page.getResources();
PDImageXObject obj = (PDImageXObject) resources.getXObject(name);
这种方式去直接提取电子发票的图片讯息;(二维码等)
方法 retryAgain();代码下面贴上
依赖引用;
1)、
<dependency>
<groupId>org.icepdf.os</groupId>
<artifactId>icepdf-core</artifactId>
<version>6.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.19</version>
</dependency>
<dependency>
<groupId>com.levigo.jbig2</groupId>
<artifactId>levigo-jbig2-imageio</artifactId>
<version>2.0</version>
</dependency>
另外遇到一个奇特的现象;
暂时不知道原因;
便是,同一代码;
PDDocument pdDocument = PDDocument.load(pdfFile);
PDFRenderer renderer = new PDFRenderer(pdDocument);
BufferedImage image = renderer.renderImageWithDPI(i, DEFAULT_DPI, ImageType.RGB);
单引入 pdfbox 包时 ,google.zxing 解析报错;
引入 pdfbox & levigo-jbig2-imageio 两个包;google.zxing 解析 成功;
总结:
如果单纯读取 pdf 内容转图片的话,采用方式 1 即可; 引入 pdfbox & levigo-jbig2-imageio 两个包;
代码:
public static List<File> pdfToImage(File pdfFile) {
List<File> list = new ArrayList<>();
try {
logger.debug(">>处理开始");
// System.setProperty(PROPERTY_KEY, PROPERTY_VALUE);
// 图像合并使用参数
// 总宽度
int width = 0;
// 保存一张图片中的RGB数据
int[] singleImgRGB;
int shiftHeight = 0;
// 保存每张图片的像素值
BufferedImage imageResult = null;
// 利用PdfBox生成图像
PDDocument pdDocument = PDDocument.load(pdfFile);
PDFRenderer renderer = new PDFRenderer(pdDocument);
// 生成目录
String fileRealName = pdfFile.getName().replace(".pdf", "");
// 循环每个页码
for (int i = 0, len = pdDocument.getNumberOfPages(); i < len; i++) {
BufferedImage image = renderer.renderImageWithDPI(i, DEFAULT_DPI, ImageType.RGB);
int imageHeight = image.getHeight();
int imageWidth = image.getWidth();
// 计算高度和偏移量
// 使用第一张图片宽度;
width = imageWidth;
// 保存每页图片的像素值
imageResult = new BufferedImage(width, imageHeight, BufferedImage.TYPE_INT_RGB);
singleImgRGB = image.getRGB(0, 0, width, imageHeight, null, 0, width);
// 写入流中
imageResult.setRGB(0, shiftHeight, width, imageHeight, singleImgRGB, 0, width);
// 对图片进行等比例缩放0.95
AffineTransformOp ato = new AffineTransformOp(AffineTransform.getScaleInstance(1, 1), null);
imageResult = ato.filter(image, null);
File file = new File(fileRealName + "_" + (i + 1) + ".png");
// 写图片
ImageIO.write(imageResult, DEFAULT_FORMAT, file);
if (i % 10 == 0 || i + 1 == len) {
logger.debug("处理进度:{}/{}", i + 1, len);
}
list.add(file);
}
pdDocument.close();
logger.debug("处理结束>>");
return list;
} catch (Exception e) {
logger.error("PDF转图片失败:{}", e);
}
return list;
}
/**
* @description 仅解析发票漂浮的盖章(二维码 或者 是整个图片); PDResources
* @param file
* @param pageIndex
* @return
*/
public static Map<String, Object> retryAgain(File file, int pageIndex) {
log.debug("前置位两种方式尝试解析都失败,最后尝试retryAgain方法解析;");
Map<String, Object> ret = new HashedMap<String, Object>();
ret.put("status", -1);
HashMap<DecodeHintType, Object> hints = new HashMap<>();
// 设置编码字符集
hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
List<BufferedImage> imageList = new ArrayList<BufferedImage>();
PDDocument document = null;
File optFile = null, tempFile = null;
try {
document = PDDocument.load(file);
int number = document.getNumberOfPages();
if (pageIndex < 0 || pageIndex >= number)
return ret;
// 电子发票读取指定一页
PDPage page = document.getPage(pageIndex);
PDResources resources = page.getResources();
// 仅解析发票漂浮的盖章(二维码 或者 是整个图片);
for (COSName name : resources.getXObjectNames())
if (resources.isImageXObject(name)) {
PDImageXObject obj = (PDImageXObject) resources.getXObject(name);
imageList.add(obj.getImage());
}
String savepath = file.getAbsolutePath().replace(".pdf", "");
// pdfbox_image.png
tempFile = new File(savepath);
if (!tempFile.exists())
tempFile.mkdirs();
Result result = null;
BinaryBitmap binaryBitmap;
String filePath = savepath + "\\pdfbox_image.png";
for (BufferedImage img : imageList) {
optFile = new File(filePath);
ImageIO.write(img, "PNG", optFile);
// 转灰色
img = IcePdf.changeImg(optFile);
binaryBitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(img)));
result = new MultiFormatReader().decode(binaryBitmap, hints);
if (null != result && !StringUtil.isNullOrEmpty(result.getText())) {
ret = getInvoiceInfo(result.getText());
break;
}
}
} catch (Exception e) {
log.error("retryAgain 异常={}", e);
} finally {
try {
// 关闭document
document.close();
// 移除业务过程中生成的目录和文件
if (null != optFile)
optFile.delete();
if (null != tempFile)
tempFile.delete();
} catch (Exception e) {
log.error("关闭document、移除业务过程中生成的目录和文件 异常 Exception={}", e);
}
}
log.debug("retryAgain 执行结束 ret = {}", ret);
return ret;
}
com.google.zxing
识别发票方法;
/**
* 扫描图片二维码并封装发票信息
*
* @param file
* @param companyId
* @param userId
* @return
* @throws IOException
* @throws ParseException
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static Map<String, Object> readQRCode(File file, String companyId, String userId)
throws IOException, ParseException {
Map<String, Object> map = new HashMap<String, Object>();
map.put("status", 1);
MultiFormatReader formatReader = new MultiFormatReader();
// 读取图片
// BufferedImage image = ImageIO.read(file);
BufferedImage image = changeImg(file);
if (null == image) {
log.error("转换图片异常,image为空");
return map;
}
BinaryBitmap binaryBitmap =
new BinaryBitmap(
new HybridBinarizer(new BufferedImageLuminanceSource(image.getSubimage(0, 0, 800, 800))));
// 定义二维码的参数
HashMap hints = new HashMap();
// 设置编码字符集
hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
// 处理读取结果
Result result = null;
try {
result = formatReader.decode(binaryBitmap, hints);
if (result != null && !StringUtil.isNullOrEmpty(result.getText())) {
map = getInvoiceInfo(result.getText());
}
} catch (NotFoundException e) {
map.put("status", -1);
log.error("readQRCode执行异常={}",e);
}
return map;
}