ほんじゃらねっと

ダイエット中プログラマのブログ

Pythonでgrep風にディレクトリ内のファイルを検索

大した機能ではないが、せっかくスクリプトを作ったので保存しておく。

せめて検索フォルダと検索文字列はコマンドオプションとして

渡せるようにしておくべきだったか。

pygrep.py

import os
import re

search_dir = "<検索対象フォルダパス>"
search_pattern = "<検索文字列>"

file_name_list = os.listdir(search_dir)
for file_name in file_name_list:
    f = open(os.path.join(search_dir, file_name))
    line = f.readline()
    line_number = 1
    while line:
        m = re.search(search_pattern, line)
        if m:
            print "Pattern Found: file:%s, line:%d, data:%s" % (file_name, line_number, line)
        line = f.readline()
        line_number = line_number + 1
    f.close()

追記

もう少しマシなコードはこちら:

blog.honjala.net

CentOSにJPEGサポート付きでPIL(Python Imaging Library)をインストールする

思いのほか苦労したのでメモ。


CentOSにPython2.5とPIL1.1.6の環境を作成してプログラムを動かしてみたところ、
JPEG画像の処理に失敗し、下記のログが表示された。

decoder jpeg not available


なんやかんやで下記のライブラリが必要だと分かったのでインストールした。

  1. libjpeg-devel
  2. freetype2

libjpeg-develはyumで、freetype2はソースからインストールした。


その後、PILを再インストールするのだが、ここの手順を調べるのに時間がかかった。
とりあえず、下記の処理を実施すると動いた。

  1. PILインストーラディレクトリのbuildディレクトリを削除
  2. python setup.py buildを実行
  3. python setup.py installを実行
  4. python selftest.pyを実行し、テストが全て通ることを確認


詳しい内容はいつか書く。

Pymacsを使ってPythonでEmacs拡張

NTEmacsを使い始めたので、Pymacsを入れて簡単なプラグインを作ってみた。
EmacsVimと比べるとまだまだ不満がいっぱいだが、色々プラグイン
作ってVimの快適さに近づけていきたい。


苦労したのでメモ。

Pymacsのインストール

Python2.5環境で試した。


あらかじめ、python-modeを入れておく


SourceForge.net: python-mode.el for Emacs and XEmacs
http://sourceforge.net/projects/python-mode/


下記のURLからPymacsをダウンロード


README file for Pymacs
http://pymacs.progiciels-bpi.ca/index.html


解凍すると色々ファイルがある中にsetup.pyがあるので、コマンドプロンプト

python setup.py install

を実行してPython側環境をインストール。


Emacs側は、解答したフォルダに入っているpymacs.elをsite-lisp内に
作ったPymacsフォルダあたりに入れる。


また、site-lispとは別にPymacs用のpythonファイルを入れておく
pymacs-elispフォルダを作った。


このあたりは本家サイトのドキュメントを参考にした。


Pymacs version 0.24-beta1
http://pymacs.progiciels-bpi.ca/pymacs.html


.emacsには下記のような設定を入れておく

