Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"files":{"Cargo.lock":"fe8cea3cb92f5bf33e18a3f13f12858daf4e63010e35a879801b9bc244eaa438","Cargo.toml":"61b4de821426cea8b389ea5a90f2dde761f7e42d5210da4a1bdcf08f72912512","LICENSE-APACHE":"a14f7d685a79c0f4fb64aee305527a6d5fa55f2401f4e6d688d7b1301868e6c0","LICENSE-MIT":"8a62fa27ffdccc3df19dce90f8bb3d572e27aecffc1a383d0c2601834dcec434","README.md":"4e9f4fdda14dd056c040d709190a0d09f4e9a304c9e979155377d9221b98e6c9","src/bbox.rs":"ed1d7236295cf1594fcd990da5d00c9c307cc7c866d2d5cc0ae49bb0934a3662","src/fixed.rs":"e2de1cfbafeda84a9a7f4440051b5408f45b9b0a63f0b48ff9b4d0f5f08cbf38","src/fword.rs":"b1dc28c20f52c4aecaf43b0fee6721837df27aaa70f0d62e79cefc1061d43fef","src/glyph_id.rs":"fc196ade245305e2b886f6e7d880e5b51d35bbc664de1d3599379712e758a50c","src/int24.rs":"d39f3bbb9c29709fab764061fe6f5ea9025891dcfabce3bc4da0b3fa511abe75","src/lib.rs":"72fa39746d599dd4cb1cff4f621fb44e2ec1a3ecd98fe8232908b7cb5cbd834d","src/longdatetime.rs":"9eecca052d6ff743ff0b3aa0d2a5c7505425e70fe01785234c43941eb346813c","src/name_id.rs":"d54c112264fd081f912ed79c3d65ba9a60f6d812fe007711a26de8e134b6c457","src/offset.rs":"cc3fa70dc4cbc78817067931ad922555d0a863e928e3be210555c7f02209a1ac","src/point.rs":"f80fb65c428826ce822326b763950ef95de32ed84eed10f13ed988c2c00a5ddb","src/raw.rs":"f1027f83b4be5bcfbe19a25e61e1d535ff7663f12f3dcc35e4b1ed2a9638bc0a","src/serde_test.rs":"c99346f679af516f78826b715038be616a63e3444fdcef39ff8d6d060cc3734c","src/tag.rs":"812c1523a80f508f716bce3d14d23e018c602f334e80e90d0799e8101d578c22","src/uint24.rs":"5c83e8fe48d321ff443aeb206d428ebac66714b5c62e552fe2f4cd81cd5b6cd9","src/version.rs":"436db7611071c97d4c9a1db04a15ac40c629529eefa9bcc1cad5b56029a6593a"},"package":"02a596f5713680923a2080d86de50fe472fb290693cf0f701187a1c8b36996b7"}

110
vendor/font-types/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,110 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "bytemuck"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "font-types"
version = "0.9.0"
dependencies = [
"bytemuck",
"serde",
"serde_json",
]
[[package]]
name = "itoa"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "proc-macro2"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "serde"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

