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

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

プログラミングについて
ホームページについて
キャドについて
電子カタログについて
書籍・雑誌
イベント
リンク集
CADを考える:文字(1)
前頁では、幾何要素/表記要素フィーチャの楕円弧について見ましたので今回は文字について見てみます。
 
幾何要素/表記要素|文字

※SXF Ver.3.1仕様書より
パラメータ説明範囲
LayerIntレイヤコード
ColorInt色コード
FontInt文字フォントコード
str[257]Char文字列0<文字列長≦256バイト
text_xdouble文字列配置基点X座標 double(64bits)の
範囲(有効桁15桁)
text_ydouble文字列配置基点Y座標 double(64bits)の
範囲(有効桁15桁)
Heightdouble文字範囲高0<高さ<1.0×1015
Widthdouble文字範囲幅0< 幅 <1.0×1015
Spcdouble文字間隔0≦間隔<1.0×1015
Angledouble文字列回転角0≦ 度 <360
Slantdoubleスラント角度-85≦ 度 ≦85
b_pntInt文字配置基点(1:左下,2:中下,3:右下,4:左中,5:中中,6:右中,7:左上,8:中上,9:右上)
DirectInt文字書出し方向(1:横書き, 2:縦書き)
備考
・文字列回転角度は水平右側が0度、反時計廻りが正、単位は度とする。
・スラント角度は垂直上側が0度、反時計廻りが負、単位は度とする。
・TrueTypeフォントでスラント角度が0度以外の値が指定された場合は、イタリック(斜体)として解釈する。
・文字要素の大きさは配置された用紙、部分図の座標系で解釈する。
・横書きフォントの縦書きにおいて、描画される文字列がすべて半角文字で構成されていても、文字範囲幅は全角文字の範囲幅を示すものとする(横書きフォントの縦書きについては、5.補足説明を参照)。


※SXF Ver.3.1仕様書より
「4.補足説明」では
3)文字フィーチャにおいて、フォント(縦/横)と縦/横書きの関係は以下の通りとする。
・「縦書きフォントを横書きする場合」の表示は任意(=ソフトウェア毎の解釈)とする。
・上記以外は下図の配置例(配置角はいずれも0度とする)に従って表示するものとする(全角文字が意図通りに表示される)。
. イタリック(=斜体)は、横書きフォントの場合は約15度傾き、縦書きフォントの場合は-15度傾くものとする。

※SXF Ver.3.1仕様書より
とあります。
 
SXF Ver.3.1実装規約では
1-5 文字
(1)2バイト文字と1バイト文字が混在する場合の文字列表示(MS ゴシック/MS 明朝フォントを使用する場合)
・2バイト文字と1バイト文字が混在する場合、半角文字の幅を以下の通りに考え、1文字の幅を算出した上で表示する。
   横書きフォント・横書き:2 バイト文字の幅の半分
   縦書きフォント・縦書き:2 バイト文字の高さの半分
【趣旨】
・仕様策定当初は、文字列全体の幅と高さで示される長方形より文字がはみ出さなければ良い、という考え方であったが、実際の図面交換ではこれでは不都合な場合がある。
・文字列の大きさは文字列全体の幅・高さ・大きさで交換されるため、一文字の幅を計算で求める必要があるが、全角文字と半角文字が混在する場合、全角文字に対する半角文字の比率を統一しなければ、異なった計算結果となる。

(2)プロポーショナルフォントの表示
・プロポーショナルフォントの表示は、SXF ブラウザの表示に従い、一文字毎にフォントサイズの比率に応じた幅を算出して表示することを推奨する。
とあります。これらを踏まえた上でプログラミングを行う必要があります。
WindowsGDIであれば、通常の横書きフォントに加えて、縦書きフォント=フォント名の前に「@」文字が付いているフォント、が利用出来ますが、GDI+ 及び Direct2D では、この縦書きフォントをそのまま利用出来ないようです。ですので本プログラムでは、横書きフォントで表現するものとし、縦書きフォントの指定は行わないようにします。
その場合、上記(補足説明)の「横書きフォント、縦書き配置」の場合、縦書きの文章を記述するには、やはり、違和感が伴います。ですのでここでは、横書きフォントで表現しますが、「縦書きフォント、縦書き配置」のスタイルで表現することとします。
 
