프로그램의 텍스트 출력 방법들

텍스트를 화면에 출력하는 것은 단순해 보이지만, 그 이면에는 폰트 파일 파싱, 글리프 추출, 셰이핑(shaping), 래스터화, 안티 앨리어싱에 이르는 복잡한 파이프라인이 존재한다. 이 파이프라인을 어느 계층에서 다루느냐에 따라 사용하는 API와 라이브러리가 달라진다.


1. Win32 API (GDI / TextOut)

가장 전통적인 Windows 텍스트 출력 방식이다. TextOut, DrawText 같은 GDI 함수를 사용하여 디바이스 컨텍스트(DC)에 텍스트를 그린다. 폰트 제어에는 주로 두 가지 구조체가 활용된다.

LOGFONT / TEXTMETRIC / NEWTEXTMETRIC

구조체 용도
LOGFONT 폰트 생성 시 속성을 지정하는 논리 폰트 기술자. 간단한 폰트 제어에 사용한다.
TEXTMETRIC 현재 선택된 폰트의 측정 정보를 담는 구조체. LOGFONT보다 많은 정보를 제공하며 세밀한 제어가 필요할 때 사용한다. non-TrueType 폰트용이다.
NEWTEXTMETRIC TEXTMETRIC에 네 개의 필드가 추가된 구조체. TrueType 폰트용이다.

한계

GDI 기반 텍스트 출력은 CPU 소프트웨어 래스터라이저에 의존하며, 서브픽셀 수준의 정교한 렌더링이나 OpenType 고급 타이포그래피 기능(합자, 커닝 등)을 지원하지 않는다. 해상도 독립성도 부족하여 고DPI 환경에서 품질이 저하될 수 있다.


2. DirectWrite (Windows 현대 텍스트 API)

DirectWrite는 Windows 7부터 도입된 현대적인 텍스트 레이아웃·렌더링 API로, GDI/GDI+와 Uniscribe를 대체하기 위해 설계되었다. Direct2D와 연동하면 GPU 하드웨어 가속을 활용한 텍스트 렌더링이 가능하다.

주요 특징

레이어 구조

DirectWrite는 기능을 계층화하여 제공한다. 최상위 IDWriteTextLayout은 서식 있는 텍스트의 측정·렌더링·히트 테스트를 담당하고, 중간 계층은 폰트 컬렉션·열거, 하위 계층은 글리프 수준의 저수준 API로 구성된다. 렌더링 백엔드는 애플리케이션이 선택할 수 있어, Direct2D(GPU 가속), GDI 비트맵, 커스텀 렌더러 중 하나를 사용할 수 있다.

DWriteCore

Windows App SDK에 포함된 DWriteCore는 DirectWrite를 Windows 8 이상에서 사용할 수 있도록 시스템으로부터 분리한 구현이다. 크로스플랫폼 활용 가능성도 열려 있다.

참고: 2024년 기준 주요 OpenType 구현체는 세 가지로 정리된다. Windows의 DirectWrite, Apple의 CoreText, 그리고 오픈소스 진영의 FreeType + HarfBuzz 조합이다.


3. 텍스트 렌더 라이브러리

운영체제 네이티브 API를 사용하지 않고 직접 텍스트를 렌더링해야 하는 경우(크로스플랫폼 게임 엔진, 임베디드 시스템, 커스텀 UI 프레임워크 등), 전문 라이브러리를 사용한다. 이들 라이브러리는 폰트 파일을 직접 파싱하여 글리프를 추출하고, 해상도를 고려한 배율을 적용한 뒤 래스터 이미지로 변환하여 화면에 출력한다. 안티 앨리어싱도 적용하여 배경과 글자가 부드럽게 어우러지도록 한다.

현대적인 텍스트 렌더링 파이프라인은 대략 아래의 단계로 구성된다.

유니코드 문자열
    → [셰이핑] 글리프 ID + 위치 결정 (HarfBuzz)
    → [글리프 추출] 폰트에서 아웃라인 추출 (FreeType)
    → [래스터화] 벡터 → 픽셀 비트맵 변환
    → [컴포지팅] 배경과 합성하여 화면 출력 (Cairo, Skia 등)

3-1. FreeType

FreeType은 글리프를 추출하고 래스터화하는 역할을 하지만, 어떤 글리프를 어떤 순서와 위치로 배치할지(셰이핑)는 처리하지 않는다. 복잡한 스크립트(아랍어, 인도계 문자 등)를 올바르게 표시하려면 HarfBuzz 같은 셰이핑 엔진을 함께 사용해야 한다.

