はるか昔の DOS の時代では、共有リソースという概念はなくワープロなどのアプリケーションではそれ専用のフォントを2,3個もっているだけであった。それでも、和文タイプライタよりはるかに便利であった。各アプリに適合するフォントを買い足すこともできたが非常に高価であった。このような環境で少しでも見栄えのする文章を作成するために、ボールドを設定するのと同じ感覚でフォントの横倍とか縦横倍(四倍)とかの文字属性を設定することができた。Windows になってからも、しばらくのあいだこのような「習慣」が続いていた。日本製のワープロソフトではいまでも設定できるものもあるかもしれない。Delphi の Font クラスには Size と Height で大きさを設定できるが、縦横比を変更できないようである。ここでは、縦横比を自由に設定したり、文字の描画方向を変更することを試してみよう。
参照元2 で調べたように、フォントは TLogFont 型のレコードのフィールドを設定して CreateFontIndirect() を実行することにより、自由に作ることができる。といっても、設定条件に一番近いフォントがOSによって選ばれる、という意味だが。ここでは、最初に、Canvas の現在のフォントの縦横比を変更することを考える。したがって、まずしなければならないことは、現在のフォントにかんする情報を TLogFont 型のレコードとして取得することである。それには、GetObject() を使う。参照元2 から引用しよう。
function GetObject(p1: HGDIOBJ; p2: Integer; p3: Pointer): Integer; stdcall;
p1 には、ペン、ブラシ、フォント、ビットマップなどのGDIオブジェクトのハンドルを指定する。 p3 には、論理ペン、論理ブラシ、論理フォント、ビットマップレコード(Windows.TBitmap)のアドレスを指定し、 p2 には p3 に指定したレコードのサイズを指定する。 ここでは、p1 には、フォントハンドル、p3 には TLogFont レコードのローカル変数のアドレス、p2 にはそのサイズを指定する。 それでは、これをつかって簡単なコードを試してみよう。
List 1
procedure TForm1.Button1Click(Sender: TObject); var LF:TLogFont; tm: TTextMetric; i:integer; begin with Canvas do begin //Font.Style := Font.Style + [fsBold]; GetObject(Font.Handle,SizeOf(LF),@LF); GetTextMetrics(Canvas.Handle, tm); for i := 10 to 20 do begin LF.lfWidth := Round(tm.tmAveCharWidth*i/10); Font.Handle := CreateFontIndirect(LF); TextOut(10,(i-9)*20,'Test 文字列です。XYZ'); end; LF.lfWidth := tm.tmAveCharWidth; Font.Handle := CreateFontIndirect(LF); end; end;
実行すると右図のようになる。フォントの幅の設定はスムースに行われるのではなく、11段階の設定に対してフォントマッパが返してくれるフォントの実際の大きさは7段階になっている。
コードを見てみよう。GetObject() で現在のフォントの属性を TLogFont レコードに写し取り、幅を設定したいのだが、現在の幅が分からない。それを取得するために GetTextMetrics() を実行する。
function GetTextMetrics(DC: HDC; var TM: TTextMetric): BOOL; stdcall;
この第二パラメータには以下のようなレコード型を設定して、値をセットしてもらい返してもらう。
type PTextMetricA = ^TTextMetricA; PTextMetric = PTextMetricA; tagTEXTMETRICA = record tmHeight: Longint; // 高さ(アセントとデセントの和) tmAscent: Longint; // アセント tmDescent: Longint; // デセント tmInternalLeading: Longint; // 内レディング tmExternalLeading: Longint; // 外レディング tmAveCharWidth: Longint; // 文字の平均幅 tmMaxCharWidth: Longint; // 文字の最大幅 tmWeight: Longint; // フォントの太さ(ウェイト) tmOverhang: Longint; // 重なり tmDigitizedAspectX: Longint; // 水平アスペクト tmDigitizedAspectY: Longint; // 垂直アスペクト tmFirstChar: AnsiChar; // フォントが定義している最初の文字 tmLastChar: AnsiChar; // フォントが定義している最後の文字 tmDefaultChar: AnsiChar; // 定義にない場合に使う文字 tmBreakChar: AnsiChar; // ワードブレイクの定義文字 tmItalic: Byte; // ゼロでなければイタリック tmUnderlined: Byte; // ゼロでなければ下線あり tmStruckOut: Byte; // ゼロでなければ打ち消し線あり tmPitchAndFamily: Byte; // ピッチとフォントファミリー tmCharSet: Byte; // 文字セット end; tagTEXTMETRIC = tagTEXTMETRICA; TTextMetricA = tagTEXTMETRICA; TTextMetric = TTextMetricA;
ここでは tmAveCharWidth をつかって現在のフォントの平均文字幅を取得している。それから、1倍から2倍まで 0.1 きざみで幅を変更しながらフォントをつくって設定し、文字列を出力している。最後に、設定をもとに戻す。
つぎに文字列の出力方向を変更してみよう。そのような描画関数があるのではなく、文字列をどの方向に描くか、というのもフォントの属性なのである。TLogFont レコード型の lfEscapement フィールドに設定する。設定単位は 0.1 度で反時計回りの角度を設定する。それではやってみよう。
List 2
procedure TForm1.Button1Click(Sender: TObject); var LF: TLogFont; i: integer; s: string; begin with Canvas do begin Font.Height := 14; Brush.Style := bsClear; GetObject(Font.Handle,SizeOf(LF),@LF); for i := 0 to 11 do begin LF.lfEscapement := i*300; Font.Handle := CreateFontIndirect(LF); s := ' テスト文字列 - '+IntToStr(i*300); TextOut(150,150,s); end; SetTextAlign(Handle,TA_LEFT or TA_BOTTOM); for i := 0 to 11 do begin LF.lfEscapement := i*300; Font.Handle := CreateFontIndirect(LF); s := ' テスト文字列 - '+IntToStr(i*300); TextOut(450,150,s); end; end end;
結果は上図のようになる。左側では、lfEscapement を 300 ごと(30度)に変更してフォントをつくり、TextOut() で出力している。分かりづらいかもしれないが、回転の中心に対して、文字列の作る境界矩形が左上になっている。右側の描画では同じことをしているが、回転の中心が各文字列の境界矩形の左下になっている。これは以下の API で設定している。
function SetTextAlign(DC: HDC; Flags: UINT): UINT; stdcall;
これは、テキストを描画するときの座標の原点を設定する。デフォルトは、境界矩形の左上である。第一パラメータはデバイスコンテキストハンドル、VCL では Canvas.Handle である。第二パラメータには水平・垂直について以下の定数を一つずつ設定する。
const { Text Alignment Options } TA_NOUPDATECP = 0; // 参照点は、テキスト描画関数を実行後も更新されずに維持(デフォルト) TA_UPDATECP = 1; // 参照点は、テキスト描画関数を実行毎に更新される TA_LEFT = 0; // 参照点は、水平方向の境界矩形の左辺(デフォルト) TA_RIGHT = 2; // 参照点は、水平方向の境界矩形の右辺 TA_CENTER = 6; // 参照点は、水平方向の境界矩形の中点 TA_TOP = 0; // 参照点は、境界矩形の上辺(デフォルト) TA_BOTTOM = 8; // 参照点は、境界矩形の下辺 TA_BASELINE = 24; // 参照点は、ベースライン TA_RTLREADING = $100; // テキストは右から左へ(中近東仕様) TA_MASK = (TA_BASELINE+TA_CENTER+TA_UPDATECP+TA_RTLREADING); // 以下は縦書きの場合の読み替えである。 VTA_BASELINE = TA_BASELINE; VTA_LEFT = TA_BOTTOM; VTA_RIGHT = TA_TOP; VTA_CENTER = TA_CENTER; VTA_BOTTOM = TA_RIGHT; VTA_TOP = TA_LEFT;
それでは、もっとも多用されると思われる日本語の縦書きを試してみよう。以下にリストを示す。
List 3
procedure DrawCircle(Cvs:TCanvas;const X,Y,Radius:integer); begin Cvs.Ellipse(X-Radius,Y-Radius,X+Radius,Y+Radius); end; procedure TForm1.Button1Click(Sender: TObject); var LF: TLogFont; s: string; begin with Canvas do begin Font.Height := 20; Font.Name := '@'+Font.Name; GetObject(Font.Handle,SizeOf(LF),@LF); LF.lfEscapement := -900; Font.Handle := CreateFontIndirect(LF); s := '縦書きのテストです。'; Pen.Color := clRed; Brush.Style := bsSolid; Brush.Color := clRed; DrawCircle(Canvas,50,30,6); Brush.Style := bsClear; TextOut(50,30,s); SetTextAlign(Handle,VTA_LEFT or VTA_TOP); Brush.Style := bsSolid; Brush.Color := clRed; DrawCircle(Canvas,100,30,6); Brush.Style := bsClear; TextOut(100,30,s); end end;
実行して Button1 を押すと右図のようになる。赤丸は参照点である。デフォルトでは左にしめしたように右上になっていて、これは使いにくい。SetTextAlign() で修正すると右側にしめしたように参照点を左上にすることができてこれはいままでと同じになる。