それでは文字のデータ構造を決めます。
UnitData.pas
type
 ・・・
 TDataText = record   // 幾何要素/表記要素|文字
  exf : Boolean ;    // 存在フラグ(True:有り False:無し)
  Layer : Integer ;   // レイヤ(1〜256)
  Color : Integer ;   // 色 (0:レイヤ色  1〜256)
  Font : Integer ;   // フォント (0:レイヤフォント 1〜1024)
  str : string ;    // 文字列 (最大256バイト)
  text_x : double ;   // 文字列配置基点X座標
  text_y : double ;   // 文字列配置基点Y座標
  Height : double ;   // 文字範囲高
  Width : double ;   // 文字範囲幅
  Spc : double ;    // 文字間隔
  Angle : double ;   // 文字列回転角[°]
  Slant : double ;   // スラント角[°]
  b_pnt : Integer ;   // 文字配置基点(1:左下,2:中下,3:右下,4:左中,5:中中,6:右中,7:左上,8:中上,9:右上)
  Direct: Integer ;   // 文字書出し方向(1:横書き, 2:縦書き)
end;
 
 TFukugoZukeiDef = record  // 構造化要素|複合図形定義
  exf : Boolean ;     // 存在フラグ(True:有り False:無し)
  name : string ;     // 複合図形名
  Flag : Integer ;     // 種別フラグ
  cnt : Integer ;     // 配置数
  ・・・
  mTxt : array of TDataText;    // 幾何要素|文字
  mTxtN : Integer ;         // 数
  mFzk : array of TDataFukugoZukei; // 構造化要素|複合図形配置
  mFzkN : Integer ;         // 数
 end;
 
 TDataClass = class
  public
  { Public 宣言 }
  ・・・
  dTxt : array of TDataText;    // 表記要素|文字
  dTxtN: Integer ;         // 数
  ・・・
 
データ追加用の関数は
function AddDataText(s,st:string;lay,col,fnt,bp,dir:integer;
 px,py,ph,pw,ps,an,sl:double) : Boolean;
のようにします。線分等と同様、最初に「データ追加先の複合図形名(幾何要素) null:用紙へ追加(表記要素)」を指定するようにしています。
 
UnitData.pas
// 文字 データ項目の追加登録
function TDataClass.AddDataText(s,st:string;lay,col,fnt,bp,dir:integer;px,py,ph,pw,ps,an,sl:double) : Boolean;
var
 m : integer ;
begin
 Result := False;
 if not(StrCheck(s)) then exit; // 文字列数チェック
 if (ph < LIMIT10)or(pw < LIMIT10) then exit ; // 範囲が <= 0
 if (lay < 1) or (lay > zLayN) then lay := 1 ;
 if (col < 0) or (col > zColN) then col := 0 ;
 if (fnt < 0) or (fnt > zFntN) then fnt := 0 ;
 if (bp < 1) or (bp > 9) then bp := 1 ;
 if (dir <> 1) then dir := 2 ;
 if (ps < 0.0) then ps := 0.0;
 an := SetAngle(an);
 if (sl < -85.0) or (sl > 85.0) then sl := 0.0;
 
 if (s = '') then begin
  // 用紙へ追加
  if (AddDataOrder(-1,8,dTxtN)) then begin
   try
    Inc(dTxtN);
    if ((dTxtN mod 200) = 1) then SetLength(dTxt, dTxtN+199);
    with dTxt[dTxtN-1] do begin
     exf := True ;
     Layer := lay ;
     Color := col ;
     Font := fnt ;
     str := st ;
     text_x := px ;
     text_y := py ;
     Height := ph ;
     Width := pw ;
     Spc := ps ;
     Angle := an ;
     Slant := sl ;
     b_pnt := bp ;
     Direct := dir ;
    end;
    Result := True ;
    AddLayerCnt(lay,col,-1,-1,fnt);
   except
    Dec(dTxtN);
    Dec(dOrdN);
   end;
  end;
 end
 else begin
  m := FZukeiNameCheck(0,s);
  if (m = 0) then exit ; // 追加先の複合図形名は無いのでエラー
  with fDef[m-1] do begin
   if (AddDataOrder(m,8,mTxtN)) then begin
    try
     Inc(mTxtN);
     if ((mTxtN mod 200) = 1) then SetLength(mTxt, mTxtN+199);
     with mTxt[mTxtN-1] do begin
      exf := True ;
      Layer := lay ;
      Color := col ;
      Font := fnt ;
      str := st ;
      text_x := px ;
      text_y := py ;
      Height := ph ;
      Width := pw ;
      Spc := ps ;
      Angle := an ;
      Slant := sl ;
      b_pnt := bp ;
      Direct := dir ;
     end;
     Result := True ;
     AddLayerFCnt(lay,col,-1,-1,fnt);
    except
     Dec(mTxtN);
     Dec(mOrdN);
    end;
   end;
  end;
 end;
