PDF 内部構造 グラフィックス

提供:yonewiki
2022年10月26日 (水) 13:06時点におけるYo-net (トーク | 投稿記録)による版 (→‎平行移動・拡大縮小・回転)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)

PDF 内部構造に戻る。

概要

 埋め込み画像を使えばいいじゃないってことですが、グラフィックスを記述することができます。拡大しても荒くならないベクター形式の図形を描くのが、このグラフィックスという項目で記述する内容です。それにしてもグラフィックスのベクター描画技術を使っているPDFってあんまり見かけないです。Illustratorのように完璧ってわけでもない機能だからと思います。でも、無駄にいろいろできるようにはなっています。ページの座標系は左下が原点で上と右に向かって座標値が増えていく感じです。この座標値を使って線の始点座標や終点座標を指定したり、矩形の左下座標と右上座標を指定したりして描きます。曲線はベジェ曲線を使います。線の考え方に、ベジェ曲線のハンドル座標を1ないしは2個指定をしたりします。あとはつながった線や曲線を閉じるという考え方があったりします。こういう操作は、PDFでは未経験でも画像処理に使うOpenGL/OpenCL/WebGL/OpenCVやHTML5やTeXやCanvas、3D検定や今はなきActionScriptでも扱うので、どこかで似たようなことを経験したことがある人もいると思います。行列をつかった変換行列・平行移動・回転・拡大なんかも経験した人もいるかもです。ちょっと独特な部分を覚えるっていう感じになるやもしれません。


 PDFは印刷と画面表示の両方を上手く扱うので、そのあたりの処理もややこしいかもしれません。


 PDFのグラフィックスを勉強するにあたって、左下原点の方眼紙があった方が、何をやっているかわかりやすくなると思いましたので、線を引き終わったPDFソースを準備しました。このファイルに、これから説明するような内容を記述して勉強することができます。100pxおきに濃線。10pxおきに淡線。50pxおきに中濃線をひいてくれるソースです。これだけの線を大量にひくとかなりのボリュームのPDFストリームを記述しないと駄目なので、楽だと思います。デザインが気に食わない人は自分で用意してもよいと思います。

 ダウンロード:リンクの上を右クリックで名前を付けて保存とかしてほしいな。
 Mesh-Line-Source.pdf

 

上記のPDFの方眼紙の中身はこんな感じ


 PDFtkによって上記ファイルをPDFに変換する必要があります。コマンドの一例は以下のようになります。

>cd "ファイルの保存先パス"
>PDFtk  Mesh-Line-Source.pdf output Mesh-Line.pdf

 PDFソースファイルの30行目~32行目は以下のとおりになっていて、その次の行あたりに、必要におうじてグラフィックのペン設定、例えば/Patternのようなグラフィックキーを記載していくことになります。

    >>
  >>
  %
  % ここにグラフィック用ペンの各種設定情報を追記
  %
  
>>
endobj 
4 0 obj 
<< >>
stream
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%ここからが方眼紙作成のグラフィックストリーム
%
0.9 G
0 10 m 1033 10 l S

 試してみたいグラフィックストリームは以下のような400行目あたりのここにグラフィックのストリームを記述しますの後に記載することで、確認できます。

1030 0 m 1030 1462 l S
%
%ここまでが方眼紙作成のグラフィックストリーム
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0 G
0 g
5 w

%
%ここにグラフィックのストリームを記述します
%

endstream

 

直線

 コンピュータグラフィックスと言えば、直線を引くことから、すべてが始まると言っても過言ではないので、直線から取り上げました。PDFの側面からみるとペンというものを動かすとか、ペンの座標系とか、縮尺、回転というペンの基礎から説明するのが、普通なのかもしれませんが、やっぱね。直線。直線引くときに知れっとペンの操作もちょっとしちゃうっていう感じ。


 直線を引くには、ちょちょいっとペンのおまじないをしてから、ストリーム部で、オペランドとオペレータを使えばいいって感じです。例えば、ページ左下を原点に上方向、右方向に増えていく座標系において、横 100px 縦 100pxの点から横方向に200の長さの直線を引くと、横 300px 縦100 の位置が直線の終点になります。次はその例です。