55
vendor/font-types/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,55 @@
# 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 are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.75"
name = "font-types"
version = "0.9.0"
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Scalar types used in fonts."
readme = "README.md"
categories = ["text-processing"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/googlefonts/fontations"
[package.metadata.docs.rs]
all-features = true
[lib]
name = "font_types"
path = "src/lib.rs"
[dependencies.bytemuck]
version = "1.13.1"
features = [
"derive",
"min_const_generics",
]
optional = true
[dependencies.serde]
version = "1.0"
features = ["derive"]
optional = true
[dev-dependencies.serde_json]
version = "1.0"
[features]
bytemuck = ["dep:bytemuck"]
serde = ["dep:serde"]
std = []

67
vendor/font-types/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,67 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2019 Colin Rothfels
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

25
vendor/font-types/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2019 Colin Rothfels
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.

12
vendor/font-types/README.md vendored Normal file
View File

@@ -0,0 +1,12 @@
# font-types
This crate contains definitions of the basic types in the [OpenType
spec][opentype], as well as informal but useful types (such as a distinct type
for a glyph ID) and traits for encoding and decoding these types as big-endian
bytes.
These types are intended to be general purpose, and useable by other Rust
projects that work with font data.
[opentype]: https://docs.microsoft.com/en-us/typography/opentype/

34
vendor/font-types/src/bbox.rs vendored Normal file
View File

@@ -0,0 +1,34 @@
use core::ops::Mul;
/// Minimum and maximum extents of a rectangular region.
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BoundingBox<T> {
/// Minimum extent in the x direction-- the left side of a region.
pub x_min: T,
/// Minimum extent in the y direction. In a Y-up coordinate system,
/// which is used by fonts, this represents the bottom of a region.
pub y_min: T,
/// Maximum extent in the x direction-- the right side of a region.
pub x_max: T,
/// Maximum extend in the y direction. In a Y-up coordinate system,
/// which is used by fonts, this represents the top of the
/// region.
pub y_max: T,
}
impl<T> BoundingBox<T>
where
T: Mul<Output = T> + Copy,
{
/// Return a `BoundingBox` scaled by a scale factor of the same type
/// as the stored bounds.
pub fn scale(&self, factor: T) -> Self {
Self {
x_min: self.x_min * factor,
y_min: self.y_min * factor,
x_max: self.x_max * factor,
y_max: self.y_max * factor,
}
}
}

488
vendor/font-types/src/fixed.rs vendored Normal file
View File

@@ -0,0 +1,488 @@
//! fixed-point numerical types
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
// shared between Fixed, F26Dot6, F2Dot14, F4Dot12, F6Dot10
macro_rules! fixed_impl {
($name:ident, $bits:literal, $fract_bits:literal, $ty:ty) => {
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern, bytemuck::NoUninit))]
#[repr(transparent)]
#[doc = concat!(stringify!($bits), "-bit signed fixed point number with ", stringify!($fract_bits), " bits of fraction." )]
pub struct $name($ty);
impl $name {
/// Minimum value.
pub const MIN: Self = Self(<$ty>::MIN);
/// Maximum value.
pub const MAX: Self = Self(<$ty>::MAX);
/// This type's smallest representable value
pub const EPSILON: Self = Self(1);
/// Representation of 0.0.
pub const ZERO: Self = Self(0);
/// Representation of 1.0.
pub const ONE: Self = Self(1 << $fract_bits);
const INT_MASK: $ty = !0 << $fract_bits;
const ROUND: $ty = 1 << ($fract_bits - 1);
const FRACT_BITS: usize = $fract_bits;
/// Creates a new fixed point value from the underlying bit representation.
#[inline(always)]
pub const fn from_bits(bits: $ty) -> Self {
Self(bits)
}
/// Returns the underlying bit representation of the value.
#[inline(always)]
pub const fn to_bits(self) -> $ty {
self.0
}
//TODO: is this actually useful?
/// Returns the nearest integer value.
#[inline(always)]
pub const fn round(self) -> Self {
Self(self.0.wrapping_add(Self::ROUND) & Self::INT_MASK)
}
/// Returns the absolute value of the number.
#[inline(always)]
pub const fn abs(self) -> Self {
Self(self.0.abs())
}
/// Returns the largest integer less than or equal to the number.
#[inline(always)]
pub const fn floor(self) -> Self {
Self(self.0 & Self::INT_MASK)
}
/// Returns the fractional part of the number.
#[inline(always)]
pub const fn fract(self) -> Self {
Self(self.0 - self.floor().0)
}
/// Wrapping addition.
#[inline(always)]
pub fn wrapping_add(self, other: Self) -> Self {
Self(self.0.wrapping_add(other.0))
}
/// Saturating addition.
#[inline(always)]
pub const fn saturating_add(self, other: Self) -> Self {
Self(self.0.saturating_add(other.0))
}
/// Checked addition.
#[inline(always)]
pub fn checked_add(self, other: Self) -> Option<Self> {
self.0.checked_add(other.0).map(|inner| Self(inner))
}
/// Wrapping substitution.
#[inline(always)]
pub const fn wrapping_sub(self, other: Self) -> Self {
Self(self.0.wrapping_sub(other.0))
}
/// Saturating substitution.
#[inline(always)]
pub const fn saturating_sub(self, other: Self) -> Self {
Self(self.0.saturating_sub(other.0))
}
/// The representation of this number as a big-endian byte array.
#[inline(always)]
pub const fn to_be_bytes(self) -> [u8; $bits / 8] {
self.0.to_be_bytes()
}
}
impl Add for $name {
type Output = Self;
#[inline(always)]
fn add(self, other: Self) -> Self {
Self(self.0.wrapping_add(other.0))
}
}
impl AddAssign for $name {
#[inline(always)]
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
}
impl Sub for $name {
type Output = Self;
#[inline(always)]
fn sub(self, other: Self) -> Self {
Self(self.0.wrapping_sub(other.0))
}
}
impl SubAssign for $name {
#[inline(always)]
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
}
};
}
/// Implements multiplication and division operators for fixed types.
macro_rules! fixed_mul_div {
($ty:ty) => {
impl $ty {
/// Multiplies `self` by `a` and divides the product by `b`.
// This one is specifically not always inlined due to size and
// frequency of use. We leave it to compiler discretion.
#[inline]
pub const fn mul_div(&self, a: Self, b: Self) -> Self {
let mut sign = 1;
let mut su = self.0 as u64;
let mut au = a.0 as u64;
let mut bu = b.0 as u64;
if self.0 < 0 {
su = 0u64.wrapping_sub(su);
sign = -1;
}
if a.0 < 0 {
au = 0u64.wrapping_sub(au);
sign = -sign;
}
if b.0 < 0 {
bu = 0u64.wrapping_sub(bu);
sign = -sign;
}
let result = if bu > 0 {
su.wrapping_mul(au).wrapping_add(bu >> 1) / bu
} else {
0x7FFFFFFF
};
Self(if sign < 0 {
-(result as i32)
} else {
result as i32
})
}
}
impl Mul for $ty {
type Output = Self;
#[inline(always)]
fn mul(self, other: Self) -> Self::Output {
let ab = self.0 as i64 * other.0 as i64;
Self(((ab + 0x8000 - i64::from(ab < 0)) >> 16) as i32)
}
}
impl MulAssign for $ty {
#[inline(always)]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl Div for $ty {
type Output = Self;
#[inline(always)]
fn div(self, other: Self) -> Self::Output {
let mut sign = 1;
let mut a = self.0;
let mut b = other.0;
if a < 0 {
a = -a;
sign = -1;
}
if b < 0 {
b = -b;
sign = -sign;
}
let q = if b == 0 {
0x7FFFFFFF
} else {
((((a as u64) << 16) + ((b as u64) >> 1)) / (b as u64)) as u32
};
Self(if sign < 0 { -(q as i32) } else { q as i32 })
}
}
impl DivAssign for $ty {
#[inline(always)]
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl Neg for $ty {
type Output = Self;
#[inline(always)]
fn neg(self) -> Self {
Self(-self.0)
}
}
};
}
/// impl float conversion methods.
///
/// We convert to different float types in order to ensure we can roundtrip
/// without floating point error.
macro_rules! float_conv {
($name:ident, $to:ident, $from:ident, $ty:ty) => {
impl $name {
#[doc = concat!("Creates a fixed point value from a", stringify!($ty), ".")]
///
/// This operation is lossy; the float will be rounded to the nearest
/// representable value.
#[inline(always)]
pub fn $from(x: $ty) -> Self {
// When x is positive: 1.0 - 0.5 = 0.5
// When x is negative: 0.0 - 0.5 = -0.5
let frac = (x.is_sign_positive() as u8 as $ty) - 0.5;
Self((x * Self::ONE.0 as $ty + frac) as _)
}
#[doc = concat!("Returns the value as an ", stringify!($ty), ".")]
///
/// This operation is lossless: all representable values can be
/// round-tripped.
#[inline(always)]
pub fn $to(self) -> $ty {
let int = ((self.0 & Self::INT_MASK) >> Self::FRACT_BITS) as $ty;
let fract = (self.0 & !Self::INT_MASK) as $ty / Self::ONE.0 as $ty;
int + fract
}
}
//hack: we can losslessly go to float, so use those fmt impls
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.$to().fmt(f)
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.$to().fmt(f)
}
}
};
}
fixed_impl!(F2Dot14, 16, 14, i16);
fixed_impl!(F4Dot12, 16, 12, i16);
fixed_impl!(F6Dot10, 16, 10, i16);
fixed_impl!(Fixed, 32, 16, i32);
fixed_impl!(F26Dot6, 32, 6, i32);
fixed_mul_div!(Fixed);
fixed_mul_div!(F26Dot6);
float_conv!(F2Dot14, to_f32, from_f32, f32);
float_conv!(F4Dot12, to_f32, from_f32, f32);
float_conv!(F6Dot10, to_f32, from_f32, f32);
float_conv!(Fixed, to_f64, from_f64, f64);
float_conv!(F26Dot6, to_f64, from_f64, f64);
crate::newtype_scalar!(F2Dot14, [u8; 2]);
crate::newtype_scalar!(F4Dot12, [u8; 2]);
crate::newtype_scalar!(F6Dot10, [u8; 2]);
crate::newtype_scalar!(Fixed, [u8; 4]);
impl Fixed {
/// Creates a 16.16 fixed point value from a 32 bit integer.
#[inline(always)]
pub const fn from_i32(i: i32) -> Self {
Self(i << 16)
}
/// Converts a 16.16 fixed point value to a 32 bit integer, rounding off
/// the fractional bits.
#[inline(always)]
pub const fn to_i32(self) -> i32 {
self.0.wrapping_add(0x8000) >> 16
}
/// Converts a 16.16 to 26.6 fixed point value.
#[inline(always)]
pub const fn to_f26dot6(self) -> F26Dot6 {
F26Dot6(self.0.wrapping_add(0x200) >> 10)
}
/// Converts a 16.16 to 2.14 fixed point value.
///
/// This specific conversion is defined by the spec:
/// <https://learn.microsoft.com/en-us/typography/opentype/spec/otvaroverview#coordinate-scales-and-normalization>
///
/// "5. Convert the final, normalized 16.16 coordinate value to 2.14 by this method: add 0x00000002,
/// and sign-extend shift to the right by 2."
#[inline(always)]
pub const fn to_f2dot14(self) -> F2Dot14 {
F2Dot14((self.0.wrapping_add(2) >> 2) as _)
}
/// Converts a 16.16 fixed point value to a single precision floating
/// point value.
///
/// This operation is lossy. Use `to_f64()` for a lossless conversion.
#[inline(always)]
pub fn to_f32(self) -> f32 {
const SCALE_FACTOR: f32 = 1.0 / 65536.0;
self.0 as f32 * SCALE_FACTOR
}
}
impl From<i32> for Fixed {
fn from(value: i32) -> Self {
Self::from_i32(value)
}
}
impl F26Dot6 {
/// Creates a 26.6 fixed point value from a 32 bit integer.
#[inline(always)]
pub const fn from_i32(i: i32) -> Self {
Self(i << 6)
}
/// Converts a 26.6 fixed point value to a 32 bit integer, rounding off
/// the fractional bits.
#[inline(always)]
pub const fn to_i32(self) -> i32 {
self.0.wrapping_add(32) >> 6
}
/// Converts a 26.6 fixed point value to a single precision floating
/// point value.
///
/// This operation is lossy. Use `to_f64()` for a lossless conversion.
#[inline(always)]
pub fn to_f32(self) -> f32 {
const SCALE_FACTOR: f32 = 1.0 / 64.0;
self.0 as f32 * SCALE_FACTOR
}
}
impl F2Dot14 {
/// Converts a 2.14 to 16.16 fixed point value.
#[inline(always)]
pub const fn to_fixed(self) -> Fixed {
Fixed(self.0 as i32 * 4)
}
}
#[cfg(test)]
mod tests {
#![allow(overflowing_literals)] // we want to specify byte values directly
use super::*;
#[test]
fn f2dot14_floats() {
// Examples from https://docs.microsoft.com/en-us/typography/opentype/spec/otff#data-types
assert_eq!(F2Dot14(0x7fff), F2Dot14::from_f32(1.999939));
assert_eq!(F2Dot14(0x7000), F2Dot14::from_f32(1.75));
assert_eq!(F2Dot14(0x0001), F2Dot14::from_f32(0.0000610356));
assert_eq!(F2Dot14(0x0000), F2Dot14::from_f32(0.0));
assert_eq!(F2Dot14(0xffff), F2Dot14::from_f32(-0.000061));
assert_eq!(F2Dot14(0x8000), F2Dot14::from_f32(-2.0));
}
#[test]
fn roundtrip_f2dot14() {
for i in i16::MIN..=i16::MAX {
let val = F2Dot14(i);
assert_eq!(val, F2Dot14::from_f32(val.to_f32()));
}
}
#[test]
fn round_f2dot14() {
assert_eq!(F2Dot14(0x7000).round(), F2Dot14::from_f32(-2.0));
assert_eq!(F2Dot14(0x1F00).round(), F2Dot14::from_f32(0.0));
assert_eq!(F2Dot14(0x2000).round(), F2Dot14::from_f32(1.0));
}
#[test]
fn round_fixed() {
//TODO: make good test cases
assert_eq!(Fixed(0x0001_7FFE).round(), Fixed(0x0001_0000));
assert_eq!(Fixed(0x0001_7FFF).round(), Fixed(0x0001_0000));
assert_eq!(Fixed(0x0001_8000).round(), Fixed(0x0002_0000));
}
// disabled because it's slow; these were just for my edification anyway
//#[test]
//fn roundtrip_fixed() {
//for i in i32::MIN..=i32::MAX {
//let val = Fixed(i);
//assert_eq!(val, Fixed::from_f64(val.to_f64()));
//}
//}
#[test]
fn fixed_floats() {
assert_eq!(Fixed(0x7fff_0000), Fixed::from_f64(32767.));
assert_eq!(Fixed(0x7000_0001), Fixed::from_f64(28672.00001525879));
assert_eq!(Fixed(0x0001_0000), Fixed::from_f64(1.0));
assert_eq!(Fixed(0x0000_0000), Fixed::from_f64(0.0));
assert_eq!(
Fixed(i32::from_be_bytes([0xff; 4])),
Fixed::from_f64(-0.000015259)
);
assert_eq!(Fixed(0x7fff_ffff), Fixed::from_f64(32768.0));
}
// We lost the f64::round() intrinsic when dropping std and the
// alternative implementation was very slightly incorrect, throwing
// off some tests. This makes sure we match.
#[test]
fn fixed_floats_rounding() {
fn with_round_intrinsic(x: f64) -> Fixed {
Fixed((x * 65536.0).round() as i32)
}
// These particular values were tripping up tests
let inputs = [0.05, 0.6, 0.2, 0.4, 0.67755];
for input in inputs {
assert_eq!(Fixed::from_f64(input), with_round_intrinsic(input));
// Test negated values as well for good measure
assert_eq!(Fixed::from_f64(-input), with_round_intrinsic(-input));
}
}
#[test]
fn fixed_to_int() {
assert_eq!(Fixed::from_f64(1.0).to_i32(), 1);
assert_eq!(Fixed::from_f64(1.5).to_i32(), 2);
assert_eq!(F26Dot6::from_f64(1.0).to_i32(), 1);
assert_eq!(F26Dot6::from_f64(1.5).to_i32(), 2);
}
#[test]
fn fixed_from_int() {
assert_eq!(Fixed::from_i32(1000).to_bits(), 1000 << 16);
assert_eq!(F26Dot6::from_i32(1000).to_bits(), 1000 << 6);
}
#[test]
fn fixed_to_f26dot6() {
assert_eq!(Fixed::from_f64(42.5).to_f26dot6(), F26Dot6::from_f64(42.5));
}
#[test]
fn fixed_muldiv() {
assert_eq!(
Fixed::from_f64(0.5) * Fixed::from_f64(2.0),
Fixed::from_f64(1.0)
);
assert_eq!(
Fixed::from_f64(0.5) / Fixed::from_f64(2.0),
Fixed::from_f64(0.25)
);
}
}

