OSDN Git Service

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