Hatena::ブログ(Diary)

アセトアミノフェンの気ままな日常

2015-12-22

PDF / PostScript の Tiling Pattern(古い gs は苦手?)

数回にわたって PDF / PostScript の Tiling Pattern を扱ってきた。

これらを経て画像変換のベストプラクティスを探しているのだが、かなり難しい。試してみると「PDF の文字テキストをアウトライン化して EPS または PDF を作る」だけで相当困難である

以下に登場する「背景塗り」は pdfTeX あるいは Quartz API (Mac) を用いた“画像に背景色を付加”する処理、「ロンダリング」は pdfwrite (gs) あるいは Quartz API (Mac) を用いた“半透明のページ以降の /Transparency 除去”を意味する。

アウトライン化 PDF:gs9.15 以降の場合

「テキストをアウトライン化した PDF」を作るベストプラクティスはこれ:

PDF →[pdfTeXでクロップ+余白付与]→ PDF(背景塗り&ロンダリング)→[gs pdfwrite]→ アウトライン化PDF

この PDF から他の形式に変換する場合には、以下のような処理を最後に追加する:

  • 破線が苦手な場合:pdfwrite で .dashpath をかませる
  • 破線もストロークも苦手な場合:pdfwrite で strokepath をかませる

こうすれば、「パターンはパターンとして保持した PDF」が完成する。

アウトライン化 PDF:gs9.14 以前の場合

PDF →[pdftops]→ EPS →[stroke修正処理を仕込んでからepstopdf]→ PDF →[pdfTeXでクロップ+余白付与]→ PDF(背景塗り&ロンダリング)→[gs epswrite]→ アウトライン化EPS →[epstopdf]→ アウトライン化PDF

この PDF から他の形式に変換する場合には、既にストローク修正が済んでいるためこのまま入力できる。

仮に pdftops によるエミュレートを経なければどうなるだろうと試してみると、この場合パターンがすべて epswrite によってビットマップ化されてしまう。epswrite によって文字はアウトライン化されるのだが、パターンがビットマップ化してしまうので困る。ところが、なんと pdftops でエミュレートした後ならば嬉しいことにパターンがビットマップ化しない。不思議だ…

2015-12-21

PDF / PostScript の Tiling Pattern(エミュレートの注意編)

先日の Tiling Pattern の件に一つ問題発生。「Type3 フォントでエミュレートした後に Ghostscript の epswrite / pdfwrite によるアウトライン化にかけるとパターンの色が失われる」という現象を確認したので調査メモ。

パターンの色が変わってしまう例

TikZ のマニュアルに載っている「赤煉瓦に白漆喰」のパターン

\documentclass[dvipdfmx]{article}
\usepackage{tikz}
\usetikzlibrary{patterns}
\pagestyle{empty}
\begin{document}
\begin{tikzpicture} % 赤煉瓦に白漆喰
\def\mypath{(0,0) -- +(0,1) arc (180:0:1.5cm) -- +(0,-1)}
\fill[red] \mypath;
\pattern[pattern color=white,pattern=bricks] \mypath;
\end{tikzpicture}
\end{document}

を、以下の経路で変換する:

TeX →…→ PDF →[pdftops]→ EPS →[epstopdf]→ PDF →[アウトライン化]→ PDF

すると、白漆喰が黒に変色してしまう

f:id:acetaminophen:20151223142303p:image

これでは

パターン描画が苦手なプログラムを通す前にはいったん pdftops を通すという対症療法の有用性が証明された

というわけにはいかない。これは困った…しかし、なんと以前述べた「stroke を strokepath fill に置き換える」という処理を施すと色が保持されることが判明した。

なぜ色が黒になるのか?

考えてみれば、これは当然のような気がする。

通常、アウトラインフォントを制作するときはパスに stroke を使うことはせず、すべて fill でグリフがデザインされている。文字に色を付けるソフトウェアも、それを前提に fill の色を指定するようになっている。gs のアウトライン化(gs9.14 以前なら epswrite の -dNOCACHE、gs9.15 以降なら pdfwrite の -dNoOutputFonts)は、そうした「fill に指定された色」を保持するように設計されているに違いない。

ところが「パターンを Type3 フォントによってエミュレートする」という状況では、パターンが stroke で描かれていればエミュレートも当然 stroke で描かれる。すなわち Type3 フォントの描画命令は stroke なのであり、これは普通のフォント用途から考えれば想定外のことであろう。この場合、Type3 フォントの stroke には色が指定されていても fill には色が指定されていないわけで、フォントのアウトライン化では色無指定とみなされる。したがって、アウトライン化するとデフォルトの黒で fill されてしまうのである。

