Logo address

Python SVG Library

2012/10/29
2012/11/15
2019/02/13
2023/06/22 library の更新

SVG がそろそろ食べ頃になったようだ。まだ幾分酸っぱいが...

こう感じたのは、ブラウザが SVG の PDF 出力に対応しているのを発見した時である。Mac 環境では、Safari、 Chrome、Firefox が共にスケーラブルな PDF を出力する。Opera は残念ながら、PDF の中身はビットマップである。Windows 環境では Chrome が PDF 出力に対応し、内容はスケーラブルになっている。

Python/Tk の代わりに、SVG が使えるかも知れない...
このページは、Python 用の SVG ライブラリの開発の途中経過を示している。
調べてみると、結構多くの Python 用の SVG library が存在するようであるが... 僕の勉強を兼ねて...

2013/10/22 追記

いろいろ他の仕事があって、一年もの間、放置されていたが、そろそろライブラリを公開することとする。まだ十分に整理されていないので、まだ変化する部分はあると思うが...

Python SVG Library (lib.tar) base.py svg.py table.py
Python3 SVG Library (lib3.tar) base.py svg3.py table3.py
svg.py および svg3.py にバグが含まれていました。更新しました(2023-06-22)

Python の import 文で取り込めるには、PYTHONPATH にライブラリの置き場所を含める必要がある。

2019/02/13 追記

プログラムが苦手な人が世の中には多いらしい。そのためにマウスを使った会話的処理が好まれる傾向にある。エクセルは使えても R は知らないとか、...
図形を描くにも手作業でないと...
会話的作業は初心者には受けるのであろうが、ちゃんとした仕事をしたいなら、プログラムを使うべきであろう。でないと正確な図を描けない。

Examples

2012/11/19

A basic Python SVG Library introduced here is written by me, and is not released yet.
But here are some examples that use the library.
This library is written in Python that should work on version 2.x.
(Python 3.x will be supported in near future. Change needs only a few lines)

Current library version is 0.3, and the code size is only 820 lines including many comments.
The library needs nothing except basic Python modules.

The Specification

2016/12/01

The library is written in the year 2012 but "under-construction" icon have been put to this page since that year. I think the specification will not be changed. So, the icon is removed.

Basic Syntax

POINTS is typically

X1,Y1,X2,Y2
but in general
POINT,POINT,...,POINT
or
[POINT,POINT,...,POINT]

POINT is one of

X,Y
(X,Y)
[x,y]

OPTS is

OPT,OPT,...,OPT
OPT is
ATTR=VALUE
where ATTR is an attribute and VALUE is the value.
All SVG attribute should have an effect. The value is defined in a SVG document.

look https://www.w3.org/TR/SVG11/struct.html for details.

Canvas( ) and close( )

import svg
c = svg.Canvas(...)
.....
.....
c.close()

Canvas( ) method

The usage is

svg.Canvas(X1,Y1,X2,Y2, OPTS)
where X1,Y1,X2,Y2 are the coordinate of viewBox, and OPTS is a list of attribute value pairs. Note that the Canvas coordinate is same as that of SVG, namely, top left corner is the origin.

The special and useful attribute is map that is given as

map=(MX1,MY1,MX2,MY2)
where the set of four coordinates MX1,MY1,MX2,MY2 defines a new coordinate system that allows users to use new coordinate system instead of original one.

Here is an example for the map option.

#!/usr/bin/env python3
# need: ebb lqr2.pdf, for latex
import svg3 as svg
mfont="MathJax_Math;size:8;style:normal;weight:normal"
tfont="Times;size:8;style:normal;weight:normal"
p,q=17,13
mx,my,sc = p,q,10
mg = 0.5 # margin
c = svg.Canvas(0,0,(1+2*mg+mx)*sc,(1+2*mg+my)*sc,map=(-1-mg,my+mg,mx+mg,-1-mg))
for k in range(0,p+1):
  c.line(k,0,k,my,stroke="black;width:0.5")
for k in range(0,q+1):
  c.line(0,k,mx,k,stroke="black;width:0.5")
c.text(0.7*mx,0,text="x",font=mfont,anchor="N",pad=0.3)
c.text(0,0.7*my,text="y",font=mfont,anchor="E",pad=0.3)
c.text(0,0,text="O",font=tfont,anchor="NE",pad=0.1)
c.text(mx/2,my/2,text="C",font=tfont,anchor="S",pad=0.3)
c.text(mx/2,0,text="M",font=tfont,anchor="N",pad=0.3)
c.text(0,my/2,text="N",font=tfont,anchor="E",pad=0.3)
c.line(0,0,mx,my,stroke="black;width:0.5;dasharray:3 1 1 1")
c.line(0,my/2,mx/2,my/2,stroke="black;width:0.5;dasharray:1 1")
c.line(mx/2,0,mx/2,my/2,stroke="black;width:0.5;dasharray:1 1")
c.circle(mx/2,my/2,r=0.1,fill="black")
c.close()

