Previous Next Table of Contents

5. X 프로그래밍 기초 : 폰트

Xlib 와 함께 하는 X 프로그래밍이 재미있으신가요? Xlib 은 아주 저수순의 함수 모음입니다. 따라서 이것 자체만으로는 프로그램을 짜내기가 상당히 힘듭니다. 실제 어플 개발에 있어서는 바로 위 상위 수준인 Toolkit 수준에서 이루어지지요. 또는 많은 사람들이 사용하고 있는 실질적인 표준 모티프(Motif)가 있습니다. 하지만 제가 강조하고 싶은 것은 Toolkit과 모티프만으로는 어떤 좋은 프로그램도 만들 수가 없습니다. Xlib 가 저수준이지만 그것은 다시 말하면 강력하다는 말도 됩니다. 이 둘이 결합해야만 성능좋은 프로그램이 나온다고 확신합니다.

아직도 해야할 얘기들이 많지만, 앞으로 남은 것들에 대해서 간략하게 말씀드리고 오늘의 강의를 시작해볼까 합니다. 오늘의 폰트 출력 강의에 이어 가장 중요하다고 생각되는 이벤트 처리문제 ( 마우스 이벤트, 키보드 이벤트 )가 남아 있습니다. X 윈도의 컬러 시스템에 대해서는 Xt ( X Toolkit ) 프로그래밍에 들어가서 설명드리겠습니다. 너무 오랫동안 Xlib 프로그래밍에 머무르면 흥미를 잃기 쉽기 때문입니다.

5.1 폰트 출력에 대하여 ...

어떠한 텍스트(Text)를 출력하기 위해서는 다음과 같이 3 가지 과정을 거쳐야 합니다.

  1. 우선 원하는 폰트를 골라서 서버더러 그 폰트를 적재하라고 요청한다. 그러면 서버는 역시 폰트에 대한 ID만을 돌려준다. 만약 요청한 폰트가 없다면 에러가 발생한다.
  2. 폰트도 결국엔 "그리는 것"이다. 따라서 그래픽 컨텍스트 GC가 이미 있어야 한다. 일단 만들어 놓은 GC에다가 폰트 ID를 지정해준다.
  3. 이제는 적절한 함수를 통해서 출력한다. 이 때 우리는 2번 과정에서 이미 폰트 ID 정보를 지니고 있는 GC 만을 사용하면 된다. 1바이트계 라틴계 문자는 XDrawString 함수를, 우리나라와 같은 2바이트계 문자는 XDrawString16 함수를 사용하여 출력한다.

  간략한 함수의 사용 도표를 그려보면,

 [1] XLoadFont() -->  [2] XSetFont()  -->  [3] XDrawString[16]()

5.2 X 윈도우의 폰트 시스템을 알아봅시다.

본인은 X 윈도우 관리자 fvwm 강의를 하면서 폰트설정 부분에서 아주 간략하게 X 윈도우의 폰트체계, XLFD라는 명명규칙에 대해서 논했다. fvwm 강의에서는 철저하게 유저(user) 입장에서 쉽게 기술하려고 했으나, 이제 프로그래머가 되려고 하시는 여러분에게는 더욱 더 전문적인 지식이 필요하리라 봅니다.

통상 X 윈도우의 폰트는 /usr/lib/X11/fonts 에 저장되어 있습니다. 그 하부 디렉토리를 보시면 100dpi, 75dpi, Speedo, Type1, misc 등의 디렉토리를 찾으실 수 있을 겁니다. 그 디렉토리에 들어가보시면 화일명이 .pcf.Z 로 끝나는 엄청나게 많은 화일들을 보실 수 있습니다. Z 는 compress로 압축되었음을 나타내 주는 것으로서 X 윈도우 시스템은 폰트에 대한 압축을 지원합니다. 여러분도 아시겠지만, 폰트가 차지하는 디스크 용량은 어머어마 합니다. 특히 M$ 윈도우 계열에서 쓰이던 완성형 저장 방식의 폰트들은 화일 하나가 1메가를 넘어가는 것이 허다했지요. 깨끗한 문서에 대한 욕심은 다양한 폰트에 대한 요구로 이어졌구요. 압축 폰트에 대한 지원은 정말로 필요한 것임을 이해하실 수 있을 겁니다. 그 다음 fonts.dir, fonts.alias와 같은 중요한 화일을 만나실 수 있습니다.

fonts.dir

이 화일은 현재 디렉토리에 설치되어 있는 폰트 화일에 대한 XLFD 정보가 들어 있습니다. 새로운 폰트를 설치하고 나서는 mkfontdir 명령을 써서 fonts.dir 화일을 갱신합니다.

fonts.alias

