> 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/vi/bai-viet-chuyen-sau/10-an-overview-of-technologies-supporting-the-use-of-colour-emoji-fonts-in-latex.md).

# Tổng quan về các công nghệ hỗ trợ việc sử dụng phông emoji màu trong LaTeX

## Giới thiệu

Bài viết này cung cấp tổng quan về nhiều [chủ đề nền tảng](#which-topics-do-we-cover) liên quan đến việc sử dụng phông màu OpenType để dàn trang emoji màu trong LaTeX. Chúng tôi đã cố gắng cung cấp nhiều tài liệu, đáp ứng một phổ rộng các mối quan tâm và trình độ chuyên môn. Để bài viết dễ quản lý, phần trình bày của chúng tôi về một số chủ đề đã lược bỏ khá nhiều chi tiết kỹ thuật, nhưng hy vọng vẫn có đủ nội dung để định hướng cho việc bạn khám phá cách dàn trang emoji màu trong LaTeX.

**Cập nhật (tháng 7 năm 2023)**: Bài viết này được đăng lần đầu vào tháng 8 năm 2021 và được sửa đổi vào tháng 7 năm 2023 để cập nhật phần về [Sử dụng các phông màu OpenType dựa trên SVG với LuaHBTeX](#using-svg-based-opentype-color-fonts-with-luahbtex).

### Chúng tôi đề cập đến những chủ đề nào?

Bài viết này bao gồm các chủ đề chung sau:

* Unicode: chuẩn mã hóa emoji thành các ký tự và quy định hành vi dự kiến của chúng trong các ứng dụng xử lý văn bản và dàn trang.
* Phông màu OpenType: các phông chuyên dụng cung cấp cách thể hiện đầy màu sắc các ký tự emoji được hiển thị trong tài liệu LaTeX của bạn.
* Dàn hình văn bản: giới thiệu một thành phần then chốt của việc dàn trang các ngôn ngữ có chữ viết phức tạp và emoji.
* HarfBuzz: thành phần của LuaHBTeX cho phép dàn trang đa ngôn ngữ nâng cao và sử dụng các phông màu OpenType để dàn emoji trong LaTeX.
* Các bộ máy TeX khác nhau: khám phá khả năng hỗ trợ phông màu OpenType của chúng và lựa chọn bộ máy TeX để dùng.
* API HarfBuzz của LuaHBTeX: phần giới thiệu về “phép màu” đằng sau [dàn hình văn bản](#the-concept-of-text-shaping) trong LuaHBTeX.

### Ba cách dàn trang emoji màu

Có thể dàn trang emoji màu bằng LaTeX theo ba phương pháp chính:

1. Sử dụng các công cụ đồ họa LaTeX tiêu chuẩn như TikZ, MetaPost hoặc Asymptote để vẽ emoji.
2. Chèn emoji bằng cách sử dụng các hình ảnh emoji đã chuẩn bị sẵn được lưu trong các tệp bên ngoài.
3. Xem emoji như văn bản được mã hóa bằng Unicode và sử dụng [dàn hình văn bản](#the-concept-of-text-shaping) với [các phông màu OpenType](#opentype-color-fonts) để dàn trang chúng.

Các tùy chọn thực tế để đưa emoji màu vào tài liệu LaTeX của bạn phụ thuộc vào bộ máy TeX được dùng để biên dịch tài liệu đó: tức là, bạn đang dùng:

* pdfLaTeX: bộ máy pdfTeX + LaTeX;
* XeLaTeX: bộ máy XeTeX + LaTeX;
* LuaLaTeX: bộ máy LuaHBTeX (bắt đầu từ TeX Live 2020) + LaTeX.

Cả ba bộ máy TeX này đều có thể dùng các công cụ hoặc gói LaTeX để vẽ emoji hoặc dùng `\includegraphics{...}` để chèn emoji được lưu trong các tệp đồ họa bên ngoài. Vẽ hoặc nhập đồ họa là những kỹ thuật lý tưởng để dàn emoji khi bạn cần một giải pháp không phụ thuộc vào bộ máy TeX dùng để biên dịch tài liệu LaTeX.

Tuy nhiên, nếu quy trình làm việc của bạn cho phép linh hoạt lựa chọn một bộ máy TeX cụ thể, và bạn muốn sử dụng phông màu OpenType cùng xử lý văn bản dựa trên Unicode, thì phiên bản mới nhất của LuaTeX, gọi là LuaHBTeX, chính là thứ bạn cần. Bắt đầu từ TeX Live 2020, LuaHBTeX được dùng để biên dịch các tài liệu LaTeX dựa trên định dạng LuaLaTeX.

## Bối cảnh về Unicode và các ký tự emoji

### Mã hóa ký tự

Máy tính lưu trữ, truyền và xử lý văn bản bằng một chuỗi các giá trị số (số nguyên) biểu thị các *ký tự*. Xử lý văn bản đáng tin cậy đòi hỏi bên tạo và bên nhận văn bản phải thống nhất những giá trị số nguyên nào được dùng để biểu diễn từng ký tự trong luồng văn bản. Nói cách khác, đó là *ký tự* *mã hóa của văn bản đó là gì?* Một bảng mã là tập hợp các giá trị số nguyên đã được thỏa thuận để biểu diễn một tập hợp ký tự nhất định: mỗi ký tự được biểu diễn bằng một giá trị số nguyên trong bảng mã đang dùng.

### Sự xuất hiện của Unicode

Trong lịch sử, vào thời kỳ văn bản 8-bit, có rất nhiều bảng mã ký tự khác nhau được dùng, luôn kéo theo nguy cơ *không khớp mã hóa*: bên tạo và bên nhận văn bản giả định nhầm các bảng mã khác nhau, dẫn đến lỗi xử lý văn bản. Bất kỳ ai đã làm việc với TeX/LaTeX trong vài năm đều có thể đã gặp tình trạng không khớp mã hóa giữa văn bản đầu vào và phông chữ dùng để dàn trang tài liệu. Nếu phông của tài liệu được cấu hình dùng một bảng mã khác với bảng mã của văn bản, rất có thể sẽ dẫn đến thiếu ký tự hoặc ký tự sai trong tệp PDF đã dàn trang.

Những vấn đề mã hóa lịch sử này có thể được giải quyết bằng một tiêu chuẩn quốc tế mã hóa toàn bộ các ký tự trên thế giới: Unicode. Tiêu chuẩn Unicode không tĩnh mà được cập nhật định kỳ để bổ sung thêm các ký tự và hệ chữ viết mới vào lược đồ mã hóa của nó. Có một [quy trình xem xét chính thức để đề xuất các ký tự mới](http://www.unicode.org/pending/proposals.html) với một [lược đồ riêng cho các ký tự emoji mới](https://www.unicode.org/emoji/proposals.html).

### Có bao nhiêu ký tự Unicode?

Unicode mã hóa tối đa về mặt lý thuyết là 1.114.112 ký tự. Mỗi trong số 1.114.112 giá trị số nguyên này được gọi là một *mã điểm*: giá trị số nguyên được gán để nhận diện từng ký tự. Tuy nhiên, vì nhiều lý do kỹ thuật, chỉ có [1.112.064 mã điểm](https://en.wikipedia.org/wiki/Unicode#Architecture_and_terminology) có thể được gán cho các ký tự thực tế: 2048 mã điểm là không thể gán và bị cấm sử dụng trong văn bản tuân thủ Unicode.

Tại thời điểm viết bài (phiên bản đầu tiên của bài viết này), Phiên bản 13 của tiêu chuẩn Unicode đã phân bổ tổng cộng 143.859 mã điểm cho các ký tự thực tế, bao gồm [3304 ký tự hiện được mã hóa thành emoji](https://www.unicode.org/L2/L2020/20114r-family-emoji-explor.pdf) (xem trang 2 của tài liệu đó). Sự tăng lên về số lượng ký tự được Unicode mã hóa được trình bày rất hay trong bài viết [Có bao nhiêu ký tự Unicode?](https://www.babelstone.co.uk/Unicode/HowMany.html) và trong một [mục Wikipedia](https://en.wikipedia.org/wiki/Unicode#Versions).

### Các mặt phẳng Unicode

Toàn bộ tập hợp 1.114.112 mã điểm Unicode được nhóm thành 17 cái gọi là Mặt phẳng: Mặt phẳng 0 đến Mặt phẳng 16, mỗi mặt phẳng chứa 65536 giá trị mã điểm, tạo thành tổng cộng $$17\times2^{16} = 1,114,112$$ ký tự. Mặt phẳng 0, được gọi là [Mặt phẳng Đa ngôn ngữ Cơ bản](https://en.wikipedia.org/wiki/Plane_\(Unicode\)#Basic_Multilingual_Plane), mã hóa các ký tự được dùng phổ biến nhất. Các Mặt phẳng 1–16 được gọi là [Các Mặt phẳng Bổ sung](http://unicode.org/glossary/#supplementary_planes).

### Sự trỗi dậy của emoji

Các ký tự mới xuất hiện cùng với những thay đổi trong cách con người giao tiếp, và công nghệ điện thoại di động đã làm nảy sinh một tập hợp ký tự như vậy: emoji, vốn phát triển ở Nhật Bản vào cuối những năm 1990. Không có gì ngạc nhiên khi [Câu hỏi thường gặp của Unicode về Emoji](https://unicode.org/faq/emoji_dingbats.html) ghi chú

> “Từ emoji xuất phát từ tiếng Nhật [絵](http://www.unicode.org/cgi-bin/GetUnihanData.pl?codepoint=%E7%B5%B5) (e ≅ hình ảnh) + [文字](http://www.unicode.org/cgi-bin/GetUnihanData.pl?codepoint=%E6%96%87) (moji ≅ ký tự viết).”

Độc giả quan tâm đến bối cảnh và sự phát triển lịch sử của emoji có thể thấy hữu ích ở bài viết này [phần giới thiệu về Unicode](https://unicode.org/reports/tr51/#Introduction) hoặc bài viết [I second that emoji: Các tiêu chuẩn, cấu trúc và sự sản xuất emoji trong xã hội](https://firstmonday.org/ojs/index.php/fm/article/view/9381).

Mãi đến năm 2010, với việc phát hành [phiên bản 6.0 của Tiêu chuẩn Unicode](https://www.unicode.org/versions/Unicode6.0.0/), nhiều emoji mới được chính thức công nhận là *ký tự* tự thân chúng. Unicode 13.0 mã hóa [3304 ký tự thành emoji](https://www.unicode.org/L2/L2020/20114r-family-emoji-explor.pdf) (xem trang 2 của tài liệu đó), trong khi Unicode 13.1 liệt kê [liệt kê 3521 emoji](https://unicode.org/emoji/charts/emoji-counts.html).

### Emoji sống trên một mặt phẳng cao hơn

Unicode đã gán nhiều ký tự emoji cho các mã điểm nằm ngoài Mặt phẳng Đa ngôn ngữ Cơ bản (BMP), được mã hóa [trong Mặt phẳng 1](https://en.wikibooks.org/wiki/Unicode/Character_reference/1F000-1FFFF) với các mã điểm trong khoảng 1F000–1FFFF—điều này có hệ quả quan trọng đối với bất kỳ ai muốn *sao chép và dán* các ký tự emoji vào các trình soạn thảo của Overleaf (Code Editor hoặc Visual Editor). Trình soạn thảo văn bản của Overleaf chỉ có thể xử lý các ký tự nằm trong Mặt phẳng Đa ngôn ngữ Cơ bản, mặc dù chúng tôi hy vọng các bản nâng cấp tương lai sẽ bổ sung hỗ trợ cho các ký tự ngoài BMP. Lưu ý rằng hạn chế này chỉ ảnh hưởng đến các ký tự ngoài BMP trong văn bản được dán vào các tệp sẽ được chỉnh sửa qua các trình soạn thảo của Overleaf. Có những cách khác để truy cập các ký tự emoji:

* Sử dụng các lệnh nguyên thủy `\char\"<code point>` hoặc `\Uchar\"<code point>` (xem [phần này](#optional-detail-luatexluahbtex-char-vs-uchar) của bài viết).
* Sử dụng các tệp văn bản đầu vào chứa các ký tự emoji ở định dạng UTF-8.
* Sử dụng các lệnh LaTeX (macro) chèn các ký tự emoji.

#### Dán emoji và các ký tự ngoài BMP khác vào Overleaf

Nếu bạn dán một ký tự emoji, ví dụ 😀, vào Overleaf Code Editor, hiện tại nó sẽ được chuyển thành các ký tự ��.

![Lỗi do sao chép + dán các ký tự ngoài BMP vào trình soạn thảo Overleaf](/files/494cba098c47db4c2802a4a40349b6dd97907e9c)

Ký tự � có mã điểm Unicode FFFD và tên chính thức là REPLACEMENT CHARACTER, được dùng để “[thay thế một ký tự không xác định, không được nhận diện hoặc không thể biểu diễn](https://en.wikipedia.org/wiki/Specials_\(Unicode_block\))”.

### Sử dụng các mã điểm Unicode (U+) trong LuaLaTeX

Tài liệu Unicode biểu diễn các giá trị mã điểm bằng ký hiệu `U+<hexadecimal value>`—chẳng hạn như `U+1F600`, trong đó `1F600` là `<hexadecimal value>` của mã điểm Unicode cho ký tự emoji 😀. Để dùng các giá trị mã điểm này trong LuaLaTeX, bạn xóa `U+` và viết `\char\"<hexadecimal value>` hoặc `\Uchar\"<hexadecimal value>`. Gói `"` ký tự cho bộ máy TeX biết rằng số được cung cấp được chỉ định ở hệ thập lục phân. Ví dụ, để dùng emoji 😀, bạn sẽ viết `\char\"1F600` hoặc `\Uchar\"1F600`—với một phông chữ có khả năng dàn trang nó.

Một ví dụ LuaLaTeX tối giản sử dụng `\char` và `\Uchar` để dàn trang ký tự emoji 😀 có thể là:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\emojifont[Renderer=Harfbuzz]{NotoColorEmoji.ttf}
%Sử dụng \emojifont trong một nhóm để giữ ảnh hưởng cục bộ
{\emojifont
\Uchar\"1F600
\char\"1F600}
\end{document}
```

[Mở ví dụ LuaLaTeX này trong 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)

**(chi tiết tùy chọn) LuaTeX/LuaHBTeX: \char so với \Uchar**

Ngoài lệnh thông thường `\char<character code>` để dàn trang một `<character code>`, bằng phông hiện tại, các bộ máy LuaTeX, LuaHBTeX và XeTeX còn cung cấp `\Uchar<character code>` lệnh. Từ góc nhìn người dùng, kết quả của `\char` và `\Uchar` trông giống nhau, nhưng có một khác biệt tinh tế trong cách các lệnh này hoạt động, như chúng tôi lưu ý bên dưới.

**Khác biệt chính: mở rộng**

`\Uchar` là một cái gọi là [lệnh có thể mở rộng](/latex/vi/bai-viet-chuyen-sau/22-how-does-expandafter-work-the-meaning-of-expansion.md#expansion-a-general-term-for-a-set-of-operations) trong khi `\char` không thể mở rộng. Khi một `\char<character code>` hoặc `\Uchar<character code>` lệnh đang được “thực thi”—tức là lệnh không được lưu trữ như một phần của macro hay danh sách token khác—các hành động sau diễn ra bên trong bộ máy TeX:

* **`\char<character code>`** ra lệnh cho bộ máy TeX chèn ngay một token ký tự, đại diện cho `<character code>`ký tự đó, vào bất kỳ phần nội dung nào nó đang dàn trang.
* Ngược lại, **`\Uchar<character code>`** có hai bước xử lý riêng biệt:

1. Tính năng `\Uchar<character code>` lệnh được *mở rộng*là `<character code>` , và [token ký tự](/latex/vi/bai-viet-chuyen-sau/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md#tex-tokens-101-28and-notions-of-expansion29) được chuyển thành một danh sách token tạm thời chứa một `<character code>`.
2. mà đại diện cho *cung cấp* cho bộ máy TeX làm nguồn của đầu vào tiếp theo. Về bản chất, bộ máy TeX “tạm thời chuyển hướng nhìn” để dùng danh sách một token đó làm vị trí của mục nhập kế tiếp (token). Theo mặc định, bộ máy TeX chỉ đơn giản quay lại để đọc (nhập) token đó và dàn trang ký tự tương ứng, tái hiện hành vi của `\char` lệnh này. **Tuy nhiên**, vì token đó `<character code>` không được dàn trang ngay lập tức, mà được tạm thời *được lưu trữ* (lưu) dưới dạng một token đơn lẻ, nên các lệnh nguyên thủy của TeX hoặc macro LaTeX có thể tận dụng (hấp thụ) token đó—nó không nhất thiết phải được dàn trang ngay mà có thể được dùng trong các xử lý tiếp theo khi cần.

Về bản chất, `\char<character code>` nói “hãy dàn trang `<character code>` này ngay bây giờ,” trong khi `\Uchar<character code>` có một dạng “hành động trì hoãn” bằng cách tạo ra một token ký tự được lưu trữ và làm cho nó sẵn sàng như mục nhập đầu vào kế tiếp (một token). Token đó có thể được dùng (hấp thụ) bởi các lệnh và macro TeX hoặc được bộ máy TeX đọc lại và dàn trang.

### Unicode (mã hóa) chưa phải là toàn bộ câu chuyện

Khả năng sử dụng các ký tự emoji trong văn bản được mã hóa bằng Unicode chỉ là một phần của câu chuyện thành công của emoji. Sự bùng nổ việc sử dụng emoji cũng được thúc đẩy bởi những tiến bộ trong [công nghệ phông OpenType](https://learn.microsoft.com/en-us/typography/opentype/)—những phông mà dữ liệu glyph (thiết kế ký tự) có thể chứa [dữ liệu màu](https://learn.microsoft.com/en-us/typography/opentype/spec/colr): cái gọi là [các phông màu OpenType](#opentype-color-fonts).

Ngoài các phông chữ phù hợp, việc sử dụng emoji màu còn đòi hỏi các thành phần phần mềm bổ sung với nhiệm vụ gồm:

* tiền xử lý (“[dàn hình](#the-concept-of-text-shaping)”) văn bản được mã hóa Unicode, *chuẩn bị* nó để hiển thị bằng một phông cụ thể;
* *kết xuất và hiển thị* các glyph emoji đầy màu sắc của một phông *glyph* lên màn hình của thiết bị.

#### Glyph so với ký tự: chẳng phải chúng là cùng một thứ sao?

Các thuật ngữ “glyph” và “ký tự” thường được dùng như thể có thể thay thế cho nhau—cùng chỉ một khái niệm cơ bản—nhưng thực ra có một khác biệt tinh tế, dù quan trọng, trong ý nghĩa của chúng.

Unicode [định nghĩa thuật ngữ “ký tự”](http://www.unicode.org/glossary/#character) là:

> “Thành phần nhỏ nhất của ngôn ngữ viết có giá trị ngữ nghĩa; chỉ ý nghĩa trừu tượng và/hoặc hình dạng, chứ không phải một hình dạng cụ thể… ”

Ngược lại, một “glyph” là một *cụ thể* *dạng* (thiết kế) *biểu diễn trực quan* của một *ký tự*.

Vấn đề ký tự so với glyph có thể dễ dàng quan sát khi văn bản chứa nhiều emoji được xem trên các hệ thống/nền tảng phần mềm khác nhau, chẳng hạn đọc cùng một văn bản trên điện thoại di động chạy iOS hay Android hoặc máy tính để bàn Windows của bạn. Dù dùng thiết bị hay nền tảng nào, văn bản nền tảng (chuỗi ký tự) vẫn sẽ chứa cùng một *emoji* *ký tự*. Chính các khả năng đặc thù của thiết bị tham gia vào *tiền xử lý* văn bản đó rồi *kết xuất* và *hiển thị* kết quả, có thể sử dụng các phông đặc thù của thiết bị, từ đó tạo ra các glyph khác nhau (thiết kế ký tự khác nhau) để biểu diễn cùng một ký tự emoji.

Của Unicode [Danh sách Emoji đầy đủ](https://unicode.org/emoji/charts/full-emoji-list.html) cung cấp các hình ảnh mẫu đại diện cho từng ký tự emoji Unicode—minh họa các glyph khác nhau được các nhà cung cấp công nghệ khác nhau sử dụng. Không chỉ các nhà thiết kế phông chữ áp dụng những thiết kế riêng của mình (glyphs) để biểu diễn các ký tự emoji, mà từng phông cũng khác nhau về số lượng ký tự emoji mà chúng hỗ trợ (có glyph cho) và có thể có hoặc không có các tính năng xử lý văn bản emoji nâng cao hơn được nêu trong các đặc tả emoji của Unicode.

Khái niệm và ý niệm về ký tự, ngữ nghĩa và mã hóa của chúng, tạo nên nền tảng cho thế giới Unicode: nó xử lý ký tự. Thiết kế và biểu diễn trực quan của từng ký tự dưới dạng glyph thuộc về công nghệ phông chữ và nghệ thuật thiết kế phông.

#### Emoji Unicode: nhiều hơn rất nhiều so với mã hóa văn bản

Vai trò cốt lõi của Unicode là cung cấp một tiêu chuẩn mã hóa toàn cầu xác định giá trị số nguyên nào, gọi là một *mã điểm,* nên được dùng để biểu diễn từng ký tự, bao gồm emoji, trong một luồng văn bản được mã hóa Unicode.

Đặc tả của Unicode cho emoji cũng xác định *các hành vi xử lý* đối với một số *chuỗi* các ký tự emoji xuất hiện trong một luồng văn bản được mã hóa Unicode. Các chuỗi emoji đã được định nghĩa có thể được “hợp nhất” thông qua một quá trình gọi là [dàn hình văn bản](#the-concept-of-text-shaping) để tạo ra một glyph emoji kết quả duy nhất (“hợp thành”)—glyph đơn đó sẽ được hệ điều hành của thiết bị dùng để biểu diễn chuỗi ký tự gốc có trong văn bản.

Báo cáo Kỹ thuật của Unicode về [Emoji Unicode](https://unicode.org/reports/tr51/) ghi lại bộ tính năng phong phú có sẵn cho phần mềm muốn cung cấp xử lý emoji tuân thủ Unicode. Ví dụ, Unicode định nghĩa (mã hóa) các ký tự gọi là [bộ sửa đổi emoji](http://www.unicode.org/reports/tr51/#Emoji_Modifiers_Table) có thể dùng để tạo ra *các biến thể* của các ký tự emoji “cơ sở”, chẳng hạn các biến thể về [màu da dựa trên thang Fitzpatrick](http://www.unicode.org/reports/tr51/#Diversity). Lưu ý rằng tập các ký tự emoji cơ sở và các bộ sửa đổi áp dụng được định nghĩa là một phần của tổng thể [tiêu chuẩn emoji Unicode](http://www.unicode.org/reports/tr51).

Trang Unicode [Chuỗi Emoji](http://unicode.org/emoji/charts/emoji-sequences.html) cung cấp bảng các chuỗi hiện được đặc tả Unicode cung cấp. Di chuyển con trỏ chuột qua bất kỳ hình glyph emoji nào để thấy một chú giải nổi nhỏ cho bạn biết chuỗi ký tự emoji Unicode nền tảng tạo ra glyph đó:

![EmojiSequenceChart.png](/files/0639d094bc8c8e8351fafd7b03314ebc41282620)

Ví dụ, glyph emoji:

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

được liệt kê trong [phần chuỗi bộ sửa đổi](http://unicode.org/emoji/charts/emoji-sequences.html#modifier_sequences) và được tạo ra bởi chuỗi hai ký tự U+1F44B U+1F3FD. Các ký tự thành phần đó là:

U+1F44B:![UnicodeWavingHandDefault.png](/files/f853b76069aa6bae7b455c197d70a5ba95514d0e) (BÀN TAY VẪY)

U+1F3FD:![FitzPatrick3.png](/files/24cbe71a20e8a2638c0f7c7c88f215fbd83e80fd) (BỘ SỬA ĐỔI EMOJI FITZPATRICK LOẠI-4)

**Sử dụng các bộ sửa đổi tông màu da trong LuaHBTeX**

Ví dụ sau dùng LuaHBTeX để minh họa việc sử dụng các bộ sửa đổi emoji:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\emojifont[Renderer=HarfBuzz,SizeFeatures={Size=20}]{NotoColorEmoji.ttf}
Tay vẫy riêng lẻ: {\emojifont\Uchar"1F44B}\par
Bộ sửa đổi riêng lẻ: {\emojifont\Uchar"1F3FD}\par
Kết quả kết hợp: {\emojifont\Uchar"1F44B\Uchar"1F3FD}
\end{document}
```

[Mở ví dụ bộ sửa đổi emoji LuaLaTeX này trong 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)

Ví dụ này tạo ra kết quả sau:

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

#### UTF-8: vai trò của nó trong việc lưu trữ văn bản Unicode

Bất kỳ văn bản hay mã nào bạn gõ hoặc dán vào Code Editor (hoặc Visual Editor) của Overleaf sẽ được lưu ở định dạng UTF-8, vì vậy chúng ta sẽ xem lướt qua UTF-8 thực sự có nghĩa là gì. UTF là viết tắt của Unicode Transformation Format, và vai trò của UTF-8 trong việc lưu trữ hoặc truyền tải văn bản được mã hóa Unicode được thể hiện qua cụm từ “Transformation *Format*.”

Các giá trị mã điểm của Unicode trải từ 0 đến tối đa 1.114.111, nên không thể biểu diễn mọi giá trị ký tự Unicode chỉ bằng một byte 8-bit duy nhất, vì byte này chỉ lưu được tối đa 256 giá trị khác nhau: từ 0 đến 255. Tuy nhiên, có thể biểu diễn bất kỳ số nguyên mã điểm Unicode nào bằng một *chuỗi liên tiếp* các giá trị cỡ byte—đó là nguyên lý đằng sau UTF-8.

UFT-8 cung cấp một “công thức” để *chuyển đổi* (tức là “mã hóa” hoặc “chuyển đổi”) một giá trị mã điểm Unicode số nguyên thành một chuỗi duy nhất gồm 1 đến 4 số nguyên cỡ byte liên tiếp: số byte liên tiếp cần dùng phụ thuộc vào giá trị của mã điểm. Vì vậy, bạn có thể thấy UTF-8 lưu trữ các ký tự Unicode dưới dạng *các chuỗi đa byte* vì một ký tự Unicode đơn lẻ (số nguyên mã điểm) được biểu diễn trong UTF-8 như một chuỗi từ 1 đến 4 byte liên tiếp.

Dĩ nhiên, văn bản lưu trong UTF-8 có thể được chuyển ngược lại thành chuỗi gốc các giá trị mã điểm Unicode số nguyên—đó là điều XeTeX hoặc LuaTeX/LuaHBTeX phải làm khi đọc một tệp đầu vào LaTeX được lưu ở định dạng UTF-8. Những bộ máy TeX này cần biết các giá trị mã điểm Unicode đầu vào (ký tự) trước khi chúng có thể dàn trang văn bản. Lưu ý rằng pdfTeX không có sẵn khả năng giải mã UTF-8 nên phải dựa vào các macro TeX để xử lý (giải mã) văn bản đầu vào được định dạng UTF-8.

**Một số ví dụ về UTF-8**

* Ký tự  ش của tiếng Ả Rập (“sheen”) có mã điểm Unicode 0634 ở hệ thập lục phân (cơ số 16) hoặc 1588 ở hệ thập phân (cơ số 10). Trong UTF-8, ش được biểu diễn bằng 2 giá trị (thập lục phân) D8 và B4, nên ký tự ش sẽ được lưu dưới dạng hai byte liên tiếp D8B4 trong văn bản mã hóa UTF-8.
* Ký tự emoji 😀 có mã điểm Unicode 1F600 ở hệ thập lục phân (cơ số 16) hoặc 128512 ở hệ thập phân (cơ số 10). Trong UTF-8, 😀 được biểu diễn bằng 4 giá trị (thập lục phân) F0, 9F, 98 và 80, nên ký tự 😀 sẽ được lưu dưới dạng 4 byte liên tiếp F09F9880 trong một tệp văn bản UTF-8.

#### Các ký tự đặc biệt được dùng trong xử lý văn bản emoji dựa trên Unicode

Không phải mọi ký tự được mã hóa trong Unicode đều nhằm mục đích trình bày trực quan qua các glyph trong phông chữ: một số ký tự được mã hóa được chỉ định là *các ký tự không in* mà mục đích của chúng là hỗ trợ các chức năng xử lý văn bản chuyên biệt (trong phần mềm hỗ trợ). Các ứng dụng phần mềm khác nhau cung cấp mức hỗ trợ khác nhau cho các ký tự không in được mã hóa trong Unicode, vì vậy kết quả sẽ phụ thuộc vào môi trường phần mềm—ứng dụng và phông chữ—đang được sử dụng.

**Hai ký tự không in cần biết**

* **Ký tự nối không độ rộng (ZWJ)**, mã điểm 200D (hệ thập lục phân), như tên gọi của nó, được thiết kế để kích hoạt “hành vi nối” của các ký tự đầu vào—nhưng chỉ khi các ký tự đầu vào đó *có* một hành vi nối được xác định.
* **Ký tự không nối không độ rộng (ZWNJ)**, mã điểm 200C (hệ thập lục phân), được thiết kế để *ngăn chặn* ngăn chặn “hành vi nối” mà các ký tự đầu vào lẽ ra có thể thể hiện. Ví dụ, bạn có thể dùng ZWNJ để ngăn hành vi nối của các ký tự Ả Rập liên tiếp vốn bình thường sẽ được xử lý (dàn hình) thành dạng nối của chúng.

Unicode đã công bố danh sách các [Chuỗi ZWJ Emoji được khuyến nghị](https://unicode.org/emoji/charts/emoji-zwj-sequences.html) sử dụng U+200D ZERO WIDTH JOINER (ZWJ) để kết hợp các chuỗi ký tự emoji thành một glyph emoji hợp thành duy nhất—nếu nó có sẵn trong phông chữ đang dùng.

**Ví dụ sử dụng ký tự không nối không độ rộng**

Đoạn mã tối giản sau sử dụng phông OpenType Scheherazade, đi kèm trong TeX Live, để định nghĩa một phông LaTeX có tên là `\arabicfont` mà chúng ta có thể dùng để dàn trang một số văn bản Ả Rập. Dòng

```latex
{\arabicfont Không nối:\textdir TRT\Uchar"0644\Uchar"200C\Uchar"0627}
```

sử dụng một ký tự không nối không độ rộng, thông qua `\Uchar"200C`, để ngăn hành vi nối bình thường của hai chữ cái Ả Rập ل (lam) và ا (alef). Lưu ý việc dùng `\textdir TRT` để đặt hướng văn bản từ phải sang trái:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\arabicfont[Script=Arabic,Renderer=Harfbuzz,SizeFeatures={Size=40}]{Scheherazade}
{\arabicfont Nối:\textdir TRT\Uchar"0644\Uchar"0627}\par
{\arabicfont Không nối:\textdir TRT\Uchar"0644\Uchar"200C\Uchar"0627}
\end{document}
```

[Mở ví dụ LuaLaTeX này trong 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)

Ví dụ này tạo ra kết quả sau:

![NonJoiner.png](/files/951054ce7f69e8474140d3bab68808a74f124c31)

## Khái niệm “dàn hình văn bản”

Hãy bắt đầu với một ví dụ trực quan dùng bản dịch tiếng Urdu của từ “educational.” Văn bản tiếng Urdu đó có thể được gõ trên bàn phím hoặc thiết bị cảm ứng, và sẽ được tạo thành một chuỗi tuyến tính đơn giản của các ký tự Ả Rập Unicode. Tuy nhiên, khi văn bản đó được dàn trang, hoặc hiển thị trên màn hình thiết bị theo [phong cách Nastaliq](https://en.wikipedia.org/wiki/Nastaliq), kết quả là một bố cục glyph hai chiều phức tạp.

Dùng ví dụ tiếng Urdu của chúng ta, hình dưới đây so sánh đầu vào tuyến tính của chữ Ả Rập Unicode *ký tự* với đầu ra được dàn theo phong cách Nastaliq, gồm một bố cục hai chiều của *glyph* có trong phông (miễn phí) [Awami Nastaliq](https://software.sil.org/awami/download/):

![](/files/1ea9f7f8666df1d17ee03d4925d7660c9daa994c)

Quá trình “dịch” các ký tự đầu vào thành một tập glyph đầu ra được đặt đúng vị trí được gọi là *dàn hình văn bản*dàn hình văn bản

Dàn hình văn bản là thiết yếu khi dùng các hệ chữ (hệ thống viết) như [Tiếng Ả Rập](https://en.wikipedia.org/wiki/Arabic), [Tiếng Hebrew](https://en.wikipedia.org/wiki/Hebrew_language), [Devanagari](https://en.wikipedia.org/wiki/Devanagari) hoặc [Tiếng Malayalam](https://en.wikipedia.org/wiki/Malayalam), chỉ là bốn ví dụ về cái gọi là *các hệ chữ phức tạp*. Để bảo đảm trình bày chính xác văn bản trong các hệ chữ đó, và các ngôn ngữ dùng chúng, quá trình dàn hình cần cẩn thận xử lý mọi quy tắc và sắc thái dàn hình có trong tổ hợp hệ chữ và ngôn ngữ cụ thể. Ví dụ, một số ngôn ngữ cần nhiều ký tự đầu vào để tạo ra một glyph đầu ra cụ thể, hoặc có thể có các yêu cầu phức tạp về việc đặt chính xác các dấu phụ, và sắp xếp lại giữa các glyph để bảo đảm từng glyph được đặt đúng vị trí (so với nhau).

Nói chung, việc dàn hình một đoạn văn bản đòi hỏi nhiều thông tin:

* Hệ chữ viết hoặc *hệ chữ* mà văn bản được viết bằng đó.
* Cụ thể *ngôn ngữ* đang được sử dụng. Các chữ viết riêng lẻ có thể được dùng cho nhiều ngôn ngữ, với mỗi tổ hợp chữ viết–ngôn ngữ có những tinh tế/khác biệt riêng trong tạo hình.
* Chữ viết *hướng* của văn bản—chẳng hạn từ phải sang trái hoặc từ trái sang phải.
* Một *phông chữ* mà cung cấp các glyph cần thiết để biểu diễn văn bản đã được tạo hình và, tùy chọn, chứa thêm các “quy tắc tạo hình” hướng dẫn quá trình tạo hình văn bản.

Yêu cầu của việc tạo hình văn bản, đặc biệt đối với các chữ viết phức tạp và các ngôn ngữ liên quan, có thể cực kỳ chi tiết và tinh vi, cho thấy nhu cầu về phần mềm chuyên dụng có thể áp dụng những “quy tắc” tạo hình văn bản rất phức tạp. Không có gì ngạc nhiên, phần mềm như vậy tồn tại và được gọi là một *bộ máy tạo hình văn bản*; bộ mà chúng ta sẽ thảo luận được gọi là [Harfbuzz](https://en.wikipedia.org/wiki/HarfBuzz), mà tài liệu của nó rất đáng đọc—ví dụ [Tại sao tôi cần một bộ máy tạo hình?](https://harfbuzz.github.io/why-do-i-need-a-shaping-engine.html).

**Tài liệu đọc thêm về tạo hình văn bản**

Rất khuyến nghị đọc các phần giới thiệu ngắn sau:

* [Tạo hình văn bản là gì?](https://harfbuzz.github.io/what-is-harfbuzz.html#what-is-text-shaping)
* [Tại sao tôi cần một bộ máy tạo hình?](https://harfbuzz.github.io/why-do-i-need-a-shaping-engine.html)

**Ghi chú TeXnical: nhiều công nghệ tạo hình (mô hình)**

Bộ máy tạo hình văn bản HarfBuzz hỗ trợ một số “công nghệ tạo hình” khác nhau ở cách chúng triển khai quá trình tạo hình—mỗi triển khai được gọi là một *bộ tạo hình*, bao gồm trong `luaotfload` tài liệu. Trọng tâm chính của bài viết này là tạo hình OpenType nhưng một công nghệ thay thế, miễn phí để sử dụng, là [Graphite](https://scripts.sil.org/cms/scripts/page.php?site_id=projects\&item_id=graphite_aboutOT), được phát triển bởi [SIL International](https://www.sil.org/). Một mô hình tạo hình khác được HarfBuzz hỗ trợ là [Apple Advanced Typography (AAT)](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html)—các phông hỗ trợ AAT thường được sử dụng trên các nền tảng công nghệ của Apple.

**Ví dụ sử dụng bộ tạo hình Graphite**

Ví dụ sau dàn trang một số văn bản Urdu bằng một phông có tên là [Awami Nastaliq](https://software.sil.org/awami/download/), phông này hỗ trợ tạo hình Graphite và có sẵn trên Overleaf. Awami Nastaliq được tạo bởi [SIL International](https://www.sil.org/), tổ chức chịu trách nhiệm phát triển công nghệ Graphite.

Ví dụ sau minh họa khả năng tạo hình nâng cao của các phông dựa trên Graphite—hãy chú ý cách `luaotfload` khai báo phông chọn tạo hình Graphite bằng `shaper=graphite2`.

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

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

\vskip 75bp

% Giáo dục
\pardir TRT\textdir TRT \urdutest تعلیمی
\end{document}
```

[Mở ví dụ này trong 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)

Ví dụ này tạo ra kết quả sau:

![](/files/5b653bdcd1243e2d18363b5f07c5a1487a15dd81)

#### Emoji và tạo hình văn bản

Tạo hình văn bản đã được giới thiệu bằng các ví dụ từ một ngôn ngữ chữ viết phức tạp, Urdu. Tuy nhiên, có thể sẽ ngạc nhiên khi biết rằng việc hiển thị đúng glyph emoji đòi hỏi phải áp dụng tạo hình văn bản cho văn bản Unicode chứa các chuỗi ký tự emoji—[như nhà phát triển chính của HarfBuzz đã lưu ý](https://github.com/harfbuzz/harfbuzz/issues/2428#issuecomment-639108677):

> ...việc tạo hình emoji bằng HarfBuzz hoàn toàn nằm trong phạm vi và thực sự cần thiết để có emoji gia đình, màu da, v.v.

Chúng ta sẽ xem các ví dụ về điều này.

### Phân chia trách nhiệm: bộ máy tạo hình văn bản + phông OpenType

Trong thực tế, tạo hình văn bản là một “hoạt động phối hợp,” hay sự phân công lao động, giữa logic và các quy tắc được tích hợp trong bộ máy tạo hình văn bản và các quy tắc cùng dữ liệu tạo hình bổ sung được tích hợp trong phông(s) đang dùng—từ đây trở đi chúng ta đề cập đến tạo hình dựa trên OpenType *chỉ*.

Để thực hiện tạo hình, bộ máy tạo hình văn bản thường được cung cấp một đoạn văn bản Unicode, một chữ viết và ngôn ngữ được chỉ định, có thể cả hướng viết, và quan trọng nhất là một phông OpenType để dùng trong quá trình tạo hình—phông sẽ cung cấp đầu ra: một tập hợp glyph và dữ liệu định vị. Nếu được yêu cầu, bộ tạo hình có thể áp dụng các quy tắc bổ sung ([tính năng OpenType](https://en.wikipedia.org/wiki/List_of_typographic_features#OpenType_typographic_features)) chứa trong phông OpenType đang dùng—những quy tắc nào được áp dụng thường do người dùng chọn từ danh sách tính năng mà phông hỗ trợ.

Kết quả của quá trình tạo hình là một *danh sách glyph* có trong phông OpenType, cùng với *giữa các glyph* dữ liệu định vị. Dữ liệu định vị đó liên quan đến *vị trí tương đối của các glyph đã được tạo hình*; nó không đề cập đến vị trí tuyệt đối trong trang đã dàn trang hoặc các phương tiện/nội dung khác như trang web, Tweet, v.v. Phần mềm kết xuất (bộ máy dàn trang, trình duyệt web, v.v.) sử dụng thông tin định vị giữa các glyph để bảo đảm các glyph được đặt đúng tương đối với nhau sau khi chúng được ghép lại và đưa vào đầu ra cuối cùng.

#### Danh sách glyph là gì?

Bên trong, mỗi glyph trong một phông OpenType được gán một định danh số, một giá trị nguyên gọi là chỉ số glyph—còn gọi là định danh glyph hoặc GID. Sau khi hoàn tất nhiệm vụ tạo hình, bộ máy tạo hình văn bản sẽ trả về kết quả của nó dưới dạng một *danh sách định danh glyph* cộng với *dữ liệu định vị* cho các glyph đó.

Các glyph riêng lẻ trong phông OpenType được người tạo phông gán các chỉ số (định danh), khiến đây là một giá trị rất đặc thù cho từng phông và mang tính tùy ý—nó cũng có thể khác nhau giữa các phiên bản của một phông nhất định. Bạn không bao giờ nên cho rằng cùng một giá trị GID sẽ áp dụng cho các glyph “tương tự” trong những phông khác nhau; gần như chắc chắn là không. Nếu bạn có một danh sách định danh glyph do bộ tạo hình cung cấp, bạn chỉ có thể dùng chúng để truy cập các glyph trong chính phông mà từ đó chúng được lấy ra.

#### Phông OpenType là gì?

Trên web đầy *tràn ngập* các lời giải thích và chi tiết về phông OpenType, vì vậy chúng ta sẽ chỉ giới hạn ở một mô tả ngắn.  [đặc tả OpenType](https://docs.microsoft.com/en-us/typography/opentype/spec/) là một tài liệu phức tạp được thiết kế cho các nhà phát triển, nhưng về bản chất, nó định nghĩa một định dạng tệp, hay vùng chứa, cho dữ liệu phông. Một phông OpenType chứa dữ liệu mô tả hình dạng glyph, cùng với thông tin về các chữ viết và ngôn ngữ được hỗ trợ, siêu dữ liệu về phông, và nhiều “bảng” khác nhau định nghĩa [các tính năng kiểu chữ](https://en.wikipedia.org/wiki/List_of_typographic_features#OpenType_typographic_features) được phông hỗ trợ.

Một bộ máy tạo hình văn bản thường có thể được chỉ định áp dụng có chọn lọc (sử dụng) các tính năng của phông trong quá trình tạo hình, áp dụng các hiệu ứng kiểu chữ cụ thể (“quy tắc”) để chọn đúng bộ glyph có trong phông. Phông được chọn sẽ cần hỗ trợ, và cung cấp các glyph cho, mọi tính năng mà bộ tạo hình văn bản được yêu cầu áp dụng.

#### “Glyph” được mã hóa và chưa mã hóa

Phông OpenType bao gồm một bảng dữ liệu có tên là [cmap](https://docs.microsoft.com/en-us/typography/opentype/spec/cmap) (Character to Glyph Index Mapping) ánh xạ tập hợp các ký tự Unicode, được phông hỗ trợ, tới chỉ số glyph tương ứng trong phông đó. Video sau cung cấp một cái nhìn thoáng qua ngắn về bảng cmap chứa trong một phông có tên là `lmmono10-regiular.otf` (đi kèm trong TeX Live).

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

Tuy nhiên, các phông thường chứa nhiều glyph không biểu diễn một ký tự Unicode cụ thể và không được đưa vào bảng cmap đó. Do đó, tập glyph có trong một phông OpenType có thể được chia thành hai tập chính:

* glyph được mã hóa đại diện cho các ký tự Unicode;
* glyph chưa mã hóa không đại diện cho các ký tự Unicode.

Các glyph được mã hóa có thể được truy cập bằng cách đưa ký tự Unicode thích hợp vào văn bản—nhưng còn các glyph chưa mã hóa thì sao, chúng được dùng/truy cập thế nào? Những glyph này thường được dùng để cung cấp đầu ra của các thao tác tạo hình văn bản, bao gồm việc áp dụng các tính năng của phông để tạo ra các hiệu ứng thị giác/kiểu chữ cụ thể.

### các phông màu OpenType

Các ký tự emoji được kỳ vọng sẽ hiển thị/kết xuất đầy đủ màu sắc—emoji đen trắng không thật sự mang lại “trải nghiệm emoji đầy đủ.” Tuy nhiên, vào thời điểm Unicode mã hóa emoji lần đầu,  [đặc tả phông OpenType](https://docs.microsoft.com/en-us/typography/opentype/spec/) không có bất kỳ cơ chế phù hợp nào để nhúng *colorful*-glyph vào trong phông OpenType. “Khoảng trống” này trong OpenType đã thúc đẩy các nhà cung cấp công nghệ/nền tảng hàng đầu tìm kiếm giải pháp, và cuộc “đua” sau đó đã dẫn tới [nhiều đề xuất mở rộng OpenType](https://www.fontlab.com/news/color-font-format-proposals/) để hỗ trợ phông màu OpenType—không chỉ để hiển thị các ký tự emoji màu (glyph) mà còn để kết xuất bất kỳ glyph nào bằng màu sắc.

#### Bốn biến thể của phông màu OpenType

[Adobe, Microsoft, Google và Apple đều đã gửi các đề xuất](https://www.fontlab.com/news/color-font-format-proposals/) để mở rộng OpenType nhằm hỗ trợ phông OpenType đầy đủ màu sắc và, cuối cùng, bốn đề xuất đã được chấp nhận và đưa vào đặc tả OpenType chính thức. Để tiện, chúng ta có thể nhóm lỏng lẻo bốn biến thể đó thành dựa trên vector và dựa trên raster—nhưng, như được cho thấy trong [kho lưu trữ GitHub](https://github.com/simoncozens/test-fonts), đặc tả OpenType đủ linh hoạt để hỗ trợ các tệp phông màu OpenType kết hợp bốn công nghệ cơ bản đó.

* **Phông OpenType dựa trên vector:**
* **Microsoft**: hình dạng glyph được mô tả bằng một dạng vector màu nhiều lớp ([COLR](https://docs.microsoft.com/en-us/typography/opentype/spec/colr) và [CPAL](https://docs.microsoft.com/en-us/typography/opentype/spec/cpal) các bảng).
* [**Adobe và Mozilla**](https://www.w3.org/2013/10/SVG_in_OpenType/) ([bảng SVG](https://docs.microsoft.com/en-us/typography/opentype/spec/svg)): hình dạng glyph được vẽ bằng SVG, hỗ trợ glyph được xây dựng từ vector *và ảnh raster*. Xem thêm [hướng dẫn sử dụng của Adobe về phông SVG](https://helpx.adobe.com/fonts/user-guide.html/fonts/using/ot-svg-color-fonts.ug.html).
* **Phông OpenType dựa trên raster:**
* **Google**: glyph được biểu diễn bằng các ảnh PNG màu nhúng trong phông ([CBDT](https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt) và [CBLC](https://docs.microsoft.com/en-us/typography/opentype/spec/cblc) các bảng).
* **Apple**: glyph cũng được biểu diễn bằng các ảnh màu nhúng trong phông. Ngoài PNG, cơ chế của Apple ([bảng sbix](https://docs.microsoft.com/en-us/typography/opentype/spec/colr)) cũng hỗ trợ JPEG và TIFF.

Hệ quả là, các hệ điều hành và phần mềm ứng dụng hỗ trợ phông màu OpenType cần xử lý bối cảnh công nghệ hỗn hợp ngày nay. Hơn nữa, bạn nên lưu ý rằng từng phông màu OpenType riêng lẻ—và *các phiên bản* của cùng một phông—sẽ:

* có phạm vi bao phủ khác nhau đối với toàn bộ tập [ký tự emoji Unicode](https://unicode.org/emoji/charts/emoji-list.html)—tức là phông cung cấp glyph cho bao nhiêu ký tự emoji;
* sử dụng các thiết kế glyph khác nhau để biểu diễn từng ký tự emoji;
* khác nhau ở các tính năng họ cung cấp để hỗ trợ các cách sử dụng nâng cao hơn của các tiêu chuẩn Unicode, chẳng hạn như [bộ sửa đổi emoji](https://unicode.org/reports/tr51/#Emoji_Modifiers_Table), và các khả năng xử lý văn bản emoji khác được mô tả trong [Unicode Technical Standard #51: Unicode Emoji](https://unicode.org/reports/tr51/).

#### Sự chú ý dành cho HarfBuzz

Chúng ta đã ám chỉ nhu cầu về một *bộ máy tạo hình văn bản*: phần mềm nhận một đoạn văn bản Unicode đầu vào, được viết bằng một tổ hợp chữ viết và ngôn ngữ cụ thể, và, dùng một phông được chỉ định, tạo hình văn bản đó thành một chuỗi glyph, cùng với dữ liệu định vị, để có thể dùng dàn trang văn bản đầu vào ban đầu.

[HarfBuzz](https://harfbuzz.github.io/) là một bộ máy tạo hình văn bản như vậy: nó là [một thư viện mã nguồn mở](https://github.com/harfbuzz/harfbuzz) và là kết quả của hơn một thập kỷ nghiên cứu và phát triển—và vẫn đang được tiếp tục phát triển và triển khai tích cực như một phần của nhiều sản phẩm phần mềm. Bản thân HarfBuzz không thực hiện “dàn trang” mà cung cấp “dịch vụ tạo hình văn bản” cho phần mềm lựa chọn tích hợp nó, bao gồm XeTeX, LuaHBTeX, [Adobe PhotoShop và Adobe InDesign](https://en.wikipedia.org/wiki/HarfBuzz).

Bằng cách tích hợp HarfBuzz, các bộ máy TeX có thể tận dụng khả năng tạo hình văn bản nâng cao của nó để cung cấp dàn trang đa ngôn ngữ rất tinh vi, đặc biệt đối với các chữ viết phức tạp như Ả Rập, Hebrew, Devanagari và nhiều chữ khác. Cũng lưu ý rằng HarfBuzz được dùng để xử lý và tạo hình các ký tự văn bản emoji Unicode, điều này chúng ta sẽ tìm hiểu chi tiết hơn.

Đồ họa sau tóm tắt vai trò của HarfBuzz khi được tích hợp với phần mềm như XeTeX hoặc LuaHBTeX trong quá trình dàn trang văn bản bằng một chữ viết phức tạp, chẳng hạn như tiếng Ả Rập:

![Tổng quan về tạo hình văn bản tiếng Ả Rập với HarfBuzz](/files/0f22d888cfe327ed3ab50e18c030e3670b2ac39b)

**Khám phá HarfBuzz**

Bất kỳ ai muốn tìm hiểu thêm về HarfBuzz và các dịch vụ tạo hình OpenType mà nó cung cấp cho XeTeX và LuaHBTeX có thể [tải xuống một bản phân phối nhị phân của HarfBuzz](https://github.com/harfbuzz/harfbuzz/releases) chứa thư viện HarfBuzz (dành cho lập trình viên) và các tiện ích dòng lệnh `hb-view` và `hb-shape`.

**Ví dụ: cách dùng hb-view**

Tạo một tệp mới trong trình soạn thảo văn bản hỗ trợ UTF-8 mà bạn thích và sao chép/dán sáu ký tự emoji sau đây 👋👋🏻👋🏼👋🏽👋🏾👋🏿 vào tệp văn bản đó, rồi lưu dưới định dạng UTF-8 vào một tệp có tên, chẳng hạn, `emoji.txt`.

Lưu ý rằng trình soạn thảo văn bản của bạn có thể hiển thị các phiên bản emoji đen trắng (dự phòng) vì nó không thể (được lập trình để) kết xuất glyph màu. Khi 6 emoji đó đã được lưu, tệp `emoji.txt` nên chứa dữ liệu UTF-8 cho chuỗi ký tự emoji Unicode sau đây—chúng tôi đã tách các bộ biến đổi emoji bằng dấu phẩy chỉ để *dễ đọc hơn*:

* `1F44B` để tạo ra 👋
* `1F44B`, `1F3FB` để tạo ra 👋🏻
* `1F44B`, `1F3FC` để tạo ra 👋🏼
* `1F44B`, `1F3FD` để tạo ra 👋🏽
* `1F44B`, `1F3FE` để tạo ra 👋🏾
* `1F44B`, `1F3FF` để tạo ra 👋🏿

Tổng cộng nên có **11** ký tự Unicode, mỗi ký tự tạo ra 4 byte dữ liệu UTF-8, vì vậy kết quả `emoji.txt` tệp nên dài 44 byte, không tính bất kỳ ký hiệu kết thúc dòng nào được dùng ở cuối dòng chứa emoji.

Tính năng `hb-view` tiện ích có thể dùng tệp `emoji.txt`, cùng với một phông màu OpenType phù hợp do bạn chọn, chẳng hạn như `NotoColorEmoji.ttf`, để tạo một tệp SVG của đầu ra đã được tạo hình bởi HarfBuzz. Ví dụ dòng lệnh sau, phải được **gõ trên một dòng** trong terminal của bạn, sẽ tạo ra tệp SVG `emoji.svg`:

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

Khi thực thi thành công, tệp `emoji.svg`, được tạo bởi `hb-view`, có thể mở bằng Inkscape và sẽ trông giống như sau:

![Hbvieemoji.png](/files/c6429573504bbfd3563ce60177e87066cb913650)

`hb-view` có thể được dùng để khám phá việc tạo hình HarfBuzz cho bất kỳ tệp văn bản Unicode và phông OpenType phù hợp nào—nó chắc chắn không chỉ giới hạn ở emoji! Gõ

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

để xem vô số tùy chọn dòng lệnh của tiện ích mạnh mẽ và hữu ích này. Chúc tạo hình vui vẻ!

## Tạo hình văn bản và các bộ máy TeX

Ở đây, chúng ta sẽ xem xét khả năng tạo hình văn bản của XeTeX và họ bộ máy TeX LuaTeX.

### XeTeX

XeTeX được phát triển vào đầu những năm 2000 và tiên phong một số đổi mới trong dàn trang dựa trên TeX, nổi bật nhất là *tích hợp sẵn* hỗ trợ cho:

* đọc văn bản Unicode ở định dạng UTF-8;
* sử dụng phông OpenType;
* tạo hình văn bản cho dàn trang đa ngôn ngữ;
* dàn trang toán học dựa trên OpenType.

Khả năng dàn trang dễ dàng và thuận tiện các ngôn ngữ có chữ viết phức tạp của XeTeX là nhờ các khả năng tạo hình văn bản tích hợp sẵn của nó—ban đầu dựa trên, nay đã lỗi thời, [ICU LayoutEngine](http://userguide.icu-project.org/layoutengine). Nhờ công việc của Khaled Hosny, XeTeX đã chuyển sang dùng HarfBuzz để tạo hình văn bản, như được nêu trong thông báo từ [tháng 3 năm 2013](https://tug.org/pipermail/xetex/2013-March/024118.html). Với bất kỳ ai muốn dàn trang văn bản đa ngôn ngữ, XeTeX thường được xem là bộ máy TeX được lựa chọn—nhưng giờ đây có một lựa chọn khác là LuaHBTeX, mà chúng ta sẽ tìm hiểu.

### LuaTeX và LuaHBTeX

Việc phát triển LuaTeX bắt đầu khoảng năm 2005 nhưng theo một triết lý thiết kế khá khác với XeTeX, vốn tích hợp các tính năng mới *trực tiếp vào* phần mềm XeTeX. Trái với XeTeX, các nhà phát triển LuaTeX chọn “...cung cấp một bộ công cụ tối thiểu và không có giải pháp.” (xem [Reference Manual for LuaTeX](https://www.pragma-ade.com/general/manuals/luatex.pdf)). Thay vì cung cấp một bộ tính năng bổ sung *tích hợp trong* các bộ máy dựa trên LuaTeX, các cơ chế nội bộ của bộ máy LuaTeX được mở ra để các nhà phát triển và người dùng thành thạo có thể tận dụng ngôn ngữ kịch bản Lua tích hợp để xây dựng giải pháp của riêng họ.

Ví dụ, khác với XeTeX, bộ máy LuaTeX không thể *trực tiếp* sử dụng phông OpenType; thay vào đó, phông OpenType phải được tải và “chuẩn bị để sử dụng” thông qua các hàm tải phông được viết bằng mã Lua. Những hàm tải phông đó được gọi là *callback* các hàm: mã Lua mà LuaTeX sẽ gọi (“thực thi”) khi có yêu cầu tải một phông.

Ngoài ra, bộ máy LuaTeX không cung cấp bất kỳ *tích hợp sẵn* khả năng tạo hình văn bản nào—những thứ đó cũng phải do mã bên ngoài cung cấp, mà bộ máy LuaTeX có thể gọi để cung cấp dịch vụ tạo hình văn bản. Một lần nữa, điều này tương phản với bộ máy XeTeX, vốn tích hợp khả năng tạo hình văn bản trong phần mềm lõi.

#### luaotfload: thiết yếu để dùng phông OpenType trong LuaTeX/LuaHBTeX

Cơ chế callback của LuaTeX để tải phông mang lại rất nhiều tính linh hoạt, dù phải trả “cái giá” là lập trình thêm. May mắn cho người dùng LuaLaTeX, cộng đồng TeX đã phát triển một gói có tên là `luaotfload`, vốn là một phần của [bản phát hành hằng năm của TeX Live](https://www.tug.org/texlive/) và dĩ nhiên có sẵn cho người dùng Overleaf.

`luaotfload` là [sẵn trên CTAN](https://ctan.org/pkg/luaotfload?lang=en) và có một [kho lưu trữ phát triển trên GitHub](https://github.com/latex3/luaotfload) nơi bạn có thể theo dõi các phát triển mới nhất và [các bản phát hành mới](https://github.com/latex3/luaotfload/releases).

`luaotfload` có thể được nạp trực tiếp vào phần đầu tài liệu LaTeX qua

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

Lưu ý rằng `luaotfload` là tên của một LaTeX *gói*, nghĩa là nó có tên tệp là `luaotfload.sty`. Nếu bạn muốn dùng `luaotfload` với plain TeX, bạn có thể làm vậy bằng cách thêm dòng

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

vào tài liệu plain TeX của bạn.

Thông thường, người dùng LuaLaTeX—tức là những người dàn trang LaTeX bằng LuaTeX/LuaHBTeX—không cần làm việc trực tiếp với `luaotfload` vì [`fontspec` gói](https://ctan.org/pkg/fontspec) sẽ tải `luaotfload` gói cho bạn, xử lý nhiều chi tiết cấp thấp thông qua các lệnh cấp người dùng do `fontspec` gói.

### LuaHBTeX: các tùy chọn mới cho tạo hình văn bản

`luaotfload` là một thư viện Lua trưởng thành và mạnh mẽ, cung cấp khả năng xử lý phông OpenType của LuaTeX—đồng thời cung cấp dịch vụ tạo hình văn bản cho một loạt ngôn ngữ và chữ viết. Ban đầu, các hàm tạo hình văn bản của `luaotfload` được triển khai hoàn toàn bằng mã Lua, nhưng bản phát hành TeX Live 2020 đã mang đến một lựa chọn chính thống khác cho tạo hình văn bản—một bộ máy mới dựa trên LuaTeX có tên là LuaHBTeX.

“HB” trong LuaHBTeX là viết tắt của HarfBuzz—về cơ bản, LuaHBTeX là bộ máy LuaTeX nguyên bản *cộng với* với một bộ máy tạo hình văn bản HarfBuzz tích hợp. Phù hợp với triết lý thiết kế của LuaTeX, việc có sẵn HarfBuzz không *tự động* bảo đảm rằng văn bản sẽ được LuaHBTeX tạo hình: HarfBuzz là một công cụ khác có thể được dùng để xây dựng các giải pháp tạo hình văn bản.

Việc tích hợp HarfBuzz của LuaHBTeX là [có thể lập trình bằng mã Lua](#introduction-to-the-luahbtex-harfbuzz-api), điều này đã cho phép `luaotfload`các nhà phát triển của nó thêm các giải pháp tạo hình văn bản dựa trên HarfBuzz. Do đó, [bắt đầu từ phiên bản 3.1, phát hành ngày 5 tháng 11 năm 2019](https://github.com/latex3/luaotfload/releases/tag/v3.1), `luaotfload` đã được nâng cấp để tận dụng HarfBuzz—giúp người dùng phổ thông dễ dàng tiếp cận các khả năng tạo hình văn bản của HarfBuzz.

Những độc giả quan tâm đến chi tiết kỹ thuật về việc tích hợp HarfBuzz với LuaTeX có thể đọc [bài báo của Khaled Hosny](https://www.tug.org/TUGboat/tb40-1/tb124hosny-harfbuzz.pdf).

### luaotfload: hai tùy chọn cho tạo hình văn bản (khi nào nên dùng HarfBuzz?)

Người dùng LuaLaTeX hiện có hai tùy chọn cho tạo hình văn bản:

* `luaotfload`cách triển khai tạo hình văn bản nguyên bản (dựa trên nút) của , được viết hoàn toàn bằng Lua;
* `luaotfload`cách tạo hình dựa trên HarfBuzz của , được truy cập bởi mã Lua gọi các hàm tạo hình văn bản của HarfBuzz.

`luaotfload` cung cấp quyền truy cập vào hai hệ thống tạo hình này qua tham số “`mode`” — dù hầu hết người dùng sẽ dùng tùy chọn tương đương `fontspec` “`Renderer`” thay vì trực tiếp dùng các hàm cấp thấp của `luaotfload`.

Mỗi `luaotfload`các giải pháp tạo hình văn bản của đều có điểm mạnh và (hiện tại) điểm yếu riêng, nhưng bạn nên dùng giải pháp nào, và khi nào? Dưới đây là một số điểm cần cân nhắc:

* `luaotfload`Xử lý dựa trên nút nguyên bản của có thể tiêu tốn nhiều bộ nhớ, đặc biệt với các phông OpenType CJK lớn. Dùng HarfBuzz để tạo hình văn bản CJK có thể cải thiện tốc độ và giảm mức sử dụng bộ nhớ.
* Hãy dùng HarfBuzz cho các chữ viết phức tạp vì nó “...cải thiện đáng kể việc hiển thị các chữ viết Ấn Độ và Ả Rập và rất được khuyến nghị cho những chữ viết như vậy.” (xem `luaotfload` sổ tay).
* Việc tích hợp HarfBuzz vào `luaotfload` vẫn còn khá mới và đang tiếp tục được phát triển. Tại thời điểm viết bài (tháng 7 năm 2021), nên dùng tạo hình tích hợp sẵn của luaotfload (đặt `mode=node`) cho các phông chính của tài liệu, đặc biệt nếu tài liệu của bạn dùng chữ Latinh. Xem [vấn đề GitHub](https://github.com/latex3/luaotfload/issues/175#issue-801120377), nơi tóm tắt các vấn đề và thảo luận. Nếu bạn muốn thử nghiệm, bạn có thể dùng `luaotfload` để nạp một tệp phông và tạo hai phông LaTeX: một phông dùng tạo hình dựa trên HarfBuzz và phông kia dùng tạo hình dựa trên Lua. Overleaf đã tạo một [dự án mẫu](#sample-project-arabic-shaping), minh họa điều này.
* Đừng dùng HarfBuzz để xử lý các phông toán học. Như các nhà phát triển trên tex.stackexchange đã thảo luận, HarfBuzz [không được thiết kế để xử lý phông cho dàn trang toán học](https://tex.stackexchange.com/questions/544881/does-luahbtex-with-harfbuzz-renderer-completely-supports-math-formating) vì vậy đừng dùng nó cho mục đích đó.

**Dự án mẫu: tạo hình tiếng Ả Rập**

Đây là một dự án Overleaf sử dụng một số kiểu chữ Ả Rập chất lượng cao để so sánh `luaotfload`các dịch vụ tạo hình văn bản dựa trên nút của (`mode=node`) với của HarfBuzz (`mode=harf`):

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

Dự án này bao gồm đầu ra được hiển thị trong hình sau:

![Dàn trang tiếng Ả Rập](/files/4dcc04201b81f8230c8e9adcccf4f0ff53fe92bf)

### Chọn “Renderer” trong fontspec

Như đã lưu ý trong [tài liệu](https://mirror.ox.ac.uk/sites/ctan.org/macros/unicodetex/latex/fontspec/fontspec.pdf), `fontspec` “...cho phép người dùng XeTeX hoặc LuaTeX nạp phông OpenType trong một tài liệu LaTeX”. Nếu bạn dùng các bộ máy LuaTeX hoặc LuaHBTeX, `fontspec` sẽ tải `luaotfload` thư viện cho bạn và, ngoài ra, cung cấp một bộ lệnh thuận tiện ở cấp người dùng giúp giảm bớt nhu cầu phải làm việc với `luaotfload`các chức năng cấp thấp của

Vậy làm sao để chọn giữa tạo hình của HarfBuzz hoặc tạo hình tích hợp sẵn do `luaotfload`? Câu trả lời nằm trong tài liệu tuyệt vời [`fontspec` tài liệu](https://mirror.ox.ac.uk/sites/ctan.org/macros/unicodetex/latex/fontspec/fontspec.pdf), cụ thể là Phần VI: các tính năng phông chỉ dành cho LuaTeX. `fontspec` cung cấp một thiết lập gọi là `Renderer` có thể được đặt khi phông được định nghĩa qua `fontspec`. `Renderer` điều khiển xử lý cấp thấp của phông. Hai tùy chọn đáng quan tâm là

* `Renderer = Node`: “mode” mặc định để dàn trang phông OpenType—nó sử dụng `luaotfload`các hàm tạo hình văn bản của được triển khai hoàn toàn bằng Lua.
* `Renderer = Harfbuzz`: “mode” này định nghĩa/tải phông để dùng với bộ máy tạo hình văn bản HarfBuzz. `luaotfload` sử dụng API của LuaHBTeX để gọi các hàm trong HarfBuzz.

Để biết thêm thông tin, xem [`fontspec` tài liệu](https://mirror.ox.ac.uk/sites/ctan.org/macros/unicodetex/latex/fontspec/fontspec.pdf).

## Các bộ máy TeX, HarfBuzz và emoji màu

Mặc dù XeTeX và LuaHBTeX đều tích hợp HarfBuzz, chúng cung cấp các mức hỗ trợ khác nhau cho một số tính năng nâng cao hơn của HarfBuzz—đặc biệt là việc nạp và sử dụng phông màu OpenType.

### XeTeX và phông màu OpenType

Như đã lưu ý, có hai loại phông màu OpenType dựa trên định dạng dữ liệu dùng để lưu glyph của phông: dựa trên vector và dựa trên raster.

#### XeTeX và phông màu OpenType dựa trên raster

XeTeX không thể nạp các phông màu OpenType dựa trên raster—chẳng hạn như của Google [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) được đi kèm với TeX Live 2020. Ví dụ, nếu bạn cố tải Noto Color Emoji (NotoColorEmoji.ttf), XeLaTeX sẽ thất bại với một thông báo lỗi có thể gây hiểu nhầm rằng Noto Color Emoji “không thể được tìm thấy”. Mã LaTeX sau đây, được dàn trang bằng XeLaTeX, *không hoạt động*:

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

[Mở mã XeLaTeX này trong Overleaf (nó ***không*** hoạt động).](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)

Nó thất bại với lỗi:

```
! Lỗi Package fontspec: Phông chữ "NotoColorEmoji" không thể được tìm thấy.
```

Tương tự, một ví dụ Plain TeX đơn giản được xử lý bởi XeTeX cũng thất bại

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

[Mở ví dụ Plain TeX (XeTeX) này trong Overleaf (nó ***không*** hoạt động).](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)

Ví dụ Plain TeX báo một thông báo lỗi tương tự, nhưng khác:

```
! Font \emojifont=[NotoColorEmoji.ttf] at 12.0pt không thể nạp: tệp Metric (TFM) fil
e hoặc phông chữ đã cài đặt không được tìm thấy.
l.1 \font\emojifont="[NotoColorEmoji.ttf]" at 12pt

Tôi không thể đọc dữ liệu kích thước của phông chữ này,
nên tôi sẽ bỏ qua chỉ định phông chữ.
[Các pháp sư có thể sửa tệp TFM bằng TFtoPL/PLtoTF.]
Bạn có thể thử chèn một chỉ định phông chữ khác;
ví dụ, gõ `I\font<same font id>=<substitute font name>'.
```

**Ví dụ Plain LuaHBTeX**

Để so sánh, đây là một ví dụ Plain TeX tối giản được biên dịch bằng LuaHBTeX

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

[Mở ví dụ Plain TeX (LuaHBTeX) này trong Overleaf (nó biên dịch thành công).](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)

#### Nguyên nhân thực sự khiến XeTeX thất bại

Các thông báo lỗi do XeTeX đưa ra phần nào che khuất nguyên nhân thực sự của vấn đề: các phông màu OpenType, đặc biệt là các biến thể dựa trên raster, là *không* được XeTeX hỗ trợ. Trên thực tế, XeTeX (Kpathsea) có thể *tìm thấy* phông Noto Color Emoji, nhưng XeTeX không thể hoàn toàn *nạp* phông đó và không thể khởi tạo các bảng dữ liệu phông nội bộ cần thiết để dùng phông đó cho việc dàn trang. Bên trong, XeTeX *bắt đầu* quá trình nạp phông và kiểm tra nó về “khả năng co giãn” (theo định nghĩa “khả năng co giãn” của FreeType), nhưng phép kiểm tra đó thất bại, và XeTeX đưa ra một thông báo lỗi chuẩn của bộ máy TeX, có phần gây hiểu nhầm.

**Ghi chú kỹ thuật**

Việc XeTeX xử lý NotoColorEmoji.ttf đã được khảo sát bằng cách biên dịch một phiên bản gỡ lỗi của tệp thực thi XeTeX. IDE Eclipse đã được dùng để đặt điểm dừng trên hàm XeTeX `creatFontFromFile(filename, index, pointsize)`, sau đó bước qua mã để quan sát quá trình xử lý tiếp theo.

#### XeTeX và các phông màu OpenType dựa trên vector

XeTeX có thể *nạp* các phông màu OpenType dựa trên vector nhưng sẽ không tạo ra emoji màu trong tệp PDF kết quả—nếu XeTeX tạo ra được PDF nào đó. Không giống LuaTeX, LuaHBTeX và pdfTeX, XeTeX không *trực tiếp* xuất tài liệu đã dàn trang ở định dạng PDF. Thay vào đó, XeTeX xuất ra một tệp trung gian `.xdv` (e**x**tended **dv**i) có định dạng tệp được chuyển đổi sang PDF bởi tiện ích có tên `xdvipdfmx`. Tại thời điểm viết bài, `xdvipdfmx` không thể nhúng dữ liệu glyph emoji màu thích hợp vào PDF, vì vậy, ở mức tốt nhất, bạn sẽ chỉ thấy emoji đơn sắc—kết quả “dự phòng”—trong PDF, hoặc thậm chí không thấy gì cả, tùy thuộc vào phông được dùng.

Đây là một ví dụ XeLaTeX sử dụng phông màu OpenType [TwemojiMozilla.ttf](https://ctan.org/tex-archive/fonts/twemoji-colr), có sẵn trong TeX Live. TwemojiMozilla.ttf sử dụng định dạng vector COLR/CPAL của Microsoft để lưu trữ các glyph màu và được đi kèm với TeX Live 2020. Trong ví dụ này, XeTeX có thể nạp phông, tạo ra một `.xdv` và tệp PDF, nhưng glyph emoji không xuất hiện trong PDF đã dàn trang:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\emojifont{TwemojiMozilla.ttf}
\newcommand{\smiley}{{\emojifont\char"1F600}}
Đây là một mặt cười: \smiley
\end{document}
```

[Mở mã XeLaTeX này trong Overleaf (nó KHÔNG hoạt động).](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)

Ngược lại, mã trên hoạt động với LuaLaTeX nếu định nghĩa `\emojifont` sử dụng `fontspec` thiết lập `[Renderer=HarfBuzz]`:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\emojifont{TwemojiMozilla.ttf}[Renderer=HarfBuzz]
\newcommand{\smiley}{{\emojifont\char"1F600}}
Đây là một mặt cười: \smiley
\end{document}
```

[Mở mã LuaLaTeX này trong Overleaf (nó hoạt động).](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 và các phông màu OpenType

Thông qua bộ máy định hình HarfBuzz tích hợp và `luaoftload` thư viện, LuaHBTeX hỗ trợ cả bốn biến thể của phông màu OpenType. Người dùng LuaLaTeX có thể tận dụng tối đa việc xử lý dựa trên Unicode đối với văn bản chứa ký tự emoji hoặc đơn giản là làm đẹp tài liệu của mình bằng văn bản rất nhiều màu sắc nhờ các phông màu OpenType.

Như đã lưu ý trước đó, bốn biến thể của phông màu OpenType có thể được phân thành hai nhóm:

* những phông chứa glyph ở định dạng ảnh raster, chẳng hạn PNG;
* những phông khác dùng định dạng vector SVG hoặc cơ chế COLR/CPAL của Microsoft.

Các định dạng glyph dựa trên vector có ưu điểm về khả năng co giãn: tạo ra đồ họa glyph sắc nét ở mọi cỡ điểm.

**Sử dụng các phông màu COLR/CPAL của Microsoft với LuaHBTeX**

Nếu bạn muốn dùng định dạng vector cho các phông emoji màu OpenType của mình, hãy xem phông [TwemojiMozilla.ttf](https://ctan.org/tex-archive/fonts/twemoji-colr?lang=en), dựa trên định dạng COLR/CPAL của Microsoft. TwemojiMozilla.ttf được bao gồm trong TeX Live, nhưng bạn có thể lấy phiên bản mới nhất từ [kho lưu trữ GitHub](https://github.com/mozilla/twemoji-colr/releases) và tải nó lên dự án Overleaf của bạn.

Đây là một ví dụ nhỏ, `fontspec`-dựa trên, sử dụng `Renderer=Harfbuzz`, với kết quả dàn trang là một con vịt emoji (vector) lớn:

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

[Mở ví dụ LuaLaTeX này để dàn trang một con vịt vector.](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)

Đây là con vịt (vector) do ví dụ trên tạo ra:

![](/files/3780e8bd0dd977788278915c7aaf01718b03f503)

#### Sử dụng các phông màu OpenType dựa trên SVG với LuaHBTeX

Tại thời điểm viết bản cập nhật bài viết này (tháng 7 năm 2023), có rất ít tài liệu chính thức về việc sử dụng phông màu OpenType kiểu SVG với LuaLaTeX. Một số [bình luận trong các thảo luận trực tuyến](https://github.com/latex3/luaotfload/issues/96) gợi ý dùng `fontspec`của `RawFeature`, như minh họa trong giả mã dưới đây. Hãy thay `*tên tệp phông SVG của bạn ở đây*` bằng tên của một tệp phông dựa trên SVG mà mã LaTeX của bạn có thể truy cập:

```latex
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\newfontfamily\emoji[RawFeature={+svg},SizeFeatures={Size=20}]{your SVG font file name here}
\emoji Emoji của bạn ở đây...
\end{document}
```

Nếu bạn bỏ qua `fontspec` và nạp `luaotfload` trực tiếp, bạn có thể cần khai báo và chỉ định một phông theo cách sau—các thử nghiệm của chúng tôi cho thấy bạn cần bỏ qua `mode=harf` tùy chọn này để nó hoạt động:

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

**Một vài lưu ý thận trọng**

Bạn đọc quan tâm đến việc dùng các phông màu OpenType kiểu SVG nên lưu ý:

* Các phông OpenType kiểu SVG chứa số lượng lớn glyph có thể [tốn nhiều tài nguyên tính toán đối với LuaLaTeX](#processing-svg-glyph-data) để xử lý, có khả năng dẫn đến [hết thời gian chờ của Overleaf](/latex/vi/co-so-tri-thuc/038-fixing-and-preventing-compile-timeouts.md).
* Sự hỗ trợ của LuaLaTeX đối với các phông này có thể được [xem là thử nghiệm](https://github.com/latex3/luaotfload/issues/96#issuecomment-530317399): kết quả có thể khác nhau tùy theo bản phát hành TeX Live mà dự án của bạn sử dụng; vì vậy, nên thử nghiệm và tiến hành thận trọng.

**Xử lý dữ liệu glyph SVG**

SVG cho phép các nhà thiết kế tạo ra những thiết kế phức tạp và nhiều màu sắc đại diện cho các glyph của phông—tuân theo một số hạn chế của SVG [được tài liệu hóa trong đặc tả OpenType](https://docs.microsoft.com/en-us/typography/opentype/spec/svg). Tuy nhiên, các bộ máy TeX, bao gồm LuaHBTeX, không thể nhập trực tiếp (sử dụng) các tệp hoặc dữ liệu SVG—chẳng hạn dữ liệu SVG dùng để mô tả hình dạng glyph trong các phông màu OpenType kiểu SVG. Dữ liệu SVG của một glyph phải được chuyển sang định dạng PDF vì LuaHBTeX có thể dùng định dạng đó để dàn trang glyph và tạo ra tài liệu PDF cuối cùng. Việc chuyển đổi SVG sang PDF đó được xử lý bởi mã Lua trong `luaoftload`: dữ liệu SVG của mỗi glyph được trích xuất từ tệp phông, lưu vào một tệp tạm thời `.svg` và chuyển đổi sang PDF bằng Inkscape qua dòng lệnh của nó. Việc trích xuất dữ liệu SVG và chuyển sang PDF tạo ra một số chi phí xử lý, dẫn đến thời gian biên dịch tài liệu có thể khá lâu—đặc biệt là các tài liệu dùng phông SVG lớn chứa hàng nghìn glyph emoji.

#### Các phông màu OpenType dựa trên raster

**Sử dụng định dạng phông màu OpenType CBDT/CBLC của Google với LuaHBTeX**

[Noto Color Emoji](https://fonts.google.com/noto/specimen/Noto+Color+Emoji) là một phông màu OpenType được bao gồm trong TeX Live, giúp dễ dùng trong một dự án Overleaf. Vì Noto Color Emoji sử dụng đồ họa định dạng PNG để biểu diễn các glyph emoji, chúng ta có thể dùng nó để dàn trang một emoji vịt lớn (raster)—như được minh họa bởi ví dụ sau. Lưu ý lại rằng `fontspec` khai báo phông (`\emojifont`) sử dụng `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}
```

[Mở ví dụ LuaLaTeX này để dàn trang một con vịt raster.](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)

Đây là con vịt raster do ví dụ trên tạo ra:

![Một emoji vịt raster được LaTeX dàn trang](/files/67f0fb23a28783dd440d2d0fda257a1e7b2064fd)

Nếu bạn cố dùng `NotoColorEmoji.ttf` nhưng bỏ qua `[Renderer=Harfbuzz]` từ gói `fontspec` khai báo, LuaHBTeX sẽ thất bại và đưa ra thông báo lỗi khi nó cố ghi ra tệp PDF:

```latex
! lỗi:  (tệp /usr/local/texlive/2020/texmf-dist/fonts/truetype/google/noto-em
oji/NotoColorEmoji.ttf) (ttf): không tìm thấy bảng loca
```

Nguyên nhân của lỗi này trong [bảng loca](https://docs.microsoft.com/en-us/typography/opentype/spec/loca) là [được giải thích trên GitHub](https://github.com/latex3/luaotfload/issues/98#issuecomment-531610153).

**Sử dụng định dạng phông màu OpenType sbix của Apple với LuaHBTeX**

Các kiểm thử ngoại tuyến cho thấy LuaHBTeX hỗ trợ `sbix` biến thể phông màu OpenType, nhưng tại thời điểm viết bài này, chúng tôi chưa thể tìm được một `sbix`-biến thể phông emoji màu để minh họa việc dàn trang một con vịt. Vui lòng [liên hệ với chúng tôi](https://www.overleaf.com/contact) nếu bạn biết một phông như vậy, và chúng tôi sẽ cập nhật bài viết này nhanh chóng để dùng nó.

## Giới thiệu về API HarfBuzz của LuaHBTeX

![Db.gif](/files/28f5f222a369c77dd7efd194fc659493d508c827) ![Db.gif](/files/28f5f222a369c77dd7efd194fc659493d508c827)

Định hình văn bản, đặc biệt đối với các ngôn ngữ có hệ chữ phức tạp, và cả emoji, vốn là một nhiệm vụ khó khăn về bản chất nên, không ngạc nhiên, HarfBuzz là một thư viện tinh vi có thể khó làm việc cùng—trừ khi bạn đã quen với các thao tác định hình văn bản. Trong phần cuối này, chúng ta sẽ xem xét sự tích hợp HarfBuzz của LuaHBTeX và cách truy cập nó thông qua mã Lua trong `\directlua`.

Ví dụ của chúng tôi sử dụng mã khá cơ bản để minh họa API HarfBuzz của LuaHBTeX. Nó hơi gượng ép, không đạt chất lượng mức sản xuất, cũng không thực tế lắm vì mục đích duy nhất là giới thiệu một vài ý tưởng cốt lõi. Chúng tôi đã chia mã Lua thành hai `\directlua` khối: khối đầu tiên tải `luaharfbuzz` thư viện và tạo một số biến toàn cục mà chúng ta sẽ dùng trong khối thứ hai `\directlua` khối, nơi chúng ta định nghĩa một macro có tên `\codestoemoji`.

Có vẻ phù hợp khi lặp lại việc Knuth dùng dấu ngoặt nguy hiểm kép (hình ảnh do [trang web này](http://www.truetex.com/db.htm)) vì nội dung này khá ở mức thấp và “nhìn vào bên trong”—mặc dù chúng tôi hy vọng nó có thể hữu ích cho những độc giả can đảm hơn. Sự tích hợp HarfBuzz của LuaHBTeX được lấy từ [dự án luaharfbuzz trên GitHub](https://github.com/ufyTeX/luaharfbuzz/wiki#projects-using-luaharfbuzz) nơi bạn có thể tìm thấy một [phần giới thiệu về dự án](https://github.com/ufyTeX/luaharfbuzz/wiki) cùng với một [danh sách API của luaharfbuzz](http://ufytex.github.io/luaharfbuzz/).

### Các bước đầu tiên: tải thư viện luaharfbuzz và tìm một phông

Để dùng API HarfBuzz của LuaHBTeX, trước hết chúng ta cần tải thư viện (module) có tên `luaharfbuzz`, được tích hợp sẵn trong LuaHBTeX, và lưu bảng trả về vào một biến (toàn cục) mà chúng ta sẽ gọi là `hblib`:

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

Tiếp theo, chúng ta cần xác định vị trí một phông màu OpenType emoji phù hợp: chúng ta sẽ dùng Noto Color Emoji—lưu ý rằng chúng ta đang khá lười và không làm bất kỳ kiểm tra lỗi nào trong trường hợp không tìm thấy nó! Để tìm nó, chúng ta sẽ dùng `kpse` thư viện (Kpathsea), vốn cũng là một phần của LuaTeX/LuaHBTeX:

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

Bây giờ chúng ta đã có quyền truy cập vào thư viện HarfBuzz, thông qua biến `hblib`, và đường dẫn đến một phông phù hợp (`pathtofontfile`), chúng ta có thể bắt đầu dùng `hblib`. `\directlua` khối mã nơi chúng ta định nghĩa macro của mình.

```latex
%Create HarfBuzz face and HarfBuzz font from Noto Color Emoji
hbface = hblib.Face.new(pathtofontfile)
hbfont = hblib.Font.new(hbface)
```

#### Phông HarfBuzz và mặt phông HarfBuzz: chúng là gì?

Một [Đối tượng HarfBuzz face](https://harfbuzz.github.io/fonts-and-faces.html) đại diện cho một kiểu chữ được nạp từ tệp phông, nhưng chưa đặt các tham số cụ thể (như kích thước). Một [đối tượng phông HarfBuzz](https://harfbuzz.github.io/fonts-and-faces.html) đại diện cho một *thể hiện cụ thể* của một HarfBuzz face; do đó, nhiều đối tượng phông HarfBuzz khác nhau có thể được suy ra từ một HarfBuzz face duy nhất: mỗi phông HarfBuzz có thể có các thuộc tính của mình, như kích thước, được đặt thành các giá trị khác nhau. Một HarfBuzz face là một mức trừu tượng cao hơn một phông HarfBuzz.

### Sử dụng glyph của phông để tạo tệp PNG

Phần cuối của khối `\directlua` đầu tiên là một hàm có tên `writePNGglyph(hbfontobject, glyphID)` mà chúng ta dùng để minh họa rằng một số phông màu OpenType, chẳng hạn Noto Color Emoji, dùng đồ họa PNG để biểu diễn các glyph emoji mà nó chứa.

Hàm này dùng API HarfBuzz của LuaHBTeX để trích xuất dữ liệu PNG từ các glyph và ghi dữ liệu đó vào một `.png` tệp có tên `Graphics<glyphID>.png`. Tên của tệp đó `.png` được trả về để dùng bởi `\includegraphics` để nhúng các ảnh glyph PNG vào PDF đã dàn trang của chúng ta.

Với `writePNGglyph(hbfontobject, glyphID)` đã có sẵn, khối mã đầu tiên của chúng ta `\directlua` trông như sau:

```latex
\directlua{

% Load the luaharfbuzz library from LuaHBTeX
hblib=require("luaharfbuzz")

% Locate the Noto Color Emoji font on Overleaf’s server
pathtofontfile=kpse.find_file("NotoColorEmoji.ttf","truetype fonts")

% Create HarfBuzz face and HarfBuzz font from Noto Color Emoji
hbface = hblib.Face.new(pathtofontfile)
hbfont = hblib.Font.new(hbface)

% This function accepts a font and a glyph ID:
% it extracts the glyphs' PNG data and writes
% it out to a .png file

function writePNGglyph(hbfontobject, glyphID)

    % Get glyph PNG data
    local pngblob=hbfontobject:ot_color_glyph_get_png(glyphID)
    local pngdata=pngblob:get_data()

    % Construct a file name for our .png file
    local fname="Glyph"..glyphID..".png"

    % Write the .png file and return the file name
    local output = assert(io.open(fname, "wb"))
    output:write(pngdata)
    output:close()

    % Return the file name for \includegraphics to use
    return fname
end
}
```

### Khối \directlua thứ hai: tạo macro \codestoemoji

Mục tiêu là định nghĩa một macro `\codestoemoji` mà chúng ta có thể gọi với một đoạn văn bản chứa các mã ký tự emoji mà chúng ta muốn HarfBuzz định hình. Cụ thể, chúng ta sẽ dùng `\Uchar<character code>` để biểu diễn từng ký tự emoji; ví dụ:

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

Có rất nhiều thứ diễn ra trong định nghĩa của `\codestoemoji` mà chúng ta sẽ giải thích bên dưới, nhưng định nghĩa trông như sau:

```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, {},{})

nếu (res) thì
    local hbglyphs=hbbuffer:get_glyphs()
    % Bảng glyph, hbglyphs, được đánh số từ 1
    local i = 1
    while hbglyphs[i] \noexpand~= nil do
        local glyph = hbglyphs[i]
        i = i + 1
        local fname=writePNGglyph(hbfont, glyph.codepoint)
        % Giảm kích thước các ảnh PNG được nhập vào của chúng ta
        local s = 0.75
        local scal="[scale="..tostring(s).."]"
        tex.print([[\noexpand\includegraphics]]..scal..[[{]]..fname..[[}]])
     end
end
}}
```

#### Hiểu định nghĩa của macro \codestoemoji

Tính năng `\codestoemoji` macro chủ yếu là mã Lua nằm trong `\directlua`, vì vậy nếu bạn muốn biết thêm về *cách* `\directlua` cách nó hoạt động, hãy xem bài viết Overleaf [Hiểu `\directlua`](/latex/vi/bai-viet-chuyen-sau/09-an-introduction-to-luatex-part-2-understanding-directlua.md). Nó giải thích cách LuaTeX và LuaHBTeX xử lý `\directlua` khi các lệnh TeX/LaTeX được đưa vào mã Lua và, đặc biệt, sự cần thiết phải dùng `\noexpand` và `\unexpanded`.

**Xử lý tham số macro: "#1"**

Macro bắt đầu bằng ba dòng sau:

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

thực hiện các nhiệm vụ sau:

* `local str="#1"`: điều này tạo ra một chuỗi Lua từ đầu vào được macro truyền vào;
* `local hbbuffer = hblib.Buffer.new()`: điều này dùng API HarfBuzz để tạo một bộ đệm chứa văn bản mà chúng ta muốn HarfBuzz định hình;
* `hbbuffer:add_utf8(str)`: điều này thêm một chuỗi ở định dạng UTF-8, được tạo từ đầu vào của macro, vào bộ đệm HarfBuzz.

Dòng mã đầu tiên

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

trông khá đơn giản nhưng hoạt động của nó liên quan đến khá nhiều sự phức tạp, đáng để tìm hiểu chi tiết hơn một chút.

Nếu chúng ta xem xét dòng mã thứ ba

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

chúng ta thấy nó dùng `str` để cung cấp cho bộ đệm HarfBuzz một chuỗi Unicode được định dạng UTF-8. Để điều đó hoạt động, biến `str` bản thân nó phải chứa văn bản Unicode được định dạng UTF-8; vì vậy câu hỏi đặt ra là: *cách* liệu LuaHBTeX có “chuyển đổi” đối số macro `"#1"`, chứa `\Uchar` các lệnh, thành biến chuỗi Lua `str` chứa văn bản UTF-8 cho HarfBuzz không?

Nếu chúng ta nhìn vào cách dự kiến sử dụng `\codestoemoji` macro:

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

đầu vào, chẳng hạn `\Uchar"1F3F4\Uchar"E0067\Uchar"E0062\Uchar"E0065...`, trông chẳng giống gì một chuỗi các ký tự emoji được mã hóa UTF-8. Hơn nữa, HarfBuzz không biết gì về các lệnh TeX. Bằng cách nào đó, đầu vào TeX thô gồm `\Uchar` các lệnh được biến đổi thành các ký tự Unicode được mã hóa UTF-8 mà HarfBuzz có thể dùng, nhưng *cách*?

Câu trả lời nằm ở hành vi của `\Uchar` lệnh: cố gắng gọi `\codestoemoji` bằng cách dùng `\char` thay vì `\Uchar` sẽ thất bại, nhưng *vì sao*?

**\Uchar: sự mở rộng trong \directlua**

Khi `\codestoemoji` macro được gọi, lệnh `\directlua` lệnh, được lưu trong định nghĩa macro, phải chuẩn bị mã Lua để gửi tới trình thông dịch Lua tích hợp sẵn của LuaHBTeX. Một phần của quá trình chuẩn bị mã đó là mở rộng bất kỳ lệnh TeX/LaTeX nào có trong mã Lua gốc trong định nghĩa macro, cùng với việc mở rộng bất kỳ đối số macro nào do người dùng cung cấp. Quá trình mở rộng đó tạo ra một danh sách token, sau đó được chuyển ngược lại thành văn bản, tạo ra mã Lua để chuyển cho trình thông dịch Lua. Để thuận tiện, chúng tôi tái hiện một sơ đồ từ bài viết Overleaf [Hiểu `\directlua`](/latex/vi/bai-viet-chuyen-sau/09-an-introduction-to-luatex-part-2-understanding-directlua.md):

![Cơ chế của \directlua](/files/41d3948bd8d9f6ff8e590101ae0224883941736f)

Macro `\codestoemoji` được dự định gọi bằng `\Uchar` các lệnh và, [như đã lưu ý trước đó trong bài viết](#the-key-difference-expansion), `\Uchar` là một lệnh có thể mở rộng, mà phần mở rộng của nó tạo ra một token ký tự. Trong các hoạt động xử lý của `\directlua`, LuaHBTeX mở rộng từng `\Uchar<character code>` lệnh, nơi nó *loại bỏ* mỗi `\Uchar<character code>` khỏi đầu vào và *thay thế* nó bằng giá trị mở rộng tương ứng: một token ký tự đại diện cho `<character code>`.

Ở giai đoạn cuối của quá trình xử lý, danh sách token ban đầu do `\directlua` được chuyển đổi *trở lại thành văn bản* để trở thành mã Lua dành cho trình thông dịch Lua (xem sơ đồ bên trên). Tất cả các token ký tự được tạo ra bởi phần mở rộng của `\Uchar` cũng được *chuyển đổi trở lại thành văn bản*: việc chuyển đổi token ký tự thành văn bản đó tạo ra các biểu diễn UTF-8 của các giá trị `<character code>` gốc.

Trong ví dụ của chúng ta, đến khi mã Lua được tạo ra và sẵn sàng cho trình thông dịch Lua, đầu vào macro cho "#1" đã được chuyển thành một chuỗi văn bản UTF-8: biến `str` bây giờ là một chuỗi văn bản UTF-8 có thể được thêm an toàn vào bộ đệm HarfBuzz.

**Vì sao \char không hoạt động?**

Câu trả lời thẳng thắn là vì `\char` là *không* một lệnh có thể mở rộng. Không giống như `\Uchar` các lệnh, `\char` lệnh *không bị loại bỏ* khỏi đầu vào trong quá trình `\directlua`xử lý ban đầu của nó để tạo danh sách token, chúng “đi qua” và được đưa vào danh sách token đang được tạo bởi `\directlua`. Ví dụ, nếu đối số của `\codestoemoji` chứa `\char"1F3F4` LuaHBTeX sẽ chuyển nó thành một chuỗi token và lưu chúng như một phần của toàn bộ danh sách token đang được tạo.

Ở giai đoạn xử lý tiếp theo, khi chuyển các token trở lại thành văn bản, mã Lua thu được sẽ chứa *chuỗi chữ* `\char"1F3F4` trong văn bản dùng để định nghĩa biến của chúng ta `str`. `str` được thêm vào bộ đệm HarfBuzz, nó sẽ không chứa một chuỗi được mã hóa UTF-8 biểu diễn ký tự emoji "1F3F4, mà sẽ chứa chuỗi chữ `\char"1F3F4`, mà HarfBuzz sẽ cố gắng tạo hình và, đối với mục đích của chúng ta, sẽ không tạo ra một glyph emoji. Nhân tiện, chuỗi `\char"1F3F4` cũng sẽ tạo ra lỗi cú pháp Lua trừ khi nó được tạo dưới dạng “chuỗi ngoặc dài”—xem [Các chuỗi thoát của Lua là gì](/latex/vi/bai-viet-chuyen-sau/09-an-introduction-to-luatex-part-2-understanding-directlua.md#what-are-e2809clua-escape-sequencese2809d3f) để biết bối cảnh về vấn đề đó.

Nếu chúng ta cố gắng dùng `\codestoemoji` bằng một `\char` lệnh, như sau:

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

LuaHBTeX sẽ thất bại và báo một lỗi cú pháp giống như thế này:

```latex
[\directlua]:1: invalid escape sequence near '"\c'.
\codestoemoji ...ing \includegraphics }.}]]) end }

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

bộ thông dịch Lua gặp sự cố, vì vậy
phần còn lại của đoạn Lua này sẽ bị bỏ qua.
```

#### Gọi hàm tạo hình HarfBuzz

**Thiết lập các tham số bộ đệm**

HarfBuzz đôi khi cần thêm thông tin về văn bản mà nó được yêu cầu tạo hình. Bạn có thể cung cấp thông tin đó bằng cách cấu hình `<buffer variable>` bằng cách dùng *các phương thức của bộ đệm*, chẳng hạn như:

* `<buffer variable>:set_direction(*HarfBuzz direction*)`;
* `<buffer variable>:set_language(*HarfBuzz language*)`;
* `<buffer variable>:set_script(*HarfBuzz script*)`.

Ví dụ, chúng ta cần thông báo cho HarfBuzz rằng hướng của văn bản emoji của chúng ta sẽ là từ trái sang phải. Để làm điều đó, chúng ta dùng `set_direction()` phương thức trên `<buffer variable>` (được gọi là `hbbuffer`) bằng cách viết:

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

trong đó `hblib.Direction.new("ltr")` tạo ra một “đối tượng hướng” phù hợp để truyền cho bộ máy HarfBuzz qua Lua.

**Thực hiện tạo hình**

Sau khi bộ đệm được khởi tạo thích hợp, chúng ta có thể yêu cầu HarfBuzz thực hiện việc tạo hình thực sự thông qua hàm `shape_full()`. Trong ví dụ của chúng ta, ta viết:

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

Tham số thứ 3 và thứ 4 của `shape_full()` hàm cần là các bảng Lua—chúng ta đã dùng các bảng rỗng “`{}`” cho cả hai tham số. Dạng tổng quát của `shape_full()` là:

```latex
shape_full(phông Harfbuzz, bộ đệm Harfbuzz, {tính năng phông}, {"shaper"}
```

* **`{"shaper"}`**: Thông thường không cần đặt nhưng các tùy chọn là `{"ot"}` hoặc `{"graphite2"}`. Có thể tìm thêm thông tin về khái niệm “shaper” trong [tài liệu HarfBuzz](https://harfbuzz.github.io/shaping-and-shape-plans.html)—lưu ý đây là tài liệu về API C cấp thấp, không phải `luaharfbuzz` phần liên kết (triển khai) dựa trên Lua.
* **`{font features}`**: Đây là một bảng liệt kê các [tính năng OpenType](https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist)—được phông hỗ trợ—mà bạn muốn HarfBuzz áp dụng trong quá trình tạo hình.

Bất kỳ tính năng phông nào bạn muốn dùng đều cần được tạo bằng một `luaharfbuzz` hàm của thư viện

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

trong đó

* `library_instance` là biến thể hiện thư viện của bạn `luaharfbuzz` (biến thể hiện thư viện`hblib` trong ví dụ của chúng ta);
* `feature_string` sử dụng một [cú pháp để định nghĩa các tính năng](https://github.com/ufytex/luaharfbuzz/wiki/Feature-Strings). Ví dụ về điều này là `+smcp` để kích hoạt chữ hoa nhỏ hoặc `-kern` để tắt kerning.

Ví dụ:

```latex
local dosmcp = hblib.Feature.new("+smcp")
local nokern = hblib.Feature.new("-kern")
% Dùng các tính năng phông của bạn như thế này
local res = hblib.shape_full(hbfont, hbbuffer, {dosmcp,nokern},{})
```

#### Truy cập kết quả: lấy các glyph

Và cuối cùng, nếu thao tác tạo hình thành công, các glyph đã được tạo hình sẽ được trả về trong biến bộ đệm `hbbuffer` mà chúng ta đã tạo trước đó trong mã.

Chúng ta truy cập các glyph thông qua phương thức bộ đệm `get_glyphs()` và dùng một vòng lặp để lấy từng glyph riêng lẻ. Lưu ý rằng bảng Lua chứa các glyph, `hbglyphs` trong ví dụ của chúng ta, được đánh chỉ số bắt đầu từ 1, không phải 0.

Mỗi glyph *mã định danh glyph* (một cách dễ gây nhầm lẫn được gọi là `codepoint`), và phông HarfBuzz (`hbfont`), được truyền tới `writePNGglyph()` hàm tạo một tệp PNG bằng cách dùng biểu diễn ảnh raster của glyph đó từ phông chữ.

`writePNGglyph()` ghi ra một tệp PNG và trả về tên tệp PNG, được dùng để nhập tệp PNG (đã được thu nhỏ) vào tài liệu LaTeX của chúng ta thông qua `\includegraphics[scale=0.75]{<fname>}`. Lưu ý cách chúng ta có thể dùng `\includegraphics` trực tiếp trong mã Lua.

```latex
nếu (res) thì
    local hbglyphs=hbbuffer:get_glyphs()
    % Bảng glyph, hbglyphs, được đánh số từ 1
    local i = 1
    while hbglyphs[i] \noexpand~= nil do
        local glyph = hbglyphs[i]
        i = i + 1
        local fname=writePNGglyph(hbfont, glyph.codepoint)
        % Giảm kích thước các ảnh PNG được nhập vào của chúng ta
        local s = 0.75
        local scal="[scale="..tostring(s).."]"
        tex.print([[\noexpand\includegraphics]]..scal..[[{]]..fname..[[}]])
     end
end
```

### Mã đầy đủ mà bạn có thể mở trong Overleaf

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

% Load the luaharfbuzz library from LuaHBTeX
hblib=require("luaharfbuzz")

% Xác định vị trí phông Noto Color Emoji trên máy chủ của Overleaf
pathtofontfile=kpse.find_file("NotoColorEmoji.ttf","truetype fonts")

% Create HarfBuzz face and HarfBuzz font from Noto Color Emoji
hbface = hblib.Face.new(pathtofontfile)
hbfont = hblib.Font.new(hbface)

% This function accepts a font and a glyph ID:
% nó trích xuất dữ liệu PNG của glyph và ghi
% it out to a .png file

function writePNGglyph(hbfontobject, glyphID)

    % Get glyph PNG data
    local pngblob=hbfontobject:ot_color_glyph_get_png(glyphID)
    local pngdata=pngblob:get_data()

    % Construct a file name for our .png file
    local fname="Glyph"..glyphID..".png"

    % Write the .png file and return the file name
    local output = assert(io.open(fname, "wb"))
    output:write(pngdata)
    output:close()

    % Return the file name for \includegraphics to use
    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, {},{})

nếu (res) thì
    local hbglyphs=hbbuffer:get_glyphs()
    % Bảng glyph, hbglyphs, được đánh chỉ số từ 1.
    local i = 1
    while hbglyphs[i] \noexpand~= nil do
        local glyph = hbglyphs[i]
        i = i + 1
        local fname=writePNGglyph(hbfont, glyph.codepoint)
        % Giảm kích thước các ảnh PNG được nhập vào của chúng ta
        local s = 0.75
        local scal="[scale="..tostring(s).."]"
        tex.print([[\noexpand\includegraphics]]..scal..[[{]]..fname..[[}]])
     end
end
}}

Một con vịt: \codestoemoji{\Uchar"1F986}

Một lá cờ: \codestoemoji{\Uchar"1F3F4\Uchar"E0067\Uchar"E0062\Uchar"E0065\Uchar"E006E\Uchar"E0067\Uchar"E007F}
\end{document}
```

[Mở ví dụ API luaharfbuzz này trong Overleaf.](/latex/vi/bai-viet-chuyen-sau/10-an-overview-of-technologies-supporting-the-use-of-colour-emoji-fonts-in-latex.md)

Ví dụ này tạo ra kết quả sau:

![Harfbuzzexample.png](/files/16006c5ed040878f83a7cce82ed087e78b167caa)

## Phần thưởng: Vui với toán học emoji

Để kết thúc bằng một ghi chú vui vẻ, một thành viên của nhóm Overleaf đã dùng [`emoji` gói LaTeX](https://ctan.org/pkg/emoji?lang=en) để tạo ra một ví dụ thú vị:

```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}
```

[Mở ví dụ thú vị này trong 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)

Ví dụ này tạo ra kết quả sau:

![Emojimath2.png](/files/3e8f9f60b551d5e4eaa1f9a8b44a844c2c05cc96)


---

# 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/vi/bai-viet-chuyen-sau/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.
