//IntelliCADで袋文字を作る・・・の主要部分。

//TrueTypeFontから文字の輪郭を得る

//PointFx型をdoubleに変換
procedure PointFixToXY(var pfix:TPOINTFX;var x,y:double);
begin
  x:=pfix.x.value+pfix.x.fract/65536.0;
  y:=pfix.y.value+pfix.y.fract/65536.0;
end;


//GetGlyphOutLine
procedure TForm1.DrawGlyph(moji:LongInt);
var
  GlyphSize: Longint;         //グリフの大きさを保持
  GlyphBits: Pointer;         //グリフの開始ポインタを保持
  P:Pointer;                  //作業用ポインタ
  GlyphMetrics: TGlyphMetrics;//グリフのメトリックス
  Matrix: TMat2;              //回転行列を保持
  
  Header:^TTPOLYGONHEADER;    //アウトラインのヘッダー部分のポインタ
  Curve:^TTPOLYCURVE;         //アウトラインの情報のポインタ
  PointA:^TPOINTFX;           //固定小数点型のPointのポインタ
  
  siz,n,nc:Longint;
  i:integer;
  typ,typC,num:integer;
  x,y:Double;
  stx,sty:Double;//開始座標を保持
begin

  //回転行列を初期化
  Matrix.eM11 := MakeFixed(1.0);
  Matrix.eM12 := MakeFixed(0.0);
  Matrix.eM21 := MakeFixed(0.0);
  Matrix.eM22 := MakeFixed(1.0);
  
  //確保するバッファの大きさを得る
  //mojiはShift-JISコードで指定する
  GlyphSize := GetGlyphOutline(Form1.Canvas.Handle, moji, GGO_NATIVE,
                                GlyphMetrics, 0, NIL, Matrix);
  
  //情報をもらうバッファを用意
  //確保できた先頭アドレスGlyphBitsを取得
  GetMem(GlyphBits, GlyphSize);

  
  //moji:アウトラインを取得しようとする文字のコード
  //ANK文字の場合
  //moji:=Ord((PChar('A'));
  //全角文字の場合、moji はShift-JISコードに変換する必要がある
  //var Chars:PChar;
  //Chars:=PChar('あ');
  //moji :=((Ord(Chars[0]) and $000000ff) shl 8) +(Ord(Chars[1]) and $000000ff);
  
  //アウトライン情報を取得
  //GlyphBitsを開始アドレスとして情報をもらう
  GetGlyphOutline(Form1.Canvas.Handle, moji, GGO_NATIVE, GlyphMetrics,
                  GlyphSize,GlyphBits, Matrix);

  //情報が書き込まれた先頭アドレスを作業用にコピー
  P:=GlyphBits;

  //最初のポインタ(格納先)にTPOLYGONHEADER構造体をあてはめ、
  //ヘッダー部分の情報を読み出す。

  Header:=P;
  
  //Header.cbには全体のデータの大きさが格納されている
  //全体のデータの大きさは、TTPOLYGONHEADERデータ+TPOLYCURVEデータの大きさ
  
  //Header.dwTypeにはヘッダーのタイプが格納されている
  //これが24であれば取得成功と判断する
  
  //Header.pfxStartにはアウトラインの開始座標格納されている
  

  //ヘッダーのタイプをここで取得しておく
  //typ=24であれば取得成功と判断する
  typ:=Header.dwType;

  //ひとつの構造体の大きさ
  n:=sizeof(TTPOLYGONHEADER);

  while typ=24 do begin
  
    //全体のデータの大きさはHeader.cbに格納されている
    //TTPOLYGONHEADERデータ+TPOLYCURVEデータの大きさ
    siz:=Header.cb;

    //アウトラインの開始座標を取得
    //取得したPointFXをDoubleに変換
    PointFixToXY(Header.pfxStart,x,y);

    stx:=x;sty:=y;//開始座標を記憶

    //Headerの大きさの分だけポインタを進めて次の情報を読む
    //開始ポインタからTPOLYGONHEADER構造体の大きさだけ進んだところが
    //POLYCURVEの情報を取得する始まりとなる
    
    Inc(Header);//POINERを進める
    P:=Header;
    
    //カーブの情報を読む
    Curve:=P;
   
    siz:=siz-n;//取得済みのTTPOLYGONHEADERの大きさを引く
    
    //サイズが0になるまで情報を取得
    while siz > 0 do begin

      //カーブ開始座標=アウトラインの開始座標または前回の最終の座標
      Memo1.Lines.Add(Format('%f,%f',[x,y]);
      
      //カーブのタイプを取得
      //カーブのタイプ 1=直線、2=曲線
      
      typC:=Curve.wType;
      Memo1.Lines.Add('Curve.wType:'+IntToStr(typC));

      //カーブの種類1(直線)か2(曲線)でなければエラーと考える
      if (typC<>1) and (typC<>2) then Break;

      //格納されているカーブの数
      num:=Curve.cpfx;
      
      if num > 0 then begin
        //座標の格納先にポインタを移動
        PointA:=@Curve.apfx;
        
        for i:=0 to num-1 do begin
          //取得したPointFXをDoubleに変換
          PointFixToXY(PointA^,x,y);
          Memo1.Lines.Add(Format('%f,%f',[x,y]);
            
          //直線であればここで描画用にセット
          if typC=1 then begin
            
            //直線の場合はこのまま描画座標として使える
            ;;
          end
          //曲線であれば座標を記憶するだけ
          else begin
            
            //曲線の場合は前回終了座標+曲線として取得した座標2点を
            //スプラインに変換する必要がある。
            //ここでは、座標を記憶するだけでスプライン変換はまとめて行う。
            
            //Win95の場合はCureve点は前回終了座標を含めて3点とは限らず、
            //4点の時がある。(つまりカーブ点が3つの時)
            //その場合は2点目と3点目の中点をとって3点の倍数にし、
            //3点ずつをスプライン変換する
            // 第1点、第2点、第2点と第3点の中点の3点
            // 第2点と第3点の中点、第3点、第4点の3点
            ;
          end;
          //次を読むためにポインターを進める
          Inc(PointA);
        end;
        //次の処理のためにポインタを保持
        P:=PointA;
      end
      else begin
        //次を読むためにポインターを進める
        Inc(Curve);
        //次の処理のためにポインタを保持
        P:=Curve;
      end;

      nc:=(sizeof(TTPOLYCURVE)+sizeof(POINTFX)*(num-1));
      siz:=siz-nc;

      //次のループのためにカーブを取得しておく
      Curve:=P;

      //曲線であれば極小線分の集合に変換して描画用に追加
      if typC=2 then begin
        
        //記憶した座標を2次B-SPLINEに変換
        
        //取得した座標3点を(xA,yA)、(xB,yB)、(xC,yC)とすると
        //x:=(xA-xB*2.0+xC)*sqr(t)+(xB*2.0-xA*2.0)*t+xA;
        //y:=(yA-yB*2.0+yC)*sqr(t)+(yB*2.0-yA*2.0)*t+yA;
        //のtの値を0.0〜1.0の間で変化させ、
        //得られたx,yの値をプロットしていく
        //tの値(0.0〜1.0)を細かく変化ほど滑らかな点の変化となる
        ;;
      end;
      //開始座標と終了座標が違っている時
      if (stx<>x) or (sty<>y) then begin
        //開始座標と終了座標をつなぐ処理
        ;;
      end;
    end;  
    Header:=P;
    //終了ポインタからHeaderを読む
    //Header.dwType=24の時は、これまでの処理を繰り返す
    typ:=Header.dwType;
  end;
  //メモリを解放
  FreeMem(GlyphBits, GlyphSize);
end;