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

基于Apache PDFBox的PDF页面缩放问题:页脚空间适配异常

问题描述

我正在尝试调整PDF页面大小,目的是在页面底部创建一块空白区域用于添加页脚水印,并且这个空白区域需要适配任意页面方向(纵向/横向)及尺寸。我已经实现了一段页面缩放代码,在大多数场景下都能正常生成所需的页脚空间,但在部分样本PDF文件上运行失效。

以下是我的实现代码:

public static void main(String[] args) { 
    // TODO Auto-generated method stub 
    try { 
        //String pdfFilename = "/MuhimbiPOC/Templates/Source_doc_withheaderfooter.pdf"; 
        //String pdfFilename = "/MuhimbiPOC/Templates/3.pdf"; 
        //String pdfFilename = "C:\\Users\\H176298\\Documents\\Documents\\ERS\\ERS-Build_20.0_Muhimbi\\Sample Files\\Pages_from_annual_report_2009.pdf"; 
        String pdfFilename = "C:\\Users\\H176298\\Documents\\Documents\\ERS\\ERS-Build_20.0_Muhimbi\\Sample Files\\extract_A17_FlightPlan.pdf"; 
        PDDocument document = PDDocument.load(new File(pdfFilename)); 
        PDDocument documentPDF = new PDDocument(); 
        PDFRenderer pdfRenderer = new PDFRenderer(document); 
        PDPage pge = new PDPage(); 
        int pageCounter = 0; 
        for (PDPage page : document.getPages()) { 
            int rotation = page.getRotation(); 
            final PDRectangle mediaBox = page.getMediaBox(); 
            final PDRectangle cropBox = page.getCropBox(); 
            boolean isLandscape = mediaBox.getWidth() > mediaBox.getHeight(); 
            /** 
             * Code to Detect the Portrait Mode of Page 
             */ 
            boolean rotate = rotation == 90 || rotation == 270; 
            System.out.println("Landscape " + isLandscape); 
            System.out.println("pageSize.getHeight() :: " + cropBox.getHeight()); 
            System.out.println("pageSize.getWidth() :: " + cropBox.getWidth()); 
            System.out.println("pageSize.getUpperRightX() :: " + cropBox.getUpperRightX()); 
            System.out.println("pageSize.getUpperRightY() :: " + cropBox.getUpperRightY()); 
            System.out.println("pageSize.getLowerLeftX() :: " + cropBox.getLowerLeftX()); 
            System.out.println("pageSize.getLowerLeftY() :: " + cropBox.getLowerLeftY()); 

            if(!rotate && !isLandscape) { 
                System.out.println("Not Rotation ............................... " + rotate); 
                mediaBox.setUpperRightX((float) (page.getMediaBox().getUpperRightX())); 
                mediaBox.setUpperRightY(page.getMediaBox().getUpperRightY() + page.getMediaBox().getHeight() * 0.5f); 
                mediaBox.setLowerLeftY(page.getMediaBox().getLowerLeftY() - page.getMediaBox().getHeight() * 0.5f); 

                cropBox.setUpperRightX((float) (page. getCropBox().getUpperRightX()) + (float) (page. getCropBox().getUpperRightX())*0.5f); 
                cropBox.setUpperRightY(page.getCropBox().getUpperRightY()); 
                cropBox.setLowerLeftX(page.getCropBox().getLowerLeftX() - page.getCropBox().getWidth() * 0.5f); 
            }else { 
                /***************************************************************************************/ 
                mediaBox.setUpperRightX((float) (Math.abs(page.getMediaBox().getUpperRightX()) + Math.abs(page.getMediaBox().getWidth()) * 0.1f)); 
                mediaBox.setUpperRightY(Math.abs(page.getMediaBox().getUpperRightY())); 
                mediaBox.setLowerLeftY(page.getMediaBox().getLowerLeftY()); 
                mediaBox.setUpperRightY(Math.abs(page.getMediaBox().getUpperRightY()) + Math.abs(page.getMediaBox().getHeight()) * 0.1f); 
                mediaBox.setLowerLeftY(page.getMediaBox().getLowerLeftY() - Math.abs(page.getMediaBox().getHeight()) * 0.1f); 

                cropBox.setUpperRightX((float) (Math.abs(page. getCropBox().getUpperRightX())) + (float) (Math.abs(page. getCropBox().getUpperRightX()))*0.1f); 
                cropBox.setUpperRightY(Math.abs(page.getMediaBox().getUpperRightY())); 
                cropBox.setLowerLeftY(page.getCropBox().getLowerLeftY()); 
                cropBox.setLowerLeftY(page.getCropBox().getLowerLeftY() - Math.abs(page.getCropBox().getHeight() * 0.1f)); 
                cropBox.setLowerLeftX(page.getCropBox().getLowerLeftX() - (float) (page. getCropBox().getLowerLeftX())*0.1f); 
                System.out.println("Rotation ..............................."); 
                // note that the page number parameter is zero based 
            } 
            /****************************************************************************/ 
            System.out.println("==================================== " + rotate); 
            System.out.println((float) (page.getMediaBox().getWidth()) + " :: " + page.getMediaBox().getHeight()); 
            System.out.println(mediaBox.getWidth() + " :: " + mediaBox.getHeight()); 
            System.out.println("pageSize.getHeight() :: " + cropBox.getHeight()); 
            System.out.println("pageSize.getWidth() :: " + cropBox.getWidth()); 
            System.out.println("pageSize.getUpperRightX() :: " + cropBox.getUpperRightX()); 
            System.out.println("pageSize.getUpperRightY() :: " + cropBox.getUpperRightY()); 
            System.out.println("pageSize.getLowerLeftX() :: " + cropBox.getLowerLeftX()); 
            System.out.println("pageSize.getLowerLeftY() :: " + cropBox.getLowerLeftY()); 
            System.out.println("===================================="); 
            page.setMediaBox(mediaBox); 
            page.setCropBox(cropBox); 
        } 
        System.out.println("No. of Pages :: " + document.getNumberOfPages()); 
        document.save(pdfFilename + "_test.pdf"); 
        System.out.println("Task Completed ... @ " + new Date()); 
        document.close(); 
    } catch (IOException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
    } 
} 
问题分析与优化建议

