이 세상에 하나는 남기고 가자

이 세상에 하나는 남기고 가자

세상에 필요한 소스코드 한줄 남기고 가자

PhantomJS 2.1.1: render PDF / zoomFactor 버그 해결

유영재

어제 작성했던 PhantomJS 2.1.1: render PDF / pageSize format 버그 해결에서 언급했던 PhantomJS에서 웹페이지를 PDF로 저장할 때 zoomFactor가 정상 동작하지 않는 문제를 개선한 코드를 새로 올린다. PhantomJS 2: PDF rendering too large, page.zoomFactor doesn't work에서 해결의 힌트가 있었음에도 불구하고 자세히 보지않아 해결하지 못했었는데 해당 부분을 조금 변형해서 조금 더 나은 방법을 적용했다(개인적인 생각).

일단 코드부터.

"use strict";
/*global phantom: false*/

var dest, source;
var v = phantom.version;
if (v.major >= 2) {
    var system = require('system');
    dest = system.args[1];
    source = system.args[2];
} else {
    dest = phantom.args[0];
    source = phantom.args[1];
}

var page = require('webpage').create(); // Web Page를 Control 하기 위한 Web Page Module 객체 생성
// fixme phantomjs 2.1.1 에서 pageSize.format A4 버그 개선
var width = 1024;
page.viewportSize = {width: width, height: width * (768 / 1024)};   // PhantomJS에서 화면을 어떤 사이즈로 출력할 것인지에 대한 값 : 미디어 쿼리도 동작
page.zoomFactor = 585 / width;  // A4 / 72 DPI : 585px X 842px

var pageSize = {
    format: 'A4',
    orientation: 'portrait',
    margin: {
        top: '0.5cm',
        bottom: '0.5cm',
        left: '0.5cm',
        right: '0.5cm'
    }
};
page.paperSize = pageSize;

page.onLoadFinished = function (status) {
    var zoom = page.zoomFactor;
    page.evaluate(function (zoom) {
        document.getElementsByTagName('body')[0].style.zoom = zoom;
    }, zoom);

    setTimeout(function () {
        page.render(dest);    // 스크린 캡쳐파일 생성
        console.log('Status: ' + status);
        phantom.exit();

    }, 0);
};

page.open(source, function (status) {
    page.evaluate(function () {
        // 폰트 문제로 사용가능한 폰트로 교체
        var cssCode = 'html body, html body * { font-family:"바른돋움OTF" !important; }';// html { zoom: 1; }
        var styleElement = document.createElement("style");
        styleElement.type = "text/css";
        if (styleElement.styleSheet) {
            styleElement.styleSheet.cssText = cssCode;
        } else {
            styleElement.appendChild(document.createTextNode(cssCode));
        }
        document.getElementsByTagName("head")[0].appendChild(styleElement);
    });
    if (status !== 'success') { // status 인자를 통해 성공, 실패여부 확인
        console.log('Cannot open webpage');
        phantom.exit();
    }
});

지난 글에서의 코드와 거의 유사하다. 변경된 부분 중 중요한 부분만 아래에 설명한다.

var width = 1024;
page.viewportSize = {width: width, height: width * (768 / 1024)};   // PhantomJS에서 화면을 어떤 사이즈로 출력할 것인지에 대한 값 : 미디어 쿼리도 동작
page.zoomFactor = 585 / width;  // A4 / 72 DPI : 585px X 842px

보여줄 가로 width에 따라 zoomFactor를 결정한다. 585는 72 DPI 기준에서 A4용지의 가로 px 이다.

page.onLoadFinished = function (status) {
    var zoom = page.zoomFactor;
    page.evaluate(function (zoom) {
        document.getElementsByTagName('body')[0].style.zoom = zoom;
    }, zoom);

    setTimeout(function () {
        page.render(dest);    // 스크린 캡쳐파일 생성
        console.log('Status: ' + status);
        phantom.exit();

    }, 0);
};

문제가 되던 부분이 zoomFactor가 적용되지 않는 것이었다. 이를 개선하기 위해 body의 zoom css를 사용해서 보정한다.

다른 부분의 자세한 내용은 PhantomJS 2.1.1: render PDF / pageSize format 버그 해결을 참고하자.


마지막으로 위 방법도 완전한 방법은 아니다. PhantomJS가 버전업 되면서 zoomFactor 버그가 해결되면 이번엔 반대로 너무 작게 출력하게되는 코드가 될 수 있다. 그런 경우을 감안한다면 지난 글에서 언급한 방법이 나을 수도 있다(실제 PDF가 A4 사이즈가 아니라는 것을 무시한다면 : 출력시에는 A4에 맞게 잘 나온다).

comments powered by Disqus