AFsoft WebSite(エーエフソフト・ウェブサイト)
 

オペレーティング・システムについて

プログラミングについて
ホームページについて
キャドについて
電子カタログについて
書籍・雑誌
イベント
リンク集
CADを考える:文字(2)
前頁の続きで、幾何要素/表記要素フィーチャの文字について見て行きます。
 
前頁にて文字の描画そのものについては記述しましたが、文字間隔・基点・縦書き文字についてはまだ触れていませんのでそれについて考えていきます。
 
文字間隔は、CADソフトによって考え方が色々と変わってくる場合が多いようです。まず AutoCADの場合、ダイナミックテキスト(DTEXT)には文字間隔の指定はありませんが、マルチテキスト(MTEXT)では書式Tで指定出来るようです。Jw_cadの場合は文字間隔というデータ項目がありますが、半角文字の次の文字間隔は全角文字の場合の1/2、となっていて文字の最後にも文字間隔が付く、というイメージです。
しかし、SXF仕様ではこのどちらでもなく、文字間隔値の指定があり、全角文字・半角文字に関わらず、文字と文字の間の空白量は一定となっており、文字の最後には文字間隔分の空白はありません。
SXF仕様では、半角文字幅は、全角文字幅の1/2になるとの事です。これはWindows用のCADではごく普通の話ですが、DOSの頃は、半角文字幅と全角文字幅は同じ、というソフトも多かったです。AutoCADのダイナミックテキストもその仕様です。つまり半角文字「A」と全角文字「A」は同じように見えたという事です。SXF仕様では、1文字1文字の文字幅を指定するのではなく、文字範囲幅=文字列全体の長さを指定する事になっています。ですので横書き文字での1つの半角文字幅(W)は
  Width=(文字バイト数)×W+(文字数−1)×Spc
  より
  W=(Width−(文字数−1)×Spc)÷(文字バイト数)
  H= Height
となります。全角文字幅はこれの倍、となります。
縦書き文字の場合は、文字範囲幅ではなく、文字範囲高で表現されますので
  W= Width÷2
  H=(Height−(文字数−1)×Spc)÷(文字バイト数)
となります。
しかし、半角文字は横長な形状になるのではなく、-90°回転した状態になります。そのため上記のW・H は入れ替わって
  W=(Height−(文字数−1)×Spc)÷(文字バイト数)
  H= Width
となります。
 
なお、上記はフォントが固定ピッチの場合です。プロポーショナルフォントを利用する場合は、
・プロポーショナルフォントの表示は、SXF ブラウザの表示に従い、一文字毎にフォントサイズの比率に応じた幅を算出して表示することを推奨する。
とあります。プロポーショナルフォントの場合には、文字によって横幅がころころと変動します。そうなると上記の式は使用出来ません。1文字1文字の幅を取得する必要が出てきますが、これはおそらく「便利な算出用関数」というのは無いと思います。TCanvasにフォントを指定して文字を作図すると想定した上でのTextWidthを取得して、という事になりそうです。縦書き文字の場合も、中央に位置合わせをするのに必要でしょう。
 
WindowsのGDI・GDI+・Direct2DでのTextOutで文字列を描画する際には任意ドット数での文字間隔の指定は出来ませんので、文字間隔を表現するには、文字を1文字ずつばらばらにして1文字ずつ描画・位置移動を繰り返す必要があります。その際、当初は単純にフォント指定部と文字表示部を別々にして、呼び出せばいいと想定していましたが、縦書き文字での半角文字の対応を考えるとそう単純には行かない様子です。また、プロポーショナルフォント利用時は1文字単位で文字幅が変わる為、文字幅指定を含むフォント指定を最初に1回すればいいという訳にも行きません。ですので UnitGraph.pas内の G_Text1を文字数分呼び出しを行う手続きを、データリンクする UnitDataGraph.pas で作成する事とします。
procedure DisplayText(lay,col,fon,bp,dir:integer;
 px,py,th,tw,ts,an,sl:double;st:string;t:TMatrix);
 