;; Pymacs
(autoload 'pymacs-apply "pymacs")
(autoload 'pymacs-call "pymacs")
(autoload 'pymacs-eval "pymacs" nil t)
(autoload 'pymacs-exec "pymacs" nil t)
(autoload 'pymacs-load "pymacs" nil t)
(eval-after-load "pymacs"
'(add-to-list 'pymacs-load-path "~/app/emacs/pymacs-elisp"))

この設定の下にpython-mode用の設定を入れておく。

;; python-mode
(autoload 'python-mode "python-mode" nil t)
(autoload 'py-shell "python-mode" "Python shell" t)
(add-to-list 'auto-mode-alist '("\\.py\\'" . python-mode))
(add-hook 'python-mode-hook
'(lambda()
(require 'pycomplete)
(setq indent-tabs-mode nil)))

python-modeに付属のpycomplete.pyはpymacs-elispフォルダの
方に入れておくと認識してくれた。

Pymacsで日本語が通るようにする

下記のサイトを参考にした。


Pymacs で日本語文字列を無理矢理通す
http://d.hatena.ne.jp/amt/20061219/PymacsDeNihonngoWoToosuP2E


emacs初心者なので理解できる範囲をかなり超えていたが、
どうやらページの中ほどに記載されている、

  1. ustr.pyをpymacs-elispフォルダに配置し、
  2. pymacs-ustr.elをsite-lisp/Pymacsに配置したら

うまく動作するようだ。


python関連の設定は上記のみのはず。
最終的にpython関連のファイルは下記のようなフォルダ構成になった

~/
.emacs
app/emacs/
site-lisp/
Pymacs/
pymacs.el
pymacs-ustr.el
python-mode-1.0/
doctest-mode.el
pycomplete.el
python-mode.el
pymacs-elisp/
ustr.py
pycomplete.py

動作テスト

これで準備ができたはずなので、簡単なプラグインでテストしてみる。

まずは下記のようなpythonファイルを作ってpymacs-elispフォルダに保存する。


helloworld.py

# -*- coding: utf-8 -*-
from Pymacs import lisp
from ustr import *
def hello():
lisp.py_insert(ustring2bytes(u"こんにちは Hello from Python!"))

これでpython側の日本語がemacs側で使えることを確認する。

方法としては、

  1. *scratch*バッファあたりの適当なバッファに移動
  2. M-x pymacs-load
  3. ロードするモジュールを聞かれるので、ファイル名"helloworld"を入力
  4. プレフィックスはデフォルトの"helloworld-"で良い
  5. これでhelloworld.pyの中の関数が(helloworld-関数名)で呼び出せるはず
  6. *scratch*上で(helloworld-hello)と記入し、行を選択して"C-j"で実行し、日本語で"こんにちは"が読めたら成功 (後ろに返り値の"nil"が入るけど気にしない)


続いて、emacsから入力した日本語をpython側で受け取れることを確認する

下記のようなpythonファイルを作ってpymacs-elispに保存する。


filetest.py

# -*- coding: utf-8 -*-
from Pymacs import lisp
from ustr import *
def writefile(b):
text = bytes2ustring(b)
f = open("c:/test/output.txt", "w")
f.write(text)
f.close()

出力先は適当に存在するフォルダを指定する。

上記のhelloworld.pyと同様にロードして実行するのだが、渡す文字列を
バイト配列に変換してやる必要がある。

  1. M-x pymacs-load
  2. "filetest"と入力
  3. プレフィックスはそのままで
  4. M-x load-libraryを実行
  5. pymacs-ustrをロード
  6. *scratch*に(filetest-writefile (py-string-to-unicode-vector "やっほー"))と記入し、行を選択して"C-j"で実行
  7. output.txtに"やっほー"が日本語で出力されていたら成功

(py-string-to-unicode-vector "文字列")でemacs側から文字列を渡し、
python側でbytes2ustringで戻せば日本語が通るようだ。

サンプル

最後に、以前vim用のプラグインとして作った1行メモプラグイン
移植したものを作ってみたので、公開してみる。


以前作ったもの


[vim][python] Pythonで1行メモVimスクリプト
http://d.hatena.ne.jp/piro_suke/20080215/1203087230


site-lisp/pymemo.el

(require 'pymacs)
(require 'pymacs-ustr)
(pymacs-load "pymemo")
(defun pymemo-add (msg)
(interactive
(list (read-from-minibuffer "memo: ")))
(pymemo-add-memo (py-string-to-unicode-vector msg)))
(provide 'pymemo)


pymacs-elisp/pymemo.py

# -*- coding: utf-8 -*-
import sys
import codecs
import os
import re
import datetime
from Pymacs import lisp
from ustr import *
LINE_HEADER= "*"
DATE_TEMPLATE = "[%Y-%m-%d %H:%M]"
ENCODING="Shift_JIS"
FILE_PATH = "C:/test/data/memo/pymemo.txt"
def add_memo(b):
msg = bytes2ustring(b)
msg = msg.encode(ENCODING)
day = datetime.datetime.now().strftime(DATE_TEMPLATE)
f = open(FILE_PATH, "a+")
f.write("%s %s %s\n" % (LINE_HEADER, msg, day))
f.close()

.emacs(Pymacsのロード処理以降に)

;; Pymemo
(require 'pymemo)


これで、

M-x pymemo-add

を実行すると"memo:"と表示されるので、1行メモを入力すると
FILE_PATHで指定したファイルに追記される。


これを応用すれば、WEB-APIとの連携もできるだろう。
Vimと共有可能なライブラリができれば良いのだけど。

UnicodeDecodeErrorが発生する文字をignoreオプションで無視する

Python文字コードを変更する際に変換対象の文字列に変換不能な文字が
含まれているとUnicodeDecodeErrorが発生して困ることが多かった。


しかし、unicode関数やencode関数のignoreオプションを使うと、
それらの変換不能な文字列を無視して変換してくれる。
こんな便利なものがあったとは。


BeatifulSoupで不正な文字列を含むXMLを読み込むと、読み込んだ時に
文字コードをご認識してしまう問題が発生していたが、ignoreオプション付きで
文字コードを再変換したらうまく認識してくれた。

# file_dataは壊れたXMLデータ(UTF-8)
soup = BeautifulSoup(file_data)
print soup.originalEncoding # 文字コードが誤認識される
soup = BeautifulSoup(unicode(file_data, "utf-8").encode("utf-8")) # UnicodeDecodeError発生
soup = BeautifulSoup(unicode(file_data, "utf-8", 'ignore').encode("utf-8"))
print soup.originalEncoding # utf-8で正しく認識される


ただ、これが正しいやり方なのかが分からない。
そもそもignoreオプションは割と使いまくるものなのだろうか。


ignoreオプションについては「速効!Python」に載っていた。
もっとちゃんと読もうと思った。

速効!Pythonプログラミング―バージョン2.5対応

速効!Pythonプログラミング―バージョン2.5対応

Pythonで1行メモVimスクリプト

先日に引き続きPythonVimに挑戦。
コマンドラインで書き込んだメモをファイルに蓄積していくVimプラグインを書いてみた。
画面に表示している内容とまったく関係ないふとした思いつきを書き込む。


:PyMemoAdd メモ

のような感じで使う。


pymemo.vim

if has('win32')
let s:vim_encoding = "shift_jis"
else
let s:vim_encoding = "utf-8"
endif
if !exists("g:pymemo_filepath")
let g:pymemo_filepath = $HOME . "\\pymemo.txt"
endif
if !exists("g:pymemo_loaded_python_file")
pyfile <sfile>:p:h/pymemo.py
python memo = PyMemo()
let g:pymemo_loaded_python_file=1
endif
command! -nargs=1 PyMemoAdd python memo.add_memo(<q-args>)


pymemo.py

# vim:set fileencoding=utf-8:
# メモ用Vimスクリプト
# 
# 指定されたファイルにどんどんメモを追加する
import vim
import sys
import codecs
import os
import re
import datetime
LINE_HEADER = "*"
UNI_FILE_HEADER = """vim:set filetype=rest:
= PyMemo
====================
"""
DATE_TEMPLATE = "[%Y-%m-%d %H:%M]"
class PyMemo():
def __init__(self):
self.vim_encoding = vim.eval("s:vim_encoding")
self._memo_path = None
def add_memo(self, text):
self._append_to_file(text)
print u"メモを追記しました".encode(self.vim_encoding)
def _enc_command(self, command):
vim.command(command.encode(self.vim_encoding))
def _enc_eval(self, command):
return vim.eval(command.encode(self.vim_encoding))
def _append_to_file(self, line):
day = datetime.datetime.now().strftime(DATE_TEMPLATE)
if not os.path.exists(self._get_memo_path()):
f = open(self._get_memo_path(), "w")
f.write(UNI_FILE_HEADER)
f.write("%s\n\n" % (day,))
else:
f = open(self._get_memo_path(), "a+")
f.write("%s %s %s\n" % (LINE_HEADER, line, day))
f.close()
def _get_memo_path(self):
if self._memo_path == None:
self._memo_path = vim.eval("g:pymemo_filepath")
return self._memo_path

BeautifulSoupで特定の属性値を持つタグを取得する

下記のようなXMLから rel="next" なタグの href 属性を取得する場合

<?xml version="1.0" encoding="utf-8"?>
<feed version="0.3" xml:lang="ja">
<title>Pirosukeのブックマーク</title>
<link rel="alternate" type="text/html" href="http://b.hatena.ne.jp/piro_suke/" />
<link rel="next" type="application/atom+xml" href="http://b.hatena.ne.jp/piro_suke/atomfeed?of=30" />
</feed>


下記のようなpython処理で取得できる

import urllib2
from BeautifulSoup import BeautifulSoup
res = urllib2.urlopen("XMLのURL").read()
soup = BeautifulSoup(res)
next_tag = soup.find('link', {"rel":'next'})
next_href = next_tag['href']

PythonでVim用のファイルブックマークプラグインを作る

初めてのVimスクリプトPythonで書いたが、
ほとんどVimの機能を呼び出しているだけになってしまった...
しかし楽しい。


環境は gVim7 + WinXP + Python2.5 + BeautifulSoup。


:PyBookmarkAdd でカレントバッファのファイルをブックマーク。
:PyBookmarkList でブックマークの一覧表示。Enterでファイルを開く。


以下の2ファイルをpluginsフォルダに配置する。


pybookmarks.vim

if has('win32')
let s:vim_encoding = "shift_jis"
else
let s:vim_encoding = "utf-8"
endif
if !exists('g:pybookmarks_file')
let g:pybookmarks_file = $HOME . "\\pybookmarks.xml"
endif
if !exists("g:pybookmarks_loaded_python_file")
pyfile <sfile>:p:h/pybookmarks.py
python pybookmarks = PyBookmarks()
let g:pybookmarks_loaded_python_file=1
endif
command! PyBookmarkList python pybookmarks.list_bookmark()
command! PyBookmarkAdd python pybookmarks.add_bookmark()
function! s:OpenSearchLine()
python pybookmarks.open_search_line()
endfunction


pybookmarks.py

# vim:set fileencoding=utf-8:
# ファイルブックマーク Vimスクリプト
# 
# XML形式のファイルにブックマークを保存
# BeautifulSoupを使って解析、編集
import vim
import sys
import codecs
import os
import re
import datetime
from BeautifulSoup import *
class PyBookmarks():
def __init__(self):
self.vim_encoding = vim.eval("s:vim_encoding")
self._xml_file = None
self._soup = None
def list_bookmark(self):
bname = "__PYBOOKMARKS__"
winnum = vim.eval('bufwinnr("%s")' % (bname,))
if vim.eval("winnr()") != winnum:
if winnum != "-1":
vim.command("exe %swincmd w" % (winnum,))
self._open_window(bname)
vim.command("setlocal bt=nofile bh=delete noswf")
# Enter時にOpenSearchLineコマンドを呼び出す
vim.command("nnoremap <silent> <buffer> <CR> :call <SID>OpenSearchLine()<CR>")
b = vim.current.buffer
vim.command("redraw!")
vim.command("setlocal modifiable")
line_format = "%s | %s | %s"
# バッファにXMLファイルの内容を一覧表示
idx = 1
soup = self._get_soup()
for bookmark in soup('bookmark'):
bookmark_href = re.escape(bookmark['href'])
self._enc_command('let b:file{%d} = "%s"' % (idx, bookmark_href))
line = line_format % (bookmark['title'], bookmark_href, bookmark['create_date'])
if vim.eval('line("$")') != "1" or vim.eval('getline("1")') != '':
self._enc_command('call append("$", "%s")' % (line,))
else:
self._enc_command('call setline("1", "%s")' % (line,))
idx = idx + 1
vim.command("setlocal nomodifiable")
vim.command("redraw!")
def add_bookmark(self):
path = vim.current.buffer.name
if path is not None:
path = path.decode(self.vim_encoding)
input = 'input("%s", "%s")' % (u'タイトル:', os.path.basename(path))
title = self._enc_eval(input)
if title is not None:
soup = self._get_soup()
title = title.decode(self.vim_encoding)
if title == "":
title = u"無名ブックマーク"
bookmark = Tag(soup, "bookmark")
bookmark['title'] = title
#bookmark.append(title)
bookmark['href'] = path
bookmark['create_date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
soup.bookmarks.append(bookmark)
self._overwrite_xml(soup.prettify())
print u"ブックマークを追加しました".encode(self.vim_encoding)
else:
print u"ファイルまたはディレクトリが開かれていません".encode(self.vim_encoding)
def open_search_line(self):
line_num = vim.eval('line(".")')
bookmark_href = vim.eval('b:file{%s}' % (line_num,))
bookmark_href = re.escape(bookmark_href)
self._eliminate_window(vim.eval('bufname("%")'))
self._open_window(bookmark_href)
def _get_xml_path(self):
if self._xml_file == None:
self._xml_file = vim.eval("g:pybookmarks_file")
return self._xml_file
def _get_soup(self):
if self._soup == None:
xml_file = self._get_xml_path()
if os.path.exists(xml_file):
f = open(xml_file)
bxml = f.read()
f.close()
self._soup = BeautifulStoneSoup(bxml, fromEncoding="UTF-8", selfClosingTags='bookmark')
else:
self._soup = BeautifulStoneSoup('<?xml version="1.0" encoding="UTF-8"?>\n<bookmarks></bookmarks>', selfClosingTags='bookmark')
return self._soup
def _overwrite_xml(self, contents):
xml_file = self._get_xml_path()
f = open(xml_file, "w")
f.write(contents)
f.close()
def _enc_command(self, command):
vim.command(command.encode(self.vim_encoding))
def _enc_eval(self, command):
return vim.eval(command.encode(self.vim_encoding))
def _open_window(self, filepath):
self._eliminate_window(filepath)
cmd = "new"
if vim.eval('expand("%:p") == "" && &modified == 0 && !has("vim_starting")') == "1":
cmd = "edit"
#vim.command('silent exe "%s ++enc=%s ++ff=&ff %s"' % (cmd, "utf-8", filepath))
self._enc_command('silent exe "%s %s"' % (cmd, filepath))
def _eliminate_window(self, name):
"""指定したバッファ名のウィンドウをすべて閉じる"""
nr = vim.eval('bufwinnr("%s")' % (name,))
while nr != "-1":
vim.command('%swincmd w' % (nr,))
if self._safe_close() == 0:
break
nr = vim.eval('bufwinnr("%s")' % (name,))
def _safe_close(self):
retval = 0
if vim.eval('&modified') != "-1":
vim.command('silent! enew')
if self._get_win_num() > 1:
vim.command("close")
retval = 1
return retval
def _get_win_num(self):
winnum = 1
nr = vim.eval('winnr()')
vim.command('silent! exe "wincmd \\<C-w>"')
while nr != vim.eval('winnr()'):
winnum = winnum + 1
vim.command('silent! exe "wincmd \\<C-w>"')
return winnum


下記のサイトのVimスクリプトを参考にさせていただきました。
http://sworddancer.funkyboy.jp/howm_vim/
http://www.vim.org/scripts/script.php?script_id=165

PythonでVim拡張


同じモノを作り続けると飽きるので、気分転換にVimscriptを書き始めた。
自分のエディタが拡張できるのはかなり楽しい。
ひとまず登録、変更、削除、一覧表示ができるようにファイルブックマークプラグインでも作る。


ん、これはVimscriptを書いているのではなく、PythonVimプラグインを書いている、ということか。



Web+DBプレスにEmacs特集が載っててまた浮気心でクラクラしたけど、EmacsにあってVimにない機能は自分で作ってみても良いのではないかと思い直した。
PythonにはEmacsよりもVimではないかという気もすることだし。

SQLObjectで昔作ったデータベースのデータを取り出してCSVファイルに落とす

データベースを移行する作業があったので、PythonのORマッパ、SQLObjectを使ってみた。
ついでにCSVファイルに出力する標準モジュールも使ってみる。
mysqlのコマンドだけでできる作業のような気がしないでもない...。
まあ、pythonでデータを操作できるし嬉しい、ということで。


SQLObjectの情報は、djangoでもお世話になったこちらのサイトから
http://ymasuda.jp/python/sqlobject/doc_0.7/SQLObject.html


SQLObjectモジュールはPython Cheese Shopからダウンロードしてeasy_installした。
http://cheeseshop.python.org/pypi/SQLObject/0.7.8


csvモジュールの情報はここから
http://www.python.jp/doc/2.4/lib/module-csv.html


SQLObjectがデータベースとテーブル名を指定するだけで自動的に
テーブル構造からモデルのプロパティを作成してくれることに感動。


どちらかというとCSVで日本語を出力する方が良く分からなくて苦労した。


oldtables.py

# vim: fileencoding=cp932
from sqlobject import *
OLD_DB_NAME = 'dbname'
OLD_DB_USER = 'username'
OLD_DB_PASS = 'password'
oldcon = connectionForURI("mysql://%s:%s@localhost/%s" % (OLD_DB_USER, OLD_DB_PASS, OLD_DB_NAME))
sqlhub.processConnection = oldcon
# companyテーブルからモデルを生成
class Company(SQLObject):
class sqlmeta:
fromDatabase = True
# shohinテーブルからモデルを生成
class Shohin(SQLObject):
class sqlmeta:
fromDatabase = True


データベースはmysql3.23で、文字コードshift_jis

output_csv.py

# vim: fileencoding=cp932
import csv
import os
import codecs
from sqlobject import *
from oldtables import *
# カレントディレクトリを取得
output_dir = os.getcwd()
def output_shohin():
row_list = Shohin.select()
file = codecs.open(os.path.join(output_dir, "shohin.csv"), "wb", 'cp932')
writer = csv.writer(file)
for shohin in row_list:
line = [
str(shohin.id),
str(shohin.companyID),
shohin.shohinName,
str(shohin.uriTanka),
]
writer.writerow([unicode(value, "cp932") for value in line])
print "shohin出力完了:" + str(row_list.count())
if __name__ == "__main__":
output_shohin()


CSVに出力するデータを日本語に変換するところでリスト内包表記を使ってみたけど、
もっとシンプルな書き方がある気がしてならない。他の箇所もそうだけど。

Pythonで辞書を要素とする配列を特定のキーでソート

探したけど見つからなかった。
初めてのlambda。

ranking_list = []
ranking_list.append({"uriage": 30, "shiire": 5, "zairyo": 20 })
ranking_list.append({"uriage": 20, "shiire": 10, "zairyo": 10 })
ranking_list.append({"uriage": 10, "shiire": 15, "zairyo": 30 })
# zairyoが高い順にソート
ranking_list.sort(lambda a, b : cmp(b["zairyo"], a["zairyo"]))
print ranking_list
# shiireが低い順にソート
ranking_list.sort(lambda a, b : cmp(a["shiire"], b["shiire"]))
print ranking_list


<追記>
Python2.4以降ではもっと短い書き方があるらしい。
ググりんぐレベルが足りなかったかー


コメント欄または下記参照
http://d.hatena.ne.jp/bonlife/20070823

Pythonで数値に3桁毎にカンマを入れる

あちこち探して、結局下のエントリと同じdecimalパッケージの
マニュアルにサンプルとして掲載されているのを発見。


ここのmoneyfmt関数。
http://www.m-takagi.org/docs/python/lib/decimal-recipes.html

print moneyfmt(Decimal("2000"), 0, dp="") # 2,000


整数の場合はdpを空にしないと.が表示されてしまう。
他にも通貨記号を表示してくれたり、便利な関数のようだ。

Pythonで10進数計算したり数値を丸めたりする

指定した桁で切り捨てたり四捨五入したり、
2進数だと丸め誤差が生じる計算を正しく行うための標準ライブラリ。

from decimal import *
x = Decimal("2.467")
y = Decimal("3.512")
# 小数点以下を切捨て
print Decimal(x).quantize(Decimal('1.'), rounding=ROUND_DOWN) # 2
print Decimal(y).quantize(Decimal('1.'), rounding=ROUND_DOWN) # 3
# 小数点以下を切り上げ
print Decimal(x).quantize(Decimal('1.'), rounding=ROUND_UP) # 3
print Decimal(y).quantize(Decimal('1.'), rounding=ROUND_UP) # 4
# 小数点以下を四捨五入
print Decimal(x).quantize(Decimal('1.'), rounding=ROUND_HALF_UP) # 2
print Decimal(y).quantize(Decimal('1.'), rounding=ROUND_HALF_UP) # 4
# 小数点2桁で四捨五入
print Decimal(x).quantize(Decimal('.00'), rounding=ROUND_HALF_UP) # 2.47
print Decimal(y).quantize(Decimal('.00'), rounding=ROUND_HALF_UP) # 3.51

丸め用の定数は上記の3つ(ROUND_UP、ROUND_DOWN、ROUND_HALF_UP)以外にも
ROUND_CEILING, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVENがあるようだが、ちがいが良く分からない。


Pythonライブラリリファレンス
http://www.python.jp/doc/nightly/lib/module-decimal.html

PythonでReportLabでPDF出力

どうやらPythonのPDF生成はReportLabがスタンダードっぽいので使う。
Unicodeでの縦書きが難しかったのでDjangoで表示する方法メモ。


views.py

...
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.platypus import BaseDocTemplate, Table, Spacer, TableStyle, Frame, PageTemplate, Paragraph, Image
from reportlab.lib import pagesizes, colors
from reportlab.lib.styles import ParagraphStyle
...
def create_pdf(request):
if request.method == "POST":
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'inline;'
fontname = "HeiseiKakuGo-W5"
pdfmetrics.registerFont(UnicodeCIDFont(fontname)) #横書き
vencoding = "UniJIS-UCS2-V"
fontnameV = fontname + "-" + vencoding
fontV = UnicodeCIDFont(fontname, isVertical=True)
fontV.name = fontnameV
fontV.fontName = fontnameV
pdfmetrics.registerFont(fontV) #縦書き
doc = BaseDocTemplate(
response,
leftMargin=10,
rightMargin=10,
topMargin=10,
bottomMargin=10,
pagesize = pagesizes.A4[::-1]
) #A4横型
elementList = []
# 色々ParagraphやらTableやらをelementListに追加
frame1 = Frame(x1=doc.leftMargin, y1=doc.height/2 + doc.bottomMargin,
width=doc.width, height=doc.height/2, id="f1", showBoundary=1)
frame2 = Frame(x1=doc.leftMargin, y1=doc.bottomMargin,
width=doc.width, height=doc.height/2, id="f2", showBoundary=1)
page = PageTemplate(id="p1", frames=[frame1, frame2])
doc.addPageTemplates([page])
doc.build(elementList)
return response


まだ良く分からない。


Python クックブック 第2版

Python クックブック 第2版