> For the complete documentation index, see [llms.txt](https://bob-mcd-team.gitbook.io/uefi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bob-mcd-team.gitbook.io/uefi/uefi-development/hii/52.russian-font-part-1.md).

# 52. Russian 글꼴 추가 - Part 1.

&#x20;UNI 파일은 UEFI의 로컬화를 위한 것이다. 지금까지 우리는, UNI 파일에서 영어와 프랑스어만 출력해 보았다. 이 두 언어의 공통점은, 라틴어 문자 집합의 기호를 사용한다는 점에서 같다.

&#x20;다른 문자 집합에서 문자열을 출력하려면 올바른 문장이 출력되지 않는다. 예를 들어, 간단하게 Hello를 러시아어로 출력하려면,

```
Print(L"Привет!\n");
```

&#x20;다음과 같이 출력된다.

<figure><img src="/files/YSRA4U0YrLHzBzPDiySe" alt=""><figcaption></figcaption></figure>

&#x20;`nographic` 모드에서는 다음과 같이 출력될 것이다.

```
FS0:\> HIIFont.efi
?@825B!
```

&#x20;이 출력을 구현하는 것에는 신경쓰지 말자. 이는 다른 번역 레벨에서 발생하는 것이고, 이번 장에서는 UEFI Graphic(Native 또는 VNC)을 살펴보도록 하자.

&#x20;어쨌건 출력으로 보이듯이, `!` 하나만 출력된 것을 볼 수 있다. UEFI 시스템 자체에는 러시아어 글꼴이 없기 때문에 위와 같은 일이 발생한 것이다.

&#x20;컴퓨터의 언어로 해석하자면, 러시아어 유니코드 기호 코드를 기호 이미지로 변환하는 방법을 UEFI 시스템은 모르기 때문이다.

&#x20;글꼴 정보는 HII DB에 저장되므로, 문제를 해결하려면 러시아 코드가 있는 유니코드 기호에 대한 "그림"이 있는 글꼴 유형 패키지가 포함된 패키지 목록을 제공하기만 하면 된다.

&#x20;UEFI는 작게는 8x19에 넓게는 16x19 글꼴은 사용한다. 코드에서 기호 데이터는 `EFI_NARROW_GLYPH` 및 `EFI_WIDE_GLYPH`(`UefiInternalFormRepresentation.h`) 구조로 인코딩된다.

```
$ cat MdePkg/Include/Uefi/UefiInternalFormRepresentation.h
---
#define EFI_GLYPH_HEIGHT                     19
#define EFI_GLYPH_WIDTH                      8
///@}

///
/// The EFI_NARROW_GLYPH has a preferred dimension (w x h) of 8 x 19 pixels.
///
typedef struct {
  ///
  /// The Unicode representation of the glyph. The term weight is the
  /// technical term for a character code.
  ///
  CHAR16                 UnicodeWeight;
  ///
  /// The data element containing the glyph definitions.
  ///
  UINT8                  Attributes;
  ///
  /// The column major glyph representation of the character. Bits
  /// with values of one indicate that the corresponding pixel is to be
  /// on when normally displayed; those with zero are off.
  ///
  UINT8                  GlyphCol1[EFI_GLYPH_HEIGHT];
} EFI_NARROW_GLYPH;

///
/// The EFI_WIDE_GLYPH has a preferred dimension (w x h) of 16 x 19 pixels, which is large enough
/// to accommodate logographic characters.
///
typedef struct {
  ///
  /// The Unicode representation of the glyph. The term weight is the
  /// technical term for a character code.
  ///
  CHAR16                 UnicodeWeight;
  ///
  /// The data element containing the glyph definitions.
  ///
  UINT8                  Attributes;
  ///
  /// The column major glyph representation of the character. Bits
  /// with values of one indicate that the corresponding pixel is to be
  /// on when normally displayed; those with zero are off.
  ///
  UINT8                  GlyphCol1[EFI_GLYPH_HEIGHT];
  ///
  /// The column major glyph representation of the character. Bits
  /// with values of one indicate that the corresponding pixel is to be
  /// on when normally displayed; those with zero are off.
  ///
  UINT8                  GlyphCol2[EFI_GLYPH_HEIGHT];
  ///
  /// Ensures that sizeof (EFI_WIDE_GLYPH) is twice the
  /// sizeof (EFI_NARROW_GLYPH). The contents of Pad must
  /// be zero.
  ///
  UINT8                  Pad[3];
} EFI_WIDE_GLYPH;
```

&#x20;

&#x20;이유는 모르겠지만, `mFontBin` 구조의 String.c에서 일부 히브리어 문자에 대한 예를 찾아볼 수 있었다. 이 구조에서 유니코드 `0x05d2`가 있는 기호 하나를 살펴보도록 하겠다.

&#x20;`GlyphCol1` 배열을 보고, 이진 시스템에서 배열 데이터를 인쇄해보자. `X/-`로도 표현해 보았다.

```
$ cat MdeModulePkg/Application/UiApp/String.c
---
        {
      0x05d2,
      0x00,
      {
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x78,      // 01111000 // -XXXX---
        0x7C,      // 01111100 // -XXXXX--
        0x0C,      // 00001100 // ----XX--
        0x0C,      // 00001100 // ----XX--
        0x0C,      // 00001100 // ----XX--
        0x0C,      // 00001100 // ----XX--
        0x0C,      // 00001100 // ----XX--
        0x0C,      // 00001100 // ----XX--
        0x1C,      // 00011100 // ---XXX--
        0x3E,      // 00111110 // --XXXXX-
        0x66,      // 01100110 // -XX--XX-
        0x66,      // 01100110 // -XX--XX-
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x00       // 00000000 // --------
      }
    },
```

&#x20;이는 실제 히브리어 문자인 gimel `&#0x05D2`(`U+05D2`)로 보인다.\
<https://unicodemap.org/details/0x05D2/index.html>

&#x20;EDKII 코드베이스에 기호에 대한 에는 없지만, 간단하다. `GlyphCol1`은 기호 이미지의 왼쪽 절반을 인코딩하고, `GlyphCol2`는 남은 오른쪽 절반을 인코딩한다.

&#x20;예를 들어, `A`는 다음과 같을 수있다.

```
    {
      0x05d2,
      0x00,
      {
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x01,      // 00000001 // -------X
        0x02,      // 00000010 // ------X-
        0x02,      // 00000010 // ------X-
        0x04,      // 00000100 // -----X--
        0x04,      // 00000100 // -----X--
        0x08,      // 00001000 // ----X---
        0x0F,      // 00001111 // ----XXXX
        0x10,      // 00010000 // ---X----
        0x10,      // 00010000 // ---X----
        0x20,      // 00100000 // --X-----
        0x20,      // 00100000 // --X-----
        0x70,      // 01110000 // -XXX----
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x00       // 00000000 // --------
      },
      {
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x80,      // 10000000 // X-------
        0x80,      // 10000000 // X-------
        0x40,      // 01000000 // -X------
        0x40,      // 01000000 // -X------
        0x20,      // 00100000 // --X-----
        0xE0,      // 11100000 // XXX-----
        0x10,      // 00010000 // ---X----
        0x10,      // 00010000 // ---X----
        0x08,      // 00001000 // ----X---
        0x08,      // 00001000 // ----X---
        0x1C,      // 00011100 // ---XXX--
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x00,      // 00000000 // --------
        0x00       // 00000000 // --------
      },
      {
        0x00,
        0x00,
        0x00
      }
    },
```

## Glyph 기본 글꼴

&#x20;기본 글꼴의 정의 위치가 궁금할 경우, `LaffStd.c`를 참조하는 것을 추천한다.

```
$ cat MdeModulePkg/Universal/Console/GraphicsConsoleDxe/LaffStd.c
---
EFI_NARROW_GLYPH  gUsStdNarrowGlyphData[] = {
  { 0x0020, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
  { 0x0021, 0x00, {0x00,0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00}},
  { 0x0022, 0x00, {0x00,0x00,0x00,0x6C,0x6C,0x6C,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
  ...
  { 0x0000, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} //EOL
};

// Get available Unicode glyphs narrow fonts(8*19 pixels) size.
UINT32 mNarrowFontSize =  sizeof (gUsStdNarrowGlyphData);
```

&#x20;이 `gUsStdNarrowGlyphData` 배열은 글꼴 패키지를 생성하고, HII 데이터베이스에 등록하는 데 사용된다. 실제 코드는 `MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsole.c` 파일의 `RegisterFontPackage` 함수를 참조하도록 하자.

&#x20;HII 데이터베이스에 패키지 목록을 조사했을 때, 이것이 이 글꼴을 담당했다.

```
PackageList[10]: GUID=F5F219D3-7006-4648-AC8D-D61DFB7BC6AD; size=0x14EC  // mFontPackageListGuid
        Package[0]: type=SIMPLE_FONTS; size=0x14D4
        Package[1]: type=END; size=0x4
```

## 러시아어 글꼴에 대한 Glyph 데이터 가져오기

&#x20;Glyph 데이터를 얻어오는 것은 글꼴을 추가하는 작업에 있어서 가장 어려운 것이다.

&#x20;우선 크기가 `8x19/16x19`인 픽셀 글꼴을 찾아야 하는데, 이는 이미 어려운 작업일 수 있다.\
<https://int10h.org/oldschool-pc-fonts/fontlist/font?ast_premiumexec>\
에서 키릴 문자가 포함된 `AST PremiumExec` 글꼴을 찾았다. 이 글꼴에는 대부분의 키릴 유니코드 블록(U+0400 - U+04FF)의 심볼들을 포함하고 있다.

&#x20;하지만 이 글꼴은 `*.woff` 형식으로 인코딩되어 있으므로, Glyph 데이터로 변환해야 한다. 수동으로 변환해주기에는 어려움이 있기에, <https://github.com/zhenghuadai/uefi-programming/tree/master/book/GUIbasics/font>서 이 작업에 HTML canvas를 사용해보자는 아이디어를 차용했다.

&#x20;

&#x20;HTML canvas의 글꼴에서 각 기호를 하나씩 인쇄한다. 각 기호에 대해 이미지 비트맵을 가져오고 해당 데이터를 사용하여 UEFI 환경에서 사용할 수 있는 Glyph 배열을 구성한다. 결국 Glyph 배열을 화면에 출력한다.

&#x20;이는 자바스크립트와 관련된 장장이 아니지만, 스크립트에 대한 설명이 필요하다고 생각한다. 먼저 글꼴을 로드하고 canvas와 스크립트 코드를 선언하는 HTML 부분이다.

```html
<html>
  <head>
    <style>
      @font-face {
        font-family: "AST";
        src: url(web_ast_premiumexec.woff) format('woff');
      }
    </style>
  </head>
  <body>
    <canvas id="canvas" width="32" height="32"></canvas>
    <script type="text/javascript">
      ...
    </script>
  </body>
</html>
```

&#x20;우리의 코드는 핵심, 다음과 같다.

```javascript
const unicode_start_code = 0x0400;
const unicode_end_code = 0x045F;
var f = new FontFace('AST', 'url(web_ast_premiumexec.woff)');
f.load().then(function() {
  document.write(UnicodeToGlyphs(unicode_start_code, unicode_end_code));
})
```

&#x20;글꼴이 로드되면, canvas에 U+0400에서 U+045F까지의 유니코드 기호를 인쇄하고, 데이터를 조사하고 화면의 UEFI에 대한 최종 C 배열을 출력하는 사용자 지정 함수 `UnicodeToGlyphs`를 실행한다.

&#x20;그리고 여기에 나머지 자바스크립트코드가 있다. 이는 매우 간단하므로, 이해하기 어렵지 않을 것이라 생각한다.

```javascript
function decimalToHex(d, padding) {
  var hex = Number(d).toString(16);
  padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding;

  while (hex.length < padding) {
    hex = "0" + hex;
  }

  return hex;
}

function UnicodeToGlyphs(unicode_start_code, unicode_end_code) {
  const threshold = 100;                // `A` threshold to count data as a black pixel
  const FW = 16;
  const FH = 19;
  const left_glyph_start_column = 0;
  const left_glyph_end_column = FW/2 - 1;
  const right_glyph_start_column = FW/2;
  const right_glyph_end_column = FW - 1;

  const canvas = document.getElementById('canvas');
  canvas.width *= window.devicePixelRatio
  canvas.height *= window.devicePixelRatio
  canvas.style.width = 32
  canvas.style.height = 32
  const ctx = canvas.getContext('2d');
  ctx.strokeRect(0, 0, FW, FH);
  ctx.font = "19px AST"
  ctx.fillstyle='#00f';

  var wide_glyphs_str="EFI_WIDE_GLYPH  gSimpleFontWideGlyphData[] = {<BR>";
  var narrow_glyphs_str="EFI_NARROW_GLYPH  gSimpleFontNarrowGlyphData[] = {<BR>";
  for(i=unicode_start_code; i<=unicode_end_code; i++){
    wide_glyph = false;
    ctx.clearRect(0, 0, FW, FH);
    ctx.fillText(String.fromCharCode(i), 0, 0 + FW-1);
    var bitmapimg = ctx.getImageData(0, 0, FW, FH);
    var bitmap = bitmapimg.data;
    var left_glyph = " ";
    var right_glyph = " ";
    for (row=0; row<FH; row++){
      left_row = 0;
      for (col=left_glyph_start_column; col<=left_glyph_end_column; col++){
        left_row <<= 1
        if (bitmap[row*FW*4 + col*4 + 3] > threshold)
          left_row |= 1;
      }
      left_glyph += "0x" + decimalToHex(left_row);

      right_row = 0;
      for (col=right_glyph_start_column; col<=right_glyph_end_column; col++){
        right_row <<= 1
        if(bitmap[row*FW*4 + col*4 + 3] > threshold) {
          wide_glyph = true;
          right_row |= 1;
        }
      }
      right_glyph += "0x" + decimalToHex(right_row);

      if (row < (FH-1)) {
        left_glyph += ",";
        right_glyph += ",";
      }
    }
    if (wide_glyph)
      wide_glyphs_str += "{ 0x" + decimalToHex(i) + ", 0x00, " + "{" +  left_glyph + "}, {" + right_glyph + "}, {0x00,0x00,0x00}}," + "<BR>";
    else
      narrow_glyphs_str += "{ 0x" + decimalToHex(i) + ", 0x00, " + "{" +  left_glyph + "}},"+ "<BR>";
  }
  narrow_glyphs_str += "};<BR>"
  narrow_glyphs_str += "UINT32 gSimpleFontNarrowBytes = sizeof(gSimpleFontNarrowGlyphData);<BR>"
  wide_glyphs_str += "{ 0x00, 0x00, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}" + "<BR>"
  wide_glyphs_str += "};<BR>"
  wide_glyphs_str += "UINT32 gSimpleFontWideBytes = sizeof(gSimpleFontWideGlyphData);<BR>"
  return wide_glyphs_str + "<BR>" + narrow_glyphs_str;
}
```

&#x20;`create_font_data.html`과 `web_ast_premiumexec.woff`를 두고 `create_font_data.html`을 연다. 이 작업을 위해 Chrome(version 95.0.4638.69)을 사용했다. 페이지는 다음과 같이 출력되어야 한다.

```
EFI_WIDE_GLYPH gSimpleFontWideGlyphData[] = {
{ 0x00, 0x00, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
};
UINT32 gSimpleFontWideBytes = sizeof(gSimpleFontWideGlyphData);

EFI_NARROW_GLYPH gSimpleFontNarrowGlyphData[] = {
{ 0x400, 0x00, { 0x60,0x30,0x00,0xfe,0x66,0x62,0x60,0x68,0x78,0x68,0x60,0x60,0x62,0x66,0xfe,0x00,0x00,0x00,0x00}},
{ 0x401, 0x00, { 0x66,0x66,0x00,0xfe,0x66,0x62,0x60,0x68,0x78,0x68,0x60,0x60,0x62,0x66,0xfe,0x00,0x00,0x00,0x00}},
{ 0x402, 0x00, { 0x00,0x00,0x00,0xfc,0x64,0x60,0x60,0x6c,0x76,0x66,0x66,0x66,0x66,0x66,0xe6,0x0c,0x00,0x00,0x00}},
{ 0x403, 0x00, { 0x0c,0x18,0x00,0xfe,0x66,0x62,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00}},
 ...
{ 0x45c, 0x00, { 0x00,0x00,0x00,0x0c,0x18,0x30,0x00,0xe6,0x66,0x6c,0x78,0x78,0x6c,0x66,0xe6,0x00,0x00,0x00,0x00}},
{ 0x45d, 0x00, { 0x00,0x00,0x00,0x60,0x30,0x18,0x00,0xc6,0xc6,0xce,0xde,0xf6,0xe6,0xc6,0xc6,0x00,0x00,0x00,0x00}},
{ 0x45e, 0x00, { 0x00,0x00,0x00,0x00,0x6c,0x38,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7e,0x06,0x06,0x0c,0xf8,0x00}},
{ 0x45f, 0x00, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xee,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0xfe,0x10,0x10,0x00,0x00}},
};
UINT32 gSimpleFontNarrowBytes = sizeof(gSimpleFontNarrowGlyphData);
```

&#x20;우리가 가져온 글꼴은 고정된 크기인 8x19 문자만 포함하므로, `gSimpleFontNarrowGlyphData` 배열만 데이터로 채워진다. 이제 UEFI 애플리케이션을 구성하고, 글꼴을 HII 데이터베이스에 추가할 때이다.


---

# 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:

```
GET https://bob-mcd-team.gitbook.io/uefi/uefi-development/hii/52.russian-font-part-1.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
