Java使用PDFBox實現(xiàn)操作PDF文檔
PDFBox操作圖片
PDFBox可以向PDF文檔中添加圖片對象,使用PDImageXObject表示一個圖片對象,對PDF文檔的內(nèi)容進(jìn)行操作,都需要借助于PDPageContentStream頁面內(nèi)容流對象來完成,PDFBox將每一個PDF頁面中的所有文本、圖片、表單等內(nèi)容看作一個流,通過流的方式來完成內(nèi)容的添加、刪除、修改等操作。這里首先介紹如何使用PDFBox添加圖片對象到PDF文檔里面。
添加本地圖片
(1)案例代碼
添加本地圖片,也就是讀取當(dāng)前磁盤中的圖片,然后將這個圖片寫入到PDPageContentStream頁面內(nèi)容流里面,案例代碼如下:
package pdfbox.demo.image; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; /** * @version 1.0.0 * @Date: 2023/7/15 14:51 * @Author ZhuYouBin * @Description: PDFBox操作圖片 */ public class PDFBoxImageUtil { /** * 將給定路徑的圖片,保存到pdf文件里面 * @param imgPath 圖片路徑 * @param destPdf 生成的pdf文件路徑 * @return 返回生成的pdf文件路徑 */ public static String generateImageToPdf(String imgPath, String destPdf) { try { // 1、創(chuàng)建PDF文檔對象 PDDocument doc = new PDDocument(); // 2、創(chuàng)建Page頁面對象 PDPage page = new PDPage(PDRectangle.A4); // 3、創(chuàng)建圖片對象 PDImageXObject image = PDImageXObject.createFromFile(imgPath, doc); // 4、創(chuàng)建頁面內(nèi)容流,指定操作哪個文檔中的哪個頁面 PDPageContentStream stream = new PDPageContentStream(doc, page); stream.drawImage(image, 10, 10); // 繪制圖片到PDF頁面里面 stream.close(); // 關(guān)閉頁面內(nèi)容流 doc.addPage(page); // 添加頁面到PDF文檔 doc.save(destPdf); // 保存PDF文檔 doc.close(); // 關(guān)閉PDF文檔 } catch (Exception e) { e.printStackTrace(); } return destPdf; } public static void main(String[] args) { String imgPath = "E:\\demo\\001.jpg"; String destPdf = "E:\\demo\\img.pdf"; generateImageToPdf(imgPath, destPdf); } }
(2)運(yùn)行效果
(3)方法介紹
PDImageXObject類中提個了一些靜態(tài)方法,常見的有下面這些:
createFromFile(imagePath,doc)方法:采用File文件的方式讀取本地磁盤中的圖片。
- imagePath參數(shù):圖片的路徑。
- doc參數(shù):PDF文檔對象。
getImage()方法:返回BufferedImage圖片對象。
getSuffix()方法:返回圖片的后綴類型,例如:jpg、png等。
添加網(wǎng)絡(luò)圖片
PDFBox中是沒有提供讀取網(wǎng)絡(luò)圖片的方法,但是可以采用下面這種方式實現(xiàn)讀取網(wǎng)絡(luò)圖片的功能,思路如下:
- 第一步:使用URL對象將網(wǎng)絡(luò)圖片下載到本地磁盤上。
- 第二步:使用createFromFile()方法從本地磁盤讀取剛剛下載的網(wǎng)絡(luò)圖片。
(1)案例代碼
package pdfbox.demo.image; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.UUID; /** * @version 1.0.0 * @Date: 2023/7/15 15:01 * @Author ZhuYouBin * @Description: PDFBox操作圖片,添加網(wǎng)絡(luò)圖片到PDF文檔 */ public class PDFBoxImageUtil { /** * 將給定路徑的圖片,保存到pdf文件里面 * @param imgPath 圖片路徑 * @param destPdf 生成的pdf文件路徑 * @return 是否生成成功 */ public static String generateImageToPdf(String imgPath, String destPdf) { try { // 1、創(chuàng)建PDF文檔對象 PDDocument doc = new PDDocument(); // 2、創(chuàng)建Page頁面對象 PDPage page = new PDPage(PDRectangle.A4); // 3、創(chuàng)建圖片對象 PDImageXObject image; boolean isTemp = false; String tempPath = null; if (imgPath.startsWith("http://") || imgPath.startsWith("https://")) { isTemp = true; tempPath = downloadImage(imgPath, null); image = PDImageXObject.createFromFile(tempPath, doc); } else { image = PDImageXObject.createFromFile(imgPath, doc); } // 4、創(chuàng)建頁面內(nèi)容流,指定操作哪個文檔中的哪個頁面 PDPageContentStream stream = new PDPageContentStream(doc, page); stream.drawImage(image, 10, 10); // 繪制圖片到PDF頁面里面 stream.close(); // 關(guān)閉頁面內(nèi)容流 doc.addPage(page); // 添加頁面到PDF文檔 doc.save(destPdf); // 保存PDF文檔 doc.close(); // 關(guān)閉PDF文檔 // 圖片添加成功之后需要刪除本地臨時文件 if (isTemp) { new File(tempPath).delete(); } } catch (Exception e) { e.printStackTrace(); } return destPdf; } /** * 下載網(wǎng)絡(luò)圖片到本地 * @param imgPath 網(wǎng)絡(luò)圖片地址 * @param fileName 文件名稱 * @return 返回本地圖片的臨時路徑 */ public static String downloadImage(String imgPath, String fileName) { try { URLConnection conn = new URL(imgPath).openConnection(); String contentType = conn.getContentType(); System.out.println(contentType); // 創(chuàng)建臨時文件目錄保存圖片 File file = new File("temp"); if (!file.exists() && !file.mkdirs()) { throw new RuntimeException("臨時目錄創(chuàng)建失敗"); } if (fileName == null || fileName.trim().equals("")) { fileName = UUID.randomUUID().toString(); } InputStream is = conn.getInputStream(); byte[] data = new byte[1024]; int len; // 下載文件到本地臨時目錄 switch (contentType) { case "image/jpeg":fileName += ".jpeg"; break; case "image/gif": fileName += ".gif"; break; case "image/webp": case "image/png": fileName += ".png"; break; } fileName = file.getAbsolutePath() + File.separator + fileName; FileOutputStream fos = new FileOutputStream(fileName); while ((len = is.read(data)) != -1) { fos.write(data, 0, len); } fos.close(); is.close(); } catch (Exception e) { e.printStackTrace(); } return fileName; } public static void main(String[] args) { String imgPath = "https://www.toopic.cn/public/uploads/small/1658043938262165804393852.jpg"; String destPdf = "E:\\demo\\img.pdf"; generateImageToPdf(imgPath, destPdf); } }
(2)運(yùn)行效果
圖片寬高自適應(yīng)(圖片縮放)
前面已經(jīng)能夠?qū)D片添加到PDF文檔中了,但是可以發(fā)現(xiàn),我們添加的圖片尺寸太大的時候,超過PDF文檔部分就會被遮擋,如何解決這個問題呢???對于這個問題,可以采用縮放圖片的方式來解決,思路如下所示:
- 第一步:獲取圖片的實際寬度、高度(JDK中獲取到的圖片寬高單位是【px】,需要將【px】轉(zhuǎn)換成【pt】單位,轉(zhuǎn)換規(guī)則:1pt = 3/4 px)。
- 第二步:獲取到PDF文檔的寬度、高度(PDFBox中獲取到的寬度、高度是采用【pt】作為單位的)。
- 第三步:圖片的實際寬高和PDF文檔的寬高進(jìn)行比較,計算縮放比例。
圖片縮放代碼
package pdfbox.demo.image; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.UUID; /** * @version 1.0.0 * @Date: 2023/7/15 15:11 * @Author ZhuYouBin * @Description: PDFBox操作圖片,圖片寬高自動縮放 */ public class PDFBoxImageUtil { /** * 將給定路徑的圖片,保存到pdf文件里面 * * @param imgPath 圖片路徑 * @param destPdf 生成的pdf文件路徑 * @return 返回生成的pdf文件路徑 */ public static boolean generateImageToPdf(String imgPath, String destPdf) { try { // 1、創(chuàng)建PDF文檔對象 PDDocument doc = new PDDocument(); // 2、創(chuàng)建Page頁面對象 PDPage page = new PDPage(PDRectangle.A4); // 3、創(chuàng)建圖片對象 PDImageXObject image; if (imgPath.startsWith("http://") || imgPath.startsWith("https://")) { String tempPath = downloadImage(imgPath, null); image = PDImageXObject.createFromFile(tempPath, doc); imgPath = tempPath; } else { image = PDImageXObject.createFromFile(imgPath, doc); } // 4、創(chuàng)建頁面內(nèi)容流,指定操作哪個文檔中的哪個頁面 PDPageContentStream stream = new PDPageContentStream(doc, page); // 獲取圖片的寬高 float[] imageWH = getImageWH(imgPath, page.getMediaBox()); stream.drawImage(image, imageWH[0], imageWH[1], imageWH[2], imageWH[3]); // 繪制圖片到PDF頁面里面 stream.close(); // 關(guān)閉頁面內(nèi)容流 doc.addPage(page); // 添加頁面到PDF文檔 doc.save(destPdf); // 保存PDF文檔 doc.close(); // 關(guān)閉PDF文檔 return true; } catch (Exception e) { e.printStackTrace(); } return false; } /** * 獲取圖片的寬度、高度,單位是【pt】 * * @param imgPath 圖片路徑 * @param box PDF文檔頁面矩形區(qū)域?qū)ο?,可以獲取到矩形區(qū)域的寬高 * @return 返回縮放之后的圖片寬高 */ public static float[] getImageWH(String imgPath, PDRectangle box) { try { File file = new File(imgPath); InputStream is = new FileInputStream(file); // 判斷是不是網(wǎng)絡(luò)上的圖片 if (imgPath.startsWith("http://") || imgPath.startsWith("https://")) { is = new URL(imgPath).openStream(); } BufferedImage bi = ImageIO.read(is); // px 轉(zhuǎn)換成 pt 單位 float xAxis; float yAxis; int w = bi.getWidth(); int h = bi.getHeight(); float width = (float) (w * 3.0 / 4); // 這里是因為 1pt = 3/4 px,pt和px單位轉(zhuǎn)換 float height = (float) (h * 3.0 / 4); float pw = box.getWidth() - 60; // 這里減不減60沒啥關(guān)系,只是設(shè)置一下空白間距 float ph = box.getHeight() - 60; // 這里減不減60沒啥關(guān)系,只是設(shè)置一下空白間距 if (width > pw) { float scale = pw / width; // 縮放比列 width = pw; // 寬度等于頁面寬度 height = height * scale; // 高度自動縮放 } else { float scale = ph / height; // 縮放比列 height = ph; // 高度等于頁面高度 width = width * scale; // 寬度自動縮放 } // 計算圖片在X、Y軸上的顯示位置 xAxis = (box.getWidth() - width) / 2; // X軸居中對齊 // yAxis = box.getHeight() - height - 10; // 距離頁面頂部10個pt yAxis = (box.getHeight() - height) / 2; // Y軸垂直居中對齊 return new float[]{xAxis, yAxis, width, height}; } catch (Exception e) { e.printStackTrace(); } return new float[]{0, 0, 0, 0}; } /** * 下載網(wǎng)絡(luò)圖片到本地 * @param imgPath 網(wǎng)絡(luò)圖片地址 * @param fileName 文件名稱 * @return 返回本地圖片的臨時路徑 */ public static String downloadImage(String imgPath, String fileName) { try { URLConnection conn = new URL(imgPath).openConnection(); String contentType = conn.getContentType(); // 創(chuàng)建臨時文件目錄保存圖片 File file = new File("temp"); if (!file.exists() && !file.mkdirs()) { throw new RuntimeException("臨時目錄創(chuàng)建失敗"); } if (fileName == null || fileName.trim().equals("")) { fileName = UUID.randomUUID().toString().replaceAll("-", ""); } InputStream is = conn.getInputStream(); byte[] data = new byte[1024]; int len; // 下載文件到本地臨時目錄 switch (contentType) { case "image/jpeg":fileName += ".jpeg"; break; case "image/gif": fileName += ".gif"; break; case "image/webp": case "image/png": fileName += ".png"; break; } fileName = file.getAbsolutePath() + File.separator + fileName; FileOutputStream fos = new FileOutputStream(fileName); while ((len = is.read(data)) != -1) { fos.write(data, 0, len); } fos.close(); is.close(); } catch (Exception e) { e.printStackTrace(); } return fileName; } public static void main(String[] args) { String imgPath = "https://www.toopic.cn/public/uploads/small/1658043938262165804393852.jpg"; String destPdf = "E:\\demo\\img.pdf"; generateImageToPdf(imgPath, destPdf); } }
讀取圖片
PDFBox也可以從PDF文檔中讀取圖片,然后將其保存到本地磁盤中,保存圖片可以使用JDK中提供的ImageIO類,這個類中提供了一個write()方法,可以將圖片對象寫入到File文件里面。
(1)案例代碼
package pdfbox.demo.image; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.graphics.PDXObject; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; /** * @version 1.0.0 * @Date: 2023/7/15 15:11 * @Author ZhuYouBin * @Description: PDFBox操作圖片,從PDF文檔中【讀取圖片】,并且保存到本地 */ public class PDFBoxImageUtil { /** * 從給定的pdf文檔里面,獲取指定頁面中的所有圖片,并且保存到本地目錄下 * <p> * pdf文檔中的圖片都是BASE64編碼,我們能夠獲取到的也就只能是圖片對應(yīng)的BASE64字符串。 * 所以,還需要將圖片的BASE64字符串編碼轉(zhuǎn)換成對應(yīng)的圖片文件 * </p> * @param pdfPath PDF文檔路徑 * @param imagePath 生成的圖片路徑以及名稱 * @param pageNum 獲取第幾頁的圖片 * @return 返回提取的圖片本地路徑 */ public static String readerImageFromPdf(String pdfPath, String imagePath, int pageNum) { try { // 1、加載PDF文檔 PDDocument doc = PDDocument.load(new File(pdfPath)); // 2、遍歷所有Page頁面,找到指定的page頁面獲取圖片 int pages = doc.getNumberOfPages(); for (int i = 0; i < pages; i++) { if (i != pageNum) { continue; } // 獲取當(dāng)前Page頁面 PDPage page = doc.getPage(i); // 獲取對應(yīng)頁面的資源對象 PDResources resources = page.getResources(); // 遍歷當(dāng)前頁面所有內(nèi)容,找出圖片對象 for (COSName cosName : resources.getXObjectNames()) { PDXObject pdxObject = resources.getXObject(cosName); // 判斷是不是圖片對象 if (pdxObject instanceof PDImageXObject) { // 獲取圖片對象 BufferedImage image = ((PDImageXObject) pdxObject).getImage(); // 保存到本地磁盤里面 ImageIO.write(image, "JPEG", new File(imagePath)); } } } doc.close(); // 關(guān)閉PDF文檔 } catch (Exception e) { e.printStackTrace(); } return imagePath; } public static void main(String[] args) { String imgPath = "E:\\img\\002.jpg"; String destPdf = "E:\\demo\\img.pdf"; readerImageFromPdf(destPdf, imgPath, 0); } }
(2)運(yùn)行效果
到此,PDFBox操作圖片就介紹完啦。
以上就是Java使用PDFBox實現(xiàn)操作PDF文檔的詳細(xì)內(nèi)容,更多關(guān)于Java PDFBox操作PDF的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
深入淺析Java中Static Class及靜態(tài)內(nèi)部類和非靜態(tài)內(nèi)部類的不同
上次有朋友問我,java中的類可以是static嗎?我給他肯定的回答是可以的,在java中我們可以有靜態(tài)實例變量、靜態(tài)方法、靜態(tài)塊。當(dāng)然類也可以是靜態(tài)的,下面小編整理了些關(guān)于java中的static class相關(guān)資料分享在腳本之家平臺供大家參考2015-11-11詳解Java如何在CompletableFuture中實現(xiàn)日志記錄
這篇文章主要為大家詳細(xì)介紹了一種slf4j自帶的MDC類,來記錄完整的請求日志,和在CompletableFuture異步線程中如何保留鏈路id,需要的可以參考一下2023-04-04解決CentOS7中運(yùn)行jar包報錯:xxx(Permission?denied)
在實際工作我們經(jīng)常會在linux上運(yùn)行Spring boot編寫的微服務(wù)程序,下面這篇文章主要給大家介紹了關(guān)于如何解決CentOS7中運(yùn)行jar包報錯:xxx(Permission?denied)的相關(guān)資料,需要的朋友可以參考下2024-02-02Java concurrency線程池之線程池原理(一)_動力節(jié)點Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了Java concurrency線程池之線程池原理,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06Java的動態(tài)代理和靜態(tài)代理及反射常用API詳解
這篇文章主要介紹了Java的動態(tài)代理和靜態(tài)代理及反射常用API詳解,動態(tài)代理是一種在運(yùn)行時動態(tài)生成代理對象的技術(shù),它是一種設(shè)計模式,用于在不修改原始對象的情況下,通過代理對象來間接訪問原始對象,并在訪問前后執(zhí)行額外的操作,需要的朋友可以參考下2024-01-01Java中過濾器、監(jiān)聽器和攔截器的區(qū)別詳解
這篇文章主要介紹了Java中過濾器、監(jiān)聽器和攔截器的區(qū)別詳解,有些朋友可能不了解過濾器、監(jiān)聽器和攔截器的區(qū)別,本文就來詳細(xì)講一下,相信看完你會有所收獲,需要的朋友可以參考下2024-01-01