> For the complete documentation index, see [llms.txt](https://overleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://overleaf-pro.ayaka.space/latex/ko/in-depth-articles/10-an-overview-of-technologies-supporting-the-use-of-colour-emoji-fonts-in-latex.md).

# LaTeX에서 색상 이모지 글꼴 사용을 지원하는 기술 개요

## 소개

이 글은 다양한 [배경 주제](#which-topics-do-we-cover) LaTeX에서 색상 이모지를 조판하기 위해 OpenType 컬러 글꼴을 사용하는 것과 관련된 주제들에 대한 개요를 제공합니다. 우리는 다양한 관심사와 전문성 수준에 맞추어 폭넓은 자료를 제공하려고 했습니다. 글의 분량을 관리하기 위해 일부 주제는 많은 기술적 세부사항을 생략했지만, LaTeX에서 색상 이모지 조판을 탐구하는 데 방향을 잡는 데 충분한 자료가 있기를 바랍니다.

**업데이트(2023년 7월)**: 이 글은 2021년 8월에 처음 출판되었고, 2023년 7월에 다음 섹션을 업데이트하기 위해 개정되었습니다. [LuaHBTeX에서 SVG 기반 OpenType 컬러 글꼴 사용](#using-svg-based-opentype-color-fonts-with-luahbtex).

### 어떤 주제를 다루나요?

이 글은 다음과 같은 일반 주제를 다룹니다:

* 유니코드: 이모지를 문자로 인코딩하고, 텍스트 처리 및 조판 애플리케이션에서 예상되는 동작을 규정하는 표준입니다.
* OpenType 컬러 글꼴: LaTeX 문서에 표시되는 이모지 문자를 다채롭게 표현해 주는 특수 글꼴입니다.
* 텍스트 셰이핑: 복합 문자 언어와 이모지의 조판에 필요한 핵심 구성 요소를 소개합니다.
* HarfBuzz: 고급 다국어 조판과 OpenType 컬러 글꼴을 사용해 LaTeX에서 이모지를 조판할 수 있게 해 주는 LuaHBTeX의 구성 요소입니다.
* 다른 TeX 엔진: OpenType 컬러 글꼴 지원을 살펴보고 어떤 TeX 엔진을 사용할지 선택합니다.
* LuaHBTeX의 HarfBuzz API: 그 뒤에 숨은 “마법”에 대한 소개 [텍스트 셰이핑](#the-concept-of-text-shaping) 에 대한 소개입니다.

### 색상 이모지를 조판하는 세 가지 방법

색상 이모지는 LaTeX에서 다음 세 가지 주요 방법으로 조판할 수 있습니다:

1. TikZ, MetaPost 또는 Asymptote 같은 표준 LaTeX 그래픽 도구를 사용해 이모지를 그립니다.
2. 외부 파일에 저장된 미리 준비된 이모지 그래픽을 삽입합니다.
3. 이모지를 유니코드로 인코딩된 텍스트로 취급하고 [텍스트 셰이핑](#the-concept-of-text-shaping) 다음으로 바꾸면 [OpenType 컬러 글꼴](#opentype-color-fonts) 을 사용해 조판합니다.

LaTeX 문서에 색상 이모지를 포함하는 실용적인 방법은 그 문서를 컴파일하는 데 사용하는 TeX 엔진에 따라 달라집니다. 즉, 다음 중 무엇을 사용하는지에 따라 달라집니다:

* pdfLaTeX: pdfTeX 엔진 + LaTeX;
* XeLaTeX: XeTeX 엔진 + LaTeX;
* LuaLaTeX: LuaHBTeX 엔진(TeX Live 2020부터) + LaTeX.

이 세 TeX 엔진 모두 LaTeX 도구나 패키지를 사용해 이모지를 그리거나 `\includegraphics{...}` 를 사용해 외부 그래픽 파일에 저장된 이모지를 삽입할 수 있습니다. 그래픽을 그리거나 가져오는 것은 LaTeX 문서를 컴파일하는 데 사용되는 TeX 엔진에 의존하지 않는 해결책이 필요할 때 이모지를 조판하기에 이상적인 기법입니다.

하지만 워크플로에서 특정 TeX 엔진을 선택할 수 있는 유연성이 있고, OpenType 컬러 글꼴과 유니코드 기반 텍스트 처리를 사용하고 싶다면 LuaTeX의 최신 버전인 LuaHBTeX가 필요합니다. TeX Live 2020부터 LuaHBTeX는 LuaLaTeX 형식을 기반으로 한 LaTeX 문서를 컴파일하는 데 사용됩니다.

## 유니코드와 이모지 문자에 대한 배경

### 문자 인코딩

컴퓨터는 텍스트를 나타내는 숫자(정수) 값의 시퀀스를 사용하여 텍스트를 저장, 전송, 처리합니다. *문자*. 신뢰할 수 있는 텍스트 처리를 위해서는 텍스트의 생산자와 소비자가 텍스트 스트림 내의 개별 문자를 나타내는 데 어떤 정수 값을 사용해야 하는지 합의해야 합니다. 다시 말해, 그 텍스트의 *문자* *인코딩은 무엇인가요?* 인코딩은 특정 문자 집합을 나타내기 위해 할당된 합의된 정수 값들의 집합입니다. 각 문자는 사용 중인 인코딩 내의 하나의 정수 값으로 표현됩니다.

### 유니코드의 등장

역사적으로 8비트 텍스트 시대에는 서로 다른 많은 문자 인코딩이 사용되었고, 그때마다 *인코딩 불일치*의 가능성이 항상 있었습니다. 즉, 텍스트의 생산자와 소비자가 서로 다른 인코딩을 잘못 가정하여 텍스트 처리 오류가 발생하는 것입니다. TeX/LaTeX를 몇 년간 사용해 본 사람이라면 입력 텍스트와 문서를 조판하는 데 사용되는 글꼴 간의 인코딩 불일치를 겪어 보았을 가능성이 큽니다. 문서 글꼴이 텍스트와 다른 인코딩을 사용하도록 설정되면, 조판된 PDF에서 문자가 누락되거나 잘못 표시될 수 있습니다.

이러한 역사적인 인코딩 문제는 세상의 모든 문자를 인코딩하는 국제 표준인 유니코드를 사용하여 해결할 수 있습니다. 유니코드 표준은 고정되어 있지 않으며, 인코딩 체계에 추가 문자와 문자 체계(문자 표기 체계)를 포함하도록 주기적으로 업데이트됩니다. 새로운 문자를 제안하기 위한 [공식 검토 절차가 있으며](http://www.unicode.org/pending/proposals.html) 특정한 [새 이모지 문자 체계](https://www.unicode.org/emoji/proposals.html).

### 유니코드 문자는 몇 개인가요?

유니코드는 이론상 최대 1,114,112자의 문자를 인코딩합니다. 1,114,112개의 정수 값 각각을 *코드 포인트*라고 부릅니다: 각 문자를 식별하기 위해 할당된 정수 값입니다. 그러나 여러 기술적 이유로 실제 문자에 할당될 수 있는 것은 [1,112,064개의 코드 포인트](https://en.wikipedia.org/wiki/Unicode#Architecture_and_terminology) 뿐입니다. 2048개의 코드 포인트는 할당 불가능하며 유니코드 호환 텍스트에서 사용할 수 없습니다.

이 글을 작성하던 시점(이 글의 첫 번째 버전)에는 유니코드 표준 13판이 실제 문자에 총 143,859개의 코드 포인트를 할당했으며, 여기에는 [이모지로 인코딩된 3304자](https://www.unicode.org/L2/L2020/20114r-family-emoji-explor.pdf) 가 포함됩니다(해당 문서의 2페이지 참조). 유니코드가 인코딩하는 문자 수의 증가는 글 [유니코드 문자는 몇 개인가?](https://www.babelstone.co.uk/Unicode/HowMany.html) 와 [위키피디아 항목](https://en.wikipedia.org/wiki/Unicode#Versions).

### 유니코드 플레인

1,114,112개의 유니코드 코드 포인트 전체는 17개의 이른바 플레인으로 묶여 있습니다. 플레인 0부터 플레인 16까지 각각 65536개의 코드 포인트 값을 포함하며, 총 $$17\times2^{16} = 1,114,112$$ 개의 문자 값을 이룹니다. 플레인 0은 [기본 다국어 평면](https://en.wikipedia.org/wiki/Plane_\(Unicode\)#Basic_Multilingual_Plane)이라고 불리며 가장 일반적으로 사용되는 문자들을 인코딩합니다. 플레인 1–16은 [보충 평면](http://unicode.org/glossary/#supplementary_planes).

### 이모지의 부상

새로운 문자들은 인간 의사소통 방식의 변화와 함께 등장하며, 모바일 전화 기술은 그런 문자 집합 중 하나인 이모지를 탄생시켰습니다. 이모지는 1990년대 후반 일본에서 발전했습니다. 그렇기 때문에 [유니코드 이모지 FAQ](https://unicode.org/faq/emoji_dingbats.html) 에서

> “emoji라는 단어는 일본어 [絵](http://www.unicode.org/cgi-bin/GetUnihanData.pl?codepoint=%E7%B5%B5) (e ≅ 그림) + [文字](http://www.unicode.org/cgi-bin/GetUnihanData.pl?codepoint=%E6%96%87) (moji ≅ 문자)에서 왔다.”

이모지의 배경과 역사적 발전에 관심 있는 독자라면 이 [유니코드 소개](https://unicode.org/reports/tr51/#Introduction) 나 글 [나는 이 이모지를 지지한다: 이모지의 표준, 구조, 그리고 사회적 생산](https://firstmonday.org/ojs/index.php/fm/article/view/9381).

가 흥미로울 수 있습니다. [유니코드 표준 6.0판](https://www.unicode.org/versions/Unicode6.0.0/)이 2010년에 발표되기 전까지는 많은 이모지가 공식적으로 *문자* 자체적으로 [3304자를 이모지로 인코딩했습니다](https://www.unicode.org/L2/L2020/20114r-family-emoji-explor.pdf) (해당 문서의 2페이지 참조). Unicode 13.1은 [3521개의 이모지를 나열합니다](https://unicode.org/emoji/charts/emoji-counts.html).

### 이모지는 더 높은 평면에 산다

유니코드는 많은 이모지 문자를 기본 다국어 평면(BMP) 밖의 코드 포인트에 할당했으며, [플레인 1에](https://en.wikibooks.org/wiki/Unicode/Character_reference/1F000-1FFFF) 1F000–1FFFF 범위의 코드 포인트로 인코딩했습니다. 이는 *복사하여 붙여넣기* 를 Overleaf 편집기(Code Editor 또는 Visual Editor)에 이모지 문자를 넣고 싶은 사람에게 중요한 결과를 가져옵니다. Overleaf의 텍스트 편집기는 기본 다국어 평면 내의 문자만 처리할 수 있지만, 앞으로의 업그레이드에서 비-BMP 문자 지원이 추가되기를 기대합니다. 이 제한은 Overleaf 편집기를 통해 편집할 파일에 붙여넣은 텍스트 내의 비-BMP 문자에만 적용된다는 점에 유의하세요. 이모지 문자에 접근하는 다른 방법도 있습니다:

* 원시 명령 `\char"<code point>` 또는 `\Uchar"<code point>` 를 사용하는 것( [이 섹션](#optional-detail-luatexluahbtex-char-vs-uchar) 참조).
* UTF-8 형식의 이모지 문자를 포함한 입력 텍스트 파일을 사용하는 것.
* 이모지 문자를 삽입하는 LaTeX 명령(매크로)을 사용하는 것.

#### Overleaf에 이모지와 기타 비-BMP 문자 붙여넣기

예를 들어 😀 같은 이모지 문자를 Overleaf Code Editor에 붙여넣으면, 현재는 그것이 �� 문자로 변환됩니다.

![Overleaf 편집기에 비-BMP 문자를 복사 + 붙여넣기해서 발생하는 오류](/files/e82e54b25cefb04025555febabcd2b6a2ecdad80)

� 문자는 유니코드 코드 포인트 FFFD를 가지며 공식 명칭은 REPLACEMENT CHARACTER이고, “[알 수 없거나 인식되지 않거나 표현할 수 없는 문자를 대체하는](https://en.wikipedia.org/wiki/Specials_\(Unicode_block\))” 데 사용됩니다.

### LuaLaTeX에서 유니코드 코드 포인트(U+) 사용

유니코드 문서는 코드 포인트 값을 다음 표기법으로 나타냅니다 `U+<16진수 값>`—예를 들면 `U+1F600`인데, 여기서 `1F600` 은 `<16진수 값>` 입니다. 이는 😀 이모지 문자의 유니코드 코드 포인트입니다. LuaLaTeX에서 이러한 코드 포인트 값을 사용하려면 `U+` 를 삭제하고 `\char"<16진수 값>` 또는 `\Uchar"<16진수 값>`. 다음 `"` 문자는 TeX 엔진에 제공된 숫자가 16진수로 지정되었음을 알려 줍니다. 예를 들어 😀 이모지를 사용하려면 `\char"1F600` 또는 `\Uchar"1F600`이라고 쓰면 됩니다—이를 조판할 수 있는 글꼴을 사용해야 합니다.

다음을 사용하여 😀 이모지 문자를 조판하는 최소 LuaLaTeX 예제 `\char` 및 `\Uchar` 는 다음과 같을 수 있습니다:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\emojifont[Renderer=Harfbuzz]{NotoColorEmoji.ttf}
%효과를 국소적으로 유지하려면 그룹 안에서 \emojifont를 사용하세요
{\emojifont
\Uchar"1F600
\char"1F600}
\end{document}
```

[이 LuaLaTeX 예제를 Overleaf에서 열기](https://www.overleaf.com/docs?engine=lualatex\&snip_name=Test+using+LuaLaTeX\&snip=%5Cdocumentclass%7Barticle%7D%0A%5Cusepackage%7Bfontspec%7D%0A%5Cbegin%7Bdocument%7D%0A%5Cnewfontfamily%5Cemojifont%5BRenderer%3DHarfbuzz%5D%7BNotoColorEmoji.ttf%7D%0A%25Use+%5Cemojifont+in+a+group+to+keep+its+effects+local%0A%7B%5Cemojifont+%0A%5CUchar%221F600%0A%5Cchar%221F600%7D%0A%5Cend%7Bdocument%7D)

**(선택 사항) LuaTeX/LuaHBTeX: \char 대 \Uchar**

기존의 `\char<문자 코드>` 명령을 사용해 특정 `<문자 코드>`를 현재 글꼴로 조판하는 것 외에도, LuaTeX, LuaHBTeX 및 XeTeX 엔진은 `\Uchar<문자 코드>` 명령도 제공합니다. 사용자 관점에서 두 결과는 `\char` 및 `\Uchar` 겉보기에는 같지만, 아래에서 설명하듯 이 명령들이 작동하는 방식에는 미묘한 차이가 있습니다.

**핵심 차이: 확장**

`\Uchar` 은 이른바 [확장 가능한 명령](/latex/ko/in-depth-articles/22-how-does-expandafter-work-the-meaning-of-expansion.md#expansion-a-general-term-for-a-set-of-operations) 인 반면 `\char` 은 확장 가능하지 않습니다. 어떤 `\char<문자 코드>` 또는 `\Uchar<문자 코드>` 명령이 “실행”될 때—즉, 그 명령이 매크로나 다른 토큰 목록의 일부로 저장되지 않을 때—TeX 엔진 내부에서는 다음 동작이 일어납니다:

* **`\char<문자 코드>`** 는 TeX 엔진에게 `<문자 코드>`을 나타내는 문자 토큰을 현재 조판 중인 콘텐츠의 적절한 위치에 즉시 삽입하라고 지시합니다.
* 반면 **`\Uchar<문자 코드>`** 은 두 개의 서로 다른 처리 단계를 거칩니다:

1. 다음 `\Uchar<문자 코드>` 명령은 *확장되고*, `<문자 코드>` 는 하나의 [문자 토큰](/latex/ko/in-depth-articles/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md#tex-tokens-101-28and-notions-of-expansion29) 을 포함하는 임시 토큰 목록으로 변환되며, 그 토큰은 `<문자 코드>`.
2. 그 하나의 문자 토큰 목록은 이제 *사용 가능해집니다* . TeX 엔진은 사실상 그 하나의 토큰 목록을 다음 입력 항목(토큰)의 위치로 사용하도록 “시선을 잠시 옮깁니다”. 기본적으로 TeX 엔진은 단순히 그 토큰을 다시 읽어(입력하여) 해당 문자를 조판함으로써 `\char` 명령을 사용해. **그러나**그 `<문자 코드>` 가 즉시 조판되지 않고 잠시 *저장* (보존)된 단일 토큰이기 때문에, 원시 TeX 명령이나 LaTeX 매크로가 그 토큰을 활용(흡수)할 수 있습니다. 즉, 반드시 즉시 조판될 필요는 없고 필요에 따라 추가 처리에 사용할 수 있습니다.

실질적으로 `\char<문자 코드>` 은 “이 `<문자 코드>` 를 지금 조판하라”는 뜻이고, `\Uchar<문자 코드>` 는 저장된 문자 토큰을 만들어 다음 입력 항목(토큰)으로 사용할 수 있게 함으로써 일종의 “지연 동작”을 합니다. 그 토큰은 TeX 명령과 매크로가 사용(흡수)할 수도 있고, TeX 엔진이 다시 읽어서 조판할 수도 있습니다.

### 유니코드(인코딩)만으로는 충분하지 않습니다

유니코드 인코딩 텍스트 내에서 이모지 문자를 사용할 수 있다는 능력은 이모지 성공 이야기의 일부일 뿐입니다. 이모지 사용 급증은 [OpenType 글꼴 기술](https://learn.microsoft.com/en-us/typography/opentype/)—즉, 문자 도안 데이터(glyph data)를 포함할 수 있는 [색상 데이터](https://learn.microsoft.com/en-us/typography/opentype/spec/colr)를 가진 글꼴—의 발전에 의해서도 가능해졌습니다: 이른바 [OpenType 컬러 글꼴](#opentype-color-fonts).

적절한 글꼴 외에도, 색상 이모지를 사용하려면 다음 작업을 포함하는 추가 소프트웨어 구성 요소가 필요합니다:

* 유니코드 인코딩 텍스트를 사전 처리(“[셰이핑](#the-concept-of-text-shaping)”)하고, *특정 글꼴로 표시할 수 있도록* 준비하는 것;
* *글꼴의 다채로운 이모지* 글리프 *를* 장치 화면에 렌더링하고 표시하는 것.

#### 글리프 vs. 문자: 정말 같은 것인가요?

“글리프”와 “문자”라는 용어는 종종 서로 바꿔 쓸 수 있는 것처럼 사용되지만—같은 기본 개념을 가리키는 듯 보이지만—의미에는 미묘하지만 중요한 차이가 있습니다.

유니코드 [는 “문자”라는 용어를](http://www.unicode.org/glossary/#character) 다음과 같이

> 정의합니다: “의미적 가치를 지닌, 문자 언어의 가장 작은 구성 요소; 특정한 모양이 아니라 추상적인 의미 및/또는 형태를 가리킨다…”

반면 “글리프”는 *특정한* *형태* (도안)로서 어떤 *시각적 표현* 을 위한 것입니다. *문자*.

문자와 글리프의 문제는 이모지가 많이 포함된 텍스트를 다양한 소프트웨어 시스템/플랫폼에서 볼 때 쉽게 관찰할 수 있습니다. 예를 들어 iOS 또는 Android 기반 모바일 전화나 Windows 데스크톱 컴퓨터에서 같은 텍스트를 읽는 경우입니다. 어떤 장치나 플랫폼을 사용하더라도, 기본 텍스트(문자 시퀀스)는 동일한 유니코드로 인코딩된 *이모지* *문자*를 포함하게 됩니다. 그런 텍스트를 *사전 처리* 하고 *렌더링하여* 및 *표시하는* 과정에는 장치별 글꼴을 사용할 수도 있으며, 그 결과 동일한 이모지 문자를 나타내기 위해 서로 다른 글리프(문자 도안)가 생성됩니다.

유니코드의 [전체 이모지 목록](https://unicode.org/emoji/charts/full-emoji-list.html) 은 각 유니코드 이모지 문자를 나타내는 샘플 이미지를 제공하며—다양한 기술 공급업체가 사용하는 여러 글리프를 보여 줍니다. 글꼴 디자이너는 이모지 문자를 나타내기 위해 자신들만의 특정한 도안(글리프)을 채택할 뿐 아니라, 개별 글꼴마다 지원하는 이모지 문자의 수(글리프를 포함하는 범위)도 다르며, 유니코드 이모지 명세에 포함된 더 고급의 이모지-텍스트 처리 기능을 포함할 수도 있고 포함하지 않을 수도 있습니다.

문자의 개념과 의미론, 인코딩은 유니코드 세계의 기초를 이룹니다. 유니코드는 문자들을 다룹니다. 개별 문자의 도안으로서의 디자인과 시각적 표현은 글꼴 기술과 글꼴 디자인의 영역입니다.

#### 유니코드 이모지: 단순한 텍스트 인코딩 그 이상

유니코드의 핵심 역할은 전 세계적 인코딩 표준을 제공하여, *코드 포인트라고 불리는* 어떤 정수 값을 이모지를 포함한 유니코드 인코딩 텍스트 스트림에서 각 문자를 나타내는 데 사용해야 하는지를 정의하는 것입니다.

이모지에 대한 유니코드 명세는 또한 특정 *처리 동작* 을 *시퀀스* 에 대해 정의합니다. 유니코드 인코딩 텍스트 스트림에 나타나는 이모지 문자들의 정의된 시퀀스는 [텍스트 셰이핑](#the-concept-of-text-shaping) 이라고 불리는 과정으로 “병합”되어 하나의 결과(“복합”) 이모지 글리프를 만들 수 있으며, 그 하나의 글리프는 장치 운영체제가 텍스트에 존재하는 원래 문자 시퀀스를 나타내는 데 사용됩니다.

유니코드의 기술 보고서 [유니코드 이모지](https://unicode.org/reports/tr51/) 는 이모지 문자에 대해 유니코드 호환 처리를 제공하려는 소프트웨어가 사용할 수 있는 풍부한 기능 집합을 문서화합니다. 예를 들어 유니코드는 [이모지 수정자](http://www.unicode.org/reports/tr51/#Emoji_Modifiers_Table) 라고 불리는 문자를 정의(인코딩)하여 *변형* 을 생성할 수 있게 합니다. 예를 들어 [피츠패트릭 척도를 기반으로 한 피부색](http://www.unicode.org/reports/tr51/#Diversity)의 변형입니다. 기본 이모지 문자 집합과 적용 가능한 수정자는 전체 [유니코드 이모지 표준](http://www.unicode.org/reports/tr51).

의 일부로 정의된다는 점에 유의하세요. [이모지 시퀀스](http://unicode.org/emoji/charts/emoji-sequences.html) 유니코드 명세에서 현재 제공되는 시퀀스 차트를 제공합니다. 어떤 이모지 글리프 이미지 위에 마우스 포인터를 올리면, 그 글리프를 만들어 내는 기저 유니코드 이모지 문자 시퀀스를 알려 주는 작은 팝업 툴팁이 나타납니다:

![EmojiSequenceChart.png](/files/507897fef6b6b24656932099ed28c017faa1af0f)

예를 들어, 이모지 글리프:

![HandMediumSkinTone.png](/files/c9e681ef3d44c6ec1b80f6fba51797f151ceaf90)

은 [수정자 시퀀스 섹션](http://unicode.org/emoji/charts/emoji-sequences.html#modifier_sequences) 에 나열되어 있으며, 두 문자 시퀀스 U+1F44B U+1F3FD에 의해 생성됩니다. 그 구성 문자는 다음과 같습니다:

U+1F44B:![UnicodeWavingHandDefault.png](/files/b18d3006995977efee278566c0c54704675c9df5) (손 흔들기)

U+1F3FD:![FitzPatrick3.png](/files/664c772687a3d7a1368e6095464c063be1617537) (이모지 수정자 피츠패트릭 유형-4)

**LuaHBTeX에서 피부색 수정자 사용**

다음 예제는 LuaHBTeX를 사용해 이모지 수정자의 사용을 보여 줍니다:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\emojifont[Renderer=HarfBuzz,SizeFeatures={Size=20}]{NotoColorEmoji.ttf}
분리된 손 흔들기: {\emojifont\Uchar"1F44B}\par
분리된 수정자: {\emojifont\Uchar"1F3FD}\par
결합 결과: {\emojifont\Uchar"1F44B\Uchar"1F3FD}
\end{document}
```

[이 LuaLaTeX 이모지 수정자 예제를 Overleaf에서 열기](https://www.overleaf.com/docs?engine=lualatex\&snip_name=Emoji+modifiers+using+LuaLaTeX\&snip=%5Cdocumentclass%7Barticle%7D%0A%5Cusepackage%7Bfontspec%7D%0A%5Cbegin%7Bdocument%7D%0A%5Cnewfontfamily%5Cemojifont%5BRenderer%3DHarfBuzz%2CSizeFeatures%3D%7BSize%3D20%7D%5D%7BNotoColorEmoji.ttf%7D%0AIsolated+waving+hand%3A+%7B%5Cemojifont%5CUchar%221F44B%7D%5Cpar%0AIsolated+modifier%3A+%7B%5Cemojifont%5CUchar%221F3FD%7D%5Cpar+%0ACombined+result%3A+%7B%5Cemojifont%5CUchar%221F44B%5CUchar%221F3FD%7D%0A%5Cend%7Bdocument%7D)

이 예제는 다음 출력을 생성합니다:

![ModifiersInLuaHBTeX.png](/files/9ac3e88699e664d7e9f412eef160e100afa20c1c)

#### UTF-8: 유니코드 텍스트 저장에서의 역할

Overleaf의 Code Editor(또는 Visual Editor)에 입력하거나 붙여넣는 모든 텍스트나 코드는 UTF-8 형식으로 저장되므로, 먼저 UTF-8이 실제로 무엇을 의미하는지 간단히 살펴보겠습니다. UTF는 Unicode Transformation Format의 약자이며, UTF-8이 유니코드 인코딩 텍스트를 저장하거나 전송하는 데 수행하는 역할은 “Transformation *Format*”이라는 구절에 나타납니다.

유니코드의 코드 포인트 값은 0에서 최대 1,114,111까지이므로, 모든 유니코드 문자 값을 단일 8비트 바이트로 표현하는 것은 불가능합니다. 8비트 바이트는 0부터 255까지의 256개 값만 저장할 수 있기 때문입니다. 하지만 *연속된 시퀀스* 의 바이트 크기 값들을 사용하여 어떤 유니코드 코드 포인트 정수든 표현하는 것은 가능합니다. 이것이 UTF-8의 원리입니다.

UFT-8은 *변환* (즉, 유니코드 정수 코드 포인트 값을 1개에서 4개까지의 연속된 바이트 크기 정수의 고유한 시퀀스로 “인코딩”하거나 “변환”하기 위한) “레시피”를 제공합니다. 필요한 연속 바이트 수는 코드 포인트 정수의 값에 따라 달라집니다. 따라서 UTF-8이 유니코드 문자를 *다중 바이트 시퀀스* 로 저장한다고 읽을 수 있는데, 이는 하나의 유니코드 문자(코드 포인트 정수)가 UTF-8에서 1개에서 4개까지의 연속된 바이트 시퀀스로 표현되기 때문입니다.

물론 UTF-8로 저장된 텍스트는 원래의 유니코드 코드 포인트 정수 시퀀스로 다시 변환될 수 있습니다. 이것이 바로 XeTeX 또는 LuaTeX/LuaHBTeX가 UTF-8 형식으로 저장된 LaTeX 입력 파일을 읽을 때 해야 하는 일입니다. 이러한 TeX 엔진은 텍스트를 조판하기 전에 입력 유니코드 코드 포인트(문자) 값을 알아야 합니다. pdfTeX는 내장 UTF-8 디코딩 기능이 없으므로 UTF-8로 형식화된 입력 텍스트를 처리(디코딩)하기 위해 TeX 매크로에 의존해야 한다는 점에 유의하세요.

**몇 가지 UTF-8 예시**

* 아랍 문자 ش(“sheen”)는 16진수(기수 16)로는 0634, 10진수(기수 10)로는 1588의 유니코드 코드 포인트를 가집니다. UTF-8에서 ش는 16진수 값 D8과 B4로 표현되므로, 문자 ش는 UTF-8로 인코딩된 텍스트 안에서 두 개의 연속된 바이트 D8B4로 저장됩니다.
* 이모지 문자 😀는 16진수(기수 16)로는 1F600, 10진수(기수 10)로는 128512의 유니코드 코드 포인트를 가집니다. UTF-8에서 😀는 16진수 값 F0, 9F, 98, 80의 네 개로 표현되므로, 문자 😀는 UTF-8 텍스트 파일 안에서 네 개의 연속된 바이트 F09F9880으로 저장됩니다.

#### 유니코드 기반 이모지 텍스트 처리에서 사용되는 특수 문자

유니코드에 인코딩된 모든 문자가 글꼴의 글리프를 통해 시각적으로 표시되도록 의도된 것은 아닙니다. 일부 인코딩된 문자는 *비인쇄 문자* 로 지정되며, 그 목적은 특수한 텍스트 처리 기능(지원 소프트웨어 내부)을 돕는 것입니다. 서로 다른 소프트웨어 애플리케이션은 유니코드에 인코딩된 비인쇄 문자에 대해 각기 다른 수준의 지원을 제공하므로, 결과는 사용 중인 소프트웨어 환경—애플리케이션과 글꼴—에 따라 달라집니다.

**알아두면 좋은 두 가지 비인쇄 문자**

* **제로 폭 결합자(ZWJ)**&#xB294; 코드 포인트 200D(16진수)이며, 이름에서 알 수 있듯 입력 문자들의 “결합 동작”을 유발하도록 설계되었습니다. 하지만 이는 그 입력 문자들이 *가져야* 결합 동작이 정의되어 있는 경우에만 그렇습니다.
* **제로 폭 비결합자(ZWNJ)**&#xB294; 코드 포인트 200C(16진수)이며, 입력 문자들이 그렇지 않으면 보일 수 있는 “결합 동작”을 *방지하도록* 설계되었습니다. 예를 들어, ZWNJ를 사용해 보통은 결합 형태로 처리(셰이핑)되는 연속 아랍 문자들의 결합 동작을 막을 수 있습니다.

유니코드는 다음 목록을 발표했습니다. [권장 이모지 ZWJ 시퀀스](https://unicode.org/emoji/charts/emoji-zwj-sequences.html) 이는 U+200D ZERO WIDTH JOINER(ZWJ)를 사용해 이모지 문자 시퀀스를 단일 복합 이모지 글리프로 결합합니다—사용 중인 글꼴에서 해당 글리프가 제공된다면 말입니다.

**제로 폭 비결합자 사용 예**

다음의 최소 코드 조각은 TeX Live에 포함된 Scheherazade OpenType 글꼴을 사용하여 `\arabicfont` 라는 LaTeX 글꼴을 정의하며, 이를 사용해 일부 아랍어를 조판할 수 있습니다.

```latex
{\arabicfont Non-joining:\textdir TRT\Uchar"0644\Uchar"200C\Uchar"0627}
```

는 `\Uchar"200C`를 통해 제로 폭 비결합자 문자를 사용하여, 아랍 문자 ل(lam)과 ا(alef)의 일반적인 결합 동작을 막습니다. `\textdir TRT` 를 사용해 텍스트 방향을 오른쪽에서 왼쪽으로 설정하는 점에 유의하세요:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\arabicfont[Script=Arabic,Renderer=Harfbuzz,SizeFeatures={Size=40}]{Scheherazade}
{\arabicfont Joining:\textdir TRT\Uchar"0644\Uchar"0627}\par
{\arabicfont Non-joining:\textdir TRT\Uchar"0644\Uchar"200C\Uchar"0627}
\end{document}
```

[이 LuaLaTeX 예제를 Overleaf에서 열기](https://www.overleaf.com/docs?engine=lualatex\&snip_name=Zero+width+non-joiner+using+LuaLaTeX\&snip=%5Cdocumentclass%7Barticle%7D%0A%5Cusepackage%7Bfontspec%7D%0A%5Cbegin%7Bdocument%7D%0A%5Cnewfontfamily%5Carabicfont%5BScript%3DArabic%2CRenderer%3DHarfbuzz%2CSizeFeatures%3D%7BSize%3D40%7D%5D%7BScheherazade%7D%0A%7B%5Carabicfont+Joining%3A%5Ctextdir+TRT%5CUchar%220644%5CUchar%220627%7D%5Cpar%0A%7B%5Carabicfont+Non-joining%3A%5Ctextdir+TRT%5CUchar%220644%5CUchar%22200C%5CUchar%220627%7D%0A%5Cend%7Bdocument%7D)

이 예제는 다음 출력을 생성합니다:

![NonJoiner.png](/files/5fe46ea7cc5be195bebc80fe36bbd29e00520850)

## “텍스트 셰이핑”의 개념

“교육적인”의 우르두어 번역을 사용한 시각적 예로 시작해 보겠습니다. 우르두어 번역 텍스트는 키보드나 터치스크린 장치에서 입력될 수 있으며, 단순한 유니코드 아랍 문자들의 선형 시퀀스로 만들어집니다. 하지만 그 텍스트가 조판되거나 장치 화면에 표시될 때 [나스타리크 스타일](https://en.wikipedia.org/wiki/Nastaliq)에서는 결과가 복잡한 2차원 글리프 배열이 됩니다.

우리의 우르두어 예를 사용하면, 다음 그림은 유니코드 아랍 문자의 선형 입력을 비교합니다 *문자* Nastaliq 스타일로 조판된 출력에, 다음의 2차원 배열을 이루는 *를* (무료) 글꼴에 포함된 [Awami Nastaliq](https://software.sil.org/awami/download/):

![](/files/a58d95137a87059c0275f2ccb5fa9edff9794a6d)

입력 문자를 올바르게 배치된 출력 글리프 집합으로 “변환”하는 과정은 *텍스트 셰이핑*이며, 표시 또는 조판 전에 텍스트를 처리하는 데 있어 중요한 구성 요소입니다. 이 예시에서는 우르두어(아랍 문자) 텍스트를 사용했는데, 이는 셰이핑의 결과가 분명하게 드러나기 때문입니다. 반면 영어와 같은 라틴 문자를 사용하는 언어에서는 셰이핑이 훨씬 덜 두드러지며—예를 들어 단순한 합자를 만드는 정도입니다.

다음과 같은 문자 체계(문자 체계)를 사용할 때 텍스트 셰이핑은 필수적입니다: [아랍 문자](https://en.wikipedia.org/wiki/Arabic), [히브리 문자](https://en.wikipedia.org/wiki/Hebrew_language), [데바나가리 문자](https://en.wikipedia.org/wiki/Devanagari) 또는 [말라얄람 문자](https://en.wikipedia.org/wiki/Malayalam), 이들은 소위 말하는 다음의 네 가지 예일 뿐입니다 *복합 문자 체계*. 이러한 문자 체계와 그것을 사용하는 언어에서 텍스트를 올바르게 표현하려면, 셰이핑 과정이 해당 문자 체계와 언어 조합에 존재하는 모든 셰이핑 규칙과 뉘앙스를 신중하게 처리해야 합니다. 예를 들어 어떤 언어는 특정 출력 글리프를 생성하기 위해 여러 입력 문자를 필요로 하거나, 발음 구별 부호를 세심하게 배치해야 하는 복잡한 요구 사항이 있을 수 있으며, 개별 글리프가 서로에 대해 올바른 상대 위치에 놓이도록 하기 위한 글리프 간 재배치가 필요할 수도 있습니다.

일반적으로 텍스트 조판에는 여러 정보가 필요합니다:

* 문자 체계 또는 *문자* 텍스트가 작성된 문자 체계.
* 구체적인 *언어* 사용 중인 언어. 개별 문자 체계는 여러 언어에 사용될 수 있으며, 각 문자 체계-언어 조합마다 고유한 조판상의 미묘함/뉘앙스가 있습니다.
* 텍스트의 *방향* —예: 오른쪽에서 왼쪽 또는 왼쪽에서 오른쪽.
* 하나의 *글꼴* 조판된 텍스트를 표현하는 데 필요한 글리프를 제공하고, 선택적으로 텍스트 조판 과정을 안내하는 추가 “조판 규칙”을 포함하는.

특히 복합 문자 체계와 그에 연관된 언어의 텍스트 조판 요구 사항은 매우 세부적이고 미묘할 수 있으므로, 잠재적으로 매우 복잡한 텍스트 조판 “규칙”을 적용할 수 있는 전문 소프트웨어의 필요성을 보여 줍니다. 놀랄 것도 없이, 그러한 소프트웨어가 존재하며 이를 *텍스트 셰이핑 엔진*이라고 부릅니다; 우리가 다룰 것은 다음과 같습니다: [HarfBuzz](https://en.wikipedia.org/wiki/HarfBuzz)이며, 문서를 읽어볼 가치가 있습니다. 예를 들면 [왜 셰이핑 엔진이 필요한가?](https://harfbuzz.github.io/why-do-i-need-a-shaping-engine.html).

**텍스트 셰이핑 추가 읽을거리**

다음의 짧은 소개 글들을 강력히 권합니다:

* [텍스트 셰이핑이란 무엇인가?](https://harfbuzz.github.io/what-is-harfbuzz.html#what-is-text-shaping)
* [왜 셰이핑 엔진이 필요한가?](https://harfbuzz.github.io/why-do-i-need-a-shaping-engine.html)

**기술적 참고: 여러 셰이핑 기술(모델)**

HarfBuzz 텍스트 셰이핑 엔진은 셰이핑 과정을 구현하는 방식이 서로 다른 여러 "셰이핑 기술"을 지원하며—각 구현은 다음과 같이 불립니다: *셰이퍼*를 포함하여 `luaotfload` 문서에서도 마찬가지입니다. 이 글의 주된 초점은 OpenType 셰이핑이지만, 대안으로 무료로 사용할 수 있는 기술은 [Graphite](https://scripts.sil.org/cms/scripts/page.php?site_id=projects\&item_id=graphite_aboutOT)이며, 다음에 의해 개발된 [SIL International](https://www.sil.org/)입니다. HarfBuzz가 지원하는 또 다른 셰이핑 모델은 [Apple Advanced Typography(AAT)](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html)—AAT를 지원하는 글꼴은 일반적으로 Apple 기술 플랫폼에서 사용됩니다.

**Graphite 셰이퍼를 사용하는 예**

다음 예시는 다음이라는 글꼴을 사용하여 일부 우르두어 텍스트를 조판합니다: [Awami Nastaliq](https://software.sil.org/awami/download/)Awami Nastaliq는 Graphite 셰이핑을 지원하며 Overleaf에서 사용할 수 있습니다. Awami Nastaliq는 다음에 의해 만들어졌습니다: [SIL International](https://www.sil.org/)Graphite 기술 개발을 담당하는 기관입니다.

다음 예시는 Graphite 기반 글꼴의 고급 셰이핑 기능을 보여 줍니다—다음에서 어떻게 `luaotfload` 글꼴 선언이 다음을 사용하여 Graphite 셰이핑을 선택하는지 주목하세요: `shaper=graphite2`.

```latex
\documentclass{article}
\usepackage{luaotfload}
\begin{document}

\font\urdutest={file:AwamiNastaliq-Regular.ttf:mode=harf;shaper=graphite2} at 100bp
% Technology
\pardir TRT\textdir TRT \urdutest ٹیکنالوجی

\vskip 75bp

% Educational
\pardir TRT\textdir TRT \urdutest تعلیمی
\end{document}
```

[Overleaf에서 이 예제를 엽니다.](https://www.overleaf.com/docs?engine=lualatex\&snip_name=Typesetting+Urdu+using+the+Graphite+shaper\&snip=%5Cdocumentclass%7Barticle%7D%0A%5Cusepackage%7Bluaotfload%7D%0A%5Cbegin%7Bdocument%7D%0A%0A%5Cfont%5Curdutest%3D%7Bfile%3AAwamiNastaliq-Regular.ttf%3Amode%3Dharf%3Bshaper%3Dgraphite2%7D+at+100bp%0A%25+Technology%0A%5Cpardir+TRT%5Ctextdir+TRT+%5Curdutest+%D9%B9%DB%8C%DA%A9%D9%86%D8%A7%D9%84%D9%88%D8%AC%DB%8C%0A%0A%5Cvskip+75bp%0A%0A%25+Educational%0A%5Cpardir+TRT%5Ctextdir+TRT+%5Curdutest+%D8%AA%D8%B9%D9%84%DB%8C%D9%85%DB%8C%0A%5Cend%7Bdocument%7D)

이 예제는 다음 출력을 생성합니다:

![](/files/adc93748411156683304ed9a697f0e7439498e64)

#### 이모지와 텍스트 셰이핑

텍스트 셰이핑은 복합 문자 체계 언어인 우르두어의 예시를 통해 소개되었습니다. 그러나 올바른 이모지 글리프를 렌더링하려면 유니코드 텍스트에 포함된 이모지 문자 시퀀스에 텍스트 셰이핑을 적용해야 한다는 사실은 다소 놀라울 수 있습니다—[HarfBuzz의 수석 개발자가 지적했듯이](https://github.com/harfbuzz/harfbuzz/issues/2428#issuecomment-639108677):

> ...HarfBuzz로 이모지를 셰이핑하는 것은 완전히 범위 내에 있으며, 가족 이모지, 피부색 등 을 얻기 위해 실제로 필요합니다.

이에 대한 예시를 살펴보겠습니다.

### 책임 분담: 텍스트 셰이핑 엔진 + OpenType 글꼴

실제로 텍스트 셰이핑은 텍스트 셰이핑 엔진에 내장된 논리와 규칙, 그리고 사용하는 글꼴에 내장된 추가 셰이핑 규칙 및 데이터 사이의 “공동 작업” 또는 역할 분담입니다. 이제부터는 OpenType 기반 셰이핑만 다루겠습니다 *만*.

셰이핑을 수행하려면 일반적으로 텍스트 셰이핑 엔진에 일부 유니코드 텍스트, 지정된 문자 체계와 언어, 경우에 따라서는 쓰기 방향, 그리고 가장 중요하게는 셰이핑 과정에서 사용할 OpenType 글꼴이 제공됩니다. 글꼴은 출력인 글리프 집합과 배치 데이터를 제공합니다. 요청하면 셰이핑 엔진은 추가 규칙([OpenType 기능](https://en.wikipedia.org/wiki/List_of_typographic_features#OpenType_typographic_features))을 사용 중인 OpenType 글꼴에 포함된 규칙을 적용할 수 있습니다. 어떤 규칙을 적용할지는 보통 글꼴이 지원하는 기능 목록에서 사용자가 선택할 수 있습니다.

셰이핑 과정의 결과는 *글리프 목록* OpenType 글꼴에 포함된 것과 *글리프 간* 배치 데이터입니다. 이 배치 데이터는 *조판된 글리프들의 상대적 배치를 나타내며*; 이는 조판된 페이지나 웹 페이지, 트윗 등 다른 매체/콘텐츠 내의 절대적 위치를 의미하지 않습니다. 렌더링 소프트웨어(조판 엔진, 웹 브라우저 등)는 글리프 간 배치 정보를 사용하여 글리프들이 조립되어 최종 출력에 포함된 뒤 서로에 대해 올바르게 배치되도록 합니다.

#### 글리프 목록이란 무엇인가?

내부적으로 OpenType 글꼴의 각 글리프에는 숫자 식별자, 즉 글리프 인덱스라고 하는 정수 값이 할당됩니다. 이는 글리프 식별자 또는 GID라고도 합니다. 셰이핑 작업을 완료한 후 텍스트 셰이핑 엔진은 결과를 다음과 같이 반환합니다: *글리프 식별자 목록* 와 *배치 데이터* 해당 글리프들에 대한.

OpenType 글꼴의 개별 글리프는 글꼴 제작자가 인덱스(식별자)를 할당하므로, 이는 매우 글꼴 특유의 임의적인 값이며 특정 글꼴의 버전마다 달라질 수도 있습니다. 서로 다른 글꼴에서 “비슷한” 글리프에 같은 GID 값이 적용될 것이라고 절대 가정해서는 안 됩니다. 거의 확실히 그렇지 않습니다. 셰이핑 엔진이 제공한 글리프 식별자 목록이 있다면, 그것들은 오직 그 식별자를 얻은 글꼴에서만 글리프에 접근하는 데 사용할 수 있습니다.

#### OpenType 글꼴이란 무엇인가?

웹에는 *넘쳐납니다* OpenType 글꼴에 대한 설명과 상세 정보가 가득하므로, 여기서는 간단한 설명으로 제한하겠습니다.  [OpenType 사양](https://docs.microsoft.com/en-us/typography/opentype/spec/) 는 개발자를 위해 설계된 복잡한 문서이지만, 본질적으로는 글꼴 데이터의 파일 형식 또는 컨테이너를 정의합니다. OpenType 글꼴에는 글리프 형태를 설명하는 데이터와 함께 지원하는 문자 체계와 언어에 대한 정보, 글꼴 메타데이터, 그리고 다음을 정의하는 다양한 “테이블”이 포함됩니다: [타이포그래피 기능](https://en.wikipedia.org/wiki/List_of_typographic_features#OpenType_typographic_features) 글꼴이 지원하는.

텍스트 셰이핑 엔진은 보통 셰이핑 과정에서 글꼴의 기능을 선택적으로 적용(사용)하도록 지시받을 수 있으며, 글꼴에 포함된 적절한 글리프 집합을 선택하는 특정 타이포그래피 효과(“규칙”)를 적용합니다. 선택된 글꼴은 텍스트 셰이핑 엔진이 적용하도록 요청받은 모든 기능을 지원하고 해당 글리프를 제공해야 합니다.

#### 인코딩된 “글리프”와 인코딩되지 않은 “글리프”

OpenType 글꼴에는 다음이라는 데이터 테이블이 포함됩니다: [cmap](https://docs.microsoft.com/en-us/typography/opentype/spec/cmap) (문자-글리프 인덱스 매핑)으로, 글꼴이 지원하는 유니코드 문자 집합을 해당 글꼴의 대응하는 글리프 인덱스에 매핑합니다. 다음 동영상은 다음이라는 글꼴에 포함된 cmap 테이블을 간단히 보여 줍니다: `lmmono10-regiular.otf` (TeX Live에 포함됨).

{% embed url="<https://videos.ctfassets.net/nrgyaltdicpt/2537Y9gOUMWgd0t1guqt0X/482c53a9d8112ecae3d622aa7e00eef8/openType_cmap.mp4>" %}

그러나 글꼴에는 보통 특정 유니코드 문자를 나타내지 않으며 해당 cmap 테이블의 일부로 포함되지 않는 글리프가 많습니다. 따라서 OpenType 글꼴에 있는 글리프 집합은 크게 두 집합으로 나눌 수 있습니다:

* 유니코드 문자를 나타내는 인코딩된 글리프;
* 유니코드 문자를 나타내지 않는 인코딩되지 않은 글리프.

인코딩된 글리프는 텍스트에 적절한 유니코드 문자를 포함시켜 접근할 수 있습니다. 그렇다면 인코딩되지 않은 글리프는 어떻게 사용/접근할까요? 그러한 글리프는 보통 특정 시각적/타이포그래피 효과를 내기 위해 글꼴 기능을 적용하는 것을 포함한 텍스트 셰이핑 작업의 출력으로 사용됩니다.

### OpenType 컬러 글꼴

이모지 문자는 전체 색상으로 표시/렌더링될 것으로 기대됩니다—흑백 이모지는 “완전한 이모지 경험”을 충분히 제공하지 못합니다. 그러나 유니코드가 이모지를 처음 인코딩할 당시에는 [OpenType 글꼴 사양](https://docs.microsoft.com/en-us/typography/opentype/spec/) 에 *컬러*-글리프 데이터를 OpenType 글꼴 안에 포함하기 위한 적절한 규정이 없었습니다. OpenType의 이러한 “공백”은 주요 기술/플랫폼 공급업체들이 해결책을 찾도록 자극했고, 그에 따른 “경쟁”의 결과로 [OpenType를 확장하기 위한 다양한 제안이](https://www.fontlab.com/news/color-font-format-proposals/) OpenType 컬러 글꼴을 지원하도록—색상 이모지 문자(글리프)를 표시하는 것뿐 아니라 어떤 글리프든 색상으로 렌더링하기 위해—제출되었습니다.

#### OpenType 컬러 글꼴의 네 가지 유형

[Adobe, Microsoft, Google, Apple은 각각 제안서를 제출했습니다](https://www.fontlab.com/news/color-font-format-proposals/) OpenType를 확장하여 풀컬러 OpenType 글꼴을 지원하도록 했고, 결국 네 가지 제안이 채택되어 공식 OpenType 사양에 포함되었습니다. 편의를 위해 이 네 가지 변형을 대략 벡터 기반과 래스터 기반으로 묶을 수 있습니다. 하지만 이 다음의 [GitHub 저장소](https://github.com/simoncozens/test-fonts)에서 보듯이 OpenType 사양은 이 네 가지 기본 기술을 결합한 OpenType 컬러 글꼴 파일을 지원할 만큼 충분히 유연합니다.

* **벡터 기반 OpenType 글꼴:**
* **마이크로소프트**: 글리프 형태는 계층화된 컬러 벡터 형식으로 설명됩니다([COLR](https://docs.microsoft.com/en-us/typography/opentype/spec/colr) 및 [CPAL](https://docs.microsoft.com/en-us/typography/opentype/spec/cpal) 테이블).
* [**Adobe와 Mozilla**](https://www.w3.org/2013/10/SVG_in_OpenType/) ([SVG 테이블](https://docs.microsoft.com/en-us/typography/opentype/spec/svg)): 글리프 형태는 SVG를 사용해 그려지며, SVG는 벡터로 구성된 글리프를 지원합니다 *그리고 래스터 이미지*. 또한 다음을 참조하세요 [SVG 글꼴에 대한 Adobe 사용자 가이드](https://helpx.adobe.com/fonts/user-guide.html/fonts/using/ot-svg-color-fonts.ug.html).
* **래스터 기반 OpenType 글꼴:**
* **구글**: 글리프는 글꼴에 포함된 컬러 PNG 이미지로 표현됩니다([CBDT](https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt) 및 [CBLC](https://docs.microsoft.com/en-us/typography/opentype/spec/cblc) 테이블).
* **Apple**: 글리프도 글꼴에 포함된 컬러 이미지로 표현됩니다. PNG 외에도 Apple의 메커니즘([sbix 테이블](https://docs.microsoft.com/en-us/typography/opentype/spec/colr))은 JPEG와 TIFF도 지원합니다.

그 결과 OpenType 컬러 글꼴을 지원하는 운영 체제와 응용 소프트웨어는 오늘날의 혼합 기술 환경을 처리해야 합니다. 또한 개별 OpenType 컬러 글꼴과 *버전은* 같은 글꼴의 버전—은 다음과 같습니다:

* 전체 집합에 대한 적용 범위가 서로 다를 수 있습니다 [유니코드 이모지 문자](https://unicode.org/emoji/charts/emoji-list.html)—즉, 글꼴이 몇 개의 이모지 문자에 대해 글리프를 제공하는지;
* 개별 이모지 문자를 나타내기 위해 서로 다른 글리프 디자인을 사용할 수 있습니다;
* 유니코드 표준의 보다 고급 사용을 지원하기 위해 제공하는 기능이 서로 다를 수 있으며, 예를 들어 [이모지 수정자](https://unicode.org/reports/tr51/#Emoji_Modifiers_Table)과 같은 기능 [유니코드 기술 표준 #51: 유니코드 이모지](https://unicode.org/reports/tr51/).

#### HarfBuzz에 대한 화제

우리는 다음의 필요성을 언급한 바 있습니다: *텍스트 셰이핑 엔진*: 특정 문자 체계와 언어 조합으로 작성된 입력 유니코드 텍스트를 받아, 지정된 글꼴을 사용해 그 텍스트를 글리프 시퀀스와 배치 데이터로 조형하여 원래 입력 텍스트를 조판하는 데 사용할 수 있는 소프트웨어입니다.

[HarfBuzz](https://harfbuzz.github.io/) 는 이러한 텍스트 셰이핑 엔진 중 하나입니다: 그것은 [오픈 소스 코드 라이브러리](https://github.com/harfbuzz/harfbuzz) 이며, 10년이 넘는 연구 개발의 산물로서 여전히 많은 소프트웨어 제품의 일부로 활발히 개발·배포되고 있습니다. HarfBuzz 자체는 “조판”을 수행하지는 않지만 XeTeX, LuaHBTeX, [Adobe Photoshop 및 Adobe InDesign](https://en.wikipedia.org/wiki/HarfBuzz).

HarfBuzz를 통합함으로써 TeX 엔진은 그 고급 텍스트 셰이핑 기능을 활용하여 매우 정교한 다국어 조판을 제공할 수 있으며, 특히 아랍어, 히브리어, 데바나가리 등 많은 복합 문자 체계에 유용합니다. 또한 HarfBuzz가 유니코드 이모지 텍스트 문자를 처리하고 셰이핑하는 데 사용된다는 점도 주목할 만하며, 이에 대해서는 더 자세히 살펴보겠습니다.

다음 그래픽은 아랍어와 같은 복합 문자 체계의 텍스트를 조판할 때 XeTeX 또는 LuaHBTeX와 같은 소프트웨어와 통합된 HarfBuzz의 역할을 요약합니다:

![HarfBuzz를 이용한 아랍어 텍스트 셰이핑 개요](/files/6377ec5591f5a15310b5ae0650b5f408b49eb0f1)

**HarfBuzz 살펴보기**

HarfBuzz와 XeTeX 및 LuaHBTeX에 제공하는 OpenType 셰이핑 서비스에 대해 더 알아보고 싶은 분은 [HarfBuzz의 바이너리 배포판을 다운로드하여](https://github.com/harfbuzz/harfbuzz/releases) HarfBuzz 라이브러리(프로그래머용)와 명령줄 유틸리티를 포함하고 있는 `hb-view` 및 `hb-shape`.

**예: hb-view 사용 방법**

즐겨 사용하는 UTF-8 지원 텍스트 편집기에서 새 파일을 만들고 다음 여섯 개의 이모지 문자 👋👋🏻👋🏼👋🏽👋🏾👋🏿를 그 텍스트 파일에 복사/붙여넣기한 다음, UTF-8 형식으로 예를 들어 다음과 같은 파일 이름으로 저장합니다: `emoji.txt`.

텍스트 편집기가 색상 글리프를 렌더링할 수 없어서(프로그램되어 있지 않아서) 이모지의 흑백 대체 버전을 표시할 수도 있습니다. 이 6개의 이모지를 저장한 뒤에는, 파일 `emoji.txt` 다음의 유니코드 이모지 문자 시퀀스에 대한 UTF-8 데이터를 포함해야 합니다—가독성을 위해서만 이모지 수정자를 쉼표로 구분했습니다 *가독성을 위해서만*:

* `1F44B는 👋를 생성함` 1F44B는 👋를 생성함
* `1F44B는 👋를 생성함`, `1F3FB는 👋🏻를 생성함` 1F3FB는 👋🏻를 생성함
* `1F44B는 👋를 생성함`, `1F3FC는 👋🏼를 생성함` 1F3FC는 👋🏼를 생성함
* `1F44B는 👋를 생성함`, `1F3FD는 👋🏽를 생성함` 1F3FD는 👋🏽를 생성함
* `1F44B는 👋를 생성함`, `1F3FE는 👋🏾를 생성함` 1F3FE는 👋🏾를 생성함
* `1F44B는 👋를 생성함`, `1F3FF는 👋🏿를 생성함` 1F3FF는 👋🏿를 생성함

전체적으로 **11** 유니코드 문자가 있어야 하며, 각 문자는 4바이트의 UTF-8 데이터를 생성하므로 결과 `emoji.txt` 파일은 이모지가 포함된 줄 끝에 사용된 줄 끝 마커를 제외하면 44바이트 길이여야 합니다.

다음 `hb-view` 유틸리티는 파일을 사용할 수 있습니다 `emoji.txt`그리고 다음과 같은 적절한 OpenType 컬러 글꼴과 함께 `NotoColorEmoji.ttf`HarfBuzz의 셰이핑된 출력을 SVG 파일로 생성합니다. 다음 명령줄 예시는 **한 줄로 입력해야 하며** 터미널에서 입력하면 SVG 파일을 생성합니다 `emoji.svg`:

```latex
hb-view --font-size=20 --output-file="emoji.svg"
--output-format=svg --text-file=emoji.txt
--font-file=NotoColorEmoji.ttf
```

성공적으로 실행되면 파일 `emoji.svg`가 `hb-view`다음에 의해 생성된

![Hbvieemoji.png](/files/83c0267c256e1c4d01c67c3006b8b89ca063e543)

`hb-view` 적절한 유니코드 텍스트 파일과 OpenType 글꼴에 대해 HarfBuzz 셰이핑을 탐색하는 데 사용할 수 있습니다. 물론 이모지에만 한정되지 않습니다! 다음을 입력하세요:

```latex
hb-view --help-all
```

이 강력하고 편리한 유틸리티의 다양한 명령줄 옵션을 확인하세요. 즐거운 셰이핑 되세요!

## 텍스트 셰이핑과 TeX 엔진

여기서는 XeTeX와 LuaTeX 계열 TeX 엔진의 텍스트 셰이핑 기능을 살펴보겠습니다.

### XeTeX

XeTeX는 2000년대 초에 개발되었으며 TeX 기반 조판에서 여러 혁신을 선도했는데, 가장 주목할 만한 것은 *내장된* 다음에 대한 지원입니다:

* UTF-8 형식의 유니코드 텍스트 읽기;
* OpenType 글꼴 사용;
* 다국어 조판을 위한 텍스트 셰이핑;
* OpenType 기반 수학 조판.

XeTeX가 복합 문자 체계 언어를 쉽고 편리하게 조판할 수 있는 능력은 내장된 텍스트 셰이핑 기능 덕분입니다. 이는 원래 현재는 더 이상 사용되지 않는 [ICU LayoutEngine](http://userguide.icu-project.org/layoutengine)에 기반했었습니다. Khaled Hosny의 작업 덕분에 XeTeX는 텍스트 셰이핑에 HarfBuzz를 사용하도록 전환했으며, 이는 다음의 발표에서 언급되었습니다: [2013년 3월](https://tug.org/pipermail/xetex/2013-March/024118.html)다국어 텍스트를 조판하려는 사람들에게 XeTeX는 보통 선호하는 TeX 엔진으로 꼽히지만, 이제 LuaHBTeX라는 또 다른 선택지가 있으며, 이를 살펴보겠습니다.

### LuaTeX와 LuaHBTeX

LuaTeX의 개발은 2005년경 시작되었지만, 새 기능을 *에 직접* 통합한 XeTeX 소프트웨어에. XeTeX와 달리 LuaTeX의 개발자들은 “...최소한의 도구만 제공하고, 해결책은 제공하지 않는다.”를 택했습니다. ( [LuaTeX 참조 매뉴얼](https://www.pragma-ade.com/general/manuals/luatex.pdf)). 추가 기능 모음을 제공하기보다는 *내장된* LuaTeX 기반 엔진의 내부 메커니즘을 개방하여 개발자와 숙련된 사용자가 통합된 Lua 스크립팅 언어를 활용해 자신만의 해결책을 만들 수 있도록 했습니다.

예를 들어, XeTeX와 달리 LuaTeX 엔진은 *직접* OpenType 글꼴을 사용할 수 없습니다. 대신 OpenType 글꼴은 Lua 코드로 작성된 글꼴 로딩 함수를 통해 로드되고 “사용 준비”가 되어야 합니다. 이러한 글꼴 로딩 함수는 다음과 같이 불립니다: *콜백* 함수: 글꼴 로드 요청이 있을 때 LuaTeX가 호출(“실행”)하는 Lua 코드입니다.

또한 LuaTeX 엔진은 어떤 *내장된* 텍스트 셰이핑 기능도 제공하지 않습니다. 그것 역시 LuaTeX 엔진이 호출할 수 있는 외부 코드가 제공해야 텍스트 셰이핑 서비스를 받을 수 있습니다. 이는 텍스트 셰이핑 기능을 핵심 소프트웨어에 통합한 XeTeX 엔진과 대조됩니다.

#### luaotfload: LuaTeX/LuaHBTeX에서 OpenType 글꼴 사용에 필수

LuaTeX의 글꼴 로딩 콜백 메커니즘은 추가 프로그래밍이라는 “대가”를 치르긴 하지만 매우 큰 유연성을 제공합니다. 다행히 LuaLaTeX 사용자들을 위해 TeX 커뮤니티는 다음이라는 패키지를 개발했습니다: `luaotfload`를 이루는 [TeX Live의 연간 배포판](https://www.tug.org/texlive/) 이며 물론 Overleaf 사용자도 사용할 수 있습니다.

`luaotfload` 는 [CTAN에서 사용할 수 있습니다](https://ctan.org/pkg/luaotfload?lang=en) 그리고 다음이 있습니다: [GitHub에 개발 저장소](https://github.com/latex3/luaotfload) 여기서 최신 개발 상황과 [새 릴리스](https://github.com/latex3/luaotfload/releases).

`luaotfload` 는 다음을 통해 LaTeX 문서 프리앰블에 직접 로드할 수 있습니다:

```latex
\usepackage{luaotfload}
```

참고로 `luaotfload` 는 LaTeX의 이름입니다 *패키지*즉 파일 이름이 `luaotfload.sty`입니다. 만약 다음을 사용하고 싶다면 `luaotfload` plain TeX에서 사용할 수 있으며, 다음 줄을 추가하면 됩니다:

```latex
\input luaotfload.sty
```

plain TeX 문서에.

일반적으로 LuaLaTeX 사용자—즉 LuaTeX/LuaHBTeX를 사용해 LaTeX를 조판하는 사람들—는 다음과 직접 상호작용할 필요가 없습니다: `luaotfload` 왜냐하면 [`fontspec` 패키지](https://ctan.org/pkg/fontspec) 가 다음을 로드할 것이기 때문입니다 `luaotfload` 사용자를 대신해 패키지를 로드하고, 다음이 제공하는 사용자 수준 명령을 통해 많은 저수준 세부 사항을 처리해 주기 때문입니다: `fontspec` 패키지.

### LuaHBTeX: 텍스트 셰이핑을 위한 새로운 옵션

`luaotfload` 는 성숙하고 강력한 Lua 라이브러리로, 다양한 언어와 문자 체계에 대한 텍스트 셰이핑 서비스를 제공하는 동시에 LuaTeX의 OpenType 글꼴 처리를 담당합니다. 원래 `luaotfload` 의 텍스트 셰이핑 함수는 순수 Lua 코드로 구현되었지만, TeX Live 2020의 출시로 텍스트 셰이핑을 위한 또 다른 주류 옵션인 LuaHBTeX라는 새로운 LuaTeX 기반 엔진이 등장했습니다.

LuaHBTeX의 “HB”는 HarfBuzz를 의미합니다. 본질적으로 LuaHBTeX는 원래의 LuaTeX 엔진에 *와* 통합된 HarfBuzz 텍스트 셰이핑 엔진을 더한 것입니다. LuaTeX의 설계 철학에 따라 HarfBuzz의 उपलब्ध 여부가 *자동으로* 텍스트가 LuaHBTeX에 의해 셰이핑된다는 것을 보장하지는 않습니다. HarfBuzz는 텍스트 셰이핑 솔루션을 만드는 데 사용할 수 있는 또 다른 도구입니다.

LuaHBTeX의 HarfBuzz 통합은 [Lua 코드로 프로그래밍할 수 있으며](#introduction-to-the-luahbtex-harfbuzz-api)이는 다음이 가능하게 했습니다 `luaotfload`의 개발자들이 HarfBuzz 기반 텍스트 셰이핑 솔루션을 추가하는 것. 그 결과 [2019년 11월 5일에 출시된 버전 3.1부터](https://github.com/latex3/luaotfload/releases/tag/v3.1), `luaotfload` HarfBuzz의 장점을 활용하도록 향상되어 일반 사용자가 HarfBuzz의 텍스트 셰이핑 기능에 쉽게 접근할 수 있게 되었습니다.

LuaTeX와의 HarfBuzz 통합에 대한 기술적 세부 사항에 관심 있는 독자는 다음을 읽을 수 있습니다: [Khaled Hosny의 논문](https://www.tug.org/TUGboat/tb40-1/tb124hosny-harfbuzz.pdf).

### luaotfload: 텍스트 셰이핑을 위한 두 가지 옵션(HarfBuzz는 언제 사용해야 하나?)

이제 LuaLaTeX 사용자는 텍스트 셰이핑에 두 가지 옵션이 있습니다:

* `luaotfload`의 원래(노드 기반) 텍스트 셰이핑 구현, 순수 Lua로 작성됨;
* `luaotfload`의 HarfBuzz 기반 셰이핑—HarfBuzz의 텍스트 셰이핑 함수를 호출하는 Lua 코드로 접근합니다.

`luaotfload` 는 이 두 셰이핑 시스템에 다음의 “를 통해 접근할 수 있게 합니다:`모드`” 매개변수—하지만 대부분의 사용자는 이에 상응하는 `fontspec` “`렌더러`” 옵션을 사용할 것이며, 직접 다음의 하위 수준 함수를 사용하지는 않을 것입니다: `luaotfload`.

다음의 각 `luaotfload`의 텍스트 셰이핑 솔루션은 각각 장점과(현재의) 약점을 가지고 있지만, 어떤 것을 언제 사용해야 할까요? 고려할 점은 다음과 같습니다:

* `luaotfload`의 기본 노드 기반 처리는 특히 큰 CJK OpenType 글꼴에서 메모리를 많이 사용할 수 있습니다. CJK 텍스트 셰이핑에 HarfBuzz를 사용하면 속도 향상과 메모리 사용량 감소를 얻을 수 있습니다.
* 복합 문자 체계에는 HarfBuzz를 사용하세요. 왜냐하면 “...인도계 문자와 아랍 문자의 렌더링을 크게 향상시키며, 이러한 문자에는 강력히 권장됩니다.” ( `luaotfload` 매뉴얼).
* 에 대한 HarfBuzz 통합은 `luaotfload` 아직 비교적 새롭고 추가 개발이 진행 중입니다. 이 글을 쓰는 시점(2021년 7월)에는 luaotfload의 내장 셰이핑을 사용하는 것이 좋습니다(설정 `mode=node`)를 주 문서 글꼴에 사용하는 것이 바람직하며, 특히 문서가 라틴 문자를 사용하는 경우 더욱 그렇습니다. 다음 [GitHub 이슈를 참조하세요](https://github.com/latex3/luaotfload/issues/175#issue-801120377)여기에는 문제와 논의가 요약되어 있습니다. 실험해 보고 싶다면 다음을 사용할 수 있습니다: `luaotfload` 글꼴 파일을 불러와 두 개의 LaTeX 글꼴을 만들 수 있습니다: 하나는 HarfBuzz 기반 셰이핑을 사용하고, 다른 하나는 Lua 기반 셰이핑을 사용합니다. Overleaf는 다음을 만들었습니다: [샘플 프로젝트](#sample-project-arabic-shaping), 이것이 이를 보여 줍니다.
* 수학 글꼴을 다루는 데 HarfBuzz를 사용하지 마세요. tex.stackexchange의 개발자들이 논의했듯이, HarfBuzz는 [수학 조판용 글꼴을 처리하도록 설계되지 않았습니다](https://tex.stackexchange.com/questions/544881/does-luahbtex-with-harfbuzz-renderer-completely-supports-math-formating) 그러므로 그런 용도로 사용하지 마세요.

**샘플 프로젝트: 아랍어 셰이핑**

다음은 여러 고품질 아랍어 서체를 사용하여 다음을 비교하는 Overleaf 프로젝트입니다: `luaotfload`의 노드 기반 텍스트 셰이핑 서비스 (`mode=node`)를 HarfBuzz의 것과 비교하면 (`mode=harf`):

* <https://www.overleaf.com/latex/examples/complex-script-shaping-using-luaotfload-and-harfbuzz/gfssprnhfddn>

이 프로젝트에는 다음 이미지에 표시된 출력이 포함되어 있습니다:

![아랍어 조판](/files/cef5343645628f97b8a9b14b20d81d034695a21e)

### fontspec에서 “Renderer” 선택하기

그 문서에서 언급한 바와 같이 [문서](https://mirror.ox.ac.uk/sites/ctan.org/macros/unicodetex/latex/fontspec/fontspec.pdf), `fontspec` “...XeTeX 또는 LuaTeX 사용자라면 누구나 LaTeX 문서에서 OpenType 글꼴을 불러올 수 있게 해줍니다”. LuaTeX 또는 LuaHBTeX 엔진을 사용한다면, `fontspec` 가 다음을 로드할 것이기 때문입니다 `luaotfload` 라이브러리를 제공하며, 추가로 다음과 상호작용할 필요를 덜어 주는 편리한 사용자 수준 명령 모음을 제공합니다 `luaotfload`의 저수준 기능.

그렇다면 HarfBuzz의 글리프 조형과 다음이 제공하는 내장 조형 중에서 어떻게 선택할까요 `luaotfload`? 그 답은 훌륭한 [`fontspec` 문서](https://mirror.ox.ac.uk/sites/ctan.org/macros/unicodetex/latex/fontspec/fontspec.pdf), 특히 제6부: LuaTeX 전용 글꼴 기능에 있습니다. `fontspec` 라는 설정을 제공합니다 `렌더러` 이는 글꼴을 다음을 통해 정의할 때 설정할 수 있습니다 `fontspec`. `렌더러` 는 글꼴의 저수준 처리를 제어합니다. 관심 있는 두 옵션은

* `Renderer = Node`: OpenType 글꼴을 조판할 때의 기본 “mode”입니다—이는 `luaotfload`의 텍스트 조형 함수를 순수 Lua로 구현한 것을 사용합니다.
* `Renderer = Harfbuzz`: 이 “mode”는 HarfBuzz 텍스트 조형 엔진에서 사용할 수 있도록 글꼴을 정의/불러옵니다. `luaotfload` LuaHBTeX의 API를 사용해 HarfBuzz의 함수를 호출합니다.

자세한 내용은 다음을 참조하세요 [`fontspec` 문서](https://mirror.ox.ac.uk/sites/ctan.org/macros/unicodetex/latex/fontspec/fontspec.pdf).

## TeX 엔진, HarfBuzz, 그리고 컬러 이모지

XeTeX와 LuaHBTeX는 둘 다 HarfBuzz를 통합하지만, HarfBuzz의 더 고급 기능 일부에 대해 서로 다른 수준의 지원을 제공합니다—특히 OpenType 컬러 글꼴을 불러오고 사용하는 기능에서 그렇습니다.

### XeTeX와 OpenType 컬러 글꼴

언급했듯이, 글꼴 글리프를 저장하는 데 사용되는 데이터 형식에 따라 OpenType 컬러 글꼴은 벡터 기반과 래스터 기반의 두 범주로 나뉩니다.

#### XeTeX와 래스터 기반 OpenType 컬러 글꼴

XeTeX는 Google의 것과 같은 래스터 기반 OpenType 컬러 글꼴을 불러올 수 없습니다— [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) TeX Live 2020에 포함되어 있습니다. 예를 들어 Noto Color Emoji(NotoColorEmoji.ttf)를 불러오려고 하면 XeLaTeX는 Noto Color Emoji를 “찾을 수 없다”고 잘못 해석될 수 있는 오류와 함께 실패합니다. 다음 LaTeX 코드는 XeLaTeX로 조판한 것으로, *작동하지 않습니다*:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\emojifont{NotoColorEmoji.ttf}
\newcommand{\smiley}{{\emojifont\char"1F600}}
\smiley
\end{document}
```

[이 XeLaTeX 코드를 Overleaf에서 열어보세요(이 코드는 ***작동하지*** 않습니다).](https://www.overleaf.com/docs?engine=xelatex\&snip_name=XeTeX+failure\&snip=%5Cdocumentclass%7Barticle%7D%0A%5Cusepackage%7Bfontspec%7D%0A%5Cbegin%7Bdocument%7D%0A%5Cnewfontfamily%5Cemojifont%7BNotoColorEmoji.ttf%7D%0A%5Cnewcommand%7B%5Csmiley%7D%7B%7B%5Cemojifont%5Cchar%221F600%7D%7D%0A%5Csmiley%0A%5Cend%7Bdocument%7D)

다음 오류와 함께 실패합니다:

```
! Package fontspec 오류: 글꼴 "NotoColorEmoji"를 찾을 수 없습니다.
```

마찬가지로, XeTeX로 처리한 간단한 Plain TeX 예제도 실패합니다

```latex
\font\emojifont="[NotoColorEmoji.ttf]" at 12pt
\emojifont \char"1F600
\bye
```

[이 Plain TeX(XeTeX) 예제를 Overleaf에서 열어보세요(이 예제는 ***작동하지*** 않습니다).](https://www.overleaf.com/docs?engine=latex_dvipdf\&snip_name\[]=main.tex\&snip\[]=%25%5Ctitle%7Bdummy+title%7D%0A%5Cfont%5Cemojifont%3D%22%5BNotoColorEmoji.ttf%5D%22+at+12pt%0A%5Cemojifont+%5Cchar%221F600%0A%5Cbye\&snip_name\[]=readme\&snip\[]=This+project+uses+a+latexmkrc+file+to+run+xetex+not+xelatex\&snip_name\[]=latexmkrc\&snip\[]=%24latex+%3D+%27xetex%25O+%25S%27%3B+%23+to+use+the+xetex+engine\&main_document=main.tex)

Plain TeX 예제는 비슷하지만 다른 오류 메시지를 보고합니다:

```
! 글꼴 \emojifont=[NotoColorEmoji.ttf] at 12.0pt를 불러올 수 없음: Metric (TFM) 파일
이나 설치된 글꼴을 찾을 수 없습니다.
l.1 \font\emojifont="[NotoColorEmoji.ttf]" at 12pt

이 글꼴의 크기 데이터를 읽을 수 없어서,
글꼴 지정은 무시하겠습니다.
[전문가들은 TFtoPL/PLtoTF를 사용해 TFM 파일을 수정할 수 있습니다.]
다른 글꼴 지정값을 넣어보는 것도 좋습니다;
예: `I\font<same font id>=<substitute font name>'를 입력합니다.
```

**Plain LuaHBTeX 예제**

비교를 위해, LuaHBTeX로 컴파일한 최소 Plain TeX 예제를 보겠습니다

```latex
\input luaotfload.sty
\font\emojifont=NotoColorEmoji.ttf:mode=harf at 12pt
\emojifont \Uchar"1F600
\bye
```

[이 Plain TeX(LuaHBTeX) 예제를 Overleaf에서 열어보세요(성공적으로 컴파일됩니다).](https://www.overleaf.com/docs?engine=latex_dvipdf\&snip_name\[]=main.tex\&snip\[]=%25%5Ctitle%7BPlain+TeX+with+LuaHBTeX%7D%0A%5Cinput+luaotfload.sty%0A%5Cfont%5Cemojifont%3DNotoColorEmoji.ttf%3Amode%3Dharf+at+12pt%0A%5Cemojifont+%5CUchar%221F600%0A%5Cbye\&snip_name\[]=readme\&snip\[]=This+project+uses+a+latexmkrc+file+to+run+luahbtex+not+lualatex\&snip_name\[]=latexmkrc\&snip\[]=%24latex+%3D+%27luahbtex+%25O+%25S%27%3B+%23+to+use+the+luahbtex+engine\&main_document=main.tex)

#### XeTeX 실패의 실제 원인

XeTeX가 제공하는 오류 메시지는 문제의 실제 원인을 부분적으로 가립니다: OpenType 컬러 글꼴, 특히 래스터 기반 변형은 *작동하지* XeTeX에서 지원됩니다. 실제로 XeTeX(Kpathsea)는 *찾을 수 있습니다* Noto Color Emoji 글꼴을 찾을 수 있지만, XeTeX는 이를 완전히 *불러올 수는 없습니다* 그 글꼴을 불러오지 못하며, 조판에 그 글꼴을 사용하기 위해 필요한 내부 글꼴 데이터 테이블을 초기화할 수 없습니다. 내부적으로 XeTeX는 *시작합니다* 글꼴을 불러오는 과정을 시작하고 “확장 가능성”(FreeType의 “확장 가능성” 정의를 사용)을 테스트하지만, 그 테스트가 실패하여 XeTeX는 표준적인, 다소 오해의 소지가 있는 TeX 엔진 오류 메시지를 표시합니다.

**기술적 메모**

NotoColorEmoji.ttf에 대한 XeTeX의 처리는 디버그 버전의 XeTeX 실행 파일을 컴파일하여 조사했습니다. Eclipse IDE를 사용해 XeTeX 함수에 중단점을 설정했습니다 `creatFontFromFile(filename, index, pointsize)`, 그런 다음 코드를 단계적으로 실행하며 이후 처리를 관찰했습니다.

#### XeTeX와 벡터 기반 OpenType 컬러 글꼴

XeTeX는 *불러올 수는 없습니다* 벡터 기반 OpenType 컬러 글꼴을 사용할 수 있지만, 결과 PDF에서 컬러 이모지를 생성하지는 않습니다—XeTeX가 PDF를 생성한다면 말입니다. LuaTeX, LuaHBTeX, pdfTeX와 달리 XeTeX는 *직접* 조판된 문서를 PDF 형식으로 출력하지 않습니다. 대신 XeTeX는 중간 `.xdv` (e**x**tended **dv**i) 파일 형식으로 출력하며, 이는 다음 도구를 사용해 PDF로 변환됩니다 `xdvipdfmx`. 이 글을 쓰는 시점에는, `xdvipdfmx` 적절한 컬러 이모지 글리프 데이터를 PDF에 포함할 수 없어서,

다음은 OpenType 컬러 글꼴을 사용하는 XeLaTeX 예제입니다 [TwemojiMozilla.ttf](https://ctan.org/tex-archive/fonts/twemoji-colr), TeX Live에서 사용할 수 있습니다. TwemojiMozilla.ttf는 색상 글리프를 저장하기 위해 Microsoft의 COLR/CPAL 벡터 형식을 사용하며 TeX Live 2020에 포함되어 있습니다. 이 예제에서 XeTeX는 글꼴을 불러오고, 다음을 생성할 수 있습니다 `.xdv` 그리고 PDF 파일을 생성할 수 있지만, 이모지 글리프는 조판된 PDF에 나타나지 않습니다:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\emojifont{TwemojiMozilla.ttf}
\newcommand{\smiley}{{\emojifont\char"1F600}}
여기 스마일리 이모지가 있습니다: \smiley
\end{document}
```

[이 XeLaTeX 코드를 Overleaf에서 열어보세요(작동하지 않습니다).](https://www.overleaf.com/docs?engine=xelatex\&snip_name=XeTeX+failure\&snip=%5Cdocumentclass%7Barticle%7D%0A%5Cusepackage%7Bfontspec%7D%0A%5Cbegin%7Bdocument%7D%0A%5Cnewfontfamily%5Cemojifont%7BTwemojiMozilla.ttf%7D%0A%5Cnewcommand%7B%5Csmiley%7D%7B%7B%5Cemojifont%5Cchar%221F600%7D%7D%0AHere+is+a+smiley%3A+%5Csmiley%0A%5Cend%7Bdocument%7D)

반면 위 코드는 LuaLaTeX에서 다음을 정의하면 작동합니다 `\emojifont` 다음 설정을 사용하여 `fontspec` 설정 `[Renderer=HarfBuzz]`:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\emojifont{TwemojiMozilla.ttf}[Renderer=HarfBuzz]
\newcommand{\smiley}{{\emojifont\char"1F600}}
여기 스마일리 이모지가 있습니다: \smiley
\end{document}
```

[이 LuaLaTeX 코드를 Overleaf에서 열어보세요(작동합니다).](https://www.overleaf.com/docs?engine=lualatex\&snip_name=LuaLaTeX+emoji+example\&snip=%5Cdocumentclass%7Barticle%7D%0A%5Cusepackage%7Bfontspec%7D%0A%5Cbegin%7Bdocument%7D%0A%5Cnewfontfamily%5Cemojifont%7BTwemojiMozilla.ttf%7D%5BRenderer%3DHarfBuzz%5D%0A%5Cnewcommand%7B%5Csmiley%7D%7B%7B%5Cemojifont%5Cchar%221F600%7D%7D%0AHere+is+a+smiley%3A+%5Csmiley%0A%5Cend%7Bdocument%7D)

### LuaHBTeX와 OpenType 컬러 글꼴

통합된 HarfBuzz 조형 엔진과 `luaoftload` 라이브러리를 통해 LuaHBTeX는 OpenType 컬러 글꼴의 네 가지 유형을 모두 지원합니다. LuaLaTeX 사용자는 이모지 문자를 포함한 텍스트의 유니코드 기반 처리를 최대한 활용하거나, OpenType 컬러 글꼴을 사용해 문서를 화려한 색상의 텍스트로 꾸밀 수 있습니다.

앞서 언급했듯이, OpenType 컬러 글꼴의 네 가지 변형은 두 그룹으로 분류할 수 있습니다:

* PNG와 같은 래스터 이미지 형식의 글리프를 포함한 것;
* SVG 또는 Microsoft의 COLR/CPAL 메커니즘 같은 벡터 기반 형식을 사용하는 것.

벡터 기반 글리프 형식은 확장성이 뛰어나다는 장점이 있어, 어떤 포인트 크기에서도 선명한 글리프 그래픽을 생성합니다.

**LuaHBTeX에서 Microsoft COLR/CPAL 컬러 글꼴 사용하기**

OpenType 컬러 이모지 글꼴에 벡터 형식을 사용하고 싶다면 다음 글꼴을 확인해 보세요 [TwemojiMozilla.ttf](https://ctan.org/tex-archive/fonts/twemoji-colr?lang=en), 이 글꼴은 Microsoft의 COLR/CPAL 형식을 기반으로 합니다. TwemojiMozilla.ttf는 TeX Live에 포함되어 있지만, 최신 버전은 해당 [GitHub 저장소](https://github.com/mozilla/twemoji-colr/releases) 곳에서 받아 Overleaf 프로젝트에 업로드할 수 있습니다.

다음은 작은 `fontspec`-기반 예제입니다 `Renderer=Harfbuzz`, 큰(벡터) 오리 이모지를 조판합니다:

```latex
\documentclass{article}
\usepackage{fontspec}
\title{Duck demo}
\begin{document}
\newfontfamily\emojifont[Renderer=Harfbuzz,SizeFeatures={Size=400}]{TwemojiMozilla.ttf}
\emojifont\Uchar"1F986
\end{document}
```

[벡터 오리를 조판하려면 이 LuaLaTeX 예제를 열어보세요.](https://www.overleaf.com/docs?engine=lualatex\&snip_name=Typesetting+an+emoji+duck\&snip=%5Cdocumentclass%7Barticle%7D%0A%5Cusepackage%7Bfontspec%7D%0A%5Ctitle%7BDuck+demo%7D%0A%5Cbegin%7Bdocument%7D%0A%5Cnewfontfamily%5Cemojifont%5BRenderer%3DHarfbuzz%2CSizeFeatures%3D%7BSize%3D400%7D%5D%7BTwemojiMozilla.ttf%7D%0A%5Cemojifont%5CUchar%221F986%0A%5Cend%7Bdocument%7D)

다음은 위 예제에서 생성된 (벡터) 오리입니다:

![](/files/ede053d20eb729b490cac2a9cda331125b35728a)

#### LuaHBTeX에서 SVG 기반 OpenType 컬러 글꼴 사용

이 글을 업데이트한 시점(2023년 7월)에는 LuaLaTeX에서 SVG 형식의 OpenType 컬러 글꼴을 사용하는 것에 대한 공식 문서가 거의 없습니다. 일부 [온라인 토론에서 나온 의견은](https://github.com/latex3/luaotfload/issues/96) 다음을 사용하라고 제안합니다 `fontspec`의 `RawFeature`, 아래의 의사 코드에 보인 것처럼 말입니다. 다음을 바꾸세요 `*여기에 SVG 글꼴 파일 이름*` 를 LaTeX 코드에서 접근 가능한 SVG 기반 글꼴 파일 이름으로:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\emoji[RawFeature={+svg},SizeFeatures={Size=20}]{your SVG font file name here}
\emoji Your emoji here...
\end{document}
```

다음을 생략하고 `fontspec` 불러오면 `luaotfload` 직접, 다음과 같이 글꼴을 선언하고 지정해야 할 수 있습니다—우리의 실험에 따르면 다음을 생략해야 `mode=harf` 이 작동합니다:

```latex
\font\emoji=[your SVG font file name here]:+svg;
```

**주의할 점**

SVG 형식의 OpenType 컬러 글꼴을 사용하려는 독자는 다음을 유의해야 합니다:

* 많은 수의 글리프를 포함한 SVG 형식의 OpenType 글꼴은 [LuaLaTeX에서 계산 비용이 높을 수 있으며](#processing-svg-glyph-data) 처리하는 데 비용이 많이 들 수 있어, 잠재적으로 다음을 초래할 수 있습니다 [Overleaf 시간 초과](/latex/ko/knowledge-base/038-fixing-and-preventing-compile-timeouts.md).
* LuaLaTeX의 이러한 글꼴 지원은 [실험적이라고 볼 수 있습니다](https://github.com/latex3/luaotfload/issues/96#issuecomment-530317399): 결과는 프로젝트에서 사용하는 TeX Live 배포판에 따라 달라질 수 있으므로, 직접 실험해 보고 신중하게 진행하는 것이 좋습니다.

**SVG 글리프 데이터 처리**

SVG는 디자이너가 글꼴의 글리프를 나타내는 복잡하고 다채로운 디자인을 만들 수 있게 해줍니다—다만 몇 가지 SVG 제한을 따릅니다 [OpenType 사양에 문서화된](https://docs.microsoft.com/en-us/typography/opentype/spec/svg). 그러나 LuaHBTeX를 포함한 TeX 엔진은 SVG 파일이나 데이터를 직접 가져와(사용해) 사용할 수 없습니다—예를 들어 SVG 형식의 OpenType 컬러 글꼴에서 글리프 모양을 설명하는 데 사용되는 SVG 데이터처럼 말입니다. 글리프의 SVG 데이터는 PDF 형식으로 변환되어야 하는데, LuaHBTeX가 그것을 사용해 글리프를 조판하고 최종 PDF 문서를 생성할 수 있기 때문입니다. 이 SVG-to-PDF 변환은 다음 내부의 Lua 코드가 처리합니다 `luaoftload`: 각 글리프의 SVG 데이터는 글꼴 파일에서 추출되어 임시 `.svg` 파일로 저장된 뒤, Inkscape를 명령줄을 통해 사용해 PDF로 변환됩니다. SVG 데이터를 추출하고 PDF로 변환하는 데는 약간의 처리 오버헤드가 발생하여, 특히 수천 개의 이모지 글리프를 포함한 큰 SVG 글꼴을 사용하는 문서는 컴파일 시간이 길어질 수 있습니다.

#### 래스터 기반 OpenType 컬러 글꼴

**LuaHBTeX에서 Google의 CBDT/CBLC OpenType 컬러 글꼴 형식 사용하기**

[Noto Color Emoji](https://fonts.google.com/noto/specimen/Noto+Color+Emoji) 은 TeX Live에 포함된 OpenType 컬러 글꼴이므로 Overleaf 프로젝트에서 쉽게 사용할 수 있습니다. Noto Color Emoji는 이모지 글리프를 나타내기 위해 PNG 형식 그래픽을 사용하므로, 다음 예제에서 보이듯 큰(래스터) 오리 이모지를 조판하는 데 사용할 수 있습니다. 다시 한 번 주의할 점은 `fontspec` 글꼴 선언(`\emojifont`)에서는 `Renderer=Harfbuzz`.

```latex
\documentclass{article}
\usepackage{fontspec}
\title{Duck demo}
\begin{document}
\newfontfamily\emojifont[Renderer=Harfbuzz,SizeFeatures={Size=400}]{NotoColorEmoji.ttf}
\emojifont\Uchar"1F986
\end{document}
```

[래스터 오리를 조판하려면 이 LuaLaTeX 예제를 열어보세요.](https://www.overleaf.com/docs?engine=lualatex\&snip_name=Typesetting+a+large+raster+duck\&snip=%5Cdocumentclass%7Barticle%7D%0A%5Cusepackage%7Bfontspec%7D%0A%5Ctitle%7BDuck+demo%7D%0A%5Cbegin%7Bdocument%7D%0A%5Cnewfontfamily%5Cemojifont%5BRenderer%3DHarfbuzz%2CSizeFeatures%3D%7BSize%3D400%7D%5D%7BNotoColorEmoji.ttf%7D%0A%5Cemojifont%5CUchar%221F986%0A%5Cend%7Bdocument%7D)

다음은 위 예제에서 생성된 래스터 오리입니다:

![LaTeX로 조판한 래스터 오리 이모지](/files/631646286f4f0ab241f573a6753253352c9c2e36)

다음을 사용하려고 하면 `NotoColorEmoji.ttf` 하지만 다음을 생략하면 `[Renderer=HarfBuzz]` 에서 `fontspec` 글꼴 선언에서 LuaHBTeX는 실패하며 PDF 파일을 기록하려고 할 때 오류 메시지를 표시합니다:

```latex
! error:  (file /usr/local/texlive/2020/texmf-dist/fonts/truetype/google/noto-em
oji/NotoColorEmoji.ttf) (ttf): loca table not found
```

이 오류가 다음에서 발생하는 이유는 [loca 테이블](https://docs.microsoft.com/en-us/typography/opentype/spec/loca) 는 [GitHub에서 설명되어 있습니다](https://github.com/latex3/luaotfload/issues/98#issuecomment-531610153).

**LuaHBTeX에서 Apple의 sbix OpenType 컬러 글꼴 형식 사용하기**

오프라인 테스트에 따르면 LuaHBTeX는 다음을 지원합니다 `sbix` OpenType 컬러 글꼴의 변형이지만, 이 글을 쓰는 시점까지 적절한 라이선스를 가진 `sbix`-변형 컬러 이모지 글꼴을 찾아 오리를 조판하는 예제를 보여주지는 못했습니다. 부디 [저희에게 연락해 주세요](https://www.overleaf.com/contact) 알고 계시다면, 이 글을 오리처럼 재빨리 업데이트해 그 글꼴을 사용하겠습니다.

## LuaHBTeX HarfBuzz API 소개

![Db.gif](/files/74fb9893faaeb4f010f2534288587e6e416af200) ![Db.gif](/files/74fb9893faaeb4f010f2534288587e6e416af200)

텍스트 조형은 특히 복잡 문자 체계 언어와 이모지에 대해 본질적으로 어려운 작업이므로, 놀랍지 않게도 HarfBuzz는 텍스트 조형 작업에 익숙하지 않다면 다루기 까다로운 정교한 라이브러리입니다. 이 마지막 절에서는 LuaHBTeX가 HarfBuzz를 통합하는 방식과 이를 다음 내부의 Lua 코드로 접근하는 방법을 살펴봅니다 `\directlua`.

우리 예제는 LuaHBTeX HarfBuzz API를 보여주기 위해 비교적 기본적인 코드를 사용합니다. 이는 다소 인위적이며, 생산 수준의 품질도 아니고 매우 실용적이지도 않습니다. 목적은 몇 가지 핵심 아이디어를 소개하는 것뿐이기 때문입니다. Lua 코드는 두 개의 `\directlua` 조각으로 나뉘어 있습니다: 첫 번째는 다음을 불러오고 `luaharfbuzz` 라이브러리를 불러오고, 두 번째 `\directlua` 조각에서 사용할 몇몇 전역 변수를 만듭니다. 그 두 번째 조각에서 우리는 다음이라는 매크로를 정의합니다 `\codestoemoji`.

Knuth가 사용한 이중 위험 굽힘 표지(이미지는 다음 제공) [이 사이트](http://www.truetex.com/db.htm))를 그대로 따르는 것이 적절해 보입니다. 왜냐하면 내용이 다소 저수준적이고 “내부를 들여다보는” 성격이기 때문입니다—다만 더 대담한 독자에게는 흥미로울 수도 있기를 바랍니다. LuaHBTeX의 HarfBuzz 통합은 다음에서 파생되었습니다 [GitHub의 luaharfbuzz 프로젝트](https://github.com/ufyTeX/luaharfbuzz/wiki#projects-using-luaharfbuzz) 여기에서 다음을 찾을 수 있습니다 [프로젝트 소개](https://github.com/ufyTeX/luaharfbuzz/wiki) 와 함께 [luaharfbuzz API 목록](http://ufytex.github.io/luaharfbuzz/).

### 첫 단계: luaharfbuzz 라이브러리를 불러오고 글꼴 찾기

LuaHBTeX의 HarfBuzz API를 사용하려면 먼저 다음이라는 라이브러리(모듈)를 불러와야 합니다 `luaharfbuzz`, 이는 LuaHBTeX에 내장되어 있으며, 반환된 테이블을 우리가 다음이라고 부를 (전역) 변수에 저장합니다 `hblib`:

```latex
hblib=require("luaharfbuzz")
```

다음으로, 적절한 이모지 OpenType 컬러 글꼴을 찾아야 합니다: 우리는 Noto Color Emoji를 사용할 것입니다—못 찾는 경우에 대비한 오류 검사는 전혀 하지 않고 있으니, 상당히 게으르다는 점을 알아두세요! 이를 찾기 위해 다음을 사용합니다 `kpse` (Kpathsea) 라이브러리로, 이것 역시 LuaTeX/LuaHBTeX의 일부입니다:

```latex
pathtofontfile=kpse.find_file("NotoColorEmoji.ttf","truetype fonts")
```

이제 변수 `hblib`, 그리고 적절한 글꼴의 경로(`pathtofontfile`)를 확보했으니, 이제 다음을 사용하기 시작할 수 있습니다 `hblib`. `\directlua` 우선, 두 번째

```latex
%Noto Color Emoji에서 HarfBuzz face와 HarfBuzz font 생성
hbface = hblib.Face.new(pathtofontfile)
hbfont = hblib.Font.new(hbface)
```

#### HarfBuzz font와 HarfBuzz face: 무엇인가요?

하나의 [HarfBuzz face 객체](https://harfbuzz.github.io/fonts-and-faces.html) 는 글꼴 파일에서 불러온 서체를 나타내지만, 크기 같은 구체적인 매개변수는 설정되지 않은 상태입니다.  [HarfBuzz font 객체](https://harfbuzz.github.io/fonts-and-faces.html) 는 다음을 나타냅니다 *구체적인 인스턴스* 의 HarfBuzz face입니다. 따라서 하나의 HarfBuzz face에서 서로 다른 HarfBuzz font 객체를 파생할 수 있으며, 각 HarfBuzz font는 크기 같은 속성을 서로 다른 값으로 설정할 수 있습니다. HarfBuzz face는 HarfBuzz font보다 더 높은 수준의 추상화입니다.

### 글꼴 글리프로 PNG 파일 만들기

첫 번째 `\directlua` 조각의 마지막 부분은 다음이라는 함수입니다 `writePNGglyph(hbfontobject, glyphID)` 이 함수는 Noto Color Emoji 같은 일부 OpenType 컬러 글꼴이 포함된 이모지 글리프를 표현하기 위해 PNG 그래픽을 사용한다는 점을 보여 주는 데 사용합니다.

이 함수는 LuaHBTeX의 HarfBuzz API를 사용해 글리프에서 PNG 데이터를 추출하고 그 데이터를 다음에 씁니다 `.png` 라는 이름의 파일 `Graphics<glyphID>.png`. 해당 `.png` 파일 이름은 다음이 사용하도록 반환됩니다 `\includegraphics` 조판된 PDF에 PNG 글리프 이미지를 포함하기 위해

다음이 준비되면 `writePNGglyph(hbfontobject, glyphID)` 가 마련되면, 첫 번째 `\directlua` 코드 조각은 다음과 같습니다:

```latex
\directlua{

% LuaHBTeX에서 luaharfbuzz 라이브러리 불러오기
hblib=require("luaharfbuzz")

% Overleaf 서버에서 Noto Color Emoji 글꼴 찾기
pathtofontfile=kpse.find_file("NotoColorEmoji.ttf","truetype fonts")

% Noto Color Emoji에서 HarfBuzz face와 HarfBuzz font 생성
hbface = hblib.Face.new(pathtofontfile)
hbfont = hblib.Font.new(hbface)

% 이 함수는 글꼴과 glyph ID를 인수로 받습니다:
% 이 함수는 글리프의 PNG 데이터를 추출하고
% 이를 .png 파일로 기록합니다

function writePNGglyph(hbfontobject, glyphID)

    % 글리프 PNG 데이터 가져오기
    local pngblob=hbfontobject:ot_color_glyph_get_png(glyphID)
    local pngdata=pngblob:get_data()

    % .png 파일 이름 만들기
    local fname="Glyph"..glyphID..".png"

    % .png 파일을 쓰고 파일 이름 반환
    local output = assert(io.open(fname, "wb"))
    output:write(pngdata)
    output:close()

    % \includegraphics가 사용할 파일 이름 반환
    return fname
end
}
```

### 두 번째 \directlua 조각: 매크로 \codestoemoji 만들기

목표는 다음 매크로를 정의하는 것입니다 `\codestoemoji` HarfBuzz가 조형할 이모지 문자 코드를 포함한 텍스트 조각과 함께 호출할 수 있는 매크로입니다. 구체적으로는 다음을 사용합니다 `\Uchar<문자 코드>` 각 이모지 문자를 나타내기 위해 사용합니다; 예:

```latex
\codestoemoji{\Uchar"1F3F4\Uchar"E0067\Uchar"E0062\Uchar"E0065\Uchar"E006E\Uchar"E0067\Uchar"E007F}
```

다음의 정의 안에는 많은 내용이 들어 있습니다 `\codestoemoji` 이에 대해서는 아래에서 설명하겠습니다. 정의는 다음과 같습니다:

```latex
\newcommand{\codestoemoji}[1]{%
\directlua{

local str="#1"
local hbbuffer = hblib.Buffer.new()
hbbuffer:add_utf8(str)

hbbuffer:set_direction(hblib.Direction.new("ltr"))
local res = hblib.shape_full(hbfont, hbbuffer, {},{})

if (res) then
    local hbglyphs=hbbuffer:get_glyphs()
    % 글리프 테이블 hbglyphs는 1부터 시작합니다
    local i = 1
    while hbglyphs[i] \noexpand~= nil do
        local glyph = hbglyphs[i]
        i = i + 1
        local fname=writePNGglyph(hbfont, glyph.codepoint)
        % 가져온 PNG 이미지의 크기를 줄입니다
        local s = 0.75
        local scal="[scale="..tostring(s).."]"
        tex.print([[\noexpand\includegraphics]]..scal..[[{]]..fname..[[}]])
     end
end
}}
```

#### 매크로 \codestoemoji의 정의 이해하기

다음 `\codestoemoji` 매크로는 대부분 다음 안에 포함된 Lua 코드입니다 `\directlua`, 따라서 이에 대해 더 알고 싶다면 *어떻게* `\directlua` 작동하는지에 대해서는 Overleaf 기사 [이해하기 `\directlua`](/latex/ko/in-depth-articles/09-an-introduction-to-luatex-part-2-understanding-directlua.md). 여기서는 LuaTeX와 LuaHBTeX가 다음을 어떻게 처리하는지 설명합니다 `\directlua` Lua 코드 안에 TeX/LaTeX 명령이 포함될 때, 특히 다음을 사용해야 하는 이유를 다룹니다 `\noexpand` 및 `\unexpanded`.

**매크로 매개변수 처리: "#1"**

매크로는 다음 세 줄로 시작합니다:

```latex
local str="#1"
local hbbuffer = hblib.Buffer.new()
hbbuffer:add_utf8(str)
```

이는 다음 작업을 수행합니다:

* `local str="#1"`: 이는 매크로가 전달한 입력으로부터 Lua 문자열을 만듭니다;
* `local hbbuffer = hblib.Buffer.new()`: 이는 HarfBuzz API를 사용해 HarfBuzz가 조형할 텍스트를 담을 버퍼를 만듭니다;
* `hbbuffer:add_utf8(str)`: 이는 매크로 입력으로 만든 UTF-8 형식 문자열을 HarfBuzz 버퍼에 추가합니다.

첫 번째 코드 줄은

```latex
local str="#1"
```

매우 단순해 보이지만, 실제 동작은 상당히 복잡하므로 조금 더 자세히 살펴볼 가치가 있습니다.

세 번째 코드 줄을 살펴보면

```latex
hbbuffer:add_utf8(str)
```

이는 우리의 `str` HarfBuzz 버퍼에 UTF-8 형식의 유니코드 문자열을 제공할 변수입니다. 이를 위해 변수는 `str` 그 자체로 UTF-8 형식의 유니코드 텍스트를 포함해야 합니다. 따라서 다음과 같은 질문이 생깁니다: *어떻게* LuaHBTeX가 매크로 인수를 “변환”했는가 `"#1"`, 포함하는 `\Uchar` 명령을 Lua 문자열 변수로 `str` HarfBuzz용 UTF-8 텍스트를 포함하도록?

의도한 사용 방식을 살펴보면 `\codestoemoji` 매크로:

```latex
\codestoemoji{\Uchar"1F3F4\Uchar"E0067\Uchar"E0062\Uchar"E0065\Uchar"E006E\Uchar"E0067\Uchar"E007F}
```

다음과 같은 입력은 `\Uchar"1F3F4\Uchar"E0067\Uchar"E0062\Uchar"E0065...`, UTF-8로 인코딩된 이모지 문자 시퀀스와는 전혀 닮지 않았습니다. 더구나 HarfBuzz는 TeX 명령에 대해 전혀 알지 못합니다. 어떻게든 다음으로 구성된 원시 TeX 입력은 `\Uchar` 명령은 HarfBuzz가 사용할 수 있는 UTF-8로 인코딩된 유니코드 문자로 변환되지만, *어떻게*?

답은 다음의 동작에 있습니다 `\Uchar` 명령: 호출을 시도하면 `\codestoemoji` 사용하여 `\char` 대신 `\Uchar` 실패하지만, *왜일까요*?

**\directlua에서의 \Uchar 확장**

다음이 `\codestoemoji` 매크로가 호출되면, `\directlua` 매크로 정의 안에 저장된 명령은 LuaHBTeX에 내장된 Lua 인터프리터로 보낼 Lua 코드를 준비해야 합니다. 해당 코드 준비 과정의 일부는 매크로 정의에 있는 원래 Lua 코드에 포함된 모든 TeX/LaTeX 명령을 확장하고, 사용자가 제공한 모든 매크로 인수를 확장하는 것입니다. 이 확장 과정은 이후 다시 텍스트로 변환되는 토큰 목록을 생성하여 Lua 인터프리터에 전달할 Lua 코드를 만듭니다. 편의를 위해 Overleaf 문서의 다이어그램을 재현합니다 [이해하기 `\directlua`](/latex/ko/in-depth-articles/09-an-introduction-to-luatex-part-2-understanding-directlua.md):

![\directlua의 작동 방식](/files/ea65a2a04afa720748b17438a121e147174afd02)

매크로 `\codestoemoji` 는 다음을 사용하여 호출하도록 의도되었습니다 `\Uchar` 명령 및, [앞서 문서에서 언급했듯이](#the-key-difference-expansion), `\Uchar` 는 확장 가능한 명령으로, 그 확장은 문자 토큰을 생성합니다. 다음의 처리 과정 내에서 `\directlua`, LuaHBTeX는 각각의 `\Uchar<문자 코드>` 명령을 해당 위치에서 확장하며 *제거합니다* 각각의 `\Uchar<문자 코드>` 를 입력에서 제거하고 *대체합니다* 이를 해당 확장 값, 즉 다음을 나타내는 문자 토큰으로 `<문자 코드>`.

처리의 최종 단계에서, 다음이 생성한 초기 토큰 목록은 `\directlua` 변환됩니다 *다시 텍스트로* Lua 인터프리터로 전달될 Lua 코드가 되도록(위 다이어그램 참조). 다음의 확장으로 생성된 모든 문자 토큰도 `\Uchar` 또한 *다시 텍스트로 변환됩니다*: 문자 토큰을 텍스트로 변환하면 원래 `<문자 코드>` 값의 UTF-8 표현이 생성됩니다.

이 예제에서는 Lua 코드가 생성되어 Lua 인터프리터에 준비될 때쯤, "#1"에 대한 매크로 입력은 UTF-8 텍스트 시퀀스로 변환되었습니다. 즉, `str` 변수는 이제 HarfBuzz 버퍼에 안전하게 추가할 수 있는 UTF-8 텍스트 문자열입니다.

**\char는 왜 작동하지 않나요?**

직접적인 답은 다음 때문입니다 `\char` 는 *작동하지* 확장 가능한 명령입니다. 다음과 달리 `\Uchar` 명령과 달리, `\char` 명령은 *제거되지 않습니다* 의 초기 처리 중 입력에서 제거되어 토큰 목록을 생성하지 않으며, 구성 중인 토큰 목록에 포함되도록 “그대로 통과”합니다 `\directlua`의 초기 처리 중 입력에서 제거되어 토큰 목록을 생성하지 않으며, 구성 중인 토큰 목록에 포함되도록 “그대로 통과”합니다 `\directlua`. 예를 들어, 다음의 인수가 `\codestoemoji` 포함하고 있었다면 `\char"1F3F4` LuaHBTeX는 이를 토큰 시퀀스로 변환하고 생성 중인 전체 토큰 목록의 일부로 저장합니다.

다음 처리 단계에서 토큰을 다시 텍스트로 변환하면, 결과 Lua 코드에는 *리터럴 문자열* `\char"1F3F4` 이(가) 변수 정의에 사용된 텍스트 안에 포함됩니다 `str`. 다음의 내용이 `str` HarfBuzz 버퍼에 추가되면, 이모지 문자 "1F3F4를 나타내는 UTF-8 인코딩 시퀀스를 포함하지 않고 리터럴 문자열 `\char"1F3F4`, 을 포함하게 됩니다. HarfBuzz는 이를 셰이핑하려 시도하며, 여기서의 목적상 이모지 글리프를 생성하지 않을 것입니다. 덧붙여 문자열 `\char"1F3F4` 도 “긴 대괄호 문자열”로 생성되지 않는 한 Lua 구문 오류를 일으킵니다. 다음을 참조하세요 [Lua 이스케이프 시퀀스란 무엇인가요](/latex/ko/in-depth-articles/09-an-introduction-to-luatex-part-2-understanding-directlua.md#what-are-e2809clua-escape-sequencese2809d3f) 에서 해당 문제의 배경을 확인할 수 있습니다.

다음을 사용하려 하면 `\codestoemoji` 를 다음과 함께 `\char` 명령, 다음과 같이:

```latex
\codestoemoji{\char"1F3F4\Uchar"E0067\Uchar"E0062\Uchar"E0065\Uchar"E006E\Uchar"E0067\Uchar"E007F}
```

LuaHBTeX는 실패하고 다음과 비슷한 구문 오류를 보고합니다:

```latex
[\directlua]:1: '"\c' 근처에 잘못된 이스케이프 시퀀스가 있습니다.
\codestoemoji ...ing \includegraphics }.}]]) end }

l.75 ...r"E0065\Uchar"E006E\Uchar"E0067\Uchar"E007F}

Lua 인터프리터에 문제가 발생했으므로,
이 Lua 청크의 나머지 부분은 무시됩니다.
```

#### HarfBuzz 셰이핑 함수 호출

**버퍼 매개변수 설정**

HarfBuzz는 때때로 셰이핑하도록 요청받은 텍스트에 관한 추가 정보를 필요로 합니다. 다음을 구성하여 해당 정보를 제공할 수 있습니다 `<버퍼 변수>` 사용하여 *버퍼 메서드*, 예:

* `<버퍼 변수>:set_direction(*HarfBuzz 방향*)`;
* `<버퍼 변수>:set_language(*HarfBuzz 언어*)`;
* `<버퍼 변수>:set_script(*HarfBuzz 스크립트*)`.

예를 들어, 이모지 텍스트의 방향이 왼쪽에서 오른쪽임을 HarfBuzz에 알려야 합니다. 이를 위해 `set_direction()` 메서드를 다음에 사용합니다 `<버퍼 변수>` (이름은 `hbbuffer`)이며, 다음과 같이 작성합니다:

```latex
hbbuffer:set_direction(hblib.Direction.new("ltr"))
```

여기서 `hblib.Direction.new("ltr")` 는 Lua를 통해 HarfBuzz 엔진에 전달하기 적합한 “방향 객체”를 생성합니다.

**셰이핑 수행**

버퍼가 적절히 초기화된 후, 다음 함수를 통해 HarfBuzz에 실제 셰이핑을 요청할 수 있습니다 `shape_full()`. 이 예제에서는 다음과 같이 작성합니다:

```latex
local res = hblib.shape_full(hbfont, hbbuffer, {},{})
```

다음의 세 번째 및 네 번째 매개변수는 `shape_full()` 함수는 Lua 테이블이어야 합니다. 두 매개변수 모두에 빈 테이블 “`{}`”을 사용했습니다. 다음의 일반 형식은 `shape_full()` 다음과 같습니다:

```latex
shape_full(Harfbuzz 글꼴, Harfbuzz 버퍼, {글꼴 기능}, {"셰이퍼"}
```

* **`{"셰이퍼"}`**: 일반적으로 설정할 필요는 없지만 옵션은 다음과 같습니다 `{"ot"}` 또는 `{"graphite2"}`. “셰이퍼” 개념에 대한 자세한 정보는 다음에서 찾을 수 있습니다 [HarfBuzz 문서](https://harfbuzz.github.io/shaping-and-shape-plans.html)—이는 Lua 기반 `luaharfbuzz` 바인딩(구현)이 아닌 저수준 C API를 문서화한 것입니다.
* **`{글꼴 기능}`**: 셰이핑 중 HarfBuzz가 적용하기를 원하는, 글꼴에서 지원하는 [OpenType 기능](https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist)—글꼴에서 지원되는— 기능을 나열하는 테이블입니다.

사용하려는 모든 글꼴 기능은 다음을 사용하여 생성해야 합니다 `luaharfbuzz` 라이브러리 함수

```latex
library_instance.Feature.new(feature_string)
```

여기서

* `library_instance` 는 여러분의 `luaharfbuzz` 라이브러리 인스턴스 변수입니다 (`hblib` 이 예제에서는);
* `feature_string` 은 다음을 사용합니다 [기능을 정의하는 구문](https://github.com/ufytex/luaharfbuzz/wiki/Feature-Strings). 그 예로는 `+smcp` 소형 대문자를 활성화하거나 `-kern` 커닝을 비활성화하는 것이 있습니다.

예를 들어:

```latex
local dosmcp = hblib.Feature.new("+smcp")
local nokern = hblib.Feature.new("-kern")
% 다음과 같이 글꼴 기능을 사용하세요
local res = hblib.shape_full(hbfont, hbbuffer, {dosmcp,nokern},{})
```

#### 결과 접근: 글리프 가져오기

마지막으로 셰이핑 작업이 성공하면, 셰이핑된 글리프가 버퍼 변수에 반환됩니다 `hbbuffer` 코드 앞부분에서 생성한 것입니다.

버퍼 메서드를 통해 글리프에 접근합니다 `get_glyphs()` 그리고 루프를 사용하여 각 개별 글리프를 가져옵니다. 글리프를 보유하는 Lua 테이블인 `hbglyphs` 는 이 예제에서 0이 아니라 1부터 인덱싱됩니다.

각 글리프의 *글리프 식별자* (혼동스럽게도 `codepoint`라고 불림), 그리고 HarfBuzz 글꼴 (`hbfont`)은 다음으로 전달됩니다 `writePNGglyph()` 함수는 해당 글리프의 글꼴 래스터 이미지 표현을 사용하여 PNG 파일을 생성합니다.

`writePNGglyph()` 는 PNG 파일을 작성하고 PNG 파일 이름을 반환하며, 이 이름은 다음을 통해 (크기 조정된) PNG 파일을 LaTeX 문서로 가져오는 데 사용됩니다 `\includegraphics[scale=0.75]{<fname>}`. 다음을 사용할 수 있다는 점에 유의하세요 `\includegraphics` Lua 코드 내에서 직접 사용할 수 있습니다.

```latex
if (res) then
    local hbglyphs=hbbuffer:get_glyphs()
    % 글리프 테이블 hbglyphs는 1부터 시작합니다
    local i = 1
    while hbglyphs[i] \noexpand~= nil do
        local glyph = hbglyphs[i]
        i = i + 1
        local fname=writePNGglyph(hbfont, glyph.codepoint)
        % 가져온 PNG 이미지의 크기를 줄입니다
        local s = 0.75
        local scal="[scale="..tostring(s).."]"
        tex.print([[\noexpand\includegraphics]]..scal..[[{]]..fname..[[}]])
     end
end
```

### Overleaf에서 열 수 있는 전체 코드

```latex
\documentclass{article}
\usepackage{graphicx}
\begin{document}
\directlua{

% LuaHBTeX에서 luaharfbuzz 라이브러리 불러오기
hblib=require("luaharfbuzz")

% Overleaf 서버에서 Noto Color Emoji 글꼴 찾기
pathtofontfile=kpse.find_file("NotoColorEmoji.ttf","truetype fonts")

% Noto Color Emoji에서 HarfBuzz face와 HarfBuzz font 생성
hbface = hblib.Face.new(pathtofontfile)
hbfont = hblib.Font.new(hbface)

% 이 함수는 글꼴과 glyph ID를 인수로 받습니다:
% 글리프의 PNG 데이터를 추출하여 작성합니다
% 이를 .png 파일로 기록합니다

function writePNGglyph(hbfontobject, glyphID)

    % 글리프 PNG 데이터 가져오기
    local pngblob=hbfontobject:ot_color_glyph_get_png(glyphID)
    local pngdata=pngblob:get_data()

    % .png 파일 이름 만들기
    local fname="Glyph"..glyphID..".png"

    % .png 파일을 쓰고 파일 이름 반환
    local output = assert(io.open(fname, "wb"))
    output:write(pngdata)
    output:close()

    % \includegraphics가 사용할 파일 이름 반환
    return fname
end
}

\newcommand{\codestoemoji}[1]{%
\directlua{

local str="#1"
local hbbuffer = hblib.Buffer.new()
hbbuffer:add_utf8(str)

hbbuffer:set_direction(hblib.Direction.new("ltr"))
local res = hblib.shape_full(hbfont, hbbuffer, {},{})

if (res) then
    local hbglyphs=hbbuffer:get_glyphs()
    % 글리프 테이블 hbglyphs는 1부터 시작합니다.
    local i = 1
    while hbglyphs[i] \noexpand~= nil do
        local glyph = hbglyphs[i]
        i = i + 1
        local fname=writePNGglyph(hbfont, glyph.codepoint)
        % 가져온 PNG 이미지의 크기를 줄입니다
        local s = 0.75
        local scal="[scale="..tostring(s).."]"
        tex.print([[\noexpand\includegraphics]]..scal..[[{]]..fname..[[}]])
     end
end
}}

오리: \codestoemoji{\Uchar"1F986}

깃발: \codestoemoji{\Uchar"1F3F4\Uchar"E0067\Uchar"E0062\Uchar"E0065\Uchar"E006E\Uchar"E0067\Uchar"E007F}
\end{document}
```

[Overleaf에서 이 luaharfbuzz API 예제를 여세요.](/latex/ko/in-depth-articles/10-an-overview-of-technologies-supporting-the-use-of-colour-emoji-fonts-in-latex.md)

이 예제는 다음 출력을 생성합니다:

![Harfbuzzexample.png](/files/ae5afc0025519a7fec4dacced0ae0695491275a6)

## 보너스 섹션: 이모지 수학으로 재미있게 놀기

가벼운 이야기로 마무리하자면, Overleaf 팀의 한 구성원이 [`이모지` LaTeX 패키지](https://ctan.org/pkg/emoji?lang=en) 를 사용하여 재미있는 예제를 만들었습니다:

```latex
\documentclass{article}
\usepackage{emoji}
\usepackage{unicode-math,fontspec}
\setmainfont{STIX}
\setmathfont{STIX Two Math}
\begin{document}
\newcommand{\emomath}[1]{\text{\emoji{#1}}}
\[
e^{\emomath{droplet} \ln\emomath{smile}}=\emomath{sweat-smile}
\]
\[
e^{\emomath{eye}\emomath{pie}}=-1
\]
\end{document}
```

[Overleaf에서 이 재미있는 예제를 여세요](https://www.overleaf.com/docs?engine=lualatex\&snip_name=Fun+with+emoji+math\&snip=%5Cdocumentclass%7Barticle%7D%0A%5Cusepackage%7Bemoji%7D%0A%5Cusepackage%7Bunicode-math%2Cfontspec%7D%0A%5Csetmainfont%7BSTIX%7D%0A%5Csetmathfont%7BSTIX+Two+Math%7D%0A%5Cbegin%7Bdocument%7D%0A%5Cnewcommand%7B%5Cemomath%7D%5B1%5D%7B%5Ctext%7B%5Cemoji%7B%231%7D%7D%7D%0A%5C%5B%0Ae%5E%7B%5Cemomath%7Bdroplet%7D+%5Cln%5Cemomath%7Bsmile%7D%7D%3D%5Cemomath%7Bsweat-smile%7D%0A%5C%5D%0A%5C%5B%0Ae%5E%7B%5Cemomath%7Beye%7D%5Cemomath%7Bpie%7D%7D%3D-1%0A%5C%5D%0A%5Cend%7Bdocument%7D)

이 예제는 다음 출력을 생성합니다:

![Emojimath2.png](/files/26e9360e01ebb9d122a74f2b4da405da6c718e85)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://overleaf-pro.ayaka.space/latex/ko/in-depth-articles/10-an-overview-of-technologies-supporting-the-use-of-colour-emoji-fonts-in-latex.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
