使用html2canvas实现网页截图并嵌入到PDF
以前我们只能通过截图工具进行截取图像。这使得在业务生产中,变得越来越不方便。目前的浏览器功能越来越强大,H5也逐渐普及,浏览器也可以实现截图了。这里来聊下之前在工作中用到的html2canvas。这里要感谢写出html2canvas库的小伙伴!
canvans的原理是将dom节点在Canvas里画出来,虽然特殊方便,但是仍有一些限制。如:
- 不支持iframe
- 不支持跨域图片
- 部分浏览器上不支持SVG图片
- 不支持Flash
- 不支持古代浏览器和IE9以下
1 function canvasImg(divName,formName,actionType){ 2 html2canvas(divName, { 3 4 onrendered : function(canvas) { 5 var myImage = canvas.toDataURL(“image/jpeg”); 6 //动态生成input框 7 var input1 = document.createElement(‘input’); 8 input1.setAttribute(‘type’, ‘hidden’); 9 input1.setAttribute(‘name’, ‘canvasImg’); 10 input1.setAttribute(‘value’,myImage); 11 formName.appendChild(input1); 12 formName.actionType.value=actionType; 13 formName.submit(); 14 } 15 }); 16 }
在这里我抽取成了公共JS,divName是$(‘#id’)取需要截图的DOM对象,function(canvas)渲染完成后回调的canvas对象formName是form表单的名字,actionType是action方法名。 我这里是将得到的截图,进行Base64编码,再通过post请求,在后台获取。
1 public static String getImgPath(String data){ 2 //图片输出路径 3 String imgFilePath = null; 4 try { 5 Base64 base64 = new Base64(); 6 //base64编码解码 7 byte[] k = base64.decode(data.substring("data:image/jpeg;base64,".length())); 8 InputStream is = new ByteArrayInputStream(k); 9 String fileName = UUID.randomUUID().toString(); 10 String pdfFilePath = ShopApplicationResource.shopResource.getString("pdftempfiles.file.root"); 11 imgFilePath = pdfFilePath + fileName + ".jpg"; 12 double ratio = 1.0; 13 BufferedImage image = ImageIO.read(is); 14 //设置图片是否缩放 15 int newWidth = (int) (image.getWidth() * ratio); 16 int newHeight = (int) (image.getHeight() * ratio); 17 Image newimage = image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH); 18 BufferedImage tag = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB); 19 Graphics g = tag.getGraphics(); 20 g.drawImage(newimage, 0, 0, null); 21 g.dispose(); 22 //使用io将图片写入文件中 23 ImageIO.write(tag, "jpg", new File(imgFilePath)); 24 } catch (IOException e) { 25 e.printStackTrace(); 26 } 27 return imgFilePath; 28 }
data是经过base64编码的图片数据,将图片通过写入文件流中。
1 public static void covertImgToPdf(String imgPath,HttpServletResponse response,HttpServletRequest request,String pdfFileName){ 2 3 Document doc = new Document(PageSize.A4,10,10,10,10); 4 FileOutputStream os = null; 5 String pdfFilePath = ShopApplicationResource.shopResource 6 .getString("pdftempfiles.file.root"); 7 String pafFile = pdfFilePath + "tempPdf.pdf"; 8 FileInputStream fis = null; 9 ServletOutputStream out = null; 10 try { 11 os=new FileOutputStream(pafFile); 12 PdfWriter pw= PdfWriter.getInstance(doc, os); 13 pw.setStrictImageSequence(true); 14 15 doc.open(); 16 //设置logo 17 //String logofile = request.getSession().getServletContext().getRealPath("/bg/images/ht_logo.png"); 18 //com.itextpdf.text.Image logoImg = com.itextpdf.text.Image.getInstance(logofile); 19 //doc.add(logoImg); 20 //logoImg.setAlignment(com.itextpdf.text.Image.UNDERLYING); 21 //logoImg.setAlignment(com.itextpdf.text.Image.LEFT); 22 //logoImg.scaleToFit(10f,100f); 23 24 com.itextpdf.text.Image img = com.itextpdf.text.Image.getInstance(imgPath); 25 //居中 26 img.setAlignment(com.itextpdf.text.Image.MIDDLE); 27 //自动缩放width*height 28 img.scaleToFit(PageSize.A4.getWidth(), PageSize.A4.getHeight()); 29 doc.add(img); 30 31 doc.close(); 32 os.flush(); 33 os.close(); 34 35 //经pdf文件写入输出流,文件在客户端进行下载 36 response.setContentType("application/pdf"); 37 String fileName = new String(("pdf"+pdfFileName+".pdf").getBytes("utf-8"), "iso-8859-1"); 38 if (request.getHeader("User-Agent").indexOf("MSIE 5.5") != -1) { 39 /** MS IE5.5 */ 40 response.setHeader("Content-disposition", "filename=\"" + fileName + "\""); 41 } else { 42 43 /** 非MS IE5.5 */ 44 response.setHeader("Content-disposition", "attachment;filename=\"" + fileName + "\""); 45 } 46 File pafFileTemp = new File(pafFile); 47 fis = new FileInputStream(pafFileTemp); 48 out = response.getOutputStream(); 49 byte buffer[] = new byte[1024]; 50 int bytestemp; 51 while((bytestemp=fis.read(buffer)) != -1){ 52 out.write(buffer, 0, bytestemp); 53 } 54 out.close(); 55 56 //删除图片 57 File imgfile = new File(imgPath); 58 if(imgfile.exists()){ 59 imgfile.delete(); 60 } 61 62 //删除临时PDF 63 if(pafFileTemp.exists()){ 64 pafFileTemp.delete(); 65 } 66 67 68 } catch (Exception e) { 69 e.printStackTrace(); 70 response.setStatus(HttpServletResponse.SC_OK); 71 }finally{ 72 if(fis!=null) { 73 try { 74 fis.close(); 75 } catch (IOException e) { 76 e.printStackTrace(); 77 } 78 } 79 } 80 }
这里的PDF使用是IText生成。
如果图片过大,则必须进行分页。但是生成的是一张大图,Itext无法进行自动分页。这里分页有两种思想。一是使用虚拟打印机,将图片分页,再进行导出。二就是将大图切成小图,这种方式依赖于图片切分的大小比列。这里我只实现了第二种方式,所以只对第二种做一些讲解。
1 BufferedImage bi = ImageIO.read(new File(imgPath)); 2 int destWidth = (int) PageSize.A4.getWidth(); // 切片宽度 3 int destHeigth = (int) PageSize.A4.getHeight(); // 切片高度 4 int srcWidth = bi.getWidth(); 5 int srcHeigth = bi.getHeight(); 6 Image image = bi.getScaledInstance(srcWidth, srcHeigth, Image.SCALE_DEFAULT); 7 int cols = 0;//切片横向向数量 8 if (srcHeigth % destHeigth == 0) { 9 cols = (int) (srcHeigth / destHeigth); 10 } else { 11 cols = (int) Math.floor(srcHeigth / destHeigth) + 1; 12 } 13 //横向循环切图片 14 for (int i = 0; i < cols ; i++) { 15 ImageFilter cropFilter; 16 Image ig; 17 cropFilter = new CropImageFilter(0, i * destHeigth,srcWidth, destHeigth); 18 ig = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(),cropFilter)); 19 BufferedImage tag = new BufferedImage(srcWidth,destHeigth, BufferedImage.TYPE_INT_RGB); 20 Graphics g = tag.getGraphics(); 21 g.drawImage(ig, 0, 0, null); // 绘制缩小后的图 22 g.dispose(); 23 24 String pdfFilePath = ShopApplicationResource.shopResource.getString("pdftempfiles.file.root"); 25 // 输出为文件 26 String imgFilePath = pdfFilePath+"temp"+i+".jpg"; 27 ImageIO.write(tag, "JPEG", new File(imgFilePath)); 28 com.itextpdf.text.Image img1 = com.itextpdf.text.Image.getInstance(imgFilePath); 29 // int percent1 = getPercent2(img1.getHeight(), PageSize.A4.getWidth()); 30 //img1.scalePercent(percent1); 31 //设置图片宽高 32 img1.scaleAbsolute(PageSize.A4.getWidth(), img1.getHeight()); 33 //居中 34 img1.setAlignment(com.itextpdf.text.Image.MIDDLE); 35 //另起一页 36 doc.newPage(); 37 doc.add(img1); 38 File file = new File(imgFilePath); 39 if(file.exists()){ 40 file.delete(); 41 } 42 }
这里的思路是:我这里只是将图片适应A4纸,所以只进行横向切片,每次按比例切出一张图片,就放入一张PDF页中,直到切完所有。这种方式肯定不是很好的,只是适用了业务的需要,就没有进行深入研究学习。如果大家有更好的方法,请一定要记得告诉我!
原文地址:http://blog.csdn.net/lan_xuan/article/details/49562343