基于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
相关产品推荐
相关产品推荐

