harfbuzz_rs-1.2.0/.cargo_vcs_info.json0000644000000001121371326442100134660ustar00{ "git": { "sha1": "335ace920759f2dc2f43ed954d4d18fe1b1a8245" } } harfbuzz_rs-1.2.0/.gitignore010064400007650000024000000000221322440216600142520ustar0000000000000000target Cargo.lock harfbuzz_rs-1.2.0/.travis.yml010064400007650000024000000002401341520537100143760ustar0000000000000000language: rust script: - cargo test --verbose --all-features rust: - stable - beta - nightly matrix: allow_failures: - rust: nightly cache: cargo harfbuzz_rs-1.2.0/CHANGELOG.md010064400007650000024000000073751371326350400141210ustar0000000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [1.2.0] 2020-08-07 ### Fixed - 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. ## [1.1.2] 2020-07-13 ### Added - Reexport cargo features from `harfbuzz-sys` (thanks zenixls2) - Add new API to append `UnicodeBuffer`s to existing buffers - Add new API `with_bytes_owned` for `Blob` to allow creating blobs from `Arc>` and the like ### Fixed - Off-by-one error in cluster ranges for features ## [1.1.1] 2020-04-29 ### Fixed - The project now builds again with rusttype < 0.9 ## [1.1.0] 2020-04-29 This release does not build. ### Changed - Updated dependencies (`harfbuzz-sys` and the optional dep. `rusttype`) ## [1.0.1] 2020-01-18 ### Fixed - Builds on Android now [#20](https://github.com/manuel-rhdt/harfbuzz_rs/pull/20) - Memory leak on face creation - Memory leak on font creation [#22](https://github.com/manuel-rhdt/harfbuzz_rs/pull/22) - BufferSerializer now correctly serializes a single glyph [#23](https://github.com/manuel-rhdt/harfbuzz_rs/issues/23) ## [1.0] 2019-01-31 This is the 1.0 release of harfbuzz_rs. There are still many API's left to be covered by this crate however I think the current API should be able to remain stable. ### Added - `Face::empty` constructor as a simple way to construct the empty face - `Feature` struct that wraps `hb_feature_t` and has an easy to use constructor - `UnicodeBuffer::add_str_item` to allow providing context to the string being shaped. - `UnicodeBuffer::preallocate` - Reexport of `harfbuzz_sys` as `hb` to facilitate use of unwrapped functions ### Changed - removed kerning callbacks from FontFuncs (following the upstream harfbuzz change). This also enabled updating to harfbuzz-sys 0.3. - updated to use Rust 2018 - Further improved docs ## [0.4.0] 2019-01-08 ### Added - constructor for `Blob` from a mutable slice - `Font::empty` as a simple way to construct the empty font - support for serializing a `GlyphBuffer`'s contents - `create_harfbuzz_rusttype_font`: a new way to create a font with Rusttype font funcs (the old `SetRustTypeFuncs` trait is deprecated) ### Fixed - lifetime of slice returned by `Blob::get_data` (could cause UB) ### Changed - The rustup feature is no longer enabled by default - `SetRustTypeFuncs` is now deprecated in favor of `create_harfbuzz_rusttype_font` - Internal representation of smart pointers (possibly more safe now) - Many improvements to documentation ## [0.3.0] 2018-08-26 ### Fixed - `Font::parent` now returns an option - `HarfbuzzObject` becomes unsafe to implement ### Changed - Smart pointers to use `NonNull` ## [0.2.0] 2018-05-01 ### Added - A new enum called TypedBuffer. It contains either a UnicodeBuffer or a GlyphBuffer. This makes reusing hb_buffer_t objects from foreign code possible. - UnicodeBuffer methods to return its contents - `from_bytes` function for `Face` ### Fixed - `Font::set_funcs` adds necessary `Send` and `Sync` bounds - UnicodeBuffer and GlyphBuffer no longer implement Clone (as they are mutable) - ### Changed - Naming: `HbArc` to `Shared` and `HbBox` to `Owned` - internal representation of `Shared` and `Owned` - `Shared::into_raw` and `Owned::into_raw` into static methods - Various improvements to documentation - `shape` becomes a free standing function (it was a method on `UnicodeBuffer`) ## [0.1.0] 2018-01-11 Initial Release harfbuzz_rs-1.2.0/Cargo.lock0000644000000206751371326442100114610ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "approx" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "autocfg" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cc" version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cmake" version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-foundation" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.74 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-foundation-sys" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "core-graphics" version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.74 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-text" version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.74 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "freetype" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "freetype-sys 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.74 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "freetype-sys" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cmake 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.74 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "harfbuzz-sys" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)", "core-text 15.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "harfbuzz_rs" version = "1.2.0" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "harfbuzz-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rusttype 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libc" version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num-traits" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ordered-float" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pkg-config" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rusttype" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "stb_truetype 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "stb_truetype" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" "checksum cc 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)" = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" "checksum cmake 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb" "checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" "checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" "checksum core-graphics 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" "checksum core-text 15.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "131b3fd1f8bd5db9f2b398fa4fdb6008c64afc04d447c306ac2c7e98fba2a61d" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum freetype 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b73222ab32d9ad65fe0e1c3258da8d614fd47cf19fce92b09eb520060c5c5ad5" "checksum freetype-sys 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d48ac0ce366dd47a115ec8e598d7c51b4a974fc52ded5e53a56b31f55f34f3ea" "checksum harfbuzz-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "845f3e65ec4e30b0b1b6e1c055900871f3776d3492cc76744e3fc5943a6129a9" "checksum libc 0.2.74 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" "checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" "checksum ordered-float 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3741934be594d77de1c8461ebcbbe866f585ea616a9753aa78f2bdc69f0e4579" "checksum pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" "checksum rusttype 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f61411055101f7b60ecf1041d87fb74205fb20b0c7a723f07ef39174cf6b4c0" "checksum stb_truetype 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f77b6b07e862c66a9f3e62a07588fee67cd90a9135a2b942409f195507b4fb51" harfbuzz_rs-1.2.0/Cargo.toml0000644000000030541371326442100114740ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "harfbuzz_rs" version = "1.2.0" authors = ["Manuel Reinhardt "] description = "A high-level interface to HarfBuzz, exposing its most important functionality in a safe manner using Rust." readme = "README.md" keywords = ["text", "ffi", "textlayout", "shaping", "harfbuzz"] categories = ["api-bindings", "external-ffi-bindings", "internationalization", "text-processing"] license = "MIT" repository = "https://github.com/manuel-rhdt/harfbuzz_rs" [dependencies.bitflags] version = "^1" [dependencies.harfbuzz-sys] version = ">= 0.3, <0.5" default-features = false [dependencies.rusttype] version = ">= 0.7, <0.9" optional = true [features] build-native-freetype = ["harfbuzz-sys/build-native-freetype"] build-native-harfbuzz = ["harfbuzz-sys/build-native-harfbuzz"] default = ["build-native-harfbuzz", "build-native-freetype"] [badges.appveyor] branch = "master" repository = "manuel-rhdt/harfbuzz_rs" service = "github" [badges.travis-ci] branch = "master" repository = "manuel-rhdt/harfbuzz_rs" harfbuzz_rs-1.2.0/Cargo.toml.orig0000644000000020361371326442100124320ustar00[package] name = "harfbuzz_rs" version = "1.2.0" authors = ["Manuel Reinhardt "] description = "A high-level interface to HarfBuzz, exposing its most important functionality in a safe manner using Rust." repository = "https://github.com/manuel-rhdt/harfbuzz_rs" readme = "README.md" keywords = ["text", "ffi", "textlayout", "shaping", "harfbuzz"] categories = [ "api-bindings", "external-ffi-bindings", "internationalization", "text-processing" ] license = "MIT" edition = "2018" [badges] travis-ci = { repository = "manuel-rhdt/harfbuzz_rs", branch = "master" } appveyor = { repository = "manuel-rhdt/harfbuzz_rs", branch = "master", service = "github" } [features] default = ["build-native-harfbuzz", "build-native-freetype"] build-native-harfbuzz = ["harfbuzz-sys/build-native-harfbuzz"] build-native-freetype = ["harfbuzz-sys/build-native-freetype"] [dependencies] harfbuzz-sys = { version = ">= 0.3, <0.5", default-features = false } rusttype = { version = ">= 0.7, <0.9", optional = true } bitflags = "^1" harfbuzz_rs-1.2.0/Cargo.toml.orig010064400007650000024000000020361371326442000151620ustar0000000000000000[package] name = "harfbuzz_rs" version = "1.2.0" authors = ["Manuel Reinhardt "] description = "A high-level interface to HarfBuzz, exposing its most important functionality in a safe manner using Rust." repository = "https://github.com/manuel-rhdt/harfbuzz_rs" readme = "README.md" keywords = ["text", "ffi", "textlayout", "shaping", "harfbuzz"] categories = [ "api-bindings", "external-ffi-bindings", "internationalization", "text-processing" ] license = "MIT" edition = "2018" [badges] travis-ci = { repository = "manuel-rhdt/harfbuzz_rs", branch = "master" } appveyor = { repository = "manuel-rhdt/harfbuzz_rs", branch = "master", service = "github" } [features] default = ["build-native-harfbuzz", "build-native-freetype"] build-native-harfbuzz = ["harfbuzz-sys/build-native-harfbuzz"] build-native-freetype = ["harfbuzz-sys/build-native-freetype"] [dependencies] harfbuzz-sys = { version = ">= 0.3, <0.5", default-features = false } rusttype = { version = ">= 0.7, <0.9", optional = true } bitflags = "^1" harfbuzz_rs-1.2.0/LICENSE010064400007650000024000000020601322440216600132730ustar0000000000000000MIT License Copyright (c) 2018 Manuel Reinhardt 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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.harfbuzz_rs-1.2.0/README.md010064400007650000024000000051231354740077600135650ustar0000000000000000# harfbuzz_rs [![Crates.io](https://img.shields.io/crates/v/harfbuzz_rs.svg)](https://crates.io/crates/harfbuzz_rs) [![Documentation](https://docs.rs/harfbuzz_rs/badge.svg)](https://docs.rs/harfbuzz_rs) [![Build Status](https://travis-ci.org/manuel-rhdt/harfbuzz_rs.svg?branch=master)](https://travis-ci.org/manuel-rhdt/harfbuzz_rs) [![Build status](https://ci.appveyor.com/api/projects/status/tg2xpx3am2iw7nxr?svg=true)](https://ci.appveyor.com/project/manuel-rhdt/harfbuzz-rs) `harfbuzz_rs` is a high-level interface to HarfBuzz, exposing its most important functionality in a safe manner using Rust. # What is HarfBuzz? HarfBuzz is a library for performing complex text layout. It does not perform any drawing. This is quite a low-level operation. If you want to simply draw some text on the screen you should maybe choose another more high-level library. However if you want to build a library for drawing text on some canvas or need a lot of control on advanced text layout then this is the right library to use. # Getting Started To shape a simple string of text you just create a `Font` from a font file, fill a `Buffer` with some text and call the `shape` function. ```rust use harfbuzz_rs::*; let path = "path/to/some/font_file.otf"; let index = 0; //< face index in the font file let face = Face::from_file(path, index)?; let mut font = Font::new(face); let buffer = UnicodeBuffer::new().add_str("Hello World!"); let output = shape(&font, buffer, &[]); // The results of the shaping operation are stored in the `output` buffer. let positions = output.get_glyph_positions(); let infos = output.get_glyph_infos(); // iterate over the shaped glyphs for (position, info) in positions.iter().zip(infos) { let gid = info.codepoint; let cluster = info.cluster; let x_advance = position.x_advance; let x_offset = position.x_offset; let y_offset = position.y_offset; // Here you would usually draw the glyphs. println!("gid{:?}={:?}@{:?},{:?}+{:?}", gid, cluster, x_advance, x_offset, y_offset); } ``` This should print out something similar to the following: ```text gid41=0@741,0+0 gid70=1@421,0+0 gid77=2@258,0+0 gid77=3@253,0+0 gid80=4@510,0+0 gid1=5@227,0+0 gid56=6@874,0+0 gid80=7@498,0+0 gid83=8@367,0+0 gid77=9@253,0+0 gid69=10@528,0+0 gid2=11@276,0+0 ``` # Supported HarfBuzz versions This crate is tested to work with harfbuzz versions 2.0 and higher. Older versions may work as well but not sure. I recommend statically linking the harfbuzz library provided by the `harfbuzz-sys` crate. # Optional Features If you want to use rusttype as font functions enable the `rusttype` feature. harfbuzz_rs-1.2.0/appveyor.yml010064400007650000024000000064751341520541700146760ustar0000000000000000# Appveyor configuration template for Rust using rustup for Rust installation # https://github.com/starkat99/appveyor-rust ## Operating System (VM environment) ## # Rust needs at least Visual Studio 2013 Appveyor OS for MSVC targets. os: Visual Studio 2015 ## Build Matrix ## # This configuration will setup a build for each channel & target combination (12 windows # combinations in all). # # There are 3 channels: stable, beta, and nightly. # # Alternatively, the full version may be specified for the channel to build using that specific # version (e.g. channel: 1.5.0) # # The values for target are the set of windows Rust build targets. Each value is of the form # # ARCH-pc-windows-TOOLCHAIN # # Where ARCH is the target architecture, either x86_64 or i686, and TOOLCHAIN is the linker # toolchain to use, either msvc or gnu. See https://www.rust-lang.org/downloads.html#win-foot for # a description of the toolchain differences. # See https://github.com/rust-lang-nursery/rustup.rs/#toolchain-specification for description of # toolchains and host triples. # # Comment out channel/target combos you do not wish to build in CI. # # You may use the `cargoflags` and `RUSTFLAGS` variables to set additional flags for cargo commands # and rustc, respectively. For instance, you can uncomment the cargoflags lines in the nightly # channels to enable unstable features when building for nightly. Or you could add additional # matrix entries to test different combinations of features. environment: matrix: ### MSVC Toolchains ### # Stable 64-bit MSVC - channel: stable target: x86_64-pc-windows-msvc # Beta 64-bit MSVC - channel: beta target: x86_64-pc-windows-msvc # Nightly 64-bit MSVC - channel: nightly target: x86_64-pc-windows-msvc #cargoflags: --features "unstable" ### Allowed failures ### # See Appveyor documentation for specific details. In short, place any channel or targets you wish # to allow build failures on (usually nightly at least is a wise choice). This will prevent a build # or test failure in the matching channels/targets from failing the entire build. matrix: allow_failures: - channel: nightly # If you only care about stable channel build failures, uncomment the following line: #- channel: beta ## Install Script ## # This is the most important part of the Appveyor configuration. This installs the version of Rust # specified by the 'channel' and 'target' environment variables from the build matrix. This uses # rustup to install Rust. # # For simple configurations, instead of using the build matrix, you can simply set the # default-toolchain and default-host manually here. install: - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe - rustup-init -yv --default-toolchain %channel% --default-host %target% - set PATH=%PATH%;%USERPROFILE%\.cargo\bin - rustc -vV - cargo -vV ## Build Script ## # 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents # the "directory does not contain a project or solution file" error. build: false # Uses 'cargo test' to run tests and build. Alternatively, the project may call compiled programs #directly or perform other testing commands. Rust will automatically be placed in the PATH # environment variable. test_script: - cargo test --verbose --all-features %cargoflags% harfbuzz_rs-1.2.0/examples/simple.rs010064400007650000024000000025031361057304000157440ustar0000000000000000use harfbuzz_rs::{shape, Face, Font, UnicodeBuffer}; // Execute this file from the root directory of this repository. // // The output should look like the following: // gid09=00@ 634,0+0 // gid32=01@ 478,0+0 // gid39=02@ 230,0+0 // gid39=03@ 230,0+0 // gid42=04@ 520,0+0 // gid01=05@ 200,0+0 // gid24=06@ 764,0+0 // gid42=07@ 532,0+0 // gid45=08@ 306,0+0 // gid39=09@ 230,0+0 // gid31=10@ 540,0+0 // gid1146=11@ 248,0+0 fn main() { let index = 0; let path = "testfiles/SourceSansVariable-Roman.ttf"; let face = Face::from_file(path, index).expect("Error reading font file."); let font = Font::new(face); // Create a buffer with some text, shape it... let buffer = UnicodeBuffer::new().add_str("Hello World!"); let result = shape(&font, buffer, &[]); // ... and get the results. let positions = result.get_glyph_positions(); let infos = result.get_glyph_infos(); // iterate over the shaped glyphs for (position, info) in positions.iter().zip(infos) { let gid = info.codepoint; let cluster = info.cluster; let x_advance = position.x_advance; let x_offset = position.x_offset; let y_offset = position.y_offset; println!( "gid{:0>2?}={:0>2?}@{:>4?},{:?}+{:?}", gid, cluster, x_advance, x_offset, y_offset ); } } harfbuzz_rs-1.2.0/src/blob.rs010064400007650000024000000207001370277351200143510ustar0000000000000000use crate::hb; use std::os::raw::c_void; use std; use std::marker::PhantomData; use std::fmt; use std::fs; use std::path::Path; use std::ptr::NonNull; use crate::common::{HarfbuzzObject, Owned, Shared}; /// A `Blob` manages raw data like e.g. file contents. It refers to a slice of /// bytes that can be either owned by the `Blob` or not. /// /// It is used to provide access to memory for `Face` and `Font`. Typically it /// contains the raw font data (e.g. an entire font file or font tables). To enable /// shared usage of its data it uses a reference counting mechanism making the /// `clone` operation very cheap as no data is cloned. /// /// # Construction /// /// A `Blob` implements `Into` for every type that satisfies the `AsRef<[u8]>` /// trait such as `Vec` and `Box<[u8]>` so owned blobs can be created easily /// from standard Rust objects. /// /// You can also create `Blob`s that contain borrowed data using the constructors /// `Blob::with_bytes` and `Blob::with_bytes_mut` for immutable and mutable access /// respectively. #[repr(C)] pub struct Blob<'a> { raw: NonNull, marker: PhantomData<&'a [u8]>, } impl<'a> Blob<'a> { /// Create a new `Blob` from the slice `bytes`. The blob will not own the /// slice's data. pub fn with_bytes(bytes: &'a [u8]) -> Owned> { let hb_blob = unsafe { hb::hb_blob_create( bytes.as_ptr() as *const _, bytes.len() as u32, hb::HB_MEMORY_MODE_READONLY, std::ptr::null_mut(), None, ) }; unsafe { Owned::from_raw(hb_blob) } } /// Create a new `Blob` from the mutable slice `bytes`. The blob will not own the /// slice's data. pub fn with_bytes_mut(bytes: &'a mut [u8]) -> Owned> { let hb_blob = unsafe { hb::hb_blob_create( bytes.as_ptr() as *const _, bytes.len() as u32, hb::HB_MEMORY_MODE_WRITABLE, std::ptr::null_mut(), None, ) }; unsafe { Owned::from_raw(hb_blob) } } /// Create a new `Blob` from a type that owns a byte slice, effectively handing over /// ownership of its data to the blob. pub fn with_bytes_owned( bytes_owner: T, projector: impl Fn(&T) -> &[u8], ) -> Owned> { let boxxed = Box::new(bytes_owner); let slice = projector(&boxxed); let len = slice.len(); let ptr = slice.as_ptr(); let data = Box::into_raw(boxxed); extern "C" fn destroy(ptr: *mut c_void) { unsafe { Box::from_raw(ptr as *mut U) }; } let hb_blob = unsafe { hb::hb_blob_create( ptr as *const _, len as u32, hb::HB_MEMORY_MODE_READONLY, data as *mut _, Some(destroy::), ) }; unsafe { Owned::from_raw(hb_blob) } } /// Create a `Blob` from the contents of the file at `path` whose contents /// will be read into memory. /// /// The result will be either a `Blob` that owns the file's contents or an /// error that happened while trying to read the file. /// /// This can be a performance problem if the file is very big. If this turns /// out to be a problem consider `mmap`ing the file or splitting it into /// smaller chunks before creating a `Blob`. pub fn from_file>(path: P) -> std::io::Result>> { let vec = fs::read(path)?; Ok(vec.into()) } /// Get a slice of the `Blob`'s bytes. pub fn get_data(&self) -> &[u8] { unsafe { let mut length = hb::hb_blob_get_length(self.as_raw()); let data_ptr = hb::hb_blob_get_data(self.as_raw(), &mut length as *mut _); std::slice::from_raw_parts(data_ptr as *const u8, length as usize) } } /// Creates an immutable `Blob` that contains part of the data of the parent /// `Blob`. The parent `Blob` will be immutable after this and the sub`Blob` /// cannot outlive its parent. /// /// ### Arguments /// * `offset`: Byte-offset of sub-blob within parent. /// * `length`: Length of the sub-blob. pub fn create_sub_blob(&self, offset: usize, length: usize) -> Shared> { let blob = unsafe { hb::hb_blob_create_sub_blob(self.as_raw(), offset as u32, length as u32) }; unsafe { Shared::from_raw_owned(blob) } } /// Returns true if the blob is immutable. /// /// HarfBuzz internally uses this value to make sure the blob is not mutated /// after being shared. In Rust this is not really necessary due to the borrow /// checker. This method is provided regardless for completeness. pub fn is_immutable(&self) -> bool { unsafe { hb::hb_blob_is_immutable(self.as_raw()) == 1 } } /// Makes this blob immutable so the bytes it refers to will never change /// during its lifetime. pub fn make_immutable(&mut self) { unsafe { hb::hb_blob_make_immutable(self.as_raw()) } } /// Try to get a mutable slice of the `Blob`'s bytes, possibly copying them. /// /// This returns `None` if the blob is immutable or memory allocation /// failed. pub fn try_get_mut_data(&mut self) -> Option<&'a mut [u8]> { unsafe { let mut length = hb::hb_blob_get_length(self.as_raw()); let data_ptr = hb::hb_blob_get_data_writable(self.as_raw(), &mut length as *mut _); if data_ptr.is_null() { None } else { Some(std::slice::from_raw_parts_mut( data_ptr as *mut u8, length as usize, )) } } } } unsafe impl<'a> Send for Blob<'a> {} unsafe impl<'a> Sync for Blob<'a> {} impl<'a> fmt::Debug for Blob<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Blob") .field("data", &self.get_data()) .field("is_immutable", &self.is_immutable()) .finish() } } unsafe impl<'a> HarfbuzzObject for Blob<'a> { type Raw = hb::hb_blob_t; unsafe fn from_raw(raw: *const hb::hb_blob_t) -> Self { Blob { raw: NonNull::new(raw as *mut _).unwrap(), marker: PhantomData, } } fn as_raw(&self) -> *mut hb::hb_blob_t { self.raw.as_ptr() } unsafe fn reference(&self) { hb::hb_blob_reference(self.as_raw()); } unsafe fn dereference(&self) { hb::hb_blob_destroy(self.as_raw()); } } use std::ops::Deref; impl<'a> Deref for Blob<'a> { type Target = [u8]; fn deref(&self) -> &[u8] { self.get_data() } } use std::convert::AsRef; impl<'a> AsRef<[u8]> for Blob<'a> { fn as_ref(&self) -> &[u8] { self.get_data() } } use std::convert::From; impl<'a, T> From for Shared> where T: 'a + Send + AsRef<[u8]>, { fn from(container: T) -> Shared> { let blob = Blob::with_bytes_owned(container, |t| t.as_ref()); blob.into() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_vec_to_blob_conversion() { let a_vec: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; let blob: Shared> = a_vec.into(); assert_eq!(blob.len(), 11); let mut counter: u8 = 1; for num in blob.iter() { assert_eq!(*num, counter); counter += 1; } } use std::sync::Arc; #[test] fn test_arc_to_blob_conversion() { let rc_slice: Arc<[u8]> = Arc::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); let blob: Shared> = rc_slice.into(); assert_eq!(blob.len(), 11); let mut counter: u8 = 1; for num in blob.iter() { assert_eq!(*num, counter); counter += 1; } } #[test] fn test_arc_vec_to_blob_conversion() { let rc_slice: Arc> = Arc::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); let blob: Owned> = Blob::with_bytes_owned(rc_slice.clone(), |t| &*t); assert_eq!(Arc::strong_count(&rc_slice), 2); assert_eq!(blob.len(), 11); let mut counter: u8 = 1; for num in blob.iter() { assert_eq!(*num, counter); counter += 1; } std::mem::drop(blob); assert_eq!(Arc::strong_count(&rc_slice), 1); } } harfbuzz_rs-1.2.0/src/buffer.rs010064400007650000024000001011441370300203300146660ustar0000000000000000use crate::common::{Direction, HarfbuzzObject, Language, Owned, Script, Tag}; use crate::font::Position; use crate::hb; use std::io::Read; use std::os; use std::os::raw::c_uint; use std::ptr::NonNull; use std::{fmt, io}; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[repr(C)] pub struct SegmentProperties { pub direction: Direction, pub script: Script, pub language: Language, } impl SegmentProperties { pub fn from_raw(raw: hb::hb_segment_properties_t) -> Self { let direction = Direction::from_raw(raw.direction); let script = Script(raw.script); let language = Language(raw.language); SegmentProperties { direction, script, language, } } pub fn into_raw(self) -> hb::hb_segment_properties_t { hb::hb_segment_properties_t { direction: self.direction.to_raw(), script: self.script.0, language: self.language.0, reserved1: std::ptr::null_mut(), reserved2: std::ptr::null_mut(), } } } /// `GlyphPosition` is the structure that holds the positions of the glyph in /// both horizontal and vertical directions. All positions in `GlyphPosition` /// are relative to the current point. #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct GlyphPosition { /// how much the line advances after drawing this glyph when setting text in /// horizontal direction. pub x_advance: Position, /// how much the line advances after drawing this glyph when setting text in /// vertical direction. pub y_advance: Position, /// how much the glyph moves on the X-axis before drawing it, this should /// not affect how much the line advances. pub x_offset: Position, /// how much the glyph moves on the Y-axis before drawing it, this should /// not affect how much the line advances. pub y_offset: Position, var: hb::hb_var_int_t, } impl GlyphPosition { pub const fn new( x_advance: Position, y_advance: Position, x_offset: Position, y_offset: Position, ) -> Self { GlyphPosition { x_advance, y_advance, x_offset, y_offset, var: hb::hb_var_int_t { u32: 0 }, } } } /// A set of flags that may be set during shaping on each glyph. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct GlyphFlags(pub hb::hb_glyph_flags_t); #[allow(clippy::trivially_copy_pass_by_ref)] impl GlyphFlags { /// If `true`, indicates that if input text is broken at the beginning of /// the cluster this glyph is part of, then both sides need to be re-shaped, /// as the result might be different. On the flip side, it means that when /// this function returns `false`, then it's safe to break the glyph-run at /// the beginning of this cluster, and the two sides represent the exact /// same result one would get if breaking input text at the beginning of /// this cluster and shaping the two sides separately. This can be used to /// optimize paragraph layout, by avoiding re-shaping of each line after /// line-breaking, or limiting the reshaping to a small piece around the /// breaking point only. pub fn unsafe_to_break(&self) -> bool { self.0 & hb::HB_GLYPH_FLAG_UNSAFE_TO_BREAK == hb::HB_GLYPH_FLAG_UNSAFE_TO_BREAK } } #[derive(Debug, Copy, Clone)] #[repr(C)] pub struct GlyphInfo { pub codepoint: u32, mask: hb::hb_mask_t, pub cluster: u32, var1: hb::hb_var_int_t, var2: hb::hb_var_int_t, } impl GlyphInfo { pub fn glyph_flags(&self) -> GlyphFlags { GlyphFlags(unsafe { hb::hb_glyph_info_get_glyph_flags(self.as_raw()) }) } fn as_raw(&self) -> *const hb::hb_glyph_info_t { (self as *const GlyphInfo) as *const _ } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ClusterLevel { MonotoneGraphemes, MonotoneCharacters, Characters, } impl ClusterLevel { pub fn from_raw(raw: hb::hb_buffer_cluster_level_t) -> Self { match raw { hb::HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES => ClusterLevel::MonotoneGraphemes, hb::HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS => ClusterLevel::MonotoneCharacters, hb::HB_BUFFER_CLUSTER_LEVEL_CHARACTERS => ClusterLevel::Characters, _ => panic!("received unrecognized HB_BUFFER_CLUSTER_LEVEL"), } } pub fn into_raw(self) -> hb::hb_buffer_cluster_level_t { match self { ClusterLevel::MonotoneGraphemes => hb::HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES, ClusterLevel::MonotoneCharacters => hb::HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS, ClusterLevel::Characters => hb::HB_BUFFER_CLUSTER_LEVEL_CHARACTERS, } } } impl Default for ClusterLevel { fn default() -> Self { ClusterLevel::MonotoneGraphemes } } #[derive(Debug)] pub(crate) struct GenericBuffer { raw: NonNull, } impl GenericBuffer { pub(crate) fn new() -> Owned { let buffer = unsafe { hb::hb_buffer_create() }; unsafe { Owned::from_raw(buffer) } } #[allow(unused)] pub(crate) fn empty() -> Owned { let buffer = unsafe { hb::hb_buffer_get_empty() }; unsafe { Owned::from_raw(buffer) } } pub(crate) fn len(&self) -> usize { unsafe { hb::hb_buffer_get_length(self.as_raw()) as usize } } pub(crate) fn is_empty(&self) -> bool { self.len() == 0 } pub(crate) fn add(&mut self, codepoint: u32, cluster: u32) { unsafe { hb::hb_buffer_add(self.as_raw(), codepoint, cluster); } } pub(crate) fn add_str_item(&mut self, string: &str, item_start: usize, item_len: usize) { assert!(item_start + item_len <= string.len()); let utf8_ptr = string.as_ptr() as *const _; unsafe { hb::hb_buffer_add_utf8( self.as_raw(), utf8_ptr, string.len() as os::raw::c_int, item_start as os::raw::c_uint, item_len as os::raw::c_int, ); } } pub(crate) fn append(&mut self, source: &GenericBuffer, start: c_uint, end: c_uint) { unsafe { hb::hb_buffer_append(self.as_raw(), source.as_raw(), start, end); } } pub(crate) fn set_direction(&mut self, direction: Direction) { unsafe { hb::hb_buffer_set_direction(self.as_raw(), direction.to_raw()) }; } /// Returns the `Buffer`'s text direction. pub(crate) fn get_direction(&self) -> Direction { Direction::from_raw(unsafe { hb::hb_buffer_get_direction(self.as_raw()) }) } pub(crate) fn set_language(&mut self, lang: Language) { unsafe { hb::hb_buffer_set_language(self.as_raw(), lang.0) } } pub(crate) fn get_language(&self) -> Option { let raw_lang = unsafe { hb::hb_buffer_get_language(self.as_raw()) }; if raw_lang.is_null() { None } else { Some(Language(raw_lang)) } } pub(crate) fn set_script(&mut self, script: hb::hb_script_t) { unsafe { hb::hb_buffer_set_script(self.as_raw(), script) } } pub(crate) fn get_script(&self) -> hb::hb_script_t { unsafe { hb::hb_buffer_get_script(self.as_raw()) } } pub(crate) fn guess_segment_properties(&mut self) { unsafe { hb::hb_buffer_guess_segment_properties(self.as_raw()) }; } pub(crate) fn get_segment_properties(&self) -> SegmentProperties { unsafe { let mut segment_props: hb::hb_segment_properties_t = std::mem::zeroed(); hb::hb_buffer_get_segment_properties(self.as_raw(), &mut segment_props as *mut _); SegmentProperties::from_raw(segment_props) } } pub(crate) fn set_cluster_level(&mut self, cluster_level: ClusterLevel) { unsafe { hb::hb_buffer_set_cluster_level(self.as_raw(), cluster_level.into_raw()) } } pub(crate) fn get_cluster_level(&self) -> ClusterLevel { ClusterLevel::from_raw(unsafe { hb::hb_buffer_get_cluster_level(self.as_raw()) }) } pub(crate) fn pre_allocate(&mut self, size: usize) { let size = size.min(std::os::raw::c_uint::max_value() as usize); unsafe { hb::hb_buffer_pre_allocate(self.as_raw(), size as _) }; } pub(crate) fn clear_contents(&mut self) { unsafe { hb::hb_buffer_clear_contents(self.as_raw()) }; } pub(crate) fn get_glyph_positions(&self) -> &[GlyphPosition] { unsafe { let mut length: u32 = 0; let glyph_pos = hb::hb_buffer_get_glyph_positions(self.as_raw(), &mut length as *mut u32); std::slice::from_raw_parts(glyph_pos as *const _, length as usize) } } pub(crate) fn get_glyph_infos(&self) -> &[GlyphInfo] { unsafe { let mut length: u32 = 0; let glyph_infos = hb::hb_buffer_get_glyph_infos(self.as_raw(), &mut length as *mut u32); std::slice::from_raw_parts(glyph_infos as *const _, length as usize) } } /// Reverse the `Buffer`'s contents. pub(crate) fn reverse(&mut self) { unsafe { hb::hb_buffer_reverse(self.as_raw()) }; } /// Reverse the `Buffer`'s contents in the range from `start` to `end`. pub(crate) fn reverse_range(&mut self, start: usize, end: usize) { assert!(start <= self.len(), end <= self.len()); unsafe { hb::hb_buffer_reverse_range(self.as_raw(), start as u32, end as u32) } } pub(crate) fn set_content_type(&self, content_type: hb::hb_buffer_content_type_t) { unsafe { hb::hb_buffer_set_content_type(self.as_raw(), content_type) } } pub(crate) fn content_type(&self) -> hb::hb_buffer_content_type_t { unsafe { hb::hb_buffer_get_content_type(self.as_raw()) } } } unsafe impl HarfbuzzObject for GenericBuffer { type Raw = hb::hb_buffer_t; unsafe fn from_raw(raw: *const Self::Raw) -> Self { GenericBuffer { raw: NonNull::new(raw as *mut _).unwrap(), } } fn as_raw(&self) -> *mut Self::Raw { self.raw.as_ptr() } unsafe fn reference(&self) { hb::hb_buffer_reference(self.as_raw()); } unsafe fn dereference(&self) { hb::hb_buffer_destroy(self.as_raw()); } } /// The serialization format used in `BufferSerializer`. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum SerializeFormat { /// A human-readable, plain text format Text, /// A machine-readable JSON format. Json, } impl From for hb::hb_buffer_serialize_format_t { fn from(fmt: SerializeFormat) -> Self { match fmt { SerializeFormat::Text => hb::HB_BUFFER_SERIALIZE_FORMAT_TEXT, SerializeFormat::Json => hb::HB_BUFFER_SERIALIZE_FORMAT_JSON, } } } bitflags! { /// Flags used for serialization with a `BufferSerializer`. #[derive(Default)] pub struct SerializeFlags: u32 { /// Do not serialize glyph cluster. const NO_CLUSTERS = hb::HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS; /// Do not serialize glyph position information. const NO_POSITIONS = hb::HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS; /// Do no serialize glyph name. const NO_GLYPH_NAMES = hb::HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES; /// Serialize glyph extents. const GLYPH_EXTENTS = hb::HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS; /// Serialize glyph flags. const GLYPH_FLAGS = hb::HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS; /// Do not serialize glyph advances, glyph offsets will reflect absolute /// glyph positions. const NO_ADVANCES = hb::HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES; } } /// A type that can be used to serialize a `GlyphBuffer`. /// /// A `BufferSerializer` is obtained by calling the `GlyphBuffer::serializer` /// method and provides a `Read` implementation that allows you to read the /// serialized buffer contents. #[derive(Debug)] pub struct BufferSerializer<'a> { font: Option<&'a crate::Font<'a>>, buffer: &'a Owned, start: usize, end: usize, format: SerializeFormat, flags: SerializeFlags, bytes: io::Cursor>, } impl<'a> Read for BufferSerializer<'a> { fn read(&mut self, buf: &mut [u8]) -> io::Result { match self.bytes.read(buf) { // if `bytes` is empty refill it Ok(0) => { if self.start > self.end.saturating_sub(1) { return Ok(0); } let mut bytes_written = 0; let num_serialized_items = unsafe { hb::hb_buffer_serialize_glyphs( self.buffer.as_raw(), self.start as u32, self.end as u32, self.bytes.get_mut().as_mut_ptr() as *mut _, self.bytes.get_ref().capacity() as u32, &mut bytes_written, self.font .map(|f| f.as_raw()) .unwrap_or(std::ptr::null_mut()), self.format.into(), self.flags.bits(), ) }; self.start += num_serialized_items as usize; self.bytes.set_position(0); unsafe { self.bytes.get_mut().set_len(bytes_written as usize) }; self.read(buf) } Ok(size) => Ok(size), Err(err) => Err(err), } } } /// This type provides an interface to create one of the buffer types from a raw /// harfbuzz pointer. #[derive(Debug)] pub enum TypedBuffer { /// Contains a `UnicodeBuffer` Unicode(UnicodeBuffer), /// Contains a `GlyphBuffer` Glyphs(GlyphBuffer), } impl TypedBuffer { /// Takes ownership of the raw `hb_buffer_t` object and converts it to are /// `TypedBuffer`. If no safe conversion is possible returns `None`. /// /// # Safety /// /// Marked as unsafe because it acceses a raw pointer. Internally calls /// `Owned::from_raw` and therefore the same ownership considerations apply. pub unsafe fn take_from_raw(raw: *mut hb::hb_buffer_t) -> Option { let generic_buf: Owned = Owned::from_raw(raw); let content_type = generic_buf.content_type(); match content_type { hb::HB_BUFFER_CONTENT_TYPE_UNICODE => { Some(TypedBuffer::Unicode(UnicodeBuffer(generic_buf))) } hb::HB_BUFFER_CONTENT_TYPE_GLYPHS => { Some(TypedBuffer::Glyphs(GlyphBuffer(generic_buf))) } _ => None, } } } /// A `UnicodeBuffer` can be filled with unicode text and corresponding cluster /// indices. /// /// # Usage /// /// The buffer manages an allocation for the unicode codepoints to be shaped. /// This allocation is reused for storing the results of the shaping operation /// in a `GlyphBuffer` object. The intended usage is to keep one (or e.g. one /// per thread) `UnicodeBuffer` around. When needed, you fill it with text that /// should be shaped and pass it as an argument to the `shape` function. That /// method returns a `GlyphBuffer` object containing the shaped glyph indices. /// Once you got the needed information out of the `GlyphBuffer` you call its /// `.clear()` method which in turn gives you a fresh `UnicodeBuffer` (also /// reusing the original allocation again). This buffer can then be used to /// shape more text. /// /// # Interaction with the raw harfbuzz API /// /// If you want to get a `UnicodeBuffer` from a pointer to a raw harfbuzz /// object, you need to use the `from_raw` static method on `TypedBuffer`. This /// ensures that a buffer of correct type is created. pub struct UnicodeBuffer(pub(crate) Owned); impl UnicodeBuffer { pub(crate) fn from_generic(generic: Owned) -> Self { generic.set_content_type(hb::HB_BUFFER_CONTENT_TYPE_UNICODE); UnicodeBuffer(generic) } /// Creates a new empty `Buffer`. /// /// # Examples /// ``` /// use harfbuzz_rs::UnicodeBuffer; /// /// let buffer = UnicodeBuffer::new(); /// assert!(buffer.is_empty()); /// ``` pub fn new() -> UnicodeBuffer { UnicodeBuffer::from_generic(GenericBuffer::new()) } /// Converts this buffer to a raw harfbuzz object pointer. pub fn into_raw(self) -> *mut hb::hb_buffer_t { Owned::into_raw(self.0) } /// Returns the length of the data of the buffer. /// /// This corresponds to the number of unicode codepoints contained in the /// buffer. /// /// # Examples /// ``` /// use harfbuzz_rs::UnicodeBuffer; /// /// let str1 = "Hello "; /// let buffer = UnicodeBuffer::new().add_str(str1); /// assert_eq!(buffer.len(), str1.len()); /// /// let str2 = "😍🙈"; /// let buffer = buffer.add_str(str2); /// assert_eq!(buffer.len(), str1.len() + 2);; /// ``` pub fn len(&self) -> usize { self.0.len() } /// Returns `true` if the buffer contains no elements. pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Add a single codepoint with the associated cluster value to the buffer. /// /// # Examples /// /// ``` /// use harfbuzz_rs::UnicodeBuffer; /// /// let buffer = UnicodeBuffer::new().add('A' as u32, 0); /// assert_eq!(buffer.string_lossy(), "A"); /// ``` pub fn add(mut self, codepoint: u32, cluster: u32) -> UnicodeBuffer { self.0.add(codepoint, cluster); self } /// Add the string slice `str_slice` to the `Buffer`'s array of codepoints. /// /// When shaping part of a larger text (e.g. a run of text from a paragraph) /// it is preferable to use `add_str_item` instead. /// /// # Examples /// /// ``` /// use harfbuzz_rs::UnicodeBuffer; /// /// let buffer = UnicodeBuffer::new().add_str("Hello"); /// let buffer = buffer.add_str(" World"); /// assert_eq!(buffer.string_lossy(), "Hello World"); /// ``` /// pub fn add_str(mut self, str_slice: &str) -> UnicodeBuffer { self.0.add_str_item(str_slice, 0, str_slice.len()); self } /// Add a string item to the buffer, providing context. /// /// Only the `item` string gets added to the buffer and will be shaped. /// `context` provides extra information to the shaper, allowing, for /// example, to do cross-run Arabic shaping or properly handle combining /// marks at the start of a run. /// /// When shaping part of a larger text (e.g. a run of text from a paragraph) /// you should pass the whole paragraph to this function as `context` /// whereas `item` refers only to the part of the string to be shaped. /// /// # Panics /// /// Panics if `item` is not a substring of `context`. Note that `item` must /// reside in the same allocation as `context`! /// /// # Examples /// /// We only want to shape the string `World` as part of the sentence `Hello /// World!`. /// /// ``` /// use harfbuzz_rs::UnicodeBuffer; /// /// let string = "Hello World!"; /// /// // the range 6..11 corresponds to `World` /// assert_eq!(&string[6..11], "World"); /// /// let buffer = UnicodeBuffer::new().add_str_item(string, &string[6..11]); /// assert_eq!(buffer.string_lossy(), "World"); /// ``` pub fn add_str_item(mut self, context: &str, item: &str) -> UnicodeBuffer { const PANIC_MSG: &str = "`item` must be a substring of `context`"; let offset = usize::checked_sub(item.as_ptr() as _, context.as_ptr() as _).expect(PANIC_MSG); assert!(offset + item.len() <= context.len(), PANIC_MSG); self.0.add_str_item(context, offset, item.len()); self } /// Append codepoints from another `UnicodeBuffer` to the end of `self`. /// /// # Examples /// /// ``` /// use harfbuzz_rs::UnicodeBuffer; /// /// let buffer = UnicodeBuffer::new().add_str("Hello"); /// let other = UnicodeBuffer::new().add_str(" World!"); /// let buffer = buffer.append(&other); /// assert_eq!(buffer.string_lossy(), "Hello World!"); /// ``` /// pub fn append(mut self, other: &UnicodeBuffer) -> UnicodeBuffer { self.0.append(&other.0, 0, c_uint::max_value()); self } /// Append a range of codepoints from another `UnicodeBuffer` to the end of /// `self`. /// /// # Examples /// /// ``` /// use harfbuzz_rs::UnicodeBuffer; /// /// let buffer = UnicodeBuffer::new().add_str("Hello"); /// let other = UnicodeBuffer::new().add_str(" World!"); /// let buffer = buffer.append_range(&other, 0..=3); /// assert_eq!(buffer.string_lossy(), "Hello Wor"); /// let buffer = buffer.append_range(&other, 4..); /// assert_eq!(buffer.string_lossy(), "Hello World!"); /// ``` /// pub fn append_range( mut self, other: &UnicodeBuffer, range: impl std::ops::RangeBounds, ) -> UnicodeBuffer { let (start, end) = crate::start_end_range(range); self.0.append(&other.0, start, end); self } /// Returns an Iterator over the stored unicode codepoints. /// /// # Examples /// ``` /// use harfbuzz_rs::UnicodeBuffer; /// /// let buffer = UnicodeBuffer::new().add_str("ab"); /// let mut iterator = buffer.codepoints(); /// /// assert_eq!('a' as u32, iterator.next().unwrap()); /// assert_eq!('b' as u32, iterator.next().unwrap()); /// assert!(iterator.next().is_none()); /// ``` pub fn codepoints(&self) -> Codepoints<'_> { Codepoints { slice_iter: self.0.get_glyph_infos().iter(), } } /// Get the stored codepoints as a `String`. /// /// Invalid codepoints get replaced by the U+FFFD replacement character. pub fn string_lossy(&self) -> String { self.codepoints() .map(|cp| std::char::from_u32(cp).unwrap_or('\u{FFFD}')) .collect() } /// Set the text direction of the `Buffer`'s contents. pub fn set_direction(mut self, direction: Direction) -> UnicodeBuffer { self.0.set_direction(direction); self } /// Returns the `Buffer`'s text direction. pub fn get_direction(&self) -> Direction { self.0.get_direction() } /// Set the script from an ISO15924 tag. pub fn set_script(mut self, script: Tag) -> UnicodeBuffer { self.0 .set_script(unsafe { hb::hb_script_from_iso15924_tag(script.0) }); self } /// Get the ISO15924 script tag. pub fn get_script(&self) -> Tag { Tag(unsafe { hb::hb_script_to_iso15924_tag(self.0.get_script()) }) } /// Set the buffer language. pub fn set_language(mut self, lang: Language) -> UnicodeBuffer { self.0.set_language(lang); self } /// Get the buffer language. pub fn get_language(&self) -> Option { self.0.get_language() } /// Guess the segment properties (direction, language, script) for the /// current buffer. pub fn guess_segment_properties(mut self) -> UnicodeBuffer { self.0.guess_segment_properties(); self } /// Get the segment properties (direction, language, script) of the current /// buffer. pub fn get_segment_properties(&self) -> SegmentProperties { self.0.get_segment_properties() } /// Set the cluster level of the buffer. pub fn set_cluster_level(mut self, cluster_level: ClusterLevel) -> UnicodeBuffer { self.0.set_cluster_level(cluster_level); self } /// Retrieve the cluster level of the buffer. pub fn get_cluster_level(&self) -> ClusterLevel { self.0.get_cluster_level() } /// Pre-allocate the buffer to hold a string at least `size` codepoints. pub fn pre_allocate(&mut self, size: usize) { self.0.pre_allocate(size) } /// Clear the contents of the buffer (i.e. the stored string of unicode /// characters). /// /// # Examples /// ``` /// use harfbuzz_rs::UnicodeBuffer; /// /// let buffer = UnicodeBuffer::new(); /// let buffer = buffer.add_str("Test!"); /// assert_eq!(buffer.len(), 5); /// let buffer = buffer.clear_contents(); /// assert!(buffer.is_empty()); /// ``` pub fn clear_contents(mut self) -> UnicodeBuffer { self.0.clear_contents(); self } } impl std::fmt::Debug for UnicodeBuffer { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fmt.debug_struct("UnicodeBuffer") .field("content", &self.string_lossy()) .field("direction", &self.get_direction()) .field("language", &self.get_language()) .field("script", &self.get_script()) .field("cluster_level", &self.get_cluster_level()) .finish() } } impl std::default::Default for UnicodeBuffer { fn default() -> UnicodeBuffer { UnicodeBuffer::new() } } /// An iterator over the codepoints stored in a `UnicodeBuffer`. /// /// You get an iterator of this type from the `.codepoints()` method on /// `UnicodeBuffer`. It yields `u32`s that should be interpreted as unicode /// codepoints stored in the underlying buffer. #[derive(Debug, Clone)] pub struct Codepoints<'a> { slice_iter: std::slice::Iter<'a, GlyphInfo>, } impl<'a> Iterator for Codepoints<'a> { type Item = u32; fn next(&mut self) -> Option { self.slice_iter.next().map(|info| info.codepoint) } } /// A `GlyphBuffer` contains the resulting output information of the shaping /// process. /// /// An object of this type is obtained through the `shape` function. pub struct GlyphBuffer(pub(crate) Owned); impl GlyphBuffer { /// Returns the length of the data of the buffer. /// /// When called before shaping this is the number of unicode codepoints /// contained in the buffer. When called after shaping it returns the number /// of glyphs stored. pub fn len(&self) -> usize { self.0.len() } /// Converts this buffer to a raw harfbuzz object pointer. pub fn into_raw(self) -> *mut hb::hb_buffer_t { Owned::into_raw(self.0) } /// Returns `true` if the buffer contains no elements. pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Get the glyph positions. pub fn get_glyph_positions(&self) -> &[GlyphPosition] { self.0.get_glyph_positions() } /// Get the glyph infos. pub fn get_glyph_infos(&self) -> &[GlyphInfo] { self.0.get_glyph_infos() } /// Reverse the `Buffer`'s contents. pub fn reverse(&mut self) { self.0.reverse() } /// Reverse the `Buffer`'s contents in the range from `start` to `end`. pub fn reverse_range(&mut self, start: usize, end: usize) { self.0.reverse_range(start, end) } /// Clears the contents of the glyph buffer and returns an empty /// `UnicodeBuffer` reusing the existing allocation. pub fn clear(mut self) -> UnicodeBuffer { self.0.clear_contents(); UnicodeBuffer::from_generic(self.0) } /// Returns a serializer that allows the contents of the buffer to be /// converted into a human or machine readable representation. /// /// # Arguments /// - `font`: Optionally a font can be provided for access to glyph names /// and glyph extents. If `None` is passed an empty font is assumed. /// - `format`: The serialization format to use. /// - `flags`: Allows you to control which information will be contained in /// the serialized output. /// /// # Examples /// /// Serialize the glyph buffer contents to a string using the textual format /// without any special flags. /// ``` /// use harfbuzz_rs::*; /// use std::io::Read; /// # use std::path::PathBuf; /// # let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); /// # path.push("testfiles/SourceSansVariable-Roman.ttf"); /// let face = Face::from_file(path, 0).expect("Error reading font file."); /// let font = Font::new(face); /// /// let buffer = UnicodeBuffer::new().add_str("ABC"); /// /// let buffer = shape(&font, buffer, &[]); /// /// let mut string = String::new(); /// buffer /// .serializer( /// Some(&font), /// SerializeFormat::Text, /// SerializeFlags::default(), /// ).read_to_string(&mut string) /// .unwrap(); /// /// assert_eq!(string, "gid2=0+520|gid3=1+574|gid4=2+562") /// ``` pub fn serializer<'a>( &'a self, font: Option<&'a crate::Font<'a>>, format: SerializeFormat, flags: SerializeFlags, ) -> BufferSerializer<'a> { BufferSerializer { font, buffer: &self.0, start: 0, end: self.len(), format, flags, bytes: io::Cursor::new(Vec::with_capacity(128)), } } } impl fmt::Debug for GlyphBuffer { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("GlyphBuffer") .field("glyph_positions", &self.get_glyph_positions()) .field("glyph_infos", &self.get_glyph_infos()) .finish() } } impl fmt::Display for GlyphBuffer { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut serializer = self.serializer(None, SerializeFormat::Text, SerializeFlags::default()); let mut string = String::new(); serializer.read_to_string(&mut string).unwrap(); write!(fmt, "{}", string)?; Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::tests::assert_memory_layout_equal; use crate::{shape, Face, Font}; #[test] fn test_memory_layouts() { assert_memory_layout_equal::(); assert_memory_layout_equal::(); } #[test] fn test_str_item_heap() { let string = String::from("Test String for test"); UnicodeBuffer::new().add_str_item(&string, &string[5..10]); } #[test] #[should_panic(expected = "must be a substring of")] fn test_str_item_different_allocations() { UnicodeBuffer::new().add_str_item("Test", "String"); } #[test] #[should_panic(expected = "must be a substring of")] fn test_str_item_not_substring() { let string = "Test String"; UnicodeBuffer::new().add_str_item(&string[0..5], &string[4..6]); } #[test] #[should_panic(expected = "must be a substring of")] fn test_str_item_not_substring2() { let string = "Test String"; UnicodeBuffer::new().add_str_item(&string[4..], &string[0..5]); } #[test] fn test_glyph_buffer_serialization_single_char() { let path = "testfiles/SourceSansVariable-Roman.ttf"; let face = Face::from_file(path, 0).unwrap(); let font = Font::new(face); let buffer = UnicodeBuffer::new().add_str("A"); let glyph_buffer = shape(&font, buffer, &[]); // serializes only glyph indices let mut serializer = glyph_buffer.serializer( Some(&font), SerializeFormat::Text, SerializeFlags::NO_ADVANCES | SerializeFlags::NO_CLUSTERS | SerializeFlags::NO_POSITIONS | SerializeFlags::NO_GLYPH_NAMES, ); let mut string = String::new(); serializer.read_to_string(&mut string).unwrap(); assert_eq!( string.parse::().unwrap(), glyph_buffer.get_glyph_infos()[0].codepoint ); } #[test] fn test_glyph_buffer_serialization_text() { let path = "testfiles/SourceSansVariable-Roman.ttf"; let face = Face::from_file(path, 0).unwrap(); let font = Font::new(face); let buffer = UnicodeBuffer::new().add_str("Hello 🌍"); let glyph_buffer = shape(&font, buffer, &[]); // serializes only glyph indices let mut serializer = glyph_buffer.serializer( Some(&font), SerializeFormat::Text, SerializeFlags::NO_ADVANCES | SerializeFlags::NO_CLUSTERS | SerializeFlags::NO_POSITIONS | SerializeFlags::NO_GLYPH_NAMES, ); let mut string = String::new(); serializer.read_to_string(&mut string).unwrap(); for (serialized_glyph, glyph_info) in string .split_terminator('|') .zip(glyph_buffer.get_glyph_infos()) { assert_eq!( serialized_glyph.parse::().unwrap(), glyph_info.codepoint ); } } } harfbuzz_rs-1.2.0/src/common.rs010064400007650000024000000433761370300002200147150ustar0000000000000000use crate::hb; use std::borrow::Borrow; use std::ops::{Deref, DerefMut}; /// A type to represent 4-byte SFNT tags. /// /// The easiest way to create a tag is by using its `From<&[u8; 4]>` impl: /// /// ``` /// # use harfbuzz_rs::Tag; /// let tag: Tag = b"abcd".into(); /// assert_eq!(&tag.to_bytes(), b"abcd"); /// ``` /// /// Tables, features, etc. in OpenType and many other font formats use SFNT tags /// as identifiers. These are 4-bytes long and usually each byte represents an /// ASCII value. `Tag` provides methods to create such identifiers from /// individual `chars` or a `str` slice and to get the string representation of /// a `Tag`. #[derive(Copy, Clone, Hash, PartialEq, Eq)] #[repr(transparent)] pub struct Tag(pub hb::hb_tag_t); impl Tag { /// Create a `Tag` from its four-char textual representation. /// /// All the arguments must be ASCII values. /// /// # Examples /// /// ``` /// use harfbuzz_rs::Tag; /// let cmap_tag = Tag::new('c', 'm', 'a', 'p'); /// assert_eq!(cmap_tag.to_string(), "cmap") /// ``` /// pub const fn new(a: char, b: char, c: char, d: char) -> Self { Tag(((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32)) } fn tag_to_string(self) -> String { let mut buf: [u8; 4] = [0; 4]; unsafe { hb::hb_tag_to_string(self.0, buf.as_mut_ptr() as *mut _) }; String::from_utf8_lossy(&buf).into() } /// Returns tag as 4-element byte array. /// /// # Examples /// ``` /// # use harfbuzz_rs::Tag; /// let tag = Tag::new('a', 'b', 'c', 'd'); /// assert_eq!(&tag.to_bytes(), b"abcd"); /// ``` pub const fn to_bytes(self) -> [u8; 4] { #[allow(clippy::identity_op)] [ (self.0 >> 24 & 0xff) as u8, (self.0 >> 16 & 0xff) as u8, (self.0 >> 8 & 0xff) as u8, (self.0 >> 0 & 0xff) as u8, ] } } use std::fmt; use std::fmt::{Debug, Display, Formatter}; impl Debug for Tag { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let string = self.tag_to_string(); let mut chars = string.chars().chain(std::iter::repeat('\u{FFFD}')); write!( f, "Tag({:?}, {:?}, {:?}, {:?})", chars.next().unwrap(), chars.next().unwrap(), chars.next().unwrap(), chars.next().unwrap() ) } } impl<'a> From<&'a [u8; 4]> for Tag { fn from(byte_array: &'a [u8; 4]) -> Tag { Tag::new( byte_array[0] as char, byte_array[1] as char, byte_array[2] as char, byte_array[3] as char, ) } } impl From for [u8; 4] { fn from(tag: Tag) -> [u8; 4] { tag.to_bytes() } } impl Display for Tag { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.tag_to_string()) } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] /// An Error generated when a `Tag` fails to parse from a `&str` with the /// `from_str` function. pub enum TagFromStrErr { /// The string contains non-ASCII characters. NonAscii, /// The string has length zero. ZeroLengthString, } use std; use std::str::FromStr; impl FromStr for Tag { type Err = TagFromStrErr; /// Parses a `Tag` from a `&str` that contains four or less ASCII /// characters. When the string's length is smaller than 4 it is extended /// with `' '` (Space) characters. The remaining bytes of strings longer /// than 4 bytes are ignored. /// /// # Examples /// /// ``` /// use harfbuzz_rs::Tag; /// use std::str::FromStr; /// let tag1 = Tag::from_str("ABCD").unwrap(); /// let tag2 = Tag::new('A', 'B', 'C', 'D'); /// assert_eq!(tag1, tag2); /// ``` /// fn from_str(s: &str) -> Result { if !s.is_ascii() { return Err(TagFromStrErr::NonAscii); } if s.is_empty() { return Err(TagFromStrErr::ZeroLengthString); } let len = std::cmp::max(s.len(), 4) as i32; unsafe { Ok(Tag(hb::hb_tag_from_string(s.as_ptr() as *mut _, len))) } } } /// Defines the direction in which text is to be read. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Direction { /// Initial, unset direction. Invalid, /// Text is set horizontally from left to right. Ltr, /// Text is set horizontally from right to left. Rtl, /// Text is set vertically from top to bottom. Ttb, /// Text is set vertically from bottom to top. Btt, } impl Direction { /// Convert into raw value of type `hb_direction_t`. pub fn to_raw(self) -> hb::hb_direction_t { match self { Direction::Invalid => hb::HB_DIRECTION_INVALID, Direction::Ltr => hb::HB_DIRECTION_LTR, Direction::Rtl => hb::HB_DIRECTION_RTL, Direction::Ttb => hb::HB_DIRECTION_TTB, Direction::Btt => hb::HB_DIRECTION_BTT, } } /// Create from raw value of type `hb_direction_t`. pub fn from_raw(dir: hb::hb_direction_t) -> Self { match dir { hb::HB_DIRECTION_LTR => Direction::Ltr, hb::HB_DIRECTION_RTL => Direction::Rtl, hb::HB_DIRECTION_TTB => Direction::Ttb, hb::HB_DIRECTION_BTT => Direction::Btt, _ => Direction::Invalid, } } } #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct Language(pub hb::hb_language_t); impl Default for Language { fn default() -> Language { Language(unsafe { hb::hb_language_get_default() }) } } impl Debug for Language { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "Language(\"{}\")", self) } } use std::ffi::CStr; impl Display for Language { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let string = unsafe { let char_ptr = hb::hb_language_to_string(self.0); if char_ptr.is_null() { return Err(fmt::Error); } CStr::from_ptr(char_ptr) .to_str() .expect("String representation of language is not valid utf8.") }; write!(f, "{}", string) } } #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct InvalidLanguage; impl FromStr for Language { type Err = InvalidLanguage; fn from_str(s: &str) -> Result { let len = std::cmp::min(s.len(), std::i32::MAX as _) as i32; let lang = unsafe { hb::hb_language_from_string(s.as_ptr() as *mut _, len) }; if lang.is_null() { Err(InvalidLanguage {}) } else { Ok(Language(lang)) } } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct Script(pub hb::hb_script_t); impl Script { pub fn from_iso15924_tag(tag: Tag) -> Self { Script(unsafe { hb::hb_script_from_iso15924_tag(tag.0) }) } pub fn to_iso15924_tag(self) -> Tag { Tag(unsafe { hb::hb_script_to_iso15924_tag(self.0) }) } pub fn horizontal_direction(self) -> Direction { Direction::from_raw(unsafe { hb::hb_script_get_horizontal_direction(self.0) }) } } /// A trait which is implemented for all harffbuzz wrapper structs. It exposes /// common functionality for converting from and to the underlying raw harfbuzz /// pointers that are useful for ffi. /// /// # Safety /// /// This trait may only be implemented for structs that are zero-sized and is /// therefore unsafe to implement. pub unsafe trait HarfbuzzObject: Sized { /// Type of the raw harfbuzz object. type Raw; /// Creates a reference from a harfbuzz object pointer. /// /// Unsafe because a raw pointer may be accessed. The reference count is not /// changed. Should not be called directly by a library user. /// /// Use the Owned and Shared abstractions instead. #[doc(hide)] unsafe fn from_raw(val: *const Self::Raw) -> Self; /// Returns the underlying harfbuzz object pointer. /// /// The caller must ensure, that this pointer is not used after `self`'s /// destruction. fn as_raw(&self) -> *mut Self::Raw; /// Increases the reference count of the HarfBuzz object. /// /// Wraps a `hb_TYPE_reference()` call. /// /// # Safety /// /// While no undefined behavior can be introduced only by increasing the /// reference count (I think) this method is still marked unsafe since there /// should be no need for it to be called from safe code. unsafe fn reference(&self); /// Decreases the reference count of the HarfBuzz object and destroys it if /// the reference count reaches zero. /// /// Wraps a `hb_TYPE_destroy()` call. /// /// # Safety /// /// You always have to call `reference` first before using this method. /// Otherwise you might accidentally hold on to already destroyed objects /// and causing UB. unsafe fn dereference(&self); } /// A smart pointer that wraps an atomically reference counted HarfBuzz object. /// /// Usually you don't create a `Shared` yourself, but get it from another /// function in this crate. You can just use the methods of the wrapped object /// through its `Deref` implementation. /// /// A `Shared` is a safe wrapper for reference counted HarfBuzz objects and /// provides shared immutable access to its inner object. As HarfBuzz' objects /// are all thread-safe `Shared` implements `Send` and `Sync`. /// /// Tries to mirror the stdlib `Arc` interface where applicable as HarfBuzz' /// reference counting has similar semantics. #[derive(Debug, PartialEq, Eq)] pub struct Shared { object: T, } impl Shared { /// Creates a `Shared` from an owned raw harfbuzz pointer. /// /// # Safety /// /// Transfers ownership. _Use of the original pointer is now forbidden!_ /// Unsafe because dereferencing a raw pointer is necessary. pub unsafe fn from_raw_owned(raw: *mut T::Raw) -> Self { let object = T::from_raw(raw); Shared { object } } /// Converts `self` into the underlying harfbuzz object pointer value. The /// resulting pointer has to be manually destroyed using `hb_TYPE_destroy` /// or be converted back into the wrapper using the `from_raw` function to /// avoid leaking memory. pub fn into_raw(shared: Shared) -> *mut T::Raw { let result = shared.object.as_raw(); std::mem::forget(shared); result } /// Creates a `Shared` by cloning a raw harfbuzz pointer. /// /// The original pointer can still be safely used but must be released at /// the end to avoid memory leaks. /// /// # Safety /// /// `raw` must be a valid pointer to the corresponding harfbuzz object or /// the behavior is undefined. /// /// Internally this method increases the reference count of `raw` so it is /// safe to call `hb_destroy_[...]` on `raw` after using `from_raw_ref`. pub unsafe fn from_raw_ref(raw: *mut T::Raw) -> Self { let object = T::from_raw(raw); object.reference(); Shared { object } } } impl Clone for Shared { /// Returns a copy and increases the reference count. /// /// This behaviour is exactly like `Arc::clone` in the standard library. fn clone(&self) -> Self { unsafe { Self::from_raw_ref(self.object.as_raw()) } } } impl Deref for Shared { type Target = T; fn deref(&self) -> &T { &self.object } } impl Borrow for Shared { fn borrow(&self) -> &T { self } } impl From> for Shared { fn from(t: Owned) -> Self { let ptr = t.object.as_raw(); std::mem::forget(t); unsafe { Shared::from_raw_owned(ptr) } } } impl Drop for Shared { fn drop(&mut self) { unsafe { self.dereference() } } } unsafe impl Send for Shared {} unsafe impl Sync for Shared {} /// A smart pointer that wraps a singly owned harfbuzz object. /// /// A `Owned` is used to wrap freshly created owned HarfBuzz objects. It permits /// mutable, non-shared access to the enclosed HarfBuzz value so it can be used /// e.g. to set up a `Font` or `Face` after its creation. /// /// There is no safe way to construct an `Owned` pointer and usually you don't /// need to create a `Owned` yourself, but get it from another function in this /// crate. You can just use the methods of the wrapped object through its /// `Deref` implementation. /// /// Interaction with `Shared` /// ------------------------- /// When you are finished mutating the inner value, you usually want to pass it /// to other HarfBuzz functions that expect shared access. Thus you need to /// convert the `Owned` to a `Shared` pointer using `.into()`. Note however that /// once a value is converted to a `Shared`, it will not possible to mutate /// it anymore. #[derive(Debug, PartialEq, Eq)] pub struct Owned { object: T, } impl Owned { /// Creates a `Owned` safely wrapping a raw harfbuzz pointer. /// /// # Safety /// /// This fully transfers ownership. _Use of the original pointer is now /// forbidden!_ Unsafe because a dereference of a raw pointer is necessary. /// /// Use this only to wrap freshly created HarfBuzz object that is not /// shared! Otherwise it is possible to have aliasing mutable references /// from safe code pub unsafe fn from_raw(raw: *mut T::Raw) -> Self { Owned { object: T::from_raw(raw), } } /// Converts `self` into the underlying harfbuzz object pointer value. The /// resulting pointer has to be manually destroyed using `hb_TYPE_destroy` /// or be converted back into the wrapper using the `from_raw` function to /// avoid leaking memory. pub fn into_raw(owned: Owned) -> *mut T::Raw { let result = owned.object.as_raw(); std::mem::forget(owned); result } /// Demotes an `Owned` pointer to a `Shared` pointer. /// /// Use this method when you don't need exclusive (mutable) access to the /// object anymore. For differences between `Owned` and `Shared` pointers /// see the documentation on the respective structs. /// /// Note that `Shared` also implements `From>` which allows /// implicit conversions in many functions. #[allow(clippy::wrong_self_convention)] //< backward compatibility is more important than clippy pub fn to_shared(self) -> Shared { self.into() } } impl Drop for Owned { fn drop(&mut self) { unsafe { self.dereference() } } } impl Deref for Owned { type Target = T; fn deref(&self) -> &T { &self.object } } impl DerefMut for Owned { fn deref_mut(&mut self) -> &mut T { &mut self.object } } #[cfg(test)] mod tests { use super::*; use std::cell::Cell; use std::mem; use std::rc::Rc; use std::str::FromStr; #[test] fn test_tag_debugging() { let tag = Tag::from_str("ABCD").unwrap(); assert_eq!("ABCD", format!("{}", tag)); assert_eq!("Tag('A', 'B', 'C', 'D')", format!("{:?}", tag)); } #[test] fn test_tag_creation() { assert!(Tag::from_str("∞BCD").is_err()); assert!(Tag::from_str("").is_err()); assert_eq!(Tag::from_str("ABCDE"), Tag::from_str("ABCD")); assert_eq!(Tag::from_str("abWd").unwrap(), Tag::new('a', 'b', 'W', 'd')); } #[test] fn test_language() { assert_eq!(Language::default().to_string(), "c"); assert_eq!(Language::from_str("ger").unwrap().to_string(), "ger"); assert_eq!(Language::from_str("ge!").unwrap().to_string(), "ge"); assert_eq!(Language::from_str("German").unwrap().to_string(), "german"); } // this is a mock struct for testing HarfbuzzObject's behaviour. #[derive(Debug, Clone)] struct ReferenceCounter { share_count: Rc>, } unsafe impl HarfbuzzObject for ReferenceCounter { type Raw = Cell; unsafe fn from_raw(raw: *const Cell) -> Self { ReferenceCounter { share_count: Rc::from_raw(raw as *mut _), } } fn as_raw(&self) -> *mut Cell { Rc::into_raw(self.share_count.clone()) as *mut _ } unsafe fn reference(&self) { println!("referencing {:?}", self); let rc = self.share_count.get(); self.share_count.set(rc + 1); } unsafe fn dereference(&self) { println!("dereferencing {:?}", self); let rc = self.share_count.get(); self.share_count.set(rc - 1); } } #[test] fn reference_counting_shared() { // Mimic a C-API that returns a pointer to a reference counted value. let object = ReferenceCounter { share_count: Rc::new(Cell::new(1)), }; // this clones the underlying `Rc` let raw = object.as_raw(); // so we expect two shared owners assert_eq!(Rc::strong_count(&object.share_count), 2); let shared: Shared = unsafe { Shared::from_raw_owned(raw) }; assert_eq!(shared.share_count.get(), 1); { // we create another `Shared` pointer... let shared2 = Shared::clone(&shared); // which clones assert_eq!(shared.share_count.get(), 2); mem::drop(shared2); } assert_eq!(shared.share_count.get(), 1); mem::drop(shared); assert_eq!(object.share_count.get(), 0); // ensure there are no dangling references assert_eq!(Rc::strong_count(&object.share_count), 1); } } harfbuzz_rs-1.2.0/src/face.rs010064400007650000024000000131611370277351200143340ustar0000000000000000use crate::hb; use std; use std::os::raw::c_void; use std::ptr::NonNull; use std::marker::PhantomData; use std::path::Path; use crate::blob::Blob; use crate::common::{HarfbuzzObject, Owned, Shared, Tag}; /// A wrapper around `hb_face_t`. /// /// An excerpt from harfbuzz documentation: /// > Font face is objects represent a single face in a font family. More /// > exactly, a font face represents a single face in a binary font file. Font /// > faces are typically built from a binary blob and a face index. Font faces /// > are used to create fonts. #[derive(Debug)] pub struct Face<'a> { raw: NonNull, marker: PhantomData<&'a [u8]>, } impl<'a> Face<'a> { /// Create a new `Face` from the data. /// /// If `data` is not a valid font then this function returns the empty face. pub fn new>>>(data: T, index: u32) -> Owned> { let blob = data.into(); let hb_face = unsafe { hb::hb_face_create(blob.as_raw(), index) }; unsafe { Owned::from_raw(hb_face) } } /// Returns a "null" face. pub fn empty() -> Owned> { let hb_face = unsafe { hb::hb_face_get_empty() }; unsafe { Owned::from_raw(hb_face) } } /// Create a new face from the contents of the file at `path`. /// /// This function reads the contents of the file at `path` into memory, /// creates a `Blob` and then calls `Face::new`. /// /// See also the discussion in `Blob::from_file`. pub fn from_file>(path: P, index: u32) -> std::io::Result>> { let blob = Blob::from_file(path)?; Ok(Face::new(blob, index)) } /// Create a face from the bytes of a given slice and an index specifying /// which font to read from an OpenType font collection. pub fn from_bytes<'b>(bytes: &'b [u8], index: u32) -> Owned> { let blob = Blob::with_bytes(bytes); Face::new(blob, index) } /// Create a new face from a closure that returns a raw /// [`Blob`](struct.Blob.html) of table data. pub fn from_table_func<'b, F>(func: F) -> Owned> where F: 'b + Send + Sync + FnMut(Tag) -> Option>>, { extern "C" fn destroy_box(ptr: *mut c_void) { unsafe { Box::from_raw(ptr as *mut U) }; } extern "C" fn table_func<'b, F>( _: *mut hb::hb_face_t, tag: hb::hb_tag_t, user_data: *mut c_void, ) -> *mut hb::hb_blob_t where F: FnMut(Tag) -> Option>>, { let tag = Tag(tag); let closure = unsafe { &mut *(user_data as *mut F) }; let blob = closure(tag); match blob { Some(blob) => Shared::into_raw(blob), None => std::ptr::null_mut(), } } let boxed_closure = Box::new(func); unsafe { let face = hb::hb_face_create_for_tables( Some(table_func::<'b, F>), Box::into_raw(boxed_closure) as *mut _, Some(destroy_box::), ); Owned::from_raw(face) } } pub fn face_data(&self) -> Shared> { unsafe { let raw_blob = hb::hb_face_reference_blob(self.as_raw()); Shared::from_raw_owned(raw_blob) } } /// Returns the slice of bytes for the table named `tag` or None if there is /// no table with `tag`. pub fn table_with_tag(&self, tag: impl Into) -> Option>> { unsafe { let raw_blob = hb::hb_face_reference_table(self.as_raw(), tag.into().0); if raw_blob.is_null() { None } else { let blob: Shared> = Shared::from_raw_owned(raw_blob); if blob.is_empty() { None } else { Some(blob) } } } } pub fn index(&self) -> u32 { unsafe { hb::hb_face_get_index(self.as_raw()) } } pub fn set_upem(&mut self, upem: u32) { unsafe { hb::hb_face_set_upem(self.as_raw(), upem) }; } pub fn upem(&self) -> u32 { unsafe { hb::hb_face_get_upem(self.as_raw()) } } pub fn set_glyph_count(&mut self, count: u32) { unsafe { hb::hb_face_set_glyph_count(self.as_raw(), count) }; } /// Returns the number of glyphs contained in the face. pub fn glyph_count(&self) -> u32 { unsafe { hb::hb_face_get_glyph_count(self.as_raw()) } } } unsafe impl<'a> HarfbuzzObject for Face<'a> { type Raw = hb::hb_face_t; unsafe fn from_raw(raw: *const hb::hb_face_t) -> Self { Face { raw: NonNull::new(raw as *mut _).unwrap(), marker: PhantomData, } } fn as_raw(&self) -> *mut Self::Raw { self.raw.as_ptr() } unsafe fn reference(&self) { hb::hb_face_reference(self.as_raw()); } unsafe fn dereference(&self) { hb::hb_face_destroy(self.as_raw()); } } unsafe impl<'a> Send for Face<'a> {} unsafe impl<'a> Sync for Face<'a> {} #[cfg(test)] mod tests { use super::*; #[test] fn test_face_from_table_func() { let face = Face::from_table_func(|table_tag| { let content = format!("{}-table", table_tag); Some(content.into_bytes().into()) }); let maxp_table = face.table_with_tag(b"maxp").unwrap(); assert_eq!(maxp_table.as_ref(), b"maxp-table"); let maxp_table = face.table_with_tag(b"hhea").unwrap(); assert_eq!(&maxp_table.as_ref(), b"hhea-table"); } } harfbuzz_rs-1.2.0/src/font.rs010064400007650000024000000347011370277351200144070ustar0000000000000000use crate::hb; use std; use std::ptr::NonNull; use std::os::raw::c_void; use crate::common::{HarfbuzzObject, Owned, Shared}; use crate::face::Face; pub use crate::font_funcs::FontFuncs; use crate::font_funcs::FontFuncsImpl; use std::ffi::CStr; use std::marker::PhantomData; pub type Glyph = u32; pub type Position = hb::hb_position_t; #[repr(C)] #[derive(Debug, Copy, Clone, Default)] pub struct FontExtents { pub ascender: Position, pub descender: Position, pub line_gap: Position, pub(crate) reserved: [Position; 9], } impl FontExtents { pub fn new(ascender: Position, descender: Position, line_gap: Position) -> FontExtents { FontExtents { ascender, descender, line_gap, ..Default::default() } } pub fn into_raw(self) -> hb::hb_font_extents_t { unsafe { std::mem::transmute(self) } } pub fn from_raw(raw: hb::hb_font_extents_t) -> FontExtents { unsafe { std::mem::transmute(raw) } } } pub type GlyphExtents = hb::hb_glyph_extents_t; pub(crate) extern "C" fn destroy_box(ptr: *mut c_void) { unsafe { Box::from_raw(ptr as *mut U) }; } /// A type representing a single font (i.e. a specific combination of typeface /// and typesize). /// /// It safely wraps `hb_font_t`. /// /// # Font Funcs /// /// A font is one of the most important structures in harfbuzz. It coordinates /// how glyph information is accessed during shaping. This is done through /// so-called font funcs. /// /// You can manually define new font funcs according to your needs, in most /// cases though the default font funcs provided by HarfBuzz will suffice. In /// that case the creation of a usable font amounts to calling the `Font::new` /// constructor with the desired `Face`. /// /// # Parents and Children /// /// Every font except the empty font has a parent font. If a font does not have /// some font func set, it will automatically use the parent's implementation of /// that font func. This behavior is useful to effectively "subclass" font /// objects to use different font function implementations for some font funcs /// while reusing the parent's implementation for the remaining funcs. /// /// Since every font created by `Font::new` by default uses HarfBuzz's internal /// font funcs they can be used as a fallback mechanism by only customizing the /// font funcs of a sub-font. /// /// # Examples /// /// Create a simple font from a `Face` using the default font funcs: /// /// ``` /// use harfbuzz_rs::*; /// # use std::path::PathBuf; /// # let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); /// # path.push("testfiles/SourceSansVariable-Roman.ttf"); /// let face = Face::from_file(path, 0).expect("Error reading font file."); /// let font = Font::new(face); /// ``` #[derive(Debug, PartialEq, Eq)] pub struct Font<'a> { raw: NonNull, marker: PhantomData<&'a hb::hb_font_t>, } impl<'a> Font<'a> { /// Create a new font from the specified `Face`. /// /// This is the default constructor of `Font`. In many cases it is the best /// choice if you simply want to shape some text. /// /// The default parent of a font created by this function is the empty font. /// /// # Font Functions /// /// The font returned by this function uses the font funcs that come with /// HarfBuzz for OpenType Fonts. The font funcs can be overwritten using /// `Font::set_font_funcs`. /// /// # Errors /// /// If for some reason no valid font can be constructed this function will /// return the empty font. pub fn new>>>(face: T) -> Owned { unsafe { let face = face.into(); let raw_font = hb::hb_font_create(face.as_raw()); // set default font funcs for a completely new font hb::hb_ot_font_set_funcs(raw_font); Owned::from_raw(raw_font) } } /// Returns an empty font. /// /// This can be useful when you need a dummy font for whatever reason. Any /// function you call on the empty font will return some reasonable default /// value. An empty font is the only font whose `.parent()` method returns /// `None`. pub fn empty() -> Owned { unsafe { let raw_font = hb::hb_font_get_empty(); Owned::from_raw(raw_font) } } /// Create a new sub font from the current font that by default inherits its /// parent font's face, scale, ppem and font funcs. /// /// The sub-font's parent will be the font on which this method is called. /// /// Creating sub-fonts is especially useful if you want to overwrite some of /// the font funcs of an already existing font. /// /// # Examples /// ``` /// use harfbuzz_rs::*; /// # use std::path::PathBuf; /// # let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); /// # path.push("testfiles/SourceSansVariable-Roman.ttf"); /// let face = Face::from_file(path, 0).expect("Error reading font file."); /// let font = Font::new(face).to_shared(); /// /// let sub_font = Font::create_sub_font(font.clone()); /// // we know that sub_font has a parent /// assert_eq!(sub_font.parent().unwrap(), font); /// ``` pub fn create_sub_font>>(font: T) -> Owned { unsafe { Owned::from_raw(hb::hb_font_create_sub_font(font.into().as_raw())) } } /// Returns a shared pointer to the parent font. /// /// If `self` is the empty font it returns `None`. /// /// # See also /// /// [`create_sub_font`](./struct.Font.html#method.create_sub_font) /// /// # Examples /// /// The empty font (and only it) has no parent: /// /// ``` /// use harfbuzz_rs::Font; /// /// let font = Font::empty(); /// assert_eq!(font.parent(), None); /// ``` pub fn parent(&self) -> Option> { unsafe { let parent = hb::hb_font_get_parent(self.as_raw()); if parent.is_null() { // hb_font_get_parent returns null-ptr if called on the empty font. None } else { Some(Shared::from_raw_ref(parent)) } } } /// Returns a shared pointer to the face from which this font was created. pub fn face(&self) -> Shared> { unsafe { Shared::from_raw_ref(hb::hb_font_get_face(self.as_raw())) } } /// Returns the EM scale of the font. pub fn scale(&self) -> (i32, i32) { let mut result = (0i32, 0i32); unsafe { hb::hb_font_get_scale(self.as_raw(), &mut result.0, &mut result.1) }; result } /// Sets the EM scale of the font. pub fn set_scale(&mut self, x: i32, y: i32) { unsafe { hb::hb_font_set_scale(self.as_raw(), x, y) }; } pub fn ppem(&self) -> (u32, u32) { let mut result = (0u32, 0u32); unsafe { hb::hb_font_get_ppem(self.as_raw(), &mut result.0, &mut result.1) }; result } pub fn set_ppem(&mut self, x: u32, y: u32) { unsafe { hb::hb_font_set_ppem(self.as_raw(), x, y) }; } /// Sets the font functions that this font will have from a value that /// implements [`FontFuncs`](./font_funcs/trait.FontFuncs.html). pub fn set_font_funcs(&mut self, funcs: FuncsType) where FuncsType: 'a + Send + Sync + FontFuncs, { let funcs_impl: Owned> = FontFuncsImpl::from_trait_impl(); let font_data = Box::new(funcs); unsafe { hb::hb_font_set_funcs( self.as_raw(), funcs_impl.as_raw(), Box::into_raw(font_data) as *mut _, Some(destroy_box::), ) }; } // scale from parent font pub(crate) fn parent_scale_x_distance(&self, f: impl Fn(&Font<'_>) -> Position) -> Position { let x_scale = self.scale().0; if let Some(parent) = self.parent() { let parent_x_scale = parent.scale().0; if parent_x_scale != x_scale { (f(&parent) as i64 * x_scale as i64 / parent_x_scale as i64) as Position } else { f(&parent) } } else { 0 } } // scale from parent font pub(crate) fn parent_scale_y_distance(&self, f: impl Fn(&Font<'_>) -> Position) -> Position { let y_scale = self.scale().0; if let Some(parent) = self.parent() { let parent_y_scale = parent.scale().0; if parent_y_scale != y_scale { (f(&parent) as i64 * y_scale as i64 / parent_y_scale as i64) as Position } else { f(&parent) } } else { 0 } } // scale from parent font pub(crate) fn parent_scale_position(&self, v: (Position, Position)) -> (Position, Position) { ( self.parent_scale_x_distance(|_| v.0), self.parent_scale_y_distance(|_| v.1), ) } pub fn get_font_h_extents(&self) -> Option { unsafe { let mut extents = FontExtents::default(); let result = hb::hb_font_get_h_extents( self.as_raw(), &mut extents as *mut FontExtents as *mut _, ); if result == 1 { Some(extents) } else { None } } } pub fn get_font_v_extents(&self) -> Option { unsafe { let mut extents = std::mem::zeroed::(); let result = hb::hb_font_get_v_extents( self.as_raw(), &mut extents as *mut FontExtents as *mut _, ); if result == 1 { Some(extents) } else { None } } } pub fn get_nominal_glyph(&self, c: char) -> Option { unsafe { let mut glyph = 0; let result = hb::hb_font_get_nominal_glyph(self.as_raw(), c as u32, &mut glyph); if result == 1 { Some(glyph) } else { None } } } pub fn get_variation_glyph(&self, c: char, v: char) -> Option { unsafe { let mut glyph = 0; let result = hb::hb_font_get_variation_glyph(self.as_raw(), c as u32, v as u32, &mut glyph); if result == 1 { Some(glyph) } else { None } } } /// Get the horizontal advance width of a glyph. pub fn get_glyph_h_advance(&self, glyph: Glyph) -> Position { unsafe { hb::hb_font_get_glyph_h_advance(self.as_raw(), glyph) } } /// Get the vertical advance width of a glyph. pub fn get_glyph_v_advance(&self, glyph: Glyph) -> Position { unsafe { hb::hb_font_get_glyph_v_advance(self.as_raw(), glyph) } } pub fn get_glyph_h_origin(&self, glyph: Glyph) -> Option<(Position, Position)> { unsafe { let mut pos = (0, 0); let result = hb::hb_font_get_glyph_h_origin(self.as_raw(), glyph, &mut pos.0, &mut pos.1); if result == 1 { Some(pos) } else { None } } } pub fn get_glyph_v_origin(&self, glyph: Glyph) -> Option<(Position, Position)> { unsafe { let mut pos = (0, 0); let result = hb::hb_font_get_glyph_v_origin(self.as_raw(), glyph, &mut pos.0, &mut pos.1); if result == 1 { Some(pos) } else { None } } } pub fn get_glyph_extents(&self, glyph: Glyph) -> Option { unsafe { let mut extents = std::mem::zeroed::(); let result = hb::hb_font_get_glyph_extents(self.as_raw(), glyph, &mut extents); if result == 1 { Some(extents) } else { None } } } pub fn get_glyph_contour_point( &self, glyph: Glyph, point_index: u32, ) -> Option<(Position, Position)> { unsafe { let mut pos = (0, 0); let result = hb::hb_font_get_glyph_contour_point( self.as_raw(), glyph, point_index, &mut pos.0, &mut pos.1, ); if result == 1 { Some(pos) } else { None } } } pub fn get_glyph_name(&self, glyph: Glyph) -> Option { let mut buffer = [0; 256]; let result = unsafe { hb::hb_font_get_glyph_name( self.as_raw(), glyph, buffer.as_mut_ptr() as *mut _, buffer.len() as u32, ) }; if result == 1 { let cstr = unsafe { CStr::from_ptr(buffer.as_ptr()) }; cstr.to_str().ok().map(|y| y.to_string()) } else { None } } pub fn get_glyph_from_name(&self, name: &str) -> Option { unsafe { let mut glyph = 0; let result = hb::hb_font_get_glyph_from_name( self.as_raw(), name.as_ptr() as *mut _, name.len() as i32, &mut glyph, ); if result == 1 { Some(glyph) } else { None } } } } unsafe impl<'a> Send for Font<'a> {} unsafe impl<'a> Sync for Font<'a> {} unsafe impl<'a> HarfbuzzObject for Font<'a> { type Raw = hb::hb_font_t; unsafe fn from_raw(raw: *const Self::Raw) -> Self { Font { raw: NonNull::new(raw as *mut _).unwrap(), marker: PhantomData, } } fn as_raw(&self) -> *mut Self::Raw { self.raw.as_ptr() } unsafe fn reference(&self) { hb::hb_font_reference(self.as_raw()); } unsafe fn dereference(&self) { hb::hb_font_destroy(self.as_raw()); } } impl<'a> Default for Owned> { fn default() -> Self { Font::empty() } } impl<'a> Default for Shared> { fn default() -> Self { Font::empty().into() } } #[cfg(test)] mod test { use super::*; use crate::tests::assert_memory_layout_equal; #[test] fn test_font_extents_layout() { assert_memory_layout_equal::() } } harfbuzz_rs-1.2.0/src/font_funcs.rs010064400007650000024000000504611370277351200156060ustar0000000000000000// Copyright (c) 2018 Manuel Reinhardt // // This software is released under the MIT License. // https://opensource.org/licenses/MIT //! Contains the `FontFuncs` trait. //! //! In the future there may be exposed other ways to create font funcs. use crate::font::destroy_box; use crate::{Font, FontExtents, Glyph, GlyphExtents, HarfbuzzObject, Owned, Position, Shared}; use crate::hb; use std::os::raw::c_void; use std; use std::ffi::{CStr, CString}; use std::fmt; use std::io::Write; use std::marker::PhantomData; use std::panic; use std::ptr::NonNull; /// This Trait specifies the font callbacks that harfbuzz uses for its shaping. /// You shouldn't call these functions yourself. They are exposed through the /// `Font` wrapper. /// /// No function in this trait needs to be implemented, the default /// implementations simply return the parent font's data. If a `Font` is created /// directly from a face, its parent is the empty `Font` which returns null /// values for every font func. #[allow(unused_variables)] pub trait FontFuncs { fn get_font_h_extents(&self, font: &Font<'_>) -> Option { font.parent()? .get_font_h_extents() .map(|extents| FontExtents { ascender: font.parent_scale_y_distance(|_| extents.ascender), descender: font.parent_scale_y_distance(|_| extents.descender), line_gap: font.parent_scale_y_distance(|_| extents.line_gap), ..extents }) } fn get_font_v_extents(&self, font: &Font<'_>) -> Option { font.parent()? .get_font_v_extents() .map(|extents| FontExtents { ascender: font.parent_scale_y_distance(|_| extents.ascender), descender: font.parent_scale_y_distance(|_| extents.descender), line_gap: font.parent_scale_y_distance(|_| extents.line_gap), ..extents }) } fn get_nominal_glyph(&self, font: &Font<'_>, unicode: char) -> Option { font.parent()?.get_nominal_glyph(unicode) } fn get_variation_glyph( &self, font: &Font<'_>, unicode: char, variation_sel: char, ) -> Option { font.parent()?.get_variation_glyph(unicode, variation_sel) } fn get_glyph_h_advance(&self, font: &Font<'_>, glyph: Glyph) -> Position { font.parent_scale_x_distance(|parent| parent.get_glyph_h_advance(glyph)) } fn get_glyph_v_advance(&self, font: &Font<'_>, glyph: Glyph) -> Position { font.parent_scale_y_distance(|parent| parent.get_glyph_v_advance(glyph)) } fn get_glyph_h_origin(&self, font: &Font<'_>, glyph: Glyph) -> Option<(Position, Position)> { font.parent()? .get_glyph_h_origin(glyph) .map(|x| font.parent_scale_position(x)) } fn get_glyph_v_origin(&self, font: &Font<'_>, glyph: Glyph) -> Option<(Position, Position)> { font.parent()? .get_glyph_v_origin(glyph) .map(|x| font.parent_scale_position(x)) } fn get_glyph_extents(&self, font: &Font<'_>, glyph: Glyph) -> Option { font.parent()? .get_glyph_extents(glyph) .map(|extents| GlyphExtents { x_bearing: font.parent_scale_x_distance(|_| extents.x_bearing), y_bearing: font.parent_scale_y_distance(|_| extents.y_bearing), width: font.parent_scale_x_distance(|_| extents.width), height: font.parent_scale_y_distance(|_| extents.height), }) } fn get_glyph_contour_point( &self, font: &Font<'_>, glyph: Glyph, point_index: u32, ) -> Option<(Position, Position)> { font.parent()? .get_glyph_contour_point(glyph, point_index) .map(|x| font.parent_scale_position(x)) } fn get_glyph_name(&self, font: &Font<'_>, glyph: Glyph) -> Option { font.parent()?.get_glyph_name(glyph) } fn get_glyph_from_name(&self, font: &Font<'_>, name: &str) -> Option { font.parent()?.get_glyph_from_name(name) } } macro_rules! hb_callback { ($func_name:ident<$($arg:ident: $datatype:ty),*> -> $ret:ty { $(argument $closure_arg:ty => $expr:expr,)* return $closure_ret_id:ident: $closure_ret:ty => $ret_expr:expr }) => { #[allow(clippy::let_and_return)] extern "C" fn $func_name( font: *mut hb::hb_font_t, font_data: *mut c_void, $( $arg: $datatype, )* closure_data: *mut c_void, ) -> $ret where F: Fn(&Font<'_>, &T, $($closure_arg),*) -> $closure_ret { let catch_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { let font_data = unsafe { &*(font_data as *const T) }; let font = unsafe { Font::from_raw(font) }; let closure = unsafe { &mut *(closure_data as *mut F) }; let $closure_ret_id = closure(&font, font_data, $($expr),*); $ret_expr })); match catch_result { Ok(val) => val, Err(_) => { // TODO: Log error Default::default() } } } }; } hb_callback!( rust_get_font_extents_closure -> hb::hb_bool_t { return value: Option => { if let Some(extents) = value { unsafe { *metrics = extents.into_raw() }; 1 } else { 0 } } } ); hb_callback!( rust_get_nominal_glyph_closure< unicode: hb::hb_codepoint_t, glyph: *mut hb::hb_codepoint_t> -> hb::hb_bool_t { argument char => { match std::char::from_u32(unicode) { Some(character) => character, None => return 0 } }, return result_glyph: Option => { if let Some(g) = result_glyph { unsafe { *glyph = g } 1 } else { 0 } } } ); hb_callback!( rust_get_variation_glyph_closure< unicode: hb::hb_codepoint_t, variation_selector: hb::hb_codepoint_t, glyph: *mut hb::hb_codepoint_t> -> hb::hb_bool_t { argument char => { match std::char::from_u32(unicode) { Some(character) => character, None => return 0 } }, argument char => { match std::char::from_u32(variation_selector) { Some(selector) => selector, None => return 0 } }, return result_glyph: Option => { if let Some(g) = result_glyph { unsafe { *glyph = g } 1 } else { 0 } } } ); hb_callback!( rust_get_glyph_advance_closure -> Position { argument Glyph => glyph, return pos: Position => pos } ); hb_callback!( rust_get_glyph_origin_closure< glyph: hb::hb_codepoint_t, x: *mut Position, y: *mut Position> -> hb::hb_bool_t { argument Glyph => glyph, return pos: Option<(Position, Position)> => { if let Some((x_origin, y_origin)) = pos { unsafe { *x = x_origin; *y = y_origin; } 1 } else { 0 } } } ); hb_callback!( rust_get_glyph_extents_closure< glyph: hb::hb_codepoint_t, extents: *mut hb::hb_glyph_extents_t> -> hb::hb_bool_t { argument Glyph => glyph, return value: Option => { match value { Some(result) => { unsafe { *extents = result }; 1 } None => 0, } } } ); hb_callback!( rust_get_glyph_contour_point_closure< glyph: hb::hb_codepoint_t, point: u32, x: *mut Position, y: *mut Position> -> hb::hb_bool_t { argument Glyph => glyph, argument u32 => point, return value: Option<(Position, Position)> => { match value { Some((x_origin, y_origin)) => unsafe { *x = x_origin; *y = y_origin; 1 }, None => 0 } } } ); hb_callback!( rust_get_glyph_name_closure< glyph: hb::hb_codepoint_t, name: *mut std::os::raw::c_char, size: u32> -> hb::hb_bool_t { argument Glyph => glyph, return value: Option => { let mut name = unsafe { std::slice::from_raw_parts_mut(name as *mut u8, size as usize) }; let result = value .and_then(|string| CString::new(string).ok()) .and_then(|cstr| name.write_all(cstr.as_bytes_with_nul()).ok()); if result.is_some() { 1 } else { name[0] = 0; 0 } } } ); hb_callback!( rust_get_glyph_from_name_closure< name: *const std::os::raw::c_char, size: i32, glyph: *mut hb::hb_codepoint_t> -> hb::hb_bool_t { argument &str => { let string = match size { // `name` is null-terminated -1 => unsafe { CStr::from_ptr(name).to_str().ok() }, // `name` has length = `size` i if i >= 1 => unsafe { std::str::from_utf8(std::slice::from_raw_parts(name as *const u8, size as usize)).ok() }, _ => None, }; match string { Some(string) => string, None => return 0, } }, return result_glyph: Option => { if let Some(g) = result_glyph { unsafe { *glyph = g } 1 } else { 0 } } } ); /// A `FontFuncsImpl` contains implementations of the font callbacks that /// harfbuzz uses. /// /// It supports two ways to assign functions to specific font funcs. Either you /// can set a unique closure per font func or set the font funcs from a type /// that implements the `FontFuncs` trait using the `from_trait_impl` /// constructor. /// /// # Examples /// /// Create a `FontFuncsImpl` from individual closures: /// /// ```ignore /// use harfbuzz_rs::*; /// use harfbuzz_rs::font_funcs::FontFuncsImpl; /// use std::mem; /// /// let mut ffuncs: Owned> = FontFuncsImpl::new(); /// let value = 113; /// ffuncs.set_font_h_extents_func(|_, _| { /// Some(FontExtents { ascender: value, .. unsafe { mem::zeroed() } }) /// }); /// ``` /// /// Create a `FontFuncsImpl` from a type that implements `FontFuncs`: /// /// ```ignore /// use harfbuzz_rs::*; /// use harfbuzz_rs::font_funcs::FontFuncsImpl; /// /// // Dummy struct implementing FontFuncs /// struct MyFontData { /// value: i32, /// } /// impl FontFuncs for MyFontData { /// fn get_glyph_h_advance(&self, _: &Font, _: Glyph) -> Position { /// self.value /// } /// // implementations of other functions... /// } /// /// let font_funcs: Owned> = FontFuncsImpl::from_trait_impl(); /// ``` pub(crate) struct FontFuncsImpl { raw: NonNull, marker: PhantomData, } impl FontFuncsImpl { /// Returns an empty `FontFuncsImpl`. Every font callback of the returned /// `FontFuncsImpl` gives a null value regardless of its input. #[allow(unused)] pub fn empty() -> Shared> { let raw = unsafe { hb::hb_font_funcs_get_empty() }; unsafe { Shared::from_raw_ref(raw) } } } impl FontFuncsImpl { /// Create a new `FontFuncsImpl` from the `FontFuncs` trait implementation /// of `T`. /// /// # Examples /// /// ```ignore /// use harfbuzz_rs::*; /// use harfbuzz_rs::font_funcs::FontFuncsImpl; /// /// // Dummy struct implementing FontFuncs /// struct MyFontData { /// value: i32, /// } /// impl FontFuncs for MyFontData { /// fn get_glyph_h_advance(&self, _: &Font, _: Glyph) -> Position { /// self.value /// } /// // implement other trait functions... /// } /// /// let font_funcs: Owned> = FontFuncsImpl::from_trait_impl(); /// ``` /// pub fn from_trait_impl() -> Owned> { let mut ffuncs = FontFuncsImpl::new(); ffuncs.set_trait_impl(); ffuncs } fn set_trait_impl(&mut self) { self.set_font_h_extents_func(|font, data| data.get_font_h_extents(font)); self.set_font_v_extents_func(|font, data| data.get_font_v_extents(font)); self.set_nominal_glyph_func(|font, data, chr| data.get_nominal_glyph(font, chr)); self.set_variation_glyph_func(|font, data, chr, var| { data.get_variation_glyph(font, chr, var) }); self.set_glyph_h_advance_func(|font, data, glyph| data.get_glyph_h_advance(font, glyph)); self.set_glyph_v_advance_func(|font, data, glyph| data.get_glyph_v_advance(font, glyph)); self.set_glyph_h_origin_func(|font, data, glyph| data.get_glyph_h_origin(font, glyph)); self.set_glyph_v_origin_func(|font, data, glyph| data.get_glyph_v_origin(font, glyph)); self.set_glyph_extents_func(|font, data, glyph| data.get_glyph_extents(font, glyph)); self.set_glyph_contour_point_func(|font, data, glyph, index| { data.get_glyph_contour_point(font, glyph, index) }); self.set_glyph_name_func(|font, data, glyph| data.get_glyph_name(font, glyph)); self.set_glyph_from_name_func(|font, data, name| data.get_glyph_from_name(font, name)); } } impl FontFuncsImpl { pub fn new() -> Owned> { unsafe { Owned::from_raw(hb::hb_font_funcs_create()) } } pub fn set_font_h_extents_func(&mut self, func: F) where F: Fn(&Font<'_>, &T) -> Option, { let user_data = Box::new(func); unsafe { hb::hb_font_funcs_set_font_h_extents_func( self.as_raw(), Some(rust_get_font_extents_closure::), Box::into_raw(user_data) as *mut _, Some(destroy_box::), ); } } pub fn set_font_v_extents_func(&mut self, func: F) where F: Fn(&Font<'_>, &T) -> Option, { let user_data = Box::new(func); unsafe { hb::hb_font_funcs_set_font_v_extents_func( self.as_raw(), Some(rust_get_font_extents_closure::), Box::into_raw(user_data) as *mut _, Some(destroy_box::), ); } } pub fn set_nominal_glyph_func(&mut self, func: F) where F: Fn(&Font<'_>, &T, char) -> Option, { let user_data = Box::new(func); unsafe { hb::hb_font_funcs_set_nominal_glyph_func( self.as_raw(), Some(rust_get_nominal_glyph_closure::), Box::into_raw(user_data) as *mut _, Some(destroy_box::), ); } } pub fn set_variation_glyph_func(&mut self, func: F) where F: Fn(&Font<'_>, &T, char, char) -> Option, { let user_data = Box::new(func); unsafe { hb::hb_font_funcs_set_variation_glyph_func( self.as_raw(), Some(rust_get_variation_glyph_closure::), Box::into_raw(user_data) as *mut _, Some(destroy_box::), ); } } pub fn set_glyph_h_advance_func(&mut self, func: F) where F: Fn(&Font<'_>, &T, Glyph) -> Position, { let user_data = Box::new(func); unsafe { hb::hb_font_funcs_set_glyph_h_advance_func( self.as_raw(), Some(rust_get_glyph_advance_closure::), Box::into_raw(user_data) as *mut _, Some(destroy_box::), ); } } pub fn set_glyph_v_advance_func(&mut self, func: F) where F: Fn(&Font<'_>, &T, Glyph) -> Position, { let user_data = Box::new(func); unsafe { hb::hb_font_funcs_set_glyph_v_advance_func( self.as_raw(), Some(rust_get_glyph_advance_closure::), Box::into_raw(user_data) as *mut _, Some(destroy_box::), ); } } pub fn set_glyph_h_origin_func(&mut self, func: F) where F: Fn(&Font<'_>, &T, Glyph) -> Option<(Position, Position)>, { let user_data = Box::new(func); unsafe { hb::hb_font_funcs_set_glyph_h_origin_func( self.as_raw(), Some(rust_get_glyph_origin_closure::), Box::into_raw(user_data) as *mut _, Some(destroy_box::), ); } } pub fn set_glyph_v_origin_func(&mut self, func: F) where F: Fn(&Font<'_>, &T, Glyph) -> Option<(Position, Position)>, { let user_data = Box::new(func); unsafe { hb::hb_font_funcs_set_glyph_v_origin_func( self.as_raw(), Some(rust_get_glyph_origin_closure::), Box::into_raw(user_data) as *mut _, Some(destroy_box::), ); } } pub fn set_glyph_extents_func(&mut self, func: F) where F: Fn(&Font<'_>, &T, Glyph) -> Option, { let user_data = Box::new(func); unsafe { hb::hb_font_funcs_set_glyph_extents_func( self.as_raw(), Some(rust_get_glyph_extents_closure::), Box::into_raw(user_data) as *mut _, Some(destroy_box::), ); } } pub fn set_glyph_contour_point_func(&mut self, func: F) where F: Fn(&Font<'_>, &T, Glyph, u32) -> Option<(Position, Position)>, { let user_data = Box::new(func); unsafe { hb::hb_font_funcs_set_glyph_contour_point_func( self.as_raw(), Some(rust_get_glyph_contour_point_closure::), Box::into_raw(user_data) as *mut _, Some(destroy_box::), ); } } pub fn set_glyph_name_func(&mut self, func: F) where F: Fn(&Font<'_>, &T, Glyph) -> Option, { let user_data = Box::new(func); unsafe { hb::hb_font_funcs_set_glyph_name_func( self.as_raw(), Some(rust_get_glyph_name_closure::), Box::into_raw(user_data) as *mut _, Some(destroy_box::), ); } } pub fn set_glyph_from_name_func(&mut self, func: F) where F: Fn(&Font<'_>, &T, &str) -> Option, { let user_data = Box::new(func); unsafe { hb::hb_font_funcs_set_glyph_from_name_func( self.as_raw(), Some(rust_get_glyph_from_name_closure::), Box::into_raw(user_data) as *mut _, Some(destroy_box::), ); } } } impl fmt::Debug for FontFuncsImpl { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FontFuncsImpl") .field("raw", &self.as_raw()) .finish() } } unsafe impl HarfbuzzObject for FontFuncsImpl { type Raw = hb::hb_font_funcs_t; unsafe fn from_raw(raw: *const Self::Raw) -> Self { FontFuncsImpl { raw: NonNull::new(raw as *mut _).unwrap(), marker: PhantomData, } } fn as_raw(&self) -> *mut Self::Raw { self.raw.as_ptr() } unsafe fn reference(&self) { hb::hb_font_funcs_reference(self.as_raw()); } unsafe fn dereference(&self) { hb::hb_font_funcs_destroy(self.as_raw()) } } unsafe impl Send for FontFuncsImpl {} unsafe impl Sync for FontFuncsImpl {} harfbuzz_rs-1.2.0/src/lib.rs010064400007650000024000000203421370300011700141640ustar0000000000000000//! `harfbuzz_rs` is a high-level interface to HarfBuzz, exposing its most important functionality //! in a safe manner using Rust. //! //! # What is HarfBuzz? //! HarfBuzz is a library for performing complex text layout. It does not perform any drawing. This //! is quite a low-level operation. If you want to simply draw some text on the screen choose //! another library. However if you want to build a library for drawing text on some canvas or //! need a lot of control on advanced text layout then this is the right library to use. //! //! # Getting Started //! //! To shape a simple string of text you just create a `Font` from a font file, fill a `Buffer` //! with some text and call the `shape` function. //! //! ``` //! use harfbuzz_rs::*; //! //! # fn try_main() -> Result<(), std::io::Error> { //! //! let path = "path/to/some/font_file.otf"; //! let index = 0; //< face index in the font file //! # let path = "testfiles/SourceSansVariable-Roman.ttf"; //! let face = Face::from_file(path, index)?; //! let mut font = Font::new(face); //! //! let buffer = UnicodeBuffer::new().add_str("Hello World!"); //! let output = shape(&font, buffer, &[]); //! //! // The results of the shaping operation are stored in the `output` buffer. //! //! let positions = output.get_glyph_positions(); //! let infos = output.get_glyph_infos(); //! //! # assert_eq!(positions.len(), 12); //! assert_eq!(positions.len(), infos.len()); //! //! // iterate over the shaped glyphs //! for (position, info) in positions.iter().zip(infos) { //! let gid = info.codepoint; //! let cluster = info.cluster; //! let x_advance = position.x_advance; //! let x_offset = position.x_offset; //! let y_offset = position.y_offset; //! //! // Here you would usually draw the glyphs. //! println!("gid{:?}={:?}@{:?},{:?}+{:?}", gid, cluster, x_advance, x_offset, y_offset); //! } //! //! # Ok(()) //! # } //! # //! # try_main().unwrap(); //! ``` //! This should print out something similar to the following: //! //! ```text //! gid41=0@741,0+0 //! gid70=1@421,0+0 //! gid77=2@258,0+0 //! gid77=3@253,0+0 //! gid80=4@510,0+0 //! gid1=5@227,0+0 //! gid56=6@874,0+0 //! gid80=7@498,0+0 //! gid83=8@367,0+0 //! gid77=9@253,0+0 //! gid69=10@528,0+0 //! gid2=11@276,0+0 //! ``` #![deny(missing_debug_implementations)] /// Reexported `harfbuzz_sys` crate to directly access the C API whenever no /// adequate wrapper is provided. // This will hopefully not cause backwards compability concerns since harfbuzz // tries to be backwards compatible. pub use harfbuzz_sys as hb; #[macro_use] extern crate bitflags; mod blob; mod buffer; mod common; mod face; mod font; pub mod font_funcs; #[cfg(feature = "rusttype")] pub mod rusttype; pub use crate::blob::*; pub use crate::buffer::*; pub use crate::common::*; pub use crate::face::*; pub use crate::font::*; use std::ops::{Bound, RangeBounds}; use std::os::raw::c_uint; pub(crate) fn start_end_range(range: impl RangeBounds) -> (c_uint, c_uint) { // We have to do careful bounds checking since c_uint may be of // different sizes on different platforms. We do assume that // sizeof(usize) >= sizeof(c_uint). const MAX_UINT: usize = c_uint::max_value() as usize; let start = match range.start_bound() { Bound::Included(&included) => included.min(MAX_UINT) as c_uint, Bound::Excluded(&excluded) => excluded.min(MAX_UINT - 1) as c_uint + 1, Bound::Unbounded => 0, }; let end = match range.end_bound() { Bound::Included(&included) => included.saturating_add(1).min(MAX_UINT) as c_uint, Bound::Excluded(&excluded) => excluded.min(MAX_UINT) as c_uint, Bound::Unbounded => c_uint::max_value(), }; (start, end) } /// A feature tag with an accompanying range specifying on which subslice of /// `shape`s input it should be applied. /// /// You can pass a slice of `Feature`s to `shape` that will be activated for the /// corresponding slices of input. /// /// # Examples /// /// Shape some text using the `calt` (Contextual Alternatives) feature. /// /// ``` /// use harfbuzz_rs::{Face, Font, UnicodeBuffer, shape, Feature, Tag}; /// /// let path = "testfiles/SourceSansVariable-Roman.ttf"; /// let face = Face::from_file(path, 0).expect("could not load face"); /// let font = Font::new(face); /// /// let buffer = UnicodeBuffer::new().add_str("Hello World!"); /// /// // contextual alternatives feature /// let feature_tag = b"calt"; /// /// // use the feature on the entire input /// let feature_range = 0..; /// let feature = Feature::new(feature_tag, 0, feature_range); /// /// let output = shape(&font, buffer, &[feature]); /// ``` #[derive(Debug, Copy, Clone)] #[repr(transparent)] pub struct Feature(hb::hb_feature_t); impl Feature { /// Create a new `Feature` struct. /// /// The feature will be applied with the given value to all glyphs which are /// in clusters contained in `range`. /// /// # Arguments /// /// - `tag`: The OpenType feature tag to use. /// - `value`: Some OpenType features accept different values to change /// their behaviour. /// - `range`: The cluster range that should be affected by this feature. pub fn new(tag: impl Into, value: u32, range: impl RangeBounds) -> Feature { let (start, end) = start_end_range(range); Feature(hb::hb_feature_t { tag: tag.into().0, value, start, end, }) } pub fn tag(&self) -> Tag { Tag(self.0.tag) } pub fn value(&self) -> u32 { self.0.value } pub fn start(&self) -> usize { self.0.start as usize } pub fn end(&self) -> usize { self.0.end as usize } } /// Shape the contents of the buffer using the provided font and activating all /// OpenType features given in `features`. /// /// This function consumes the `buffer` and returns a `GlyphBuffer` containing /// the resulting glyph indices and the corresponding positioning information. /// Once all the information from the `GlyphBuffer` has been processed as /// necessary you can reuse the `GlyphBuffer` as an `UnicodeBuffer` (using /// `GlyphBuffer::clear_contents`) and use that to call `shape` again with new /// data. /// /// By default some basic OpenType features are enabled according to the /// language and the script set in the buffer. /// /// # Arguments /// - `font` – a reference to the harfbuzz font used to shape the text. /// - `buffer` – a `UnicodeBuffer` that is filled with the text to be shaped and /// also contains metadata about the text in the form of segment properties. /// - `features` – a slice of additional features to activate pub fn shape(font: &Font<'_>, buffer: UnicodeBuffer, features: &[Feature]) -> GlyphBuffer { let buffer = buffer.guess_segment_properties(); unsafe { hb::hb_shape( font.as_raw(), buffer.0.as_raw(), features.as_ptr() as *mut _, features.len() as u32, ) }; GlyphBuffer(buffer.0) } #[cfg(test)] mod tests { use std::mem::{align_of, size_of}; pub(crate) fn assert_memory_layout_equal() { assert_eq!(size_of::(), size_of::()); assert_eq!(align_of::(), align_of::()); } #[test] fn it_works() {} fn assert_feature(feat: Feature, tag: Tag, value: u32, start: usize, end: usize) { assert_eq!(feat.tag(), tag); assert_eq!(feat.value(), value); assert_eq!(feat.start(), start); assert_eq!(feat.end(), end); } use super::{Feature, Tag}; #[test] fn feature_new() { let tag = b"abcd".into(); const UINT_MAX: usize = std::os::raw::c_uint::max_value() as usize; let feature = Feature::new(tag, 100, 2..100); assert_feature(feature, tag, 100, 2, 100); let feature = Feature::new(tag, 100, 2..=100); assert_feature(feature, tag, 100, 2, 101); let feature = Feature::new(tag, 100, 2..); assert_feature(feature, tag, 100, 2, UINT_MAX); let feature = Feature::new(tag, 100, ..100); assert_feature(feature, tag, 100, 0, 100); let feature = Feature::new(tag, 100, ..=100); assert_feature(feature, tag, 100, 0, 101); let feature = Feature::new(tag, 100, ..); assert_feature(feature, tag, 100, 0, UINT_MAX); } } harfbuzz_rs-1.2.0/src/rusttype.rs010064400007650000024000000122031361057304000153210ustar0000000000000000//! This module allows you to use rusttype to provide the font operations that harfbuzz needs. use crate::common::Tag; pub use rusttype::Error; use rusttype::Font as RTFont; use rusttype::{Codepoint, GlyphId, Scale}; use crate::face; use crate::font; use crate::font::{Font, FontFuncs, Glyph as GlyphIndex, GlyphExtents, Position}; use std; use std::fmt::Debug; use std::str::FromStr; // Work around weird rusttype scaling by reading the hhea table. fn get_font_height(font: &font::Font<'_>) -> Result { let face = font.face(); let tag = Tag::from_str("hhea").unwrap(); let hhea_table = face.table_with_tag(tag).ok_or(Error::IllFormed)?; if hhea_table.len() >= 8 { unsafe { let ascent_ptr = (&hhea_table)[4..6].as_ptr() as *const i16; let ascent = i16::from_be(*ascent_ptr); let descent_ptr = (&hhea_table)[6..8].as_ptr() as *const i16; let descent = i16::from_be(*descent_ptr); Ok(ascent as i32 - descent as i32) } } else { Err(Error::IllFormed) } } fn rusttype_font_from_face<'a>(face: &face::Face<'a>) -> Result, Error> { // It is unfortunate that we have to copy the face data here. let font_blob = face.face_data().as_ref().to_owned(); let index = face.index(); let collection = rusttype::FontCollection::from_bytes(font_blob)?; collection.font_at(index as usize) } fn rusttype_scale_from_hb_font(font: &font::Font<'_>) -> Result { let font_height = get_font_height(font)? as f32; let em_scale = font.scale(); let x_scale = em_scale.0 as f32; let y_scale = em_scale.1 as f32; Ok(Scale { x: font_height * x_scale / y_scale, y: font_height, }) } struct ScaledRusttypeFont<'a> { font: rusttype::Font<'a>, scale: Scale, } impl<'a> Debug for ScaledRusttypeFont<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ScaledRusttypeFont") .field("scale", &self.scale) .finish() } } impl<'a> ScaledRusttypeFont<'a> { fn from_hb_font<'b>(hb_font: &font::Font<'b>) -> Result, Error> { let font = rusttype_font_from_face(&hb_font.face())?; let scale = rusttype_scale_from_hb_font(hb_font)?; Ok(ScaledRusttypeFont { font, scale }) } } impl<'a> FontFuncs for ScaledRusttypeFont<'a> { fn get_glyph_h_advance(&self, _: &Font<'_>, glyph: GlyphIndex) -> Position { let glyph = self.font.glyph(GlyphId(glyph)); let glyph = glyph.scaled(self.scale); glyph.h_metrics().advance_width.round() as Position } fn get_glyph_extents(&self, _: &Font<'_>, glyph: GlyphIndex) -> Option { let glyph = self.font.glyph(GlyphId(glyph)); let glyph = glyph.scaled(self.scale); glyph.exact_bounding_box().map(|bbox| GlyphExtents { x_bearing: bbox.min.x.round() as i32, y_bearing: bbox.min.y.round() as i32, width: (bbox.max.x - bbox.min.x).round() as i32, height: (bbox.max.y - bbox.min.y).round() as i32, }) } fn get_nominal_glyph(&self, _: &font::Font<'_>, unicode: char) -> Option { let glyph = self.font.glyph(Codepoint(unicode as u32)); Some(glyph.id().0) } } use std::sync::Arc; /// Creates a new HarfBuzz `Font` object that uses RustType to provide font data. /// /// # Examples /// /// Create a basic font that uses rusttype font funcs: /// ``` /// use std::fs; /// use std::sync::Arc; /// /// use harfbuzz_rs::rusttype::create_harfbuzz_rusttype_font; /// /// let path = "testfiles/SourceSansVariable-Roman.ttf"; /// let bytes: Arc<[u8]> = fs::read(path).unwrap().into(); /// let font = create_harfbuzz_rusttype_font(bytes, 0); /// ``` pub fn create_harfbuzz_rusttype_font( bytes: impl Into>, index: u32, ) -> Result>, Error> { let bytes = bytes.into(); let face = crate::Face::new(bytes.clone(), index); let mut font = Font::new(face); let rt_font = rusttype::FontCollection::from_bytes(bytes)?.font_at(index as usize)?; let scaled_font = ScaledRusttypeFont { font: rt_font, scale: rusttype_scale_from_hb_font(&font)?, }; font.set_font_funcs(scaled_font); Ok(font) } /// Extends the harfbuzz font to allow setting RustType as font funcs provider. #[deprecated(since = "0.4.0")] pub trait SetRustTypeFuncs { /// Let a font use rusttype's font API for getting information like the /// advance width of some glyph or its extents. /// /// # Deprecated /// /// This function is deprecated because it doesn't fit well with the design /// of RustType (Calling this method requires to make a copy of the font /// data used). You should use `create_harfbuzz_rusttype_font` instead. #[deprecated(since = "0.4.0")] fn set_rusttype_funcs(&mut self) -> Result<(), Error>; } #[allow(deprecated)] impl<'a> SetRustTypeFuncs for Font<'a> { fn set_rusttype_funcs(&mut self) -> Result<(), Error> { let font_data = ScaledRusttypeFont::from_hb_font(self)?; self.set_font_funcs(font_data); Ok(()) } }