亀場でプログラミング:文字情報とフォント
このページでは、「文字」についてあれこれ考えてみたい。
参考資料:
文字と文書
(弘前大学・内海淳先生)
1. 文字情報とコンピューターでの表現
言うまでもなく、文字は我々のコミュニケーションにとって必要不可欠な手段であって、 パソコンや携帯電話に限らず、文字が扱えない(あるいは文字を全く用いない)情報機器というのは、探すのに苦労するくらいだ。 パソコンで文字情報を扱えるのは「当たり前」であるが、そこのところの仕掛けについて、少し考えてみたい。
C言語を学んだ諸君は、すでに、コンピュータは基本的に数値(0,1の並び)の操作しかできない機械であることは認識しているはずだ。 その2進数の並び方のパターン(数値)を人間が解釈し直して、整数を表したり(int型)、実数を近似的に表したり(floatやdouble型)している。 コンピュータの内部に「文字」がそのまま埋め込まれているわけがないので、もちろん、文字も、何らかの0と1の数値情報として表現されていることになる。
ここで「文字」には、二つの側面があることに注目したい。例えば、アルファベットのAという文字をどんな風に書こうが、どんなデザインで 表わそうが、AはAである。つまりAはそれ自体が文字(character)として存立している。
その一方で、Aをどのようなデザインで表すか、については、様々な選択肢がある。パソコンで用いる文字の基本デザインのことを フォント(font)と呼び、それぞれの文字の具体的な形状をグリフ(glyph)と呼んで、「文字」そのものとは区別して考える。
文字の表現:文字コード
C言語のchar型は、ASCIIコードをデータとして扱うための型である。
文字にどのような数値を割り当てるかについては、歴史的に、さまざまな流儀がある。 そんな中でも、(いわゆる半角の)アルファベットと数字については、 ASCII(アスキー)コードが事実上の標準となっている。 ASCIIコードで、アルファベットの大文字のAは(10進数で)65, Bは66,...といった具合に、127の文字記号にコードが割り当てられている。 であるから、ASCIIコードを使うと、1文字を1バイト(8ビット)で表現することができるわけだ。
日本語文字や漢字を含め、さまざまな文字を表現するにはもちろん1バイト(最大で256パターン)では足りるはずがないので、 多バイト(マルチバイト)コードが用いられる。 代表的なのはUnicode(ユニコード)で、現在のパソコンや情報機器の ほとんどはUnicodeを採用している。
Unicodeについては、色々な文化的な事情・背景や文字の解釈など、トピックに事欠かない。また、文字コードを通信やデータの中で どのように表現するか(文字符号化形式)についても、 何通りもの方式があり、なかなか複雑である。
グリフの表現
文字コードから具体的な記号(図形)を生成する方式は、おおまかに、ビットマップ形式と、ベクター(アウトライン)方式に分けられる。
ビットマップは、Gimp, Photoshopなどのいわゆる「お絵かきソフト」で 扱われるようなビットマップ情報としてフォントを表現する方式。 一方、ベクター方式は、Inkscape, Illustratorなどの「ドロー系ソフト」 のように、図形の座標データを元にしてフォントを扱う方式である。
ビットマップフォントはサイズを変えると「ギザギザ」して見栄えが悪くなる一方で、デザイン寸法にあった縮尺で表示すると、くっきりと見易い。 アウトラインフォントは、縮尺を変えても線が滑らかなまま表示することができる。 だいたいのパソコンにはアウトラインフォント(TrueType, OpenType等) が搭載されている。
2. フォント(グリフ)をデザインする
英数字のみにしか対応していないが、亀場に線画で文字を描くための簡単なライブラリをこちらのページに紹介した。
タートルグラフィックスを使うと自在に線を描くことができるのだから、当然、文字を線画として表現することも可能である。 ただ、単純な線分で構成したフォントでは人間味に欠けるので、ここでは、亀場のBrush(毛筆)モードを使って、毛筆タッチの ベクターフォントをデザインしてみたい。
例として、「山」を描画するプログラムを以下に示す。
原点(0,0)を文字の左下隅として、200x200ピクセルの正方形(図中点線)に収まるようにデザインしてみた例。
#include "turtle.h" main() { CON("localhost") ; CLR() ; /* 画面クリア */ BGC(1,1,1) ; /* 背景を白に */ COL(0,0,0) ; /* 文字を黒に */ BRUSH() ; /* 毛筆モードに */ LW(30) ; /* 線の幅を30に */ JUMP(0,0); /* (0,0) に移動して */ RJUMP(95,190); SOUTH(); LT(0.5); /* 相対的に(+95,+190)だけ移動して、下を向いて、左に0.5°回転 */ PD(); FD(170); FD(0); PU(); /* ペンを下ろし、170前進し、点を止めて、ペンを上げる */ JUMP(0,0); RJUMP(15,70); SOUTH(); RT(5); PD(); FD(60); LT(100); FD(180); FD(0); PU(); JUMP(0,0); RJUMP(185,85); SOUTH(); LT(5); PD(); FD(75); FD(0); PU(); }
ここで、JUMP(x,y)
はカメを座標(x,y)へジャンプさせる命令。
また、これと似ているが、RJUMP(dx,dy)
は、現在地点から相対的に(+dx,+dy)だけ移動させる命令である。
上のプログラムでは、原点(0,0)を基準点(文字の左下隅の位置)として、そこからの相対的な位置関係を指定しながら、文字を描いている。
SOUTH()
はカメを南(下)に向けるための命令。同様にEAST(),WEST(),NORTH()
も使える。
線の終点をきっちり止めるには、PU()の前にFD(0)を置く。 BRUSHモードでは、FD( )の移動量が1以下の場合、そこで点を打つようになっている。
FD(180); FD(0); PU();
のように、ペンアップの直前にFD(0)を呼んでいるのは、筆の「トメ」の効果を出すためである。
文字を関数に対応させる
上の例題は、「習字」としては悪くないかもしれないが、文字をあちらこちらに好きなサイズで配置したいときに、とても不便だ。 パソコンのフォントのように、画面の好きな位置に好きな大きさで表示できるように、 「山」のプログラムに少し手を加えてみる。
長さスケールを変換するには、長さに関係した関数(FD( ), RJUMP())のパラメータを調整する必要がある。
角度は無次元量なので長さや場所が変わっても同じまま。つまりLT( )やRT( )のパラメータは調整する必要がない。
#include "turtle.h" void yama(float x, float y, float width) { float scale = width/200.0 ; LW(30*scale) ; JUMP(x,y); RJUMP(95*scale,190*scale); SOUTH(); LT(0.5); PD(); FD(170*scale); FD(0); PU(); JUMP(x,y); RJUMP(15*scale,70*scale); SOUTH(); RT(5); PD(); FD(60*scale); LT(100); FD(180*scale); FD(0); PU(); JUMP(x,y); RJUMP(185*scale,85*scale); SOUTH(); LT(5); PD(); FD(75*scale); FD(0); PU(); } main() { CON("localhost") ; CLR() ; /* 画面クリア */ BGC(1,1,1) ; /* 背景を白に */ COL(0,0,0) ; /* 文字を黒に */ BRUSH() ; /* 毛筆モードに */ yama(0, 0, 100) ; yama(0,-200, 100) ; yama(-200, 0, 100) ; yama(-200, -200, 100) ; }
「山」を描いている箇所をCの関数として分離・独立させ、さらに、関数の引数として、文字の左下の座標(x,y)と文字の幅widthを 指定できるようになっている。そして、main( )の内部では、文字幅100の大きさで、画面の4箇所に「山」を描いている。
ブラシの効果を利用してフォントを飾る
以下に「文字の部品」とすることが出来そうないくつかのパターンと、それを表示するためのCプログラム例を示した。 亀場は、線が引かれる方向等を手がかりに、習字に近い感覚で、線の端点に「トメ」、「ハネ」、「ハライ」などの飾り(serifと呼ぶ) を付加できるようにプログラムされている。 図中のアルファベットと、サンプルプログラム中のコメントが対応しているので、文字デザインの際の参考にしてほしい。
「文字の部品」の例と、その表示プログラム
#include "turtle.h" main() { CON("localhost") ; CLR() ; RST() ; BGC(0.8,0.8,0.7) ; COL(0.0,0.0,0.0) ; LW(20) ; BRUSH() ; JUMP(100,200); /* A */ EAST(); LT(5); PD(); FD(100); FD(0) ; PU(); JUMP(150,150); /* B */ SOUTH(); PD(); FD(100); PU(); JUMP(150,0); /* C */ SOUTH(); PD(); FD(70); LT(130); FD(50); PU(); JUMP(150,-130); /* D */ SOUTH(); PD(); FD(70); RT(130); FD(40); PU() ; JUMP(-70,200); /* E */ EAST(); LT(5); PD(); FD(100); RT(130); FD(40); PU() ; JUMP(-70,100); /* F */ SOUTH(); LT(30); PD(); FD(70); LT(30); FD(70); PU(); JUMP(20,-100); /* G */ SOUTH(); RT(30); PD(); FD(70); RT(25); FD(70); PU(); JUMP(-180,200); /* H */ PD(); FD(0); PU(); JUMP(-150,120); /* I */ SOUTH(); RT(50); PD(); FD(70); PU(); JUMP(-180,10); /* J */ SOUTH(); LT(60); PD(); FD(30); RT(120); FD(30); PU(); JUMP(-220,-100); /* K */ EAST(); LT(5); PD(); FD(100); RT(100); FD(100); FD(0); PU(); JUMP(-220,-220); }
練習:2〜4文字熟語
2名〜4名でチームを組んで、「人数」分の文字数から成る熟語をひとつ選ぶ。各人が分担して、「山」の例にならって その文字を描画する関数をデザインする。
文字が描かれる場所、文字を大きさを微調整した上で、チームの「代表」の亀場に、全員分の文字を描き、熟語を完成させる。
ヒント:
画面をリセットしたい場合は、 亀場サーバーのメニュー(マウス右クリック)を使うと図形やゾンビ亀を消去することができる。
プログラム中のCLR()
は削除、あるいはコメントアウトしておく。さもないと、折角クラスメートが描いてあった文字が消されてしまう。
代表は、自分の亀場を「パブリックモード」に設定しておく(マウスを右クリックでメニューを選択)。ウィンドウのタイトルバーが Public Turtle Field @ ... になっていることを確認する。
代表の亀場に描く場合は、プログラムの中のCON("localhost");
の箇所を
CON("代表のパソコンのアドレス") ;
M棟の演習室のパソコンの場合はicl1234
(アイ・シー・エル+数字)の形式
に変更する。代表の亀場のウィンドウのタイトル "Public Turtle Field @ 何々"の何々の箇所が、パソコンのアドレス(ホスト名)になっている ので、代表はチームのメンバーにそれを知らせる。