harfbuzz-ruby 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +258 -0
- data/Rakefile +8 -0
- data/benchmark/shaping_bench.rb +77 -0
- data/examples/basic_shaping.rb +67 -0
- data/examples/glyph_outlines.rb +79 -0
- data/examples/opentype_features.rb +91 -0
- data/examples/render_svg.rb +112 -0
- data/examples/render_waterfall.rb +177 -0
- data/examples/variable_fonts.rb +73 -0
- data/lib/harfbuzz/aat/layout.rb +78 -0
- data/lib/harfbuzz/blob.rb +136 -0
- data/lib/harfbuzz/buffer.rb +497 -0
- data/lib/harfbuzz/c/aat/layout.rb +15 -0
- data/lib/harfbuzz/c/base.rb +114 -0
- data/lib/harfbuzz/c/blob.rb +23 -0
- data/lib/harfbuzz/c/buffer.rb +127 -0
- data/lib/harfbuzz/c/common.rb +39 -0
- data/lib/harfbuzz/c/draw.rb +22 -0
- data/lib/harfbuzz/c/enums.rb +146 -0
- data/lib/harfbuzz/c/face.rb +37 -0
- data/lib/harfbuzz/c/font.rb +88 -0
- data/lib/harfbuzz/c/font_funcs.rb +58 -0
- data/lib/harfbuzz/c/map.rb +28 -0
- data/lib/harfbuzz/c/ot/color.rb +32 -0
- data/lib/harfbuzz/c/ot/font.rb +7 -0
- data/lib/harfbuzz/c/ot/layout.rb +83 -0
- data/lib/harfbuzz/c/ot/math.rb +23 -0
- data/lib/harfbuzz/c/ot/meta.rb +10 -0
- data/lib/harfbuzz/c/ot/metrics.rb +16 -0
- data/lib/harfbuzz/c/ot/name.rb +13 -0
- data/lib/harfbuzz/c/ot/shape.rb +10 -0
- data/lib/harfbuzz/c/ot/var.rb +22 -0
- data/lib/harfbuzz/c/paint.rb +38 -0
- data/lib/harfbuzz/c/set.rb +42 -0
- data/lib/harfbuzz/c/shape.rb +11 -0
- data/lib/harfbuzz/c/shape_plan.rb +24 -0
- data/lib/harfbuzz/c/structs.rb +120 -0
- data/lib/harfbuzz/c/subset.rb +49 -0
- data/lib/harfbuzz/c/unicode.rb +40 -0
- data/lib/harfbuzz/c/version.rb +25 -0
- data/lib/harfbuzz/draw_funcs.rb +112 -0
- data/lib/harfbuzz/error.rb +27 -0
- data/lib/harfbuzz/face.rb +186 -0
- data/lib/harfbuzz/feature.rb +76 -0
- data/lib/harfbuzz/flags.rb +85 -0
- data/lib/harfbuzz/font.rb +404 -0
- data/lib/harfbuzz/font_funcs.rb +286 -0
- data/lib/harfbuzz/glyph_info.rb +35 -0
- data/lib/harfbuzz/glyph_position.rb +41 -0
- data/lib/harfbuzz/library.rb +98 -0
- data/lib/harfbuzz/map.rb +157 -0
- data/lib/harfbuzz/ot/color.rb +125 -0
- data/lib/harfbuzz/ot/font.rb +16 -0
- data/lib/harfbuzz/ot/layout.rb +583 -0
- data/lib/harfbuzz/ot/math.rb +111 -0
- data/lib/harfbuzz/ot/meta.rb +34 -0
- data/lib/harfbuzz/ot/metrics.rb +54 -0
- data/lib/harfbuzz/ot/name.rb +81 -0
- data/lib/harfbuzz/ot/shape.rb +34 -0
- data/lib/harfbuzz/ot/var.rb +116 -0
- data/lib/harfbuzz/paint_funcs.rb +134 -0
- data/lib/harfbuzz/set.rb +272 -0
- data/lib/harfbuzz/shape_plan.rb +115 -0
- data/lib/harfbuzz/shaping_result.rb +94 -0
- data/lib/harfbuzz/subset.rb +130 -0
- data/lib/harfbuzz/unicode_funcs.rb +201 -0
- data/lib/harfbuzz/variation.rb +49 -0
- data/lib/harfbuzz/version.rb +5 -0
- data/lib/harfbuzz-ffi.rb +4 -0
- data/lib/harfbuzz.rb +313 -0
- data/sig/harfbuzz.rbs +594 -0
- metadata +132 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HarfBuzz
|
|
4
|
+
# Wraps hb_font_t — a sized, positioned font instance
|
|
5
|
+
class Font
|
|
6
|
+
attr_reader :ptr
|
|
7
|
+
|
|
8
|
+
# Creates a Font from a Face
|
|
9
|
+
# @param face [Face] Font face
|
|
10
|
+
def initialize(face)
|
|
11
|
+
@face = face
|
|
12
|
+
@ptr = C.hb_font_create(face.ptr)
|
|
13
|
+
raise AllocationError, "Failed to create font" if @ptr.null?
|
|
14
|
+
|
|
15
|
+
register_finalizer
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Attaches a FontFuncs callback table to this font
|
|
19
|
+
# @param funcs [FontFuncs] Font funcs object (should be immutable)
|
|
20
|
+
# @param font_data [FFI::Pointer] User data pointer passed to callbacks (optional)
|
|
21
|
+
# @return [self]
|
|
22
|
+
def set_funcs(funcs, font_data: FFI::Pointer::NULL)
|
|
23
|
+
C.hb_font_set_funcs(@ptr, funcs.ptr, font_data, nil)
|
|
24
|
+
@funcs = funcs # keep alive
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Sets the font funcs (setter alias for set_funcs without font_data)
|
|
29
|
+
# @param funcs [FontFuncs] FontFuncs object
|
|
30
|
+
def funcs=(funcs)
|
|
31
|
+
set_funcs(funcs)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Creates a sub-font of this font
|
|
35
|
+
# @return [Font]
|
|
36
|
+
def create_sub_font
|
|
37
|
+
ptr = C.hb_font_create_sub_font(@ptr)
|
|
38
|
+
self.class.wrap_owned(ptr)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Returns the singleton empty font
|
|
42
|
+
# @return [Font]
|
|
43
|
+
def self.empty
|
|
44
|
+
wrap_borrowed(C.hb_font_get_empty)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Returns the face this font was created from (borrowed reference)
|
|
48
|
+
# @return [Face]
|
|
49
|
+
def face
|
|
50
|
+
ptr = C.hb_font_get_face(@ptr)
|
|
51
|
+
Face.wrap_borrowed(ptr)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns scale as [x_scale, y_scale]
|
|
55
|
+
# @return [Array<Integer>]
|
|
56
|
+
def scale
|
|
57
|
+
x_ptr = FFI::MemoryPointer.new(:int)
|
|
58
|
+
y_ptr = FFI::MemoryPointer.new(:int)
|
|
59
|
+
C.hb_font_get_scale(@ptr, x_ptr, y_ptr)
|
|
60
|
+
[x_ptr.read_int, y_ptr.read_int]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Sets font scale
|
|
64
|
+
# @param xy [Array<Integer>, Integer] [x, y] or single value for both
|
|
65
|
+
def scale=(xy)
|
|
66
|
+
x, y = Array(xy).length == 2 ? xy : [xy, xy]
|
|
67
|
+
C.hb_font_set_scale(@ptr, x, y)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Returns pixels per em as [x_ppem, y_ppem]
|
|
71
|
+
# @return [Array<Integer>]
|
|
72
|
+
def ppem
|
|
73
|
+
x_ptr = FFI::MemoryPointer.new(:uint)
|
|
74
|
+
y_ptr = FFI::MemoryPointer.new(:uint)
|
|
75
|
+
C.hb_font_get_ppem(@ptr, x_ptr, y_ptr)
|
|
76
|
+
[x_ptr.read_uint, y_ptr.read_uint]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Sets pixels per em
|
|
80
|
+
# @param xy [Array<Integer>] [x_ppem, y_ppem]
|
|
81
|
+
def ppem=(xy)
|
|
82
|
+
x, y = xy
|
|
83
|
+
C.hb_font_set_ppem(@ptr, x, y)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @return [Float] Points per em
|
|
87
|
+
def ptem
|
|
88
|
+
C.hb_font_get_ptem(@ptr)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @param pt [Float] Points per em
|
|
92
|
+
def ptem=(pt)
|
|
93
|
+
C.hb_font_set_ptem(@ptr, pt)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Returns synthetic bold as [x_embolden, y_embolden, in_place]
|
|
97
|
+
# @return [Array]
|
|
98
|
+
def synthetic_bold
|
|
99
|
+
x_ptr = FFI::MemoryPointer.new(:float)
|
|
100
|
+
y_ptr = FFI::MemoryPointer.new(:float)
|
|
101
|
+
ip_ptr = FFI::MemoryPointer.new(:int)
|
|
102
|
+
C.hb_font_get_synthetic_bold(@ptr, x_ptr, y_ptr, ip_ptr)
|
|
103
|
+
[x_ptr.read_float, y_ptr.read_float, C.from_hb_bool(ip_ptr.read_int)]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Sets synthetic bold
|
|
107
|
+
# @param xyz [Array] [x_embolden, y_embolden, in_place]
|
|
108
|
+
def synthetic_bold=(xyz)
|
|
109
|
+
x, y, ip = xyz
|
|
110
|
+
C.hb_font_set_synthetic_bold(@ptr, x, y, C.to_hb_bool(ip))
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# @return [Float] Synthetic slant
|
|
114
|
+
def synthetic_slant
|
|
115
|
+
C.hb_font_get_synthetic_slant(@ptr)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @param slant [Float] Synthetic slant
|
|
119
|
+
def synthetic_slant=(slant)
|
|
120
|
+
C.hb_font_set_synthetic_slant(@ptr, slant)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Sets variation axis values from an array of Variation objects
|
|
124
|
+
# @param variations [Array<Variation>]
|
|
125
|
+
def variations=(variations)
|
|
126
|
+
structs = variations.map(&:to_struct)
|
|
127
|
+
ptr = FFI::MemoryPointer.new(C::HbVariationT, structs.size)
|
|
128
|
+
structs.each_with_index do |s, i|
|
|
129
|
+
ptr.put_bytes(i * C::HbVariationT.size,
|
|
130
|
+
s.to_ptr.read_bytes(C::HbVariationT.size))
|
|
131
|
+
end
|
|
132
|
+
C.hb_font_set_variations(@ptr, ptr, structs.size)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Sets a single variation axis value
|
|
136
|
+
# @param tag [Integer] Axis tag
|
|
137
|
+
# @param value [Float] Axis value
|
|
138
|
+
def set_variation(tag, value)
|
|
139
|
+
C.hb_font_set_variation(@ptr, tag, value)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Sets design-space variation coordinates
|
|
143
|
+
# @param coords [Array<Float>]
|
|
144
|
+
def var_coords_design=(coords)
|
|
145
|
+
ptr = FFI::MemoryPointer.new(:float, coords.size)
|
|
146
|
+
ptr.put_array_of_float(0, coords)
|
|
147
|
+
C.hb_font_set_var_coords_design(@ptr, ptr, coords.size)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Returns design-space variation coordinates
|
|
151
|
+
# @return [Array<Float>]
|
|
152
|
+
def var_coords_design
|
|
153
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
154
|
+
coords_ptr = C.hb_font_get_var_coords_design(@ptr, count_ptr)
|
|
155
|
+
count = count_ptr.read_uint
|
|
156
|
+
return [] if coords_ptr.null? || count.zero?
|
|
157
|
+
|
|
158
|
+
coords_ptr.read_array_of_float(count)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Sets normalized variation coordinates
|
|
162
|
+
# @param coords [Array<Integer>]
|
|
163
|
+
def var_coords_normalized=(coords)
|
|
164
|
+
ptr = FFI::MemoryPointer.new(:int32, coords.size)
|
|
165
|
+
ptr.put_array_of_int32(0, coords)
|
|
166
|
+
C.hb_font_set_var_coords_normalized(@ptr, ptr, coords.size)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Returns normalized variation coordinates
|
|
170
|
+
# @return [Array<Integer>]
|
|
171
|
+
def var_coords_normalized
|
|
172
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
173
|
+
coords_ptr = C.hb_font_get_var_coords_normalized(@ptr, count_ptr)
|
|
174
|
+
count = count_ptr.read_uint
|
|
175
|
+
return [] if coords_ptr.null? || count.zero?
|
|
176
|
+
|
|
177
|
+
coords_ptr.read_array_of_int32(count)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# @return [Boolean] true if font is immutable
|
|
181
|
+
def immutable?
|
|
182
|
+
C.from_hb_bool(C.hb_font_is_immutable(@ptr))
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Makes the font immutable (thread-safe to share after this)
|
|
186
|
+
# @return [self]
|
|
187
|
+
def make_immutable!
|
|
188
|
+
C.hb_font_make_immutable(@ptr)
|
|
189
|
+
self
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Returns glyph ID for a codepoint (with optional variation selector)
|
|
193
|
+
# @param cp [Integer] Unicode codepoint
|
|
194
|
+
# @param variation_selector [Integer] Variation selector codepoint (0 = none)
|
|
195
|
+
# @return [Integer, nil] Glyph ID or nil if not found
|
|
196
|
+
def glyph(cp, variation_selector = 0)
|
|
197
|
+
glyph_ptr = FFI::MemoryPointer.new(:uint32)
|
|
198
|
+
ok = C.hb_font_get_glyph(@ptr, cp, variation_selector, glyph_ptr)
|
|
199
|
+
ok.zero? ? nil : glyph_ptr.read_uint32
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Returns glyph ID for a nominal (non-variation) codepoint
|
|
203
|
+
# @param cp [Integer] Unicode codepoint
|
|
204
|
+
# @return [Integer, nil] Glyph ID or nil if not found
|
|
205
|
+
def nominal_glyph(cp)
|
|
206
|
+
glyph_ptr = FFI::MemoryPointer.new(:uint32)
|
|
207
|
+
ok = C.hb_font_get_nominal_glyph(@ptr, cp, glyph_ptr)
|
|
208
|
+
ok.zero? ? nil : glyph_ptr.read_uint32
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Returns nominal glyph IDs for multiple codepoints in a single call
|
|
212
|
+
# @param codepoints [Array<Integer>] Unicode codepoints
|
|
213
|
+
# @return [Array<Integer>] Glyph IDs (0 for unmapped codepoints)
|
|
214
|
+
def nominal_glyphs(codepoints)
|
|
215
|
+
count = codepoints.size
|
|
216
|
+
return [] if count.zero?
|
|
217
|
+
|
|
218
|
+
cp_ptr = FFI::MemoryPointer.new(:uint32, count)
|
|
219
|
+
cp_ptr.put_array_of_uint32(0, codepoints)
|
|
220
|
+
glyph_ptr = FFI::MemoryPointer.new(:uint32, count)
|
|
221
|
+
C.hb_font_get_nominal_glyphs(@ptr, count, cp_ptr, 4, glyph_ptr, 4)
|
|
222
|
+
glyph_ptr.read_array_of_uint32(count)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Returns glyph ID for a variation sequence
|
|
226
|
+
# @param cp [Integer] Unicode codepoint
|
|
227
|
+
# @param selector [Integer] Variation selector
|
|
228
|
+
# @return [Integer, nil] Glyph ID or nil if not found
|
|
229
|
+
def variation_glyph(cp, selector)
|
|
230
|
+
glyph_ptr = FFI::MemoryPointer.new(:uint32)
|
|
231
|
+
ok = C.hb_font_get_variation_glyph(@ptr, cp, selector, glyph_ptr)
|
|
232
|
+
ok.zero? ? nil : glyph_ptr.read_uint32
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# @param glyph [Integer] Glyph ID
|
|
236
|
+
# @return [Integer] Horizontal advance in font units
|
|
237
|
+
def glyph_h_advance(glyph)
|
|
238
|
+
C.hb_font_get_glyph_h_advance(@ptr, glyph)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# @param glyph [Integer] Glyph ID
|
|
242
|
+
# @return [Integer] Vertical advance in font units
|
|
243
|
+
def glyph_v_advance(glyph)
|
|
244
|
+
C.hb_font_get_glyph_v_advance(@ptr, glyph)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Returns horizontal advances for multiple glyphs
|
|
248
|
+
# @param glyphs [Array<Integer>] Glyph IDs
|
|
249
|
+
# @return [Array<Integer>] Advances
|
|
250
|
+
def glyph_h_advances(glyphs)
|
|
251
|
+
count = glyphs.size
|
|
252
|
+
glyph_ptr = FFI::MemoryPointer.new(:uint32, count)
|
|
253
|
+
glyph_ptr.put_array_of_uint32(0, glyphs)
|
|
254
|
+
advance_ptr = FFI::MemoryPointer.new(:int32, count)
|
|
255
|
+
C.hb_font_get_glyph_h_advances(@ptr, count, glyph_ptr, 4, advance_ptr, 4)
|
|
256
|
+
advance_ptr.read_array_of_int32(count)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Returns vertical advances for multiple glyphs
|
|
260
|
+
# @param glyphs [Array<Integer>] Glyph IDs
|
|
261
|
+
# @return [Array<Integer>] Advances
|
|
262
|
+
def glyph_v_advances(glyphs)
|
|
263
|
+
count = glyphs.size
|
|
264
|
+
glyph_ptr = FFI::MemoryPointer.new(:uint32, count)
|
|
265
|
+
glyph_ptr.put_array_of_uint32(0, glyphs)
|
|
266
|
+
advance_ptr = FFI::MemoryPointer.new(:int32, count)
|
|
267
|
+
C.hb_font_get_glyph_v_advances(@ptr, count, glyph_ptr, 4, advance_ptr, 4)
|
|
268
|
+
advance_ptr.read_array_of_int32(count)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Returns horizontal origin for a glyph
|
|
272
|
+
# @param glyph [Integer] Glyph ID
|
|
273
|
+
# @return [Array<Integer>, nil] [x, y] or nil
|
|
274
|
+
def glyph_h_origin(glyph)
|
|
275
|
+
x_ptr = FFI::MemoryPointer.new(:int32)
|
|
276
|
+
y_ptr = FFI::MemoryPointer.new(:int32)
|
|
277
|
+
ok = C.hb_font_get_glyph_h_origin(@ptr, glyph, x_ptr, y_ptr)
|
|
278
|
+
ok.zero? ? nil : [x_ptr.read_int32, y_ptr.read_int32]
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Returns vertical origin for a glyph
|
|
282
|
+
# @param glyph [Integer] Glyph ID
|
|
283
|
+
# @return [Array<Integer>, nil] [x, y] or nil
|
|
284
|
+
def glyph_v_origin(glyph)
|
|
285
|
+
x_ptr = FFI::MemoryPointer.new(:int32)
|
|
286
|
+
y_ptr = FFI::MemoryPointer.new(:int32)
|
|
287
|
+
ok = C.hb_font_get_glyph_v_origin(@ptr, glyph, x_ptr, y_ptr)
|
|
288
|
+
ok.zero? ? nil : [x_ptr.read_int32, y_ptr.read_int32]
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Returns horizontal kerning between two glyphs
|
|
292
|
+
# @param left [Integer] Left glyph ID
|
|
293
|
+
# @param right [Integer] Right glyph ID
|
|
294
|
+
# @return [Integer] Kerning value
|
|
295
|
+
def glyph_h_kerning(left, right)
|
|
296
|
+
C.hb_font_get_glyph_h_kerning(@ptr, left, right)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Returns glyph extents
|
|
300
|
+
# @param glyph [Integer] Glyph ID
|
|
301
|
+
# @return [C::HbGlyphExtentsT, nil] Extents or nil
|
|
302
|
+
def glyph_extents(glyph)
|
|
303
|
+
extents = C::HbGlyphExtentsT.new
|
|
304
|
+
ok = C.hb_font_get_glyph_extents(@ptr, glyph, extents)
|
|
305
|
+
ok.zero? ? nil : extents
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Returns a contour point for a glyph
|
|
309
|
+
# @param glyph [Integer] Glyph ID
|
|
310
|
+
# @param idx [Integer] Contour point index
|
|
311
|
+
# @return [Array<Integer>, nil] [x, y] or nil
|
|
312
|
+
def glyph_contour_point(glyph, idx)
|
|
313
|
+
x_ptr = FFI::MemoryPointer.new(:int32)
|
|
314
|
+
y_ptr = FFI::MemoryPointer.new(:int32)
|
|
315
|
+
ok = C.hb_font_get_glyph_contour_point(@ptr, glyph, idx, x_ptr, y_ptr)
|
|
316
|
+
ok.zero? ? nil : [x_ptr.read_int32, y_ptr.read_int32]
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Returns the glyph name
|
|
320
|
+
# @param glyph [Integer] Glyph ID
|
|
321
|
+
# @return [String, nil] Glyph name or nil
|
|
322
|
+
def glyph_name(glyph)
|
|
323
|
+
buf = FFI::MemoryPointer.new(:char, 64)
|
|
324
|
+
ok = C.hb_font_get_glyph_name(@ptr, glyph, buf, 64)
|
|
325
|
+
ok.zero? ? nil : buf.read_string
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Returns the glyph ID for a name
|
|
329
|
+
# @param name [String] Glyph name
|
|
330
|
+
# @return [Integer, nil] Glyph ID or nil
|
|
331
|
+
def glyph_from_name(name)
|
|
332
|
+
glyph_ptr = FFI::MemoryPointer.new(:uint32)
|
|
333
|
+
ok = C.hb_font_get_glyph_from_name(@ptr, name, name.bytesize, glyph_ptr)
|
|
334
|
+
ok.zero? ? nil : glyph_ptr.read_uint32
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Returns glyph advance for a direction as [x, y]
|
|
338
|
+
# @param glyph [Integer] Glyph ID
|
|
339
|
+
# @param dir [Symbol] Direction (:ltr, :rtl, :ttb, :btt)
|
|
340
|
+
# @return [Array<Integer>] [x_advance, y_advance]
|
|
341
|
+
def glyph_advance_for_direction(glyph, dir)
|
|
342
|
+
x_ptr = FFI::MemoryPointer.new(:int32)
|
|
343
|
+
y_ptr = FFI::MemoryPointer.new(:int32)
|
|
344
|
+
C.hb_font_get_glyph_advance_for_direction(@ptr, glyph, dir, x_ptr, y_ptr)
|
|
345
|
+
[x_ptr.read_int32, y_ptr.read_int32]
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Returns font extents for a direction
|
|
349
|
+
# @param dir [Symbol] Direction (:ltr, :rtl, :ttb, :btt)
|
|
350
|
+
# @return [C::HbFontExtentsT]
|
|
351
|
+
def extents_for_direction(dir)
|
|
352
|
+
extents = C::HbFontExtentsT.new
|
|
353
|
+
C.hb_font_get_extents_for_direction(@ptr, dir, extents)
|
|
354
|
+
extents
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# Draws the outline of a glyph using DrawFuncs callbacks
|
|
358
|
+
# @param glyph [Integer] Glyph ID
|
|
359
|
+
# @param draw_funcs [DrawFuncs] Draw callbacks object
|
|
360
|
+
def draw_glyph(glyph, draw_funcs)
|
|
361
|
+
C.hb_font_draw_glyph(@ptr, glyph, draw_funcs.ptr, nil)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# Paints a glyph using PaintFuncs callbacks
|
|
365
|
+
# @param glyph [Integer] Glyph ID
|
|
366
|
+
# @param paint_funcs [PaintFuncs] Paint callbacks object
|
|
367
|
+
# @param palette_index [Integer] Color palette index (default 0)
|
|
368
|
+
# @param foreground [Integer] Foreground color as RGBA uint32 (default 0xFF000000)
|
|
369
|
+
def paint_glyph(glyph, paint_funcs, palette_index: 0, foreground: 0xFF000000)
|
|
370
|
+
C.hb_font_paint_glyph(@ptr, glyph, paint_funcs.ptr, nil, palette_index, foreground)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def inspect
|
|
374
|
+
"#<HarfBuzz::Font scale=#{scale} ppem=#{ppem} immutable=#{immutable?}>"
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def self.wrap_owned(ptr)
|
|
378
|
+
obj = allocate
|
|
379
|
+
obj.instance_variable_set(:@ptr, ptr)
|
|
380
|
+
obj.send(:register_finalizer)
|
|
381
|
+
obj
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def self.wrap_borrowed(ptr)
|
|
385
|
+
obj = allocate
|
|
386
|
+
obj.instance_variable_set(:@ptr, ptr)
|
|
387
|
+
obj.instance_variable_set(:@borrowed, true)
|
|
388
|
+
obj
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
private
|
|
392
|
+
|
|
393
|
+
def register_finalizer
|
|
394
|
+
return if instance_variable_defined?(:@borrowed) && @borrowed
|
|
395
|
+
|
|
396
|
+
HarfBuzz::Font.define_finalizer(self, @ptr)
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def self.define_finalizer(obj, ptr)
|
|
400
|
+
destroy = C.method(:hb_font_destroy)
|
|
401
|
+
ObjectSpace.define_finalizer(obj, proc { destroy.call(ptr) })
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
end
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HarfBuzz
|
|
4
|
+
# Wraps hb_font_funcs_t — customizable font callback table
|
|
5
|
+
#
|
|
6
|
+
# FontFuncs allows you to override how HarfBuzz queries glyph data from a font.
|
|
7
|
+
# This is used to implement custom font backends (e.g., connecting to FreeType,
|
|
8
|
+
# CoreText, or a custom renderer).
|
|
9
|
+
#
|
|
10
|
+
# @example Custom nominal glyph lookup
|
|
11
|
+
# funcs = HarfBuzz::FontFuncs.new
|
|
12
|
+
# funcs.on_nominal_glyph do |_font, codepoint|
|
|
13
|
+
# my_cmap[codepoint]
|
|
14
|
+
# end
|
|
15
|
+
# funcs.make_immutable!
|
|
16
|
+
# font.set_funcs(funcs)
|
|
17
|
+
class FontFuncs
|
|
18
|
+
attr_reader :ptr
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@ptr = C.hb_font_funcs_create
|
|
22
|
+
raise AllocationError, "Failed to create font funcs" if @ptr.null?
|
|
23
|
+
|
|
24
|
+
# Keep FFI::Function objects alive as long as this object is alive
|
|
25
|
+
@callbacks = {}
|
|
26
|
+
HarfBuzz::FontFuncs.define_finalizer(self, @ptr)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns the singleton empty font funcs
|
|
30
|
+
# @return [FontFuncs]
|
|
31
|
+
def self.empty
|
|
32
|
+
obj = allocate
|
|
33
|
+
obj.instance_variable_set(:@ptr, C.hb_font_funcs_get_empty)
|
|
34
|
+
obj.instance_variable_set(:@borrowed, true)
|
|
35
|
+
obj.instance_variable_set(:@callbacks, {})
|
|
36
|
+
obj
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [Boolean] true if immutable
|
|
40
|
+
def immutable?
|
|
41
|
+
C.from_hb_bool(C.hb_font_funcs_is_immutable(@ptr))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Makes the funcs table immutable (required before attaching to a font)
|
|
45
|
+
# @return [self]
|
|
46
|
+
def make_immutable!
|
|
47
|
+
C.hb_font_funcs_make_immutable(@ptr)
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Sets the horizontal font extents callback
|
|
52
|
+
# @yield [font] Called with the HarfBuzz font pointer
|
|
53
|
+
# @yieldreturn [Hash, nil] Hash with :ascender, :descender, :line_gap keys (in font units), or nil
|
|
54
|
+
def on_font_h_extents(&block)
|
|
55
|
+
cb = FFI::Function.new(:int, [:pointer, :pointer, :pointer, :pointer]) do |font_ptr, _font_data, extents_ptr, _user_data|
|
|
56
|
+
result = block.call(font_ptr)
|
|
57
|
+
next 0 unless result
|
|
58
|
+
|
|
59
|
+
extents = C::HbFontExtentsT.new(extents_ptr)
|
|
60
|
+
extents[:ascender] = result[:ascender] || 0
|
|
61
|
+
extents[:descender] = result[:descender] || 0
|
|
62
|
+
extents[:line_gap] = result[:line_gap] || 0
|
|
63
|
+
1
|
|
64
|
+
end
|
|
65
|
+
@callbacks[:font_h_extents] = cb
|
|
66
|
+
C.hb_font_funcs_set_font_h_extents_func(@ptr, cb, nil, nil)
|
|
67
|
+
self
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Sets the vertical font extents callback
|
|
71
|
+
# @yield [font] Called with the HarfBuzz font pointer
|
|
72
|
+
# @yieldreturn [Hash, nil] Hash with :ascender, :descender, :line_gap keys, or nil
|
|
73
|
+
def on_font_v_extents(&block)
|
|
74
|
+
cb = FFI::Function.new(:int, [:pointer, :pointer, :pointer, :pointer]) do |font_ptr, _font_data, extents_ptr, _user_data|
|
|
75
|
+
result = block.call(font_ptr)
|
|
76
|
+
next 0 unless result
|
|
77
|
+
|
|
78
|
+
extents = C::HbFontExtentsT.new(extents_ptr)
|
|
79
|
+
extents[:ascender] = result[:ascender] || 0
|
|
80
|
+
extents[:descender] = result[:descender] || 0
|
|
81
|
+
extents[:line_gap] = result[:line_gap] || 0
|
|
82
|
+
1
|
|
83
|
+
end
|
|
84
|
+
@callbacks[:font_v_extents] = cb
|
|
85
|
+
C.hb_font_funcs_set_font_v_extents_func(@ptr, cb, nil, nil)
|
|
86
|
+
self
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Sets the nominal glyph lookup callback
|
|
90
|
+
# @yield [font, codepoint] Called with font pointer and Unicode codepoint
|
|
91
|
+
# @yieldreturn [Integer, nil] Glyph ID or nil if not found
|
|
92
|
+
def on_nominal_glyph(&block)
|
|
93
|
+
cb = FFI::Function.new(:int, [:pointer, :pointer, :uint32, :pointer, :pointer]) do |font_ptr, _font_data, codepoint, glyph_out, _user_data|
|
|
94
|
+
glyph_id = block.call(font_ptr, codepoint)
|
|
95
|
+
next 0 unless glyph_id
|
|
96
|
+
|
|
97
|
+
glyph_out.write_uint32(glyph_id)
|
|
98
|
+
1
|
|
99
|
+
end
|
|
100
|
+
@callbacks[:nominal_glyph] = cb
|
|
101
|
+
C.hb_font_funcs_set_nominal_glyph_func(@ptr, cb, nil, nil)
|
|
102
|
+
self
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Sets the variation glyph lookup callback
|
|
106
|
+
# @yield [font, codepoint, variation_selector] Called with font pointer, codepoint, and selector
|
|
107
|
+
# @yieldreturn [Integer, nil] Glyph ID or nil if not found
|
|
108
|
+
def on_variation_glyph(&block)
|
|
109
|
+
cb = FFI::Function.new(:int, [:pointer, :pointer, :uint32, :uint32, :pointer, :pointer]) do |font_ptr, _font_data, codepoint, selector, glyph_out, _user_data|
|
|
110
|
+
glyph_id = block.call(font_ptr, codepoint, selector)
|
|
111
|
+
next 0 unless glyph_id
|
|
112
|
+
|
|
113
|
+
glyph_out.write_uint32(glyph_id)
|
|
114
|
+
1
|
|
115
|
+
end
|
|
116
|
+
@callbacks[:variation_glyph] = cb
|
|
117
|
+
C.hb_font_funcs_set_variation_glyph_func(@ptr, cb, nil, nil)
|
|
118
|
+
self
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Sets the horizontal glyph advance callback
|
|
122
|
+
# @yield [font, glyph] Called with font pointer and glyph ID
|
|
123
|
+
# @yieldreturn [Integer] Horizontal advance in font units
|
|
124
|
+
def on_glyph_h_advance(&block)
|
|
125
|
+
cb = FFI::Function.new(:int32, [:pointer, :pointer, :uint32, :pointer]) do |font_ptr, _font_data, glyph, _user_data|
|
|
126
|
+
block.call(font_ptr, glyph).to_i
|
|
127
|
+
end
|
|
128
|
+
@callbacks[:glyph_h_advance] = cb
|
|
129
|
+
C.hb_font_funcs_set_glyph_h_advance_func(@ptr, cb, nil, nil)
|
|
130
|
+
self
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Sets the vertical glyph advance callback
|
|
134
|
+
# @yield [font, glyph] Called with font pointer and glyph ID
|
|
135
|
+
# @yieldreturn [Integer] Vertical advance in font units
|
|
136
|
+
def on_glyph_v_advance(&block)
|
|
137
|
+
cb = FFI::Function.new(:int32, [:pointer, :pointer, :uint32, :pointer]) do |font_ptr, _font_data, glyph, _user_data|
|
|
138
|
+
block.call(font_ptr, glyph).to_i
|
|
139
|
+
end
|
|
140
|
+
@callbacks[:glyph_v_advance] = cb
|
|
141
|
+
C.hb_font_funcs_set_glyph_v_advance_func(@ptr, cb, nil, nil)
|
|
142
|
+
self
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Sets the horizontal glyph origin callback
|
|
146
|
+
# @yield [font, glyph] Called with font pointer and glyph ID
|
|
147
|
+
# @yieldreturn [Array<Integer>, nil] [x, y] origin or nil
|
|
148
|
+
def on_glyph_h_origin(&block)
|
|
149
|
+
cb = FFI::Function.new(:int, [:pointer, :pointer, :uint32, :pointer, :pointer, :pointer]) do |font_ptr, _font_data, glyph, x_out, y_out, _user_data|
|
|
150
|
+
result = block.call(font_ptr, glyph)
|
|
151
|
+
next 0 unless result
|
|
152
|
+
|
|
153
|
+
x_out.write_int32(result[0])
|
|
154
|
+
y_out.write_int32(result[1])
|
|
155
|
+
1
|
|
156
|
+
end
|
|
157
|
+
@callbacks[:glyph_h_origin] = cb
|
|
158
|
+
C.hb_font_funcs_set_glyph_h_origin_func(@ptr, cb, nil, nil)
|
|
159
|
+
self
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Sets the vertical glyph origin callback
|
|
163
|
+
# @yield [font, glyph] Called with font pointer and glyph ID
|
|
164
|
+
# @yieldreturn [Array<Integer>, nil] [x, y] origin or nil
|
|
165
|
+
def on_glyph_v_origin(&block)
|
|
166
|
+
cb = FFI::Function.new(:int, [:pointer, :pointer, :uint32, :pointer, :pointer, :pointer]) do |font_ptr, _font_data, glyph, x_out, y_out, _user_data|
|
|
167
|
+
result = block.call(font_ptr, glyph)
|
|
168
|
+
next 0 unless result
|
|
169
|
+
|
|
170
|
+
x_out.write_int32(result[0])
|
|
171
|
+
y_out.write_int32(result[1])
|
|
172
|
+
1
|
|
173
|
+
end
|
|
174
|
+
@callbacks[:glyph_v_origin] = cb
|
|
175
|
+
C.hb_font_funcs_set_glyph_v_origin_func(@ptr, cb, nil, nil)
|
|
176
|
+
self
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Sets the horizontal glyph kerning callback
|
|
180
|
+
# @yield [font, left_glyph, right_glyph] Called with font pointer and two glyph IDs
|
|
181
|
+
# @yieldreturn [Integer] Kerning value in font units
|
|
182
|
+
def on_glyph_h_kerning(&block)
|
|
183
|
+
cb = FFI::Function.new(:int32, [:pointer, :pointer, :uint32, :uint32, :pointer]) do |font_ptr, _font_data, left, right, _user_data|
|
|
184
|
+
block.call(font_ptr, left, right).to_i
|
|
185
|
+
end
|
|
186
|
+
@callbacks[:glyph_h_kerning] = cb
|
|
187
|
+
C.hb_font_funcs_set_glyph_h_kerning_func(@ptr, cb, nil, nil)
|
|
188
|
+
self
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Sets the glyph extents callback
|
|
192
|
+
# @yield [font, glyph] Called with font pointer and glyph ID
|
|
193
|
+
# @yieldreturn [Hash, nil] Hash with :x_bearing, :y_bearing, :width, :height keys, or nil
|
|
194
|
+
def on_glyph_extents(&block)
|
|
195
|
+
cb = FFI::Function.new(:int, [:pointer, :pointer, :uint32, :pointer, :pointer]) do |font_ptr, _font_data, glyph, extents_ptr, _user_data|
|
|
196
|
+
result = block.call(font_ptr, glyph)
|
|
197
|
+
next 0 unless result
|
|
198
|
+
|
|
199
|
+
extents = C::HbGlyphExtentsT.new(extents_ptr)
|
|
200
|
+
extents[:x_bearing] = result[:x_bearing] || 0
|
|
201
|
+
extents[:y_bearing] = result[:y_bearing] || 0
|
|
202
|
+
extents[:width] = result[:width] || 0
|
|
203
|
+
extents[:height] = result[:height] || 0
|
|
204
|
+
1
|
|
205
|
+
end
|
|
206
|
+
@callbacks[:glyph_extents] = cb
|
|
207
|
+
C.hb_font_funcs_set_glyph_extents_func(@ptr, cb, nil, nil)
|
|
208
|
+
self
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Sets the glyph name callback
|
|
212
|
+
# @yield [font, glyph] Called with font pointer and glyph ID
|
|
213
|
+
# @yieldreturn [String, nil] Glyph name or nil
|
|
214
|
+
def on_glyph_name(&block)
|
|
215
|
+
cb = FFI::Function.new(:int, [:pointer, :pointer, :uint32, :pointer, :uint, :pointer]) do |font_ptr, _font_data, glyph, name_buf, name_buf_size, _user_data|
|
|
216
|
+
name = block.call(font_ptr, glyph)
|
|
217
|
+
next 0 unless name
|
|
218
|
+
|
|
219
|
+
bytes = name.bytesize
|
|
220
|
+
size = [bytes, name_buf_size - 1].min
|
|
221
|
+
name_buf.put_bytes(0, name, 0, size)
|
|
222
|
+
name_buf.put_uint8(size, 0) # null terminate
|
|
223
|
+
1
|
|
224
|
+
end
|
|
225
|
+
@callbacks[:glyph_name] = cb
|
|
226
|
+
C.hb_font_funcs_set_glyph_name_func(@ptr, cb, nil, nil)
|
|
227
|
+
self
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Sets the glyph-from-name lookup callback
|
|
231
|
+
# @yield [font, name] Called with font pointer and glyph name string
|
|
232
|
+
# @yieldreturn [Integer, nil] Glyph ID or nil if not found
|
|
233
|
+
def on_glyph_from_name(&block)
|
|
234
|
+
cb = FFI::Function.new(:int, [:pointer, :pointer, :pointer, :int, :pointer, :pointer]) do |font_ptr, _font_data, name_ptr, name_len, glyph_out, _user_data|
|
|
235
|
+
name = name_len < 0 ? name_ptr.read_string : name_ptr.read_bytes(name_len)
|
|
236
|
+
glyph_id = block.call(font_ptr, name)
|
|
237
|
+
next 0 unless glyph_id
|
|
238
|
+
|
|
239
|
+
glyph_out.write_uint32(glyph_id)
|
|
240
|
+
1
|
|
241
|
+
end
|
|
242
|
+
@callbacks[:glyph_from_name] = cb
|
|
243
|
+
C.hb_font_funcs_set_glyph_from_name_func(@ptr, cb, nil, nil)
|
|
244
|
+
self
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Sets the glyph contour point callback
|
|
248
|
+
# @yield [font, glyph, point_index] Called with font pointer, glyph ID, and point index
|
|
249
|
+
# @yieldreturn [Array<Integer>, nil] [x, y] contour point coordinates, or nil if not found
|
|
250
|
+
def on_glyph_contour_point(&block)
|
|
251
|
+
cb = FFI::Function.new(:int, [:pointer, :pointer, :uint32, :uint, :pointer, :pointer, :pointer]) do |font_ptr, _font_data, glyph, point_index, x_out, y_out, _user_data|
|
|
252
|
+
result = block.call(font_ptr, glyph, point_index)
|
|
253
|
+
next 0 unless result
|
|
254
|
+
|
|
255
|
+
x_out.write_int32(result[0])
|
|
256
|
+
y_out.write_int32(result[1])
|
|
257
|
+
1
|
|
258
|
+
end
|
|
259
|
+
@callbacks[:glyph_contour_point] = cb
|
|
260
|
+
C.hb_font_funcs_set_glyph_contour_point_func(@ptr, cb, nil, nil)
|
|
261
|
+
self
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Sets the draw glyph callback (called when font draws a glyph via draw funcs)
|
|
265
|
+
# @yield [font, glyph, draw_funcs_ptr, draw_data_ptr] Called with font pointer, glyph ID, and draw state
|
|
266
|
+
def on_draw_glyph(&block)
|
|
267
|
+
cb = FFI::Function.new(:void, [:pointer, :pointer, :uint32, :pointer, :pointer, :pointer]) do |font_ptr, _font_data, glyph, draw_funcs_ptr, draw_data_ptr, _user_data|
|
|
268
|
+
block.call(font_ptr, glyph, draw_funcs_ptr, draw_data_ptr)
|
|
269
|
+
end
|
|
270
|
+
@callbacks[:draw_glyph] = cb
|
|
271
|
+
C.hb_font_funcs_set_draw_glyph_func(@ptr, cb, nil, nil)
|
|
272
|
+
self
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def inspect
|
|
276
|
+
"#<HarfBuzz::FontFuncs immutable=#{immutable?}>"
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def self.define_finalizer(obj, ptr)
|
|
280
|
+
return if obj.instance_variable_defined?(:@borrowed) && obj.instance_variable_get(:@borrowed)
|
|
281
|
+
|
|
282
|
+
destroy = C.method(:hb_font_funcs_destroy)
|
|
283
|
+
ObjectSpace.define_finalizer(obj, proc { destroy.call(ptr) })
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|