OSDN Git Service

新しい動画に利用されるコメントファイルに対応。
[pynv/pylib-nicovideo.git] / setup.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 #       Copyright © 2014,2016 dyknon
5 #
6 #       This file is part of Pylib-nicovideo.
7 #
8 #       Pylib-nicovideo is free software: you can redistribute it and/or modify
9 #       it under the terms of the GNU General Public License as published by
10 #       the Free Software Foundation, either version 3 of the License, or
11 #       (at your option) any later version.
12 #
13 #       This program is distributed in the hope that it will be useful,
14 #       but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #       GNU General Public License for more details.
17 #
18 #       You should have received a copy of the GNU General Public License
19 #       along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 import distutils.core
22 import distutils.cmd
23 import distutils.command.build
24 import struct
25 import sys
26 import os
27
28 class TtfError(Exception):
29         def __init__(self, val=""):
30                 self.value = val
31         def __str__(self):
32                 return repr(self.value)
33
34 class TtfReader:
35         def __init__(self, filehandle, offset=0):
36                 self.fh = filehandle
37                 self.offset = offset
38                 self.tables = {}
39         
40         def detect_type(self):
41                 self.fh.seek(self.offset, os.SEEK_SET)
42                 mn = self.fh.read(4)
43                 if mn == b"\x00\x01\x00\x00":
44                         return "ttf"
45                 elif mn == b"ttcf":
46                         if self.fh.read(4) == b"\x00\x01\x00\x00":
47                                 return "ttc"
48                 return None
49         
50         def read_ttc_head(self):
51                 self.fh.seek(self.offset, os.SEEK_SET)
52                 if self.fh.read(8) != b"ttcf\x00\x01\x00\x00":
53                         raise TtfError("TTCファイルヘッダ読み込みエラー")
54                 self.tttable = []
55                 numfn = struct.unpack(">L", self.fh.read(4))[0]
56                 for i in range(numfn):
57                         self.tttable.append(struct.unpack(">L", self.fh.read(4))[0])
58                 return self.tttable
59
60         def read_ttf_head(self):
61                 self.fh.seek(self.offset, os.SEEK_SET)
62                 if self.fh.read(4) != b"\x00\x01\x00\x00":
63                         raise TtfError("TTFファイルヘッダ読み込みエラー")
64                 (num_tables, search_range, entry_selector, range_shift) \
65                         = struct.unpack(">HHHH", self.fh.read(8))
66                 self.table_list = {}
67                 for i in range(num_tables):
68                         tag = self.fh.read(4).decode("ascii")
69                         (cksum, offset, length) = struct.unpack(">LLL", self.fh.read(12))
70                         self.table_list[tag] = {"s": cksum, "o": offset, "l": length}
71                 return self.table_list
72
73         def check_ttf_head(self):
74                 try:
75                         self.table_list
76                 except AttributeError:
77                         self.read_ttf_head()
78
79         def get_table_head(self):
80                 if not "head" in self.tables:
81                         self.read_table_head()
82                 return self.tables["head"]
83
84         def read_table_head(self):
85                 self.check_ttf_head()
86                 self.fh.seek(self.table_list["head"]["o"], os.SEEK_SET)
87                 (version, font_revidion, check_sum_adjustment, magic_number,
88                         flags, units_per_em, created, modified,
89                         x_min, y_min, x_max, y_max, mac_style, lowest_rec_ppem,
90                         font_direction_hint, index_to_loc_format, glyph_data_format) \
91                         = struct.unpack(">llLLHHqqhhhhHHhhh", self.fh.read(54));
92                 version /= 0x10000
93                 font_revidion /= 0x10000
94                 self.tables["head"] = {
95                         "version": version,
96                         "fontRevidion": font_revidion,
97                         "checkSumAdjustment": check_sum_adjustment,
98                         "magicNumber": magic_number,
99                         "flags": flags,
100                         "unitsPerEm": units_per_em,
101                         "created": created,
102                         "modified": modified,
103                         "xMin": x_min,
104                         "yMin": y_min,
105                         "xMax": x_max,
106                         "yMax": y_max,
107                         "macStyle": mac_style,
108                         "lowestRecPPEM": lowest_rec_ppem,
109                         "fontDirectionHint": font_direction_hint,
110                         "indexToLocFormat": index_to_loc_format,
111                         "glyphDataFormat": glyph_data_format
112                 }
113                 return self.tables["head"]
114
115         def get_table_loca(self):
116                 if not "loca" in self.tables:
117                         self.read_table_loca()
118                 return self.tables["loca"]
119
120         def read_table_loca(self):
121                 self.check_ttf_head()
122                 if not "head" in self.tables:
123                         self.read_table_head()
124                 if not "maxp" in self.tables:
125                         self.read_table_maxp()
126                 self.fh.seek(self.table_list["loca"]["o"], os.SEEK_SET)
127                 if self.tables["head"]["indexToLocFormat"] == 0:
128                         table_format = struct.Struct(">H")
129                 elif self.tables["head"]["indexToLocFormat"] == 1:
130                         table_format = struct.Struct(">L")
131                 else:
132                         raise TtfError("headテーブルのindexToLocFormat値が不正。")
133                 self.tables["loca"] = []
134                 for i in range(self.tables["maxp"]["numGlyphs"]+1):
135                         offset = table_format.unpack(self.fh.read(table_format.size))
136                         self.tables["loca"].append(offset)
137                 return self.tables["loca"]
138
139         def get_glyph_from_index(self, index):
140                 self.check_ttf_head()
141                 self.fh.seek(self.table_list["glyf"]["o"]+index, os.SEEK_SET)
142                 (number_of_contours, x_min, y_min, x_max, y_max) \
143                         = struct.unpack(">hhhhh", self.fh.read(10))
144                 return {
145                         "numberOfContours": number_of_contours,
146                         "xMin": x_min,
147                         "yMin": y_min,
148                         "xMax": x_max,
149                         "yMax": y_max
150                 }
151
152         def get_glyph_from_no(self, no):
153                 if "glyf" in self.tables and no in self.tables["glyf"]:
154                         return self.tables["glyf"][no]
155                 if not "glyf" in self.tables:
156                         self.tables["glyf"] = {}
157                 if not "loca" in self.tables:
158                         self.read_table_loca()
159                 glyph = self.get_glyph_from_index(self.tables["loca"][no])
160                 self.tables["glyf"][no] = glyph
161                 return glyph
162
163         def get_glyph_from_char(self, char):
164                 if not "cmap" in self.tables:
165                         self.read_table_cmap()
166                 return self.get_glyph_from_no(self.tables["cmap"][char])
167
168         def get_table_glyf(self):
169                 if not "glyf" in self.tables:
170                         self.read_table_glyf()
171                 return self.tables["glyf"]
172
173         def read_table_glyf(self):
174                 if not "maxp" in self.tables:
175                         self.read_table_maxp()
176                 for i in range(self.tables["maxp"]["numGlyphs"]+1):
177                         self.get_glyph_from_no(i)
178                 return self.tables["glyf"]
179
180         def get_table_cmap(self):
181                 if not "cmap" in self.tables:
182                         self.read_table_cmap()
183                 return self.tables["cmap"]
184
185         def read_table_cmap(self):              #手抜きとかそういうのじゃない。無理。
186                 self.check_ttf_head()
187                 self.fh.seek(self.table_list["cmap"]["o"], os.SEEK_SET)
188                 (version, num_tables) = struct.unpack(">HH", self.fh.read(4));
189                 if version != 0:
190                         raise TtfError("cmapテーブルのバージョンがあたらしすぎまっす。")
191                 encoding_records = []
192                 maxscore = 0
193                 no = -1
194                 for i in range(num_tables):
195                         (platform_id, encoding_id, offset) \
196                                 = struct.unpack(">HHL", self.fh.read(8))
197                         score = 0
198                         if platform_id == 3:
199                                 if encoding_id == 0:
200                                         score += 1
201                                 elif encoding_id == 1:
202                                         score += 3
203                         if maxscore < score:
204                                 maxscore = score
205                                 no = i
206                         encoding_records.append({
207                                 "platformID": platform_id,
208                                 "encodingID": encoding_id,
209                                 "offset": offset
210                         })
211                 if no < 0:
212                         raise TtfError("文字マップのエンコード方式に非対応のものしかありません")
213                 encoding = None
214                 if encoding_records[no]["platformID"] == 3:
215                         if encoding_records[no]["encodingID"] == 0:
216                                 encoding = "ascii"
217                         if encoding_records[no]["encodingID"] == 1:
218                                 encoding = "utf-16be"
219                         if encoding_records[no]["encodingID"] == 2:
220                                 encoding = "sjis"
221                 if encoding == None:
222                         raise TtfError("文字マップのエンコード方式に非対応のものしかありません")
223                 self.fh.seek(self.table_list["cmap"]["o"]
224                         + encoding_records[no]["offset"], os.SEEK_SET)
225                 maptype = struct.unpack(">H", self.fh.read(2))[0]
226                 mapdict = {}
227                 if maptype == 0:                                #要修正: デコードの仕方ダメ
228                         (length, language) = struct.unpack(">HH", self.fh.read(4))
229                         for i in range(256):
230                                 code = struct.pack(">L", i).decode(encoding, "replace")
231                                 mapdict[code] = struct.unpack(">B", self.fh.read(1))[0]
232                 elif maptype == 2:
233                         (length, language) = struct.unpack(">HH", self.fh.read(4))
234                         sub_header_keys = []
235                         for i in range(256):
236                                 sub_header_keys.append(struct.unpack(">H", self.fh.read(2))[0])
237                         start_of_sub_header = self.fh.tell()
238                         for i in range(256):
239                                 self.fh.seek(start_of_sub_header + sub_header_keys[i], os.SEEK_SET)
240                                 (first_code, entry_count, id_delta, id_range_offset) \
241                                         = struct.unpack(">HHsH", self.fh.read(8))
242                                 if first_code + entry_count >= 256:
243                                         continue
244                                 self.fh.seek(id_range_offset, SEEK_CUR)
245                                 for j in range(entry_count):
246                                         glyph_index = struct.unpack(">H", self.fh.read(2))[0]
247                                         if glyph_index == 0:
248                                                 continue
249                                         glyph_index += id_delta
250                                         if i == 0:
251                                                 code = j + first_code
252                                         else:
253                                                 code = (i << 8)|((j+first_code) & 0x0f)
254                                         code = struct.pack("B", code).decode(encoding, "replace")
255                                         mapdict[code] = glyph_index
256                 elif maptype == 4:
257                         (length, language, seg_count_x2, search_range, entry_selector,
258                                 range_shift) = struct.unpack(">HHHHHH", self.fh.read(12))
259                         seg_count = int(seg_count_x2 / 2)
260                         end_count = []
261                         for i in range(seg_count):
262                                 end_count.append(struct.unpack(">H", self.fh.read(2))[0])
263                         reserved_pad = struct.unpack(">H", self.fh.read(2))[0]
264                         start_count = []
265                         for i in range(seg_count):
266                                 start_count.append(struct.unpack(">H", self.fh.read(2))[0])
267                         id_delta = []
268                         for i in range(seg_count):
269                                 id_delta.append(struct.unpack(">h", self.fh.read(2))[0])
270                         id_range_offset = []
271                         for i in range(seg_count):
272                                 id_range_offset.append(struct.unpack(">H", self.fh.read(2))[0])
273                         start_of_glyph_id_array = self.fh.tell()
274                         for i in range(seg_count):
275                                 start = start_count[i]
276                                 end = end_count[i]
277                                 delta = id_delta[i]
278                                 range_offset = id_range_offset[i]
279                                 if start == 65535 or end == 65535:
280                                         continue
281                                 for j in range(start, end+1):
282                                         if range_offset == 0:
283                                                 code = struct.pack(">H", j)
284                                                 code = code.decode(encoding, "replace")
285                                                 mapdict[code] = (j + delta) % 65536
286                                         else:
287                                                 self.fh.seek(
288                                                         start_of_glyph_id_array
289                                                                 + int((range_offset/2+j-start+i-seg_count)*2),
290                                                         os.SEEK_SET)
291                                                 glyph_index = struct.unpack(">H", self.fh.read(2))[0]
292                                                 if glyph_index == 0:
293                                                         continue
294                                                 code = struct.pack(">H", j)
295                                                 code = code.decode(encoding, "replace")
296                                                 mapdict[code] = (glyph_index + delta) % 65536
297                 else:
298                         raise TtfError("ID{}の文字マップには対応していません".format(maptype))
299                 self.tables["cmap"] = mapdict
300                 return self.tables["cmap"]
301
302         def get_table_maxp(self):
303                 if not "maxp" in self.tables:
304                         self.read_table_maxp()
305                 return self.tables["maxp"]
306
307         def read_table_maxp(self):
308                 self.check_ttf_head()
309                 self.fh.seek(self.table_list["maxp"]["o"], os.SEEK_SET)
310                 self.tables["maxp"] = {}
311                 version = struct.unpack(">L", self.fh.read(4))[0]
312                 if version == 0x00005000:
313                         self.tables["maxp"]["numGlyphs"] \
314                                 = struct.unpack(">S", self.fh.read(2))[0]
315                 elif version == 0x00010000:             #手抜き(v1.0で追加されたのを無視する)
316                         self.tables["maxp"]["numGlyphs"] \
317                                 = struct.unpack(">S", self.fh.read(2))[0]
318                 else:
319                         raise TtfError("maxpテーブルのバージョンが非対応なやつです")
320
321         def get_table_hhea(self):
322                 if not "hhea" in self.tables:
323                         self.read_table_hhea()
324                 return self.tables["hhea"]
325
326         def read_table_hhea(self):
327                 self.check_ttf_head()
328                 self.fh.seek(self.table_list["hhea"]["o"], os.SEEK_SET)
329                 (vesion, ascender, descender, line_gap, advance_width_max,
330                 min_left_side_bearing, min_right_side_bearing, x_max_extent,
331                 caret_slope_rise, caret_slope_run, caret_offset,
332                 zero0, zero1, zero2, zero3, metric_data_format, number_of_h_metrics) \
333                         = struct.unpack(">LhhhHhhhhhhhhhhhH", self.fh.read(36));
334                 self.tables["hhea"] = {
335                         "vesion": vesion,
336                         "ascender": ascender,
337                         "descender": descender,
338                         "lineGap": line_gap,
339                         "advanceWidthMax": advance_width_max,
340                         "minLeftSideBearing": min_left_side_bearing,
341                         "minRightSideBearing": min_right_side_bearing,
342                         "xMaxExtent": x_max_extent,
343                         "caretSlopeRise": caret_slope_rise,
344                         "caretSlopeRun": caret_slope_run,
345                         "caretOffset": caret_offset,
346                         "metricDataFormat": metric_data_format,
347                         "numberOfHMetrics": number_of_h_metrics
348                 }
349                 return self.tables["hhea"]
350
351         def get_table_hmtx(self):
352                 if not "hmtx" in self.tables:
353                         self.read_table_hmtx()
354                 return self.tables["hmtx"]
355
356         def read_table_hmtx(self):              #手抜き
357                 self.check_ttf_head()
358                 if not "hhea" in self.tables:
359                         self.read_table_hhea()
360                 self.fh.seek(self.table_list["hmtx"]["o"], os.SEEK_SET)
361                 long_hor_metric = []
362                 for i in range(self.tables["hhea"]["numberOfHMetrics"]):
363                         (advance_width, lsb) = struct.unpack(">Hh", self.fh.read(4))
364                         long_hor_metric.append({"advanceWidth": advance_width, "lsb": lsb})
365                 self.tables["hmtx"] = long_hor_metric
366                 return long_hor_metric
367
368         def get_charwidth_from_no(self, no):
369                 if not "hmtx" in self.tables:
370                         self.read_table_hmtx()
371                 if len(self.tables["hmtx"]) <= no:
372                         return None
373                 return self.tables["hmtx"][no]["advanceWidth"]
374
375         def get_charwidth_from_char(self, char):
376                 if not "cmap" in self.tables:
377                         self.read_table_cmap()
378                 return self.get_charwidth_from_no(self.tables["cmap"][char])
379
380         def get_table_name(self):
381                 if not "name" in self.tables:
382                         self.read_table_name()
383                 return self.tables["name"]
384
385         def read_table_name(self):              #だいぶ手抜き
386                 self.check_ttf_head()
387                 self.fh.seek(self.table_list["name"]["o"], os.SEEK_SET)
388                 (format_num, count, string_offset) \
389                         = struct.unpack(">HHH", self.fh.read(6))
390                 name_record = []
391                 for i in range(count):
392                         (platform_id, encoding_id, language_id, name_id, length, offset) \
393                                 = struct.unpack(">HHHHHH", self.fh.read(12))
394                         name_record.append({
395                                 "platformID": platform_id,
396                                 "encodingID": encoding_id,
397                                 "languageID": language_id,
398                                 "nameID": name_id,
399                                 "length": length,
400                                 "offset": offset
401                         })
402                 storagepos = self.table_list["name"]["o"] + string_offset
403                 self.tables["name"] = []
404                 for nr in name_record:                  #要修正: encID=0はAsciiだよ。雑過ぎ。
405                         encoding = None
406                         if nr["platformID"] == 3:
407                                 if nr["encodingID"] == 0:
408                                         encoding = "utf-16be"
409                                 elif nr["encodingID"] == 1:
410                                         encoding = "utf-16be"
411                                 elif nr["encodingID"] == 2:
412                                         encoding = "sjis"
413                         if not encoding:
414                                 continue
415                         self.fh.seek(storagepos + nr["offset"], os.SEEK_SET)
416                         string = self.fh.read(nr["length"]).decode(encoding, "replace")
417                         nr.update({"string": string})
418                         self.tables["name"].append(nr)
419                 return self.tables["name"]
420
421         def get_font_name(self):
422                 if not "name" in self.tables:
423                         self.read_table_name()
424                 fontname = None
425                 maxscore = -1
426                 for name in self.tables["name"]:
427                         score = 0
428                         if name["nameID"] == 4:
429                                 score += 3
430                         if name["languageID"] == 0x0411:
431                                 score += 2
432                         if name["encodingID"] == 1:
433                                 score += 1
434                         if maxscore < score:
435                                 maxscore = score
436                                 fontname = name["string"]
437                 return fontname
438
439         def has_name(self, rqname):
440                 if not "name" in self.tables:
441                         self.read_table_name()
442                 for name in self.tables["name"]:
443                         if name["nameID"] == 4:
444                                 if name["string"] == rqname:
445                                         return True
446                 return False
447
448 # ** フォント優先順位テーブルについて **
449 #適切なフォントが検出できない時のためにいくつか自動検出の候補を用意しておく。
450 #自分の手元にあったフォントからテキトーに選んだだけ。
451
452 #半角文字用フォントの優先順位。HelveticaというやつはArialと幅が同じらしい。
453 HWalphanumeric_priority = [
454         "Arial", "Helvetica", "DejaVu Sans", "VL PGothic Regular"
455 ]
456 #ゴシック体用フォントの優先順位。MS Pゴシック以外はテキトーに並べただけ。
457 gothic_priority = [
458         "MS Pゴシック", "VL PGothic Regular"
459 ]
460 gothic_replacer = ""
461 #明朝体用のフォントの優先順位。SimSun以外はテキトーに並べただけ。最後はゴシック
462 mincho_priority = [
463         "SimSun", "VL PGothic Regular"
464 ]
465 mincho_replacer = "\u02c9\u02ca\u02cb\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u2589\u258a\u258b\u258c\u258d\u258e\u258f\u311a"
466 #丸文字体用のフォント優先順位。Gulim以外はただのゴシック
467 marumoji_priority = [
468         "Gulim", "MS Pゴシック", "VL PGothic Regular"
469 ]
470 marumoji_replacer = "\u2660\u2661\u2663\u2664\u2665\u2667\u2668\u2669\u266c\uadf8"
471
472 hwfont_reader = None
473 fwfont_readers = []
474 fwfont_replacers = {}
475
476 def gen_fontwidth_py(outputfile):
477         #自動設定はWin以外は今のところ非対応。
478         fontfiles = []
479         fontdirs = []
480         if os.path.isdir("fonts"):
481                 fontdirs.append("fonts")
482         if os.name == "nt":
483                 if os.path.isdir("C:\\Windows\\Fonts\\"):
484                         fontdirs.append("C:\\Windows\\Fonts\\")
485         for fontdir in fontdirs:
486                 infontdir = os.listdir(fontdir)
487                 for filename in infontdir:
488                         filename = os.path.join(fontdir, filename)
489                         if os.path.isfile(filename) and os.access(filename, os.R_OK):
490                                 fontfiles.append(filename)
491         fonts = []
492         for fontfile in fontfiles:
493                 fh = open(fontfile, "br")
494                 headreader = TtfReader(fh)
495                 filetype = headreader.detect_type()
496                 if filetype == "ttf":
497                         fonts.append(headreader)
498                 elif filetype == "ttc":
499                         for offset in headreader.read_ttc_head():
500                                 fonts.append(TtfReader(fh, offset))
501         font_can_HW = None
502         font_can_gothic = None
503         font_can_mincho = None
504         font_can_marumoji = None
505         for rqname in reversed(HWalphanumeric_priority):
506                 for font in fonts:
507                         if font.has_name(rqname):
508                                 font_can_HW = font
509         for rqname in reversed(gothic_priority):
510                 for font in fonts:
511                         if font.has_name(rqname):
512                                 font_can_gothic = font
513         for rqname in reversed(mincho_priority):
514                 for font in fonts:
515                         if font.has_name(rqname):
516                                 font_can_mincho = font
517         for rqname in reversed(marumoji_priority):
518                 for font in fonts:
519                         if font.has_name(rqname):
520                                 font_can_marumoji = font
521         if not(font_can_HW and font_can_gothic and
522                 font_can_mincho and font_can_marumoji):
523                 print("適切なフォントをfonts/に入れてください。(README参照)")
524                 raise TtfError("必要なフォントを自動検出できんかったよ")
525         hwfont_reader = font_can_HW
526         fwfont_readers = [font_can_gothic, font_can_mincho, font_can_marumoji]
527         fwfont_replacers = {
528                 font_can_gothic.get_font_name(): gothic_replacer,
529                 font_can_mincho.get_font_name(): mincho_replacer,
530                 font_can_marumoji.get_font_name(): marumoji_replacer
531         }
532
533         outputfile.write("#Generated by nicovideo-tools")
534         outputfile.write("\n")
535         outputfile.write("font_HWalphanumeric = \"{}\"".format(hwfont_reader.get_font_name()))
536         outputfile.write("\n")
537         nametable = []
538         for tr in fwfont_readers:
539                 nametable.append("\"{}\"".format(tr.get_font_name()))
540         outputfile.write("font_priority = [{}]".format(",".join(nametable)))
541         outputfile.write("\n")
542         outputfile.write("font_change_chars = {")
543         outputfile.write("\n")
544         for key in fwfont_replacers.keys():
545                 line = "\t\"{}\": \"".format(key)
546                 for char in fwfont_replacers[key]:
547                         line += "\\U{:0>8x}".format(ord(char))
548                 line += "\","
549                 outputfile.write(line)
550                 outputfile.write("\n")
551         outputfile.write("\t\"\": \"\"")
552         outputfile.write("\n")
553         outputfile.write("}")
554         outputfile.write("\n")
555         outputfile.write("fontlist = {")
556         outputfile.write("\n")
557         for tr in fwfont_readers:
558                 outputfile.write("\t\"" + tr.get_font_name() + "\": {")
559                 outputfile.write("\n")
560                 outputfile.write("\t\t\"h\": {},".format(tr.get_table_head()["unitsPerEm"]))
561                 outputfile.write("\n")
562                 outputfile.write("\t\t\"w\": {")
563                 outputfile.write("\n")
564                 charmap = tr.get_table_cmap()
565                 for char in charmap.keys():
566                         width = tr.get_charwidth_from_char(char)
567                         if width != None:
568                                 outputfile.write("\t\t\t\"\\U{:0>8x}\": {},".format(ord(char), width))
569                                 outputfile.write("\n")
570                 outputfile.write("\t\t\t\"\": 0")
571                 outputfile.write("\n")
572                 outputfile.write("\t\t}")
573                 outputfile.write("\n")
574                 outputfile.write("\t},")
575                 outputfile.write("\n")
576
577         outputfile.write("\t\"" + hwfont_reader.get_font_name() + "\": {")
578         outputfile.write("\n")
579         outputfile.write("\t\t\"h\": {},".format(hwfont_reader.get_table_head()["unitsPerEm"]))
580         outputfile.write("\n")
581         outputfile.write("\t\t\"w\": {")
582         outputfile.write("\n")
583         charmap = hwfont_reader.get_table_cmap()
584         for char in charmap.keys():
585                 width = hwfont_reader.get_charwidth_from_char(char)
586                 if width != None:
587                         outputfile.write("\t\t\t\"\\U{:0>8x}\": {},".format(ord(char), width))
588                         outputfile.write("\n")
589         outputfile.write("\t\t\t\"\": 0")
590         outputfile.write("\n")
591         outputfile.write("\t\t}")
592         outputfile.write("\n")
593         outputfile.write("\t}")
594         outputfile.write("\n")
595         outputfile.write("}")
596         outputfile.write("\n")
597
598 class build_fontwidth_py(distutils.cmd.Command):
599         description = "\"build\" fontwidth.py"
600
601         user_options = [("build-lib=", "d", "directory to \"build\" to")]
602         boolean_options = []
603
604         def initialize_options(self):
605                 self.build_lib = None
606
607         def finalize_options(self):
608                 self.set_undefined_options("build", ('build_lib', 'build_lib'))
609
610         def run(self):
611                 self.mkpath(os.path.join(self.build_lib, "nicovideo"))
612                 outfile = os.path.join(self.build_lib, "nicovideo", "fontwidth.py")
613                 outfilehandle = open(outfile, mode="w", encoding="utf-8")
614                 outfilehandle.write("# -*- coding: utf-8 -*-\n")
615                 gen_fontwidth_py(outfilehandle)
616                 outfilehandle.close()
617
618 class my_build(distutils.command.build.build):
619         def run(self):
620                 self.run_command("build_fontwidth_py")
621                 distutils.command.build.build.run(self)
622
623 distutils.core.setup(   name="pylib-nicovideo",
624                                                 version="0.3.0.2",
625                                                 description="ニコニコにアクセスしたりできる。",
626                                                 author="dyknon",
627                                                 author_email="dyknon@users.sourceforge.jp",
628                                                 url="http://sourceforge.jp/users/dyknon/",
629                                                 packages=["nicovideo"],
630                                                 cmdclass={
631                                                         "build": my_build,
632                                                         "build_fontwidth_py": build_fontwidth_py
633                                                 }
634 )