97
vendor/font-types/src/fword.rs vendored Normal file
View File

@@ -0,0 +1,97 @@
//! 16-bit signed and unsigned font-units
use super::Fixed;
/// 16-bit signed quantity in font design units.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct FWord(i16);
/// 16-bit unsigned quantity in font design units.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct UfWord(u16);
impl FWord {
pub const fn new(raw: i16) -> Self {
Self(raw)
}
pub const fn to_i16(self) -> i16 {
self.0
}
/// Converts this number to a 16.16 fixed point value.
pub const fn to_fixed(self) -> Fixed {
Fixed::from_i32(self.0 as i32)
}
/// The representation of this number as a big-endian byte array.
pub const fn to_be_bytes(self) -> [u8; 2] {
self.0.to_be_bytes()
}
}
impl UfWord {
pub const fn new(raw: u16) -> Self {
Self(raw)
}
pub const fn to_u16(self) -> u16 {
self.0
}
/// Converts this number to a 16.16 fixed point value.
pub const fn to_fixed(self) -> Fixed {
Fixed::from_i32(self.0 as i32)
}
/// The representation of this number as a big-endian byte array.
pub const fn to_be_bytes(self) -> [u8; 2] {
self.0.to_be_bytes()
}
}
impl std::fmt::Display for FWord {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Display for UfWord {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl From<u16> for UfWord {
fn from(src: u16) -> Self {
UfWord(src)
}
}
impl From<i16> for FWord {
fn from(src: i16) -> Self {
FWord(src)
}
}
impl From<FWord> for i16 {
fn from(src: FWord) -> Self {
src.0
}
}
impl From<UfWord> for u16 {
fn from(src: UfWord) -> Self {
src.0
}
}
crate::newtype_scalar!(FWord, [u8; 2]);
crate::newtype_scalar!(UfWord, [u8; 2]);
//TODO: we can add addition/etc as needed

175
vendor/font-types/src/glyph_id.rs vendored Normal file
View File

@@ -0,0 +1,175 @@
//! Glyph Identifiers.
//!
//! Although these are treated as u16s in the spec, we choose to represent them
//! as a distinct type.
/// A 16-bit glyph identifier.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct GlyphId16(u16);
impl GlyphId16 {
/// The identifier reserved for unknown glyphs
pub const NOTDEF: GlyphId16 = GlyphId16(0);
/// Construct a new `GlyphId16`.
pub const fn new(raw: u16) -> Self {
GlyphId16(raw)
}
/// The identifier as a u16.
pub const fn to_u16(self) -> u16 {
self.0
}
/// The identifier as a u32.
pub const fn to_u32(self) -> u32 {
self.0 as u32
}
pub const fn to_be_bytes(self) -> [u8; 2] {
self.0.to_be_bytes()
}
}
impl Default for GlyphId16 {
fn default() -> Self {
GlyphId16::NOTDEF
}
}
impl From<u16> for GlyphId16 {
fn from(value: u16) -> Self {
Self(value)
}
}
impl From<GlyphId16> for usize {
fn from(value: GlyphId16) -> Self {
value.0 as usize
}
}
impl std::fmt::Display for GlyphId16 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "GID_{}", self.0)
}
}
impl From<GlyphId16> for u32 {
fn from(value: GlyphId16) -> u32 {
value.to_u32()
}
}
crate::newtype_scalar!(GlyphId16, [u8; 2]);
/// A 32-bit glyph identifier.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct GlyphId(u32);
impl GlyphId {
/// The identifier reserved for unknown glyphs.
pub const NOTDEF: GlyphId = GlyphId(0);
/// Construct a new `GlyphId`.
pub const fn new(raw: u32) -> Self {
Self(raw)
}
/// The identifier as a u32.
pub const fn to_u32(self) -> u32 {
self.0
}
}
impl Default for GlyphId {
fn default() -> Self {
GlyphId::NOTDEF
}
}
impl From<u16> for GlyphId {
fn from(value: u16) -> Self {
Self(value as u32)
}
}
impl From<u32> for GlyphId {
fn from(value: u32) -> Self {
Self(value)
}
}
impl std::fmt::Display for GlyphId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "GID_{}", self.0)
}
}
impl From<GlyphId> for u32 {
fn from(value: GlyphId) -> u32 {
value.to_u32()
}
}
impl From<GlyphId16> for GlyphId {
fn from(value: GlyphId16) -> GlyphId {
Self(value.to_u32())
}
}
impl PartialEq<GlyphId16> for GlyphId {
fn eq(&self, other: &GlyphId16) -> bool {
self.0 == other.0 as u32
}
}
impl PartialOrd<GlyphId16> for GlyphId {
fn partial_cmp(&self, other: &GlyphId16) -> Option<core::cmp::Ordering> {
Some(self.0.cmp(&(other.0 as u32)))
}
}
impl PartialEq<GlyphId> for GlyphId16 {
fn eq(&self, other: &GlyphId) -> bool {
self.0 as u32 == other.0
}
}
impl PartialOrd<GlyphId> for GlyphId16 {
fn partial_cmp(&self, other: &GlyphId) -> Option<core::cmp::Ordering> {
Some((self.0 as u32).cmp(&other.0))
}
}
impl TryFrom<GlyphId> for GlyphId16 {
type Error = TryFromGlyphIdError;
fn try_from(value: GlyphId) -> Result<Self, Self::Error> {
Ok(Self(
value
.0
.try_into()
.map_err(|_| TryFromGlyphIdError(value.0))?,
))
}
}
/// The error type returned when a glyph identifier conversion fails.
#[derive(Debug)]
pub struct TryFromGlyphIdError(u32);
impl core::fmt::Display for TryFromGlyphIdError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "glyph identifier {} too large for conversion", self.0)
}
}
#[cfg(feature = "std")]
impl std::error::Error for TryFromGlyphIdError {}

138
vendor/font-types/src/int24.rs vendored Normal file
View File

@@ -0,0 +1,138 @@
/// 24-bit unsigned integer.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct Int24(i32);
impl Int24 {
/// The smallest value that can be represented by this integer type.
pub const MIN: Self = Int24(-0x80_00_00);
/// The largest value that can be represented by this integer type.
pub const MAX: Self = Int24(0x7F_FF_FF);
/// Create from a i32. Saturates on overflow.
pub const fn new(raw: i32) -> Int24 {
let overflow = raw > Self::MAX.0;
let underflow = raw < Self::MIN.0;
let raw = raw * !(overflow || underflow) as i32
+ Self::MAX.0 * overflow as i32
+ Self::MIN.0 * underflow as i32;
Int24(raw)
}
/// Create from a i32, returning `None` if the value overflows.
pub const fn checked_new(raw: i32) -> Option<Int24> {
if raw > Self::MAX.0 || raw < Self::MIN.0 {
None
} else {
Some(Int24(raw))
}
}
/// Returns this value as an unsigned 32-bit integer.
pub const fn to_i32(self) -> i32 {
self.0
}
pub const fn to_be_bytes(self) -> [u8; 3] {
let bytes = self.0.to_be_bytes();
[bytes[1], bytes[2], bytes[3]]
}
pub const fn from_be_bytes(bytes: [u8; 3]) -> Self {
let extra_byte = ((bytes[0] & 0b10000000) >> 7) * 0b11111111;
let extra_byte = (extra_byte as i32) << 24;
Int24::new(
extra_byte | ((bytes[0] as i32) << 16) | ((bytes[1] as i32) << 8) | bytes[2] as i32,
)
}
}
impl From<Int24> for i32 {
fn from(src: Int24) -> i32 {
src.0
}
}
impl std::fmt::Display for Int24 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constructor() {
assert_eq!(Int24::MAX, Int24::new(i32::MAX));
assert_eq!(Int24::MIN, Int24::new(i32::MIN));
assert_eq!(-10, Int24::new(-10).to_i32());
assert_eq!(10, Int24::new(10).to_i32());
}
#[test]
fn to_be_bytes() {
assert_eq!(
Int24::new(0).to_be_bytes(),
[0b00000000, 0b00000000, 0b00000000]
);
assert_eq!(
Int24::new(123_456).to_be_bytes(),
[0b00000001, 0b11100010, 0b01000000]
);
assert_eq!(
Int24::new(-123_456).to_be_bytes(),
[0b11111110, 0b00011101, 0b11000000]
);
assert_eq!(
Int24::new(0x7F_FF_FF).to_be_bytes(),
[0b01111111, 0b11111111, 0b11111111]
);
assert_eq!(
Int24::new(-0x80_00_00).to_be_bytes(),
[0b10000000, 0b00000000, 0b00000000]
);
}
#[test]
fn from_be_bytes() {
assert_eq!(
Int24::from_be_bytes([0b00000000, 0b00000000, 0b00000000]),
Int24::new(0)
);
assert_eq!(
Int24::from_be_bytes([0b00000001, 0b11100010, 0b01000000]),
Int24::new(123_456)
);
assert_eq!(
Int24::from_be_bytes([0b11111110, 0b00011101, 0b11000000]),
Int24::new(-123_456)
);
assert_eq!(
Int24::from_be_bytes([0b01111111, 0b11111111, 0b11111111]),
Int24::new(0x7F_FF_FF)
);
assert_eq!(
Int24::from_be_bytes([0b10000000, 0b00000000, 0b00000000]),
Int24::new(-0x80_00_00)
);
}
#[test]
fn round_trip() {
for v in Int24::MIN.to_i32()..=Int24::MAX.to_i32() {
let int = Int24::new(v);
let bytes = int.to_be_bytes();
let int_prime = Int24::from_be_bytes(bytes);
assert_eq!(int_prime, int);
}
}
}

