53. Russian 글꼴 추가 - Part 2.

EFI_HII_SIMPLE_FONT_PACKAGE 구성 및 HII 데이터베이스에 채우기

이제 우리 글꼴을 HII 데이터베이스에 추가할 시점이다.

새로운 애플리케이션을 초기화한다.

./createNewApp.sh HIIAddRussianFont

패키지 DSC 파일(UefiLessonsPkg.dsc)에 추가한다.

$ vi UefiLessonsPkg/UefiLessonsPkg.dsc
---
[Components]
  UefiLessonsPkg/HIIAddRussianFont/HIIAddRussianFont.inf

애플리케이션에서는 HII 데이터베이스에 패키지 목록을 채울 것이므로, UefiLessonsPkg.dec에서 이 패키지 목록에 대한 GUID를 선언해야 한다.

$ vi UefiLessonsPkg/UefiLessonsPkg.dec
---
[Guids]
  ...
  gHIIAddRussianFontGuid = { 0x9fe2f616, 0x323c, 0x45a7, { 0x87, 0xa2, 0xdf, 0xef, 0xf5, 0x17, 0xcc, 0x66 }}

그리고 애플리케이션을 이용하기 위해서 INF 파일을 선언한다.

$ vi UefiLessonsPkg/HIIAddRussianFont/HIIAddRussianFont.inf
---
[Packages]
  ...
  UefiLessonsPkg/UefiLessonsPkg.dec

[Guids]
  gHIIAddRussianFontGuid

또한 HII 서비스(ex. HiiAddPackages 함수)용 라이브러리를 사용하므로 처음부터 포함시키도록 하자.

[Packages]
  ...
  MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
  ...
  HiiLib

String.uni file

애플리케이션 소스(HIIAddRussianFont.inf)에 문자열 UNI 파일을 추가한다.

$ vi UefiLessonsPkg/HIIAddRussianFont/HIIAddRussianFont.inf
---
[Sources]
  ...
  Strings.uni

그 다음 아래의 문자열을 사용하여 이 Strings.uni 파일을 만든다.

  • "Hello!"

  • "Bye!"

  • 대문자 알파벳

  • 소문자 알파벳

#langdef en-US "English"
#langdef fr-FR "Francais"
#langdef ru-RU "Russian"

#string STR_HELLO               #language en-US  "Hello!"
                                #language fr-FR  "Bonjour!"
                                #language ru-RU  "Привет!"

#string STR_BYE                 #language en-US  "Bye!"
                                #language fr-FR  "Au revoir!"
                                #language ru-RU  "До свидания!"

#string STR_ALPHABET_UPPERCASE  #language en-US  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                #language fr-FR  "ABCDEFGHIJKLMNOPQRSTUVWXYZÀÈÙÉÂÊÎÔÛËÏÜŸÆŒÇ"
                                #language ru-RU  "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЪЭЮЯ"

#string STR_ALPHABET_LOWERCASE  #language en-US  "abcdefghijklmnopqrstuvwxyz"
                                #language fr-FR  "abcdefghijklmnopqrstuvwxyzàèùéâêîôûëïüÿæœç"
                                #language ru-RU  "абвгдеёжзийклмнопрстуфхцчшщьъэюя"

알파벳 문자열은 해당 언어의 모든 문자가 올바르게 출력되었는지 확인하는 데 도움이 된다.

RussianFont.c

이전 장에서 받은 러시아어 글꼴 Glyph가 포함된 파일을 HIIAddRussianFont.inf에 추가한다.

$ vi UefiLessonsPkg/HIIAddRussianFont/HIIAddRussianFont.inf
---
[Sources]
  ...
  RussianFont.c