100 100 m
300 100 l S

 と、上記のようになります。最初の1行目はペンの移動をするストリームです。そして2行目が直線をひくオペレータを使ったストリームでペンの移動した位置から、オペランドで指定した位置まで直線をひきます。例では 300 100 となっている部分が該当のオペランドで、l (小文字のエル)がオペレーターです。オペレータがとるオペランドの数はオペレータ毎に固有の数になります。l に続く S という単独のオペレータでそれまでのペンの動きが反映された、線が実際に描画されます。ストロークのSです。 線のSに対して、塗りのf 、両方のbが似た役割のオペレータになります。線の太さは、こちらで用意した方眼紙テンプレートを使った場合は、太さ 5 が適用されます。方眼紙を引き終わった後で、太さ 5になるように太さ変更のオペレータをオペランドを伴って記載しているからです。線を引くストリームの直前の5 w というところが該当の箇所になります。自分で用意した人はデフォルトの 1で描かれたかもしれません。以降の項目では、この線の太さの事情については同じなので述べません。以下が結果描かれるグラフィックです。

直線


Graphic-Line-Source.pdfの404, 405行目が追加した部分です。以降はどこがストリーム部であるとかという挿入部分へは言及しません。

 

矩形

 直線を引けたら次は矩形です。箱です。英語ではrectですね。rectangular:直方形・長方形から来てます。プログラム言語界隈では、rectはよく使われる省略記法です。領域とかのニュアンスも持ちます。rectangleが矩形って意味です。


 矩形の左下の起点のx座標 y座標の準で最初の2つのオペランドで指定します。次の二つで、x方向の長さ、y方向の長さを指定します。右上の座標ではないことに注意が必要といったところでしょうか。右上の座標を指定するよりは矩形の大きさがわかりやすいので良いのかなと思ったり、双方にメリットデメリットはありそうです。いずれにしても、場合によっては図形の大きさについては計算が必要になるのでしょう。座標x = 100, y =100を左下起点とする横、縦の長さ50, 50の大きさの塗りだけの矩形、座標x = 200, y =100を左下起点とする横、縦の長さ50, 50の大きさの塗りと線の矩形、座標x = 300, y =100を左下起点とする横、縦の長さ50, 50の大きさの線のみの矩形の例は以下のとおりです。


0.5 g % 塗りを灰色にする。仕組みは後で説明
100 100 50 50 re f  % 塗りのみ
200 100 50 50 re b % 塗りと線の両方
300 100 50 50 re S % 線のみ

 と、上記のようになります。

 

矩形

 

曲線

 ベジェ曲線を使って、曲がった線を描くことができます。ベクターグラフィックを扱う場合は、ベジェ曲線をよく使います。直線の始点と終点にそれぞれ曲げる量を決定する座標を追加するようなイメージです。始点用の曲げるための座標と終点用の曲げるための座標です。ひとつのベジェ曲線ではせいぜいS字程度くらいに曲がるものしか描けません。何回もうねうね曲がる曲線はベジェ曲線を細かく繋げることで複雑な線を描くことができます。初心者から中級者とって、この曲げる量を決定する座標の配置は、描きたい曲線に適した座標を求めるのは、かなり難しいです。アプリケーションによっては、画面上で描いたり、なぞったりした、曲線に対して最適なベジェ曲線を自動で算出してくれるものもありますので、こういったアプリケーションで慣れるとよいと思います。自動算出してくれるようなアプリでも、算出された後から、編集という行為によって、始点用の曲げる量を決定する座標は始点とつながった表示をしてくれるので、座標を動かして、曲がり方を調整できるようになってます。終点も同じです。後から制御点を微調整できるものがあるということです。始点や終点につながった曲げる量を決める座標のつながっている部分と座標をハンドルと呼びます。曲げる量の座標は制御点ともいいます。ちなみにPDFでは円を描画するオペレータがないのでベジェ曲線で円を描きます。


 オペランドに始点側の曲げ量を決めるX座標 そのY座標 終点側の曲げ量を決めるX座標 そのY座標 終点のX座標 Y座標の6個の数値オペランドとオペレータ c を使います。c はcurveの c だと思われます。重複をさけて、オペレータの当て字がうまいこといっているのは関心させられます。アルファベット52文字は優秀です。


0.5 g % 塗りを灰色にする。仕組みは後で説明
100 200 m
100 252 148 300 200 300 c S 

%ハンドル(制御点)の描画
2 w
100 200 m
100 247 l S
 95 247 10 10 re S

200 300 m
152 300 l S
143 295 10 10 re S

 と、上記のようになります。

 

ベジェ曲線

 