57
vendor/font-types/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,57 @@
//! Common [scalar data types][data types] used in font files
//!
//! [data types]: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#data-types
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![deny(rustdoc::broken_intra_doc_links)]
#![warn(clippy::doc_markdown)]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "std")]
#[macro_use]
extern crate std;
#[cfg(not(feature = "std"))]
#[macro_use]
extern crate core as std;
mod bbox;
mod fixed;
mod fword;
mod glyph_id;
mod int24;
mod longdatetime;
mod name_id;
mod offset;
mod point;
mod raw;
mod tag;
mod uint24;
mod version;
#[cfg(all(test, feature = "serde"))]
mod serde_test;
pub use bbox::BoundingBox;
pub use fixed::{F26Dot6, F2Dot14, F4Dot12, F6Dot10, Fixed};
pub use fword::{FWord, UfWord};
pub use glyph_id::{GlyphId, GlyphId16, TryFromGlyphIdError};
pub use int24::Int24;
pub use longdatetime::LongDateTime;
pub use name_id::NameId;
pub use offset::{Nullable, Offset16, Offset24, Offset32};
pub use point::Point;
pub use raw::{BigEndian, FixedSize, Scalar};
pub use tag::{InvalidTag, Tag};
pub use uint24::Uint24;
pub use version::{Compatible, MajorMinor, Version16Dot16};
/// The header tag for a font collection file.
pub const TTC_HEADER_TAG: Tag = Tag::new(b"ttcf");
/// The SFNT version for fonts containing TrueType outlines.
pub const TT_SFNT_VERSION: u32 = 0x00010000;
/// The SFNT version for legacy Apple fonts containing TrueType outlines.
pub const TRUE_SFNT_VERSION: u32 = 0x74727565;
/// The SFNT version for fonts containing CFF outlines.
pub const CFF_SFNT_VERSION: u32 = 0x4F54544F;

33
vendor/font-types/src/longdatetime.rs vendored Normal file
View File

@@ -0,0 +1,33 @@
//! a datetime type
/// A simple datetime type.
///
/// This represented as a number of seconds since 12:00 midnight, January 1, 1904, UTC.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct LongDateTime(i64);
impl LongDateTime {
/// Create with a number of seconds relative to 1904-01-01 00:00.
pub const fn new(secs: i64) -> Self {
Self(secs)
}
/// The number of seconds since 00:00 1904-01-01, UTC.
///
/// This can be a negative number, which presumably represents a date prior
/// to the reference date.
pub const fn as_secs(&self) -> i64 {
self.0
}
/// The representation of this datetime as a big-endian byte array.
pub const fn to_be_bytes(self) -> [u8; 8] {
self.0.to_be_bytes()
}
}
crate::newtype_scalar!(LongDateTime, [u8; 8]);
//TODO: maybe a 'chrono' feature for constructing these sanely?

270
vendor/font-types/src/name_id.rs vendored Normal file
View File

@@ -0,0 +1,270 @@
//! Name Identifiers
//!
//! Although these are treated as u16s in the spec, we choose to represent them
//! as a distinct type.
use core::fmt;
/// Identifier for an informational string (or name).
///
/// A set of predefined identifiers exist for accessing names and other various metadata
/// about the font and those are provided as associated constants on this type.
///
/// IDs 26 to 255, inclusive, are reserved for future standard names. IDs 256 to 32767,
/// inclusive, are reserved for font-specific names such as those referenced by a font's
/// layout features.
///
/// For more detail, see <https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids>
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct NameId(u16);
impl NameId {
/// Copyright notice.
pub const COPYRIGHT_NOTICE: Self = Self(0);
/// Font family name.
///
/// The font family name is used in combination with font subfamily name (ID 2),
/// and should be shared among at most four fonts that differ only in weight or style (as described below).
pub const FAMILY_NAME: Self = Self(1);
/// Font subfamily name.
///
/// The font subfamily name is used in combination with font family name (ID 1),
/// and distinguishes the fonts in a group with the same font family name. This should be used for style
/// and weight variants only (as described below).
pub const SUBFAMILY_NAME: Self = Self(2);
/// Unique font identifier.
pub const UNIQUE_ID: Self = Self(3);
/// Full font name that reflects all family and relevant subfamily descriptors.
///
/// The full font name is generally a combination of IDs 1 and 2, or of IDs 16 and
/// 17, or a similar human-readable variant.
pub const FULL_NAME: Self = Self(4);
/// Version string.
///
/// Should begin with the syntax “Version number.number” (upper case, lower case, or mixed,
/// with a space between “Version” and the number).
pub const VERSION_STRING: Self = Self(5);
/// PostScript name for the font.
///
/// ID 6 specifies a string which is used to invoke a PostScript language font
/// that corresponds to this OpenType font. When translated to ASCII, the name string must be no longer than 63
/// characters and restricted to the printable ASCII subset, codes 33 to 126, except for the 10 characters '[', ']',
/// '(', ')', '{', '}', '<', '>', '/', '%'.
pub const POSTSCRIPT_NAME: Self = Self(6);
/// Trademark; this is used to save any trademark notice/information for this font.
///
/// Such information should be based on legal advice. This is distinctly separate from the copyright.
pub const TRADEMARK: Self = Self(7);
/// Manufacturer name.
pub const MANUFACTURER: Self = Self(8);
/// Name of the designer of the typeface.
pub const DESIGNER: Self = Self(9);
/// Description of the typeface.
///
/// Can contain revision information, usage recommendations, history, features, etc.
pub const DESCRIPTION: Self = Self(10);
/// URL of font vendor (with protocol, e.g., http://, ftp://).
///
/// If a unique serial number is embedded in the URL, it can be used to register the font.
pub const VENDOR_URL: Self = Self(11);
/// URL of typeface designer (with protocol, e.g., http://, ftp://).
pub const DESIGNER_URL: Self = Self(12);
/// License description.
///
/// A description of how the font may be legally used, or different example scenarios for licensed use.
/// This field should be written in plain language, not legalese.
pub const LICENSE_DESCRIPTION: Self = Self(13);
/// URL where additional licensing information can be found.
pub const LICENSE_URL: Self = Self(14);
/// Typographic family name.
///
/// The typographic family grouping doesn't impose any constraints on the number of faces within it,
/// in contrast with the 4-style family grouping (ID 1), which is present both for historical reasons and to express style
/// linking groups.
pub const TYPOGRAPHIC_FAMILY_NAME: Self = Self(16);
/// Typographic subfamily name.
///
/// This allows font designers to specify a subfamily name within the typographic family grouping.
/// This string must be unique within a particular typographic family.
pub const TYPOGRAPHIC_SUBFAMILY_NAME: Self = Self(17);
/// Compatible full (Macintosh only).
///
/// On the Macintosh, the menu name is constructed using the FOND resource. This usually matches
/// the full name. If you want the name of the font to appear differently than the Full Name, you can insert the compatible full
/// name in ID 18.
pub const COMPATIBLE_FULL_NAME: Self = Self(18);
/// Sample text.
///
/// This can be the font name, or any other text that the designer thinks is the best sample to display the font in.
pub const SAMPLE_TEXT: Self = Self(19);
/// PostScript CID findfont name.
///
/// Its presence in a font means that the ID 6 holds a PostScript font name that is meant to be
/// used with the “composefont” invocation in order to invoke the font in a PostScript interpreter.
pub const POSTSCRIPT_CID_NAME: Self = Self(20);
/// WWS family name.
///
/// Used to provide a WWS-conformant family name in case the entries for IDs 16 and 17 do not conform to the WWS model.
pub const WWS_FAMILY_NAME: Self = Self(21);
/// WWS subfamily name.
///
/// Used in conjunction with ID 21, this ID provides a WWS-conformant subfamily name (reflecting only weight, width
/// and slope attributes) in case the entries for IDs 16 and 17 do not conform to the WWS model.
pub const WWS_SUBFAMILY_NAME: Self = Self(22);
/// Light background palette.
///
/// This ID, if used in the CPAL table's Palette Labels Array, specifies that the corresponding color palette in
/// the CPAL table is appropriate to use with the font when displaying it on a light background such as white.
pub const LIGHT_BACKGROUND_PALETTE: Self = Self(23);
/// Dark background palette.
///
/// This ID, if used in the CPAL table's Palette Labels Array, specifies that the corresponding color palette in
/// the CPAL table is appropriate to use with the font when displaying it on a dark background such as black.
pub const DARK_BACKGROUND_PALETTE: Self = Self(24);
/// Variations PostScript name prefix.
///
/// If present in a variable font, it may be used as the family prefix in the PostScript Name Generation
/// for Variation Fonts algorithm.
pub const VARIATIONS_POSTSCRIPT_NAME_PREFIX: Self = Self(25);
/// The last value that is explicitly reserved for standard names.
///
/// Values after this are available to be used for font-specific names.
///
/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids>
pub const LAST_RESERVED_NAME_ID: Self = Self(255);
/// The last value that is available for font-specific names.
///
/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids>
pub const LAST_ALLOWED_NAME_ID: Self = Self(32767);
}
impl NameId {
/// Create a new identifier from a raw u16 value.
#[inline]
pub const fn new(raw: u16) -> Self {
Self(raw)
}
/// Returns an iterator over the set of predefined identifiers according to the
/// specification.
#[inline]
pub fn predefined() -> impl Iterator<Item = Self> + Clone {
// Poor name id 15 got lost...
(0..15).chain(16..=25).map(Self)
}
/// Return the identifier as a u16.
#[inline]
pub const fn to_u16(self) -> u16 {
self.0
}
/// Returns `true` if self is in the range `0..=255` (reserved for standard names.)
#[inline]
pub const fn is_reserved(self) -> bool {
self.0 <= Self::LAST_RESERVED_NAME_ID.0
}
/// Returns a new `NameId` by adding `rhs` to the current value.
///
/// Returns `None` if the result would be greater than `Self::LAST_ALLOWED_NAME_ID`.
#[inline]
pub const fn checked_add(self, rhs: u16) -> Option<Self> {
let result = Self(self.0.saturating_add(rhs));
if result.0 > Self::LAST_ALLOWED_NAME_ID.0 {
None
} else {
Some(result)
}
}
/// Return the memory representation of this identifier as a byte array in big-endian
/// (network) byte order.
#[inline]
pub const fn to_be_bytes(self) -> [u8; 2] {
self.0.to_be_bytes()
}
}
impl Default for NameId {
fn default() -> Self {
Self(0xFFFF)
}
}
impl From<u16> for NameId {
fn from(value: u16) -> Self {
Self(value)
}
}
impl fmt::Debug for NameId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = match *self {
Self::COPYRIGHT_NOTICE => "COPYRIGHT_NOTICE",
Self::FAMILY_NAME => "FAMILY_NAME",
Self::SUBFAMILY_NAME => "SUBFAMILY_NAME",
Self::UNIQUE_ID => "UNIQUE_ID",
Self::FULL_NAME => "FULL_NAME",
Self::VERSION_STRING => "VERSION_STRING",
Self::POSTSCRIPT_NAME => "POSTSCRIPT_NAME",
Self::TRADEMARK => "TRADEMARK",
Self::MANUFACTURER => "MANUFACTURER",
Self::DESIGNER => "DESIGNER",
Self::DESCRIPTION => "DESCRIPTION",
Self::VENDOR_URL => "VENDOR_URL",
Self::DESIGNER_URL => "DESIGNER_URL",
Self::LICENSE_DESCRIPTION => "LICENSE_DESCRIPTION",
Self::LICENSE_URL => "LICENSE_URL",
Self::TYPOGRAPHIC_FAMILY_NAME => "TYPOGRAPHIC_FAMILY_NAME",
Self::TYPOGRAPHIC_SUBFAMILY_NAME => "TYPOGRAPHIC_SUBFAMILY_NAME",
Self::COMPATIBLE_FULL_NAME => "COMPATIBLE_FULL_NAME",
Self::SAMPLE_TEXT => "SAMPLE_TEXT",
Self::POSTSCRIPT_CID_NAME => "POSTSCRIPT_CID_NAME",
Self::WWS_FAMILY_NAME => "WWS_FAMILY_NAME",
Self::WWS_SUBFAMILY_NAME => "WWS_SUBFAMILY_NAME",
Self::LIGHT_BACKGROUND_PALETTE => "LIGHT_BACKGROUND_PALETTE",
Self::DARK_BACKGROUND_PALETTE => "DARK_BACKGROUND_PALETTE",
Self::VARIATIONS_POSTSCRIPT_NAME_PREFIX => "VARIATIONS_POSTSCRIPT_NAME_PREFIX",
_ => return write!(f, "NameId {}", self.0),
};
f.write_str(name)
}
}
impl fmt::Display for NameId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
crate::newtype_scalar!(NameId, [u8; 2]);