XLFD 식의 명명방식으로 폰트 이름을 써준다는 것은 상당한 고역이 아닐 수 없습니다. 그래서 alias 별명을 사용합니다. 예를 들어서 우리가 가장 많이 사용하고 있는 fixed 라는 이름의 폰트는 각각의 시스템마다 실제로는 다른 폰트를 지칭하고 있을 수도 있습니다. 이러한 alias는 폰트이름을 외우기 좋게 사용자가 정의해서 쓸 수 있도록 해준다는 면도 가지고 있고, 특정 폰트이름이 모든 시스템에 항상 있는 것처럼 할 수도 있다는데 그 의의가 있습니다. 바로 위에서 예를 든 fixed라는 이름의 폰트이 좋은 예입니다. 참고로 fixed 라는 폰트 alias 가 지정되어 있지 않으면 대부분의 경우 X 자체가 뜨다가 말 겁니다. 이 화일은 자동으로 생성되는 것이 아니니 꼭 백업을 해두고 수정하시기 바랍니다.

fonts.scale

여러분이 Speedo나 Type1 디렉토리로 가보시면 fonts.scale이라는 화일을 찾으실 수 있습니다. 폰트 화일명과 그에 대한 XLFD 방식의 표기가 씌여져 있습니다. 이 두 가지 특수한 폰트에 대해서는 조금 있다가 설명드리기로 합니다. 대부분의 경우 fonts.dir 화일과 내용이 같을 겁니다.

5.3 X Logical Font Descriptio : XLFD 에 대하여

X 윈도우 시스템에서는 폰트가 갖고 있는 성질을 지시하기 위하여 14개의 필드로 이루어진 기술방식을 씁니다. 바로 이것을 XLFD라고 하지요. 다음과 같이 이루어져 있습니다.

  -misc-fixed-medium-r-normal--10-100-75-75-c-60-iso8859-1

14개의 필드는 모두 - 문자로 분리되어 있습니다. 각각의 의미에 대해서 알아보도 록 할까요?

제작자 필드 ( Foundry Field )

보통 맨 앞에 오는 이 필드는 * 문자를 써서 어떤 곳에서 만들었는지 상관하지 않는 경우가 많습니다. 보통 Adobe 회사가 만든 폰트의 경우에는 adobe라는 문자가 들어가 있지요. 이 밖에도 bitstream, b&h, schumacher, sun, kaist, hanyang, misc 등의 문자열이 들어가 있는 것을 종종 보실 수 있을 겁니다.

패밀리 필드 ( Family Field )

이 녀석이 그 폰트의 전체적인 모양을 결정합니다. 우리가 많이 들어본 Helvetica, Times Roman, Courier 등등이 그것입니다. 우리 폰트인 경우에는 myeongjo, gothic 등이 바로 그것이죠.

무게 필드(?) ( Weight Field )

medium, bold, demibold 등의 값을 가집니다. 어떤 의미인지 아실 겁니다.

경사도 필드(?) ( Slant Field )

활자의 경사에 대한 지시자입니다. r 은 우리가 알고 있는 정상적인 로만체, i는 이탤릭체를 말하며, o 는 무엇일까요? Oblique 라고 해서 이것도 경사문자체인데, 뭐라고 설명드려야 할지... 이것 말고도 ri, ro 등의 값이 있는데 각각 reverse italic, reverse oblique 의 의미를 갖습니다.

폭 필드 ( Setwidth Name Field )

보통의 경우 모두 normal로 설정되어 있습니다. 이외에 condensed, narrow 등의 값을 가집니다.

부가적 스타일 필드(?) ( Additional Style Field )

세리프(Serif)와 산세리프(Sans Serif)의 차이 같이 부가적 스타일의 차이를 나타내 주는 필드입니다. 세리프는 보통 우리가 보는 로마자 활자로서 I, M 과 같은 글자 위 아래에 있는 가늘고 짧은 선을 말합니다. 뭔지 감이 오십니까? I 자 위 아래에 있는 옆으로 가는 선 보이시죠? 여러분이 한텀을 쓰시고 또한 기본 폰트 (-kaist-*-johab-* ...)를 쓰신다면 지금 당장 확인하실 수 있습니다. 세리프 문자란 바로 그러한 장식을 갖는 문자를 말합니다. 산세리프는 그러한 장식이 없는 문자를 말합니다. Sans 라고 하는 것이 영어 고어로 Without 이라는 의미이기 때문입니다. Sans Serif 또는 Sanserif 라고 합니다. 보통 nil 의 값, 아무 값도 없는 경우가 허다합니다. 여러분이 폰트명을 살펴보시면 항상 6번째 필드가 아무 값도 없이 -- 이런 식으로 처리되어 있는 것을 보실 수 있습니다.

활자 크기 필드 ( Pixel Size Field )