平行移動・拡大縮小・回転

 描画の座標空間の取り扱いを変更することで、描かれるものの、平行移動や拡大・縮小や回転という操作に似たようなことが出来ます。


 え?何言ってんの?コイツ。取り扱いを変更?座標空間?


 自分もそう思ってたときがありました。ペンの世界が変わるみたいな感じです。ペンを使う前に世界を変えるので、描き終わったものを動かすわけではないこと気がつければ理解しやすいと思います。よく使うグラフィックアプリケーションの大抵は描いてから、全体を選択して、回転や拡大の変形操作を行います。描く前に変換するやり方では、難しいですが、PDFのソースに記述する場合は見ながら描くのではなく、予想して描くわけですから、先に指定することのほうが助かるという訳ですね。


 原点を変換するときは cm オペレータを使って 1 0 0 1 dx(xの原点移動量) dy(xの原点移動量) cm のように指定します。このときの1 0 0 1 は座標変換行列の要素になっていて、3Dの座標変換なんかでも使われるものです。行列は座標を変換するのに必要な4つの成分になっています。このことは、行列 座標変換 とかの検索ワードで出てくる文献を読まないと深い理解は得られないです。1 0 0 1 の意味するところは1倍という意味で、なにも変換に影響しない行列です単位行列とも呼ばれています。\( x_{trans} \) を変換後の X 座標、\( x_{orgin} \) を変換前の X 座標、\( X_{Mag} \)を x 軸方向の拡大縮小倍率で Mag は Magnification 倍率の意味のように表すとすると y についても同様の扱いとして、


 ■拡大縮小 $$ \begin{pmatrix} x_{orgin} & y_{orgin} \end{pmatrix}  \begin{pmatrix} X_{Mag} & 0 \\ 0 & Y_{Mag} \end{pmatrix} = \begin{pmatrix} x_{trans} & y_{trans} \end{pmatrix}  $$

 ■2行2列 単位行列

$$\begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix}$$

 ■回転

$$ \begin{pmatrix} x_{orgin} & y_{orgin} \end{pmatrix}  \begin{pmatrix} \cos \theta & \sin \theta\\ -\sin \theta & \cos \theta \end{pmatrix} = \begin{pmatrix} x_{trans} & y_{trans} \end{pmatrix}  $$


 上記のように表せます。これを具体的な四則演算で書き直すと行列を使えば、回転の式の場合はいかのような計算になります。この演算方法は行列意味を検索するなりして学習する必要がありますが、このような法則で演算することになっています。


$$ x_{trans} = x_{orgin} \times \cos \theta + y_{orgin} \times (-\sin \theta) $$

$$ y_{trans} = x_{orgin} \times \sin \theta + y_{orgin} \times \cos \theta $$

 このような計算の仕組みのおかげで、2行2列の行列が変換行列として機能することが、いろんなグラフの座標を想定して試算することで確かめることができます。


 $ \theta $ が 0 のときはゼロ度の回転を意味していて、結局回転しないわけですが、回転の変換行列は左上が $ \cos \theta = \cos 0 = 1 $ で1になります。右上は$ \sin \theta = \sin 0 = 0 $ で 0 です。左下は、同様にして 0、右下は 1 になります。拡大が1倍のときの形です。左下と右上が 0 なら、左上の値が x 倍、右下の値が y 倍の変換に相当することも確かめられます。この cm オペレータはそのような行列値の指定と、原点の移動を示すオペランド値で機能していることがわかると思います。このようにコンピュータグラフィックにおける数値指定によって変換がされたりする仕組みを提供するオペレータのような役割をするものがある場合、このような科学技術演算が駆使されていて、そこの深い理由が存在しているということを把握できれば十分だと思います。すべてを理解するのは面倒です。


 ひとつひとつの意味を遡って理解しながら学習するのも有意義ですが、あまり考えているとよくそんなことを思いついたなということばかりですし、以降の記事ではあまり言及しません。わけのわからない指定方法だけどなんか複雑な事やってくれてるんだなって思った方が楽です。そして必要最低限のパラメータとして必須の構造なんだという複雑さに対する諦めを持つことができます。PDFを少し解剖しただけでもこのような記事がかけるほど複雑な技術になっているという深さ。コンピュータの演算技術は深い。


 以下は原点を x と y ともに100 左上方向に移動して、拡大率を 2 倍にする例です。


 例

1 0 0 1 100 100 cm
2 0 0 2 0 0 cm


 と、上記のようになります。前の例でしめしたような曲線についても、もともとの描画をシフトして、そのまま2倍にした場合にすると以下のようになるでしょう。前の例で使ったいろいろな座標を2倍にした状態で計算しなくて済みます。平行移動は回転の原点を移動することに関係することも注意してください。また拡大・縮小、回転、平行移動の順番を変更すると最終結果は変わりますので、その点にも注意が必要です。グラフィック業界では、位置調整の感覚がわかりやすいという意味で、平行移動→回転→拡大・縮小の順で動かすことを推奨していて、元の位置に戻す操作を入れる場合は、平行移動→回転→拡大・縮小→(回転戻し or 平行移動戻し or 回転戻し→平行移動戻し)のように、操作した順を折り返すように戻すといいでしょう。

 