118
vendor/font-types/src/offset.rs vendored Normal file
View File

@@ -0,0 +1,118 @@
//! Offsets to tables
use crate::{Scalar, Uint24};
/// An offset of a given width for which NULL (zero) is a valid value.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct Nullable<T>(T);
// internal implementation detail; lets us implement Default for nullable offsets.
trait NullValue {
const NULL: Self;
}
impl<T: Scalar> Scalar for Nullable<T> {
type Raw = T::Raw;
#[inline]
fn from_raw(raw: Self::Raw) -> Self {
Self(T::from_raw(raw))
}
#[inline]
fn to_raw(self) -> Self::Raw {
self.0.to_raw()
}
}
impl<T> Nullable<T> {
/// Return a reference to the inner offset
#[inline]
pub fn offset(&self) -> &T {
&self.0
}
}
impl<T: PartialEq<u32>> Nullable<T> {
#[inline]
pub fn is_null(&self) -> bool {
self.0 == 0
}
}
impl<T: PartialEq<u32>> PartialEq<u32> for Nullable<T> {
#[inline]
fn eq(&self, other: &u32) -> bool {
self.0 == *other
}
}
impl<T: NullValue> Default for Nullable<T> {
fn default() -> Self {
Self(T::NULL)
}
}
macro_rules! impl_offset {
($name:ident, $bits:literal, $rawty:ty) => {
#[doc = concat!("A", stringify!($bits), "-bit offset to a table.")]
///
/// Specific offset fields may or may not permit NULL values; however we
/// assume that errors are possible, and expect the caller to handle
/// the `None` case.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct $name($rawty);
impl $name {
/// Create a new offset.
#[inline]
pub const fn new(raw: $rawty) -> Self {
Self(raw)
}
/// Return `true` if this offset is null.
#[inline]
pub fn is_null(self) -> bool {
self.to_u32() == 0
}
#[inline]
pub fn to_u32(self) -> u32 {
self.0.into()
}
}
impl crate::raw::Scalar for $name {
type Raw = <$rawty as crate::raw::Scalar>::Raw;
fn from_raw(raw: Self::Raw) -> Self {
let raw = <$rawty>::from_raw(raw);
$name::new(raw)
}
fn to_raw(self) -> Self::Raw {
self.0.to_raw()
}
}
// useful for debugging
impl PartialEq<u32> for $name {
fn eq(&self, other: &u32) -> bool {
self.to_u32() == *other
}
}
impl NullValue for $name {
const NULL: $name = $name(<$rawty>::MIN);
}
};
}
impl_offset!(Offset16, 16, u16);
impl_offset!(Offset24, 24, Uint24);
impl_offset!(Offset32, 32, u32);

282
vendor/font-types/src/point.rs vendored Normal file
View File