아주 많이 사용하는 필드로서 활자의 크기를 나타내는 필드입니다. 우리가 가장 많이 사용하는 영역은 아마도 10   20 사이가 아닐까 생각합니다. Scalable 폰트에는 해당사항이 없습니다. 자유롭게 지정해 주십시요. 그 이외의 비트맵 폰트들은 특정 크기의 활자들만이 존재합니다.

필드 ( Point Size Field )

단도직입적으로 모릅니다! :)

X 방향 해상도 필드 ( X Resolution Field )
Y 방향 해상도 필드 ( Y Resolution Field )

각각 X, Y 방향의 해상도를 나타냅니다. 여러분이 75 dpi, 100 dpi 폰트를 설치 하셨다면 그 디렉토리에 가서 확인해보십시요. 75, 100 등의 숫자가 씌여져 있습니다. Scalable에서는 0 입니다.

공간 필드 ( Space Field )

영문자에서 활자 I와 M을 생각해 봅시다. 두 문자의 폭이 다르죠? I 자는 홀쭉한 문자이고 M 자는 뚱뚱한 문자인데, 컴퓨터에서는 두 활자를 똑같은 사각형의 영역에 넣어 생각하는 경우가 대부분입니다. 그냥 출력하기 편하니까요. 그러한 일반적인 경우를 m , Monospace 라고 하며, I와 M 자와 같이 폭에 따라 간격을 조화롭게 정렬해주는 것을 p , Proportional 비례문자라고 합니다. 출판물 그리고 수준 높은 에디터의 경우 p 활자를 쓰겠지요? c 는 m 과 동일한 의미입니다. 우리가 쓰고자 하는 활자는 대부분 m 입니다. 계산이 간편하니까요.

평균 폭 필드 ( Average Width Field )

글자 그대로 입니다. 평균이라고 한 이유는 위에서 말씀드리는 것에 의하면 쉽게 이해하실 수 있을 겁니다. 한글에는 적용사항 없는 것 같습니다.

등록 필드 ( Registry Field )

등록 필드입니다. 여기에 들어가는 값 몇 가지를 소개함으로써 설명을 대신 하겠습니다. adobe, dec, iso646.1991, 우리가 아주 자주 보는 iso8859, johab, johabs, ksc5601.1987 등이 그것입니다. 즉 문자 세트를 의미하는 것 같죠? johab(s)은 조합형 방식의 자소 폰트이며, ksc5601.1987은 완성형 방식의 글자 폰트입니다. 일반적인 영문자는 iso8859입니다.

인코딩 필드 ( Encoding Field )

휴, 마지막 필드이군요. 0, 1, 8, irv 등의 값들이 들어가 있습니다. 잘 모르는 필드입니다. 13번째 필드와 연관하여 생각하시면 좋습니다. 보통 iso8859-1, ksc5601.1987-0, iso646.1991-irv 과 같은 끄트머리 글자들을 보실 수 있습니다.

5.4 영문 텍스트 출력 연습

출력 예를 살펴보기로 합시다.

 #include <stdio.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>

 int main()
 {
        Display         *dpy;
        Window          w;
        Font            f;
        GC              gc;
        XSetWindowAttributes xswa;

        xswa.override_redirect = True;
        dpy = XOpenDisplay ( NULL);
        w   = XCreateSimpleWindow( dpy, RootWindow( dpy, 0 ), 50, 50,
                                        400, 300, 5, BlackPixel( dpy, 0 ),
                                                WhitePixel( dpy, 0 ) );
        XChangeWindowAttributes ( dpy, w, CWOverrideRedirect, &xswa );

        XMapWindow ( dpy, w );

        /* ----------------- 여기서부터가 본격적으로 중요한 부분 ----- */
        gc = XCreateGC( dpy, w, 0L, ( XGCValues * ) NULL );     /* [1] */
        f  = XLoadFont( dpy, "fixed" );                         /* [2] */
        XSetFont ( dpy, gc, f );                                /* [3] */

        /* 폰트 등록이 된 GC 를 가지고 전경색으로 문자열 출력 */
        XDrawString( dpy, w, gc, 100, 130, 
                        "Hello, Linuxers! Never Seen :)", 16 ); /* [4] */ 
        /* ----------------------------------------------------------- */

        XFlush( dpy );
        getchar();

        /* 뒷처리 */
        XUnloadFont( dpy, f );
        XFreeGC( dpy, gc );
        XDestroyWindow( dpy, w );
        XCloseDisplay( dpy );
 }

컴파일 방법은

     gcc -o drawstring drawstring.c -L/usr/X11/lib -lX11

소스 예제가 좀 좋지 않더라도 이해하시기 바랍니다. :) 이미 전에 설명드린 부분은 빼고 본격적으로 다른 부분만 설명드리겠습니다.

