| 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 |