使用html2canvas實現網頁截圖,並嵌入到PDF
使用html2canvas實現網頁截圖並嵌入到PDF
以前我們只能通過截圖工具進行擷取影象。這使得在業務生產中,變得越來越不方便。目前的瀏覽器功能越來越強大,H5也逐漸普及,瀏覽器也可以實現截圖了。這裡來聊下之前在工作中用到的html2canvas。這裡要感謝寫出html2canvas庫的小夥伴!
canvans的原理是將dom節點在Canvas裡畫出來,雖然特殊方便,但是仍有一些限制。如:
- 不支援iframe
- 不支援跨域圖片
- 部分瀏覽器上不支援SVG圖片
- 不支援Flash
不支援古代瀏覽器和IE9以下
function canvasImg(divName,formName,actionType){
html2canvas(divName, {
onrendered : function(canvas) {
var myImage = canvas.toDataURL(“image/jpeg”);
//動態生成input框
var input1 = document.createElement(‘input’);
input1.setAttribute(‘type’, ‘hidden’);
input1.setAttribute(‘name’, ‘canvasImg’);
input1.setAttribute(‘value’,myImage);
formName.appendChild(input1);
formName.actionType.value=actionType;
formName.submit();
}
});
}
在這裡我抽取成了公共JS,divName是$(‘#id’)取需要截圖的DOM物件,function(canvas)渲染完成後回撥的canvas物件formName是form表單的名字,actionType是action方法名。 我這裡是將得到的截圖,進行Base64編碼,再通過post請求,在後臺獲取。
public static String getImgPath(String data){ //圖片輸出路徑 String imgFilePath = null; try { Base64 base64 = new Base64(); //base64編碼解碼 byte[] k = base64.decode(data.substring("data:image/jpeg;base64," .length())); InputStream is = new ByteArrayInputStream(k); String fileName = UUID.randomUUID().toString(); String pdfFilePath = ShopApplicationResource.shopResource .getString("pdftempfiles.file.root"); imgFilePath = pdfFilePath + fileName + ".jpg"; double ratio = 1.0; BufferedImage image = ImageIO.read(is); //設定圖片是否縮放 int newWidth = (int) (image.getWidth() * ratio); int newHeight = (int) (image.getHeight() * ratio); Image newimage = image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH); BufferedImage tag = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB); Graphics g = tag.getGraphics(); g.drawImage(newimage, 0, 0, null); g.dispose(); //使用io將圖片寫入檔案中 ImageIO.write(tag, "jpg", new File(imgFilePath)); } catch (IOException e) { e.printStackTrace(); } return imgFilePath; }
data是經過base64編碼的圖片資料,將圖片通過寫入檔案流中。
public static void covertImgToPdf(String imgPath,HttpServletResponse response,HttpServletRequest request,String pdfFileName){ Document doc = new Document(PageSize.A4,10,10,10,10); FileOutputStream os = null; String pdfFilePath = ShopApplicationResource.shopResource .getString("pdftempfiles.file.root"); String pafFile = pdfFilePath + "tempPdf.pdf"; FileInputStream fis = null; ServletOutputStream out = null; try { os=new FileOutputStream(pafFile); PdfWriter pw= PdfWriter.getInstance(doc, os); pw.setStrictImageSequence(true); doc.open(); //設定logo //String logofile = request.getSession().getServletContext().getRealPath("/bg/images/ht_logo.png"); //com.itextpdf.text.Image logoImg = com.itextpdf.text.Image.getInstance(logofile); //doc.add(logoImg); //logoImg.setAlignment(com.itextpdf.text.Image.UNDERLYING); //logoImg.setAlignment(com.itextpdf.text.Image.LEFT); //logoImg.scaleToFit(10f,100f); com.itextpdf.text.Image img = com.itextpdf.text.Image.getInstance(imgPath); //居中 img.setAlignment(com.itextpdf.text.Image.MIDDLE); //自動縮放width*height img.scaleToFit(PageSize.A4.getWidth(), PageSize.A4.getHeight()); doc.add(img); doc.close(); os.flush(); os.close(); //經pdf檔案寫入輸出流,檔案在客戶端進行下載 response.setContentType("application/pdf"); String fileName = new String(("pdf"+pdfFileName+".pdf").getBytes("utf-8"), "iso-8859-1"); if (request.getHeader("User-Agent").indexOf("MSIE 5.5") != -1) { /** MS IE5.5 */ response.setHeader("Content-disposition", "filename=\"" + fileName + "\""); } else { /** 非MS IE5.5 */ response.setHeader("Content-disposition", "attachment;filename=\"" + fileName + "\""); } File pafFileTemp = new File(pafFile); fis = new FileInputStream(pafFileTemp); out = response.getOutputStream(); byte buffer[] = new byte[1024]; int bytestemp; while((bytestemp=fis.read(buffer)) != -1){ out.write(buffer, 0, bytestemp); } out.close(); //刪除圖片 File imgfile = new File(imgPath); if(imgfile.exists()){ imgfile.delete(); } //刪除臨時PDF if(pafFileTemp.exists()){ pafFileTemp.delete(); } } catch (Exception e) { e.printStackTrace(); response.setStatus(HttpServletResponse.SC_OK); }finally{ if(fis!=null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } }
這裡的PDF使用是IText生成。
如果圖片過大,則必須進行分頁。但是生成的是一張大圖,Itext無法進行自動分頁。這裡分頁有兩種思想。一是使用虛擬印表機,將圖片分頁,再進行匯出。二就是將大圖切成小圖,這種方式依賴於圖片切分的大小比列。這裡我只實現了第二種方式,所以只對第二種做一些講解。
BufferedImage bi = ImageIO.read(new File(imgPath));
int destWidth = (int) PageSize.A4.getWidth(); // 切片寬度
int destHeigth = (int) PageSize.A4.getHeight(); // 切片高度
int srcWidth = bi.getWidth();
int srcHeigth = bi.getHeight();
Image image = bi.getScaledInstance(srcWidth, srcHeigth, Image.SCALE_DEFAULT);
int cols = 0;//切片橫向向數量
if (srcHeigth % destHeigth == 0) {
cols = (int) (srcHeigth / destHeigth);
} else {
cols = (int) Math.floor(srcHeigth / destHeigth) + 1;
}
//橫向迴圈切圖片
for (int i = 0; i < cols ; i++) {
ImageFilter cropFilter;
Image ig;
cropFilter = new CropImageFilter(0, i * destHeigth,srcWidth, destHeigth);
ig = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(),cropFilter));
BufferedImage tag = new BufferedImage(srcWidth,destHeigth, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(ig, 0, 0, null); // 繪製縮小後的圖
g.dispose();
String pdfFilePath = ShopApplicationResource.shopResource.getString("pdftempfiles.file.root");
// 輸出為檔案
String imgFilePath = pdfFilePath+"temp"+i+".jpg";
ImageIO.write(tag, "JPEG", new File(imgFilePath));
com.itextpdf.text.Image img1 = com.itextpdf.text.Image.getInstance(imgFilePath);
// int percent1 = getPercent2(img1.getHeight(), PageSize.A4.getWidth());
//img1.scalePercent(percent1);
//設定圖片寬高
img1.scaleAbsolute(PageSize.A4.getWidth(), img1.getHeight());
//居中
img1.setAlignment(com.itextpdf.text.Image.MIDDLE);
//另起一頁
doc.newPage();
doc.add(img1);
File file = new File(imgFilePath);
if(file.exists()){
file.delete();
}
}
這裡的思路是:我這裡只是將圖片適應A4紙,所以只進行橫向切片,每次按比例切出一張圖片,就放入一張PDF頁中,直到切完所有。這種方式肯定不是很好的,只是適用了業務的需要,就沒有進行深入研究學習。如果大家有更好的方法,請一定要記得告訴我!
注:本人很少寫部落格,語言表述方面可能欠佳,請大家海涵!