2013年4月1日月曜日

svgの印刷結果をクロスブラウザ化する

svgはxml記述で自由にグラフィックを描くことが出来る上,今日ほとんどのwebブラウザで表示・印刷することが可能であるなど,使い勝手が良いのが魅力です.

そこで,レポート用紙などの用紙テンプレートをsvgで作ることを考えてみました.一般のテンプレートファイルはオフィスアプリケーションで作られていたりpdfで配布されているため,専用のツールを利用する必要があります.これをsvgで作っておけば環境を選ばずに出力可能となるためより利便性が高まるはずです.

しかし実際に試してみるとwebブラウザ毎に出力結果が異なる等,中々思い通りには行かないようです.そこで,今回はsvgをwebブラウザで印刷する際の挙動についてまとめて見ました.

svgを印刷用途で用いる


svgは一般にディスプレイ上にグラフィックを描画するものとして考えられていますが,かつてのsvg1.2ではsvg printとして印刷用途としての利用が模索されていました.この考え方はsvg1.2の仕様検討が中止されたことにより意味のないものとなってしまいましたが,もし実現していればpdfの代替技術となっていたかもしれません.

このようにsvgを印刷用途に応用するというのはごく自然な事と言えるのですが,実際に試してみると中々手強いことがわかります.というのもwebブラウザでsvgグラフィックを印刷しようとすると,ブラウザ毎の動作の違いに悩まされるからです.注意すべき点を列挙してみましょう.
  1. ブラウザ毎に規定の余白幅が異なる.
    編集可能なものもある.
  2. ブラウザ毎に1ページに印刷可能な範囲が異なる.
    また,ブラウザによっては勝手にリサイズ処理が発生する.
  3. ブラウザ毎に改ページの扱いが異なる. 
  4. 印刷時に用いられるdpi値がブラウザ毎に異なる.
    そのままではブラウザ毎にグラフィックの出力サイズが違いすぎる.
    なお,mm等の単位を指定しても必ずしも出力結果に反映されるとは限らない.
    従ってsvgのクロスブラウザでのプリンタ出力を目指すのであれば,これらの問題を解決しなければなりません.

    ブラウザごとの特徴


    それでは主なブラウザ毎の動作の違いについて見てみましょう.なお,調査には各ブラウザの印刷プレビューを利用しました.なおいずれのブラウザにおいてもA4用紙サイズ(210mm×297mm)が規定の印刷サイズとなっています.

    firefox(22)


    svgの印刷は常に元のサイズにて行われ,1ページに収まらない部分は印刷されません.

    印刷可能範囲:660px×980px

    chrome(26)


    svgの印刷は横幅のサイズが673pxまでは元のサイズで行われます.
    それよりも幅が大きかった場合,1078pxまでは自動的に幅が自動的に縮小されます.
    それ以上の場合は横にはみ出した部分が印刷されません.
    また,縦方向にはみ出した場合は2ページ目以降に出力されます.
    ※印刷時の空白幅をカスタマイズしていない場合.

    元のサイズに依る印刷の上限幅:673px×940px
    リサイズの上限幅:1078px×1505px

    opera/presto(12.14)


    svgの印刷は横幅のサイズが885pxまでは元のサイズで行われます.
    それよりも幅が大きかった場合,横幅いっぱいに自動的にリサイズされます.

    元のサイズに依る印刷の上限幅:885px×1211px

    ie(10)


    svgの印刷は横幅のサイズが650pxまでは元のサイズで行われます.
    それよりも幅が大きかった場合,1082pxまでは自動的に幅が自動的に縮小されます.
    それ以上の場合は横にはみ出した部分が印刷されません.
    また,縦方向にはみ出した場合は2ページ目以降に出力されます.

    元のサイズに依る印刷の上限幅:650px×988px
    リサイズの上限幅:1082px×1630px

    ※safariでは未調査ですが,概ねchromeと同じような値となるものと思われます.
    このように,ブラウザ毎の印刷範囲には大きな違いが見られます.

    実際に印刷してみる


    とりあえずこれで印刷可能な領域が判明しました.そこで今度は実際に印刷してみてその内容を調べて見た処,operaでは不具合が出ることがわかりました.

    opera・safariにおいてはsvgの出力結果がぼやけてしまいます.これはグラフィックのラスタライズのタイミングが他のブラウザと異なることに起因するものと考えられます.また,operaではグラフィックの左上が欠けてしまう現象が発生しました.
    その為残念ながらoperaはこのような印刷物の出力用途には不適と言えます.
    (safari6での動作確認はしていません.)

    参考までにページ余白の測定値について掲載します.余白の幅はプリンタ環境によって異なるものと思われます.


    ブラウザ印刷可能範囲
    px
    アスペクト比余白描画範囲の実サイズ
    A4サイズからの差分値
    firefox(gecko) 660×9801.484818mm18mm17mm20mm174mm×260mm
    chrome(webkit)673×9401.396711mm10mm11mm20mm189mm×266mm
    opera(presto)885×12111.368310mm13mm14mm25mm187mm×258mm
    ie(trident)650×9881.5220mm20mm18mm15mm170mm×264mm

    ブラウザ間の違いを吸収する


    ところでsvgではviewBox属性を使って座標軸の伸縮を自在に行うことができます.またswitch要素を使って(余り良くないものの)ブラウザの判定処理が行えます.

    従って,次のようなsvgコードを記述することでブラウザに依らない一定サイズの印刷結果を得ることが可能となります.※多少の誤差は発生します.
     (firefox/chrome/opera(一部)/ieをサポート)

    <?xml version="1.0" standalone="no"?>
    <svg height="99.9%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
     <!--height属性を設定することでchromeでの強制改ページを回避する-->
     <defs>
      <!--共通の出力レイアウトを記述するsvg要素-->
      <!--サイズは最も狭いものに合わせる 幅:ie,高さ:opera-->
      <svg id="base" width="170" height="253">
       <rect width="100%" height="100%" fill="none" stroke="blue" stroke-width="2"/>
      </svg>
     </defs>
     <!--
     ブラウザごとの印刷処理の挙動を吸収するswitch
     出力px値を実際のmmスケールに割り当て直している
     -->
     <switch>
      <g requiredFeatures="http://www.w3.org/TR/SVG11/feature#SVG-dynamic">
       <switch>
        <!--opera-->
        <!--Note:operaでは左上部が切れるので,左上の基準をずらす.-->
        <svg requiredFeatures="http://www.w3.org/TR/SVG11/feature#SVG-animation"
         width="885" height="1211" viewBox="-9 -5 187 258">
         <use xlink:href="#base"/>
         <text y="15">opera</text>
        </svg>
        <!--ie-->
        <svg width="650" height="988" viewBox="0 0 170 264">
         <use xlink:href="#base"/>
         <text y="15">ie</text>
        </svg>
       </switch>
      </g>
      <!--chrome-->
      <!--Note:chromeは印刷可能領域が広いので,左上の基準をずらしている-->
      <svg requiredFeatures="http://www.w3.org/TR/SVG11/feature#SVG-static"
       width="673" height="940" viewBox="-9 -5 189 266">
       <use xlink:href="#base"/>
       <text y="15">chrome</text>
      </svg>
      <!--safari-->
      <!--Note:safariは最も外側のsvg要素のサイズのみを参照する.
      その為,印刷時にスクリーンに表示されていない部分が印刷されない-->
      <svg requiredFeatures="http://www.w3.org/TR/SVG11/feature#SVG-animation"
       width="673" height="940" viewBox="0 0 189 266">
       <use xlink:href="#base"/>
       <text y="15">safari</text>
      </svg>
      <!--firefox-->
      <svg width="660" height="980" viewBox="0 0 174 260">
       <use xlink:href="#base"/>
       <text y="15">firefox</text>
      </svg>
     </switch>
    </svg>

    • switch要素でブラウザの判定を行なっています.これはjavascriptの動作有無に依存しないようにするためです.
      その結果,safariでの動作を諦めることになります.
      ※なおsafariでも動作させたいのであれば,switch要素ではなくjavascriptでルートのsvg要素のサイズ・viewBox属性を書き換えるようにします.
      ※もうひとつの方法としてはxsltでブラウザ判定を行う方法があります.
    • switch要素配下のsvg要素では1頁ぴったりに出力可能なピクセル範囲を,実際の出力サイズ(mm単位)に割り当て直しています.こうすることでブラウザごとのピクセルあたりの印刷サイズの違いを吸収することができます.
    • 実際のグラフィックは別途defs要素内部で定義しています.use要素でこの内容を複写することでグラフィック定義を一箇所にまとめることができます.
    ※なおあくまでも筆者の環境での結果なので,その他の環境で同じ結果となるわけではないでしょう.しかし,似たようなコードで実現出来るものと思います. 

    このsvg文書の構造自体はA4サイズの用紙にグラフィックを印刷する場合に限り使いまわすことができます.よって予めテンプレートとして保存しておくと便利かも知れません.

    まとめ


    svgを印刷用途に用いるにはブラウザ毎の違いを考慮しなければならない.
    svgにおいては座標軸の変換を巧みに行うことで環境に依存しない印刷結果を得ることが出来る
    プリンタに依存しないかどうかは未調査.あくまで簡易出力目的で利用するならば有効と言える.

    応用


    • ブラウザに依存しない帳票出力に応用できる.
      テキストの挿入が面倒だけれど…
    • 簡易オフィスツールとしての利用が可能.
      svgの仕様を理解していれば簡単に文書ファイルを作成可能
      ↑そこが大変なんだってば!

    サンプル

    例えばこんな.この他にも画像を挿入できたりと自由自在.

    0 件のコメント:

    コメントを投稿