Pycairo を使ってみる(1)
(有)シンビー 2022.3.1
https://github.com/Eridanus-Hawaii/Drawing-with-Python
モチベーション:動画にText 画像を入れたい
こういうのを
作りたい
• 最初は Fireworks
というアプリで
作ったが、、、
• 文言を頻繁に変
えそうだ、、、
であれば
• プログラミング
Pycairo とはなんぞや?
• Pycairo is a Python module providing bindings for the cairo
graphics library. It depends on cairo >= 1.15.10 and works
with Python 3.7+. Pycairo, including this documentation, is
licensed under the LGPL-2.1-only OR MPL-1.1.
あ~そうかそうか Pycairo の前に Cario があるのか、、、
https://pycairo.readthedocs.io/en/latest/
Cairo とはなんぞや?
• Cairo is a 2D graphics library with support for multiple output
devices. Currently supported output targets include the X
Window System (via both Xlib and XCB), Quartz, Win32,
image buffers, PostScript, PDF, and SVG file output.
Experimental backends include OpenGL, BeOS, OS/2, and
DirectFB.
• まぁ平たく言うと 2D のグラフィック・ライブラリだ。
お絵描きが出来る。
https://cairographics.org/
Pycairo ことはじめ
• install
• pip3 install pycairo
• C のライブラリを必要とするから wheel とかがその前に必要かも。
• pip3 install wheel
• 使う時
• import cairo
Pycairo さっそく使ってみる
import cairo
#----------------------------------------------------------------
if __name__ == '__main__':
image_file = 't1.png'
width = 200
height = 200
line_width = 10
surface = cairo.ImageSurface(cairo.FORMAT_RGB24, width, height)
context = cairo.Context(surface)
context.set_line_width(line_width)
context.set_source_rgb(0xff, 0x99, 0)
context.rectangle(10, 10, 100, 100)
context.stroke()
surface.write_to_png(image_file)
1. Surface を作る
2. Context を作る
3. 線の太さを設定
4. 色を設定
5. 四角の指定
6. 描画
7. PNG として書き出し
難しかったけど出来た
• img2sixel で確認
あれ?思ってた色と違う
色指定は小数だった。
• context.set_source_rgb(0xff/0xff, 0x99/0xff, 0)
それではとテキストを追加
• できた!
context.set_font_size(font_size)
context.move_to(15, 35)
context.show_text("こんにちは")
context.stroke()
1. フォントの大きさを設定
2. 場所を指定
3. 文字列を指定
4. 描画
改良点を検討
• 真四角だ!!
• ということで、まずは丸い縁を描画することを目指す。
円とラインを組み合わせれば出来そうだ
• ということで検索
pycairoで角丸四角を描画する
• コードを拝借 x = 40
y = 50
w = 120
h = 100
r = 20
ctx.move_to(x+r, y)
ctx.line_to(x+w-r-1, y)
ctx.arc(x+w-r-1, y+r, r, -0.5*math.pi, 0)
ctx.line_to(x+w-1, y+h-r-1)
ctx.arc(x+w-r-1, y+h-r-1, r, 0, 0.5*math.pi)
ctx.line_to(x+r, y+h-1)
ctx.arc(x+r, y+h-r-1, r, 0.5*math.pi, math.pi)
ctx.line_to(x, y+r)
ctx.arc(x+r, y+r, r, math.pi, 1.5*math.pi)
ctx.close_path()
ctx.set_source_rgb(0xbb/float(0xff), 0xdd/float(0xff), 0xff/float(0xff))
ctx.fill_preserve()
ctx.set_source_rgb(0x11/float(0xff), 0x33/float(0xff), 0x77/float(0xff))
ctx.stroke()
エラー
• math がたりない
• import math が必要
• 修正して再実行
• できた!
File "t3.py", line 22, in <module>
context.arc(x+w-r-1, y+r, r, -0.5*math.pi, 0)
NameError: name 'math' is not defined
色を自分の指定したい色にする
context.set_source_rgb(0xff/float(0xff), 0xff/float(0xff), 0xff/float(0xff))
context.fill_preserve()
context.set_source_rgb(0xff/float(0xff), 0x99/float(0xff), 0x00/float(0xff))
context.stroke()
枠の中の色(#FFFFFF = WHITE)
枠の色(#FF9900 = ORANGE)
色について(Web などでよく使う形式)
• #FF FF FF
赤 0~255(16進で 0x00~0xFF)
緑 0~255(16進で 0x00~0xFF)
青 0~255(16進で 0x00~0xFF)
R G B
色について(今回の cairo)
• 0~1.0 0~1.0 0~1.0
赤 0~1.0
緑 0~1.0
青 0~1.0
R G B
0xFF = 1.0
0x99 = 0.6
ここでは便宜上 1.0 という数字を使っているが、有効桁は 32bit 浮動小数点数だから 23bit
線の太さを変える
line_width = 20
context.set_line_width(line_width)
大きさを変える
• surface を大きくする
• w を 320 にする
width = 400
height = 200
surface = cairo.ImageSurface(cairo.FORMAT_RGB24, width, height)
x = 40
y = 50
w = 320
h = 100
r = 20
テキストも変える
font_size = 30
context.set_source_rgb(0, 0, 0)
context.set_font_size(font_size)
context.move_to(55, 95)
context.show_text("こんにちは")
context.stroke()
色を黒に
サイズを大きく
場所を適当に
おまけ:ファイル名の自動生成
image_file = 'image_' + sys.argv[0].replace('.py',
'.png’)
...
...
...
surface.write_to_png(image_file)
sys.argv[0] には t5.py などの実行される Python のファイル名が入る
そこで、その前に ‘image_’ を足す
.py を .png へ replace する。
すると、 image_t5.png という文字列(string) が出来る。
結果として
$ python3 t5.py
と実行すると
image_t5.png
が出来る
ポイント
• 困った!!
• もっと改善できないか?
気付いた後は
検索するなり
人に聞くなりすればいい
をつくる
• 〇(arc) を描いて
• “真ん中”に 1 を書く
をつくる:番外編
• 参考までに書くと 〇に1は環境依存文字です。
(つまり、あまり使わない方がよい)
• 画像にする分には環境関係ない! By 農林水産省
別に農林水産省が定義している言葉
ではないが、なぜか、検索でヒット
したので載せておく
〇を描く
context.set_source_rgb(0, 0, 1)
r = 30
line_width = 3
context.set_line_width(line_width)
context.arc(250, 88, r, 0, math.pi * 2)
context.stroke()
色を青に
半径が 30
線の太さが3
円を指定
描画指示
context.arc を検索する
arc のドキュメント(ウェブサイト)
https://pycairo.readthedocs.io が本家。そこを参照する
X の位置
Y の位置
半径
スタートの角度
エンドの角度
角度は radians
1
2
π
π
3
2
π
0 または 2π
本当は、、、無限にパターンがある
… −
3
2
π ,
1
2
π,
5
2
π,
9
2
π …
… − 2π , 0,2 π, 4 π …
… −
1
2
π ,
3
2
π,
7
2
π,
11
2
π …
… − π , π, 3 π, 5π …
プログラムではどうなっていたか
context.arc(250, 88, r, 0, math.pi * 2)
スタートが0 エンドが2π
0 から
2π まで
0から 2πまで。すなわち360度
をつくる
• 〇(arc) を描いて → 達成
• “真ん中”に 1 を書く
context.set_source_rgb(0, 0, 1)
r = 30
line_width = 3
context.set_line_width(line_width)
context.arc(250, 88, r, 0, math.pi *
2)
context.stroke()
座標 x, y を同じにして1を書く
font_size = 30
context.set_source_rgb(0, 0, 1)
context.set_font_size(font_size)
context.move_to(250, 88)
context.show_text('1')
context.stroke()
context.arc(250, 88, r, 0, math.pi *
2)
ずれてます!!
スケールド・フォントとメトリック
• スケーラブル・フォント(スケールド・フォント)
• 拡大・縮小しても形が崩れない
• フォント・メトリック
• 印刷用語
https://www.jfpi.or.jp/webyogo/index.php?term=3498
ビットマップフォントVSスケーラブルフォント
拡大すると
なめらかでなくなる
拡大・縮小しても
なめらか
何故か、cairo の API は scaled_font という名称
wikipedia から
フォント・メトリック
• アルファベット・フォント文化の集大成!!
https://docs.microsoft.com/ja-
jp/dotnet/desktop/winforms/advanced/how-to-obtain-
font-metrics?view=netframeworkdesktop-4.8
フォント・メトリック
• Arial
• Old English Text MT(読めん!!)
• 等幅フォント(Courier New)
読みやすいように i とか
は左右の空白が狭い
プログラムに便利なように
幅が一定
等幅vsプロポーショナル
等幅だと文字の位置が揃っている
=
編集に便利
プロポーショナルだと文字の位置はばらばら
=
編集に不便(見た目はきれい)
cairo のフォント情報
extents = sf.text_extents('1’)
print(extents)
python3 font-metric.py
cairo.TextExtents(
x_bearing=3.0,
y_bearing=-22.0,
width=9.0,
height=22.0,
x_advance=17.0,
y_advance=0.0)
API text_extents を使う
TextExtents という
オブジェクトで管理されている
TextExtents
TextExtens を書き込んだ画像を作る
high を拡大
1) スタート
座標
2) スタート座標に
x_bearing, y_bearing を
足した座標
3) width
4) height
5) x_advance 6) y_advance はゼロ
1 を拡大
中心を求める
中心はここ
〇の中心に1を入れる
x = 250
y = 88
str = '1'
font_size = 30
sf = scaled_font.get_scaled_font('Arial', font_size )
extents = sf.text_extents(str)
context.set_source_rgb(0, 0, 1)
context.set_font_size(font_size)
context.move_to(x, y)
context.rel_move_to(-extents.width/2, -extents.height/2)
context.rel_move_to(-extents.x_bearing, -extents.y_bearing)
context.show_text(str)
context.stroke()
あとは適当に位置と色を調整
第一部 完

Pycairo を使ってみる その1