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,583 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HarfBuzz
|
|
4
|
+
module OT
|
|
5
|
+
# OpenType Layout table queries (GSUB/GPOS)
|
|
6
|
+
module Layout
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# @param face [Face] Font face
|
|
10
|
+
# @return [Boolean] true if the face has GDEF glyph classes
|
|
11
|
+
def has_glyph_classes?(face)
|
|
12
|
+
C.from_hb_bool(C.hb_ot_layout_has_glyph_classes(face.ptr))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @param face [Face] Font face
|
|
16
|
+
# @return [Boolean] true if the face has a GSUB table
|
|
17
|
+
def has_substitution?(face)
|
|
18
|
+
C.from_hb_bool(C.hb_ot_layout_has_substitution(face.ptr))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @param face [Face] Font face
|
|
22
|
+
# @return [Boolean] true if the face has a GPOS table
|
|
23
|
+
def has_positioning?(face)
|
|
24
|
+
C.from_hb_bool(C.hb_ot_layout_has_positioning(face.ptr))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Returns the glyph class from GDEF
|
|
28
|
+
# @param face [Face] Font face
|
|
29
|
+
# @param glyph [Integer] Glyph ID
|
|
30
|
+
# @return [Symbol] Glyph class (:base_glyph, :ligature, :mark, :component)
|
|
31
|
+
def glyph_class(face, glyph)
|
|
32
|
+
C.hb_ot_layout_get_glyph_class(face.ptr, glyph)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns all glyphs in a given GDEF class
|
|
36
|
+
# @param face [Face] Font face
|
|
37
|
+
# @param klass [Symbol] Glyph class
|
|
38
|
+
# @return [HarfBuzz::Set] Set of glyph IDs
|
|
39
|
+
def glyphs_in_class(face, klass)
|
|
40
|
+
set = HarfBuzz::Set.new
|
|
41
|
+
C.hb_ot_layout_get_glyphs_in_class(face.ptr, klass, set.ptr)
|
|
42
|
+
set
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns the script tags for a table
|
|
46
|
+
# @param face [Face] Font face
|
|
47
|
+
# @param table_tag [Integer] GSUB (0x47535542) or GPOS (0x47504F53)
|
|
48
|
+
# @return [Array<Integer>] Script tags
|
|
49
|
+
def script_tags(face, table_tag)
|
|
50
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
51
|
+
count_ptr.write_uint(0)
|
|
52
|
+
C.hb_ot_layout_table_get_script_tags(face.ptr, table_tag, 0, count_ptr, nil)
|
|
53
|
+
count = count_ptr.read_uint
|
|
54
|
+
return [] if count.zero?
|
|
55
|
+
|
|
56
|
+
tags_ptr = FFI::MemoryPointer.new(:uint32, count)
|
|
57
|
+
count_ptr.write_uint(count)
|
|
58
|
+
C.hb_ot_layout_table_get_script_tags(face.ptr, table_tag, 0, count_ptr, tags_ptr)
|
|
59
|
+
tags_ptr.read_array_of_uint32(count_ptr.read_uint)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Returns attachment point list for a glyph
|
|
63
|
+
# @param face [Face] Font face
|
|
64
|
+
# @param glyph [Integer] Glyph ID
|
|
65
|
+
# @return [Array<Integer>] Attachment point indices
|
|
66
|
+
def attach_points(face, glyph)
|
|
67
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
68
|
+
count_ptr.write_uint(0)
|
|
69
|
+
total = C.hb_ot_layout_get_attach_points(face.ptr, glyph, 0, count_ptr, nil)
|
|
70
|
+
return [] if total.zero?
|
|
71
|
+
|
|
72
|
+
points_ptr = FFI::MemoryPointer.new(:uint, total)
|
|
73
|
+
count_ptr.write_uint(total)
|
|
74
|
+
C.hb_ot_layout_get_attach_points(face.ptr, glyph, 0, count_ptr, points_ptr)
|
|
75
|
+
points_ptr.read_array_of_uint(count_ptr.read_uint)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Returns ligature caret positions for a glyph
|
|
79
|
+
# @param font [Font] Font
|
|
80
|
+
# @param dir [Symbol] Direction (:ltr, :rtl)
|
|
81
|
+
# @param glyph [Integer] Glyph ID
|
|
82
|
+
# @return [Array<Integer>] Caret positions
|
|
83
|
+
def ligature_carets(font, dir, glyph)
|
|
84
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
85
|
+
count_ptr.write_uint(0)
|
|
86
|
+
total = C.hb_ot_layout_get_ligature_carets(font.ptr, dir, glyph, 0, count_ptr, nil)
|
|
87
|
+
return [] if total.zero?
|
|
88
|
+
|
|
89
|
+
carets_ptr = FFI::MemoryPointer.new(:int32, total)
|
|
90
|
+
count_ptr.write_uint(total)
|
|
91
|
+
C.hb_ot_layout_get_ligature_carets(font.ptr, dir, glyph, 0, count_ptr, carets_ptr)
|
|
92
|
+
carets_ptr.read_array_of_int32(count_ptr.read_uint)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Finds a script in the table, returns [found, index]
|
|
96
|
+
# @param face [Face] Font face
|
|
97
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
98
|
+
# @param script_tag [Integer] Script tag to find
|
|
99
|
+
# @return [Array] [Boolean, Integer] found and script index
|
|
100
|
+
def find_script(face, table_tag, script_tag)
|
|
101
|
+
idx_ptr = FFI::MemoryPointer.new(:uint)
|
|
102
|
+
found = C.from_hb_bool(
|
|
103
|
+
C.hb_ot_layout_table_find_script(face.ptr, table_tag, script_tag, idx_ptr)
|
|
104
|
+
)
|
|
105
|
+
[found, idx_ptr.read_uint]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Selects the best matching script from a list
|
|
109
|
+
# @param face [Face] Font face
|
|
110
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
111
|
+
# @param script_tags [Array<Integer>] Candidate script tags (in preference order)
|
|
112
|
+
# @return [Array] [Boolean, Integer, Integer] exact_match, script_index, chosen_script_tag
|
|
113
|
+
def select_script(face, table_tag, script_tags)
|
|
114
|
+
tags_ptr = FFI::MemoryPointer.new(:uint32, script_tags.size)
|
|
115
|
+
tags_ptr.put_array_of_uint32(0, script_tags)
|
|
116
|
+
idx_ptr = FFI::MemoryPointer.new(:uint)
|
|
117
|
+
chosen_tag_ptr = FFI::MemoryPointer.new(:uint32)
|
|
118
|
+
exact = C.from_hb_bool(
|
|
119
|
+
C.hb_ot_layout_table_select_script(
|
|
120
|
+
face.ptr, table_tag, script_tags.size, tags_ptr, idx_ptr, chosen_tag_ptr
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
[exact, idx_ptr.read_uint, chosen_tag_ptr.read_uint32]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Returns language tags for a script
|
|
127
|
+
# @param face [Face] Font face
|
|
128
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
129
|
+
# @param script_index [Integer] Script index
|
|
130
|
+
# @return [Array<Integer>] Language tags
|
|
131
|
+
def language_tags(face, table_tag, script_index)
|
|
132
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
133
|
+
count_ptr.write_uint(0)
|
|
134
|
+
total = C.hb_ot_layout_script_get_language_tags(
|
|
135
|
+
face.ptr, table_tag, script_index, 0, count_ptr, nil
|
|
136
|
+
)
|
|
137
|
+
return [] if total.zero?
|
|
138
|
+
|
|
139
|
+
tags_ptr = FFI::MemoryPointer.new(:uint32, total)
|
|
140
|
+
count_ptr.write_uint(total)
|
|
141
|
+
C.hb_ot_layout_script_get_language_tags(
|
|
142
|
+
face.ptr, table_tag, script_index, 0, count_ptr, tags_ptr
|
|
143
|
+
)
|
|
144
|
+
tags_ptr.read_array_of_uint32(count_ptr.read_uint)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Selects the best matching language, returns [found, language_index]
|
|
148
|
+
# @param face [Face] Font face
|
|
149
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
150
|
+
# @param script_index [Integer] Script index
|
|
151
|
+
# @param language_tags [Array<Integer>] Candidate language tags
|
|
152
|
+
# @return [Array] [Boolean, Integer] found and language index
|
|
153
|
+
def select_language(face, table_tag, script_index, language_tags)
|
|
154
|
+
tags_ptr = FFI::MemoryPointer.new(:uint32, language_tags.size)
|
|
155
|
+
tags_ptr.put_array_of_uint32(0, language_tags)
|
|
156
|
+
lang_idx_ptr = FFI::MemoryPointer.new(:uint)
|
|
157
|
+
found = C.from_hb_bool(
|
|
158
|
+
C.hb_ot_layout_script_select_language(
|
|
159
|
+
face.ptr, table_tag, script_index,
|
|
160
|
+
language_tags.size, tags_ptr, lang_idx_ptr
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
[found, lang_idx_ptr.read_uint]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Returns the required feature index for a language, or nil if none
|
|
167
|
+
# @param face [Face] Font face
|
|
168
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
169
|
+
# @param script_index [Integer] Script index
|
|
170
|
+
# @param language_index [Integer] Language index
|
|
171
|
+
# @return [Integer, nil] Feature index or nil
|
|
172
|
+
def required_feature_index(face, table_tag, script_index, language_index)
|
|
173
|
+
idx_ptr = FFI::MemoryPointer.new(:uint)
|
|
174
|
+
found = C.from_hb_bool(
|
|
175
|
+
C.hb_ot_layout_language_get_required_feature_index(
|
|
176
|
+
face.ptr, table_tag, script_index, language_index, idx_ptr
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
found ? idx_ptr.read_uint : nil
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Returns [feature_index, feature_tag] for the required feature, or nil
|
|
183
|
+
# @param face [Face] Font face
|
|
184
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
185
|
+
# @param script_index [Integer] Script index
|
|
186
|
+
# @param language_index [Integer] Language index
|
|
187
|
+
# @return [Array, nil] [feature_index, feature_tag] or nil
|
|
188
|
+
def required_feature(face, table_tag, script_index, language_index)
|
|
189
|
+
idx_ptr = FFI::MemoryPointer.new(:uint)
|
|
190
|
+
tag_ptr = FFI::MemoryPointer.new(:uint32)
|
|
191
|
+
found = C.from_hb_bool(
|
|
192
|
+
C.hb_ot_layout_language_get_required_feature(
|
|
193
|
+
face.ptr, table_tag, script_index, language_index, idx_ptr, tag_ptr
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
found ? [idx_ptr.read_uint, tag_ptr.read_uint32] : nil
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Returns feature indices for a language
|
|
200
|
+
# @param face [Face] Font face
|
|
201
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
202
|
+
# @param script_index [Integer] Script index
|
|
203
|
+
# @param language_index [Integer] Language index
|
|
204
|
+
# @return [Array<Integer>] Feature indices
|
|
205
|
+
def feature_indexes(face, table_tag, script_index, language_index)
|
|
206
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
207
|
+
count_ptr.write_uint(0)
|
|
208
|
+
total = C.hb_ot_layout_language_get_feature_indexes(
|
|
209
|
+
face.ptr, table_tag, script_index, language_index, 0, count_ptr, nil
|
|
210
|
+
)
|
|
211
|
+
return [] if total.zero?
|
|
212
|
+
|
|
213
|
+
idx_ptr = FFI::MemoryPointer.new(:uint, total)
|
|
214
|
+
count_ptr.write_uint(total)
|
|
215
|
+
C.hb_ot_layout_language_get_feature_indexes(
|
|
216
|
+
face.ptr, table_tag, script_index, language_index, 0, count_ptr, idx_ptr
|
|
217
|
+
)
|
|
218
|
+
idx_ptr.read_array_of_uint(count_ptr.read_uint)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Returns feature tags for a language
|
|
222
|
+
# @param face [Face] Font face
|
|
223
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
224
|
+
# @param script_index [Integer] Script index
|
|
225
|
+
# @param language_index [Integer] Language index
|
|
226
|
+
# @return [Array<Integer>] Feature tags
|
|
227
|
+
def feature_tags_for_lang(face, table_tag, script_index, language_index)
|
|
228
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
229
|
+
count_ptr.write_uint(0)
|
|
230
|
+
total = C.hb_ot_layout_language_get_feature_tags(
|
|
231
|
+
face.ptr, table_tag, script_index, language_index, 0, count_ptr, nil
|
|
232
|
+
)
|
|
233
|
+
return [] if total.zero?
|
|
234
|
+
|
|
235
|
+
tags_ptr = FFI::MemoryPointer.new(:uint32, total)
|
|
236
|
+
count_ptr.write_uint(total)
|
|
237
|
+
C.hb_ot_layout_language_get_feature_tags(
|
|
238
|
+
face.ptr, table_tag, script_index, language_index, 0, count_ptr, tags_ptr
|
|
239
|
+
)
|
|
240
|
+
tags_ptr.read_array_of_uint32(count_ptr.read_uint)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Finds a specific feature in a language, returns [found, feature_index]
|
|
244
|
+
# @param face [Face] Font face
|
|
245
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
246
|
+
# @param script_index [Integer] Script index
|
|
247
|
+
# @param language_index [Integer] Language index
|
|
248
|
+
# @param feature_tag [Integer] Feature tag to find
|
|
249
|
+
# @return [Array] [Boolean, Integer] found and feature index
|
|
250
|
+
def find_feature(face, table_tag, script_index, language_index, feature_tag)
|
|
251
|
+
idx_ptr = FFI::MemoryPointer.new(:uint)
|
|
252
|
+
found = C.from_hb_bool(
|
|
253
|
+
C.hb_ot_layout_language_find_feature(
|
|
254
|
+
face.ptr, table_tag, script_index, language_index, feature_tag, idx_ptr
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
[found, idx_ptr.read_uint]
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Returns lookup indices for a feature
|
|
261
|
+
# @param face [Face] Font face
|
|
262
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
263
|
+
# @param feature_index [Integer] Feature index
|
|
264
|
+
# @return [Array<Integer>] Lookup indices
|
|
265
|
+
def feature_lookups(face, table_tag, feature_index)
|
|
266
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
267
|
+
count_ptr.write_uint(0)
|
|
268
|
+
total = C.hb_ot_layout_feature_get_lookups(
|
|
269
|
+
face.ptr, table_tag, feature_index, 0, count_ptr, nil
|
|
270
|
+
)
|
|
271
|
+
return [] if total.zero?
|
|
272
|
+
|
|
273
|
+
idx_ptr = FFI::MemoryPointer.new(:uint, total)
|
|
274
|
+
count_ptr.write_uint(total)
|
|
275
|
+
C.hb_ot_layout_feature_get_lookups(
|
|
276
|
+
face.ptr, table_tag, feature_index, 0, count_ptr, idx_ptr
|
|
277
|
+
)
|
|
278
|
+
idx_ptr.read_array_of_uint(count_ptr.read_uint)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Returns size design parameters for the face
|
|
282
|
+
# @param face [Face] Font face
|
|
283
|
+
# @return [Hash, nil] Size params hash or nil if not available
|
|
284
|
+
def size_params(face)
|
|
285
|
+
design_size_ptr = FFI::MemoryPointer.new(:uint)
|
|
286
|
+
subfamily_id_ptr = FFI::MemoryPointer.new(:uint)
|
|
287
|
+
name_id_ptr = FFI::MemoryPointer.new(:uint)
|
|
288
|
+
range_start_ptr = FFI::MemoryPointer.new(:uint)
|
|
289
|
+
range_end_ptr = FFI::MemoryPointer.new(:uint)
|
|
290
|
+
found = C.from_hb_bool(
|
|
291
|
+
C.hb_ot_layout_get_size_params(
|
|
292
|
+
face.ptr,
|
|
293
|
+
design_size_ptr, subfamily_id_ptr, name_id_ptr,
|
|
294
|
+
range_start_ptr, range_end_ptr
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
return nil unless found
|
|
298
|
+
|
|
299
|
+
{
|
|
300
|
+
design_size: design_size_ptr.read_uint,
|
|
301
|
+
subfamily_id: subfamily_id_ptr.read_uint,
|
|
302
|
+
subfamily_name_id: name_id_ptr.read_uint,
|
|
303
|
+
range_start: range_start_ptr.read_uint,
|
|
304
|
+
range_end: range_end_ptr.read_uint
|
|
305
|
+
}
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Returns OpenType feature UI name IDs
|
|
309
|
+
# @param face [Face] Font face
|
|
310
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
311
|
+
# @param feature_index [Integer] Feature index
|
|
312
|
+
# @return [Hash, nil] Name ID hash or nil
|
|
313
|
+
def feature_name_ids(face, table_tag, feature_index)
|
|
314
|
+
label_id_ptr = FFI::MemoryPointer.new(:uint)
|
|
315
|
+
tooltip_id_ptr = FFI::MemoryPointer.new(:uint)
|
|
316
|
+
sample_id_ptr = FFI::MemoryPointer.new(:uint)
|
|
317
|
+
num_params_ptr = FFI::MemoryPointer.new(:uint)
|
|
318
|
+
first_param_id_ptr = FFI::MemoryPointer.new(:uint)
|
|
319
|
+
found = C.from_hb_bool(
|
|
320
|
+
C.hb_ot_layout_feature_get_name_ids(
|
|
321
|
+
face.ptr, table_tag, feature_index,
|
|
322
|
+
label_id_ptr, tooltip_id_ptr, sample_id_ptr,
|
|
323
|
+
num_params_ptr, first_param_id_ptr
|
|
324
|
+
)
|
|
325
|
+
)
|
|
326
|
+
return nil unless found
|
|
327
|
+
|
|
328
|
+
{
|
|
329
|
+
label_id: label_id_ptr.read_uint,
|
|
330
|
+
tooltip_id: tooltip_id_ptr.read_uint,
|
|
331
|
+
sample_id: sample_id_ptr.read_uint,
|
|
332
|
+
num_named_parameters: num_params_ptr.read_uint,
|
|
333
|
+
first_param_id: first_param_id_ptr.read_uint
|
|
334
|
+
}
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Returns codepoints for a named feature's characters
|
|
338
|
+
# @param face [Face] Font face
|
|
339
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
340
|
+
# @param feature_index [Integer] Feature index
|
|
341
|
+
# @return [Array<Integer>] Codepoints
|
|
342
|
+
def feature_characters(face, table_tag, feature_index)
|
|
343
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
344
|
+
count_ptr.write_uint(0)
|
|
345
|
+
total = C.hb_ot_layout_feature_get_characters(
|
|
346
|
+
face.ptr, table_tag, feature_index, 0, count_ptr, nil
|
|
347
|
+
)
|
|
348
|
+
return [] if total.zero?
|
|
349
|
+
|
|
350
|
+
cps_ptr = FFI::MemoryPointer.new(:uint32, total)
|
|
351
|
+
count_ptr.write_uint(total)
|
|
352
|
+
C.hb_ot_layout_feature_get_characters(
|
|
353
|
+
face.ptr, table_tag, feature_index, 0, count_ptr, cps_ptr
|
|
354
|
+
)
|
|
355
|
+
cps_ptr.read_array_of_uint32(count_ptr.read_uint)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Returns a baseline coordinate value
|
|
359
|
+
# @param font [Font] Font
|
|
360
|
+
# @param baseline_tag [Integer] Baseline tag (HB_OT_LAYOUT_BASELINE_TAG_*)
|
|
361
|
+
# @param dir [Symbol] Direction (:ltr, :rtl, :ttb, :btt)
|
|
362
|
+
# @param script_tag [Integer] Script tag
|
|
363
|
+
# @param language [FFI::Pointer, nil] Language pointer (nil = default)
|
|
364
|
+
# @return [Integer, nil] Baseline coordinate or nil if not available
|
|
365
|
+
def baseline(font, baseline_tag, dir, script_tag, language = nil)
|
|
366
|
+
lang_ptr = language || FFI::Pointer::NULL
|
|
367
|
+
coord_ptr = FFI::MemoryPointer.new(:int32)
|
|
368
|
+
found = C.from_hb_bool(
|
|
369
|
+
C.hb_ot_layout_get_baseline(
|
|
370
|
+
font.ptr, baseline_tag, dir, script_tag, lang_ptr, coord_ptr
|
|
371
|
+
)
|
|
372
|
+
)
|
|
373
|
+
found ? coord_ptr.read_int32 : nil
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# Returns a baseline coordinate with fallback
|
|
377
|
+
# @param font [Font] Font
|
|
378
|
+
# @param baseline_tag [Integer] Baseline tag
|
|
379
|
+
# @param dir [Symbol] Direction
|
|
380
|
+
# @param script_tag [Integer] Script tag
|
|
381
|
+
# @param language [FFI::Pointer, nil] Language pointer
|
|
382
|
+
# @return [Integer] Baseline coordinate (with fallback)
|
|
383
|
+
def baseline_with_fallback(font, baseline_tag, dir, script_tag, language = nil)
|
|
384
|
+
lang_ptr = language || FFI::Pointer::NULL
|
|
385
|
+
coord_ptr = FFI::MemoryPointer.new(:int32)
|
|
386
|
+
C.hb_ot_layout_get_baseline_with_fallback(
|
|
387
|
+
font.ptr, baseline_tag, dir, script_tag, lang_ptr, coord_ptr
|
|
388
|
+
)
|
|
389
|
+
coord_ptr.read_int32
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# Returns font extents from OT tables
|
|
393
|
+
# @param font [Font] Font
|
|
394
|
+
# @param dir [Symbol] Direction
|
|
395
|
+
# @param script_tag [Integer] Script tag
|
|
396
|
+
# @param language [FFI::Pointer, nil] Language pointer
|
|
397
|
+
# @return [Hash, nil] Font extents hash or nil
|
|
398
|
+
def font_extents(font, dir, script_tag, language = nil)
|
|
399
|
+
lang_ptr = language || FFI::Pointer::NULL
|
|
400
|
+
extents = C::HbFontExtentsT.new
|
|
401
|
+
found = C.from_hb_bool(
|
|
402
|
+
C.hb_ot_layout_get_font_extents(
|
|
403
|
+
font.ptr, dir, script_tag, lang_ptr, extents.to_ptr
|
|
404
|
+
)
|
|
405
|
+
)
|
|
406
|
+
return nil unless found
|
|
407
|
+
|
|
408
|
+
{
|
|
409
|
+
ascender: extents[:ascender],
|
|
410
|
+
descender: extents[:descender],
|
|
411
|
+
line_gap: extents[:line_gap]
|
|
412
|
+
}
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# Returns a baseline coordinate value (v2: takes hb_script_t + hb_language_t)
|
|
416
|
+
# @param font [Font] Font
|
|
417
|
+
# @param baseline_tag [Integer] Baseline tag
|
|
418
|
+
# @param dir [Symbol] Direction
|
|
419
|
+
# @param script [Integer] Script value (hb_script_t, e.g. from HarfBuzz.script("Latn"))
|
|
420
|
+
# @param language [FFI::Pointer, nil] Language pointer (nil = default)
|
|
421
|
+
# @return [Integer, nil] Baseline coordinate or nil if not available
|
|
422
|
+
def baseline2(font, baseline_tag, dir, script, language = nil)
|
|
423
|
+
lang_ptr = language || FFI::Pointer::NULL
|
|
424
|
+
coord_ptr = FFI::MemoryPointer.new(:int32)
|
|
425
|
+
found = C.from_hb_bool(
|
|
426
|
+
C.hb_ot_layout_get_baseline2(
|
|
427
|
+
font.ptr, baseline_tag, dir, script, lang_ptr, coord_ptr
|
|
428
|
+
)
|
|
429
|
+
)
|
|
430
|
+
found ? coord_ptr.read_int32 : nil
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
# Returns a baseline coordinate with fallback (v2: takes hb_script_t + hb_language_t)
|
|
434
|
+
# @param font [Font] Font
|
|
435
|
+
# @param baseline_tag [Integer] Baseline tag
|
|
436
|
+
# @param dir [Symbol] Direction
|
|
437
|
+
# @param script [Integer] Script value (hb_script_t)
|
|
438
|
+
# @param language [FFI::Pointer, nil] Language pointer
|
|
439
|
+
# @return [Integer] Baseline coordinate (with fallback)
|
|
440
|
+
def baseline_with_fallback2(font, baseline_tag, dir, script, language = nil)
|
|
441
|
+
lang_ptr = language || FFI::Pointer::NULL
|
|
442
|
+
coord_ptr = FFI::MemoryPointer.new(:int32)
|
|
443
|
+
C.hb_ot_layout_get_baseline_with_fallback2(
|
|
444
|
+
font.ptr, baseline_tag, dir, script, lang_ptr, coord_ptr
|
|
445
|
+
)
|
|
446
|
+
coord_ptr.read_int32
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# Returns font extents from OT tables (v2: takes hb_script_t + hb_language_t)
|
|
450
|
+
# @param font [Font] Font
|
|
451
|
+
# @param dir [Symbol] Direction
|
|
452
|
+
# @param script [Integer] Script value (hb_script_t)
|
|
453
|
+
# @param language [FFI::Pointer, nil] Language pointer
|
|
454
|
+
# @return [Hash, nil] Font extents hash or nil
|
|
455
|
+
def font_extents2(font, dir, script, language = nil)
|
|
456
|
+
lang_ptr = language || FFI::Pointer::NULL
|
|
457
|
+
extents = C::HbFontExtentsT.new
|
|
458
|
+
found = C.from_hb_bool(
|
|
459
|
+
C.hb_ot_layout_get_font_extents2(
|
|
460
|
+
font.ptr, dir, script, lang_ptr, extents.to_ptr
|
|
461
|
+
)
|
|
462
|
+
)
|
|
463
|
+
return nil unless found
|
|
464
|
+
|
|
465
|
+
{
|
|
466
|
+
ascender: extents[:ascender],
|
|
467
|
+
descender: extents[:descender],
|
|
468
|
+
line_gap: extents[:line_gap]
|
|
469
|
+
}
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
# Returns the horizontal baseline tag for a script
|
|
473
|
+
# @param script [Integer] Script value
|
|
474
|
+
# @return [Integer] Baseline tag
|
|
475
|
+
def horizontal_baseline_tag_for_script(script)
|
|
476
|
+
C.hb_ot_layout_get_horizontal_baseline_tag_for_script(script)
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# Returns the feature tags for a table
|
|
480
|
+
# @param face [Face] Font face
|
|
481
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
482
|
+
# @return [Array<Integer>] Feature tags
|
|
483
|
+
def feature_tags(face, table_tag)
|
|
484
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
485
|
+
count_ptr.write_uint(0)
|
|
486
|
+
C.hb_ot_layout_table_get_feature_tags(face.ptr, table_tag, 0, count_ptr, nil)
|
|
487
|
+
count = count_ptr.read_uint
|
|
488
|
+
return [] if count.zero?
|
|
489
|
+
|
|
490
|
+
tags_ptr = FFI::MemoryPointer.new(:uint32, count)
|
|
491
|
+
count_ptr.write_uint(count)
|
|
492
|
+
C.hb_ot_layout_table_get_feature_tags(face.ptr, table_tag, 0, count_ptr, tags_ptr)
|
|
493
|
+
tags_ptr.read_array_of_uint32(count_ptr.read_uint)
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
# Collects all lookups referenced by features
|
|
497
|
+
# @param face [Face] Font face
|
|
498
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
499
|
+
# @param scripts [Array<Integer>, nil] Script tags filter (nil = all)
|
|
500
|
+
# @param languages [Array<Integer>, nil] Language tags filter (nil = all)
|
|
501
|
+
# @param features [Array<Integer>, nil] Feature tags filter (nil = all)
|
|
502
|
+
# @return [HarfBuzz::Set] Set of lookup indices
|
|
503
|
+
def collect_lookups(face, table_tag, scripts: nil, languages: nil, features: nil)
|
|
504
|
+
set = HarfBuzz::Set.new
|
|
505
|
+
scripts_ptr = build_tag_array_ptr(scripts)
|
|
506
|
+
languages_ptr = build_tag_array_ptr(languages)
|
|
507
|
+
features_ptr = build_tag_array_ptr(features)
|
|
508
|
+
C.hb_ot_layout_collect_lookups(
|
|
509
|
+
face.ptr, table_tag, scripts_ptr, languages_ptr, features_ptr, set.ptr
|
|
510
|
+
)
|
|
511
|
+
set
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
# Collects all feature indices
|
|
515
|
+
# @param face [Face] Font face
|
|
516
|
+
# @param table_tag [Integer] GSUB or GPOS tag
|
|
517
|
+
# @return [HarfBuzz::Set] Set of feature indices
|
|
518
|
+
def collect_features(face, table_tag, scripts: nil, languages: nil, features: nil)
|
|
519
|
+
set = HarfBuzz::Set.new
|
|
520
|
+
scripts_ptr = build_tag_array_ptr(scripts)
|
|
521
|
+
languages_ptr = build_tag_array_ptr(languages)
|
|
522
|
+
features_ptr = build_tag_array_ptr(features)
|
|
523
|
+
C.hb_ot_layout_collect_features(
|
|
524
|
+
face.ptr, table_tag, scripts_ptr, languages_ptr, features_ptr, set.ptr
|
|
525
|
+
)
|
|
526
|
+
set
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
# Converts script+language to OT tags
|
|
530
|
+
# @param script [Integer] Script value
|
|
531
|
+
# @param language [FFI::Pointer] Language pointer
|
|
532
|
+
# @return [Array<Array<Integer>>] [script_tags, language_tags]
|
|
533
|
+
def tags_from_script_and_language(script, language)
|
|
534
|
+
s_count = FFI::MemoryPointer.new(:uint)
|
|
535
|
+
l_count = FFI::MemoryPointer.new(:uint)
|
|
536
|
+
s_count.write_uint(8)
|
|
537
|
+
l_count.write_uint(8)
|
|
538
|
+
s_tags = FFI::MemoryPointer.new(:uint32, 8)
|
|
539
|
+
l_tags = FFI::MemoryPointer.new(:uint32, 8)
|
|
540
|
+
C.hb_ot_tags_from_script_and_language(script, language, s_count, s_tags, l_count, l_tags)
|
|
541
|
+
[
|
|
542
|
+
s_tags.read_array_of_uint32(s_count.read_uint),
|
|
543
|
+
l_tags.read_array_of_uint32(l_count.read_uint)
|
|
544
|
+
]
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
# Converts OT tags to script + language
|
|
548
|
+
# @param script_tag [Integer] OT script tag
|
|
549
|
+
# @param language_tag [Integer] OT language tag
|
|
550
|
+
# @return [Array] [script, language_ptr]
|
|
551
|
+
def tags_to_script_and_language(script_tag, language_tag)
|
|
552
|
+
script_ptr = FFI::MemoryPointer.new(:uint32)
|
|
553
|
+
lang_ptr = FFI::MemoryPointer.new(:pointer)
|
|
554
|
+
C.hb_ot_tags_to_script_and_language(script_tag, language_tag, script_ptr, lang_ptr)
|
|
555
|
+
[script_ptr.read_uint32, lang_ptr.read_pointer]
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
# Converts an OT tag to a language pointer
|
|
559
|
+
# @param tag [Integer] Language tag
|
|
560
|
+
# @return [FFI::Pointer] Language pointer
|
|
561
|
+
def tag_to_language(tag)
|
|
562
|
+
C.hb_ot_tag_to_language(tag)
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# Converts an OT tag to a script value
|
|
566
|
+
# @param tag [Integer] Script tag
|
|
567
|
+
# @return [Integer] Script value
|
|
568
|
+
def tag_to_script(tag)
|
|
569
|
+
C.hb_ot_tag_to_script(tag)
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def build_tag_array_ptr(tags)
|
|
573
|
+
return FFI::Pointer::NULL unless tags
|
|
574
|
+
|
|
575
|
+
ptr = FFI::MemoryPointer.new(:uint32, tags.size + 1)
|
|
576
|
+
ptr.put_array_of_uint32(0, tags)
|
|
577
|
+
ptr.put_uint32(tags.size * 4, HarfBuzz::C::HB_SET_VALUE_INVALID)
|
|
578
|
+
ptr
|
|
579
|
+
end
|
|
580
|
+
private_class_method :build_tag_array_ptr
|
|
581
|
+
end
|
|
582
|
+
end
|
|
583
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HarfBuzz
|
|
4
|
+
module OT
|
|
5
|
+
# OpenType Math Typesetting API
|
|
6
|
+
module Math
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# @param face [Face] Font face
|
|
10
|
+
# @return [Boolean] true if the face has a MATH table
|
|
11
|
+
def has_data?(face)
|
|
12
|
+
C.from_hb_bool(C.hb_ot_math_has_data(face.ptr))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Returns a MATH constant value
|
|
16
|
+
# @param font [Font] Sized font
|
|
17
|
+
# @param constant [Integer] Math constant index
|
|
18
|
+
# @return [Integer] Constant value in font units
|
|
19
|
+
def constant(font, constant)
|
|
20
|
+
C.hb_ot_math_get_constant(font.ptr, constant)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @param font [Font] Sized font
|
|
24
|
+
# @param glyph [Integer] Glyph ID
|
|
25
|
+
# @return [Integer] Italics correction in font units
|
|
26
|
+
def glyph_italics_correction(font, glyph)
|
|
27
|
+
C.hb_ot_math_get_glyph_italics_correction(font.ptr, glyph)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @param font [Font] Sized font
|
|
31
|
+
# @param glyph [Integer] Glyph ID
|
|
32
|
+
# @return [Integer] Top accent attachment in font units
|
|
33
|
+
def glyph_top_accent_attachment(font, glyph)
|
|
34
|
+
C.hb_ot_math_get_glyph_top_accent_attachment(font.ptr, glyph)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @param face [Face] Font face
|
|
38
|
+
# @param glyph [Integer] Glyph ID
|
|
39
|
+
# @return [Boolean] true if glyph is an extended shape
|
|
40
|
+
def glyph_extended_shape?(face, glyph)
|
|
41
|
+
C.from_hb_bool(C.hb_ot_math_is_glyph_extended_shape(face.ptr, glyph))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Returns the math kerning value
|
|
45
|
+
# @param font [Font] Sized font
|
|
46
|
+
# @param glyph [Integer] Glyph ID
|
|
47
|
+
# @param kern [Integer] Kern type
|
|
48
|
+
# @param correction_height [Integer] Correction height
|
|
49
|
+
# @return [Integer] Kerning value
|
|
50
|
+
def glyph_kerning(font, glyph, kern, correction_height)
|
|
51
|
+
C.hb_ot_math_get_glyph_kerning(font.ptr, glyph, kern, correction_height)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns math glyph variants
|
|
55
|
+
# @param font [Font] Sized font
|
|
56
|
+
# @param glyph [Integer] Glyph ID
|
|
57
|
+
# @param dir [Symbol] Direction
|
|
58
|
+
# @return [Array<C::HbOtMathGlyphVariantT>] Variants
|
|
59
|
+
def glyph_variants(font, glyph, dir)
|
|
60
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
61
|
+
count_ptr.write_uint(0)
|
|
62
|
+
C.hb_ot_math_get_glyph_variants(font.ptr, glyph, dir, 0, count_ptr, nil)
|
|
63
|
+
count = count_ptr.read_uint
|
|
64
|
+
return [] if count.zero?
|
|
65
|
+
|
|
66
|
+
variants_ptr = FFI::MemoryPointer.new(C::HbOtMathGlyphVariantT, count)
|
|
67
|
+
count_ptr.write_uint(count)
|
|
68
|
+
C.hb_ot_math_get_glyph_variants(font.ptr, glyph, dir, 0, count_ptr, variants_ptr)
|
|
69
|
+
actual = count_ptr.read_uint
|
|
70
|
+
actual.times.map do |i|
|
|
71
|
+
C::HbOtMathGlyphVariantT.new(variants_ptr + i * C::HbOtMathGlyphVariantT.size)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Returns math glyph assembly (extensible glyph parts)
|
|
76
|
+
# @param font [Font] Sized font
|
|
77
|
+
# @param glyph [Integer] Glyph ID
|
|
78
|
+
# @param dir [Symbol] Direction
|
|
79
|
+
# @return [Hash] { parts: Array<C::HbOtMathGlyphPartT>, italics_correction: Integer }
|
|
80
|
+
def glyph_assembly(font, glyph, dir)
|
|
81
|
+
count_ptr = FFI::MemoryPointer.new(:uint)
|
|
82
|
+
count_ptr.write_uint(0)
|
|
83
|
+
italics_ptr = FFI::MemoryPointer.new(:int32)
|
|
84
|
+
total = C.hb_ot_math_get_glyph_assembly(
|
|
85
|
+
font.ptr, glyph, dir, 0, count_ptr, nil, italics_ptr
|
|
86
|
+
)
|
|
87
|
+
parts = if total.zero?
|
|
88
|
+
[]
|
|
89
|
+
else
|
|
90
|
+
parts_ptr = FFI::MemoryPointer.new(C::HbOtMathGlyphPartT, total)
|
|
91
|
+
count_ptr.write_uint(total)
|
|
92
|
+
C.hb_ot_math_get_glyph_assembly(
|
|
93
|
+
font.ptr, glyph, dir, 0, count_ptr, parts_ptr, italics_ptr
|
|
94
|
+
)
|
|
95
|
+
actual = count_ptr.read_uint
|
|
96
|
+
actual.times.map do |i|
|
|
97
|
+
C::HbOtMathGlyphPartT.new(parts_ptr + i * C::HbOtMathGlyphPartT.size)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
{ parts: parts, italics_correction: italics_ptr.read_int32 }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @param font [Font] Sized font
|
|
104
|
+
# @param dir [Symbol] Direction
|
|
105
|
+
# @return [Integer] Minimum connector overlap in font units
|
|
106
|
+
def min_connector_overlap(font, dir)
|
|
107
|
+
C.hb_ot_math_get_min_connector_overlap(font.ptr, dir)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|