首页 文章

将PDF转换为多页tiff(第4组)

提问于
浏览
4

我正在尝试将org.apache.pdfbox.pdmodel.PDDocument类和icafe库(https://github.com/dragon66/icafe/)所代表的PDF转换为具有第4组压缩和300 dpi的多页tiff . 示例代码适用于我288 dpi,但奇怪的是不是300 dpi,导出的tiff仍然只是白色 . 有谁知道这里的问题是什么?

我在示例中使用的示例pdf位于:http://www.bergophil.ch/a.pdf

import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;

import cafe.image.ImageColorType;
import cafe.image.ImageParam;
import cafe.image.options.TIFFOptions;
import cafe.image.tiff.TIFFTweaker;
import cafe.image.tiff.TiffFieldEnum.Compression;
import cafe.io.FileCacheRandomAccessOutputStream;
import cafe.io.RandomAccessOutputStream;

public class Pdf2TiffConverter {
    public static void main(String[] args) {
        String pdf = "a.pdf";
        PDDocument pddoc = null;
        try {
            pddoc = PDDocument.load(pdf);
        } catch (IOException e) {
        }

        try {
            savePdfAsTiff(pddoc);
        } catch (IOException e) {
        }
    }

    private static void savePdfAsTiff(PDDocument pdf) throws IOException {
        BufferedImage[] images = new BufferedImage[pdf.getNumberOfPages()];
        for (int i = 0; i < images.length; i++) {
            PDPage page = (PDPage) pdf.getDocumentCatalog().getAllPages()
                    .get(i);
            BufferedImage image;
            try {
//              image = page.convertToImage(BufferedImage.TYPE_INT_RGB, 288); //works
                image = page.convertToImage(BufferedImage.TYPE_INT_RGB, 300); // does not work
                images[i] = image;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        FileOutputStream fos = new FileOutputStream("a.tiff");
        RandomAccessOutputStream rout = new FileCacheRandomAccessOutputStream(
                fos);
        ImageParam.ImageParamBuilder builder = ImageParam.getBuilder();
        ImageParam[] param = new ImageParam[1];
        TIFFOptions tiffOptions = new TIFFOptions();
        tiffOptions.setTiffCompression(Compression.CCITTFAX4);
        builder.imageOptions(tiffOptions);
        builder.colorType(ImageColorType.BILEVEL);
        param[0] = builder.build();
        TIFFTweaker.writeMultipageTIFF(rout, param, images);
        rout.close();
        fos.close();
    }
}

或者是否有另一个库来编写多页TIFF?

编辑:

感谢dragon66, icafe 中的错误现已修复 . 与此同时,我尝试了其他库,并调用了 ghostscript . 我认为 ghostscript 是非常可靠的,因为id是一种广泛使用的工具,另一方面,我必须依赖我的代码的用户有一个 ghostscript-installation ,如下所示:

/**
 * Converts a given pdf as specified by its path to an tiff using group 4 compression
 *
 * @param pdfFilePath The absolute path of the pdf
 * @param tiffFilePath The absolute path of the tiff to be created
 * @param dpi The resolution of the tiff
 * @throws MyException If the conversion fails
 */
private static void convertPdfToTiffGhostscript(String pdfFilePath, String tiffFilePath, int dpi) throws MyException {
    // location of gswin64c.exe
    String ghostscriptLoc = context.getGhostscriptLoc();

    // enclose src and dest. with quotes to avoid problems if the paths contain whitespaces
    pdfFilePath = "\"" + pdfFilePath + "\"";
    tiffFilePath = "\"" + tiffFilePath + "\"";

    logger.debug("invoking ghostscript to convert {} to {}", pdfFilePath, tiffFilePath);
    String cmd = ghostscriptLoc + " -dQUIET -dBATCH -o " + tiffFilePath + " -r" + dpi + " -sDEVICE=tiffg4 " + pdfFilePath;
    logger.debug("The following command will be invoked: {}", cmd);

    int exitVal = 0;
    try {
        exitVal = Runtime.getRuntime().exec(cmd).waitFor();
    } catch (Exception e) {
        logger.error("error while converting to tiff using ghostscript", e);
        throw new MyException(ErrorMessages.GHOSTSTSCRIPT_ERROR, e);
    }
    if (exitVal != 0) {
        logger.error("error while converting to tiff using ghostscript, exitval is {}", exitVal);
        throw new MyException(ErrorMessages.GHOSTSTSCRIPT_ERROR);
    }
}

我发现 ghostscript 产生的 tificafe 产生的 tiff 质量差异很大( tiff 来自 ghostscript 看起来像灰度一样)

2 回答

  • 7

    问题问题已经有一段时间了,我终于找到时间和一个精彩的有序抖动矩阵,这让我可以提供一些关于如何使用“icafe”来获得与调用外部ghostscript可执行文件类似或更好结果的细节 . 最近在“icafe”中添加了一些新功能,例如更好的量化和有序抖动算法,这些算法在以下示例代码中使用 .

    这里我将使用的样本pdf是princeCatalogue . 以下大多数代码来自OP,由于包名称更改和更多ImageParam控件设置而发生一些更改 .

    import java.awt.image.BufferedImage;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    import org.apache.pdfbox.pdmodel.PDDocument;
    import org.apache.pdfbox.pdmodel.PDPage;
    
    import com.icafe4j.image.ImageColorType;
    import com.icafe4j.image.ImageParam;
    import com.icafe4j.image.options.TIFFOptions;
    import com.icafe4j.image.quant.DitherMethod;
    import com.icafe4j.image.quant.DitherMatrix;
    import com.icafe4j.image.tiff.TIFFTweaker;
    import com.icafe4j.image.tiff.TiffFieldEnum.Compression;
    import com.icafe4j.io.FileCacheRandomAccessOutputStream;
    import com.icafe4j.io.RandomAccessOutputStream;
    
    public class Pdf2TiffConverter {
        public static void main(String[] args) {
            String pdf = "princecatalogue.pdf";
            PDDocument pddoc = null;
            try {
                pddoc = PDDocument.load(pdf);
            } catch (IOException e) {
            }
    
            try {
                savePdfAsTiff(pddoc);
            } catch (IOException e) {
            }
        }
    
        private static void savePdfAsTiff(PDDocument pdf) throws IOException {
            BufferedImage[] images = new BufferedImage[pdf.getNumberOfPages()];
            for (int i = 0; i < images.length; i++) {
                PDPage page = (PDPage) pdf.getDocumentCatalog().getAllPages()
                        .get(i);
                BufferedImage image;
                try {
    //              image = page.convertToImage(BufferedImage.TYPE_INT_RGB, 288); //works
                    image = page.convertToImage(BufferedImage.TYPE_INT_RGB, 300); // does not work
                    images[i] = image;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            FileOutputStream fos = new FileOutputStream("a.tiff");
            RandomAccessOutputStream rout = new FileCacheRandomAccessOutputStream(
                    fos);
            ImageParam.ImageParamBuilder builder = ImageParam.getBuilder();
            ImageParam[] param = new ImageParam[1];
            TIFFOptions tiffOptions = new TIFFOptions();
            tiffOptions.setTiffCompression(Compression.CCITTFAX4);
            builder.imageOptions(tiffOptions);
            builder.colorType(ImageColorType.BILEVEL).ditherMatrix(DitherMatrix.getBayer8x8Diag()).applyDither(true).ditherMethod(DitherMethod.BAYER);
            param[0] = builder.build();
            TIFFTweaker.writeMultipageTIFF(rout, param, images);
            rout.close();
            fos.close();
        }
    }
    

    对于ghostscript,我直接使用命令行和OP提供的相同参数 . 所得TIFF图像第一页的屏幕截图如下所示:

    enter image description here

    左侧显示“ghostscript”的输出,右侧显示“icafe”的输出 . 可以看出,至少在这种情况下,“icafe”的输出优于“ghostscript”的输出 .

    使用CCITTFAX4压缩,“ghostscript”的文件大小为2.22M,“icafe”的文件大小为2.08M . 考虑到在创建黑白输出时使用抖动的事实,两者都不是那么好 . 实际上,不同的压缩算法会创建更小的文件大小 . 例如,使用LZW,“icafe”的相同输出仅为634K,如果使用DEFLATE压缩,则输出文件大小降至582K .

  • 2

    这里有一些代码保存在我使用PDFBox的多页tiff中 . 它需要来自PDFBox的TIFFUtil class(它不是公共的,因此您必须制作副本) .

    void saveAsMultipageTIFF(ArrayList<BufferedImage> bimTab, String filename, int dpi) throws IOException
    {
        Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("tiff");
        ImageWriter imageWriter = writers.next();
    
        ImageOutputStream ios = ImageIO.createImageOutputStream(new File(filename));
        imageWriter.setOutput(ios);
        imageWriter.prepareWriteSequence(null);
        for (BufferedImage image : bimTab)
        {
            ImageWriteParam param = imageWriter.getDefaultWriteParam();
            IIOMetadata metadata = imageWriter.getDefaultImageMetadata(new ImageTypeSpecifier(image), param);
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            TIFFUtil.setCompressionType(param, image);
            TIFFUtil.updateMetadata(metadata, image, dpi);
            imageWriter.writeToSequence(new IIOImage(image, null, metadata), param);
        }
        imageWriter.endWriteSequence();
        imageWriter.dispose();
        ios.flush();
        ios.close();
    }
    

    前段时间我通过使用以下代码对自己进行了实验:https://www.java.net/node/670205(我用过解决方案2)

    然而...

    如果你创建一个包含大量图像的数组,你的内存消耗肯定会增加 . 因此,渲染图像,然后将其添加到tiff文件,然后渲染下一页并丢失前一页的引用可能会更好,这样gc可以在需要时获取空间 .

相关问题