PDFファイルにはフォントのテキスト情報だけでなく文字の形状を埋め込むことも可能らしい。

フォント・ファイルの中には、文字コードからグリフIDへの対応表も含まれていますが、これをcmapと言います。

文字コードとグリフIDは一対一対応にはなりません。簡単な例では、日本語での表記法には、横書きと縦書きがありますが、記号類は横書きと縦書き用の2種類のグリフを持たせなければなりません。そして横書きと縦書きとでcmapを切り替える必要があります。
(中略)
PDFにフォントが埋め込まれていない場合、PDFの中には、普通、文字コード(コードポイント)の並びがあり、その文字コードを表示するためのフォント・オブジェクトが指定されています。
(中略)
PDFにフォントを埋め込むと、埋め込み処理の際に、コンピュータ上のフォント・ファイルから必要最小限の情報が取り出されて、PDF内部に取り込まれます。さらに、文字列オブジェクトは、原則として、文字コードではなくグリフIDの並びとなり、符号化方式(Adobe Readerのプロパティに表示されるEncoding)はIdentityとなります。
(中略)
著作権者は節度のあるフォント埋め込みは認める傾向
(中略)
従って、ユーザは、「埋め込んだフォントを用いて編集・校正などの新たな文字組を行う行為」、フォント埋め込みを不可としているタイプフェイス権利のフォントを埋め込まない、フォントが埋め込まれたPDFを配布するに際して、当該フォントの使用許諾契約書に従うこと、を求めています。
(中略)
フォントファイルの埋め込み許可フラグによる制御
(中略)
比較的新しいフォントであれば、このようにプログラムで埋め込み許可状態をチェックできます。しかし、Type1のような古いフォントでは、こうしたフラグはありませんので、権利者が許可しているかどうかを個別に確認する必要があります。

PDFへのフォント埋め込みとは?

iText でも フォント埋め込みすることが可能。

Fonts from jars (and font packs):
CID fonts are Postscript-based fonts which can support a large number of characters (65,536 max). The format is often used for CJK fonts (CJK = Chinese Japanese Korean). CID format makes it possible to change the character order (encoding) of a font through the use of character-to-glyph tables (CMaps) which are external to the font.
If you want to use CJK fonts in iText, you need an extra jar: iTextAsian.jar. If you want to be able to read the text you generated with iText using CJK fonts, you will also need to download and install a special font pack for Acrobat Reader (or maybe your Reader will ask you to install such a font pack upon opening a PDF file with CJK fonts). For the rest, creating a CJK font supported by the iTextAsian.jar is as easy as using any other font:

BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font FontChinese = new Font(bfChinese, 12, Font.NORMAL);
Paragraph p = new Paragraph(chinese, FontChinese);
document.add(p);
(中略)
This is the list of fonts supported in the iTextAsian.jar:

* Chinese Simplified:
STSong-Light and STSongStd-Light with the encodings UniGB-UCS2-H and UniGB-UCS2-V
* Chinese Traditional:
MHei-Medium, MSung-Light and MSungStd-Light with the encodings UniCNS-UCS2-H and UniCNS-UCS2-V
* Japanese:
HeiseiMin-W3, HeiseiKakuGo-W5 and KozMinPro-Regular with the encodings UniJIS-UCS2-H, UniJIS-UCS2-V, UniJIS-UCS2-HW-H and UniJIS-UCS2-HW-V
* Korean:
HYGoThic-Medium, HYSMyeongJo-Medium and HYSMyeongJoStd with the encodings UniKS-UCS2-H and UniKS-UCS2-V
(中略)
CID fonts
But that's not all, there is also an extra jar iTextAsianCmaps.jar. It contains the cmaps for lots of other CID fonts. This is how you can use them:

PdfEncodings.loadCmap(
"GBK2K-H",
PdfEncodings.CRLF_CID_NEWLINE); // needs to be done only once
byte text[] = my_GB_encoded_text;
String cid = PdfEncodings.convertCmap("GBK2K-H", text);
BaseFont bf = BaseFont.createFont("STSong-Light", "Identity-H", false);
Paragraph p = new Paragraph(cid, new Font(bf, 14));
document .add(p);

iText Tutorial: Getting fonts

ttf, otf, afm とかのフォントファイルを利用することもできるが、それらのフォントファイル無しで日本語を使うためには iTextAsian.jar が必要とのこと。

CID フォントは、Postscript ベースのフォントであり、非常に多くの文字数(最大65,536)をサポートしています。このフォーマットは CJK フォント(CJK = Chinese Japanese Korean) に良く使われます。CID フォーマットは、フォント外部の文字-グリフテーブル(CMaps)の使用により文字の順序(エンコーディング)を変更することができます。
iText で、CJK フォントを使いたいならば、拡張 jar iTextAsian.jar が必要です。iText によって生成した CJK フォントを使ったテキストを読むならば、特別な Acrobat Reader フォントパックをダウンロード、インストールすることが必要です。(CJK フォントを含む PDF ファイルを開くときに、Reader が問い合わせるでしょう。)iTextAsian.jar によってサポートされているCJK フォントをインスタンス化することに関しては、他のフォントと同じぐらい容易です。
(中略)
しかし、こればすべてではありません。iTextAsianCmaps.jar という拡張 jar があります。これには、他の多くの CID フォントの cmaps が含まれています。次に、使い方を示します。

iText Tutorial: Getting fonts (フォントの取得)

iTextAsian.jar(と iTextAsianCmaps.jar)で日本語フォントを使えることがわかった。

