パスワードを忘れた? アカウント作成
84113 journal

C0FFEEの日記: サーブレットで日本語フォントを用いた画像生成を行う

日記 by C0FFEE

マイグレーションのために複数のJREバージョン、複数の要件が発生して面倒だった。
運用ポリシーやJREアップデートなど制約によって選択肢は異なるものの、
以下の5つの点を確認することで大体解決したのでメモ。

■画像を出力するための環境設定
X11を起動出来ない環境におけるJava1.3以前のサーブレットは、PJA Toolkitをクラスパスに追加する必要があった。
Xvfbという仮想フレームバッファを使う方法もあるが、導入の手軽さで周囲ではPJA Toolkitのほうが採用実績が多かった。
PJA Toolkit(開発元eTeksのページ)はjava.awtを上書きするライブラリで、CLASSPATHに追加するだけで利用できる。
1.4以降は標準でヘッドレスがサポートされているので、起動オプションに"-Djava.awt.headless=true"を加えるだけで済む。

■フォントを指定するための環境設定
{Javaインストールディレクトリ}/jre/lib/fontsにttfファイルを置くのが手っ取り早く確認可能な点では確実な解決策。
Java5では{Javaインストールディレクトリ}/jre/lib/fontsにfallbackディレクトリを作成、
シンボリックリンクを張る方法が簡単で可搬性も確保しやすい。
Java1.4以前、あるいはJava5以降でもfallbackを使わない場合フォント構成ファイルを変更しなければいけないが、
Solaris9上のサーブレットにおいては前述のように/jre/lib/fontsに設置可能なら必須ではない。

アプリケーションにフォントを組み込んで配布する等で環境設定に制約がある場合には、Font.createFontを使う。
Java1.4ではcreateFont(int, java.io.InputStream)しか使えないため1.5(Java5)以降のcreateFont(int, java.io.File)で実装されたコードがエラーになるので注意。

参考:フォント構成ファイル font.properties(fontconfig.properties)の説明
Java1.3 font.properties ファイルの編集
Java1.4 font.properties ファイル
Java5.0 フォント構成ファイル

■各言語に対応、使えるフォントを増やす
(1)日本語フォント以外を使う
Gallery of Unicode Fontsを参照する。

(2)ttcファイルの場合ttfに戻す
使いたいフォントがTTFではなくTTC (TrueTypeコレクション) ファイルだった場合はBREAKTTCを使って分解する。
WindowsのコマンドアプリPerlスクリプトがある。

■物理フォントと論理フォントの指定
Font fontset = new Font(フォント名,フォント形状,フォントサイズ);
論理フォントは「Serif」「SansSerif」「Monospace」「Dialog」「DialogInput」の5書体で、
1.5(Java5)までは記法で区別がつかなかったが、1.6(Java6)以降は定数で区別がつくようになった。
「Font.SERIF」「Font.SANS_SERIF」「Font.MONOSPACED」「Font.DIALOG」「Font.DIALOG_INPUT」
社内サーブレットに関しては定数指定の有用性は希薄かも知れない。

■環境確認用サンプルソース
とりあえず必要な情報を確認するために簡単なJSPを配置してみる。

システムプロパティJSP:現状のJREのバージョン、CLASSPATH等を確認

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.awt.*,java.awt.image.*,com.sun.image.codec.jpeg.*,java.util.*" %>
<html>
<body>
<table><%Iterator it = System.getProperties().keySet().iterator();
while (it.hasNext()) {
String key = (String) it.next();%>
<tr>
<td><%out.print(key);%></td>
<td><%out.println(System.getProperty(key));%></td>
</tr><%}%></table>
</body>
</html>

フォント確認用JSP:指定可能なフォントと設定変更後の反映を確認

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.awt.*,java.awt.image.*,com.sun.image.codec.jpeg.*,java.util.*" %>
<html>
<body>
<table><% GraphicsEnvironment graphicsEnvironment= GraphicsEnvironment.getLocalGraphicsEnvironment();
Font[] fonts = graphicsEnvironment.getAllFonts();
for (int i=0; i<fonts.length; i++) {%>
<tr>
<td><%out.println(fonts[i]);%></td>
</tr><%}%>
</table>
</body>
</html>

描画確認用JSP(1):ヘッドレスモードで画像が生成できる事を確認

<%@ page contentType="image/jpeg" pageEncoding="UTF-8" import="java.awt.*,java.awt.image.*,com.sun.image.codec.jpeg.*" %><%
response.setContentType( "image/jpeg" );
    BufferedImage image = new BufferedImage(20,20,BufferedImage.TYPE_INT_RGB);
    Graphics graphics = image.getGraphics();
    graphics.setColor(new Color(255,255,255));
    graphics.fillRect(0,0,200,200);
    graphics.setColor(Color.BLACK);
    graphics.setFont(new Font("IPAGothic",Font.PLAIN,18));
    graphics.drawString( "漢",1,18);
    JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(response.getOutputStream());
    JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);
    param.setQuality((float)1.0, false);
    encoder.setJPEGEncodeParam(param);
    encoder.encode(image);
    graphics.dispose(); %>

