TPdfFontDataResolver = function(const FontName: WideString;
out FontData: TBytes): Boolean of object;
EPdfHarfBuzzError = class(Exception);
TPdfHarfBuzzTextShaper = class(TPdfTextShaper)
constructor Create(const Resolver: TPdfFontDataResolver);
destructor Destroy; override;
function Shape(const Text, FontName: WideString; FontSize: Single;
const Options: TPdfTextShapingOptions;
out Shaped: TPdfShapedText): Boolean; override;
end;
function HarfBuzzAvailable: Boolean;
function HarfBuzzVersionString: string;
FPdfTextShapingHb는 usp10.dll이 없는 대상을 위한, FPdfTextShaping의 Uniscribe 백엔드에 대응하는 유닛입니다. Delphi는 Uniscribe 백엔드가 적용되는 Windows를 대상으로 하므로, 인터페이스는 Free Pascal에서만 컴파일됩니다
두 백엔드는 작업을 서로 다르게 나눕니다. Uniscribe는 한 번의 패스에서 항목 분리, 셰이핑, 배치, 재정렬을 모두 처리하는 반면, HarfBuzz는 방향과 스크립트가 이미 알려진 단일 런을 셰이핑합니다. 나머지는 이 유닛이 담당합니다. FPdfBidi가 임베딩 레벨을 해석하고, 각 문자의 Unicode 스크립트가 텍스트를 런으로 분할하며, 런은 UAX #9 규칙 L2가 주는 시각 순서로 배치됩니다. Common, Inherited, 알 수 없는 스크립트 문자는 속한 런을 이어가므로 숫자나 공백 하나가 단어를 가르지 않습니다
libharfbuzz는 DynLibs를 통해 런타임에 바인딩되므로 빌드 의존성이 아니며, 라이브러리가 없어도 로드가 실패하지 않고 그 사실이 보고됩니다. HarfBuzzAvailable은 라이브러리를 찾고 필요한 엔트리 포인트를 해석했는지 보고하고, HarfBuzzVersionString은 로드된 라이브러리의 버전을 반환합니다
이 셰이퍼는 폰트를 직접 찾지 않습니다. Uniscribe는 GDI 디바이스 컨텍스트에서 폰트 바이너리를 읽는데 여기에 이식 가능한 대응물이 없으므로, 호스트가 폰트 이름을 TrueType 또는 OpenType 바이트로 연결하는 TPdfFontDataResolver를 Create에 전달합니다. 덕분에 fontconfig, CoreText, 애플리케이션 자체 폰트 폴더 같은 폰트 열거 정책은 그럴 이유가 없는 유닛 밖에 머뭅니다
| Function | Description |
|---|---|
HarfBuzzAvailable | libharfbuzz를 찾고 필요한 엔트리 포인트를 해석했으면 True입니다. 첫 호출이 바인딩을 수행합니다 |
HarfBuzzVersionString | 로드된 라이브러리 버전을 "major.minor.micro" 형식으로 반환하며, 라이브러리를 사용할 수 없으면 빈 문자열입니다 |
| Type | Description |
|---|---|
TPdfFontDataResolver | FontName을 FontData의 폰트 파일 바이트로 연결하는 메서드 참조입니다. False를 반환하면 Windows에서 읽을 수 없는 GDI 폰트와 마찬가지로 셰이퍼가 요청을 실패 처리합니다 |
EPdfHarfBuzzError | resolver가 구성되지 않았거나, libharfbuzz를 사용할 수 없거나, HarfBuzz blob, face, font, buffer를 만들 수 없을 때 발생하는 예외입니다 |
TPdfHarfBuzzTextShaper | Create, Destroy, Shape를 갖춘 HarfBuzz 기반 TPdfTextShaper 구현입니다 |
| Member | Description |
|---|---|
Create(Resolver) | 셰이퍼를 만들고 모든 폰트 조회에 사용할 TPdfFontDataResolver를 저장합니다 |
Destroy | 캐시된 HarfBuzz face와 blob을 해제한 뒤 상속받은 소멸자를 실행합니다 |
Shape(Text, FontName, FontSize, Options, Shaped) | 임베딩 레벨을 해석하고, 레벨과 스크립트로 텍스트를 분할하고, 각 런을 HarfBuzz로 셰이핑한 뒤 시각 순서의 런을 Shaped에 담아 반환합니다. 텍스트가 글리프를 만들지 못하면 False입니다 |
HarfBuzzAvailable 호출이 Windows에서는 libharfbuzz-0.dll과 harfbuzz.dll, macOS에서는 libharfbuzz.0.dylib와 libharfbuzz.dylib, 그 외 플랫폼에서는 libharfbuzz.so.0과 libharfbuzz.so를 탐색합니다Shape는 빈 텍스트, 빈 폰트 이름, 0 이하의 폰트 크기, 잘못된 Options 한도, MaxCharacters를 초과하는 텍스트, MaxGlyphs를 초과하는 글리프, resolver가 제공하지 못한 폰트 데이터에 대해 EPdfTextShapingError를 발생시킵니다Shape는 TPdfFontDataResolver가 구성되지 않았거나, libharfbuzz를 사용할 수 없거나, HarfBuzz 객체를 만들 수 없을 때 EPdfHarfBuzzError를 발생시킵니다Options.MaxRuns로 제한되고, Options.Direction은 ptdLeftToRight와 ptdRightToLeft를 고정 단락 방향으로 매핑하며 그 외 값은 자동으로 해석됩니다Shaped.FontData는 resolver가 반환한 바이트를 담으며, CID 폰트를 만들 때 TPdf.AddShapedText가 필요로 하는 데이터입니다