end;
 
さて、これで文字データの登録は出来るようになりました。
 
次は、文字の描画についてです。
GDIでの文字描画については、「CAD作ろ!」の文字を描く(1)以降で記述しています。GDI+での文字描画についてはこちらも既に「GDI+による落書きテストプログラム」にて記述済みです。Direct2Dの場合は、「Delphi2010」のDirect2D(9)にて記述済みです。多少調整を行って単純な文字描画を行う手続きを作ってみます。
UnitGraph.pas
// 文字描画
// at : 属性 +1:太さ +2:斜体 +4:下線 +8:取消線
// x,y: 文字位置(左上点)
// h,w: 文字高・文字幅
// an : 文字角度[°]
// fo : フォント名
// st : 文字列
procedure TGraphClass.G_Text1(at,x,y,h,w:integer;an:double;fo,st:string);
var
 LogFont : TLogFont ;
 lf : HFONT ;
 ff : TGPFontFamily;
 fon: TGPFont;
 m : TGPMatrix ;
 xy : double ;
 t : D2D_MATRIX_3X2_F ;
begin
 if (w < 2) then w := 2 ;
 if (h < 2) then h := 2 ;
 xy := w/h;
 case(DisplayMode)of
 0: begin // GDI
   with LogFont do begin
    lfHeight := h ; // 文字高さ
    lfWidth := w div 2 ; // 文字幅
    lfEscapement := Round(an*10); // 回転角度[°] 10倍値
    lfOrientation := Round(an*10); // lfEscapementと同じ値
    lfWeight := FW_NORMAL ; // 太さ指定 通常は FW_NORMAL
    lfItalic := 0 ; // 斜体指定 有=1 無=0
    lfUnderline := 0 ; // 下線指定 有=1 無=0
    lfStrikeOut := 0 ; // 取消線指定 有=1 無=0
    if ((at and 1) > 0) then lfWeight := FW_BOLD ;
    if ((at and 2) > 0) then lfItalic := 1 ;
    if ((at and 4) > 0) then lfUnderline := 1 ;
    if ((at and 8) > 0) then lfStrikeOut := 1 ;
    lfCharSet := DEFAULT_CHARSET ;
    lfOutPrecision := OUT_DEFAULT_PRECIS ;
    lfClipPrecision:= CLIP_DEFAULT_PRECIS ;
    lfQuality := DEFAULT_QUALITY ;
    lfPitchAndFamily := DEFAULT_PITCH ;
    StrPCopy(lfFaceName ,fo);
   end;
   lf := CreateFontIndirect(LogFont);
   with Ca do begin
    Font.Handle := lf ;
    Font.Color := Pen.Color ;
    Brush.Style := bsClear ;
    TextOut(x,y,st);
   end;
   DeleteObject(lf);
  end;
 1: begin // GDI+
   ff := nil;
   try
    ff := TGPFontFamily.Create(fo);
    fon:= nil;
    try
     fon:= TGPFont.Create(ff, h, at, UnitPixel);
     m := TGPMatrix.Create(
      xy*cos(-an/180.0*Pi),xy*sin(-an/180.0*Pi),
       - sin(-an/180.0*Pi), cos(-an/180.0*Pi),
       x,y);
     gra.SetTransform(m);
     m.Free ;
     gra.DrawString(st,-1,fon,MakePoint(-0.17/xy*w,0.0),br0);
     gra.ResetTransform;
    except
     ;
    end;
    if (fon <> nil) then fon.Free;
   except
    ;
   end;
   if (ff <> nil) then ff.Free;
  end;
 2: begin // Direct2D
   with D2DCanvas do begin
    t._11 := xy*cos(-an/180.0*Pi) ;
    t._12 := xy*sin(-an/180.0*Pi) ;
    t._21 :=- sin(-an/180.0*Pi) ;
    t._22 := cos(-an/180.0*Pi) ;
    t._31 := x ;
    t._32 := y ;
    RenderTarget.SetTransform(t);
    Brush.Style := bsClear ;
    Font.Brush.Handle := D2Dbr ;
    Font.Name := fo ;
    Font.Style := [] ;
    if ((at and 1)>0) then Font.Style := Font.Style+[fsBold];
    if ((at and 2)>0) then Font.Style := Font.Style+[fsItalic];
    if ((at and 4)>0) then Font.Style := Font.Style+[fsUnderline];
    if ((at and 8)>0) then Font.Style := Font.Style+[fsStrikeOut];
    Font.Height := - h ;
    TextOut(0,0,st);
    t._11 := 1.0 ;
    t._12 := 0.0 ;
    t._21 := 0.0 ;
    t._22 := 1.0 ;
    t._31 := 0.0 ;
    t._32 := 0.0 ;
    RenderTarget.SetTransform(t);
   end;
  end;
 end;