描画確認用JSP(2):Jar,Warで配布する場合等createFont(cinecaption.ttf)

<%@ page contentType="image/jpeg" pageEncoding="UTF-8" import="java.net.*,java.awt.*,java.awt.image.*,com.sun.image.codec.jpeg.*,java.io.File" %><%
response.setContentType( "image/jpeg" );
InputStream is = null;
Font font = null;
URL ttf= new File( application.getRealPath("/WEB-INF/fonts/" + "cinecaption.ttf" ) ).toURL();
 
  try {
    is = ttf.openStream();
    font = Font.createFont( Font.TRUETYPE_FONT, is ).deriveFont(18.0f);
    is.close();
  }catch(IOException ioe) {
    ioe.printStackTrace();
  }catch(FontFormatException ffe) {
    ffe.printStackTrace();
  }finally{
    if(is!=null) {
      try{
        is.close();
      }catch(IOException ioex) {
        ioex.printStackTrace();
      }
    }
  }
 
    BufferedImage image = new BufferedImage(20,20,BufferedImage.TYPE_INT_RGB);
    Graphics graphics = image.getGraphics();
    graphics.setColor(new Color(255,255,255));
    graphics.fillRect(0,0,200,200);
    graphics.setColor(Color.BLACK);
    graphics.setFont(font);
    graphics.drawString( "漢",1,18);
    JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(response.getOutputStream());
    JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);
    param.setQuality((float)1.0, false);
    encoder.setJPEGEncodeParam(param);
    encoder.encode(image);
    graphics.dispose(); %>

[おまけ]PDF生成確認JSP:iText2.03+bcprov-jdk15-137.jarで暗号化 (参考1,参考2)

<%@ page language="java" import="java.net.*,java.io.*,java.text.*, java.util.*, java.util.regex.*,import java.awt.Color, com.lowagie.text.*, com.lowagie.text.pdf.*" %><%@ page contentType="application/pdf"%><%@ page pageEncoding="UTF-8" %><%
 
response.setContentType( "application/pdf" );
Document document = new Document(PageSize.A4.rotate());
 
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance( document, buffer );
writer.setPdfVersion(PdfWriter.VERSION_1_5);
            float width = document.getPageSize().width();
            float height = document.getPageSize().height();
writer.setEncryption( null , ("password").getBytes() , PdfWriter.ALLOW_PRINTING, PdfWriter.ENCRYPTION_AES_128);
 
document.addTitle("DocumentTitle foobar");
document.addAuthor("Author foobar");
document.addSubject("Subject foobar");
document.addCreator("Creator foobar" );
document.addKeywords("Keyword foobar");
document.setPageSize(PageSize.A4.rotate());
 
document.open();
 
String text = "漢字①~";
String text_cn = "联网语闻";
 
BaseFont bfg=BaseFont.createFont("HeiseiKakuGo-W5","UniJIS-UCS2-HW-H",false);
Font font12g=new Font(bfg,12);
Paragraph pg = new Paragraph(text, font12g);
document.add(pg);
 
BaseFont bfm=BaseFont.createFont("HeiseiMin-W3","UniJIS-UCS2-HW-H",false);
Font font12m=new Font(bfm,12);
Paragraph pm = new Paragraph(text, font12m);
document.add(pm);
 
BaseFont bfcn=BaseFont.createFont("STSong-Light","UniGB-UCS2-H",false);
Font font12cn=new Font(bfcn,12);
Paragraph pcn = new Paragraph(text_cn, font12cn);
document.add(pcn);
 
document.close();
 
DataOutput output = new DataOutputStream( response.getOutputStream() );
byte[] bytes = buffer.toByteArray();
response.setContentLength(bytes.length);
for( int i = 0; i < bytes.length; i++ ) { output.writeByte( bytes[i] ); }
%>

□その他補足
JAVA_FONTSを設定するという解決策も見つかるけど、サーブレット限定の利用においては必要なし。
LinuxだとJava1.4.2で化けるという事例もあるらしい。
もしかしたら、appendedfontpathの設定とぶつかるのかもしれない。
Linux環境について今のところ自分は嵌って無いので、font.properties.jaの設定例DB2の事例をメモ。

この議論は、C0FFEE (36377)によって トモ専用として作成されたが、今となっては 新たにコメントを付けることはできません。
typodupeerror

物事のやり方は一つではない -- Perlな人

読み込み中...