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,497 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HarfBuzz
|
|
4
|
+
# Wraps hb_buffer_t — text buffer for shaping
|
|
5
|
+
class Buffer
|
|
6
|
+
attr_reader :ptr
|
|
7
|
+
|
|
8
|
+
# Creates a new empty buffer
|
|
9
|
+
def initialize
|
|
10
|
+
@ptr = C.hb_buffer_create
|
|
11
|
+
raise AllocationError, "Failed to create buffer" if @ptr.null?
|
|
12
|
+
|
|
13
|
+
register_finalizer
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Creates a buffer similar to this one (same properties, empty content)
|
|
17
|
+
# @return [Buffer]
|
|
18
|
+
def create_similar
|
|
19
|
+
ptr = C.hb_buffer_create_similar(@ptr)
|
|
20
|
+
self.class.wrap_owned(ptr)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Returns the singleton empty buffer
|
|
24
|
+
# @return [Buffer]
|
|
25
|
+
def self.empty
|
|
26
|
+
wrap_borrowed(C.hb_buffer_get_empty)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Resets all buffer state
|
|
30
|
+
# @return [self]
|
|
31
|
+
def reset
|
|
32
|
+
C.hb_buffer_reset(@ptr)
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Clears text content but keeps properties
|
|
37
|
+
# @return [self]
|
|
38
|
+
def clear
|
|
39
|
+
C.hb_buffer_clear_contents(@ptr)
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Pre-allocates buffer capacity
|
|
44
|
+
# @param size [Integer] Number of glyphs to pre-allocate
|
|
45
|
+
# @return [Boolean] Success
|
|
46
|
+
def pre_allocate(size)
|
|
47
|
+
C.from_hb_bool(C.hb_buffer_pre_allocate(@ptr, size))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @return [Boolean] true if last allocation was successful
|
|
51
|
+
def allocation_successful?
|
|
52
|
+
C.from_hb_bool(C.hb_buffer_allocation_successful(@ptr))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Adds a single codepoint with cluster index
|
|
56
|
+
# @param codepoint [Integer] Unicode codepoint
|
|
57
|
+
# @param cluster [Integer] Cluster index
|
|
58
|
+
# @return [self]
|
|
59
|
+
def add(codepoint, cluster)
|
|
60
|
+
C.hb_buffer_add(@ptr, codepoint, cluster)
|
|
61
|
+
self
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Adds UTF-8 text to the buffer
|
|
65
|
+
# @param text [String] Text to add (will be encoded as UTF-8)
|
|
66
|
+
# @param item_offset [Integer] Offset into text (for cluster calculation)
|
|
67
|
+
# @param item_length [Integer] Length (-1 = full text)
|
|
68
|
+
# @return [self]
|
|
69
|
+
def add_utf8(text, item_offset: 0, item_length: -1)
|
|
70
|
+
encoded = text.encode("UTF-8")
|
|
71
|
+
mem = FFI::MemoryPointer.from_string(encoded)
|
|
72
|
+
C.hb_buffer_add_utf8(@ptr, mem, encoded.bytesize, item_offset, item_length)
|
|
73
|
+
self
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Adds UTF-16 text to the buffer
|
|
77
|
+
# @param text [String] Text (will be encoded as UTF-16LE)
|
|
78
|
+
# @param item_offset [Integer] Offset in code units
|
|
79
|
+
# @param item_length [Integer] Length in code units (-1 = full text)
|
|
80
|
+
# @return [self]
|
|
81
|
+
def add_utf16(text, item_offset: 0, item_length: -1)
|
|
82
|
+
encoded = text.encode("UTF-16LE")
|
|
83
|
+
mem = FFI::MemoryPointer.new(:uint16, encoded.bytesize / 2 + 1)
|
|
84
|
+
mem.put_bytes(0, encoded)
|
|
85
|
+
C.hb_buffer_add_utf16(@ptr, mem, encoded.bytesize / 2, item_offset, item_length)
|
|
86
|
+
self
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Adds UTF-32 text to the buffer
|
|
90
|
+
# @param text [String] Text (will be encoded as UTF-32LE)
|
|
91
|
+
# @param item_offset [Integer] Offset in code units
|
|
92
|
+
# @param item_length [Integer] Length in code units (-1 = full text)
|
|
93
|
+
# @return [self]
|
|
94
|
+
def add_utf32(text, item_offset: 0, item_length: -1)
|
|
95
|
+
encoded = text.encode("UTF-32LE")
|
|
96
|
+
mem = FFI::MemoryPointer.new(:uint32, encoded.bytesize / 4 + 1)
|
|
97
|
+
mem.put_bytes(0, encoded)
|
|
98
|
+
C.hb_buffer_add_utf32(@ptr, mem, encoded.bytesize / 4, item_offset, item_length)
|
|
99
|
+
self
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Adds Latin-1 (ISO-8859-1) text to the buffer
|
|
103
|
+
# @param text [String] Text to add (will be encoded as ISO-8859-1)
|
|
104
|
+
# @param item_offset [Integer] Offset into text (for cluster calculation)
|
|
105
|
+
# @param item_length [Integer] Length (-1 = full text)
|
|
106
|
+
# @return [self]
|
|
107
|
+
def add_latin1(text, item_offset: 0, item_length: -1)
|
|
108
|
+
encoded = text.encode("ISO-8859-1")
|
|
109
|
+
mem = FFI::MemoryPointer.new(:uint8, encoded.bytesize)
|
|
110
|
+
mem.put_bytes(0, encoded)
|
|
111
|
+
C.hb_buffer_add_latin1(@ptr, mem, encoded.bytesize, item_offset, item_length)
|
|
112
|
+
self
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Adds an array of Unicode codepoints to the buffer
|
|
116
|
+
# @param codepoints [Array<Integer>] Array of Unicode codepoints
|
|
117
|
+
# @param item_offset [Integer] Offset
|
|
118
|
+
# @param item_length [Integer] Length (-1 = full array)
|
|
119
|
+
# @return [self]
|
|
120
|
+
def add_codepoints(codepoints, item_offset: 0, item_length: -1)
|
|
121
|
+
mem = FFI::MemoryPointer.new(:uint32, codepoints.size)
|
|
122
|
+
mem.put_array_of_uint32(0, codepoints)
|
|
123
|
+
C.hb_buffer_add_codepoints(@ptr, mem, codepoints.size, item_offset, item_length)
|
|
124
|
+
self
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Appends part of another buffer
|
|
128
|
+
# @param other [Buffer] Source buffer
|
|
129
|
+
# @param start [Integer] Start index
|
|
130
|
+
# @param end_ [Integer] End index
|
|
131
|
+
# @return [self]
|
|
132
|
+
def append(other, start, end_)
|
|
133
|
+
C.hb_buffer_append(@ptr, other.ptr, start, end_)
|
|
134
|
+
self
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# @return [Symbol] Content type (:invalid, :unicode, :glyphs)
|
|
138
|
+
def content_type
|
|
139
|
+
C.hb_buffer_get_content_type(@ptr)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# @param type [Symbol] Content type
|
|
143
|
+
def content_type=(type)
|
|
144
|
+
C.hb_buffer_set_content_type(@ptr, type)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# @return [Symbol] Buffer direction (:ltr, :rtl, :ttb, :btt, :invalid)
|
|
148
|
+
def direction
|
|
149
|
+
C.hb_buffer_get_direction(@ptr)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# @param dir [Symbol] Direction (:ltr, :rtl, :ttb, :btt)
|
|
153
|
+
def direction=(dir)
|
|
154
|
+
C.hb_buffer_set_direction(@ptr, dir)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# @return [Integer] Script (hb_script_t value)
|
|
158
|
+
def script
|
|
159
|
+
C.hb_buffer_get_script(@ptr)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# @param script [Integer] Script value
|
|
163
|
+
def script=(script)
|
|
164
|
+
C.hb_buffer_set_script(@ptr, script)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# @return [FFI::Pointer] Language pointer
|
|
168
|
+
def language
|
|
169
|
+
C.hb_buffer_get_language(@ptr)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# @param lang [FFI::Pointer] Language pointer (use HarfBuzz.language("en"))
|
|
173
|
+
def language=(lang)
|
|
174
|
+
C.hb_buffer_set_language(@ptr, lang)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# @return [UnicodeFuncs] Unicode functions associated with this buffer
|
|
178
|
+
def unicode_funcs
|
|
179
|
+
ptr = C.hb_buffer_get_unicode_funcs(@ptr)
|
|
180
|
+
UnicodeFuncs.wrap_borrowed(ptr)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# @param ufuncs [UnicodeFuncs] Unicode functions to use for this buffer
|
|
184
|
+
def unicode_funcs=(ufuncs)
|
|
185
|
+
C.hb_buffer_set_unicode_funcs(@ptr, ufuncs.ptr)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# @return [C::HbSegmentPropertiesT] Segment properties struct
|
|
189
|
+
def segment_properties
|
|
190
|
+
props = C::HbSegmentPropertiesT.new
|
|
191
|
+
C.hb_buffer_get_segment_properties(@ptr, props.to_ptr)
|
|
192
|
+
props
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# @param props [C::HbSegmentPropertiesT] Segment properties struct
|
|
196
|
+
def segment_properties=(props)
|
|
197
|
+
C.hb_buffer_set_segment_properties(@ptr, props.to_ptr)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Guesses direction/script/language from buffer contents
|
|
201
|
+
# @return [self]
|
|
202
|
+
def guess_segment_properties
|
|
203
|
+
C.hb_buffer_guess_segment_properties(@ptr)
|
|
204
|
+
self
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# @return [Integer] Buffer flags bitmask
|
|
208
|
+
def flags
|
|
209
|
+
C.hb_buffer_get_flags(@ptr)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# @param flags [Integer] Buffer flags bitmask
|
|
213
|
+
def flags=(flags)
|
|
214
|
+
C.hb_buffer_set_flags(@ptr, flags)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# @return [Symbol] Cluster level
|
|
218
|
+
def cluster_level
|
|
219
|
+
C.hb_buffer_get_cluster_level(@ptr)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# @param level [Symbol] Cluster level
|
|
223
|
+
def cluster_level=(level)
|
|
224
|
+
C.hb_buffer_set_cluster_level(@ptr, level)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# @return [Integer] Replacement codepoint for invalid input
|
|
228
|
+
def replacement_codepoint
|
|
229
|
+
C.hb_buffer_get_replacement_codepoint(@ptr)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# @param cp [Integer] Replacement codepoint
|
|
233
|
+
def replacement_codepoint=(cp)
|
|
234
|
+
C.hb_buffer_set_replacement_codepoint(@ptr, cp)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# @return [Integer] Invisible glyph ID
|
|
238
|
+
def invisible_glyph
|
|
239
|
+
C.hb_buffer_get_invisible_glyph(@ptr)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# @param glyph [Integer] Invisible glyph ID
|
|
243
|
+
def invisible_glyph=(glyph)
|
|
244
|
+
C.hb_buffer_set_invisible_glyph(@ptr, glyph)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# @return [Integer] Not-found glyph ID
|
|
248
|
+
def not_found_glyph
|
|
249
|
+
C.hb_buffer_get_not_found_glyph(@ptr)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# @param glyph [Integer] Not-found glyph ID
|
|
253
|
+
def not_found_glyph=(glyph)
|
|
254
|
+
C.hb_buffer_set_not_found_glyph(@ptr, glyph)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# @return [Integer] Random state
|
|
258
|
+
# @raise [NotImplementedError] if HarfBuzz < 7.2.0
|
|
259
|
+
def random_state
|
|
260
|
+
unless C.respond_to?(:hb_buffer_get_random_state)
|
|
261
|
+
raise NotImplementedError, "random_state requires HarfBuzz >= 7.2.0"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
C.hb_buffer_get_random_state(@ptr)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# @param state [Integer] Random state
|
|
268
|
+
# @raise [NotImplementedError] if HarfBuzz < 7.2.0
|
|
269
|
+
def random_state=(state)
|
|
270
|
+
unless C.respond_to?(:hb_buffer_set_random_state)
|
|
271
|
+
raise NotImplementedError, "random_state= requires HarfBuzz >= 7.2.0"
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
C.hb_buffer_set_random_state(@ptr, state)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# @return [Integer] Number of items in buffer
|
|
278
|
+
def length
|
|
279
|
+
C.hb_buffer_get_length(@ptr)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
alias size length
|
|
283
|
+
|
|
284
|
+
# @param len [Integer] New buffer length
|
|
285
|
+
def length=(len)
|
|
286
|
+
C.hb_buffer_set_length(@ptr, len)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# @return [Array<GlyphInfo>] Array of glyph info structs
|
|
290
|
+
def glyph_infos
|
|
291
|
+
length_ptr = FFI::MemoryPointer.new(:uint)
|
|
292
|
+
infos_ptr = C.hb_buffer_get_glyph_infos(@ptr, length_ptr)
|
|
293
|
+
count = length_ptr.read_uint
|
|
294
|
+
return [] if infos_ptr.null? || count.zero?
|
|
295
|
+
|
|
296
|
+
count.times.map do |i|
|
|
297
|
+
GlyphInfo.new(infos_ptr + (i * C::HbGlyphInfoT.size))
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# @return [Array<GlyphPosition>] Array of glyph position structs
|
|
302
|
+
def glyph_positions
|
|
303
|
+
length_ptr = FFI::MemoryPointer.new(:uint)
|
|
304
|
+
positions_ptr = C.hb_buffer_get_glyph_positions(@ptr, length_ptr)
|
|
305
|
+
count = length_ptr.read_uint
|
|
306
|
+
return [] if positions_ptr.null? || count.zero?
|
|
307
|
+
|
|
308
|
+
count.times.map do |i|
|
|
309
|
+
GlyphPosition.new(positions_ptr + (i * C::HbGlyphPositionT.size))
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# @return [Boolean] true if the buffer has glyph position data
|
|
314
|
+
def has_positions?
|
|
315
|
+
C.from_hb_bool(C.hb_buffer_has_positions(@ptr))
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Normalizes glyph clusters
|
|
319
|
+
# @return [self]
|
|
320
|
+
def normalize_glyphs
|
|
321
|
+
C.hb_buffer_normalize_glyphs(@ptr)
|
|
322
|
+
self
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Reverses buffer contents
|
|
326
|
+
# @return [self]
|
|
327
|
+
def reverse
|
|
328
|
+
C.hb_buffer_reverse(@ptr)
|
|
329
|
+
self
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# Reverses a range of buffer contents
|
|
333
|
+
# @param start [Integer] Start index
|
|
334
|
+
# @param end_ [Integer] End index
|
|
335
|
+
# @return [self]
|
|
336
|
+
def reverse_range(start, end_)
|
|
337
|
+
C.hb_buffer_reverse_range(@ptr, start, end_)
|
|
338
|
+
self
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Reverses clusters
|
|
342
|
+
# @return [self]
|
|
343
|
+
def reverse_clusters
|
|
344
|
+
C.hb_buffer_reverse_clusters(@ptr)
|
|
345
|
+
self
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Sets a message callback for debugging/tracing shaping
|
|
349
|
+
# @yield [message] Called with a debug message string
|
|
350
|
+
# @return [self]
|
|
351
|
+
def on_message(&block)
|
|
352
|
+
@message_callback = block
|
|
353
|
+
cb = FFI::Function.new(:int, [:pointer, :pointer, :string, :pointer]) do
|
|
354
|
+
|_buf_ptr, _font_ptr, message, _user_data|
|
|
355
|
+
block.call(message)
|
|
356
|
+
1
|
|
357
|
+
end
|
|
358
|
+
@message_ffi = cb
|
|
359
|
+
C.hb_buffer_set_message_func(@ptr, cb, nil, nil)
|
|
360
|
+
self
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Computes difference flags between this buffer and another
|
|
364
|
+
# @param other [Buffer] Buffer to compare against
|
|
365
|
+
# @param dottedcircle_glyph [Integer] Dotted circle glyph ID
|
|
366
|
+
# @param position_fuzz [Integer] Position comparison tolerance
|
|
367
|
+
# @return [Integer] Difference flags bitmask
|
|
368
|
+
def diff(other, dottedcircle_glyph, position_fuzz)
|
|
369
|
+
C.hb_buffer_diff(@ptr, other.ptr, dottedcircle_glyph, position_fuzz)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Serializes buffer contents (glyphs or unicode) to a string representation
|
|
373
|
+
# @param font [Font, nil] Font used for glyph names (optional, used for glyphs content)
|
|
374
|
+
# @param format [Symbol] Serialize format (:text or :json), defaults to :text
|
|
375
|
+
# @param flags [Integer] Serialize flags (0 = default)
|
|
376
|
+
# @return [String] Serialized data
|
|
377
|
+
def serialize(font: nil, format: :text, flags: 0)
|
|
378
|
+
buf_size = 4096
|
|
379
|
+
buf = FFI::MemoryPointer.new(:char, buf_size)
|
|
380
|
+
written_ptr = FFI::MemoryPointer.new(:uint)
|
|
381
|
+
font_ptr = font ? font.ptr : FFI::Pointer::NULL
|
|
382
|
+
C.hb_buffer_serialize(@ptr, 0, length, buf, buf_size, written_ptr, font_ptr, format, flags)
|
|
383
|
+
buf.read_string(written_ptr.read_uint)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
# Serializes shaped glyphs to a string representation
|
|
387
|
+
# @param font [Font, nil] Font used for glyph names (optional)
|
|
388
|
+
# @param format [Symbol] Serialize format (:text or :json), defaults to :text
|
|
389
|
+
# @param flags [Integer] Serialize flags (0 = default)
|
|
390
|
+
# @return [String] Serialized glyph data
|
|
391
|
+
def serialize_glyphs(font: nil, format: :text, flags: 0)
|
|
392
|
+
buf_size = 4096
|
|
393
|
+
buf = FFI::MemoryPointer.new(:char, buf_size)
|
|
394
|
+
written_ptr = FFI::MemoryPointer.new(:uint)
|
|
395
|
+
font_ptr = font ? font.ptr : FFI::Pointer::NULL
|
|
396
|
+
C.hb_buffer_serialize_glyphs(@ptr, 0, length, buf, buf_size, written_ptr, font_ptr, format, flags)
|
|
397
|
+
buf.read_string(written_ptr.read_uint)
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# Serializes Unicode codepoints to a string representation
|
|
401
|
+
# @param format [Symbol] Serialize format (:text or :json), defaults to :text
|
|
402
|
+
# @param flags [Integer] Serialize flags (0 = default)
|
|
403
|
+
# @return [String] Serialized unicode data
|
|
404
|
+
def serialize_unicode(format: :text, flags: 0)
|
|
405
|
+
buf_size = 4096
|
|
406
|
+
buf = FFI::MemoryPointer.new(:char, buf_size)
|
|
407
|
+
written_ptr = FFI::MemoryPointer.new(:uint)
|
|
408
|
+
C.hb_buffer_serialize_unicode(@ptr, 0, length, buf, buf_size, written_ptr, format, flags)
|
|
409
|
+
buf.read_string(written_ptr.read_uint)
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Deserializes glyph data from a string into this buffer
|
|
413
|
+
# @param str [String] Serialized glyph data
|
|
414
|
+
# @param font [Font, nil] Font for glyph name resolution
|
|
415
|
+
# @param format [Symbol] Format (:text or :json), defaults to :text
|
|
416
|
+
# @return [Boolean] true if successful
|
|
417
|
+
def deserialize_glyphs(str, font: nil, format: :text)
|
|
418
|
+
font_ptr = font ? font.ptr : FFI::Pointer::NULL
|
|
419
|
+
end_ptr = FFI::MemoryPointer.new(:pointer)
|
|
420
|
+
C.from_hb_bool(
|
|
421
|
+
C.hb_buffer_deserialize_glyphs(@ptr, str, str.bytesize, end_ptr, font_ptr, format)
|
|
422
|
+
)
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# Deserializes Unicode codepoints from a string into this buffer
|
|
426
|
+
# @param str [String] Serialized unicode data
|
|
427
|
+
# @param format [Symbol] Format (:text or :json), defaults to :text
|
|
428
|
+
# @return [Boolean] true if successful
|
|
429
|
+
def deserialize_unicode(str, format: :text)
|
|
430
|
+
end_ptr = FFI::MemoryPointer.new(:pointer)
|
|
431
|
+
C.from_hb_bool(
|
|
432
|
+
C.hb_buffer_deserialize_unicode(@ptr, str, str.bytesize, end_ptr, format)
|
|
433
|
+
)
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def inspect
|
|
437
|
+
"#<HarfBuzz::Buffer length=#{length} direction=#{direction} content_type=#{content_type}>"
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# Converts a format string to a serialize format symbol
|
|
441
|
+
# @param str [String] Format name (e.g., "text", "json")
|
|
442
|
+
# @return [Symbol] Format symbol
|
|
443
|
+
def self.serialize_format(str)
|
|
444
|
+
C.hb_buffer_serialize_format_from_string(str, str.bytesize)
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
# Converts a serialize format symbol to a string
|
|
448
|
+
# @param fmt [Symbol] Format symbol
|
|
449
|
+
# @return [String] Format name
|
|
450
|
+
def self.serialize_format_name(fmt)
|
|
451
|
+
C.hb_buffer_serialize_format_to_string(fmt)
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
# Returns a list of available serialize format names
|
|
455
|
+
# @return [Array<String>] Format names
|
|
456
|
+
def self.serialize_formats
|
|
457
|
+
ptr = C.hb_buffer_serialize_list_formats
|
|
458
|
+
result = []
|
|
459
|
+
i = 0
|
|
460
|
+
loop do
|
|
461
|
+
p = ptr.get_pointer(i * FFI::Pointer.size)
|
|
462
|
+
break if p.null?
|
|
463
|
+
|
|
464
|
+
result << p.read_string
|
|
465
|
+
i += 1
|
|
466
|
+
end
|
|
467
|
+
result
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def self.wrap_owned(ptr)
|
|
471
|
+
obj = allocate
|
|
472
|
+
obj.instance_variable_set(:@ptr, ptr)
|
|
473
|
+
obj.send(:register_finalizer)
|
|
474
|
+
obj
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def self.wrap_borrowed(ptr)
|
|
478
|
+
obj = allocate
|
|
479
|
+
obj.instance_variable_set(:@ptr, ptr)
|
|
480
|
+
obj.instance_variable_set(:@borrowed, true)
|
|
481
|
+
obj
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
private
|
|
485
|
+
|
|
486
|
+
def register_finalizer
|
|
487
|
+
return if instance_variable_defined?(:@borrowed) && @borrowed
|
|
488
|
+
|
|
489
|
+
HarfBuzz::Buffer.define_finalizer(self, @ptr)
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
def self.define_finalizer(obj, ptr)
|
|
493
|
+
destroy = C.method(:hb_buffer_destroy)
|
|
494
|
+
ObjectSpace.define_finalizer(obj, proc { destroy.call(ptr) })
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HarfBuzz
|
|
4
|
+
module C
|
|
5
|
+
attach_function :hb_aat_layout_has_substitution, [:hb_face_t], :hb_bool_t
|
|
6
|
+
attach_function :hb_aat_layout_has_positioning, [:hb_face_t], :hb_bool_t
|
|
7
|
+
attach_function :hb_aat_layout_has_tracking, [:hb_face_t], :hb_bool_t
|
|
8
|
+
attach_function :hb_aat_layout_get_feature_types,
|
|
9
|
+
[:hb_face_t, :uint, :pointer, :pointer], :uint
|
|
10
|
+
attach_function :hb_aat_layout_feature_type_get_name_id,
|
|
11
|
+
[:hb_face_t, :uint, :pointer], :hb_bool_t
|
|
12
|
+
attach_function :hb_aat_layout_feature_type_get_selector_infos,
|
|
13
|
+
[:hb_face_t, :uint, :uint, :pointer, :pointer, :pointer], :uint
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ffi"
|
|
4
|
+
|
|
5
|
+
module HarfBuzz
|
|
6
|
+
# Low-level FFI bindings to HarfBuzz C API
|
|
7
|
+
module C
|
|
8
|
+
extend FFI::Library
|
|
9
|
+
|
|
10
|
+
# Load the HarfBuzz shared library
|
|
11
|
+
ffi_lib HarfBuzz.library_path
|
|
12
|
+
|
|
13
|
+
# === Basic Types ===
|
|
14
|
+
# hb_bool_t is int in C
|
|
15
|
+
typedef :int, :hb_bool_t
|
|
16
|
+
|
|
17
|
+
# Codepoint (Unicode code point)
|
|
18
|
+
typedef :uint32, :hb_codepoint_t
|
|
19
|
+
|
|
20
|
+
# Position value (signed 26.6 fixed point or font units)
|
|
21
|
+
typedef :int32, :hb_position_t
|
|
22
|
+
|
|
23
|
+
# Bitmask
|
|
24
|
+
typedef :uint32, :hb_mask_t
|
|
25
|
+
|
|
26
|
+
# OpenType tag (4-byte identifier)
|
|
27
|
+
typedef :uint32, :hb_tag_t
|
|
28
|
+
|
|
29
|
+
# Color value (RGBA)
|
|
30
|
+
typedef :uint32, :hb_color_t
|
|
31
|
+
|
|
32
|
+
# OpenType name ID
|
|
33
|
+
typedef :uint, :hb_ot_name_id_t
|
|
34
|
+
|
|
35
|
+
# === Opaque Pointer Types ===
|
|
36
|
+
# Each opaque pointer type is typedef'd for semantic clarity
|
|
37
|
+
|
|
38
|
+
# Blob (binary data)
|
|
39
|
+
typedef :pointer, :hb_blob_t
|
|
40
|
+
|
|
41
|
+
# Buffer (text buffer for shaping)
|
|
42
|
+
typedef :pointer, :hb_buffer_t
|
|
43
|
+
|
|
44
|
+
# Face (font face)
|
|
45
|
+
typedef :pointer, :hb_face_t
|
|
46
|
+
|
|
47
|
+
# Font (sized font instance)
|
|
48
|
+
typedef :pointer, :hb_font_t
|
|
49
|
+
|
|
50
|
+
# Font functions table
|
|
51
|
+
typedef :pointer, :hb_font_funcs_t
|
|
52
|
+
|
|
53
|
+
# Unicode functions table
|
|
54
|
+
typedef :pointer, :hb_unicode_funcs_t
|
|
55
|
+
|
|
56
|
+
# Draw functions table
|
|
57
|
+
typedef :pointer, :hb_draw_funcs_t
|
|
58
|
+
|
|
59
|
+
# Paint functions table
|
|
60
|
+
typedef :pointer, :hb_paint_funcs_t
|
|
61
|
+
|
|
62
|
+
# Map (integer to integer mapping)
|
|
63
|
+
typedef :pointer, :hb_map_t
|
|
64
|
+
|
|
65
|
+
# Set (integer set)
|
|
66
|
+
typedef :pointer, :hb_set_t
|
|
67
|
+
|
|
68
|
+
# Shape plan (cached shaping plan)
|
|
69
|
+
typedef :pointer, :hb_shape_plan_t
|
|
70
|
+
|
|
71
|
+
# Subset input
|
|
72
|
+
typedef :pointer, :hb_subset_input_t
|
|
73
|
+
|
|
74
|
+
# Subset plan
|
|
75
|
+
typedef :pointer, :hb_subset_plan_t
|
|
76
|
+
|
|
77
|
+
# === Helper Methods ===
|
|
78
|
+
|
|
79
|
+
# Converts Ruby boolean to hb_bool_t (int)
|
|
80
|
+
# @param value [Boolean] Ruby boolean
|
|
81
|
+
# @return [Integer] 1 for true, 0 for false
|
|
82
|
+
def self.to_hb_bool(value)
|
|
83
|
+
value ? 1 : 0
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Converts hb_bool_t (int) to Ruby boolean
|
|
87
|
+
# @param value [Integer] HarfBuzz boolean (0 or non-zero)
|
|
88
|
+
# @return [Boolean] Ruby boolean
|
|
89
|
+
def self.from_hb_bool(value)
|
|
90
|
+
!value.zero?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Converts a 4-character tag string to hb_tag_t (uint32)
|
|
94
|
+
# @param tag [String] 4-character tag (e.g., "GSUB")
|
|
95
|
+
# @return [Integer] Tag as uint32
|
|
96
|
+
def self.tag_from_string(tag)
|
|
97
|
+
raise ArgumentError, "Tag must be a String" unless tag.is_a?(String)
|
|
98
|
+
|
|
99
|
+
# Pad or truncate to 4 bytes
|
|
100
|
+
bytes = tag.bytes.take(4)
|
|
101
|
+
bytes += [0x20] * (4 - bytes.size) if bytes.size < 4
|
|
102
|
+
|
|
103
|
+
# Pack as big-endian uint32
|
|
104
|
+
bytes.pack("C4").unpack1("N")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Converts hb_tag_t (uint32) to 4-character string
|
|
108
|
+
# @param tag [Integer] Tag as uint32
|
|
109
|
+
# @return [String] 4-character tag string
|
|
110
|
+
def self.tag_to_string(tag)
|
|
111
|
+
[tag].pack("N").unpack("C4").pack("C*")
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HarfBuzz
|
|
4
|
+
module C
|
|
5
|
+
attach_function :hb_blob_create,
|
|
6
|
+
[:pointer, :uint, :hb_memory_mode_t, :pointer, :pointer], :hb_blob_t
|
|
7
|
+
attach_function :hb_blob_create_from_file, [:string], :hb_blob_t
|
|
8
|
+
attach_function :hb_blob_create_from_file_or_fail, [:string], :hb_blob_t
|
|
9
|
+
attach_function :hb_blob_create_sub_blob, [:hb_blob_t, :uint, :uint], :hb_blob_t
|
|
10
|
+
attach_function :hb_blob_copy_writable_or_fail, [:hb_blob_t], :hb_blob_t
|
|
11
|
+
attach_function :hb_blob_get_empty, [], :hb_blob_t
|
|
12
|
+
attach_function :hb_blob_destroy, [:hb_blob_t], :void
|
|
13
|
+
attach_function :hb_blob_reference, [:hb_blob_t], :hb_blob_t
|
|
14
|
+
attach_function :hb_blob_get_length, [:hb_blob_t], :uint
|
|
15
|
+
attach_function :hb_blob_get_data, [:hb_blob_t, :pointer], :pointer
|
|
16
|
+
attach_function :hb_blob_get_data_writable, [:hb_blob_t, :pointer], :pointer
|
|
17
|
+
attach_function :hb_blob_is_immutable, [:hb_blob_t], :hb_bool_t
|
|
18
|
+
attach_function :hb_blob_make_immutable, [:hb_blob_t], :void
|
|
19
|
+
attach_function :hb_blob_set_user_data,
|
|
20
|
+
[:hb_blob_t, :pointer, :pointer, :pointer, :hb_bool_t], :hb_bool_t
|
|
21
|
+
attach_function :hb_blob_get_user_data, [:hb_blob_t, :pointer], :pointer
|
|
22
|
+
end
|
|
23
|
+
end
|