PDF转化图片及其优化

PDF转化图片及其优化

前言

在打印发票的情况下,由于没有确定的发票模板格式,所以需要将第三方提供的发票pdf文件转换为图片,打印机直接进行图片打印。但热敏打印机在图片打印上并不具有优势,所以需要对整个过程 pdf->图片,图片->打印 进行一定的优化。

pdf转图片

网上常见的开源pdf转换库有pdf-box,pdfium,itext等。

PDFBox

PDFBox由Apache提供,Java实现的PDF文档协作类库,提供PDF文档的创建、处理以及文档内容提取功能,也包含了一些命令行实用工具。
PDFBox最新的版本为 2019-06-272.0.16,最新特征包括:获取Unicode文字分离合并PDF支持pdf表格文字的读取和填写根据PDF / A-1b标准验证PDF文件可以使用Java标准打印Api打印pdf将PDF以图片格式(PNG和JPEG)保存以图文创建pdf数字签名pdf
Android上有一个库,名为PdfBox-Android,提供了Android侧的使用库,但是基于PDFBox 1.8版本,在使用中会有一些字体上渲染的问题。

tip: PdfBox-Android上有一个基于PDFBox 2.0版本的分支,基本使用没有问题。根据作者本人所说是因为 pdfbox 2.0还在一直更新,所以就一直没有合并分支。

简单的pdf转图片代码示例,需要传入pdf文件地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private fun pdfBoxTest(path: String): Bitmap? {
try {
// Load in an already created PDF
val document = PDDocument.load(File(path))
// Create a renderer for the document
val renderer = PDFRenderer(document)
// Render the image to an RGB Bitmap
val pageImage = renderer.renderImage(0, 1f, Bitmap.Config.RGB_565)
saveBitmapFile(pageImage,"box")
// Save the render result to an image
return pageImage
} catch (e: IOException) {
Log.e("PdfBox-Android-Sample", "Exception thrown while rendering file", e)
}
return null
}

预览图如下:

pdfbox转图片

log中输出 No glyph for 28246 (CID 07b5) in font STSong-Light\n No glyph for 31246 (CID 0da5) in font KaiTi_GB2312 即在Android系统没有找到 KaiTi_GB2312字体,导致无法正常显示中文。

pdfium

pdfium由Google开源提供,C++实现,pdfium是用在chrome中显示pdf文件和打印预览。
由于pdfium是由 c++ 实现的,所以要想在 Android 中使用必须要自己进行编译成对应架构的so库,当然也可以直接使用别人编译好的第三方库。
PdfiumAndroid是集成pdfium提供给Android使用的一个库,支持 mips,arm,x86(包括所有64版本)架构。

tip: AndroidPdfViewer 一款基于pdfium用于阅读pdf的控件

简单代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private fun pdfiumTest(path: String): Bitmap {
val mPdfCore = PdfiumCore(this);
val mPdfDoc = mPdfCore.newDocument(ParcelFileDescriptor.open(File(path), ParcelFileDescriptor.MODE_READ_WRITE))
val mPageCount = mPdfCore.getPageCount(mPdfDoc)
val i = 0
mPdfCore.openPage(mPdfDoc, i)

val width = mPdfCore.getPageWidthPoint(mPdfDoc, i)
val height = mPdfCore.getPageHeightPoint(mPdfDoc, i)


// ARGB_8888 - best quality, high memory usage, higher possibility of OutOfMemoryError
// RGB_565 - little worse quality, twice less memory usage
val bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.RGB_565)

mPdfCore.renderPageBitmap(mPdfDoc, bitmap, i, 0, 0,
width, height, true)

saveBitmapFile(bitmap, "ium")

return bitmap
}

预览图如下:

pdfium转图片

文字能够正确的渲染,但缺点也很明显,图片质量模糊即便是设置成Bitmap.Config.ARGB_8888

比对与优化

字体库缺失的问题在Linux上可能比较好解决,但在Android上,若想要添加额外的字体则需要添加自定义的字体库文件到 frameworks/base/data/fonts 文件中,也就是说需要重新编译 Android Framwork或者联系厂商修改。整个过程繁杂且周期长,而且还不确定是否能够覆盖所有的第三方字体。

图片质量模糊可以通过调整 pdfium 渲染的 bitmap 大小进行优化,下图是将 bitmap 放大两倍后进行渲染再缩放回来的预览图,明显清晰不少。。很神奇

pdfium转化图片 X2

虽然PDFBox在pdf上可能支持的功能更多,但是在转图片的表现上优先选择 pdfium

但是在 Android 5.1 上即便使用 pdfium 也会出现字体缺失(出现方块)的情况,而在 Android 7 上没有,应该是Android7的字体库比Android5携带更多的字体吧

图片转Byte数据优化

热敏打印机需要接受 bitmap 的 byte数据才能进行图片的打印,又因为热敏打印机只能打印黑白两色,所有可以针对图片做灰度化及二值化的处理。

灰度化代码

1
2
3
4
5
6
private int grayPixle(int pixel) {
int red=(pixel & 0x00ff0000) >> 16;//获取r分量
int green= (pixel & 0x0000ff00) >> 8;//获取g分量
int blue= pixel & 0x000000ff;//获取b分量
return (int) (red*0.3f+green*0.59f+blue*0.11f);//加权平均法进行灰度化
}

在RGB模型中,当R=G=B时,则彩色表示一种灰度颜色,其中R=G=B的值叫灰度值。因此,灰度图每个像素只需一个字节存放灰度值,灰度范围为0-255.一般有分量法 、最大值法、平均值法、加权平均法对彩色图像进行灰度化。
加权平均法:由于人眼对绿色的敏感最高,对蓝色敏感最低,因此,按下式对RGB三分量进行加权平均能得到比较合理的灰度图像。
Gray=0.30R+0.59G+0.11B

获取灰度值后可以根据一个阈值来调整图像的黑白比例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public static Bitmap getSingleColorBitmap(Bitmap bitmap, @IntRange(from = 0, to = 255) int avgMax) {
int[] pix = new int[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(pix, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
boolean isBitmapEmpty = true;
for (int y = 0; y < bitmap.getHeight(); y++) {
for (int x = 0; x < bitmap.getWidth(); x++) {
int index = y * bitmap.getWidth() + x;
//pix[index] = WHITE;
isBitmapEmpty = false;
//int curColor = bitmap.getPixel(x, y); //这个在bitmap取值就是耗时的原因
int curColor = pix[index];
//获得三原色
int red = Color.red(curColor);
int green = Color.green(curColor);
int blue = Color.blue(curColor);
int alpha = Color.alpha(curColor);
int avg = (red + green + blue) / 3;
if (avg >= avgMax) { //亮度:avg>=126
//设置颜色
pix[index] = Color.argb(alpha, 255, 255, 255);// white
} else {
pix[index] = Color.argb(alpha, 0, 0, 0);// black
}
}
}
if (isBitmapEmpty) {
return null;
}
bitmap.setPixels(pix, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
return bitmap;
}

其中 avgMax>=200中200作为一个阈值,越接近255,黑色部分越多,反之黑色部分越少。

综述

当前只选择了 PDFBoxPDFium 比较有代表性的两个库进行比对,而且还没有算上 PDFBox 的2.0分支,样本案例有点少。从结论上看 PDFBox 的功能性是强于 PDFium 的,但只是单论阅读功能的话,PDFium的支持更加完善,以上。

文章作者: cpacm
文章链接: http://www.cpacm.net/2019/09/06/PDF转化图片及其优化/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 cpacm
打赏
  • 微信
  • 支付宝

评论