자, 폰트도 결국은 점을 찍어서 창에다 그리는 것이므로 GC 를 필요로 합니다. 우선은 GC 를 하나 만들어야겠죠?

  gc = XCreateGC( dpy, w, 0L, ( XGCValues* ) NULL );

그리고 위에서 강조했던 폰트 적재와 사용의 중간 과정을 머리 속에 떠올리십시요.

폰트 적재 ( Font Loading )

 함수 원형 :

 Font XLoadFont( Display *dpy, char *font_name );

font_name 문자열은 바로 위에서 길게 설명드렸던 XLFD 식의 완전한 폰트이름 또는 폰트 별명(alias)를 지정해주시면 됩니다. 성공하면 Font 형 변수를 반환합니다. 실패하면 물론 NULL 이겠지요?

위에서는 fixed 라는 별명을 가진 X 윈도우의 가장 기본적인 폰트를 사용하였습니다. 그 폰트가 지정되어 있지 않다면 X 윈도우 자체가 아마 시작하지 않았을것입니다. 여러분이 아시는 폰트 이름 또는 직접 /usr/X11/lib/fonts 디렉토리에 가셔서 각 디렉토리의 fonts.dir 에 나와있는 것들을 시험해보시기 바랍니다.

한 번 멋진 아도비사의 폰트를 사용해볼까요? 그럼 fixed 라고 쓰신 부분을 바꿔 보십시요.

  -adobe-courier-medium-o-normal--25-180-100-100-m-150-iso8859-1

그리고 다시 컴파일...

그런데 만약에 여러분께서 지정해주신 폰트를 X 서버( 정확히는 폰트에 관하여는 X 폰트 서버 xfs 의 담당 )가 찾지 못할 때, 또는 지원하지 않는 경우 에러가 발생하며 Font 형 반환값에 어떤 값이 올 지는 정확히 모릅니다.

그리고 stderr 에 다음과 같은 메세지가 출력됩니다.

X Error of failed request: BadName(named color or font does not exist)
  Major opcode of failed request:  45 (X_OpenFont)
  Serial number of failed request:  9
  Current serial number in output stream:  15

폰트 적재와 관한 한 실제 프로그래밍에서 XLoadFont 함수를 쓰지는 않는 것 같습니다. 대신 XLoadQueryFont 라고 하는 함수를 사용하지요. :) 자... 그런데 요건 여러분께 과제로 남겨야 하겠군요. 바로 다음에 설명을 드리겠습니다. 과제는 XLoadQueryFont, 이와 관련한 XFontStruct 의 구조 조사입니다. 맨페이지에 너무도 정확히 나와 있으니깐 걱정하지 마시기 바랍니다.

폰트를 GC 에 등록

 함수 원형 :

 XSetFont( Display *dpy, GC gc, Font font );

폰트 ID 값을 GC 정보에 수록하도록 요청합니다. 이렇게 함으로써 앞으로 출력되는 텍스트는 GC 에 지정되어 있는 전경색으로 표시되게 됩니다.

그러고 보니 GC 라고 하는 것은 그래픽에 관한 모든 정보를 지니고 있는 녀석이라고 할 수가 있네요. 다시 한 번 기억합시다! X 윈도우 그래픽에 있어서 중요한 요 GC 를.... :)

실제 텍스트 출력

 함수 원형 :

 XDrawString(display, d, gc, x, y, string, length)
              Display *display;
              Drawable d;
              GC gc;
              int x, y;
              char *string;
              int length;

Drawable 은 Window 형 변수나 Pixmap 형 변수을 말하죠? 자, 창 안에서의 좌표값을 주시고 문자열 string 을 넘겨 줍니다. 그리고 마지막으로 잊지 마셔야 할것은 문자열의 길이가 얼마인지를 length 에 저장해서 넣어주셔야 된다는 사실입니다.

여러분, 위 예제 프로그램 소스와 실행키신 화면에 출력된 결과에서 뭔가 이상한 점을 발견하지 않으셨습니까? :>>

예. 그렇습니다. "Hello, Linuxers! Never Seen :)" 라고 문자열을 주긴 했지만 뒷부분 즉, 'Never Seen :)' 이라고 하는 부분은 실제 출력이 되질 않습니다.

뒷처리

세상만사 시작도 중요하고 과정도 중요하지만 유종의 미를 거두는 것도 중요하다고 생각합니다. /* 뒷처리 */라고 주석을 단 부분에서는 XUnloadFont() 을 사용하고 있습니다. 그 뒤의 모든 과정도 마찬가지이지만 프로그램 종료 후 자동으로 이루어지는 과정이긴 합니다. 하지만, 여러분께서 뭔가 상용 프로그램이나 뭔가 그럴듯한 프로그램을 만드시려고 하신다면 꼭 뒷처리를 잘해주십시요.


Previous Next Table of Contents