安全策:ストロークのアウトラインをとる!

最初に結論を述べたが、「stroke を strokepath fill に置き換える」という処理は、stroke の色を fill にそのまま移行することができる。したがって、仮に Type3 フォントが stroke で描かれていても fill にしてしまえば通常のアウトラインフォントと同じように扱われ、無事もとの見た目を保持することができるわけである。

ところが、実際にフォントをアウトライン化してみるとなぜか gs9.14 以前の epswrite でのみ成功する。gs9.15 以降の pdfwrite の -dNoOutputFonts は、こうしてエミュレートされた細かいパターンをアウトライン化できずにビットマップ化してしまう場合がある*1

これらを経て、変換経路のベストプラクティスを考察する→こちら

*1:しかも、今回示した白漆喰はアウトライン化されるが、前回示した細かい斜線のパターンは左下のほうはアウトライン化・右上のほうはビットマップ化という謎挙動…

2015-12-18

PDF / PostScript の Tiling Pattern(Xpdf と Poppler)

先日の補足…というか今日気づいたこと

Poppler 0.38.0 の pdftops は標準で EPS は Language Level 2 を吐き、パターンのアウトライン化エミュレートは行わない。一方、LanguageLevel 1 出力を明示指定するとエミュレートを行うが、カラー画像が白黒になってしまう(CMYK がサポートされない)。
Xpdf 3.04 の pdftops は標準で EPS は LanguageLevel 2 を吐くが、パターンのアウトライン化エミュレートはなぜか行う(LanguageLevel 3 を明示指定しても同様にエミュレートを実行)。LanguageLevel 1 出力を明示指定した場合は Poppler と同じくエミュレートを行うが、カラー画像が白黒になってしまう。

Poppler は Xpdf の「Type3 フォントを並べることによるエミュレート」を嫌ったようで、わざわざソースを変えて Level 2 以上の場合に本来のパターン塗りを行うようにしたらしい。Poppler の Releases をみると

poppler-0.17.3.tar.gz (Mon Aug 29, 2011):
core:
* PSOutputDev: Use Patterns for tiling fill when PS level >= 2

だそうな。Poppler は本家 Xpdf 3.00 から fork し、随時 Xpdf のコードも取り込みつつ独自の改良を続けてきたようだ。その過程で「PS level 2 以上で Patterns が使われないのは変だ」ということになったのだろう。

2015-12-16

PDF / PostScript の Tiling Pattern(エミュレート編)

一昨日の件の続き。

PostScript による Tiling Pattern の例示コード

先日の TikZ による pattern と同等の描画を行う PostScript コードは以下のとおり(既に TeX2img Issue で例示済み)。

%!PS-Adobe-3.0
currentcolor
<< % Begin pattern dictionary
  /PatternType 1
  /PaintType 2
  /TilingType 1
  /BBox [-0.99628 -0.99628 3.9851 3.9851]
  /XStep 2.98883
  /YStep 2.98883
  /PaintProc {
    gsave
      0.3985 setlinewidth
      0 0 moveto
      3.08846 3.08846 lineto
      stroke
    grestore
  }
>> % End pattern dictionary
matrix % Identity matrix
makepattern setpattern
0 0 141.73404 141.73404 rectfill
showpage

この例を PDF 化してみると、Adobe ReaderInkscape の見た目が既に異なることを確認できる。原因の最小化は重要である。

Tiling Pattern をエミュレートする

この Tiling Pattern は PostScript Level2 以上でのみサポートされている feature であるということを、前回少しだけ述べた。さらによくみると

With some effort, it is possible to achieve a limited form of tiling patterns in LanguageLevel 1 by defining them as character glyphs in a special font and painting them repeatedly with the show operator.

と書かれている。要するに、PostScript Level1 でパターン塗りを実現したい場合の一例として「Type3 フォントを定義してそれを並べる」という方法を示唆してくれているのだ。実は、この Type3 フォントを用いた Tiling Pattern の emulate については Adobe TechNote #5112 に詳細に解説されている。

Tiling Pattern のエミュレート実践例

実は、この emulate を実践した例は既に登場している;そう、pdftops だ(追記:Poppler ではなく Xpdf の方)。pdftops から得た EPS を PDF 化したときに「xxx...」という文字列が選択可能になったのは、まさに pdftops による「単位タイルの描画内容を x のグリフとして格納し、x を敷き詰める」という処理の結果である。

pdftops が出力する PS ファイルのヘッダーには「LanguageLevel: 2」と書かれているので、わざわざ Tiling Pattern を PS Level1 の範囲で emulate している理由は不明である。しかし pdftops の挙動を見る限り、この emulate には