end;
これを実行するためのテストは、
gp.G_Text1(0,50,100,20,20,30,'MS 明朝','ABCあいう');
のような感じです。但しこれには文字間隔の指定、基点の指定はありません。また、GDI+での文字描画が、GDI、Direct2Dでの場合と位置・大きさが合わない為、目分量で微調整していますがそれでもピッタリは合っていません。
 
上記には、文字のスラント角度が考慮されていません。GDIの場合には文字属性を斜体にするしかないと思われますが、GDI+、Direct2Dの場合には、文字を変換行列で変形させるという事が出来ますので、せん断の変換を行う事で実現が可能です。UnitFunc.pas にて手続き MtxSlant()を用意し、UnitGraph.pasのuses節で UnitFunc を追加します。変換行列の処理が面倒なので、アフィン変換を行います。
1)GDI+の場合には最初に微調整分の移動行列を作成
2)文字高・文字幅の比率のための倍率行列を作成
3)文字スラント用のせん断行列を作成
4)せん断でずれてしまった位置補正を行う移動行列を作成
5)文字回転を行う行列を作成
6)文字位置を移動する行列を作成
MtxMove(mt, -0.17/xy*w,0.0); // 目分量微調整
MtxScale(mt1, xy,1.0);
mt := MtxMult(mt, mt1);
MtxSlant(mt1, -Tan(sl/180.0*Pi),0.0);
mt := MtxMult(mt, mt1);
MtxMove(mt1, h*Tan(sl/180.0*Pi),0.0);
mt := MtxMult(mt, mt1);
MtxRotD(mt1, -an);
mt := MtxMult(mt, mt1);
MtxMove(mt1, x,y);
mt := MtxMult(mt, mt1);
これを文字描画のための行列へセットして、描画させるようにします。
 
この手続きは、文字間隔や縦書文字には対応していませんので、これをこのまま使う事はありませんが、取り合えず少し長くなりましたので、この頁はここまでにして、次回に続く、という事にします。
 
取り合えず簡単なテストです。
 

 
Delphi2010では、プログラムコード上で Unicode文字を使えますから上記のように書けますけれども、HTMLをSHIFT-JISで作成すると Unicode文字は使えませんので上記は画像化していますが、やはり、Unicode文字が使えると、使える文字の幅が広がって面白いですね。但し、他のSHIFT-JIS対応のCADソフト等へデータを受け渡すと文字化けを起こすと思われますので、注意が必要でしょう。

 

 

まだデータとはリンクしていません。文字を描画するだけのテスト状態です。文字位置は文字の左上点となります。上の小さい文字は、スラント角度を10°としています。下の大きい文字は、スラント角度を-10°としています。GDI+・Direct2Dではスラント角度は有効となっていますが、GDIでは無効となっています。
GDIでも、文字形状を取得し、得られる線分・ベジェ曲線情報を変形することでおそらくはスラント角度を実現出来ると思われますが、文字形状内の塗り潰しを行う処理も大変になってきますし、仮にそれが出来たとしても、処理速度も掛かるでしょうから、本プログラムではそういった事は行いません。
 
それでは、ここまでのテストプログラムです。実行ファイル、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.