OSDN Git Service

change history of the search text field a little
[sawarabi-fonts/sawarabi-fonts.git] / script / export_as_svg.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # Copyright (C) 2009, mshio <mshio@users.sourceforge.jp>
5 #
6 # This program is free software: you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19 import fontforge
20 import optparse
21 import os
22 import sys
23 import xml.parsers.expat
24
25 COPYRIGHT = 'Copyright: (c) 2008-2009 mshio <mshio@users.sourceforge.jp>'
26
27 class SvgCustomizer:
28   '''
29   FontForge が書き出した SVG ファイルの内容を書き換え、
30   ウェブブラウザで表示できるようにするためのクラスです。
31
32   インスタンスを生成した後、execute メソッドを呼び出してください。
33
34   This class is for converting SVG files that are generated by FontForge
35   to ones that can be displayed by a web browser.
36
37   After getting an instance, call its execute method.
38   '''
39
40   def __init__(self, fill_color, comment):
41     '''
42     コンストラクタ
43
44     引数:
45       fill_color -- SVG のパスを塗りつぶすための色。文字列
46       comment    -- コピーライトコメントを付けるかどうか。True/False
47
48     Constructor
49
50     Arguments:
51       fill_color -- color name for filling svg pathes.
52       comment    -- True if adding copyright comment.
53     '''
54     self.fill_color = fill_color
55     self.with_comment = comment
56
57   def execute(self, glyph, file):
58     '''
59     FontForge が書き出した SVG ファイルを解析し、目的の形式に書き換えます。
60
61     引数:
62       glyph -- FontForge のもつグリフオブジェクト
63       file  -- FontForge の書き出した SVG ファイルのファイル名。文字列
64
65     Parses the SVG file that is output by FontForge, and outputs a new 
66     SVG file that can be displayed by a web browser.
67
68     Arguments:
69       glyph -- glyph object
70       file  -- SVG file name
71     '''
72     self.setup(glyph)
73     try:
74       c = self.get_contents(file)
75       self.out = open(file, 'w')
76       self.parser.Parse(c, 1)
77     except:
78       print sys.exc_info()[0]
79     self.out.close()
80
81   def setup(self, glyph):
82     '''内部にもつ XML パーサーのセットアップを行います。'''
83     self.glyph = glyph
84     self.parser = xml.parsers.expat.ParserCreate()
85     self.parser.XmlDeclHandler = self.start_xml_declaration
86     self.parser.StartDoctypeDeclHandler = self.start_doctype_declaration
87     self.parser.StartElementHandler = self.start_element
88     self.parser.EndElementHandler = self.end_element
89
90   def get_contents(self, file):
91     '''指定したファイルの内容を読み込んで返します。'''
92     h = open(file, 'r')
93     c = h.read()
94     h.close()
95     return c
96
97   def start_xml_declaration(self, version, encoding, standalone):
98     '''XML 宣言部分のパースと書き出しを行います。'''
99     self.out.write('<?xml')
100     if version:
101       self.out.write(' version="%s"' % version)
102     if encoding:
103       self.out.write(' encoding="%s"' % encoding)
104     if standalone != -1:
105       val = 'yes' if standalone == 1 else 'no'
106       self.out.write(' standalone="%s"' % val)
107     self.out.write('?>')
108
109   def start_doctype_declaration(self, doctype, sys_id, pub_id, has_subset):
110     '''DOCTYPE 部分のパースと書き出しを行います。'''
111     self.out.write('<!DOCTYPE %s' % doctype)
112     if pub_id:
113       self.out.write(' PUBLIC "%s"' % pub_id)
114     if sys_id:
115       self.out.write(' "%s"' % sys_id)
116     self.out.write('>')
117
118   def start_element(self, name, attrs):
119     '''
120     各要素の開始部分を読み込み、その部分の書き出しを行います。
121     要素名が 'svg' の場合、xmlns 属性を付加し、さらに viewBox の値を変更し、
122     またさらにコピーライトの出力が指示されている場合は、svg 要素の後に
123     metadata 要素を追加し、そこにコピーライトを出力します。
124     要素名が 'path' の場合は、fill 属性の有無をチェックし、
125     あれば、その値を指定の値に変更します。
126     '''
127     self.out.write('<%s' % name)
128     is_svg = name == 'svg'
129     is_path = name == 'path'
130     if is_svg:
131       self.out.write(' xmlns="http://www.w3.org/2000/svg"')
132     for k in attrs.keys():
133       if is_svg and k == 'viewBox':
134         org = attrs[k].split()
135         w = self.glyph.width - int(org[0])
136         # here, using magic numbers at the values of top and height.
137         # if using this script for some other fonts,
138         # you might have to change these values.
139         val = '%s -100 %d 1200' % (org[0], w)
140         self.out.write(' %s="%s"' % (k, val))
141       elif is_path and k == 'fill':
142         self.out.write(' %s="%s"' % (k, self.fill_color))
143       else:
144         self.out.write(' %s="%s"' % (k, attrs[k]))
145     self.out.write('>')
146     # output copyright string
147     if is_svg and self.with_comment:
148       self.out.write('<metadata><![CDATA[ %s ]]></metadata>' % COPYRIGHT)
149
150   def end_element(self, name):
151     '''各要素の終了部分を読み込み、その部分の書き出しを行います。'''
152     self.out.write('</%s>' % name)
153
154 #
155 # functions
156 #
157 def make_svg(customizer, glyph, output_dir):
158   '''
159   SVG ファイルの生成と書き換えを行います。
160
161   まず FontForge の export 機能で SVG ファイルを生成し、
162   次いで SvgCustomizer でその内容を書き換えます。
163
164   引数:
165     customizer -- SvgCustomizer のインスタンス
166     glyph      -- 書き出す対象のグリフオブジェクト
167     output_dir -- 出力先のディレクトリ
168
169   Exports a SVG file from the specified glyph, and
170   outputs new one that can be displayed with a web browser.
171
172   Arguments:
173     customizer -- an instance of SvgCustomizer
174     glyph      -- target glyph object
175     output_dir -- directory for SVG files that will be outputed by this script
176   '''
177   path = '%s/%04x.svg' % (output_dir, glyph.unicode)
178   print path
179   glyph.export(path, 1)
180   customizer.execute(glyph, path)
181
182
183 if __name__ == '__main__':
184
185   class InvalidArgumentError(Exception):
186     '''コマンドライン引数が正しくない場合に生成される例外です。'''
187     def __init__(self, value):
188       self.value = value
189
190   def parse_args():
191     '''
192     コマンドライン引数の処理をします。
193     第一引数はフォントファイル、第二引数は保存先のディレクトリです。
194     オプションは下記のとおりです。
195
196      -f, --fill :
197             SVG のパスを塗りつぶす色を指定します。
198             省略すると、black が指定されたことになります。
199      -c, --copyright :
200             コピーライト(内容は固定)を含める場合に指定します。
201             コピーライトは、metadata のデータとして挿入されます。
202      -g, --glpyh :
203             フォントが持つ全グリフではなく、特定の文字のみの
204             SVG ファイルを出力したい場合に指定します。
205             指定は、16 進数の文字コード(Unicode)で行います。
206
207     Processes the arguments of command line.
208     The first of them is font file path, and the second of them is path of
209     directory in which the svg files will be stored.
210     The options are like this:
211
212      -f, --fill:
213             The color name with which fill the svg pathes.
214             Default is 'black.'
215      -c, --copyright:
216             When this option is specified, the copyright is written as 
217             'metadata' in each svg files.
218      -g, --glyph:
219             Character code (hex). When this option is specified, 
220             this script will output only a SVG file of the specified character.
221             When not, it will output ones of all glyphs.
222     '''
223     usage = 'usage: %prog [-f color] [-c] fontfile directory'
224     p = optparse.OptionParser(usage=usage)
225     p.add_option('-f', '--fill', dest='color', default='black',
226                  help='name of color that is used when filling the svg pathes')
227     p.add_option('-c', '--copyright', dest='copyright', 
228                  action='store_true', default=False,
229                  help="write mshio's copyright in each output files")
230     p.add_option('-g', '--glyph', dest='code', default=None,
231                  help='char code that wants to be output')
232     (opt, args) = p.parse_args()
233
234     if len(args) != 2:
235       p.error('incorrect number of arguments')
236       raise InvalidArgumentError
237
238     return (args[0], args[1], opt.color, opt.copyright, opt.code)
239
240
241   try:
242     (font_path, output_dir, color_name, comment, code) = parse_args()
243   except InvalidArgumentError:
244     sys.exit(1)
245
246   char_code = int(code, 16) if code else None
247
248   customizer = SvgCustomizer(color_name, comment)
249   font = fontforge.open(font_path)
250   if char_code:
251     make_svg(customizer, font[char_code], output_dir)
252   else:
253     for g in font:
254       if g[0] != '.':
255         glyph = font[g]
256         if glyph.unicode > 0:
257           make_svg(customizer, glyph, output_dir)