パターン辞書を完全に崩して“普通の描画”へと平易化する

という効果を認めてよいだろう。したがって、ひとまずパターン描画が苦手なプログラムを通す前にはいったん pdftops を通すという対症療法の有用性が証明された*1

いくつかの注意点:

  • エミュレートによって発生した Type3 フォントは確実にストロークをアウトライン化しておくべきである!→追記 (2015-12-21)
  • pdftops については Xpdf と Poppler で挙動が異なり、上記は Xpdf でのみ成り立つ。→追記 (2015-12-18)

*1:当然、Type3 フォントで使われている文字 x が選択できるようになってしまう。気持ち悪い場合は、そのあとにグリフのアウトライン化を通せば OK になる。

2015-12-14

PDF / PostScript の Tiling Pattern(基礎)

再び TeX2img Issue より。

TikZのパターンがSVG化の際に欠ける
以下のソースを TeX2img (mudraw) で SVG 化するとパターンが欠ける。しかもブラウザと Inkscape / Illustrator で見た目が異なる。preview パッケージにかけるとまた違う結果になる。

\documentclass[dvipdfmx]{article}
\usepackage{tikz}
\usetikzlibrary{patterns}
\pagestyle{empty}
%\usepackage[dvipdfmx,tightpage,active]{preview} % この2行を有効にしたり
%\PreviewEnvironment{tikzpicture}                % しなかったりして実験
\begin{document}
\begin{tikzpicture}
\filldraw[pattern = north east lines] (0,0) rectangle (5,5);
\end{tikzpicture}
\end{document}

preview パッケージを dvipdfmx で使う方法は、ZR さんの記事を参照。これらの結果はいまのところここにまとめてある。「gs を通すとヘンになる」とか「TeX2img に通すとヘンになる」とか、何が原因なのかサッパリわからない。とはいえ、少なくとも gs(文字のアウトライン化などに使用)も mudraw(SVG 形式への変換に使用)も PDF や PostScript の Tiling Pattern が苦手なのは確かである。

Tiling Pattern とは

PDF Reference sixth edition の "4.6 Patterns" や PostScript Language Reference Manual third edition の "4.9 Patterns" に登場する。

A tiling pattern consists of a small graphical figure called a pattern cell. Painting with the pattern replicates the cell at fixed horizontal and vertical intervals to fill an area...
The pattern cell can include graphical elements such as filled areas, text, and sampled images. (中略) When performing painting operations such as S (stroke) or f (fill), the application paints the cell on the current page as many times as necessary to fill an area...

Tiling patterns consist of a small graphical figure (called a pattern cell) that is replicated at fixed horizontal and vertical intervals to fill the area to be painted.
Note: The ability to paint with patterns is a feature of LanguageLevels 2 (tiling patterns) and 3 (shading patterns). With some effort, it is possible to achieve a limited form of tiling patterns in LanguageLevel 1 by defining them as character glyphs in a special font and painting them repeatedly with the show operator...

どちらも Adobe が策定したものなので、ほぼ同じ意味内容が書かれている。その続きの部分を要約すると

  1. パターンは「辞書」部分(PDF や PS の << ... >> の部分)に登録される
  2. その辞書を参照するかたちで繰り返し単位を敷き詰める

という仕組みになっているらしい。

注目点1:「パターン」は繰り返す

はじめに注目すべきは「領域を埋めるのに必要な回数だけ繰り返し単位を敷き詰める」こと。以前に破線について調べた際に

PostScript の setdash で破線パターンを用いて書かれている場合、PDF ライブラリ側が両端以外の座標を補間しなければならない

と書いた。簡単のため真っ直ぐな破線の場合で考えると、破線の描画は「ストロークの両端の座標」と「破線パターン」で決まっている。破線パターンはストロークの二点間を埋めるように繰り返される必要があり、そこが EMF 変換のネックになっていた(最近になってようやく解決)。今回のタイリングパターンもやはり「パターン」というだけあって、破線と同様にライブラリ側が描画すべき内容を計算する必要があるのだろう。

注目点2:PostScript のパターンは LanguageLevel 1 が対象外

もう一つ注目すべきは、PostScript のほうでは Tiling Pattern が LanguageLevel 2 以上でしかサポートされていないこと。Xpdf の pdftops で PS に変換して再度 epstopdf で PDF に戻してみると、この Tiling Pattern がなぜか「xxx...」という Type3 フォントの文字列になってしまうという現象も確認済みで、どうやらこの LanguageLevel が関係しそうである

続く