← 블로그

Text.js의 FIGlet 글꼴

@textjs/font는 FIGlet .flf 글꼴을 파싱하고 figlet 프로그램처럼 배치해요. 테스트에서 figlet 2.2.5로 기록한 출력 273개를 재현하며, 모두 일치해요.

Adam

Text.js 0.3에 FIGlet 폰트용 파서와 렌더러인 @textjs/font가 추가됐어요. FIGlet 2.2.5 배포판의 폰트 17개를 각각 하나의 ES 모듈로 제공하며, 출력은 figlet 2.2.5 바이너리와 정확히 일치해요.

import { FigletText } from "@textjs/font/react";import { slant } from "@textjs/font/slant";export default function Page() {  return <FigletText font={slant} color="accent">Text.js</FigletText>;}
  ______          __      _ /_  __/__  _  __/ /_    (_)____  / / / _ \| |/_/ __/   / / ___/ / / /  __/>  </ /__   / (__  )/_/  \___/_/|_|\__(_)_/ /____/                   /___/

FIGlet을 선택한 이유

<AsciiFont>는 0.1부터 block과 compact라는 두 가지 내장 글자 스타일을 제공했어요. FIGlet은 1991년부터 이런 글자 스타일의 표준이었고, 수백 개의 .flf 폰트가 있어요. 이 형식을 지원하면 어느 폰트든 콘솔 앱에 넣을 수 있어요.

FIGlet 폰트는 일반 텍스트 파일이에요. 헤더 줄에서 높이, 하드블랭크 문자, 레이아웃 규칙을 설정하고, 각 문자를 여러 행으로 된 블록으로 그리며 각 행 끝에는 종료 표시를 붙여요. 기본 레이아웃에서 FIGlet은 각 문자를 이전 문자에 닿을 때까지 왼쪽으로 옮긴 다음, 폰트의 겹침 규칙으로 맞닿은 두 구성 문자를 하나로 합칠지 결정해요.

레이아웃 모드

figlet에는 가로 레이아웃이 다섯 가지 있고, renderFiglet도 layout으로 같은 다섯 가지를 받아요:

layoutfiglet효과
"default"-s폰트 자체 레이아웃, 대개 겹침 적용
"full"-W전체 너비 사용, 문자끼리 닿지 않음
"fitted"-k문자끼리 닿을 때까지 간격 축소
"smushed"-S폰트 규칙에 따른 겹침
"universal"-o뒤 문자가 앞 문자 위에 겹침

-1~63의 숫자는 -m 레이아웃 모드의 원시 값이에요. -1은 전체 너비, 0은 맞춤 배치이고, 1~63은 겹침 규칙을 선택해요. 가로 겹침 규칙 6가지인 동일 문자, 밑줄, 계층, 반대 쌍, 큰 X, 하드블랭크를 모두 구현했어요. 파서는 형식의 나머지 요소도 처리해요. 주석 줄, 10진수·8진수·16진수 코드 태그가 붙은 문자, 누락 문자 글리프, ivrit 같은 오른쪽에서 왼쪽으로 쓰는 폰트도 지원해요.

줄바꿈도 FIGlet을 따라요. width는 figlet -w와 같은 위치인 단어 경계에서 줄을 바꾸고, 단어가 줄보다 길면 단어 안에서 줄을 바꿔요. figlet -w 80은 최대 79열을 출력하므로, -w N은 width: N - 1에 해당해요.

바이너리와의 출력 일치

packages/font/test/fixtures/references.json에는 실제 figlet 2.2.5로 모든 번들 폰트를 모든 레이아웃 모드에서 실행해 생성한 출력 273개가 있어요. 줄바꿈, 정렬, 오른쪽에서 왼쪽으로 쓰는 텍스트, 문단 모드, FIGlet 자체 회귀 테스트 입력을 run-tests.sh의 플래그와 함께 사용했어요. 각 사례는 인수, 입력, 정확한 출력을 저장하고, 테스트는 명령줄을 다시 옵션으로 변환해요:

for (const f of flags) {  if (f === "W") opts.layout = "full";  else if (f === "k") opts.layout = "fitted";  else if (f === "S") opts.layout = "smushed";  else if (f === "o") opts.layout = "universal";  else if (f === "s") opts.layout = "default";  else if (f === "c") opts.align = "center";  else if (f === "r") opts.align = "right";  else if (f === "l") opts.align = "left";  else if (f === "R") opts.direction = "rtl";  else if (f === "L") opts.direction = "ltr";  else if (f === "p") opts.paragraph = true;  else throw new Error(`unsupported flag -${f}`);}

273개가 모두 일치해요. 픽스처를 다시 생성하려면 figlet 2.2.5를 PATH에 넣고 bun run --cwd packages/font references를 실행하세요.

셀 그리드 위의 폰트

모든 번들 폰트는 출력 가능한 ASCII 또는 Latin-1로 그리므로, 각 구성 문자는 Menlo에서 정확히 1개 셀을 차지하고 그리드에 맞춰져요. <FigletText>는 행을 셀 단위로 측정하므로 글자가 다른 <Text>처럼 flexbox 레이아웃에 배치되고, columns와 align이 상자 안에서 줄바꿈과 정렬을 처리해요.

각 폰트는 별도 모듈이므로 앱에는 가져온 폰트만 포함되고, 폰트는 가져올 때가 아니라 처음 렌더링할 때 파싱돼요. @textjs/font/all은 갤러리용으로 모든 폰트를 내보내요.

다른 FIGfont도 런타임에 가져와 파싱할 수 있어요:

import { parseFont } from "@textjs/font";const doom = parseFont(await (await fetch("/fonts/doom.flf")).text(), "Doom");

라이선스

번들 폰트는 figlet-2.2.5.tar.gz에 있는 폰트와 바이트 단위로 동일하며, FIGlet의 BSD 3-Clause 라이선스로 배포돼요. 각 폰트는 주석 줄에 제작자 정보를 유지하며, font.comment로 확인할 수 있어요. Doom, Larry 3D, ANSI Shadow, Calvin S처럼 재배포 허가가 없는 인기 폰트는 포함하지 않아요.

전체 레퍼런스는 폰트에 있어요.