使用 Itext 将 Pdf 页面转换为字节数组
Convert Pdf pages to Byte array using Itext
我的问题
我正在寻找一种方法将单个 pdf 页面转换为 byte[](如每个 pdf 页面一个 byte[]),以便我可以将它们转换为 BufferedImage[] .
这样一来,所有的转换都在内存中完成,而不是制作临时文件,从而使转换更快、更简洁。稍后我也可以使用字节数组进行服务调用。如果我能让图书馆只使用 itext 就好了,但是,如果没有任何其他方式,我对其他图书馆开放。
我现在拥有的
这是我目前拥有的代码
public static BufferedImage toBufferedImage(byte[] input) throws IOException {
InputStream in = new ByteArrayInputStream(input);
BufferedImage bimg = ImageIO.read(in);
return bimg;
}
public static BufferedImage[] extract(final String fileName) throws IOException {
PdfReader reader = new PdfReader(fileName);
int pageNum = reader.getNumberOfPages();
BufferedImage[] imgArray = new BufferedImage[pageNum];
for (int page = 0; page < pageNum; page++) {
//TODO: You may need to decode the bytearray first?
imgArray[page] = toBufferedImage(reader.getPageContent(pageNum));
}
reader.close();
return imgArray;
}
public static void convert() throws IOException {
String fileName = getProps("file_in");
BufferedImage[] bim = extract(fileName);
// close streams; Closed implicitily by try-with-resources
}
这是我目前检查过的链接列表(非代表性)。
Useful, but not quite what I want
Uses a different library
我做了一些挖掘并提出了一个解决方案!希望其他人在需要时能找到它,并尽可能提供帮助。干杯!
扩展 RenderListener Class
我环顾四周,发现 this. 查看代码和 classes,我发现 PdfImageObjects 有一个 getBufferedImage()
,这正是我要找的。现在不需要转换为 byte[]
,这是我最初认为我必须要做的。使用给定的示例代码,我想出了这个 class:
public class MyImageRenderListener implements RenderListener {
protected String path = "";
protected ArrayList<BufferedImage> bimg = new ArrayList<>();
/**
* Creates a RenderListener that will look for images.
*/
public MyImageRenderListener(String path) {
this.path = path;
}
public ArrayList<BufferedImage> getBimgArray() {
return bimg;
}
/**
* @see com.itextpdf.text.pdf.parser.RenderListener#renderImage(
* com.itextpdf.text.pdf.parser.ImageRenderInfo)
*/
public void renderImage(ImageRenderInfo renderInfo) {
try {
PdfImageObject image = renderInfo.getImage();
if (image == null) {
return;
}
bimg.add(image.getBufferedImage());
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
与上面的 link 相比,此处需要注意的重要变化是添加了一个新字段 ArrayList<BufferedImage> bimg
、该字段的 getter 以及 renderImage()
函数。
我还更改了我项目的另一个 class 中的一些方法:
将 PDF 分流到 BufferedImage 的代码[]
// Credit to Mihai. Code found here:
public static ArrayList<BufferedImage> getBufImgArr(final String BasePath) throws IOException {
PdfReader reader = new PdfReader(BasePath);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
MyImageRenderListener listener = new MyImageRenderListener(BasePath + "extract/image%s.%s");
for (int page = 1; page <= reader.getNumberOfPages(); page++) {
parser.processContent(page, listener);
}
reader.close();
return listener.getBimgArray();
}
将 BufferedImage[] 转换为多页 Tiff 的代码
public static void convert(String fin) throws FileNotFoundException, IOException {
ArrayList<BufferedImage> bimgArrL = getBufImgArr(fin);
BufferedImage[] bim = new BufferedImage[bimgArrL.size()];
bimgArrL.toArray(bim);
try (RandomAccessOutputStream rout = new FileCacheRandomAccessOutputStream(
new FileOutputStream("/path/you/want/result/to/go.tiff"))) {
// The options for the tiff file are set here.
// **THIS BLOCK USES THE ICAFE LIBRARY TO CONVERT TO MULTIPAGE-TIFF**
// ICAFE: https://github.com/dragon66/icafe
ImageParam.ImageParamBuilder builder = ImageParam.getBuilder();
TIFFOptions tiffOptions = new TIFFOptions();
tiffOptions.setApplyPredictor(true);
tiffOptions.setTiffCompression(Compression.CCITTFAX4);
tiffOptions.setDeflateCompressionLevel(0);
builder.imageOptions(tiffOptions);
TIFFTweaker.writeMultipageTIFF(rout, bim);
// I found this block of code here: https://github.com/dragon66/icafe/wiki
// About 3/4 of the way down the page
}
}
开始整个过程:
public static void main(String[] args){
convert("/path/to/pdf/image.pdf");
}
重要提示:
您可能会注意到 listener.renderImage()
从未在我的代码中显式调用。似乎 renderImage()
是一个辅助函数,在将侦听器对象传递到解析器对象时在其他地方调用。这发生在 getBufImgArr(param)
方法中。
正如@mkl 在下面的评论中指出的那样,代码正在提取 pdf 页面中的所有图像,因为 pdf 页面本身并不是图像。如果您 运行 此代码位于使用 OCR 扫描的 pdf 或具有多层的 pdf 上,则可能会出现问题。在这种情况下,当您(可能)希望它们一起留在一个页面上时,您会将单个 pdf 页面中的多个图像转换为多个 tiff 图像。
我找到的好资源:
我的问题
我正在寻找一种方法将单个 pdf 页面转换为 byte[](如每个 pdf 页面一个 byte[]),以便我可以将它们转换为 BufferedImage[] .
这样一来,所有的转换都在内存中完成,而不是制作临时文件,从而使转换更快、更简洁。稍后我也可以使用字节数组进行服务调用。如果我能让图书馆只使用 itext 就好了,但是,如果没有任何其他方式,我对其他图书馆开放。
我现在拥有的
这是我目前拥有的代码
public static BufferedImage toBufferedImage(byte[] input) throws IOException {
InputStream in = new ByteArrayInputStream(input);
BufferedImage bimg = ImageIO.read(in);
return bimg;
}
public static BufferedImage[] extract(final String fileName) throws IOException {
PdfReader reader = new PdfReader(fileName);
int pageNum = reader.getNumberOfPages();
BufferedImage[] imgArray = new BufferedImage[pageNum];
for (int page = 0; page < pageNum; page++) {
//TODO: You may need to decode the bytearray first?
imgArray[page] = toBufferedImage(reader.getPageContent(pageNum));
}
reader.close();
return imgArray;
}
public static void convert() throws IOException {
String fileName = getProps("file_in");
BufferedImage[] bim = extract(fileName);
// close streams; Closed implicitily by try-with-resources
}
这是我目前检查过的链接列表(非代表性)。
Useful, but not quite what I want
Uses a different library
我做了一些挖掘并提出了一个解决方案!希望其他人在需要时能找到它,并尽可能提供帮助。干杯!
扩展 RenderListener Class
我环顾四周,发现 this. 查看代码和 classes,我发现 PdfImageObjects 有一个 getBufferedImage()
,这正是我要找的。现在不需要转换为 byte[]
,这是我最初认为我必须要做的。使用给定的示例代码,我想出了这个 class:
public class MyImageRenderListener implements RenderListener {
protected String path = "";
protected ArrayList<BufferedImage> bimg = new ArrayList<>();
/**
* Creates a RenderListener that will look for images.
*/
public MyImageRenderListener(String path) {
this.path = path;
}
public ArrayList<BufferedImage> getBimgArray() {
return bimg;
}
/**
* @see com.itextpdf.text.pdf.parser.RenderListener#renderImage(
* com.itextpdf.text.pdf.parser.ImageRenderInfo)
*/
public void renderImage(ImageRenderInfo renderInfo) {
try {
PdfImageObject image = renderInfo.getImage();
if (image == null) {
return;
}
bimg.add(image.getBufferedImage());
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
与上面的 link 相比,此处需要注意的重要变化是添加了一个新字段 ArrayList<BufferedImage> bimg
、该字段的 getter 以及 renderImage()
函数。
我还更改了我项目的另一个 class 中的一些方法:
将 PDF 分流到 BufferedImage 的代码[]
// Credit to Mihai. Code found here:
public static ArrayList<BufferedImage> getBufImgArr(final String BasePath) throws IOException {
PdfReader reader = new PdfReader(BasePath);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
MyImageRenderListener listener = new MyImageRenderListener(BasePath + "extract/image%s.%s");
for (int page = 1; page <= reader.getNumberOfPages(); page++) {
parser.processContent(page, listener);
}
reader.close();
return listener.getBimgArray();
}
将 BufferedImage[] 转换为多页 Tiff 的代码
public static void convert(String fin) throws FileNotFoundException, IOException {
ArrayList<BufferedImage> bimgArrL = getBufImgArr(fin);
BufferedImage[] bim = new BufferedImage[bimgArrL.size()];
bimgArrL.toArray(bim);
try (RandomAccessOutputStream rout = new FileCacheRandomAccessOutputStream(
new FileOutputStream("/path/you/want/result/to/go.tiff"))) {
// The options for the tiff file are set here.
// **THIS BLOCK USES THE ICAFE LIBRARY TO CONVERT TO MULTIPAGE-TIFF**
// ICAFE: https://github.com/dragon66/icafe
ImageParam.ImageParamBuilder builder = ImageParam.getBuilder();
TIFFOptions tiffOptions = new TIFFOptions();
tiffOptions.setApplyPredictor(true);
tiffOptions.setTiffCompression(Compression.CCITTFAX4);
tiffOptions.setDeflateCompressionLevel(0);
builder.imageOptions(tiffOptions);
TIFFTweaker.writeMultipageTIFF(rout, bim);
// I found this block of code here: https://github.com/dragon66/icafe/wiki
// About 3/4 of the way down the page
}
}
开始整个过程:
public static void main(String[] args){
convert("/path/to/pdf/image.pdf");
}
重要提示:
您可能会注意到 listener.renderImage()
从未在我的代码中显式调用。似乎 renderImage()
是一个辅助函数,在将侦听器对象传递到解析器对象时在其他地方调用。这发生在 getBufImgArr(param)
方法中。
正如@mkl 在下面的评论中指出的那样,代码正在提取 pdf 页面中的所有图像,因为 pdf 页面本身并不是图像。如果您 运行 此代码位于使用 OCR 扫描的 pdf 或具有多层的 pdf 上,则可能会出现问题。在这种情况下,当您(可能)希望它们一起留在一个页面上时,您会将单个 pdf 页面中的多个图像转换为多个 tiff 图像。