@@ -0,0 +1,282 @@
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
/// Two dimensional point with a generic coordinate type.
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(C)]
pub struct Point<T> {
/// X coordinate.
pub x: T,
/// Y coordinate.
pub y: T,
}
/// SAFETY:
/// This trait has four preconditions:
///
/// 1. All fields in the struct must implement `NoUninit`
/// 2. The struct must be `#[repr(C)]` or `#[repr(transparent)]`
/// 3. The struct must not contain any padding bytes
/// 4. The struct must contain no generic parameters
///
/// We satisfy the first and second preconditions trivially. The third
/// condition is satisfied because the struct is repr(C) and contains
/// two fields of the same type which guarantees no padding.
///
/// The fourth condition is obviously not satisfied which is what
/// requires implementing this trait manually rather than deriving
/// it. This condition only exists because the bytemuck derive
/// macro cannot guarantee the first three conditions in a type
/// with generic parameters.
#[cfg(feature = "bytemuck")]
unsafe impl<T> bytemuck::NoUninit for Point<T> where T: bytemuck::NoUninit {}
impl<T> Point<T> {
/// Creates a new point with the given x and y coordinates.
#[inline(always)]
pub const fn new(x: T, y: T) -> Self {
Self { x, y }
}
/// Creates a new point from a single value assigned to both coordinates.
pub const fn broadcast(xy: T) -> Self
where
T: Copy,
{
Self { x: xy, y: xy }
}
/// Maps `Point<T>` to `Point<U>` by applying a function to each coordinate.
#[inline(always)]
pub fn map<U>(self, mut f: impl FnMut(T) -> U) -> Point<U> {
Point {
x: f(self.x),
y: f(self.y),
}
}
}
impl<T> Add for Point<T>
where
T: Add<Output = T>,
{
type Output = Self;
#[inline(always)]
fn add(self, rhs: Self) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl<T> AddAssign for Point<T>
where
T: AddAssign,
{
#[inline(always)]
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
}
}
impl<T> Sub for Point<T>
where
T: Sub<Output = T>,
{
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self::Output {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl<T> SubAssign for Point<T>
where
T: SubAssign,
{
#[inline(always)]
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
}
}
impl<T> Mul for Point<T>
where
T: Mul<Output = T>,
{
type Output = Self;
#[inline(always)]
fn mul(self, rhs: Self) -> Self::Output {
Self {
x: self.x * rhs.x,
y: self.y * rhs.y,
}
}
}
impl<T> Mul<T> for Point<T>
where
T: Mul<Output = T> + Copy,
{
type Output = Self;
#[inline(always)]
fn mul(self, rhs: T) -> Self::Output {
Self {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl<T> MulAssign for Point<T>
where
T: MulAssign,
{
#[inline(always)]
fn mul_assign(&mut self, rhs: Self) {
self.x *= rhs.x;
self.y *= rhs.y;
}
}
impl<T> MulAssign<T> for Point<T>
where
T: MulAssign + Copy,
{
#[inline(always)]
fn mul_assign(&mut self, rhs: T) {
self.x *= rhs;
self.y *= rhs;
}
}
impl<T> Div for Point<T>
where
T: Div<Output = T>,
{
type Output = Self;
#[inline(always)]
fn div(self, rhs: Self) -> Self::Output {
Self {
x: self.x / rhs.x,
y: self.y / rhs.y,
}
}
}
impl<T> Div<T> for Point<T>
where
T: Div<Output = T> + Copy,
{
type Output = Self;
#[inline(always)]
fn div(self, rhs: T) -> Self::Output {
Self {
x: self.x / rhs,
y: self.y / rhs,
}
}
}
impl<T> DivAssign for Point<T>
where
T: DivAssign,
{
#[inline(always)]
fn div_assign(&mut self, rhs: Self) {
self.x /= rhs.x;
self.y /= rhs.y;
}
}
impl<T> DivAssign<T> for Point<T>
where
T: DivAssign + Copy,
{
#[inline(always)]
fn div_assign(&mut self, rhs: T) {
self.x /= rhs;
self.y /= rhs;
}
}
impl<T> Neg for Point<T>
where
T: Neg<Output = T>,
{
type Output = Self;
#[inline(always)]
fn neg(self) -> Self::Output {
Self {
x: -self.x,
y: -self.y,
}
}
}
#[cfg(test)]
mod tests {
use super::Point;
use crate::F26Dot6;
#[test]
fn map() {
assert_eq!(
Point::new(42.5, 20.1).map(F26Dot6::from_f64),
Point::new(F26Dot6::from_f64(42.5), F26Dot6::from_f64(20.1))
);
}
#[test]
fn add() {
assert_eq!(Point::new(1, 2) + Point::new(3, 4), Point::new(4, 6));
let mut point = Point::new(1, 2);
point += Point::new(3, 4);
assert_eq!(point, Point::new(4, 6));
}
#[test]
fn sub() {
assert_eq!(Point::new(1, 2) - Point::new(3, 4), Point::new(-2, -2));
let mut point = Point::new(1, 2);
point -= Point::new(3, 4);
assert_eq!(point, Point::new(-2, -2));
}
#[test]
fn mul() {
assert_eq!(Point::new(1, 2) * Point::new(3, 4), Point::new(3, 8));
let mut point = Point::new(1, 2);
point *= Point::new(3, 4);
assert_eq!(point, Point::new(3, 8));
assert_eq!(Point::new(1, 2) * 8, Point::new(8, 16));
}
#[test]
fn div() {
assert_eq!(Point::new(10, 16) / Point::new(2, 3), Point::new(5, 5));
let mut point = Point::new(10, 16);
point /= Point::new(2, 3);
assert_eq!(point, Point::new(5, 5));
assert_eq!(Point::new(10, 16) / 2, Point::new(5, 8));
}
#[test]
fn neg() {
assert_eq!(-Point::new(1, -2), Point::new(-1, 2));
}
}

217
vendor/font-types/src/raw.rs vendored Normal file
View File

@@ -0,0 +1,217 @@
//! types for working with raw big-endian bytes
/// A trait for font scalars.
///
/// This is an internal trait for encoding and decoding big-endian bytes.
///
/// You do not need to implement this trait directly; it is an implementation
/// detail of the [`BigEndian`] wrapper.
pub trait Scalar: Sized {
/// The raw byte representation of this type.
type Raw: sealed::BeByteArray;
/// Create an instance of this type from raw big-endian bytes
fn from_raw(raw: Self::Raw) -> Self;
/// Encode this type as raw big-endian bytes
fn to_raw(self) -> Self::Raw;
/// Attempt to read a scalar from a slice.
///
/// This will always succeed if `slice.len() == Self::RAW_BYTE_LEN`, and will
/// always return `None` otherwise.
fn read(slice: &[u8]) -> Option<Self> {
sealed::BeByteArray::from_slice(slice).map(Self::from_raw)
}
}
/// A trait for types that have a known, constant size.
pub trait FixedSize: Sized {
/// The raw size of this type, in bytes.
///
/// This is the size required to represent this type in a font file, which
/// may differ from the size of the native type:
///
/// ```
/// # use font_types::{FixedSize, Offset24};
/// assert_eq!(std::mem::size_of::<u16>(), u16::RAW_BYTE_LEN);
/// assert_eq!(Offset24::RAW_BYTE_LEN, 3);
/// assert_eq!(std::mem::size_of::<Offset24>(), 4);
/// ```
const RAW_BYTE_LEN: usize;
}
/// we hide this trait; it isn't part of the public API, and this clarifies
/// the guarantee that it is only implemented for [u8; N]
mod sealed {
/// A trait representing any fixed-size big-endian byte array.
///
/// This is only used in `Scalar`, as a way of expressing the condition that the
/// `Raw` type is always a fixed-size byte array.
#[cfg(not(feature = "bytemuck"))]
pub trait BeByteArray: Copy + AsRef<[u8]> {
/// Must always succeed for `[u8; N]` if `slice.len() == N`, must fail otherwise
fn from_slice(slice: &[u8]) -> Option<Self>;
}
#[cfg(feature = "bytemuck")]
pub trait BeByteArray:
Copy + AsRef<[u8]> + bytemuck::AnyBitPattern + bytemuck::Zeroable
{
/// Must always succeed for `[u8; N]` if `slice.len() == N`, must fail otherwise
fn from_slice(slice: &[u8]) -> Option<Self>;
}
impl<const N: usize> BeByteArray for [u8; N] {
fn from_slice(slice: &[u8]) -> Option<Self> {
slice.try_into().ok()
}
}
}
/// A wrapper around raw big-endian bytes for some type.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(transparent)]
pub struct BigEndian<T: Scalar>(pub(crate) T::Raw);
// # SAFETY:
//
// `BigEndian<T>` has the bound `T: Scalar`, and contains only a single value,
// `<T as Scalar>::Raw` which is only ever a byte array.
#[cfg(feature = "bytemuck")]
unsafe impl<T> bytemuck::Zeroable for BigEndian<T> where T: Scalar + Copy {}
#[cfg(feature = "bytemuck")]
unsafe impl<T> bytemuck::AnyBitPattern for BigEndian<T> where T: Scalar + Copy + 'static {}
impl<T: Scalar> BigEndian<T> {
/// construct a new `BigEndian<T>` from raw bytes
pub fn new(raw: T::Raw) -> BigEndian<T> {
BigEndian(raw)
}
/// Attempt to construct a new raw value from this slice.
///
/// This will fail if `slice.len() != T::RAW_BYTE_LEN`.
pub fn from_slice(slice: &[u8]) -> Option<Self> {
sealed::BeByteArray::from_slice(slice).map(Self)
}
/// Convert this raw type to its native representation.
#[inline(always)]
pub fn get(&self) -> T {
T::from_raw(self.0)
}
/// Set the value, overwriting the bytes.
pub fn set(&mut self, value: T) {
self.0 = value.to_raw();
}
/// Get the raw big-endian bytes.
pub fn be_bytes(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<T: Scalar> From<T> for BigEndian<T> {
#[inline]
fn from(val: T) -> Self {
BigEndian(val.to_raw())
}
}
impl<T: Scalar + Default> Default for BigEndian<T> {
fn default() -> Self {
Self::from(T::default())
}
}
//NOTE: do to the orphan rules, we cannot impl the inverse of this, e.g.
// impl<T> PartialEq<BigEndian<T>> for T (<https://doc.rust-lang.org/error_codes/E0210.html>)
impl<T: Scalar + Copy + PartialEq> PartialEq<T> for BigEndian<T> {
fn eq(&self, other: &T) -> bool {
self.get() == *other
}
}
impl<T: Scalar + Copy + PartialOrd + PartialEq> PartialOrd for BigEndian<T>
where
<T as Scalar>::Raw: PartialEq,
{
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.get().partial_cmp(&other.get())
}
}
impl<T: Scalar + Copy + Ord + Eq> Ord for BigEndian<T>
where
<T as Scalar>::Raw: Eq,
{
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.get().cmp(&other.get())
}
}
impl<T: Scalar> FixedSize for T {
const RAW_BYTE_LEN: usize = std::mem::size_of::<T::Raw>();
}
impl<T: Scalar> FixedSize for BigEndian<T> {
const RAW_BYTE_LEN: usize = T::RAW_BYTE_LEN;
}
/// An internal macro for implementing the `RawType` trait.
#[macro_export]
macro_rules! newtype_scalar {
($ty:ident, $raw:ty) => {
impl $crate::raw::Scalar for $ty {
type Raw = $raw;
fn to_raw(self) -> $raw {
self.0.to_raw()
}
#[inline(always)]
fn from_raw(raw: $raw) -> Self {
Self($crate::raw::Scalar::from_raw(raw))
}
}
};
}
macro_rules! int_scalar {
($ty:ty, $raw:ty) => {
impl crate::raw::Scalar for $ty {
type Raw = $raw;
fn to_raw(self) -> $raw {
self.to_be_bytes()
}
#[inline(always)]
fn from_raw(raw: $raw) -> $ty {
Self::from_be_bytes(raw)
}
}
};
}
int_scalar!(u8, [u8; 1]);
int_scalar!(i8, [u8; 1]);
int_scalar!(u16, [u8; 2]);
int_scalar!(i16, [u8; 2]);
int_scalar!(u32, [u8; 4]);
int_scalar!(i32, [u8; 4]);
int_scalar!(i64, [u8; 8]);
int_scalar!(crate::Uint24, [u8; 3]);
int_scalar!(crate::Int24, [u8; 3]);
impl<T: std::fmt::Debug + Scalar + Copy> std::fmt::Debug for BigEndian<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.get().fmt(f)
}
}
impl<T: std::fmt::Display + Scalar + Copy> std::fmt::Display for BigEndian<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.get().fmt(f)
}
}

39
vendor/font-types/src/serde_test.rs vendored Normal file
View File

@@ -0,0 +1,39 @@
//! ensure serde is working as expected
use super::*;
#[test]
fn test_serde() {
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
struct MyTypes {
f1: Fixed,
f2: F26Dot6,
f3: F2Dot14,
gid: GlyphId16,
date: LongDateTime,
name_id: NameId,
offset: Offset16,
tag: Tag,
u24: Uint24,
version1: MajorMinor,
version2: Version16Dot16,
}
let my_instance = MyTypes {
f1: Fixed::from_f64(521.5),
f2: F26Dot6::from_f64(-1001.1),
f3: F2Dot14::from_f32(1.2),
gid: GlyphId16::new(69),
date: LongDateTime::new(1_234_569_101),
name_id: NameId::new(8214),
offset: Offset16::new(42),
tag: Tag::new(b"cool"),
u24: Uint24::new(16_777_215),
version1: MajorMinor::new(10, 5),
version2: Version16Dot16::VERSION_2_5,
};
let dumped = serde_json::to_string(&my_instance).unwrap();
let loaded: MyTypes = serde_json::from_str(&dumped).unwrap();
assert_eq!(my_instance, loaded)
}