다음 내용이 포함된 파일일 경우를 대비하여

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}},
{ 0x404, 0x00, { 0x00,0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc8,0xf8,0xc8,0xc0,0xc0,0xc2,0x66,0x3c,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);

HIIAddRussianFont.c

이제 기본 코드를 작성할 차례이다.

#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/HiiLib.h>
#include <Library/MemoryAllocationLib.h>

extern EFI_WIDE_GLYPH gSimpleFontWideGlyphData[];
extern UINT32 gSimpleFontWideBytes;
extern EFI_NARROW_GLYPH gSimpleFontNarrowGlyphData[];
extern UINT32 gSimpleFontNarrowBytes;


EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  UINT8* FontPackage = CreateSimpleFontPkg(gSimpleFontWideGlyphData,
                                          gSimpleFontWideBytes,
                                          gSimpleFontNarrowGlyphData,
                                          gSimpleFontNarrowBytes);

  EFI_HII_HANDLE Handle = HiiAddPackages(&gHIIAddRussianFontGuid,
                                         NULL,
                                         FontPackage,
                                         HIIAddRussianFontStrings,
                                         NULL);

  FreePool(FontPackage);

  if (Handle == NULL)
  {
    Print(L"Error! Can't perform HiiAddPackages\n");
    return EFI_INVALID_PARAMETER;
  }

  Print(L"en-US ID=1: %s\n", HiiGetString(Handle, 1, "en-US"));
  Print(L"en-US ID=2: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_HELLO), "en-US"));
  Print(L"en-US ID=3: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_BYE), "en-US"));
  Print(L"en-US ID=4: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_ALPHABET_UPPERCASE), "en-US"));
  Print(L"en-US ID=5: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_ALPHABET_LOWERCASE), "en-US"));
  Print(L"fr-FR ID=1: %s\n", HiiGetString(Handle, 1, "fr-FR"));
  Print(L"fr-FR ID=2: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_HELLO), "fr-FR"));
  Print(L"fr-FR ID=3: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_BYE), "fr-FR"));
  Print(L"fr-FR ID=4: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_ALPHABET_UPPERCASE), "fr-FR"));
  Print(L"fr-FR ID=5: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_ALPHABET_LOWERCASE), "fr-FR"));
  Print(L"ru-RU ID=1: %s\n", HiiGetString(Handle, 1, "ru-RU"));
  Print(L"ru-RU ID=2: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_HELLO), "ru-RU"));
  Print(L"ru-RU ID=3: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_BYE), "ru-RU"));
  Print(L"ru-RU ID=4: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_ALPHABET_UPPERCASE), "ru-RU"));
  Print(L"ru-RU ID=5: %s\n", HiiGetString(Handle, STRING_TOKEN(STR_ALPHABET_LOWERCASE), "ru-RU"));

  return EFI_SUCCESS;
}

여기서 우리는,

  • RussianFont.c 파일의 Glyph 배열에서 글꼴 패키지 데이터를 구성한다. 이 다음에 CreateSimpleFontPkg 함수를 정의한다.

  • HiiAddPackages를 사용하여 글꼴 패키지와 애플리케이션 문자열 패키지를 모두 추가한다. (이 함수는 가변적이며, 다양한 수의 패키지를 사용하여 모두 push하여 새 패키지 목록을 만들 수 있다. https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/UefiHiiLib/HiiLib.c)

  • 패키지를 추가하면, 더 이상 FontPackage가 필요하지 않으므로 FreePool 기능으로 해제하는 것을 잊지 않도록 하자.

  • 모든 것이 정상이라면, HiiGetString을 사용하여 모든 언어의 모든 문자열을 출력한다.

이제 CreateSimpleFontPkg 함수를 정의한다. 먼저, 글꼴 패키지 생성에 필요한 정의 및 유형이다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiInternalFormRepresentation.h

///
/// A simplified font package consists of a font header
/// followed by a series of glyph structures.
///
typedef struct _EFI_HII_SIMPLE_FONT_PACKAGE_HDR {
  EFI_HII_PACKAGE_HEADER Header;
  UINT16                 NumberOfNarrowGlyphs;
  UINT16                 NumberOfWideGlyphs;
  // EFI_NARROW_GLYPH       NarrowGlyphs[];
  // EFI_WIDE_GLYPH         WideGlyphs[];
} EFI_HII_SIMPLE_FONT_PACKAGE_HDR;

///
/// The header found at the start of each package.
///
typedef struct {
  UINT32  Length:24;
  UINT32  Type:8;
  // UINT8  Data[...];
} EFI_HII_PACKAGE_HEADER;


//
// Value of HII package type
//
...
#define EFI_HII_PACKAGE_SIMPLE_FONTS         0x07
...

함수에서 글꼴 패키지에 필요한 배열을 할당해야 한다. 패키지 자체를 위한 공간을 할당하는 것 외에도 모든 데이터 앞에 추가할 패키지 크기에 대해 4바이트를 추가해야 한다. 알다시피 이것은 HiiAddPackages 함수에 필요한 데이터 형식이다.

UINT8* CreateSimpleFontPkg(EFI_WIDE_GLYPH* WideGlyph,
                           UINT32 WideGlyphSizeInBytes,
                           EFI_NARROW_GLYPH* NarrowGlyph,
                           UINT32 NarrowGlyphSizeInBytes)
{
  UINT32 PackageLen = sizeof(EFI_HII_SIMPLE_FONT_PACKAGE_HDR) + WideGlyphSizeInBytes + NarrowGlyphSizeInBytes + 4;
  UINT8* FontPackage = (UINT8*)AllocateZeroPool (PackageLen);

  *(UINT32*)FontPackage = PackageLen;

  ...

  return FontPackage;
}

이제 패키지 데이터를 채우자. 먼저 헤더를 채운다.

EFI_HII_SIMPLE_FONT_PACKAGE_HDR *SimpleFont;
SimpleFont = (EFI_HII_SIMPLE_FONT_PACKAGE_HDR*)(FontPackage + 4);
SimpleFont->Header.Length = (UINT32)(PackageLen - 4);
SimpleFont->Header.Type = EFI_HII_PACKAGE_SIMPLE_FONTS;
SimpleFont->NumberOfNarrowGlyphs = (UINT16)(NarrowGlyphSizeInBytes / sizeof(EFI_NARROW_GLYPH));
SimpleFont->NumberOfWideGlyphs = (UINT16)(WideGlyphSizeInBytes / sizeof(EFI_WIDE_GLYPH));

그런 다음 Glyphs 데이터를 복사한다(CopyMem 함수의 경우, <Library/BaseMemoryLib.h> 헤더를 포함한다.).

UINT8* Location = (UINT8*)(&SimpleFont->NumberOfWideGlyphs + 1);
CopyMem(Location, NarrowGlyph, NarrowGlyphSizeInBytes);
CopyMem(Location + NarrowGlyphSizeInBytes, WideGlyph, WideGlyphSizeInBytes);

애플리케이션을 빌드하고 실행하면, 러시아어 문자열이 올바르게 출력되는 것을 볼 수 있다.

애플리케이션의 시작 부분과 끝 부분에 러시아어로 일부 문자열을 출력해보겠다.

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  Print(L"Привет!\n");

  ...

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

  return EFI_SUCCESS;
}

첫 번째 출력내용은 !만 출력한다. 러시아어 유니코드 기호 Glyph를 표시하는 방법에 대한 정보가 시스템에 없기 때문이다. 그러나 마지막에 글꼴 패키지를 추가한 후의 내용에는 예상된 결과를 출력한다.

Last updated