Home‎ > ‎

Pisa

Pisa は HTML/XHTML と CSS から PDF を生成するものです。2010年2月1日時点では、まだ日本語に対応しておらず、フォントとしては Courier, Helvetica, Times, Symbol 程度しか扱えないものです。しかし基盤としては ReportLab を利用しており、若干の修正を加えることで、日本語の出力が可能になります。

CID フォントを使う

$PISADIR/sx/pisa3/pisa_context.py に手を入れます。Unix/Linux で CID フォントを使用するだけなら knock氏が書かれているように 次の修正を加えるだけです。

--- pisa_context.py.orig    2010-02-02 13:30:36.904441000 +0900
+++ pisa_context.py    2010-02-02 13:32:48.014971000 +0900
@@ -26,6 +26,7 @@
 
 from reportlab.pdfbase import pdfmetrics
 from reportlab.pdfbase.ttfonts import TTFont
+from reportlab.pdfbase.cidfonts import UnicodeCIDFont
 from reportlab.lib.fonts import addMapping
 
 from sx.w3c import css, cssDOMElementInterface
@@ -421,6 +422,8 @@
         self.warn = 0     
         self.text = u""
         self.uidctr = 0
+        self.loadCIDFont(["HeiseiMin-W3", "mincho"], "HeiseiMin-W3")
+        self.loadCIDFont(["HeiseiKakuGo-W5", "gothic"], "HeiseiKakuGo-W5")
         self.multiBuild = False
 
         self.pageSize = A4
@@ -1047,4 +1050,30 @@
                     log.warning(self.warning("wrong attributes for <pdf:font>"))
    
             except Exception:
-                log.warn(self.warning("Loading font '%s'", fontName), exc_info=1)               
+                log.warn(self.warning("Loading font '%s'", fontName), exc_info=1)
+
+    def loadCIDFont(self, names, src):
+        if names and src:
+            if type(names) is types.ListType:
+                fontAlias = names
+            else:
+                fontAlias = [x.lower().strip() for x in names.split(",") if x]
+            # XXX Problems with unicode here
+            fontAlias = [str(x) for x in fontAlias]
+            src = str(src)
+            fontName = fontAlias[0]
+            try:
+                if fontName in self.fontList:
+                    self.warning("Repeated font embed for %s, skip new embed" % fontName)
+                else:
+                    self.registerFont(fontName, fontAlias)
+                    #font = UnicodeCIDFont(fontName, src)
+                    font = UnicodeCIDFont(fontName, False, True)
+                    pdfmetrics.registerFont(font)
+                    #addMapping(fontName, 0, 0, fontName)  # normal
+                    #addMapping(fontName, 0, 1, fontName)  # italic
+                    #addMapping(fontName, 1, 0, fontName)  # bold
+                    #addMapping(fontName, 1, 1, fontName)  # bold italic
+            except Exception, e:
+                log.warn(self.warning("Loading font '%s'", fontName), exc_info=1)
+

TrueType フォントを使う

TrueType フォントの使用は、*.ttf についてはもともと考慮されています。ただし *.ttc については考慮されていないようなので、これも受け付けるようにして、subfontIndex を追加できるようにしてやります。

ReportLab 全般(だけかどうかまでは知らない)で起こるフォントの問題があります。Windows ではなぜか HeiseiMin-W3 が MS P明朝 へ関連付けられています。「HeiseiMin-W3 だから Monospaced Font だよな」と思って作られた PDF (文字の位置が固定ピッチで指定されています) の各文字が、実際のグリフ描画では Proportional を想定した文字幅となってしまい、結果として文字間隔がバラバラ(幅がバラバラな文字が固定ピッチで並ぶ)という珍妙なことになります(右図)。そこで、Windows 上で閲覧させる場合、PDF には Windows でおかしな変換をされないようなフォント指定を含めてやる必要があります。

Windows 上で PDF 生成する場合、MS 明朝 などの TrueType フォントを追加するなら、次のようにします。pisaFileObject を使用することと、subfontIndex を指定することがポイントです。これで、CSS などで日本語のフォントを指定できるようになります。なお、ここでは OCRB フォントも追加しています。OCRB フォントを利用できない環境の場合は、非商業利用可能な ocrb10会津若松市製 OCRB フォントを使うなどすればよいでしょう。

--- pisa_context.py.orig    2010-06-23 16:03:12.473267100 +0900
+++ pisa_context.py    2010-06-23 16:04:59.815464600 +0900
@@ -36,6 +36,7 @@
 
 from reportlab.pdfbase import pdfmetrics
 from reportlab.pdfbase.ttfonts import TTFont
+from reportlab.pdfbase.cidfonts import UnicodeCIDFont
 from reportlab.lib.fonts import addMapping
 
 from sx.w3c import css, cssDOMElementInterface
@@ -431,6 +432,32 @@
         self.warn = 0
         self.text = u""
         self.uidctr = 0
