harfbuzz 0.1
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/.gitignore +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +58 -0
- data/Rakefile +3 -0
- data/examples/example.rb +75 -0
- data/harfbuzz.gemspec +27 -0
- data/lib/harfbuzz.rb +40 -0
- data/lib/harfbuzz/base.rb +17 -0
- data/lib/harfbuzz/blob.rb +41 -0
- data/lib/harfbuzz/buffer.rb +103 -0
- data/lib/harfbuzz/face.rb +46 -0
- data/lib/harfbuzz/ffi_additions.rb +30 -0
- data/lib/harfbuzz/font.rb +68 -0
- data/lib/harfbuzz/shaping.rb +75 -0
- data/lib/harfbuzz/version.rb +5 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 88bd6dee65ceba054a1ff2ba5b4329bfea310689
|
4
|
+
data.tar.gz: fc6e012e4e25a3f579cea5946f9e6a7afcd963e4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4ddabd5869af1957fbe68864b1bba1f0dba915b0392f6c66549645b5d3f34ae1c6c2bffd473d104443cdd383aabcfc763dba1a1b2ffd0c2ebe28fa19fc058b7c
|
7
|
+
data.tar.gz: 1d5ed18e405ae2c595d31403898facdc1722f28d268176799042cfb8f9aeb18b505bbf2dfb6eac83f0e86225472cdec2613199660dde863f1e4cb3c7d3014f9f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 John Labovitz
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
The Harfbuzz gem is a Ruby interface to the Harfbuzz text shaping engine. By using this gem, some input text, a font file, and a list of features, you can convert Unicode text to glyph descriptions that are ready to be rendered.
|
2
|
+
|
3
|
+
For more information about Harfbuzz itself, see [harfbuzz.org](http://harfbuzz.org).
|
4
|
+
|
5
|
+
For an example of use, see [example.rb](harfbuzz/examples/example.rb). From the base directory of this gem, you can run this example:
|
6
|
+
|
7
|
+
ruby examples/example.rb
|
8
|
+
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
You must have the Harfbuzz library available on your system. OS X users can use Homebrew to install it:
|
13
|
+
|
14
|
+
brew install harfbuzz
|
15
|
+
|
16
|
+
Once you've installed the Harfbuzz library, install this gem by the usual:
|
17
|
+
|
18
|
+
sudo gem install harfbuzz
|
19
|
+
|
20
|
+
or if you're using a Ruby installed in Homebrew, as a normal user:
|
21
|
+
|
22
|
+
gem install harfbuzz
|
23
|
+
|
24
|
+
|
25
|
+
## Design philosophy
|
26
|
+
|
27
|
+
The Harfbuzz gem is composed of two layers: a low-level direct interface to Harfbuzz itself, and a high-level abstract interface that uses traditional Ruby objects and idioms.
|
28
|
+
|
29
|
+
The low-level interface is a one-to-one mapping of Harfbuzz C functions to Ruby methods, implemented using the [FFI](https://github.com/ffi/ffi) library. For example, the Harfbuzz C function `hb_version_string()` is available in Ruby as `Harfbuzz.hb_version_string`. However, as the low-level methods return C pointers wrapped in FFI objects, this layer is generally not useful to a Ruby programmer.
|
30
|
+
|
31
|
+
The high-level interface abstracts the C functions, structures, and pointers into Ruby objects. For example, the aftermentioned `hb_version_string()` function is more usefully available via `Harfbuzz.version_string`, which returns a Ruby string containing with the version of the Harfbuzz library.
|
32
|
+
|
33
|
+
Where appropriate, Ruby classes have been constructed to map to the concepts in Harfbuzz. Hence:
|
34
|
+
|
35
|
+
| C | Ruby
|
36
|
+
| ----------- | ----
|
37
|
+
| hb_blob_t | Harfbuzz::Blob
|
38
|
+
| hb_buffer_t | Harfbuzz::Buffer
|
39
|
+
| hb_face_t | Harfbuzz::Face
|
40
|
+
| hb_font_t | Harfbuzz::Font
|
41
|
+
|
42
|
+
The shaping method itself is in the base Harfbuzz module, as `Harfbuzz.shape(...)`.
|
43
|
+
|
44
|
+
|
45
|
+
## Caveats & bugs
|
46
|
+
|
47
|
+
Only a small number of basic Harfbuzz functions are mapped to Ruby methods. More will be added as needed.
|
48
|
+
|
49
|
+
There is no documentation except this file and the example script.
|
50
|
+
|
51
|
+
There are no tests or specs.
|
52
|
+
|
53
|
+
Memory management may not be correct.
|
54
|
+
|
55
|
+
|
56
|
+
## Feedback
|
57
|
+
|
58
|
+
As this is a new project, I'd love to hear your feedback. Email me at [johnl@johnlabovitz.com](mailto:johnl@johnlabovitz.com).
|
data/Rakefile
ADDED
data/examples/example.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift 'lib'
|
4
|
+
require 'harfbuzz'
|
5
|
+
|
6
|
+
#
|
7
|
+
# Show some information about Harfbuzz
|
8
|
+
#
|
9
|
+
|
10
|
+
puts "Harfbuzz version: #{Harfbuzz.version_string}"
|
11
|
+
puts "Shapers: #{Harfbuzz.shapers.join(', ')}"
|
12
|
+
|
13
|
+
#
|
14
|
+
# Create a font to be used for shaping. To get the most of out this example, use an OpenType font.
|
15
|
+
#
|
16
|
+
|
17
|
+
face = Harfbuzz::Face.new(File.open('/Library/Fonts/ACaslonPro-Regular.otf', 'rb'))
|
18
|
+
font = Harfbuzz::Font.new(face)
|
19
|
+
|
20
|
+
#
|
21
|
+
# Create a buffer to hold the text and the resulting glyphs/positions.
|
22
|
+
#
|
23
|
+
|
24
|
+
buffer = Harfbuzz::Buffer.new
|
25
|
+
|
26
|
+
#
|
27
|
+
# Add some text to the buffer.
|
28
|
+
#
|
29
|
+
|
30
|
+
buffer.add_utf8('WAVE first.')
|
31
|
+
|
32
|
+
#
|
33
|
+
# Guess direction, script, & language from the text.
|
34
|
+
#
|
35
|
+
|
36
|
+
if true
|
37
|
+
buffer.guess_segment_properties
|
38
|
+
else
|
39
|
+
#FIXME: these functions are not yet bound
|
40
|
+
# hb_buffer_set_direction(buffer, hb_direction_from_string(direction, -1))
|
41
|
+
# hb_buffer_set_script(buffer, hb_script_from_string(script, -1))
|
42
|
+
# hb_buffer_set_language(buffer, hb_language_from_string(language, -1))
|
43
|
+
# hb_buffer_set_flags(buffer, shape_flags)
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Shape the text in the buffer using some interesting features.
|
48
|
+
#
|
49
|
+
|
50
|
+
Harfbuzz.shape(font, buffer, %w{+dlig +hlig})
|
51
|
+
|
52
|
+
#
|
53
|
+
# Normalize glyph clusters (FIXME: why?).
|
54
|
+
#
|
55
|
+
|
56
|
+
buffer.normalize_glyphs
|
57
|
+
|
58
|
+
#
|
59
|
+
# Iterate through the buffer, examining the glyphs & related positions.
|
60
|
+
#
|
61
|
+
|
62
|
+
glyph_infos = buffer.get_glyph_infos
|
63
|
+
glyph_positions = buffer.get_glyph_positions
|
64
|
+
buffer.length.times do |i|
|
65
|
+
info, position = glyph_infos[i], glyph_positions[i]
|
66
|
+
glyph_name = font.glyph_to_string(info[:codepoint])
|
67
|
+
puts "/%-10.10s %5u | mask: %04X | cluster: %2u | advance: %4u,%4u | offset: %4u,%4u" % [
|
68
|
+
glyph_name,
|
69
|
+
info[:codepoint],
|
70
|
+
info[:mask],
|
71
|
+
info[:cluster],
|
72
|
+
position[:x_advance], position[:y_advance],
|
73
|
+
position[:x_offset], position[:y_offset],
|
74
|
+
]
|
75
|
+
end
|
data/harfbuzz.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'harfbuzz/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |s|
|
9
|
+
s.name = 'harfbuzz'
|
10
|
+
s.version = Harfbuzz::VERSION
|
11
|
+
s.summary = 'Ruby interface to the Harfbuzz text shaping engine'
|
12
|
+
s.author = 'John Labovitz'
|
13
|
+
s.email = 'johnl@johnlabovitz.com'
|
14
|
+
s.description = %q{
|
15
|
+
Harfbuzz is a Ruby interface to the Harfbuzz text shaping engine.
|
16
|
+
}
|
17
|
+
s.homepage = 'http://github.com/jslabovitz/harfbuzz-gem'
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_path = 'lib'
|
22
|
+
|
23
|
+
s.add_dependency 'ffi'
|
24
|
+
|
25
|
+
s.add_development_dependency 'bundler'
|
26
|
+
s.add_development_dependency 'rake'
|
27
|
+
end
|
data/lib/harfbuzz.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
require 'harfbuzz/ffi_additions'
|
4
|
+
|
5
|
+
module Harfbuzz
|
6
|
+
|
7
|
+
extend FFI::Library
|
8
|
+
|
9
|
+
ffi_lib 'harfbuzz'
|
10
|
+
|
11
|
+
typedef :pointer, :hb_destroy_func_t
|
12
|
+
typedef :uint32, :hb_codepoint_t
|
13
|
+
|
14
|
+
attach_function :hb_version_string, [], :string
|
15
|
+
attach_function :hb_version, [:pointer, :pointer, :pointer], :void
|
16
|
+
|
17
|
+
def self.version_string
|
18
|
+
hb_version_string
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.version
|
22
|
+
major_ptr = FFI::MemoryPointer.new(:uint, 1)
|
23
|
+
minor_ptr = FFI::MemoryPointer.new(:uint, 1)
|
24
|
+
micro_ptr = FFI::MemoryPointer.new(:uint, 1)
|
25
|
+
hb_version(major_ptr, minor_ptr, micro_ptr)
|
26
|
+
[
|
27
|
+
major_ptr.read_uint,
|
28
|
+
minor_ptr.read_uint,
|
29
|
+
micro_ptr.read_uint,
|
30
|
+
]
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'harfbuzz/base'
|
36
|
+
require 'harfbuzz/blob'
|
37
|
+
require 'harfbuzz/face'
|
38
|
+
require 'harfbuzz/font'
|
39
|
+
require 'harfbuzz/buffer'
|
40
|
+
require 'harfbuzz/shaping'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Harfbuzz
|
2
|
+
|
3
|
+
class Base
|
4
|
+
|
5
|
+
def self.finalize(method, ptr)
|
6
|
+
proc {
|
7
|
+
Harfbuzz.send(method, ptr) unless ptr == 0
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def define_finalizer(method, ptr)
|
12
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(method, ptr))
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Harfbuzz
|
2
|
+
|
3
|
+
typedef :pointer, :hb_blob_t
|
4
|
+
|
5
|
+
enum :hb_memory_mode_t, [
|
6
|
+
:HB_MEMORY_MODE_DUPLICATE,
|
7
|
+
:HB_MEMORY_MODE_READONLY,
|
8
|
+
:HB_MEMORY_MODE_WRITABLE,
|
9
|
+
:HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
|
10
|
+
]
|
11
|
+
|
12
|
+
attach_function :hb_blob_create, [
|
13
|
+
:pointer, # data
|
14
|
+
:uint, # length
|
15
|
+
:hb_memory_mode_t, # mode,
|
16
|
+
:pointer, # user_data,
|
17
|
+
:hb_destroy_func_t, # destroy
|
18
|
+
], :hb_blob_t
|
19
|
+
attach_function :hb_blob_get_length, [
|
20
|
+
:hb_blob_t, # blob
|
21
|
+
], :uint
|
22
|
+
attach_function :hb_blob_destroy, [:hb_blob_t], :void
|
23
|
+
|
24
|
+
class Blob < Base
|
25
|
+
|
26
|
+
attr_reader :hb_blob
|
27
|
+
|
28
|
+
def initialize(data, mode=0, user_data=nil)
|
29
|
+
data_ptr = FFI::MemoryPointer.new(:char, data.size)
|
30
|
+
data_ptr.put_bytes(0, data)
|
31
|
+
@hb_blob = Harfbuzz.hb_blob_create(data_ptr, data.size, mode, user_data, nil)
|
32
|
+
define_finalizer(:hb_blob_destroy, @hb_blob)
|
33
|
+
end
|
34
|
+
|
35
|
+
def length
|
36
|
+
Harfbuzz.hb_blob_get_length(@hb_blob)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Harfbuzz
|
2
|
+
|
3
|
+
typedef :pointer, :hb_buffer_t
|
4
|
+
typedef :uint32, :hb_mask_t
|
5
|
+
typedef :int32, :hb_position_t
|
6
|
+
|
7
|
+
class GlyphInfo < FFI::Struct
|
8
|
+
|
9
|
+
layout \
|
10
|
+
:codepoint, :hb_codepoint_t,
|
11
|
+
:mask, :hb_mask_t,
|
12
|
+
:cluster, :uint32,
|
13
|
+
:var1, :uint32, # private
|
14
|
+
:var2, :uint32 # private
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
class GlyphPosition < FFI::Struct
|
19
|
+
|
20
|
+
layout \
|
21
|
+
:x_advance, :hb_position_t,
|
22
|
+
:y_advance, :hb_position_t,
|
23
|
+
:x_offset, :hb_position_t,
|
24
|
+
:y_offset, :hb_position_t,
|
25
|
+
:var, :uint32 # private
|
26
|
+
|
27
|
+
def inspect
|
28
|
+
"<#{self} %s>" % [
|
29
|
+
%w{x_advance y_advance x_offset y_offset}.map { |k|
|
30
|
+
"#{k} = #{self[k.to_sym].inspect}"
|
31
|
+
}.join(', ')
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
attach_function :hb_buffer_create, [], :hb_buffer_t
|
38
|
+
attach_function :hb_buffer_add_utf8, [
|
39
|
+
:hb_buffer_t, # buffer
|
40
|
+
:pointer, # text
|
41
|
+
:int, # text_length
|
42
|
+
:uint, # item_offset
|
43
|
+
:int, # item_length
|
44
|
+
], :void
|
45
|
+
attach_function :hb_buffer_guess_segment_properties, [:hb_buffer_t], :void
|
46
|
+
attach_function :hb_buffer_get_length, [:hb_buffer_t], :uint
|
47
|
+
attach_function :hb_buffer_get_glyph_infos, [
|
48
|
+
:hb_buffer_t, # buffer
|
49
|
+
:pointer, # length
|
50
|
+
], GlyphInfo
|
51
|
+
attach_function :hb_buffer_get_glyph_positions, [
|
52
|
+
:hb_buffer_t, # buffer
|
53
|
+
:pointer, # length
|
54
|
+
], GlyphPosition
|
55
|
+
attach_function :hb_buffer_normalize_glyphs, [:hb_buffer_t], :void
|
56
|
+
|
57
|
+
class Buffer < Base
|
58
|
+
|
59
|
+
attr_reader :hb_buffer
|
60
|
+
|
61
|
+
def initialize
|
62
|
+
@hb_buffer = Harfbuzz.hb_buffer_create
|
63
|
+
define_finalizer(:hb_buffer_destroy, @hb_buffer)
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_utf8(text, offset=0, length=-1)
|
67
|
+
text_ptr = FFI::MemoryPointer.new(:char, text.bytesize)
|
68
|
+
text_ptr.put_bytes(0, text)
|
69
|
+
Harfbuzz.hb_buffer_add_utf8(@hb_buffer, text_ptr, text.bytesize, offset, length)
|
70
|
+
end
|
71
|
+
|
72
|
+
def guess_segment_properties
|
73
|
+
Harfbuzz.hb_buffer_guess_segment_properties(@hb_buffer)
|
74
|
+
end
|
75
|
+
|
76
|
+
def normalize_glyphs
|
77
|
+
Harfbuzz.hb_buffer_normalize_glyphs(@hb_buffer)
|
78
|
+
end
|
79
|
+
|
80
|
+
def length
|
81
|
+
Harfbuzz.hb_buffer_get_length(@hb_buffer)
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_glyph_infos
|
85
|
+
length_ptr = FFI::MemoryPointer.new(:uint, 1)
|
86
|
+
info_ptr = Harfbuzz.hb_buffer_get_glyph_infos(@hb_buffer, length_ptr)
|
87
|
+
length = length_ptr.read_uint
|
88
|
+
length.times.map do |i|
|
89
|
+
GlyphInfo.new(info_ptr + (i * GlyphInfo.size))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def get_glyph_positions
|
94
|
+
length_ptr = FFI::MemoryPointer.new(:uint, 1)
|
95
|
+
positions_ptr = Harfbuzz.hb_buffer_get_glyph_positions(@hb_buffer, length_ptr)
|
96
|
+
length = length_ptr.read_uint
|
97
|
+
length.times.map do |i|
|
98
|
+
GlyphPosition.new(positions_ptr + (i * GlyphPosition.size))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Harfbuzz
|
2
|
+
|
3
|
+
typedef :pointer, :hb_face_t
|
4
|
+
|
5
|
+
attach_function :hb_face_create, [
|
6
|
+
:hb_blob_t, # blob
|
7
|
+
:uint, # index
|
8
|
+
], :hb_face_t
|
9
|
+
attach_function :hb_face_get_index, [:hb_face_t], :uint
|
10
|
+
attach_function :hb_face_get_upem, [:hb_face_t], :uint
|
11
|
+
attach_function :hb_face_get_glyph_count, [:hb_face_t], :uint
|
12
|
+
|
13
|
+
class Face < Base
|
14
|
+
|
15
|
+
attr_reader :hb_face
|
16
|
+
|
17
|
+
def initialize(input, face_index=0)
|
18
|
+
blob = case input
|
19
|
+
when IO
|
20
|
+
Harfbuzz::Blob.new(input.read, :HB_MEMORY_MODE_READONLY)
|
21
|
+
when Blob
|
22
|
+
# use as-is
|
23
|
+
when String
|
24
|
+
Harfbuzz::Blob.new(input, :HB_MEMORY_MODE_READONLY)
|
25
|
+
else
|
26
|
+
raise "Unknown input type: #{input.class}"
|
27
|
+
end
|
28
|
+
@hb_face = Harfbuzz.hb_face_create(blob.hb_blob, face_index)
|
29
|
+
define_finalizer(:hb_buffer_destroy, @hb_face)
|
30
|
+
end
|
31
|
+
|
32
|
+
def index
|
33
|
+
Harfbuzz.hb_face_get_index(@hb_face)
|
34
|
+
end
|
35
|
+
|
36
|
+
def upem
|
37
|
+
Harfbuzz.hb_face_get_upem(@hb_face)
|
38
|
+
end
|
39
|
+
|
40
|
+
def glyph_count
|
41
|
+
Harfbuzz.hb_face_get_glyph_count(@hb_face)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class FFI::Pointer
|
2
|
+
|
3
|
+
# after https://dzone.com/articles/getting-array-strings-char-ffi
|
4
|
+
|
5
|
+
def read_array_of_strings
|
6
|
+
elements = []
|
7
|
+
loc = self
|
8
|
+
until (element = loc.read_pointer).null?
|
9
|
+
elements << element.read_string
|
10
|
+
loc += FFI::Type::POINTER.size
|
11
|
+
end
|
12
|
+
elements
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
class FFI::MemoryPointer
|
18
|
+
|
19
|
+
# after http://zegoggl.es/2009/05/ruby-ffi-recipes.html
|
20
|
+
|
21
|
+
def self.from_array_of_strings(strings)
|
22
|
+
string_ptrs = strings.map { |s| FFI::MemoryPointer.from_string(s) } + [nil]
|
23
|
+
strings_ptr = new(:pointer, string_ptrs.length)
|
24
|
+
string_ptrs.each_with_index do |ptr, i|
|
25
|
+
strings_ptr[i].put_pointer(0, ptr)
|
26
|
+
end
|
27
|
+
strings_ptr
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Harfbuzz
|
2
|
+
|
3
|
+
typedef :pointer, :hb_font_t
|
4
|
+
|
5
|
+
attach_function :hb_font_create, [:hb_face_t], :hb_font_t
|
6
|
+
attach_function :hb_font_set_scale, [
|
7
|
+
:hb_font_t, # font
|
8
|
+
:int, # x_scale
|
9
|
+
:int, # y_scale
|
10
|
+
], :void
|
11
|
+
attach_function :hb_ft_font_set_funcs, [:hb_font_t], :void
|
12
|
+
attach_function :hb_font_get_scale, [
|
13
|
+
:hb_font_t, # font
|
14
|
+
:pointer, # int *x_scale
|
15
|
+
:pointer, # int *y_scale
|
16
|
+
], :void
|
17
|
+
attach_function :hb_font_get_ppem, [
|
18
|
+
:hb_font_t, # font
|
19
|
+
:pointer, # unsigned int *x_ppem
|
20
|
+
:pointer, # unsigned int *y_ppem
|
21
|
+
], :void
|
22
|
+
attach_function :hb_font_glyph_to_string, [
|
23
|
+
:hb_font_t, # font
|
24
|
+
:hb_codepoint_t, # glyph
|
25
|
+
:pointer, # s
|
26
|
+
:uint, # size
|
27
|
+
], :void
|
28
|
+
|
29
|
+
class Font < Base
|
30
|
+
|
31
|
+
attr_reader :hb_font
|
32
|
+
|
33
|
+
def initialize(face)
|
34
|
+
@hb_font = Harfbuzz.hb_font_create(face.hb_face)
|
35
|
+
Harfbuzz.hb_font_set_scale(@hb_font, face.upem, face.upem)
|
36
|
+
Harfbuzz.hb_ft_font_set_funcs(@hb_font)
|
37
|
+
define_finalizer(:hb_font_destroy, @hb_font)
|
38
|
+
end
|
39
|
+
|
40
|
+
def scale
|
41
|
+
x_scale_ptr = FFI::MemoryPointer.new(:int, 1)
|
42
|
+
y_scale_ptr = FFI::MemoryPointer.new(:int, 1)
|
43
|
+
Harfbuzz.hb_font_get_scale(@hb_font, x_scale_ptr, y_scale_ptr)
|
44
|
+
[
|
45
|
+
x_scale_ptr.read_int,
|
46
|
+
y_scale_ptr.read_int,
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
def ppem
|
51
|
+
ppem_x_ptr = FFI::MemoryPointer.new(:uint, 1)
|
52
|
+
ppem_y_ptr = FFI::MemoryPointer.new(:uint, 1)
|
53
|
+
Harfbuzz.hb_font_get_ppem(@hb_font, ppem_x_ptr, ppem_y_ptr)
|
54
|
+
[
|
55
|
+
ppem_x_ptr.read_uint,
|
56
|
+
ppem_y_ptr.read_uint,
|
57
|
+
]
|
58
|
+
end
|
59
|
+
|
60
|
+
def glyph_to_string(glyph)
|
61
|
+
string_ptr = FFI::MemoryPointer.new(:char, 20)
|
62
|
+
Harfbuzz.hb_font_glyph_to_string(@hb_font, glyph, string_ptr, 20)
|
63
|
+
string_ptr.get_string(0, 20)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Harfbuzz
|
2
|
+
|
3
|
+
typedef :uint32, :hb_tag_t
|
4
|
+
|
5
|
+
class Feature < FFI::Struct
|
6
|
+
|
7
|
+
layout \
|
8
|
+
:tag, :hb_tag_t,
|
9
|
+
:value, :uint32,
|
10
|
+
:start, :uint,
|
11
|
+
:end, :uint
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
# typedef Feature, :hb_feature_t
|
16
|
+
|
17
|
+
attach_function :hb_shape_list_shapers, [], :pointer
|
18
|
+
attach_function :hb_shape, [
|
19
|
+
:hb_font_t, # font
|
20
|
+
:hb_buffer_t, # buffer
|
21
|
+
:pointer, # features
|
22
|
+
:uint, # num_features
|
23
|
+
], :void
|
24
|
+
attach_function :hb_shape_full, [
|
25
|
+
:hb_font_t, # font
|
26
|
+
:hb_buffer_t, # buffer
|
27
|
+
:pointer, # features
|
28
|
+
:uint, # num_features
|
29
|
+
:pointer, # shaper_list
|
30
|
+
], :bool
|
31
|
+
attach_function :hb_feature_from_string, [
|
32
|
+
:pointer, # str
|
33
|
+
:int, # len
|
34
|
+
Feature.by_ref, # feature
|
35
|
+
], :bool
|
36
|
+
attach_function :hb_feature_to_string, [
|
37
|
+
Feature.by_ref, # feature
|
38
|
+
:pointer, # buf
|
39
|
+
:uint, # size,
|
40
|
+
], :void
|
41
|
+
|
42
|
+
def self.shapers
|
43
|
+
Harfbuzz.hb_shape_list_shapers.read_array_of_strings
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.shape(font, buffer, features=nil, shapers=nil)
|
47
|
+
features_ptr, features_len = features_from_strings(features)
|
48
|
+
shapers_ptr = shapers ? FFI::MemoryPointer.from_array_of_strings(shapers) : nil
|
49
|
+
Harfbuzz.hb_shape_full(
|
50
|
+
font.hb_font,
|
51
|
+
buffer.hb_buffer,
|
52
|
+
features_ptr,
|
53
|
+
features_len,
|
54
|
+
shapers_ptr,
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.features_from_strings(feature_strings)
|
59
|
+
features_len = feature_strings ? feature_strings.length : 0
|
60
|
+
if features_len > 0
|
61
|
+
features_ptr = FFI::MemoryPointer.new(Feature, features_len)
|
62
|
+
feature_strings.each_with_index do |feature_string, i|
|
63
|
+
feature_string_ptr = FFI::MemoryPointer.new(:char, feature_string.bytesize)
|
64
|
+
feature_string_ptr.put_bytes(0, feature_string)
|
65
|
+
feature_ptr = Feature.new(features_ptr + (i * Feature.size))
|
66
|
+
hb_feature_from_string(feature_string_ptr, feature_string.bytesize, feature_ptr) \
|
67
|
+
or raise "Can't get feature from string: #{feature_string.inspect}"
|
68
|
+
end
|
69
|
+
else
|
70
|
+
features_ptr = nil
|
71
|
+
end
|
72
|
+
[features_ptr, features_len]
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: harfbuzz
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Labovitz
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: "\n Harfbuzz is a Ruby interface to the Harfbuzz text shaping engine.\n
|
56
|
+
\ "
|
57
|
+
email: johnl@johnlabovitz.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE.txt
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- examples/example.rb
|
68
|
+
- harfbuzz.gemspec
|
69
|
+
- lib/harfbuzz.rb
|
70
|
+
- lib/harfbuzz/base.rb
|
71
|
+
- lib/harfbuzz/blob.rb
|
72
|
+
- lib/harfbuzz/buffer.rb
|
73
|
+
- lib/harfbuzz/face.rb
|
74
|
+
- lib/harfbuzz/ffi_additions.rb
|
75
|
+
- lib/harfbuzz/font.rb
|
76
|
+
- lib/harfbuzz/shaping.rb
|
77
|
+
- lib/harfbuzz/version.rb
|
78
|
+
homepage: http://github.com/jslabovitz/harfbuzz-gem
|
79
|
+
licenses: []
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.4.8
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: Ruby interface to the Harfbuzz text shaping engine
|
101
|
+
test_files: []
|