382
vendor/font-types/src/tag.rs vendored Normal file
View File

@@ -0,0 +1,382 @@
use std::{
borrow::Borrow,
fmt::{Debug, Display, Formatter},
str::FromStr,
};
/// An OpenType tag.
///
/// [Per the spec][spec], a tag is a 4-byte array where each byte is in the
/// printable ASCII range `(0x20..=0x7E)`.
///
/// We do not strictly enforce this constraint as it is possible to encounter
/// invalid tags in existing fonts, and these need to be representable.
///
/// When creating new tags we encourage ensuring that the tag is valid,
/// either by using [`Tag::new_checked`] or by calling [`Tag::validate`] on an
/// existing tag.
///
/// [spec]: https://learn.microsoft.com/en-us/typography/opentype/spec/otff#data-types
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct Tag([u8; 4]);
impl Tag {
/// Construct a `Tag` from raw bytes.
///
/// This does not perform any validation; use [`Tag::new_checked`] for a
/// constructor that validates input.
pub const fn new(src: &[u8; 4]) -> Tag {
Tag(*src)
}
/// Attempt to create a `Tag` from raw bytes.
///
/// The slice must contain between 1 and 4 bytes, each in the printable
/// ascii range (`0x20..=0x7E`).
///
/// If the input has fewer than four bytes, it will be padded with spaces.
///
/// This method returns an [`InvalidTag`] error if the tag does conform to
/// the spec.
pub const fn new_checked(src: &[u8]) -> Result<Self, InvalidTag> {
if src.is_empty() || src.len() > 4 {
return Err(InvalidTag::InvalidLength(src.len()));
}
let mut raw = [0x20; 4];
let mut i = 0;
let mut seen_space = false;
while i < src.len() {
let byte = match src[i] {
byte @ 0x20 if i == 0 => return Err(InvalidTag::InvalidByte { pos: i, byte }),
byte @ 0..=0x1F | byte @ 0x7f.. => {
return Err(InvalidTag::InvalidByte { pos: i, byte })
}
byte @ 0x21..=0x7e if seen_space => {
return Err(InvalidTag::InvalidByte { pos: i, byte })
}
byte => byte,
};
seen_space |= byte == 0x20;
raw[i] = byte;
i += 1;
}
Ok(Tag(raw))
}
/// Construct a new `Tag` from a big-endian `u32`, without performing validation.
///
/// This is provided as a convenience method for interop with code that
/// stores tags as big-endian u32s.
pub const fn from_u32(src: u32) -> Self {
Self::from_be_bytes(src.to_be_bytes())
}
/// Create a tag from raw big-endian bytes.
///
/// Prefer to use [`Tag::new`] (in const contexts) or [`Tag::new_checked`]
/// when creating a `Tag`.
///
/// This does not check the input, and is only intended to be used during
/// parsing, where invalid inputs are accepted.
pub const fn from_be_bytes(bytes: [u8; 4]) -> Self {
Self(bytes)
}
// for symmetry with integer types / other things we encode/decode
/// Return the memory representation of this tag.
pub const fn to_be_bytes(self) -> [u8; 4] {
self.0
}
/// Return the raw byte array representing this tag.
pub fn into_bytes(self) -> [u8; 4] {
self.0
}
/// Check that the tag conforms with the spec.
///
/// This is intended for use during things like santization or lint passes
/// on existing fonts; if you are creating a new tag, you should prefer
/// [`Tag::new_checked`].
///
/// Specifically, this checks the following conditions
///
/// - the tag is not empty
/// - the tag contains only characters in the printable ascii range (`0x20..=0x1F`)
/// - the tag does not begin with a space
/// - the tag does not contain any non-space characters after the first space
pub fn validate(self) -> Result<(), InvalidTag> {
if self == Tag::default() {
return Err(InvalidTag::InvalidLength(0));
}
let mut seen_space = false;
for (i, byte) in self.0.as_slice().iter().copied().enumerate() {
match byte {
0x20 if i == 0 => return Err(InvalidTag::InvalidByte { pos: i, byte }),
0x20 => seen_space = true,
0..=0x1F | 0x7f.. => return Err(InvalidTag::InvalidByte { pos: i, byte }),
0x21..=0x7e if seen_space => return Err(InvalidTag::ByteAfterSpace { pos: i }),
_ => (),
}
}
Ok(())
}
}
/// An error representing an invalid tag.
///
/// This is returned as an error from [`Tag::new_checked`] and
/// [`Tag::validate`].
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum InvalidTag {
/// The tag was not between 1 and 4 bytes in length.
InvalidLength(usize),
/// The tag contained an invalid byte, not within the printable
/// ASCII range `(0x20..=0x7E)`.
InvalidByte { pos: usize, byte: u8 },
/// The tag contained one or more non-space characters after a space.
ByteAfterSpace { pos: usize },
}
impl FromStr for Tag {
type Err = InvalidTag;
fn from_str(src: &str) -> Result<Self, Self::Err> {
Tag::new_checked(src.as_bytes())
}
}
impl crate::raw::Scalar for Tag {
type Raw = [u8; 4];
fn to_raw(self) -> Self::Raw {
self.to_be_bytes()
}
fn from_raw(raw: Self::Raw) -> Self {
Self::from_be_bytes(raw)
}
}
#[cfg(feature = "std")]
impl std::error::Error for InvalidTag {}
impl Borrow<[u8; 4]> for Tag {
fn borrow(&self) -> &[u8; 4] {
&self.0
}
}
impl PartialEq<[u8; 4]> for Tag {
fn eq(&self, other: &[u8; 4]) -> bool {
&self.0 == other
}
}
impl PartialEq<str> for Tag {
fn eq(&self, other: &str) -> bool {
self.0 == other.as_bytes()
}
}
impl PartialEq<&str> for Tag {
fn eq(&self, other: &&str) -> bool {
self == *other
}
}
impl PartialEq<&[u8]> for Tag {
fn eq(&self, other: &&[u8]) -> bool {
self.0.as_ref() == *other
}
}
impl AsRef<[u8]> for Tag {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl Display for Tag {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
for byte in self.0 {
if (0x20..=0x7E).contains(&byte) {
write!(f, "{}", byte as char)?;
} else {
write!(f, "{{0x{:02X}}}", byte)?;
}
}
Ok(())
}
}
impl Display for InvalidTag {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
InvalidTag::InvalidByte { pos, byte } => {
write!(f, "Invalid byte 0x{byte:X} at index {pos}")
}
InvalidTag::InvalidLength(len) => write!(f, "Invalid length ({len})"),
InvalidTag::ByteAfterSpace { .. } => write!(f, "Non-space character after first space"),
}
}
}
impl Debug for Tag {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "Tag({})", self)
}
}
// a meaningless placeholder value.
impl Default for Tag {
fn default() -> Self {
Tag([b' '; 4])
}
}
// fancy impls: these will serialize to a string if the target format is
// human-readable, but to bytes otherwise.
//
// NOTE: this means that tags which are not utf-8 will fail to serialize to
// json/yaml.
#[cfg(feature = "serde")]
impl serde::Serialize for Tag {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
std::str::from_utf8(&self.0)
.map_err(serde::ser::Error::custom)?
.serialize(serializer)
} else {
self.0.serialize(serializer)
}
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Tag {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct TagStrVisitor;
impl serde::de::Visitor<'_> for TagStrVisitor {
type Value = Tag;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a four-byte ascii string")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
v.parse().map_err(serde::de::Error::custom)
}
}
if deserializer.is_human_readable() {
deserializer.deserialize_str(TagStrVisitor)
} else {
<[u8; 4]>::deserialize(deserializer).map(|raw| Tag::new(&raw))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smoke_test() {
Tag::new(b"head");
assert!(Tag::new_checked(b"").is_err());
assert!(Tag::new_checked(b" ").is_err());
assert!(Tag::new_checked(b"a").is_ok());
assert!(Tag::new_checked(b"ab").is_ok());
assert!(Tag::new_checked(b"abc").is_ok());
assert!(Tag::new_checked(b"abcd").is_ok());
assert!(Tag::new_checked(b"abcde").is_err());
assert!(Tag::new_checked(b" bc").is_err()); // space invalid in first position
assert!(Tag::new_checked(b"b c").is_err()); // non-space cannot follow space
assert_eq!(Tag::new_checked(b"bc"), Ok(Tag::new(b"bc ")));
// ascii only:
assert!(Tag::new_checked(&[0x19]).is_err());
assert!(Tag::new_checked(&[0x21]).is_ok());
assert!(Tag::new_checked(&[0x7E]).is_ok());
assert!(Tag::new_checked(&[0x7F]).is_err());
}
#[test]
fn validate_test() {
assert!(Tag::new(b" ").validate().is_err());
assert!(Tag::new(b"a ").validate().is_ok());
assert!(Tag::new(b"ab ").validate().is_ok());
assert!(Tag::new(b"abc ").validate().is_ok());
assert!(Tag::new(b"abcd").validate().is_ok());
assert!(Tag::new(b" bcc").validate().is_err()); // space invalid in first position
assert!(Tag::new(b"b cc").validate().is_err()); // non-space cannot follow space
// ascii only:
assert!(Tag::new(&[0x19, 0x33, 0x33, 0x33]).validate().is_err());
assert!(Tag::new(&[0x21, 0x33, 0x33, 0x33]).validate().is_ok());
assert!(Tag::new(&[0x7E, 0x33, 0x33, 0x33]).validate().is_ok());
assert!(Tag::new(&[0x7F, 0x33, 0x33, 0x33]).validate().is_err());
}
#[test]
#[cfg(feature = "std")]
fn display() {
let bad_tag = Tag::new(&[0x19, b'z', b'@', 0x7F]);
assert_eq!(bad_tag.to_string(), "{0x19}z@{0x7F}");
}
}
#[cfg(all(test, feature = "serde"))]
mod serde_tests {
use super::*;
#[derive(PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
struct TestMe {
tag: Tag,
}
#[test]
fn serde_json_good() {
let ser_me = TestMe {
tag: Tag::new(b"yolo"),
};
let json_str = serde_json::to_string(&ser_me).unwrap();
assert_eq!(json_str, r#"{"tag":"yolo"}"#);
let de_me: TestMe = serde_json::from_str(&json_str).unwrap();
assert_eq!(de_me, ser_me);
}
#[test]
#[should_panic(expected = "invalid utf-8")]
fn serde_json_bad() {
let ser_me = TestMe {
tag: Tag::new(&[3, 244, 0, 221]),
};
serde_json::to_string(&ser_me).unwrap();
}
// ensure that we impl DeserializeOwned
#[test]
fn deser_json_owned() {
let json = r#"{"tag":"yolo"}"#;
let de_me: TestMe = serde_json::from_reader(json.as_bytes()).unwrap();
assert_eq!(de_me.tag, Tag::new(b"yolo"));
}
}

102
vendor/font-types/src/uint24.rs vendored Normal file
View File

@@ -0,0 +1,102 @@
/// 24-bit unsigned integer.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct Uint24(u32);
impl Uint24 {
/// The smallest value that can be represented by this integer type.
pub const MIN: Self = Uint24(0);
/// The largest value that can be represented by this integer type.
pub const MAX: Self = Uint24(0xffffff);
/// Create from a u32. Saturates on overflow.
pub const fn new(raw: u32) -> Uint24 {
let overflow = raw > Self::MAX.0;
let raw = raw * !overflow as u32 + Self::MAX.0 * overflow as u32;
Uint24(raw)
}
/// Create from a u32, returning `None` if the value overflows.
pub const fn checked_new(raw: u32) -> Option<Uint24> {
if raw > Self::MAX.0 {
None
} else {
Some(Uint24(raw))
}
}
/// Returns this value as an unsigned 32-bit integer.
pub const fn to_u32(self) -> u32 {
self.0
}
pub const fn to_be_bytes(self) -> [u8; 3] {
let bytes = self.0.to_be_bytes();
[bytes[1], bytes[2], bytes[3]]
}
pub const fn from_be_bytes(bytes: [u8; 3]) -> Self {
Uint24::new(((bytes[0] as u32) << 16) | ((bytes[1] as u32) << 8) | bytes[2] as u32)
}
}
impl From<Uint24> for u32 {
fn from(src: Uint24) -> u32 {
src.0
}
}
impl From<Uint24> for usize {
fn from(src: Uint24) -> usize {
src.0 as usize
}
}
/// Indicates an error converting an integer value into a Uint24 due to overflow.
#[derive(Debug)]
pub struct TryFromUint24Error;
impl std::fmt::Display for TryFromUint24Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "failed to convert usize value into Uint24.")
}
}
#[cfg(feature = "std")]
impl std::error::Error for TryFromUint24Error {}
impl TryFrom<usize> for Uint24 {
type Error = TryFromUint24Error;
fn try_from(value: usize) -> Result<Self, Self::Error> {
let u32_value = u32::try_from(value).map_err(|_| TryFromUint24Error)?;
Uint24::checked_new(u32_value).ok_or(TryFromUint24Error)
}
}
impl std::fmt::Display for Uint24 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constructor() {
assert_eq!(Uint24::MAX, Uint24::new(u32::MAX));
assert!(Uint24::checked_new(u32::MAX).is_none())
}
#[test]
fn be_bytes() {
let bytes = [0xff, 0b10101010, 0b11001100];
let val = Uint24::from_be_bytes(bytes);
assert_eq!(val.to_be_bytes(), bytes);
}
}

