Skip to content

Instantly share code, notes, and snippets.

@kiyota-yoji
Last active August 29, 2015 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kiyota-yoji/2b01c2ab4150769e0ebd to your computer and use it in GitHub Desktop.
Save kiyota-yoji/2b01c2ab4150769e0ebd to your computer and use it in GitHub Desktop.
QRコード付きカードの印刷
東京駅 http://ja.wikipedia.org/wiki/%E6%9D%B1%E4%BA%AC%E9%A7%85 東京駅(とうきょうえき)は、東京都千代田区丸の内一丁目にある、東日本旅客鉄道(JR東日本)・東海旅客鉄道(JR東海)・東京地下鉄(東京メトロ)の駅である。 http://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Tokyo_station_from_marunouchi_oazo.JPG/800px-Tokyo_station_from_marunouchi_oazo.JPG http://commons.wikimedia.org/wiki/File:Tokyo_station_from_marunouchi_oazo.JPG Toshinori baba, CC BY-SA 3.0
簗場駅 http://ja.wikipedia.org/wiki/%E7%B0%97%E5%A0%B4%E9%A7%85 簗場駅(やなばえき)は、長野県大町市平中綱にある、東日本旅客鉄道(JR東日本)大糸線の駅である。 http://upload.wikimedia.org/wikipedia/ja/thumb/7/76/%2709.1.10_Yanaba-Sta.jpg/800px-%2709.1.10_Yanaba-Sta.jpg http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:%2709.1.10_Yanaba-Sta.jpg XT, CC BY 3.0
東山駅 (北海道) http://ja.wikipedia.org/wiki/%E6%9D%B1%E5%B1%B1%E9%A7%85_(%E5%8C%97%E6%B5%B7%E9%81%93) 東山駅(ひがしやまえき)は、北海道(渡島総合振興局)茅部郡森町にある北海道旅客鉄道(JR北海道)函館本線(本線)の駅である。駅番号はH64。電報略号はヒヤ。 http://upload.wikimedia.org/wikipedia/ja/thumb/3/32/%2709.3.8_Higashiyama-Sta.JPG/800px-%2709.3.8_Higashiyama-Sta.JPG http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:%2709.3.8_Higashiyama-Sta.JPG XT, CC BY 3.0
姫川駅 (北海道) http://ja.wikipedia.org/wiki/%E5%A7%AB%E5%B7%9D%E9%A7%85_(%E5%8C%97%E6%B5%B7%E9%81%93) 姫川駅(ひめかわえき)は、北海道茅部郡森町字姫川にある北海道旅客鉄道(JR北海道)函館本線(本線)の駅。駅番号はH63。電報略号はヒメ。 http://upload.wikimedia.org/wikipedia/ja/thumb/3/3d/%2709.3.8_Himekawa-Sta.JPG/800px-%2709.3.8_Himekawa-Sta.JPG http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:%2709.3.8_Himekawa-Sta.JPG XT, CC BY 3.0
神城駅 http://ja.wikipedia.org/wiki/%E7%A5%9E%E5%9F%8E%E9%A7%85 神城駅(かみしろえき)は、長野県北安曇郡白馬村大字神城飯田にある、東日本旅客鉄道(JR東日本)大糸線の駅である。 http://upload.wikimedia.org/wikipedia/commons/thumb/5/57/Kamishiro-station_Oito-Line_JREast.jpg/800px-Kamishiro-station_Oito-Line_JREast.jpg http://commons.wikimedia.org/wiki/File:Kamishiro-station_Oito-Line_JREast.jpg Angus Chan, CC BY-SA 2.5
油須原駅 http://ja.wikipedia.org/wiki/%E6%B2%B9%E9%A0%88%E5%8E%9F%E9%A7%85 油須原駅(ゆすばるえき)は、福岡県田川郡赤村大字赤にある平成筑豊鉄道田川線の駅である。ネーミングライツにより森商事 油須原駅(もりしょうじ ゆすばるえき)と呼ばれる。 http://upload.wikimedia.org/wikipedia/commons/thumb/3/38/Heiseichikuho_Yusubaru_station01.jpg/800px-Heiseichikuho_Yusubaru_station01.jpg http://commons.wikimedia.org/wiki/File:Heiseichikuho_Yusubaru_station01.jpg Muyo, CC BY-SA 3.0
三河一宮駅 http://ja.wikipedia.org/wiki/%E4%B8%89%E6%B2%B3%E4%B8%80%E5%AE%AE%E9%A7%85 三河一宮駅(みかわいちのみやえき)は、愛知県豊川市一宮町下新切にある、東海旅客鉄道(JR東海)飯田線の駅である。 http://upload.wikimedia.org/wikipedia/commons/thumb/7/78/Mikawa-Ichinomiya_Station.jpg/800px-Mikawa-Ichinomiya_Station.jpg http://commons.wikimedia.org/wiki/File:Mikawa-Ichinomiya_Station.jpg Lombroso, Public Domain
トロッコ保津峡駅 http://ja.wikipedia.org/wiki/%E3%83%88%E3%83%AD%E3%83%83%E3%82%B3%E4%BF%9D%E6%B4%A5%E5%B3%A1%E9%A7%85 トロッコ保津峡駅(トロッコほづきょうえき)は、京都府京都市西京区嵐山北松尾山にある、嵯峨野観光鉄道嵯峨野観光線の駅。 http://upload.wikimedia.org/wikipedia/commons/thumb/3/39/Truck_Hozukyo_station-201106.jpg/800px-Truck_Hozukyo_station-201106.jpg http://commons.wikimedia.org/wiki/File:Truck_Hozukyo_station-201106.jpg Rsa, CC BY-SA 3.0
鞍馬駅 http://ja.wikipedia.org/wiki/%E9%9E%8D%E9%A6%AC%E9%A7%85 鞍馬駅(くらまえき)は、京都府京都市左京区鞍馬本町にある叡山電鉄鞍馬線の駅。終着駅である。駅ナンバリングはE17。 http://upload.wikimedia.org/wikipedia/ja/thumb/0/06/KuramaSt.JPG/799px-KuramaSt.JPG http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:KuramaSt.JPG Bergmann, GFDL
奥多摩駅 http://ja.wikipedia.org/wiki/%E5%A5%A5%E5%A4%9A%E6%91%A9%E9%A7%85 奥多摩駅(おくたまえき)は、東京都西多摩郡奥多摩町氷川にある、東日本旅客鉄道(JR東日本)青梅線の駅である。 http://upload.wikimedia.org/wikipedia/commons/thumb/7/79/Okutama_Station_in_Okutama%2C_Tokyo_3.JPG/800px-Okutama_Station_in_Okutama%2C_Tokyo_3.JPG http://commons.wikimedia.org/wiki/File:Okutama_Station_in_Okutama,_Tokyo_3.JPG Tx-re, CC BY-SA 3.0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
QRコード付きカード作成用のHTMLファイルを生成する
License: MIT License
"""
__author__ = 'KIYOTA Yoji <kiyota.yoji@gmail.com>'
__status__ = 'production'
__version__ = '0.1'
__date__ = '2015-03-20'
"""
HTMLヘッダ文字列
"""
HTML_HEADER = u"""\
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>QRコード付きカード印刷データ</title>
<style type="text/css">
/* hrタグで改ページ */
hr {
page-break-after: always;
visibility: hidden;
}
/* ヘッダは右上に配置 */
div.header {
text-align: right;
font-size: 75%;
}
/* QRコードは左下に配置 */
div.qrcode {
text-align: right;
}
/* 画像ファイルを中央に配置 */
div.image {
text-align: center;
}
/* 画像の著作権表示 */
div.author {
text-align: right;
font-size: 75%;
}
/* タイトルのフォント指定 */
h2 {
font-family: "Hiragino Kaku Gothic ProN";
}
</style>
</head>
<body>
"""
"""
HTMLフッタ文字列
"""
HTML_FOOTER = u"""\
</body>
</html>
"""
"""
カードのHTMLタグテンプレート
"""
CARD_TEMPLATE = u"""\
<div class="header"><a href="%s">%d</a></div>
<div class="image"><img src="%s" %s /></div>
<div class="author"><a href="%s">%s</a></div>
<h2>%s</h2>
<p>%s</p>
<div class="qrcode"><img src="%s" width="50" /></div>
"""
# 画像の大きさの制限値
IMAGE_WIDTH_LIMIT = 300
IMAGE_HEIGHT_LIMIT = 180
# タイトル、説明文の文字数の制限値
TITLE_LIMIT = 20
DESCRIPTION_LIMIT = 100
import argparse, qrcode, sys, urllib, cStringIO, os, traceback
import PIL.Image as Image
class CardHtmlMaker(object):
"""
QRコード付きカード作成用のHTMLを生成するクラス
"""
def __init__(self, qrcode_dir):
"""
initialization: QRコード画像ファイル用のディレクトリ準備
@param qrcode_dir QRコード画像を格納するディレクトリ
"""
self.qrcode_dir = os.path.normpath(qrcode_dir)
if not os.path.isdir(self.qrcode_dir):
os.mkdir(self.qrcode_dir)
def _get_image_size_tag(self, image_url):
"""
Web上の画像サイズをHTTPで取得し、制限サイズを計算
@param image_url 画像のURL
@return imgタグのサイズ指定文字列
"""
try:
res = urllib.urlopen(image_url)
except IOError, e:
raise Exception(u'画像ファイルの取得中にエラーが発生しました: %s: %s' % (image_url, e))
if res.code != 200:
raise Exception(u'画像ファイルが取得できません: %s: status=%d' % (image_url, res.code))
fp = cStringIO.StringIO(res.read())
image = Image.open(fp)
(width, height) = image.size
if (float(width) / height) > (float(IMAGE_WIDTH_LIMIT) / IMAGE_HEIGHT_LIMIT):
# 制限サイズより横長 -> 幅でサイズ指定
return 'width="%d"' % IMAGE_WIDTH_LIMIT
else:
# 制限サイズより縦長 -> 高さでサイズ指定
return 'height="%d"' % IMAGE_HEIGHT_LIMIT
def _make_card(self, num, line, page_break=True):
"""
1枚のカードのHTML文字列を生成
@param num カード番号(1から始まる)
@param line TSVファイルの1行
@param page_break 改ページを行うか否か
"""
fields = line.rstrip().decode('utf-8').split('\t')
if len(fields) != 6:
raise Exception(u'入力ファイルのフィールド数が6個ではありません: %d行目: %s' % (num, repr(fields)))
(title, page_url, description, image_url, author_url, author_name) = fields
# QRコード生成 (page_urlをQRコード化)
qrcode.make(page_url).save('./qrcode/%08d.png' % (num))
# 画像サイズを取得
image_size_tag = self._get_image_size_tag(image_url)
# 文字数を詰める
if len(title) > TITLE_LIMIT:
title = title[:TITLE_LIMIT] + '...'
if len(description) > DESCRIPTION_LIMIT:
description = description[:DESCRIPTION_LIMIT] + '...'
# HTMLタグの生成
card_body = CARD_TEMPLATE % (page_url, num,
image_url, image_size_tag,
author_url, author_name,
title,
description,
'./qrcode/%08d.png' % (num))
if page_break:
return card_body + '<hr>\n\n'
else:
return card_body
def output(self, input_data, output_html):
"""
TSVファイルで入力されたデータから、HTMLファイルを生成する
@param input_data 入力ファイル(TSV)のファイルハンドル
@param output_html 出力ファイル(HTML)のファイルハンドル
"""
output_html.write(HTML_HEADER.encode('utf-8'))
lines = input_data.readlines()
input_data.close()
num_cards = len(lines)
num_pages = (num_cards - 1) / 4 + 1
for page in range(num_pages):
# 1枚目(左上)
card_id = page
output_html.write(self._make_card(card_id+1, lines[card_id]).encode('utf-8'))
# 2枚目(左下)
card_id = page + num_pages * 1
if card_id > num_cards - 1:
output_html.write('<hr>\n\n')
else:
output_html.write(self._make_card(card_id+1, lines[card_id]).encode('utf-8'))
# 3枚目(右上)
card_id = page + num_pages * 2
if card_id > num_cards - 1:
output_html.write('<hr>\n\n')
else:
output_html.write(self._make_card(card_id+1, lines[card_id]).encode('utf-8'))
# 4枚目(右下)
card_id = page + num_pages * 3
if card_id > num_cards - 1:
if page == num_pages - 1:
pass
else:
output_html.write('<hr>\n\n')
else:
if page == num_pages - 1:
output_html.write(self._make_card(card_id+1, lines[card_id],
page_break=False).encode('utf-8'))
else:
output_html.write(self._make_card(card_id+1, lines[card_id]).encode('utf-8'))
output_html.write(HTML_FOOTER.encode('utf-8'))
output_html.close()
def main():
parser = argparse.ArgumentParser(description=u'QRコード付きカード印刷用のHTMLを生成')
parser.add_argument('input_data', type=argparse.FileType('r'), help=u'入力ファイル名 (TSV)')
parser.add_argument('output_html', type=argparse.FileType('w'), help=u'出力ファイル名 (HTML)')
parser.add_argument('--qrcode-dir', default='./qrcode', help=u'QRコードの画像ファイル用ディレクトリ')
args = parser.parse_args()
try:
maker = CardHtmlMaker(args.qrcode_dir)
maker.output(args.input_data, args.output_html)
except Exception, e:
print e.message.encode('utf-8')
traceback.print_exc()
os.remove(args.output_html.name)
sys.exit(1)
if __name__ == '__main__':
main()
target = card_printout.html card_printout.pdf
qrcode_dir = ./qrcode
default: $(target)
card_printout.html: card_data.txt
python make_html.py --qrcode-dir $(qrcode_dir) $^ $@
card_printout.pdf: card_printout.html
wkhtmltopdf -s A6 $^ $@
clean:
rm -fr $(target) $(qrcode_dir)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment