Commitcee58ff0Recorded2 Mar 2026Repositorysigil-harfbuzz
Add FreeType and HarfBuzz FFI bindings (sigil-freetype, sigil-harfbuzz)
Message
Pure-Scheme bindings using (sigil ffi) for font loading and text shaping.
sigil-freetype: Library/face lifecycle, size configuration, glyph loading, face property accessors (family name, ascender/descender, units-per-EM), and glyph metrics via c-struct-layout modeling of FTFaceRec and FTGlyphSlotRec.
sigil-harfbuzz: Buffer lifecycle, text input, direction/script/language control, shaping, and glyph info/position extraction as lists of dicts. Includes hb-tag/hb-tag-string helpers and common script constants. HarfBuzz font creation from FreeType faces via hbftfontcreatereferenced.
Replaces the placeholder sigil-pango workspace entry with sigil-freetype and sigil-harfbuzz.
Changed
examples/demo.sgl | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
package.sgl | 12 ++++++
src/harfbuzz.sgl | 359 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
test/test-harfbuzz.sgl | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 626 insertions(+)Diff
examples/demo.sgladded
@@ -0,0 +1,140 @@
+1
;;; HarfBuzz text shaping demo+2
;;;+3
;;; Shapes text with HarfBuzz and renders it on a Cairo surface using+4
;;; glyph-level positioning. Demonstrates the full FreeType + HarfBuzz ++5
;;; Cairo integration pipeline.+6
;;;+7
;;; Usage: sigil eval -f packages/sigil-harfbuzz/examples/demo.sgl+8
;;;+9
;;; Requires: libfreetype, libharfbuzz, libcairo, and a system font+10
;;; (uses fontconfig to locate one automatically).+11
+12
(import (sigil core)+13
(sigil io)+14
(sigil process)+15
(sigil ffi)+16
(freetype)+17
(harfbuzz)+18
(cairo))+19
+20
;; ============================================================+21
;; Font discovery+22
;; ============================================================+23
+24
(define (find-font name)+25
(guard (exn (#t #f))+26
(call-with-process "fc-match" (list "--format=%{file}" name)+27
(lambda (p)+28
(let ((line (read-line (process-stdout p))))+29
(if (eof-object? line) #f line))))))+30
+31
;; ============================================================+32
;; Text rendering helper+33
;; ============================================================+34
+35
;; Shape a string and render it at (x, y) on a Cairo context.+36
;; Returns the x position after the last glyph (for chaining).+37
(define (draw-shaped-text cr hb-font text x y)+38
(let ((buf (hb-buffer-create)))+39
(hb-buffer-add-utf8 buf text)+40
(hb-buffer-guess-segment-properties buf)+41
(hb-shape hb-font buf)+42
(let ((infos (hb-buffer-get-glyph-infos buf))+43
(positions (hb-buffer-get-glyph-positions buf)))+44
;; Build the glyph list with accumulated positions+45
(let loop ((is infos) (ps positions) (cx x) (glyphs '()))+46
(if (null? is)+47
(begin+48
(cairo-show-glyphs cr (reverse glyphs))+49
(hb-buffer-destroy buf)+50
cx)+51
(let* ((info (car is))+52
(pos (car ps))+53
(gx (+ cx (/ (dict-ref pos x-offset:) 64.0)))+54
(gy (- y (/ (dict-ref pos y-offset:) 64.0))))+55
(loop (cdr is) (cdr ps)+56
(+ cx (/ (dict-ref pos x-advance:) 64.0))+57
(cons (dict index: (dict-ref info codepoint:)+58
x: gx+59
y: gy)+60
glyphs))))))))+61
+62
;; ============================================================+63
;; Main+64
;; ============================================================+65
+66
(define width 600)+67
(define height 300)+68
(define output-path "/tmp/sigil-harfbuzz-demo.png")+69
+70
(let* ((font-path (or (find-font "sans")+71
(error "No font found — install fontconfig and fonts")))+72
(ft-library (ft-init))+73
(face (ft-new-face ft-library font-path)))+74
+75
(display (string-append "Font: " (ft-face-family-name face)+76
" " (ft-face-style-name face) "\n"))+77
+78
(let ((surface (cairo-image-surface-create CAIRO_FORMAT_ARGB32 width height))+79
(cairo-face (cairo-ft-font-face-create face 0)))+80
+81
(with-cairo-context surface+82
(lambda (cr)+83
;; White background+84
(cairo-set-source-rgb cr 1.0 1.0 1.0)+85
(cairo-paint cr)+86
+87
;; Set the FreeType font on the Cairo context+88
(cairo-set-font-face cr cairo-face)+89
+90
;; --- Title (large) ---+91
(ft-set-char-size face (* 32 64) 0 72 72)+92
(cairo-set-font-size cr 32.0)+93
(let ((hb-font (hb-ft-font-create face)))+94
(cairo-set-source-rgb cr 0.15 0.15 0.15)+95
(draw-shaped-text cr hb-font "HarfBuzz + Sigil" 40.0 55.0)+96
(hb-font-destroy hb-font))+97
+98
;; --- Subtitle ---+99
(ft-set-char-size face (* 16 64) 0 72 72)+100
(cairo-set-font-size cr 16.0)+101
(let ((hb-font (hb-ft-font-create face)))+102
(cairo-set-source-rgba cr 0.4 0.4 0.4 1.0)+103
(draw-shaped-text cr hb-font "Professional text shaping from pure Scheme" 40.0 85.0)+104
(hb-font-destroy hb-font))+105
+106
;; --- Body text samples ---+107
(ft-set-char-size face (* 18 64) 0 72 72)+108
(cairo-set-font-size cr 18.0)+109
(let ((hb-font (hb-ft-font-create face))+110
(lines '("The quick brown fox jumps over the lazy dog."+111
"ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789"+112
"Ligatures: fi fl ffi ffl — Typography!")))+113
(cairo-set-source-rgb cr 0.2 0.2 0.2)+114
(let loop ((ls lines) (y 135.0))+115
(when (pair? ls)+116
(draw-shaped-text cr hb-font (car ls) 40.0 y)+117
(loop (cdr ls) (+ y 32.0))))+118
(hb-font-destroy hb-font))+119
+120
;; --- Different sizes ---+121
(cairo-set-source-rgb cr 0.1 0.3 0.6)+122
(let loop ((sizes '(10 14 18 24)) (x 40.0))+123
(when (pair? sizes)+124
(let ((sz (car sizes)))+125
(ft-set-char-size face (* sz 64) 0 72 72)+126
(cairo-set-font-size cr (* sz 1.0))+127
(let ((hb-font (hb-ft-font-create face)))+128
(let ((next-x (draw-shaped-text cr hb-font+129
(string-append (number->string sz) "pt ")+130
x 265.0)))+131
(hb-font-destroy hb-font)+132
(loop (cdr sizes) next-x))))))))+133
+134
(cairo-surface-write-to-png surface output-path)+135
(cairo-font-face-destroy cairo-face)+136
(cairo-surface-destroy surface))+137
+138
(ft-done-face face)+139
(ft-done-freetype ft-library)+140
(display (string-append "Wrote " output-path "\n")))package.sgladded
@@ -0,0 +1,12 @@
+1
(package+2
name: "sigil-harfbuzz"+3
version: "0.7.0"+4
description: "HarfBuzz text shaping bindings for Sigil"+5
url: "https://codeberg.org/sigil/sigil"+6
license: "BSD-3-Clause"+7
authors: (list "David Wilson <[email protected]>")+8
+9
dependencies: (list+10
(from-workspace name: "sigil-stdlib")+11
(from-workspace name: "sigil-ffi")+12
(from-workspace name: "sigil-freetype")))src/harfbuzz.sgladded
@@ -0,0 +1,359 @@
+1
;;; (harfbuzz) — HarfBuzz text shaping bindings for Sigil+2
;;;+3
;;; Pure-Scheme bindings using the dynamic FFI. No native C code required.+4
;;; Requires libharfbuzz.so / libharfbuzz.dylib to be installed on the system.+5
+6
(define-library (harfbuzz)+7
(import (sigil core)+8
(sigil ffi)+9
(sigil math))+10
+11
(export+12
;; Library handle+13
hb-lib+14
+15
;; Constants — Direction+16
HB_DIRECTION_INVALID+17
HB_DIRECTION_LTR+18
HB_DIRECTION_RTL+19
HB_DIRECTION_TTB+20
HB_DIRECTION_BTT+21
+22
;; Constants — Scripts+23
HB_SCRIPT_COMMON+24
HB_SCRIPT_LATIN+25
HB_SCRIPT_ARABIC+26
HB_SCRIPT_HAN+27
HB_SCRIPT_CYRILLIC+28
HB_SCRIPT_DEVANAGARI+29
HB_SCRIPT_GREEK+30
HB_SCRIPT_HANGUL+31
HB_SCRIPT_HIRAGANA+32
HB_SCRIPT_KATAKANA+33
HB_SCRIPT_THAI+34
HB_SCRIPT_HEBREW+35
+36
;; Tag helpers+37
hb-tag+38
hb-tag-string+39
+40
;; Struct layouts+41
hb-glyph-info-layout+42
hb-glyph-position-layout+43
+44
;; Buffer lifecycle+45
hb-buffer-create+46
hb-buffer-destroy+47
+48
;; Buffer configuration+49
hb-buffer-add-utf8+50
hb-buffer-set-direction+51
hb-buffer-set-script+52
hb-buffer-set-language+53
hb-buffer-guess-segment-properties+54
+55
;; Shaping+56
hb-shape+57
+58
;; Glyph access+59
hb-buffer-get-length+60
hb-buffer-get-glyph-infos+61
hb-buffer-get-glyph-positions+62
+63
;; Font lifecycle+64
hb-ft-font-create+65
hb-font-destroy)+66
+67
(begin+68
+69
;; ============================================================+70
;; Library Loading+71
;; ============================================================+72
+73
(define hb-lib (c-library "libharfbuzz"))+74
+75
;; ============================================================+76
;; Constants — Direction+77
;; ============================================================+78
+79
(define HB_DIRECTION_INVALID 0)+80
(define HB_DIRECTION_LTR 4)+81
(define HB_DIRECTION_RTL 5)+82
(define HB_DIRECTION_TTB 6)+83
(define HB_DIRECTION_BTT 7)+84
+85
;; ============================================================+86
;; Tag Helpers+87
;; ============================================================+88
+89
;;; Create a HarfBuzz 4-byte tag from four integer byte values.+90
(define (hb-tag c1 c2 c3 c4)+91
(: integer? integer? integer? integer? -> integer?)+92
(bitwise-ior+93
(arithmetic-shift (bitwise-and c1 #xFF) 24)+94
(arithmetic-shift (bitwise-and c2 #xFF) 16)+95
(arithmetic-shift (bitwise-and c3 #xFF) 8)+96
(bitwise-and c4 #xFF)))+97
+98
;;; Create a HarfBuzz tag from a 4-character string.+99
;;;+100
;;; Examples:+101
;;; ```scheme+102
;;; (hb-tag-string "Latn") ; => Latin script tag+103
;;; ```+104
(define (hb-tag-string s)+105
(: string? -> integer?)+106
(hb-tag (char->integer (string-ref s 0))+107
(char->integer (string-ref s 1))+108
(char->integer (string-ref s 2))+109
(char->integer (string-ref s 3))))+110
+111
;; ============================================================+112
;; Constants — Scripts (ISO 15924 4-char tags)+113
;; ============================================================+114
+115
(define HB_SCRIPT_COMMON (hb-tag-string "Zyyy"))+116
(define HB_SCRIPT_LATIN (hb-tag-string "Latn"))+117
(define HB_SCRIPT_ARABIC (hb-tag-string "Arab"))+118
(define HB_SCRIPT_HAN (hb-tag-string "Hani"))+119
(define HB_SCRIPT_CYRILLIC (hb-tag-string "Cyrl"))+120
(define HB_SCRIPT_DEVANAGARI (hb-tag-string "Deva"))+121
(define HB_SCRIPT_GREEK (hb-tag-string "Grek"))+122
(define HB_SCRIPT_HANGUL (hb-tag-string "Hang"))+123
(define HB_SCRIPT_HIRAGANA (hb-tag-string "Hira"))+124
(define HB_SCRIPT_KATAKANA (hb-tag-string "Kana"))+125
(define HB_SCRIPT_THAI (hb-tag-string "Thai"))+126
(define HB_SCRIPT_HEBREW (hb-tag-string "Hebr"))+127
+128
;; ============================================================+129
;; Struct Layouts+130
;; ============================================================+131
+132
;; hb_glyph_info_t — 5 x uint32 = 20 bytes+133
(define hb-glyph-info-layout+134
(c-struct-layout+135
codepoint: ffi/uint32+136
mask: ffi/uint32+137
cluster: ffi/uint32+138
var1: ffi/uint32+139
var2: ffi/uint32))+140
+141
;; hb_glyph_position_t — 4 x int32 + 1 x uint32 = 20 bytes+142
(define hb-glyph-position-layout+143
(c-struct-layout+144
x-advance: ffi/int32+145
y-advance: ffi/int32+146
x-offset: ffi/int32+147
y-offset: ffi/int32+148
var: ffi/uint32))+149
+150
;; ============================================================+151
;; Finalizer Pointers (looked up once)+152
;; ============================================================+153
+154
(define %hb-buffer-destroy-ptr (c-symbol hb-lib "hb_buffer_destroy"))+155
(define %hb-font-destroy-ptr (c-symbol hb-lib "hb_font_destroy"))+156
+157
;; ============================================================+158
;; Raw Function Bindings+159
;; ============================================================+160
+161
(define %hb-buffer-create+162
(c-function hb-lib "hb_buffer_create" '() ffi/pointer))+163
+164
(define %hb-buffer-destroy+165
(c-function hb-lib "hb_buffer_destroy"+166
(list ffi/pointer) ffi/void))+167
+168
(define %hb-buffer-add-utf8+169
(c-function hb-lib "hb_buffer_add_utf8"+170
(list ffi/pointer ffi/string ffi/int ffi/uint ffi/int) ffi/void))+171
+172
(define %hb-buffer-set-direction+173
(c-function hb-lib "hb_buffer_set_direction"+174
(list ffi/pointer ffi/int) ffi/void))+175
+176
(define %hb-buffer-set-script+177
(c-function hb-lib "hb_buffer_set_script"+178
(list ffi/pointer ffi/uint32) ffi/void))+179
+180
(define %hb-buffer-set-language+181
(c-function hb-lib "hb_buffer_set_language"+182
(list ffi/pointer ffi/pointer) ffi/void))+183
+184
(define %hb-buffer-guess-segment-properties+185
(c-function hb-lib "hb_buffer_guess_segment_properties"+186
(list ffi/pointer) ffi/void))+187
+188
(define %hb-shape+189
(c-function hb-lib "hb_shape"+190
(list ffi/pointer ffi/pointer ffi/pointer ffi/uint) ffi/void))+191
+192
(define %hb-buffer-get-glyph-infos+193
(c-function hb-lib "hb_buffer_get_glyph_infos"+194
(list ffi/pointer ffi/pointer) ffi/pointer))+195
+196
(define %hb-buffer-get-glyph-positions+197
(c-function hb-lib "hb_buffer_get_glyph_positions"+198
(list ffi/pointer ffi/pointer) ffi/pointer))+199
+200
(define %hb-buffer-get-length+201
(c-function hb-lib "hb_buffer_get_length"+202
(list ffi/pointer) ffi/uint))+203
+204
(define %hb-ft-font-create-referenced+205
(c-function hb-lib "hb_ft_font_create_referenced"+206
(list ffi/pointer) ffi/pointer))+207
+208
(define %hb-font-destroy+209
(c-function hb-lib "hb_font_destroy"+210
(list ffi/pointer) ffi/void))+211
+212
(define %hb-language-from-string+213
(c-function hb-lib "hb_language_from_string"+214
(list ffi/string ffi/int) ffi/pointer))+215
+216
;; ============================================================+217
;; Public API — Buffer Lifecycle+218
;; ============================================================+219
+220
;;; Create a new HarfBuzz shaping buffer.+221
(define (hb-buffer-create)+222
(: -> pointer?)+223
(let ((buf (%hb-buffer-create)))+224
(set-pointer-finalizer! buf %hb-buffer-destroy-ptr)+225
buf))+226
+227
;;; Destroy a shaping buffer, freeing its resources.+228
(define (hb-buffer-destroy buf)+229
(: pointer? -> void?)+230
(set-pointer-finalizer! buf #f)+231
(%hb-buffer-destroy buf))+232
+233
;; ============================================================+234
;; Public API — Buffer Configuration+235
;; ============================================================+236
+237
;;; Add UTF-8 text to a shaping buffer.+238
(define (hb-buffer-add-utf8 buf text)+239
(: pointer? string? -> void?)+240
(%hb-buffer-add-utf8 buf text -1 0 -1))+241
+242
;;; Set the text direction for a buffer.+243
;;;+244
;;; Use HB_DIRECTION_LTR, HB_DIRECTION_RTL, etc.+245
(define (hb-buffer-set-direction buf direction)+246
(: pointer? integer? -> void?)+247
(%hb-buffer-set-direction buf direction))+248
+249
;;; Set the script for a buffer.+250
;;;+251
;;; Use script constants like HB_SCRIPT_LATIN, or create custom+252
;;; tags with hb-tag-string.+253
(define (hb-buffer-set-script buf script)+254
(: pointer? integer? -> void?)+255
(%hb-buffer-set-script buf script))+256
+257
;;; Set the language for a buffer from a BCP 47 string.+258
;;;+259
;;; Examples:+260
;;; ```scheme+261
;;; (hb-buffer-set-language buf "en")+262
;;; (hb-buffer-set-language buf "ar")+263
;;; ```+264
(define (hb-buffer-set-language buf lang-str)+265
(: pointer? string? -> void?)+266
(%hb-buffer-set-language buf (%hb-language-from-string lang-str -1)))+267
+268
;;; Auto-detect direction, script, and language from buffer content.+269
;;;+270
;;; Convenience function — call after adding text but before shaping.+271
(define (hb-buffer-guess-segment-properties buf)+272
(: pointer? -> void?)+273
(%hb-buffer-guess-segment-properties buf))+274
+275
;; ============================================================+276
;; Public API — Shaping+277
;; ============================================================+278
+279
;;; Shape text in a buffer using a font.+280
;;;+281
;;; Converts Unicode content in the buffer to positioned glyphs.+282
;;; Call hb-buffer-get-glyph-infos and hb-buffer-get-glyph-positions+283
;;; to retrieve the results.+284
(define (hb-shape font buf)+285
(: pointer? pointer? -> void?)+286
(%hb-shape font buf #f 0))+287
+288
;; ============================================================+289
;; Public API — Glyph Access+290
;; ============================================================+291
+292
;;; Get the number of glyphs in a shaped buffer.+293
(define (hb-buffer-get-length buf)+294
(: pointer? -> integer?)+295
(%hb-buffer-get-length buf))+296
+297
;;; Get glyph info from a shaped buffer as a list of dicts.+298
;;;+299
;;; Each dict has keys: codepoint: (glyph index after shaping),+300
;;; cluster: (index in original text).+301
(define (hb-buffer-get-glyph-infos buf)+302
(: pointer? -> list?)+303
(let* ((len-buf (c-alloc (c-sizeof ffi/uint)))+304
(infos-ptr (%hb-buffer-get-glyph-infos buf len-buf))+305
(len (pointer-ref len-buf ffi/uint 0))+306
(info-size (c-struct-size hb-glyph-info-layout)))+307
(c-free len-buf)+308
(let loop ((i 0) (result '()))+309
(if (>= i len)+310
(reverse result)+311
(let ((entry (pointer+ infos-ptr (* i info-size))))+312
(loop (+ i 1)+313
(cons (dict+314
codepoint: (c-struct-ref entry hb-glyph-info-layout codepoint:)+315
cluster: (c-struct-ref entry hb-glyph-info-layout cluster:))+316
result)))))))+317
+318
;;; Get glyph positions from a shaped buffer as a list of dicts.+319
;;;+320
;;; Each dict has keys: x-advance:, y-advance:, x-offset:, y-offset:.+321
;;; Values are in font units (typically 26.6 fixed-point).+322
(define (hb-buffer-get-glyph-positions buf)+323
(: pointer? -> list?)+324
(let* ((len-buf (c-alloc (c-sizeof ffi/uint)))+325
(pos-ptr (%hb-buffer-get-glyph-positions buf len-buf))+326
(len (pointer-ref len-buf ffi/uint 0))+327
(pos-size (c-struct-size hb-glyph-position-layout)))+328
(c-free len-buf)+329
(let loop ((i 0) (result '()))+330
(if (>= i len)+331
(reverse result)+332
(let ((entry (pointer+ pos-ptr (* i pos-size))))+333
(loop (+ i 1)+334
(cons (dict+335
x-advance: (c-struct-ref entry hb-glyph-position-layout x-advance:)+336
y-advance: (c-struct-ref entry hb-glyph-position-layout y-advance:)+337
x-offset: (c-struct-ref entry hb-glyph-position-layout x-offset:)+338
y-offset: (c-struct-ref entry hb-glyph-position-layout y-offset:))+339
result)))))))+340
+341
;; ============================================================+342
;; Public API — Font Lifecycle+343
;; ============================================================+344
+345
;;; Create a HarfBuzz font from a FreeType face.+346
;;;+347
;;; The FreeType face must have its size set before calling this.+348
;;; Uses hb_ft_font_create_referenced which manages FT_Face lifetime.+349
(define (hb-ft-font-create ft-face)+350
(: pointer? -> pointer?)+351
(let ((font (%hb-ft-font-create-referenced ft-face)))+352
(set-pointer-finalizer! font %hb-font-destroy-ptr)+353
font))+354
+355
;;; Destroy a HarfBuzz font, freeing its resources.+356
(define (hb-font-destroy font)+357
(: pointer? -> void?)+358
(set-pointer-finalizer! font #f)+359
(%hb-font-destroy font))))test/test-harfbuzz.sgladded
@@ -0,0 +1,115 @@
+1
;;; HarfBuzz test suite+2
;;; Requires libharfbuzz, libfreetype, and a TrueType font.+3
+4
(import (sigil core)+5
(sigil test)+6
(sigil ffi)+7
(sigil io)+8
(sigil process)+9
(freetype)+10
(harfbuzz))+11
+12
;; Find a test font using fc-match+13
(define (find-test-font)+14
(let ((env-font (getenv "SIGIL_TEST_FONT")))+15
(if env-font+16
env-font+17
(let ((fc-path (guard (exn (#t #f))+18
(call-with-process "fc-match" '("--format=%{file}" "sans")+19
(lambda (p)+20
(let ((line (read-line (process-stdout p))))+21
(if (eof-object? line) #f line)))))))+22
(if (and fc-path (file-exists? fc-path))+23
fc-path+24
(error "No test font found — set SIGIL_TEST_FONT or install fontconfig"))))))+25
+26
(define test-font-path (find-test-font))+27
+28
(test-group "harfbuzz"+29
+30
(test-group "Buffer lifecycle"+31
(test "create and destroy buffer"+32
(let ((buf (hb-buffer-create)))+33
(assert-true (pointer? buf))+34
(hb-buffer-destroy buf))))+35
+36
(test-group "Tag helpers"+37
(test "hb-tag creates correct value"+38
(let ((tag (hb-tag-string "Latn")))+39
(assert-true (integer? tag))+40
(assert-true (> tag 0))))+41
+42
(test "script constants are valid"+43
(assert-true (integer? HB_SCRIPT_LATIN))+44
(assert-true (integer? HB_SCRIPT_ARABIC))+45
(assert-true (not (= HB_SCRIPT_LATIN HB_SCRIPT_ARABIC)))))+46
+47
(test-group "Text shaping"+48
(test "shape Latin text"+49
(let* ((lib (ft-init))+50
(face (ft-new-face lib test-font-path)))+51
(ft-set-char-size face (* 16 64) 0 72 72)+52
(let ((font (hb-ft-font-create face))+53
(buf (hb-buffer-create)))+54
(hb-buffer-add-utf8 buf "Hello")+55
(hb-buffer-set-direction buf HB_DIRECTION_LTR)+56
(hb-buffer-set-script buf HB_SCRIPT_LATIN)+57
(hb-buffer-set-language buf "en")+58
(hb-shape font buf)+59
+60
(assert-equal 5 (hb-buffer-get-length buf))+61
+62
(let ((infos (hb-buffer-get-glyph-infos buf))+63
(positions (hb-buffer-get-glyph-positions buf)))+64
(assert-equal 5 (length infos))+65
(assert-equal 5 (length positions))+66
;; Each info should have a codepoint (glyph index)+67
(assert-true (> (dict-ref (car infos) codepoint:) 0))+68
;; Each position should have an x-advance+69
(assert-true (> (dict-ref (car positions) x-advance:) 0)))+70
+71
(hb-buffer-destroy buf)+72
(hb-font-destroy font))+73
(ft-done-face face)+74
(ft-done-freetype lib)))+75
+76
(test "guess segment properties"+77
(let* ((lib (ft-init))+78
(face (ft-new-face lib test-font-path)))+79
(ft-set-char-size face (* 16 64) 0 72 72)+80
(let ((font (hb-ft-font-create face))+81
(buf (hb-buffer-create)))+82
(hb-buffer-add-utf8 buf "Test")+83
(hb-buffer-guess-segment-properties buf)+84
(hb-shape font buf)+85
+86
(assert-true (> (hb-buffer-get-length buf) 0))+87
+88
(hb-buffer-destroy buf)+89
(hb-font-destroy font))+90
(ft-done-face face)+91
(ft-done-freetype lib)))+92
+93
(test "glyph positions have structure"+94
(let* ((lib (ft-init))+95
(face (ft-new-face lib test-font-path)))+96
(ft-set-char-size face (* 12 64) 0 96 96)+97
(let ((font (hb-ft-font-create face))+98
(buf (hb-buffer-create)))+99
(hb-buffer-add-utf8 buf "AB")+100
(hb-buffer-set-direction buf HB_DIRECTION_LTR)+101
(hb-buffer-set-script buf HB_SCRIPT_LATIN)+102
(hb-shape font buf)+103
+104
(let ((positions (hb-buffer-get-glyph-positions buf)))+105
(assert-equal 2 (length positions))+106
;; Both glyphs should have x-advance > 0+107
(assert-true (> (dict-ref (car positions) x-advance:) 0))+108
(assert-true (> (dict-ref (cadr positions) x-advance:) 0))+109
;; y-offset should be 0 for simple Latin+110
(assert-equal 0 (dict-ref (car positions) y-offset:)))+111
+112
(hb-buffer-destroy buf)+113
(hb-font-destroy font))+114
(ft-done-face face)+115
(ft-done-freetype lib)))))