+        self.loadCIDFont(["HeiseiMin-W3", "mincho"], "HeiseiMin-W3")
+        self.loadCIDFont(["HeiseiKakuGo-W5", "gothic"], "HeiseiKakuGo-W5")
+        self.loadFont(
+                ["MS Mincho", u"MS 明朝", "msmincho"],
+                pisaFileObject("msmincho.ttc", basepath=r"C:\Windows\Fonts"),
+                encoding="UniJIS-UCS2-HW-H", subfontIndex=0)
+        self.loadFont(
+                ["MS PMincho", u"MS P明朝", "mspmincho"],
+                pisaFileObject("msmincho.ttc", basepath=r"C:\Windows\Fonts"),
+                encoding="UniJIS-UCS2-HW-H", subfontIndex=1)
+        self.loadFont(
+                ["MS Gothic", u"MS ゴシック", "msgothic"],
+                pisaFileObject("msgothic.ttc", basepath=r"C:\Windows\Fonts"),
+                encoding="UniJIS-UCS2-HW-H", subfontIndex=0)
+        self.loadFont(
+                ["MS PGothic", u"MS Pゴシック", "mspgothic"],
+                pisaFileObject("msgothic.ttc", basepath=r"C:\Windows\Fonts"),
+                encoding="UniJIS-UCS2-HW-H", subfontIndex=1)
+        self.fontList["Times"] = "MS Mincho"
+        self.fontList["Helvetica"] = "MS Gothic"
+        # for pdf:barcode
+        self.loadFont(
+                ["OCRB",],
+                pisaFileObject("ocrb.ttf", basepath=r"C:\Windows\Fonts"),
+                encoding="ascii")
+
         self.multiBuild = False
 
         self.pageSize = A4
@@ -955,7 +982,7 @@
         for a in alias:
             self.fontList[str(a)] = str(fontname)
 
-    def loadFont(self, names, src, encoding="WinAnsiEncoding", bold=0, italic=0):
+    def loadFont(self, names, src, subfontIndex=0, encoding="WinAnsiEncoding", bold=0, italic=0):
 
         # XXX Just works for local filenames!
         if names and src: # and src.local:
@@ -980,7 +1007,7 @@
 
             try:
 
-                if suffix == "ttf":
+                if suffix in ("ttf", "ttc"):
 
                     # determine full font name according to weight and style
                     fullFontName = "%s_%d%d" % (fontName, bold, italic)
@@ -992,13 +1019,13 @@
 
                         # Register TTF font and special name
                         filename = file.getNamedFile()
-                        pdfmetrics.registerFont(TTFont(fullFontName, filename))
+                        pdfmetrics.registerFont(TTFont(fontName, filename, subfontIndex=subfontIndex))
 
                         # Add or replace missing styles
                         for bold in (0, 1):
                             for italic in (0, 1):
                                 if ("%s_%d%d" % (fontName, bold, italic)) not in self.fontList:
-                                    addMapping(fontName, bold, italic, fullFontName)
+                                    addMapping(fontName, bold, italic, fontName)
 
                         # Register "normal" name and the place holder for style
                         self.registerFont(fontName, fontAlias + [fullFontName])
@@ -1058,3 +1085,28 @@
 
             except Exception:
                 log.warn(self.warning("Loading font '%s'", fontName), exc_info=1)
+
+    def loadCIDFont(self, names, src):
+        if names and src:
+            if type(names) is types.ListType:
+                fontAlias = names
+            else:
+                fontAlias = [x.lower().strip() for x in names.split(",") if x]
+            # XXX Problems with unicode here
+            fontAlias = [str(x) for x in fontAlias]
+            src = str(src)
+            fontName = fontAlias[0]
+            try:
+                if fontName in self.fontList:
+                    self.warning("Repeated font embed for %s, skip new embed" % fontName)
+                else:
+                    self.registerFont(fontName, fontAlias)
+                    #font = UnicodeCIDFont(fontName, src)
+                    font = UnicodeCIDFont(fontName, False, True)
+                    pdfmetrics.registerFont(font)
+                    #addMapping(fontName, 0, 0, fontName)  # normal
+                    #addMapping(fontName, 0, 1, fontName)  # italic
+                    #addMapping(fontName, 1, 0, fontName)  # bold
+                    #addMapping(fontName, 1, 1, fontName)  # bold italic
+            except Exception, e:
+                 log.warn(self.warning("Loading font '%s'", fontName), exc_info=1)



注: 上記 diff をこのページに添付してありますが、下のファイル pisa_context.py は行末の空白があちこちにあったので、まずそれらを削除してから diff をとっています。いきなり patch してもうまくいかないかもしれません。

こうしておいて CSS で font-family: 'MS P明朝' などとすると、まっとうな出力になります(下図)。
 
ま、PDF 生成する環境に msmincho.ttc などが必要、というのが問題かもしれません。代わりに IPAフォント / IPAexフォント を利用してもいいでしょう。

行の折り返し (word wrapping) を管理する

xhtml2pdf メーリングリストに Lasconic 氏が寄せたメッセージです。

Message-ID: <f91e4888-39c0-47cf-981d-09dde2d729f6@q32g2000yqb.googlegroups.com>
Date: Thu, 6 May 2010 06:17:21 -0700 (PDT)
From: lasconic <lasconic@gmail.com>
To: Pisa XHTML2PDF Support <xhtml2pdf@googlegroups.com>
Subject: [xhtml2pdf] Support for CJK wrapping in XHTML2PDF

Hi,
There is apparently no way to specify how the text should wrap from
the CSS or HTML, but pisa has a nice feature for wrapping CJK
languages and a style.wordWrap attribute. I create a patch and submit
it in this issue 63
I hope it can be added to the 3.0 branch for a next release.

Pisa is used to generate the PDF handbook of MuseScore in more than 15 languages included
Japanese, Chinese, Russian etc...

Lasconic

おまけ: バーコードを使う

ここ にあるパッチをあてると、ReportLab が本来持っている機能である、1次元バーコードの出力が可能になります。
サブページ (1): Pisa ドキュメント
ċ
pisa_context.py.diff
(5k)
Hideki HAYASI,
2010/06/23 0:08
Comments