Note that this program is written in Python3

In this program, we have a line:

c = svg.Canvas(0,0,(1+2*mg+mx)*sc,(1+2*mg+my)*sc,map=(-1-mg,my+mg,mx+mg,-1-mg))
By mapping:
map=(-1-mg,my+mg,mx+mg,-1-mg)
a point located at (-1-mg,my+mg) is mapped to the viewport (0,0) and a point located at (mx+mg,-1-mg) is mapped to the viewport ((1+2*mg+mx)*sc,(1+2*mg+my)*sc).

This mapping is used for having margin space and labeling space.

Other attribute value pairs give default values to the canvas. These values are strings of standard string values of SVG attributes. For example

font="family:Helvetica;size:16"
stroke="red;width:3"
All SVG attributes should have effect.

Some attributes are special to the SVG library.
For example a simpler form for font may be written as:

font="Helvetica;size:16"
Values of all other attributes peculiar to the library are not string. For example, value of map is a tuple, not a string.

close( ) method

grid( ) method

Draw grid for the canvas.
You will find the example in other pages, for example,

The code in svg library listed below shows the usage for grid( ):

    """=== usage example
	c.grid(1,1,range=(-1.3,1.3,1.3,-1.3),stroke="cyan",
	frame="green;width:5",axis="black;width:2",
	font="Helvetica;size:16",label=("X","Y"))
      # note that font is for the scale numbers, not for the axis label
      # it's unable to make fine tuning to label. if you need, do manually!
      # making too much is worse than do nothing.
    ==="""

Example

#encoding: utf-8
import svg
c = svg.Canvas(0,0,800,600,map=(-400,-300,400,300))
h = c.clip(id="C",clip="rule:nonezero")
h.circle(0,0,r=200)
c.image(-400,-300,400,300,clip="path:url(#C)",href="bird.jpg")
c.close()

You need a image file "bird.jpg" in the current directory, or change the name to something else you have.

Group Families

In the followings, c is a canvas instance.
The group family creates a new canvas instance.

group( )

g = c.group(OPTS)

define( )

g = c.define(OPTS)

mask( )

g = c.mask(OPTS)

clip( )

g = c.clip(OPTS)

Drawing Elements

text( )

usually

c.text(POINT,text=TEXT,OPTS)
or
c.text(text=TEXT,ref=REF,OPTS)
for a text along a path

line( )

line() draws a line or polyline. the usage is,

c.line(POINT,POINT,OPTS)
for a line.

For a polyline,

c.line(POINTS,OPTS)

OPTS for line() method is

linecap:

stroke="linecap:SHAPE"
where SHAPE is one of
butt, round, square

linejoin:

stroke="linejoin:SHAPE"
where SHAPE is
miter, round, bevel

rect( )

c.rect(POINT,POINT,OPTS)

polygon( )

c.polygon(POINTS,OPTS)

circle( )

for circle of radius R

c.circle(POINT,r=R,OPTS)
and for ellipse
c.circle(POINT,r=(RX,RY),OPTS)
where RX and RY are the two radius (for X-axis and Y-axis respectively).

arc( )

c.arc(POINT,start=START,extent=EXTENT,OPTS)

image( )

c.image(POINT,POINT,OPTS)

path( )

c.path(PATH,OPTS)
where PATH is a string of the format of SVG path tag:
<path d=PATH>

Symbol Families

symbol( )

pattern( )

marker( )

Filter

filter( )

Attribute

Conventional aliases of SVG atributes

library		SVG
font  		font-family
href  		xlink:href
and
ref="FOO"	xlink:href="#FOO"

Unsupported SVG Specification

Some of SVG Specifications are not supported yet.

Unsupported attribute

xml:base
xml:lang
xml:space

Multiple svg tags

Internals

All group families have an output buffer.
Contents in the buffer (including all it's children's buffer) are flushed to the parent output buffer by 'end()' method.

The 'close()' method is a special 'end()' method, that can act only to the root group (i.e., Canvas).

SVG と TeX

TeXの数式フォントを含む SVG 画像

次の SVG 画像について考えてみる。

あなたのブラウザは SVG をサポートしていません。最新のブラウザをお使いください。
図1: TeXの数式フォントを含む SVG 画像

図1は、uniform B-spline 曲線がコントロールポイント(赤丸で示されている)によってどのように決まっているのかを示している。
この例に見られるように、図を書く時には、点の説明等に数式の変数名が要求される場合がある。この図が LaTeX の中で使われるならば、LaTeX のスタイルに合わせる必要があろう。

変数名程度であれば、簡単にやっていける。このケースでは

def tex2svg(s):
  t = s.split("_")
  return t[0] + '<tspan dy="0.2em" font-size="80%">'+ t[1] + "</tspan>"

