java - pdfbox - pdf 转换为灰度后增加大小
问题描述
我需要将扫描的 PDF 转换为灰度 PDF。我为此找到了 2 个解决方案。
第一个是只使用renderImage
private void convertToGray() throws IOException {
File pdfFile = new File(PATH);
try (PDDocument originalPdf = PDDocument.load(pdfFile);
PDDocument doc = new PDDocument()) {
LOGGER.info("Current heap after loading file: {}", Runtime.getRuntime().totalMemory());
PDFRenderer pdfRenderer = new PDFRenderer(originalPdf);
for (int pageNum = 0; pageNum < originalPdf.getNumberOfPages(); pageNum++) {
// PDImageXObject pdImage = LosslessFactory.createFromImage(doc, bufferedImage);
BufferedImage grayImage = pdfRenderer.renderImageWithDPI(pageNum, 300F, ImageType.GRAY);
PDImageXObject pdImage = JPEGFactory.createFromImage(doc, grayImage);
float pageWight = originalPdf.getPage(pageNum).getMediaBox().getWidth();
float pageHeight = originalPdf.getPage(pageNum).getMediaBox().getHeight();
PDPage page = new PDPage(new PDRectangle(pageWight, pageHeight));
doc.addPage(page);
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
contentStream.drawImage(pdImage, 0F, 0F, pageWight, pageHeight);
}
}
doc.save(NEW_PATH);
}
}
但这会导致文件大小增加(因为某些 PDF 的 DPI 低于 300。
第二个是用灰色模拟替换现有图像
private void convertByImageToGray() throws IOException {
File pdfFile = new File(PATH);
try (PDDocument document = PDDocument.load(pdfFile)) {
List<COSObject> objects = document.getDocument().getObjectsByType(COSName.IMAGE);
for (COSObject object : objects) {
LOGGER.info("Class: {}; {}", object.getClass(), object.toString());
}
for (int pageNum = 0; pageNum < document.getNumberOfPages(); pageNum++) {
PDPage page = document.getPage(pageNum);
replaceImage(document, page);
}
document.save(NEW_PATH);
}
}
private void replaceImage(PDDocument document, PDPage page) throws IOException {
PDResources resources = page.getResources();
Iterable<COSName> xObjectNames = resources.getXObjectNames();
if (xObjectNames != null) {
for (COSName xObjectName : xObjectNames) {
PDXObject object = resources.getXObject(xObjectName);
if (object instanceof PDImageXObject) {
PDImageXObject img1 = (PDImageXObject) object;
BufferedImage bufferedImage1 = img1.getImage();
BufferedImage grayBufferedImage = convertBufferedImageToGray(bufferedImage1);
// PDImageXObject grayImage = JPEGFactory.createFromImage(document, grayBufferedImage);
PDImageXObject grayImage = LosslessFactory.createFromImage(document, grayBufferedImage);
resources.put(xObjectName, grayImage);
}
}
}
}
private static BufferedImage convertBufferedImageToGray(BufferedImage sourceImg) {
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorConvertOp op = new ColorConvertOp(sourceImg.getColorModel().getColorSpace(), cs, null);
op.filter(sourceImg, sourceImg);
return sourceImg;
}
但是仍然有些文件的大小增加了 3 倍(即使它们已经是灰度的;有趣的是 int 这种情况下JPEGFactory
会产生比 更大的文件LosslessFactory
)。灰度 PDF 中的所有图像都具有与原始图像相同的大小。我不明白为什么。
也许有更好的方法来制作具有可预测大小的灰度 PDF(ghostscript 除外)?
更新:我刚刚意识到问题在于从图像创建 PDF。它也不会压缩。
例如,我有一个小于 1 Mb 的虚拟 1 页扫描文件。但是,如果我从中获取图像(通过 Acrobat Reader 直接复制到 Paint,或通过上面的代码),它的大小约为 8-10 Mb,具体取决于方法。如果我从这个图像创建新的 PDF,它几乎不会被压缩。这是示例代码:
File pdfFile = new File(FULL_FILE);
try (PDDocument document = PDDocument.load(pdfFile)) {
PDPage page = new PDPage();
document.addPage(page);
PDImageXObject pdImage = PDImageXObject.createFromFile("example.png", document);
try (PDPageContentStream contents = new PDPageContentStream(document, page)) {
contents.drawImage(pdImage, 0F, 0F);
}
document.save(FULL_FILE_NEW);
}
解决方案
是的,与 JPEGFactory 相比,LosslessFactory 生成的文件更小
在下面的链接中,有不同的方法可以尝试实现相同的目标。总体而言,最佳质量的灰度图像是选项 6 中的图像,但这绝不是最快的(我自己使用了选项 4)。还提供比较供您选择
此链接包含将彩色图像转换为黑色的可能方法。这对我帮助很大。让我知道它是否对您有用,如果有帮助,请批准我的回答。
推荐阅读
- docker - Docker 中的 Go Lang 1.8 服务器未侦听给定端口
- java - 我的井字游戏的 CheckingWinner 方法
- css - 如何设置 v-slot:cell 模板的样式?
- api - 使用 newman 运行 API 时出现“无法读取 'RequestName' 内的测试脚本中未定义的属性 'access_token'”的错误
- javascript - 如何用Jquery修改html标签href链接?
- postman - 如何从 POSTMAN 中的请求正文中提取特定值并保存在变量中
- php - Laravel 7.x 自定义 500 未显示自定义错误视图
- html - Flutter Web iOS:上传后用iphone相机拍照的通用html拉伸
- reactjs - 如何防止应用程序不渲染两次?(反应)
- spring - 用户长时间不活动后如何使 JWT 令牌无效