※SXF Ver.3.1仕様書より
 
文字の回転の基準点となるのは、文字左下点となりますので、指定された文字配置基点から、左下点を取得します。
case(bp)of
2: begin   // 2:中下
  dx := -tw/2.0 ;
  dy := 0.0 ;
 end;
3: begin   // 3:右下
  dx := -tw ;
  dy := 0.0 ;
 end;
4: begin   // 4:左中
  dx := 0.0 ;
  dy := -th/2.0 ;
 end;
5: begin   // 5:中中
  dx := -tw/2.0 ;
  dy := -th/2.0 ;
 end;
6: begin   // 6:右中
  dx := -tw ;
  dy := -th/2.0 ;
 end;
7: begin   // 7:左上
  dx := 0.0 ;
  dy := -th ;
 end;
8: begin   // 8:中上
  dx := -tw/2.0 ;
  dy := -th ;
 end;
9: begin   // 9:右上
  dx := -tw ;
  dy := -th ;
 end;
else begin  // 1:左下
  dx := 0.0 ;
  dy := 0.0 ;
 end;
end;
px := px + dx ;
py := py + dy ;
それから変換行列を考えます。文字は回転を行いますので、この左下点をまず(0,0)状態にしないといけませんから、移動を行い、回転をして、再度移動をして元の位置に戻します。そして部分図〜の変換行列を掛けます。
MtxMove(tr, -px,-py);
MtxRotD(tr1,an);
tr := MtxMult(tr,tr1) ;
MtxMove(tr1, px,py);
tr := MtxMult(tr,tr1) ;
t := MtxMult(tr,t) ;
部分図〜によって、文字が変形状態になるやもしれません。しかしこれは SXF仕様により、変形しないよう正規化を行う必要があります。

※SXF Ver.3.1仕様書より
また、部分図〜によって文字の描画角度が変わってくる場合がありますので、文字下線部分を計算、その角度を取得します。文字枠を算出したら、クリッピングチェックを行っておきます。
 
文字属性(太字・斜体・下線・取消線)は取り合えず何もせず通常状態としておきます。但し、GDIの場合、スラント角度を指定した場合には斜体にするようにしています。フォント名の先頭に「@」が付いていたらこれを除去するようにしておきます。
at := 0 ;
if (gp.DisplayMode = 0)and(Abs(sl) > LIMIT8) then at := 2;
if (fon = 0) then fon := CData.zLay[lay-1].Mfont ;
fo := CData.zFnt[fon-1].name ; // フォント名
s1 := Copy(fo,1,1);
if (s1 = '@')or(s1 = '@') then fo := Copy(fo,2,Length(fo));
そして、横書き・縦書きを別々に、文字を1文字ずつばらばらにして、作図していくようにします。また、文字1文字単位でクリッピングチェックも行うようにしておきます。文字の描画は結構遅くなりますので、クリッピングをやらないよりは、やっておいた方がトータル的に速くなると思われます。
if (dir = 1) then begin
 // 横書き
 w1 := 0.0;
 w2 := gp.G_TextWidth(at,32,32,fo,st);
 mh1:= th ;        // 行列変換前の文字高
 mw1:= tw - (l1-1.0)*ts ; // 行列変換前の文字幅範囲(文字間隔除去
 mh2:= my ;        // 行列変換後の文字高
 mw2:= mx - (l1-1.0)*ts*mx/tw ; // 行列変換後の文字幅範囲
 w5 := gp.G_TextWidth(at,100,100,fo,st);
 w4 := 100.0*mw1/w5;
 w5 := 100.0*mw2/w5;
 for i:=1 to l1 do begin
  s1 := Copy(st,i,1);
  w3 := gp.G_TextWidth(at,32,32,fo,s1) / w2 ;
  // 1文字単位のクリッピングチェック
  x1 := px+w1;  y1 := py;  MtxXY(x1,y1, x1,y1, t);
  x2 := px+w1+w4; y2 := py;  MtxXY(x2,y2, x2,y2, t);
  x3 := px+w1+w4; y3 := py;  MtxXY(x3,y3, x3,y3, tr1);
  x4 := px+w1;  y4 := py;  MtxXY(x4,y4, x4,y4, tr1);
  if (Clip4Point(x1,y1,x2,y2,x3,y3,x4,y4, WndX1,WndY1,WndX2,WndY2) < 2) then begin
   x1 := px + w1 ;
   y1 := py ;
   MtxXY(x1,y1, x1,y1, tr1);
   Z2D(ix,iy, x1,y1);
   gp.G_Text1(at,ix,iy,Round(mh2*mm_dot),Round(w5*mm_dot),ma,sl,fo,s1);
  end;
  w1 := w1 + mw1*w3 + ts;
 end;
