상세 컨텐츠

본문 제목

HTML5와 html2canvas 이용한 웹화면 PDF로 저장시 해상도 깨지는 문제 해결하는 방법

개발이야기

by 꾸데따 2021. 12. 9. 13:54

본문

[사용라이브러리]

javascript lib => html2canvas.js

pdf java lib => pdfbox

 

 

[해결방법]

html2canvas를 이용해 scale 옵션을 지정해줘야 함.  

 

 

 

 

[js]

pdf 저장을 위한 스크립트 실행부분

var frm = document.getElementById("pdfFrm");
var fd = new FormData(frm);
var sPageDirection     = $("#paperDirection").val();
var sPageSize     =$("#paperSize").val();
var oElOuterPreview = $('#outer-preview');

html2canvas(oElOuterPreview.get(0), {
    scale: 2 //옵션을 줘야 훨씬 깨끗해짐.
}).then(function (canvas){

    var tmpCanvas = document.createElement('canvas');
    tmpCanvas.width = canvas.width;
    tmpCanvas.height = canvas.height;

    var ctx = tmpCanvas.getContext('2d');
    ctx.drawImage(canvas,0,0,canvas.width,canvas.height,0,0,canvas.width,canvas.height);

    tmpCanvas.toBlob(function(blob) {
        if(navigator.msSaveOrOpenBlob || !fd.set){
            fd.append("imgFile", blob)
        } else {
            fd.append("imgFile", blob)
        }
        fd.append("direction", sPageDirection);
        fd.append("size", sPageSize);

        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
            if (this.readyState == 4 && this.status == 200){

                var filename = "";
                var disposition = xhr.getResponseHeader('Content-Disposition');
                if (disposition && disposition.indexOf('attachment') !== -1) {
                    var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                    var matches = filenameRegex.exec(disposition);
                    if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
                }

                // this.response is what you're looking for
                console.log(this.response, typeof this.response);

                if(navigator.msSaveOrOpenBlob){
                    // ie 전용
                    navigator.msSaveOrOpenBlob(this.response, filename);
                }  else {
                    var a = document.createElement("a");
                    var url = URL.createObjectURL(this.response)
                    a.href = url;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                    window.URL.revokeObjectURL(url);
                }
            }
        }
        xhr.open('POST', '/saveToPdf.do');
        xhr.responseType = 'blob';
        xhr.send(fd);

        cvs_watermark.hide();

    });
})

 

 

[java]

넘겨받은 image를 pdf로 저장하는 부분

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;
@RequestMapping(value = "/saveToPdf.do", method = RequestMethod.POST)
public void saveToPdf(MultipartHttpServletRequest request, HttpServletResponse response, HttpSession session) throws Exception {

   String rootPath = propertyService.getString("Globals.fileStorePath");
   long timestamp = System.currentTimeMillis();
   SimpleDateFormat dayTime = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss");
   String timeStr = dayTime.format(timestamp);
   String page_direction = request.getParameter("paperDirection"); //landscape, a4
   String page_size = request.getParameter("paperSize"); //a4, a3
   float POINTS_PER_INCH = 72;
   float POINTS_PER_MM = 1 / (10 * 2.54f) * POINTS_PER_INCH;  //2.834645669291339
   //float POINTS_PER_MM = 3.834349419193889f;

   
      try (final PDDocument doc = new PDDocument()){

         PDPage page = null;

         if(page_size.equalsIgnoreCase("a4")) {
            if(page_direction.equalsIgnoreCase("landscape")) {
               page = new PDPage(new PDRectangle(297 * POINTS_PER_MM, 210 * POINTS_PER_MM));
            }
            else {
               page = new PDPage(new PDRectangle(210 * POINTS_PER_MM, 297 * POINTS_PER_MM));
            }
         }
         else { //A3
            if(page_direction.equalsIgnoreCase("landscape")) {
               page = new PDPage(new PDRectangle(420 * POINTS_PER_MM, 297 * POINTS_PER_MM));
            }
            else {
               page = new PDPage(new PDRectangle(297 * POINTS_PER_MM, 420 * POINTS_PER_MM));
            }
         }

         doc.addPage(page);

         Iterator<String> itr =  request.getFileNames();
         List<MultipartFile> mpf = request.getFiles(itr.next().toString());
         String contentType = mpf.get(0).getContentType().replace("image/", "");
         byte[] imgArr = mpf.get(0).getBytes();

         PDImageXObject pdImage = PDImageXObject.createFromByteArray(doc, imgArr, "a");

         //현재 설정된 PDF 페이지의 가로/세로 구하기
         int pageWidth = Math.round(page.getCropBox().getWidth());
         int pageHeight = Math.round(page.getCropBox().getHeight());

         //이미지 가로사이즈가 PDF 가로사이즈보다 클 경우를 대비해서 이미지 리사이징 실행
         //현재 설정된 PDF 페이지 가로 , 이미지 가로 사이즈로 비율 측정
         float imgRatio = 1;
         if ( pageWidth < pdImage.getWidth()) {
            imgRatio = (float)pdImage.getWidth() / (float)pageWidth;
         }
         //설정된 비율로 이미지 리사이징

         BufferedImage bfImage = pdImage.getImage();

         
         //이미지 sharpen 처리 (효과 없는 듯)
         Kernel kernel = new Kernel(3, 3, new float[] { 1f, 1f, 1f, 1f, 2f, 1f, 1f, 1f, 1f });
         BufferedImageOp op = new ConvolveOp(kernel);
         bfImage = op.filter(bfImage, null); 

         int imgWidth = Math.round(pdImage.getWidth() / imgRatio);
         int imgHeight = Math.round(pdImage.getHeight() / imgRatio);

         //이미지를 가운데 정렬하기 위해 좌표 설정
         int pageWidthPosition = ((pageWidth - imgWidth) / 2 ) + 10;
         int pageHeightPosition = ((pageHeight - imgHeight) / 2);

         PDPageContentStream contents = new PDPageContentStream(doc, page);
         PDRectangle mediaBox = page.getMediaBox();
         
         contents.drawImage(pdImage, pageWidthPosition, pageHeightPosition, imgWidth, imgHeight);
         contents.close();
         File f = new File(rootPath+"uploads\\imagePdfTest.pdf");
         doc.save(f);

         String downloadName = null;
         String browser = request.getHeader("User-Agent");
         //파일 인코딩
         if(browser.contains("MSIE") || browser.contains("Trident") || browser.contains("Chrome")){
            //브라우저 확인 파일명 encode
            downloadName = URLEncoder.encode(f.getName(), "UTF-8").replaceAll("\\+", "%20");
         }else{
            downloadName = new String(f.getName().getBytes("UTF-8"), "ISO-8859-1");
         }
         response.setHeader("Content-Disposition", "attachment; filename=\"" + downloadName +"\"");
         response.setContentLength((int)f.length());
         response.setContentType("application/octet-stream");
         BufferedInputStream in = new BufferedInputStream(
               new FileInputStream(f));

         FileCopyUtils.copy(in, response.getOutputStream());
         in.close();

         System.out.println(f.getAbsolutePath());
         response.getOutputStream().flush();
         response.getOutputStream().close();



      } catch (IOException e){
         System.err.println("Exception while trying to create pdf document - " + e);
      }

}

 

관련글 더보기