197
vendor/font-types/src/version.rs vendored Normal file
View File

@@ -0,0 +1,197 @@
/// A legacy 16/16 version encoding
/// Packed 32-bit value with major and minor version numbers.
///
/// This is a legacy type with an unusual representation. See [the spec][spec] for
/// additional details.
///
/// [spec]: https://learn.microsoft.com/en-us/typography/opentype/spec/otff#table-version-numbers
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct Version16Dot16(u32);
/// A type representing a major, minor version pair.
///
/// This is not part of [the spec][spec], but versions in the spec are frequently
/// represented as a `major_version`, `minor_version` pair. This type encodes
/// those as a single type, which is useful for some of the generated code that
/// parses out a version.
///
/// [spec]: https://learn.microsoft.com/en-us/typography/opentype/spec/otff#table-version-numbers
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(C)]
pub struct MajorMinor {
/// The major version number
pub major: u16,
/// The minor version number
pub minor: u16,
}
/// A trait for determining whether versions are compatible.
pub trait Compatible<Rhs = Self>: Sized {
/// return `true` if this version is field-compatible with `other`.
///
/// This is kind of poorly defined, but basically means 'same major version,
/// greater than or equal minor version'.
fn compatible(&self, other: Rhs) -> bool;
}
impl Version16Dot16 {
/// Version 0.5
pub const VERSION_0_5: Version16Dot16 = Version16Dot16::new(0, 5);
/// Version 1.0
pub const VERSION_1_0: Version16Dot16 = Version16Dot16::new(1, 0);
/// Version 1.1
pub const VERSION_1_1: Version16Dot16 = Version16Dot16::new(1, 1);
/// Version 2.0
pub const VERSION_2_0: Version16Dot16 = Version16Dot16::new(2, 0);
/// Version 2.5
pub const VERSION_2_5: Version16Dot16 = Version16Dot16::new(2, 5);
/// Version 3.0
pub const VERSION_3_0: Version16Dot16 = Version16Dot16::new(3, 0);
/// Create a new version with the provided major and minor parts.
///
/// The minor version must be in the range 0..=9.
///
/// # Panics
///
/// Panics if `minor > 9`.
pub const fn new(major: u16, minor: u16) -> Self {
assert!(minor < 10, "minor version must be in the range [0, 9]");
let version = ((major as u32) << 16) | ((minor as u32) << 12);
Version16Dot16(version)
}
/// Return the separate major & minor version numbers.
pub const fn to_major_minor(self) -> (u16, u16) {
let major = (self.0 >> 16) as u16;
let minor = ((self.0 & 0xFFFF) >> 12) as u16;
(major, minor)
}
/// The representation of this version as a big-endian byte array.
#[inline]
pub const fn to_be_bytes(self) -> [u8; 4] {
self.0.to_be_bytes()
}
}
crate::newtype_scalar!(Version16Dot16, [u8; 4]);
impl MajorMinor {
/// Version 1.0
pub const VERSION_1_0: MajorMinor = MajorMinor::new(1, 0);
/// Version 1.1
pub const VERSION_1_1: MajorMinor = MajorMinor::new(1, 1);
/// Version 1.2
pub const VERSION_1_2: MajorMinor = MajorMinor::new(1, 2);
/// Version 1.3
pub const VERSION_1_3: MajorMinor = MajorMinor::new(1, 3);
/// Version 2.0
pub const VERSION_2_0: MajorMinor = MajorMinor::new(2, 0);
/// Create a new version with major and minor parts.
#[inline]
pub const fn new(major: u16, minor: u16) -> Self {
MajorMinor { major, minor }
}
/// The representation of this version as a big-endian byte array.
#[inline]
pub const fn to_be_bytes(self) -> [u8; 4] {
let [a, b] = self.major.to_be_bytes();
let [c, d] = self.minor.to_be_bytes();
[a, b, c, d]
}
}
impl crate::Scalar for MajorMinor {
type Raw = [u8; 4];
fn from_raw(raw: Self::Raw) -> Self {
let major = u16::from_be_bytes([raw[0], raw[1]]);
let minor = u16::from_be_bytes([raw[2], raw[3]]);
Self { major, minor }
}
fn to_raw(self) -> Self::Raw {
self.to_be_bytes()
}
}
impl Compatible for Version16Dot16 {
#[inline]
fn compatible(&self, other: Self) -> bool {
let (self_major, self_minor) = self.to_major_minor();
let (other_major, other_minor) = other.to_major_minor();
self_major == other_major && self_minor >= other_minor
}
}
impl Compatible<(u16, u16)> for Version16Dot16 {
fn compatible(&self, other: (u16, u16)) -> bool {
self.compatible(Version16Dot16::new(other.0, other.1))
}
}
impl Compatible for MajorMinor {
#[inline]
fn compatible(&self, other: Self) -> bool {
self.major == other.major && self.minor >= other.minor
}
}
impl Compatible<(u16, u16)> for MajorMinor {
fn compatible(&self, other: (u16, u16)) -> bool {
self.compatible(MajorMinor::new(other.0, other.1))
}
}
impl Compatible for u16 {
#[inline]
fn compatible(&self, other: Self) -> bool {
*self >= other
}
}
impl std::fmt::Debug for Version16Dot16 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Version16Dot16({:08x})", self.0)
}
}
impl std::fmt::Display for Version16Dot16 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let (major, minor) = self.to_major_minor();
write!(f, "{major}.{minor}")
}
}
impl std::fmt::Display for MajorMinor {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let MajorMinor { major, minor } = self;
write!(f, "{major}.{minor}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_smoke_test() {
assert_eq!(Version16Dot16(0x00005000).to_major_minor(), (0, 5));
assert_eq!(Version16Dot16(0x00011000).to_major_minor(), (1, 1));
assert_eq!(Version16Dot16::new(0, 5).0, 0x00005000);
assert_eq!(Version16Dot16::new(1, 1).0, 0x00011000);
}
#[test]
#[should_panic]
fn minor_version_out_of_range_test() {
Version16Dot16::new(1, 10);
}
}