You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

同代码在不同环境下Graphics2D生成不同尺寸图像的原因及解决

跨环境PDF转PNG的尺寸差异问题及解决方案

需求

将PDF页面转换为PNG图像,供遗留应用处理后发送至特定打印机。

实现代码

使用pdfbox与AWT编写的转换代码如下:

import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;


public class ImageConverter {

    public static void main(String[] args) throws IOException {     
        String pdfFilePath = "input_folder";
        String imageFilePath = "output_folder"; 
        int width = 1540;
        int height = 2200;
        float dpi = (float) 299.3;
        PDDocument document = PDDocument.load(new File(pdfFilePath));       
        convertPdftoPng(document, imageFilePath, width, height, dpi);   
    }

    private static void convertPdftoPng(PDDocument document, String imageFilePath, int width, int height, float dpi)
            throws IOException {        
        PDFRenderer pdfRenderer = new PDFRenderer(document);
        
        for (int page = 0; page < document.getNumberOfPages(); page++)
        { 
            BufferedImage outputImage = pdfRenderer.renderImageWithDPI(page, dpi, ImageType.RGB);
            ImageIO.write(outputImage, "png", new java.io.File(imageFilePath + "00" + (page + 1) + ".png"));
        }
        document.close();
    }
}

问题现象

指定目标分辨率1540×2200、DPI为299.3时:

  • Windows环境(Eclipse捆绑JDK19,项目配置Java11)下生成1540×2204的PNG;
  • 在KaaS的Kubernetes Pod(Tomcat+OpenJDK11)环境下,需将DPI设为186才能生成1537×2197的近似尺寸。

该代码为后端无交互逻辑,由文件监听触发SFTP上传后的PDF处理。

原因分析

  • AWT渲染依赖底层系统环境:Windows下的JDK(含捆绑的JDK19)与Linux容器内的OpenJDK11使用的AWT实现不同,前者依赖Windows GDI,后者多运行在Headless模式下,依赖libfreetype、libfontconfig等图形库,不同库的DPI计算、页面尺寸映射逻辑存在差异。
  • PDFBox的尺寸计算逻辑:renderImageWithDPI通过PDF页面原始点尺寸(1点=1/72英寸)乘以(DPI/72)得到像素数,但不同环境对页面边界、字体渲染的微小处理差异,会导致最终像素尺寸出现偏差。
  • 容器Headless模式限制:K8s Pod中的Tomcat通常运行在Headless AWT模式下,缺少完整桌面图形环境,渲染时的尺寸计算可能存在舍入或精度误差。

解决办法

1. 直接指定目标像素尺寸(推荐)

放弃通过DPI反推尺寸的方式,直接基于目标像素尺寸计算缩放比例,确保跨环境生成的PNG尺寸一致。修改转换方法如下:

private static void convertPdftoPng(PDDocument document, String imageFilePath, int targetWidth, int targetHeight)
        throws IOException {        
    PDFRenderer pdfRenderer = new PDFRenderer(document);
    
    for (int page = 0; page < document.getNumberOfPages(); page++) {
        PDPage pdPage = document.getPage(page);
        // 获取PDF页面的原始媒体框尺寸(单位:点)
        PDRectangle pageRect = pdPage.getMediaBox();
        float pageWidth = pageRect.getWidth();
        float pageHeight = pageRect.getHeight();
        
        // 计算缩放因子,强制适配目标尺寸
        float scaleX = targetWidth / pageWidth;
        float scaleY = targetHeight / pageHeight;
        
        // 按目标尺寸的缩放因子渲染图像
        BufferedImage outputImage = pdfRenderer.renderImage(page, scaleX, ImageType.RGB);
        
        // 若渲染后尺寸与目标有偏差,强制缩放至目标尺寸
        if (outputImage.getWidth() != targetWidth || outputImage.getHeight() != targetHeight) {
            BufferedImage scaledImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = scaledImage.createGraphics();
            g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2d.drawImage(outputImage, 0, 0, targetWidth, targetHeight, null);
            g2d.dispose();
            outputImage = scaledImage;
        }
        
        ImageIO.write(outputImage, "png", new File(imageFilePath + "00" + (page + 1) + ".png"));
    }
    document.close();
}

同时修改main方法,直接传入目标宽高即可,无需指定DPI。

2. 统一容器环境的图形依赖

在K8s Pod的镜像中安装完整的图形依赖库,比如libx11、libxext、libfontconfig、libfreetype6等,确保OpenJDK的AWT渲染环境与Windows下尽可能一致。

3. 固定PDF页面尺寸解析逻辑

在代码中明确指定使用PDF的媒体框(MediaBox)进行尺寸计算,避免不同环境对CropBox、TrimBox等其他页面框的解析差异,确保页面原始尺寸的获取逻辑统一。


内容的提问来源于stack exchange,提问作者Sergio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 09:10:27