座標変換

 

線の色・塗りの色・グラデーション・透明

 色を自由に変更するには、カラー指定の方法をペンに伝えたり、具体的に色を設定したりします。グラデーションの場合もグラデーションのタイプとグラデーションの中心座標や繰り返しパターンといったものを指定します。


 色の指定方法にはRGB系やCMYKや白黒 白黒階調指定あり、といった指定方法があります。まずはこの分類でどういう色空間を扱うかを指定することから始まります。オペランドとオペレータには以下のようなものがあります。


キー 指定する値の内容
/DeviceGray オペレータ 線ストロークはCS、塗りはcs指定します。
/DeviceRGB オペレータ 線ストロークはCS、塗りはcs指定します。
/DeviceCMYK オペレータ 線ストロークはCS、塗りはcs指定します。


 白黒カラースペースには/DeviceGray キーを使います。 ストリーム部で以下のようなキー定義をつかうことで 0 に相当する黒色に設定ができます。0 ~ 1の小数点表記を含めた表記で黒から白へ変化していく階調表現のひとつの色を指定します。


 例

/DeviceGray cs
0 sc

/DeviceGray CS
0 SC

 上記の2行の記述と以下の記述は同等です。2行をまとめることができます。

0 g

0 G


 光の三原色を使ったRGBカラースペースには/DeviceRGB キーを使います。 ストリーム部で以下のようなキー定義をつかうことで RGB = 1 1 0 に相当する紫色に設定ができます。0 ~ 1の小数点表記を含めた表記で階調表現のひとつの色を指定します。0x00 = 0 ~ 0xff = 255 の 256 段階段階で表現するのも一般的ですが、ここでは0~1の割合で表現します。0xが先頭につく値は16進数表記を意味しています。コンピュータにおける16進数表記の意義については、別の部分で理解して下さい。

 例

/DeviceRGB cs
1 1 0 sc

/DeviceRGB CS
1 1 0 SC

 上記の2行の記述と以下の記述は同等です。2行をまとめることができます。

1 1 0 rg

1 1 0 RG

   印刷業界でよく使われるCMYKカラースペースには/DeviceCMYK キーを使います。 C=シアン(水色っぽい)、M=マゼンダ(紫色っぽい)、Y=イエロー、K=ブラックの分量で設定するカラーです。ストリーム部で以下のようなキー定義をつかうことで CMYK = 1 1 0 0 に相当する青色に設定ができます。0 ~ 1の小数点表記を含めた表記で階調表現のひとつの色を指定します。

 例

/DeviceCMYK cs
1 1 0 0 sc

/DeviceCMYK CS
1 1 0 0 SC

 上記の2行の記述と以下の記述は同等です。2行をまとめることができます。

1 1 0 0 k

1 1 0 0 K


 

ペン状態のスタックとリストア

 グラフィックを描くときの線の太さや線のタイプ。塗りのパターン。たくさんの事をペン状態として設定しないといけないのですが、このペンの状態を記憶部分にスタックさせる仕組みがあります。この記憶は入れ子・ネストすることができるため、ペンの設定を繰り返し使う方法が一定の順序になる場合には便利です。


 ペン状態 A を使った後から B に移るときに A をスタック(記憶領域に挿入)するとします。そして Bもスタックしてから C へ移動して、C を使ったあとに C の状態から抜けるとBに戻ります。B からまた違うペンの D に移動して、D を使った後に状態から抜けるとまた B の状態になります。さらに B の状態から抜けると、最初にスタックしていた A の状態になります。新しい状態を記憶する宣言のオペレータ Q とひとつ前に戻る宣言のオペレータ q を使って操るというイメージです。


0.5 G
100 100 200 200 re S
q
0 G
110 110 180 180 re S
Q
120 120 160 160 re S

 と、上記のようになります。上記の例では最初の 0.5 G(グレーのストローク)という状態をqでスタックして、覚えさせています。次に新しい状態の 0 G (黒色のストローク)という状態を設定して描画した後のQで状態をレストアします。これでスタックしておいたグレーのストロークの状態に戻ります。このような記憶がポップスタックの仕組み。お盆をある専用の保管場所において、どんどんつみあげるように記憶させ、保管場所からお盆を一枚づづ取り出すようにして、読み起こす仕組みです。目的の状態に戻すためにお盆を沢山取り外さないといけないこともあるし、お盆をどんどん取り出すと記録しておいたものはどんどん消えていきます。順番にうまくいったり来たりして使うという工夫を忘れてはいけないです。簡易的なグラフィックス状態の記憶ですね。

 


 

PDF 内部構造に戻る。