history | TOP |
2004/10/03:作成
overview | TOP |
以前に .TTFからフォント名抽出 というのを(翻訳ベースで)やりましたが、今回は参考にしたサイト(CodeGuru: Retrieving the Font Name from a TTF File)のリファレンスにあったページ The OpenType Font File を自分でちゃんと読んで、表題の通り .TTCファイルからフォント名を抽出 しようじゃないかと考えました。
誰かのあるいは自分の役に立ちますように。
参考サイト | TOP |
code | TOP |
(* http://www.microsoft.com/typography/otspec/otff.htm The OpenType Font File *) unit otff; interface uses Windows, SysUtils, Classes; procedure GetFontNameFromTTC(lpszFilePath: string; Results: TStrings); implementation type USHORT = Word; ULONG = Longword; Tag = array[0..3] of char; Fixed = ULONG; // 実際は 32bit fixed-point number(16.16) type // Version 1.0 の TTC ヘッダ TTC_HEADER_VERSION_10 = packed record TTCTag : Tag; // TrueType Collection ID string: 'ttcf' Version : ULONG; // Version of the TTC Header (1.0), 0x00010000 numFonts : ULONG; // numFonts Number of fonts in TTC OffsetTable : array[0..0] of ULONG; // 実際は array[0..numFonts-1] of ULONG; // Array of offsets to the OffsetTable // for each font from the beginning of the file end; // Version 2.0 の TTC ヘッダ // フォント名が欲しいだけで、シグネチャなんかどうでもよい。 // 先頭の 4フィールドは共通のため、Ver.2.0 は使用しない。 TTC_HEADER_VERSION_20 = packed record TTCTag : Tag; // TrueType Collection ID string: 'ttcf' Version : ULONG; // Version of the TTC Header (2.0), 0x00020000 numFonts : ULONG; // numFonts Number of fonts in TTC OffsetTable : array[0..0] of ULONG; // 実際は array[0..numFonts-1] of ULONG; // Array of offsets to the OffsetTable // for each font from the beginning of the file ulDsigTag : ULONG; // Tag indicating that a DSIG table exists, // 0x44534947 ('DSIG') (null if no signature) ulDsigLength: ULONG; // The length (in bytes) of the DSIG table // (null if no signature) ulDsigOffset: ULONG; // The offset (in bytes) of the DSIG table // from the beginning of the TTC file // (null if no signature) end; // Offset Table TTC_OFFSET_TABLE = packed record sfntversion : Fixed; // 0x00010000 for version 1.0. numTables : USHORT; // Number of tables. searchRange : USHORT; // (Maximum power of 2 <= numTables) x 16. entrySelector: USHORT; // Log2(maximum power of 2 <= numTables). rangeShift : USHORT; // NumTables x 16-searchRange. end; // Table Directory TTC_TABLE_DIRECTORY = packed record tag : array[0..3] of char; // 4 -byte identifier. checkSum: ULONG; // CheckSum for this table. offset : ULONG; // Offset from beginning of TrueType font file. length : ULONG; // Length of this table. end; // Naming Table TTC_NAMING_TABLE = packed record end; // ----------------------------------------- from CodeGuruC3659.pas // Header of the names table TT_NAME_TABLE_HEADER = packed record uFSelector : USHORT; // format selector. Always 0 uNRCount : USHORT; // Name Records count uStorageOffset: USHORT; // Offset for strings storage, // from start of the table end; // ----------------------------------------- from CodeGuruC3659.pas // Records in the names table TT_NAME_RECORD = packed record uPlatformID : USHORT; uEncodingID : USHORT; uLanguageID : USHORT; uNameID : USHORT; uStringLength : USHORT; uStringOffset : USHORT; // from start of storage area end; // --------------------------------------------------------------------------- // #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x)) function SWAPWORD(x: Word): Word; begin Result := MakeWord(HiByte(x), LoByte(x)); end; // --------------------------------------------------------------------------- // #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)),SWAPWORD(LOWORD(x))) function SWAPLONG(x: Cardinal): Cardinal; begin Result := MakeLong(SWAPWORD(HiWord(x)), SWAPWORD(LoWord(x))); end; // --------------------------------------------------------------------------- procedure GetFontNameFromTTC(lpszFilePath: string; Results: TStrings); var F: TFileStream; ttc_header_ver10: TTC_HEADER_VERSION_10; FontCount: integer; Offsets: array[0..256] of ULONG; i, j: integer; ttcOffsetTable: TTC_OFFSET_TABLE; TableCount: integer; ttcTableDir: TTC_TABLE_DIRECTORY; csTemp: string; ttNTHeader: TT_NAME_TABLE_HEADER; ttRecord: TT_NAME_RECORD; bFound: Boolean; nPos, nPos2: integer; Buf: array[0..1024] of char; // 結局... begin // lpszFilePath is the path to our font file F := TFileStream.Create(lpszFilePath, fmOpenRead or fmShareDenyWrite); try // ヘッダ情報読込 F.Read(ttc_header_ver10, SizeOf(ttc_header_ver10)); // TrueType Collection かを確認 if (ttc_header_ver10.TTCTag <> 'ttcf') then exit; // バージョンを Big Endian -> Little Endian ttc_header_ver10.Version := SWAPLONG(ttc_header_ver10.Version); // バージョン判定(不要) if (ttc_header_ver10.Version = $00010000) then begin // Version 1.0 end else if (ttc_header_ver10.Version = $00020000) then begin // Version 2.0 end; // 含まれるフォントの数を調べる ttc_header_ver10.numFonts := SWAPLONG(ttc_header_ver10.numFonts); FontCount := ttc_header_ver10.numFonts; // 各フォント情報のオフセット位置を格納する配列をクリア FillChar(Offsets, SizeOf(Offsets), 0); // ストリームを少しだけ巻き戻し F.Seek(- SizeOf(ULONG), soFromCurrent); // オフセット位置を読み込み F.Read(Offsets, SizeOf(ULONG) * FontCount); // オフセット位置をエンディアン変換 for i := 0 to FontCount - 1 do begin Offsets[i] := SWAPLONG(Offsets[i]); end; for i := 0 to FontCount - 1 do begin // ストリームを早送り F.Seek(Offsets[i], soFromBeginning); // オフセットテーブルを読み込む F.Read(ttcOffsetTable, SizeOf(ttcOffsetTable)); // エンディアン変換 ttcOffsetTable.numTables := SWAPWORD(ttcOffsetTable.numTables); TableCount := ttcOffsetTable.numTables; // [name]タグがあるかどうかフラグ bFound := false; // ここからすぐに Table Directory がはじまる for j := 0 to TableCount - 1 do begin // 読込 F.Read(ttcTableDir, SizeOf(ttcTableDir)); // [name]タグを検索 if (ttcTableDir.tag = 'name') then begin bFound := true; break; end; end; // 見つからなかったら次 if not bFound then continue; // エンディアン変換 ttcTableDir.checkSum := SWAPLONG(ttcTableDir.checkSum); ttcTableDir.offset := SWAPLONG(ttcTableDir.offset); ttcTableDir.length := SWAPLONG(ttcTableDir.length); // 現在位置記録 nPos := F.Position; // 指定位置まで移動 F.Seek(ttcTableDir.offset, soFromBeginning); // ※以降はへろへろ。 // 名称構造体読込 F.Read(ttNTHeader, SizeOf(ttNTHeader)); // エンディアン変換 ttNTHeader.uNRCount := SWAPWORD(ttNTHeader.uNRCount); ttNTHeader.uStorageOffset := SWAPWORD(ttNTHeader.uStorageOffset); // 名称レコードの数だけ繰り返し for j := 0 to ttNTHeader.uNRCount - 1 do begin F.Read(ttRecord, SizeOf(TT_NAME_RECORD)); ttRecord.uNameID := SWAPWORD(ttRecord.uNameID); // 4 はフルネーム if (ttRecord.uNameID = 6) then begin ttRecord.uStringLength := SWAPWORD(ttRecord.uStringLength); ttRecord.uStringOffset := SWAPWORD(ttRecord.uStringOffset); // 現在位置記録 nPos2 := F.Position; F.Seek(ttcTableDir.offset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset, soFromBeginning); FillChar(Buf, SizeOf(Buf), 0); F.Read(Buf, ttRecord.uStringLength); csTemp := string(Buf); // あれば次へ if (csTemp <> '') then begin Results.Add(String(Buf)); break; end; F.Seek(nPos2, soFromBeginning); end; end; // 巻き戻し F.Seek(nPos, soFromBeginning); end; finally F.Free; end; end; end.
sample | TOP |
ソースコードと実行ファイルです。
20041003fontstudy.zip(178,865bytes)
フォルダ選択ボタンで適当にTrue Type Fontが入っている場所を選択すると、サブフォルダを適当に掘って.ttf/.ttcファイルを列挙し「取り出したフォント名 << ファイル名」の形でTMemoに追加していきます。
前回は「.ttf」だけでしたが、今回は「.ttf + .ttc」になりました。でも外見は全く同じです。
それから、なぜか今回の関数では、コンパイラオプションの実行時エラーで「範囲チェック」をはずさないとエラーになってしまいます。厭な感じです。
以前にDelphi6にサンプルでついているリソースエクスプローラをいじってたときにも、こんなことになったような気がしますが、バイナリファイルを解析するような処理には付き物なんでしょうか?もしくは自分がへぼすぎるんでしょうか?
あ、最後になりましたが、動いています。正しいかどうかは別として。
EOF | TOP |