Skip to content

Commit bb2b63a

Browse files
Add a new safe harfbuzz crate.
1 parent d812e19 commit bb2b63a

File tree

7 files changed

+515
-2
lines changed

7 files changed

+515
-2
lines changed

.gitignore

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
*.dll
99
*.dummy
1010
/doc
11-
/harfbuzz-sys/target
12-
/harfbuzz-sys/Cargo.lock
11+
/target
12+
/Cargo.lock
1313

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[workspace]
2+
members = ["harfbuzz", "harfbuzz-sys"]

harfbuzz/Cargo.toml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "harfbuzz"
3+
version = "0.2.0"
4+
5+
authors = ["The Servo Project Developers"]
6+
license = "MIT / Apache-2.0"
7+
8+
description = "Rust bindings to the HarfBuzz text shaping engine"
9+
repository = "https://github.com/servo/rust-harfbuzz"
10+
documentation = "https://docs.rs/harfbuzz/"
11+
keywords = ["opentype", "font", "text", "layout", "unicode"]
12+
13+
[dependencies.harfbuzz-sys]
14+
path = "../harfbuzz-sys"
15+
version = "0.1.15"

harfbuzz/src/buffer.rs

+311
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
// Copyright 2018 The Servo Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution.
3+
//
4+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7+
// option. This file may not be copied, modified, or distributed
8+
// except according to those terms.
9+
10+
use std;
11+
use sys;
12+
13+
use {Direction, Language};
14+
15+
/// A series of Unicode characters.
16+
///
17+
/// ## Adding Text
18+
///
19+
/// Since in Rust, a value of type `&str` must contain valid UTF-8
20+
/// text, adding text to a `Buffer` is simple:
21+
///
22+
/// ```
23+
/// # use harfbuzz::Buffer;
24+
/// let mut b = Buffer::new();
25+
/// b.add_str("Hello World");
26+
/// assert_eq!(b.is_empty(), false);
27+
/// ```
28+
///
29+
/// or, more simply:
30+
///
31+
/// ```
32+
/// # use harfbuzz::Buffer;
33+
/// let b = Buffer::with("Hello World");
34+
/// assert_eq!(b.is_empty(), false);
35+
/// ```
36+
///
37+
/// ## Segment Properties
38+
///
39+
/// In addition to the text itself, there are three important properties
40+
/// that influence how a piece of text is shaped:
41+
///
42+
/// * Direction: The direction in which the output glyphs flow. This is
43+
/// typically left to right or right to left. This is controlled via
44+
/// the [`set_direction`] method on `Buffer`.
45+
/// * Script: Script is crucial for choosing the proper shaping behaviour
46+
/// for scripts that require it (e.g. Arabic) and the which OpenType
47+
/// features defined in the font to be applied. This is controlled via
48+
/// the [`set_script`] method on `Buffer`.
49+
/// * Language: Languages are crucial for selecting which OpenType feature
50+
/// to apply to the buffer which can result in applying language-specific
51+
/// behaviour. Languages are orthogonal to the scripts, and though they
52+
/// are related, they are different concepts and should not be confused
53+
/// with each other. This is controlled via the [`set_language`] method
54+
/// on `Buffer`.
55+
///
56+
/// Additionally, Harfbuzz can attempt to infer the values for these
57+
/// properties using the [`guess_segment_properties`] method on `Buffer`:
58+
///
59+
/// ```
60+
/// # use harfbuzz::{Buffer, Direction, sys};
61+
/// let mut b = Buffer::with("مساء الخير");
62+
/// b.guess_segment_properties();
63+
/// assert_eq!(b.get_direction(), Direction::RTL);
64+
/// assert_eq!(b.get_script(), sys::HB_SCRIPT_ARABIC);
65+
/// ```
66+
///
67+
/// [`set_direction`]: #method.set_direction
68+
/// [`set_script`]: #method.set_script
69+
/// [`set_language`]: #method.set_language
70+
/// [`guess_segment_properties`]: #method.guess_segment_properties
71+
pub struct Buffer {
72+
/// The underlying `hb_buffer_t` from the `harfbuzz-sys` crate.
73+
///
74+
/// This isn't commonly needed unless interfacing directly with
75+
/// functions from the `harfbuzz-sys` crate that haven't been
76+
/// safely exposed.
77+
pub raw: *mut sys::hb_buffer_t,
78+
}
79+
80+
impl Buffer {
81+
/// Create a new, empty buffer.
82+
///
83+
/// ```
84+
/// # use harfbuzz::Buffer;
85+
/// let b = Buffer::new();
86+
/// assert!(b.is_empty());
87+
/// ```
88+
pub fn new() -> Self {
89+
Buffer::default()
90+
}
91+
92+
/// Create a new buffer with the given text.
93+
pub fn with(text: &str) -> Self {
94+
let mut b = Buffer::new();
95+
b.add_str(text);
96+
b
97+
}
98+
99+
/// Create a new, empty buffer with the specified capacity.
100+
pub fn with_capacity(capacity: usize) -> Self {
101+
let mut b = Buffer::default();
102+
b.reserve(capacity);
103+
b
104+
}
105+
106+
/// Add UTF-8 encoded text to the buffer.
107+
pub fn add_str(&mut self, text: &str) {
108+
unsafe {
109+
sys::hb_buffer_add_utf8(
110+
self.raw,
111+
text.as_ptr() as *const std::os::raw::c_char,
112+
text.len() as std::os::raw::c_int,
113+
0,
114+
text.len() as std::os::raw::c_int,
115+
)
116+
};
117+
}
118+
119+
/// Append part of the contents of another buffer to this one.
120+
///
121+
/// ```
122+
/// # use harfbuzz::Buffer;
123+
/// let mut b1 = Buffer::with("butter");
124+
/// let b2 = Buffer::with("fly");
125+
/// b1.append(&b2, 0, 3);
126+
/// assert_eq!(b1.len(), "butterfly".len());
127+
/// ```
128+
pub fn append(&mut self, other: &Buffer, start: usize, end: usize) {
129+
unsafe {
130+
sys::hb_buffer_append(
131+
self.raw,
132+
other.raw,
133+
start as std::os::raw::c_uint,
134+
end as std::os::raw::c_uint,
135+
)
136+
};
137+
}
138+
139+
/// Throw away text stored in the buffer, but maintain the
140+
/// currently configured Unicode functions and flags.
141+
///
142+
/// Text, glyph info, and segment properties will be discarded.
143+
pub fn clear_contents(&mut self) {
144+
unsafe { sys::hb_buffer_clear_contents(self.raw) };
145+
}
146+
147+
/// Throw away all data stored in the buffer as well as configuration
148+
/// parameters like Unicode functions, flags, and segment properties.
149+
pub fn reset(&mut self) {
150+
unsafe { sys::hb_buffer_reset(self.raw) };
151+
}
152+
153+
/// Preallocate space to fit at least *size* number of items.
154+
///
155+
/// FIXME: Does this correctly match the expected semantics?
156+
pub fn reserve(&mut self, size: usize) {
157+
unsafe { sys::hb_buffer_pre_allocate(self.raw, size as u32) };
158+
}
159+
160+
/// Returns the number of elements in the buffer, also referred to as its 'length'.
161+
pub fn len(&self) -> usize {
162+
unsafe { sys::hb_buffer_get_length(self.raw) as usize }
163+
}
164+
165+
/// Returns `true` if the buffer contains no data.
166+
pub fn is_empty(&self) -> bool {
167+
self.len() == 0
168+
}
169+
170+
/// Sets unset buffer segment properties based on buffer Unicode
171+
/// contents.
172+
///
173+
/// If buffer is not empty, it must have content type
174+
/// `HB_BUFFER_CONTENT_TYPE_UNICODE`.
175+
///
176+
/// If buffer script is not set (ie. is `HB_SCRIPT_INVALID`), it will
177+
/// be set to the Unicode script of the first character in the buffer
178+
/// that has a script other than `HB_SCRIPT_COMMON`,
179+
/// `HB_SCRIPT_INHERITED`, and `HB_SCRIPT_UNKNOWN`.
180+
///
181+
/// Next, if buffer direction is not set (ie. is `Direction::Invalid`),
182+
/// it will be set to the natural horizontal direction of the buffer
183+
/// script as returned by `hb_script_get_horizontal_direction()`.
184+
///
185+
/// Finally, if buffer language is not set (ie. is `HB_LANGUAGE_INVALID`),
186+
/// it will be set to the process's default language as returned by
187+
/// `hb_language_get_default()`. This may change in the future by
188+
/// taking buffer script into consideration when choosing a language.
189+
///
190+
/// ```
191+
/// # use harfbuzz::{Buffer, Direction, sys};
192+
/// let mut b = Buffer::with("Hello, world!");
193+
/// b.guess_segment_properties();
194+
/// assert_eq!(b.get_direction(), Direction::LTR);
195+
/// assert_eq!(b.get_script(), sys::HB_SCRIPT_LATIN);
196+
/// ```
197+
///
198+
/// See also:
199+
///
200+
/// * [`get_direction`](#method.get_direction)
201+
/// * [`set_direction`](#method.set_direction)
202+
/// * [`get_script`](#method.get_script)
203+
/// * [`set_script`](#method.set_script)
204+
/// * [`get_language`](#method.get_language)
205+
/// * [`set_language`](#method.set_language)
206+
pub fn guess_segment_properties(&mut self) {
207+
unsafe { sys::hb_buffer_guess_segment_properties(self.raw) };
208+
}
209+
210+
/// Set the text flow direction of the buffer.
211+
///
212+
/// No shaping can happen without setting buffer direction, and
213+
/// it controls the visual direction for the output glyphs; for
214+
/// RTL direction the glyphs will be reversed. Many layout features
215+
/// depend on the proper setting of the direction, for example,
216+
/// reversing RTL text before shaping, then shaping with LTR direction
217+
/// is not the same as keeping the text in logical order and shaping
218+
/// with RTL direction.
219+
///
220+
/// See also:
221+
///
222+
/// * [`get_direction`](#method.get_direction)
223+
/// * [`guess_segment_properties`](#method.guess_segment_properties)
224+
pub fn set_direction(&mut self, direction: Direction) {
225+
unsafe { sys::hb_buffer_set_direction(self.raw, direction.into()) };
226+
}
227+
228+
/// Get the text flow direction for the buffer.
229+
///
230+
/// See also:
231+
///
232+
/// * [`set_direction`](#method.set_direction)
233+
pub fn get_direction(&self) -> Direction {
234+
(unsafe { sys::hb_buffer_get_direction(self.raw) }).into()
235+
}
236+
237+
/// Sets the script of buffer to *script*.
238+
///
239+
/// Script is crucial for choosing the proper shaping behaviour
240+
/// for scripts that require it (e.g. Arabic) and the which
241+
/// OpenType features defined in the font to be applied.
242+
///
243+
/// See also:
244+
///
245+
/// * [`get_script`](#method.get_script)
246+
/// * [`guess_segment_properties`](#method.guess_segment_properties)
247+
pub fn set_script(&mut self, script: sys::hb_script_t) {
248+
unsafe { sys::hb_buffer_set_script(self.raw, script) };
249+
}
250+
251+
/// Get the script for the buffer.
252+
///
253+
/// See also:
254+
///
255+
/// * [`set_script`](#method.set_script)
256+
pub fn get_script(&self) -> sys::hb_script_t {
257+
unsafe { sys::hb_buffer_get_script(self.raw) }
258+
}
259+
260+
/// Sets the language of buffer to *language*.
261+
///
262+
/// Languages are crucial for selecting which OpenType feature
263+
/// to apply to the buffer which can result in applying
264+
/// language-specific behaviour. Languages are orthogonal to
265+
/// the scripts, and though they are related, they are different
266+
/// concepts and should not be confused with each other.
267+
///
268+
/// See also:
269+
///
270+
/// * [`get_language`](#method.get_language)
271+
/// * [`guess_segment_properties`](#method.guess_segment_properties)
272+
pub fn set_language(&mut self, language: Language) {
273+
unsafe { sys::hb_buffer_set_language(self.raw, language.raw) };
274+
}
275+
276+
/// Get the language for the buffer.
277+
///
278+
/// See also:
279+
///
280+
/// * [`set_language`](#method.set_language)
281+
pub fn get_language(&self) -> Language {
282+
Language {
283+
raw: unsafe { sys::hb_buffer_get_language(self.raw) },
284+
}
285+
}
286+
}
287+
288+
impl std::fmt::Debug for Buffer {
289+
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
290+
fmt.debug_struct("Buffer")
291+
.field("direction", &self.get_direction())
292+
.field("script", &self.get_script())
293+
.field("language", &self.get_language())
294+
.finish()
295+
}
296+
}
297+
298+
impl Default for Buffer {
299+
/// Create a new, empty buffer.
300+
fn default() -> Self {
301+
Buffer {
302+
raw: unsafe { sys::hb_buffer_create() },
303+
}
304+
}
305+
}
306+
307+
impl Drop for Buffer {
308+
fn drop(&mut self) {
309+
unsafe { sys::hb_buffer_destroy(self.raw) }
310+
}
311+
}

0 commit comments

Comments
 (0)