Chinaunix首页 | 论坛 | 博客
  • 博客访问: 791416
  • 博文数量: 83
  • 博客积分: 7030
  • 博客等级: 少将
  • 技术积分: 1097
  • 用 户 组: 普通用户
  • 注册时间: 2007-08-06 15:50
文章分类

全部博文(83)

文章存档

2011年(2)

2010年(9)

2009年(56)

2008年(16)

我的朋友

分类:

2009-04-10 17:57:40

 Windows系统的TTF字体具有字体优美、可无级缩放等优点,最适合应用在CAD类图形处理等软件中。直接分析TTF字体的文件格式并读出每个字的轮廓矢量是相当困难的,我们可以借助API函数来方便地获得这些数据。

---- 调用函数GetGlyphOutline可以得到一个字的轮廓矢量或者位图。

---- 函数原型如下:

    DWORD GetGlyphOutline(
      HDC hdc,            // 设备句柄
      UINT uChar,    // 将要读取的字符
      UINT uFormat,    // 返回数据的格式
    LPGLYPHMETRICS lpgm,  // GLYPHMETRICS结构地址
      DWORD cbBuffer,  // 数据缓冲区的大小
      LPVOID lpvBuffer,  // 数据缓冲区的地址
      CONST MAT2 *lpmat2 // 转置矩阵的地址
    ); 
 
---- 其中,参数uFormat取值如下:

    GGO_NATIVE  - 要求函数返回字符的轮廓矢量数据;
    GGO_METRICS - 函数仅返回GLYPHMETRICS结构至lpgm;
    参数lpgm指向GLYPHMETRICS结构,该结构描述字符的位置。
    参数lpmat2指向字符的转置矩阵。
 
---- 本文以下C++ Builder程序示范如何在画布上以指定的大小绘制字符串。

---- 首先,建立一个新项目,在主窗口上放置一个Image控件,一个Edit控件,一个Button控件;然后,在Button的点击事件中加入如下代码:

#include < stdlib.h >

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  TRect ClipRect = Rect(0,0,Image1->Width,Image1->Height);
  Image1->Picture = 0;
  StretchTextRect(Image1->Canvas, ClipRect, Edit1->Text);
}

---- 添加如下子程序:

