PDF.jsで解像度を変更し、ぼやけの問題を解消する方法

「PDFをブラウザで表示する」前回記事の改善がテーマ

前回のブログ(「PDFをダウンロードさせずにブラウザで表示させる」)ではPDF.jsを利用して、ブラウザ上でのPDF閲覧を実現しました。

しかし、その後「文字がぼやけて閲覧しにくい」という問題が発生したので、今回はその対策方法について解説したいと思います。

実装方法1 デフォルトの拡大率を変更する ※不採用

var viewport = page.getViewport({scale: scale}); 

この部分のscale のデフォルト値を変更しました。
しかしこれは、表示エリアが拡大し、見やすくはなるかもしれませんが、ぼやけた文字が大きくなるだけで全く見当違いでした。

実装方法2 devicePixelRatio を考慮して描画する ※保留(あとで登場)

var canvas = document.getElementById({PDFを描画するcanvasのid});
canvas.width = viewport.width * window.devicePixelRatio;
canvas.styles.width = viewport.width + "px";

WindowsOSで開発してると違いが判らない。。。。という結果になりました。
iOSのRetinaディスプレイであれば若干効能はあります。
またレガシーなブラウザでは、利用できない属性ですので使用する際はご利用環境をご確認ください。(店番長がサポート範囲としているブラウザでは、利用できます)

実装方法3 canvasの属性とstyleで調整する ※これだけでは不足

PDF.jsのviewer.htmlを細かく見ていくとviewer.jsというjsを利用して描画していることがわかります。
であれば、このjsで、どのようにレンダリングしているか見てみるしかないと腹をくくって確認し始めました。

そこで発見したコードが以下です。

function renderPage(activeServiceOnEntry, pdfDocument, pageNumber, size) {
    var scratchCanvas = activeService.scratchCanvas;
    var PRINT_RESOLUTION = _app_options.AppOptions.get("printResolution") || 150;
    var PRINT_UNITS = PRINT_RESOLUTION / 72.0;

    scratchCanvas.width = Math.floor(size.width * PRINT_UNITS);
    scratchCanvas.height = Math.floor(size.height * PRINT_UNITS);

    var width = Math.floor(size.width * _ui_utils.CSS_UNITS) + "px";
    var height = Math.floor(size.height * _ui_utils.CSS_UNITS) + "px";
    var ctx = scratchCanvas.getContext("2d");
    ctx.save();
    ctx.fillStyle = "rgb(255, 255, 255)";
    ctx.fillRect(0, 0, scratchCanvas.width, scratchCanvas.height);
    ctx.restore();
    return pdfDocument.getPage(pageNumber).then(function (pdfPage) {
        var renderContext = {
            canvasContext: ctx,
            transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
            viewport: pdfPage.getViewport({
                scale: 1,
                rotation: size.rotation
            }),
            intent: "print"
        };
        return pdfPage.render(renderContext).promise;
    }).then(function () {
        return {
            width: width,
            height: height
        };
   });
} 

3行目のPRINT_RESOLUTION って、解像度のことではないかと見てみると4行目にある、

var PRINT_UNITS = PRINT_RESOLUTION / 72.0; 

この 72 が、WEBの推奨画像解像度であることが判明しました。

ちなみに、豆知識的な話ですが、
印刷物:300dpi /WEB:72dpi が推奨解像度のようです(私も調べて、はじめて知りました)

scratchCanvas.width = Math.floor(size.width * PRINT_UNITS);
scratchCanvas.height = Math.floor(size.height * PRINT_UNITS); 

そうだ、canvasに描画してるだけなんだ。。。。であれば、PDF.jsはいったん忘れて、canvasの解像度の変更方法に関して調べれば、答えが見つかるはず、ということでさらに深堀り調査。

Canvasでの解像度の変更方法

<canvas id="pdfView" height="1262" width="892" style="height: 1262px; width: 892px;"></canvas>

Style部分で、描画領域を設定し、属性側で、描画する画像のサイズを定義します。
上記の場合、等倍で表示されます。

<canvas id="pdfView" height="631" width="446" style="height: 1262px; width: 892px;"></canvas>

属性側を半分にすると、描画する画像を小さくし(解像度を下げ) styleのサイズまで引き延ばして表示する(粗くなる)

<canvas id="pdfView" height="1893" width="1338" style="height: 1262px; width: 892px;"></canvas>

属性側を1.5倍にすると、描画する画像を大きくし(解像度を上げ) styleのサイズまで縮小して表示する(細かくなる)
実際この解像度を上げる方で試してみても、期待する結果はでません。

pdfjs_resolution_canvas_edit.PNG

表示領域そのままで、画像が小さくなってしまいました。

実装方法4 canvasの属性とstyleで調整する + transform パラメータを利用する

もう一度、viewer.jsをみてみると、transformというパラメータがありました。
その部分で、先のPRINT_RESOLUTIONの計算結果であるPRINT_UNITを使用していることに気づきました。

var renderContext = {
    canvasContext: ctx,
    transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
    viewport: pdfPage.getViewport({
        scale: 1,
        rotation: size.rotation
    }),
    intent: "print"
};

試しに 実装方法3の記述を残したまま、実装方法2の記述を追記してみると、
見事に解像度の変更が可能になりました。

さっそく、解像度を上げてみます。

PRINT_RESOLUTION = 120;

pdfjs_resolution_ok.png

次は、解像度を下げてみます。

PRINT_RESOLUTION = 6;

pdfjs_resolution_ng.png

このパラメータですが、高い値を設定する(解像度をあげる)と、描画に非常にCPUを使います。
なので、高ければ良いというわけではないので、注意が必要です。

ちなみに、他のパラメータを変更してみたところ、右上が下がったり

transform: [PRINT_UNITS, 1, 0, PRINT_UNITS, 0, 0]

pdfjs_resolution_trans1_s.PNG

左下が、右側におされたり、

,transform: [PRINT_UNITS, 0, 1, PRINT_UNITS, 0, 0]

pdfjs_resolution_trans2_s.PNG

などの変化が見られました。

後ろの2つのパラメータは、見た目上変化しませんでした。

,transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 10, 0]
,transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 10]

他のパラメータは、今回変更する必要がなさそうなので、細かい調査は、また別の機会としました。

ということで完成です!
と最後はあっさりなのですが、canvas自体の属性とstyleも両方設定するというところ、なかなか気づけませんでした。

参考資料

PDFをダウンロードさせずにブラウザで表示させる

https://www.linkcom.com/blog/2019/12/howtouse-pdfjs.html

前へ

ニューノーマルという新しい行動様式

次へ

アパホテル様 「店番長」導入事例のご紹介