2011/09/25

scan-file*

(defun remove-from-keyword-args (args &rest keywords)
(loop for (a b) on args by #'cddr
unless (member a keywords :test #'eq)
append (list a b)))

(series::defS scan-file* (name &rest args-for-open &key (reader #'read-line) &allow-other-keys)
"like scan-file. accept options for open."
(series::fragl ((name) (reader) (args-for-open)) ; args
((items t)) ; rets
((items t) ; aux
(lastcons cons (list nil))
(lst list))
() ; alt
((setq lst lastcons) ; prolog
(with-open-stream (f (apply #'open name :direction
:input (remove-from-keyword-args args-for-open :reader)))
(cl:let ((done (list nil)))
(loop
(cl:let ((item (cl:funcall reader f nil done)))
(when (eq item done)
(return nil))
(setq lastcons (setf (cdr lastcons) (cons item nil)))))))
(setq lst (cdr lst)))
((if (null lst) (go series::end)) ; body
(setq items (car lst))
(setq lst (cdr lst)))
() ; epilog
() ; wraprs
:context) ; impure
:optimizer
(series::apply-literal-frag
(cl:let ((file (series::new-var 'file)))
`((((reader)) ; args
((items t)) ; rets
((items t) (done t (list nil))) ; aux
() ; alt
() ; prolog
((if (eq (setq items (cl:funcall reader ,file nil done)) done) ; body
(go series::end)))
() ; epilog
((#'(lambda (code) ; wraprs
(list 'with-open-file
'(,file ,name :direction :input ,@(remove-from-keyword-args args-for-open :reader))
code)) :loop))
:context) ; impure
,reader)))) ; これは何?

2011/09/18

Heroku で Lokka するメモ

# rvm 関連
rvm use ruby-1.9.2
rvm gemset create sumire-lokka
rvm gemset use sumire-lokka
gem env
gem install bundler

# Lokka
gem install heroku bundler
git clone git://github.com/komagata/lokka.git
cd lokka
heroku create sumire
git push heroku master
heroku rake db:setup
heroku apps:open

# ローカルから Heroku を DB を反映
gem install taps
heroku db:push --confirm sumire sqlite://db/development.sqlite3

# メンテナンス画面のオン、オフ
heroku maintenance:on
heroku maintenance:off

「すごく簡単だ」

村上春樹「ダンス・ダンス・ダンス」より

2011/09/10

Rails3 セットアップメモ

rvm と bundler 使うと綺麗にセットアップできるな。 Common Lisp にもこういうの欲しい。

# rvm 関連
rvm use ruby-1.9.2
rvm gemset create sumire
rvm gemset use sumire
gem env
gem install bundler

# プロジェクトディレクトリの作成
mkdir sumire
cd sumire

# Gemfile 作成
cat > Gemfile
source 'http://rubygems.org'
gem 'rails', '3.1.0'
C-d

# bundle
bundle install --path vendor/bundle
bundle exec rails new .
# ↑でエラーが出るが気にせず次へ
bundle install

# 起動 http://localhost:3000/
bundle exec rails s

2011/08/29

Rucksack のパラレルなトランザクション

Rucksack はとても素敵なピュア Common Lisp のパーシステントシステムなんだけど、今の実装はトランザクションが全てシリアライズされて、同時に1トランザクションしか走ることがでない。

Rucksack のメーリングリストですでにパッチを持っている人がいるという話を出ていたけど、そのパッチが全然出てこない。素直に待てばいいかなと思いつつも、ついついなんとかできないかなと思っていじってしまった。

https://github.com/quek/rucksack/tree/parallel2

まず、再帰ロック sb-thread:with-recursive-lock を使っているので SBCL でしか動かない。

コミット時に他のトランザクションがすでに同じオブジェクトをコミットしてないか調べて、コミットされていたら transaction-conflict を投げる。次のように transaction-conflict で rucksack::retry をリスタートすれば、トランザクションがリトライされる。

(handler-bind ((transaction-conflict
(lambda (c)
(invoke-restart 'rucksack::retry))))
(with-transaction ()
...))

Rucksack のコードはきれいだと思った。既にパラレルなトランザクションのためのコードが書かれていて、それを利用することができた。

とりあえず、ちゃんと動いているようだけど、正しいという自信がない。もう少し様子を見てからパッチを投げるとかしたい。

2011/07/25

cl-pdf はもともと日本語 True Type フォントが使えたよ

cl-pdf はもともと日本語 True Type フォントが使えたよ。

unicode-readme.txt に書かれているとおりフォントメトリクスファイルを使ってやれば使えた。でも、 TakaoExMincho の方は表示されないグリフがあったりいろいろおかしい。

(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :cl-typesetting)
(ql:quickload :trivial-shell))

(defpackage :try-cl-typesetting
(:use :cl :typeset))

(in-package :try-cl-typesetting)

;; cl-pdf/unicode-readme.txt に書かれているとおり
;; ttf2pt1 を使ってフォントメトリクスファイルを作成する。
(defvar *ufm-dir* (ensure-directories-exist #p"/tmp/ufm/"))
(defvar *ttf2pt1* "/home/ancient/tmp/ttf2pt1-cl-pdf/ttf2pt1")
(let ((*default-pathname-defaults* (pathname *ttf2pt1*)))
(loop for font in '(#p"/usr/share/fonts/truetype/takao/TakaoExMincho.ttf"
#p"/usr/share/fonts/truetype/ttf-mplus/mplus-1m-regular.ttf")
for ufm = (merge-pathnames *ufm-dir* (pathname-name font))
do (trivial-shell:shell-command
(format nil "~a -a -F ~a ~a" *ttf2pt1* font ufm))
(pdf:load-ttu-font (merge-pathnames ufm "x.ufm") font)))
;; ↑ちょっと時間がかかる。


(defun foo (&optional (file #P"/tmp/hello.pdf"))
(pdf:with-document ()
(pdf:with-page ()
(let* ((text "羊獏駱駝 あいう 123 ABCDEFGHIJKLMN abcdefghijklmn. 第9条 日本国民は、正義と秩序を基調とする国際平和を誠実に希求し、国権の発動たる戦争と、武力による威嚇又は武力の行使は、国際紛争を解決する手段としては、永久にこれを放棄する。2 前項の目的を達するため、陸海空軍その他の戦力は、これを保持しない。国の交戦権は、これを認めない。")
(content
(compile-text ()
(vspace 100)
(paragraph (:h-align :left :font "TakaoExMincho" :font-size 18)
text)
(paragraph (:h-align :left :font "mplus-1m-regular" :font-size 18)
text))))
(typeset::draw-block content 20 800 500 700)))
(pdf:write-document file))
(trivial-shell:shell-command (format nil "evince ~a" file)))

(foo)

iText の iTextAsian.jar にフォントを追加する

Java の iText の iTextAsian.jar にフォントを追加するこころみ。本当にやりたいことは MS ゴシック等をうめこまずに使うこと。

iTextAsian.jar の中を見てみると、フォント毎のメトリクス情報が書かれたプロパティファイル、 cjkfonts.properties というフォント名とそのエンコーディングを書いたファイルが必要になることがわかる。

cjkfonts.properties の方は他の行をコピーすればよい。自分のソースディレクトリに com.lowagie.text.pdf.fonts パッケージを作成し、 cjkfonts.properties をコピーして、一番下に TakaoPGothic を足した。

# Supported CJK fonts and encodings
HeiseiMin-W3=Adobe-Japan1-UCS2_UniJIS-UCS2-H_UniJIS-UCS2-V_UniJIS-UCS2-HW-H_UniJIS-UCS2-HW-V_
HeiseiKakuGo-W5=Adobe-Japan1-UCS2_UniJIS-UCS2-H_UniJIS-UCS2-V_UniJIS-UCS2-HW-H_UniJIS-UCS2-HW-V_
KozMinPro-Regular=Adobe-Japan1-UCS2_UniJIS-UCS2-H_UniJIS-UCS2-V_UniJIS-UCS2-HW-H_UniJIS-UCS2-HW-V_

STSong-Light=Adobe-GB1-UCS2_UniGB-UCS2-H_UniGB-UCS2-V_
STSongStd-Light=Adobe-GB1-UCS2_UniGB-UCS2-H_UniGB-UCS2-V_

MHei-Medium=Adobe-CNS1-UCS2_UniCNS-UCS2-H_UniCNS-UCS2-V_
MSung-Light=Adobe-CNS1-UCS2_UniCNS-UCS2-H_UniCNS-UCS2-V_
MSungStd-Light=Adobe-CNS1-UCS2_UniCNS-UCS2-H_UniCNS-UCS2-V_

HYGoThic-Medium=Adobe-Korea1-UCS2_UniKS-UCS2-H_UniKS-UCS2-V_
HYSMyeongJo-Medium=Adobe-Korea1-UCS2_UniKS-UCS2-H_UniKS-UCS2-V_
HYSMyeongJoStd-Medium=Adobe-Korea1-UCS2_UniKS-UCS2-H_UniKS-UCS2-V_

TakaoPGothic=Adobe-Japan1-UCS2_UniJIS-UCS2-H_UniJIS-UCS2-V_UniJIS-UCS2-HW-H_UniJIS-UCS2-HW-V_

問題はフォント毎のメトリクス情報が書かれたプロパティファイルの方。いろいろもがいて com.lowagie.text.pdf.TrueTypeFontUnicode を使って作成することにした。次のようなような感じ。

package com.lowagie.text.pdf;

import java.io.IOException;
import java.io.PrintStream;

import com.lowagie.text.DocumentException;

public class FontMetrics extends TrueTypeFontUnicode {

public static void main(String[] args) throws Exception {
// 使いたいフォント。ttc の場合は foo.ttc,0 のようにインデックス指定する。
String fontPath = "/usr/share/fonts/truetype/ttf-japanese-gothic.ttf";
FontMetrics fm = new FontMetrics(fontPath);
// プロパティファイルの出力先
String dir = "/home/ancient/letter/java/eclipse-workspace/itext/src/com/lowagie/text/pdf/fonts/";
PrintStream out = new PrintStream(dir + fm.getPostscriptFontName()
+ ".properties");
fm.dump(out);
out.close();
}

public FontMetrics(String ttFile) throws DocumentException, IOException {
super(ttFile, "Identity-H", true, null, true);
}

public void dump(PrintStream out) throws DocumentException {
CJKFont cjkFont = new CJKFont("HeiseiKakuGo-W5", "UniJIS-UCS2-HW-H",
false);
System.out.println(getPostscriptFontName() + ".properties");

out.println("Flags=" + getFlags());
out.println("FontBBox=[" + getFontDescriptor(BBOXLLX) + " "
+ getFontDescriptor(BBOXLLY) + " " + getFontDescriptor(BBOXURX)
+ " " + getFontDescriptor(BBOXURY) + "]");
out.println("ItalicAngle=" + getFontDescriptor(ITALICANGLE));
out.println("Ascent=" + getFontDescriptor(ASCENT));
out.println("Descent=" + getFontDescriptor(DESCENT));
out.println("CapHeight=" + getFontDescriptor(CAPHEIGHT));
out.println("StemV=80");
out.println("Registry=Adobe");
out.println("Ordering=Japan1");
out.println("Supplement=4");

out.print("W=");
for (int i = 0; i < 0xffff; ++i) {
int width = getWidth(i);
int cid = cjkFont.getCidCode(i);
if (width != 0 && width != 1000) {
out.print("" + cid + " " + width + " ");
}
}
out.println();
out.println("W2=");
}

public int getFontDescriptor(int key) {
return (int) getFontDescriptor(key, 1000);
}

public int getFlags() {
int flags = 0;
if (isFixedPitch)
flags |= 1;
flags |= fontSpecific ? 4 : 32;
if ((head.macStyle & 2) != 0)
flags |= 64;
if ((head.macStyle & 1) != 0)
flags |= 262144;
return flags;
}
}

これで、次のように使えるようになったと思う。

Font font = new Font(BaseFont.createFont("TakaoPGothic",
"UniJIS-UCS2-HW-H", BaseFont.NOT_EMBEDDED), 9, Font.NORMAL);

2011/07/24

iText の iTextAsian.jar にフォントを追加するためのメモ

これは完全に不完全なメモです。 iText の iTextAsian.jar にフォントを追加する を参照してください。

[ヅラド] iText にて iTextAsianCmaps.jar と iTextAsian.jar を使った日本語フォント描画 を参考にさせていただきました。ありがとうございます。

package com.lowagie.text.pdf;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Font;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Rectangle;

public class Que {

public static void main(String[] args) throws Exception {
a7();
tt();
}

private static void tt() throws DocumentException, IOException {
CJKFont j = new CJKFont("HeiseiKakuGo-W5", "UniJIS-UCS2-HW-H", false);
System.out.println("" + j.getCidCode('M') + " " + j.getWidth('M'));
TrueTypeFontUnicode f = new TrueTypeFontUnicode(
"/usr/share/fonts/truetype/ttf-japanese-gothic.ttf",
"Identity-H", true, null, true);
for (int i = 0; i < 0xffff; ++i) {
int width = f.getWidth(i);
int cid = j.getCidCode(i);
if (width != 0 && width != 1000) {
// System.out.println(" " + i + " " + cid + " " + width);
// W=
System.out.print(" " + cid + " " + width);
}
}
// System.out.println(f);
}

public static void a7() throws Exception {
byte[] a7 = createA7();

out(a7, "/tmp/20070802_a7.pdf");
}

private static byte[] createA7() throws Exception {

// ゴシック体 HeiseiKakuGo-W5
Font font3 = new Font(BaseFont.createFont("TakaoPGothic",
"UniJIS-UCS2-HW-H",
// "Identity-H",
BaseFont.NOT_EMBEDDED), 9, Font.NORMAL);

// DPI
float dpi = 72.0f;

// A7作成
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
Rectangle pagesize2 = PageSize.A7;
// 左端のマージン: 5ミリメートル
// 右端のマージン: 10ミリメートル
// 上端のマージン: 5ミリメートル
// 下端のマージン: 10ミリメートル
float left = mm2pixel(1f, dpi);
float right = mm2pixel(5f, dpi);
float top = mm2pixel(5f, dpi);
float bottom = mm2pixel(10f, dpi);
Document document2 = new Document(pagesize2, left, right, top, bottom);
PdfWriter writer2 = PdfWriter.getInstance(document2, baos2);
document2.open();

String text3 = "あい う え おアイウエオABCABC MIXlix";
Paragraph p3 = new Paragraph(text3, font3);
document2.add(p3);

document2.close(); // close を忘れないで
return baos2.toByteArray();
}

private static float mm2pixel(float mm, float dpi) {
// Ref. http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%B3%E3%83%81
// 国際インチ(international inch): 1 インチ = 25.4 ミリメートル
return mm * dpi / 25.4f;
}

private static void out(byte[] pdfdata, String file) throws Exception {
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
os.write(pdfdata);
os.flush();
}
}