で TeX の下付き文字を SVG 形式に変換する関数を(極めてなまくらなやり方で)定義しておいて

label=[]
for n in range(0,len(P)):
  label.append(tex2svg("P_%s"%n))
c.mark(P,ref=sym,label=label,pad=0.3,anchor="SW",decoration=True)
label=[]
for n in range(0,len(Q)):
  label.append(tex2svg("Q_%s"%n))
c.mark(Q,ref=sym2,label=label,pad=0.3,anchor="NE",decoration=True)
とやっているだけである。

もちろん、tex2svg は汎用性を持っていない。このケースに即した極めて限定的、便宜的なものである。しかし、こうした限定性を超えた時の代償は極めて大きい。 TeX の数式の解析ルーチンの他に、文字毎に高さや横幅の詳細な情報が必要になろう。実際のニーズの程度に比べると、払う代償は大きすぎる。

PDF Cnverter

2013/01/13

SVG ファイルから PDF ファイルを生成するのに使えそうなツールについて整理してみた。

以下の情報は2013年1月での現状に基づいている。

ブラウザ

一番簡単な方法である。そして、この方法がベストだと思う。
ブラウザの印刷メニューの中の PDF 出力を使う。
通常、ブラウザはヘッダやフッタもプリントするので、これを外す。次の図2はSafari の例である。

図2: Safari のプリントオプション

欠点は、コマンドではやれない事である。しかし、大量の SVG ファイルを処理するニーズが存在しない限り、この事は問題にはならない。
Latex などで使うときにはトリミングが必要になる。(「LaTeX への取り込み」を見よ)

Python svglib

PDF 変換にコマンドツールを使えるらしいが、"ReportLab package" が要求される。この方法は、大量の SVG をリアルタイムで PDF に変換するサーバ向けの方法である。

[1] svglib 0.6.3
http://pypi.python.org/pypi/svglib/

[2] ReportLab
http://www.reportlab.com/

Python svgplotlib

Python でチャートを描く事を目標としているライブラリである注1
このライブラリの特徴は、TeX 数式のサポートが行われている事であろう。TeX のパーサを持っている。

注1: この点では、僕の方向性 -- SVG を Python から使いやすくする -- と異なっている。

[3] svgplotlib 0.2
http://pypi.python.org/pypi/svgplotlib/

Adobe Illustrator

Adobe Illustrator は MathJax_Math をインストールしてあっても、(最初にロードした時点では) Ariel フォントを使ってしまう。手作業でフォントを MathJax_Math に切り替える必要がある。

Drawing appls in iPad or Mac

Inkscape

Inkscape(0.48.2)は MathJax_Math を認識してはいるが、文字の出力位置の微調整を行っている dx, dy を認識していない。そのために文字位置がずれる。その様子を図3に示す。正しくは図1ようにならなくてはならない。

図3: Inkscape は文字位置の調整にバグがある

Inkscape の PDF 保存は、余計な空白が入らないので使いやすい。それだけに、残念である。文字位置の決定は dx, dy 無しでもやれるが、Inkscape のためにわざわざプログラムを変更する気にはなれない。

OSX 10.8 (Mountain Lion) では Inkscape が動く為には XQuartz が必要である([4,5])。

[4] About X11 and OS X Mountain Lion
http://support.apple.com/kb/HT5293

[5] XQuartz
http://xquartz.macosforge.org/landing/

Inkscape: LaTex with PSTrick macro

Inkscape(version 0.48 以上) の出力オプションに「LaTex with PSTrick macro」がある。このオプションは、LaTeX の中に、Inkscape で作った PDF ファイルを取り込む時に、TeX の数式を PDF 画像に重ねる事を意図している。Inkscape の中で、LaTeX で表現したい部分を $\frac23$ のように $ で括ると、出力される PDF ファイルからはその部分は除去され、代わりに、式を表示するだけを目的とした LaTeX のテキストが併せて生成される。つまり数式を含まない PDF ファイルと、数式だけを含む小さな LaTeX ファイルの共同によって、LaTeX の中に数式を含む図形を実現しようとする。

この方法は、数式表現に LaTeX そのものを使うので極めて強力なのであるが、重大な欠陥を持っている。2つのファイルに分かれている為に、図形を取り込む LaTeX 文書の中で、図形のサイズ調整がままならない。

LaTeX への取り込み

SVG ファイルは直接 LaTeX には取り込めない。ブラウザを使って PDF に変換すると余計な空白が発生する。そのためにトリミングが必要になる。この例では次のようにトリミングした。

\begin{figure}[h]
\begin{center}
\scalebox{0.5}{\includegraphics[trim=4cm 5cm 3cm 10cm, clip=true]{bspline1c.pdf}}
\end{center}
\end{figure}

Other SVG library for Python

Blogs on Python SVG Library