ツール

PHPでQRコードを画面表示するために、TCPDF同梱のTCPDF2DBarcodeでSVGを生成したところ、CSSでサイズを大きくしてもQRコードの絵が原寸のまま左上に残ってしまう現象に遭遇しました。原因はTCPDFが出力するSVGにviewBoxが無いことです。width/height属性をviewBoxに置き換えれば、CSSで自由に拡大縮小できるようになります。

TCPDFでQRコードのSVGを生成する

TCPDFはEC-CUBEをはじめ多くのPHPプロジェクトにvendorとして最初から入っているので、QRコードのためだけに新しいライブラリを追加せずに済みます。2次元バーコード用のクラスを使うと2行でSVGが得られます。

$barcode = new \TCPDF2DBarcode('SAMPLE-TICKET-0001', 'QRCODE,M');
$svg = $barcode->getBarcodeSVGcode(4, 4, 'black');

これをTwigなどのテンプレートにrawで埋め込めば、画像ファイルを作らずにQRコードを表示できます。

症状:コンテナを広げてもQRコードは原寸のまま

PC表示では180pxの固定サイズで問題なく表示できていました。スマホでは受付端末での読み取りやすさを考えて70vw(最大280px)まで拡大しようと、次のCSSを書きました。

.qr {
    width: 70vw;
    height: 70vw;
    max-width: 280px;
    max-height: 280px;
}
.qr svg {
    width: 100%;
    height: 100%;
}

ところが実際にスマホ表示にすると、枠だけが広がってQRコードの絵は左上に原寸のまま。svg要素自体は100%に広がっているのに、中の描画がまったく拡大されません。

原因:TCPDFのSVGはviewBoxを持たない

TCPDFが出力するSVGの開始タグを見ると、こうなっています(実際の出力)。

<svg width="84" height="84" version="1.1" xmlns="http://www.w3.org/2000/svg">

viewBoxがありません。SVGはviewBoxがあって初めて「描画内容を表示領域に合わせて伸縮する」挙動になります。viewBoxの無いSVGは、CSSで要素の表示領域を広げても中の図形は元の座標のまま描かれるため、拡大した領域の左上に原寸の絵が残るという今回の症状になります。

解決:width/height属性をviewBoxに置き換える

SVG生成後に、固定のwidth/height属性を同じ値のviewBoxへ置換します。

/**
 * QRコードのSVGを生成する
 *
 * @param string $code QRコードに埋め込む文字列
 * @return string SVGマークアップ
 */
private function generateQrSvg(string $code): string
{
    $barcode = new \TCPDF2DBarcode($code, 'QRCODE,M');
    $svg = $barcode->getBarcodeSVGcode(4, 4, 'black');

    // TCPDFのSVGはviewBoxを持たないため、固定のwidth/height属性を
    // viewBoxに置き換えてCSSでの拡大縮小(レスポンシブ表示)を可能にする
    if (preg_match('/<svg width="([\d.]+)" height="([\d.]+)"/', $svg, $m)) {
        $svg = preg_replace(
            '/<svg width="[\d.]+" height="[\d.]+"/',
            sprintf('<svg viewBox="0 0 %s %s"', $m[1], $m[2]),
            $svg,
            1
        );
    }

    return $svg;
}

これで先ほどのCSSがそのまま効くようになります。実測では、PCで180px、幅390pxのスマホ(iPhone 13相当)で273pxに拡大表示されることを確認しました。QRコードはもともとベクターデータなので、SVGのまま拡大しても読み取り品質は落ちません。