使用 Itext 将 Pdf 页面转换为字节数组

Posted

技术标签:

【中文标题】使用 Itext 将 Pdf 页面转换为字节数组【英文标题】:Convert Pdf pages to Byte array using Itext 【发布时间】:2016-06-17 15:08:02 【问题描述】:

我的问题

我正在寻找一种将单个 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

【问题讨论】:

首先,“将 PDF 转换为图像”真的是指“从 PDF 中提取现有图像”吗?查看您的代码,它似乎是关于提取而不是转换。 @ChrisHaas 目标是转换它。现在,itext 正在做的事情(据我所知)是使 pdf 中的每一页成为单独的 jpg 文件。然后将每个 jpg 文件合并为一个多页 tiff。我想停止制作本地临时文件,并在内存中执行 pdf -&gt; byte[] -&gt; BufferedImage -&gt; MultipageTiff 全部 就像@Chris 说的那样,您的问题作为一个整体并不清楚,部分是关于将页面呈现为图像,部分是关于从页面中提取位图图像。 Itext(尚)不包含图像渲染 API,但它确实具有位图提取 API。 pdf -&gt; byte[] -&gt; BufferedImage -&gt; MultipageTiff - 你希望byte[] 包含什么? 我已经精简了这个问题,希望它更清楚。 @mkl所以,如果我理解您在第一条评论中所说的话, itext 不会制作图像,而是提取它们?这就是我的第二条评论,我可能有点不清楚。我想尝试将pdf中的每一页提取到一个单独的字节[],而不是将整个pdf提取到一个字节[]。 【参考方案1】:

我做了一些挖掘并想出了一个解决方案!希望其他人在需要时能找到它,并尽可能提供帮助。干杯!

扩展 RenderListener 类

我环顾四周,发现this. 通过代码和类,我发现PdfImageObjects 有一个getBufferedImage(),这正是我要找的。现在无需转换为byte[],这是我最初认为我必须要做的。使用给定的示例代码,我想出了这个类:

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());
    

与上面的链接相比,此处需要注意的重要变化是添加了新字段 ArrayList&lt;BufferedImage&gt; bimg、该字段的 getter 以及 renderImage() 函数的重组。

我还更改了我项目其他类中的一些方法:

将 PDF 突发到 BufferedImage[] 的代码

// Credit to Mihai. Code found here: http://***.com/questions/6851385/save-tiff-ccittfaxdecode-from-pdf-page-using-itext-and-java
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) 方法中。

正如下面 cmets 中的@mkl 所指出的,代码正在提取 pdf 页面中的所有图像,因为 pdf 页面本身并不是图像。如果您在使用 OCR 扫描的 pdf 或具有多层的 pdf 上运行此代码,则可能会出现问题。在这种情况下,当您(可能)希望它们一起保存在一个页面上时,您会将单个 pdf 页面中的多个图像转换为多个 tiff 图像。

我找到的好资源:

Programcreek search for PdfReaderContentParser

【讨论】:

与您的问题相反,您的代码 (A) 通常不会呈现整个页面,而是仅从页面中提取位图图像 --- 在扫描的情况下PDF 中这些概念可能是一致的,尽管 --- 和 B 这里根本看不到 byte 数组。如果您从一开始就要求提供一种从 PDF 中提取嵌入位图图像的方法,那么您很快就会得到答案。 我似乎不太了解 pdf 是什么/它们如何在其中存储数据。现在将这些特定的 pdf 文件视​​为容器内的图像更有意义。至于B,这似乎是我没有完全理解问题范围和实际需要的结果。无论哪种方式,我都感谢你花时间解释和帮助我@mkl

以上是关于使用 Itext 将 Pdf 页面转换为字节数组的主要内容,如果未能解决你的问题,请参考以下文章

使用 iText 将 HTML 转换为 PDF

使用 iText 将 SVG 转换为 PDF,SVG 未在 PDF 中完全显示

iText7 将 HTML 转换为 PDF“System.NullReferenceException”。

如何使用 iText 将 HTML 转换为 PDF [重复]

仿百度文库方案[openoffice.org 3+swftools+flexpaper] 使用iText将jpgjpegpng转换为pdf

仿百度文库方案[openoffice.org 3+swftools+flexpaper] 使用iText将jpgjpegpng转换为pdf