从代码来看,当前的实现存在几个可能导致部分PDF失效的问题:

1. 页面方向判断逻辑不严谨

当前通过rotation == 90 || rotation == 270判断页面是否旋转,但有些PDF可能存在其他旋转值(比如180度),或者旋转后的页面宽高判断与实际显示方向不符。建议先计算实际显示的页面宽高(考虑旋转):

int rotation = page.getRotation();
PDRectangle mediaBox = page.getMediaBox();
float actualWidth = mediaBox.getWidth();
float actualHeight = mediaBox.getHeight();

// 旋转90/270度时,宽高互换
if (rotation == 90 || rotation == 270) {
    float temp = actualWidth;
    actualWidth = actualHeight;
    actualHeight = temp;
}
boolean isLandscape = actualWidth > actualHeight;

2. MediaBox与CropBox修改逻辑混乱

在else分支中,重复对mediaBox.setUpperRightY()和mediaBox.setLowerLeftY()赋值,这会导致之前的设置被覆盖,逻辑冲突。另外,当前代码在纵向页面中修改了CropBox的左右X值(添加左右空白),但你的需求是底部空白,这明显偏离了目标。

3. 空白区域计算逻辑错误

正确的底部空白实现应该是:

  • 保持页面内容区域(CropBox)的宽度不变,将内容向上偏移,留出底部空白;
  • 同时扩大MediaBox的高度,以容纳新增的空白区域(如果需要保留原页面内容的显示范围)。

优化后的代码示例

以下是调整后的代码,专注于在页面底部创建固定比例的空白区域,适配所有页面方向:

public static void main(String[] args) {
    try {
        String pdfFilename = "C:\\Users\\H176298\\Documents\\Documents\\ERS\\ERS-Build_20.0_Muhimbi\\Sample Files\\extract_A17_FlightPlan.pdf";
        PDDocument document = PDDocument.load(new File(pdfFilename));
        float footerRatio = 0.1f; // 预留10%的页面高度作为页脚空白

        for (PDPage page : document.getPages()) {
            int rotation = page.getRotation();
            PDRectangle mediaBox = new PDRectangle(page.getMediaBox()); // 复制原MediaBox
            PDRectangle cropBox = new PDRectangle(page.getCropBox()); // 复制原CropBox

            // 计算实际显示的宽高(考虑旋转)
            float actualWidth = mediaBox.getWidth();
            float actualHeight = mediaBox.getHeight();
            boolean isRotated = rotation == 90 || rotation == 270;
            if (isRotated) {
                float temp = actualWidth;
                actualWidth = actualHeight;
                actualHeight = temp;
            }
            float footerHeight = actualHeight * footerRatio;

            // 根据页面方向调整MediaBox和CropBox
            if (isRotated) {
                // 旋转页面:底部空白对应横向的视觉底部,统一扩大MediaBox宽度并偏移内容
                mediaBox.setUpperRightX(mediaBox.getUpperRightX() + footerHeight);
                cropBox.setLowerLeftX(cropBox.getLowerLeftX() + footerHeight); // 内容右移,留出视觉底部空白
            } else {
                // 非旋转页面:直接在底部添加空白
                mediaBox.setLowerLeftY(mediaBox.getLowerLeftY() - footerHeight); // 扩大MediaBox高度
                cropBox.setLowerLeftY(cropBox.getLowerLeftY() + footerHeight); // 内容上移,留出底部空白
            }

            // 应用修改
            page.setMediaBox(mediaBox);
            page.setCropBox(cropBox);
        }

        document.save(pdfFilename + "_test.pdf");
        document.close();
        System.out.println("Task Completed ... @ " + new Date());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

关键改进点

  • 统一处理旋转页面的方向判断,确保空白区域始终在视觉底部;
  • 分离MediaBox和CropBox的修改逻辑,MediaBox负责扩大页面尺寸,CropBox负责调整内容显示区域;
  • 使用固定比例计算空白高度,适配任意页面尺寸;
  • 避免重复赋值和逻辑冲突,代码更简洁易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:33:30