2014년 7월 10일

Freetype 을 이용한 트루타입 폰트 분석

이전 게시글에서 윈도우 폰트의 원리및 해당 경로(BeginPath, EndPath)를 추출하는 API 를 소개한바 있다.


트루타입 폰트


오늘 소개하려는 방식은 오픈 소스인 Freetype 라이브러리를 사용한것으로 이를 이용해 개발자가 다양한 프로그램 예를 들어 폰트 제작, 편집, 뷰어등을 만들수도 있다.

또한 폰트를 각종 인쇄 매체에 프린트하는 기계장치를 제어하는 분야나, 레이저를 이용해 글자를 새기는 장비, 홈메이드 CNC 장치등 활용 범위가 다양하다.




Freetype 를 사용하는 가장 쉬운 방법은 위와같이 당연히 예제를 작성해 보는 것이다.

첨부된 예제에서는 폰트파일을 하나 지정해 폰트를 구성하는 모든 글자들(Glyph)를 순회하면서, 각 글자를 구성하는 모든 좌표정보를 출력하는 간단한 기능을 제공한다.




이같은 Raw 데이타를 이용하면 앞서 제시한 다양한 분야에 적용이 가능하다.

위 캡춰화면은 Arial.ttf 폰트 파일을 분석한 것으로 개별 글자의 구성요소를 보여준다. 즉 이동(MOVE TO), 선분(LINE TO), 그리고 제어점 정보(CONIC) 들이 출력된다. 즉 시작점(p0), 제어점(p1,2), 끝점(p3)이 있다고 가정하면, 아래 pseudo code 로 각 보간(interpolation) 점들을 연산해 낼수 있다.

#define CONIC_B1(t) (1.0-t)*(1.0-t)
#define CONIC_B2(t) 2.0*t*(1.0-t)
#define CONIC_B3(t) (t*t)
// ...
int steps = 100;
double t = 0.0;
double bias = 1.0 / steps;
do
{
  x = p1.x * CONIC_B1(t) + p2.x * CONIC_B2(t) + p3.x * CONIC_B3(t);
  y = p1.y * CONIC_B1(t) + p2.y * CONIC_B2(t) + p3.y * CONIC_B3(t);
  // 해당 x,y 위치정보 이용
  // 화면 출력, 기계장치 제어 등
  t += bias;
}while( t< 1.0);

// ...

위 코드를 살펴보면 결국 곡선을 미분화된 직선으로 보간하는것으로 얼마나 잘게 쪼갤지는 사용자가 지정한 steps 값에 의해 결정된다. 이를 통해 표현되는 곡선의 미려함을 지정할수 있게 될뿐 아니라, 확대 축소할경우 부드러운 곡선을 보간하고자 한다면 이 값을 키우거나 적절히 줄이면 품질, 속도를 조절 할 수 있게 된다.

곡선의 구현에 대한 정보는 다음의 링크에서 상세하게 기술되어 있다 링크 : http://www.codeproject.com/Articles/747928/Spline-Interpolation-history-theory-and-implementa


------------------------ main.cpp 소스--------------------

#include <windows.h>
#include <tchar.h>
#include <iostream>


/// 
/// copyright to sepwind@gmail.com (http://sepwind.blogspot.com)
/// 

#include ".\freetype\include\ft2build.h"
#include FT_FREETYPE_H
#include FT_MODULE_H
#include FT_OUTLINE_H
#include FT_GLYPH_H

#pragma comment(lib, "freetype.lib")


void usage() 
{
std::cout << "Copyright to sepwind 2014. \nblog site: http://sepwind.blogspot.com email: sepwind@gmail.com\n\n";
    std::cout << "Usage : fontconverter.exe <options> <ttf file> <sep file>\n";
    std::cout << "  ttf file : true type font input file\n";
    std::cout << "  sep file : sepwind font output file \n";
}

int moveTo(FT_Vector* to, void* );
int lineTo(FT_Vector* to, void* );
int conicTo(FT_Vector* control, FT_Vector* to, void* );
int cubicTo(FT_Vector* control1, FT_Vector* control2, FT_Vector* to, void* );


static const FT_Outline_Funcs decomposeFunctions = 
{
      (FT_Outline_MoveTo_Func) moveTo,
      (FT_Outline_LineTo_Func) lineTo,
      (FT_Outline_ConicTo_Func)conicTo,
      (FT_Outline_CubicTo_Func)cubicTo,
      0, 
 0
};


