1. 程式人生 > >使用html2canvas實現網頁截圖,並嵌入到PDF

使用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頁中,直到切完所有。這種方式肯定不是很好的,只是適用了業務的需要,就沒有進行深入研究學習。如果大家有更好的方法,請一定要記得告訴我!

注:本人很少寫部落格,語言表述方面可能欠佳,請大家海涵!