FreeTypeを使うのソースを表示
新しいページはコチラ
移動:
案内
,
検索
[[フォント TrueType 構造解析]]に戻る。 == '''概要''' == この記事はFreeTypeを使うであって、FreeTypeをまとめる記事ではないです。なので遊び場程度の記事になります。遊べるかは謎です。Font情報をどれくらい操作できるのか試してみるところです。目標はPDFを作るためのグリフ番号取得と、フォントプログラムの再構成。サブセットフォントを作るということです。 そこまですることなんか?とは思っています。フォントの構造を知ることに重きを置きながらがんばります。 まずはコンソールプログラムでチュートリアルっぽいことを。 ってチュートリアルのとおり、作り始めたらQt64bitコンソールアプリケーションを作ってるせいで、64bitのライブラリが必要になってしまった。開発時にダイナミックリンクを使うと勉強の質が低下してしまうので、64bit32bit混在で動かすわけにもいかず、似非64bitのfreetypeライブラリを作成するために、libpngで似非64bitプラットフォームを作成した。似非というのは、何もプログラムを変更しないで、ただ64bit宣言するだけのことです。bit演算が凄まじいアプリケーション群なので、これが原因でアドレスの使い方が変わってバグる可能性はあるが、莫大なコードを目の前に、全てを潰していくのはサハラ砂漠にダイアモンドを埋めたのを探すようなモノ。無謀な道とは知りつつ、しばらく、これで突き進む。 ちな 似非を作るには[[VC PlusPlus:似非64bitプラットフォームの追加|コチラ]]の手順として記載。 Qtコンソールプログラムの書き始めは以下のような状態。Qtについては、Qt導入の記事をみて下さい。なんでQtなん?って思う人いるとおもいますけど、さほどQtの要素は使わないので、安心して下さい。Qtやってるっていう見せかけですよ。 <Syntaxhighlight2 lang="cpp"> #include <QtCore/QCoreApplication> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); return a.exec(); } </Syntaxhighlight2> 自分は64bit版ですが、64bit版を生成するのが面倒な人はQtの32bit版のLibraryを導入して新規作成するとよいね。Qtの64bit版を使ってるつもりで説明します。ところどころ32bit版の人向けのコメントも添えます。 まずはプロジェクトの設定を変えて、freetypeが使える状態にします。 インクルードファイルのありかを設定します。ライブラリを使うときはまずはヘッダファイルで関数の全ての情報が必要です。標準関数でさえ#include <stdio.h>って設定するもんね。同じこと。 ソリューション構成をDebug、ソリューションプラットフォームをx64<span>(</span>32bitに人はWin32<span>)</span>に設定します。ツールバーのリストを選ぶところですね。次に、メニューの[プロジェクト]-[プロパティ]を選択します。 [VC++ディレクトリ]の項目の中の[外部インクルードディレクトリ]に以下を追加します。 C:\…\…\…\…\freetype-2.9.1\include [VC++ディレクトリ]の項目の中の[ライブラリディレクトリ]に以下を追加します。 C:\…\…\…\…\freetype-2.9.1\objs\x64\Debug 絶対パスなので、…の部分はそれぞれの、freetypeの配置した場所に、freetype-2.9.1となっているところもそれぞれのバージョン番号だったり、名前を変えてfreetypeにしたりしてる場合も合わせて下さい。64bitの場合はx64ですが、32bitのQtアプリの場合はx64ではなくWin32です。 構成のRelease版も合わせて変更しておくとよいでしょう。インクルードディレクトリは同じで、ライブラリディレクトリはDebugがReleaseに代わります。 実際にデバッグでもなんでもテストでアプリを動かす時は、ダイナミックリンクライブラリを使うので、dllをがうごかせるように実行ファイルと同じディレクトリ置くとか、環境変数のPathの参照可能な範囲に配置する指定をしないと駄目です。プロジェクトごとにどの環境で動かすのかを見極めて環境変数の設定しないとだめです。プログラマなら実行ファイルが完成するまではDebug版のダイナミックリンクライブラリを使うはずなので、環境変数Pathにfreetype.libのx64 <span>(</span>Win32をQtアプリの人はWin32<span>)</span> のDebug版にPathを設定しましょう。 コンパネのシステムの右側の詳細設定から詳細。環境変数の中のPathに C:¥…\…\…\…\…\freetype-2.9.1\objs\x64\Debug を追加しましょう。コマンドプロンプトでwhere %path% freetype.dll とすると、フルパスが帰ってきたら、パスが通っていて、freetype.dllがどの実行ファイルからも参照できるようになっていることを意味します。フルパスが表示されず。また次のプロンプトだけが表示された場合は失敗しています。もう一度確認しましょう。デバッグ押しても、もちろんアプリを起動できません。 そうすると、うまく設定できたかを確かめるために、少しだけプログラムを記述してビルドして確かめてみます。 <Syntaxhighlight2 lang="cpp" line=1> #include <QtCore/QCoreApplication> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); FT_Library library; int error; error = FT_Init_FreeType(&library); if (error) { fprintf(stderr, qPrintable("")); exit(1); } return a.exec(); } </Syntaxhighlight2> 6行目から14行目を追加しました。これで一度ビルド Ctrl+Shift+B します。ちゃんと動くか確かめるためです。もちろん管理人はスーパープログラマなので、うまく動かせました。え。なにがスーパーやねん。わかります。へっぽこでした。 うまくいくと例の安心の出力になります。この正常終了って文字。プログラマにとってはアドレナリン・ドーパミン?だっけがでますね。 <Syntaxhighlight2 lang="text"> ビルドを開始しました... 1>------ ビルド開始: プロジェクト: QtConsole###########, 構成: Debug x64 ------ 1>QtConsole###########.vcxproj -> C:\…\…\…\…\QtConsole###########\x64\Debug\QtConsole###########.exe ========== ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ ========== </Syntaxhighlight2> === '''サンプル1.Qt ConsoleApplicationでテキスト出力 ''' === ここまできたらFreetypeを使える状態になったと言えます。いよいよ使っていきましょう。ところでfreetypeって、どういうことができるのか、 ひとことで言えば、フォントから文字を描画するのに必要な情報を取り出す作業ができる。どんな情報?っていうのが詳しい説明になるわけですが、 *フォントデータからプログラマが指定したフォントサイズ<span>(</span>ポイント<span>)</span>をもとに、指定したグリフ番号のビットマップモノトーン値をピクセル毎に取得できます。 :なかなかえぐい計算量ですが、まぁ最近のPCの早いこと。しゅっとフォントがビットマップになります。 *前項のような作業のために文字コード番号から標準のグリフを取得したり、文字コード+付帯情報でその関連グリフの番号を取得できる。 :つまり、PDFでやりたかった文字コードからグリフ番号を取得する作業ができるということです。このライブラリを使えば目的は一つ達成できる。 これが最初のチュートリアルで紹介するサンプルの機能です。公式サイトにはQtでのサンプルもありますので、管理人と同じくQtで頑張っている人と共に勉強していけるはずです。 <Syntaxhighlight2 lang="cpp" line=1> #include <QtCore/QCoreApplication> #include <QString> #include <QFile> #include <QTextStream> #include <QImage> #include <QColor> #include <QSize> #include <ft2build.h> #include FT_FREETYPE_H static unsigned int text[] = { 0x00003042, 0x00003044, 0x00003046, 0x00003048, //あ、い、う、え 0x0000304a, 0x0000304b, 0x0000304d, 0x0000304f, //お、か、き、く 0x00003051, 0x00003053, 0x00003055, 0x00003057, //け、こ、さ、し 0x00003059, 0x0000305b, 0x0000305d, 0x0000305f, //す、せ、そ、た 0x00003061, 0x00003064, 0x00003066, 0x00003068, //ち、つ、て、と 0x0000000a, //改行 0x00000061, 0x00000062, 0x00000063, 0x00000064, //a、b、c、d 0x00000065, 0x00000066, 0x00000067, 0x00000068, //e、f、g、h 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c, //i、j、k、l 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070, //m、n、o、p 0x00000071, 0x00000072, 0x00000073, 0x00000074, //q、r、s、t 0x00000075, 0x00000076, 0x00000077, 0x00000078, //u、v、w、x 0x00000079, 0x0000007a, //y, z 0x00000061, 0x00000062, 0x00000063, 0x00000064, //a、b、c、d 0x00000065, 0x00000066, 0x00000067, 0x00000068, //e、f、g、h 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c, //i、j、k、l 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070, //m、n、 0x0000000a, //改行 }; #define LEN (sizeof text / sizeof text[0]) #define WIDTH 1024 #define HEIGHT 256 #define LEFT 16 #define TOP 32 static unsigned char canvas[HEIGHT][WIDTH]; static void draw(FT_Bitmap* bitmap, int x, int y) { for (int j = 0; j < bitmap->rows; j++) { for (int i = 0; i < bitmap->width; i++) { unsigned char c = bitmap->buffer[j * bitmap->pitch + i]; if (c) { if (y + j >= 0 && y + j < HEIGHT) { if (x + i >= 0 && x + i < WIDTH) { canvas[y + j][x + i] = c; } } } } } } int main(int argc, char* argv[]) { QCoreApplication a(argc, argv); QString QString_FileName = "C:/FreeTypeStep01Test.txt"; QString QString_ImageFileName = "C:/FreeTypeStep01Test.bmp"; QFile QFile_Text(QString_FileName); QString QString_ImageRowMonoBuffer = ""; QSize QSize_xy(WIDTH, HEIGHT); QImage QImage_Canvas(QSize_xy, QImage::Format_ARGB32_Premultiplied);//空イメージ FT_Library library; int error; error = FT_Init_FreeType(&library); if (error) { fprintf(stderr, qPrintable("ft init error\n")); exit(1); } FT_Face face; error = FT_New_Face(library, qPrintable("C:\\Windows\\Fonts\\KozGoPr6N-Medium.otf"), 0, &face); if (error) { fprintf(stderr, qPrintable("new face error\n")); exit(1); } error = FT_Set_Char_Size(face, 0, 8 * 64, 300, 300); if (error) { fprintf(stderr, qPrintable("set size error\n")); exit(1); } #define LEFT64(x) ((x) << 6) // 1/64が1の 32 bit 26.6 ニィロク ロク固定小数。 #define RIGHT64(x) ((x) >> 6) FT_Vector pen; pen.x = LEFT64(LEFT); pen.y = LEFT64(TOP); unsigned int idces[LEN]; for (int i = 0; i < LEN; i++) { if (text[i] == '\n') { fprintf(stderr, qPrintable("%ld\n"), pen.x); pen.x = LEFT64(LEFT); pen.y += LEFT64(48); continue; } idces[i] = FT_Get_Char_Index(face, text[i]); error = FT_Load_Glyph(face, idces[i], FT_LOAD_DEFAULT); if (error) { fprintf(stderr, qPrintable("no glyph error/\n")); exit(1); } error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); if (error) { fprintf(stderr, qPrintable("glyph render error\n")); exit(1); } draw(&face->glyph->bitmap, RIGHT64(pen.x) + face->glyph->bitmap_left, RIGHT64(pen.y) + face->glyph->bitmap_top); pen.x += face->glyph->advance.x; } fprintf(stderr, qPrintable("\n")); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { QImage_Canvas.setPixel(x, y, qRgb(canvas[y][x], canvas[y][x], canvas[y][x])); } } QImage_Canvas.save(QString_ImageFileName, "BMP"); printf(qPrintable("P3\n%d %d\n%d\n"), WIDTH, HEIGHT, 255); if (QFile_Text.open(QIODevice::WriteOnly)) { QTextStream out(&QFile_Text); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { if (canvas[y][x]) { QString_ImageRowMonoBuffer = QString_ImageRowMonoBuffer + " 1"; //printf(qPrintable("%2d"), 1); } else { QString_ImageRowMonoBuffer = QString_ImageRowMonoBuffer + " 0"; //printf(qPrintable("%2d"), 0); } } QString_ImageRowMonoBuffer = QString_ImageRowMonoBuffer + "\n"; //printf(qPrintable("\n")); out << QString_ImageRowMonoBuffer; QString_ImageRowMonoBuffer = ""; } QFile_Text.close(); } else { QString QString_Error(qPrintable("No Open or Create Error\n")); QString_Error = QString_Error + QFile_Text.errorString(); fprintf(stderr, qPrintable(QString_Error)); } return a.exec(); } </Syntaxhighlight2> テキストファイルと画像を出力するQtアプリケーションfreetype連動プログラムになっています。 生成される画像 [[ファイル:FreeTypeStep01Test.bmp|400px|thumb|none|実行結果]] 文字列は白黒を1ビットづつ見やすいように出力したものです。画像の方はNormalRenderingのアンチエイリアスがかかっていますので少し綺麗。文字が上にいったり下にいったりしてるのは、Adobe AcrobatReaderの小塚ゴシックを参照したため。この仕組みで、違反者をさがしてるのかな。それともだいたいのフォントがこんな風にずれるようになってるのか。気になる。そのまえに、FreeTypeの基本について説明していかないとな。 ついでにQtの勉強にもなるという。いいね。このSiteは、いいねボタンないけどね。 FreeTypeの勉強っていうけど、一般の人からしたら、だから何がうれしいのっていうようなプログラムですね。日々、目にしているグラフィックツールのテキストレンダリングは、これぐらい膨大なプログラミングによって実現されているっていうね。テレビや動画や音楽もしかり、実はものすごい技術です。LEDで文字が表示されてる看板とかね。この形の文字になるように、だれかが作ってんだよって言ってやりたいね。手で書いてるのとは訳が違う。そういえば、コンピュータのごとく手で書いちゃう人もいましたね。あれはあれであれのほうが凄い。コンピュータは誰かが苦労して書いたやつをみんなで再利用してるという効率の良さ。原理を知らない人ばかりになるとどうなるんだこの世界。 === '''サンプル1.Qt ConsoleApplicationでテキスト画像表示の解説 ''' === プログラム最初らへんのQで始まるヘッダファイル、Qt 関連のインクルード挿入。割と最小構成でインクルードする手間のかかる指定をする癖があります。それほど無駄がないと確信したら、巨大なインクルードで済ませることもありますが、こういう巨大なライブラリだと延々と理解できないので、無駄が多くなりがち。以下のように対応しています。 *<QtCore/QCoreApplication> : QCoreApplication クラスと、そのメンバ関数 exec()、 qPrintable()関数 *<QString> : QString クラス *<QFile> : QFile クラス *<QTextStream> : QTextStream クラス *<QImage> : QImage クラス *<QColor> : qRGB() QColorの静的メンバ関数 *<QSize> : QImage 関数の初期化に使う。QSize クラス freetype ライブラリを使うためのヘッダファイルを宣言し、そのヘッダファイルで宣言されているヘッダ名マクロをインクルードします。 <Syntaxhighlight2 lang="cpp"> #include <ft2build.h> #include FT_FREETYPE_H </Syntaxhighlight2> 12行目~32行目:処理したい文字配列です。8桁の16進数で、文字コードの「あ~と」までの20文字。番号がとびとびになっているのはUnicodeでは「あ」の後は「ぁ」とか濁点が付いたモノのように順番になっているからです。フォントファイルの中では実際はグリフIDというのが必要ですが、CMapのような構造をフォントファイル内に保持していて、どのような順番になっているかは、フォントファイルの構造を掌握していて、対応表を検出できるようなプログラム。例えば、今回のfreetypeのようなものが必要です。一番最後にLENというマクロが配列の大きさを保持するように設定しています。 34行目35行目のWIDTHとHEIGHTは、キャンバスの幅と高さについてのピクセル数を保持するマクロです。符号なし文字型<span>(</span>符号なし1バイト数値<span>)</span> canvasという型でその配列の大きさを利用します。値はcanvasの型どおりですが、0~255の値を保持します。白黒なので256段階の数値が保持できれば十分です。 LEFT RIGHTマクロは最初の「あ」を描画する位置を保持します。単位はポイントで8ポイントで1文字ぶんくらいの余白になります。 41~54行目はdrawという関数で、RGB=#000000の真っ黒の描画エリア=canvasに対して、受け取る引数。1文字分のグリフのbitmap情報とxとyによる文字描画位置から1文字分の全ピクセルの範囲について白黒濃度値を設定していきます。全部の文字分処理、LENの数値分、繰り返したら、全部を反映したことになります。text 配列の配列数分がLENです。その繰り返しの回数だけ関数が呼び出されます。 60行目からはメイン処理の追加文です。 QtのQStringで画像ファイルとテキストファイルのパスを指定します。長いパスは疲れるのでC:¥とCドライブの直下に置くことにしました。 テキストクラスのQFileに初期値として、パス名を与えることで、初期化できるので利用した感じです。あとから違うファイルを開くためにファイルをcloseした状態でならファイルパスを変更することもできると思いますが、今回はやっていません。 QString_ImageRowMonoBufferというQStringクラスの変数を実体化していますが、これは後々、キャンバスサイズの1行分の白黒情報を2桁の整数で吐き出します。実際は0と1の2値なので、1文字空白が発生するようになります。テキストでみたときに2文字が全角1文字なので、1文字が正方形で表せるため縦横比がテキストファイル上でいい感じにとらえることができます。 67行目でQImageクラスの変数を実体化しています。空の画像格納用変数になります。最初の引数に画像の横幅と縦幅を格納したQSizeクラスの値が必要なので、その上の行で、QSize クラスの変数を実体化しています。第二引数はどういう形式の画像にするかというフラグ設定列挙子です。今回はフルカラーRGBと透明度が設定可能な形式。 69行目からはfreetypeのおきまりの初期化が始まります。91行目までは、このようなサンプルのおきまりの初期化の流れになります。freetypeの初期化をlibraryというクラス変数で実施して、次にその初期化されたクラス変数設定状態を有した状態でフォント情報を保持したfaceというクラスを生成。 81行目 FT_New_Face(library, qPrintable("C:\\Windows\\Fonts\\KozGoPr6N-Medium.otf"), 0, &face); のようにして、face変数に第一引数のフォント名を関連付けたオブジェクトとして操作できるようになる。 そして87行目で1文字を読み取るためのグリフサイズをポイント値で指定。第一引数はこれまでの初期化で生成されたオブジェクトfaceで、次の引数の8は8ポイントを意味していて、ここに指定する1単位は1/64倍されたポイント数になるので、8ポイントにするための8/64を64倍して、8ポイントとして扱うことができる。このとき、このポイント数は26.6<span>(</span>ニィロク ロク<span>)</span>固定小数と呼ぶ形式になっていると言える。その後ろの2つつづく300は横・縦の内部解像度の扱いを指定している。表示する部分がせいぜい8ポイント程度の画面上の表示でも1インチあたりは300個の情報を内部的に保持する形式になり、印刷してもそれなりに綺麗にみえる。300dpiというが、これは印刷業界の一般的な知識です。300dpiなら、人間にはそれなりに見えるということだ。画面上で拡大していくと汚くても。印刷したら大丈夫。これが大事。 [[フォント TrueType 構造解析]]に戻る。
FreeTypeを使う
に戻る。
個人用ツール
ログイン
名前空間
ページ
議論
変種
表示
閲覧
ソースを表示
履歴表示
操作
検索
案内
メインページ
コミュニティ・ポータル
最近の出来事
最近の更新
おまかせ表示
ヘルプ
ツールボックス
リンク元
関連ページの更新状況
特別ページ