int moveTo(FT_Vector* to, void* ) 
{
std::cout << "move to\t\t" << to->x << ",\t" << to->y << std::endl;
    return 0;
}


int lineTo(FT_Vector* to, void* ) 
{
std::cout << "line to\t\t" << to->x << ",\t" << to->y << std::endl;
    return 0;
}


int conicTo(FT_Vector* control, FT_Vector* to, void* ) 
{
std::cout << "conic to\t" << to->x << ",\t" << to->y << "\tcontrol : " << \
control->x << ",\t" << control->y << std::endl;
    return 0;
}


int cubicTo(FT_Vector* /*control1*/, FT_Vector* /*control2*/, FT_Vector* to, void* ) 
{
std::cout << "cubic to\t" << to->x << ",\t" << to->y << std::endl;    
    return 0;
}


BOOL convertGlyph(FT_Face face, FT_ULong charcode)
{
    FT_Error error;
    FT_Glyph glyph;

    // load glyph
    error = FT_Load_Glyph(face,
                          FT_Get_Char_Index(face, charcode),
                          FT_LOAD_NO_BITMAP | FT_LOAD_NO_SCALE);
    if (error) 
{
        std::cerr << "FT_Load_Glyph: " << error << std::endl;
        return FALSE;
    }

FT_Get_Glyph(face->glyph, &glyph);
    FT_OutlineGlyph outlineGlyph = (FT_OutlineGlyph)glyph;
    if (face->glyph->format != ft_glyph_format_outline) {
        std::cerr << "Not an outline font\n";
return FALSE;
    }

///charcode
std::cout << ">>> begin of character code : " << charcode << std::endl;

    // trace outline of the glyph
    error = FT_Outline_Decompose(&(outlineGlyph->outline), &decomposeFunctions, 0);
    //error = FT_Outline_Decompose(&(outlineGlyph->outline), &decomposeFunctions, 0);

    if (error) 
{
        std::cerr << "FT_Outline_Decompose: " << error << std::endl;
        return FALSE;
    }
std::cout << "<<< end of character code : " << charcode << std::endl << std::endl;

return TRUE;
}


int _tmain(int argc, _TCHAR* argv[])
{
if (argc != 3)
{
usage();
return -1;
}

std::string inputfile = argv[1];
std::string outputfile = argv[2];    
    std::cout << "input file: " << inputfile.c_str() << "\n";
    std::cout << "output file: " << outputfile.c_str() << "\n";

FT_Library library;
FT_Face face;

FT_Error error = FT_Init_FreeType(&library);
    if (error) 
{
        std::cerr << "FT_Init_FreeType error : " << error << std::endl;
        return -1;
    }

error = FT_New_Face(library,
inputfile.c_str(),
                        0,
                        &face);
    if (error) 
{
        std::cerr << "FT_New_Face error : " << error << std::endl;
std::cerr << "please verify the input font filename and path " << std::endl;
return -1;
    }
        
    std::cout << "family:    " << face->family_name << "\n";
    std::cout << "height:    " << face->height << "\n";
    std::cout << "ascender:  " << face->ascender << "\n";
    std::cout << "descender: " << face->descender << "\n";
    
unsigned first = 0;
FT_Get_First_Char(face, &first);

FT_ULong  charcode;
    FT_UInt   gindex = 0;

    // iterate through glyphs
    charcode = FT_Get_First_Char( face, &gindex );
    while (gindex != 0) {
if (!convertGlyph(face, charcode))
break;
        charcode = FT_Get_Next_Char(face, charcode, &gindex);
    }

    FT_Done_Face(face);
    FT_Done_Library(library);
return 0;
}


ps1. Freetype 라이브러리를 기반으로 OpenGL 렌더링이 필요한 분들은 FTGL 라이브러리를 참고

ps2. 곡선에 대해 좀더 자세한 정보를 얻고자 한다면 "베지어 곡선" 으로 검색


댓글 없음:

댓글 쓰기

시리우스 라이브러리 홈페이지 오픈

현재 시리우스(Sirius) 라이브러리라는 제품을 개발하고 이를 소개하는 홈페이지를 오픈 하였습니다. 관심있는 분들의 많은 방문 요청드립니다. 앞으로 업데이트 소식및 변경사항은 스파이럴랩 홈페이지를 통해 진행할 예정입니다. 스파이럴랩 홈페이지 :  h...