├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── examples └── simple.rs ├── src ├── bindings.rs ├── blob.rs ├── buffer.rs ├── common.rs ├── draw_funcs.rs ├── face.rs ├── font.rs ├── font_funcs.rs ├── lib.rs └── rusttype.rs ├── testfiles └── SourceSansVariable-Roman.ttf ├── update.sh └── wrapper.h /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | submodules: 'true' 21 | - name: Build 22 | run: cargo build --verbose --all-features 23 | - name: Run tests 24 | run: cargo test --verbose 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "harfbuzz"] 2 | path = harfbuzz 3 | url = https://github.com/harfbuzz/harfbuzz/ 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [2.0.1] 2021-08-28 9 | 10 | ### Added 11 | 12 | - Add basic support for font variations (see PR [#32](https://github.com/manuel-rhdt/harfbuzz_rs/pull/32)) 13 | - Improve documentation around font variations and font funcs. 14 | 15 | ## [2.0.0] 2021-08-22 16 | 17 | ### Changed 18 | 19 | - **Breaking** Remove reexport of harfbuzz-sys (this was a bad idea, since we inherit any breaking changes made in harfuzz-sys automatically) 20 | - Update harfbuzz-sys to 0.5 21 | - Update rusttype to 0.9 22 | - Improve README 23 | 24 | ### Fixed 25 | 26 | - Cargo test no longer may fail spuriously because of harfbuzz-internal serialization format not being consistent 27 | 28 | ## [1.2.0] 2020-08-07 29 | 30 | ### Fixed 31 | 32 | - Since harfbuzz-sys 0.5.0 made breaking changes (see issue [#27](https://github.com/manuel-rhdt/harfbuzz_rs/issues/27)) we do not support it for now. If you need the newest harfbuzz-sys please make a comment on the issue. The current plan is to push a new major release of harfbuzz_rs 2.0.0. 33 | 34 | ## [1.1.2] 2020-07-13 35 | 36 | ### Added 37 | 38 | - Reexport cargo features from `harfbuzz-sys` (thanks zenixls2) 39 | - Add new API to append `UnicodeBuffer`s to existing buffers 40 | - Add new API `with_bytes_owned` for `Blob` to allow creating blobs from `Arc>` and the like 41 | 42 | ### Fixed 43 | 44 | - Off-by-one error in cluster ranges for features 45 | 46 | ## [1.1.1] 2020-04-29 47 | 48 | ### Fixed 49 | 50 | - The project now builds again with rusttype < 0.9 51 | 52 | ## [1.1.0] 2020-04-29 53 | 54 | This release does not build. 55 | 56 | ### Changed 57 | 58 | - Updated dependencies (`harfbuzz-sys` and the optional dep. `rusttype`) 59 | 60 | ## [1.0.1] 2020-01-18 61 | 62 | ### Fixed 63 | 64 | - Builds on Android now [#20](https://github.com/manuel-rhdt/harfbuzz_rs/pull/20) 65 | - Memory leak on face creation 66 | - Memory leak on font creation [#22](https://github.com/manuel-rhdt/harfbuzz_rs/pull/22) 67 | - BufferSerializer now correctly serializes a single glyph [#23](https://github.com/manuel-rhdt/harfbuzz_rs/issues/23) 68 | 69 | ## [1.0] 2019-01-31 70 | 71 | This is the 1.0 release of harfbuzz_rs. There are still many API's left to be 72 | covered by this crate however I think the current API should be able to remain 73 | stable. 74 | 75 | ### Added 76 | 77 | - `Face::empty` constructor as a simple way to construct the empty face 78 | - `Feature` struct that wraps `hb_feature_t` and has an easy to use constructor 79 | - `UnicodeBuffer::add_str_item` to allow providing context to the string 80 | being shaped. 81 | - `UnicodeBuffer::preallocate` 82 | - Reexport of `harfbuzz_sys` as `hb` to facilitate use of unwrapped functions 83 | 84 | ### Changed 85 | 86 | - removed kerning callbacks from FontFuncs (following the upstream harfbuzz 87 | change). This also enabled updating to harfbuzz-sys 0.3. 88 | - updated to use Rust 2018 89 | - Further improved docs 90 | 91 | ## [0.4.0] 2019-01-08 92 | 93 | ### Added 94 | 95 | - constructor for `Blob` from a mutable slice 96 | - `Font::empty` as a simple way to construct the empty font 97 | - support for serializing a `GlyphBuffer`'s contents 98 | - `create_harfbuzz_rusttype_font`: a new way to create a font with Rusttype font 99 | funcs (the old `SetRustTypeFuncs` trait is deprecated) 100 | 101 | ### Fixed 102 | 103 | - lifetime of slice returned by `Blob::get_data` (could cause UB) 104 | 105 | ### Changed 106 | 107 | - The rustup feature is no longer enabled by default 108 | - `SetRustTypeFuncs` is now deprecated in favor of 109 | `create_harfbuzz_rusttype_font` 110 | - Internal representation of smart pointers (possibly more safe now) 111 | - Many improvements to documentation 112 | 113 | ## [0.3.0] 2018-08-26 114 | 115 | ### Fixed 116 | 117 | - `Font::parent` now returns an option 118 | - `HarfbuzzObject` becomes unsafe to implement 119 | 120 | ### Changed 121 | 122 | - Smart pointers to use `NonNull` 123 | 124 | ## [0.2.0] 2018-05-01 125 | 126 | ### Added 127 | 128 | - A new enum called TypedBuffer. It contains either a UnicodeBuffer or a GlyphBuffer. This makes reusing hb_buffer_t objects from foreign code possible. 129 | - UnicodeBuffer methods to return its contents 130 | - `from_bytes` function for `Face` 131 | 132 | ### Fixed 133 | 134 | - `Font::set_funcs` adds necessary `Send` and `Sync` bounds 135 | - UnicodeBuffer and GlyphBuffer no longer implement Clone (as they are 136 | mutable) 137 | - 138 | 139 | ### Changed 140 | 141 | - Naming: `HbArc` to `Shared` and `HbBox` to `Owned` 142 | - internal representation of `Shared` and `Owned` 143 | - `Shared::into_raw` and `Owned::into_raw` into static methods 144 | - Various improvements to documentation 145 | - `shape` becomes a free standing function (it was a method on `UnicodeBuffer`) 146 | 147 | ## [0.1.0] 2018-01-11 148 | 149 | Initial Release 150 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "harfbuzz_rs" 3 | version = "2.0.2-alpha.0" 4 | authors = ["Manuel Reinhardt "] 5 | description = "A high-level interface to HarfBuzz, exposing its most important functionality in a safe manner using Rust." 6 | repository = "https://github.com/harfbuzz/harfbuzz_rs" 7 | readme = "README.md" 8 | keywords = ["text", "ffi", "textlayout", "shaping", "harfbuzz"] 9 | categories = [ 10 | "api-bindings", 11 | "external-ffi-bindings", 12 | "internationalization", 13 | "text-processing", 14 | ] 15 | license = "MIT" 16 | edition = "2018" 17 | links = "harfbuzz" 18 | 19 | [badges] 20 | maintainence = { status = "passively-maintained" } 21 | 22 | [features] 23 | default = ["build-native-harfbuzz"] 24 | build-native-harfbuzz = [] 25 | 26 | [dependencies] 27 | rusttype = { version = "0.9", optional = true } 28 | bitflags = "^1" 29 | 30 | [build-dependencies] 31 | cc = "1.0" 32 | pkg-config = "0.3.24" 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Manuel Reinhardt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # harfbuzz_rs 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/harfbuzz_rs.svg)](https://crates.io/crates/harfbuzz_rs) 4 | [![Documentation](https://docs.rs/harfbuzz_rs/badge.svg)](https://docs.rs/harfbuzz_rs) 5 | [![Build status](https://github.com/harfbuzz/harfbuzz_rs/actions/workflows/rust.yml/badge.svg)](https://github.com/harfbuzz/harfbuzz_rs/actions/workflows/rust.yml) 6 | 7 | `harfbuzz_rs` is a high-level interface to HarfBuzz, exposing its most important 8 | functionality in a safe manner using Rust. 9 | 10 | # What is HarfBuzz? 11 | 12 | HarfBuzz is a library for performing complex text layout. It does not perform 13 | any drawing. This is quite a low-level operation. If you want to simply draw 14 | some text on the screen you should maybe choose another more high-level library. 15 | However if you want to build a library for drawing text on some canvas or need a 16 | lot of control on advanced text layout then this is the right library to use. 17 | 18 | # Getting Started 19 | 20 | To shape a simple string of text you just create a `Font` from a font file, fill 21 | a `Buffer` with some text and call the `shape` function. 22 | 23 | ```rust 24 | use harfbuzz_rs::*; 25 | 26 | let path = "path/to/some/font_file.otf"; 27 | let index = 0; //< face index in the font file 28 | let face = Face::from_file(path, index)?; 29 | let mut font = Font::new(face); 30 | 31 | let buffer = UnicodeBuffer::new().add_str("Hello World!"); 32 | let output = shape(&font, buffer, &[]); 33 | 34 | // The results of the shaping operation are stored in the `output` buffer. 35 | 36 | let positions = output.get_glyph_positions(); 37 | let infos = output.get_glyph_infos(); 38 | 39 | // iterate over the shaped glyphs 40 | for (position, info) in positions.iter().zip(infos) { 41 | let gid = info.codepoint; 42 | let cluster = info.cluster; 43 | let x_advance = position.x_advance; 44 | let x_offset = position.x_offset; 45 | let y_offset = position.y_offset; 46 | 47 | // Here you would usually draw the glyphs. 48 | println!("gid{:?}={:?}@{:?},{:?}+{:?}", gid, cluster, x_advance, x_offset, y_offset); 49 | } 50 | ``` 51 | 52 | This should print out something similar to the following: 53 | 54 | ```text 55 | gid41=0@741,0+0 56 | gid70=1@421,0+0 57 | gid77=2@258,0+0 58 | gid77=3@253,0+0 59 | gid80=4@510,0+0 60 | gid1=5@227,0+0 61 | gid56=6@874,0+0 62 | gid80=7@498,0+0 63 | gid83=8@367,0+0 64 | gid77=9@253,0+0 65 | gid69=10@528,0+0 66 | gid2=11@276,0+0 67 | ``` 68 | 69 | The values of `x_advance`, `x_offset`, `y_advance` and `y_offset` are all given in so-called _font units_ by default. 70 | By calling `face.upem()` you get the number of font units per [EM]() for 71 | a specific `face`. This `upem` value can be used to scale the advances and offsets to a given font-size. 72 | For example, if you want to display a font at 16 point (pt) size, that means that _1 EM = 16 pt_. 73 | In this example, to convert a value, say `x_advance`, from font-units to points, we compute `((x_advance * font_size) as f64) / (upem as f64)`, where `font_size = 16` is a variable specifying the font size in points. 74 | 75 | Note that harfbuzz internally supports scaling fonts itself as well (using `font.set_scale(...)`, etc.) but in my opinion it is easier to scale the results oneself as described in the paragraph above. 76 | 77 | # Supported HarfBuzz versions 78 | 79 | This crate is tested to work with harfbuzz versions 2.0 and higher. Older versions may work as well. I recommend statically linking the harfbuzz library provided by the `harfbuzz-sys` crate which is always up-to-date. 80 | 81 | # Optional Features 82 | 83 | If you want to use rusttype as font functions enable the `rusttype` feature. 84 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "build-native-harfbuzz")] 2 | extern crate cc; 3 | #[cfg(feature = "build-native-harfbuzz")] 4 | extern crate pkg_config; 5 | 6 | #[cfg(feature = "build-native-harfbuzz")] 7 | fn main() { 8 | use std::env; 9 | 10 | let target = env::var("TARGET").unwrap(); 11 | 12 | println!("cargo:rerun-if-env-changed=HARFBUZZ_SYS_NO_PKG_CONFIG"); 13 | if (target.contains("wasm32") || env::var_os("HARFBUZZ_SYS_NO_PKG_CONFIG").is_none()) 14 | && pkg_config::probe_library("harfbuzz").is_ok() 15 | { 16 | return; 17 | } 18 | 19 | let mut cfg = cc::Build::new(); 20 | cfg.cpp(true) 21 | .flag("-std=c++11") 22 | .warnings(false) 23 | .include("harfbuzz/src") 24 | .file("harfbuzz/src/harfbuzz.cc"); 25 | 26 | if !target.contains("windows") { 27 | cfg.define("HAVE_PTHREAD", "1"); 28 | } 29 | 30 | // if target.contains("apple") { 31 | // cfg.define("HAVE_CORETEXT", "1"); 32 | // } 33 | 34 | if target.contains("windows-gnu") { 35 | cfg.flag("-Wa,-mbig-obj"); 36 | } 37 | 38 | cfg.compile("embedded_harfbuzz"); 39 | } 40 | 41 | #[cfg(not(feature = "build-native-harfbuzz"))] 42 | fn main() {} 43 | -------------------------------------------------------------------------------- /examples/simple.rs: -------------------------------------------------------------------------------- 1 | use harfbuzz_rs::{shape, Face, Font, UnicodeBuffer}; 2 | 3 | // Execute this file from the root directory of this repository. 4 | // 5 | // The output should look like the following: 6 | // gid09=00@ 634,0+0 7 | // gid32=01@ 478,0+0 8 | // gid39=02@ 230,0+0 9 | // gid39=03@ 230,0+0 10 | // gid42=04@ 520,0+0 11 | // gid01=05@ 200,0+0 12 | // gid24=06@ 764,0+0 13 | // gid42=07@ 532,0+0 14 | // gid45=08@ 306,0+0 15 | // gid39=09@ 230,0+0 16 | // gid31=10@ 540,0+0 17 | // gid1146=11@ 248,0+0 18 | fn main() { 19 | let index = 0; 20 | let path = "testfiles/SourceSansVariable-Roman.ttf"; 21 | let face = Face::from_file(path, index).expect("Error reading font file."); 22 | let font = Font::new(face); 23 | 24 | // Create a buffer with some text, shape it... 25 | let buffer = UnicodeBuffer::new().add_str("Hello World!"); 26 | 27 | let result = shape(&font, buffer, &[]); 28 | 29 | // ... and get the results. 30 | let positions = result.get_glyph_positions(); 31 | let infos = result.get_glyph_infos(); 32 | 33 | // iterate over the shaped glyphs 34 | for (position, info) in positions.iter().zip(infos) { 35 | let gid = info.codepoint; 36 | let cluster = info.cluster; 37 | let x_advance = position.x_advance; 38 | let x_offset = position.x_offset; 39 | let y_offset = position.y_offset; 40 | 41 | println!( 42 | "gid{:0>2?}={:0>2?}@{:>4?},{:?}+{:?}", 43 | gid, cluster, x_advance, x_offset, y_offset 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/blob.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_void; 2 | 3 | use std::marker::PhantomData; 4 | 5 | use std::fmt; 6 | use std::fs; 7 | use std::path::Path; 8 | use std::ptr::NonNull; 9 | 10 | use crate::bindings::hb_blob_create; 11 | use crate::bindings::hb_blob_create_sub_blob; 12 | use crate::bindings::hb_blob_destroy; 13 | use crate::bindings::hb_blob_get_data; 14 | use crate::bindings::hb_blob_get_data_writable; 15 | use crate::bindings::hb_blob_get_length; 16 | use crate::bindings::hb_blob_is_immutable; 17 | use crate::bindings::hb_blob_make_immutable; 18 | use crate::bindings::hb_blob_reference; 19 | use crate::bindings::hb_blob_t; 20 | use crate::bindings::HB_MEMORY_MODE_READONLY; 21 | use crate::bindings::HB_MEMORY_MODE_WRITABLE; 22 | use crate::common::{HarfbuzzObject, Owned, Shared}; 23 | 24 | /// A `Blob` manages raw data like e.g. file contents. It refers to a slice of 25 | /// bytes that can be either owned by the `Blob` or not. 26 | /// 27 | /// It is used to provide access to memory for `Face` and `Font`. Typically it 28 | /// contains the raw font data (e.g. an entire font file or font tables). To enable 29 | /// shared usage of its data it uses a reference counting mechanism making the 30 | /// `clone` operation very cheap as no data is cloned. 31 | /// 32 | /// # Construction 33 | /// 34 | /// A `Blob` implements `Into` for every type that satisfies the `AsRef<[u8]>` 35 | /// trait such as `Vec` and `Box<[u8]>` so owned blobs can be created easily 36 | /// from standard Rust objects. 37 | /// 38 | /// You can also create `Blob`s that contain borrowed data using the constructors 39 | /// `Blob::with_bytes` and `Blob::with_bytes_mut` for immutable and mutable access 40 | /// respectively. 41 | #[repr(C)] 42 | pub struct Blob<'a> { 43 | raw: NonNull, 44 | marker: PhantomData<&'a [u8]>, 45 | } 46 | impl<'a> Blob<'a> { 47 | /// Create a new `Blob` from the slice `bytes`. The blob will not own the 48 | /// slice's data. 49 | pub fn with_bytes(bytes: &'a [u8]) -> Owned> { 50 | let hb_blob = unsafe { 51 | hb_blob_create( 52 | bytes.as_ptr() as *const _, 53 | bytes.len() as u32, 54 | HB_MEMORY_MODE_READONLY, 55 | std::ptr::null_mut(), 56 | None, 57 | ) 58 | }; 59 | unsafe { Owned::from_raw(hb_blob) } 60 | } 61 | 62 | /// Create a new `Blob` from the mutable slice `bytes`. The blob will not own the 63 | /// slice's data. 64 | pub fn with_bytes_mut(bytes: &'a mut [u8]) -> Owned> { 65 | let hb_blob = unsafe { 66 | hb_blob_create( 67 | bytes.as_ptr() as *const _, 68 | bytes.len() as u32, 69 | HB_MEMORY_MODE_WRITABLE, 70 | std::ptr::null_mut(), 71 | None, 72 | ) 73 | }; 74 | unsafe { Owned::from_raw(hb_blob) } 75 | } 76 | 77 | /// Create a new `Blob` from a type that owns a byte slice, effectively handing over 78 | /// ownership of its data to the blob. 79 | pub fn with_bytes_owned( 80 | bytes_owner: T, 81 | projector: impl Fn(&T) -> &[u8], 82 | ) -> Owned> { 83 | let boxxed = Box::new(bytes_owner); 84 | let slice = projector(&boxxed); 85 | let len = slice.len(); 86 | let ptr = slice.as_ptr(); 87 | 88 | let data = Box::into_raw(boxxed); 89 | 90 | extern "C" fn destroy(ptr: *mut c_void) { 91 | _ = unsafe { Box::from_raw(ptr as *mut U) }; 92 | } 93 | 94 | let hb_blob = unsafe { 95 | hb_blob_create( 96 | ptr as *const _, 97 | len as u32, 98 | HB_MEMORY_MODE_READONLY, 99 | data as *mut _, 100 | Some(destroy::), 101 | ) 102 | }; 103 | unsafe { Owned::from_raw(hb_blob) } 104 | } 105 | 106 | /// Create a `Blob` from the contents of the file at `path` whose contents 107 | /// will be read into memory. 108 | /// 109 | /// The result will be either a `Blob` that owns the file's contents or an 110 | /// error that happened while trying to read the file. 111 | /// 112 | /// This can be a performance problem if the file is very big. If this turns 113 | /// out to be a problem consider `mmap`ing the file or splitting it into 114 | /// smaller chunks before creating a `Blob`. 115 | pub fn from_file>(path: P) -> std::io::Result>> { 116 | let vec = fs::read(path)?; 117 | Ok(vec.into()) 118 | } 119 | 120 | /// Get a slice of the `Blob`'s bytes. 121 | pub fn get_data(&self) -> &[u8] { 122 | unsafe { 123 | let mut length = hb_blob_get_length(self.as_raw()); 124 | let data_ptr = hb_blob_get_data(self.as_raw(), &mut length as *mut _); 125 | std::slice::from_raw_parts(data_ptr as *const u8, length as usize) 126 | } 127 | } 128 | 129 | /// Creates an immutable `Blob` that contains part of the data of the parent 130 | /// `Blob`. The parent `Blob` will be immutable after this and the sub`Blob` 131 | /// cannot outlive its parent. 132 | /// 133 | /// ### Arguments 134 | /// * `offset`: Byte-offset of sub-blob within parent. 135 | /// * `length`: Length of the sub-blob. 136 | pub fn create_sub_blob(&self, offset: usize, length: usize) -> Shared> { 137 | let blob = unsafe { hb_blob_create_sub_blob(self.as_raw(), offset as u32, length as u32) }; 138 | unsafe { Shared::from_raw_owned(blob) } 139 | } 140 | 141 | /// Returns true if the blob is immutable. 142 | /// 143 | /// HarfBuzz internally uses this value to make sure the blob is not mutated 144 | /// after being shared. In Rust this is not really necessary due to the borrow 145 | /// checker. This method is provided regardless for completeness. 146 | pub fn is_immutable(&self) -> bool { 147 | unsafe { hb_blob_is_immutable(self.as_raw()) == 1 } 148 | } 149 | 150 | /// Makes this blob immutable so the bytes it refers to will never change 151 | /// during its lifetime. 152 | pub fn make_immutable(&mut self) { 153 | unsafe { hb_blob_make_immutable(self.as_raw()) } 154 | } 155 | 156 | /// Try to get a mutable slice of the `Blob`'s bytes, possibly copying them. 157 | /// 158 | /// This returns `None` if the blob is immutable or memory allocation 159 | /// failed. 160 | pub fn try_get_mut_data(&mut self) -> Option<&'a mut [u8]> { 161 | unsafe { 162 | let mut length = hb_blob_get_length(self.as_raw()); 163 | let data_ptr = hb_blob_get_data_writable(self.as_raw(), &mut length as *mut _); 164 | if data_ptr.is_null() { 165 | None 166 | } else { 167 | Some(std::slice::from_raw_parts_mut( 168 | data_ptr as *mut u8, 169 | length as usize, 170 | )) 171 | } 172 | } 173 | } 174 | } 175 | 176 | unsafe impl<'a> Send for Blob<'a> {} 177 | unsafe impl<'a> Sync for Blob<'a> {} 178 | 179 | impl<'a> fmt::Debug for Blob<'a> { 180 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 181 | f.debug_struct("Blob") 182 | .field("data", &self.get_data()) 183 | .field("is_immutable", &self.is_immutable()) 184 | .finish() 185 | } 186 | } 187 | 188 | unsafe impl<'a> HarfbuzzObject for Blob<'a> { 189 | type Raw = hb_blob_t; 190 | 191 | unsafe fn from_raw(raw: *const hb_blob_t) -> Self { 192 | Blob { 193 | raw: NonNull::new(raw as *mut _).unwrap(), 194 | marker: PhantomData, 195 | } 196 | } 197 | 198 | fn as_raw(&self) -> *mut hb_blob_t { 199 | self.raw.as_ptr() 200 | } 201 | 202 | unsafe fn reference(&self) { 203 | hb_blob_reference(self.as_raw()); 204 | } 205 | 206 | unsafe fn dereference(&self) { 207 | hb_blob_destroy(self.as_raw()); 208 | } 209 | } 210 | 211 | use std::ops::Deref; 212 | impl<'a> Deref for Blob<'a> { 213 | type Target = [u8]; 214 | 215 | fn deref(&self) -> &[u8] { 216 | self.get_data() 217 | } 218 | } 219 | 220 | use std::convert::AsRef; 221 | impl<'a> AsRef<[u8]> for Blob<'a> { 222 | fn as_ref(&self) -> &[u8] { 223 | self.get_data() 224 | } 225 | } 226 | 227 | use std::convert::From; 228 | 229 | impl<'a, T> From for Shared> 230 | where 231 | T: 'a + Send + AsRef<[u8]>, 232 | { 233 | fn from(container: T) -> Shared> { 234 | let blob = Blob::with_bytes_owned(container, |t| t.as_ref()); 235 | blob.into() 236 | } 237 | } 238 | 239 | #[cfg(test)] 240 | mod tests { 241 | use super::*; 242 | 243 | #[test] 244 | fn test_vec_to_blob_conversion() { 245 | let a_vec: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; 246 | let blob: Shared> = a_vec.into(); 247 | 248 | assert_eq!(blob.len(), 11); 249 | 250 | let mut counter: u8 = 1; 251 | for num in blob.iter() { 252 | assert_eq!(*num, counter); 253 | counter += 1; 254 | } 255 | } 256 | 257 | use std::sync::Arc; 258 | #[test] 259 | fn test_arc_to_blob_conversion() { 260 | let rc_slice: Arc<[u8]> = Arc::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); 261 | let blob: Shared> = rc_slice.into(); 262 | 263 | assert_eq!(blob.len(), 11); 264 | 265 | let mut counter: u8 = 1; 266 | for num in blob.iter() { 267 | assert_eq!(*num, counter); 268 | counter += 1; 269 | } 270 | } 271 | 272 | #[test] 273 | fn test_arc_vec_to_blob_conversion() { 274 | let rc_slice: Arc> = Arc::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); 275 | let blob: Owned> = Blob::with_bytes_owned(rc_slice.clone(), |t| t); 276 | assert_eq!(Arc::strong_count(&rc_slice), 2); 277 | 278 | assert_eq!(blob.len(), 11); 279 | 280 | let mut counter: u8 = 1; 281 | for num in blob.iter() { 282 | assert_eq!(*num, counter); 283 | counter += 1; 284 | } 285 | 286 | std::mem::drop(blob); 287 | assert_eq!(Arc::strong_count(&rc_slice), 1); 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /src/buffer.rs: -------------------------------------------------------------------------------- 1 | use crate::bindings::{ 2 | hb_buffer_add, hb_buffer_add_utf8, hb_buffer_append, hb_buffer_clear_contents, 3 | hb_buffer_cluster_level_t, hb_buffer_content_type_t, hb_buffer_create, hb_buffer_destroy, 4 | hb_buffer_get_cluster_level, hb_buffer_get_content_type, hb_buffer_get_direction, 5 | hb_buffer_get_empty, hb_buffer_get_glyph_infos, hb_buffer_get_glyph_positions, 6 | hb_buffer_get_language, hb_buffer_get_length, hb_buffer_get_script, 7 | hb_buffer_get_segment_properties, hb_buffer_guess_segment_properties, hb_buffer_pre_allocate, 8 | hb_buffer_reference, hb_buffer_reverse, hb_buffer_reverse_range, hb_buffer_serialize_format_t, 9 | hb_buffer_serialize_glyphs, hb_buffer_set_cluster_level, hb_buffer_set_content_type, 10 | hb_buffer_set_direction, hb_buffer_set_language, hb_buffer_set_script, hb_buffer_t, 11 | hb_glyph_flags_t, hb_glyph_info_get_glyph_flags, hb_glyph_info_t, hb_mask_t, 12 | hb_script_from_iso15924_tag, hb_script_t, hb_script_to_iso15924_tag, hb_segment_properties_t, 13 | hb_var_int_t, HB_BUFFER_CLUSTER_LEVEL_CHARACTERS, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS, 14 | HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES, HB_BUFFER_CONTENT_TYPE_GLYPHS, 15 | HB_BUFFER_CONTENT_TYPE_UNICODE, HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS, 16 | HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS, HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES, 17 | HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS, HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES, 18 | HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS, HB_BUFFER_SERIALIZE_FORMAT_JSON, 19 | HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_GLYPH_FLAG_UNSAFE_TO_BREAK, 20 | }; 21 | use crate::common::{Direction, HarfbuzzObject, Language, Owned, Script, Tag}; 22 | use crate::font::Position; 23 | 24 | use fmt::Formatter; 25 | use std::io::Read; 26 | use std::os; 27 | use std::os::raw::c_uint; 28 | use std::ptr::NonNull; 29 | use std::{fmt, io}; 30 | 31 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 32 | #[repr(C)] 33 | pub struct SegmentProperties { 34 | pub direction: Direction, 35 | pub script: Script, 36 | pub language: Language, 37 | } 38 | 39 | impl SegmentProperties { 40 | pub fn from_raw(raw: hb_segment_properties_t) -> Self { 41 | let direction = Direction::from_raw(raw.direction); 42 | let script = Script(raw.script); 43 | let language = Language(raw.language); 44 | SegmentProperties { 45 | direction, 46 | script, 47 | language, 48 | } 49 | } 50 | 51 | pub fn into_raw(self) -> hb_segment_properties_t { 52 | hb_segment_properties_t { 53 | direction: self.direction.to_raw(), 54 | script: self.script.0, 55 | language: self.language.0, 56 | reserved1: std::ptr::null_mut(), 57 | reserved2: std::ptr::null_mut(), 58 | } 59 | } 60 | } 61 | 62 | /// `GlyphPosition` is the structure that holds the positions of the glyph in 63 | /// both horizontal and vertical directions. All positions in `GlyphPosition` 64 | /// are relative to the current point. 65 | #[repr(C)] 66 | #[derive(Copy, Clone)] 67 | pub struct GlyphPosition { 68 | /// how much the line advances after drawing this glyph when setting text in 69 | /// horizontal direction. 70 | pub x_advance: Position, 71 | /// how much the line advances after drawing this glyph when setting text in 72 | /// vertical direction. 73 | pub y_advance: Position, 74 | /// how much the glyph moves on the X-axis before drawing it, this should 75 | /// not affect how much the line advances. 76 | pub x_offset: Position, 77 | /// how much the glyph moves on the Y-axis before drawing it, this should 78 | /// not affect how much the line advances. 79 | pub y_offset: Position, 80 | var: hb_var_int_t, 81 | } 82 | 83 | impl std::fmt::Debug for GlyphPosition { 84 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 85 | f.debug_struct("GlyphPosition") 86 | .field("x_advance", &self.x_advance) 87 | .field("y_advance", &self.y_advance) 88 | .field("x_offset", &self.x_offset) 89 | .field("y_offset", &self.y_offset) 90 | .finish() 91 | } 92 | } 93 | 94 | impl GlyphPosition { 95 | pub const fn new( 96 | x_advance: Position, 97 | y_advance: Position, 98 | x_offset: Position, 99 | y_offset: Position, 100 | ) -> Self { 101 | GlyphPosition { 102 | x_advance, 103 | y_advance, 104 | x_offset, 105 | y_offset, 106 | var: hb_var_int_t { u32_: 0 }, 107 | } 108 | } 109 | } 110 | 111 | /// A set of flags that may be set during shaping on each glyph. 112 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 113 | pub struct GlyphFlags(pub hb_glyph_flags_t); 114 | 115 | #[allow(clippy::trivially_copy_pass_by_ref)] 116 | impl GlyphFlags { 117 | /// If `true`, indicates that if input text is broken at the beginning of 118 | /// the cluster this glyph is part of, then both sides need to be re-shaped, 119 | /// as the result might be different. On the flip side, it means that when 120 | /// this function returns `false`, then it's safe to break the glyph-run at 121 | /// the beginning of this cluster, and the two sides represent the exact 122 | /// same result one would get if breaking input text at the beginning of 123 | /// this cluster and shaping the two sides separately. This can be used to 124 | /// optimize paragraph layout, by avoiding re-shaping of each line after 125 | /// line-breaking, or limiting the reshaping to a small piece around the 126 | /// breaking point only. 127 | pub fn unsafe_to_break(&self) -> bool { 128 | self.0 & HB_GLYPH_FLAG_UNSAFE_TO_BREAK == HB_GLYPH_FLAG_UNSAFE_TO_BREAK 129 | } 130 | } 131 | 132 | #[derive(Copy, Clone)] 133 | #[repr(C)] 134 | pub struct GlyphInfo { 135 | pub codepoint: u32, 136 | mask: hb_mask_t, 137 | pub cluster: u32, 138 | var1: hb_var_int_t, 139 | var2: hb_var_int_t, 140 | } 141 | 142 | impl std::fmt::Debug for GlyphInfo { 143 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 144 | f.debug_struct("GlyphInfo") 145 | .field("codepoint", &self.codepoint) 146 | .field("cluster", &self.cluster) 147 | .field("flags", &self.glyph_flags()) 148 | .finish() 149 | } 150 | } 151 | 152 | impl GlyphInfo { 153 | pub fn glyph_flags(&self) -> GlyphFlags { 154 | GlyphFlags(unsafe { hb_glyph_info_get_glyph_flags(self.as_raw()) }) 155 | } 156 | 157 | fn as_raw(&self) -> *const hb_glyph_info_t { 158 | (self as *const GlyphInfo) as *const _ 159 | } 160 | } 161 | 162 | #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)] 163 | pub enum ClusterLevel { 164 | #[default] 165 | MonotoneGraphemes, 166 | MonotoneCharacters, 167 | Characters, 168 | } 169 | 170 | impl ClusterLevel { 171 | pub fn from_raw(raw: hb_buffer_cluster_level_t) -> Self { 172 | match raw { 173 | HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES => ClusterLevel::MonotoneGraphemes, 174 | HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS => ClusterLevel::MonotoneCharacters, 175 | HB_BUFFER_CLUSTER_LEVEL_CHARACTERS => ClusterLevel::Characters, 176 | _ => panic!("received unrecognized HB_BUFFER_CLUSTER_LEVEL"), 177 | } 178 | } 179 | 180 | pub fn into_raw(self) -> hb_buffer_cluster_level_t { 181 | match self { 182 | ClusterLevel::MonotoneGraphemes => HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES, 183 | ClusterLevel::MonotoneCharacters => HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS, 184 | ClusterLevel::Characters => HB_BUFFER_CLUSTER_LEVEL_CHARACTERS, 185 | } 186 | } 187 | } 188 | 189 | #[derive(Debug)] 190 | pub(crate) struct GenericBuffer { 191 | raw: NonNull, 192 | } 193 | impl GenericBuffer { 194 | pub(crate) fn new() -> Owned { 195 | let buffer = unsafe { hb_buffer_create() }; 196 | unsafe { Owned::from_raw(buffer) } 197 | } 198 | 199 | #[allow(unused)] 200 | pub(crate) fn empty() -> Owned { 201 | let buffer = unsafe { hb_buffer_get_empty() }; 202 | unsafe { Owned::from_raw(buffer) } 203 | } 204 | 205 | pub(crate) fn len(&self) -> usize { 206 | unsafe { hb_buffer_get_length(self.as_raw()) as usize } 207 | } 208 | 209 | pub(crate) fn is_empty(&self) -> bool { 210 | self.len() == 0 211 | } 212 | 213 | pub(crate) fn add(&mut self, codepoint: u32, cluster: u32) { 214 | unsafe { 215 | hb_buffer_add(self.as_raw(), codepoint, cluster); 216 | } 217 | } 218 | 219 | pub(crate) fn add_str_item(&mut self, string: &str, item_start: usize, item_len: usize) { 220 | assert!(item_start + item_len <= string.len()); 221 | let utf8_ptr = string.as_ptr() as *const _; 222 | unsafe { 223 | hb_buffer_add_utf8( 224 | self.as_raw(), 225 | utf8_ptr, 226 | string.len() as os::raw::c_int, 227 | item_start as os::raw::c_uint, 228 | item_len as os::raw::c_int, 229 | ); 230 | } 231 | } 232 | 233 | pub(crate) fn append(&mut self, source: &GenericBuffer, start: c_uint, end: c_uint) { 234 | unsafe { 235 | hb_buffer_append(self.as_raw(), source.as_raw(), start, end); 236 | } 237 | } 238 | 239 | pub(crate) fn set_direction(&mut self, direction: Direction) { 240 | unsafe { hb_buffer_set_direction(self.as_raw(), direction.to_raw()) }; 241 | } 242 | 243 | /// Returns the `Buffer`'s text direction. 244 | pub(crate) fn get_direction(&self) -> Direction { 245 | Direction::from_raw(unsafe { hb_buffer_get_direction(self.as_raw()) }) 246 | } 247 | 248 | pub(crate) fn set_language(&mut self, lang: Language) { 249 | unsafe { hb_buffer_set_language(self.as_raw(), lang.0) } 250 | } 251 | 252 | pub(crate) fn get_language(&self) -> Option { 253 | let raw_lang = unsafe { hb_buffer_get_language(self.as_raw()) }; 254 | if raw_lang.is_null() { 255 | None 256 | } else { 257 | Some(Language(raw_lang)) 258 | } 259 | } 260 | 261 | pub(crate) fn set_script(&mut self, script: hb_script_t) { 262 | unsafe { hb_buffer_set_script(self.as_raw(), script) } 263 | } 264 | 265 | pub(crate) fn get_script(&self) -> hb_script_t { 266 | unsafe { hb_buffer_get_script(self.as_raw()) } 267 | } 268 | 269 | pub(crate) fn guess_segment_properties(&mut self) { 270 | unsafe { hb_buffer_guess_segment_properties(self.as_raw()) }; 271 | } 272 | 273 | pub(crate) fn get_segment_properties(&self) -> SegmentProperties { 274 | unsafe { 275 | let mut segment_props: hb_segment_properties_t = std::mem::zeroed(); 276 | hb_buffer_get_segment_properties(self.as_raw(), &mut segment_props as *mut _); 277 | SegmentProperties::from_raw(segment_props) 278 | } 279 | } 280 | 281 | pub(crate) fn set_cluster_level(&mut self, cluster_level: ClusterLevel) { 282 | unsafe { hb_buffer_set_cluster_level(self.as_raw(), cluster_level.into_raw()) } 283 | } 284 | 285 | pub(crate) fn get_cluster_level(&self) -> ClusterLevel { 286 | ClusterLevel::from_raw(unsafe { hb_buffer_get_cluster_level(self.as_raw()) }) 287 | } 288 | 289 | pub(crate) fn pre_allocate(&mut self, size: usize) { 290 | let size = size.min(std::os::raw::c_uint::MAX as usize); 291 | unsafe { hb_buffer_pre_allocate(self.as_raw(), size as _) }; 292 | } 293 | 294 | pub(crate) fn clear_contents(&mut self) { 295 | unsafe { hb_buffer_clear_contents(self.as_raw()) }; 296 | } 297 | 298 | pub(crate) fn get_glyph_positions(&self) -> &[GlyphPosition] { 299 | unsafe { 300 | let mut length: u32 = 0; 301 | let glyph_pos = hb_buffer_get_glyph_positions(self.as_raw(), &mut length as *mut u32); 302 | std::slice::from_raw_parts(glyph_pos as *const _, length as usize) 303 | } 304 | } 305 | 306 | pub(crate) fn get_glyph_infos(&self) -> &[GlyphInfo] { 307 | unsafe { 308 | let mut length: u32 = 0; 309 | let glyph_infos = hb_buffer_get_glyph_infos(self.as_raw(), &mut length as *mut u32); 310 | std::slice::from_raw_parts(glyph_infos as *const _, length as usize) 311 | } 312 | } 313 | 314 | /// Reverse the `Buffer`'s contents. 315 | pub(crate) fn reverse(&mut self) { 316 | unsafe { hb_buffer_reverse(self.as_raw()) }; 317 | } 318 | 319 | /// Reverse the `Buffer`'s contents in the range from `start` to `end`. 320 | pub(crate) fn reverse_range(&mut self, start: usize, end: usize) { 321 | assert!(start <= self.len(), "{}", end <= self.len()); 322 | unsafe { hb_buffer_reverse_range(self.as_raw(), start as u32, end as u32) } 323 | } 324 | 325 | pub(crate) fn set_content_type(&self, content_type: hb_buffer_content_type_t) { 326 | unsafe { hb_buffer_set_content_type(self.as_raw(), content_type) } 327 | } 328 | 329 | pub(crate) fn content_type(&self) -> hb_buffer_content_type_t { 330 | unsafe { hb_buffer_get_content_type(self.as_raw()) } 331 | } 332 | } 333 | 334 | unsafe impl HarfbuzzObject for GenericBuffer { 335 | type Raw = hb_buffer_t; 336 | 337 | unsafe fn from_raw(raw: *const Self::Raw) -> Self { 338 | GenericBuffer { 339 | raw: NonNull::new(raw as *mut _).unwrap(), 340 | } 341 | } 342 | 343 | fn as_raw(&self) -> *mut Self::Raw { 344 | self.raw.as_ptr() 345 | } 346 | 347 | unsafe fn reference(&self) { 348 | hb_buffer_reference(self.as_raw()); 349 | } 350 | 351 | unsafe fn dereference(&self) { 352 | hb_buffer_destroy(self.as_raw()); 353 | } 354 | } 355 | 356 | /// The serialization format used in `BufferSerializer`. 357 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 358 | pub enum SerializeFormat { 359 | /// A human-readable, plain text format 360 | Text, 361 | /// A machine-readable JSON format. 362 | Json, 363 | } 364 | 365 | impl From for hb_buffer_serialize_format_t { 366 | fn from(fmt: SerializeFormat) -> Self { 367 | match fmt { 368 | SerializeFormat::Text => HB_BUFFER_SERIALIZE_FORMAT_TEXT, 369 | SerializeFormat::Json => HB_BUFFER_SERIALIZE_FORMAT_JSON, 370 | } 371 | } 372 | } 373 | 374 | bitflags! { 375 | /// Flags used for serialization with a `BufferSerializer`. 376 | #[derive(Default)] 377 | pub struct SerializeFlags: u32 { 378 | /// Do not serialize glyph cluster. 379 | const NO_CLUSTERS = HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS; 380 | /// Do not serialize glyph position information. 381 | const NO_POSITIONS = HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS; 382 | /// Do no serialize glyph name. 383 | const NO_GLYPH_NAMES = HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES; 384 | /// Serialize glyph extents. 385 | const GLYPH_EXTENTS = HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS; 386 | /// Serialize glyph flags. 387 | const GLYPH_FLAGS = HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS; 388 | /// Do not serialize glyph advances, glyph offsets will reflect absolute 389 | /// glyph positions. 390 | const NO_ADVANCES = HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES; 391 | } 392 | } 393 | 394 | /// A type that can be used to serialize a `GlyphBuffer`. 395 | /// 396 | /// A `BufferSerializer` is obtained by calling the `GlyphBuffer::serializer` 397 | /// method and provides a `Read` implementation that allows you to read the 398 | /// serialized buffer contents. 399 | #[derive(Debug)] 400 | pub struct BufferSerializer<'a> { 401 | font: Option<&'a crate::Font<'a>>, 402 | buffer: &'a Owned, 403 | start: usize, 404 | end: usize, 405 | format: SerializeFormat, 406 | flags: SerializeFlags, 407 | 408 | bytes: io::Cursor>, 409 | } 410 | 411 | impl<'a> Read for BufferSerializer<'a> { 412 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 413 | match self.bytes.read(buf) { 414 | // if `bytes` is empty refill it 415 | Ok(0) => { 416 | if self.start > self.end.saturating_sub(1) { 417 | return Ok(0); 418 | } 419 | let mut bytes_written = 0; 420 | let num_serialized_items = unsafe { 421 | hb_buffer_serialize_glyphs( 422 | self.buffer.as_raw(), 423 | self.start as u32, 424 | self.end as u32, 425 | self.bytes.get_mut().as_mut_ptr() as *mut _, 426 | self.bytes.get_ref().capacity() as u32, 427 | &mut bytes_written, 428 | self.font 429 | .map(|f| f.as_raw()) 430 | .unwrap_or(std::ptr::null_mut()), 431 | self.format.into(), 432 | self.flags.bits(), 433 | ) 434 | }; 435 | self.start += num_serialized_items as usize; 436 | self.bytes.set_position(0); 437 | unsafe { self.bytes.get_mut().set_len(bytes_written as usize) }; 438 | 439 | self.read(buf) 440 | } 441 | Ok(size) => Ok(size), 442 | Err(err) => Err(err), 443 | } 444 | } 445 | } 446 | 447 | /// This type provides an interface to create one of the buffer types from a raw 448 | /// harfbuzz pointer. 449 | #[derive(Debug)] 450 | pub enum TypedBuffer { 451 | /// Contains a `UnicodeBuffer` 452 | Unicode(UnicodeBuffer), 453 | /// Contains a `GlyphBuffer` 454 | Glyphs(GlyphBuffer), 455 | } 456 | 457 | impl TypedBuffer { 458 | /// Takes ownership of the raw `hb_buffer_t` object and converts it to a 459 | /// `TypedBuffer`. If no safe conversion is possible returns `None`. 460 | /// 461 | /// # Safety 462 | /// 463 | /// Marked as unsafe because it acceses a raw pointer. Internally calls 464 | /// `Owned::from_raw` and therefore the same ownership considerations apply. 465 | pub unsafe fn take_from_raw(raw: *mut hb_buffer_t) -> Option { 466 | let generic_buf: Owned = Owned::from_raw(raw); 467 | let content_type = generic_buf.content_type(); 468 | match content_type { 469 | HB_BUFFER_CONTENT_TYPE_UNICODE => { 470 | Some(TypedBuffer::Unicode(UnicodeBuffer(generic_buf))) 471 | } 472 | HB_BUFFER_CONTENT_TYPE_GLYPHS => Some(TypedBuffer::Glyphs(GlyphBuffer(generic_buf))), 473 | _ => None, 474 | } 475 | } 476 | } 477 | 478 | /// A `UnicodeBuffer` can be filled with unicode text and corresponding cluster 479 | /// indices. 480 | /// 481 | /// # Usage 482 | /// 483 | /// The buffer manages an allocation for the unicode codepoints to be shaped. 484 | /// This allocation is reused for storing the results of the shaping operation 485 | /// in a `GlyphBuffer` object. The intended usage is to keep one (or e.g. one 486 | /// per thread) `UnicodeBuffer` around. When needed, you fill it with text that 487 | /// should be shaped and pass it as an argument to the `shape` function. That 488 | /// method returns a `GlyphBuffer` object containing the shaped glyph indices. 489 | /// Once you got the needed information out of the `GlyphBuffer` you call its 490 | /// `.clear()` method which in turn gives you a fresh `UnicodeBuffer` (also 491 | /// reusing the original allocation again). This buffer can then be used to 492 | /// shape more text. 493 | /// 494 | /// # Interaction with the raw harfbuzz API 495 | /// 496 | /// If you want to get a `UnicodeBuffer` from a pointer to a raw harfbuzz 497 | /// object, you need to use the `from_raw` static method on `TypedBuffer`. This 498 | /// ensures that a buffer of correct type is created. 499 | pub struct UnicodeBuffer(pub(crate) Owned); 500 | impl UnicodeBuffer { 501 | pub(crate) fn from_generic(generic: Owned) -> Self { 502 | generic.set_content_type(HB_BUFFER_CONTENT_TYPE_UNICODE); 503 | UnicodeBuffer(generic) 504 | } 505 | 506 | /// Creates a new empty `Buffer`. 507 | /// 508 | /// # Examples 509 | /// ``` 510 | /// use harfbuzz_rs::UnicodeBuffer; 511 | /// 512 | /// let buffer = UnicodeBuffer::new(); 513 | /// assert!(buffer.is_empty()); 514 | /// ``` 515 | pub fn new() -> UnicodeBuffer { 516 | UnicodeBuffer::from_generic(GenericBuffer::new()) 517 | } 518 | 519 | /// Converts this buffer to a raw harfbuzz object pointer. 520 | pub fn into_raw(self) -> *mut hb_buffer_t { 521 | Owned::into_raw(self.0) 522 | } 523 | 524 | /// Returns the length of the data of the buffer. 525 | /// 526 | /// This corresponds to the number of unicode codepoints contained in the 527 | /// buffer. 528 | /// 529 | /// # Examples 530 | /// ``` 531 | /// use harfbuzz_rs::UnicodeBuffer; 532 | /// 533 | /// let str1 = "Hello "; 534 | /// let buffer = UnicodeBuffer::new().add_str(str1); 535 | /// assert_eq!(buffer.len(), str1.len()); 536 | /// 537 | /// let str2 = "😍🙈"; 538 | /// let buffer = buffer.add_str(str2); 539 | /// assert_eq!(buffer.len(), str1.len() + 2);; 540 | /// ``` 541 | pub fn len(&self) -> usize { 542 | self.0.len() 543 | } 544 | 545 | /// Returns `true` if the buffer contains no elements. 546 | pub fn is_empty(&self) -> bool { 547 | self.0.is_empty() 548 | } 549 | 550 | /// Add a single codepoint with the associated cluster value to the buffer. 551 | /// 552 | /// # Examples 553 | /// 554 | /// ``` 555 | /// use harfbuzz_rs::UnicodeBuffer; 556 | /// 557 | /// let buffer = UnicodeBuffer::new().add('A' as u32, 0); 558 | /// assert_eq!(buffer.string_lossy(), "A"); 559 | /// ``` 560 | pub fn add(mut self, codepoint: u32, cluster: u32) -> UnicodeBuffer { 561 | self.0.add(codepoint, cluster); 562 | self 563 | } 564 | 565 | /// Add the string slice `str_slice` to the `Buffer`'s array of codepoints. 566 | /// 567 | /// When shaping part of a larger text (e.g. a run of text from a paragraph) 568 | /// it is preferable to use `add_str_item` instead. 569 | /// 570 | /// # Examples 571 | /// 572 | /// ``` 573 | /// use harfbuzz_rs::UnicodeBuffer; 574 | /// 575 | /// let buffer = UnicodeBuffer::new().add_str("Hello"); 576 | /// let buffer = buffer.add_str(" World"); 577 | /// assert_eq!(buffer.string_lossy(), "Hello World"); 578 | /// ``` 579 | /// 580 | pub fn add_str(mut self, str_slice: &str) -> UnicodeBuffer { 581 | self.0.add_str_item(str_slice, 0, str_slice.len()); 582 | self 583 | } 584 | 585 | /// Add a string item to the buffer, providing context. 586 | /// 587 | /// Only the `item` string gets added to the buffer and will be shaped. 588 | /// `context` provides extra information to the shaper, allowing, for 589 | /// example, to do cross-run Arabic shaping or properly handle combining 590 | /// marks at the start of a run. 591 | /// 592 | /// When shaping part of a larger text (e.g. a run of text from a paragraph) 593 | /// you should pass the whole paragraph to this function as `context` 594 | /// whereas `item` refers only to the part of the string to be shaped. 595 | /// 596 | /// # Panics 597 | /// 598 | /// Panics if `item` is not a substring of `context`. Note that `item` must 599 | /// reside in the same allocation as `context`! 600 | /// 601 | /// # Examples 602 | /// 603 | /// We only want to shape the string `World` as part of the sentence `Hello 604 | /// World!`. 605 | /// 606 | /// ``` 607 | /// use harfbuzz_rs::UnicodeBuffer; 608 | /// 609 | /// let string = "Hello World!"; 610 | /// 611 | /// // the range 6..11 corresponds to `World` 612 | /// assert_eq!(&string[6..11], "World"); 613 | /// 614 | /// let buffer = UnicodeBuffer::new().add_str_item(string, &string[6..11]); 615 | /// assert_eq!(buffer.string_lossy(), "World"); 616 | /// ``` 617 | pub fn add_str_item(mut self, context: &str, item: &str) -> UnicodeBuffer { 618 | const PANIC_MSG: &str = "`item` must be a substring of `context`"; 619 | let offset = 620 | usize::checked_sub(item.as_ptr() as _, context.as_ptr() as _).expect(PANIC_MSG); 621 | assert!(offset + item.len() <= context.len(), "{}", PANIC_MSG); 622 | self.0.add_str_item(context, offset, item.len()); 623 | self 624 | } 625 | 626 | /// Append codepoints from another `UnicodeBuffer` to the end of `self`. 627 | /// 628 | /// # Examples 629 | /// 630 | /// ``` 631 | /// use harfbuzz_rs::UnicodeBuffer; 632 | /// 633 | /// let buffer = UnicodeBuffer::new().add_str("Hello"); 634 | /// let other = UnicodeBuffer::new().add_str(" World!"); 635 | /// let buffer = buffer.append(&other); 636 | /// assert_eq!(buffer.string_lossy(), "Hello World!"); 637 | /// ``` 638 | /// 639 | pub fn append(mut self, other: &UnicodeBuffer) -> UnicodeBuffer { 640 | self.0.append(&other.0, 0, c_uint::MAX); 641 | self 642 | } 643 | 644 | /// Append a range of codepoints from another `UnicodeBuffer` to the end of 645 | /// `self`. 646 | /// 647 | /// # Examples 648 | /// 649 | /// ``` 650 | /// use harfbuzz_rs::UnicodeBuffer; 651 | /// 652 | /// let buffer = UnicodeBuffer::new().add_str("Hello"); 653 | /// let other = UnicodeBuffer::new().add_str(" World!"); 654 | /// let buffer = buffer.append_range(&other, 0..=3); 655 | /// assert_eq!(buffer.string_lossy(), "Hello Wor"); 656 | /// let buffer = buffer.append_range(&other, 4..); 657 | /// assert_eq!(buffer.string_lossy(), "Hello World!"); 658 | /// ``` 659 | /// 660 | pub fn append_range( 661 | mut self, 662 | other: &UnicodeBuffer, 663 | range: impl std::ops::RangeBounds, 664 | ) -> UnicodeBuffer { 665 | let (start, end) = crate::start_end_range(range); 666 | self.0.append(&other.0, start, end); 667 | self 668 | } 669 | 670 | /// Returns an Iterator over the stored unicode codepoints. 671 | /// 672 | /// # Examples 673 | /// ``` 674 | /// use harfbuzz_rs::UnicodeBuffer; 675 | /// 676 | /// let buffer = UnicodeBuffer::new().add_str("ab"); 677 | /// let mut iterator = buffer.codepoints(); 678 | /// 679 | /// assert_eq!('a' as u32, iterator.next().unwrap()); 680 | /// assert_eq!('b' as u32, iterator.next().unwrap()); 681 | /// assert!(iterator.next().is_none()); 682 | /// ``` 683 | pub fn codepoints(&self) -> Codepoints<'_> { 684 | Codepoints { 685 | slice_iter: self.0.get_glyph_infos().iter(), 686 | } 687 | } 688 | 689 | /// Get the stored codepoints as a `String`. 690 | /// 691 | /// Invalid codepoints get replaced by the U+FFFD replacement character. 692 | pub fn string_lossy(&self) -> String { 693 | self.codepoints() 694 | .map(|cp| std::char::from_u32(cp).unwrap_or('\u{FFFD}')) 695 | .collect() 696 | } 697 | 698 | /// Set the text direction of the `Buffer`'s contents. 699 | pub fn set_direction(mut self, direction: Direction) -> UnicodeBuffer { 700 | self.0.set_direction(direction); 701 | self 702 | } 703 | 704 | /// Returns the `Buffer`'s text direction. 705 | pub fn get_direction(&self) -> Direction { 706 | self.0.get_direction() 707 | } 708 | 709 | /// Set the script from an ISO15924 tag. 710 | pub fn set_script(mut self, script: Tag) -> UnicodeBuffer { 711 | self.0 712 | .set_script(unsafe { hb_script_from_iso15924_tag(script.0) }); 713 | self 714 | } 715 | 716 | /// Get the ISO15924 script tag. 717 | pub fn get_script(&self) -> Tag { 718 | Tag(unsafe { hb_script_to_iso15924_tag(self.0.get_script()) }) 719 | } 720 | 721 | /// Set the buffer language. 722 | pub fn set_language(mut self, lang: Language) -> UnicodeBuffer { 723 | self.0.set_language(lang); 724 | self 725 | } 726 | 727 | /// Get the buffer language. 728 | pub fn get_language(&self) -> Option { 729 | self.0.get_language() 730 | } 731 | 732 | /// Guess the segment properties (direction, language, script) for the 733 | /// current buffer. 734 | pub fn guess_segment_properties(mut self) -> UnicodeBuffer { 735 | self.0.guess_segment_properties(); 736 | self 737 | } 738 | 739 | /// Get the segment properties (direction, language, script) of the current 740 | /// buffer. 741 | pub fn get_segment_properties(&self) -> SegmentProperties { 742 | self.0.get_segment_properties() 743 | } 744 | 745 | /// Set the cluster level of the buffer. 746 | pub fn set_cluster_level(mut self, cluster_level: ClusterLevel) -> UnicodeBuffer { 747 | self.0.set_cluster_level(cluster_level); 748 | self 749 | } 750 | 751 | /// Retrieve the cluster level of the buffer. 752 | pub fn get_cluster_level(&self) -> ClusterLevel { 753 | self.0.get_cluster_level() 754 | } 755 | 756 | /// Pre-allocate the buffer to hold a string at least `size` codepoints. 757 | pub fn pre_allocate(&mut self, size: usize) { 758 | self.0.pre_allocate(size) 759 | } 760 | 761 | /// Clear the contents of the buffer (i.e. the stored string of unicode 762 | /// characters). 763 | /// 764 | /// # Examples 765 | /// ``` 766 | /// use harfbuzz_rs::UnicodeBuffer; 767 | /// 768 | /// let buffer = UnicodeBuffer::new(); 769 | /// let buffer = buffer.add_str("Test!"); 770 | /// assert_eq!(buffer.len(), 5); 771 | /// let buffer = buffer.clear_contents(); 772 | /// assert!(buffer.is_empty()); 773 | /// ``` 774 | pub fn clear_contents(mut self) -> UnicodeBuffer { 775 | self.0.clear_contents(); 776 | self 777 | } 778 | } 779 | 780 | impl std::fmt::Debug for UnicodeBuffer { 781 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 782 | fmt.debug_struct("UnicodeBuffer") 783 | .field("content", &self.string_lossy()) 784 | .field("direction", &self.get_direction()) 785 | .field("language", &self.get_language()) 786 | .field("script", &self.get_script()) 787 | .field("cluster_level", &self.get_cluster_level()) 788 | .finish() 789 | } 790 | } 791 | 792 | impl std::default::Default for UnicodeBuffer { 793 | fn default() -> UnicodeBuffer { 794 | UnicodeBuffer::new() 795 | } 796 | } 797 | 798 | /// An iterator over the codepoints stored in a `UnicodeBuffer`. 799 | /// 800 | /// You get an iterator of this type from the `.codepoints()` method on 801 | /// `UnicodeBuffer`. It yields `u32`s that should be interpreted as unicode 802 | /// codepoints stored in the underlying buffer. 803 | #[derive(Debug, Clone)] 804 | pub struct Codepoints<'a> { 805 | slice_iter: std::slice::Iter<'a, GlyphInfo>, 806 | } 807 | 808 | impl<'a> Iterator for Codepoints<'a> { 809 | type Item = u32; 810 | 811 | fn next(&mut self) -> Option { 812 | self.slice_iter.next().map(|info| info.codepoint) 813 | } 814 | } 815 | 816 | /// A `GlyphBuffer` contains the resulting output information of the shaping 817 | /// process. 818 | /// 819 | /// An object of this type is obtained through the `shape` function. 820 | pub struct GlyphBuffer(pub(crate) Owned); 821 | 822 | impl GlyphBuffer { 823 | /// Returns the length of the data of the buffer. 824 | /// 825 | /// When called before shaping this is the number of unicode codepoints 826 | /// contained in the buffer. When called after shaping it returns the number 827 | /// of glyphs stored. 828 | pub fn len(&self) -> usize { 829 | self.0.len() 830 | } 831 | 832 | /// Converts this buffer to a raw harfbuzz object pointer. 833 | pub fn into_raw(self) -> *mut hb_buffer_t { 834 | Owned::into_raw(self.0) 835 | } 836 | 837 | /// Returns `true` if the buffer contains no elements. 838 | pub fn is_empty(&self) -> bool { 839 | self.0.is_empty() 840 | } 841 | 842 | /// Get the glyph positions. 843 | pub fn get_glyph_positions(&self) -> &[GlyphPosition] { 844 | self.0.get_glyph_positions() 845 | } 846 | 847 | /// Get the glyph infos. 848 | pub fn get_glyph_infos(&self) -> &[GlyphInfo] { 849 | self.0.get_glyph_infos() 850 | } 851 | 852 | /// Reverse the `Buffer`'s contents. 853 | pub fn reverse(&mut self) { 854 | self.0.reverse() 855 | } 856 | 857 | /// Reverse the `Buffer`'s contents in the range from `start` to `end`. 858 | pub fn reverse_range(&mut self, start: usize, end: usize) { 859 | self.0.reverse_range(start, end) 860 | } 861 | 862 | /// Clears the contents of the glyph buffer and returns an empty 863 | /// `UnicodeBuffer` reusing the existing allocation. 864 | pub fn clear(mut self) -> UnicodeBuffer { 865 | self.0.clear_contents(); 866 | UnicodeBuffer::from_generic(self.0) 867 | } 868 | 869 | /// Returns a serializer that allows the contents of the buffer to be 870 | /// converted into a human or machine readable representation. 871 | /// 872 | /// # Arguments 873 | /// - `font`: Optionally a font can be provided for access to glyph names 874 | /// and glyph extents. If `None` is passed an empty font is assumed. 875 | /// - `format`: The serialization format to use. 876 | /// - `flags`: Allows you to control which information will be contained in 877 | /// the serialized output. 878 | /// 879 | /// # Examples 880 | /// 881 | /// Serialize the glyph buffer contents to a string using the textual format 882 | /// without any special flags. 883 | /// ``` 884 | /// use harfbuzz_rs::*; 885 | /// use std::io::Read; 886 | /// # use std::path::PathBuf; 887 | /// # let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 888 | /// # path.push("testfiles/SourceSansVariable-Roman.ttf"); 889 | /// let face = Face::from_file(path, 0).expect("Error reading font file."); 890 | /// let font = Font::new(face); 891 | /// 892 | /// let buffer = UnicodeBuffer::new().add_str("ABC"); 893 | /// 894 | /// let buffer = shape(&font, buffer, &[]); 895 | /// 896 | /// let mut string = String::new(); 897 | /// buffer 898 | /// .serializer( 899 | /// Some(&font), 900 | /// SerializeFormat::Text, 901 | /// SerializeFlags::default(), 902 | /// ).read_to_string(&mut string) 903 | /// .unwrap(); 904 | /// 905 | /// assert!(!string.is_empty()) // `string` contains the serialized values 906 | /// ``` 907 | pub fn serializer<'a>( 908 | &'a self, 909 | font: Option<&'a crate::Font<'a>>, 910 | format: SerializeFormat, 911 | flags: SerializeFlags, 912 | ) -> BufferSerializer<'a> { 913 | BufferSerializer { 914 | font, 915 | buffer: &self.0, 916 | start: 0, 917 | end: self.len(), 918 | format, 919 | flags, 920 | bytes: io::Cursor::new(Vec::with_capacity(128)), 921 | } 922 | } 923 | } 924 | 925 | impl fmt::Debug for GlyphBuffer { 926 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 927 | fmt.debug_struct("GlyphBuffer") 928 | .field("glyph_positions", &self.get_glyph_positions()) 929 | .field("glyph_infos", &self.get_glyph_infos()) 930 | .finish() 931 | } 932 | } 933 | 934 | impl fmt::Display for GlyphBuffer { 935 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 936 | let mut serializer = 937 | self.serializer(None, SerializeFormat::Text, SerializeFlags::default()); 938 | let mut string = String::new(); 939 | serializer.read_to_string(&mut string).unwrap(); 940 | write!(fmt, "{}", string)?; 941 | Ok(()) 942 | } 943 | } 944 | 945 | #[cfg(test)] 946 | mod tests { 947 | use super::*; 948 | use crate::bindings::hb_glyph_position_t; 949 | use crate::tests::assert_memory_layout_equal; 950 | use crate::{shape, Face, Font}; 951 | 952 | #[test] 953 | fn test_memory_layouts() { 954 | assert_memory_layout_equal::(); 955 | assert_memory_layout_equal::(); 956 | } 957 | 958 | #[test] 959 | fn test_str_item_heap() { 960 | let string = String::from("Test String for test"); 961 | UnicodeBuffer::new().add_str_item(&string, &string[5..10]); 962 | } 963 | 964 | #[test] 965 | #[should_panic(expected = "must be a substring of")] 966 | fn test_str_item_different_allocations() { 967 | UnicodeBuffer::new().add_str_item("Test", "String"); 968 | } 969 | 970 | #[test] 971 | #[should_panic(expected = "must be a substring of")] 972 | fn test_str_item_not_substring() { 973 | let string = "Test String"; 974 | UnicodeBuffer::new().add_str_item(&string[0..5], &string[4..6]); 975 | } 976 | 977 | #[test] 978 | #[should_panic(expected = "must be a substring of")] 979 | fn test_str_item_not_substring2() { 980 | let string = "Test String"; 981 | UnicodeBuffer::new().add_str_item(&string[4..], &string[0..5]); 982 | } 983 | 984 | #[test] 985 | fn test_glyph_buffer_serialization_single_char() { 986 | let path = "testfiles/SourceSansVariable-Roman.ttf"; 987 | let face = Face::from_file(path, 0).unwrap(); 988 | let font = Font::new(face); 989 | let buffer = UnicodeBuffer::new().add_str("A"); 990 | let glyph_buffer = shape(&font, buffer, &[]); 991 | 992 | // serializes only glyph indices 993 | let mut serializer = glyph_buffer.serializer( 994 | Some(&font), 995 | SerializeFormat::Text, 996 | SerializeFlags::NO_ADVANCES 997 | | SerializeFlags::NO_CLUSTERS 998 | | SerializeFlags::NO_POSITIONS 999 | | SerializeFlags::NO_GLYPH_NAMES, 1000 | ); 1001 | let mut string = String::new(); 1002 | serializer.read_to_string(&mut string).unwrap(); 1003 | let string = string.replace(|c: char| !c.is_ascii_digit(), ""); 1004 | assert_eq!( 1005 | string.parse::().unwrap(), 1006 | glyph_buffer.get_glyph_infos()[0].codepoint 1007 | ); 1008 | } 1009 | 1010 | #[test] 1011 | fn test_glyph_buffer_serialization_text() { 1012 | let path = "testfiles/SourceSansVariable-Roman.ttf"; 1013 | let face = Face::from_file(path, 0).unwrap(); 1014 | let font = Font::new(face); 1015 | let buffer = UnicodeBuffer::new().add_str("Hello 🌍"); 1016 | let glyph_buffer = shape(&font, buffer, &[]); 1017 | 1018 | // serializes only glyph indices 1019 | let mut serializer = glyph_buffer.serializer( 1020 | Some(&font), 1021 | SerializeFormat::Text, 1022 | SerializeFlags::NO_ADVANCES 1023 | | SerializeFlags::NO_CLUSTERS 1024 | | SerializeFlags::NO_POSITIONS 1025 | | SerializeFlags::NO_GLYPH_NAMES, 1026 | ); 1027 | let mut string = String::new(); 1028 | serializer.read_to_string(&mut string).unwrap(); 1029 | for (serialized_glyph, glyph_info) in string 1030 | .split_terminator::<&[char]>(&['|', '[', ']']) 1031 | .filter(|c| !c.is_empty()) 1032 | .zip(glyph_buffer.get_glyph_infos()) 1033 | { 1034 | assert_eq!( 1035 | serialized_glyph.parse::().unwrap(), 1036 | glyph_info.codepoint 1037 | ); 1038 | } 1039 | } 1040 | } 1041 | -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Borrow; 2 | use std::ops::{Deref, DerefMut}; 3 | 4 | /// A type to represent 4-byte SFNT tags. 5 | /// 6 | /// The easiest way to create a tag is by using its `From<&[u8; 4]>` impl: 7 | /// 8 | /// ``` 9 | /// # use harfbuzz_rs::Tag; 10 | /// let tag: Tag = b"abcd".into(); 11 | /// assert_eq!(&tag.to_bytes(), b"abcd"); 12 | /// ``` 13 | /// 14 | /// Tables, features, etc. in OpenType and many other font formats use SFNT tags 15 | /// as identifiers. These are 4-bytes long and usually each byte represents an 16 | /// ASCII value. `Tag` provides methods to create such identifiers from 17 | /// individual `chars` or a `str` slice and to get the string representation of 18 | /// a `Tag`. 19 | #[derive(Copy, Clone, Hash, PartialEq, Eq)] 20 | #[repr(transparent)] 21 | pub struct Tag(pub hb_tag_t); 22 | 23 | impl Tag { 24 | /// Create a `Tag` from its four-char textual representation. 25 | /// 26 | /// All the arguments must be ASCII values. 27 | /// 28 | /// # Examples 29 | /// 30 | /// ``` 31 | /// use harfbuzz_rs::Tag; 32 | /// let cmap_tag = Tag::new('c', 'm', 'a', 'p'); 33 | /// assert_eq!(cmap_tag.to_string(), "cmap") 34 | /// ``` 35 | /// 36 | pub const fn new(a: char, b: char, c: char, d: char) -> Self { 37 | Tag(((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32)) 38 | } 39 | 40 | fn tag_to_string(self) -> String { 41 | let mut buf: [u8; 4] = [0; 4]; 42 | unsafe { hb_tag_to_string(self.0, buf.as_mut_ptr() as *mut _) }; 43 | String::from_utf8_lossy(&buf).into() 44 | } 45 | 46 | /// Returns tag as 4-element byte array. 47 | /// 48 | /// # Examples 49 | /// ``` 50 | /// # use harfbuzz_rs::Tag; 51 | /// let tag = Tag::new('a', 'b', 'c', 'd'); 52 | /// assert_eq!(&tag.to_bytes(), b"abcd"); 53 | /// ``` 54 | pub const fn to_bytes(self) -> [u8; 4] { 55 | #[allow(clippy::identity_op)] 56 | [ 57 | (self.0 >> 24 & 0xff) as u8, 58 | (self.0 >> 16 & 0xff) as u8, 59 | (self.0 >> 8 & 0xff) as u8, 60 | (self.0 >> 0 & 0xff) as u8, 61 | ] 62 | } 63 | } 64 | 65 | use std::fmt; 66 | use std::fmt::{Debug, Display, Formatter}; 67 | impl Debug for Tag { 68 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 69 | let string = self.tag_to_string(); 70 | let mut chars = string.chars().chain(std::iter::repeat('\u{FFFD}')); 71 | write!( 72 | f, 73 | "Tag({:?}, {:?}, {:?}, {:?})", 74 | chars.next().unwrap(), 75 | chars.next().unwrap(), 76 | chars.next().unwrap(), 77 | chars.next().unwrap() 78 | ) 79 | } 80 | } 81 | 82 | impl<'a> From<&'a [u8; 4]> for Tag { 83 | fn from(byte_array: &'a [u8; 4]) -> Tag { 84 | Tag::new( 85 | byte_array[0] as char, 86 | byte_array[1] as char, 87 | byte_array[2] as char, 88 | byte_array[3] as char, 89 | ) 90 | } 91 | } 92 | 93 | impl From for [u8; 4] { 94 | fn from(tag: Tag) -> [u8; 4] { 95 | tag.to_bytes() 96 | } 97 | } 98 | 99 | impl Display for Tag { 100 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 101 | write!(f, "{}", self.tag_to_string()) 102 | } 103 | } 104 | 105 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 106 | /// An Error generated when a `Tag` fails to parse from a `&str` with the 107 | /// `from_str` function. 108 | pub enum TagFromStrErr { 109 | /// The string contains non-ASCII characters. 110 | NonAscii, 111 | /// The string has length zero. 112 | ZeroLengthString, 113 | } 114 | 115 | use std::str::FromStr; 116 | 117 | impl FromStr for Tag { 118 | type Err = TagFromStrErr; 119 | /// Parses a `Tag` from a `&str` that contains four or less ASCII 120 | /// characters. When the string's length is smaller than 4 it is extended 121 | /// with `' '` (Space) characters. The remaining bytes of strings longer 122 | /// than 4 bytes are ignored. 123 | /// 124 | /// # Examples 125 | /// 126 | /// ``` 127 | /// use harfbuzz_rs::Tag; 128 | /// use std::str::FromStr; 129 | /// let tag1 = Tag::from_str("ABCD").unwrap(); 130 | /// let tag2 = Tag::new('A', 'B', 'C', 'D'); 131 | /// assert_eq!(tag1, tag2); 132 | /// ``` 133 | /// 134 | fn from_str(s: &str) -> Result { 135 | if !s.is_ascii() { 136 | return Err(TagFromStrErr::NonAscii); 137 | } 138 | if s.is_empty() { 139 | return Err(TagFromStrErr::ZeroLengthString); 140 | } 141 | let len = std::cmp::max(s.len(), 4) as i32; 142 | unsafe { Ok(Tag(hb_tag_from_string(s.as_ptr() as *mut _, len))) } 143 | } 144 | } 145 | 146 | /// Defines the direction in which text is to be read. 147 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 148 | pub enum Direction { 149 | /// Initial, unset direction. 150 | Invalid, 151 | /// Text is set horizontally from left to right. 152 | Ltr, 153 | /// Text is set horizontally from right to left. 154 | Rtl, 155 | /// Text is set vertically from top to bottom. 156 | Ttb, 157 | /// Text is set vertically from bottom to top. 158 | Btt, 159 | } 160 | 161 | impl Direction { 162 | /// Convert into raw value of type `hb_direction_t`. 163 | pub fn to_raw(self) -> hb_direction_t { 164 | match self { 165 | Direction::Invalid => HB_DIRECTION_INVALID, 166 | Direction::Ltr => HB_DIRECTION_LTR, 167 | Direction::Rtl => HB_DIRECTION_RTL, 168 | Direction::Ttb => HB_DIRECTION_TTB, 169 | Direction::Btt => HB_DIRECTION_BTT, 170 | } 171 | } 172 | 173 | /// Create from raw value of type `hb_direction_t`. 174 | pub fn from_raw(dir: hb_direction_t) -> Self { 175 | match dir { 176 | HB_DIRECTION_LTR => Direction::Ltr, 177 | HB_DIRECTION_RTL => Direction::Rtl, 178 | HB_DIRECTION_TTB => Direction::Ttb, 179 | HB_DIRECTION_BTT => Direction::Btt, 180 | _ => Direction::Invalid, 181 | } 182 | } 183 | } 184 | 185 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] 186 | pub struct Language(pub hb_language_t); 187 | 188 | impl Default for Language { 189 | fn default() -> Language { 190 | Language(unsafe { hb_language_get_default() }) 191 | } 192 | } 193 | 194 | impl Debug for Language { 195 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 196 | write!(f, "Language(\"{}\")", self) 197 | } 198 | } 199 | 200 | use std::ffi::CStr; 201 | 202 | use crate::bindings::{ 203 | hb_direction_t, hb_language_from_string, hb_language_get_default, hb_language_t, 204 | hb_language_to_string, hb_script_from_iso15924_tag, hb_script_get_horizontal_direction, 205 | hb_script_t, hb_script_to_iso15924_tag, hb_tag_from_string, hb_tag_t, hb_tag_to_string, 206 | HB_DIRECTION_BTT, HB_DIRECTION_INVALID, HB_DIRECTION_LTR, HB_DIRECTION_RTL, HB_DIRECTION_TTB, 207 | }; 208 | impl Display for Language { 209 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 210 | let string = unsafe { 211 | let char_ptr = hb_language_to_string(self.0); 212 | if char_ptr.is_null() { 213 | return Err(fmt::Error); 214 | } 215 | CStr::from_ptr(char_ptr) 216 | .to_str() 217 | .expect("String representation of language is not valid utf8.") 218 | }; 219 | write!(f, "{}", string) 220 | } 221 | } 222 | 223 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 224 | pub struct InvalidLanguage; 225 | 226 | impl FromStr for Language { 227 | type Err = InvalidLanguage; 228 | fn from_str(s: &str) -> Result { 229 | let len = std::cmp::min(s.len(), i32::MAX as _) as i32; 230 | let lang = unsafe { hb_language_from_string(s.as_ptr() as *mut _, len) }; 231 | if lang.is_null() { 232 | Err(InvalidLanguage {}) 233 | } else { 234 | Ok(Language(lang)) 235 | } 236 | } 237 | } 238 | 239 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 240 | pub struct Script(pub hb_script_t); 241 | 242 | impl Script { 243 | pub fn from_iso15924_tag(tag: Tag) -> Self { 244 | Script(unsafe { hb_script_from_iso15924_tag(tag.0) }) 245 | } 246 | 247 | pub fn to_iso15924_tag(self) -> Tag { 248 | Tag(unsafe { hb_script_to_iso15924_tag(self.0) }) 249 | } 250 | 251 | pub fn horizontal_direction(self) -> Direction { 252 | Direction::from_raw(unsafe { hb_script_get_horizontal_direction(self.0) }) 253 | } 254 | } 255 | 256 | /// A trait which is implemented for all harffbuzz wrapper structs. It exposes 257 | /// common functionality for converting from and to the underlying raw harfbuzz 258 | /// pointers that are useful for ffi. 259 | /// 260 | /// # Safety 261 | /// 262 | /// This trait may only be implemented for structs that are zero-sized and is 263 | /// therefore unsafe to implement. 264 | pub unsafe trait HarfbuzzObject: Sized { 265 | /// Type of the raw harfbuzz object. 266 | type Raw; 267 | 268 | /// Creates a reference from a harfbuzz object pointer. 269 | /// 270 | /// Unsafe because a raw pointer may be accessed. The reference count is not 271 | /// changed. Should not be called directly by a library user. 272 | /// 273 | /// Use the Owned and Shared abstractions instead. 274 | #[doc(hidden)] 275 | unsafe fn from_raw(val: *const Self::Raw) -> Self; 276 | 277 | /// Returns the underlying harfbuzz object pointer. 278 | /// 279 | /// The caller must ensure, that this pointer is not used after `self`'s 280 | /// destruction. 281 | fn as_raw(&self) -> *mut Self::Raw; 282 | 283 | /// Returns the underlying harfbuzz object pointer with the intention of 284 | /// mutating the object. 285 | /// 286 | /// The caller must ensure, that this pointer is not used after `self`'s 287 | /// destruction. 288 | fn as_raw_mut(&mut self) -> *mut Self::Raw { 289 | self.as_raw() 290 | } 291 | 292 | /// Increases the reference count of the HarfBuzz object. 293 | /// 294 | /// Wraps a `hb_TYPE_reference()` call. 295 | /// 296 | /// # Safety 297 | /// 298 | /// While no undefined behavior can be introduced only by increasing the 299 | /// reference count (I think) this method is still marked unsafe since there 300 | /// should be no need for it to be called from safe code. 301 | unsafe fn reference(&self); 302 | 303 | /// Decreases the reference count of the HarfBuzz object and destroys it if 304 | /// the reference count reaches zero. 305 | /// 306 | /// Wraps a `hb_TYPE_destroy()` call. 307 | /// 308 | /// # Safety 309 | /// 310 | /// You always have to call `reference` first before using this method. 311 | /// Otherwise you might accidentally hold on to already destroyed objects 312 | /// and causing UB. 313 | unsafe fn dereference(&self); 314 | } 315 | 316 | /// A smart pointer that wraps an atomically reference counted HarfBuzz object. 317 | /// 318 | /// Usually you don't create a `Shared` yourself, but get it from another 319 | /// function in this crate. You can just use the methods of the wrapped object 320 | /// through its `Deref` implementation. 321 | /// 322 | /// A `Shared` is a safe wrapper for reference counted HarfBuzz objects and 323 | /// provides shared immutable access to its inner object. As HarfBuzz' objects 324 | /// are all thread-safe `Shared` implements `Send` and `Sync`. 325 | /// 326 | /// Tries to mirror the stdlib `Arc` interface where applicable as HarfBuzz' 327 | /// reference counting has similar semantics. 328 | #[derive(Debug, PartialEq, Eq)] 329 | pub struct Shared { 330 | object: T, 331 | } 332 | 333 | impl Shared { 334 | /// Creates a `Shared` from an owned raw harfbuzz pointer. 335 | /// 336 | /// # Safety 337 | /// 338 | /// Transfers ownership. _Use of the original pointer is now forbidden!_ 339 | /// Unsafe because dereferencing a raw pointer is necessary. 340 | pub unsafe fn from_raw_owned(raw: *mut T::Raw) -> Self { 341 | let object = T::from_raw(raw); 342 | Shared { object } 343 | } 344 | 345 | /// Converts `self` into the underlying harfbuzz object pointer value. The 346 | /// resulting pointer has to be manually destroyed using `hb_TYPE_destroy` 347 | /// or be converted back into the wrapper using the `from_raw` function to 348 | /// avoid leaking memory. 349 | pub fn into_raw(shared: Shared) -> *mut T::Raw { 350 | let result = shared.object.as_raw(); 351 | std::mem::forget(shared); 352 | result 353 | } 354 | 355 | /// Creates a `Shared` by cloning a raw harfbuzz pointer. 356 | 357 | /// 358 | /// The original pointer can still be safely used but must be released at 359 | /// the end to avoid memory leaks. 360 | /// 361 | /// # Safety 362 | /// 363 | /// `raw` must be a valid pointer to the corresponding harfbuzz object or 364 | /// the behavior is undefined. 365 | /// 366 | /// Internally this method increases the reference count of `raw` so it is 367 | /// safe to call `hb_destroy_[...]` on `raw` after using `from_raw_ref`. 368 | pub unsafe fn from_raw_ref(raw: *mut T::Raw) -> Self { 369 | let object = T::from_raw(raw); 370 | object.reference(); 371 | Shared { object } 372 | } 373 | } 374 | 375 | impl Clone for Shared { 376 | /// Returns a copy and increases the reference count. 377 | /// 378 | /// This behaviour is exactly like `Arc::clone` in the standard library. 379 | fn clone(&self) -> Self { 380 | unsafe { Self::from_raw_ref(self.object.as_raw()) } 381 | } 382 | } 383 | 384 | impl Deref for Shared { 385 | type Target = T; 386 | 387 | fn deref(&self) -> &T { 388 | &self.object 389 | } 390 | } 391 | 392 | impl Borrow for Shared { 393 | fn borrow(&self) -> &T { 394 | self 395 | } 396 | } 397 | 398 | impl From> for Shared { 399 | fn from(t: Owned) -> Self { 400 | let ptr = t.object.as_raw(); 401 | std::mem::forget(t); 402 | unsafe { Shared::from_raw_owned(ptr) } 403 | } 404 | } 405 | 406 | impl Drop for Shared { 407 | fn drop(&mut self) { 408 | unsafe { self.dereference() } 409 | } 410 | } 411 | 412 | unsafe impl Send for Shared {} 413 | unsafe impl Sync for Shared {} 414 | 415 | /// A smart pointer that wraps a singly owned harfbuzz object. 416 | /// 417 | /// A `Owned` is used to wrap freshly created owned HarfBuzz objects. It permits 418 | /// mutable, non-shared access to the enclosed HarfBuzz value so it can be used 419 | /// e.g. to set up a `Font` or `Face` after its creation. 420 | /// 421 | /// There is no safe way to construct an `Owned` pointer and usually you don't 422 | /// need to create a `Owned` yourself, but get it from another function in this 423 | /// crate. You can just use the methods of the wrapped object through its 424 | /// `Deref` implementation. 425 | /// 426 | /// Interaction with `Shared` 427 | /// ------------------------- 428 | /// When you are finished mutating the inner value, you usually want to pass it 429 | /// to other HarfBuzz functions that expect shared access. Thus you need to 430 | /// convert the `Owned` to a `Shared` pointer using `.into()`. Note however that 431 | /// once a value is converted to a `Shared`, it will not possible to mutate 432 | /// it anymore. 433 | #[derive(Debug, PartialEq, Eq)] 434 | pub struct Owned { 435 | object: T, 436 | } 437 | 438 | impl Owned { 439 | /// Creates a `Owned` safely wrapping a raw harfbuzz pointer. 440 | /// 441 | /// # Safety 442 | /// 443 | /// This fully transfers ownership. _Use of the original pointer is now 444 | /// forbidden!_ Unsafe because a dereference of a raw pointer is necessary. 445 | /// 446 | /// Use this only to wrap freshly created HarfBuzz object that is not 447 | /// shared! Otherwise it is possible to have aliasing mutable references 448 | /// from safe code 449 | pub unsafe fn from_raw(raw: *mut T::Raw) -> Self { 450 | Owned { 451 | object: T::from_raw(raw), 452 | } 453 | } 454 | 455 | /// Converts `self` into the underlying harfbuzz object pointer value. The 456 | /// resulting pointer has to be manually destroyed using `hb_TYPE_destroy` 457 | /// or be converted back into the wrapper using the `from_raw` function to 458 | /// avoid leaking memory. 459 | pub fn into_raw(owned: Owned) -> *mut T::Raw { 460 | let result = owned.object.as_raw(); 461 | std::mem::forget(owned); 462 | result 463 | } 464 | 465 | /// Demotes an `Owned` pointer to a `Shared` pointer. 466 | /// 467 | /// Use this method when you don't need exclusive (mutable) access to the 468 | /// object anymore. For differences between `Owned` and `Shared` pointers 469 | /// see the documentation on the respective structs. 470 | /// 471 | /// Note that `Shared` also implements `From>` which allows 472 | /// implicit conversions in many functions. 473 | #[allow(clippy::wrong_self_convention)] //< backward compatibility is more important than clippy 474 | pub fn to_shared(self) -> Shared { 475 | self.into() 476 | } 477 | } 478 | 479 | impl Drop for Owned { 480 | fn drop(&mut self) { 481 | unsafe { self.dereference() } 482 | } 483 | } 484 | 485 | impl Deref for Owned { 486 | type Target = T; 487 | 488 | fn deref(&self) -> &T { 489 | &self.object 490 | } 491 | } 492 | 493 | impl DerefMut for Owned { 494 | fn deref_mut(&mut self) -> &mut T { 495 | &mut self.object 496 | } 497 | } 498 | 499 | #[cfg(test)] 500 | mod tests { 501 | use super::*; 502 | use std::cell::Cell; 503 | use std::mem; 504 | use std::rc::Rc; 505 | use std::str::FromStr; 506 | 507 | #[test] 508 | fn test_tag_debugging() { 509 | let tag = Tag::from_str("ABCD").unwrap(); 510 | assert_eq!("ABCD", format!("{}", tag)); 511 | assert_eq!("Tag('A', 'B', 'C', 'D')", format!("{:?}", tag)); 512 | } 513 | 514 | #[test] 515 | fn test_tag_creation() { 516 | assert!(Tag::from_str("∞BCD").is_err()); 517 | assert!(Tag::from_str("").is_err()); 518 | assert_eq!(Tag::from_str("ABCDE"), Tag::from_str("ABCD")); 519 | assert_eq!(Tag::from_str("abWd").unwrap(), Tag::new('a', 'b', 'W', 'd')); 520 | } 521 | 522 | #[test] 523 | fn test_language() { 524 | assert_eq!(Language::default().to_string(), "c"); 525 | assert_eq!(Language::from_str("ger").unwrap().to_string(), "ger"); 526 | assert_eq!(Language::from_str("ge!").unwrap().to_string(), "ge"); 527 | assert_eq!(Language::from_str("German").unwrap().to_string(), "german"); 528 | } 529 | 530 | // this is a mock struct for testing HarfbuzzObject's behaviour. 531 | #[derive(Debug, Clone)] 532 | struct ReferenceCounter { 533 | share_count: Rc>, 534 | } 535 | 536 | unsafe impl HarfbuzzObject for ReferenceCounter { 537 | type Raw = Cell; 538 | 539 | unsafe fn from_raw(raw: *const Cell) -> Self { 540 | ReferenceCounter { 541 | share_count: Rc::from_raw(raw as *mut _), 542 | } 543 | } 544 | 545 | fn as_raw(&self) -> *mut Cell { 546 | Rc::into_raw(self.share_count.clone()) as *mut _ 547 | } 548 | 549 | unsafe fn reference(&self) { 550 | println!("referencing {:?}", self); 551 | let rc = self.share_count.get(); 552 | self.share_count.set(rc + 1); 553 | } 554 | 555 | unsafe fn dereference(&self) { 556 | println!("dereferencing {:?}", self); 557 | let rc = self.share_count.get(); 558 | self.share_count.set(rc - 1); 559 | } 560 | } 561 | 562 | #[test] 563 | fn reference_counting_shared() { 564 | // Mimic a C-API that returns a pointer to a reference counted value. 565 | let object = ReferenceCounter { 566 | share_count: Rc::new(Cell::new(1)), 567 | }; 568 | 569 | // this clones the underlying `Rc` 570 | let raw = object.as_raw(); 571 | 572 | // so we expect two shared owners 573 | assert_eq!(Rc::strong_count(&object.share_count), 2); 574 | 575 | let shared: Shared = unsafe { Shared::from_raw_owned(raw) }; 576 | 577 | assert_eq!(shared.share_count.get(), 1); 578 | { 579 | // we create another `Shared` pointer... 580 | let shared2 = Shared::clone(&shared); 581 | // which clones 582 | assert_eq!(shared.share_count.get(), 2); 583 | mem::drop(shared2); 584 | } 585 | assert_eq!(shared.share_count.get(), 1); 586 | mem::drop(shared); 587 | 588 | assert_eq!(object.share_count.get(), 0); 589 | 590 | // ensure there are no dangling references 591 | assert_eq!(Rc::strong_count(&object.share_count), 1); 592 | } 593 | } 594 | -------------------------------------------------------------------------------- /src/draw_funcs.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Manuel Reinhardt 2 | // 3 | // This software is released under the MIT License. 4 | // https://opensource.org/licenses/MIT 5 | 6 | //! Contains the `DrawFuncs` trait. 7 | 8 | use crate::bindings::{ 9 | hb_draw_funcs_create, hb_draw_funcs_destroy, hb_draw_funcs_get_empty, hb_draw_funcs_reference, 10 | hb_draw_funcs_set_close_path_func, hb_draw_funcs_set_cubic_to_func, 11 | hb_draw_funcs_set_line_to_func, hb_draw_funcs_set_move_to_func, 12 | hb_draw_funcs_set_quadratic_to_func, hb_draw_funcs_t, hb_draw_state_t, 13 | }; 14 | use crate::common::{HarfbuzzObject, Owned, Shared}; 15 | use crate::font::destroy_box; 16 | 17 | use std::os::raw::c_void; 18 | 19 | use std::{self, fmt, marker::PhantomData, panic, ptr::NonNull}; 20 | 21 | #[derive(Copy, Clone, Debug)] 22 | pub struct DrawState { 23 | pub path_open: bool, 24 | pub path_start_x: f32, 25 | pub path_start_y: f32, 26 | pub current_x: f32, 27 | pub current_y: f32, 28 | } 29 | 30 | /// This Trait specifies the font callbacks that harfbuzz uses when asked 31 | /// to draw a glyph. 32 | #[allow(unused_variables)] 33 | pub trait DrawFuncs { 34 | fn move_to(&mut self, st: &DrawState, to_x: f32, to_y: f32); 35 | fn line_to(&mut self, st: &DrawState, to_x: f32, to_y: f32); 36 | fn quadratic_to( 37 | &mut self, 38 | st: &DrawState, 39 | control_x: f32, 40 | control_y: f32, 41 | to_x: f32, 42 | to_y: f32, 43 | ); 44 | #[allow(clippy::too_many_arguments)] 45 | fn cubic_to( 46 | &mut self, 47 | st: &DrawState, 48 | control1_x: f32, 49 | control1_y: f32, 50 | control2_x: f32, 51 | control2_y: f32, 52 | to_x: f32, 53 | to_y: f32, 54 | ); 55 | fn close_path(&mut self, st: &DrawState); 56 | } 57 | 58 | macro_rules! hb_callback { 59 | ($func_name:ident<$($arg:ident: $datatype:ty),*>{ 60 | $(argument $closure_arg:ty => $expr:expr,)* 61 | }) => { 62 | #[allow(clippy::let_and_return)] 63 | extern "C" fn $func_name( 64 | _dfuncs: *mut hb_draw_funcs_t, 65 | draw_data: *mut ::std::os::raw::c_void, 66 | $( 67 | $arg: $datatype, 68 | )* 69 | closure_data: *mut c_void, 70 | ) where F: Fn(&mut T, $($closure_arg),*) { 71 | let catch_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { 72 | let draw_data = unsafe { &mut *(draw_data as *mut T) }; 73 | let closure = unsafe { &mut *(closure_data as *mut F) }; 74 | closure(draw_data, $($expr),*); 75 | })); 76 | match catch_result { 77 | Ok(val) => val, 78 | Err(_) => { 79 | // TODO: Log error 80 | Default::default() 81 | } 82 | } 83 | } 84 | }; 85 | } 86 | 87 | hb_callback!( 88 | rust_move_to_closure { 89 | argument DrawState => DrawState { 90 | path_open: unsafe { (*st).path_open != 0 }, 91 | path_start_x: unsafe { (*st).path_start_x }, 92 | path_start_y: unsafe { (*st).path_start_y }, 93 | current_x: unsafe { (*st).current_x }, 94 | current_y: unsafe { (*st).current_y } 95 | }, 96 | argument f32 => to_x, 97 | argument f32 => to_y, 98 | } 99 | ); 100 | 101 | hb_callback!( 102 | rust_line_to_closure { 103 | argument DrawState => DrawState { 104 | path_open: unsafe { (*st).path_open != 0 }, 105 | path_start_x: unsafe { (*st).path_start_x }, 106 | path_start_y: unsafe { (*st).path_start_y }, 107 | current_x: unsafe { (*st).current_x }, 108 | current_y: unsafe { (*st).current_y } 109 | }, 110 | argument f32 => to_x, 111 | argument f32 => to_y, 112 | } 113 | ); 114 | 115 | hb_callback!( 116 | rust_quadratic_to_closure { 117 | argument DrawState => DrawState { 118 | path_open: unsafe { (*st).path_open != 0 }, 119 | path_start_x: unsafe { (*st).path_start_x }, 120 | path_start_y: unsafe { (*st).path_start_y }, 121 | current_x: unsafe { (*st).current_x }, 122 | current_y: unsafe { (*st).current_y } 123 | }, 124 | argument f32 => control_x, 125 | argument f32 => control_y, 126 | argument f32 => to_x, 127 | argument f32 => to_y, 128 | } 129 | ); 130 | 131 | hb_callback!( 132 | rust_cubic_to_closure { 133 | argument DrawState => DrawState { 134 | path_open: unsafe { (*st).path_open != 0 }, 135 | path_start_x: unsafe { (*st).path_start_x }, 136 | path_start_y: unsafe { (*st).path_start_y }, 137 | current_x: unsafe { (*st).current_x }, 138 | current_y: unsafe { (*st).current_y } 139 | }, 140 | argument f32 => control1_x, 141 | argument f32 => control1_y, 142 | argument f32 => control2_x, 143 | argument f32 => control2_y, 144 | argument f32 => to_x, 145 | argument f32 => to_y, 146 | } 147 | ); 148 | hb_callback!( 149 | rust_close_path_closure { 150 | argument DrawState => DrawState { 151 | path_open: unsafe { (*st).path_open != 0 }, 152 | path_start_x: unsafe { (*st).path_start_x }, 153 | path_start_y: unsafe { (*st).path_start_y }, 154 | current_x: unsafe { (*st).current_x }, 155 | current_y: unsafe { (*st).current_y } 156 | }, 157 | } 158 | ); 159 | 160 | /// A `DrawFuncsImpl` contains implementations of the font callbacks that 161 | /// harfbuzz uses to draw a glyph. 162 | /// 163 | /// To use this, set the font funcs from a type that implements the `DrawFuncs` 164 | /// trait using the `from_trait_impl` constructor. 165 | /// 166 | /// # Example 167 | /// 168 | /// ```ignore 169 | /// use harfbuzz_rs::*; 170 | /// use harfbuzz_rs::draw_funcs::DrawFuncsImpl; 171 | /// 172 | /// // Dummy struct implementing DrawFuncs 173 | /// struct MyDrawFuncs { 174 | /// points: Vec<(f32,f32)>, 175 | /// } 176 | /// impl DrawFuncs for MyDrawFuncs { 177 | /// fn move_to(&self, _: &DrawState, x: f32, y: f32) { 178 | /// // ... 179 | /// } 180 | /// // implementations of other functions... 181 | /// } 182 | /// 183 | /// let draw_funcs: Owned> = DrawFuncsImpl::from_trait_impl(); 184 | /// ``` 185 | pub(crate) struct DrawFuncsImpl { 186 | raw: NonNull, 187 | marker: PhantomData, 188 | } 189 | 190 | impl DrawFuncsImpl { 191 | /// Returns an empty `DrawFuncsImpl`. Every font callback of the returned 192 | /// `DrawFuncsImpl` gives a null value regardless of its input. 193 | #[allow(unused)] 194 | pub fn empty() -> Shared> { 195 | let raw = unsafe { hb_draw_funcs_get_empty() }; 196 | unsafe { Shared::from_raw_ref(raw) } 197 | } 198 | } 199 | 200 | impl DrawFuncsImpl { 201 | /// Create a new `DrawFuncsImpl` from the `DrawFuncs` trait implementation 202 | /// of `T`. 203 | /// 204 | /// # Examples 205 | /// 206 | /// ```ignore 207 | /// use harfbuzz_rs::*; 208 | /// use harfbuzz_rs::draw_funcs::DrawFuncsImpl; 209 | /// 210 | /// // Dummy struct implementing DrawFuncs 211 | /// struct MyDrawFuncs { 212 | /// points: Vec<(f32,f32)>, 213 | /// } 214 | /// impl DrawFuncs for MyDrawFuncs { 215 | /// fn move_to(&self, _: &DrawState, x: f32, y: f32) { 216 | /// // ... 217 | /// } 218 | /// // implementations of other functions... 219 | /// } 220 | /// 221 | /// let draw_funcs: Owned> = DrawFuncsImpl::from_trait_impl(); 222 | /// ``` 223 | /// 224 | pub fn from_trait_impl() -> Owned> { 225 | let mut ffuncs = DrawFuncsImpl::new(); 226 | ffuncs.set_trait_impl(); 227 | ffuncs 228 | } 229 | 230 | fn set_trait_impl(&mut self) { 231 | self.set_move_to_func(|data, st, x, y| data.move_to(&st, x, y)); 232 | self.set_line_to_func(|data, st, x, y| data.line_to(&st, x, y)); 233 | self.set_quadratic_to_func(|data, st, cx, cy, x, y| data.quadratic_to(&st, cx, cy, x, y)); 234 | self.set_cubic_to_func(|data, st, c1x, c1y, c2x, c2y, x, y| { 235 | data.cubic_to(&st, c1x, c1y, c2x, c2y, x, y) 236 | }); 237 | self.set_close_path_func(|data, st| data.close_path(&st)); 238 | } 239 | } 240 | 241 | impl DrawFuncsImpl { 242 | pub fn new() -> Owned> { 243 | unsafe { Owned::from_raw(hb_draw_funcs_create()) } 244 | } 245 | 246 | pub fn set_move_to_func(&mut self, func: F) 247 | where 248 | F: Fn(&mut T, DrawState, f32, f32), 249 | { 250 | let user_data = Box::new(func); 251 | unsafe { 252 | hb_draw_funcs_set_move_to_func( 253 | self.as_raw(), 254 | Some(rust_move_to_closure::), 255 | Box::into_raw(user_data) as *mut _, 256 | Some(destroy_box::), 257 | ); 258 | } 259 | } 260 | 261 | pub fn set_quadratic_to_func(&mut self, func: F) 262 | where 263 | F: Fn(&mut T, DrawState, f32, f32, f32, f32), 264 | { 265 | let user_data = Box::new(func); 266 | unsafe { 267 | hb_draw_funcs_set_quadratic_to_func( 268 | self.as_raw(), 269 | Some(rust_quadratic_to_closure::), 270 | Box::into_raw(user_data) as *mut _, 271 | Some(destroy_box::), 272 | ); 273 | } 274 | } 275 | 276 | pub fn set_line_to_func(&mut self, func: F) 277 | where 278 | F: Fn(&mut T, DrawState, f32, f32), 279 | { 280 | let user_data = Box::new(func); 281 | unsafe { 282 | hb_draw_funcs_set_line_to_func( 283 | self.as_raw(), 284 | Some(rust_line_to_closure::), 285 | Box::into_raw(user_data) as *mut _, 286 | Some(destroy_box::), 287 | ); 288 | } 289 | } 290 | 291 | pub fn set_cubic_to_func(&mut self, func: F) 292 | where 293 | F: Fn(&mut T, DrawState, f32, f32, f32, f32, f32, f32), 294 | { 295 | let user_data = Box::new(func); 296 | unsafe { 297 | hb_draw_funcs_set_cubic_to_func( 298 | self.as_raw(), 299 | Some(rust_cubic_to_closure::), 300 | Box::into_raw(user_data) as *mut _, 301 | Some(destroy_box::), 302 | ); 303 | } 304 | } 305 | 306 | pub fn set_close_path_func(&mut self, func: F) 307 | where 308 | F: Fn(&mut T, DrawState), 309 | { 310 | let user_data = Box::new(func); 311 | unsafe { 312 | hb_draw_funcs_set_close_path_func( 313 | self.as_raw(), 314 | Some(rust_close_path_closure::), 315 | Box::into_raw(user_data) as *mut _, 316 | Some(destroy_box::), 317 | ); 318 | } 319 | } 320 | } 321 | 322 | impl fmt::Debug for DrawFuncsImpl { 323 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 324 | f.debug_struct("DrawFuncsImpl") 325 | .field("raw", &self.as_raw()) 326 | .finish() 327 | } 328 | } 329 | 330 | unsafe impl HarfbuzzObject for DrawFuncsImpl { 331 | type Raw = hb_draw_funcs_t; 332 | 333 | unsafe fn from_raw(raw: *const Self::Raw) -> Self { 334 | DrawFuncsImpl { 335 | raw: NonNull::new(raw as *mut _).unwrap(), 336 | marker: PhantomData, 337 | } 338 | } 339 | 340 | fn as_raw(&self) -> *mut Self::Raw { 341 | self.raw.as_ptr() 342 | } 343 | 344 | unsafe fn reference(&self) { 345 | hb_draw_funcs_reference(self.as_raw()); 346 | } 347 | 348 | unsafe fn dereference(&self) { 349 | hb_draw_funcs_destroy(self.as_raw()) 350 | } 351 | } 352 | 353 | unsafe impl Send for DrawFuncsImpl {} 354 | unsafe impl Sync for DrawFuncsImpl {} 355 | 356 | #[cfg(test)] 357 | mod tests { 358 | use crate::draw_funcs::DrawFuncs; 359 | 360 | use crate::{Face, Font, *}; 361 | use std::path::PathBuf; 362 | 363 | #[repr(C)] 364 | #[derive(Debug)] 365 | struct TestDrawFuncs { 366 | output: String, 367 | } 368 | impl DrawFuncs for TestDrawFuncs { 369 | fn move_to(&mut self, _st: &draw_funcs::DrawState, to_x: f32, to_y: f32) { 370 | self.output.push_str(&format!("M {:} {:} ", to_x, to_y)); 371 | } 372 | 373 | fn line_to(&mut self, _st: &draw_funcs::DrawState, to_x: f32, to_y: f32) { 374 | self.output.push_str(&format!("L {:} {:} ", to_x, to_y)); 375 | } 376 | 377 | fn quadratic_to( 378 | &mut self, 379 | _st: &draw_funcs::DrawState, 380 | control_x: f32, 381 | control_y: f32, 382 | to_x: f32, 383 | to_y: f32, 384 | ) { 385 | self.output.push_str(&format!( 386 | "Q {:} {:}, {:} {:} ", 387 | control_x, control_y, to_x, to_y 388 | )); 389 | } 390 | 391 | fn cubic_to( 392 | &mut self, 393 | _st: &draw_funcs::DrawState, 394 | control1_x: f32, 395 | control1_y: f32, 396 | control2_x: f32, 397 | control2_y: f32, 398 | to_x: f32, 399 | to_y: f32, 400 | ) { 401 | self.output.push_str(&format!( 402 | "C {:} {:}, {:}, {:}, {:} {:} ", 403 | control1_x, control1_y, control2_x, control2_y, to_x, to_y 404 | )); 405 | } 406 | 407 | fn close_path(&mut self, _st: &draw_funcs::DrawState) { 408 | self.output.push('Z'); 409 | } 410 | } 411 | 412 | #[test] 413 | fn test_move_to() { 414 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 415 | path.push("testfiles/SourceSansVariable-Roman.ttf"); 416 | let face = Face::from_file(path, 0).expect("Error reading font file."); 417 | let font = Font::new(face); 418 | let shape = TestDrawFuncs { 419 | output: String::new(), 420 | }; 421 | font.draw_glyph(2, &shape); 422 | println!("After"); 423 | assert_eq!(shape.output, "M 10 0 L 246 660 L 274 660 L 510 0 L 476 0 L 338 396 Q 317 456, 298.5 510 Q 280 564, 262 626 L 258 626 Q 240 564, 221.5 510 Q 203 456, 182 396 L 42 0 L 10 0 ZM 112 236 L 112 264 L 405 264 L 405 236 L 112 236 Z"); 424 | } 425 | } 426 | -------------------------------------------------------------------------------- /src/face.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_void; 2 | use std::ptr::NonNull; 3 | 4 | use std::marker::PhantomData; 5 | use std::path::Path; 6 | 7 | use crate::bindings::{ 8 | hb_blob_t, hb_face_create, hb_face_create_for_tables, hb_face_destroy, hb_face_get_empty, 9 | hb_face_get_glyph_count, hb_face_get_index, hb_face_get_upem, hb_face_reference, 10 | hb_face_reference_blob, hb_face_reference_table, hb_face_set_glyph_count, hb_face_set_upem, 11 | hb_face_t, hb_tag_t, 12 | }; 13 | use crate::blob::Blob; 14 | use crate::common::{HarfbuzzObject, Owned, Shared, Tag}; 15 | 16 | /// A wrapper around `hb_face_t`. 17 | /// 18 | /// An excerpt from harfbuzz documentation: 19 | /// > Font face is objects represent a single face in a font family. More 20 | /// > exactly, a font face represents a single face in a binary font file. Font 21 | /// > faces are typically built from a binary blob and a face index. Font faces 22 | /// > are used to create fonts. 23 | #[derive(Debug)] 24 | pub struct Face<'a> { 25 | raw: NonNull, 26 | marker: PhantomData<&'a [u8]>, 27 | } 28 | 29 | impl<'a> Face<'a> { 30 | /// Create a new `Face` from the data. 31 | /// 32 | /// If `data` is not a valid font then this function returns the empty face. 33 | pub fn new>>>(data: T, index: u32) -> Owned> { 34 | let blob = data.into(); 35 | let hb_face = unsafe { hb_face_create(blob.as_raw(), index) }; 36 | unsafe { Owned::from_raw(hb_face) } 37 | } 38 | 39 | /// Returns a "null" face. 40 | pub fn empty() -> Owned> { 41 | let hb_face = unsafe { hb_face_get_empty() }; 42 | unsafe { Owned::from_raw(hb_face) } 43 | } 44 | 45 | /// Create a new face from the contents of the file at `path`. 46 | /// 47 | /// This function reads the contents of the file at `path` into memory, 48 | /// creates a `Blob` and then calls `Face::new`. 49 | /// 50 | /// See also the discussion in `Blob::from_file`. 51 | pub fn from_file>(path: P, index: u32) -> std::io::Result>> { 52 | let blob = Blob::from_file(path)?; 53 | Ok(Face::new(blob, index)) 54 | } 55 | 56 | /// Create a face from the bytes of a given slice and an index specifying 57 | /// which font to read from an OpenType font collection. 58 | pub fn from_bytes(bytes: &[u8], index: u32) -> Owned> { 59 | let blob = Blob::with_bytes(bytes); 60 | Face::new(blob, index) 61 | } 62 | 63 | /// Create a new face from a closure that returns a raw 64 | /// [`Blob`](struct.Blob.html) of table data. 65 | pub fn from_table_func<'b, F>(func: F) -> Owned> 66 | where 67 | F: 'b + Send + Sync + FnMut(Tag) -> Option>>, 68 | { 69 | extern "C" fn destroy_box(ptr: *mut c_void) { 70 | _ = unsafe { Box::from_raw(ptr as *mut U) }; 71 | } 72 | extern "C" fn table_func<'b, F>( 73 | _: *mut hb_face_t, 74 | tag: hb_tag_t, 75 | user_data: *mut c_void, 76 | ) -> *mut hb_blob_t 77 | where 78 | F: FnMut(Tag) -> Option>>, 79 | { 80 | let tag = Tag(tag); 81 | let closure = unsafe { &mut *(user_data as *mut F) }; 82 | let blob = closure(tag); 83 | match blob { 84 | Some(blob) => Shared::into_raw(blob), 85 | None => std::ptr::null_mut(), 86 | } 87 | } 88 | let boxed_closure = Box::new(func); 89 | unsafe { 90 | let face = hb_face_create_for_tables( 91 | Some(table_func::<'b, F>), 92 | Box::into_raw(boxed_closure) as *mut _, 93 | Some(destroy_box::), 94 | ); 95 | Owned::from_raw(face) 96 | } 97 | } 98 | 99 | pub fn face_data(&self) -> Shared> { 100 | unsafe { 101 | let raw_blob = hb_face_reference_blob(self.as_raw()); 102 | Shared::from_raw_owned(raw_blob) 103 | } 104 | } 105 | 106 | /// Returns the slice of bytes for the table named `tag` or None if there is 107 | /// no table with `tag`. 108 | pub fn table_with_tag(&self, tag: impl Into) -> Option>> { 109 | unsafe { 110 | let raw_blob = hb_face_reference_table(self.as_raw(), tag.into().0); 111 | if raw_blob.is_null() { 112 | None 113 | } else { 114 | let blob: Shared> = Shared::from_raw_owned(raw_blob); 115 | if blob.is_empty() { 116 | None 117 | } else { 118 | Some(blob) 119 | } 120 | } 121 | } 122 | } 123 | 124 | pub fn index(&self) -> u32 { 125 | unsafe { hb_face_get_index(self.as_raw()) } 126 | } 127 | 128 | pub fn set_upem(&mut self, upem: u32) { 129 | unsafe { hb_face_set_upem(self.as_raw(), upem) }; 130 | } 131 | 132 | pub fn upem(&self) -> u32 { 133 | unsafe { hb_face_get_upem(self.as_raw()) } 134 | } 135 | 136 | pub fn set_glyph_count(&mut self, count: u32) { 137 | unsafe { hb_face_set_glyph_count(self.as_raw(), count) }; 138 | } 139 | 140 | /// Returns the number of glyphs contained in the face. 141 | pub fn glyph_count(&self) -> u32 { 142 | unsafe { hb_face_get_glyph_count(self.as_raw()) } 143 | } 144 | 145 | #[cfg(variation_support)] 146 | pub fn get_variation_axis_infos(&self) -> Vec { 147 | let mut count = unsafe { hb_ot_var_get_axis_count(self.as_raw()) }; 148 | let mut vector: Vec = Vec::with_capacity(count as usize); 149 | unsafe { 150 | hb_ot_var_get_axis_infos(self.as_raw(), 0, &mut count, vector.as_mut_ptr() as *mut _) 151 | }; 152 | unsafe { vector.set_len(count as usize) }; 153 | vector 154 | } 155 | } 156 | 157 | #[cfg(variation_support)] 158 | #[derive(Debug, Clone, Copy)] 159 | #[repr(transparent)] 160 | pub struct VariationAxisInfo(pub hb_ot_var_axis_info_t); 161 | 162 | unsafe impl<'a> HarfbuzzObject for Face<'a> { 163 | type Raw = hb_face_t; 164 | 165 | unsafe fn from_raw(raw: *const hb_face_t) -> Self { 166 | Face { 167 | raw: NonNull::new(raw as *mut _).unwrap(), 168 | marker: PhantomData, 169 | } 170 | } 171 | 172 | fn as_raw(&self) -> *mut Self::Raw { 173 | self.raw.as_ptr() 174 | } 175 | 176 | unsafe fn reference(&self) { 177 | hb_face_reference(self.as_raw()); 178 | } 179 | 180 | unsafe fn dereference(&self) { 181 | hb_face_destroy(self.as_raw()); 182 | } 183 | } 184 | 185 | unsafe impl<'a> Send for Face<'a> {} 186 | unsafe impl<'a> Sync for Face<'a> {} 187 | 188 | #[cfg(test)] 189 | mod tests { 190 | use super::*; 191 | 192 | #[test] 193 | fn test_face_from_table_func() { 194 | let face = Face::from_table_func(|table_tag| { 195 | let content = format!("{}-table", table_tag); 196 | Some(content.into_bytes().into()) 197 | }); 198 | 199 | let maxp_table = face.table_with_tag(b"maxp").unwrap(); 200 | assert_eq!(maxp_table.as_ref(), b"maxp-table"); 201 | 202 | let maxp_table = face.table_with_tag(b"hhea").unwrap(); 203 | assert_eq!(&maxp_table.as_ref(), b"hhea-table"); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/font.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::NonNull; 2 | 3 | use std::os::raw::c_void; 4 | 5 | use crate::bindings::{ 6 | hb_font_create, hb_font_create_sub_font, hb_font_destroy, hb_font_draw_glyph, 7 | hb_font_extents_t, hb_font_get_empty, hb_font_get_face, hb_font_get_glyph_contour_point, 8 | hb_font_get_glyph_extents, hb_font_get_glyph_from_name, hb_font_get_glyph_h_advance, 9 | hb_font_get_glyph_h_origin, hb_font_get_glyph_name, hb_font_get_glyph_v_advance, 10 | hb_font_get_glyph_v_origin, hb_font_get_h_extents, hb_font_get_nominal_glyph, 11 | hb_font_get_parent, hb_font_get_ppem, hb_font_get_scale, hb_font_get_v_extents, 12 | hb_font_get_variation_glyph, hb_font_reference, hb_font_set_funcs, hb_font_set_ppem, 13 | hb_font_set_scale, hb_font_set_variations, hb_font_t, hb_glyph_extents_t, hb_position_t, 14 | }; 15 | use crate::common::{HarfbuzzObject, Owned, Shared}; 16 | pub use crate::draw_funcs::DrawFuncs; 17 | use crate::draw_funcs::DrawFuncsImpl; 18 | use crate::face::Face; 19 | pub use crate::font_funcs::FontFuncs; 20 | use crate::font_funcs::FontFuncsImpl; 21 | use crate::Variation; 22 | 23 | use std::ffi::CStr; 24 | use std::marker::PhantomData; 25 | 26 | pub type Glyph = u32; 27 | pub type Position = hb_position_t; 28 | 29 | #[repr(C)] 30 | #[derive(Debug, Copy, Clone, Default)] 31 | pub struct FontExtents { 32 | pub ascender: Position, 33 | pub descender: Position, 34 | pub line_gap: Position, 35 | pub(crate) reserved: [Position; 9], 36 | } 37 | 38 | impl FontExtents { 39 | pub fn new(ascender: Position, descender: Position, line_gap: Position) -> FontExtents { 40 | FontExtents { 41 | ascender, 42 | descender, 43 | line_gap, 44 | ..Default::default() 45 | } 46 | } 47 | 48 | pub fn into_raw(self) -> hb_font_extents_t { 49 | unsafe { std::mem::transmute(self) } 50 | } 51 | 52 | pub fn from_raw(raw: hb_font_extents_t) -> FontExtents { 53 | unsafe { std::mem::transmute(raw) } 54 | } 55 | } 56 | 57 | pub type GlyphExtents = hb_glyph_extents_t; 58 | 59 | pub(crate) extern "C" fn destroy_box(ptr: *mut c_void) { 60 | _ = unsafe { Box::from_raw(ptr as *mut U) }; 61 | } 62 | 63 | /// A type representing a single font (i.e. a specific combination of typeface, 64 | /// font-size and font-variation settings) 65 | /// 66 | /// It safely wraps the raw HarfBuzz type `hb_font_t *`. 67 | /// 68 | /// # Font Funcs 69 | /// 70 | /// A font is one of the most important structures in harfbuzz. It coordinates 71 | /// how glyph information is accessed during shaping. This is done through 72 | /// so-called font funcs. 73 | /// 74 | /// You can manually define new font funcs according to your needs 75 | /// (see [`FontFuncs`]), in most 76 | /// cases though the default font funcs provided by HarfBuzz will suffice. In 77 | /// that case the creation of a usable font amounts to calling the [`Font::new()`] 78 | /// constructor with the desired [`Face`]. 79 | /// 80 | /// # Parents and Children 81 | /// 82 | /// Every font except the empty font has a parent font. If a font does not have 83 | /// some font func set, it will automatically use the parent's implementation of 84 | /// that font func. This behavior is useful to effectively "subclass" font 85 | /// objects to use different font function implementations for some font funcs 86 | /// while reusing the parent's implementation for the remaining funcs. 87 | /// 88 | /// A child font is created through [`Self::create_sub_font()`]. 89 | /// 90 | /// Since every font created by `Font::new` by default uses HarfBuzz's internal 91 | /// font funcs they can be used as a fallback mechanism by only customizing the 92 | /// font funcs of a sub-font. 93 | /// 94 | /// # Font Variations 95 | /// 96 | /// Using OpenType font variations is supported (but variation axis enumeration 97 | /// is not supported yet unfortunately). See an example for how to use font 98 | /// variations in [`Self::set_variations()`]. 99 | /// 100 | /// # Examples 101 | /// 102 | /// Create a simple font from a `Face` using the default font funcs: 103 | /// 104 | /// ``` 105 | /// use harfbuzz_rs::*; 106 | /// # use std::path::PathBuf; 107 | /// # let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 108 | /// # path.push("testfiles/SourceSansVariable-Roman.ttf"); 109 | /// let face = Face::from_file(path, 0).expect("Error reading font file."); 110 | /// let font = Font::new(face); 111 | /// ``` 112 | #[derive(Debug, PartialEq, Eq)] 113 | pub struct Font<'a> { 114 | raw: NonNull, 115 | marker: PhantomData<&'a hb_font_t>, 116 | } 117 | 118 | impl<'a> Font<'a> { 119 | /// Create a new font from the specified `Face`. 120 | /// 121 | /// This is the default constructor of `Font`. In many cases it is the best 122 | /// choice if you simply want to shape some text. 123 | /// 124 | /// The default parent of a font created by this function is the empty font. 125 | /// 126 | /// # Font Functions 127 | /// 128 | /// The font returned by this function uses the font funcs that come with 129 | /// HarfBuzz for OpenType Fonts. The font funcs can be overwritten using 130 | /// `Font::set_font_funcs`. 131 | /// 132 | /// # Errors 133 | /// 134 | /// If for some reason no valid font can be constructed this function will 135 | /// return the empty font. 136 | pub fn new>>>(face: T) -> Owned { 137 | unsafe { 138 | let face = face.into(); 139 | let raw_font = hb_font_create(face.as_raw()); 140 | Owned::from_raw(raw_font) 141 | } 142 | } 143 | 144 | /// Returns an empty font. 145 | /// 146 | /// This can be useful when you need a dummy font for whatever reason. Any 147 | /// function you call on the empty font will return some reasonable default 148 | /// value. An empty font is the only font whose `.parent()` method returns 149 | /// `None`. 150 | /// 151 | /// # Examples 152 | /// 153 | /// An empty font does not have a parent. 154 | /// ``` 155 | /// use harfbuzz_rs::Font; 156 | /// let empty_font = Font::empty(); 157 | /// assert!(empty_font.parent().is_none()); 158 | /// ``` 159 | pub fn empty() -> Owned { 160 | unsafe { 161 | let raw_font = hb_font_get_empty(); 162 | Owned::from_raw(raw_font) 163 | } 164 | } 165 | 166 | /// Create a new sub font from the current font that by default inherits its 167 | /// parent font's face, scale, ppem and font funcs. 168 | /// 169 | /// The sub-font's parent will be the font on which this method is called. 170 | /// 171 | /// Creating sub-fonts is especially useful if you want to overwrite some of 172 | /// the font funcs of an already existing font. 173 | /// 174 | /// # Examples 175 | /// ``` 176 | /// use harfbuzz_rs::*; 177 | /// # use std::path::PathBuf; 178 | /// # let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 179 | /// # path.push("testfiles/SourceSansVariable-Roman.ttf"); 180 | /// let face = Face::from_file(path, 0).expect("Error reading font file."); 181 | /// let font = Font::new(face).to_shared(); 182 | /// 183 | /// let sub_font = Font::create_sub_font(font.clone()); 184 | /// // we know that sub_font has a parent 185 | /// assert_eq!(sub_font.parent().unwrap(), font); 186 | /// ``` 187 | pub fn create_sub_font>>(font: T) -> Owned { 188 | unsafe { Owned::from_raw(hb_font_create_sub_font(font.into().as_raw())) } 189 | } 190 | 191 | /// Returns a shared pointer to the parent font. 192 | /// 193 | /// If `self` is the empty font it returns `None`. 194 | /// 195 | /// # See also 196 | /// 197 | /// [`create_sub_font`](./struct.Font.html#method.create_sub_font) 198 | /// 199 | /// # Examples 200 | /// 201 | /// The empty font (and only it) has no parent: 202 | /// 203 | /// ``` 204 | /// use harfbuzz_rs::Font; 205 | /// 206 | /// let font = Font::empty(); 207 | /// assert_eq!(font.parent(), None); 208 | /// ``` 209 | pub fn parent(&self) -> Option> { 210 | unsafe { 211 | let parent = hb_font_get_parent(self.as_raw()); 212 | if parent.is_null() { 213 | // hb_font_get_parent returns null-ptr if called on the empty font. 214 | None 215 | } else { 216 | Some(Shared::from_raw_ref(parent)) 217 | } 218 | } 219 | } 220 | 221 | /// Returns a shared pointer to the face from which this font was created. 222 | pub fn face(&self) -> Shared> { 223 | unsafe { Shared::from_raw_ref(hb_font_get_face(self.as_raw())) } 224 | } 225 | 226 | /// Returns the EM scale of the font. 227 | pub fn scale(&self) -> (i32, i32) { 228 | let mut result = (0i32, 0i32); 229 | unsafe { hb_font_get_scale(self.as_raw(), &mut result.0, &mut result.1) }; 230 | result 231 | } 232 | 233 | /// Sets the EM scale of the font. 234 | pub fn set_scale(&mut self, x: i32, y: i32) { 235 | unsafe { hb_font_set_scale(self.as_raw_mut(), x, y) }; 236 | } 237 | 238 | pub fn ppem(&self) -> (u32, u32) { 239 | let mut result = (0u32, 0u32); 240 | unsafe { hb_font_get_ppem(self.as_raw(), &mut result.0, &mut result.1) }; 241 | result 242 | } 243 | 244 | pub fn set_ppem(&mut self, x: u32, y: u32) { 245 | unsafe { hb_font_set_ppem(self.as_raw_mut(), x, y) }; 246 | } 247 | 248 | /// Sets the font functions that this font will have from a value that 249 | /// implements [`FontFuncs`]. 250 | pub fn set_font_funcs(&mut self, funcs: FuncsType) 251 | where 252 | FuncsType: 'a + Send + Sync + FontFuncs, 253 | { 254 | let funcs_impl: Owned> = FontFuncsImpl::from_trait_impl(); 255 | let font_data = Box::new(funcs); 256 | unsafe { 257 | hb_font_set_funcs( 258 | self.as_raw(), 259 | funcs_impl.as_raw(), 260 | Box::into_raw(font_data) as *mut _, 261 | Some(destroy_box::), 262 | ) 263 | }; 264 | } 265 | 266 | // scale from parent font 267 | pub(crate) fn parent_scale_x_distance(&self, f: impl Fn(&Font<'_>) -> Position) -> Position { 268 | let x_scale = self.scale().0; 269 | if let Some(parent) = self.parent() { 270 | let parent_x_scale = parent.scale().0; 271 | 272 | if parent_x_scale != x_scale { 273 | (f(&parent) as i64 * x_scale as i64 / parent_x_scale as i64) as Position 274 | } else { 275 | f(&parent) 276 | } 277 | } else { 278 | 0 279 | } 280 | } 281 | 282 | // scale from parent font 283 | pub(crate) fn parent_scale_y_distance(&self, f: impl Fn(&Font<'_>) -> Position) -> Position { 284 | let y_scale = self.scale().0; 285 | if let Some(parent) = self.parent() { 286 | let parent_y_scale = parent.scale().0; 287 | 288 | if parent_y_scale != y_scale { 289 | (f(&parent) as i64 * y_scale as i64 / parent_y_scale as i64) as Position 290 | } else { 291 | f(&parent) 292 | } 293 | } else { 294 | 0 295 | } 296 | } 297 | 298 | // scale from parent font 299 | pub(crate) fn parent_scale_position(&self, v: (Position, Position)) -> (Position, Position) { 300 | ( 301 | self.parent_scale_x_distance(|_| v.0), 302 | self.parent_scale_y_distance(|_| v.1), 303 | ) 304 | } 305 | 306 | pub fn get_font_h_extents(&self) -> Option { 307 | unsafe { 308 | let mut extents = FontExtents::default(); 309 | let result = 310 | hb_font_get_h_extents(self.as_raw(), &mut extents as *mut FontExtents as *mut _); 311 | if result == 1 { 312 | Some(extents) 313 | } else { 314 | None 315 | } 316 | } 317 | } 318 | 319 | pub fn get_font_v_extents(&self) -> Option { 320 | unsafe { 321 | let mut extents = std::mem::zeroed::(); 322 | let result = 323 | hb_font_get_v_extents(self.as_raw(), &mut extents as *mut FontExtents as *mut _); 324 | if result == 1 { 325 | Some(extents) 326 | } else { 327 | None 328 | } 329 | } 330 | } 331 | 332 | pub fn get_nominal_glyph(&self, c: char) -> Option { 333 | unsafe { 334 | let mut glyph = 0; 335 | let result = hb_font_get_nominal_glyph(self.as_raw(), c as u32, &mut glyph); 336 | if result == 1 { 337 | Some(glyph) 338 | } else { 339 | None 340 | } 341 | } 342 | } 343 | 344 | pub fn get_variation_glyph(&self, c: char, v: char) -> Option { 345 | unsafe { 346 | let mut glyph = 0; 347 | let result = hb_font_get_variation_glyph(self.as_raw(), c as u32, v as u32, &mut glyph); 348 | if result == 1 { 349 | Some(glyph) 350 | } else { 351 | None 352 | } 353 | } 354 | } 355 | 356 | /// Get the horizontal advance width of a glyph. 357 | pub fn get_glyph_h_advance(&self, glyph: Glyph) -> Position { 358 | unsafe { hb_font_get_glyph_h_advance(self.as_raw(), glyph) } 359 | } 360 | 361 | /// Get the vertical advance width of a glyph. 362 | pub fn get_glyph_v_advance(&self, glyph: Glyph) -> Position { 363 | unsafe { hb_font_get_glyph_v_advance(self.as_raw(), glyph) } 364 | } 365 | 366 | pub fn get_glyph_h_origin(&self, glyph: Glyph) -> Option<(Position, Position)> { 367 | unsafe { 368 | let mut pos = (0, 0); 369 | let result = hb_font_get_glyph_h_origin(self.as_raw(), glyph, &mut pos.0, &mut pos.1); 370 | if result == 1 { 371 | Some(pos) 372 | } else { 373 | None 374 | } 375 | } 376 | } 377 | 378 | pub fn get_glyph_v_origin(&self, glyph: Glyph) -> Option<(Position, Position)> { 379 | unsafe { 380 | let mut pos = (0, 0); 381 | let result = hb_font_get_glyph_v_origin(self.as_raw(), glyph, &mut pos.0, &mut pos.1); 382 | if result == 1 { 383 | Some(pos) 384 | } else { 385 | None 386 | } 387 | } 388 | } 389 | 390 | pub fn get_glyph_extents(&self, glyph: Glyph) -> Option { 391 | unsafe { 392 | let mut extents = std::mem::zeroed::(); 393 | let result = hb_font_get_glyph_extents(self.as_raw(), glyph, &mut extents); 394 | if result == 1 { 395 | Some(extents) 396 | } else { 397 | None 398 | } 399 | } 400 | } 401 | 402 | pub fn get_glyph_contour_point( 403 | &self, 404 | glyph: Glyph, 405 | point_index: u32, 406 | ) -> Option<(Position, Position)> { 407 | unsafe { 408 | let mut pos = (0, 0); 409 | let result = hb_font_get_glyph_contour_point( 410 | self.as_raw(), 411 | glyph, 412 | point_index, 413 | &mut pos.0, 414 | &mut pos.1, 415 | ); 416 | if result == 1 { 417 | Some(pos) 418 | } else { 419 | None 420 | } 421 | } 422 | } 423 | 424 | pub fn get_glyph_name(&self, glyph: Glyph) -> Option { 425 | let mut buffer = [0; 256]; 426 | let result = unsafe { 427 | hb_font_get_glyph_name( 428 | self.as_raw(), 429 | glyph, 430 | buffer.as_mut_ptr() as *mut _, 431 | buffer.len() as u32, 432 | ) 433 | }; 434 | if result == 1 { 435 | let cstr = unsafe { CStr::from_ptr(buffer.as_ptr()) }; 436 | cstr.to_str().ok().map(|y| y.to_string()) 437 | } else { 438 | None 439 | } 440 | } 441 | 442 | pub fn get_glyph_from_name(&self, name: &str) -> Option { 443 | unsafe { 444 | let mut glyph = 0; 445 | let result = hb_font_get_glyph_from_name( 446 | self.as_raw(), 447 | name.as_ptr() as *mut _, 448 | name.len() as i32, 449 | &mut glyph, 450 | ); 451 | if result == 1 { 452 | Some(glyph) 453 | } else { 454 | None 455 | } 456 | } 457 | } 458 | 459 | pub fn draw_glyph(&self, glyph: Glyph, drawfuncs: &FuncsType) 460 | where 461 | FuncsType: 'a + Send + Sync + DrawFuncs + std::fmt::Debug, 462 | { 463 | let funcs_impl: Owned> = DrawFuncsImpl::from_trait_impl(); 464 | unsafe { 465 | hb_font_draw_glyph( 466 | self.as_raw(), 467 | glyph, 468 | funcs_impl.as_raw(), 469 | drawfuncs as *const _ as *mut std::ffi::c_void, 470 | ) 471 | }; 472 | } 473 | 474 | /// Set font variation settings. 475 | /// 476 | /// # Examples 477 | /// 478 | /// Setup a font with two variations enabled: 479 | /// ``` 480 | /// # use harfbuzz_rs::*; 481 | /// use harfbuzz_rs::{Face, Font, Variation}; 482 | /// # use std::path::PathBuf; 483 | /// # let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 484 | /// # path.push("testfiles/SourceSansVariable-Roman.ttf"); 485 | /// let face = Face::from_file(path, 0).expect("Error reading font file."); 486 | /// let mut font = Font::new(face); 487 | /// 488 | /// let variation_vec : Vec = vec![Variation::new(b"wght", 800.0), Variation::new(b"wdth", 30.0)]; 489 | /// font.set_variations(&variation_vec); 490 | /// ``` 491 | pub fn set_variations(&mut self, variations: &[Variation]) { 492 | unsafe { 493 | hb_font_set_variations( 494 | self.as_raw_mut(), 495 | variations.as_ptr() as *mut _, 496 | variations.len() as u32, 497 | ) 498 | }; 499 | } 500 | } 501 | 502 | unsafe impl<'a> Send for Font<'a> {} 503 | unsafe impl<'a> Sync for Font<'a> {} 504 | 505 | unsafe impl<'a> HarfbuzzObject for Font<'a> { 506 | type Raw = hb_font_t; 507 | 508 | unsafe fn from_raw(raw: *const Self::Raw) -> Self { 509 | Font { 510 | raw: NonNull::new(raw as *mut _).unwrap(), 511 | marker: PhantomData, 512 | } 513 | } 514 | 515 | fn as_raw(&self) -> *mut Self::Raw { 516 | self.raw.as_ptr() 517 | } 518 | 519 | unsafe fn reference(&self) { 520 | hb_font_reference(self.as_raw()); 521 | } 522 | 523 | unsafe fn dereference(&self) { 524 | hb_font_destroy(self.as_raw()); 525 | } 526 | } 527 | 528 | impl<'a> Default for Owned> { 529 | fn default() -> Self { 530 | Font::empty() 531 | } 532 | } 533 | 534 | impl<'a> Default for Shared> { 535 | fn default() -> Self { 536 | Font::empty().into() 537 | } 538 | } 539 | 540 | #[cfg(test)] 541 | mod test { 542 | use super::*; 543 | use crate::tests::assert_memory_layout_equal; 544 | 545 | #[test] 546 | fn test_font_extents_layout() { 547 | assert_memory_layout_equal::() 548 | } 549 | } 550 | -------------------------------------------------------------------------------- /src/font_funcs.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Manuel Reinhardt 2 | // 3 | // This software is released under the MIT License. 4 | // https://opensource.org/licenses/MIT 5 | 6 | //! Contains the `FontFuncs` trait. 7 | //! 8 | //! In the future there may be exposed other ways to create font funcs. 9 | 10 | use crate::bindings::{ 11 | hb_bool_t, hb_codepoint_t, hb_font_extents_t, hb_font_funcs_create, hb_font_funcs_destroy, 12 | hb_font_funcs_get_empty, hb_font_funcs_reference, hb_font_funcs_set_font_h_extents_func, 13 | hb_font_funcs_set_font_v_extents_func, hb_font_funcs_set_glyph_contour_point_func, 14 | hb_font_funcs_set_glyph_extents_func, hb_font_funcs_set_glyph_from_name_func, 15 | hb_font_funcs_set_glyph_h_advance_func, hb_font_funcs_set_glyph_h_origin_func, 16 | hb_font_funcs_set_glyph_name_func, hb_font_funcs_set_glyph_v_advance_func, 17 | hb_font_funcs_set_glyph_v_origin_func, hb_font_funcs_set_nominal_glyph_func, 18 | hb_font_funcs_set_variation_glyph_func, hb_font_funcs_t, hb_font_t, hb_glyph_extents_t, 19 | }; 20 | use crate::font::destroy_box; 21 | use crate::{Font, FontExtents, Glyph, GlyphExtents, HarfbuzzObject, Owned, Position, Shared}; 22 | 23 | use std::os::raw::c_void; 24 | 25 | use std; 26 | use std::ffi::{CStr, CString}; 27 | use std::fmt; 28 | use std::io::Write; 29 | use std::marker::PhantomData; 30 | use std::panic; 31 | use std::ptr::NonNull; 32 | 33 | /// This Trait specifies the font callbacks that harfbuzz uses for its shaping. 34 | /// 35 | /// Custom font data providers can implement this trait to supply the HarfBuzz 36 | /// font shaping engine with the necessary information it needs during the 37 | /// shaping process. 38 | /// 39 | /// Users wishing to use a font data provider that implements `FontFuncs` can 40 | /// tell HarfBuzz to use the data provider by calling [`Font::set_font_funcs()`] 41 | /// on a [`Font`]. 42 | /// 43 | /// Usually this is not necessary, since HarfBuzz comes by default with an 44 | /// integrated implementation of `FontFuncs` that reads the required data from 45 | /// an OpenType font file. 46 | /// 47 | /// When implementing this trait, you may only want to override some of the 48 | /// functions, while the rest of the functions should fall back to the HarfBuzz 49 | /// provided font-funcs. This is possible through the use of *subfonts*. 50 | /// 51 | /// Note that if a `Font` is created directly from a face, its parent is the 52 | /// empty `Font` which returns null values for every font func. 53 | #[allow(unused_variables)] 54 | pub trait FontFuncs { 55 | fn get_font_h_extents(&self, font: &Font<'_>) -> Option { 56 | font.parent()? 57 | .get_font_h_extents() 58 | .map(|extents| FontExtents { 59 | ascender: font.parent_scale_y_distance(|_| extents.ascender), 60 | descender: font.parent_scale_y_distance(|_| extents.descender), 61 | line_gap: font.parent_scale_y_distance(|_| extents.line_gap), 62 | ..extents 63 | }) 64 | } 65 | fn get_font_v_extents(&self, font: &Font<'_>) -> Option { 66 | font.parent()? 67 | .get_font_v_extents() 68 | .map(|extents| FontExtents { 69 | ascender: font.parent_scale_y_distance(|_| extents.ascender), 70 | descender: font.parent_scale_y_distance(|_| extents.descender), 71 | line_gap: font.parent_scale_y_distance(|_| extents.line_gap), 72 | ..extents 73 | }) 74 | } 75 | fn get_nominal_glyph(&self, font: &Font<'_>, unicode: char) -> Option { 76 | font.parent()?.get_nominal_glyph(unicode) 77 | } 78 | fn get_variation_glyph( 79 | &self, 80 | font: &Font<'_>, 81 | unicode: char, 82 | variation_sel: char, 83 | ) -> Option { 84 | font.parent()?.get_variation_glyph(unicode, variation_sel) 85 | } 86 | fn get_glyph_h_advance(&self, font: &Font<'_>, glyph: Glyph) -> Position { 87 | font.parent_scale_x_distance(|parent| parent.get_glyph_h_advance(glyph)) 88 | } 89 | fn get_glyph_v_advance(&self, font: &Font<'_>, glyph: Glyph) -> Position { 90 | font.parent_scale_y_distance(|parent| parent.get_glyph_v_advance(glyph)) 91 | } 92 | fn get_glyph_h_origin(&self, font: &Font<'_>, glyph: Glyph) -> Option<(Position, Position)> { 93 | font.parent()? 94 | .get_glyph_h_origin(glyph) 95 | .map(|x| font.parent_scale_position(x)) 96 | } 97 | fn get_glyph_v_origin(&self, font: &Font<'_>, glyph: Glyph) -> Option<(Position, Position)> { 98 | font.parent()? 99 | .get_glyph_v_origin(glyph) 100 | .map(|x| font.parent_scale_position(x)) 101 | } 102 | fn get_glyph_extents(&self, font: &Font<'_>, glyph: Glyph) -> Option { 103 | font.parent()? 104 | .get_glyph_extents(glyph) 105 | .map(|extents| GlyphExtents { 106 | x_bearing: font.parent_scale_x_distance(|_| extents.x_bearing), 107 | y_bearing: font.parent_scale_y_distance(|_| extents.y_bearing), 108 | width: font.parent_scale_x_distance(|_| extents.width), 109 | height: font.parent_scale_y_distance(|_| extents.height), 110 | }) 111 | } 112 | fn get_glyph_contour_point( 113 | &self, 114 | font: &Font<'_>, 115 | glyph: Glyph, 116 | point_index: u32, 117 | ) -> Option<(Position, Position)> { 118 | font.parent()? 119 | .get_glyph_contour_point(glyph, point_index) 120 | .map(|x| font.parent_scale_position(x)) 121 | } 122 | fn get_glyph_name(&self, font: &Font<'_>, glyph: Glyph) -> Option { 123 | font.parent()?.get_glyph_name(glyph) 124 | } 125 | fn get_glyph_from_name(&self, font: &Font<'_>, name: &str) -> Option { 126 | font.parent()?.get_glyph_from_name(name) 127 | } 128 | } 129 | 130 | macro_rules! hb_callback { 131 | ($func_name:ident<$($arg:ident: $datatype:ty),*> -> $ret:ty { 132 | $(argument $closure_arg:ty => $expr:expr,)* 133 | return $closure_ret_id:ident: $closure_ret:ty => $ret_expr:expr 134 | }) => { 135 | #[allow(clippy::let_and_return)] 136 | extern "C" fn $func_name( 137 | font: *mut hb_font_t, 138 | font_data: *mut c_void, 139 | $( 140 | $arg: $datatype, 141 | )* 142 | closure_data: *mut c_void, 143 | ) -> $ret where F: Fn(&Font<'_>, &T, $($closure_arg),*) -> $closure_ret { 144 | let catch_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { 145 | let font_data = unsafe { &*(font_data as *const T) }; 146 | let font = unsafe { Font::from_raw(font) }; 147 | let closure = unsafe { &mut *(closure_data as *mut F) }; 148 | let $closure_ret_id = closure(&font, font_data, $($expr),*); 149 | $ret_expr 150 | })); 151 | match catch_result { 152 | Ok(val) => val, 153 | Err(_) => { 154 | // TODO: Log error 155 | Default::default() 156 | } 157 | } 158 | } 159 | }; 160 | } 161 | 162 | hb_callback!( 163 | rust_get_font_extents_closure -> hb_bool_t { 164 | return value: Option => { 165 | if let Some(extents) = value { 166 | unsafe { *metrics = extents.into_raw() }; 167 | 1 168 | } else { 169 | 0 170 | } 171 | } 172 | } 173 | ); 174 | 175 | hb_callback!( 176 | rust_get_nominal_glyph_closure< 177 | unicode: hb_codepoint_t, 178 | glyph: *mut hb_codepoint_t> 179 | -> hb_bool_t { 180 | argument char => { 181 | match std::char::from_u32(unicode) { 182 | Some(character) => character, 183 | None => return 0 184 | } 185 | }, 186 | return result_glyph: Option => { 187 | if let Some(g) = result_glyph { 188 | unsafe { *glyph = g } 189 | 1 190 | } else { 191 | 0 192 | } 193 | } 194 | } 195 | ); 196 | 197 | hb_callback!( 198 | rust_get_variation_glyph_closure< 199 | unicode: hb_codepoint_t, 200 | variation_selector: hb_codepoint_t, 201 | glyph: *mut hb_codepoint_t> -> hb_bool_t { 202 | argument char => { 203 | match std::char::from_u32(unicode) { 204 | Some(character) => character, 205 | None => return 0 206 | } 207 | }, 208 | argument char => { 209 | match std::char::from_u32(variation_selector) { 210 | Some(selector) => selector, 211 | None => return 0 212 | } 213 | }, 214 | return result_glyph: Option => { 215 | if let Some(g) = result_glyph { 216 | unsafe { *glyph = g } 217 | 1 218 | } else { 219 | 0 220 | } 221 | } 222 | } 223 | ); 224 | 225 | hb_callback!( 226 | rust_get_glyph_advance_closure -> Position { 227 | argument Glyph => glyph, 228 | return pos: Position => pos 229 | } 230 | ); 231 | 232 | hb_callback!( 233 | rust_get_glyph_origin_closure< 234 | glyph: hb_codepoint_t, 235 | x: *mut Position, 236 | y: *mut Position> 237 | -> hb_bool_t { 238 | argument Glyph => glyph, 239 | return pos: Option<(Position, Position)> => { 240 | if let Some((x_origin, y_origin)) = pos { 241 | unsafe { 242 | *x = x_origin; 243 | *y = y_origin; 244 | } 245 | 1 246 | } else { 247 | 0 248 | } 249 | } 250 | } 251 | ); 252 | 253 | hb_callback!( 254 | rust_get_glyph_extents_closure< 255 | glyph: hb_codepoint_t, 256 | extents: *mut hb_glyph_extents_t> 257 | -> hb_bool_t { 258 | argument Glyph => glyph, 259 | return value: Option => { 260 | match value { 261 | Some(result) => { 262 | unsafe { *extents = result }; 263 | 1 264 | } 265 | None => 0, 266 | } 267 | } 268 | } 269 | ); 270 | 271 | hb_callback!( 272 | rust_get_glyph_contour_point_closure< 273 | glyph: hb_codepoint_t, 274 | point: u32, 275 | x: *mut Position, 276 | y: *mut Position> 277 | -> hb_bool_t { 278 | argument Glyph => glyph, 279 | argument u32 => point, 280 | return value: Option<(Position, Position)> => { 281 | match value { 282 | Some((x_origin, y_origin)) => unsafe { 283 | *x = x_origin; 284 | *y = y_origin; 285 | 1 286 | }, 287 | None => 0 288 | } 289 | } 290 | } 291 | ); 292 | 293 | hb_callback!( 294 | rust_get_glyph_name_closure< 295 | glyph: hb_codepoint_t, 296 | name: *mut std::os::raw::c_char, 297 | size: u32> 298 | -> hb_bool_t { 299 | argument Glyph => glyph, 300 | return value: Option => { 301 | let mut name = unsafe { std::slice::from_raw_parts_mut(name as *mut u8, size as usize) }; 302 | let result = value 303 | .and_then(|string| CString::new(string).ok()) 304 | .and_then(|cstr| name.write_all(cstr.as_bytes_with_nul()).ok()); 305 | if result.is_some() { 306 | 1 307 | } else { 308 | name[0] = 0; 309 | 0 310 | } 311 | } 312 | } 313 | ); 314 | 315 | hb_callback!( 316 | rust_get_glyph_from_name_closure< 317 | name: *const std::os::raw::c_char, 318 | size: i32, 319 | glyph: *mut hb_codepoint_t> 320 | -> hb_bool_t { 321 | argument &str => { 322 | let string = match size { 323 | // `name` is null-terminated 324 | -1 => unsafe { CStr::from_ptr(name).to_str().ok() }, 325 | // `name` has length = `size` 326 | i if i >= 1 => unsafe { 327 | std::str::from_utf8(std::slice::from_raw_parts(name as *const u8, size as usize)).ok() 328 | }, 329 | _ => None, 330 | }; 331 | match string { 332 | Some(string) => string, 333 | None => return 0, 334 | } 335 | }, 336 | return result_glyph: Option => { 337 | if let Some(g) = result_glyph { 338 | unsafe { *glyph = g } 339 | 1 340 | } else { 341 | 0 342 | } 343 | } 344 | } 345 | ); 346 | 347 | /// A `FontFuncsImpl` contains implementations of the font callbacks that 348 | /// harfbuzz uses. 349 | /// 350 | /// It supports two ways to assign functions to specific font funcs. Either you 351 | /// can set a unique closure per font func or set the font funcs from a type 352 | /// that implements the `FontFuncs` trait using the `from_trait_impl` 353 | /// constructor. 354 | /// 355 | /// # Examples 356 | /// 357 | /// Create a `FontFuncsImpl` from individual closures: 358 | /// 359 | /// ```ignore 360 | /// use harfbuzz_rs::*; 361 | /// use harfbuzz_rs::font_funcs::FontFuncsImpl; 362 | /// use std::mem; 363 | /// 364 | /// let mut ffuncs: Owned> = FontFuncsImpl::new(); 365 | /// let value = 113; 366 | /// ffuncs.set_font_h_extents_func(|_, _| { 367 | /// Some(FontExtents { ascender: value, .. unsafe { mem::zeroed() } }) 368 | /// }); 369 | /// ``` 370 | /// 371 | /// Create a `FontFuncsImpl` from a type that implements `FontFuncs`: 372 | /// 373 | /// ```ignore 374 | /// use harfbuzz_rs::*; 375 | /// use harfbuzz_rs::font_funcs::FontFuncsImpl; 376 | /// 377 | /// // Dummy struct implementing FontFuncs 378 | /// struct MyFontData { 379 | /// value: i32, 380 | /// } 381 | /// impl FontFuncs for MyFontData { 382 | /// fn get_glyph_h_advance(&self, _: &Font, _: Glyph) -> Position { 383 | /// self.value 384 | /// } 385 | /// // implementations of other functions... 386 | /// } 387 | /// 388 | /// let font_funcs: Owned> = FontFuncsImpl::from_trait_impl(); 389 | /// ``` 390 | pub(crate) struct FontFuncsImpl { 391 | raw: NonNull, 392 | marker: PhantomData, 393 | } 394 | 395 | impl FontFuncsImpl { 396 | /// Returns an empty `FontFuncsImpl`. Every font callback of the returned 397 | /// `FontFuncsImpl` gives a null value regardless of its input. 398 | #[allow(unused)] 399 | pub fn empty() -> Shared> { 400 | let raw = unsafe { hb_font_funcs_get_empty() }; 401 | unsafe { Shared::from_raw_ref(raw) } 402 | } 403 | } 404 | 405 | impl FontFuncsImpl { 406 | /// Create a new `FontFuncsImpl` from the `FontFuncs` trait implementation 407 | /// of `T`. 408 | /// 409 | /// # Examples 410 | /// 411 | /// ```ignore 412 | /// use harfbuzz_rs::*; 413 | /// use harfbuzz_rs::font_funcs::FontFuncsImpl; 414 | /// 415 | /// // Dummy struct implementing FontFuncs 416 | /// struct MyFontData { 417 | /// value: i32, 418 | /// } 419 | /// impl FontFuncs for MyFontData { 420 | /// fn get_glyph_h_advance(&self, _: &Font, _: Glyph) -> Position { 421 | /// self.value 422 | /// } 423 | /// // implement other trait functions... 424 | /// } 425 | /// 426 | /// let font_funcs: Owned> = FontFuncsImpl::from_trait_impl(); 427 | /// ``` 428 | /// 429 | pub fn from_trait_impl() -> Owned> { 430 | let mut ffuncs = FontFuncsImpl::new(); 431 | ffuncs.set_trait_impl(); 432 | ffuncs 433 | } 434 | 435 | fn set_trait_impl(&mut self) { 436 | self.set_font_h_extents_func(|font, data| data.get_font_h_extents(font)); 437 | self.set_font_v_extents_func(|font, data| data.get_font_v_extents(font)); 438 | self.set_nominal_glyph_func(|font, data, chr| data.get_nominal_glyph(font, chr)); 439 | self.set_variation_glyph_func(|font, data, chr, var| { 440 | data.get_variation_glyph(font, chr, var) 441 | }); 442 | self.set_glyph_h_advance_func(|font, data, glyph| data.get_glyph_h_advance(font, glyph)); 443 | self.set_glyph_v_advance_func(|font, data, glyph| data.get_glyph_v_advance(font, glyph)); 444 | self.set_glyph_h_origin_func(|font, data, glyph| data.get_glyph_h_origin(font, glyph)); 445 | self.set_glyph_v_origin_func(|font, data, glyph| data.get_glyph_v_origin(font, glyph)); 446 | self.set_glyph_extents_func(|font, data, glyph| data.get_glyph_extents(font, glyph)); 447 | self.set_glyph_contour_point_func(|font, data, glyph, index| { 448 | data.get_glyph_contour_point(font, glyph, index) 449 | }); 450 | self.set_glyph_name_func(|font, data, glyph| data.get_glyph_name(font, glyph)); 451 | self.set_glyph_from_name_func(|font, data, name| data.get_glyph_from_name(font, name)); 452 | } 453 | } 454 | 455 | impl FontFuncsImpl { 456 | pub fn new() -> Owned> { 457 | unsafe { Owned::from_raw(hb_font_funcs_create()) } 458 | } 459 | 460 | pub fn set_font_h_extents_func(&mut self, func: F) 461 | where 462 | F: Fn(&Font<'_>, &T) -> Option, 463 | { 464 | let user_data = Box::new(func); 465 | unsafe { 466 | hb_font_funcs_set_font_h_extents_func( 467 | self.as_raw(), 468 | Some(rust_get_font_extents_closure::), 469 | Box::into_raw(user_data) as *mut _, 470 | Some(destroy_box::), 471 | ); 472 | } 473 | } 474 | 475 | pub fn set_font_v_extents_func(&mut self, func: F) 476 | where 477 | F: Fn(&Font<'_>, &T) -> Option, 478 | { 479 | let user_data = Box::new(func); 480 | unsafe { 481 | hb_font_funcs_set_font_v_extents_func( 482 | self.as_raw(), 483 | Some(rust_get_font_extents_closure::), 484 | Box::into_raw(user_data) as *mut _, 485 | Some(destroy_box::), 486 | ); 487 | } 488 | } 489 | 490 | pub fn set_nominal_glyph_func(&mut self, func: F) 491 | where 492 | F: Fn(&Font<'_>, &T, char) -> Option, 493 | { 494 | let user_data = Box::new(func); 495 | unsafe { 496 | hb_font_funcs_set_nominal_glyph_func( 497 | self.as_raw(), 498 | Some(rust_get_nominal_glyph_closure::), 499 | Box::into_raw(user_data) as *mut _, 500 | Some(destroy_box::), 501 | ); 502 | } 503 | } 504 | 505 | pub fn set_variation_glyph_func(&mut self, func: F) 506 | where 507 | F: Fn(&Font<'_>, &T, char, char) -> Option, 508 | { 509 | let user_data = Box::new(func); 510 | unsafe { 511 | hb_font_funcs_set_variation_glyph_func( 512 | self.as_raw(), 513 | Some(rust_get_variation_glyph_closure::), 514 | Box::into_raw(user_data) as *mut _, 515 | Some(destroy_box::), 516 | ); 517 | } 518 | } 519 | 520 | pub fn set_glyph_h_advance_func(&mut self, func: F) 521 | where 522 | F: Fn(&Font<'_>, &T, Glyph) -> Position, 523 | { 524 | let user_data = Box::new(func); 525 | unsafe { 526 | hb_font_funcs_set_glyph_h_advance_func( 527 | self.as_raw(), 528 | Some(rust_get_glyph_advance_closure::), 529 | Box::into_raw(user_data) as *mut _, 530 | Some(destroy_box::), 531 | ); 532 | } 533 | } 534 | 535 | pub fn set_glyph_v_advance_func(&mut self, func: F) 536 | where 537 | F: Fn(&Font<'_>, &T, Glyph) -> Position, 538 | { 539 | let user_data = Box::new(func); 540 | unsafe { 541 | hb_font_funcs_set_glyph_v_advance_func( 542 | self.as_raw(), 543 | Some(rust_get_glyph_advance_closure::), 544 | Box::into_raw(user_data) as *mut _, 545 | Some(destroy_box::), 546 | ); 547 | } 548 | } 549 | 550 | pub fn set_glyph_h_origin_func(&mut self, func: F) 551 | where 552 | F: Fn(&Font<'_>, &T, Glyph) -> Option<(Position, Position)>, 553 | { 554 | let user_data = Box::new(func); 555 | unsafe { 556 | hb_font_funcs_set_glyph_h_origin_func( 557 | self.as_raw(), 558 | Some(rust_get_glyph_origin_closure::), 559 | Box::into_raw(user_data) as *mut _, 560 | Some(destroy_box::), 561 | ); 562 | } 563 | } 564 | 565 | pub fn set_glyph_v_origin_func(&mut self, func: F) 566 | where 567 | F: Fn(&Font<'_>, &T, Glyph) -> Option<(Position, Position)>, 568 | { 569 | let user_data = Box::new(func); 570 | unsafe { 571 | hb_font_funcs_set_glyph_v_origin_func( 572 | self.as_raw(), 573 | Some(rust_get_glyph_origin_closure::), 574 | Box::into_raw(user_data) as *mut _, 575 | Some(destroy_box::), 576 | ); 577 | } 578 | } 579 | 580 | pub fn set_glyph_extents_func(&mut self, func: F) 581 | where 582 | F: Fn(&Font<'_>, &T, Glyph) -> Option, 583 | { 584 | let user_data = Box::new(func); 585 | unsafe { 586 | hb_font_funcs_set_glyph_extents_func( 587 | self.as_raw(), 588 | Some(rust_get_glyph_extents_closure::), 589 | Box::into_raw(user_data) as *mut _, 590 | Some(destroy_box::), 591 | ); 592 | } 593 | } 594 | 595 | pub fn set_glyph_contour_point_func(&mut self, func: F) 596 | where 597 | F: Fn(&Font<'_>, &T, Glyph, u32) -> Option<(Position, Position)>, 598 | { 599 | let user_data = Box::new(func); 600 | unsafe { 601 | hb_font_funcs_set_glyph_contour_point_func( 602 | self.as_raw(), 603 | Some(rust_get_glyph_contour_point_closure::), 604 | Box::into_raw(user_data) as *mut _, 605 | Some(destroy_box::), 606 | ); 607 | } 608 | } 609 | 610 | pub fn set_glyph_name_func(&mut self, func: F) 611 | where 612 | F: Fn(&Font<'_>, &T, Glyph) -> Option, 613 | { 614 | let user_data = Box::new(func); 615 | unsafe { 616 | hb_font_funcs_set_glyph_name_func( 617 | self.as_raw(), 618 | Some(rust_get_glyph_name_closure::), 619 | Box::into_raw(user_data) as *mut _, 620 | Some(destroy_box::), 621 | ); 622 | } 623 | } 624 | 625 | pub fn set_glyph_from_name_func(&mut self, func: F) 626 | where 627 | F: Fn(&Font<'_>, &T, &str) -> Option, 628 | { 629 | let user_data = Box::new(func); 630 | unsafe { 631 | hb_font_funcs_set_glyph_from_name_func( 632 | self.as_raw(), 633 | Some(rust_get_glyph_from_name_closure::), 634 | Box::into_raw(user_data) as *mut _, 635 | Some(destroy_box::), 636 | ); 637 | } 638 | } 639 | } 640 | 641 | impl fmt::Debug for FontFuncsImpl { 642 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 643 | f.debug_struct("FontFuncsImpl") 644 | .field("raw", &self.as_raw()) 645 | .finish() 646 | } 647 | } 648 | 649 | unsafe impl HarfbuzzObject for FontFuncsImpl { 650 | type Raw = hb_font_funcs_t; 651 | 652 | unsafe fn from_raw(raw: *const Self::Raw) -> Self { 653 | FontFuncsImpl { 654 | raw: NonNull::new(raw as *mut _).unwrap(), 655 | marker: PhantomData, 656 | } 657 | } 658 | 659 | fn as_raw(&self) -> *mut Self::Raw { 660 | self.raw.as_ptr() 661 | } 662 | 663 | unsafe fn reference(&self) { 664 | hb_font_funcs_reference(self.as_raw()); 665 | } 666 | 667 | unsafe fn dereference(&self) { 668 | hb_font_funcs_destroy(self.as_raw()) 669 | } 670 | } 671 | 672 | unsafe impl Send for FontFuncsImpl {} 673 | unsafe impl Sync for FontFuncsImpl {} 674 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! `harfbuzz_rs` is a high-level interface to HarfBuzz, exposing its most important functionality 2 | //! in a safe manner using Rust. 3 | //! 4 | //! # What is HarfBuzz? 5 | //! HarfBuzz is a library for performing complex text layout. It does not perform any drawing. This 6 | //! is quite a low-level operation. If you want to simply draw some text on the screen choose 7 | //! another library. However if you want to build a library for drawing text on some canvas or 8 | //! need a lot of control on advanced text layout then this is the right library to use. 9 | //! 10 | //! # Getting Started 11 | //! 12 | //! To shape a simple string of text you just create a `Font` from a font file, fill a `Buffer` 13 | //! with some text and call the `shape` function. 14 | //! 15 | //! ``` 16 | //! use harfbuzz_rs::*; 17 | //! 18 | //! # fn try_main() -> Result<(), std::io::Error> { 19 | //! 20 | //! let path = "path/to/some/font_file.otf"; 21 | //! let index = 0; //< face index in the font file 22 | //! # let path = "testfiles/SourceSansVariable-Roman.ttf"; 23 | //! let face = Face::from_file(path, index)?; 24 | //! let mut font = Font::new(face); 25 | //! 26 | //! let buffer = UnicodeBuffer::new().add_str("Hello World!"); 27 | //! let output = shape(&font, buffer, &[]); 28 | //! 29 | //! // The results of the shaping operation are stored in the `output` buffer. 30 | //! 31 | //! let positions = output.get_glyph_positions(); 32 | //! let infos = output.get_glyph_infos(); 33 | //! 34 | //! # assert_eq!(positions.len(), 12); 35 | //! assert_eq!(positions.len(), infos.len()); 36 | //! 37 | //! // iterate over the shaped glyphs 38 | //! for (position, info) in positions.iter().zip(infos) { 39 | //! let gid = info.codepoint; 40 | //! let cluster = info.cluster; 41 | //! let x_advance = position.x_advance; 42 | //! let x_offset = position.x_offset; 43 | //! let y_offset = position.y_offset; 44 | //! 45 | //! // Here you would usually draw the glyphs. 46 | //! println!("gid{:?}={:?}@{:?},{:?}+{:?}", gid, cluster, x_advance, x_offset, y_offset); 47 | //! } 48 | //! 49 | //! # Ok(()) 50 | //! # } 51 | //! # 52 | //! # try_main().unwrap(); 53 | //! ``` 54 | //! This should print out something similar to the following: 55 | //! 56 | //! ```text 57 | //! gid41=0@741,0+0 58 | //! gid70=1@421,0+0 59 | //! gid77=2@258,0+0 60 | //! gid77=3@253,0+0 61 | //! gid80=4@510,0+0 62 | //! gid1=5@227,0+0 63 | //! gid56=6@874,0+0 64 | //! gid80=7@498,0+0 65 | //! gid83=8@367,0+0 66 | //! gid77=9@253,0+0 67 | //! gid69=10@528,0+0 68 | //! gid2=11@276,0+0 69 | //! ``` 70 | #![deny(missing_debug_implementations)] 71 | 72 | /// Reexported `harfbuzz_sys` crate to directly access the C API whenever no 73 | /// adequate wrapper is provided. 74 | // This will hopefully not cause backwards compability concerns since harfbuzz 75 | // tries to be backwards compatible. 76 | #[macro_use] 77 | extern crate bitflags; 78 | 79 | #[allow(non_camel_case_types)] 80 | #[allow(non_snake_case)] 81 | #[allow(non_upper_case_globals)] 82 | #[allow(deref_nullptr)] 83 | #[allow(dead_code)] 84 | mod bindings; 85 | mod blob; 86 | mod buffer; 87 | mod common; 88 | pub mod draw_funcs; 89 | mod face; 90 | mod font; 91 | pub mod font_funcs; 92 | 93 | #[cfg(feature = "rusttype")] 94 | pub mod rusttype; 95 | 96 | use bindings::hb_feature_t; 97 | use bindings::hb_shape; 98 | use bindings::hb_variation_t; 99 | 100 | pub use crate::blob::*; 101 | pub use crate::buffer::*; 102 | pub use crate::common::*; 103 | pub use crate::face::*; 104 | pub use crate::font::*; 105 | 106 | use std::ops::{Bound, RangeBounds}; 107 | use std::os::raw::c_uint; 108 | 109 | pub(crate) fn start_end_range(range: impl RangeBounds) -> (c_uint, c_uint) { 110 | // We have to do careful bounds checking since c_uint may be of 111 | // different sizes on different platforms. We do assume that 112 | // sizeof(usize) >= sizeof(c_uint). 113 | const MAX_UINT: usize = c_uint::MAX as usize; 114 | let start = match range.start_bound() { 115 | Bound::Included(&included) => included.min(MAX_UINT) as c_uint, 116 | Bound::Excluded(&excluded) => excluded.min(MAX_UINT - 1) as c_uint + 1, 117 | Bound::Unbounded => 0, 118 | }; 119 | let end = match range.end_bound() { 120 | Bound::Included(&included) => included.saturating_add(1).min(MAX_UINT) as c_uint, 121 | Bound::Excluded(&excluded) => excluded.min(MAX_UINT) as c_uint, 122 | Bound::Unbounded => c_uint::MAX, 123 | }; 124 | (start, end) 125 | } 126 | 127 | /// A variation selector which can be applied to a specific font. 128 | /// 129 | /// To use OpenType variations when shaping see the documentation of [`Font`]. 130 | /// 131 | /// # Fields 132 | /// 133 | /// - `tag`: The OpenType tag of the variation. 134 | /// - `value`: Some OpenType variant accept different values to change 135 | /// their behaviour. 136 | #[derive(Debug, Copy, Clone)] 137 | #[repr(transparent)] 138 | pub struct Variation(hb_variation_t); 139 | 140 | impl Variation { 141 | /// Create a new Variation with provided `tag` and `value`. 142 | /// 143 | /// # Examples 144 | /// 145 | /// ``` 146 | /// use harfbuzz_rs::Variation; 147 | /// Variation::new(b"wght", 800.0); 148 | /// ``` 149 | pub fn new(tag: impl Into, value: f32) -> Variation { 150 | Variation(hb_variation_t { 151 | tag: tag.into().0, 152 | value, 153 | }) 154 | } 155 | 156 | /// Returns the `tag` of the variation. 157 | pub fn tag(&self) -> Tag { 158 | Tag(self.0.tag) 159 | } 160 | 161 | /// Returns the value of the variation. 162 | pub fn value(&self) -> f32 { 163 | self.0.value 164 | } 165 | } 166 | 167 | /// A feature tag with an accompanying range specifying on which subslice of 168 | /// `shape`s input it should be applied. 169 | /// 170 | /// You can pass a slice of `Feature`s to `shape` that will be activated for the 171 | /// corresponding slices of input. 172 | /// 173 | /// # Examples 174 | /// 175 | /// Shape some text using the `calt` (Contextual Alternatives) feature. 176 | /// 177 | /// ``` 178 | /// use harfbuzz_rs::{Face, Font, UnicodeBuffer, shape, Feature, Tag}; 179 | /// 180 | /// let path = "testfiles/SourceSansVariable-Roman.ttf"; 181 | /// let face = Face::from_file(path, 0).expect("could not load face"); 182 | /// let font = Font::new(face); 183 | /// 184 | /// let buffer = UnicodeBuffer::new().add_str("Hello World!"); 185 | /// 186 | /// // contextual alternatives feature 187 | /// let feature_tag = b"calt"; 188 | /// 189 | /// // use the feature on the entire input 190 | /// let feature_range = 0..; 191 | /// let feature = Feature::new(feature_tag, 0, feature_range); 192 | /// 193 | /// let output = shape(&font, buffer, &[feature]); 194 | /// ``` 195 | #[derive(Debug, Copy, Clone)] 196 | #[repr(transparent)] 197 | pub struct Feature(hb_feature_t); 198 | 199 | impl Feature { 200 | /// Create a new `Feature` struct. 201 | /// 202 | /// The feature will be applied with the given value to all glyphs which are 203 | /// in clusters contained in `range`. 204 | /// 205 | /// # Arguments 206 | /// 207 | /// - `tag`: The OpenType feature tag to use. 208 | /// - `value`: Some OpenType features accept different values to change 209 | /// their behaviour. 210 | /// - `range`: The cluster range that should be affected by this feature. 211 | pub fn new(tag: impl Into, value: u32, range: impl RangeBounds) -> Feature { 212 | let (start, end) = start_end_range(range); 213 | Feature(hb_feature_t { 214 | tag: tag.into().0, 215 | value, 216 | start, 217 | end, 218 | }) 219 | } 220 | 221 | pub fn tag(&self) -> Tag { 222 | Tag(self.0.tag) 223 | } 224 | 225 | pub fn value(&self) -> u32 { 226 | self.0.value 227 | } 228 | 229 | pub fn start(&self) -> usize { 230 | self.0.start as usize 231 | } 232 | 233 | pub fn end(&self) -> usize { 234 | self.0.end as usize 235 | } 236 | } 237 | 238 | /// Shape the contents of the buffer using the provided font and activating all 239 | /// OpenType features given in `features`. 240 | /// 241 | /// This function consumes the `buffer` and returns a `GlyphBuffer` containing 242 | /// the resulting glyph indices and the corresponding positioning information. 243 | /// Once all the information from the `GlyphBuffer` has been processed as 244 | /// necessary you can reuse the `GlyphBuffer` as an `UnicodeBuffer` (using 245 | /// `GlyphBuffer::clear_contents`) and use that to call `shape` again with new 246 | /// data. 247 | /// 248 | /// By default some basic OpenType features are enabled according to the 249 | /// language and the script set in the buffer. 250 | /// 251 | /// # Arguments 252 | /// - `font` – a reference to the harfbuzz font used to shape the text. 253 | /// - `buffer` – a `UnicodeBuffer` that is filled with the text to be shaped and 254 | /// also contains metadata about the text in the form of segment properties. 255 | /// - `features` – a slice of additional features to activate 256 | pub fn shape(font: &Font<'_>, buffer: UnicodeBuffer, features: &[Feature]) -> GlyphBuffer { 257 | let buffer = buffer.guess_segment_properties(); 258 | unsafe { 259 | hb_shape( 260 | font.as_raw(), 261 | buffer.0.as_raw(), 262 | features.as_ptr() as *mut _, 263 | features.len() as u32, 264 | ) 265 | }; 266 | GlyphBuffer(buffer.0) 267 | } 268 | 269 | #[cfg(test)] 270 | mod tests { 271 | use std::mem::{align_of, size_of}; 272 | 273 | pub(crate) fn assert_memory_layout_equal() { 274 | assert_eq!(size_of::(), size_of::()); 275 | assert_eq!(align_of::(), align_of::()); 276 | } 277 | 278 | #[test] 279 | fn it_works() {} 280 | 281 | fn assert_feature(feat: Feature, tag: Tag, value: u32, start: usize, end: usize) { 282 | assert_eq!(feat.tag(), tag); 283 | assert_eq!(feat.value(), value); 284 | assert_eq!(feat.start(), start); 285 | assert_eq!(feat.end(), end); 286 | } 287 | 288 | use super::{Feature, Tag}; 289 | #[test] 290 | fn feature_new() { 291 | let tag = b"abcd".into(); 292 | const UINT_MAX: usize = std::os::raw::c_uint::MAX as usize; 293 | 294 | let feature = Feature::new(tag, 100, 2..100); 295 | assert_feature(feature, tag, 100, 2, 100); 296 | 297 | let feature = Feature::new(tag, 100, 2..=100); 298 | assert_feature(feature, tag, 100, 2, 101); 299 | 300 | let feature = Feature::new(tag, 100, 2..); 301 | assert_feature(feature, tag, 100, 2, UINT_MAX); 302 | 303 | let feature = Feature::new(tag, 100, ..100); 304 | assert_feature(feature, tag, 100, 0, 100); 305 | 306 | let feature = Feature::new(tag, 100, ..=100); 307 | assert_feature(feature, tag, 100, 0, 101); 308 | 309 | let feature = Feature::new(tag, 100, ..); 310 | assert_feature(feature, tag, 100, 0, UINT_MAX); 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /src/rusttype.rs: -------------------------------------------------------------------------------- 1 | //! This module allows you to use rusttype to provide the font operations that harfbuzz needs. 2 | 3 | use crate::common::Tag; 4 | use rusttype::Font as RTFont; 5 | use rusttype::{GlyphId, Scale}; 6 | 7 | use crate::face; 8 | use crate::font; 9 | use crate::font::{Font, FontFuncs, Glyph as GlyphIndex, GlyphExtents, Position}; 10 | 11 | use std; 12 | use std::fmt::Debug; 13 | use std::str::FromStr; 14 | 15 | // Work around weird rusttype scaling by reading the hhea table. 16 | fn get_font_height(font: &font::Font<'_>) -> Option { 17 | let face = font.face(); 18 | let tag = Tag::from_str("hhea").unwrap(); 19 | let hhea_table = face.table_with_tag(tag)?; 20 | if hhea_table.len() >= 8 { 21 | unsafe { 22 | let ascent_ptr = (&hhea_table)[4..6].as_ptr() as *const i16; 23 | let ascent = i16::from_be(*ascent_ptr); 24 | let descent_ptr = (&hhea_table)[6..8].as_ptr() as *const i16; 25 | let descent = i16::from_be(*descent_ptr); 26 | Some(ascent as i32 - descent as i32) 27 | } 28 | } else { 29 | None 30 | } 31 | } 32 | 33 | fn rusttype_font_from_face<'a>(face: &face::Face<'a>) -> Option> { 34 | // It is unfortunate that we have to copy the face data here. 35 | let font_blob = face.face_data().as_ref().to_owned(); 36 | let index = face.index(); 37 | RTFont::try_from_vec_and_index(font_blob.to_owned(), index) 38 | } 39 | 40 | fn rusttype_scale_from_hb_font(font: &font::Font<'_>) -> Option { 41 | let font_height = get_font_height(font)? as f32; 42 | let em_scale = font.scale(); 43 | let x_scale = em_scale.0 as f32; 44 | let y_scale = em_scale.1 as f32; 45 | Some(Scale { 46 | x: font_height * x_scale / y_scale, 47 | y: font_height, 48 | }) 49 | } 50 | 51 | struct ScaledRusttypeFont<'a> { 52 | font: rusttype::Font<'a>, 53 | scale: Scale, 54 | } 55 | 56 | impl<'a> Debug for ScaledRusttypeFont<'a> { 57 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 58 | f.debug_struct("ScaledRusttypeFont") 59 | .field("scale", &self.scale) 60 | .finish() 61 | } 62 | } 63 | 64 | impl<'a> ScaledRusttypeFont<'a> { 65 | fn from_hb_font<'b>(hb_font: &font::Font<'b>) -> Option> { 66 | let font = rusttype_font_from_face(&hb_font.face())?; 67 | let scale = rusttype_scale_from_hb_font(hb_font)?; 68 | Some(ScaledRusttypeFont { font, scale }) 69 | } 70 | } 71 | 72 | impl<'a> FontFuncs for ScaledRusttypeFont<'a> { 73 | fn get_glyph_h_advance(&self, _: &Font<'_>, glyph: GlyphIndex) -> Position { 74 | let glyph = self.font.glyph(GlyphId(glyph as _)); 75 | let glyph = glyph.scaled(self.scale); 76 | glyph.h_metrics().advance_width.round() as Position 77 | } 78 | fn get_glyph_extents(&self, _: &Font<'_>, glyph: GlyphIndex) -> Option { 79 | let glyph = self.font.glyph(GlyphId(glyph as _)); 80 | let glyph = glyph.scaled(self.scale); 81 | glyph.exact_bounding_box().map(|bbox| GlyphExtents { 82 | x_bearing: bbox.min.x.round() as i32, 83 | y_bearing: bbox.min.y.round() as i32, 84 | width: (bbox.max.x - bbox.min.x).round() as i32, 85 | height: (bbox.max.y - bbox.min.y).round() as i32, 86 | }) 87 | } 88 | fn get_nominal_glyph(&self, _: &font::Font<'_>, unicode: char) -> Option { 89 | let glyph = self.font.glyph(unicode); 90 | Some(glyph.id().0 as GlyphIndex) 91 | } 92 | } 93 | 94 | use std::sync::Arc; 95 | 96 | /// Creates a new HarfBuzz `Font` object that uses RustType to provide font data. 97 | /// 98 | /// # Examples 99 | /// 100 | /// Create a basic font that uses rusttype font funcs: 101 | /// ``` 102 | /// use std::fs; 103 | /// use std::sync::Arc; 104 | /// 105 | /// use harfbuzz_rs::rusttype::create_harfbuzz_rusttype_font; 106 | /// 107 | /// let path = "testfiles/SourceSansVariable-Roman.ttf"; 108 | /// let bytes: Arc<[u8]> = fs::read(path).unwrap().into(); 109 | /// let font = create_harfbuzz_rusttype_font(bytes, 0); 110 | /// ``` 111 | pub fn create_harfbuzz_rusttype_font( 112 | bytes: impl Into>, 113 | index: u32, 114 | ) -> Option>> { 115 | let bytes: Arc<[u8]> = bytes.into(); 116 | let face = crate::Face::new(bytes.clone(), index); 117 | let mut font = Font::new(face); 118 | 119 | let rt_font = rusttype::Font::try_from_vec_and_index((&*bytes).to_owned(), index)?; 120 | let scaled_font = ScaledRusttypeFont { 121 | font: rt_font, 122 | scale: rusttype_scale_from_hb_font(&font)?, 123 | }; 124 | font.set_font_funcs(scaled_font); 125 | 126 | Some(font) 127 | } 128 | 129 | /// Extends the harfbuzz font to allow setting RustType as font funcs provider. 130 | #[deprecated(since = "0.4.0")] 131 | pub trait SetRustTypeFuncs { 132 | /// Let a font use rusttype's font API for getting information like the 133 | /// advance width of some glyph or its extents. 134 | /// 135 | /// # Deprecated 136 | /// 137 | /// This function is deprecated because it doesn't fit well with the design 138 | /// of RustType (Calling this method requires to make a copy of the font 139 | /// data used). You should use `create_harfbuzz_rusttype_font` instead. 140 | #[deprecated(since = "0.4.0")] 141 | fn set_rusttype_funcs(&mut self) -> Option<()>; 142 | } 143 | 144 | #[allow(deprecated)] 145 | impl<'a> SetRustTypeFuncs for Font<'a> { 146 | fn set_rusttype_funcs(&mut self) -> Option<()> { 147 | let font_data = ScaledRusttypeFont::from_hb_font(self)?; 148 | self.set_font_funcs(font_data); 149 | Some(()) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /testfiles/SourceSansVariable-Roman.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/harfbuzz_rs/30f0fec1744bb320ac554f9f62831b1ce1d2985d/testfiles/SourceSansVariable-Roman.ttf -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION="10.0.0" 4 | 5 | pushd harfbuzz 6 | git fetch 7 | git checkout $VERSION 8 | popd 9 | 10 | bindgen --no-prepend-enum-name --allowlist-function hb_.\* --allowlist-type hb_.\* wrapper.h > src/bindings.rs 11 | -------------------------------------------------------------------------------- /wrapper.h: -------------------------------------------------------------------------------- 1 | #include "harfbuzz/src/hb.h" 2 | #include "harfbuzz/src/hb-ot.h" 3 | #include "harfbuzz/src/hb-aat.h" 4 | --------------------------------------------------------------------------------