간단 텍스트 렌더링 예제

1) 헤더 포함

#include <ft2build.h>
#include FT_FREETYPE_H

2) 라이브러리 초기화

FT_Library library;

error = FT_Init_FreeType(&library);
if (error) {
    // 초기화 오류 처리
}

3) 폰트 페이스 로딩

FT_Face face;
error = FT_New_Face(library, "/path/to/font.ttf", 0, &face);

4) 기본적인 텍스트 렌더링

FT_GlyphSlot slot = face->glyph;
int pen_x = 300, pen_y = 200;

for (int n = 0; n < num_chars; n++) {
    FT_UInt glyph_index = FT_Get_Char_Index(face, text[n]);

    error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
    if (error) continue;

    // 안티 앨리어싱 적용 비트맵으로 변환
    error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
    if (error) continue;

    // 타깃 서피스에 그리기
    my_draw_bitmap(&slot->bitmap,
                   pen_x + slot->bitmap_left,
                   pen_y - slot->bitmap_top);

    // 펜 위치 전진 (26.6 고정소수점 → 픽셀 변환)
    pen_x += slot->advance.x >> 6;
    pen_y += slot->advance.y >> 6;
}

3-2. HarfBuzz

셰이핑이 필요한 이유는 유니코드 코드포인트와 실제 렌더링할 글리프 사이의 관계가 1:1이 아니기 때문이다. 예를 들어 아랍 문자는 단어 내 위치에 따라 모양이 달라지고, 합자(fi → fi)는 두 문자를 하나의 글리프로 합쳐야 한다. HarfBuzz는 폰트의 GSUB(글리프 치환)·GPOS(글리프 위치) 테이블을 분석하여 이를 처리한다.

주요 특징

채택 현황

Android, Chrome, Firefox, Safari(WebKit), LibreOffice, Qt, GNOME, Figma, Adobe Photoshop/Illustrator/InDesign, Unreal Engine, Godot Engine 등 사실상 모든 주요 플랫폼과 소프트웨어에서 사용된다.

FreeType과 HarfBuzz는 서로를 보완한다. HarfBuzz가 "어떤 글리프를 어디에 놓을지"를 결정하면, FreeType이 그 글리프의 아웃라인을 픽셀로 변환한다. 두 라이브러리는 상호 참조 구조를 가지며, FreeType 2.14.0부터는 HarfBuzz를 동적으로 로드할 수 있어 순환 의존성 문제가 해소되었다.


3-3. Pango


3-4. Cairo

간단 텍스트 렌더링 예제 (Pango + Cairo)

1) 헤더 포함

#include <pango/pangocairo.h>

2) RAW 데이터 서피스 및 Cairo 컨텍스트 생성

unsigned char *buffer = calloc(channels * width * height, sizeof(unsigned char));
cairo_surface_t *surf = cairo_image_surface_create_for_data(
    buffer, CAIRO_FORMAT_ARGB32, width, height, channels * width);
cairo_t *cr = cairo_create(surf);

3) PangoLayout 생성 및 폰트·텍스트 설정

PangoLayout *layout = pango_cairo_create_layout(cr);
pango_layout_set_text(layout, text, -1);

PangoFontDescription *desc = pango_font_description_from_string("Sans 16");
pango_layout_set_font_description(layout, desc);
pango_font_description_free(desc);

4) 텍스트 렌더링

cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); // 흰색
pango_cairo_show_layout(cr, layout);

// 정리
g_object_unref(layout);
cairo_destroy(cr);
cairo_surface_destroy(surf);
free(buffer);

4. 비교 요약

Win32 GDI DirectWrite FreeType + HarfBuzz Pango + Cairo
플랫폼 Windows 전용 Windows (DWriteCore로 크로스플랫폼 시도 중) 크로스플랫폼 크로스플랫폼
GPU 가속 없음 Direct2D와 연동 시 가능 없음(래스터화만) 없음
셰이핑 Uniscribe 내장 HarfBuzz 별도 사용 HarfBuzz 내장
고급 타이포그래피 제한적 OpenType 완전 지원 OpenType 완전 지원 OpenType 완전 지원
사용 난이도 쉬움 보통 어려움(저수준) 보통
주요 사용처 레거시 Win32 앱 현대 Windows 앱 게임 엔진, 임베디드 GNOME/GTK 앱

5. 참고 자료



Retrieved from https://hyacinth.kr:443/moniwiki/wiki.php/프로그램의 텍스트 출력 방법들
last modified 2026-03-13 17:15:48