使えるフォントの種類は iTextAsian.jar とか iTextAsianCmaps.jar の中身を見ればよさそう。
iTextAsian.jar 内 \com\lowagie\text\pdf\fonts にあったファイルの一覧はこんな感じ。

  • Adobe-CNS1-UCS2.cmap
  • Adobe-GB1-UCS2.cmap
  • Adobe-Japan1-UCS2.cmap
  • Adobe-Korea1-UCS2.cmap
  • cjkencodings.properties
  • cjkfonts.properties
  • HeiseiKakuGo-W5.properties
  • HeiseiMin-W3.properties
  • HYGoThic-Medium.properties
  • HYSMyeongJo-Medium.properties
  • HYSMyeongJoStd-Medium.properties
  • KozMinPro-Regular.properties
  • MHei-Medium.properties
  • MSung-Light.properties
  • MSungStd-Light.properties
  • STSong-Light.properties
  • STSongStd-Light.properties
  • UniCNS-UCS2-H.cmap
  • UniCNS-UCS2-V.cmap
  • UniGB-UCS2-H.cmap
  • UniGB-UCS2-V.cmap
  • UniJIS-UCS2-H.cmap
  • UniJIS-UCS2-HW-H.cmap
  • UniJIS-UCS2-HW-V.cmap
  • UniJIS-UCS2-V.cmap
  • UniKS-UCS2-H.cmap
  • UniKS-UCS2-V.cmap

サンプルソースコード(FontSample.java)

実際にいくつかのフォントを埋め込んだり埋め込まなかったりしてみる。


import java.awt.geom.*;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
 
import com.lowagie.text.*;
import com.lowagie.text.pdf.*;
 
public class FontSample {
 
  public static void main(String[] args) throws Exception {
 
    // メモ
    // Fonts in TrueType collections are addressed by index such as "msgothic.ttc,0".
    // BaseFont.IDENTITY_H; // The Unicode encoding with horizontal writing.
    // BaseFont.EMBEDDED; // if the font has to be embedded
    
    // embedded
    List<BaseFont> embedded = new ArrayList<BaseFont>();
    embedded.add(
      BaseFont.createFont(
        "C:\\WINDOWS\\Fonts\\msgothic.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED));
    embedded.add(
      BaseFont.createFont(
        "C:\\WINDOWS\\Fonts\\Diego Regular.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED));
    embedded.add(
      BaseFont.createFont(
        "KozMinPro-Regular", "UniJIS-UCS2-H", BaseFont.EMBEDDED)); // using iTextAsian.jar
 
    // not embedded
    List<BaseFont> notEmbedded = new ArrayList<BaseFont>();
    notEmbedded.add(
      BaseFont.createFont(
        "C:\\WINDOWS\\Fonts\\msgothic.ttc,0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED));
    notEmbedded.add(
      BaseFont.createFont(
        "C:\\WINDOWS\\Fonts\\Diego Regular.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED));
    notEmbedded.add(
      BaseFont.createFont(
        "KozMinPro-Regular", "UniJIS-UCS2-H", BaseFont.NOT_EMBEDDED)); // using iTextAsian.jar
    
    output(   embedded, "20070804_embedded.pdf");
    output(notEmbedded, "20070804_notEmbedded.pdf");
  }
 
  private static void output(List<BaseFont> basefonts, String file) throws Exception {
    
    Document doc = new Document();
    PdfWriter.getInstance(doc, new FileOutputStream(file));
    doc.open();
 
    for(BaseFont bf: basefonts){
      Paragraph paragraph = new Paragraph();
      Font font = new Font(bf, 18, Font.NORMAL);
      paragraph.add(new Chunk(tos(bf.getFullFontName()), font));
      paragraph.add(Chunk.NEWLINE);
      paragraph.add(new Chunk(bf.getEncoding(), font));
      paragraph.add(Chunk.NEWLINE);
      paragraph.add(new Chunk("hello, world, こんにチワワ世界。想定外\\0", font)); 
      paragraph.add(Chunk.NEWLINE);
      doc.add(paragraph);
    }
    
    doc.close();
  }
  
  private static String tos(String[][] s){
    StringBuffer sb = new StringBuffer();
    for(int i=0; i<s.length; i++){
      for(int j=0; j<s[i].length; j++){
        sb.append(s[i][j]);
        sb.append(":");
      }
    }
    return sb.toString();
  }
  
}

出力されたPDFファイル
-20070804_embedded.pdf
-20070804_notEmbedded.pdf

なんだか2つのファイルに違いがないような気が……
埋め込んだつもりじゃないフォントが「埋め込みサブセット」になってしまう(自分のコードのミス?)。
問答無用で、埋め込めるフォントは埋め込まれてて、埋め込めないフォントは埋め込まない……みたいな。

ちなみに、iTextAsian.jar が必要なのにクラスパスに通さないとこんなかんじの例外が発生してしまう。


Exception in thread "main" com.lowagie.text.DocumentException: Font 'KozMinPro-Regular' with 'UniJIS-UCS2-H' is not recognized.
  at com.lowagie.text.pdf.BaseFont.createFont(Unknown Source)
  at com.lowagie.text.pdf.BaseFont.createFont(Unknown Source)
  at com.lowagie.text.pdf.BaseFont.createFont(Unknown Source)
  at FontSample.main(FontSample.java:19)

また、com.lowagie.text.FontFactory クラスを使っても Font オブジェクトを取得できる
iText Tutorial: The Font objects (フォントオブジェクト) が参考になる。

tags: zlashdot Java Java PDF iText

Posted by NI-Lab. (@nilab)