2012年11月28日水曜日

住所ラベルをsvgで作ろう→pattern要素の罠

年賀状用に自宅の住所を印刷したラベルを作る際,これまではofficeアプリケーションを使って無理矢理作っていました.・・・が,1ページにたくさん同じレイアウトの画像を並べるというのは非常にストレスが溜まります.なんと言っても内容を修正するのがとてつもなく面倒なんですね.
だったらsvgを使って楽をすることが出来るんじゃないかと考えたのが今回のテーマです.折角学んだのですから,使いたいですよね.

svgでは一つの図形を繰り返し出力する機構として次の二つが用意されています.従ってこれらを上手く使えば,ラベル1枚分のレイアウトを作り,それをコピーして並べることで1枚の紙にたくさんのラベルを並べることが出来るはずです.
  • use要素
  • pattern要素
use要素は既存の図形を複製して別の場所に配置するものです.これはこれで非常に有用な機能なのですが,位置を自分で指定する必要があるため今回の自動的に並んで欲しいといった用途には不向きです.

よって今回はpattern要素を使うこととしました.では早速サンプルコードを見てみましょう.

pattern要素を使ってレイアウトしてみる



<?xml version="1.0" standalone="no"?>
<svg width="189mm" height="256mm" viewBox="0 0 189 256" 
 xmlns="http://www.w3.org/2000/svg" 
 xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
 <!--A4 サイズ:210 × 297 ミリ-->
 <defs>
  <pattern id="cardPattern" patternUnits="userSpaceOnUse" viewBox="0 0 47 15"
   x="0" y="0" width="47" height="15">
   <rect x="0" y="0" width="100%" height="100%" fill="white"/>
   <g stroke="black" stroke-width="0.2" stroke-dasharray="1">
    <line x1="0" y1="0" x2="0" y2="100%"/>
    <line x1="100%" y1="0" x2="100%" y2="100%"/>
    <line x1="0" y1="0" x2="100%" y2="0"/>
    <line x1="0" y1="100%" x2="100%" y2="100%"/>
   </g>
   <g font-family="sans-serif">
    <text font-size="3" x="1" y="4">999-9999</text>
    <text font-size="3" x="18" y="4">℡090-9999-9999</text>
    <text font-size="3" x="1" y="8">〇〇県△△市□□町999-9</text>
    <text font-size="4" x="1" y="13">山田太郎</text>
    <text font-size="2.5" x="18" y="13">xxx@yyy.ne.jp</text>
   </g>
  </pattern>
 </defs>
 <rect fill="url(#cardPattern)" x="0" y="0" width="100%" height="100%"/>
</svg>

実にこれだけです.境界部分の破線が重なるように工夫している点以外は実にシンプルで,pattern要素で1枚分のレイアウトを作り,それをrect要素で紙1面に敷き詰めています.後はテキストの内容を書き換えるだけで幾らでもラベルを作ることができます.

印刷時に問題発生!

と思ったのですが,実際にプリンタ出力してみると…文字がボケボケになってしまいますね
どうやらpattern要素を使った塗り潰し図形は印刷処理よりも前にラスタライズされてそのままのようです.それどころか,pattern要素配下のshape-rendering属性が無効となることも判りました.従ってpattern要素で生成した図形は特別扱いされているようです.pdf形式で出力するとよく判ります.pattern要素で塗りつぶした部分がラスタ画像となっているのです.

まあこれはsvgの処理パフォーマンスを考えてみれば当然なことなので,理解できないことはないのですが盲点でした.ちなみにfirefox,chrome,operaの何れも同じ動作となります.

つまり印刷を前提とするのであればpattern要素は使いにくい事になります.
となるともう残された道はuse要素しかありません.ではコードを見てみましょう.

use要素を使って書きなおしてみる



<?xml version="1.0" standalone="no"?>
<svg width="189mm" height="256mm" viewBox="0 0 189 256" 
 xmlns="http://www.w3.org/2000/svg" 
 xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
 <!--A4 サイズ:210 × 297 ミリ-->
 <defs>
  <g id="unit">
   <g stroke="black" stroke-width="0.2" stroke-dasharray="1">
    <line x1="0" y1="0" x2="0" y2="15"/>
    <line x1="47" y1="0" x2="47" y2="15"/>
    <line x1="0" y1="0" x2="47" y2="0"/>
    <line x1="0" y1="15" x2="47" y2="15"/>
   </g>
   <g font-family="sans-serif" id="a">
    <text font-size="3" x="1" y="4">999-9999</text>
    <text font-size="3" x="18" y="4">℡090-9999-9999</text>
    <text font-size="3" x="1" y="8">〇〇県△△市□□町999-9</text>
    <text font-size="4" x="1" y="13">山田太郎</text>
    <text font-size="2.5" x="18" y="13">xxx@yyy.ne.jp</text>
   </g>
  </g>
  <g id="line">
   <use xlink:href="#unit" x="0"/>
   <use xlink:href="#unit" x="47"/>
   <use xlink:href="#unit" x="94"/>
   <use xlink:href="#unit" x="141"/>
  </g>
 </defs>
 <use xlink:href="#line" y="0"/>
 <use xlink:href="#line" y="15"/>
 <use xlink:href="#line" y="30"/>
 <use xlink:href="#line" y="45"/>
 <use xlink:href="#line" y="60"/>
 <use xlink:href="#line" y="75"/>
 <use xlink:href="#line" y="90"/>
 <use xlink:href="#line" y="105"/>
 <use xlink:href="#line" y="120"/>
 <use xlink:href="#line" y="135"/>
 <use xlink:href="#line" y="150"/>
 <use xlink:href="#line" y="165"/>
 <use xlink:href="#line" y="180"/>
 <use xlink:href="#line" y="195"/>
 <use xlink:href="#line" y="210"/>
 <use xlink:href="#line" y="225"/>
 <use xlink:href="#line" y="240"/>
</svg>

ソースコードは若干長くなってしまいましたが,一度作ってしまえば大枠を変更する必要はありませんからこれで良いこととしましょう.こちらの方法であれば,問題なくくっきりとした印刷結果が得られます.

スクリーンでの出力結果もよーく見るとpattern要素に依るものは境界がぼけていることが判りますね.このようにpattern要素は使い処を間違えると意図しない品質の低下をもたらすのです.

教訓)レイアウト目的でpattern要素を使うと印刷時に痛い目を見るよ!


では宛名シールはどうでしょう?

宛名シールの場合は同じレイアウトに異なるデータを挿入して並べると言った処理を行う必要があります.いわゆる差し込み処理ですね.

ちょっと考えただけですが,これをsvgだけで実現するのは難しいでしょう.javascriptを使えば可能ですが,描画位置を考慮する必要があるなどいささか面倒です.このような自動レイアウトを必要とするのであればhtml+cssを使ったほうが良さそうです.

0 件のコメント:

コメントを投稿