end
 
さて、問題なのは縦書き文字です。
半角文字は、横に寝かした状態にしなければなりません。そこで、はたと気付くのですが、半角文字とは、一体、何なのでしょうか?
Shift-JISでの旧来の文字システムでは、半角文字=ASCIIコードの1バイト文字、全角文字=2バイト文字(※2バイト系半角文字というのもありますが)で半角文字は全角文字の半分の文字幅で描画される、となります。
 
しかし、ここでは Shift-JISコードではなく、Unicode(UTF16) を利用します。ですので、半角文字も全角文字も双方とも、2バイトです。Length関数もバイト数ではなく文字数を返しますし、Copy関数も文字数で考えて扱います。ですのでバイト数でのチェックも出来ない訳です。また、文字幅については、プロポーショナルフォントを利用するとなると、文字1文字1文字で文字幅はころころ変わる訳ですから、文字幅によってチェックすることも出来ません。更に、Unicodeですから、アルファベット・日本語だけでなく、様々な言語が扱えるようになります。果たして、どの文字が半角文字で、どの文字が全角文字だと言えるのか明確に出来るでしょうか・・・!?
ここでは、UnitFunc.pas にて
{ 文字列(1文字)が半角文字かどうかをチェックする
  s : 文字列(1文字)
  (ret) : True : 半角文字 False : 全角文字
}
function HanCheck(s:string) : Boolean ;
var
 i : integer ;
begin
 Result := False ;
 i := Ord(s[1]) ;
 if (i >= $20)and(i <= $7F)or(i = $203E) then Result := True ;
 if (i >= $ff61)and(i <= $ffdc)
  or(i >= $ffe8)and(i <= $ffee) then Result := True ;
end;
としています。新たに「半角文字」に入れたい文字があればここに記述してください。
 
次に、全角文字でも、「」や()のような文字は、縦書きにすると








になってしまいますので、半角文字同様、回転状態にしなければなりません。また、











のように、句読点は左側に寄ってしまうので、右のほうへ少し移動する必要があります。また、プロポーショナルフォントを利用している場合は、文字幅が1文字1文字違ってきます。なるべく中央に合わせるように補正を掛けていますが、それでも、GDI、GDI+、Direct2D により描画システムが異なるため、多少違って見えてしまいます。どのみち完全に一致しませんし、そういった調整に時間を掛けて別の不具合を作っても意味が有りませんので、ここでは余りこだわりません。
 
これまでのテストに追加して描かせているので少し画面的に綺麗ではありませんが・・・

という風に、横書き文字、縦書き文字、が書けるようになっています。
 
それでは、ここまでのテストプログラムです。実行ファイル、gdiplus.dll、gdipフォルダは入っていません。ソースのみです。
 
CAD装置(1)
CAD装置(2)
メディア
AutoCADの
DIESELマクロ
CSV
DXF
PCES
IGES
STEP
数学とCAD
CAD作ろ!
CADを考える
 ▲PREV
 ▼NEXT
M7
Jw_cad
 
お問い合わせ 
本サイトはリンクフリーです
リンクバナー
(C)Copyright 1999-2015 By AFsoft All Rights Reserved.