//---------------------
void TForm1::StretchTextRect(TCanvas
*pCanvas, TRect ClipRect, AnsiString Text)
{
  pCanvas->Font->Size = 100;
  pCanvas->Font->Name = “宋体";
  pCanvas->Pen->Color = clBlack;
  pCanvas->Pen->Mode = pmCopy;
  pCanvas->Pen->Style = psSolid;
  pCanvas->Pen->Width = 1;
  int XSize = ClipRect.Width() / Text.Length();
  int YSize = ClipRect.Height();

  MAT2 mat2;  // 转置矩阵,不用变换
  mat2.eM11.value = 1;mat2.eM11.fract = 0;
  mat2.eM12.value = 0;mat2.eM12.fract = 0;
  mat2.eM21.value = 0;mat2.eM21.fract = 0;
  mat2.eM22.value = 1;mat2.eM22.fract = 0;

  GLYPHMETRICS gm,gmm;

  // 首先获得字符的位置矩阵,存入gm
  GetGlyphOutlineA(pCanvas->Handle,0x0b0a1,
   GGO_METRICS,&gm,0,NULL,&mat2);

  char *ptr = Text.c_str();
  TRect TheRect;
  for(int i = 0;i < Text.Length();) {
    int c1 = (unsigned char)*ptr;
    int c2 = (unsigned char)*(ptr + 1);
    UINT nChar;
    TheRect.Left = i * XSize + ClipRect.Left;
    TheRect.Top = ClipRect.Top;
   TheRect.Right = (i + 2) * XSize + ClipRect.Left;
    TheRect.Bottom = ClipRect.Top + YSize;
    if(c1 > 127) {  // 当前字符是汉字
        nChar = c1 * 256 + c2;
        ptr+=2;i+=2;
    }
    else {   // 字母或数字
        nChar = c1;
        ptr++;i++;
    }
    // 获得当前字符数据的数组的大小
 DWORD cbBuffer = GetGlyphOutlineA(pCanvas->
Handle,nChar,GGO_NATIVE,&gmm,0,NULL,&mat2);
    if(cbBuffer == GDI_ERROR) break;
    void *lpBuffer = malloc(cbBuffer);
    if(lpBuffer != NULL) {
      // 读入数据置缓冲区
      if(GetGlyphOutlineA(pCanvas->
      Handle,nChar,GGO_NATIVE,
     &gmm,cbBuffer,lpBuffer,&mat2) != GDI_ERROR) {
 // 分析数据并绘制字符
    TMemoryStream *MBuffer = new TMemoryStream();
    MBuffer->Write(lpBuffer,cbBuffer);
    MBuffer->Position = 0;
    for(;MBuffer->Position < MBuffer->Size;) {
    int CurPos = MBuffer->Position;
    TTPOLYGONHEADER polyheader;

    int ptn = 0;
  MBuffer->Read(&polyheader,sizeof(polyheader));
    ptn++;
for(int j = 0;j < (int)(polyheader.cb
 - sizeof(polyheader));) {
    WORD wtype,cpfx;
    MBuffer->Read(&wtype,sizeof(WORD));
    MBuffer->Read(&cpfx,sizeof(WORD));
  MBuffer->Position += cpfx * sizeof(POINTFX);
    j += sizeof(WORD) * 2 + cpfx * sizeof(POINTFX);
    if(wtype == TT_PRIM_LINE) ptn += cpfx;
    else ptn += (cpfx - 1) * 3 + 1;
    }

    TPoint *pts = new TPoint[ptn+1];  // 存储多边形顶点
    MBuffer->Position = CurPos;
    ptn = 0;
  MBuffer->Read(&polyheader,sizeof(polyheader));
 TPoint pt0 = POINTFX2TPoint(polyheader.pfxStart,TheRect,&gm);
     pts[ptn++] = pt0;
    for(int j = 0;j < (int)(polyheader.cb - sizeof(polyheader));) {
    TPoint pt1;
    WORD wtype,cpfx;
    MBuffer->Read(&wtype,sizeof(WORD));
    MBuffer->Read(&cpfx,sizeof(WORD));
    POINTFX *pPfx = new POINTFX[cpfx];
    MBuffer->Read((void *)pPfx,cpfx * sizeof(POINTFX));
    j += sizeof(WORD) * 2 + cpfx * sizeof(POINTFX);
    if(wtype == TT_PRIM_LINE) { // 直线段
    for(int i = 0;i < cpfx;i++) {
    pt1 = POINTFX2TPoint(pPfx[i],TheRect,&gm);
    pts[ptn++] = pt1;
    }
  }
    else {  // Bezier曲线
    TPoint p0,p1,p2,p3,p11,p22,pp0,pp1,pp2,pt11,pt22;
    int i;
    for(i = 0;i < cpfx-1;i++) {
    pt11 = POINTFX2TPoint(pPfx[i],TheRect,&gm);
    pt22 = POINTFX2TPoint(pPfx[i+1],TheRect,&gm);
    pp0 = pts[ptn-1];
    pp1 = pt11;
    pp2.x = (pt11.x + pt22.x)/2;
    pp2.y = (pt11.y + pt22.y)/2;

    p0 = pp0;
    p1.x = pp0.x/3 + 2 * pp1.x/3;
    p1.y = pp0.y/3 + 2 * pp1.y/3;
    p2.x = 2 * pp1.x/3 + pp2.x/3;
    p2.y = 2 * pp1.y/3 + pp2.y/3;
    p3 = pp2;

    for(float t = 0.0f;t <= 1.0f;t += 0.5f) {
float x = (1-t)*(1-t)*(1-t)*p0.x+
3*t*(1-t)*(1-t)*p1.x+ 3*t*t
*(1-t)*p2.x + t*t*t*p3.x;
float y = (1-t)*(1-t)*(1-t)*p0.y
+ 3*t*(1-t)*(1-t)*p1.y+3
*t*t*(1-t)*p2.y + t*t*t*p3.y;
    pts[ptn].x = x;
    pts[ptn].y = y;
    ptn++;
    }
  }
    pt1 = POINTFX2TPoint(pPfx[i],TheRect,&gm);
    pts[ptn++] = pt1;
    }
    delete pPfx;
    }
    pts[ptn] = pts[0]; // 封闭多边形
    pCanvas->Brush->Color = clWhite;
    pCanvas->Pen->Mode = pmXor;
    pCanvas->Pen->Style = psClear;
    pCanvas->Brush->Style = bsSolid;
    pCanvas->Polygon(pts,ptn);

    delete pts;
    }
    delete MBuffer;
    }
    free(lpBuffer);
    }
  }
}
//---------------------
TPoint TForm1::POINTFX2TPoint(POINTFX pf,
TRect TheRect,GLYPHMETRICS *gm)
{
  TPoint point;
  float fx,fy;
  fx = pf.x.value + pf.x.fract / 65536.0f + 0.5f;
  fx = fx
     / (float)(gm->gmBlackBoxX + gm->gmptGlyphOrigin.x)
     * (float)TheRect.Width() + (float)TheRect.Left;
  fy = pf.y.value + pf.y.fract / 65536.0f + 0.5f;
  fy = ((float)gm->gmBlackBoxY - fy)
     / (float)(gm->gmBlackBoxX + gm->gmptGlyphOrigin.x)
     * (float)TheRect.Height() + (float)TheRect.Top;
  point.x = int(fx);
  point.y = int(fy);
  return point;
}

 

阅读(6607) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~