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

676
vendor/swash/src/attributes.rs vendored Normal file
View File

@@ -0,0 +1,676 @@
//! Basic font attributes: stretch, weight and style.
use super::internal::{head::Os2, RawFont};
use super::{tag_from_bytes, FontRef, Setting, Tag};
use core::fmt;
use core::hash::{Hash, Hasher};
// Variations that apply to attributes.
const WDTH: Tag = tag_from_bytes(b"wdth");
const WGHT: Tag = tag_from_bytes(b"wght");
const SLNT: Tag = tag_from_bytes(b"slnt");
const ITAL: Tag = tag_from_bytes(b"ital");
/// Primary attributes for font classification: stretch, weight and style.
///
/// This struct is created by the [`attributes`](FontRef::attributes) method on [`FontRef`].
#[derive(Copy, Clone)]
pub struct Attributes(pub u32);
impl Attributes {
/// Creates new font attributes from the specified stretch, weight and
/// style.
pub const fn new(stretch: Stretch, weight: Weight, style: Style) -> Self {
let stretch = stretch.0 as u32 & 0x1FF;
let weight = weight.0 as u32 & 0x3FF;
let style = style.pack();
Self(style | weight << 9 | stretch << 19)
}
/// Extracts the attributes from the specified font.
pub fn from_font<'a>(font: &FontRef<'a>) -> Self {
let mut attrs = Self::from_os2(font.os2().as_ref());
let mut var_bits = 0;
for var in font.variations() {
match var.tag() {
WDTH => var_bits |= 1,
WGHT => var_bits |= 2,
SLNT => var_bits |= 4,
ITAL => var_bits |= 8,
_ => {}
}
}
attrs.0 |= var_bits << 28;
attrs
}
pub(crate) fn from_os2(os2: Option<&Os2>) -> Self {
if let Some(os2) = os2 {
let flags = os2.selection_flags();
let style = if flags.italic() {
Style::Italic
} else if flags.oblique() {
Style::Oblique(ObliqueAngle::default())
} else {
Style::Normal
};
let weight = Weight(os2.weight_class() as u16);
let stretch = Stretch::from_raw(os2.width_class() as u16);
Self::new(stretch, weight, style)
} else {
Self::default()
}
}
/// Returns the stretch attribute.
pub fn stretch(&self) -> Stretch {
Stretch((self.0 >> 19 & 0x1FF) as u16)
}
/// Returns the weight attribute.
pub fn weight(&self) -> Weight {
Weight((self.0 >> 9 & 0x3FF) as u16)
}
/// Returns the style attribute.
pub fn style(&self) -> Style {
Style::unpack(self.0 & 0x1FF)
}
/// Returns a tuple containing all attributes.
pub fn parts(&self) -> (Stretch, Weight, Style) {
(self.stretch(), self.weight(), self.style())
}
/// Returns true if the font has variations corresponding to primary
/// attributes.
pub fn has_variations(&self) -> bool {
(self.0 >> 28) != 0
}
/// Returns true if the font has a variation for the stretch attribute.
pub fn has_stretch_variation(&self) -> bool {
let var_bits = self.0 >> 28;
var_bits & 1 != 0
}
/// Returns true if the font has a variation for the weight attribute.
pub fn has_weight_variation(&self) -> bool {
let var_bits = self.0 >> 28;
var_bits & 2 != 0
}
/// Returns true if the font has a variation for the oblique style
/// attribute.
pub fn has_oblique_variation(&self) -> bool {
let var_bits = self.0 >> 28;
var_bits & 4 != 0
}
/// Returns true if the font has a variation for the italic style
/// attribute.
pub fn has_italic_variation(&self) -> bool {
let var_bits = self.0 >> 28;
var_bits & 8 != 0
}
/// Returns a synthesis analysis based on the requested attributes with
/// respect to this set of attributes.
pub fn synthesize(&self, requested: Attributes) -> Synthesis {
let mut synth = Synthesis::default();
if self.0 << 4 == requested.0 << 4 {
return synth;
}
let mut len = 0usize;
if self.has_stretch_variation() {
let stretch = self.stretch();
let req_stretch = requested.stretch();
if stretch != requested.stretch() {
synth.vars[len] = Setting {
tag: WDTH,
value: req_stretch.to_percentage(),
};
len += 1;
}
}
let (weight, req_weight) = (self.weight(), requested.weight());
if weight != req_weight {
if self.has_weight_variation() {
synth.vars[len] = Setting {
tag: WGHT,
value: req_weight.0 as f32,
};
len += 1;
} else if req_weight > weight {
synth.embolden = true;
}
}
let (style, req_style) = (self.style(), requested.style());
if style != req_style {
match req_style {
Style::Normal => {}
Style::Italic => {
if style == Style::Normal {
if self.has_italic_variation() {
synth.vars[len] = Setting {
tag: ITAL,
value: 1.,
};
len += 1;
} else if self.has_oblique_variation() {
synth.vars[len] = Setting {
tag: SLNT,
value: 14.,
};
len += 1;
} else {
synth.skew = 14;
}
}
}
Style::Oblique(angle) => {
if style == Style::Normal {
let degrees = angle.to_degrees();
if self.has_oblique_variation() {
synth.vars[len] = Setting {
tag: SLNT,
value: degrees,
};
len += 1;
} else if self.has_italic_variation() && degrees > 0. {
synth.vars[len] = Setting {
tag: ITAL,
value: 1.,
};
len += 1;
} else {
synth.skew = degrees as i8;
}
}
}
}
}
synth.len = len as u8;
synth
}
}
impl Default for Attributes {
fn default() -> Self {
Self::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal)
}
}
impl PartialEq for Attributes {
fn eq(&self, other: &Self) -> bool {
self.0 << 4 == other.0 << 4
}
}
impl Eq for Attributes {}
impl Hash for Attributes {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.0 << 4).hash(state);
}
}
impl fmt::Display for Attributes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut space = "";
let (stretch, weight, style) = self.parts();
if style == Style::Normal && weight == Weight::NORMAL && stretch == Stretch::NORMAL {
return write!(f, "regular");
}
if stretch != Stretch::NORMAL {
write!(f, "{}", stretch)?;
space = " ";
}
if style != Style::Normal {
write!(f, "{}{}", space, style)?;
space = " ";
}
if weight != Weight::NORMAL {
write!(f, "{}{}", space, weight)?;
}
Ok(())
}
}
impl fmt::Debug for Attributes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.parts())?;
if self.has_stretch_variation() {
write!(f, "+wdth")?;
}
if self.has_weight_variation() {
write!(f, "+wght")?;
}
if self.has_italic_variation() {
write!(f, "+ital")?;
}
if self.has_oblique_variation() {
write!(f, "+slnt")?;
}
Ok(())
}
}
impl From<Stretch> for Attributes {
fn from(s: Stretch) -> Self {
Self::new(s, Weight::default(), Style::default())
}
}
impl From<Weight> for Attributes {
fn from(w: Weight) -> Self {
Self::new(Stretch::default(), w, Style::default())
}
}
impl From<Style> for Attributes {
fn from(s: Style) -> Self {
Self::new(Stretch::default(), Weight::default(), s)
}
}
impl From<()> for Attributes {
fn from(_: ()) -> Self {
Self::default()
}
}
impl From<(Stretch, Weight, Style)> for Attributes {
fn from(parts: (Stretch, Weight, Style)) -> Self {
Self::new(parts.0, parts.1, parts.2)
}
}
/// Angle of an oblique style in degrees from -90 to 90.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ObliqueAngle(pub(crate) u8);
impl ObliqueAngle {
/// Creates a new oblique angle from degrees.
pub fn from_degrees(degrees: f32) -> Self {
let a = degrees.clamp(-90., 90.) + 90.;
Self(a as u8)
}
/// Creates a new oblique angle from radians.
pub fn from_radians(radians: f32) -> Self {
let degrees = radians * 180. / core::f32::consts::PI;
Self::from_degrees(degrees)
}
/// Creates a new oblique angle from gradians.
pub fn from_gradians(gradians: f32) -> Self {
Self::from_degrees(gradians / 400. * 360.)
}
/// Creates a new oblique angle from turns.
pub fn from_turns(turns: f32) -> Self {
Self::from_degrees(turns * 360.)
}
/// Returns the oblique angle in degrees.
pub fn to_degrees(self) -> f32 {
self.0 as f32 - 90.
}
}
impl Default for ObliqueAngle {
fn default() -> Self {
Self::from_degrees(14.)
}
}
/// Visual style or 'slope' of a font.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Style {
Normal,
Italic,
Oblique(ObliqueAngle),
}
impl Style {
/// Parses a style from a CSS style value.
pub fn parse(mut s: &str) -> Option<Self> {
s = s.trim();
Some(match s {
"normal" => Self::Normal,
"italic" => Self::Italic,
"oblique" => Self::Oblique(ObliqueAngle::from_degrees(14.)),
_ => {
if s.starts_with("oblique ") {
s = s.get(8..)?;
if s.ends_with("deg") {
s = s.get(..s.len() - 3)?;
if let Ok(a) = s.trim().parse::<f32>() {
return Some(Self::Oblique(ObliqueAngle::from_degrees(a)));
}
} else if s.ends_with("grad") {
s = s.get(..s.len() - 4)?;
if let Ok(a) = s.trim().parse::<f32>() {
return Some(Self::Oblique(ObliqueAngle::from_gradians(a)));
}
} else if s.ends_with("rad") {
s = s.get(..s.len() - 3)?;
if let Ok(a) = s.trim().parse::<f32>() {
return Some(Self::Oblique(ObliqueAngle::from_radians(a)));
}
} else if s.ends_with("turn") {
s = s.get(..s.len() - 4)?;
if let Ok(a) = s.trim().parse::<f32>() {
return Some(Self::Oblique(ObliqueAngle::from_turns(a)));
}
}
return Some(Self::Oblique(ObliqueAngle::default()));
}
return None;
}
})
}
/// Creates a new oblique style with the specified angle
/// in degrees.
pub fn from_degrees(degrees: f32) -> Self {
Self::Oblique(ObliqueAngle::from_degrees(degrees))
}
/// Returns the angle of the style in degrees.
pub fn to_degrees(self) -> f32 {
match self {
Self::Italic => 14.,
Self::Oblique(angle) => angle.to_degrees(),
_ => 0.,
}
}
fn unpack(bits: u32) -> Self {
if bits & 1 != 0 {
Self::Oblique(ObliqueAngle((bits >> 1) as u8))
} else if bits == 0b110 {
Self::Italic
} else {
Self::Normal
}
}
const fn pack(&self) -> u32 {
match self {
Self::Normal => 0b10,
Self::Italic => 0b110,
Self::Oblique(angle) => 1 | (angle.0 as u32) << 1,
}
}
}
impl fmt::Display for Style {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Normal => "normal",
Self::Italic => "italic",
Self::Oblique(angle) => {
let degrees = angle.to_degrees();
if degrees == 14. {
"oblique"
} else {
return write!(f, "oblique({}deg)", degrees);
}
}
}
)
}
}
impl Default for Style {
fn default() -> Self {
Self::Normal
}
}
/// Visual weight class of a font on a scale from 1 to 1000.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
pub struct Weight(pub u16);
impl Weight {
pub const THIN: Weight = Weight(100);
pub const EXTRA_LIGHT: Weight = Weight(200);
pub const LIGHT: Weight = Weight(300);
pub const NORMAL: Weight = Weight(400);
pub const MEDIUM: Weight = Weight(500);
pub const SEMI_BOLD: Weight = Weight(600);
pub const BOLD: Weight = Weight(700);
pub const EXTRA_BOLD: Weight = Weight(800);
pub const BLACK: Weight = Weight(900);
/// Parses a CSS style font weight attribute.
pub fn parse(s: &str) -> Option<Self> {
let s = s.trim();
Some(match s {
"normal" => Self::NORMAL,
"bold" => Self::BOLD,
_ => Self(s.parse::<u32>().ok()?.clamp(1, 1000) as u16),
})
}
}
impl fmt::Display for Weight {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match *self {
Self::THIN => "thin",
Self::EXTRA_LIGHT => "extra-light",
Self::LIGHT => "light",
Self::NORMAL => "normal",
Self::MEDIUM => "medium",
Self::SEMI_BOLD => "semi-bold",
Self::BOLD => "bold",
Self::EXTRA_BOLD => "extra-bold",
Self::BLACK => "black",
_ => "",
};
if s.is_empty() {
write!(f, "{}", self.0)
} else {
write!(f, "{}", s)
}
}
}
impl Default for Weight {
fn default() -> Self {
Self::NORMAL
}
}
/// Visual width of a font-- a relative change from the normal aspect
/// ratio.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct Stretch(pub(crate) u16);
impl Stretch {
pub const ULTRA_CONDENSED: Self = Self(0);
pub const EXTRA_CONDENSED: Self = Self(25);
pub const CONDENSED: Self = Self(50);
pub const SEMI_CONDENSED: Self = Self(75);
pub const NORMAL: Self = Self(100);
pub const SEMI_EXPANDED: Self = Self(125);
pub const EXPANDED: Self = Self(150);
pub const EXTRA_EXPANDED: Self = Self(200);
pub const ULTRA_EXPANDED: Self = Self(300);
/// Creates a stretch attribute from a percentage. The value will be
/// clamped at half percentage increments between 50% and 200%,
/// inclusive.
pub fn from_percentage(percentage: f32) -> Self {
let value = ((percentage.clamp(50., 200.) - 50.) * 2.) as u16;
Self(value)
}
/// Converts the stretch value to a percentage.
pub fn to_percentage(self) -> f32 {
(self.0 as f32) * 0.5 + 50.
}
/// Returns true if the stretch is normal.
pub fn is_normal(self) -> bool {
self == Self::NORMAL
}
/// Returns true if the stretch is condensed (less than normal).
pub fn is_condensed(self) -> bool {
self < Self::NORMAL
}
/// Returns true if the stretch is expanded (greater than normal).
pub fn is_expanded(self) -> bool {
self > Self::NORMAL
}
/// Parses the stretch from a CSS style keyword or a percentage value.
pub fn parse(s: &str) -> Option<Self> {
let s = s.trim();
Some(match s {
"ultra-condensed" => Self::ULTRA_CONDENSED,
"extra-condensed" => Self::EXTRA_CONDENSED,
"condensed" => Self::CONDENSED,
"semi-condensed" => Self::SEMI_CONDENSED,
"normal" => Self::NORMAL,
"semi-expanded" => Self::SEMI_EXPANDED,
"extra-expanded" => Self::EXTRA_EXPANDED,
"ultra-expanded" => Self::ULTRA_EXPANDED,
_ => {
if s.ends_with('%') {
let p = s.get(..s.len() - 1)?.parse::<f32>().ok()?;
return Some(Self::from_percentage(p));
}
return None;
}
})
}
/// Returns the raw value of the stretch attribute.
pub fn raw(self) -> u16 {
self.0
}
pub(crate) fn from_raw(raw: u16) -> Self {
match raw {
1 => Self::ULTRA_CONDENSED,
2 => Self::EXTRA_CONDENSED,
3 => Self::CONDENSED,
4 => Self::SEMI_CONDENSED,
5 => Self::NORMAL,
6 => Self::SEMI_EXPANDED,
7 => Self::EXPANDED,
8 => Self::EXTRA_EXPANDED,
9 => Self::ULTRA_EXPANDED,
_ => Self::NORMAL,
}
}
}
impl fmt::Display for Stretch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match *self {
Self::ULTRA_CONDENSED => "ultra-condensed",
Self::EXTRA_CONDENSED => "extra-condensed",
Self::CONDENSED => "condensed",
Self::SEMI_CONDENSED => "semi-condensed",
Self::NORMAL => "normal",
Self::SEMI_EXPANDED => "semi-expanded",
Self::EXPANDED => "expanded",
Self::EXTRA_EXPANDED => "extra-expanded",
Self::ULTRA_EXPANDED => "ultra-expanded",
_ => {
return write!(f, "{}%", self.to_percentage());
}
}
)
}
}
impl fmt::Debug for Stretch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Stretch({})", self.to_percentage())
}
}
impl Default for Stretch {
fn default() -> Self {
Self::NORMAL
}
}
/// Synthesis suggestions for mismatched font attributes.
///
/// This is generated by the [`synthesize`](Attributes::synthesize) method on
/// [`Attributes`].
#[derive(Copy, Clone, Default, Debug)]
pub struct Synthesis {
vars: [Setting<f32>; 4],
len: u8,
embolden: bool,
skew: i8,
}
impl Synthesis {
#[doc(hidden)]
pub fn new(variations: impl Iterator<Item = Setting<f32>>, embolden: bool, skew: f32) -> Self {
let mut synth = Self {
embolden,
skew: skew as i8,
..Default::default()
};
for (i, setting) in variations.take(4).enumerate() {
synth.vars[i] = setting;
synth.len = i as u8 + 1;
}
synth
}
/// Returns true if any synthesis suggestions are available.
pub fn any(&self) -> bool {
self.len != 0 || self.embolden || self.skew != 0
}
/// Returns the variations that should be applied to match the requested
/// attributes.
pub fn variations(&self) -> &[Setting<f32>] {
&self.vars[..self.len as usize]
}
/// Returns true if the scaler should apply a faux bold.
pub fn embolden(&self) -> bool {
self.embolden
}
/// Returns a skew angle for faux italic/oblique, if requested.
pub fn skew(&self) -> Option<f32> {
if self.skew != 0 {
Some(self.skew as f32)
} else {
None
}
}
}
impl PartialEq for Synthesis {
fn eq(&self, other: &Self) -> bool {
if self.len != other.len {
return false;
}
if self.len != 0 && self.variations() != other.variations() {
return false;
}
self.embolden == other.embolden && self.skew == other.skew
}
}

100
vendor/swash/src/cache.rs vendored Normal file
View File

@@ -0,0 +1,100 @@
use super::FontRef;
use alloc::vec::Vec;
/// Uniquely generated value for identifying and caching fonts.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
pub struct CacheKey(pub(crate) u64);
impl CacheKey {
/// Generates a new cache key.
pub fn new() -> Self {
use core::sync::atomic::{AtomicUsize, Ordering};
static KEY: AtomicUsize = AtomicUsize::new(1);
Self(KEY.fetch_add(1, Ordering::Relaxed).try_into().unwrap())
}
/// Returns the underlying value of the key.
pub fn value(self) -> u64 {
self.0
}
}
impl Default for CacheKey {
fn default() -> Self {
Self::new()
}
}
pub struct FontCache<T> {
entries: Vec<Entry<T>>,
max_entries: usize,
epoch: u64,
}
impl<T> FontCache<T> {
pub fn new(max_entries: usize) -> Self {
Self {
entries: Vec::new(),
epoch: 0,
max_entries,
}
}
pub fn get<'a>(
&'a mut self,
font: &FontRef,
id_override: Option<[u64; 2]>,
mut f: impl FnMut(&FontRef) -> T,
) -> ([u64; 2], &'a T) {
let id = id_override.unwrap_or([font.key.value(), u64::MAX]);
let (found, index) = self.find(id);
if found {
let entry = &mut self.entries[index];
entry.epoch = self.epoch;
(entry.id, &entry.data)
} else {
self.epoch += 1;
let data = f(font);
if index == self.entries.len() {
self.entries.push(Entry {
epoch: self.epoch,
id,
data,
});
let entry = self.entries.last().unwrap();
(id, &entry.data)
} else {
let entry = &mut self.entries[index];
entry.epoch = self.epoch;
entry.id = id;
entry.data = data;
(id, &entry.data)
}
}
}
fn find(&self, id: [u64; 2]) -> (bool, usize) {
let mut lowest = 0;
let mut lowest_epoch = self.epoch;
for (i, entry) in self.entries.iter().enumerate() {
if entry.id == id {
return (true, i);
}
if entry.epoch < lowest_epoch {
lowest_epoch = entry.epoch;
lowest = i;
}
}
if self.entries.len() < self.max_entries {
(false, self.entries.len())
} else {
(false, lowest)
}
}
}
struct Entry<T> {
epoch: u64,
id: [u64; 2],
data: T,
}

71
vendor/swash/src/charmap.rs vendored Normal file
View File

@@ -0,0 +1,71 @@
/*!
Mapping characters to nominal glyph identifiers.
*/
use super::internal::cmap;
use super::{FontRef, GlyphId};
/// Proxy for rematerializing a character map.
#[derive(Copy, Clone, Default, Debug)]
pub struct CharmapProxy(u32, u8, bool);
impl CharmapProxy {
/// Creates character map proxy from the specified font.
pub fn from_font(font: &FontRef) -> Self {
if let Some((offset, format, symbol)) = cmap::subtable(font) {
Self(offset, format, symbol)
} else {
Self(0, 0, false)
}
}
/// Materializes a character map from the specified font. This proxy must
/// have been created from the same font.
pub fn materialize<'a>(&self, font: &FontRef<'a>) -> Charmap<'a> {
Charmap {
data: font.data,
proxy: *self,
}
}
}
/// Maps characters to nominal glyph identifiers.
#[derive(Copy, Clone)]
pub struct Charmap<'a> {
data: &'a [u8],
proxy: CharmapProxy,
}
impl<'a> Charmap<'a> {
/// Creates a character map from the specified font.
pub fn from_font(font: &FontRef<'a>) -> Self {
let proxy = CharmapProxy::from_font(font);
Self {
data: font.data,
proxy,
}
}
/// Returns the associated proxy.
pub fn proxy(&self) -> CharmapProxy {
self.proxy
}
/// Returns a nominal glyph identifier for the specified codepoint.
pub fn map(&self, codepoint: impl Into<u32>) -> GlyphId {
let codepoint = codepoint.into();
let mut glyph_id = cmap::map(self.data, self.proxy.0, self.proxy.1, codepoint).unwrap_or(0);
// Remap U+0000..=U+00FF to U+F000..=U+F0FF for symbol encodings
if glyph_id == 0 && self.proxy.2 && codepoint <= 0x00FF {
glyph_id =
cmap::map(self.data, self.proxy.0, self.proxy.1, codepoint + 0xF000).unwrap_or(0);
}
glyph_id
}
/// Invokes the specified closure with all codepoint/glyph identifier
/// pairs in the character map.
pub fn enumerate(&self, f: impl FnMut(u32, GlyphId)) {
cmap::enumerate(self.data, self.proxy.0, f);
}
}

91
vendor/swash/src/feature/aat.rs vendored Normal file
View File

@@ -0,0 +1,91 @@
use super::internal::{aat::morx, raw_tag};
use super::util::*;
pub use morx::chains;
use morx::Chains;
#[derive(Copy, Clone)]
pub struct Features<'a> {
chains: Chains<'a>,
features: Option<morx::Features<'a>>,
kern: bool,
seen: SeenFeatures,
}
impl<'a> Features<'a> {
pub fn new(chains: Chains<'a>, kern: bool) -> Self {
Self {
chains,
features: None,
kern,
seen: SeenFeatures::new(),
}
}
}
impl<'a> Iterator for Features<'a> {
type Item = (u32, &'static str);
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.features.is_none() {
if let Some(chain) = self.chains.next() {
self.features = Some(chain.features());
} else if self.kern {
let tag = raw_tag(b"kern");
let (_, desc) = desc_from_at(tag).unwrap_or((0, "Kerning"));
self.kern = false;
return Some((tag, desc));
} else {
return None;
}
}
if let Some(features) = &mut self.features {
if let Some(feature) = features.next() {
if let Some((index, tag, desc)) =
desc_from_aat(feature.selector, feature.setting_selector)
{
if self.seen.mark(index) {
return Some((tag, desc));
}
}
} else {
self.features = None;
continue;
}
}
}
}
}
pub type OnceItem<'a> = Option<Item<'a>>;
// #[derive(Copy, Clone)]
// pub struct OnceItem<'a> {
// pub item: Item<'a>,
// pub given: bool,
// }
// impl<'a> OnceItem<'a> {
// pub fn new(item: Item<'a>) -> Self {
// Self { item, given: false }
// }
// }
// impl<'a> Iterator for OnceItem<'a> {
// type Item = Item<'a>;
// fn next(&mut self) -> Option<Self::Item> {
// if self.given {
// None
// } else {
// self.given = true;
// Some(self.item)
// }
// }
// }
#[derive(Copy, Clone)]
pub struct Item<'a> {
pub chains: Chains<'a>,
pub kern: bool,
}

405
vendor/swash/src/feature/at.rs vendored Normal file
View File

@@ -0,0 +1,405 @@
use super::super::Tag;
use super::internal::{at::*, *};
use super::util::*;
#[derive(Copy, Clone)]
pub struct Script<'a> {
data: Bytes<'a>,
gsub: u32,
gpos: u32,
gsub_offset: u32,
gpos_offset: u32,
tag: Tag,
}
impl<'a> Script<'a> {
pub fn languages(&self) -> Languages<'a> {
Languages::new(
self.data,
self.gsub,
self.gpos,
self.gsub_offset,
self.gpos_offset,
)
}
}
#[derive(Copy, Clone)]
pub struct Scripts<'a> {
data: Bytes<'a>,
gsub: u32,
gpos: u32,
in_gsub: bool,
len: u16,
cur: u16,
done: bool,
}
impl<'a> Scripts<'a> {
pub fn new(data: Bytes<'a>, gsub: u32, gpos: u32) -> Self {
Self {
data,
gsub,
gpos,
in_gsub: true,
len: script_count(&data, gsub),
cur: 0,
done: false,
}
}
fn get_next(&mut self) -> Option<Script<'a>> {
if self.in_gsub {
if self.cur < self.len {
let index = self.cur;
self.cur += 1;
let (tag, offset) = script_at(&self.data, self.gsub, index)?;
let gpos_offset = script_by_tag(&self.data, self.gpos, tag).unwrap_or(0);
return Some(Script {
data: self.data,
gsub: self.gsub,
gpos: self.gpos,
gsub_offset: offset,
gpos_offset,
tag,
});
} else {
self.in_gsub = false;
self.cur = 0;
self.len = script_count(&self.data, self.gpos);
}
} else if self.cur < self.len {
let index = self.cur;
self.cur += 1;
let (tag, offset) = script_at(&self.data, self.gpos, index)?;
if script_by_tag(&self.data, self.gsub, tag).is_some() {
return None;
}
return Some(Script {
data: self.data,
gsub: self.gsub,
gpos: self.gpos,
gsub_offset: 0,
gpos_offset: offset,
tag,
});
} else {
self.done = true;
}
None
}
}
impl<'a> Iterator for Scripts<'a> {
type Item = Script<'a>;
fn next(&mut self) -> Option<Self::Item> {
while !self.done {
let item = self.get_next();
if item.is_some() {
return item;
}
}
None
}
}
/// Specifies language system specific features used for shaping glyphs in
/// a particular script.
#[derive(Copy, Clone)]
pub struct Language<'a> {
data: Bytes<'a>,
gsub: u32,
gpos: u32,
gsub_offset: u32,
gpos_offset: u32,
tag: Tag,
}
/// An iterator over languages supported by a script.
#[derive(Copy, Clone)]
pub struct Languages<'a> {
data: Bytes<'a>,
gsub: u32,
gpos: u32,
gsub_script: u32,
gpos_script: u32,
in_gsub: bool,
len: u16,
cur: u16,
done: bool,
}
impl<'a> Languages<'a> {
fn new(data: Bytes<'a>, gsub: u32, gpos: u32, gsub_script: u32, gpos_script: u32) -> Self {
Self {
data,
gsub,
gpos,
gsub_script,
gpos_script,
in_gsub: true,
len: script_language_count(&data, gsub_script),
cur: 0,
done: false,
}
}
fn get_next(&mut self) -> Option<Language<'a>> {
if self.in_gsub {
if self.cur < self.len {
let index = self.cur;
self.cur += 1;
let (tag, offset) = script_language_at(&self.data, self.gsub_script, index)?;
let gsub_default = tag == DFLT;
let (gpos_offset, _) = if gsub_default {
(
script_default_language(&self.data, self.gpos_script).unwrap_or(0),
true,
)
} else {
script_language_by_tag(&self.data, self.gpos_script, Some(tag))
.unwrap_or((0, false))
};
return Some(Language {
data: self.data,
gsub: self.gsub,
gpos: self.gpos,
gsub_offset: offset,
gpos_offset,
tag,
});
} else {
self.in_gsub = false;
self.cur = 0;
self.len = script_language_count(&self.data, self.gpos_script);
}
} else if self.cur < self.len {
let index = self.cur;
self.cur += 1;
let (tag, offset) = script_language_at(&self.data, self.gpos_script, index)?;
if script_language_by_tag(&self.data, self.gsub_script, Some(tag)).is_some() {
return None;
}
return Some(Language {
data: self.data,
gsub: self.gsub,
gpos: self.gpos,
gsub_offset: 0,
gpos_offset: offset,
tag,
});
} else {
self.done = true;
}
None
}
}
impl<'a> Iterator for Languages<'a> {
type Item = Language<'a>;
fn next(&mut self) -> Option<Self::Item> {
while !self.done {
let item = self.get_next();
if item.is_some() {
return item;
}
}
None
}
}
#[derive(Copy, Clone)]
pub struct WritingSystem<'a> {
lang: Language<'a>,
script_tag: Tag,
}
impl<'a> WritingSystem<'a> {
pub fn script_tag(&self) -> Tag {
self.script_tag
}
pub fn language_tag(&self) -> Tag {
self.lang.tag
}
pub fn features(&self) -> Features<'a> {
Features::new(&self.lang)
}
}
#[derive(Copy, Clone)]
pub struct WritingSystems<'a> {
scripts: Scripts<'a>,
langs: Option<Languages<'a>>,
script_tag: Tag,
}
impl<'a> WritingSystems<'a> {
pub fn new(scripts: Scripts<'a>) -> Self {
Self {
scripts,
langs: None,
script_tag: 0,
}
}
}
impl<'a> Iterator for WritingSystems<'a> {
type Item = WritingSystem<'a>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.langs.is_none() {
let script = self.scripts.next()?;
self.script_tag = script.tag;
self.langs = Some(script.languages());
}
if let Some(lang) = self.langs.as_mut().unwrap().next() {
return Some(WritingSystem {
lang,
script_tag: self.script_tag,
});
} else {
self.langs = None;
}
}
}
}
/// Represents a single typographic feature-- a substitution or positioning
/// action that is a component of text shaping.
#[derive(Copy, Clone)]
pub struct Feature {
pub stage: u8,
pub tag: Tag,
}
#[derive(Copy, Clone)]
pub struct Features<'a> {
data: Bytes<'a>,
gsub: u32,
gpos: u32,
gsub_language: u32,
gpos_language: u32,
stage: u8,
len: u16,
cur: u16,
done: bool,
}
impl<'a> Features<'a> {
fn new(language: &Language<'a>) -> Self {
Self {
data: language.data,
gsub: language.gsub,
gpos: language.gpos,
gsub_language: language.gsub_offset,
gpos_language: language.gpos_offset,
stage: 0,
len: language_feature_count(&language.data, language.gsub_offset),
cur: 0,
done: false,
}
}
fn get_next(&mut self) -> Option<Feature> {
let (gsubgpos, language) = match self.stage {
0 => (self.gsub, self.gsub_language),
_ => (self.gpos, self.gpos_language),
};
if self.cur < self.len {
let index = self.cur;
self.cur += 1;
let feature = language_feature_at(&self.data, language, index)?;
let (tag, _offset) = feature_at(&self.data, gsubgpos, feature)?;
return Some(Feature {
stage: self.stage,
tag,
});
} else if self.stage == 0 {
self.stage = 1;
self.len = language_feature_count(&self.data, self.gpos_language);
self.cur = 0;
} else {
self.done = true;
}
None
}
}
impl<'a> Iterator for Features<'a> {
type Item = Feature;
fn next(&mut self) -> Option<Self::Item> {
while !self.done {
let item = self.get_next();
if item.is_some() {
return item;
}
}
None
}
}
#[derive(Copy, Clone)]
pub struct AllFeatures<'a> {
data: Bytes<'a>,
seen: SeenFeatures,
table: u32,
next_table: u32,
stage: u8,
len: u16,
cur: u16,
}
impl<'a> AllFeatures<'a> {
pub fn new(data: Bytes<'a>, gsub: u32, gpos: u32) -> Self {
let len = feature_count(&data, gsub);
Self {
data,
seen: SeenFeatures::new(),
table: gsub,
next_table: gpos,
stage: 0,
len,
cur: 0,
}
}
}
impl<'a> Iterator for AllFeatures<'a> {
type Item = (u8, Tag);
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.cur >= self.len {
if self.next_table == 0 {
return None;
}
self.table = self.next_table;
self.next_table = 0;
self.stage = 1;
self.cur = 0;
self.len = feature_count(&self.data, self.table);
if self.len == 0 {
return None;
}
}
let index = self.cur;
self.cur += 1;
if let Some((tag, _)) = feature_at(&self.data, self.table, index) {
match FEATURES.binary_search_by(|pair| pair.0.cmp(&tag)) {
Ok(index) => {
if self.seen.mark(index) {
return Some((self.stage, tag));
}
}
_ => continue,
}
}
}
}
}

269
vendor/swash/src/feature/mod.rs vendored Normal file
View File

@@ -0,0 +1,269 @@
mod aat;
mod at;
mod util;
use super::internal::{self, raw_tag, Bytes, RawFont};
use super::{FontRef, Tag};
use crate::text::{Language, Script};
const DFLT: u32 = raw_tag(b"DFLT");
#[derive(Copy, Clone)]
enum Kind {
None,
/// GSUB, GPOS offsets
At(u32, u32),
/// Morx offset, kerning available
Aat(u32, bool),
}
impl Kind {
fn from_font(font: &FontRef) -> Self {
let gsub = font.table_offset(raw_tag(b"GSUB"));
let gpos = font.table_offset(raw_tag(b"GPOS"));
if gsub != 0 || gpos != 0 {
return Self::At(gsub, gpos);
}
let morx = font.table_offset(raw_tag(b"morx"));
if morx != 0 {
let kern = font.table_offset(raw_tag(b"kern")) != 0
|| font.table_offset(raw_tag(b"kerx")) != 0;
return Self::Aat(morx, kern);
}
Self::None
}
}
#[derive(Copy, Clone)]
enum WritingSystemsKind<'a> {
None,
At(at::WritingSystems<'a>),
Aat(aat::OnceItem<'a>),
}
/// Iterator over a collection of writing systems.
#[derive(Copy, Clone)]
pub struct WritingSystems<'a> {
kind: WritingSystemsKind<'a>,
}
impl<'a> WritingSystems<'a> {
pub(crate) fn from_font(font: &FontRef<'a>) -> Self {
let kind = Kind::from_font(font);
WritingSystems {
kind: match kind {
Kind::At(gsub, gpos) => WritingSystemsKind::At(at::WritingSystems::new(
at::Scripts::new(Bytes::new(font.data), gsub, gpos),
)),
Kind::Aat(morx, kern) => WritingSystemsKind::Aat(Some(aat::Item {
chains: aat::chains(font.data, morx),
kern,
})),
_ => WritingSystemsKind::None,
},
}
}
}
#[derive(Copy, Clone)]
enum WritingSystemKind<'a> {
At(at::WritingSystem<'a>),
Aat(aat::Item<'a>),
}
impl<'a> Iterator for WritingSystems<'a> {
type Item = WritingSystem<'a>;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.kind {
WritingSystemsKind::At(iter) => {
let item = iter.next()?;
Some(WritingSystem {
kind: WritingSystemKind::At(item),
script_tag: item.script_tag(),
lang_tag: item.language_tag(),
lang: Language::from_opentype(item.language_tag()),
})
}
WritingSystemsKind::Aat(iter) => {
let item = iter.take()?;
Some(WritingSystem {
kind: WritingSystemKind::Aat(item),
script_tag: DFLT,
lang_tag: DFLT,
lang: None,
})
}
_ => None,
}
}
}
/// Script, language and associated typographic features available in a font.
#[derive(Copy, Clone)]
pub struct WritingSystem<'a> {
kind: WritingSystemKind<'a>,
script_tag: Tag,
lang_tag: Tag,
lang: Option<Language>,
}
impl<'a> WritingSystem<'a> {
/// Returns the OpenType script tag for the writing system.
pub fn script_tag(&self) -> Tag {
self.script_tag
}
/// Returns the OpenType language tag for the writing system.
pub fn language_tag(&self) -> Tag {
self.lang_tag
}
/// Returns the script for the writing system.
pub fn script(&self) -> Option<Script> {
Script::from_opentype(self.script_tag)
}
/// Returns the language for the writing system.
pub fn language(&self) -> Option<Language> {
self.lang
}
/// Returns an iterator over the features provided by the writing
/// system.
pub fn features(&self) -> Features<'a> {
Features {
kind: match self.kind {
WritingSystemKind::At(item) => FeaturesKind::At(item.features()),
WritingSystemKind::Aat(item) => {
FeaturesKind::Aat(aat::Features::new(item.chains, item.kern))
}
},
}
}
}
#[derive(Copy, Clone)]
enum FeaturesKind<'a> {
None,
At(at::Features<'a>),
AtAll(at::AllFeatures<'a>),
Aat(aat::Features<'a>),
}
/// Typographic rule that produces modifications to a sequence of glyphs.
#[derive(Copy, Clone)]
pub struct Feature {
tag: Tag,
name: Option<&'static str>,
action: Action,
}
impl Feature {
fn from_tag(tag: Tag, action: Action) -> Self {
Self {
tag,
name: util::desc_from_at(tag).map(|x| x.1),
action,
}
}
/// Returns the feature tag.
pub fn tag(&self) -> Tag {
self.tag
}
/// Returns the name of the feature, if available.
pub fn name(&self) -> Option<&'static str> {
self.name
}
/// Returns the action of the feature.
pub fn action(&self) -> Action {
self.action
}
}
/// Modification performed by a feature.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Action {
/// Replaces one or more glyphs such as in ligation.
Substitution,
/// Attaches one glyph to another such as in accent mark placement.
Attachment,
/// Adjusts the position of one or more glyphs such as in kerning.
Adjustment,
}
/// Iterator over a collection of typographic features.
#[derive(Copy, Clone)]
pub struct Features<'a> {
kind: FeaturesKind<'a>,
}
impl<'a> Features<'a> {
pub(crate) fn from_font(font: &FontRef<'a>) -> Self {
let kind = Kind::from_font(font);
Self {
kind: match kind {
Kind::At(gsub, gpos) => {
FeaturesKind::AtAll(at::AllFeatures::new(Bytes::new(font.data), gsub, gpos))
}
Kind::Aat(morx, kern) => {
FeaturesKind::Aat(aat::Features::new(aat::chains(font.data, morx), kern))
}
_ => FeaturesKind::None,
},
}
}
}
const MARK: u32 = raw_tag(b"mark");
const MKMK: u32 = raw_tag(b"mkmk");
impl<'a> Iterator for Features<'a> {
type Item = Feature;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.kind {
FeaturesKind::At(iter) => {
let item = iter.next()?;
let action = if item.stage == 0 {
Action::Substitution
} else {
match item.tag {
MARK | MKMK => Action::Attachment,
_ => Action::Adjustment,
}
};
Some(Feature::from_tag(item.tag, action))
}
FeaturesKind::AtAll(iter) => {
let (stage, tag) = iter.next()?;
let action = if stage == 0 {
Action::Substitution
} else {
match tag {
MARK | MKMK => Action::Attachment,
_ => Action::Adjustment,
}
};
Some(Feature::from_tag(tag, action))
}
FeaturesKind::Aat(iter) => {
let (tag, name) = iter.next()?;
let action = if tag == raw_tag(b"kern") {
Action::Adjustment
} else {
Action::Substitution
};
Some(Feature {
tag,
name: Some(name),
action,
})
}
_ => None,
}
}
}

269
vendor/swash/src/feature/util.rs vendored Normal file
View File

@@ -0,0 +1,269 @@
use super::internal::{raw_tag, RawTag};
#[derive(Copy, Clone)]
pub struct SeenFeatures {
bits: [u32; 6],
}
impl SeenFeatures {
pub fn new() -> Self {
Self { bits: [0; 6] }
}
pub fn mark(&mut self, feature_index: usize) -> bool {
let word = &mut self.bits[feature_index / 32];
let bit = 1 << (feature_index % 32);
if *word & bit == 0 {
*word |= bit;
true
} else {
false
}
}
}
/// Returns a feature description for the specified tag.
pub fn desc_from_at(tag: RawTag) -> Option<(usize, &'static str)> {
Some(match FEATURES.binary_search_by(|e| e.0.cmp(&tag)) {
Ok(index) => (index, FEATURES[index].1),
_ => return None,
})
}
/// Returns a feature tag and description from an AAT feature
/// and selector.
pub fn desc_from_aat(feature: u16, selector: u16) -> Option<(usize, RawTag, &'static str)> {
let key = (feature << 8) | selector;
Some(match AAT_TO_AT.binary_search_by(|pair| pair.0.cmp(&key)) {
Ok(index) => {
let (tag, desc) = FEATURES[AAT_TO_AT[index].1 as usize];
(index, tag, desc)
}
_ => return None,
})
}
pub const FEATURES: [(u32, &'static str); 142] = [
(raw_tag(b"aalt"), "Access All Alternates"),
(raw_tag(b"abvf"), "Above-base Forms"),
(raw_tag(b"abvm"), "Above-base Mark Positioning"),
(raw_tag(b"abvs"), "Above-base Substitutions"),
(raw_tag(b"afrc"), "Alternative Fractions"),
(raw_tag(b"akhn"), "Akhands"),
(raw_tag(b"blwf"), "Below-base Forms"),
(raw_tag(b"blwm"), "Below-base Mark Positioning"),
(raw_tag(b"blws"), "Below-base Substitutions"),
(raw_tag(b"c2pc"), "Petite Capitals From Capitals"),
(raw_tag(b"c2sc"), "Small Capitals From Capitals"),
(raw_tag(b"calt"), "Contextual Alternates"),
(raw_tag(b"case"), "Case-Sensitive Forms"),
(raw_tag(b"ccmp"), "Glyph Composition / Decomposition"),
(raw_tag(b"cfar"), "Conjunct Form After Ro"),
(raw_tag(b"chws"), "Contextual Half-width Spacing"),
(raw_tag(b"cjct"), "Conjunct Forms"),
(raw_tag(b"clig"), "Contextual Ligatures"),
(raw_tag(b"cpct"), "Centered CJK Punctuation"),
(raw_tag(b"cpsp"), "Capital Spacing"),
(raw_tag(b"cswh"), "Contextual Swash"),
(raw_tag(b"curs"), "Cursive Positioning"),
(raw_tag(b"dist"), "Distances"),
(raw_tag(b"dlig"), "Discretionary Ligatures"),
(raw_tag(b"dnom"), "Denominators"),
(raw_tag(b"dtls"), "Dotless Forms"),
(raw_tag(b"expt"), "Expert Forms"),
(raw_tag(b"falt"), "Final Glyph on Line Alternates"),
(raw_tag(b"fin2"), "Terminal Forms #2"),
(raw_tag(b"fin3"), "Terminal Forms #3"),
(raw_tag(b"fina"), "Terminal Forms"),
(raw_tag(b"flac"), "Flattened accent forms"),
(raw_tag(b"frac"), "Fractions"),
(raw_tag(b"fwid"), "Full Widths"),
(raw_tag(b"half"), "Half Forms"),
(raw_tag(b"haln"), "Halant Forms"),
(raw_tag(b"halt"), "Alternate Half Widths"),
(raw_tag(b"hist"), "Historical Forms"),
(raw_tag(b"hkna"), "Horizontal Kana Alternates"),
(raw_tag(b"hlig"), "Historical Ligatures"),
(raw_tag(b"hngl"), "Hangul"),
(raw_tag(b"hojo"), "Hojo Kanji Forms"),
(raw_tag(b"hwid"), "Half Widths"),
(raw_tag(b"init"), "Initial Forms"),
(raw_tag(b"isol"), "Isolated Forms"),
(raw_tag(b"ital"), "Italics"),
(raw_tag(b"jalt"), "Justification Alternates"),
(raw_tag(b"jp78"), "JIS78 Forms"),
(raw_tag(b"jp83"), "JIS83 Forms"),
(raw_tag(b"jp90"), "JIS90 Forms"),
(raw_tag(b"jp04"), "JIS2004 Forms"),
(raw_tag(b"kern"), "Kerning"),
(raw_tag(b"lfbd"), "Left Bounds"),
(raw_tag(b"liga"), "Standard Ligatures"),
(raw_tag(b"ljmo"), "Leading Jamo Forms"),
(raw_tag(b"lnum"), "Lining Figures"),
(raw_tag(b"locl"), "Localized Forms"),
(raw_tag(b"ltra"), "Left-to-right alternates"),
(raw_tag(b"ltrm"), "Left-to-right mirrored forms"),
(raw_tag(b"mark"), "Mark Positioning"),
(raw_tag(b"med2"), "Medial Forms #2"),
(raw_tag(b"medi"), "Medial Forms"),
(raw_tag(b"mgrk"), "Mathematical Greek"),
(raw_tag(b"mkmk"), "Mark to Mark Positioning"),
(raw_tag(b"mset"), "Mark Positioning via Substitution"),
(raw_tag(b"nalt"), "Alternate Annotation Forms"),
(raw_tag(b"nlck"), "NLC Kanji Forms"),
(raw_tag(b"nukt"), "Nukta Forms"),
(raw_tag(b"numr"), "Numerators"),
(raw_tag(b"onum"), "Oldstyle Figures"),
(raw_tag(b"opbd"), "Optical Bounds"),
(raw_tag(b"ordn"), "Ordinals"),
(raw_tag(b"ornm"), "Ornaments"),
(raw_tag(b"palt"), "Proportional Alternate Widths"),
(raw_tag(b"pcap"), "Petite Capitals"),
(raw_tag(b"pkna"), "Proportional Kana"),
(raw_tag(b"pnum"), "Proportional Figures"),
(raw_tag(b"pref"), "Pre-Base Forms"),
(raw_tag(b"pres"), "Pre-base Substitutions"),
(raw_tag(b"pstf"), "Post-base Forms"),
(raw_tag(b"psts"), "Post-base Substitutions"),
(raw_tag(b"pwid"), "Proportional Widths"),
(raw_tag(b"qwid"), "Quarter Widths"),
(raw_tag(b"rand"), "Randomize"),
(raw_tag(b"rclt"), "Required Contextual Alternates"),
(raw_tag(b"rkrf"), "Rakar Forms"),
(raw_tag(b"rlig"), "Required Ligatures"),
(raw_tag(b"rphf"), "Reph Forms"),
(raw_tag(b"rtbd"), "Right Bounds"),
(raw_tag(b"rtla"), "Right-to-left alternates"),
(raw_tag(b"rtlm"), "Right-to-left mirrored forms"),
(raw_tag(b"ruby"), "Ruby Notation Forms"),
(raw_tag(b"rvrn"), "Required Variation Alternates"),
(raw_tag(b"salt"), "Stylistic Alternates"),
(raw_tag(b"sinf"), "Scientific Inferiors"),
(raw_tag(b"size"), "Optical size"),
(raw_tag(b"smcp"), "Small Capitals"),
(raw_tag(b"smpl"), "Simplified Forms"),
(raw_tag(b"ss01"), "Stylistic Set 1"),
(raw_tag(b"ss02"), "Stylistic Set 2"),
(raw_tag(b"ss03"), "Stylistic Set 3"),
(raw_tag(b"ss04"), "Stylistic Set 4"),
(raw_tag(b"ss05"), "Stylistic Set 5"),
(raw_tag(b"ss06"), "Stylistic Set 6"),
(raw_tag(b"ss07"), "Stylistic Set 7"),
(raw_tag(b"ss08"), "Stylistic Set 8"),
(raw_tag(b"ss09"), "Stylistic Set 9"),
(raw_tag(b"ss10"), "Stylistic Set 10"),
(raw_tag(b"ss11"), "Stylistic Set 11"),
(raw_tag(b"ss12"), "Stylistic Set 12"),
(raw_tag(b"ss13"), "Stylistic Set 13"),
(raw_tag(b"ss14"), "Stylistic Set 14"),
(raw_tag(b"ss15"), "Stylistic Set 15"),
(raw_tag(b"ss16"), "Stylistic Set 16"),
(raw_tag(b"ss17"), "Stylistic Set 17"),
(raw_tag(b"ss18"), "Stylistic Set 18"),
(raw_tag(b"ss19"), "Stylistic Set 19"),
(raw_tag(b"ss20"), "Stylistic Set 20"),
(raw_tag(b"ssty"), "Math script style alternates"),
(raw_tag(b"stch"), "Stretching Glyph Decomposition"),
(raw_tag(b"subs"), "Subscript"),
(raw_tag(b"sups"), "Superscript"),
(raw_tag(b"swsh"), "Swash"),
(raw_tag(b"titl"), "Titling"),
(raw_tag(b"tjmo"), "Trailing Jamo Forms"),
(raw_tag(b"tnam"), "Traditional Name Forms"),
(raw_tag(b"tnum"), "Tabular Figures"),
(raw_tag(b"trad"), "Traditional Forms"),
(raw_tag(b"twid"), "Third Widths"),
(raw_tag(b"unic"), "Unicase"),
(raw_tag(b"valt"), "Alternate Vertical Metrics"),
(raw_tag(b"vatu"), "Vattu Variants"),
(raw_tag(b"vchw"), "Vertical Contextual Half-width Spacing"),
(raw_tag(b"vert"), "Vertical Writing"),
(raw_tag(b"vhal"), "Alternate Vertical Half Metrics"),
(raw_tag(b"vjmo"), "Vowel Jamo Forms"),
(raw_tag(b"vkna"), "Vertical Kana Alternates"),
(raw_tag(b"vkrn"), "Vertical Kerning"),
(raw_tag(b"vpal"), "Proportional Alternate Vertical Metrics"),
(raw_tag(b"vrt2"), "Vertical Alternates and Rotation"),
(raw_tag(b"vrtr"), "Vertical Alternates for Rotation"),
(raw_tag(b"zero"), "Slashed Zero"),
];
pub const AAT_TO_AT: [(u16, u16); 77] = [
(256, 86),
(258, 53),
(260, 23),
(274, 15),
(276, 37),
(276, 39),
(782, 129),
(1024, 133),
(1024, 139),
(1536, 126),
(1537, 76),
(2561, 121),
(2562, 120),
(2563, 71),
(2564, 94),
(2817, 4),
(2818, 32),
(3588, 141),
(3850, 62),
(4868, 123),
(5120, 127),
(5121, 97),
(5122, 47),
(5123, 48),
(5124, 49),
(5130, 26),
(5131, 50),
(5132, 41),
(5133, 66),
(5134, 125),
(5376, 69),
(5377, 55),
(5632, 81),
(5632, 75),
(5633, 33),
(5634, 42),
(5635, 128),
(5636, 82),
(5637, 73),
(5637, 130),
(5637, 138),
(5638, 36),
(5638, 134),
(5889, 40),
(7170, 91),
(8194, 45),
(8448, 10),
(8450, 17),
(8704, 38),
(8706, 136),
(8962, 98),
(8964, 99),
(8966, 100),
(8968, 101),
(8970, 102),
(8972, 103),
(8974, 104),
(8976, 105),
(8978, 106),
(8980, 107),
(8982, 108),
(8984, 109),
(8986, 110),
(8988, 111),
(8990, 112),
(8992, 113),
(8994, 114),
(8996, 115),
(8998, 116),
(9000, 117),
(9216, 9),
(9218, 122),
(9220, 18),
(9473, 96),
(9474, 74),
(9729, 21),
(9730, 20),
];

264
vendor/swash/src/font.rs vendored Normal file
View File

@@ -0,0 +1,264 @@
use super::cache::CacheKey;
use super::internal::{raw_data, RawFont};
use super::Tag;
/// Reference to the content of a font file.
#[derive(Copy, Clone)]
pub struct FontDataRef<'a> {
data: &'a [u8],
len: usize,
}
impl<'a> FontDataRef<'a> {
/// Creates font data from the specified bytes. Returns `None` if the bytes
/// cannot trivially be determined to represent a font.
pub fn new(data: &'a [u8]) -> Option<Self> {
if !raw_data::is_font(data, 0) && !raw_data::is_collection(data) {
None
} else {
Some(Self {
data,
len: raw_data::count(data) as usize,
})
}
}
/// Returns true if the data represents a font collection.
pub fn is_collection(&self) -> bool {
raw_data::is_collection(self.data)
}
/// Returns the underlying data.
pub fn data(&self) -> &'a [u8] {
self.data
}
/// Returns the number of available fonts.
pub fn len(&self) -> usize {
self.len
}
/// Returns true if there are no available fonts.
pub fn is_empty(&self) -> bool {
self.len == 0
}
/// Returns the font at the specified index.
pub fn get(&self, index: usize) -> Option<FontRef<'a>> {
FontRef::from_offset(self.data, raw_data::offset(self.data, index as u32)?)
}
/// Returns an iterator over the available fonts.
pub fn fonts(&self) -> Fonts<'a> {
Fonts {
data: *self,
pos: 0,
}
}
}
/// Reference to a font.
///
/// This struct encapsulates the data required to access font resources and
/// uniquely identify the font in various caches. Font files can be organized
/// into collections of multiple fonts, so along with a reference to the actual
/// content of the file, we also store a byte offset to the header of the
/// selected font (although most fonts are not collections so this is almost always
/// zero). Note that internal references in the font are stored relative to the
/// base of the file, so the entire file must be kept in memory and it is an error
/// to slice the data at the offset.
///
/// # Getting started
/// As a primer, let's write a function to read a font file, construct a font reference
/// and print the [`Attributes`](super::Attributes) and all of the associated
/// [`LocalizedString`](super::LocalizedString)s (including names, copyright information
/// and other metadata).
///
/// ```
/// fn print_localized_strings(font_path: &str) -> Option<()> {
/// use swash::FontRef;
/// // Read the full font file
/// let font_data = std::fs::read(font_path).ok()?;
/// // Create a font reference for the first font in the file
/// let font = FontRef::from_index(&font_data, 0)?;
/// // Print the font attributes (stretch, weight and style)
/// println!("{}", font.attributes());
/// // Iterate through the localized strings
/// for string in font.localized_strings() {
/// // Print the string identifier and the actual value
/// println!("[{:?}] {}", string.id(), string.to_string());
/// }
/// Some(())
/// }
/// ```
///
/// # Owning your fonts
/// The [`FontRef`] struct is designed to be agnostic with regard to the font management
/// policy of higher level crates and applications, and as such, contains borrowed
/// data and is intended to be used as a transient resource. If you're using this
/// library, you'll likely want to create your own type to represent a font.
/// Regardless of the complexity of your management strategy, the basic pattern remains
/// the same, so we'll build a simple `Font` struct here that can load fonts from a
/// file using a basic `Vec<u8>` as a backing store.
/// ```
/// use swash::{Attributes, CacheKey, Charmap, FontRef};
///
/// pub struct Font {
/// // Full content of the font file
/// data: Vec<u8>,
/// // Offset to the table directory
/// offset: u32,
/// // Cache key
/// key: CacheKey,
/// }
///
/// impl Font {
/// pub fn from_file(path: &str, index: usize) -> Option<Self> {
/// // Read the full font file
/// let data = std::fs::read(path).ok()?;
/// // Create a temporary font reference for the first font in the file.
/// // This will do some basic validation, compute the necessary offset
/// // and generate a fresh cache key for us.
/// let font = FontRef::from_index(&data, index)?;
/// let (offset, key) = (font.offset, font.key);
/// // Return our struct with the original file data and copies of the
/// // offset and key from the font reference
/// Some(Self { data, offset, key })
/// }
///
/// // As a convenience, you may want to forward some methods.
/// pub fn attributes(&self) -> Attributes {
/// self.as_ref().attributes()
/// }
///
/// pub fn charmap(&self) -> Charmap {
/// self.as_ref().charmap()
/// }
///
/// // Create the transient font reference for accessing this crate's
/// // functionality.
/// pub fn as_ref(&self) -> FontRef {
/// // Note that you'll want to initialize the struct directly here as
/// // using any of the FontRef constructors will generate a new key which,
/// // while completely safe, will nullify the performance optimizations of
/// // the caching mechanisms used in this crate.
/// FontRef {
/// data: &self.data,
/// offset: self.offset,
/// key: self.key
/// }
/// }
/// }
/// ```
/// In the example above, it's trivial to replace the `Vec<u8>` with an
/// `Rc<Vec<u8>>` for a reference counted version or an `Arc<Vec<u8>>` for fonts
/// that are shareable across threads. You may also consider memory mapping
/// your font data, particularly for larger fonts (hello Apple Color Emoji!).
///
#[derive(Copy, Clone)]
pub struct FontRef<'a> {
/// Full content of a file containing the font.
pub data: &'a [u8],
/// Offset to the table directory of the font.
pub offset: u32,
/// Key for identifying a font in various caches.
pub key: CacheKey,
}
impl<'a> FontRef<'a> {
/// Creates a new font from the specified font data and the index of the
/// desired font. Returns `None` if the data does not represent a font file
/// or the index is out of bounds.
pub fn from_index(data: &'a [u8], index: usize) -> Option<Self> {
FontDataRef::new(data)?.get(index)
}
/// Creates a new font from the specified font data and offset to the
/// table directory. Returns `None` if the offset is out of bounds or the
/// data at the offset does not represent a table directory.
pub fn from_offset(data: &'a [u8], offset: u32) -> Option<Self> {
if !raw_data::is_font(data, offset) {
None
} else {
Some(Self {
data,
offset,
key: CacheKey::new(),
})
}
}
}
impl<'a> RawFont<'a> for FontRef<'a> {
fn data(&self) -> &'a [u8] {
self.data
}
fn offset(&self) -> u32 {
self.offset
}
}
/// Iterator over a collection of fonts.
pub struct Fonts<'a> {
data: FontDataRef<'a>,
pos: usize,
}
impl<'a> Iterator for Fonts<'a> {
type Item = FontRef<'a>;
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.data.len - self.pos;
(remaining, Some(remaining))
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let pos = self.pos.checked_add(n)?;
self.pos = pos.checked_add(1)?;
self.data.get(pos)
}
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.data.len {
None
} else {
let pos = self.pos;
self.pos += 1;
self.data.get(pos)
}
}
}
impl<'a> ExactSizeIterator for Fonts<'a> {
fn len(&self) -> usize {
self.data.len - self.pos
}
}
impl<'a> IntoIterator for FontDataRef<'a> {
type IntoIter = Fonts<'a>;
type Item = FontRef<'a>;
fn into_iter(self) -> Self::IntoIter {
self.fonts()
}
}
/// Source that can provide table data by tag.
pub trait TableProvider {
/// Returns the table for the specified tag.
fn table_by_tag(&self, tag: Tag) -> Option<&[u8]>;
}
impl<'a> TableProvider for FontRef<'a> {
fn table_by_tag(&self, tag: Tag) -> Option<&[u8]> {
self.table_data(tag)
}
}
impl<'a> TableProvider for &'a FontRef<'a> {
fn table_by_tag(&self, tag: Tag) -> Option<&[u8]> {
self.table_data(tag)
}
}

1830
vendor/swash/src/internal/aat.rs vendored Normal file

File diff suppressed because it is too large Load Diff

958
vendor/swash/src/internal/at.rs vendored Normal file
View File

@@ -0,0 +1,958 @@
//! OpenType advanced typography tables.
use super::{raw_tag, Bytes, RawTag};
pub const GDEF: RawTag = raw_tag(b"GDEF");
pub const GSUB: RawTag = raw_tag(b"GSUB");
pub const GPOS: RawTag = raw_tag(b"GPOS");
pub const DFLT: RawTag = raw_tag(b"DFLT");
/// Glyph definition table.
#[derive(Copy, Clone)]
pub struct Gdef<'a> {
data: Bytes<'a>,
classes: u16,
mark_classes: u16,
mark_sets: u16,
var_store: u32,
}
impl<'a> Gdef<'a> {
pub fn new(data: &'a [u8]) -> Option<Self> {
let b = Bytes::new(data);
let major = b.read::<u16>(0)?;
let minor = b.read::<u16>(2)?;
let classes = b.read::<u16>(4)?;
let mark_classes = b.read::<u16>(10)?;
let mark_sets = if major > 1 || minor >= 2 {
b.read_or_default::<u16>(12)
} else {
0
};
let var_store = if major > 1 || minor >= 3 {
b.read_or_default::<u32>(14)
} else {
0
};
Some(Self {
data: b,
classes,
mark_classes,
mark_sets,
var_store,
})
}
pub fn from_offset(data: &'a [u8], offset: u32) -> Option<Self> {
if offset == 0 {
return None;
}
Self::new(data.get(offset as usize..)?)
}
pub fn empty() -> Self {
Self {
data: Bytes::new(&[]),
classes: 0,
mark_classes: 0,
mark_sets: 0,
var_store: 0,
}
}
pub fn ok(&self) -> bool {
self.data.len() != 0
}
/// Returns true if glyph classes are available.
pub fn has_classes(&self) -> bool {
self.classes != 0
}
/// Returns the class for the specified glyph id.
pub fn class(&self, glyph_id: u16) -> u16 {
classdef(&self.data, self.classes as u32, glyph_id)
}
/// Returns true if mark glyph classes are available.
pub fn has_mark_classes(&self) -> bool {
self.mark_classes != 0
}
/// Returns the mark class for the specified glyph id.
pub fn mark_class(&self, glyph_id: u16) -> u16 {
classdef(&self.data, self.mark_classes as u32, glyph_id)
}
pub fn mark_set_coverage(&self, set_offset: u32, glyph_id: u16) -> Option<u16> {
if set_offset == 0 {
return None;
}
// Coverage is validated by mark_set_offset() below.
unsafe { fast_coverage(&self.data, set_offset, glyph_id) }
}
pub fn mark_set_offset(&self, set_index: u16) -> Option<u32> {
if self.mark_sets == 0 {
return None;
}
let set = set_index as usize;
let b = &self.data;
let sets_base = self.mark_sets as usize;
let len = b.read::<u16>(sets_base + 2)? as usize;
if set >= len {
return None;
}
let offset = b.read::<u32>(sets_base + 4 + set * 4)?;
let set_offset = sets_base as u32 + offset;
(offset != 0 && validate_coverage(b, set_offset)).then_some(set_offset)
}
pub fn has_var_store(&self) -> bool {
self.var_store != 0
}
pub fn delta(&self, outer: u16, inner: u16, coords: &[i16]) -> f32 {
if self.var_store != 0 {
super::var::item_delta(self.data.data(), self.var_store, outer, inner, coords)
.map(|d| d.to_f32())
.unwrap_or(0.)
} else {
0.
}
}
}
/// Feature lookup kind.
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum LookupKind {
SingleSub,
MultiSub,
AltSub,
LigSub,
SingleAdj,
PairAdj,
Cursive,
MarkToBase,
MarkToLig,
MarkToMark,
Context,
ChainContext,
RevChainContext,
}
/// Data associated with a feature lookup.
#[derive(Copy, Clone)]
pub struct LookupData {
pub index: u16,
pub stage: u8,
pub kind: LookupKind,
pub feature: u16,
pub mask: u8,
pub ignored: u8,
pub is_ext: bool,
pub offset: u32,
pub coverage: u32,
pub count: u16,
pub subtables: (u16, u16),
pub mark_set: u32,
pub mark_check: u8,
pub mark_class: u8,
}
impl LookupData {
pub fn subtable_data(&self, b: &Bytes, index: u16) -> Option<SubtableData> {
let base = self.offset as usize;
let subtable_base = base + 6;
let mut offset = base + b.read::<u16>(subtable_base + index as usize * 2)? as usize;
if self.is_ext {
offset = offset + b.read::<u32>(offset + 4)? as usize;
}
let fmt = b.read::<u16>(offset)?;
subtable_data(b, offset as u32, self.kind, fmt)
}
}
/// Lookup subtable kind, flattened to include the associated format.
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum SubtableKind {
SingleSub1,
SingleSub2,
MultiSub1,
AltSub1,
LigSub1,
SingleAdj1,
SingleAdj2,
PairAdj1,
PairAdj2,
Cursive1,
MarkToBase1,
MarkToLig1,
MarkToMark1,
Context1,
Context2,
Context3,
ChainContext1,
ChainContext2,
ChainContext3,
RevChainContext1,
}
/// Data associated with a lookup subtable.
#[derive(Copy, Clone)]
pub struct SubtableData {
pub offset: u32,
pub kind: SubtableKind,
pub coverage: u16,
}
impl SubtableData {
pub fn coverage(&self, b: &Bytes, glyph_id: u16) -> Option<u16> {
unsafe { fast_coverage(b, self.offset + self.coverage as u32, glyph_id) }
}
}
/// Feature substitutions for variable fonts.
#[derive(Copy, Clone)]
pub struct FeatureSubsts(u32);
impl FeatureSubsts {
pub fn new(b: &Bytes, offset: u32, coords: &[i16]) -> Option<Self> {
if offset == 0 || coords.is_empty() {
return None;
}
let base = offset as usize;
let count = b.read::<u32>(base + 4)? as usize;
for i in 0..count {
let rec = base + 8 + i * 8;
let condset_table = base + b.read::<u32>(rec)? as usize;
let condset_count = b.read::<u16>(condset_table)? as usize;
let mut matched = 0;
for j in 0..condset_count {
let cond_table = condset_table + b.read::<u32>(condset_table + 2 + j * 4)? as usize;
let format = b.read::<u16>(cond_table)?;
if format != 1 {
break;
}
let axis = b.read::<u16>(cond_table + 2)? as usize;
if axis >= coords.len() {
break;
}
let coord = coords[axis];
let min = b.read::<i16>(cond_table + 4)?;
if coord < min {
break;
}
let max = b.read::<i16>(cond_table + 6)?;
if coord > max {
break;
}
matched += 1;
}
if matched == condset_count {
return Some(Self(offset + b.read::<u32>(rec + 4)?));
}
}
None
}
pub fn apply(self, b: &Bytes, index: u16) -> Option<usize> {
let mut base = self.0 as usize;
let count = b.read::<u16>(base + 4)? as usize;
base += 6;
let mut l = 0;
let mut h = count;
while l < h {
use core::cmp::Ordering::*;
let i = (l + h) / 2;
let rec = base + i * 6;
let idx = b.read::<u16>(rec)?;
match index.cmp(&idx) {
Less => h = i,
Greater => l = i + 1,
Equal => return Some((self.0 + b.read::<u32>(rec + 2)?) as usize),
}
}
None
}
}
pub fn script_count(b: &Bytes, gsubgpos_offset: u32) -> u16 {
if gsubgpos_offset == 0 {
return 0;
}
let base = gsubgpos_offset as usize;
let offset = b.read_or_default::<u16>(base + 4) as usize;
if offset == 0 {
return 0;
}
b.read_or_default::<u16>(base + offset)
}
pub fn script_at(b: &Bytes, gsubgpos_offset: u32, index: u16) -> Option<(RawTag, u32)> {
if gsubgpos_offset == 0 {
return None;
}
let base = gsubgpos_offset as usize;
let sbase = base + b.read::<u16>(base + 4)? as usize;
let rec = sbase + 2 + index as usize * 6;
let tag = b.read::<u32>(rec)?;
let offset = sbase as u32 + b.read::<u16>(rec + 4)? as u32;
Some((tag, offset))
}
pub fn script_by_tag(b: &Bytes, gsubgpos_offset: u32, script: RawTag) -> Option<u32> {
if gsubgpos_offset == 0 {
return None;
}
let base = gsubgpos_offset as usize;
let sbase = base + b.read::<u16>(base + 4)? as usize;
let mut l = 0;
let mut h = b.read::<u16>(sbase)? as usize;
while l < h {
use core::cmp::Ordering::*;
let i = l + (h - l) / 2;
let rec = sbase + 2 + i * 6;
let t = b.read::<u32>(rec)?;
match script.cmp(&t) {
Less => h = i,
Greater => l = i + 1,
Equal => return Some(sbase as u32 + b.read::<u16>(rec + 4)? as u32),
}
}
None
}
pub fn script_language_count(b: &Bytes, script_offset: u32) -> u16 {
if script_offset == 0 {
return 0;
}
b.read::<u16>(script_offset as usize + 2)
.map(|n| n + 1)
.unwrap_or(0)
}
pub fn script_default_language(b: &Bytes, script_offset: u32) -> Option<u32> {
if script_offset == 0 {
return None;
}
let offset = b.read::<u16>(script_offset as usize)? as u32;
if offset == 0 {
None
} else {
Some(script_offset + offset)
}
}
pub fn script_language_at(b: &Bytes, script_offset: u32, index: u16) -> Option<(RawTag, u32)> {
if script_offset == 0 {
return None;
}
let index = if index == 0 {
return Some((DFLT, script_default_language(b, script_offset)?));
} else {
index - 1
};
let rec = script_offset as usize + 4 + index as usize * 6;
let tag = b.read::<u32>(rec)?;
let offset = b.read::<u16>(rec + 4)? as u32;
if offset == 0 {
return None;
}
Some((tag, script_offset + offset))
}
pub fn script_language_by_tag(
b: &Bytes,
script_offset: u32,
language: Option<RawTag>,
) -> Option<(u32, bool)> {
if script_offset == 0 {
return None;
}
let base = script_offset as usize;
if let Some(lang) = language {
let mut l = 0;
let mut h = b.read::<u16>(base + 2)? as usize;
while l < h {
use core::cmp::Ordering::*;
let i = (l + h) / 2;
let rec = base + 4 + i * 6;
let t = b.read::<u32>(rec)?;
match lang.cmp(&t) {
Less => h = i,
Greater => l = i + 1,
Equal => {
let lang_offset = b.read::<u16>(rec + 4)? as usize;
if lang_offset == 0 {
return None;
}
return Some((script_offset + lang_offset as u32, false));
}
}
}
}
let default = b.read::<u16>(base)? as usize;
if default == 0 {
return None;
}
Some(((base + default) as u32, true))
}
pub fn language_or_default_by_tags(
b: &Bytes,
gsubgpos_offset: u32,
script: RawTag,
lang: Option<RawTag>,
) -> Option<(u32, [RawTag; 2])> {
if let Some(script_offset) = script_by_tag(b, gsubgpos_offset, script) {
let (lang_offset, is_default) = script_language_by_tag(b, script_offset, lang)?;
Some((
lang_offset,
[
script,
if is_default {
DFLT
} else {
lang.unwrap_or(DFLT)
},
],
))
} else {
let (lang_offset, is_default) = language_by_tags(b, gsubgpos_offset, DFLT, lang)?;
Some((
lang_offset,
[
DFLT,
if is_default {
DFLT
} else {
lang.unwrap_or(DFLT)
},
],
))
}
}
pub fn language_by_tags(
b: &Bytes,
gsubgpos_offset: u32,
script: RawTag,
language: Option<RawTag>,
) -> Option<(u32, bool)> {
script_language_by_tag(b, script_by_tag(b, gsubgpos_offset, script)?, language)
}
pub fn language_feature_count(b: &Bytes, language_offset: u32) -> u16 {
if language_offset == 0 {
return 0;
}
b.read_or_default(language_offset as usize + 4)
}
pub fn language_feature_at(b: &Bytes, language_offset: u32, index: u16) -> Option<u16> {
b.read(language_offset as usize + 6 + index as usize * 2)
}
pub fn language_features<'a>(
b: Bytes<'a>,
gsubgpos_offset: u32,
language_offset: u32,
) -> impl Iterator<Item = (RawTag, u32)> + 'a + Clone {
let mut count = language_feature_count(&b, language_offset);
if gsubgpos_offset == 0 {
count = 0;
}
let base = gsubgpos_offset as usize;
let fbase = b.read_or_default::<u16>(base + 6) as usize;
if fbase == 0 {
count = 0;
}
let fbase = base + fbase;
(0..count).filter_map(move |i| {
let index = language_feature_at(&b, language_offset, i)?;
let rec = fbase + 2 + index as usize * 6;
let tag = b.read::<u32>(rec)?;
let offset = b.read::<u16>(rec + 4)?;
if offset == 0 {
return None;
}
Some((tag, fbase as u32 + offset as u32))
})
}
pub fn feature_count(b: &Bytes, gsubgpos_offset: u32) -> u16 {
if gsubgpos_offset == 0 {
return 0;
}
let base = gsubgpos_offset as usize;
let fbase = b.read_or_default::<u16>(base + 6) as usize;
if fbase == 0 {
return 0;
}
b.read_or_default::<u16>(base + fbase)
}
pub fn feature_at(b: &Bytes, gsubgpos_offset: u32, index: u16) -> Option<(RawTag, u32)> {
if gsubgpos_offset == 0 {
return None;
}
let base = gsubgpos_offset as usize;
let fbase = b.read::<u16>(base + 6)? as usize;
if fbase == 0 {
return None;
}
let fbase = base + fbase;
let rec = fbase + 2 + index as usize * 6;
let tag = b.read::<u32>(rec)?;
let offset = b.read::<u16>(rec + 4)?;
if offset == 0 {
return None;
}
Some((tag, fbase as u32 + offset as u32))
}
pub fn feature_var_offset(b: &Bytes, gsubgpos_offset: u32) -> u32 {
if gsubgpos_offset == 0 {
return 0;
}
let base = gsubgpos_offset as usize;
let major = b.read_or_default::<u16>(base);
if major > 1 || (major == 1 && b.read_or_default::<u16>(base + 2) >= 1) {
let offset = b.read_or_default::<u32>(base + 10);
if offset != 0 {
gsubgpos_offset + offset
} else {
0
}
} else {
0
}
}
pub fn lookup_data(
b: &Bytes,
stage: u8,
list_base: u32,
index: u16,
mask: u8,
gdef: Option<&Gdef>,
) -> Option<LookupData> {
if list_base == 0 {
return None;
}
let base = list_base as usize;
let rec = base + 2 + index as usize * 2;
let offset = b.read::<u16>(rec)?;
let base = base + offset as usize;
let mut kind = b.read::<u16>(base)? as u8;
let flag = b.read::<u16>(base + 2)?;
let f = flag as u8;
let count = b.read::<u16>(base + 4)?;
let mark_class = (flag >> 8) as u8;
let ignore_marks = f & (1 << 3) != 0;
let mut mark_check = 0;
let mut mark_set = 0;
if !ignore_marks {
if let Some(gdef) = gdef {
mark_check = (mark_class != 0 && gdef.has_mark_classes()) as u8;
mark_set = if gdef.ok() && flag & 0x10 != 0 {
let idx = b.read::<u16>(base + 6 + count as usize * 2)?;
mark_check = 1;
gdef.mark_set_offset(idx).unwrap_or(0)
} else {
0
};
}
}
let is_sub = stage == 0;
let subtables = base + 6;
let is_ext = (is_sub && kind == 7) || (!is_sub && kind == 9);
if is_ext && count > 0 {
let s = base + b.read::<u16>(subtables)? as usize;
kind = b.read::<u16>(s + 2)? as u8;
}
use LookupKind::*;
let kind = if stage == 0 {
match kind {
1 => SingleSub,
2 => MultiSub,
3 => AltSub,
4 => LigSub,
5 => Context,
6 => ChainContext,
8 => RevChainContext,
_ => return None,
}
} else {
match kind {
1 => SingleAdj,
2 => PairAdj,
3 => Cursive,
4 => MarkToBase,
5 => MarkToLig,
6 => MarkToMark,
7 => Context,
8 => ChainContext,
_ => return None,
}
};
let ignored = (f & 0b1110) | 1 << 5;
Some(LookupData {
index,
stage,
kind,
feature: 0,
mask,
ignored,
is_ext,
offset: base as u32,
count,
coverage: !0,
subtables: (0, 0),
mark_class,
mark_set,
mark_check,
})
}
pub fn subtable_data(b: &Bytes, offset: u32, kind: LookupKind, fmt: u16) -> Option<SubtableData> {
let base = offset as usize;
fn cov(b: &Bytes, base: usize, offset: usize) -> Option<u16> {
let c = b.read::<u16>(base + offset)?;
validate_coverage(b, base as u32 + c as u32).then_some(c)
}
use LookupKind::*;
match kind {
SingleSub => {
let kind = match fmt {
1 => SubtableKind::SingleSub1,
2 => SubtableKind::SingleSub2,
_ => return None,
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
MultiSub => {
let kind = match fmt {
1 => SubtableKind::MultiSub1,
_ => return None,
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
AltSub => {
let kind = match fmt {
1 => SubtableKind::AltSub1,
_ => return None,
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
LigSub => {
let kind = match fmt {
1 => SubtableKind::LigSub1,
_ => return None,
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
SingleAdj => {
let kind = match fmt {
1 => SubtableKind::SingleAdj1,
2 => SubtableKind::SingleAdj2,
_ => return None,
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
PairAdj => {
let kind = match fmt {
1 => SubtableKind::PairAdj1,
2 => SubtableKind::PairAdj2,
_ => return None,
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
Cursive => {
let kind = match fmt {
1 => SubtableKind::Cursive1,
_ => return None,
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
MarkToBase => {
let kind = match fmt {
1 => SubtableKind::MarkToBase1,
_ => return None,
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
MarkToLig => {
let kind = match fmt {
1 => SubtableKind::MarkToLig1,
_ => return None,
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
MarkToMark => {
let kind = match fmt {
1 => SubtableKind::MarkToMark1,
_ => return None,
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
Context => match fmt {
1 | 2 => {
let kind = if fmt == 1 {
SubtableKind::Context1
} else {
SubtableKind::Context2
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
3 => {
let coverage = cov(b, base, 6)?;
Some(SubtableData {
kind: SubtableKind::Context3,
offset,
coverage,
})
}
_ => None,
},
ChainContext => match fmt {
1 | 2 => {
let kind = if fmt == 1 {
SubtableKind::ChainContext1
} else {
SubtableKind::ChainContext2
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
3 => {
let backtrack_len = b.read::<u16>(base + 2)? as usize * 2;
let input_len = b.read::<u16>(base + backtrack_len + 4)?;
if input_len == 0 {
return None;
}
let coverage = cov(b, base, backtrack_len + 6)?;
Some(SubtableData {
kind: SubtableKind::ChainContext3,
offset,
coverage,
})
}
_ => None,
},
RevChainContext => {
let kind = match fmt {
1 => SubtableKind::RevChainContext1,
_ => return None,
};
let coverage = cov(b, base, 2)?;
Some(SubtableData {
offset,
kind,
coverage,
})
}
}
}
fn validate_coverage(b: &Bytes, coverage_offset: u32) -> bool {
if coverage_offset == 0 {
return false;
}
let base = coverage_offset as usize;
let arr = base + 4;
match (b.read::<u16>(base), b.read::<u16>(base + 2)) {
// Empty subtable coverage is useless, so mark empty coverage subtables as invalid.
(Some(_), Some(0)) => false,
(Some(1), Some(len)) => b.check_range(arr, len as usize * 2),
(Some(2), Some(len)) => b.check_range(arr, len as usize * 6),
_ => false,
}
}
pub unsafe fn fast_coverage(b: &Bytes, coverage_offset: u32, glyph_id: u16) -> Option<u16> {
let base = coverage_offset as usize;
let fmt = b.read_unchecked::<u16>(base);
let len = b.read_unchecked::<u16>(base + 2) as usize;
let arr = base + 4;
if fmt == 1 {
let mut l = 0;
let mut h = len;
while l < h {
use core::cmp::Ordering::*;
let i = (l + h) / 2;
let g = b.read_unchecked::<u16>(arr + i * 2);
match glyph_id.cmp(&g) {
Less => h = i,
Greater => l = i + 1,
Equal => return Some(i as u16),
}
}
} else if fmt == 2 {
let mut l = 0;
let mut h = len;
while l < h {
let i = (l + h) / 2;
let rec = arr + i * 6;
let start = b.read_unchecked::<u16>(rec);
if glyph_id < start {
h = i;
} else if glyph_id > b.read_unchecked::<u16>(rec + 2) {
l = i + 1;
} else {
let base = b.read_unchecked::<u16>(rec + 4);
return Some(base + glyph_id - start);
}
}
}
None
}
pub fn coverage(b: &Bytes, coverage_offset: u32, glyph_id: u16) -> Option<u16> {
if coverage_offset == 0 {
return None;
}
let base = coverage_offset as usize;
let fmt = b.read::<u16>(base)?;
let len = b.read::<u16>(base + 2)? as usize;
let arr = base + 4;
if fmt == 1 {
if !b.check_range(arr, len * 2) {
return None;
}
let mut l = 0;
let mut h = len;
while l < h {
use core::cmp::Ordering::*;
let i = (l + h) / 2;
let g = unsafe { b.read_unchecked::<u16>(arr + i * 2) };
match glyph_id.cmp(&g) {
Less => h = i,
Greater => l = i + 1,
Equal => return Some(i as u16),
}
}
} else if fmt == 2 {
if !b.check_range(arr, len * 6) {
return None;
}
let mut l = 0;
let mut h = len;
while l < h {
let i = (l + h) / 2;
let rec = arr + i * 6;
let start = unsafe { b.read_unchecked::<u16>(rec) };
if glyph_id < start {
h = i;
} else if glyph_id > unsafe { b.read_unchecked::<u16>(rec + 2) } {
l = i + 1;
} else {
let base = unsafe { b.read_unchecked::<u16>(rec + 4) };
return Some(base + (glyph_id - start));
}
}
}
None
}
pub fn classdef(b: &Bytes, classdef_offset: u32, glyph_id: u16) -> u16 {
if classdef_offset == 0 {
return 0;
}
let base = classdef_offset as usize;
let fmt = b.read_or_default::<u16>(base);
if fmt == 1 {
let start = b.read_or_default::<u16>(base + 2);
let len = b.read_or_default::<u16>(base + 4);
let end = start + len - 1;
let arr = base + 6;
if glyph_id >= start && glyph_id <= end {
return b.read_or_default::<u16>(arr + (glyph_id - start) as usize * 2);
}
return 0;
} else if fmt == 2 {
let len = b.read_or_default::<u16>(base + 2) as usize;
let arr = base + 4;
if !b.check_range(arr, len * 6) {
return 0;
}
let mut l = 0;
let mut h = len;
while l < h {
let i = (l + h) / 2;
let rec = arr + i * 6;
let start = unsafe { b.read_unchecked::<u16>(rec) };
if glyph_id < start {
h = i;
} else if glyph_id > unsafe { b.read_unchecked::<u16>(rec + 2) } {
l = i + 1;
} else {
return unsafe { b.read_unchecked::<u16>(rec + 4) };
}
}
}
0
}

275
vendor/swash/src/internal/cmap.rs vendored Normal file
View File

@@ -0,0 +1,275 @@
//! Character to glyph mapping table.
use super::{raw_tag, Array, Bytes, RawFont, RawTag, Stream};
pub const CMAP: RawTag = raw_tag(b"cmap");
/// Finds a suitable character map subtable for the specified font.
pub fn subtable<'a>(font: impl RawFont<'a>) -> Option<(u32, u8, bool)> {
let cmap = font.table_offset(CMAP);
if cmap == 0 {
return None;
}
let mut s = Stream::with_offset(font.data(), cmap as usize)?;
s.skip(2)?;
let len = s.read_u16()? as usize;
let b = Bytes::new(s.data());
let mut best = None;
for _ in 0..len {
let platform = s.read_u16()?;
let encoding = s.read_u16()?;
let offset = s.read_u32()?;
let format = b.read_u16(offset as usize)? as u8;
if format != 4 && format != 12 {
continue;
}
let offset = cmap.checked_add(offset)?;
if is_symbol(platform, encoding) {
return Some((offset, format, true));
} else if (format == 12 && is_unicode(platform, encoding))
|| (best.is_none() && is_unicode(platform, encoding))
{
best = Some((offset, format, false));
}
}
best
}
/// Maps a codepoint to a glyph identifier.
pub fn map(data: &[u8], subtable: u32, format: u8, codepoint: u32) -> Option<u16> {
if subtable == 0 {
return None;
}
let b = Bytes::with_offset(data, subtable as usize)?;
if format == 4 {
if codepoint >= 65535 {
return None;
}
let c = codepoint as u16;
let segcount_x2 = b.read_u16(6)? as usize;
let segcount = segcount_x2 / 2;
b.ensure_range(0, 16 + segcount_x2 * 4)?;
let end_codes_offset = 14;
let start_codes_offset = end_codes_offset + segcount_x2 + 2;
let mut l = 0;
let mut h = segcount;
while l < h {
let i = (l + h) / 2;
let i2 = i * 2;
let start = unsafe { b.read_unchecked::<u16>(start_codes_offset + i2) };
if c < start {
h = i;
} else if c > unsafe { b.read_unchecked::<u16>(end_codes_offset + i2) } {
l = i + 1;
} else {
let deltas_offset = start_codes_offset + segcount_x2;
let ranges_offset = deltas_offset + segcount_x2;
let mut range_base = ranges_offset + i2;
let range = unsafe { b.read_unchecked::<u16>(range_base) as usize };
let delta = unsafe { b.read_unchecked::<i16>(deltas_offset + i2) as i32 };
if range == 0 {
return Some((codepoint as i32 + delta) as u16);
}
range_base += range;
let diff = (c - start) as usize * 2;
let id = b.read::<u16>(range_base + diff).unwrap_or(0);
return if id != 0 {
Some((id as i32 + delta) as u16)
} else {
Some(0)
};
}
}
} else if format == 12 {
let base = 16;
let len = b.read::<u32>(base - 4).unwrap_or(0) as usize;
b.ensure_range(base, len * 12)?;
let mut l = 0;
let mut h = len;
while l < h {
let i = (l + h) / 2;
let rec = base + i * 12;
let start = unsafe { b.read_unchecked::<u32>(rec) };
if codepoint < start {
h = i;
} else if codepoint > unsafe { b.read_unchecked::<u32>(rec + 4) } {
l = i + 1;
} else {
let delta = unsafe { b.read_unchecked::<u32>(rec + 8) };
return Some((codepoint - start + delta) as u16);
}
}
}
None
}
/// Enumerates all codepoint/glyph pairs in the table.
pub fn enumerate(data: &[u8], subtable: u32, mut f: impl FnMut(u32, u16)) {
if subtable == 0 {
return;
}
let b = if let Some(b) = Bytes::with_offset(data, subtable as usize) {
b
} else {
return;
};
let format = b.read_or_default::<u16>(0);
if format == 4 {
let segcount_x2 = b.read::<u16>(6).unwrap_or(0) as usize;
let segcount = segcount_x2 / 2;
if !b.check_range(0, 16 + segcount_x2 * 4) {
return;
}
let end_code_offset = 14;
let start_code_offset = end_code_offset + segcount_x2 + 2;
let deltas_offset = start_code_offset + segcount_x2;
let ranges_offset = deltas_offset + segcount_x2;
let start_codes = b
.read_array::<u16>(start_code_offset, segcount)
.unwrap_or_else(|| Array::new(&[]));
let end_codes = b
.read_array::<u16>(end_code_offset, segcount)
.unwrap_or_else(|| Array::new(&[]));
let deltas = b
.read_array::<i16>(deltas_offset, segcount)
.unwrap_or_else(|| Array::new(&[]));
for (i, ((start, end), delta)) in start_codes
.iter()
.zip(end_codes.iter())
.zip(deltas.iter())
.enumerate()
{
let mut range_base = ranges_offset + i * 2;
if let Some(range) = b.read_u16(range_base) {
if range == 0 {
for codepoint in start..=end {
let id = (codepoint as i32 + delta as i32) as u16;
if id != 0 {
f(codepoint as u32, id);
}
}
} else {
range_base += range as usize;
for codepoint in start..=end {
let diff = (codepoint - start) as usize * 2;
if let Some(mut id) = b.read::<u16>(range_base + diff) {
if id != 0 {
id = (id as i32 + delta as i32) as u16;
f(codepoint as u32, id);
}
}
}
}
}
}
} else if format == 12 {
let base = 16;
let len = b.read::<u32>(base - 4).unwrap_or(0) as usize;
if !b.check_range(base, len * 12) {
return;
}
for i in 0..len {
let rec = base + i * 12;
let (start, end, offset) = unsafe {
(
b.read_unchecked::<u32>(rec),
b.read_unchecked::<u32>(rec + 4),
b.read_unchecked::<u32>(rec + 8),
)
};
for codepoint in start..=end {
let id = (offset + codepoint - start) as u16;
if id != 0 {
f(codepoint, id);
}
}
}
}
}
fn is_unicode(platform: u16, encoding: u16) -> bool {
matches!((platform, encoding), (0, _) | (3, 1) | (3, 10))
}
fn is_symbol(platform: u16, encoding: u16) -> bool {
platform == 3 && encoding == 0
}
/// Result of the mapping a codepoint with a variation selector.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum MapVariant {
/// Use the default glyph mapping.
UseDefault,
/// Use the specified variant.
Variant(u16),
}
/// Maps a codepoint with variation selector to a glyph identifier using the
/// format 14 subtable at the specified offset in data.
///
/// <https://docs.microsoft.com/en-us/typography/opentype/spec/cmap#format-14-unicode-variation-sequences>
pub fn map_variant(
data: &[u8],
offset: u32,
codepoint: u32,
variation_selector: u32,
) -> Option<MapVariant> {
use core::cmp::Ordering;
let b = Bytes::with_offset(data, offset as usize)?;
let len = b.read_u32(6)? as usize;
let base = 10;
let mut lo = 0;
let mut hi = len;
let mut default_uvs_offset = 0;
let mut non_default_uvs_offset = 0;
while lo < hi {
let i = (lo + hi) / 2;
let rec = base + i * 11;
let vs = b.read_u24(rec)?;
match variation_selector.cmp(&vs) {
Ordering::Less => hi = i,
Ordering::Greater => lo = i + 1,
Ordering::Equal => {
default_uvs_offset = b.read_u32(rec + 3)? as usize;
non_default_uvs_offset = b.read_u32(rec + 7)? as usize;
break;
}
}
}
if default_uvs_offset != 0 {
let base = default_uvs_offset;
let len = b.read_u32(base)? as usize;
let mut lo = 0;
let mut hi = len;
while lo < hi {
let i = (lo + hi) / 2;
let rec = base + 4 + i * 4;
let start = b.read_u24(rec)?;
if codepoint < start {
hi = i;
} else if codepoint > (start + b.read_u8(rec + 3)? as u32) {
lo = i + 1;
} else {
// Fallback to standard mapping.
return Some(MapVariant::UseDefault);
}
}
}
if non_default_uvs_offset != 0 {
let base = non_default_uvs_offset;
let len = b.read_u32(base)? as usize;
let mut lo = 0;
let mut hi = len;
while lo < hi {
let i = (lo + hi) / 2;
let rec = base + 4 + i * 5;
let value = b.read_u24(rec)?;
match codepoint.cmp(&value) {
Ordering::Less => hi = i,
Ordering::Greater => lo = i + 1,
Ordering::Equal => return Some(MapVariant::Variant(b.read_u16(rec + 3)?)),
}
}
}
None
}

207
vendor/swash/src/internal/fixed.rs vendored Normal file
View File

@@ -0,0 +1,207 @@
//! Minimal fixed point math types and functions used internally.
use super::parse::FromBeData;
use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
/// Fixed point value in 16.16 format.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug)]
pub struct Fixed(pub i32);
impl Fixed {
pub const MIN: Self = Self(0x80000000u32 as i32);
pub const MAX: Self = Self(0x7FFFFFFF);
pub const EPSILON: Self = Self(1);
pub const ZERO: Self = Self(0);
pub const ONE: Self = Self(0x10000);
pub const fn from_i32(x: i32) -> Self {
Self(x << 16)
}
pub fn from_f32(x: f32) -> Self {
Self((x * 65536. + 0.5) as i32)
}
pub fn from_f2dot14(x: i16) -> Self {
Self(x as i32 * 4)
}
pub fn round(self) -> Self {
Self(((self.0 + 0x8000) as u32 & 0xFFFF0000) as i32)
}
pub fn floor(self) -> Self {
Self((self.0 as u32 & 0xFFFF0000) as i32)
}
pub fn abs(self) -> Self {
Self(self.0.abs())
}
pub fn min(self, rhs: Self) -> Self {
Self(self.0.min(rhs.0))
}
pub fn max(self, rhs: Self) -> Self {
Self(self.0.max(rhs.0))
}
pub fn fract(self) -> Self {
Self(self.0 - self.floor().0)
}
pub fn to_i32(self) -> i32 {
(self.0 + 0x8000) >> 16
}
pub fn to_f32(self) -> f32 {
self.0 as f32 / 65536.
}
pub fn to_f2dot14(self) -> i16 {
((self.0 + 2) >> 2) as i16
}
}
impl Add for Fixed {
type Output = Self;
#[inline(always)]
fn add(self, rhs: Self) -> Self {
Self((self.0 as u32).wrapping_add(rhs.0 as u32) as i32)
}
}
impl AddAssign for Fixed {
fn add_assign(&mut self, rhs: Self) {
self.0 = self.0.wrapping_add(rhs.0);
}
}
impl Sub for Fixed {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self {
Self((self.0 as u32).wrapping_sub(rhs.0 as u32) as i32)
}
}
impl Mul for Fixed {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: Self) -> Self {
Self(mul(self.0, rhs.0))
}
}
impl Div for Fixed {
type Output = Self;
#[inline(always)]
fn div(self, rhs: Self) -> Self {
Self(div(self.0, rhs.0))
}
}
impl Div<i32> for Fixed {
type Output = Self;
#[inline(always)]
fn div(self, rhs: i32) -> Self {
Self(self.0 / rhs)
}
}
impl Neg for Fixed {
type Output = Self;
fn neg(self) -> Self {
Self(-self.0)
}
}
impl From<f32> for Fixed {
fn from(f: f32) -> Self {
Self::from_f32(f)
}
}
impl From<Fixed> for f32 {
fn from(f: Fixed) -> Self {
f.to_f32()
}
}
impl FromBeData for Fixed {
unsafe fn from_be_data_unchecked(data: &[u8], offset: usize) -> Self {
Self(i32::from_be_data_unchecked(data, offset))
}
}
/// Fixed point floor.
pub fn floor(x: i32) -> i32 {
x & !63
}
/// Fixed point ceil.
pub fn ceil(x: i32) -> i32 {
floor(x + 63)
}
/// Fixed point round.
pub fn round(x: i32) -> i32 {
floor(x + 32)
}
/// Fixed point multiply.
#[inline(always)]
pub fn mul(a: i32, b: i32) -> i32 {
let ab = a as i64 * b as i64;
((ab + 0x8000 - if ab < 0 { 1 } else { 0 }) >> 16) as i32
}
/// Fixed point divide.
pub fn div(mut a: i32, mut b: i32) -> i32 {
let mut s = 1;
if a < 0 {
a = -a;
s = -1;
}
if b < 0 {
b = -b;
s = -s;
}
let q = if b == 0 {
0x7FFFFFFF
} else {
((((a as u64) << 16) + ((b as u64) >> 1)) / (b as u64)) as u32
};
if s < 0 {
-(q as i32)
} else {
q as i32
}
}
/// Fixed point multiply/divide.
pub fn muldiv(mut a: i32, mut b: i32, mut c: i32) -> i32 {
let mut s = 1;
if a < 0 {
a = -a;
s = -1;
}
if b < 0 {
b = -b;
s = -s;
}
if c < 0 {
c = -c;
s = -s;
}
let d = if c > 0 {
((a as i64) * (b as i64) + ((c as i64) >> 1)) / c as i64
} else {
0x7FFFFFFF
};
if s < 0 {
-(d as i32)
} else {
d as i32
}
}

52
vendor/swash/src/internal/glyf.rs vendored Normal file
View File

@@ -0,0 +1,52 @@
//! Glyph data table.
use super::{raw_tag, Bytes, RawTag, Stream};
pub const GLYF: RawTag = raw_tag(b"glyf");
pub const LOCA: RawTag = raw_tag(b"loca");
pub const CVT_: RawTag = raw_tag(b"cvt ");
pub const FPGM: RawTag = raw_tag(b"fpgm");
pub const PREP: RawTag = raw_tag(b"prep");
pub const CVAR: RawTag = raw_tag(b"cvar");
pub const GVAR: RawTag = raw_tag(b"gvar");
/// Returns the data for the specified glyph.
pub fn get<'a>(
data: &'a [u8],
loca_fmt: u8,
loca: u32,
glyf: u32,
glyph_id: u16,
) -> Option<&'a [u8]> {
let range = {
let b = Bytes::with_offset(data, loca as usize)?;
let (start, end) = if loca_fmt == 0 {
let offset = glyph_id as usize * 2;
let start = b.read::<u16>(offset)? as usize * 2;
let end = b.read::<u16>(offset + 2)? as usize * 2;
(start, end)
} else if loca_fmt == 1 {
let offset = glyph_id as usize * 4;
let start = b.read::<u32>(offset)? as usize;
let end = b.read::<u32>(offset + 4)? as usize;
(start, end)
} else {
return None;
};
if end < start {
return None;
}
start..end
};
let glyf = Bytes::with_offset(data, glyf as usize)?;
glyf.data().get(range)
}
/// Returns the y-max value of the specified glyph from the bounding box in the
/// `glyf` table.
pub fn ymax(data: &[u8], loca_fmt: u8, loca: u32, glyf: u32, glyph_id: u16) -> Option<i16> {
let glyph_data = get(data, loca_fmt, loca, glyf, glyph_id)?;
let mut s = Stream::new(glyph_data);
s.skip(8)?;
s.read()
}

880
vendor/swash/src/internal/head.rs vendored Normal file
View File

@@ -0,0 +1,880 @@
//! Font header tables.
use super::{raw_tag, Bytes, RawFont, RawTag};
pub const HEAD: RawTag = raw_tag(b"head");
pub const OS_2: RawTag = raw_tag(b"OS/2");
pub const POST: RawTag = raw_tag(b"post");
pub const MAXP: RawTag = raw_tag(b"maxp");
pub const HHEA: RawTag = raw_tag(b"hhea");
pub const VHEA: RawTag = raw_tag(b"vhea");
/// Font header table.
#[derive(Copy, Clone)]
pub struct Head<'a>(Bytes<'a>);
impl<'a> Head<'a> {
/// The expected value of the 'magic' field in the header table.
pub const MAGIC: u32 = 0x5F0F3CF5;
/// Creates a font header table wrapping the specified data.
pub fn new(data: &'a [u8]) -> Self {
Self(Bytes::new(data))
}
/// Creates a font header table from the specified font.
/// Returns `None` if the font does not contain a `head` table.
pub fn from_font(font: impl RawFont<'a>) -> Option<Self> {
Some(Self::new(font.table_data(HEAD)?))
}
/// Returns the underlying bytes of the table.
pub fn data(&self) -> &'a [u8] {
self.0.data()
}
/// Returns the major version of the header table. Set to 1.
pub fn major_version(&self) -> u16 {
self.0.read(0).unwrap_or(0)
}
/// Returns the minor version of the header table. Set to 0.
pub fn minor_version(&self) -> u16 {
self.0.read(2).unwrap_or(0)
}
/// Returns a revision value. Set by font manufacturer.
pub fn revision(&self) -> u32 {
self.0.read(4).unwrap_or(0)
}
/// Returns a checksum adjustment value.
pub fn checksum_adjustment(&self) -> u32 {
self.0.read(8).unwrap_or(0)
}
/// Returns a magic number for validation. Set to 0x5F0F3CF5.
pub fn magic(&self) -> u32 {
self.0.read(12).unwrap_or(0)
}
/// Returns a set of header bit flags.
/// - 0: Baseline at y = 0
/// - 1: Left sidebearing at x = 0
/// - 2: Instructions may depend on point size
/// - 3: Force ppem to integer values
/// - 4: Instructions may alter advance width
/// - 5-10: Unused
/// - 11: Font data is lossless
/// - 12: Font has been converted
/// - 13: Optimized for ClearType
/// - 14: Last resort font
pub fn flags(&self) -> u16 {
self.0.read(16).unwrap_or(0)
}
/// Returns the design units per em. Valid values are 16..=16384.
pub fn units_per_em(&self) -> u16 {
self.0.read(18).unwrap_or(0)
}
/// Number of seconds since 12:00 midnight that started January 1st 1904 in GMT/UTC time zone.
pub fn created(&self) -> u64 {
self.0.read(20).unwrap_or(0)
}
/// Number of seconds since 12:00 midnight that started January 1st 1904 in GMT/UTC time zone.
pub fn modified(&self) -> u64 {
self.0.read(28).unwrap_or(0)
}
/// Returns the union of all glyph bounding boxes.
pub fn bounds(&self) -> [(i16, i16); 2] {
[
(
self.0.read_or_default::<i16>(36),
self.0.read_or_default::<i16>(38),
),
(
self.0.read_or_default::<i16>(40),
self.0.read_or_default::<i16>(42),
),
]
}
/// Returns the mac style bit flags.
/// - 0: Bold
/// - 1: Italic
/// - 2: Underline
/// - 3: Outline
/// - 4: Shadow
/// - 5: Condensed
/// - 6: Extended
/// - 7-15: Reserved
pub fn mac_style(&self) -> u16 {
self.0.read(44).unwrap_or(0)
}
/// Returns the smallest readable size in pixels.
pub fn lowest_recommended_ppem(&self) -> u16 {
self.0.read(46).unwrap_or(0)
}
/// Deprecated. Returns a hint about the directionality of the glyphs.
/// Set to 2.
pub fn direction_hint(&self) -> u16 {
self.0.read(48).unwrap_or(0)
}
/// Returns the format the the offset array in the 'loca' table.
/// - 0: 16-bit offsets (divided by 2)
/// - 1: 32-bit offsets
pub fn index_to_location_format(&self) -> u16 {
self.0.read(50).unwrap_or(0)
}
/// Unused. Set to 0.
pub fn glyph_data_format(&self) -> i16 {
self.0.read(52).unwrap_or(0)
}
}
/// OS/2 and Windows metrics table.
#[derive(Copy, Clone)]
pub struct Os2<'a>(Bytes<'a>);
impl<'a> Os2<'a> {
/// Creates an OS/2 table wrapping the specified data.
pub fn new(data: &'a [u8]) -> Self {
Self(Bytes::new(data))
}
/// Creates an OS/2 table from the specified font.
/// Returns `None` if the font does not contain an `OS/2` table.
pub fn from_font(font: impl RawFont<'a>) -> Option<Self> {
Some(Self::new(font.table_data(OS_2)?))
}
/// Returns the underlying bytes of the table.
pub fn data(&self) -> &'a [u8] {
self.0.data()
}
/// Returns the version number for the OS/2 table: 0x0000 to 0x0005.
pub fn version(&self) -> u16 {
self.0.read(0).unwrap_or(0)
}
/// Returns the average advance width of all non-zero width glyphs in the
/// font.
pub fn average_char_width(&self) -> i16 {
self.0.read(2).unwrap_or(0)
}
/// Returns the visual weight class on a scale from 1 to 1000.
/// Common values:
/// - 100: Thin
/// - 200: Extra-light (Ultra-light)
/// - 300: Light
/// - 400: Normal (Regular)
/// - 500: Medium
/// - 600: Semi-bold
/// - 700: Bold
/// - 800: Extra-bold (Ultra-bold)
/// - 900: Black (Heavy)
pub fn weight_class(&self) -> i16 {
self.0.read(4).unwrap_or(0)
}
/// Returns the visual width class-- a relative change from the normal aspect
/// ratio.
/// - 1: Ultra-condensed
/// - 2: Extra-condensed
/// - 3: Condensed
/// - 4: Semi-condensed
/// - 5: Medium (Normal)
/// - 6: Semi-expanded
/// - 7: Expanded
/// - 8: Extra-expanded
/// - 9: Ultra-expanded
pub fn width_class(&self) -> i16 {
self.0.read(6).unwrap_or(0)
}
/// Returns the font type bit flags.
/// Bits:
/// - 0-3: Usage permissions
/// - 4-7: Reserved (set to 0)
/// - 8: No subsetting
/// - 9: Bitmap embedding only
/// - 10-15: Reserved (set to 0)
pub fn type_flags(&self) -> i16 {
self.0.read(8).unwrap_or(0)
}
/// Returns a rectangle describing suggested subscript positioning.
pub fn subscript(&self) -> [(i32, i32); 2] {
[
(
self.0.read::<i16>(14).unwrap_or(0) as i32,
self.0.read::<i16>(16).unwrap_or(0) as i32,
),
(
self.0.read::<i16>(10).unwrap_or(0) as i32,
self.0.read::<i16>(12).unwrap_or(0) as i32,
),
]
}
/// Returns a rectangle describing suggested superscript positioning.
pub fn superscript(&self) -> [(i32, i32); 2] {
[
(
self.0.read::<i16>(22).unwrap_or(0) as i32,
self.0.read::<i16>(24).unwrap_or(0) as i32,
),
(
self.0.read::<i16>(18).unwrap_or(0) as i32,
self.0.read::<i16>(20).unwrap_or(0) as i32,
),
]
}
/// Returns the suggested position of the top of the strikeout stroke from
/// the baseline.
pub fn strikeout_position(&self) -> i16 {
self.0.read(28).unwrap_or(0)
}
/// Returns the suggested thickness for the strikeout stroke.
pub fn strikeout_size(&self) -> i16 {
self.0.read(26).unwrap_or(0)
}
/// Returns the font family class and subclass. For values:
/// <https://docs.microsoft.com/en-us/typography/opentype/spec/ibmfc>
pub fn family_class(&self) -> i16 {
self.0.read(30).unwrap_or(0)
}
/// Returns a 10-byte PANOSE classification number.
/// <https://monotype.github.io/panose/>
pub fn panose(&self) -> &'a [u8] {
self.0
.read_bytes(32, 10)
.unwrap_or(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
}
/// Returns a 128-bit value describing the Unicode blocks that are
/// supported by the font.
pub fn unicode_range(&self) -> (u32, u32, u32, u32) {
(
self.0.read::<u32>(42).unwrap_or(0),
self.0.read::<u32>(46).unwrap_or(0),
self.0.read::<u32>(50).unwrap_or(0),
self.0.read::<u32>(54).unwrap_or(0),
)
}
/// Returns a four character font vendor identifier.
pub fn vendor_id(&self) -> &'a str {
core::str::from_utf8(self.0.read_bytes(58, 4).unwrap_or(b"none")).unwrap_or("none")
}
/// Returns the font selection bit flags.
/// Bits:
/// - 0: Italic
/// - 1: Underscore
/// - 2: Negative
/// - 3: Outlined
/// - 4: Strikeout
/// - 5: Bold
/// - 6: Regular
/// - 7: Use typographic metrics
/// - 8: WWS (Weight/Width/Slope names)
/// - 9: Oblique
/// - 10-15: Reserved (set to 0)
pub fn selection_flags(&self) -> Flags {
Flags(self.0.read(62).unwrap_or(0))
}
/// Returns the minimum and maximum Unicode codepoints supported by the
/// font. Note that this does not cover supplementary characters.
pub fn char_range(&self) -> (u16, u16) {
(
self.0.read::<u16>(64).unwrap_or(0),
self.0.read::<u16>(66).unwrap_or(0),
)
}
/// Returns the typographic ascender.
pub fn typographic_ascender(&self) -> i16 {
self.0.read(68).unwrap_or(0)
}
/// Returns the typographic descender.
pub fn typographic_descender(&self) -> i16 {
self.0.read(70).unwrap_or(0)
}
/// Returns the typographic line gap.
pub fn typographic_line_gap(&self) -> i16 {
self.0.read(72).unwrap_or(0)
}
/// Returns a Windows specific value that defines the upper extent of
/// the clipping region.
pub fn win_ascent(&self) -> u16 {
self.0.read(74).unwrap_or(0)
}
/// Returns a Windows specific value that defines the lower extent of
/// the clipping region.
pub fn win_descent(&self) -> u16 {
self.0.read(76).unwrap_or(0)
}
/// Returns Windows specific code page ranges supported by the font.
/// (table version >= 1)
pub fn code_page_range(&self) -> (u32, u32) {
if self.version() < 1 {
return (0, 0);
}
(
self.0.read::<u32>(78).unwrap_or(0),
self.0.read::<u32>(82).unwrap_or(0),
)
}
/// Returns the approximate distance above the baseline for non-descending
/// lowercase letters (table version >= 2)
pub fn x_height(&self) -> i16 {
if self.version() < 2 {
return 0;
}
self.0.read(86).unwrap_or(0)
}
/// Returns the approximate distance above the baseline for uppercase letters.
/// (table version >= 2)
pub fn cap_height(&self) -> i16 {
if self.version() < 2 {
return 0;
}
self.0.read(88).unwrap_or(0)
}
/// Returns a Unicode codepoint for the default character to use if
/// a requested character is not supported by the font.
/// (table version >= 2)
pub fn default_char(&self) -> u16 {
if self.version() < 2 {
return 0;
}
self.0.read(90).unwrap_or(0)
}
/// Returns a Unicode codepoint for the default character used to separate
/// words and justify text. (table version >= 2)
pub fn break_char(&self) -> u16 {
if self.version() < 2 {
return 0;
}
self.0.read(92).unwrap_or(0)
}
/// Returns the maximum length of a target glyph context for any feature in
/// the font. (table version >= 2)
pub fn max_context(&self) -> u16 {
if self.version() < 2 {
return 0;
}
self.0.read(94).unwrap_or(0)
}
/// Returns the lower value of the size range for which this font has been
/// designed. The units are TWIPS (1/20 points). (table version >= 5)
pub fn lower_optical_point_size(&self) -> u16 {
if self.version() < 5 {
return 0;
}
self.0.read(96).unwrap_or(0)
}
/// Returns the upper value of the size range for which this font has been
/// designed. The units are TWIPS (1/20 points). (table version >= 5)
pub fn upper_optical_point_size(&self) -> u16 {
if self.version() < 5 {
return 0;
}
self.0.read(98).unwrap_or(0)
}
}
/// OS/2 selection flags.
#[derive(Copy, Clone)]
pub struct Flags(pub u16);
impl Flags {
/// Font contains italic glyphs.
pub fn italic(self) -> bool {
self.bit(0)
}
/// Glyphs are underscored.
pub fn underscore(self) -> bool {
self.bit(1)
}
/// Glyphs have their foreground and background reversed.
pub fn negative(self) -> bool {
self.bit(2)
}
/// Hollow glyphs.
pub fn outlined(self) -> bool {
self.bit(3)
}
/// Glyphs are overstruck.
pub fn strikeout(self) -> bool {
self.bit(4)
}
/// Glyphs are emboldened.
pub fn bold(self) -> bool {
self.bit(5)
}
/// Glyphs are in the standard weight/style for the font.
pub fn regular(self) -> bool {
self.bit(6)
}
/// Typographic metrics are recommended for default line spacing.
pub fn use_typographic_metrics(self) -> bool {
self.bit(7)
}
/// Font has name table strings consistent with WWS family naming.
pub fn wws_names(self) -> bool {
self.bit(8)
}
/// Font contains oblique glyphs.
pub fn oblique(self) -> bool {
self.bit(9)
}
fn bit(self, bit: u16) -> bool {
self.0 & 1 << bit != 0
}
}
/// PostScript table.
#[derive(Copy, Clone)]
pub struct Post<'a>(Bytes<'a>);
impl<'a> Post<'a> {
/// Creates a PostScript table wrapping the specified data.
pub fn new(data: &'a [u8]) -> Self {
Self(Bytes::new(data))
}
/// Creates a PostScript table from the specified font.
/// Returns `None` if the font does not contain a `post` table.
pub fn from_font(font: impl RawFont<'a>) -> Option<Self> {
Some(Self::new(font.table_data(POST)?))
}
/// Returns the underlying bytes of the table.
pub fn data(&self) -> &'a [u8] {
self.0.data()
}
/// Returns the version of the PostScript table.
pub fn version(&self) -> u32 {
self.0.read(0).unwrap_or(0)
}
/// Returns the italic angle in counter-clockwise degrees from the vertical.
pub fn italic_angle(&self) -> u32 {
self.0.read(4).unwrap_or(0)
}
/// Returns the suggested position of the top of the underline stroke from
/// the baseline.
pub fn underline_position(&self) -> i16 {
self.0.read(8).unwrap_or(0)
}
/// Returns the suggested thickness for the underline stroke.
pub fn underline_size(&self) -> i16 {
self.0.read(10).unwrap_or(0)
}
/// Returns true if the font is not proportionally spaced (i.e. monospaced).
pub fn is_fixed_pitch(&self) -> bool {
self.0.read::<u32>(12).unwrap_or(0) != 0
}
/// Returns true if the table can provide glyph names. Only versions 1.0
/// (0x00010000) and 2.0 (0x00020000).
pub fn has_names(&self) -> bool {
let v = self.version();
v == 0x10000 || v == 0x20000
}
/// Returns the name of the specified glyph id if available.
pub fn name(&self, glyph_id: u16) -> Option<&'a str> {
if !self.has_names() {
return None;
}
let v = self.version();
if v == 0x10000 {
if glyph_id >= 258 {
return None;
}
return Some(DEFAULT_GLYPH_NAMES[glyph_id as usize]);
} else if v == 0x20000 {
let b = &self.0;
let count = b.read::<u16>(32)?;
if glyph_id >= count {
return None;
}
let mut index = b.read::<u16>(34 + glyph_id as usize * 2)? as usize;
if index < 258 {
return Some(DEFAULT_GLYPH_NAMES[index]);
}
index -= 258;
let mut base = 34 + count as usize * 2;
for _ in 0..index {
let len = b.read::<u8>(base)? as usize;
base += len + 1;
}
let len = b.read::<u8>(base)? as usize;
base += 1;
let bytes = b.read_bytes(base, len)?;
return core::str::from_utf8(bytes).ok();
}
None
}
}
/// Maximum profile table.
#[derive(Copy, Clone)]
pub struct Maxp<'a>(Bytes<'a>);
impl<'a> Maxp<'a> {
/// Creates a maximum profile table wrapping the specified data.
pub fn new(data: &'a [u8]) -> Self {
Self(Bytes::new(data))
}
/// Creates a maximum profile table from the specified font.
/// Returns `None` if the font does not contain a `maxp` table.
pub fn from_font(font: impl RawFont<'a>) -> Option<Self> {
Some(Self::new(font.table_data(MAXP)?))
}
/// Returns the underlying bytes of the table.
pub fn data(&self) -> &'a [u8] {
self.0.data()
}
/// Returns the version of the table.
/// - 0x00005000: Version 0.5 - only `num_glyphs` will return a meaningful value.
/// - 0x00010000: Version 1.0
pub fn version(&self) -> u32 {
self.0.read(0).unwrap_or(0)
}
/// Returns the number of glyphs in the font.
pub fn glyph_count(&self) -> u16 {
self.0.read(4).unwrap_or(0)
}
/// Returns true if the 'max_' methods will return meaningful values--
/// specifically, if the table version is 1.0 (0x00010000).
pub fn is_truetype(&self) -> bool {
self.version() == 0x00010000
}
/// Returns the maximum points in a simple glyph.
pub fn max_points(&self) -> u16 {
self.0.read(6).unwrap_or(0)
}
/// Returns the maximum contours in a simple glyph.
pub fn max_contours(&self) -> u16 {
self.0.read(8).unwrap_or(0)
}
/// Returns the maximum points in a composite glyph.
pub fn max_composite_points(&self) -> u16 {
self.0.read(10).unwrap_or(0)
}
/// Returns the maximum contours in a composite glyph.
pub fn max_composite_contours(&self) -> u16 {
self.0.read(12).unwrap_or(0)
}
/// Returns 2 if instructions require a 'twilight zone' or 1 otherwise.
pub fn max_zones(&self) -> u16 {
self.0.read(14).unwrap_or(0)
}
/// Returns the maximum twilight points used in zone 0.
pub fn max_twilight_points(&self) -> u16 {
self.0.read(16).unwrap_or(0)
}
/// Returns the maximum storage area locations.
pub fn max_storage(&self) -> u16 {
self.0.read(18).unwrap_or(0)
}
/// Returns the maximum function definitions.
pub fn max_function_definitions(&self) -> u16 {
self.0.read(20).unwrap_or(0)
}
/// Returns the maximum instruction definitions.
pub fn max_instruction_definitions(&self) -> u16 {
self.0.read(22).unwrap_or(0)
}
/// Returns the maximum stack depth across all programs in the font.
pub fn max_stack_depth(&self) -> u16 {
self.0.read(24).unwrap_or(0)
}
/// Returns the maximum size of glyph instructions.
pub fn max_instructions_size(&self) -> u16 {
self.0.read(26).unwrap_or(0)
}
/// Returns the maximum number of components for a single composite glyph.
pub fn max_component_elements(&self) -> u16 {
self.0.read(28).unwrap_or(0)
}
/// Returns the maximum nesting level for any composite glyph.
pub fn max_component_depth(&self) -> u16 {
self.0.read(30).unwrap_or(0)
}
}
/// Horizontal header table.
#[derive(Copy, Clone)]
pub struct Hhea<'a>(Bytes<'a>);
impl<'a> Hhea<'a> {
/// Creates a horizontal header table wrapping the specified data.
pub fn new(data: &'a [u8]) -> Self {
Self(Bytes::new(data))
}
/// Creates a horizontal header table from the specified font.
/// Returns `None` if the font does not contain an `hhea` table.
pub fn from_font(font: impl RawFont<'a>) -> Option<Self> {
Some(Self::new(font.table_data(HHEA)?))
}
/// Returns the underlying bytes of the table.
pub fn data(&self) -> &'a [u8] {
self.0.data()
}
/// Returns the major version of the horizontal header table. Set to 1.
pub fn major_version(&self) -> u16 {
self.0.read(0).unwrap_or(0)
}
/// Returns the minor version of the horizontal header table. Set to 0.
pub fn minor_version(&self) -> u16 {
self.0.read(2).unwrap_or(0)
}
/// Returns the typographic ascender.
pub fn ascender(&self) -> i16 {
self.0.read(4).unwrap_or(0)
}
/// Returns the typographic descender.
pub fn descender(&self) -> i16 {
self.0.read(6).unwrap_or(0)
}
/// Returns the typographic line gap.
pub fn line_gap(&self) -> i16 {
self.0.read(8).unwrap_or(0)
}
/// Returns the maximum advance width.
pub fn max_advance(&self) -> u16 {
self.0.read(10).unwrap_or(0)
}
/// Returns the minimum left sidebearing.
pub fn min_lsb(&self) -> i16 {
self.0.read(12).unwrap_or(0)
}
/// Returns the minimum right sidebearing. (min(advance - lsb - (xmax - xmin)))
pub fn min_rsb(&self) -> i16 {
self.0.read(14).unwrap_or(0)
}
/// Returns the maximum horizontal extent. (max(lsb + (xmax - xmin)))
pub fn max_extent(&self) -> i16 {
self.0.read(16).unwrap_or(0)
}
/// Returns the slope of the cursor in the form (rise, run).
pub fn caret_slope(&self) -> (i16, i16) {
(self.0.read(18).unwrap_or(0), self.0.read(20).unwrap_or(0))
}
/// Returns the amount by which a slanted highlight on a glyph should be
/// shifted.
pub fn caret_offset(&self) -> i16 {
self.0.read(22).unwrap_or(0)
}
/// Unused in current format. Set to 0.
pub fn metric_data_format(&self) -> i16 {
self.0.read(32).unwrap_or(0)
}
/// Returns the number of "long" metric entries in the horizontal metrics
/// table.
pub fn num_long_metrics(&self) -> u16 {
self.0.read(34).unwrap_or(0)
}
}
/// Vertical header table.
#[derive(Copy, Clone)]
pub struct Vhea<'a>(Bytes<'a>);
impl<'a> Vhea<'a> {
/// Creates a vertical header table wrapping the specified data.
pub fn new(data: &'a [u8]) -> Self {
Self(Bytes::new(data))
}
/// Creates a vertical header table from the specified font.
/// Returns `None` if the font does not contain a `vhea` table.
pub fn from_font(font: impl RawFont<'a>) -> Option<Self> {
Some(Self::new(font.table_data(VHEA)?))
}
/// Returns the underlying bytes of the table.
pub fn data(&self) -> &'a [u8] {
self.0.data()
}
/// Returns the major version of the vertical header table. Set to 1.
pub fn major_version(&self) -> u16 {
self.0.read(0).unwrap_or(0)
}
/// Returns the minor version of the vertical header table. Set to 0.
pub fn minor_version(&self) -> u16 {
self.0.read(2).unwrap_or(0)
}
/// Returns the distance in design units from the centerline to the
/// previous line's descent.
pub fn ascender(&self) -> i16 {
self.0.read(4).unwrap_or(0)
}
/// Returns the distance in design units from the centerline to the next
/// line's ascent.
pub fn descender(&self) -> i16 {
self.0.read(6).unwrap_or(0)
}
/// Recommended additional spacing between columns of vertical text.
pub fn line_gap(&self) -> i16 {
self.0.read(8).unwrap_or(0)
}
/// Returns the maximum advance height.
pub fn max_advance(&self) -> u16 {
self.0.read(10).unwrap_or(0)
}
/// Returns the minimum top sidebearing.
pub fn min_tsb(&self) -> i16 {
self.0.read(12).unwrap_or(0)
}
/// Returns the minimum bottom sidebearing.
pub fn min_bsb(&self) -> i16 {
self.0.read(14).unwrap_or(0)
}
/// Returns the maximum vertical extent. (max(tsb + (ymax - ymin)))
pub fn max_extent(&self) -> i16 {
self.0.read(16).unwrap_or(0)
}
/// Returns the slope of the cursor in the form (rise, run).
pub fn caret_slope(&self) -> (i16, i16) {
(self.0.read(18).unwrap_or(0), self.0.read(20).unwrap_or(0))
}
/// Returns the amount by which a slanted highlight on a glyph should be
/// shifted.
pub fn caret_offset(&self) -> i16 {
self.0.read(22).unwrap_or(0)
}
/// Unused in current format. Set to 0.
pub fn metric_data_format(&self) -> i16 {
self.0.read(32).unwrap_or(0)
}
/// Returns the number of "long" metric entries in the vertical metrics
/// table.
pub fn num_long_metrics(&self) -> u16 {
self.0.read(34).unwrap_or(0)
}
}
#[rustfmt::skip]
const DEFAULT_GLYPH_NAMES: [&'static str; 258] = [
".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl", "numbersign", "dollar",
"percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma",
"hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
"eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B",
"C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
"V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
"underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright",
"asciitilde", "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis",
"aacute", "agrave", "acircumflex", "adieresis", "atilde", "aring", "ccedilla", "eacute",
"egrave", "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis", "ntilde",
"oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", "ucircumflex",
"udieresis", "dagger", "degree", "cent", "sterling", "section", "bullet", "paragraph",
"germandbls", "registered", "copyright", "trademark", "acute", "dieresis", "notequal", "AE",
"Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff",
"summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", "ae",
"oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin", "approxequal",
"Delta", "guillemotleft", "guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde",
"Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", "quoteleft",
"quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction", "currency",
"guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase",
"quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave",
"Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", "apple", "Ograve",
"Uacute", "Ucircumflex", "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve",
"dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash",
"Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth", "Yacute", "yacute", "Thorn",
"thorn", "minus", "multiply", "onesuperior", "twosuperior", "threesuperior", "onehalf",
"onequarter", "threequarters", "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla",
"scedilla", "Cacute", "cacute", "Ccaron", "ccaron", "dcroat",
];

181
vendor/swash/src/internal/mod.rs vendored Normal file
View File

@@ -0,0 +1,181 @@
//! Low level OpenType parsing and internal data types.
#![allow(dead_code)]
pub mod fixed;
pub mod aat;
pub mod at;
pub mod cmap;
pub mod glyf;
pub mod head;
pub mod var;
pub mod vorg;
pub mod xmtx;
mod parse;
pub use parse::*;
use head::*;
pub type RawTag = u32;
/// Returns a tag value for the specified four bytes.
pub const fn raw_tag(bytes: &[u8; 4]) -> RawTag {
(bytes[0] as u32) << 24 | (bytes[1] as u32) << 16 | (bytes[2] as u32) << 8 | bytes[3] as u32
}
/// Functions for checking the validity of a font file and extracting
/// fonts from collections.
pub mod raw_data {
use super::{raw_tag, Bytes, RawTag};
const OTTO: RawTag = raw_tag(b"OTTO");
const TTCF: RawTag = raw_tag(b"ttcf");
const FONT: RawTag = 0x10000;
const TRUE: RawTag = raw_tag(b"true");
/// Returns true if the data represents a font collection.
pub fn is_collection(data: &[u8]) -> bool {
Bytes::new(data).read_u32(0) == Some(TTCF)
}
/// Returns true if the data represents a font at the specified offset.
pub fn is_font(data: &[u8], offset: u32) -> bool {
let tag = Bytes::new(data).read_u32(offset as usize).unwrap_or(0);
tag == FONT || tag == OTTO || tag == TRUE
}
/// Returns the number of fonts contained in the specified data.
pub fn count(data: &[u8]) -> u32 {
if is_collection(data) {
Bytes::new(data).read_u32(8).unwrap_or(0)
} else if is_font(data, 0) {
1
} else {
0
}
}
/// Returns the byte offset for the font at the specified index in the data.
pub fn offset(data: &[u8], index: u32) -> Option<u32> {
if index >= count(data) {
return None;
}
if is_font(data, 0) {
Some(0)
} else {
Bytes::new(data).read_u32(12 + index as usize * 4)
}
}
}
/// Trait for types that can supply font tables.
pub trait RawFont<'a>: Sized {
/// Returns the font data.
fn data(&self) -> &'a [u8];
/// Returns the offset to the table directory.
fn offset(&self) -> u32;
#[cfg(feature = "std")]
fn dump_tables(&self) -> Option<()> {
let base = self.offset() as usize;
let b = Bytes::new(self.data());
let len = b.read_u16(base.checked_add(4)?)? as usize;
let record_base = base.checked_add(12)?;
let reclen = 16usize;
for i in 0..len {
let recbase = reclen.checked_mul(i)?.checked_add(record_base)?;
let mut s = b.stream_at(recbase)?;
let table_tag = s.read_u32()?;
let tb = table_tag.to_be_bytes();
println!("{}", core::str::from_utf8(&tb).unwrap_or("??"));
}
Some(())
}
/// Returns the range for the table with the specified tag.
fn table_range(&self, tag: RawTag) -> Option<(u32, u32)> {
let base = self.offset() as usize;
let b = Bytes::new(self.data());
let len = b.read_u16(base.checked_add(4)?)? as usize;
let record_base = base.checked_add(12)?;
let reclen = 16usize;
let mut l = 0;
let mut h = len;
while l < h {
use core::cmp::Ordering::*;
let i = (l + h) / 2;
let recbase = reclen.checked_mul(i)?.checked_add(record_base)?;
let mut s = b.stream_at(recbase)?;
let table_tag = s.read_u32()?;
match tag.cmp(&table_tag) {
Less => h = i,
Greater => l = i + 1,
Equal => {
s.skip(4)?;
let start = s.read_u32()?;
let len = s.read_u32()?;
let end = start.checked_add(len)?;
return Some((start, end));
}
}
}
None
}
/// Returns the byte offset of the table with the specified tag.
fn table_offset(&self, tag: RawTag) -> u32 {
self.table_range(tag).map(|r| r.0).unwrap_or(0)
}
/// Returns the data for the table with the specified tag.
fn table_data(&self, tag: RawTag) -> Option<&'a [u8]> {
let r = self.table_range(tag)?;
self.data().get(r.0 as usize..r.1 as usize)
}
/// Returns the font header table.
fn head(&self) -> Option<Head<'a>> {
Head::from_font(self)
}
/// Returns the OS/2 and Windows metrics table.
fn os2(&self) -> Option<Os2<'a>> {
Os2::from_font(self)
}
/// Returns the PostScript table.
fn post(&self) -> Option<Post<'a>> {
Post::from_font(self)
}
/// Returns the maximum profile table.
fn maxp(&self) -> Option<Maxp<'a>> {
Maxp::from_font(self)
}
/// Returns the horizontal header table.
fn hhea(&self) -> Option<Hhea<'a>> {
Hhea::from_font(self)
}
/// Returns the vertical header table.
fn vhea(&self) -> Option<Vhea<'a>> {
Vhea::from_font(self)
}
}
impl<'a, T> RawFont<'a> for &T
where
T: RawFont<'a>,
{
fn data(&self) -> &'a [u8] {
(*self).data()
}
fn offset(&self) -> u32 {
(*self).offset()
}
}

526
vendor/swash/src/internal/parse.rs vendored Normal file
View File

@@ -0,0 +1,526 @@
//! Parsing primitives.
use core::ops::Range;
/// Buffer wrapping a byte slice for safely reading big endian data.
#[derive(Copy, Clone)]
pub struct Bytes<'a>(pub &'a [u8]);
impl<'a> Bytes<'a> {
/// Creates a new bytes instance for the specified buffer.
pub fn new(data: &'a [u8]) -> Self {
Self(data)
}
/// Creates a new bytes instance for the specified buffer and offset.
pub fn with_offset(data: &'a [u8], offset: usize) -> Option<Self> {
Some(Self(data.get(offset..)?))
}
/// Creates a new bytes instance with the specified range of data.
pub fn with_range(data: &'a [u8], range: Range<usize>) -> Option<Self> {
Some(Self(data.get(range)?))
}
/// Returns the underlying data.
pub fn data(&self) -> &'a [u8] {
self.0
}
/// Returns the length of the underlying data.
pub fn len(&self) -> usize {
self.0.len()
}
/// Returns true if the specified range is within the bounds of the
/// underlying data.
pub fn check_range(&self, offset: usize, len: usize) -> bool {
let end = self.0.len();
(offset < end) & (end - offset >= len)
}
/// Returns an error if the specified range is not within the bounds of
/// the underlying data.
pub fn ensure_range(&self, offset: usize, len: usize) -> Option<()> {
if self.check_range(offset, len) {
Some(())
} else {
None
}
}
/// Reads a value of the specified type at some offset.
#[inline(always)]
pub fn read<T: FromBeData>(&self, offset: usize) -> Option<T> {
T::from_be_data(self.0, offset)
}
/// Reads a u8 value at some offset.
#[inline(always)]
pub fn read_u8(&self, offset: usize) -> Option<u8> {
u8::from_be_data(self.0, offset)
}
/// Reads a u16 value at some offset.
#[inline(always)]
pub fn read_u16(&self, offset: usize) -> Option<u16> {
u16::from_be_data(self.0, offset)
}
/// Reads a u24 value at the specified offset.
#[inline(always)]
pub fn read_u24(&self, offset: usize) -> Option<u32> {
U24::from_be_data(self.0, offset).map(|x| x.0)
}
/// Reads a u32 value at some offset.
#[inline(always)]
pub fn read_u32(&self, offset: usize) -> Option<u32> {
u32::from_be_data(self.0, offset)
}
/// Reads an i8 value at some offset.
#[inline(always)]
pub fn read_i8(&self, offset: usize) -> Option<i8> {
i8::from_be_data(self.0, offset)
}
/// Reads an i16 value at some offset.
#[inline(always)]
pub fn read_i16(&self, offset: usize) -> Option<i16> {
i16::from_be_data(self.0, offset)
}
/// Reads a value of the specified type at some offset, or returns the
/// default value on bounds check failure.
pub fn read_or_default<T: FromBeData + Default>(&self, offset: usize) -> T {
T::from_be_data(self.0, offset).unwrap_or_default()
}
/// Returns a value of the specified type at some offset without bounds
/// checking.
#[inline(always)]
pub unsafe fn read_unchecked<T: FromBeData>(&self, offset: usize) -> T {
T::from_be_data_unchecked(self.0, offset)
}
/// Reads an array of values of the specified type and length at some
/// offset.
pub fn read_array<T: FromBeData>(&self, offset: usize, len: usize) -> Option<Array<'a, T>> {
let len = len * T::SIZE;
if !self.check_range(offset, len) {
return None;
}
Some(Array::new(&self.0[offset..offset + len]))
}
/// Reads a sequence of bytes at the specified offset and length.
pub fn read_bytes(&self, offset: usize, len: usize) -> Option<&'a [u8]> {
if !self.check_range(offset, len) {
return None;
}
Some(&self.0[offset..offset + len])
}
/// Creates a new stream at the specified offset.
pub fn stream_at(&self, offset: usize) -> Option<Stream<'a>> {
Stream::with_offset(self.0, offset)
}
}
impl<'a> core::ops::Deref for Bytes<'a> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.0
}
}
/// Stream over a byte slice for safely reading big endian data.
#[derive(Copy, Clone)]
pub struct Stream<'a> {
data: &'a [u8],
offset: usize,
}
impl<'a> Stream<'a> {
/// Creates a new stream wrapping the specified bytes.
pub fn new(data: &'a [u8]) -> Self {
Self { data, offset: 0 }
}
/// Creates a new stream with the specified data and offset.
pub fn with_offset(data: &'a [u8], offset: usize) -> Option<Self> {
let data = data.get(offset..)?;
Some(Self { data, offset: 0 })
}
/// Creates a new stream with the specified range of data.
pub fn with_range(data: &'a [u8], range: Range<usize>) -> Option<Self> {
let data = data.get(range)?;
Some(Self { data, offset: 0 })
}
/// Returns the underlying buffer for the cursor.
pub fn data(&self) -> &'a [u8] {
self.data
}
/// Returns the length of the underlying buffer.
pub fn len(&self) -> usize {
self.data.len()
}
/// Returns the current offset.
pub fn offset(&self) -> usize {
self.offset
}
/// Returns the number of bytes available for reading.
pub fn remaining(&self) -> usize {
self.data.len() - self.offset
}
/// Sets the offset.
pub fn set_offset(&mut self, offset: usize) -> Option<()> {
if offset > self.data.len() {
return None;
}
self.offset = offset;
Some(())
}
/// Returns true if the specified number of bytes can be read.
pub fn check_range(&self, len: usize) -> bool {
self.data.len() - self.offset >= len
}
/// Returns an error of the specified number of bytes cannot be read.
pub fn ensure_range(&self, len: usize) -> Option<()> {
if self.check_range(len) {
Some(())
} else {
None
}
}
/// Skips the specified number of bytes.
pub fn skip(&mut self, bytes: usize) -> Option<()> {
self.set_offset(self.offset.checked_add(bytes)?)
}
/// Reads a value of the specified type and advances the offset.
pub fn read<T: FromBeData>(&mut self) -> Option<T> {
if self.data.len() - self.offset < T::SIZE {
None
} else {
let v = unsafe { T::from_be_data_unchecked(self.data, self.offset) };
self.offset += T::SIZE;
Some(v)
}
}
/// Reads a u8 value and advances the offset.
#[inline(always)]
pub fn read_u8(&mut self) -> Option<u8> {
self.read::<u8>()
}
/// Reads a u16 value and advances the offset.
#[inline(always)]
pub fn read_u16(&mut self) -> Option<u16> {
self.read::<u16>()
}
/// Reads a u32 value and advances the offset.
#[inline(always)]
pub fn read_u32(&mut self) -> Option<u32> {
self.read::<u32>()
}
/// Reads an i8 value and advances the offset.
#[inline(always)]
pub fn read_i8(&mut self) -> Option<i8> {
self.read::<i8>()
}
/// Reads an i16 value and advances the offset.
#[inline(always)]
pub fn read_i16(&mut self) -> Option<i16> {
self.read::<i16>()
}
/// Reads an array of values of the specified type and length and
/// advances the offset.
pub fn read_array<T: FromBeData>(&mut self, len: usize) -> Option<Array<'a, T>> {
let len = len * T::SIZE;
if !self.check_range(len) {
return None;
}
let arr = Array::new(&self.data[self.offset..self.offset + len]);
self.offset += len;
Some(arr)
}
/// Reads a sequence of bytes of the specified length and advances the
/// offset.
pub fn read_bytes(&mut self, len: usize) -> Option<&'a [u8]> {
if !self.check_range(len) {
return None;
}
let bytes = &self.data[self.offset..self.offset + len];
self.offset += len;
Some(bytes)
}
}
/// An array wrapping a byte buffer over a sequence of values that implement
/// [`FromBeData`].
#[derive(Copy, Clone)]
pub struct Array<'a, T: FromBeData> {
data: &'a [u8],
len: usize,
_p: core::marker::PhantomData<T>,
}
impl<T> core::fmt::Debug for Array<'_, T>
where
T: core::fmt::Debug + FromBeData,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "[")?;
for (i, value) in self.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{:?}", value)?;
}
write!(f, "]")
}
}
impl<'a, T: FromBeData> Array<'a, T> {
pub(crate) fn new(data: &'a [u8]) -> Self {
Self {
data,
len: data.len() / T::SIZE,
_p: core::marker::PhantomData {},
}
}
/// Returns the length of the array.
pub fn len(&self) -> usize {
self.len
}
/// Returns the element at the specified index.
pub fn get(&self, index: usize) -> Option<T> {
if index >= self.len {
None
} else {
unsafe { Some(T::from_be_data_unchecked(self.data, index * T::SIZE)) }
}
}
/// Returns the element at the specified index, or some value if the index
/// is out of bounds.
pub fn get_or(&self, index: usize, or: T) -> T {
if index >= self.len {
or
} else {
unsafe { T::from_be_data_unchecked(self.data, index * T::SIZE) }
}
}
/// Returns the element at the specified index without bounds checking.
pub unsafe fn get_unchecked(&self, index: usize) -> T {
T::from_be_data_unchecked(self.data, index * T::SIZE)
}
/// Performs a binary search over the array using the specified comparator
/// function. Returns the index and value of the element on success, or
/// `None` if a match was not found.
pub fn binary_search_by<F>(&self, mut f: F) -> Option<(usize, T)>
where
F: FnMut(&T) -> core::cmp::Ordering,
{
// Taken from Rust core library.
use core::cmp::Ordering::*;
let mut size = self.len;
if size == 0 {
return None;
}
let mut base = 0usize;
while size > 1 {
let half = size / 2;
let mid = base + half;
// SAFETY: the call is made safe by the following inconstants:
// - `mid >= 0`: by definition
// - `mid < size`: `mid = size / 2 + size / 4 + size / 8 ...`
let element = unsafe { self.get_unchecked(mid) };
base = match f(&element) {
Greater => base,
Less => mid,
Equal => return Some((mid, element)),
};
size -= half;
}
None
}
/// Returns an iterator over the elements of the array.
pub fn iter(&self) -> ArrayIter<'a, T> {
ArrayIter {
inner: *self,
cur: 0,
}
}
}
/// Iterator over the elements of an array.
#[derive(Clone)]
#[doc(hidden)]
pub struct ArrayIter<'a, T: FromBeData> {
inner: Array<'a, T>,
cur: usize,
}
impl<'a, T: FromBeData + 'a> Iterator for ArrayIter<'a, T> {
type Item = T;
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.inner.len - self.cur;
(remaining, Some(remaining))
}
fn next(&mut self) -> Option<T> {
if self.cur >= self.inner.len {
return None;
}
self.cur += 1;
unsafe { Some(self.inner.get_unchecked(self.cur - 1)) }
}
}
impl<'a, T: FromBeData + 'a> IntoIterator for Array<'a, T> {
type IntoIter = ArrayIter<'a, T>;
type Item = T;
fn into_iter(self) -> Self::IntoIter {
ArrayIter {
inner: self,
cur: 0,
}
}
}
/// Interface for reading big endian data from a buffer.
pub trait FromBeData: Sized + Copy + Clone {
const SIZE: usize = core::mem::size_of::<Self>();
#[inline(always)]
fn from_be_data(buf: &[u8], offset: usize) -> Option<Self> {
let len = buf.len();
if (offset < len) && ((len - offset) >= Self::SIZE) {
unsafe { Some(Self::from_be_data_unchecked(buf, offset)) }
} else {
None
}
}
unsafe fn from_be_data_unchecked(buf: &[u8], offset: usize) -> Self;
}
pub(crate) const USE_UNALIGNED_READS_LE: bool =
cfg!(any(target_arch = "x86", target_arch = "x86_64")) && cfg!(not(debug_assertions));
impl FromBeData for u8 {
unsafe fn from_be_data_unchecked(buf: &[u8], offset: usize) -> Self {
*buf.get_unchecked(offset)
}
}
impl FromBeData for i8 {
unsafe fn from_be_data_unchecked(buf: &[u8], offset: usize) -> Self {
*buf.get_unchecked(offset) as i8
}
}
impl FromBeData for u16 {
#[inline(always)]
unsafe fn from_be_data_unchecked(buf: &[u8], offset: usize) -> Self {
if USE_UNALIGNED_READS_LE {
(buf.as_ptr().add(offset) as *const u16)
.read_unaligned()
.swap_bytes()
} else {
(*buf.get_unchecked(offset) as u16) << 8 | *buf.get_unchecked(offset + 1) as u16
}
}
}
impl FromBeData for i16 {
unsafe fn from_be_data_unchecked(buf: &[u8], offset: usize) -> Self {
u16::from_be_data_unchecked(buf, offset) as i16
}
}
impl FromBeData for u32 {
unsafe fn from_be_data_unchecked(buf: &[u8], offset: usize) -> Self {
if USE_UNALIGNED_READS_LE {
(buf.as_ptr().add(offset) as *const u32)
.read_unaligned()
.swap_bytes()
} else {
(*buf.get_unchecked(offset) as u32) << 24
| (*buf.get_unchecked(offset + 1) as u32) << 16
| (*buf.get_unchecked(offset + 2) as u32) << 8
| *buf.get_unchecked(offset + 3) as u32
}
}
}
impl FromBeData for i32 {
unsafe fn from_be_data_unchecked(buf: &[u8], offset: usize) -> Self {
u32::from_be_data_unchecked(buf, offset) as i32
}
}
impl FromBeData for u64 {
unsafe fn from_be_data_unchecked(buf: &[u8], offset: usize) -> Self {
if USE_UNALIGNED_READS_LE {
(buf.as_ptr().add(offset) as *const u64)
.read_unaligned()
.swap_bytes()
} else {
(*buf.get_unchecked(offset) as u64) << 56
| (*buf.get_unchecked(offset + 1) as u64) << 48
| (*buf.get_unchecked(offset + 2) as u64) << 40
| (*buf.get_unchecked(offset + 3) as u64) << 32
| (*buf.get_unchecked(offset + 4) as u64) << 24
| (*buf.get_unchecked(offset + 5) as u64) << 16
| (*buf.get_unchecked(offset + 6) as u64) << 8
| *buf.get_unchecked(offset + 7) as u64
}
}
}
/// Unsigned 24-bit integer.
#[derive(Copy, Clone)]
#[doc(hidden)]
pub struct U24(pub u32);
impl FromBeData for U24 {
const SIZE: usize = 3;
unsafe fn from_be_data_unchecked(buf: &[u8], offset: usize) -> Self {
Self(
(*buf.get_unchecked(offset) as u32) << 16
| (*buf.get_unchecked(offset + 1) as u32) << 8
| *buf.get_unchecked(offset + 2) as u32,
)
}
}
impl FromBeData for () {
unsafe fn from_be_data_unchecked(_buf: &[u8], _offset: usize) -> Self {}
}

445
vendor/swash/src/internal/var.rs vendored Normal file
View File

@@ -0,0 +1,445 @@
//! Font and metric variation tables.
use super::{fixed::Fixed, raw_tag, Array, Bytes, RawFont, RawTag, U24};
pub const FVAR: RawTag = raw_tag(b"fvar");
pub const AVAR: RawTag = raw_tag(b"avar");
pub const HVAR: RawTag = raw_tag(b"HVAR");
pub const VVAR: RawTag = raw_tag(b"VVAR");
pub const MVAR: RawTag = raw_tag(b"MVAR");
/// Font variations table.
#[derive(Copy, Clone)]
pub struct Fvar<'a> {
data: Bytes<'a>,
axis_offset: u16,
axis_count: u16,
axis_size: u16,
inst_count: u16,
inst_size: u16,
}
impl<'a> Fvar<'a> {
pub fn new(data: &'a [u8]) -> Self {
let b = Bytes::new(data);
let axis_offset = b.read_or_default::<u16>(4);
let axis_count = b.read_or_default::<u16>(8);
let axis_size = b.read_or_default::<u16>(10);
let inst_count = b.read_or_default::<u16>(12);
let inst_size = b.read_or_default::<u16>(14);
Self {
data: b,
axis_offset,
axis_count,
axis_size,
inst_count,
inst_size,
}
}
pub fn from_font(font: impl RawFont<'a>) -> Option<Self> {
Some(Self::new(font.table_data(FVAR)?))
}
pub fn axis_count(&self) -> u16 {
self.axis_count
}
pub fn get_axis(&self, index: u16) -> Option<VarAxis> {
if index >= self.axis_count {
return None;
}
let b = &self.data;
let base = self.axis_offset as usize;
let offset = base + index as usize * self.axis_size as usize;
let tag = b.read::<u32>(offset)?;
let min = Fixed(b.read::<i32>(offset + 4)?);
let default = Fixed(b.read::<i32>(offset + 8)?);
let max = Fixed(b.read::<i32>(offset + 12)?);
let flags = b.read::<u16>(offset + 16)?;
let name_id = b.read::<u16>(offset + 18)?;
Some(VarAxis {
index,
tag,
name_id,
flags,
min,
default,
max,
})
}
pub fn get_axis_by_tag(&self, tag: RawTag) -> Option<VarAxis> {
let b = &self.data;
let base = self.axis_offset as usize;
let axis_size = self.axis_size as usize;
for i in 0..self.axis_count as usize {
let tag_offset = base + i * axis_size;
if b.read_u32(tag_offset) == Some(tag) {
return self.get_axis(i as u16);
}
}
None
}
pub fn instance_count(&self) -> u16 {
self.inst_count
}
pub fn get_instance(&self, index: u16) -> Option<VarInstance<'a>> {
if index >= self.inst_count {
return None;
}
let b = &self.data;
let base = self.axis_offset as usize + (self.axis_count as usize * self.axis_size as usize);
let offset = base + index as usize * self.inst_size as usize;
let name_id = b.read::<u16>(offset)?;
let values = b.read_array::<Fixed>(offset + 4, self.axis_count as usize)?;
let ps_name_offset = 4 + self.axis_count as usize * 4;
let postscript_name_id = if ps_name_offset == self.inst_size as usize - 2 {
b.read::<u16>(ps_name_offset)
} else {
None
};
Some(VarInstance {
index,
name_id,
postscript_name_id,
values,
})
}
}
/// Axis of variation in a variable font.
#[derive(Copy, Clone, Default)]
pub struct VarAxis {
pub index: u16,
pub tag: RawTag,
pub name_id: u16,
pub flags: u16,
pub min: Fixed,
pub default: Fixed,
pub max: Fixed,
}
impl VarAxis {
/// Returns true if the axis should be hidden in a user interface.
pub fn is_hidden(&self) -> bool {
self.flags & 1 != 0
}
/// Returns a normalized axis coordinate for the specified value in 2.14
/// fixed point format.
pub fn normalized_coord(&self, mut value: Fixed, avar: Option<(&[u8], u32)>) -> i16 {
use core::cmp::Ordering::*;
if value < self.min {
value = self.min;
} else if value > self.max {
value = self.max;
}
value = match value.cmp(&self.default) {
Less => -((self.default - value) / (self.default - self.min)),
Greater => (value - self.default) / (self.max - self.default),
Equal => Fixed(0),
};
value = value.min(Fixed::ONE).max(-Fixed::ONE);
value = avar
.and_then(|(data, avar)| adjust_axis(data, avar, self.index, value))
.unwrap_or(value);
value.to_f2dot14()
}
}
/// Named instance in a variable font.
#[derive(Copy, Clone)]
pub struct VarInstance<'a> {
pub index: u16,
pub name_id: u16,
pub postscript_name_id: Option<u16>,
pub values: Array<'a, Fixed>,
}
/// Metrics variation table.
pub struct Mvar<'a> {
data: Bytes<'a>,
coords: &'a [i16],
rec_size: usize,
rec_count: usize,
store: u32,
}
impl<'a> Mvar<'a> {
pub fn new(data: &'a [u8], mvar: u32, coords: &'a [i16]) -> Option<Self> {
let b = Bytes::with_offset(data, mvar as usize)?;
let rec_size = b.read::<u16>(6)? as usize;
let rec_count = b.read::<u16>(8)? as usize;
let store = b.read::<u16>(10)? as u32;
if rec_count == 0 || store == 0 {
return None;
}
Some(Self {
data: b,
coords,
rec_size,
rec_count,
store,
})
}
pub fn delta(&self, metric: RawTag) -> f32 {
self.read_delta(metric).map(|d| d.to_f32()).unwrap_or(0.)
}
#[inline(always)]
fn read_delta(&self, metric: RawTag) -> Option<Fixed> {
let base = 12;
let b = &self.data;
let rec_size = self.rec_size;
let mut l = 0;
let mut h = self.rec_count;
while l < h {
use core::cmp::Ordering::*;
let i = (l + h) / 2;
let offset = base + i * rec_size;
let t = b.read::<u32>(offset)?;
match metric.cmp(&t) {
Less => h = i,
Greater => l = i + 1,
Equal => {
let inner = b.read::<u16>(offset + 4)?;
let outer = b.read::<u16>(offset + 6)?;
return item_delta(b.data(), self.store, outer, inner, self.coords);
}
}
}
None
}
}
/// Returns the advance delta for the specified glyph.
pub fn advance_delta(data: &[u8], xvar: u32, glyph_id: u16, coords: &[i16]) -> f32 {
metric_delta(data, xvar, 8, glyph_id, coords)
.map(|d| d.to_f32())
.unwrap_or(0.)
}
/// Returns the side bearing delta for the specified glyph.
pub fn sb_delta(data: &[u8], xvar: u32, glyph_id: u16, coords: &[i16]) -> f32 {
metric_delta(data, xvar, 12, glyph_id, coords)
.map(|d| d.to_f32())
.unwrap_or(0.)
}
/// Applies adjustments to a coordinate according to the optional axis
/// variation table.
pub fn adjust_axis(data: &[u8], avar: u32, axis: u16, coord: Fixed) -> Option<Fixed> {
use skrifa::raw::{tables::avar::Avar, types::Fixed, FontData, FontRead};
if avar == 0 {
return None;
}
let avar = Avar::read(FontData::new(data.get(avar as usize..)?)).ok()?;
let mapping = avar
.axis_segment_maps()
.get(axis as usize)
.transpose()
.ok()??;
Some(Fixed(mapping.apply(Fixed::from_bits(coord.0)).to_bits()))
}
/// Returns a delta from an item variation store.
pub fn item_delta(
data: &[u8],
offset: u32,
outer: u16,
inner: u16,
coords: &[i16],
) -> Option<Fixed> {
if offset == 0 {
return None;
}
let b = Bytes::new(data);
let store = offset as usize;
if outer >= b.read::<u16>(store + 6)? {
return None;
}
let region_base = store + b.read::<u32>(store + 2)? as usize;
let axis_count = b.read::<u16>(region_base)? as usize;
let region_record_size = axis_count * 6;
let region_count = b.read::<u16>(region_base + 2)? as usize;
let data_base = store + b.read::<u32>(store + 8 + outer as usize * 4)? as usize;
let region_index_base = data_base + 6;
let region_index_count = b.read::<u16>(data_base + 4)? as usize;
let (short_count, mut delta_base) = {
let inner = inner as usize;
let short_count = b.read::<u16>(data_base + 2)? as usize;
let count = region_index_count;
let base = data_base + 6 + count * 2;
let elem_len = (count - short_count) + short_count * 2;
let offset = base + inner * elem_len;
(short_count, offset)
};
const ZERO: Fixed = Fixed::ZERO;
const ONE: Fixed = Fixed::ONE;
let mut idx = 0;
let mut delta = ZERO;
for i in 0..region_index_count {
let region_index = b.read::<u16>(region_index_base + i * 2)? as usize;
if region_index >= region_count {
return None;
}
let region_offset = region_base + 4 + region_index * region_record_size;
let mut scalar = ONE;
for axis in 0..axis_count {
let region_axis_base = region_offset + axis * 6;
let start = Fixed::from_f2dot14(b.read::<i16>(region_axis_base)?);
let peak = Fixed::from_f2dot14(b.read::<i16>(region_axis_base + 2)?);
let end = Fixed::from_f2dot14(b.read::<i16>(region_axis_base + 4)?);
let coord = coords
.get(axis)
.map(|c| Fixed::from_f2dot14(*c))
.unwrap_or(ZERO);
if start > peak || peak > end || peak == ZERO || start < ZERO && end > ZERO {
continue;
} else if coord < start || coord > end {
scalar = ZERO;
break;
} else if coord == peak {
continue;
} else if coord < peak {
scalar = scalar * (coord - start) / (peak - start);
} else {
scalar = scalar * (end - coord) / (end - peak);
};
}
let val = if idx >= short_count {
delta_base += 1;
b.read::<i8>(delta_base - 1)? as i16
} else {
delta_base += 2;
b.read::<i16>(delta_base - 2)?
};
idx += 1;
delta += scalar * Fixed::from_i32(val as i32);
}
Some(delta)
}
#[inline(always)]
fn metric_delta(
data: &[u8],
base: u32,
which: usize,
glyph_id: u16,
coords: &[i16],
) -> Option<Fixed> {
if base == 0 {
return None;
}
let b = Bytes::new(data);
let mut store = b.read::<u32>(base as usize + 4)?;
if store == 0 {
return None;
}
store += base;
let mut offset = b.read::<u32>(base as usize + which)? as usize;
if offset == 0 {
if which == 8 {
return item_delta(data, store, 0, glyph_id, coords);
} else {
return None;
}
}
offset += base as usize;
let format = b.read::<u16>(offset)? as u32;
let count = b.read::<u16>(offset + 2)?;
let bit_count = (format & 0xF) + 1;
let entry_size = ((format & 0x30) >> 4) + 1;
let base = offset + 4;
let index = if glyph_id >= count {
count - 1
} else {
glyph_id
} as usize;
let entry = match entry_size {
1 => b.read::<u8>(base + index)? as u32,
2 => b.read::<u16>(base + index * 2)? as u32,
3 => b.read::<U24>(base + index * 3)?.0,
4 => b.read::<u32>(base + index * 4)?,
_ => return None,
};
let outer = entry >> bit_count;
let inner = entry & ((1 << bit_count) - 1);
item_delta(data, store, outer as u16, inner as u16, coords)
}
/// Tags for metrics from the `MVAR` table.
pub mod mvar_tags {
use super::{raw_tag, RawTag};
/// Horizontal ascender.
pub const HASC: RawTag = raw_tag(b"hasc");
/// Horizontal descender.
pub const HDSC: RawTag = raw_tag(b"hdsc");
/// Horizontal line gap.
pub const HLGP: RawTag = raw_tag(b"hlgp");
/// Horizontal caret rise.
pub const HCRS: RawTag = raw_tag(b"hcrs");
/// Horizontal caret run.
pub const HCRN: RawTag = raw_tag(b"hcrn");
/// Horizontal caret offset.
pub const HCOF: RawTag = raw_tag(b"hcof");
/// Horizontal clipping ascent.
pub const HCLA: RawTag = raw_tag(b"hcla");
/// Horizontal clipping descent.
pub const HCLD: RawTag = raw_tag(b"hcld");
/// Vertical ascender.
pub const VASC: RawTag = raw_tag(b"vasc");
/// Vertical descender.
pub const VDSC: RawTag = raw_tag(b"vdsc");
/// Vertical line gap.
pub const VLGP: RawTag = raw_tag(b"vlgp");
/// Vertical caret rise.
pub const VCRS: RawTag = raw_tag(b"vcrs");
/// Vertical caret run.
pub const VCRN: RawTag = raw_tag(b"vcrn");
/// Vertical caret offset.
pub const VCOF: RawTag = raw_tag(b"vcof");
/// X-height.
pub const XHGT: RawTag = raw_tag(b"xhgt");
/// Cap height.
pub const CPHT: RawTag = raw_tag(b"cpht");
/// Underline offset.
pub const UNDO: RawTag = raw_tag(b"undo");
/// Underline size.
pub const UNDS: RawTag = raw_tag(b"unds");
/// Strikeout offset.
pub const STRO: RawTag = raw_tag(b"stro");
/// Strikeout size.
pub const STRS: RawTag = raw_tag(b"strs");
/// Subscript x-offset.
pub const SBXO: RawTag = raw_tag(b"sbxo");
/// Subscript y-offset.
pub const SBYO: RawTag = raw_tag(b"sbyo");
/// Subscript x-size.
pub const SBXS: RawTag = raw_tag(b"sbxs");
/// Subscript y-size.
pub const SBYS: RawTag = raw_tag(b"sbys");
/// Superscript x-offset.
pub const SPXO: RawTag = raw_tag(b"spxo");
/// Superscript y-offset.
pub const SPYO: RawTag = raw_tag(b"spyo");
/// Superscript x-size.
pub const SPXS: RawTag = raw_tag(b"spxs");
/// Superscript y-size.
pub const SPYS: RawTag = raw_tag(b"spys");
}

30
vendor/swash/src/internal/vorg.rs vendored Normal file
View File

@@ -0,0 +1,30 @@
//! Vertical origin table.
use super::{raw_tag, Bytes, RawTag};
pub const VORG: RawTag = raw_tag(b"VORG");
/// Returns the vertical origin for the specified glyph.
pub fn origin(data: &[u8], vorg: u32, glyph_id: u16) -> Option<i16> {
if vorg == 0 {
return None;
}
let b = Bytes::new(data);
let base = vorg as usize;
let default = b.read::<i16>(base + 4)?;
let count = b.read::<u16>(base + 6)? as usize;
let mut l = 0;
let mut h = count;
while l < h {
use core::cmp::Ordering::*;
let i = (l + h) / 2;
let rec = base + 8 + i * 4;
let g = b.read::<u16>(rec)?;
match glyph_id.cmp(&g) {
Less => h = i,
Greater => l = i + 1,
Equal => return b.read::<i16>(rec + 2),
}
}
Some(default)
}

28
vendor/swash/src/internal/xmtx.rs vendored Normal file
View File

@@ -0,0 +1,28 @@
//! Glyph metrics tables.
use super::{raw_tag, Bytes, RawTag};
pub const HMTX: RawTag = raw_tag(b"hmtx");
pub const VMTX: RawTag = raw_tag(b"vmtx");
/// Returns the advance for the specified glyph.
pub fn advance(data: &[u8], xmtx: u32, long_metric_count: u16, glyph_id: u16) -> u16 {
let b = Bytes::new(data);
let offset = if glyph_id < long_metric_count {
glyph_id as usize * 4
} else {
(long_metric_count - 1) as usize * 4
};
b.read_u16(offset + xmtx as usize).unwrap_or(0)
}
/// Returns the side bearing for the specified glyph.
pub fn sb(data: &[u8], xmtx: u32, long_metric_count: u16, glyph_id: u16) -> i16 {
let b = Bytes::new(data);
let offset = if glyph_id < long_metric_count {
glyph_id as usize * 4 + 2
} else {
long_metric_count as usize * 4 + (glyph_id - long_metric_count) as usize * 2
};
b.read_i16(offset + xmtx as usize).unwrap_or(0)
}

177
vendor/swash/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,177 @@
/*!
Font introspection, complex text shaping and glyph rendering.
For a comprehensive list of features provided by this crate, please check out
the [readme](https://github.com/dfrg/swash/blob/main/README.md) on GitHub.
# Note
This is a low level library focusing on implementations of OpenType and
various related Unicode specifications for building high quality, high performance
text layout and rendering components with minimal overhead.
If you're looking for something higher level, please stay tuned-- work is in
progress.
# Usage
The primary currency in this crate is the [`FontRef`] struct so you'll want to
start there to learn how to construct and use fonts.
Documentation for [shaping](shape) and [scaling](scale) is provided in
the respective modules.
*/
#![allow(clippy::float_cmp)]
#![allow(clippy::many_single_char_names)]
#![allow(clippy::needless_lifetimes)]
#![allow(clippy::redundant_static_lifetimes)]
#![allow(clippy::too_many_arguments)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(not(any(test, feature = "std")), no_std)]
extern crate alloc;
#[cfg(feature = "scale")]
pub use zeno;
#[macro_use]
mod macros;
mod attributes;
mod cache;
mod charmap;
mod feature;
mod font;
mod internal;
mod metrics;
mod palette;
mod setting;
mod strike;
mod string;
mod tag;
mod variation;
#[cfg(feature = "scale")]
pub mod scale;
pub mod shape;
pub mod text;
pub use attributes::*;
pub use cache::CacheKey;
pub use charmap::Charmap;
pub use feature::{Action, Feature, WritingSystem};
pub use font::{FontDataRef, FontRef, TableProvider};
pub use metrics::{GlyphMetrics, Metrics};
pub use palette::{ColorPalette, Usability};
pub use setting::Setting;
pub use strike::BitmapStrike;
pub use string::{LocalizedString, StringId};
pub use tag::{tag_from_bytes, tag_from_str_lossy, Tag};
pub use variation::{Instance, Variation};
/// Collection of various iterators over metadata contained in a font.
pub mod iter {
pub use super::feature::{Features, WritingSystems};
pub use super::font::Fonts;
pub use super::palette::ColorPalettes;
pub use super::strike::BitmapStrikes;
pub use super::string::{Chars, LocalizedStrings};
pub use super::variation::{Instances, Variations};
}
/// Proxies used to efficiently rematerialize metadata.
pub mod proxy {
pub use super::charmap::CharmapProxy;
pub use super::metrics::MetricsProxy;
pub use super::strike::BitmapStrikesProxy;
pub use super::variation::VariationsProxy;
}
use iter::*;
use proxy::BitmapStrikesProxy;
/// Glyph identifier.
pub type GlyphId = u16;
/// Normalized variation coordinate in 2.14 fixed point format.
pub type NormalizedCoord = i16;
impl<'a> FontRef<'a> {
/// Returns the primary attributes for the font.
pub fn attributes(&self) -> Attributes {
Attributes::from_font(self)
}
/// Returns an iterator over the localized strings for the font.
pub fn localized_strings(&self) -> LocalizedStrings<'a> {
LocalizedStrings::from_font(self)
}
/// Returns an iterator over the variations for the font.
pub fn variations(&self) -> Variations<'a> {
Variations::from_font(self)
}
/// Returns an iterator over the named instances for the font.
pub fn instances(&self) -> Instances<'a> {
Instances::from_font(self)
}
/// Returns an iterator over writing systems supported by the font.
pub fn writing_systems(&self) -> WritingSystems<'a> {
WritingSystems::from_font(self)
}
/// Returns an iterator over the features supported by a font.
pub fn features(&self) -> Features<'a> {
Features::from_font(self)
}
/// Returns metrics for the font and the specified normalized variation
/// coordinates.
pub fn metrics(&self, coords: &'a [NormalizedCoord]) -> Metrics {
Metrics::from_font(self, coords)
}
/// Returns glyph metrics for the font and the specified normalized
/// variation coordinates.
pub fn glyph_metrics(&self, coords: &'a [NormalizedCoord]) -> GlyphMetrics<'a> {
GlyphMetrics::from_font(self, coords)
}
/// Returns the character map for the font.
pub fn charmap(&self) -> Charmap<'a> {
Charmap::from_font(self)
}
/// Returns an iterator over the color palettes for the font.
pub fn color_palettes(&self) -> ColorPalettes<'a> {
ColorPalettes::from_font(self)
}
/// Returns an iterator over the alpha bitmap strikes for the font.
pub fn alpha_strikes(&self) -> BitmapStrikes<'a> {
BitmapStrikesProxy::from_font(self).materialize_alpha(self)
}
/// Returns an iterator over the color bitmap strikes for the font.
pub fn color_strikes(&self) -> BitmapStrikes<'a> {
BitmapStrikesProxy::from_font(self).materialize_color(self)
}
/// Returns the table data for the specified tag.
pub fn table(&self, tag: Tag) -> Option<&'a [u8]> {
use internal::RawFont;
let range = self.table_range(tag)?;
self.data.get(range.0 as usize..range.1 as usize)
}
/// Returns the name for the specified glyph identifier. This is an internal
/// function used for testing and stability is not guaranteed.
#[doc(hidden)]
pub fn glyph_name(&self, glyph_id: GlyphId) -> Option<&'a str> {
use internal::head::Post;
Post::from_font(self)?.name(glyph_id)
}
}

40
vendor/swash/src/macros.rs vendored Normal file
View File

@@ -0,0 +1,40 @@
/// Macro for consistent implementation of font resource iterators.
macro_rules! impl_iter {
($name:ident, $item:ident) => {
impl<'a> Iterator for $name<'a> {
type Item = $item<'a>;
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.len - self.pos;
(remaining, Some(remaining))
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let pos = self.pos.checked_add(n)?;
if pos >= self.len {
self.pos = self.len;
None
} else {
self.pos = pos + 1;
self.get(pos)
}
}
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.len {
None
} else {
let pos = self.pos;
self.pos += 1;
self.get(pos)
}
}
}
impl<'a> ExactSizeIterator for $name<'a> {
fn len(&self) -> usize {
self.len - self.pos
}
}
};
}

505
vendor/swash/src/metrics.rs vendored Normal file
View File

@@ -0,0 +1,505 @@
/*!
Font and glyph metrics.
*/
use super::internal::*;
use super::{FontRef, GlyphId, NormalizedCoord};
/// Proxy for rematerializing metrics.
#[derive(Copy, Clone, Default)]
pub struct MetricsProxy {
units_per_em: u16,
glyph_count: u16,
is_monospace: bool,
has_vertical_metrics: bool,
ascent: i16,
descent: i16,
leading: i16,
vertical_ascent: i16,
vertical_descent: i16,
vertical_leading: i16,
cap_height: i16,
x_height: i16,
average_width: u16,
max_width: u16,
underline_offset: i16,
strikeout_offset: i16,
stroke_size: i16,
mvar: u32,
hmtx: u32,
hvar: u32,
hmtx_count: u16,
has_vvar: bool,
vertical: Vertical,
}
impl MetricsProxy {
/// Creates a metrics proxy for the specified font.
pub fn from_font(font: &FontRef) -> Self {
let mut metadata = Self {
units_per_em: 1,
..Self::default()
};
metadata.fill(font);
metadata
}
/// Materializes font metrics for the specified font and
/// normalized variation coordinates. This proxy must have been created
/// from the same font.
pub fn materialize_metrics(&self, font: &FontRef, coords: &[NormalizedCoord]) -> Metrics {
let data = font.data;
let mut m = Metrics {
units_per_em: self.units_per_em,
glyph_count: self.glyph_count,
is_monospace: self.is_monospace,
has_vertical_metrics: self.has_vertical_metrics,
ascent: self.ascent as f32,
descent: self.descent as f32,
leading: self.leading as f32,
vertical_ascent: self.vertical_ascent as f32,
vertical_descent: self.vertical_descent as f32,
vertical_leading: self.vertical_leading as f32,
cap_height: self.cap_height as f32,
x_height: self.x_height as f32,
average_width: self.average_width as f32,
max_width: self.max_width as f32,
underline_offset: self.underline_offset as f32,
strikeout_offset: self.strikeout_offset as f32,
stroke_size: self.stroke_size as f32,
};
if self.mvar != 0 && !coords.is_empty() {
if let Some(v) = var::Mvar::new(data, self.mvar, coords) {
use var::mvar_tags::*;
m.ascent += v.delta(HASC);
m.descent += v.delta(HDSC);
m.leading += v.delta(HLGP);
if self.has_vertical_metrics {
m.vertical_ascent += v.delta(VASC);
m.vertical_descent += v.delta(VDSC);
m.vertical_leading += v.delta(VLGP);
}
m.cap_height += v.delta(CPHT);
m.x_height += v.delta(XHGT);
m.underline_offset += v.delta(UNDO);
m.strikeout_offset += v.delta(STRO);
m.stroke_size += v.delta(UNDS);
}
}
m
}
/// Materializes glyph metrics for the specified font and
/// normalized variation coordinates. This proxy must have been created
/// from the same font.
pub fn materialize_glyph_metrics<'a>(
&self,
font: &FontRef<'a>,
coords: &'a [NormalizedCoord],
) -> GlyphMetrics<'a> {
let data = font.data;
let mut vertical = self.vertical;
if !coords.is_empty() {
if let Vertical::Synthesized {
mvar,
advance,
origin,
} = &mut vertical
{
if *mvar != 0 {
if let Some(v) = var::Mvar::new(data, *mvar, coords) {
use var::mvar_tags::*;
let ascent_delta = v.delta(HASC);
let descent_delta = v.delta(HDSC);
*advance += ascent_delta + descent_delta;
*origin += ascent_delta;
}
}
}
}
GlyphMetrics {
data,
coords,
units_per_em: self.units_per_em,
glyph_count: self.glyph_count,
hmtx: self.hmtx,
hvar: self.hvar,
hmtx_count: self.hmtx_count,
has_vvar: self.has_vvar,
vertical,
scale: 1.,
}
}
/// Returns the number of font design units per em unit.
pub fn units_per_em(&self) -> u16 {
self.units_per_em
}
/// Returns the number of glyphs in the font.
pub fn glyph_count(&self) -> u16 {
self.glyph_count
}
fn fill(&mut self, font: &FontRef) -> Option<()> {
let head = font.head()?;
self.units_per_em = head.units_per_em();
self.glyph_count = font.maxp()?.glyph_count();
let mut have_line_metrics = false;
let os2 = font.os2();
if let Some(os2) = os2 {
let flags = os2.selection_flags();
self.average_width = os2.average_char_width() as u16;
self.strikeout_offset = os2.strikeout_position();
self.stroke_size = os2.strikeout_size();
self.x_height = os2.x_height();
self.cap_height = os2.cap_height();
if flags.use_typographic_metrics() {
self.ascent = os2.typographic_ascender();
self.descent = -os2.typographic_descender();
self.leading = os2.typographic_line_gap();
have_line_metrics = self.ascent != 0;
}
}
let hhea = font.hhea();
if let Some(hhea) = hhea {
self.max_width = hhea.max_advance();
if !have_line_metrics {
self.ascent = hhea.ascender();
self.descent = -hhea.descender();
self.leading = hhea.line_gap();
}
}
let vhea = font.vhea();
if let Some(vhea) = vhea {
self.has_vertical_metrics = true;
self.vertical_ascent = vhea.ascender();
self.vertical_descent = -vhea.descender();
self.vertical_leading = vhea.line_gap();
} else {
self.vertical_ascent = (self.units_per_em / 2) as i16;
self.vertical_descent = self.vertical_ascent;
}
if let Some(post) = font.post() {
self.underline_offset = post.underline_position();
self.stroke_size = post.underline_size();
self.is_monospace = post.is_fixed_pitch();
}
self.mvar = font.table_offset(var::MVAR);
self.hmtx_count = hhea.map(|t| t.num_long_metrics()).unwrap_or(1);
self.hmtx = font.table_offset(xmtx::HMTX);
self.hvar = font.table_offset(var::HVAR);
let mut vmtx = 0;
if vhea.is_some() {
vmtx = font.table_offset(xmtx::VMTX);
}
if vmtx != 0 {
let long_count = vhea.unwrap().num_long_metrics();
let vvar = font.table_offset(var::VVAR);
self.has_vvar = vvar != 0;
let vorg = font.table_offset(vorg::VORG);
if vorg != 0 {
self.vertical = Vertical::VmtxVorg {
long_count,
vmtx,
vvar,
vorg,
};
} else {
let glyf = font.table_offset(glyf::GLYF);
let loca = font.table_offset(glyf::LOCA);
let loca_fmt = font
.head()
.map(|t| t.index_to_location_format() as u8)
.unwrap_or(0xFF);
if glyf != 0 && loca != 0 && loca_fmt != 0xFF {
self.vertical = Vertical::VmtxGlyf {
loca_fmt,
long_count,
vmtx,
vvar,
glyf,
loca,
}
}
}
} else {
self.vertical = Vertical::Synthesized {
mvar: self.mvar,
advance: self.ascent as f32 + self.descent as f32,
origin: self.ascent as f32,
};
}
Some(())
}
}
/// Global font metrics.
#[derive(Copy, Clone, Default, Debug)]
pub struct Metrics {
/// Number of font design units per em unit.
pub units_per_em: u16,
/// Number of glyphs in the font.
pub glyph_count: u16,
/// True if the font is monospace.
pub is_monospace: bool,
/// True if the font provides canonical vertical metrics.
pub has_vertical_metrics: bool,
/// Distance from the baseline to the top of the alignment box.
pub ascent: f32,
/// Distance from the baseline to the bottom of the alignment box.
pub descent: f32,
/// Recommended additional spacing between lines.
pub leading: f32,
/// Distance from the vertical center baseline to the right edge of
/// the design space.
pub vertical_ascent: f32,
/// Distance from the vertical center baseline to the left edge of
/// the design space.
pub vertical_descent: f32,
/// Recommended additional spacing between columns.
pub vertical_leading: f32,
/// Distance from the baseline to the top of a typical English capital.
pub cap_height: f32,
/// Distance from the baseline to the top of the lowercase "x" or
/// similar character.
pub x_height: f32,
/// Average width of all non-zero characters in the font.
pub average_width: f32,
/// Maximum advance width of all characters in the font.
pub max_width: f32,
/// Recommended distance from the baseline to the top of an underline
/// stroke.
pub underline_offset: f32,
/// Recommended distance from the baseline to the top of a strikeout
/// stroke.
pub strikeout_offset: f32,
/// Recommended thickness of an underline or strikeout stroke.
pub stroke_size: f32,
}
impl Metrics {
/// Creates a new set of metrics from the specified font and
/// normalized variation coordinates.
pub(crate) fn from_font(font: &FontRef, coords: &[i16]) -> Self {
let meta = MetricsProxy::from_font(font);
meta.materialize_metrics(font, coords)
}
/// Creates a new set of metrics scaled for the specified pixels
/// per em unit.
pub fn scale(&self, ppem: f32) -> Self {
self.linear_scale(if self.units_per_em != 0 {
ppem / self.units_per_em as f32
} else {
1.
})
}
/// Creates a new set of metrics scaled by the specified factor.
pub fn linear_scale(&self, s: f32) -> Self {
let mut m = *self;
m.ascent *= s;
m.descent *= s;
m.leading *= s;
m.vertical_ascent *= s;
m.vertical_descent *= s;
m.vertical_leading *= s;
m.cap_height *= s;
m.x_height *= s;
m.average_width *= s;
m.max_width *= s;
m.underline_offset *= s;
m.strikeout_offset *= s;
m.stroke_size *= s;
m
}
}
/// Glyph advances, side bearings and vertical origins.
#[derive(Copy, Clone)]
pub struct GlyphMetrics<'a> {
data: &'a [u8],
coords: &'a [i16],
units_per_em: u16,
glyph_count: u16,
hmtx: u32,
hvar: u32,
hmtx_count: u16,
has_vvar: bool,
vertical: Vertical,
scale: f32,
}
impl<'a> GlyphMetrics<'a> {
/// Creates a new set of glyph metrics from the specified font and
/// normalized variation coordinates.
pub(crate) fn from_font(font: &FontRef<'a>, coords: &'a [NormalizedCoord]) -> Self {
let proxy = MetricsProxy::from_font(font);
proxy.materialize_glyph_metrics(font, coords)
}
/// Returns the number of font design units per em unit.
pub fn units_per_em(&self) -> u16 {
self.units_per_em
}
/// Returns the number of glyphs in the font.
pub fn glyph_count(&self) -> u16 {
self.glyph_count
}
/// Returns true if the font provides canonical vertical glyph metrics.
pub fn has_vertical_metrics(&self) -> bool {
!matches!(self.vertical, Vertical::Synthesized { .. })
}
/// Returns true if variations are supported.
pub fn has_variations(&self) -> bool {
self.hvar != 0 || self.has_vvar
}
/// Creates a new set of metrics scaled for the specified pixels
/// per em unit.
pub fn scale(&self, ppem: f32) -> Self {
self.linear_scale(if self.units_per_em() != 0 {
ppem / self.units_per_em() as f32
} else {
1.
})
}
/// Creates a new set of metrics scaled by the specified factor.
pub fn linear_scale(&self, scale: f32) -> Self {
let mut copy = *self;
copy.scale = scale;
copy
}
/// Returns the horizontal advance for the specified glyph.
pub fn advance_width(&self, glyph_id: GlyphId) -> f32 {
let mut v = xmtx::advance(self.data, self.hmtx, self.hmtx_count, glyph_id) as f32;
if self.hvar != 0 {
v += var::advance_delta(self.data, self.hvar, glyph_id, self.coords);
}
v * self.scale
}
/// Returns the left side bearing for the specified glyph.
pub fn lsb(&self, glyph_id: GlyphId) -> f32 {
let mut v = xmtx::sb(self.data, self.hmtx, self.hmtx_count, glyph_id) as f32;
if self.hvar != 0 {
v += var::sb_delta(self.data, self.hvar, glyph_id, self.coords);
}
v * self.scale
}
/// Returns the vertical advance for the specified glyph.
pub fn advance_height(&self, glyph_id: GlyphId) -> f32 {
self.scale
* match self.vertical {
Vertical::VmtxGlyf {
vmtx,
vvar,
long_count,
..
}
| Vertical::VmtxVorg {
vmtx,
vvar,
long_count,
..
} => {
let mut v = xmtx::advance(self.data, vmtx, long_count, glyph_id) as f32;
if vvar != 0 {
v += var::advance_delta(self.data, vvar, glyph_id, self.coords);
}
v
}
Vertical::Synthesized { advance, .. } => advance,
}
}
/// Returns the top side bearing for the specified glyph.
pub fn tsb(&self, glyph_id: GlyphId) -> f32 {
self.scale
* match self.vertical {
Vertical::VmtxGlyf {
vmtx,
vvar,
long_count,
..
}
| Vertical::VmtxVorg {
vmtx,
vvar,
long_count,
..
} => {
let mut v = xmtx::sb(self.data, vmtx, long_count, glyph_id) as f32;
if vvar != 0 {
v += var::sb_delta(self.data, vvar, glyph_id, self.coords);
}
v
}
Vertical::Synthesized { .. } => 0.,
}
}
/// Returns the vertical origin for the specified glyph id.
pub fn vertical_origin(&self, glyph_id: GlyphId) -> f32 {
self.scale
* match self.vertical {
Vertical::VmtxGlyf {
loca_fmt,
loca,
glyf,
..
} => {
if let Some(max_y) = glyf::ymax(self.data, loca_fmt, loca, glyf, glyph_id) {
max_y as f32 + self.tsb(glyph_id)
} else {
self.units_per_em as f32
}
}
Vertical::VmtxVorg { vorg, .. } => vorg::origin(self.data, vorg, glyph_id)
.unwrap_or(self.units_per_em as i16)
as f32,
Vertical::Synthesized { origin, .. } => origin,
}
}
}
#[derive(Copy, Clone)]
#[repr(u8)]
enum Vertical {
VmtxGlyf {
loca_fmt: u8,
long_count: u16,
vmtx: u32,
vvar: u32,
glyf: u32,
loca: u32,
},
VmtxVorg {
long_count: u16,
vmtx: u32,
vvar: u32,
vorg: u32,
},
Synthesized {
mvar: u32,
advance: f32,
origin: f32,
},
}
impl Default for Vertical {
fn default() -> Self {
Self::Synthesized {
mvar: 0,
advance: 0.,
origin: 0.,
}
}
}

167
vendor/swash/src/palette.rs vendored Normal file
View File

@@ -0,0 +1,167 @@
//! Collections of colors for layered outlines.
use super::internal::*;
use super::{
string::{LocalizedString, StringId},
FontRef,
};
const CPAL: RawTag = raw_tag(b"CPAL");
/// Iterator over a collection of color palettes.
#[derive(Copy, Clone)]
pub struct ColorPalettes<'a> {
font: FontRef<'a>,
data: Bytes<'a>,
len: usize,
pos: usize,
}
impl<'a> ColorPalettes<'a> {
pub(crate) fn new(font: FontRef<'a>, data: &'a [u8]) -> Self {
let data = Bytes::new(data);
let len = data.read_or_default::<u16>(4) as usize;
Self {
font,
data,
len,
pos: 0,
}
}
pub(crate) fn from_font(font: &FontRef<'a>) -> Self {
let data = font.table_data(CPAL).unwrap_or(&[]);
Self::new(*font, data)
}
// Unused when render feature is disabled.
#[allow(dead_code)]
pub(crate) fn from_font_and_offset(font: &FontRef<'a>, offset: u32) -> Self {
let data = font.data.get(offset as usize..).unwrap_or(&[]);
Self::new(*font, data)
}
fn get(&self, index: usize) -> Option<ColorPalette<'a>> {
if index >= self.len {
return None;
}
let b = &self.data;
let version = b.read::<u16>(0)?;
let num_entries = b.read::<u16>(2)?;
let offset = b.read::<u32>(8)? as usize;
let first = b.read::<u16>(12 + index * 2)? as usize;
let offset = offset + first * 4;
Some(ColorPalette {
font: self.font,
data: *b,
version,
index,
num_entries,
offset,
})
}
}
impl_iter!(ColorPalettes, ColorPalette);
/// Collection of colors.
#[derive(Copy, Clone)]
pub struct ColorPalette<'a> {
font: FontRef<'a>,
data: Bytes<'a>,
version: u16,
index: usize,
num_entries: u16,
offset: usize,
}
impl<'a> ColorPalette<'a> {
/// Returns the index of the palette.
pub fn index(&self) -> u16 {
self.index as u16
}
/// Returns the name identifier for the palette, if available.
pub fn name_id(&self) -> Option<StringId> {
if self.version == 0 {
return None;
}
let d = &self.data;
let num_palettes = d.read::<u16>(4)? as usize;
let base = 16 + num_palettes * 2;
let labels_offset = d.read::<u32>(base)? as usize;
if labels_offset == 0 {
return None;
}
Some(StringId::Other(
d.read::<u16>(labels_offset + self.index * 2)?,
))
}
/// Returns the name for the palette, optionally for a particular
/// language.
pub fn name(&self, language: Option<&str>) -> Option<LocalizedString<'a>> {
self.name_id()
.and_then(|id| self.font.localized_strings().find_by_id(id, language))
}
/// Returns the theme usability of the palette, if available.
pub fn usability(&self) -> Option<Usability> {
let flags = self.flags()?;
Some(match flags & 0b11 {
0b01 => Usability::Light,
0b10 => Usability::Dark,
0b11 => Usability::Both,
_ => return None,
})
}
/// Returns the number of color entries in the palette.
pub fn len(&self) -> u16 {
self.num_entries
}
/// Returns whether this palette is empty.
pub fn is_empty(&self) -> bool {
self.num_entries == 0
}
/// Returns the color for the specified entry in RGBA order.
pub fn get(&self, index: u16) -> [u8; 4] {
if index >= self.num_entries {
return [0; 4];
}
let offset = self.offset + index as usize * 4;
let d = &self.data;
let b = d.read_or_default::<u8>(offset);
let g = d.read_or_default::<u8>(offset + 1);
let r = d.read_or_default::<u8>(offset + 2);
let a = d.read_or_default::<u8>(offset + 3);
[r, g, b, a]
}
fn flags(&self) -> Option<u32> {
if self.version == 0 {
return None;
}
let d = &self.data;
let num_palettes = d.read::<u16>(4)? as usize;
let base = 12 + num_palettes * 2;
let types_offset = d.read::<u32>(base)? as usize;
if types_offset == 0 {
return None;
}
d.read::<u32>(types_offset + self.index * 4)
}
}
/// Theme of a palette with respect to background color.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Usability {
/// Usable with light backgrounds.
Light,
/// Usable with dark backgrounds.
Dark,
/// Usable with both light and dark backgrounds.
Both,
}

411
vendor/swash/src/scale/bitmap/mod.rs vendored Normal file
View File

@@ -0,0 +1,411 @@
//! Bitmap scaling.
#![allow(dead_code)]
use alloc::vec::Vec;
#[cfg(feature = "libm")]
use core_maths::CoreFloat;
mod png;
/// Decodes a PNG image.
pub fn decode_png(data: &[u8], scratch: &mut Vec<u8>, target: &mut [u8]) -> Option<(u32, u32)> {
png::decode(data, scratch, target)
.map(|(w, h, _)| (w, h))
.ok()
}
pub fn blit(
mask: &[u8],
mask_width: u32,
mask_height: u32,
x: i32,
y: i32,
color: [u8; 4],
target: &mut [u8],
target_width: u32,
target_height: u32,
) {
if mask_width == 0 || mask_height == 0 || target_width == 0 || target_height == 0 {
return;
}
let source_width = mask_width as usize;
let source_height = mask_height as usize;
let dest_width = target_width as usize;
let dest_height = target_height as usize;
let source_x = if x < 0 { -x as usize } else { 0 };
let source_y = if y < 0 { -y as usize } else { 0 };
if source_x >= source_width || source_y >= source_height {
return;
}
let dest_x = if x < 0 { 0 } else { x as usize };
let dest_y = if y < 0 { 0 } else { y as usize };
if dest_x >= dest_width || dest_y >= dest_height {
return;
}
let source_end_x = (source_width).min(dest_width - dest_x + source_x);
let source_end_y = (source_height).min(dest_height - dest_y + source_y);
let source_columns = source_y..source_end_y;
let source_rows = source_x..source_end_x;
let dest_pitch = target_width as usize * 4;
let color_a = color[3] as u32;
let mut dy = dest_y;
for sy in source_columns {
let src_row = &mask[sy * mask_width as usize..];
let dst_row = &mut target[dy * dest_pitch..];
dy += 1;
let mut dx = dest_x * 4;
for sx in source_rows.clone() {
let a = (src_row[sx] as u32 * color_a) >> 8;
if a >= 255 {
dst_row[dx + 3] = 255;
dst_row[dx..(dx + 3)].copy_from_slice(&color[..3]);
dst_row[dx + 3] = 255;
} else if a != 0 {
let inverse_a = 255 - a;
for i in 0..3 {
let d = dst_row[dx + i] as u32;
let c = ((inverse_a * d) + (a * color[i] as u32)) >> 8;
dst_row[dx + i] = c as u8;
}
let d = dst_row[dx + 3] as u32;
let c = ((inverse_a * d) + a * 255) >> 8;
dst_row[dx + 3] = c as u8;
}
dx += 4;
}
}
}
#[derive(Copy, Clone, PartialEq)]
pub enum Filter {
Nearest,
Bilinear,
Bicubic,
Mitchell,
Lanczos3,
Gaussian,
}
pub fn resize(
image: &[u8],
width: u32,
height: u32,
channels: u32,
target: &mut [u8],
target_width: u32,
target_height: u32,
filter: Filter,
scratch: Option<&mut Vec<u8>>,
) -> bool {
if target_width == 0 || target_height == 0 {
return true;
}
let mut tmp = Vec::new();
let scratch = if let Some(scratch) = scratch {
scratch
} else {
&mut tmp
};
let image_size = (width * height * channels) as usize;
if image.len() < image_size {
return false;
}
let target_size = (target_width * target_height * channels) as usize;
if target.len() < target_size {
return false;
}
let scratch_size = (target_width * height * channels) as usize;
scratch.resize(scratch_size, 0);
use Filter::*;
match filter {
Nearest => resample(
image,
width,
height,
channels,
target,
target_width,
target_height,
scratch,
0.,
&nearest,
),
Bilinear => resample(
image,
width,
height,
channels,
target,
target_width,
target_height,
scratch,
1.,
&bilinear,
),
Bicubic => resample(
image,
width,
height,
channels,
target,
target_width,
target_height,
scratch,
2.,
&bicubic,
),
Mitchell => resample(
image,
width,
height,
channels,
target,
target_width,
target_height,
scratch,
2.,
&mitchell,
),
Lanczos3 => resample(
image,
width,
height,
channels,
target,
target_width,
target_height,
scratch,
3.,
&lanczos3,
),
Gaussian => resample(
image,
width,
height,
channels,
target,
target_width,
target_height,
scratch,
3.,
&|x| gaussian(x, 0.5),
),
}
}
fn resample<Filter>(
image: &[u8],
width: u32,
height: u32,
channels: u32,
target: &mut [u8],
target_width: u32,
target_height: u32,
scratch: &mut [u8],
support: f32,
filter: &Filter,
) -> bool
where
Filter: Fn(f32) -> f32,
{
let tmp_width = target_width;
let tmp_height = height;
let s = 1. / 255.;
if channels == 1 {
sample_dir(
&|x, y| [0., 0., 0., image[(y * width + x) as usize] as f32 * s],
width,
height,
target_width,
filter,
support,
&mut |x, y, p| scratch[(y * tmp_width + x) as usize] = (p[3] * 255.) as u8,
);
sample_dir(
&|y, x| [0., 0., 0., scratch[(y * tmp_width + x) as usize] as f32 * s],
tmp_height,
tmp_width,
target_height,
filter,
support,
&mut |y, x, p| target[(y * target_width + x) as usize] = (p[3] * 255.) as u8,
);
true
} else if channels == 4 {
sample_dir(
&|x, y| {
let row = (y * width * channels + x * channels) as usize;
[
image[row] as f32 * s,
image[row + 1] as f32 * s,
image[row + 2] as f32 * s,
image[row + 3] as f32 * s,
]
},
width,
height,
target_width,
filter,
support,
&mut |x, y, p| {
let row = (y * target_width * channels + x * channels) as usize;
scratch[row] = (p[0] * 255.) as u8;
scratch[row + 1] = (p[1] * 255.) as u8;
scratch[row + 2] = (p[2] * 255.) as u8;
scratch[row + 3] = (p[3] * 255.) as u8;
},
);
sample_dir(
&|y, x| {
let row = (y * tmp_width * channels + x * channels) as usize;
[
scratch[row] as f32 * s,
scratch[row + 1] as f32 * s,
scratch[row + 2] as f32 * s,
scratch[row + 3] as f32 * s,
]
},
tmp_height,
tmp_width,
target_height,
filter,
support,
&mut |y, x, p| {
let row = (y * target_width * channels + x * channels) as usize;
target[row] = (p[0] * 255.) as u8;
target[row + 1] = (p[1] * 255.) as u8;
target[row + 2] = (p[2] * 255.) as u8;
target[row + 3] = (p[3] * 255.) as u8;
},
);
true
} else {
false
}
}
fn sample_dir<Input, Output, Filter>(
input: &Input,
width: u32,
height: u32,
new_width: u32,
filter: &Filter,
support: f32,
output: &mut Output,
) where
Input: Fn(u32, u32) -> [f32; 4],
Output: FnMut(u32, u32, &[f32; 4]),
Filter: Fn(f32) -> f32,
{
const MAX_WEIGHTS: usize = 64;
let mut weights = [0f32; MAX_WEIGHTS];
let mut num_weights;
let ratio = width as f32 / new_width as f32;
let sratio = ratio.max(1.);
let src_support = support * sratio;
let isratio = 1. / sratio;
for outx in 0..new_width {
let inx = (outx as f32 + 0.5) * ratio;
let left = (inx - src_support).floor() as i32;
let mut left = left.max(0).min(width as i32 - 1) as usize;
let right = (inx + src_support).ceil() as i32;
let mut right = right.max(left as i32 + 1).min(width as i32) as usize;
let inx = inx - 0.5;
while right - left > MAX_WEIGHTS {
right -= 1;
left += 1;
}
num_weights = 0;
let mut sum = 0.;
for i in left..right {
let w = filter((i as f32 - inx) * isratio);
weights[num_weights] = w;
num_weights += 1;
sum += w;
}
let isum = 1. / sum;
let weights = &weights[..num_weights];
for y in 0..height {
let mut accum = [0f32; 4];
for (i, w) in weights.iter().enumerate() {
let p = input((left + i) as u32, y);
let a = p[3];
accum[0] += p[0] * w * a;
accum[1] += p[1] * w * a;
accum[2] += p[2] * w * a;
accum[3] += p[3] * w;
}
if accum[3] != 0. {
let a = 1. / accum[3];
accum[0] *= a;
accum[1] *= a;
accum[2] *= a;
accum[3] *= isum;
}
output(outx, y, &accum);
}
}
}
fn sinc(t: f32) -> f32 {
let a = t * core::f32::consts::PI;
if t == 0. {
1.
} else {
a.sin() / a
}
}
fn lanczos3(x: f32) -> f32 {
if x.abs() < 3. {
(sinc(x) * sinc(x / 3.)).abs()
} else {
0.
}
}
fn bilinear(x: f32) -> f32 {
let x = x.abs();
if x < 1. {
1. - x
} else {
0.
}
}
fn bicubic(x: f32) -> f32 {
let a = x.abs();
let b = 0.;
let c = 0.5;
let k = if a < 1. {
(12. - 9. * b - 6. * c) * a.powi(3) + (-18. + 12. * b + 6. * c) * a.powi(2) + (6. - 2. * b)
} else if a < 2. {
(-b - 6. * c) * a.powi(3)
+ (6. * b + 30. * c) * a.powi(2)
+ (-12. * b - 48. * c) * a
+ (8. * b + 24. * c)
} else {
0.
};
(k / 6.).abs()
}
fn mitchell(x: f32) -> f32 {
let x = x.abs();
if x < 1. {
((16. + x * x * (21. * x - 36.)) / 18.).abs()
} else if x < 2. {
((32. + x * (-60. + x * (36. - 7. * x))) / 18.).abs()
} else {
0.
}
}
fn nearest(_x: f32) -> f32 {
1.
}
fn gaussian(x: f32, r: f32) -> f32 {
((2. * core::f32::consts::PI).sqrt() * r).recip() * (-x.powi(2) / (2. * r.powi(2))).exp()
}

589
vendor/swash/src/scale/bitmap/png.rs vendored Normal file
View File

@@ -0,0 +1,589 @@
//! PNG decoder.
use alloc::vec::Vec;
/// PNG magic bytes.
pub const SIGNATURE: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
/// Errors that can occur during decoding.
#[derive(Debug)]
pub enum DecodeError {
/// The file format was not supported.
UnsupportedFileFormat,
/// Conversion into the requested format failed.
ConversionFailed,
/// Invalid signature in an image.
InvalidSignature,
/// The file contains a pixel format this is not supported.
UnsupportedPixelFormat,
/// The file enables a feature that is not supported.
UnsupportedFeature,
/// Some size limit was exceeded.
LimitExceeded,
/// Some index into the image was out of bounds.
IndexOutOfBounds,
/// Some portion of the file was corrupt.
CorruptData,
/// An "end of file" was reached prematurely.
UnexpectedEof,
}
impl From<yazi::Error> for DecodeError {
fn from(_: yazi::Error) -> Self {
Self::CorruptData
}
}
/// The possible color types for a PNG image.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ColorType {
Greyscale = 0,
GreyscaleAlpha = 4,
Indexed = 3,
TrueColor = 2,
TrueColorAlpha = 6,
}
/// The PNG header.
#[derive(Copy, Clone, Debug)]
pub struct Header {
pub width: u32,
pub height: u32,
pub color_type: ColorType,
pub depth: u8,
pub interlaced: bool,
}
impl Header {
/// Attempts to decode a PNG header from the specified buffer.
pub fn decode(png: &[u8]) -> Option<Self> {
const IHDR: u32 = 73 << 24 | 72 << 16 | 68 << 8 | 82;
if png.len() < 33 || !check_signature(png) {
return None;
}
let mut o = 8;
let len = get_u32be(png, o);
if len != 13 || get_u32be(png, o + 4) != IHDR {
return None;
}
o += 4;
let width = get_u32be(png, o + 4);
let height = get_u32be(png, o + 8);
let depth = png[o + 12];
let color_type = png[o + 13];
let compression_method = png[o + 14];
let filter_method = png[o + 15];
let interlace_method = png[o + 16];
if compression_method != 0
|| filter_method != 0
|| (interlace_method != 0 && interlace_method != 1)
{
return None;
}
let _crc = get_u32be(png, o + 17);
let d = depth;
use ColorType::*;
let color_type = match color_type {
0 => Greyscale,
2 => TrueColor,
3 => Indexed,
4 => GreyscaleAlpha,
6 => TrueColorAlpha,
_ => return None,
};
match color_type {
Greyscale | Indexed => {
if d != 1 && d != 2 && d != 4 && d != 8 && d != 16 {
return None;
}
if color_type == Indexed && d == 16 {
return None;
}
}
TrueColor | TrueColorAlpha | GreyscaleAlpha => {
if d != 8 && d != 16 {
return None;
}
}
};
Some(Self {
width,
height,
color_type,
depth,
interlaced: interlace_method != 0,
})
}
}
/// Returns true if the specified buffer might represent a PNG image.
pub fn check_signature(png: &[u8]) -> bool {
if png.len() >= 8 {
for i in 0..8 {
if png[i] != SIGNATURE[i] {
return false;
}
}
return true;
}
false
}
pub fn decode(
data: &[u8],
scratch: &mut Vec<u8>,
target: &mut [u8],
) -> Result<(u32, u32, bool), DecodeError> {
let mut state = State::new(data, scratch)?;
let w = state.header.width;
let h = state.header.height;
if w == 0 || h == 0 {
return Ok((w, h, false));
}
let decomp_len = scratch.len();
scratch.resize(decomp_len + state.extra_bytes, 0);
let (decomp, extra) = scratch.split_at_mut(decomp_len);
if target.len() < (w * h * 4) as usize {
return Err(DecodeError::LimitExceeded);
}
state.trunc_16 = true;
state.expand_alpha = true;
decode_data::<EmitRgba8>(&mut state, decomp, extra, target).ok_or(DecodeError::CorruptData)?;
Ok((w, h, state.has_alpha))
}
struct State<'a> {
header: Header,
data: &'a [u8],
palette: &'a [u8],
trans: &'a [u8],
gamma: Option<f32>,
effective_depth: u8,
bpp: usize,
channels: usize,
pitch: usize,
extra_bytes: usize,
bwidth: usize,
has_alpha: bool,
trunc_16: bool,
expand_alpha: bool,
}
impl<'a> State<'a> {
fn new(data: &'a [u8], decomp: &mut Vec<u8>) -> Result<Self, DecodeError> {
let header = Header::decode(data).ok_or(DecodeError::CorruptData)?;
let mut this = Self {
header,
data,
palette: &[],
trans: &[],
gamma: None,
effective_depth: header.depth,
bpp: 0,
channels: 0,
pitch: 0,
extra_bytes: 0,
bwidth: 0,
has_alpha: false,
trunc_16: false,
expand_alpha: false,
};
let w = header.width as usize;
let h = header.height as usize;
if w == 0 || h == 0 {
return Ok(this);
}
use ColorType::*;
let (channels, has_alpha) = match header.color_type {
TrueColor => (3, false),
TrueColorAlpha => (4, true),
GreyscaleAlpha => (2, true),
Greyscale => (1, false),
Indexed => (1, false),
};
this.has_alpha = has_alpha;
this.bpp = this.header.depth as usize * channels;
this.pitch = (w * this.bpp + 7) / 8;
this.bwidth = (this.bpp + 7) / 8;
this.extra_bytes = this.pitch * 2 + w * 8;
decomp.clear();
decomp.reserve(this.extra_bytes + (this.pitch + 1) * h);
let limit = data.len();
let mut offset = 33;
let mut dec = yazi::Decoder::new();
dec.set_format(yazi::Format::Zlib);
let mut stream = dec.stream_into_vec(decomp);
loop {
if offset + 8 > limit {
return Err(DecodeError::CorruptData);
}
let len = get_u32be(data, offset) as usize;
offset += 4;
let ty = get_u32be(data, offset);
offset += 4;
if offset + len > limit {
return Err(DecodeError::CorruptData);
}
let bytes = data
.get(offset..offset + len)
.ok_or(DecodeError::CorruptData)?;
const PLTE: u32 = chunk_name(b"PLTE");
const TRNS: u32 = chunk_name(b"tRNS");
const IDAT: u32 = chunk_name(b"IDAT");
const GAMA: u32 = chunk_name(b"gAMA");
const IEND: u32 = chunk_name(b"IEND");
match ty {
PLTE => this.palette = bytes,
TRNS => this.trans = bytes,
IDAT => {
stream.write(bytes)?;
}
GAMA => {
if bytes.len() > 4 && this.gamma.is_none() {
this.gamma = Some(get_u32be(bytes, 0) as f32 / 100000.);
}
}
IEND => break,
_ => {}
}
offset += len + 4;
}
stream.finish()?;
if header.color_type == Indexed {
if this.palette.is_empty() {
return Err(DecodeError::CorruptData);
}
if !this.trans.is_empty() {
this.has_alpha = true;
}
}
Ok(this)
}
}
fn decode_data<E: Emit>(
state: &mut State,
decomp: &mut [u8],
extra: &mut [u8],
target: &mut [u8],
) -> Option<()> {
let has_palette = !state.palette.is_empty();
let w = state.header.width as usize;
let h = state.header.height as usize;
let (mut line, extra) = extra.split_at_mut(state.pitch);
let (mut prev_line, out_line) = extra.split_at_mut(state.pitch);
let depth = state.header.depth;
let bpp = state.bpp;
let pitch = state.pitch;
let bwidth = state.bwidth;
let trunc_16 = state.trunc_16;
if state.header.interlaced {
const ROW_START: [u8; 7] = [0, 0, 4, 0, 2, 0, 1];
const ROW_INCREMENT: [u8; 7] = [8, 8, 8, 4, 4, 2, 2];
const COL_START: [u8; 7] = [0, 4, 0, 2, 0, 1, 0];
const COL_INCREMENT: [u8; 7] = [8, 8, 4, 4, 2, 2, 1];
let mut pass = 0;
let mut y = 0;
let mut offset = 0;
loop {
let cols = match pass {
0 => (w + 7) / 8,
1 => (w + 3) / 8,
2 => (w + 3) / 4,
3 => (w + 1) / 4,
4 => (w + 1) / 2,
5 => w / 2,
6 => w,
_ => return None,
};
if cols == 0 {
pass += 1;
continue;
}
let start = COL_START[pass] as usize;
let inc = COL_INCREMENT[pass] as usize;
let row_inc = ROW_INCREMENT[pass] as usize;
while y < h {
let pitch = (cols * bpp + 7) / 8;
let end = offset + pitch + 1;
let source = decomp.get(offset..end)?;
offset = end;
let ty = source[0];
defilter(
ty,
source.get(1..)?,
line.get_mut(..pitch)?,
prev_line.get(..pitch)?,
bwidth,
)?;
if depth == 8 {
E::emit(state, line, target, start, y, w, inc, cols)?;
} else {
normalize(line, out_line, depth, has_palette, cols, trunc_16)?;
E::emit(state, out_line, target, start, y, w, inc, cols)?;
}
core::mem::swap(&mut prev_line, &mut line);
y += row_inc;
}
if pass == 6 {
break;
}
pass += 1;
y = ROW_START[pass] as usize;
for byte in prev_line.iter_mut() {
*byte = 0;
}
}
} else if depth == 8 {
for y in 0..h {
let offset = y * (pitch + 1);
let end = offset + pitch + 1;
let source = decomp.get(offset..end)?;
let ty = *source.get(0)?;
defilter(ty, source.get(1..)?, line, prev_line, bwidth)?;
E::emit(state, line, target, 0, y, w, 1, w)?;
core::mem::swap(&mut prev_line, &mut line);
}
} else {
for y in 0..h {
let offset = y * (pitch + 1);
let end = offset + pitch + 1;
let source = decomp.get(offset..end)?;
let ty = *source.get(0)?;
defilter(ty, source.get(1..)?, line, prev_line, bwidth)?;
normalize(line, out_line, depth, has_palette, w, trunc_16)?;
E::emit(state, out_line, target, 0, y, w, 1, w)?;
core::mem::swap(&mut prev_line, &mut line);
}
}
Some(())
}
#[cfg(target_endian = "little")]
const IS_LITTLE_ENDIAN: bool = true;
#[cfg(not(target_endian = "little"))]
const IS_LITTLE_ENDIAN: bool = false;
fn defilter(ty: u8, source: &[u8], dest: &mut [u8], last: &[u8], bwidth: usize) -> Option<()> {
let len = source.len();
match ty {
0 => {
dest.copy_from_slice(source);
}
1 => {
dest.get_mut(..bwidth)?
.copy_from_slice(source.get(..bwidth)?);
for i in bwidth..len {
dest[i] = source[i].wrapping_add(dest[i - bwidth]);
}
}
2 => {
for ((dest, source), last) in dest.iter_mut().zip(source.iter()).zip(last.iter()) {
*dest = source.wrapping_add(*last);
}
}
3 => {
for i in 0..bwidth {
dest[i] = source[i].wrapping_add((last[i] as u32 / 2) as u8);
}
for i in bwidth..len {
dest[i] = source[i].wrapping_add(
((dest[i - bwidth] as u32).wrapping_add(last[i] as u32) / 2) as u8,
);
}
}
4 => {
for i in 0..bwidth {
dest[i] = source[i].wrapping_add(last[i]);
}
for i in bwidth..len {
dest[i] =
source[i].wrapping_add(paeth(dest[i - bwidth], last[i], last[i - bwidth]));
}
}
_ => return None,
}
Some(())
}
fn normalize(
source: &[u8],
dest: &mut [u8],
depth: u8,
palette: bool,
width: usize,
trunc_16: bool,
) -> Option<()> {
match depth {
16 => {
if trunc_16 {
for (i, d) in dest.iter_mut().enumerate() {
*d = source[i * 2];
}
} else if IS_LITTLE_ENDIAN {
for (s, d) in source.chunks(2).zip(dest.chunks_mut(2)) {
d[1] = s[0];
d[0] = s[1];
}
} else {
dest.copy_from_slice(source);
}
}
8 => {
dest.copy_from_slice(source);
}
4 => {
let conv = if !palette { 17 } else { 1 };
for (i, d) in dest.get_mut(..width)?.iter_mut().enumerate() {
*d = (source[i / 2] >> (4 - i % 2 * 4) & 15) * conv;
}
}
2 => {
let conv = if !palette { 85 } else { 1 };
for (i, d) in dest.get_mut(..width)?.iter_mut().enumerate() {
*d = (source[i / 4] >> (6 - i % 4 * 2) & 3) * conv;
}
}
1 => {
let conv = if !palette { 255 } else { 1 };
for (i, d) in dest.get_mut(..width)?.iter_mut().enumerate() {
*d = (source[i / 8] >> (7 - i % 8) & 1) * conv;
}
}
_ => {}
}
Some(())
}
trait Emit {
fn emit(
state: &State,
source: &[u8],
image: &mut [u8],
x: usize,
y: usize,
width: usize,
inc: usize,
len: usize,
) -> Option<()>;
}
struct EmitRgba8;
impl Emit for EmitRgba8 {
fn emit(
state: &State,
source: &[u8],
image: &mut [u8],
x: usize,
y: usize,
width: usize,
inc: usize,
len: usize,
) -> Option<()> {
use ColorType::*;
let src = source;
let mut out = y * width * 4 + x * 4;
let mut i = 0;
match state.header.color_type {
Indexed => {
let palette = state.palette;
let trans = state.trans;
let palette_len = palette.len();
let trans_len = trans.len();
for _ in 0..len {
let t = src[i] as usize;
let p = t * 3;
if p + 2 >= palette_len {
image[out] = 0;
image[out + 1] = 0;
image[out + 2] = 0;
} else {
image[out] = palette[p];
image[out + 1] = palette[p + 1];
image[out + 2] = palette[p + 2];
}
if t >= trans_len {
image[out + 3] = 255;
} else {
image[out + 3] = trans[t];
}
i += 1;
out += 4 * inc;
}
}
TrueColor => {
for _ in 0..len {
image[out] = src[i];
image[out + 1] = src[i + 1];
image[out + 2] = src[i + 2];
image[out + 3] = 255;
i += 3;
out += 4 * inc;
}
}
TrueColorAlpha => {
for _ in 0..len {
image[out] = src[i];
image[out + 1] = src[i + 1];
image[out + 2] = src[i + 2];
image[out + 3] = src[i + 3];
i += 4;
out += 4 * inc;
}
}
Greyscale => {
for c in src[..len].iter().copied() {
image[out] = c;
image[out + 1] = c;
image[out + 2] = c;
image[out + 3] = 255;
out += 4 * inc;
}
}
GreyscaleAlpha => {
let mut i = 0;
for _ in 0..len {
let c = src[i];
image[out] = c;
image[out + 1] = c;
image[out + 2] = c;
image[out + 3] = src[i + 1];
i += 2;
out += 4 * inc;
}
}
}
Some(())
}
}
#[inline(always)]
fn paeth(a: u8, b: u8, c: u8) -> u8 {
let pa = ((b as i32).wrapping_sub(c as i32)).abs();
let pb = ((a as i32).wrapping_sub(c as i32)).abs();
let pc = ((a as i32)
.wrapping_add(b as i32)
.wrapping_sub(c as i32)
.wrapping_sub(c as i32))
.abs();
if pc < pa && pc < pb {
c
} else if pb < pa {
b
} else {
a
}
}
fn get_u32be(buf: &[u8], offset: usize) -> u32 {
(buf[offset] as u32) << 24
| (buf[offset + 1] as u32) << 16
| (buf[offset + 2] as u32) << 8
| buf[offset + 3] as u32
}
const fn chunk_name(bytes: &[u8; 4]) -> u32 {
(bytes[0] as u32) << 24 | (bytes[1] as u32) << 16 | (bytes[2] as u32) << 8 | bytes[3] as u32
}

96
vendor/swash/src/scale/color.rs vendored Normal file
View File

@@ -0,0 +1,96 @@
use super::super::{
palette::{ColorPalette, ColorPalettes},
FontRef, GlyphId,
};
use super::internal::{raw_tag, Bytes, RawFont, RawTag};
const COLR: RawTag = raw_tag(b"COLR");
const CPAL: RawTag = raw_tag(b"CPAL");
#[derive(Copy, Clone, Default)]
pub struct ColorProxy {
pub colr: u32,
pub cpal: u32,
}
impl ColorProxy {
pub fn from_font(font: &FontRef) -> Self {
Self {
colr: font.table_offset(COLR),
cpal: font.table_offset(CPAL),
}
}
pub fn layers<'a>(&self, data: &'a [u8], glyph_id: GlyphId) -> Option<Layers<'a>> {
let b = Bytes::with_offset(data, self.colr as usize)?;
let count = b.read::<u16>(2)? as usize;
let base_offset = b.read::<u32>(4)? as usize;
let mut l = 0;
let mut h = count;
while l < h {
use core::cmp::Ordering::*;
let i = l + (h - l) / 2;
let rec = base_offset + i * 6;
let id = b.read::<u16>(rec)?;
match glyph_id.cmp(&id) {
Less => h = i,
Greater => l = i + 1,
Equal => {
let first = b.read::<u16>(rec + 2)? as usize;
let offset = b.read::<u32>(8)? as usize + first * 4;
let len = b.read::<u16>(rec + 4)?;
return Some(Layers {
data: b,
offset,
len,
});
}
}
}
None
}
// Unused when render feature is disabled.
#[allow(dead_code)]
pub fn palette<'a>(&self, font: &FontRef<'a>, index: u16) -> Option<ColorPalette<'a>> {
if self.cpal != 0 {
ColorPalettes::from_font_and_offset(font, self.cpal).nth(index as usize)
} else {
None
}
}
}
#[derive(Copy, Clone)]
pub struct Layers<'a> {
data: Bytes<'a>,
offset: usize,
len: u16,
}
impl<'a> Layers<'a> {
pub fn len(&self) -> u16 {
self.len
}
pub fn get(&self, index: u16) -> Option<Layer> {
let b = &self.data;
let base = self.offset + index as usize * 4;
let glyph_id = b.read::<u16>(base)?;
let color_index = b.read::<u16>(base + 2)?;
Some(Layer {
glyph_id,
color_index: if color_index != 0xFFFF {
Some(color_index)
} else {
None
},
})
}
}
#[derive(Copy, Clone)]
pub struct Layer {
pub glyph_id: GlyphId,
pub color_index: Option<u16>,
}

95
vendor/swash/src/scale/hinting_cache.rs vendored Normal file
View File

@@ -0,0 +1,95 @@
use alloc::vec::Vec;
use skrifa::{
instance::{NormalizedCoord, Size},
outline::{
HintingInstance, HintingMode, LcdLayout, OutlineGlyphCollection, OutlineGlyphFormat,
},
};
/// We keep this small to enable a simple LRU cache with a linear
/// search. Regenerating hinting data is low to medium cost so it's fine
/// to redo it occasionally.
const MAX_CACHED_HINT_INSTANCES: usize = 8;
pub(crate) struct HintingKey<'a> {
pub id: [u64; 2],
pub outlines: &'a OutlineGlyphCollection<'a>,
pub size: Size,
pub coords: &'a [NormalizedCoord],
}
impl<'a> HintingKey<'a> {
fn new_instance(&self) -> Option<HintingInstance> {
HintingInstance::new(self.outlines, self.size, self.coords, HINTING_MODE).ok()
}
}
const HINTING_MODE: HintingMode = HintingMode::Smooth {
lcd_subpixel: Some(LcdLayout::Horizontal),
preserve_linear_metrics: true,
};
#[derive(Default)]
pub(super) struct HintingCache {
// Split caches for glyf/cff because the instance type can reuse
// internal memory when reconfigured for the same format.
glyf_entries: Vec<HintingEntry>,
cff_entries: Vec<HintingEntry>,
serial: u64,
}
impl HintingCache {
pub(super) fn get(&mut self, key: &HintingKey) -> Option<&HintingInstance> {
let entries = match key.outlines.format()? {
OutlineGlyphFormat::Glyf => &mut self.glyf_entries,
OutlineGlyphFormat::Cff | OutlineGlyphFormat::Cff2 => &mut self.cff_entries,
};
let (entry_ix, is_current) = find_hinting_entry(entries, key)?;
let entry = entries.get_mut(entry_ix)?;
self.serial += 1;
entry.serial = self.serial;
if !is_current {
entry.id = key.id;
entry
.instance
.reconfigure(key.outlines, key.size, key.coords, HINTING_MODE)
.ok()?;
}
Some(&entry.instance)
}
}
struct HintingEntry {
id: [u64; 2],
instance: HintingInstance,
serial: u64,
}
fn find_hinting_entry(entries: &mut Vec<HintingEntry>, key: &HintingKey) -> Option<(usize, bool)> {
let mut found_serial = u64::MAX;
let mut found_index = 0;
for (ix, entry) in entries.iter().enumerate() {
if entry.id == key.id
&& entry.instance.size() == key.size
&& entry.instance.location().coords() == key.coords
{
return Some((ix, true));
}
if entry.serial < found_serial {
found_serial = entry.serial;
found_index = ix;
}
}
if entries.len() < MAX_CACHED_HINT_INSTANCES {
let instance = key.new_instance()?;
let ix = entries.len();
entries.push(HintingEntry {
id: key.id,
instance,
serial: 0,
});
Some((ix, true))
} else {
Some((found_index, false))
}
}

52
vendor/swash/src/scale/image.rs vendored Normal file
View File

@@ -0,0 +1,52 @@
/*!
Rendered glyph image.
*/
use super::Source;
use alloc::vec::Vec;
use zeno::Placement;
/// Content of a scaled glyph image.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Content {
/// 8-bit alpha mask.
Mask,
/// 32-bit RGBA subpixel mask.
SubpixelMask,
/// 32-bit RGBA bitmap.
Color,
}
impl Default for Content {
fn default() -> Self {
Self::Mask
}
}
/// Scaled glyph image.
#[derive(Clone, Default)]
pub struct Image {
/// Source of the image.
pub source: Source,
/// Content of the image.
pub content: Content,
/// Offset and size of the image.
pub placement: Placement,
/// Raw image data.
pub data: Vec<u8>,
}
impl Image {
/// Creates a new empty scaled image.
pub fn new() -> Self {
Self::default()
}
/// Resets the image to a default state.
pub fn clear(&mut self) {
self.source = Source::default();
self.content = Content::default();
self.placement = Placement::default();
self.data.clear();
}
}

993
vendor/swash/src/scale/mod.rs vendored Normal file
View File

@@ -0,0 +1,993 @@
/*!
Scaling, hinting and rasterization of visual glyph representations.
Scaling is the process of generating an appropriately sized visual
representation of a glyph. The scaler can produce rendered glyph
[images](Image) from outlines, layered color outlines and embedded
bitmaps. Alternatively, you can request raw, optionally hinted
[outlines](Outline) that can then be further processed by [zeno] or
fed into other crates like [lyon](https://github.com/nical/lyon) or
[pathfinder](https://github.com/servo/pathfinder) for tessellation and
GPU rendering.
# Building the scaler
All scaling in this crate takes place within the purview of a
[`ScaleContext`]. This opaque struct manages internal LRU caches and scratch
buffers that are necessary for the scaling process. Generally, you'll
want to keep an instance with your glyph cache, or if doing multithreaded
glyph rasterization, one instance per thread.
The only method available on the context is [`builder`](ScaleContext::builder)
which takes a type that can be converted into a [`FontRef`] as an argument
and produces a [`ScalerBuilder`] that provides options for configuring and
building a [`Scaler`].
Here, we'll create a context and build a scaler for a size of 14px with
hinting enabled:
```
# use swash::{FontRef, CacheKey, scale::*};
# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
// let font = ...;
let mut context = ScaleContext::new();
let mut scaler = context.builder(font)
.size(14.)
.hint(true)
.build();
```
You can specify variation settings by calling the [`variations`](ScalerBuilder::variations)
method with an iterator that yields a sequence of values that are convertible
to [`Setting<f32>`]. Tuples of (&str, f32) will work in a pinch. For example,
you can request a variation of the weight axis like this:
```
# use swash::{FontRef, CacheKey, scale::*};
# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
// let font = ...;
let mut context = ScaleContext::new();
let mut scaler = context.builder(font)
.size(14.)
.hint(true)
.variations(&[("wght", 520.5)])
.build();
```
Alternatively, you can specify variations using the
[`normalized_coords`](ScalerBuilder::normalized_coords) method which takes an iterator
that yields [`NormalizedCoord`]s (a type alias for `i16` which is a fixed point value
in 2.14 format). This method is faster than specifying variations by tag and value, but
the difference is likely negligible outside of microbenchmarks. The real advantage
is that a sequence of `i16` is more compact and easier to fold into a key in a glyph
cache. You can compute these normalized coordinates by using the
[`Variation::normalize`](crate::Variation::normalize) method for each available axis in
the font. The best strategy, however, is to simply capture these during shaping with
the [`Shaper::normalized_coords`](crate::shape::Shaper::normalized_coords) method which
will have already computed them for you.
See [`ScalerBuilder`] for available options and default values.
# Outlines and bitmaps
The [`Scaler`] struct essentially provides direct access to the outlines and embedded
bitmaps that are available in the font. In the case of outlines, it can produce the
raw outline in font units or an optionally hinted, scaled outline. For example, to
extract the raw outline for the letter 'Q':
```
# use swash::{FontRef, CacheKey, scale::*};
# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
// let font = ...;
let mut context = ScaleContext::new();
let mut scaler = context.builder(font).build();
let glyph_id = font.charmap().map('Q');
let outline = scaler.scale_outline(glyph_id);
```
For the same, but hinted at 12px:
```
# use swash::{FontRef, CacheKey, scale::*};
# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
// let font = ...;
let mut context = ScaleContext::new();
let mut scaler = context.builder(font)
.hint(true)
.size(12.)
.build();
let glyph_id = font.charmap().map('Q');
let outline = scaler.scale_outline(glyph_id);
```
The [`scale_outline`](Scaler::scale_outline) method returns an [`Outline`] wrapped
in an option. It will return `None` if an outline was not available or if there was
an error during the scaling process. Note that
[`scale_color_outline`](Scaler::scale_color_outline) can be used to access layered
color outlines such as those included in the Microsoft _Segoe UI Emoji_ font. Finally,
the `_into` variants of these methods ([`scale_outline_into`](Scaler::scale_outline_into)
and [`scale_color_outline_into`](Scaler::scale_color_outline_into)) will return
their results in a previously allocated outline avoiding the extra allocations.
Similar to outlines, bitmaps can be retrieved with the [`scale_bitmap`](Scaler::scale_bitmap)
and [`scale_color_bitmap`](Scaler::scale_color_bitmap) for alpha and color bitmaps,
respectively. These methods return an [`Image`] wrapped in an option. The associated
`_into` variants are also available.
Unlike outlines, bitmaps are available in [`strike`](crate::BitmapStrike)s of various sizes.
When requesting a bitmap, you specify the strategy for strike selection using the
[`StrikeWith`] enum.
For example, if we want the largest available unscaled image for the fire emoji:
```
# use swash::{FontRef, CacheKey, scale::*};
# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
// let font = ...;
let mut context = ScaleContext::new();
let mut scaler = context.builder(font).build();
let glyph_id = font.charmap().map('🔥');
let image = scaler.scale_color_bitmap(glyph_id, StrikeWith::LargestSize);
```
Or, to produce a scaled image for a size of 18px:
```
# use swash::{FontRef, CacheKey, scale::*};
# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
// let font = ...;
let mut context = ScaleContext::new();
let mut scaler = context.builder(font)
.size(18.)
.build();
let glyph_id = font.charmap().map('🔥');
let image = scaler.scale_color_bitmap(glyph_id, StrikeWith::BestFit);
```
This will select the best strike for the requested size and return
a bitmap that is scaled appropriately for an 18px run of text.
Alpha bitmaps should generally be avoided unless you're rendering small East
Asian text where these are sometimes still preferred over scalable outlines. In
this case, you should only use [`StrikeWith::ExactSize`] to select the strike,
falling back to an outline if a bitmap is unavailable.
# Rendering
In the general case of text rendering, you'll likely not care about the specific
details of outlines or bitmaps and will simply want an appropriately sized
image that represents your glyph. For this purpose, you'll want to use the
[`Render`] struct which is a builder that provides options for rendering an image.
This struct is constructed with a slice of [`Source`]s in priority order and
will iterate through them until it finds one that satisfies the request. Typically,
you'll want to use the following order:
```
# use swash::scale::*;
Render::new(&[
// Color outline with the first palette
Source::ColorOutline(0),
// Color bitmap with best fit selection mode
Source::ColorBitmap(StrikeWith::BestFit),
// Standard scalable outline
Source::Outline,
]);
```
The [`Render`] struct offers several options that control rasterization of
outlines such as [`format`](Render::format) for selecting a subpixel rendering mode,
[`offset`](Render::offset) for applying fractional positioning, and others. See the
struct documentation for detail.
After selecting your options, call the [`render`](Render::render) method, passing your
configured [`Scaler`] and the requested glyph identifier to produce an [`Image`].
Let's put it all together by writing a simple function that will render subpixel glyphs
with fractional positioning:
```
# use swash::{scale::{*, image::Image}, FontRef, GlyphId};
fn render_glyph(
context: &mut ScaleContext,
font: &FontRef,
size: f32,
hint: bool,
glyph_id: GlyphId,
x: f32,
y: f32,
) -> Option<Image> {
use zeno::{Format, Vector};
// Build the scaler
let mut scaler = context.builder(*font).size(size).hint(hint).build();
// Compute the fractional offset-- you'll likely want to quantize this
// in a real renderer
let offset = Vector::new(x.fract(), y.fract());
// Select our source order
Render::new(&[
Source::ColorOutline(0),
Source::ColorBitmap(StrikeWith::BestFit),
Source::Outline,
])
// Select a subpixel format
.format(Format::Subpixel)
// Apply the fractional offset
.offset(offset)
// Render the image
.render(&mut scaler, glyph_id)
}
```
Note that rendering also takes care of correctly scaling, rasterizing and
compositing layered color outlines for us.
There are other options available for emboldening, transforming with an
affine matrix, and applying path effects. See the methods on [`Render`] for
more detail.
*/
pub mod image;
pub mod outline;
mod bitmap;
mod color;
mod hinting_cache;
mod proxy;
use hinting_cache::HintingCache;
use image::*;
use outline::*;
use skrifa::{
instance::{NormalizedCoord as SkrifaNormalizedCoord, Size as SkrifaSize},
outline::OutlineGlyphCollection,
GlyphId as SkrifaGlyphId, MetadataProvider,
};
use super::internal;
use super::{cache::FontCache, setting::Setting, FontRef, GlyphId, NormalizedCoord};
use alloc::vec::Vec;
use core::borrow::Borrow;
#[cfg(all(feature = "libm", feature = "render"))]
use core_maths::CoreFloat;
use proxy::*;
use zeno::Placement;
#[cfg(feature = "render")]
use zeno::{Format, Mask, Origin, Point, Scratch, Style, Transform, Vector};
pub(crate) use bitmap::decode_png;
/// Index of a color palette.
pub type PaletteIndex = u16;
/// Index of a bitmap strike.
pub type StrikeIndex = u32;
/// Bitmap strike selection mode.
#[derive(Copy, Clone, Debug)]
pub enum StrikeWith {
/// Load a bitmap only if the exact size is available.
ExactSize,
/// Load a bitmap of the best available size.
BestFit,
/// Loads a bitmap of the largest size available.
LargestSize,
/// Load a bitmap from the specified strike.
Index(StrikeIndex),
}
/// Glyph sources for the renderer.
#[derive(Copy, Clone, Debug)]
pub enum Source {
/// Scalable outlines.
Outline,
/// Layered color scalable outlines.
ColorOutline(PaletteIndex),
/// Embedded alpha bitmaps.
Bitmap(StrikeWith),
/// Embedded color bitmaps.
ColorBitmap(StrikeWith),
}
impl Default for Source {
fn default() -> Self {
Self::Outline
}
}
/// Context that manages caches and scratch buffers for scaling.
///
/// See the module level [documentation](index.html#building-the-scaler) for detail.
pub struct ScaleContext {
fonts: FontCache<ScalerProxy>,
state: State,
hinting_cache: HintingCache,
coords: Vec<SkrifaNormalizedCoord>,
}
struct State {
scratch0: Vec<u8>,
scratch1: Vec<u8>,
outline: Outline,
#[cfg(feature = "render")]
rcx: Scratch,
}
impl ScaleContext {
/// Creates a new scaling context.
pub fn new() -> Self {
Self::with_max_entries(8)
}
/// Creates a new scaling context with the specified maximum number of
/// cache entries.
pub fn with_max_entries(max_entries: usize) -> Self {
let max_entries = max_entries.clamp(1, 64);
Self {
fonts: FontCache::new(max_entries),
state: State {
scratch0: Vec::new(),
scratch1: Vec::new(),
outline: Outline::new(),
#[cfg(feature = "render")]
rcx: Scratch::new(),
},
hinting_cache: HintingCache::default(),
coords: Vec::new(),
}
}
/// Creates a new builder for constructing a scaler with this context
/// and the specified font.
pub fn builder<'a>(&'a mut self, font: impl Into<FontRef<'a>>) -> ScalerBuilder<'a> {
ScalerBuilder::new(self, font, None)
}
/// Creates a new builder for constructing a scaler with this context,
/// specified font and a custom unique identifier.
pub fn builder_with_id<'a>(
&'a mut self,
font: impl Into<FontRef<'a>>,
id: [u64; 2],
) -> ScalerBuilder<'a> {
ScalerBuilder::new(self, font, Some(id))
}
}
impl Default for ScaleContext {
fn default() -> Self {
Self::new()
}
}
/// Builder for configuring a scaler.
pub struct ScalerBuilder<'a> {
state: &'a mut State,
hinting_cache: &'a mut HintingCache,
font: FontRef<'a>,
outlines: Option<OutlineGlyphCollection<'a>>,
proxy: &'a ScalerProxy,
id: [u64; 2],
coords: &'a mut Vec<SkrifaNormalizedCoord>,
size: f32,
hint: bool,
}
impl<'a> ScalerBuilder<'a> {
fn new(
context: &'a mut ScaleContext,
font: impl Into<FontRef<'a>>,
id: Option<[u64; 2]>,
) -> Self {
let font = font.into();
let (id, proxy) = context.fonts.get(&font, id, ScalerProxy::from_font);
let skrifa_font = if font.offset == 0 {
skrifa::FontRef::new(font.data).ok()
} else {
// TODO: make this faster
let index = crate::FontDataRef::new(font.data)
.and_then(|font_data| font_data.fonts().position(|f| f.offset == font.offset));
index.and_then(|index| skrifa::FontRef::from_index(font.data, index as u32).ok())
};
let outlines = skrifa_font.map(|font_ref| font_ref.outline_glyphs());
Self {
state: &mut context.state,
hinting_cache: &mut context.hinting_cache,
font,
outlines,
proxy,
id,
coords: &mut context.coords,
size: 0.,
hint: false,
}
}
/// Specifies the font size in pixels per em. The default value is `0` which will produce
/// unscaled glyphs in original font units.
pub fn size(mut self, ppem: f32) -> Self {
self.size = ppem.max(0.);
self
}
/// Specifies whether to apply hinting to outlines. The default value is `false`.
pub fn hint(mut self, yes: bool) -> Self {
self.hint = yes;
self
}
/// Adds variation settings to the scaler.
pub fn variations<I>(self, settings: I) -> Self
where
I: IntoIterator,
I::Item: Into<Setting<f32>>,
{
if self.proxy.coord_count != 0 {
let vars = self.font.variations();
self.coords.resize(vars.len(), Default::default());
for setting in settings {
let setting = setting.into();
for var in vars {
if var.tag() == setting.tag {
let value = var.normalize(setting.value);
if let Some(c) = self.coords.get_mut(var.index()) {
*c = SkrifaNormalizedCoord::from_bits(value);
}
}
}
}
}
self
}
/// Specifies the variation settings in terms of normalized coordinates. This will replace
/// any previous variation settings.
pub fn normalized_coords<I>(self, coords: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<NormalizedCoord>,
{
self.coords.clear();
self.coords.extend(
coords
.into_iter()
.map(|c| SkrifaNormalizedCoord::from_bits(*c.borrow())),
);
self
}
/// Builds a scaler for the current configuration.
pub fn build(self) -> Scaler<'a> {
let upem = self.proxy.metrics.units_per_em();
let skrifa_size = if self.size != 0.0 && upem != 0 {
SkrifaSize::new(self.size)
} else {
SkrifaSize::unscaled()
};
let hinting_instance = match (self.hint, &self.outlines) {
(true, Some(outlines)) => {
let key = hinting_cache::HintingKey {
id: self.id,
outlines,
size: skrifa_size,
coords: self.coords,
};
self.hinting_cache.get(&key)
}
_ => None,
};
Scaler {
state: self.state,
font: self.font,
outlines: self.outlines,
hinting_instance,
proxy: self.proxy,
coords: &self.coords[..],
size: self.size,
skrifa_size,
}
}
}
/// Scales outline and bitmap glyphs.
///
/// See the module level [documentation](index.html#outlines-and-bitmaps) for detail.
pub struct Scaler<'a> {
state: &'a mut State,
font: FontRef<'a>,
outlines: Option<OutlineGlyphCollection<'a>>,
hinting_instance: Option<&'a skrifa::outline::HintingInstance>,
proxy: &'a ScalerProxy,
coords: &'a [SkrifaNormalizedCoord],
size: f32,
skrifa_size: SkrifaSize,
}
impl<'a> Scaler<'a> {
/// Returns true if scalable glyph outlines are available.
pub fn has_outlines(&self) -> bool {
self.outlines
.as_ref()
.map(|outlines| outlines.format().is_some())
.unwrap_or_default()
}
/// Scales an outline for the specified glyph into the provided outline.
pub fn scale_outline_into(&mut self, glyph_id: GlyphId, outline: &mut Outline) -> bool {
outline.clear();
self.scale_outline_impl(glyph_id, None, Some(outline))
}
/// Scales an outline for the specified glyph.
pub fn scale_outline(&mut self, glyph_id: GlyphId) -> Option<Outline> {
let mut outline = Outline::new();
if self.scale_outline_into(glyph_id, &mut outline) {
Some(outline)
} else {
None
}
}
/// Returns true if scalable color glyph outlines are available.
pub fn has_color_outlines(&self) -> bool {
self.proxy.color.colr != 0 && self.proxy.color.cpal != 0
}
/// Scales a color outline for the specified glyph into the provided outline.
pub fn scale_color_outline_into(&mut self, glyph_id: GlyphId, outline: &mut Outline) -> bool {
outline.clear();
if !self.has_color_outlines() {
return false;
}
let layers = match self.proxy.color.layers(self.font.data, glyph_id) {
Some(layers) => layers,
_ => return false,
};
for i in 0..layers.len() {
let layer = match layers.get(i) {
Some(layer) => layer,
_ => return false,
};
if !self.scale_outline_impl(layer.glyph_id, layer.color_index, Some(outline)) {
return false;
}
}
outline.set_color(true);
true
}
/// Scales a color outline for the specified glyph.
pub fn scale_color_outline(&mut self, glyph_id: GlyphId) -> Option<Outline> {
let mut outline = Outline::new();
if self.scale_color_outline_into(glyph_id, &mut outline) {
Some(outline)
} else {
None
}
}
fn scale_outline_impl(
&mut self,
glyph_id: GlyphId,
color_index: Option<u16>,
outline: Option<&mut Outline>,
) -> bool {
let mut outline = match outline {
Some(x) => x,
_ => &mut self.state.outline,
};
if let Some(outlines) = &self.outlines {
if let Some(glyph) = outlines.get(SkrifaGlyphId::from(glyph_id)) {
outline.begin_layer(color_index);
let settings: skrifa::outline::DrawSettings =
if let Some(hinting_instance) = &self.hinting_instance {
(*hinting_instance).into()
} else {
(
self.skrifa_size,
skrifa::instance::LocationRef::new(self.coords),
)
.into()
};
if glyph
.draw(settings, &mut OutlineWriter(&mut outline))
.is_ok()
{
outline.maybe_close();
outline.finish();
return true;
}
}
}
false
}
// Unused when render feature is disabled.
#[allow(dead_code)]
fn scale_color_outline_impl(&mut self, glyph_id: GlyphId) -> bool {
if !self.has_color_outlines() {
return false;
}
let layers = match self.proxy.color.layers(self.font.data, glyph_id) {
Some(layers) => layers,
_ => return false,
};
self.state.outline.clear();
for i in 0..layers.len() {
let layer = match layers.get(i) {
Some(layer) => layer,
_ => return false,
};
if !self.scale_outline_impl(layer.glyph_id, layer.color_index, None) {
return false;
}
}
true
}
/// Returns true if alpha bitmaps are available.
pub fn has_bitmaps(&self) -> bool {
self.proxy.bitmaps.has_alpha()
}
/// Scales a bitmap for the specified glyph and mode into the provided image.
pub fn scale_bitmap_into(
&mut self,
glyph_id: u16,
strike: StrikeWith,
image: &mut Image,
) -> bool {
self.scale_bitmap_impl(glyph_id, false, strike, image) == Some(true)
}
/// Scales a bitmap for the specified glyph and mode.
pub fn scale_bitmap(&mut self, glyph_id: u16, strike: StrikeWith) -> Option<Image> {
let mut image = Image::new();
if self.scale_bitmap_into(glyph_id, strike, &mut image) {
Some(image)
} else {
None
}
}
/// Returns true if color bitmaps are available.
pub fn has_color_bitmaps(&self) -> bool {
self.proxy.bitmaps.has_color()
}
/// Scales a color bitmap for the specified glyph and mode into the provided image.
pub fn scale_color_bitmap_into(
&mut self,
glyph_id: u16,
strike: StrikeWith,
image: &mut Image,
) -> bool {
self.scale_bitmap_impl(glyph_id, true, strike, image) == Some(true)
}
/// Scales a color bitmap for the specified glyph and mode.
pub fn scale_color_bitmap(&mut self, glyph_id: u16, strike: StrikeWith) -> Option<Image> {
let mut image = Image::new();
if self.scale_color_bitmap_into(glyph_id, strike, &mut image) {
Some(image)
} else {
None
}
}
fn scale_bitmap_impl(
&mut self,
glyph_id: GlyphId,
color: bool,
strike: StrikeWith,
image: &mut Image,
) -> Option<bool> {
image.clear();
let size = self.size;
let mut strikes = if color {
self.proxy.bitmaps.materialize_color(&self.font)
} else {
self.proxy.bitmaps.materialize_alpha(&self.font)
};
let bitmap = match strike {
StrikeWith::ExactSize => {
if self.size == 0. {
None
} else {
strikes
.find_by_exact_ppem(size as u16, glyph_id)?
.get(glyph_id)
}
}
StrikeWith::BestFit => {
if self.size == 0. {
None
} else {
strikes
.find_by_nearest_ppem(size as u16, glyph_id)?
.get(glyph_id)
}
}
StrikeWith::LargestSize => strikes.find_by_largest_ppem(glyph_id)?.get(glyph_id),
StrikeWith::Index(i) => strikes
.nth(i as usize)
.and_then(|strike| strike.get(glyph_id)),
}?;
if bitmap.ppem == 0 {
return None;
}
let (_, _, bufsize) = bitmap.scaled_size(size);
image.data.resize(bufsize, 0);
self.state.scratch0.clear();
self.state.scratch1.clear();
let mut w = bitmap.width;
let mut h = bitmap.height;
let scale = size / bitmap.ppem as f32;
image.placement = if size != 0. && scale != 1. {
self.state
.scratch0
.resize(bitmap.format.buffer_size(w, h), 0);
w = (w as f32 * scale) as u32;
h = (h as f32 * scale) as u32;
image.data.resize(bitmap.format.buffer_size(w, h), 0);
if !bitmap.decode(Some(&mut self.state.scratch1), &mut self.state.scratch0) {
return None;
}
if !bitmap::resize(
&self.state.scratch0,
bitmap.width,
bitmap.height,
bitmap.format.channels(),
&mut image.data,
w,
h,
bitmap::Filter::Mitchell,
Some(&mut self.state.scratch1),
) {
return None;
}
let left = (bitmap.left as f32 * scale) as i32;
let top = (bitmap.top as f32 * scale) as i32;
Placement {
left,
top,
width: w,
height: h,
}
} else {
image.data.resize(bitmap.format.buffer_size(w, h), 0);
if !bitmap.decode(Some(&mut self.state.scratch1), &mut image.data) {
return None;
}
Placement {
left: bitmap.left,
top: bitmap.top,
width: w,
height: h,
}
};
image.source = match color {
true => Source::ColorBitmap(strike),
false => Source::Bitmap(strike),
};
image.content = match bitmap.format.channels() {
1 => Content::Mask,
_ => Content::Color,
};
// let mut advance = bitmap.advance() as f32;
// if options.size != 0. && options.size as u16 != bitmap.ppem() {
// advance *= options.size / bitmap.ppem() as f32;
// }
Some(true)
}
}
/// Builder type for rendering a glyph into an image.
///
/// See the module level [documentation](index.html#rendering) for detail.
#[cfg(feature = "render")]
pub struct Render<'a> {
sources: &'a [Source],
format: Format,
offset: Point,
transform: Option<Transform>,
embolden: f32,
foreground: [u8; 4],
style: Style<'a>,
}
#[cfg(feature = "render")]
impl<'a> Render<'a> {
/// Creates a new builder for configuring rendering using the specified
/// prioritized list of sources.
pub fn new(sources: &'a [Source]) -> Self {
Self {
sources,
format: Format::Alpha,
offset: Point::new(0., 0.),
transform: None,
embolden: 0.,
foreground: [128, 128, 128, 255],
style: Style::default(),
}
}
/// Specifies the target format for rasterizing an outline. Default is
/// [`Format::Alpha`].
pub fn format(&mut self, format: Format) -> &mut Self {
self.format = format;
self
}
/// Specifies the path style to use when rasterizing an outline. Default is
/// [`Fill::NonZero`](zeno::Fill::NonZero).
pub fn style(&mut self, style: impl Into<Style<'a>>) -> &mut Self {
self.style = style.into();
self
}
/// Specifies an additional offset to apply when rasterizing an outline.
/// Default is `(0, 0)`.
pub fn offset(&mut self, offset: Vector) -> &mut Self {
self.offset = offset;
self
}
/// Specifies a transformation matrix to apply when rasterizing an
/// outline. Default is `None`.
pub fn transform(&mut self, transform: Option<Transform>) -> &mut Self {
self.transform = transform;
self
}
/// Specifies the strength of a faux bold transform to apply when
/// rasterizing an outline. Default is `0`.
pub fn embolden(&mut self, strength: f32) -> &mut Self {
self.embolden = strength;
self
}
/// Specifies an RGBA color to use when rasterizing layers of a color
/// outline that do not directly reference a palette color. Default is
/// `[128, 128, 128, 255]`.
pub fn default_color(&mut self, color: [u8; 4]) -> &mut Self {
self.foreground = color;
self
}
/// Renders the specified glyph using the current configuration into the
/// provided image.
pub fn render_into(&self, scaler: &mut Scaler, glyph_id: GlyphId, image: &mut Image) -> bool {
for source in self.sources {
match source {
Source::Outline => {
if !scaler.has_outlines() {
continue;
}
scaler.state.outline.clear();
if scaler.scale_outline_impl(glyph_id, None, None) {
let state = &mut scaler.state;
let rcx = &mut state.rcx;
let outline = &mut state.outline;
if self.embolden != 0. {
outline.embolden(self.embolden, self.embolden);
}
if let Some(transform) = &self.transform {
outline.transform(transform);
}
let placement = Mask::with_scratch(outline.path(), rcx)
.format(self.format)
.origin(Origin::BottomLeft)
.style(self.style)
.offset(self.offset)
.render_offset(self.offset)
.inspect(|fmt, w, h| {
image.data.resize(fmt.buffer_size(w, h), 0);
})
.render_into(&mut image.data[..], None);
image.placement = placement;
image.content = if self.format == Format::Alpha {
Content::Mask
} else {
Content::SubpixelMask
};
image.source = Source::Outline;
return true;
}
}
Source::ColorOutline(palette_index) => {
if !scaler.has_color_outlines() {
continue;
}
scaler.state.outline.clear();
if scaler.scale_color_outline_impl(glyph_id) {
let font = &scaler.font;
let proxy = &scaler.proxy;
let state = &mut scaler.state;
let scratch = &mut state.scratch0;
let rcx = &mut state.rcx;
let outline = &mut state.outline;
// Cool effect, but probably not generally desirable.
// Maybe expose a separate option?
// if self.embolden != 0. {
// outline.embolden(self.embolden, self.embolden);
// }
if let Some(transform) = &self.transform {
outline.transform(transform);
}
let palette = proxy.color.palette(font, *palette_index);
let total_bounds = outline.bounds();
// need to take offset into account when placing glyph
let base_x = (total_bounds.min.x + self.offset.x).floor() as i32;
let base_y = (total_bounds.min.y + self.offset.y).ceil() as i32;
let base_w = total_bounds.width().ceil() as u32;
let base_h = total_bounds.height().ceil() as u32;
image.data.resize((base_w * base_h * 4) as usize, 0);
image.placement.left = base_x;
image.placement.top = base_h as i32 + base_y;
image.placement.width = total_bounds.width().ceil() as u32;
image.placement.height = total_bounds.height().ceil() as u32;
let mut ok = true;
for i in 0..outline.len() {
let layer = match outline.get(i) {
Some(layer) => layer,
_ => {
ok = false;
break;
}
};
scratch.clear();
let placement = Mask::with_scratch(layer.path(), rcx)
.origin(Origin::BottomLeft)
.style(self.style)
.offset(self.offset)
.render_offset(self.offset)
.inspect(|fmt, w, h| {
scratch.resize(fmt.buffer_size(w, h), 0);
})
.render_into(&mut scratch[..], None);
let color = layer
.color_index()
.and_then(|i| palette.map(|p| p.get(i)))
.unwrap_or(self.foreground);
bitmap::blit(
&scratch[..],
placement.width,
placement.height,
placement.left.wrapping_sub(base_x),
(base_h as i32 + base_y).wrapping_sub(placement.top),
color,
&mut image.data,
base_w,
base_h,
);
}
if ok {
image.source = Source::ColorOutline(*palette_index);
image.content = Content::Color;
return true;
}
}
}
Source::Bitmap(mode) => {
if !scaler.has_bitmaps() {
continue;
}
if scaler.scale_bitmap_into(glyph_id, *mode, image) {
return true;
}
}
Source::ColorBitmap(mode) => {
if !scaler.has_color_bitmaps() {
continue;
}
if scaler.scale_color_bitmap_into(glyph_id, *mode, image) {
return true;
}
}
}
}
false
}
/// Renders the specified glyph using the current configuration.
pub fn render(&self, scaler: &mut Scaler, glyph_id: GlyphId) -> Option<Image> {
let mut image = Image::new();
if self.render_into(scaler, glyph_id, &mut image) {
Some(image)
} else {
None
}
}
}

427
vendor/swash/src/scale/outline.rs vendored Normal file
View File

@@ -0,0 +1,427 @@
/*!
Glyph outline.
*/
use alloc::vec::Vec;
use zeno::{Bounds, PathData, Point, Transform, Verb};
/// Scaled glyph outline represented as a collection of layers and a sequence
/// of points and verbs.
#[derive(Clone, Default)]
pub struct Outline {
layers: Vec<LayerData>,
points: Vec<Point>,
verbs: Vec<Verb>,
is_color: bool,
}
impl Outline {
/// Creates a new empty outline.
pub fn new() -> Self {
Self::default()
}
/// Returns true if the outline has color layers.
pub fn is_color(&self) -> bool {
self.is_color
}
/// Returns the number of layers in the outline.
pub fn len(&self) -> usize {
self.layers.len()
}
/// Returns true if there are no layers in the outline.
pub fn is_empty(&self) -> bool {
self.layers.is_empty()
}
/// Returns a reference to the layer at the specified index.
pub fn get<'a>(&'a self, index: usize) -> Option<Layer<'a>> {
let data = self.layers.get(index)?;
let points = self.points.get(data.points.0..data.points.1)?;
let verbs = self.verbs.get(data.verbs.0..data.verbs.1)?;
let color_index = data.color_index;
Some(Layer {
points,
verbs,
color_index,
})
}
/// Returns a mutable reference to the layer at the specified index.
pub fn get_mut<'a>(&'a mut self, index: usize) -> Option<LayerMut<'a>> {
let data = self.layers.get(index)?;
let points = self.points.get_mut(data.points.0..data.points.1)?;
let verbs = self.verbs.get(data.verbs.0..data.verbs.1)?;
let color_index = data.color_index;
Some(LayerMut {
points,
verbs,
color_index,
})
}
/// Returns a reference to the sequence of points in the outline.
pub fn points(&self) -> &[Point] {
&self.points
}
/// Returns a mutable reference to the sequence of points in the outline.
pub fn points_mut(&mut self) -> &mut [Point] {
&mut self.points
}
/// Returns a reference to the sequence of verbs in the outline.
pub fn verbs(&self) -> &[Verb] {
&self.verbs
}
/// Returns path data for the outline.
pub fn path(&self) -> impl PathData + '_ {
(&self.points[..], &self.verbs[..])
}
/// Computes the bounding box of the outline.
pub fn bounds(&self) -> Bounds {
Bounds::from_points(&self.points)
}
/// Transforms the outline by the specified matrix.
pub fn transform(&mut self, transform: &Transform) {
for p in &mut self.points {
*p = transform.transform_point(*p);
}
}
/// Applies a faux bold to the outline with the specified strengths in the
/// x and y directions.
pub fn embolden(&mut self, x_strength: f32, y_strength: f32) {
for i in 0..self.len() {
if let Some(mut layer) = self.get_mut(i) {
layer.embolden(x_strength, y_strength);
}
}
}
/// Clears the outline.
pub fn clear(&mut self) {
self.points.clear();
self.verbs.clear();
self.layers.clear();
self.is_color = false;
}
}
/// Reference to a layer in a scaled outline.
#[derive(Copy, Clone)]
pub struct Layer<'a> {
points: &'a [Point],
verbs: &'a [Verb],
color_index: Option<u16>,
}
impl<'a> Layer<'a> {
/// Returns the sequence of points for the layer.
pub fn points(&self) -> &'a [Point] {
self.points
}
/// Returns the sequence of verbs for the layer.
pub fn verbs(&self) -> &'a [Verb] {
self.verbs
}
/// Returns path data for the layer.
pub fn path(&self) -> impl PathData + 'a {
(self.points(), self.verbs())
}
/// Computes the bounding box of the layer.
pub fn bounds(&self) -> Bounds {
Bounds::from_points(self.points())
}
/// Returns the color index for the layer.
pub fn color_index(&self) -> Option<u16> {
self.color_index
}
}
/// Mutable reference to a layer in a scaled outline.
pub struct LayerMut<'a> {
points: &'a mut [Point],
verbs: &'a [Verb],
color_index: Option<u16>,
}
impl<'a> LayerMut<'a> {
/// Returns the sequence of points for the layer.
pub fn points(&'a self) -> &'a [Point] {
self.points
}
/// Returns a mutable reference the sequence of points for the layer.
pub fn points_mut(&'a mut self) -> &'a mut [Point] {
&mut *self.points
}
/// Returns the sequence of verbs for the layer.
pub fn verbs(&self) -> &'a [Verb] {
self.verbs
}
/// Returns path data for the layer.
pub fn path(&'a self) -> impl PathData + 'a {
(self.points(), self.verbs())
}
/// Computes the bounding box of the layer.
pub fn bounds(&self) -> Bounds {
Bounds::from_points(self.points())
}
/// Returns the color index for the layer.
pub fn color_index(&self) -> Option<u16> {
self.color_index
}
/// Transforms this layer by the specified matrix.
pub fn transform(&'a mut self, transform: &Transform) {
for p in self.points.iter_mut() {
*p = transform.transform_point(*p);
}
}
/// Applies a faux bold to this layer with the specified strengths in the
/// x and y directions.
pub fn embolden(&mut self, x_strength: f32, y_strength: f32) {
let mut point_start = 0;
let mut pos = 0;
let winding = compute_winding(self.points);
for verb in self.verbs {
match verb {
Verb::MoveTo | Verb::Close => {
if let Some(points) = self.points.get_mut(point_start..pos) {
if !points.is_empty() {
embolden(points, winding, x_strength, y_strength);
}
point_start = pos;
if *verb == Verb::MoveTo {
pos += 1;
}
} else {
return;
}
}
Verb::LineTo => pos += 1,
Verb::QuadTo => pos += 2,
Verb::CurveTo => pos += 3,
}
}
if pos > point_start {
if let Some(points) = self.points.get_mut(point_start..pos) {
embolden(points, winding, x_strength, y_strength);
}
}
}
}
#[derive(Copy, Clone, Default)]
struct LayerData {
points: (usize, usize),
verbs: (usize, usize),
color_index: Option<u16>,
}
impl Outline {
pub(super) fn set_color(&mut self, color: bool) {
self.is_color = color;
}
pub(super) fn move_to(&mut self, p: Point) {
self.maybe_close();
self.points.push(p);
self.verbs.push(Verb::MoveTo);
}
pub(super) fn line_to(&mut self, p: Point) {
self.points.push(p);
self.verbs.push(Verb::LineTo);
}
pub(super) fn quad_to(&mut self, p0: Point, p1: Point) {
self.points.push(p0);
self.points.push(p1);
self.verbs.push(Verb::QuadTo);
}
pub(super) fn curve_to(&mut self, p0: Point, p1: Point, p2: Point) {
self.points.push(p0);
self.points.push(p1);
self.points.push(p2);
self.verbs.push(Verb::CurveTo);
}
pub(super) fn close(&mut self) {
self.verbs.push(Verb::Close);
}
pub(super) fn maybe_close(&mut self) {
if !self.verbs.is_empty() && self.verbs.last() != Some(&Verb::Close) {
self.close();
}
}
pub(super) fn begin_layer(&mut self, color_index: Option<u16>) {
let points_end = self.points.len();
let verbs_end = self.verbs.len();
if let Some(last) = self.layers.last_mut() {
last.points.1 = points_end;
last.verbs.1 = verbs_end;
}
self.layers.push(LayerData {
points: (points_end, points_end),
verbs: (verbs_end, verbs_end),
color_index,
});
}
pub(super) fn finish(&mut self) {
let points_end = self.points.len();
let verbs_end = self.verbs.len();
if let Some(last) = self.layers.last_mut() {
last.points.1 = points_end;
last.verbs.1 = verbs_end;
} else {
self.layers.push(LayerData {
points: (0, points_end),
verbs: (0, verbs_end),
color_index: None,
});
}
}
}
fn embolden(points: &mut [Point], winding: u8, x_strength: f32, y_strength: f32) {
if points.is_empty() {
return;
}
let last = points.len() - 1;
let mut i = last;
let mut j = 0;
let mut k = !0;
let mut out_len;
let mut in_len = 0.;
let mut anchor_len = 0.;
let mut anchor = Point::ZERO;
let mut out;
let mut in_ = Point::ZERO;
while j != i && i != k {
if j != k {
out = points[j] - points[i];
out_len = out.length();
if out_len == 0. {
j = if j < last { j + 1 } else { 0 };
continue;
} else {
let s = 1. / out_len;
out.x *= s;
out.y *= s;
}
} else {
out = anchor;
out_len = anchor_len;
}
if in_len != 0. {
if k == !0 {
k = i;
anchor = in_;
anchor_len = in_len;
}
let mut d = (in_.x * out.x) + (in_.y * out.y);
let shift = if d > -0.9396 {
d += 1.;
let mut sx = in_.y + out.y;
let mut sy = in_.x + out.x;
if winding == 0 {
sx = -sx;
} else {
sy = -sy;
}
let mut q = (out.x * in_.y) - (out.y * in_.x);
if winding == 0 {
q = -q;
}
let l = in_len.min(out_len);
if x_strength * q <= l * d {
sx = sx * x_strength / d;
} else {
sx = sx * l / q;
}
if y_strength * q <= l * d {
sy = sy * y_strength / d;
} else {
sy = sy * l / q;
}
Point::new(sx, sy)
} else {
Point::ZERO
};
while i != j {
points[i].x += x_strength + shift.x;
points[i].y += y_strength + shift.y;
i = if i < last { i + 1 } else { 0 };
}
} else {
i = j;
}
in_ = out;
in_len = out_len;
j = if j < last { j + 1 } else { 0 };
}
}
fn compute_winding(points: &[Point]) -> u8 {
if points.is_empty() {
return 0;
}
let mut area = 0.;
let last = points.len() - 1;
let mut prev = points[last];
for cur in points[0..=last].iter() {
area += (cur.y - prev.y) * (cur.x + prev.x);
prev = *cur;
}
if area > 0. {
1
} else {
0
}
}
// The OutlineWriter wrapper allows us to make the trait implementation of OutlinePen
// private which allows us to keep skrifa as a private dependency of swash
pub(crate) struct OutlineWriter<'a>(pub &'a mut Outline);
impl skrifa::outline::OutlinePen for OutlineWriter<'_> {
fn move_to(&mut self, x: f32, y: f32) {
self.0.move_to((x, y).into());
}
fn line_to(&mut self, x: f32, y: f32) {
self.0.line_to((x, y).into());
}
fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
self.0.quad_to((cx0, cy0).into(), (x, y).into());
}
fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
self.0
.curve_to((cx0, cy0).into(), (cx1, cy1).into(), (x, y).into());
}
fn close(&mut self) {
self.0.close();
}
}

23
vendor/swash/src/scale/proxy.rs vendored Normal file
View File

@@ -0,0 +1,23 @@
use super::{
super::{metrics::MetricsProxy, strike::BitmapStrikesProxy, FontRef},
color::ColorProxy,
};
#[derive(Copy, Clone)]
pub struct ScalerProxy {
pub metrics: MetricsProxy,
pub color: ColorProxy,
pub bitmaps: BitmapStrikesProxy,
pub coord_count: u16,
}
impl ScalerProxy {
pub fn from_font(font: &FontRef) -> Self {
Self {
metrics: MetricsProxy::from_font(font),
color: ColorProxy::from_font(font),
bitmaps: BitmapStrikesProxy::from_font(font),
coord_count: font.variations().len() as u16,
}
}
}

203
vendor/swash/src/setting.rs vendored Normal file
View File

@@ -0,0 +1,203 @@
use super::{tag_from_bytes, tag_from_str_lossy, Tag};
use core::fmt;
/// Setting combining a tag and a value for features and variations.
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
pub struct Setting<T> {
/// The tag that identifies the setting.
pub tag: Tag,
/// The value for the setting.
pub value: T,
}
impl<T: fmt::Display> fmt::Display for Setting<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = self.tag.to_be_bytes();
let tag_name = core::str::from_utf8(&bytes).unwrap_or("");
write!(f, "\"{}\" {}", tag_name, self.value)
}
}
impl Setting<u16> {
/// Parses a feature setting according to the CSS grammar.
pub fn parse(s: &str) -> Option<Self> {
Self::parse_list(s).next()
}
/// Parses a comma separated list of feature settings according to the CSS
/// grammar.
pub fn parse_list(s: &str) -> impl Iterator<Item = Self> + '_ + Clone {
ParseList::new(s)
.map(|(_, tag, value_str)| {
let (ok, value) = match value_str {
"on" | "" => (true, 1),
"off" => (true, 0),
_ => match value_str.parse::<u16>() {
Ok(value) => (true, value),
_ => (false, 0),
},
};
(ok, tag, value)
})
.take_while(|(ok, _, _)| *ok)
.map(|(_, tag, value)| Self { tag, value })
}
}
impl Setting<f32> {
/// Parses a variation setting according to the CSS grammar.
pub fn parse(s: &str) -> Option<Self> {
Self::parse_list(s).next()
}
/// Parses a comma separated list of variation settings according to the
/// CSS grammar.
pub fn parse_list(s: &str) -> impl Iterator<Item = Self> + '_ + Clone {
ParseList::new(s)
.map(|(_, tag, value_str)| {
let (ok, value) = match value_str.parse::<f32>() {
Ok(value) => (true, value),
_ => (false, 0.),
};
(ok, tag, value)
})
.take_while(|(ok, _, _)| *ok)
.map(|(_, tag, value)| Self { tag, value })
}
}
impl<T> From<(Tag, T)> for Setting<T> {
fn from(v: (Tag, T)) -> Self {
Self {
tag: v.0,
value: v.1,
}
}
}
impl<T: Copy> From<&(Tag, T)> for Setting<T> {
fn from(v: &(Tag, T)) -> Self {
Self {
tag: v.0,
value: v.1,
}
}
}
impl<T: Copy> From<&([u8; 4], T)> for Setting<T> {
fn from(v: &([u8; 4], T)) -> Self {
Self {
tag: tag_from_bytes(&v.0),
value: v.1,
}
}
}
impl<T: Copy> From<&(&[u8; 4], T)> for Setting<T> {
fn from(v: &(&[u8; 4], T)) -> Self {
Self {
tag: tag_from_bytes(v.0),
value: v.1,
}
}
}
impl<T> From<(&str, T)> for Setting<T> {
fn from(v: (&str, T)) -> Self {
Self {
tag: tag_from_str_lossy(v.0),
value: v.1,
}
}
}
impl<T: Copy> From<&(&str, T)> for Setting<T> {
fn from(v: &(&str, T)) -> Self {
Self {
tag: tag_from_str_lossy(v.0),
value: v.1,
}
}
}
#[derive(Clone)]
struct ParseList<'a> {
source: &'a [u8],
len: usize,
pos: usize,
}
impl<'a> ParseList<'a> {
fn new(source: &'a str) -> Self {
Self {
source: source.as_bytes(),
len: source.len(),
pos: 0,
}
}
}
impl<'a> Iterator for ParseList<'a> {
type Item = (usize, Tag, &'a str);
fn next(&mut self) -> Option<Self::Item> {
let mut pos = self.pos;
while pos < self.len && {
let ch = self.source[pos];
ch.is_ascii_whitespace() || ch == b','
} {
pos += 1;
}
self.pos = pos;
if pos >= self.len {
return None;
}
let first = self.source[pos];
let mut start = pos;
let quote = match first {
b'"' | b'\'' => {
pos += 1;
start += 1;
first
}
_ => return None,
};
let mut tag_str = None;
while pos < self.len {
if self.source[pos] == quote {
tag_str = core::str::from_utf8(self.source.get(start..pos)?).ok();
pos += 1;
break;
}
pos += 1;
}
self.pos = pos;
let tag_str = tag_str?;
if tag_str.len() != 4 || !tag_str.is_ascii() {
return None;
}
let tag = tag_from_str_lossy(tag_str);
while pos < self.len {
if !self.source[pos].is_ascii_whitespace() {
break;
}
pos += 1;
}
self.pos = pos;
start = pos;
let mut end = start;
while pos < self.len {
if self.source[pos] == b',' {
pos += 1;
break;
}
pos += 1;
end += 1;
}
let value = core::str::from_utf8(self.source.get(start..end)?)
.ok()?
.trim();
self.pos = pos;
Some((pos, tag, value))
}
}

412
vendor/swash/src/shape/aat.rs vendored Normal file
View File

@@ -0,0 +1,412 @@
use super::buffer::*;
use super::internal::aat::*;
pub fn apply_morx(
data: &[u8],
morx: u32,
buffer: &mut Buffer,
selectors: &[(u16, u16)],
) -> Option<()> {
use morx::*;
let max_ops = buffer.glyphs.len() * 16;
for chain in chains(data, morx) {
let mut ops = 0;
let mut flags = chain.default_flags();
if !selectors.is_empty() {
for feature in chain.features() {
let key = (feature.selector, feature.setting_selector);
if selectors.binary_search(&key).is_ok() {
flags = flags & feature.disable_flags | feature.enable_flags;
}
}
}
for (_i, subtable) in chain.subtables().enumerate() {
if subtable.flags() & flags == 0 {
// if TRACE {
// println!(" <SKIP chain subtable {}>", i);
// }
continue;
} else {
// if TRACE {
// println!(" <chain subtable {} order: {:?}>", i, subtable.order());
// }
}
let reverse = subtable.should_reverse(buffer.is_rtl);
buffer.ensure_order(reverse);
let kind = match subtable.kind() {
Some(kind) => kind,
_ => continue,
};
match kind {
SubtableKind::Rearrangement(t) => {
//println!(".. rearrangement");
let mut i = 0;
let mut state = RearrangementState::new();
while i < buffer.glyphs.len() && ops < max_ops {
let g = buffer.glyphs[i].id;
match t.next(&mut state, i, g, false, |r| {
// if TRACE {
// println!("Rearrange!");
// }
r.apply(&mut buffer.glyphs);
Some(())
}) {
Some(advance) => i += advance,
None => break,
}
ops += 1;
}
// Apply END_OF_TEXT state
t.next(&mut state, i, 0, true, |r| {
r.apply(&mut buffer.glyphs);
Some(())
});
}
SubtableKind::Contextual(t) => {
//println!(".. contextual");
let mut state = ContextualState::new();
for i in 0..buffer.glyphs.len() {
let g = buffer.glyphs[i].id;
t.next(&mut state, i, g, false, |i, g| {
buffer.substitute(i, g);
Some(())
});
}
// Apply END_OF_TEXT state
if let Some(last_id) = buffer.glyphs.last().map(|g| g.id) {
t.next(
&mut state,
buffer.glyphs.len() - 1,
last_id,
true,
|i, g| {
buffer.substitute(i, g);
Some(())
},
);
}
}
SubtableKind::NonContextual(t) => {
//println!(".. non-contextual");
for (_i, g) in buffer.glyphs.iter_mut().enumerate() {
if let Some(s) = t.substitute(g.id) {
// if TRACE {
// println!("NonContextual[{}] {} -> {}", i, g.id, s);
// }
g.id = s;
}
}
}
SubtableKind::Ligature(t) => {
//println!(".. ligature");
let mut i = 0;
let mut state = LigatureState::new();
while i < buffer.glyphs.len() && ops < max_ops {
let g = buffer.glyphs[i].id;
let f = |i, g, comps: &[usize]| {
buffer.substitute_ligature(i, g, comps);
Some(())
};
if t.next(&mut state, i, g, false, f).is_none() {
break;
}
i += 1;
ops += 1;
}
// Apply END_OF_TEXT state
t.next(
&mut state,
buffer.glyphs.len().saturating_sub(1),
0,
true,
|i, g, comps| {
buffer.substitute_ligature(i, g, comps);
Some(())
},
);
}
SubtableKind::Insertion(t) => {
//println!(".. insertion");
let mut i = 0;
let mut state = InsertionState::new();
while i < buffer.glyphs.len() && ops < max_ops {
let g = buffer.glyphs[i].id;
match t.next(&mut state, i, g, false, |i, array| {
// if TRACE {
// let rep = array.iter().collect::<Vec<_>>();
// println!("Insert[{}] {:?}", i, &rep);
// }
buffer.multiply(i, array.len());
let start = i;
let end = start + array.len();
for (g, s) in buffer.glyphs[start..end].iter_mut().zip(array.iter()) {
g.id = s;
g.flags = 0;
}
Some(())
}) {
Some(advance) => i += advance,
None => break,
}
ops += 1;
}
// Apply END_OF_TEXT state
t.next(
&mut state,
buffer.glyphs.len().saturating_sub(1),
0,
true,
|i, array| {
buffer.multiply(i, array.len());
let start = i;
let end = start + array.len();
for (g, s) in buffer.glyphs[start..end].iter_mut().zip(array.iter()) {
g.id = s;
g.flags = 0;
}
Some(())
},
);
}
}
}
}
buffer.ensure_order(false);
Some(())
}
pub fn apply_kerx(
data: &[u8],
kerx: u32,
ankr: u32,
buffer: &mut Buffer,
disable_kern: bool,
) -> Option<()> {
use kerx::*;
for (_i, subtable) in subtables(data, kerx, ankr).enumerate() {
// if TRACE {
// println!(" <kerx subtable {}>", i);
// }
let reverse = subtable.should_reverse(buffer.is_rtl);
buffer.ensure_order(reverse);
let kind = match subtable.kind() {
Some(kind) => kind,
_ => continue,
};
if subtable.is_vertical() || subtable.is_cross_stream() {
continue;
}
match kind {
SubtableKind::Format0(t) => {
if disable_kern {
continue;
}
let len = buffer.len();
let mut left_index = if let Some((index, _)) = buffer
.glyphs
.iter()
.enumerate()
.find(|(_, g)| g.joining_type != 6)
{
index
} else {
continue;
};
let mut left = buffer.glyphs[left_index].id;
for i in left_index + 1..len {
if buffer.glyphs[i].joining_type == 6 {
continue;
}
let right = buffer.glyphs[i].id;
if let Some(kerning) = t.get(left, right) {
if kerning != 0 {
// if TRACE {
// println!("KERN [{} & {}] {}", left_index, i, kerning);
// }
buffer.positions[left_index].advance += kerning as f32;
}
}
left_index = i;
left = right;
}
}
SubtableKind::Format1(t) => {
if disable_kern {
continue;
}
let mut i = 0;
let len = buffer.glyphs.len();
let mut state = ContextualState::new();
while i < len {
match t.next(&mut state, i, buffer.glyphs[i].id, |i, kerning| {
buffer.positions[i].advance += kerning as f32;
Some(())
}) {
Some(advance) => i += advance,
None => break,
}
}
}
SubtableKind::Format2(t) => {
if disable_kern {
continue;
}
//println!("142/116 = {:?}", t.get(142, 116));
let len = buffer.len();
let mut left_index = if let Some((index, _)) = buffer
.glyphs
.iter()
.enumerate()
.find(|(_, g)| g.joining_type != 6)
{
index
} else {
continue;
};
let mut left = buffer.glyphs[left_index].id;
for i in left_index + 1..len {
if buffer.glyphs[i].joining_type == 6 {
continue;
}
let right = buffer.glyphs[i].id;
if let Some(kerning) = t.get(left, right) {
if kerning != 0 {
// if TRACE {
// println!("KERN [{} & {}] {}", left_index, i, kerning);
// }
buffer.positions[left_index].advance += kerning as f32;
}
}
left_index = i;
left = right;
}
}
SubtableKind::Format4(t) => {
let mut i = 0;
let len = buffer.glyphs.len();
let mut state = Format4State::new();
while i < len {
match t.next(&mut state, i, buffer.glyphs[i].id, |i, base, x, y| {
buffer.position_mark(i, base, x, y);
Some(())
}) {
Some(advance) => i += advance,
None => break,
}
}
}
}
}
buffer.ensure_order(false);
Some(())
}
pub fn apply_kern(data: &[u8], kern: u32, buffer: &mut Buffer) -> Option<()> {
use kern::*;
for (_i, subtable) in subtables(data, kern).enumerate() {
let kind = match subtable.kind() {
Some(kind) => kind,
_ => continue,
};
if !subtable.is_horizontal() {
continue;
}
buffer.ensure_order(buffer.is_rtl);
let cross_stream = subtable.cross_stream();
match kind {
SubtableKind::Format0(t) => {
buffer.ensure_order(false);
let len = buffer.len();
let mut left_index = if let Some((index, _)) = buffer
.glyphs
.iter()
.enumerate()
.find(|(_, g)| g.joining_type != 6)
{
index
} else {
continue;
};
let mut left = buffer.glyphs[left_index].id;
for i in left_index + 1..len {
if buffer.glyphs[i].joining_type == 6 {
continue;
}
let right = buffer.glyphs[i].id;
if let Some(kerning) = t.get(left, right) {
if kerning != 0 {
// if TRACE {
// println!("KERN [{} & {}] {}", left_index, i, kerning);
// }
buffer.positions[left_index].advance += kerning as f32;
}
}
left_index = i;
left = right;
}
}
SubtableKind::Format1(t) => {
let mut i = 0;
let len = buffer.glyphs.len();
let mut state = Format1State::new();
while i < len {
match t.next(&mut state, i, buffer.glyphs[i].id, |i, kerning| {
let g = &buffer.glyphs[i];
if g.joining_type == 6 {
if cross_stream {
let pos = &mut buffer.positions[i];
if pos.y == 0. {
pos.y = kerning as f32;
}
} else if let Some(base) = find_base(buffer, buffer.is_rtl, i) {
let diff = if base >= i { base - i } else { i - base };
if diff < 255 {
let pos = &mut buffer.positions[i];
if pos.base == 0 {
pos.flags |= MARK_ATTACH;
pos.base = diff as u8;
pos.x = kerning as f32;
buffer.has_marks = true;
}
}
}
}
Some(())
}) {
Some(advance) => i += advance,
None => break,
}
}
}
}
}
buffer.ensure_order(false);
Some(())
}
fn find_base(buffer: &Buffer, reverse: bool, index: usize) -> Option<usize> {
use crate::text::cluster::ShapeClass;
let cluster = buffer.glyphs[index].cluster;
if reverse {
for i in index + 1..buffer.len() {
let g = &buffer.glyphs[i];
if g.cluster != cluster {
return None;
}
if g.char_class == ShapeClass::Base {
return Some(i);
}
}
} else if index > 0 {
for i in (0..index).rev() {
let g = &buffer.glyphs[i];
if g.cluster != cluster {
return None;
}
if g.char_class == ShapeClass::Base {
return Some(i);
}
}
}
None
}

1826
vendor/swash/src/shape/at.rs vendored Normal file

File diff suppressed because it is too large Load Diff

708
vendor/swash/src/shape/buffer.rs vendored Normal file
View File

@@ -0,0 +1,708 @@
use super::cluster::{Glyph, GlyphInfo};
use super::feature::*;
use crate::text::{
cluster::{Char, CharCluster, ClusterInfo, ShapeClass, SourceRange, MAX_CLUSTER_SIZE},
JoiningType,
};
use alloc::vec::Vec;
use core::ops::Range;
// Glyph flags.
pub const SUBSTITUTED: u16 = 1;
pub const LIGATED: u16 = 2;
pub const COMPONENT: u16 = 4;
pub const MARK_ATTACH: u16 = 8;
pub const CURSIVE_ATTACH: u16 = 16;
pub const IGNORABLE: u16 = 64;
/// Per glyph shaping data.
#[derive(Copy, Clone, Default, Debug)]
pub struct GlyphData {
pub id: u16,
pub flags: u16,
pub class: u8,
pub char_class: ShapeClass,
pub mark_type: u8,
pub joining_type: u8,
pub mask: u8,
pub skip: bool,
pub component: u8,
pub cluster: u32,
pub data: u32,
}
impl GlyphData {
pub fn is_component(&self) -> bool {
self.flags & COMPONENT != 0
}
}
/// Per glyph shaping position data.
#[derive(Copy, Clone, Default, Debug)]
pub struct PositionData {
pub base: u8,
pub flags: u16,
pub x: f32,
pub y: f32,
pub advance: f32,
}
impl Glyph {
pub(super) fn new(g: &GlyphData, p: &PositionData) -> Self {
Self {
id: g.id,
info: GlyphInfo(p.flags),
x: p.x,
y: p.y,
advance: p.advance,
data: g.data,
}
}
}
#[derive(Clone, Default)]
pub struct Buffer {
pub glyphs: Vec<GlyphData>,
pub positions: Vec<PositionData>,
pub infos: Vec<(ClusterInfo, bool, u32)>,
pub ranges: Vec<SourceRange>,
pub shaped_glyphs: Vec<Glyph>,
pub is_rtl: bool,
pub dotted_circle: Option<u16>,
pub has_cursive: bool,
pub has_marks: bool,
pub reversed: bool,
pub next_cluster: u32,
pub skip_state: SkipState,
pub sub_args: Vec<u16>,
pub pos_args: Vec<u16>,
}
#[derive(Copy, Clone, PartialEq, Eq, Default)]
pub struct SkipState {
pub flags: u8,
pub mask: u8,
pub mark_check: u8,
pub mark_class: u8,
pub mark_set: u32,
}
impl Buffer {
pub fn new() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.glyphs.len()
}
pub fn push(&mut self, cluster: &CharCluster) -> Range<usize> {
let start = self.glyphs.len();
let chars = cluster.mapped_chars();
if cluster.info().is_broken() {
if let Some(id) = self.dotted_circle {
let first = &chars[0];
self.push_char(&Char {
ch: '\u{25cc}',
shape_class: ShapeClass::Base,
joining_type: JoiningType::U,
ignorable: false,
contributes_to_shaping: true,
glyph_id: id,
offset: first.offset,
data: first.data,
});
}
}
for ch in chars {
self.push_char(ch);
}
self.next_cluster += 1;
self.push_cluster(cluster);
start..self.glyphs.len()
}
pub fn push_order(&mut self, cluster: &CharCluster, order: &[usize]) -> Range<usize> {
let start = self.glyphs.len();
let chars = cluster.mapped_chars();
if cluster.info().is_broken() {
if let Some(id) = self.dotted_circle {
let first = &chars[order[0]];
self.push_char(&Char {
ch: '\u{25cc}',
shape_class: ShapeClass::Base,
joining_type: JoiningType::U,
ignorable: false,
contributes_to_shaping: true,
glyph_id: id,
offset: first.offset,
data: first.data,
});
}
}
for ch in order[..chars.len()].iter().map(|i| &chars[*i]) {
self.push_char(ch);
}
self.next_cluster += 1;
self.push_cluster(cluster);
start..self.glyphs.len()
}
pub fn _push_hangul(&mut self, cluster: &CharCluster) -> Range<usize> {
let start = self.glyphs.len();
let chars = cluster.mapped_chars();
if cluster.info().is_broken() {
if let Some(id) = self.dotted_circle {
let first = &chars[0];
self.push_char(&Char {
ch: '\u{25cc}',
shape_class: ShapeClass::Base,
joining_type: JoiningType::U,
ignorable: false,
contributes_to_shaping: true,
glyph_id: id,
offset: first.offset,
data: first.data,
});
}
}
for ch in chars {
self._push_hangul_char(ch);
}
self.next_cluster += 1;
self.push_cluster(cluster);
start..self.glyphs.len()
}
#[inline(always)]
fn push_char(&mut self, ch: &Char) {
let cluster = self.next_cluster;
self.glyphs.push(GlyphData {
id: ch.glyph_id,
flags: (ch.ignorable as u16) << 6,
class: 0,
char_class: ch.shape_class,
joining_type: ch.joining_type as u8,
mark_type: 0,
mask: 0,
skip: false,
component: !0,
cluster,
data: ch.data,
});
}
fn _push_hangul_char(&mut self, ch: &Char) {
let cluster = self.next_cluster;
let c = ch.ch as u32;
let mask = if (0x1100..=0x115F).contains(&c) || (0xA960..=0xA97C).contains(&c) {
1
} else if (0x1160..=0x11A7).contains(&c) || (0xD7B0..=0xD7C6).contains(&c) {
2
} else if (0x11A8..=0x11FF).contains(&c) || (0xD7CB..=0xD7FB).contains(&c) {
4
} else {
1 | 2 | 4
};
self.glyphs.push(GlyphData {
id: ch.glyph_id,
flags: (ch.ignorable as u16) << 6,
class: 0,
char_class: ch.shape_class,
joining_type: ch.joining_type as u8,
mark_type: 0,
mask,
skip: false,
component: !0,
cluster,
data: ch.data,
});
}
fn push_cluster(&mut self, cluster: &CharCluster) {
self.infos
.push((cluster.info(), false, cluster.user_data()));
self.ranges.push(cluster.range());
}
pub fn clear(&mut self) {
self.glyphs.clear();
self.positions.clear();
self.infos.clear();
self.ranges.clear();
self.is_rtl = false;
self.reversed = false;
self.has_cursive = false;
self.has_marks = false;
self.dotted_circle = None;
self.next_cluster = 0;
self.skip_state = SkipState::default();
}
pub fn ensure_order(&mut self, reversed: bool) {
if reversed != self.reversed {
self.glyphs.reverse();
if !self.positions.is_empty() {
self.positions.reverse();
}
self.reversed = reversed;
}
}
pub fn clear_flags(&mut self, flags: u16, range: Option<Range<usize>>) {
if let Some(range) = range {
for g in &mut self.glyphs[range] {
g.flags &= !flags;
}
} else {
for g in &mut self.glyphs {
g.flags &= !flags;
}
}
}
pub fn setup_positions(&mut self, was_morx: bool) {
if was_morx {
self.glyphs
.retain(|g| g.flags & COMPONENT == 0 && g.id != 0xFFFF);
} else {
self.glyphs.retain(|g| g.flags & COMPONENT == 0);
}
self.positions.clear();
self.positions
.resize(self.glyphs.len(), PositionData::default());
}
pub fn substitute(&mut self, index: usize, id: u16) {
let g = &mut self.glyphs[index];
// if TRACE {
// println!("!subst[{}] {} -> {}", index, g.id, id);
// }
g.id = id;
g.flags |= SUBSTITUTED;
}
pub fn substitute_ligature(&mut self, index: usize, id: u16, components: &[usize]) {
// if TRACE {
// print!("!subst[{}] {}", index, self.glyphs[index].id);
// for c in components {
// print!(" {}", self.glyphs[*c].id)
// }
// println!(" -> {}", id);
// }
if components.is_empty() {
return;
}
let g = &mut self.glyphs[index];
g.id = id;
g.flags |= SUBSTITUTED | LIGATED;
let cluster = g.cluster;
let mut last_index = index;
for (i, &index) in components.iter().enumerate() {
let g = &mut self.glyphs[index];
self.infos[g.cluster as usize].1 = true;
g.id = 0xFFFF;
g.flags |= COMPONENT;
g.class = 5;
g.cluster = cluster;
g.skip = true;
if (index - last_index) > 1 {
let component = i as u8;
for g in &mut self.glyphs[last_index + 1..index] {
if g.mark_type != 0 || g.class == 3 {
g.component = component;
g.cluster = cluster;
}
}
}
last_index = index;
}
if (last_index + 1) < self.glyphs.len() {
let last_component = components.len() as u8;
for g in &mut self.glyphs[last_index + 1..] {
if g.mark_type != 0 || g.class == 3 {
g.component = last_component;
g.cluster = cluster;
} else {
break;
}
}
}
}
pub fn substitute_multiple(&mut self, index: usize, ids: &[u16]) {
let count = ids.len();
if count == 0 {
self.glyphs.remove(index);
return;
} else if count == 1 {
self.substitute(index, ids[0]);
return;
}
// if TRACE {
// println!("!subst[{}] {} -> {:?}", index, self.glyphs[index].id, ids);
// }
let g = self.glyphs[index];
self.glyphs
.splice(index..index + 1, SubstIter { ids, g, cur: 0 });
}
pub fn multiply(&mut self, index: usize, count: usize) {
let g = self
.glyphs
.get(index)
.copied()
.unwrap_or_else(GlyphData::default);
self.glyphs.splice(index..index, (0..count).map(|_| g));
}
pub fn position(&mut self, index: usize, x: f32, y: f32, xadvance: f32, _yadvance: f32) {
let p = &mut self.positions[index];
p.x += x;
p.y += y;
p.advance += xadvance;
}
pub fn position_cursive(&mut self, index: usize, next: usize, x: f32, y: f32) {
let p = &mut self.positions[index];
self.has_cursive = true;
p.flags = CURSIVE_ATTACH;
if true {
//self.dir.is_horizontal() {
p.y = y;
//p.advance -= x;
} else {
p.x = x;
}
p.base = (next - index) as u8;
}
pub fn position_mark(&mut self, index: usize, base: usize, dx: f32, dy: f32) {
let p = &mut self.positions[index];
self.has_marks = true;
p.flags = MARK_ATTACH;
p.base = (index - base) as u8;
p.x = dx;
p.y = dy;
}
pub fn set_join_masks(&mut self) {
let mut prev: Option<usize> = None;
let mut state = 0;
let glyphs = &mut self.glyphs;
let len = glyphs.len();
// Transparent joining type.
const JOIN_T: u8 = 6;
for i in 0..len {
let ty = glyphs[i].joining_type;
if ty == JOIN_T {
continue;
}
let entry = JOIN_STATES[state][ty as usize];
if let Some(j) = prev {
if entry.0 != NONE_MASK {
glyphs[j].mask = entry.0;
}
}
glyphs[i].mask = entry.1;
prev = Some(i);
state = entry.2 as usize;
}
}
}
struct SubstIter<'a> {
ids: &'a [u16],
g: GlyphData,
cur: usize,
}
impl<'a> Iterator for SubstIter<'a> {
type Item = GlyphData;
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.ids.len() - self.cur;
(remaining, Some(remaining))
}
fn next(&mut self) -> Option<Self::Item> {
if self.cur >= self.ids.len() {
return None;
}
let g = GlyphData {
id: self.ids[self.cur],
flags: SUBSTITUTED,
..self.g
};
self.cur += 1;
Some(g)
}
}
pub fn reorder_myanmar(chars: &[Char], order: &mut Vec<usize>) {
use ShapeClass::*;
let mut ignored = [false; MAX_CLUSTER_SIZE];
let mut base = None;
let mut kinzi: Option<Range<usize>> = None;
let mut medial_ra = None;
let mut vpre: Option<Range<usize>> = None;
let mut vblw: Option<usize> = None;
let mut anus: Option<Range<usize>> = None;
let mut i = 0;
let mut last_vblw = false;
let len = chars.len();
if len == 0 {
return;
}
if order.len() < len {
order.resize(chars.len(), 0);
}
if chars[0].shape_class == Kinzi {
kinzi = Some(0..3);
ignored[0] = true;
ignored[1] = true;
ignored[2] = true;
i = 3;
}
while i < len {
let ch = chars[i];
let k = ch.shape_class;
if last_vblw && k == Anusvara {
anus = match anus {
Some(r) => Some(r.start..i - r.start + 1),
None => Some(i..i + 1),
};
ignored[i] = true;
i += 1;
continue;
}
last_vblw = false;
if k == VBlw {
if vblw.is_none() {
vblw = Some(i);
}
last_vblw = true;
}
if k == Base && base.is_none() {
base = Some(i);
ignored[i] = true;
} else if k == MedialRa {
medial_ra = Some(i);
ignored[i] = true;
} else if k == VPre {
vpre = match vpre {
Some(r) => Some(r.start..i - r.start + 1),
None => Some(i..i + 1),
};
ignored[i] = true;
}
i += 1;
}
i = 0;
if let Some(r) = vpre {
for j in r {
order[i] = j;
i += 1;
}
}
if let Some(j) = medial_ra {
order[i] = j;
i += 1;
}
if let Some(j) = base {
order[i] = j;
i += 1;
}
if let Some(r) = kinzi {
for j in r {
order[i] = j;
i += 1;
}
}
let mut j = 0;
while j < len {
if ignored[j] {
j += 1;
continue;
}
if Some(j) == vblw && anus.is_some() {
for k in anus.take().unwrap() {
order[i] = k;
i += 1;
}
}
order[i] = j;
i += 1;
j += 1;
}
}
#[allow(clippy::needless_range_loop)]
pub fn reorder_complex(glyphs: &mut [GlyphData], buf: &mut Vec<GlyphData>, order: &mut Vec<usize>) {
use ShapeClass::*;
let mut first_base = None;
let mut last_base = None;
let mut last_halant = None;
let mut reph = None;
let mut pref = None;
let mut vpre: Option<Range<usize>> = None;
let mut vmpre: Option<Range<usize>> = None;
let mut ignored = [false; 64];
let len = glyphs.len();
if buf.len() < glyphs.len() {
buf.resize(len, GlyphData::default());
}
let buf = &mut buf[..len];
if order.len() < len {
order.resize(len, 0);
}
let order = &mut order[..len];
for (i, g) in glyphs.iter().enumerate() {
if g.is_component() {
continue;
}
match g.char_class {
Base => {
if first_base.is_none() {
first_base = Some(i);
ignored[i] = true;
}
if last_halant.is_none() {
last_base = Some(i);
}
}
Halant => {
last_halant = Some(i);
}
Reph => {
if reph.is_none() {
reph = Some(i);
ignored[i] = true;
}
}
Pref => {
if pref.is_none() {
pref = Some(i);
ignored[i] = true;
}
}
VPre => {
vpre = match vpre {
Some(r) => Some(r.start..i - r.start + 1),
None => Some(i..i + 1),
};
ignored[i] = true;
}
VMPre => {
vmpre = match vmpre {
Some(r) => Some(r.start..i - r.start + 1),
None => Some(i..i + 1),
};
ignored[i] = true;
}
_ => {}
}
}
let mut j = 0;
// No explicit virama; insert vmpre, vpre, pref
if last_halant.is_none() {
if let Some(r) = vmpre.clone() {
for i in r {
order[j] = i;
j += 1;
}
}
if let Some(r) = vpre.clone() {
for i in r {
order[j] = i;
j += 1;
}
}
if let Some(i) = pref {
order[j] = i;
j += 1;
}
}
// Insert the base...
if let Some(i) = first_base {
order[j] = i;
j += 1;
}
if last_base.is_none() {
// ... and the reph
if let Some(i) = reph {
order[j] = i;
j += 1;
}
}
// Now the rest
let len = glyphs.len();
for i in 0..len {
if ignored[i] {
continue;
}
// println!(" -> i = {}, j = {}", i, j);
// println!("order: {:?}", order);
// println!("ignored: {:?}", &ignored[0..order.len()]);
order[j] = i;
j += 1;
// Insert reph after final base
if Some(i) == last_base {
if let Some(i) = reph {
order[j] = i;
j += 1;
}
}
// Move vmpre, vpre and pref after the final virama
if Some(i) == last_halant {
if let Some(r) = vmpre.clone() {
for i in r {
order[j] = i;
j += 1;
}
}
if let Some(r) = vpre.clone() {
for i in r {
order[j] = i;
j += 1;
}
}
if let Some(i) = pref {
order[j] = i;
j += 1;
}
}
}
// Reorder glyphs
buf.copy_from_slice(glyphs);
for (i, j) in order.iter().enumerate() {
glyphs[i] = buf[*j];
}
// println!("order: {:?}", order);
// let new = glyphs
// .iter()
// .map(|g| (g.class, char_kind(g.subclass)))
// .collect::<Vec<_>>();
// println!("new: {:?}", &new[..]);
}
// Matches the Arabic joining table from Harfbuzz.
#[rustfmt::skip]
const JOIN_STATES: [[(u8, u8, u8); 6]; 7] = [
// U, L, R, D, ALAPH, DALATH_RISH
// State 0: prev was U, not willing to join.
[ (NONE_MASK,NONE_MASK,0), (NONE_MASK,ISOL_MASK,2), (NONE_MASK,ISOL_MASK,1), (NONE_MASK,ISOL_MASK,2), (NONE_MASK,ISOL_MASK,1), (NONE_MASK,ISOL_MASK,6), ],
// State 1: prev was R or ISOL_MASK/ALAPH, not willing to join.
[ (NONE_MASK,NONE_MASK,0), (NONE_MASK,ISOL_MASK,2), (NONE_MASK,ISOL_MASK,1), (NONE_MASK,ISOL_MASK,2), (NONE_MASK,FIN2_MASK,5), (NONE_MASK,ISOL_MASK,6), ],
// State 2: prev was D/L in ISOL_MASK form, willing to join.
[ (NONE_MASK,NONE_MASK,0), (NONE_MASK,ISOL_MASK,2), (INIT_MASK,FINA_MASK,1), (INIT_MASK,FINA_MASK,3), (INIT_MASK,FINA_MASK,4), (INIT_MASK,FINA_MASK,6), ],
// State 3: prev was D in FINA_MASK form, willing to join. */
[ (NONE_MASK,NONE_MASK,0), (NONE_MASK,ISOL_MASK,2), (MEDI_MASK,FINA_MASK,1), (MEDI_MASK,FINA_MASK,3), (MEDI_MASK,FINA_MASK,4), (MEDI_MASK,FINA_MASK,6), ],
// State 4: prev was FINA_MASK ALAPH, not willing to join. */
[ (NONE_MASK,NONE_MASK,0), (NONE_MASK,ISOL_MASK,2), (MED2_MASK,ISOL_MASK,1), (MED2_MASK,ISOL_MASK,2), (MED2_MASK,FIN2_MASK,5), (MED2_MASK,ISOL_MASK,6), ],
// State 5: prev was FIN2_MASK/FIN3_MASK ALAPH, not willing to join. */
[ (NONE_MASK,NONE_MASK,0), (NONE_MASK,ISOL_MASK,2), (ISOL_MASK,ISOL_MASK,1), (ISOL_MASK,ISOL_MASK,2), (ISOL_MASK,FIN2_MASK,5), (ISOL_MASK,ISOL_MASK,6), ],
// State 6: prev was DALATH/RISH, not willing to join. */
[ (NONE_MASK,NONE_MASK,0), (NONE_MASK,ISOL_MASK,2), (NONE_MASK,ISOL_MASK,1), (NONE_MASK,ISOL_MASK,2), (NONE_MASK,FIN3_MASK,5), (NONE_MASK,ISOL_MASK,6), ]
];

122
vendor/swash/src/shape/cache.rs vendored Normal file
View File

@@ -0,0 +1,122 @@
use super::at::FeatureStore;
use super::engine::EngineMetadata;
use super::internal::var::Fvar;
use crate::{charmap::CharmapProxy, metrics::MetricsProxy, FontRef};
use alloc::vec::Vec;
pub type Epoch = u64;
pub struct FontEntry {
pub metrics: MetricsProxy,
pub charmap: CharmapProxy,
pub coord_count: u16,
pub metadata: EngineMetadata,
}
impl FontEntry {
pub fn new(font: &FontRef) -> Self {
Self {
metrics: MetricsProxy::from_font(font),
charmap: CharmapProxy::from_font(font),
coord_count: Fvar::from_font(font)
.map(|fvar| fvar.axis_count())
.unwrap_or(0),
metadata: EngineMetadata::from_font(font),
}
}
}
pub struct FeatureEntry {
pub epoch: Epoch,
pub id: [u64; 2],
pub coords: Vec<i16>,
pub tags: [u32; 4],
pub store: FeatureStore,
}
pub struct FeatureCache {
entries: Vec<FeatureEntry>,
epoch: Epoch,
max_entries: usize,
}
pub enum FeatureCacheEntry<'a> {
New(&'a mut FeatureStore),
Present(&'a mut FeatureStore),
}
impl FeatureCache {
pub fn new(max_entries: usize) -> Self {
Self {
entries: Default::default(),
epoch: 0,
max_entries,
}
}
pub fn entry<'a>(
&'a mut self,
id: [u64; 2],
coords: &[i16],
has_feature_vars: bool,
tags: &[u32; 4],
) -> FeatureCacheEntry<'a> {
match self.find_entry(id, coords, has_feature_vars, tags) {
(true, index) => {
let entry = &mut self.entries[index];
entry.epoch = self.epoch;
FeatureCacheEntry::Present(&mut entry.store)
}
(false, index) => {
self.epoch += 1;
let entry = &mut self.entries[index];
entry.epoch = self.epoch;
FeatureCacheEntry::New(&mut entry.store)
}
}
}
fn find_entry(
&mut self,
id: [u64; 2],
coords: &[i16],
has_feature_vars: bool,
tags: &[u32; 4],
) -> (bool, usize) {
let epoch = self.epoch;
let mut lowest_serial = epoch;
let mut lowest_index = 0;
for (i, entry) in self.entries.iter().enumerate() {
if entry.id == id && &entry.tags == tags {
if has_feature_vars && coords != &entry.coords[..] {
continue;
}
return (true, i);
}
if entry.epoch < lowest_serial {
lowest_serial = entry.epoch;
lowest_index = i;
}
}
if self.entries.len() < self.max_entries {
lowest_index = self.entries.len();
self.entries.push(FeatureEntry {
epoch,
id,
coords: Vec::from(coords),
store: FeatureStore::default(),
tags: *tags,
});
} else {
let entry = &mut self.entries[lowest_index];
entry.epoch = epoch;
entry.id = id;
entry.coords.clear();
entry.coords.extend_from_slice(coords);
entry.store.clear();
entry.tags = *tags;
}
(false, lowest_index)
}
}

93
vendor/swash/src/shape/cluster.rs vendored Normal file
View File

@@ -0,0 +1,93 @@
/*!
Glyph cluster modeling-- output from the shaper.
*/
use super::buffer::MARK_ATTACH;
use crate::text::cluster::{ClusterInfo, SourceRange, UserData};
use crate::GlyphId;
/// Information for a glyph.
#[derive(Copy, Clone, Default, Debug)]
pub struct GlyphInfo(pub u16);
impl GlyphInfo {
/// Returns true if the glyph is an attached mark.
pub fn is_mark(self) -> bool {
self.0 & MARK_ATTACH != 0
}
}
/// Glyph identifier and positioning information as a result of shaping.
#[derive(Copy, Clone, Default, Debug)]
pub struct Glyph {
/// Glyph identifier.
pub id: GlyphId,
/// Glyph flags.
pub info: GlyphInfo,
/// Horizontal offset.
pub x: f32,
/// Vertical offset.
pub y: f32,
/// Advance width or height.
pub advance: f32,
/// Arbitrary user data.
pub data: UserData,
}
/// Collection of glyphs and associated metadata corresponding to one or
/// more source clusters.
#[derive(Copy, Clone, Debug)]
pub struct GlyphCluster<'a> {
/// Full source range of the cluster in original units supplied to the
/// shaper.
pub source: SourceRange,
/// Information about the textual content of the cluster.
pub info: ClusterInfo,
/// Sequence of glyphs for the cluster. May be empty for clusters whose
/// source consisted entirely of control characters.
pub glyphs: &'a [Glyph],
/// If the cluster is a ligature, this contains the source range
/// of each ligature component. Empty otherwise.
pub components: &'a [SourceRange],
/// Arbitrary user data-- taken from the initial character of the cluster.
pub data: UserData,
}
impl<'a> GlyphCluster<'a> {
/// Returns true if the cluster is empty. Empty clusters still represent
/// characters in the source text, but contain no glyphs. This will be
/// true, for example, with newline sequences (\n or \r\n) as well as other
/// control characters.
pub fn is_empty(&self) -> bool {
self.glyphs.is_empty()
}
/// Returns true if the cluster contains a single glyph. Note that a simple
/// cluster can also be a ligature.
pub fn is_simple(&self) -> bool {
self.glyphs.len() == 1
}
/// Returns true if the cluster corresponds to multiple source clusters.
/// Note that a ligature cluster can also be complex.
pub fn is_ligature(&self) -> bool {
!self.components.is_empty()
}
/// Returns true if the cluster is complex-- that is if it contains more
/// than one glyph. This will be true for clusters containing marks and is
/// also commonly true for syllabic languages such as those in the Indic
/// family.
pub fn is_complex(&self) -> bool {
self.glyphs.len() > 1
}
/// Computes the full advance width or height of the cluster.
pub fn advance(&self) -> f32 {
let mut advance = 0.;
for g in self.glyphs {
advance += g.advance;
}
advance
}
}

300
vendor/swash/src/shape/engine.rs vendored Normal file
View File

@@ -0,0 +1,300 @@
use super::{aat, at};
use super::buffer::*;
use super::internal::{self, at::Gdef, raw_tag, Bytes, RawFont, RawTag};
use crate::font::FontRef;
use crate::text::{Language, Script};
use alloc::vec::Vec;
use core::ops::Range;
/// Shaping engine that handles the various methods available in
/// an OpenType font.
pub struct Engine<'a> {
pub data: Bytes<'a>,
pub gdef: Gdef<'a>,
pub gsub: at::StageOffsets,
pub gpos: at::StageOffsets,
pub morx: u32,
pub kerx: u32,
pub ankr: u32,
pub kern: u32,
pub storage: at::Storage,
pub coords: &'a [i16],
pub script: Script,
pub tags: [RawTag; 4],
pub sub_mode: SubMode,
pub pos_mode: PosMode,
pub use_ot: bool,
pub mode: EngineMode,
}
impl<'a> Engine<'a> {
/// Creates a new shaping engine from precreated metadata.
pub fn new(
metadata: &EngineMetadata,
font_data: &'a [u8],
coords: &'a [i16],
script: Script,
lang: Option<Language>,
) -> Self {
let data = Bytes::new(font_data);
let gdef = Gdef::from_offset(font_data, metadata.gdef).unwrap_or_else(Gdef::empty);
let script_tag = script.to_opentype();
let lang_tag = lang.and_then(|l| l.to_opentype());
let (gsub, stags) = if metadata.sub_mode == SubMode::Gsub {
at::StageOffsets::new(&data, metadata.gsub, script_tag, lang_tag).unwrap_or_default()
} else {
(at::StageOffsets::default(), [0, 0])
};
let (gpos, ptags) = if metadata.pos_mode == PosMode::Gpos {
at::StageOffsets::new(&data, metadata.gpos, script_tag, lang_tag).unwrap_or_default()
} else {
(at::StageOffsets::default(), [0, 0])
};
let tags = [stags[0], stags[1], ptags[0], ptags[1]];
let use_ot = gsub.lang != 0 || gpos.lang != 0;
let mode = if gsub.lang != 0 && script.is_complex() {
if script == Script::Myanmar {
EngineMode::Myanmar
} else {
EngineMode::Complex
}
} else {
EngineMode::Simple
};
let mut sub_mode = metadata.sub_mode;
let mut pos_mode = metadata.pos_mode;
if sub_mode == SubMode::Gsub && gsub.lang == 0 {
sub_mode = SubMode::None;
}
if pos_mode == PosMode::Gpos && gpos.lang == 0 {
pos_mode = PosMode::None;
}
Self {
data,
gdef,
gsub,
gpos,
morx: metadata.morx,
kerx: metadata.kerx,
ankr: metadata.ankr,
kern: metadata.kern,
storage: at::Storage::default(),
coords,
script,
tags,
sub_mode,
pos_mode,
use_ot,
mode,
}
}
}
/// OpenType shaping.
impl<'a> Engine<'a> {
/// Returns the script and language tags that have been selected for
/// the GSUB and GPOS tables.
pub fn tags(&self) -> &[RawTag; 4] {
&self.tags
}
/// Builds a feature store for the current engine configuration.
pub fn collect_features(
&self,
builder: &mut at::FeatureStoreBuilder,
store: &mut at::FeatureStore,
) {
builder.build(
store,
self.data.data(),
self.coords,
&self.gdef,
&self.gsub,
&self.gpos,
);
store.groups = store.groups(self.script);
}
/// Returns true if feature variations are supported.
pub fn has_feature_vars(&self) -> bool {
self.gsub.var != 0 || self.gpos.var != 0
}
/// Sets glyph and mark classes for the specified range of the buffer.
pub fn set_classes(&self, buffer: &mut Buffer, range: Option<Range<usize>>) {
if !self.gdef.ok() {
return;
}
let slice = if let Some(range) = range {
&mut buffer.glyphs[range]
} else {
&mut buffer.glyphs[..]
};
let gdef = &self.gdef;
if gdef.has_mark_classes() {
for g in slice.iter_mut() {
g.class = gdef.class(g.id) as u8;
g.mark_type = gdef.mark_class(g.id) as u8;
}
} else {
for g in slice.iter_mut() {
g.class = gdef.class(g.id) as u8;
}
}
}
/// Applies the GSUB features to the specified range of the buffer.
pub fn gsub(
&mut self,
store: &at::FeatureStore,
feature_mask: impl Into<at::FeatureMask>,
buffer: &mut Buffer,
buffer_range: Option<Range<usize>>,
) -> bool {
at::apply(
0,
&self.data,
self.gsub.base,
self.coords,
&self.gdef,
&mut self.storage,
store,
feature_mask.into(),
buffer,
buffer_range,
) == Some(true)
}
/// Applies the GPOS features to the specified range of the buffer.
pub fn gpos(
&mut self,
store: &at::FeatureStore,
feature_mask: impl Into<at::FeatureMask>,
buffer: &mut Buffer,
buffer_range: Option<Range<usize>>,
) -> bool {
at::apply(
1,
&self.data,
self.gpos.base,
self.coords,
&self.gdef,
&mut self.storage,
store,
feature_mask.into(),
buffer,
buffer_range,
) == Some(true)
}
}
/// Apple shaping.
impl<'a> Engine<'a> {
/// Converts a feature list into a sorted collection of AAT selectors.
pub fn collect_selectors(&self, features: &[(RawTag, u16)], selectors: &mut Vec<(u16, u16)>) {
use internal::aat::morx::feature_from_tag;
selectors.clear();
for (tag, value) in features {
if let Some((selector, [on, off])) = feature_from_tag(*tag) {
let setting = if *value == 0 { off } else { on };
selectors.push((selector, setting));
}
}
selectors.sort_unstable();
}
/// Applies the extended metamorphosis table.
pub fn morx(&self, buffer: &mut Buffer, selectors: &[(u16, u16)]) {
if self.morx != 0 {
aat::apply_morx(self.data.data(), self.morx, buffer, selectors);
buffer.ensure_order(false);
}
}
/// Applies the extended kerning table.
pub fn kerx(&self, buffer: &mut Buffer, disable_kern: bool) {
if self.kerx != 0 {
aat::apply_kerx(self.data.data(), self.kerx, self.ankr, buffer, disable_kern);
buffer.ensure_order(false);
}
}
/// Applies the kerning table.
pub fn kern(&self, buffer: &mut Buffer) {
if self.kern != 0 {
aat::apply_kern(self.data.data(), self.kern, buffer);
}
}
}
/// The overall mode of the engine based on a combination of the
/// supported tables and the selected script.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum EngineMode {
Simple,
Myanmar,
Complex,
}
/// The substitution mode supported by the engine.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum SubMode {
None,
Gsub,
Morx,
}
/// The positioning mode supported by the engine.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum PosMode {
None,
Gpos,
Kerx,
Kern,
}
/// Metadata for creating a shaping engine.
#[derive(Copy, Clone)]
pub struct EngineMetadata {
pub gdef: u32,
pub gsub: u32,
pub gpos: u32,
pub morx: u32,
pub kerx: u32,
pub ankr: u32,
pub kern: u32,
pub sub_mode: SubMode,
pub pos_mode: PosMode,
}
impl EngineMetadata {
pub fn from_font(font: &FontRef) -> Self {
let mut this = Self {
gdef: font.table_offset(raw_tag(b"GDEF")),
gsub: font.table_offset(raw_tag(b"GSUB")),
gpos: font.table_offset(raw_tag(b"GPOS")),
morx: font.table_offset(raw_tag(b"morx")),
// ltag: font.table_offset(raw_tag(b"ltag")),
kerx: font.table_offset(raw_tag(b"kerx")),
ankr: font.table_offset(raw_tag(b"ankr")),
kern: font.table_offset(raw_tag(b"kern")),
sub_mode: SubMode::None,
pos_mode: PosMode::None,
};
if this.gsub != 0 {
this.sub_mode = SubMode::Gsub;
} else if this.morx != 0 {
this.sub_mode = SubMode::Morx;
}
if this.gpos != 0 {
this.pos_mode = PosMode::Gpos;
} else if this.kerx != 0 {
this.pos_mode = PosMode::Kerx;
} else if this.kern != 0 {
this.pos_mode = PosMode::Kern;
}
this
}
}

71
vendor/swash/src/shape/feature.rs vendored Normal file
View File

@@ -0,0 +1,71 @@
//! Feature constants.
use super::internal::{raw_tag, RawTag};
// Default tag used in various places.
pub const _DFLT: RawTag = raw_tag(b"DFLT");
// Substitution features.
pub const CCMP: RawTag = raw_tag(b"ccmp");
pub const LOCL: RawTag = raw_tag(b"locl");
pub const RVRN: RawTag = raw_tag(b"rvrn");
pub const LIGA: RawTag = raw_tag(b"liga");
pub const CLIG: RawTag = raw_tag(b"clig");
pub const RLIG: RawTag = raw_tag(b"rlig");
pub const _DLIG: RawTag = raw_tag(b"dlig");
pub const CALT: RawTag = raw_tag(b"calt");
pub const LJMO: RawTag = raw_tag(b"ljmo");
pub const VJMO: RawTag = raw_tag(b"vjmo");
pub const TJMO: RawTag = raw_tag(b"tjmo");
pub const NUKT: RawTag = raw_tag(b"nukt");
pub const AKHN: RawTag = raw_tag(b"akhn");
pub const RKRF: RawTag = raw_tag(b"rkrf");
pub const HALF: RawTag = raw_tag(b"half");
pub const HALN: RawTag = raw_tag(b"haln");
pub const VATU: RawTag = raw_tag(b"vatu");
pub const CJCT: RawTag = raw_tag(b"cjct");
pub const ISOL: RawTag = raw_tag(b"isol");
pub const INIT: RawTag = raw_tag(b"init");
pub const MEDI: RawTag = raw_tag(b"medi");
pub const MED2: RawTag = raw_tag(b"med2");
pub const FINA: RawTag = raw_tag(b"fina");
pub const FIN2: RawTag = raw_tag(b"fin2");
pub const FIN3: RawTag = raw_tag(b"fin3");
pub const MSET: RawTag = raw_tag(b"mset");
pub const RPHF: RawTag = raw_tag(b"rphf");
pub const PREF: RawTag = raw_tag(b"pref");
pub const ABVF: RawTag = raw_tag(b"abvf");
pub const BLWF: RawTag = raw_tag(b"blwf");
pub const PSTF: RawTag = raw_tag(b"pstf");
pub const PRES: RawTag = raw_tag(b"pres");
pub const ABVS: RawTag = raw_tag(b"abvs");
pub const BLWS: RawTag = raw_tag(b"blws");
pub const PSTS: RawTag = raw_tag(b"psts");
pub const RCLT: RawTag = raw_tag(b"rclt");
pub const VERT: RawTag = raw_tag(b"vert");
pub const VRT2: RawTag = raw_tag(b"vrt2");
pub const RTLM: RawTag = raw_tag(b"rtlm");
// Positioning features.
pub const KERN: RawTag = raw_tag(b"kern");
pub const DIST: RawTag = raw_tag(b"dist");
pub const ABVM: RawTag = raw_tag(b"abvm");
pub const BLWM: RawTag = raw_tag(b"blwm");
pub const CURS: RawTag = raw_tag(b"curs");
pub const MARK: RawTag = raw_tag(b"mark");
pub const MKMK: RawTag = raw_tag(b"mkmk");
// Arabic joining masks.
pub const ISOL_MASK: u8 = 1;
pub const INIT_MASK: u8 = 2;
pub const MEDI_MASK: u8 = 4;
pub const FINA_MASK: u8 = 8;
pub const MED2_MASK: u8 = 16;
pub const FIN2_MASK: u8 = 32;
pub const FIN3_MASK: u8 = 64;
pub const NONE_MASK: u8 = 0;
// Hangul jamo masks.
pub const LJMO_MASK: u8 = 1;
pub const VJMO_MASK: u8 = 2;
pub const TJMO_MASK: u8 = 4;

936
vendor/swash/src/shape/mod.rs vendored Normal file
View File

@@ -0,0 +1,936 @@
/*!
Mapping complex text to a sequence of positioned glyphs.
Shaping is the process of converting a sequence of
[character clusters](CharCluster) into a sequence of
[glyph clusters](GlyphCluster) with respect to the rules of a particular
writing system and the typographic features available in a font. The shaper
operates on one _item_ at a time where an item is a run of text with
a single script, language, direction, font, font size, and set of variation/feature
settings. The process of producing these runs is called _itemization_
and is out of scope for this crate.
# Building the shaper
All shaping in this crate takes place within the purview of a
[`ShapeContext`]. This opaque struct manages internal LRU caches and scratch
buffers that are necessary for the shaping process. Generally, you'll
want to keep an instance that persists for more than one layout pass as
this amortizes the cost of allocations, reduces contention for the global
heap and increases the hit rate for the internal acceleration structures. If
you're doing multithreaded layout, you should keep a context per thread.
The only method available on the context is [`builder`](ShapeContext::builder)
which takes a type that can be converted into a [`FontRef`] as an argument
and produces a [`ShaperBuilder`] that provides options for configuring and
building a [`Shaper`].
Here, we'll create a context and build a shaper for Arabic text at 16px:
```
# use swash::{FontRef, CacheKey, shape::*, text::Script};
# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
// let font = ...;
let mut context = ShapeContext::new();
let mut shaper = context.builder(font)
.script(Script::Arabic)
.direction(Direction::RightToLeft)
.size(16.)
.build();
```
You can specify feature settings by calling the [`features`](ShaperBuilder::features)
method with an iterator that yields a sequence of values that are convertible
to [`Setting<u16>`]. Tuples of (&str, u16) will work in a pinch. For example,
you can enable discretionary ligatures like this:
```
# use swash::{FontRef, CacheKey, shape::*, text::Script, tag_from_bytes};
# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
// let font = ...;
let mut context = ShapeContext::new();
let mut shaper = context.builder(font)
.script(Script::Latin)
.size(14.)
.features(&[("dlig", 1)])
.build();
```
A value of `0` will disable a feature while a non-zero value will enable it.
Some features use non-zero values as an argument. The stylistic alternates
feature, for example, often offers a collection of choices per glyph. The argument
is used as an index to select among them. If a requested feature is not present
in a font, the setting is ignored.
Font variation settings are specified in a similar manner with the
[`variations`](ShaperBuilder::variations) method but take an `f32`
to define the value within the variation space for the requested axis:
```
# use swash::{FontRef, CacheKey, shape::*, text::Script, tag_from_bytes};
# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
// let font = ...;
let mut context = ShapeContext::new();
let mut shaper = context.builder(font)
.script(Script::Latin)
.size(14.)
.variations(&[("wght", 520.5)])
.build();
```
See [`ShaperBuilder`] for available options and default values.
# Feeding the shaper
Once we have a properly configured shaper, we need to feed it some
clusters. The simplest approach is to call the [`add_str`](Shaper::add_str)
method with a string:
```
# use swash::{FontRef, CacheKey, shape::*, text::Script, tag_from_bytes};
# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
# let mut context = ShapeContext::new();
# let mut shaper = context.builder(font).build();
shaper.add_str("a quick brown fox?");
```
You can call [`add_str`](Shaper::add_str) multiple times to add a sequence
of text fragments to the shaper.
This simple approach is certainly reasonable when dealing with text consisting
of a single run on one line with a font that is known to contain all the
necessary glyphs. A small text label in a UI is a good example.
For more complex scenarios, the shaper can be fed a single cluster at a time.
This method allows you to provide:
- accurate source ranges per character even if your runs
and items span multiple non-contiguous fragments
- user data per character (a single `u32`) that can be used, for
example, to associate each resulting glyph with a style span
- boundary analysis per character, carrying word boundaries and
line break opportunities through the shaper.
This also provides a junction point for inserting a font fallback
mechanism.
All of this is served by the functionality in the
[`text::cluster`](crate::text::cluster) module.
Let's see a somewhat contrived example that demonstrates the process:
```
use swash::text::cluster::{CharCluster, CharInfo, Parser, Token};
# use swash::{FontRef, CacheKey, shape::*, text::Script, tag_from_bytes};
# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
# let mut context = ShapeContext::new();
let mut shaper = context.builder(font)
.script(Script::Latin)
.build();
// We'll need the character map for our font
let charmap = font.charmap();
// And some storage for the cluster we're working with
let mut cluster = CharCluster::new();
// Now we build a cluster parser which takes a script and
// an iterator that yields a Token per character
let mut parser = Parser::new(
Script::Latin,
"a quick brown fox?".char_indices().map(|(i, ch)| Token {
// The character
ch,
// Offset of the character in code units
offset: i as u32,
// Length of the character in code units
len: ch.len_utf8() as u8,
// Character information
info: ch.into(),
// Pass through user data
data: 0,
})
);
// Loop over all of the clusters
while parser.next(&mut cluster) {
// Map all of the characters in the cluster
// to nominal glyph identifiers
cluster.map(|ch| charmap.map(ch));
// Add the cluster to the shaper
shaper.add_cluster(&cluster);
}
```
Phew! That's quite a lot of work. It also happens to be exactly what
[`add_str`](Shaper::add_str) does internally.
So why bother? As mentioned earlier, this method allows you to customize
the per-character data that passes through the shaper. Is your source text in
UTF-16 instead of UTF-8? No problem. Set the [`offset`](Token::offset) and
[`len`](Token::len) fields of your [`Token`]s to appropriate values. Are you shaping
across style spans? Set the [`data`](Token::data) field to the index of your span so
it can be recovered. Have you used the
[`Analyze`](crate::text::Analyze) iterator to generate
[`CharInfo`](crate::text::cluster::CharInfo)s containing boundary analysis? This
is where you apply them to the [`info`](Token::info) fields of your [`Token`]s.
That last one deserves a quick example, showing how you might build a cluster
parser with boundary analysis:
```
use swash::text::{analyze, Script};
use swash::text::cluster::{CharInfo, Parser, Token};
let text = "a quick brown fox?";
let mut parser = Parser::new(
Script::Latin,
text.char_indices()
// Call analyze passing the same text and zip
// the results
.zip(analyze(text.chars()))
// Analyze yields the tuple (Properties, Boundary)
.map(|((i, ch), (props, boundary))| Token {
ch,
offset: i as u32,
len: ch.len_utf8() as u8,
// Create character information from properties and boundary
info: CharInfo::new(props, boundary),
data: 0,
}),
);
```
That leaves us with font fallback. This crate does not provide the infrastructure
for such, but a small example can demonstrate the idea. The key is in
the return value of the [`CharCluster::map`] method which describes the
[`Status`](crate::text::cluster::Status) of the mapping operation. This function
will return the index of the best matching font:
```
use swash::FontRef;
use swash::text::cluster::{CharCluster, Status};
fn select_font<'a>(fonts: &[FontRef<'a>], cluster: &mut CharCluster) -> Option<usize> {
let mut best = None;
for (i, font) in fonts.iter().enumerate() {
let charmap = font.charmap();
match cluster.map(|ch| charmap.map(ch)) {
// This font provided a glyph for every character
Status::Complete => return Some(i),
// This font provided the most complete mapping so far
Status::Keep => best = Some(i),
// A previous mapping was more complete
Status::Discard => {}
}
}
best
}
```
Note that [`CharCluster`] maintains internal composed and decomposed sequences
of the characters in the cluster so that it can select the best form for each
candidate font.
Since this process is done during shaping, upon return we compare the selected
font with our current font and if they're different, we complete shaping for the
clusters submitted so far and continue the process by building a new shaper with
the selected font. By doing manual cluster parsing and nominal glyph mapping
_outside_ the shaper, we can implement per-cluster font fallback without the costly
technique of heuristically shaping runs.
# Collecting the prize
Finish up shaping by calling [`Shaper::shape_with`] with a closure that will be
invoked with each resulting [`GlyphCluster`]. This structure contains borrowed data
and thus cannot be stored directly. The data you extract from each cluster and the
method in which you store it will depend entirely on the design of your text layout
system.
Please note that, unlike HarfBuzz, this shaper does _not_ reverse runs that are in
right-to-left order. The reasoning is that, for correctness, line breaking must be
done in logical order and reversing runs should occur during bidi reordering.
Also pertinent to right-to-left runs: you'll need to ensure that you reverse
_clusters_ and not _glyphs_. Intra-cluster glyphs must remain in logical order
for proper mark placement.
*/
pub mod cluster;
#[doc(hidden)]
pub mod partition;
mod aat;
mod at;
mod buffer;
mod cache;
mod engine;
mod feature;
use cluster::*;
use super::{
cache::FontCache, charmap::Charmap, internal, metrics::Metrics, setting::Setting, FontRef,
NormalizedCoord,
};
use crate::text::{
cluster::{CharCluster, Parser, ShapeClass, Token},
Language, Script,
};
use alloc::vec::Vec;
use at::{FeatureMask, FeatureStore, FeatureStoreBuilder};
use buffer::*;
use cache::{FeatureCache, FontEntry};
use core::borrow::Borrow;
use engine::{Engine, EngineMode};
const DEFAULT_SIZE: usize = 16;
/// Text direction.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Direction {
LeftToRight,
RightToLeft,
}
/// Context that manages caches and transient buffers for shaping.
///
/// See the module level [documentation](index.html#building-the-shaper) for detail.
pub struct ShapeContext {
font_cache: FontCache<FontEntry>,
feature_cache: FeatureCache,
coords: Vec<i16>,
state: State,
}
impl ShapeContext {
/// Creates a new shaping context.
pub fn new() -> Self {
Self::with_max_entries(DEFAULT_SIZE)
}
/// Creates a new shaping context with the specified maximum number of
/// cache entries.
pub fn with_max_entries(max_entries: usize) -> Self {
let max_entries = max_entries.clamp(1, 64);
Self {
font_cache: FontCache::new(max_entries),
feature_cache: FeatureCache::new(max_entries),
coords: Vec::new(),
state: State::new(),
}
}
/// Creates a new builder for constructing a shaper with this context
/// and the specified font.
pub fn builder<'a>(&'a mut self, font: impl Into<FontRef<'a>>) -> ShaperBuilder<'a> {
ShaperBuilder::new(self, font)
}
/// Creates a new builder for constructing a shaper with this context
/// and the specified font.
pub fn builder_with_id<'a>(
&'a mut self,
font: impl Into<FontRef<'a>>,
id: [u64; 2],
) -> ShaperBuilder<'a> {
ShaperBuilder::new_with_id(self, font, id)
}
}
impl Default for ShapeContext {
fn default() -> Self {
Self::new()
}
}
struct State {
buffer: Buffer,
store_builder: FeatureStoreBuilder,
order: Vec<usize>,
glyphs: Vec<GlyphData>,
disable_kern: bool,
features: Vec<(u32, u16)>,
selectors: Vec<(u16, u16)>,
}
impl State {
pub fn new() -> Self {
Self {
buffer: Buffer::new(),
store_builder: FeatureStoreBuilder::default(),
order: Vec::new(),
glyphs: Vec::new(),
disable_kern: false,
features: Vec::new(),
selectors: Vec::new(),
}
}
pub fn reset(&mut self) {
self.buffer.clear();
self.features.clear();
self.disable_kern = false;
}
}
/// Builder for configuring a shaper.
///
/// See the module level [documentation](index.html#building-the-shaper) for more detail.
pub struct ShaperBuilder<'a> {
state: &'a mut State,
feature_cache: &'a mut FeatureCache,
font: FontRef<'a>,
font_id: [u64; 2],
font_entry: &'a FontEntry,
coords: &'a mut Vec<i16>,
charmap: Charmap<'a>,
dotted_circle: Option<u16>,
retain_ignorables: bool,
size: f32,
script: Script,
lang: Option<Language>,
dir: Direction,
}
impl<'a> ShaperBuilder<'a> {
/// Creates a new builder for configuring a shaper with the specified
/// context and font.
fn new(context: &'a mut ShapeContext, font: impl Into<FontRef<'a>>) -> Self {
let font = font.into();
let id = [font.key.value(), u64::MAX];
Self::new_with_id(context, font, id)
}
/// Creates a new builder for configuring a shaper with the specified
/// context and font.
fn new_with_id(
context: &'a mut ShapeContext,
font: impl Into<FontRef<'a>>,
id: [u64; 2],
) -> Self {
let font = font.into();
let (font_id, font_entry) = context.font_cache.get(&font, Some(id), FontEntry::new);
context.state.reset();
context.coords.clear();
Self {
state: &mut context.state,
feature_cache: &mut context.feature_cache,
font,
font_id,
font_entry,
coords: &mut context.coords,
charmap: font_entry.charmap.materialize(&font),
dotted_circle: None,
retain_ignorables: false,
size: 0.,
script: Script::Latin,
lang: None,
dir: Direction::LeftToRight,
}
}
/// Specifies the script. The default value is [`Script::Latin`].
pub fn script(mut self, script: Script) -> Self {
self.script = script;
self
}
/// Specifies the language. The default value is `None`.
pub fn language(mut self, language: Option<Language>) -> Self {
self.lang = language;
self
}
/// Specifies the text direction. The default value is [`Direction::LeftToRight`].
pub fn direction(mut self, direction: Direction) -> Self {
self.dir = direction;
self
}
/// Specifies the font size in pixels per em. The default value is `0`
/// which will produce glyphs with offsets and advances in font units.
pub fn size(mut self, ppem: f32) -> Self {
self.size = ppem.max(0.);
self
}
/// Adds feature settings to the shaper.
pub fn features<I>(self, settings: I) -> Self
where
I: IntoIterator,
I::Item: Into<Setting<u16>>,
{
for feature in settings {
let feature = feature.into();
if feature.tag == feature::KERN {
self.state.disable_kern = feature.value == 0;
}
self.state.features.push((feature.tag, feature.value));
}
self
}
/// Adds variation settings to the shaper.
pub fn variations<I>(self, settings: I) -> Self
where
I: IntoIterator,
I::Item: Into<Setting<f32>>,
{
if self.font_entry.coord_count != 0 {
let vars = self.font.variations();
self.coords.resize(vars.len(), 0);
for setting in settings {
let setting = setting.into();
for var in vars {
if var.tag() == setting.tag {
let value = var.normalize(setting.value);
if let Some(c) = self.coords.get_mut(var.index()) {
*c = value;
}
}
}
}
}
self
}
/// Specifies the variation settings in terms of normalized coordinates.
pub fn normalized_coords<I>(self, coords: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<NormalizedCoord>,
{
self.coords.clear();
self.coords.extend(coords.into_iter().map(|c| *c.borrow()));
self
}
/// Specifies whether to insert dotted circles for broken clusters. The
/// default value is `false`.
pub fn insert_dotted_circles(mut self, yes: bool) -> Self {
if yes {
let gid = self.charmap.map('\u{25cc}');
if gid != 0 {
self.dotted_circle = Some(gid);
}
} else {
self.dotted_circle = None;
}
self
}
/// Specifies whether characters defined as default ignorable should be
/// retained by the shaper. The default is `false`.
pub fn retain_ignorables(mut self, yes: bool) -> Self {
self.retain_ignorables = yes;
self
}
/// Builds a shaper for the current configuration.
pub fn build(self) -> Shaper<'a> {
let engine = Engine::new(
&self.font_entry.metadata,
self.font.data,
&self.coords[..],
self.script,
self.lang,
);
self.state.buffer.dotted_circle = self.dotted_circle;
let rtl = self.dir == Direction::RightToLeft;
self.state.buffer.is_rtl = rtl;
let (store, sub_mask, pos_mask) = if engine.use_ot {
use cache::FeatureCacheEntry;
let store = match self.feature_cache.entry(
self.font_id,
&self.coords[..],
engine.has_feature_vars(),
engine.tags(),
) {
FeatureCacheEntry::Present(store) => store,
FeatureCacheEntry::New(store) => {
engine.collect_features(&mut self.state.store_builder, store);
store
}
};
let buf = &mut self.state.buffer;
let (sub, pos) = store.custom_masks(
&self.state.features[..],
&mut buf.sub_args,
&mut buf.pos_args,
self.dir,
);
(Some(store as _), sub, pos)
} else {
(None, FeatureMask::default(), FeatureMask::default())
};
Shaper {
state: self.state,
font: self.font,
font_entry: self.font_entry,
charmap: self.charmap,
retain_ignorables: self.retain_ignorables,
size: self.size,
script: self.script,
joined: engine.use_ot && self.script.is_joined(),
dir: self.dir,
engine,
store,
sub_mask,
pos_mask,
}
}
}
/// Maps character clusters to positioned glyph clusters according to
/// typographic rules and features.
///
/// See the module level [documentation](index.html#feeding-the-shaper) for detail.
pub struct Shaper<'a> {
state: &'a mut State,
font: FontRef<'a>,
font_entry: &'a FontEntry,
charmap: Charmap<'a>,
retain_ignorables: bool,
size: f32,
script: Script,
joined: bool,
dir: Direction,
engine: Engine<'a>,
store: Option<&'a FeatureStore>,
sub_mask: FeatureMask,
pos_mask: FeatureMask,
}
impl<'a> Shaper<'a> {
/// Adds a character cluster to the shaper.
pub fn add_cluster(&mut self, cluster: &CharCluster) {
let buf = &mut self.state.buffer;
match self.engine.mode {
EngineMode::Simple => {
buf.push(cluster);
}
EngineMode::Myanmar => {
let e = &mut self.engine;
let s = self.store.unwrap();
let chars = cluster.mapped_chars();
reorder_myanmar(chars, &mut self.state.order);
let range = buf.push_order(cluster, &self.state.order);
e.set_classes(buf, Some(range.clone()));
let start = range.start;
e.gsub(s, s.groups.default, buf, Some(range));
let end = buf.len();
e.gsub(s, s.groups.reph, buf, Some(start..end));
let end = buf.len();
e.gsub(s, s.groups.pref, buf, Some(start..end));
let end = buf.len();
e.gsub(s, s.groups.stage1, buf, Some(start..end));
let end = buf.len();
e.gsub(s, s.groups.stage2, buf, Some(start..end));
}
EngineMode::Complex => {
let e = &mut self.engine;
let s = self.store.unwrap();
let range = buf.push(cluster);
e.set_classes(buf, Some(range.clone()));
let start = range.start;
// Default group
e.gsub(s, s.groups.default, buf, Some(range.clone()));
for g in &mut buf.glyphs[range] {
if g.char_class == ShapeClass::Halant && g.flags & SUBSTITUTED != 0 {
// Don't prevent reordering across a virama that has been substituted
g.char_class = ShapeClass::Other;
}
}
// Reph identification
let len = 3.min(buf.glyphs.len() - start);
let end = start + len;
buf.clear_flags(buffer::SUBSTITUTED, Some(start..end));
e.gsub(s, s.groups.reph, buf, Some(start..end));
for g in &mut buf.glyphs[start..end] {
if g.flags & buffer::SUBSTITUTED != 0 {
g.char_class = ShapeClass::Reph;
break;
}
}
// Pref identification
let end = buf.len();
buf.clear_flags(buffer::SUBSTITUTED, Some(start..end));
e.gsub(s, s.groups.pref, buf, Some(start..end));
for g in &mut buf.glyphs[start..end] {
if g.flags & buffer::SUBSTITUTED != 0 {
g.char_class = ShapeClass::Pref;
break;
}
}
// Orthographic group
let end = buf.len();
e.gsub(s, s.groups.stage1, buf, Some(start..end));
// Reordering
let len = (buf.len() - start).min(64);
let end = start + len;
reorder_complex(
&mut buf.glyphs[start..end],
&mut self.state.glyphs,
&mut self.state.order,
);
}
}
}
/// Adds a string to the shaper.
pub fn add_str(&mut self, s: &str) {
use crate::text::Codepoint;
let mut cluster = CharCluster::new();
let mut parser = Parser::new(
self.script,
s.char_indices().map(|(i, ch)| Token {
ch,
offset: i as u32,
len: ch.len_utf8() as u8,
info: ch.properties().into(),
data: 0,
}),
);
let charmap = self.charmap;
while parser.next(&mut cluster) {
cluster.map(|ch| charmap.map(ch));
self.add_cluster(&cluster);
}
}
/// Returns the current normalized variation coordinates in use by the
/// shaper.
pub fn normalized_coords(&self) -> &[NormalizedCoord] {
self.engine.coords
}
/// Returns the current font metrics in use by the shaper.
pub fn metrics(&self) -> Metrics {
let scale = if self.size != 0. { self.size } else { 1. };
self.font_entry
.metrics
.materialize_metrics(&self.font, self.engine.coords)
.scale(scale)
}
/// Shapes the text and invokes the specified closure with each
/// resulting glyph cluster.
pub fn shape_with(mut self, mut f: impl FnMut(&GlyphCluster)) {
self.finish();
let buf = &mut self.state.buffer;
buf.shaped_glyphs.clear();
let mut sentinel = (
buffer::GlyphData::default(),
buffer::PositionData::default(),
);
sentinel.0.cluster = buf.ranges.len() as u32;
let mut last_cluster = 0;
for (g, p) in buf
.glyphs
.iter()
.zip(&buf.positions)
.chain(core::iter::once((&sentinel.0, &sentinel.1)))
{
if g.cluster != last_cluster {
// Simple and common case: no ligatures and no empty clusters.
if last_cluster > g.cluster || g.cluster - last_cluster == 1 {
let index = last_cluster as usize;
let info = &buf.infos[index];
let cluster = GlyphCluster {
source: buf.ranges[index],
info: info.0,
glyphs: &buf.shaped_glyphs,
components: &[],
data: info.2,
};
f(&cluster);
buf.shaped_glyphs.clear();
} else {
// Collect the range for the non-empty cluster.
let end = g.cluster as usize;
let start = last_cluster as usize;
let mut group_end = start + 1;
while group_end < end && buf.infos[group_end].1 {
group_end += 1;
}
if !buf.shaped_glyphs.is_empty() {
// We have some glyphs. Emit the cluster.
let mut source = buf.ranges[start];
source.end = buf.ranges[group_end - 1].end;
// If the range spans more than one cluster, we have a ligature.
let components = if group_end > start + 1 {
&buf.ranges[start..group_end]
} else {
&[]
};
let info = &buf.infos[start];
let cluster = GlyphCluster {
source,
info: info.0,
glyphs: &buf.shaped_glyphs,
components,
data: info.2,
};
f(&cluster);
buf.shaped_glyphs.clear();
}
if end > group_end {
// We have a trailing sequence of empty clusters. Emit
// them one by one.
for (info, source) in buf.infos[group_end..end]
.iter()
.zip(&buf.ranges[group_end..end])
{
let cluster = GlyphCluster {
source: *source,
info: info.0,
glyphs: &[],
components: &[],
data: info.2,
};
f(&cluster);
}
}
}
}
last_cluster = g.cluster;
if self.retain_ignorables || g.flags & IGNORABLE == 0 {
buf.shaped_glyphs.push(Glyph::new(g, p));
}
}
}
// FIXME: when writing docs, I realized that it's impossible
// to use the result of this function correctly with RTL runs
// that contain marks.
// /// Shapes the text and invokes the specified closure with each
// /// resulting glyph.
// pub fn shape_glyphs_with(mut self, mut f: impl FnMut(&Glyph)) {
// self.finish();
// let buf = &self.state.buffer;
// for (g, p) in buf.glyphs.iter().zip(&buf.positions) {
// if g.flags & IGNORABLE == 0 {
// f(&Glyph::new(g, p))
// }
// }
// }
fn finish(&mut self) {
use engine::{PosMode, SubMode};
if self.state.buffer.glyphs.is_empty() {
return;
}
let e = &mut self.engine;
let buf = &mut self.state.buffer;
match e.mode {
EngineMode::Simple => match e.sub_mode {
SubMode::Gsub => {
let s = self.store.unwrap();
e.set_classes(buf, None);
if self.joined {
buf.set_join_masks();
}
e.gsub(s, self.sub_mask, buf, None);
}
SubMode::Morx => {
e.collect_selectors(&self.state.features, &mut self.state.selectors);
e.morx(buf, &self.state.selectors);
}
_ => {}
},
EngineMode::Myanmar => {
let s = self.store.unwrap();
e.gsub(s, self.sub_mask | s.groups.stage2, buf, None);
}
EngineMode::Complex => {
let s = self.store.unwrap();
if self.joined {
buf.set_join_masks();
e.gsub(s, s.groups.stage2 | self.sub_mask, buf, None);
} else {
e.gsub(s, self.sub_mask, buf, None);
}
}
}
buf.setup_positions(e.sub_mode == SubMode::Morx);
match e.pos_mode {
PosMode::Gpos => {
let s = self.store.unwrap();
e.gpos(s, self.pos_mask, buf, None);
}
PosMode::Kerx => {
e.kerx(buf, self.state.disable_kern);
}
PosMode::Kern => {
if !self.state.disable_kern {
e.kern(buf);
}
}
_ => {}
}
// let metrics = self
// .font_entry
// .metrics
// .materialize_metrics(self.font.data, self.engine.coords);
let glyph_metrics = self
.font_entry
.metrics
.materialize_glyph_metrics(&self.font, self.engine.coords);
for (g, p) in buf.glyphs.iter_mut().zip(buf.positions.iter_mut()) {
if g.flags & MARK_ATTACH == 0 {
p.advance += glyph_metrics.advance_width(g.id);
}
g.flags |= p.flags;
}
if buf.has_cursive {
if self.dir == Direction::RightToLeft {
for (i, g) in buf.glyphs.iter().enumerate().rev() {
if g.flags & buffer::CURSIVE_ATTACH != 0 {
let base_offset = buf.positions[i].base as usize;
if base_offset != 0 {
let (x, y) = {
let base = &buf.positions[i + base_offset];
(base.x, base.y)
};
let pos = &mut buf.positions[i];
pos.x += x;
pos.y += y;
}
}
}
} else {
for (i, g) in buf.glyphs.iter().enumerate() {
if g.flags & buffer::CURSIVE_ATTACH != 0 {
let base_offset = buf.positions[i].base as usize;
if base_offset != 0 {
let (x, y) = {
let base = &buf.positions[i + base_offset];
(base.x, base.y)
};
let pos = &mut buf.positions[i];
pos.x += x;
pos.y += y;
}
}
}
}
}
if buf.has_marks {
fn round_f32(f: f32) -> f32 {
f
}
for (i, g) in buf.glyphs.iter().enumerate() {
if g.flags & buffer::MARK_ATTACH != 0 {
let base_offset = buf.positions[i].base as usize;
if base_offset != 0 {
let (x, y) = {
let base = &buf.positions[i - base_offset];
(base.x - round_f32(base.advance), base.y)
};
let pos = &mut buf.positions[i];
pos.x += x;
pos.y += y;
}
}
}
}
let upem = glyph_metrics.units_per_em();
if self.size != 0. && upem != 0 {
let s = self.size / upem as f32;
for p in buf.positions.iter_mut() {
p.x *= s;
p.y *= s;
p.advance *= s;
}
}
}
}

253
vendor/swash/src/shape/partition.rs vendored Normal file
View File

@@ -0,0 +1,253 @@
/*!
Shaper that partitions by font using trait based per-cluster selection.
*/
use super::{Direction, ShapeContext, Shaper};
use crate::text::cluster::{CharCluster, Parser, Token};
use crate::text::{Codepoint as _, Language, Script};
use crate::{FontRef, Setting, Synthesis};
use core::iter::Copied;
use core::slice;
/// Trait for types that can select appropriate fonts for character clusters.
pub trait Selector {
/// Type of font returned by the provider.
type SelectedFont: SelectedFont;
/// Selects a font for the specified character cluster.
fn select_font(&mut self, cluster: &mut CharCluster) -> Option<Self::SelectedFont>;
}
/// Trait for a font provided by a font selector.
pub trait SelectedFont: PartialEq {
/// Returns a reference to the underlying font.
fn font(&self) -> FontRef;
fn id_override(&self) -> Option<[u64; 2]> {
None
}
/// Returns an optional synthesis state for the font.
fn synthesis(&self) -> Option<Synthesis> {
None
}
}
/// Trait for types that specify shaping options.
pub trait ShapeOptions {
/// Iterator over the feature settings for a fragment.
type Features: Iterator<Item = Setting<u16>>;
/// Iterator over the variation settings for a fragment.
type Variations: Iterator<Item = Setting<f32>>;
/// Returns the script of the text in the fragment.
fn script(&self) -> Script {
Script::Latin
}
/// Returns the language of the text in the fragment.
fn language(&self) -> Option<Language> {
None
}
/// Returns the direction for the fragment.
fn direction(&self) -> Direction {
Direction::LeftToRight
}
/// Returns the font size for the fragment.
fn size(&self) -> f32 {
0.
}
/// Returns an iterator over the feature settings for the fragment.
fn features(&self) -> Self::Features;
/// Returns an iterator over the variation settings for the fragment.
fn variations(&self) -> Self::Variations;
/// Returns true if the shaper should insert dotted circles for broken
/// clusters in the fragment.
fn insert_dotted_circles(&self) -> bool {
false
}
}
/// Simple implementation of the shape options trait.
#[derive(Copy, Clone)]
pub struct SimpleShapeOptions<'a> {
/// Script for the fragment.
pub script: Script,
/// Language for the fragment.
pub language: Option<Language>,
/// Text direction of the fragment.
pub direction: Direction,
/// Font size for the fragment.
pub size: f32,
/// Feature settings for the fragment.
pub features: &'a [Setting<u16>],
/// Variation settings for the fragment.
pub variations: &'a [Setting<f32>],
/// True if the shaper should insert dotted circles for broken clusters.
pub insert_dotted_circles: bool,
}
impl Default for SimpleShapeOptions<'_> {
fn default() -> Self {
Self {
script: Script::Latin,
language: None,
direction: Direction::LeftToRight,
size: 0.,
features: &[],
variations: &[],
insert_dotted_circles: false,
}
}
}
impl<'a> ShapeOptions for SimpleShapeOptions<'a> {
type Features = Copied<slice::Iter<'a, Setting<u16>>>;
type Variations = Copied<slice::Iter<'a, Setting<f32>>>;
fn script(&self) -> Script {
self.script
}
fn language(&self) -> Option<Language> {
self.language
}
fn direction(&self) -> Direction {
self.direction
}
fn size(&self) -> f32 {
self.size
}
fn features(&self) -> Self::Features {
self.features.iter().copied()
}
fn variations(&self) -> Self::Variations {
self.variations.iter().copied()
}
fn insert_dotted_circles(&self) -> bool {
self.insert_dotted_circles
}
}
/// Shapes a run of text (provided as a [`Token`] iterator) using the specified [`Selector`] to assign
/// per-cluster fonts. Invokes the specified closure `f` for each contiguous fragment with the chosen font
/// and a prepared [`Shaper`] that contains the cluster content of the fragment.
pub fn shape<S, T>(
context: &mut ShapeContext,
selector: &mut S,
options: &impl ShapeOptions,
tokens: T,
mut f: impl FnMut(&S::SelectedFont, Shaper),
) where
S: Selector,
T: IntoIterator<Item = Token>,
T::IntoIter: Clone,
{
let mut cluster = CharCluster::new();
if options.direction() == Direction::RightToLeft {
let mut parser = Parser::new(
options.script(),
tokens.into_iter().map(|mut t| {
t.ch = t.ch.mirror().unwrap_or(t.ch);
t
}),
);
if !parser.next(&mut cluster) {
return;
}
let mut font = selector.select_font(&mut cluster);
while shape_clusters(
context,
selector,
options,
&mut parser,
&mut cluster,
&mut font,
&mut f,
) {}
} else {
let mut parser = Parser::new(options.script(), tokens.into_iter());
if !parser.next(&mut cluster) {
return;
}
let mut font = selector.select_font(&mut cluster);
while shape_clusters(
context,
selector,
options,
&mut parser,
&mut cluster,
&mut font,
&mut f,
) {}
}
}
fn shape_clusters<S, T>(
context: &mut ShapeContext,
selector: &mut S,
options: &impl ShapeOptions,
parser: &mut Parser<T>,
cluster: &mut CharCluster,
current_font: &mut Option<S::SelectedFont>,
f: &mut impl FnMut(&S::SelectedFont, Shaper),
) -> bool
where
S: Selector,
T: Iterator<Item = Token> + Clone,
{
if current_font.is_none() {
return false;
}
let font = current_font.as_ref().unwrap();
let font_ref = font.font();
let id = font
.id_override()
.unwrap_or([font_ref.key.value(), u64::MAX]);
let mut shaper = context
.builder_with_id(font.font(), id)
.script(options.script())
.language(options.language())
.direction(options.direction())
.size(options.size())
.features(options.features())
.variations(
font.synthesis()
.as_ref()
.map(|s| s.variations())
.unwrap_or(&[])
.iter()
.copied(),
)
.variations(options.variations())
.insert_dotted_circles(options.insert_dotted_circles())
.build();
loop {
shaper.add_cluster(cluster);
if !parser.next(cluster) {
f(font, shaper);
return false;
}
if let Some(next_font) = selector.select_font(cluster) {
if next_font != *font {
f(font, shaper);
*current_font = Some(next_font);
return true;
}
} else {
return false;
}
}
}

835
vendor/swash/src/strike.rs vendored Normal file
View File

@@ -0,0 +1,835 @@
//! Alpha and color bitmaps.
use super::internal::*;
use super::{FontRef, GlyphId};
#[cfg(feature = "scale")]
use alloc::vec::Vec;
#[cfg(all(feature = "libm", feature = "scale"))]
use core_maths::CoreFloat;
/// Proxy for rematerializing strike collections.
#[derive(Copy, Clone)]
pub struct BitmapStrikesProxy {
bitmaps: (u32, u32),
color_bitmaps: (u32, u32),
upem: u16,
is_apple: bool,
}
impl BitmapStrikesProxy {
/// Creates strike collection proxy from the specified font.
pub fn from_font<'a>(font: &FontRef<'a>) -> Self {
let upem = font.head().map(|h| h.units_per_em()).unwrap_or(1);
let mut bitmaps = (0, 0);
let eblc = font.table_offset(raw_tag(b"EBLC"));
if eblc != 0 {
let ebdt = font.table_offset(raw_tag(b"EBDT"));
if ebdt != 0 {
bitmaps = (eblc, ebdt);
}
}
let mut color_bitmaps = (0, 0);
let sbix = font.table_offset(raw_tag(b"sbix"));
let mut is_apple = false;
if sbix != 0 {
color_bitmaps = (sbix, sbix);
use super::string::StringId::Family;
if let Some(name) = font.localized_strings().find_by_id(Family, None) {
is_apple = name.chars().eq("Apple Color Emoji".chars());
}
} else {
let cblc = font.table_offset(raw_tag(b"CBLC"));
if cblc != 0 {
let cbdt = font.table_offset(raw_tag(b"CBDT"));
if cbdt != 0 {
color_bitmaps = (cblc, cbdt);
}
}
}
Self {
bitmaps,
color_bitmaps,
upem,
is_apple,
}
}
/// Returns true if the font has alpha bitmap strikes.
pub fn has_alpha(&self) -> bool {
self.bitmaps.0 != 0
}
/// Returns true if the font has color bitmap strikes.
pub fn has_color(&self) -> bool {
self.color_bitmaps.0 != 0
}
/// Materializes an alpha strike iterator for the specified font. This
/// proxy must have been created from the same font.
pub fn materialize_alpha<'a>(&self, font: &FontRef<'a>) -> BitmapStrikes<'a> {
self.materialize_impl(font.data, self.bitmaps.0, self.bitmaps.1, self.upem, false)
}
/// Materializes a color strike iterator for the specified font. This
/// proxy must have been created from the same font.
pub fn materialize_color<'a>(&self, font: &FontRef<'a>) -> BitmapStrikes<'a> {
self.materialize_impl(
font.data,
self.color_bitmaps.0,
self.color_bitmaps.1,
self.upem,
self.is_apple,
)
}
fn materialize_impl<'a>(
&self,
data: &'a [u8],
loc: u32,
dat: u32,
upem: u16,
is_apple: bool,
) -> BitmapStrikes<'a> {
if loc == 0 {
BitmapStrikes::new(&[], &[], upem, false, false)
} else if loc == dat {
let loc = data.get(loc as usize..).unwrap_or(&[]);
BitmapStrikes::new(loc, loc, upem, true, is_apple)
} else {
let loc = data.get(loc as usize..).unwrap_or(&[]);
let dat = data.get(dat as usize..).unwrap_or(&[]);
BitmapStrikes::new(loc, dat, upem, false, false)
}
}
}
/// Iterator over a collection of bitmap strikes.
#[derive(Copy, Clone)]
pub struct BitmapStrikes<'a> {
data: Bytes<'a>,
bitmap_data: &'a [u8],
is_sbix: bool,
is_apple: bool,
upem: u16,
len: usize,
pos: usize,
}
impl<'a> BitmapStrikes<'a> {
fn new(
data: &'a [u8],
bitmap_data: &'a [u8],
upem: u16,
is_sbix: bool,
is_apple: bool,
) -> Self {
let data = Bytes::new(data);
Self {
data,
bitmap_data,
upem,
is_sbix,
is_apple,
len: data.read_or_default::<u32>(4) as usize,
pos: 0,
}
}
/// Returns the bitmap strike at the specified index.
fn get(&self, index: usize) -> Option<BitmapStrike<'a>> {
if index >= self.len {
return None;
}
let offset = if self.is_sbix {
self.data.read::<u32>(8 + index * 4)? as usize
} else {
8 + index * 48
};
Some(BitmapStrike {
data: self.data,
bitmap_data: self.bitmap_data,
upem: self.upem,
is_sbix: self.is_sbix,
is_apple: self.is_apple,
offset,
})
}
/// Searches for a strike that matches the specified size and glyph
/// identifier. Returns the strike of the nearest suitable size, preferring
/// larger strikes if no exact match is available.
///
/// ## Iteration behavior
/// This function searches the entire strike collection without regard
/// for the current state of the iterator.
pub fn find_by_nearest_ppem(&self, ppem: u16, glyph_id: GlyphId) -> Option<BitmapStrike<'a>> {
let mut best = None;
let mut best_size = 0;
for i in 0..self.len {
let strike = match self.get(i) {
Some(strike) => strike,
_ => continue,
};
if !strike.contains(glyph_id) {
continue;
}
best = Some(strike);
let strike_ppem = strike.ppem();
if strike_ppem > best_size {
best = Some(strike);
best_size = strike_ppem;
}
if strike_ppem >= ppem {
return Some(strike);
}
}
best
}
/// Searches for a strike that exactly matches the specified size and glyph
/// identifier.
///
/// ## Iteration behavior
/// This function searches the entire strike collection without regard
/// for the current state of the iterator.
pub fn find_by_exact_ppem(&self, ppem: u16, glyph_id: GlyphId) -> Option<BitmapStrike<'a>> {
for i in 0..self.len {
let strike = match self.get(i) {
Some(strike) => strike,
_ => continue,
};
if !strike.contains(glyph_id) {
continue;
}
if strike.ppem() == ppem {
return Some(strike);
}
}
None
}
/// Searches for a strike with the largest size that contains the specified
/// glyph.
///
/// ## Iteration behavior
/// This function searches the entire strike collection without regard
/// for the current state of the iterator.
pub fn find_by_largest_ppem(&self, glyph_id: GlyphId) -> Option<BitmapStrike<'a>> {
let mut largest = None;
let mut largest_ppem = 0;
for i in 0..self.len {
let strike = match self.get(i) {
Some(strike) => strike,
_ => continue,
};
if !strike.contains(glyph_id) {
continue;
}
let strike_ppem = strike.ppem();
if largest.is_none() || strike_ppem > largest_ppem {
largest = Some(strike);
largest_ppem = strike_ppem;
}
}
largest
}
}
impl_iter!(BitmapStrikes, BitmapStrike);
/// Collection of bitmaps of a specific size and format.
#[derive(Copy, Clone)]
#[allow(dead_code)]
pub struct BitmapStrike<'a> {
data: Bytes<'a>,
bitmap_data: &'a [u8],
offset: usize,
upem: u16,
is_sbix: bool,
is_apple: bool,
}
impl<'a> BitmapStrike<'a> {
/// Returns the device pixel density for which the strike was designed.
pub fn ppi(&self) -> u16 {
if self.is_sbix {
self.data.read_or_default::<u16>(self.offset + 2)
} else {
72
}
}
/// Returns the bit depth of the strike.
pub fn bit_depth(&self) -> u8 {
if self.is_sbix {
return 32;
}
self.data.read_or_default::<u8>(self.offset + 46)
}
/// Returns the size of the strike in pixels per em.
pub fn ppem(&self) -> u16 {
if self.is_sbix {
self.data.read_or_default::<u16>(self.offset)
} else {
self.data.read_or_default::<u8>(self.offset + 45) as u16
}
}
/// Returns true if the specified glyph is covered by the strike.
pub fn contains(&self, glyph_id: GlyphId) -> bool {
get_coverage(&self.data, self.offset, self.is_sbix, glyph_id).unwrap_or(false)
}
/// Returns the bitmap for the specified glyph, if available.
#[cfg(feature = "scale")]
pub(crate) fn get(&self, glyph_id: GlyphId) -> Option<Bitmap<'a>> {
let loc = get_location(&self.data, self.offset, self.is_sbix, glyph_id)?;
loc.get(self.bitmap_data, self.upem, self.is_apple)
}
}
/// Format of a bitmap.
#[cfg(feature = "scale")]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum BitmapFormat {
/// Alpha mask with the specified number of bits.
Alpha(u8),
/// Packed alpha mask with the specified number of bits.
Packed(u8),
/// 32-bit RGBA color.
Color,
/// PNG encoded.
Png,
}
#[cfg(feature = "scale")]
impl BitmapFormat {
/// Returns the number of channels.
pub fn channels(&self) -> u32 {
match self {
Self::Alpha(..) | Self::Packed(..) => 1,
_ => 4,
}
}
/// Returns the buffer size for a bitmap of the specified dimensions in this
/// format.
pub fn buffer_size(&self, width: u32, height: u32) -> usize {
(width * height * self.channels()) as usize
}
}
/// Bitmap glyph.
#[cfg(feature = "scale")]
#[derive(Copy, Clone)]
pub struct Bitmap<'a> {
pub format: BitmapFormat,
pub ppem: u16,
pub width: u32,
pub height: u32,
pub left: i32,
pub top: i32,
pub data: &'a [u8],
}
#[cfg(feature = "scale")]
impl<'a> Bitmap<'a> {
fn new(data: &BitmapData<'a>, upem: u16, is_apple: bool) -> Option<Self> {
let format = if data.is_packed {
BitmapFormat::Packed(data.bit_depth)
} else if data.is_png {
BitmapFormat::Png
} else if data.bit_depth == 32 {
BitmapFormat::Color
} else {
BitmapFormat::Alpha(data.bit_depth)
};
let (width, height, left, top) = if data.is_png {
let png = Bytes::new(data.data);
let width = png.read::<u32>(16)?;
let height = png.read::<u32>(20)?;
let (left, mut top) = (data.metrics.x as i32, data.metrics.y as i32);
if data.is_sbix {
if top == 0 && is_apple {
let s = data.ppem as f32 / upem as f32;
top = (-100. * s).round() as i32;
}
top += height as i32;
}
(width, height, left, top)
} else {
(
data.width as u32,
data.height as u32,
data.metrics.x as i32,
data.metrics.y as i32,
)
};
Some(Bitmap {
format,
ppem: data.ppem,
width,
height,
left,
top,
data: data.data,
})
}
/// Returns the size of an image buffer necessary for storing the decoded bitmap.
pub fn decoded_size(&self) -> usize {
self.format.buffer_size(self.width, self.height)
}
/// Returns the width, height and buffer size for a scaled version of the bitmap.
pub fn scaled_size(&self, size: f32) -> (u32, u32, usize) {
let mut w = self.width;
let mut h = self.height;
if size != 0. {
let scale = size / self.ppem as f32;
w = (w as f32 * scale) as u32;
h = (h as f32 * scale) as u32;
}
(w, h, self.format.buffer_size(w, h))
}
/// Decodes the bitmap into the specified target buffer.
pub fn decode(&self, scratch: Option<&mut Vec<u8>>, target: &mut [u8]) -> bool {
let mut tmp = Vec::new();
let scratch = if let Some(scratch) = scratch {
scratch
} else {
&mut tmp
};
let size = self.decoded_size();
if target.len() < size {
return false;
}
let w = self.width as usize;
let h = self.height as usize;
let src = self.data;
let dst = &mut *target;
match self.format {
BitmapFormat::Packed(bits) => match bits {
1 => {
for x in 0..(w * h) {
dst[x] = (src[x >> 3] >> (!x & 7) & 1) * 255;
}
}
2 => {
for x in 0..(w * h) {
dst[x] = (src[x >> 2] >> (!(x * 2) & 2) & 3) * 85;
}
}
4 => {
for x in 0..(w * h) {
dst[x] = (src[x >> 1] >> (!(x * 4) & 4) & 15) * 17;
}
}
8 | 32 => {
dst.copy_from_slice(src);
}
_ => return false,
},
BitmapFormat::Alpha(bits) => match bits {
1 => {
let mut dst_idx = 0;
for row in src.chunks(((w * bits as usize) + 7) / 8) {
for x in 0..w {
dst[dst_idx] = (row[x >> 3] >> (!x & 7) & 1) * 255;
dst_idx += 1;
}
}
}
2 => {
let mut dst_idx = 0;
for row in src.chunks(((w * bits as usize) + 7) / 8) {
for x in 0..w {
dst[dst_idx] = (row[x >> 2] >> (!(x * 2) & 2) & 3) * 85;
dst_idx += 1;
}
}
}
4 => {
let mut dst_idx = 0;
for row in src.chunks(((w * bits as usize) + 7) / 8) {
for x in 0..w {
dst[dst_idx] = (row[x >> 1] >> (!(x * 4) & 4) & 15) * 17;
dst_idx += 1;
}
}
}
8 | 32 => {
dst.copy_from_slice(src);
}
_ => return false,
},
BitmapFormat::Color => {
dst.copy_from_slice(src);
}
BitmapFormat::Png => {
use super::scale::decode_png;
scratch.clear();
if decode_png(src, scratch, target).is_none() {
return false;
}
}
}
true
}
}
/// The location of a bitmap in the bitmap data table.
#[cfg(feature = "scale")]
#[derive(Copy, Clone)]
struct Location {
format: u8,
flags: u8,
offset: u32,
size: u32,
ppem: u16,
bit_depth: u8,
width: u8,
height: u8,
metrics: Metrics,
vertical_metrics: Metrics,
}
#[cfg(feature = "scale")]
impl Location {
/// Gets a bitmap from this location in the specified data source.
pub fn get<'a>(&self, data: &'a [u8], upem: u16, is_apple: bool) -> Option<Bitmap<'a>> {
Bitmap::new(&get_data(data, self)?, upem, is_apple)
}
}
fn get_coverage(table: &[u8], strike_base: usize, is_sbix: bool, glyph_id: u16) -> Option<bool> {
if is_sbix {
return Some(sbix_range(table, strike_base, glyph_id, 0).is_some());
}
let b = Bytes::with_offset(table, strike_base)?;
if glyph_id < b.read(40)? || glyph_id > b.read(42)? {
return None;
}
let count = b.read::<u32>(8)? as usize;
let array_offset = b.read::<u32>(0)? as usize;
let b = Bytes::with_offset(table, array_offset)?;
for i in 0..count {
let offset = i * 8;
let first = b.read::<u16>(offset)?;
if glyph_id < first {
return None;
}
if glyph_id > b.read::<u16>(offset + 2)? {
continue;
}
return Some(true);
}
None
}
#[cfg(feature = "scale")]
fn get_location(
table: &[u8],
strike_base: usize,
is_sbix: bool,
glyph_id: u16,
) -> Option<Location> {
let d = Bytes::new(table);
if is_sbix {
let (start, end) = sbix_range(table, strike_base, glyph_id, 0)?;
let len = (end - start) as usize;
let start = start as usize;
let x = d.read::<i16>(start)?;
let y = d.read::<i16>(start + 2)?;
let ppem = d.read::<u16>(strike_base)?;
return Some(Location {
ppem,
bit_depth: 32,
width: 0,
height: 0,
metrics: Metrics {
x: x as i8,
y: y as i8,
advance: 0,
},
vertical_metrics: Metrics::default(),
format: 0xFF,
flags: 1,
offset: start as u32 + 8,
size: len as u32 - 8,
});
}
if glyph_id < d.read(strike_base + 40)? || glyph_id > d.read(strike_base + 42)? {
return None;
}
let count = d.read::<u32>(strike_base + 8)? as usize;
let ppem = d.read::<u8>(strike_base + 45)? as u16;
let bit_depth = d.read::<u8>(strike_base + 46)?;
let flags = d.read::<u8>(strike_base + 47)?;
let array_offset = d.read::<u32>(strike_base)? as usize;
for i in 0..count {
let offset = array_offset + i * 8;
let first = d.read::<u16>(offset)?;
if glyph_id < first {
return None;
}
if glyph_id > d.read::<u16>(offset + 2)? {
continue;
}
let offset = array_offset + d.read::<u32>(offset + 4)? as usize;
let index_format = d.read::<u16>(offset)?;
let image_format = d.read::<u16>(offset + 2)? as u8;
let image_offset = d.read::<u32>(offset + 4)?;
let base = offset + 8;
let mut loc = Location {
ppem,
bit_depth,
width: 0,
height: 0,
metrics: Metrics::default(),
vertical_metrics: Metrics::default(),
format: image_format,
flags,
offset: 0,
size: 0,
};
match index_format {
1 => {
loc.offset =
image_offset + d.read::<u32>(base + (glyph_id - first) as usize * 4)?;
return Some(loc);
}
2 => {
loc.size = d.read::<u32>(base)?;
loc.offset = image_offset + loc.size * (glyph_id - first) as u32;
let (w, h) = get_metrics(
&d,
base + 4,
flags,
true,
&mut loc.metrics,
&mut loc.vertical_metrics,
)?;
loc.width = w;
loc.height = h;
return Some(loc);
}
3 => {
loc.offset =
image_offset + d.read::<u16>(base + (glyph_id - first) as usize * 2)? as u32;
return Some(loc);
}
4 => {
let mut l = 0;
let mut h = d.read::<u32>(base)? as usize;
while l < h {
use core::cmp::Ordering::*;
let i = (l + h) / 2;
let rec = base + i * 4;
let id = d.read::<u16>(rec)?;
match glyph_id.cmp(&id) {
Less => h = i,
Greater => l = i + i,
Equal => {
let offset1 = d.read::<u16>(rec + 2)? as u32;
let offset2 = d.read::<u16>(rec + 6)? as u32;
if offset2 <= offset1 {
return None;
}
loc.offset = image_offset + offset1;
loc.size = offset2 - offset1;
return Some(loc);
}
}
}
}
_ => {
return None;
}
}
}
None
}
#[cfg(feature = "scale")]
fn get_data<'a>(table: &'a [u8], loc: &Location) -> Option<BitmapData<'a>> {
let depth = loc.bit_depth as usize;
let mut bitmap = BitmapData {
data: &[],
ppem: loc.ppem,
bit_depth: loc.bit_depth,
width: loc.width,
height: loc.height,
metrics: loc.metrics,
vertical_metrics: loc.vertical_metrics,
is_packed: false,
is_png: false,
is_sbix: false,
};
let d = &Bytes::new(table);
let offset = loc.offset as usize;
let flags = loc.flags;
let size = loc.size as usize;
match loc.format {
0xFF => {
bitmap.data = d.read_bytes(offset, size)?;
bitmap.is_png = true;
bitmap.is_sbix = true;
Some(bitmap)
}
1 => {
bitmap.read_metrics(d, offset, flags, false)?;
let w = (bitmap.width as usize * depth + 7) / 8;
let h = bitmap.height as usize;
bitmap.data = d.read_bytes(offset + 5, w * h)?;
Some(bitmap)
}
2 => {
bitmap.read_metrics(d, offset, flags, false)?;
let w = bitmap.width as usize * depth;
let h = bitmap.height as usize;
bitmap.data = d.read_bytes(offset + 5, (w * h + 7) / 8)?;
bitmap.is_packed = true;
Some(bitmap)
}
5 => {
bitmap.data = d.read_bytes(offset, size)?;
bitmap.is_packed = true;
Some(bitmap)
}
6 => {
bitmap.read_metrics(d, offset, flags, true)?;
let w = (bitmap.width as usize * depth + 7) / 8;
let h = bitmap.height as usize;
bitmap.data = d.read_bytes(offset + 8, w * h)?;
Some(bitmap)
}
7 => {
bitmap.read_metrics(d, offset, flags, true)?;
let w = bitmap.width as usize * depth;
let h = bitmap.height as usize;
bitmap.data = d.read_bytes(offset + 8, (w * h + 7) / 8)?;
bitmap.is_packed = true;
Some(bitmap)
}
17 => {
bitmap.read_metrics(d, offset, flags, false)?;
let size = d.read::<u32>(offset + 5)? as usize;
bitmap.data = d.read_bytes(offset + 9, size)?;
bitmap.is_png = true;
Some(bitmap)
}
18 => {
bitmap.read_metrics(d, offset, flags, true)?;
let size = d.read::<u32>(offset + 8)? as usize;
bitmap.data = d.read_bytes(offset + 12, size)?;
bitmap.is_png = true;
Some(bitmap)
}
19 => {
let size = d.read::<u32>(offset)? as usize;
bitmap.data = d.read_bytes(offset + 4, size)?;
bitmap.is_png = true;
Some(bitmap)
}
_ => None,
}
}
fn sbix_range(table: &[u8], strike_base: usize, glyph_id: u16, recurse: i32) -> Option<(u32, u32)> {
const DUPE: RawTag = raw_tag(b"dupe");
const PNG: RawTag = raw_tag(b"png ");
if recurse > 1 {
return None;
}
let b = Bytes::new(table);
let id = glyph_id as usize;
let base = strike_base + 4;
let mut start = b.read::<u32>(base + id * 4)?;
let mut end = b.read::<u32>(base + (id + 1) * 4)?;
if end <= start {
return None;
}
start += strike_base as u32;
end += strike_base as u32;
let tag = b.read::<u32>(start as usize + 4)?;
if tag == DUPE {
let dupe = b.read::<u16>(start as usize + 8)?;
sbix_range(table, strike_base, dupe, recurse + 1)
} else if tag == PNG {
Some((start, end))
} else {
None
}
}
#[cfg(feature = "scale")]
#[derive(Copy, Clone)]
struct BitmapData<'a> {
pub data: &'a [u8],
pub ppem: u16,
pub bit_depth: u8,
pub width: u8,
pub height: u8,
pub metrics: Metrics,
pub vertical_metrics: Metrics,
pub is_packed: bool,
pub is_png: bool,
pub is_sbix: bool,
}
#[cfg(feature = "scale")]
impl<'a> BitmapData<'a> {
fn read_metrics(&mut self, d: &Bytes, offset: usize, flags: u8, big: bool) -> Option<()> {
let (w, h) = get_metrics(
d,
offset,
flags,
big,
&mut self.metrics,
&mut self.vertical_metrics,
)?;
self.width = w;
self.height = h;
Some(())
}
}
#[cfg(feature = "scale")]
#[derive(Copy, Clone, Default)]
struct Metrics {
pub x: i8,
pub y: i8,
pub advance: u8,
}
#[cfg(feature = "scale")]
fn get_metrics(
d: &Bytes,
offset: usize,
flags: u8,
big: bool,
h: &mut Metrics,
v: &mut Metrics,
) -> Option<(u8, u8)> {
let width = d.read::<u8>(offset + 1)?;
let height = d.read::<u8>(offset)?;
if big {
h.x = d.read::<i8>(offset + 2)?;
h.y = d.read::<i8>(offset + 3)?;
h.advance = d.read::<u8>(offset + 4)?;
v.x = d.read::<i8>(offset + 5)?;
v.y = d.read::<i8>(offset + 6)?;
v.advance = d.read::<u8>(offset + 7)?;
} else {
let m = if flags & 2 != 0 { v } else { h };
m.x = d.read::<i8>(offset + 2)?;
m.y = d.read::<i8>(offset + 3)?;
m.advance = d.read::<u8>(offset + 4)?;
}
Some((width, height))
}

482
vendor/swash/src/string.rs vendored Normal file
View File

@@ -0,0 +1,482 @@
/*!
Localized names and other metadata.
*/
use core::fmt::Write;
use super::internal::*;
use super::FontRef;
const NAME: RawTag = raw_tag(b"name");
/// Identifier for well-known localized strings in a font.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum StringId {
Copyright,
Family,
SubFamily,
UniqueId,
Full,
Version,
PostScript,
Trademark,
Manufacturer,
Designer,
Description,
VendorUrl,
DesignerUrl,
License,
LicenseUrl,
TypographicFamily,
TypographicSubFamily,
CompatibleFull,
SampleText,
PostScriptCid,
WwsFamily,
WwsSubFamily,
LightBackgroundPalette,
DarkBackgroundPalette,
VariationsPostScriptNamePrefix,
Other(u16),
}
impl StringId {
pub fn from_raw(value: u16) -> Self {
use StringId::*;
match value {
0 => Copyright,
1 => Family,
2 => SubFamily,
3 => UniqueId,
4 => Full,
5 => Version,
6 => PostScript,
7 => Trademark,
8 => Manufacturer,
9 => Designer,
10 => Description,
11 => VendorUrl,
12 => DesignerUrl,
13 => License,
14 => LicenseUrl,
16 => TypographicFamily,
17 => TypographicSubFamily,
18 => CompatibleFull,
19 => SampleText,
20 => PostScriptCid,
21 => WwsFamily,
22 => WwsSubFamily,
23 => LightBackgroundPalette,
24 => DarkBackgroundPalette,
25 => VariationsPostScriptNamePrefix,
_ => Other(value),
}
}
pub fn to_raw(self) -> u16 {
use StringId::*;
match self {
Other(id) => id,
Copyright => 0,
Family => 1,
SubFamily => 2,
UniqueId => 3,
Full => 4,
Version => 5,
PostScript => 6,
Trademark => 7,
Manufacturer => 8,
Designer => 9,
Description => 10,
VendorUrl => 11,
DesignerUrl => 12,
License => 13,
LicenseUrl => 14,
TypographicFamily => 16,
TypographicSubFamily => 17,
CompatibleFull => 18,
SampleText => 19,
PostScriptCid => 20,
WwsFamily => 21,
WwsSubFamily => 22,
LightBackgroundPalette => 23,
DarkBackgroundPalette => 24,
VariationsPostScriptNamePrefix => 25,
}
}
}
/// Iterator over a collection of localized strings.
#[derive(Copy, Clone)]
pub struct LocalizedStrings<'a> {
data: Bytes<'a>,
len: usize,
pos: usize,
}
impl<'a> LocalizedStrings<'a> {
pub(crate) fn new(data: &'a [u8]) -> Self {
let data = Bytes::new(data);
let len = data.read_or_default::<u16>(2) as usize;
Self { data, len, pos: 0 }
}
pub(crate) fn from_font(font: &FontRef<'a>) -> Self {
Self::new(font.table_data(NAME).unwrap_or(&[]))
}
/// Searches for a string with the specified identifier, and if specified,
/// language.
///
/// ## Iteration behavior
/// This function searches the entire string collection without regard
/// for the current state of the iterator.
pub fn find_by_id(&self, id: StringId, language: Option<&str>) -> Option<LocalizedString<'a>> {
let mut first = None;
let mut best = None;
let raw_id = id.to_raw();
for i in 0..self.len() {
let rec = match self.get(i) {
Some(rec) => rec,
_ => continue,
};
if rec.raw_id() != raw_id {
continue;
}
if first.is_none() {
first = Some(rec);
}
let encoding = rec.encoding();
if let Some(lang) = language {
if rec.language().starts_with(lang) {
if encoding == Encoding::Unicode {
return Some(rec);
} else if encoding.is_decodable() {
best = Some(rec);
}
}
} else if rec.language() == "" {
if encoding == Encoding::Unicode {
return Some(rec);
} else if encoding.is_decodable() {
best = Some(rec);
}
}
}
if best.is_some() {
best
} else if language.is_none() {
first
} else {
None
}
}
/// Returns the string at the specified index.
fn get(&self, index: usize) -> Option<LocalizedString<'a>> {
if index >= self.len {
return None;
}
let b = &self.data;
let offset = 6 + index * 12;
b.ensure_range(offset, 12)?;
Some(LocalizedString {
data: *b,
storage: b.read_or_default::<u16>(4) as usize,
offset,
})
}
}
impl_iter!(LocalizedStrings, LocalizedString);
/// Represents a single localized string in a font.
///
/// Localized strings contain an [identifier](StringId) that describes the
/// content of the string (such as family name, copyright notice, sample text, etc),
/// a language that specifies the audience for which the string is intended and
/// some encoded data containing the value of the string. A string with a
/// particular identifier can appear multiple times in a font with various
/// languages and encodings.
#[derive(Copy, Clone)]
pub struct LocalizedString<'a> {
data: Bytes<'a>,
storage: usize,
offset: usize,
}
impl<'a> LocalizedString<'a> {
/// Returns the string identifier.
pub fn id(&self) -> StringId {
StringId::from_raw(self.raw_id())
}
/// Returns the language of the string.
pub fn language(&self) -> &str {
get_language(self.platform_id(), self.language_id())
}
/// Returns true if the encoding for the string is unicode.
pub fn is_unicode(&self) -> bool {
self.encoding() == Encoding::Unicode
}
/// Returns true if the string can be decoded.
pub fn is_decodable(&self) -> bool {
self.encoding().is_decodable()
}
/// Returns an iterator over the sequence of characters representing the
/// decoded string if the encoding is known. Will generate an empty string
/// otherwise.
pub fn chars(&self) -> Chars<'a> {
let encoding = self.encoding();
if !encoding.is_decodable() {
return Chars {
record: *self,
bytes: &[],
encoding,
offset: 0,
len: 0,
cur: 0,
};
}
let len = self.data.read_or_default::<u16>(self.offset + 8) as usize;
let offset = self.data.read_or_default::<u16>(self.offset + 10) as usize + self.storage;
Chars {
record: *self,
bytes: if encoding == Encoding::MacRoman {
self.bytes().unwrap_or(&[])
} else {
&[]
},
encoding,
offset,
len,
cur: 0,
}
}
fn raw_id(&self) -> u16 {
self.data.read::<u16>(self.offset + 6).unwrap_or(0xFFFF)
}
fn platform_id(&self) -> u16 {
self.data.read_or_default::<u16>(self.offset)
}
fn encoding_id(&self) -> u16 {
self.data.read_or_default::<u16>(self.offset + 2)
}
fn language_id(&self) -> u16 {
self.data.read_or_default::<u16>(self.offset + 4)
}
fn encoding(&self) -> Encoding {
Encoding::from_raw_parts(self.platform_id(), self.encoding_id())
}
fn bytes(&self) -> Option<&'a [u8]> {
let len = self.data.read::<u16>(self.offset + 8)? as usize;
let offset = self.data.read::<u16>(self.offset + 10)? as usize + self.storage;
self.data.read_bytes(offset, len)
}
}
impl<'a> core::fmt::Display for LocalizedString<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for c in self.chars() {
f.write_char(c)?;
}
Ok(())
}
}
/// Iterator over the characters in a localized string.
#[derive(Copy, Clone)]
pub struct Chars<'a> {
record: LocalizedString<'a>,
bytes: &'a [u8],
encoding: Encoding,
offset: usize,
len: usize,
cur: usize,
}
impl<'a> Iterator for Chars<'a> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
if self.cur >= self.len {
return None;
}
use core::char::from_u32;
let rep = core::char::REPLACEMENT_CHARACTER;
let d = &self.record.data;
match self.encoding {
Encoding::Unicode => {
let mut c = d.read::<u16>(self.offset + self.cur)? as u32;
self.cur += 2;
if (0xD800..0xDC00).contains(&c) {
let c2 = d.read::<u16>(self.offset + self.cur)? as u32;
self.cur += 2;
c = ((c & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000;
}
Some(from_u32(c).unwrap_or(rep))
}
Encoding::MacRoman => {
let c = self.bytes[self.cur] as u32;
self.cur += 1;
if c > 127 {
let idx = c as usize - 128;
Some(from_u32(MAC_ROMAN[idx] as u32).unwrap_or(rep))
} else {
Some(from_u32(c).unwrap_or(rep))
}
}
_ => None,
}
}
}
impl<'a> IntoIterator for LocalizedString<'a> {
type IntoIter = Chars<'a>;
type Item = char;
fn into_iter(self) -> Self::IntoIter {
self.chars()
}
}
/// Encoding of a localized string.
///
/// Fonts can contain a variety of platform specific and legacy encodings.
/// Only the ones we decode are listed here.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Encoding {
Unicode,
MacRoman,
Other { platform_id: u16, encoding_id: u16 },
}
impl Encoding {
pub(crate) fn from_raw_parts(platform_id: u16, encoding_id: u16) -> Self {
match (platform_id, encoding_id) {
(0, _) => Self::Unicode,
(1, 0) => Self::MacRoman,
(3, 0) => Self::Unicode,
(3, 1) => Self::Unicode,
(3, 10) => Self::Unicode,
_ => Self::Other {
platform_id,
encoding_id,
},
}
}
/// Returns true if this encoding is can be turned into a string.
pub fn is_decodable(&self) -> bool {
!matches!(self, Self::Other { .. })
}
}
#[rustfmt::skip]
const MAC_ROMAN: [u16; 128] = [
196, 197, 199, 201, 209, 214, 220, 225, 224, 226, 228, 227, 229, 231, 233,
232, 234, 235, 237, 236, 238, 239, 241, 243, 242, 244, 246, 245, 250, 249,
251, 252, 8224, 176, 162, 163, 167, 8226, 182, 223, 174, 169, 8482, 180,
168, 8800, 198, 216, 8734, 177, 8804, 8805, 165, 181, 8706, 8721, 8719,
960, 8747, 170, 186, 937, 230, 248, 191, 161, 172, 8730, 402, 8776, 8710,
171, 187, 8230, 160, 192, 195, 213, 338, 339, 8211, 8212, 8220, 8221, 8216,
8217, 247, 9674, 255, 376, 8260, 8364, 8249, 8250, 64257, 64258, 8225, 183,
8218, 8222, 8240, 194, 202, 193, 203, 200, 205, 206, 207, 204, 211, 212,
63743, 210, 218, 219, 217, 305, 710, 732, 175, 728, 729, 730, 184, 733,
731, 711,
];
#[rustfmt::skip]
const LANGUAGES: [(u32, &'static str); 334] = [
(0x10000, "en"), (0x10001, "fr"), (0x10002, "de"), (0x10003, "it"), (0x10004, "nl"),
(0x10005, "sv"), (0x10006, "es"), (0x10007, "da"), (0x10008, "pt"), (0x10009, "no"),
(0x1000A, "he"), (0x1000B, "ja"), (0x1000C, "ar"), (0x1000D, "fi"), (0x1000E, "el"),
(0x1000F, "is"), (0x10010, "mt"), (0x10011, "tr"), (0x10012, "hr"), (0x10013, "zh-tw"),
(0x10014, "ur"), (0x10015, "hi"), (0x10016, "th"), (0x10017, "ko"), (0x10018, "lt"),
(0x10019, "pl"), (0x1001A, "hu"), (0x1001B, "et"), (0x1001C, "lv"), (0x1001E, "fo"),
(0x1001F, "fa"), (0x10020, "ru"), (0x10021, "zh-cn"), (0x10022, "nl"), (0x10023, "ga"),
(0x10024, "sq"), (0x10025, "ro"), (0x10026, "cs"), (0x10027, "sk"), (0x10028, "sl"),
(0x10029, "yi"), (0x1002A, "sr"), (0x1002B, "mk"), (0x1002C, "bg"), (0x1002D, "uk"),
(0x1002E, "be"), (0x1002F, "uz"), (0x10030, "kk"), (0x10031, "az"), (0x10031, "az"),
(0x10032, "ar"), (0x10033, "hy"), (0x10034, "ka"), (0x10035, "mo"), (0x10036, "ky"),
(0x10037, "tg"), (0x10038, "tk"), (0x10039, "mn"), (0x10039, "mn"), (0x1003A, "mn"),
(0x1003B, "ps"), (0x1003C, "ku"), (0x1003D, "ks"), (0x1003E, "sd"), (0x1003F, "bo"),
(0x10040, "ne"), (0x10041, "sa"), (0x10042, "mr"), (0x10043, "bn"), (0x10044, "as"),
(0x10045, "gu"), (0x10046, "pa"), (0x10047, "or"), (0x10048, "ml"), (0x10049, "kn"),
(0x1004A, "ta"), (0x1004B, "te"), (0x1004C, "si"), (0x1004D, "my"), (0x1004E, "km"),
(0x1004F, "lo"), (0x10050, "vi"), (0x10051, "id"), (0x10052, "tl"), (0x10053, "ms"),
(0x10054, "ms"), (0x10055, "am"), (0x10056, "ti"), (0x10057, "om"), (0x10058, "so"),
(0x10059, "sw"), (0x1005A, "rw"), (0x1005B, "rn"), (0x1005C, "ny"), (0x1005D, "mg"),
(0x1005E, "eo"), (0x10080, "cy"), (0x10081, "eu"), (0x10082, "ca"), (0x10083, "la"),
(0x10084, "qu"), (0x10085, "gn"), (0x10086, "ay"), (0x10087, "tt"), (0x10088, "ug"),
(0x10089, "dz"), (0x1008A, "jw"), (0x1008B, "su"), (0x1008C, "gl"), (0x1008D, "af"),
(0x1008E, "br"), (0x1008F, "iu"), (0x10090, "gd"), (0x10091, "gv"), (0x10092, "ga"),
(0x10093, "to"), (0x10094, "el"), (0x10095, "ik"), (0x10096, "az"), (0x30001, "ar"),
(0x30004, "zh"), (0x30009, "en"), (0x30401, "ar"), (0x30402, "bg"), (0x30403, "ca"),
(0x30404, "zh-tw"), (0x30405, "cs"), (0x30406, "da"), (0x30407, "de"), (0x30408, "el"),
(0x30409, "en"), (0x3040A, "es"), (0x3040B, "fi"), (0x3040C, "fr"), (0x3040D, "he"),
(0x3040E, "hu"), (0x3040F, "is"), (0x30410, "it"), (0x30411, "ja"), (0x30412, "ko"),
(0x30413, "nl"), (0x30414, "no"), (0x30415, "pl"), (0x30416, "pt"), (0x30417, "rm"),
(0x30418, "ro"), (0x30419, "ru"), (0x3041A, "hr"), (0x3041B, "sk"), (0x3041C, "sq"),
(0x3041D, "sv"), (0x3041E, "th"), (0x3041F, "tr"), (0x30420, "ur"), (0x30421, "id"),
(0x30422, "uk"), (0x30423, "be"), (0x30424, "sl"), (0x30425, "et"), (0x30426, "lv"),
(0x30427, "lt"), (0x30428, "tg"), (0x30429, "fa"), (0x3042A, "vi"), (0x3042B, "hy"),
(0x3042C, "az"), (0x3042D, "eu"), (0x3042E, "wen"), (0x3042F, "mk"), (0x30430, "st"),
(0x30431, "ts"), (0x30432, "tn"), (0x30433, "ven"), (0x30434, "xh"), (0x30435, "zu"),
(0x30436, "af"), (0x30437, "ka"), (0x30438, "fo"), (0x30439, "hi"), (0x3043A, "mt"),
(0x3043B, "se"), (0x3043C, "ga"), (0x3043D, "yi"), (0x3043E, "ms"), (0x3043F, "kk"),
(0x30440, "ky"), (0x30441, "sw"), (0x30442, "tk"), (0x30443, "uz"), (0x30444, "tt"),
(0x30445, "bn"), (0x30446, "pa"), (0x30447, "gu"), (0x30448, "or"), (0x30449, "ta"),
(0x3044A, "te"), (0x3044B, "kn"), (0x3044C, "ml"), (0x3044D, "as"), (0x3044E, "mr"),
(0x3044F, "sa"), (0x30450, "mn"), (0x30451, "bo"), (0x30452, "cy"), (0x30453, "km"),
(0x30454, "lo"), (0x30455, "my"), (0x30456, "gl"), (0x30457, "kok"), (0x30458, "mni"),
(0x30459, "sd"), (0x3045A, "syr"), (0x3045B, "si"), (0x3045C, "chr"), (0x3045D, "iu"),
(0x3045E, "am"), (0x30460, "ks"), (0x30461, "ne"), (0x30462, "fy"), (0x30463, "ps"),
(0x30464, "phi"), (0x30465, "div"), (0x30468, "ha"), (0x3046A, "yo"), (0x30470, "ibo"),
(0x30471, "kau"), (0x30472, "om"), (0x30473, "ti"), (0x30474, "gn"), (0x30475, "haw"),
(0x30476, "la"), (0x30477, "so"), (0x30479, "pap"), (0x30481, "mi"), (0x30801, "ar"),
(0x30804, "zh-cn"), (0x30807, "de"), (0x30809, "en"), (0x3080A, "es"), (0x3080C, "fr"),
(0x30810, "it"), (0x30812, "ko"), (0x30813, "nl"), (0x30814, "nn"), (0x30816, "pt"),
(0x30818, "mo"), (0x30819, "ru"), (0x3081A, "sr"), (0x3081D, "sv"), (0x30820, "ur"),
(0x30827, "lt"), (0x3082C, "az"), (0x3083C, "gd"), (0x3083E, "ms"), (0x30843, "uz"),
(0x30845, "bn"), (0x30846, "ar"), (0x30850, "mn"), (0x30851, "bo"), (0x30851, "dz"),
(0x30860, "ks"), (0x30861, "ne"), (0x30873, "ti"), (0x30C01, "ar"), (0x30C04, "zh-hk"),
(0x30C07, "de"), (0x30C09, "en"), (0x30C0A, "es"), (0x30C0C, "fr"), (0x30C1A, "sr"),
(0x31001, "ar"), (0x31004, "zh-sg"), (0x31007, "de"), (0x31009, "en"), (0x3100A, "es"),
(0x3100C, "fr"), (0x31401, "ar"), (0x31404, "zh-mo"), (0x31407, "de"), (0x31409, "en"),
(0x3140A, "es"), (0x3140C, "fr"), (0x3141A, "bs"), (0x31801, "ar"), (0x31809, "en"),
(0x3180A, "es"), (0x3180C, "fr"), (0x31C01, "ar"), (0x31C09, "en"), (0x31C0A, "es"),
(0x31C0C, "fr"), (0x32001, "ar"), (0x32009, "en"), (0x3200A, "es"), (0x3200C, "fr"),
(0x32401, "ar"), (0x32409, "en"), (0x3240A, "es"), (0x3240C, "fr"), (0x32801, "ar"),
(0x32809, "en"), (0x3280A, "es"), (0x3280C, "fr"), (0x32C01, "ar"), (0x32C09, "en"),
(0x32C0A, "es"), (0x32C0C, "fr"), (0x33001, "ar"), (0x33009, "en"), (0x3300A, "es"),
(0x3300C, "fr"), (0x33401, "ar"), (0x33409, "en"), (0x3340A, "es"), (0x3340C, "fr"),
(0x33801, "ar"), (0x3380A, "es"), (0x3380C, "fr"), (0x33C01, "ar"), (0x33C09, "en"),
(0x33C0A, "es"), (0x33C0C, "fr"), (0x34001, "ar"), (0x34009, "en"), (0x3400A, "es"),
(0x34409, "en"), (0x3440A, "es"), (0x34809, "en"), (0x3480A, "es"), (0x34C0A, "es"),
(0x3500A, "es"), (0x3540A, "es"), (0x3E40A, "es"), (0x3E40C, "fr"),
];
fn get_language(platform_id: u16, language_id: u16) -> &'static str {
match platform_id {
0 => "",
1 | 3 => {
let key = (platform_id as u32) << 16 | language_id as u32;
if let Ok(idx) = LANGUAGES.binary_search_by(|x| x.0.cmp(&key)) {
LANGUAGES[idx].1
} else {
"zz"
}
}
_ => "zz",
}
}

17
vendor/swash/src/tag.rs vendored Normal file
View File

@@ -0,0 +1,17 @@
/// Four byte tag value.
pub type Tag = u32;
/// Creates a tag from four bytes.
pub const fn tag_from_bytes(bytes: &[u8; 4]) -> Tag {
(bytes[0] as u32) << 24 | (bytes[1] as u32) << 16 | (bytes[2] as u32) << 8 | bytes[3] as u32
}
/// Creates a tag from the first four bytes of a string, inserting
/// spaces for any missing bytes.
pub fn tag_from_str_lossy(s: &str) -> Tag {
let mut bytes = [b' '; 4];
for (i, b) in s.as_bytes().iter().enumerate().take(4) {
bytes[i] = *b;
}
tag_from_bytes(&bytes)
}

395
vendor/swash/src/text/analyze.rs vendored Normal file
View File

@@ -0,0 +1,395 @@
use super::{cluster::Boundary, Codepoint, LineBreak, Properties, WordBreak};
use core::borrow::Borrow;
/// Returns an iterator yielding unicode properties and boundary analysis for
/// each character in the specified sequence.
pub fn analyze<I>(chars: I) -> Analyze<I::IntoIter>
where
I: IntoIterator,
I::IntoIter: Clone,
I::Item: Borrow<char>,
{
Analyze {
chars: chars.into_iter(),
state: BoundaryState::new(),
}
}
/// Iterator that yields Unicode properties and boundary analysis.
/// This iterator is created by the [`analyze`] function.
#[derive(Clone)]
pub struct Analyze<I> {
chars: I,
state: BoundaryState,
}
impl<I> Iterator for Analyze<I>
where
I: Iterator + Clone,
I::Item: Borrow<char>,
{
type Item = (Properties, Boundary);
fn next(&mut self) -> Option<Self::Item> {
self.state.next(&mut self.chars)
}
}
impl<I> Analyze<I> {
/// Returns true if the analysis indicates that bidi resolution is
/// required.
pub fn needs_bidi_resolution(&self) -> bool {
self.state.needs_bidi
}
/// Sets the word breaking strength that will be used to analyze the next character.
pub fn set_break_strength(&mut self, strength: WordBreakStrength) {
self.state.strength = strength;
}
}
/// Word breaking strength (corresponds to <https://drafts.csswg.org/css-text/#word-break-property>).
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
#[repr(u8)]
pub enum WordBreakStrength {
/// Words can be broken according to their normal Unicode rules.
#[default]
Normal,
/// Breaking treats numeric, alphabetic, and Southeast Asian classes as Ideographic. Note that this does not affect
/// breaking punctuation.
BreakAll,
/// Breaking between typographic letter units or the NU, AL, AI, or ID classes is prohibited.
KeepAll,
}
#[derive(Clone)]
struct BoundaryState {
strength: WordBreakStrength,
prev: WordBreak,
prevent_next: bool,
ri_count: u8,
emoji: bool,
next_emoji: bool,
line_state: (u8, Option<LineBreak>),
first: bool,
needs_bidi: bool,
}
impl BoundaryState {
fn new() -> Self {
Self {
strength: WordBreakStrength::default(),
prev: WordBreak::EX,
prevent_next: false,
ri_count: 0,
emoji: false,
next_emoji: false,
line_state: (sot, None),
first: true,
needs_bidi: false,
}
}
fn reset_state(&mut self) {
self.ri_count = 0;
self.emoji = false;
}
fn check_word<I>(&mut self, props: Properties, iter: &mut I) -> bool
where
I: Iterator + Clone,
I::Item: Borrow<char>,
{
use WordBreak::*;
let b = props.word_break();
let emoji = props.is_extended_pictographic();
if self.first {
self.first = false;
self.prev = b;
self.next_emoji = emoji;
if b == RI {
self.ri_count = 1;
}
return true;
}
let prev_emoji = self.emoji;
self.emoji = self.emoji || self.next_emoji;
self.next_emoji = emoji;
let a = self.prev;
self.prev = b;
if self.prevent_next {
self.prevent_next = false;
return false;
}
if a == CR && b == LF {
self.reset_state();
return false;
}
let a_mask = a.mask();
let b_mask = b.mask();
const AH_LETTER: u32 = LE.mask() | HL.mask();
const MID_NUM_LET_Q: u32 = MB.mask() | SQ.mask();
const WB3_A: u32 = NL.mask() | CR.mask() | LF.mask();
if a_mask & WB3_A != 0 || b_mask & WB3_A != 0 {
// (Newline | CR | LF) ÷
// ÷ (Newline | CR | LF)
self.reset_state();
return true;
}
if a == ZWJ && emoji {
self.reset_state();
return false;
}
const WB_4: u32 = Extend.mask() | FO.mask() | ZWJ.mask();
if b_mask & WB_4 != 0 {
// Ignore format and extend characters
self.reset_state();
self.prev = a;
return false;
}
if a == WSegSpace && b == WSegSpace {
// WSegSpace × WSegSpace
self.reset_state();
return false;
}
if a_mask & AH_LETTER != 0 {
// AHLetter × AHLetter
// AHLetter × Numeric
if b_mask & (AH_LETTER | NU.mask()) != 0 {
self.reset_state();
return false;
}
if b_mask & (ML.mask() | MID_NUM_LET_Q) != 0 {
// AHLetter × (MidLetter | MidNumLetQ) AHLetter
// AHLetter (MidLetter | MidNumLetQ) × AHLetter
if let Some(c) = iter
.clone()
.next()
.map(|p| p.borrow().properties().word_break())
{
if c.mask() & AH_LETTER != 0 {
self.prevent_next = true;
self.reset_state();
return false;
}
}
}
}
if a == HL {
if b == SQ {
self.reset_state();
return false;
}
if b == DQ {
// Hebrew_Letter × Double_Quote Hebrew_Letter
// Hebrew_Letter Double_Quote × Hebrew_Letter
if let Some(c) = iter
.clone()
.next()
.map(|p| p.borrow().properties().word_break())
{
if c == HL {
self.prevent_next = true;
self.reset_state();
return false;
}
}
}
}
if a_mask & NU.mask() != 0 {
// Numeric × Numeric
// Numeric × AHLetter
if b_mask & (NU.mask() | AH_LETTER) != 0 {
self.reset_state();
return false;
}
if b_mask & (MN.mask() | MID_NUM_LET_Q) != 0 {
if let Some(c) = iter
.clone()
.next()
.map(|p| p.borrow().properties().word_break())
{
// Numeric (MidNum | MidNumLetQ) × Numeric
// Numeric × (MidNum | MidNumLetQ) Numeric
if c == NU {
self.prevent_next = true;
self.reset_state();
return false;
}
}
}
}
if a == KA && b == KA {
// Katakana × Katakana
self.reset_state();
return false;
}
const WB13_A: u32 = AH_LETTER | NU.mask() | KA.mask() | EX.mask();
if a_mask & WB13_A != 0 && b == EX {
// (AHLetter | Numeric | Katakana | ExtendNumLet) × ExtendNumLet
self.reset_state();
return false;
}
const WB13_B: u32 = AH_LETTER | NU.mask() | KA.mask();
if a == EX && b_mask & WB13_B != 0 {
// ExtendNumLet × (AHLetter | Numeric | Katakana)
self.reset_state();
return false;
}
if prev_emoji && a == ZWJ && emoji {
self.ri_count = 0;
return false;
}
if self.ri_count == 2 {
self.reset_state();
if b == RI {
self.ri_count = 1;
}
return true;
}
if b == RI {
self.ri_count += 1;
if a != RI {
self.reset_state();
self.ri_count = 1;
return true;
}
self.emoji = false;
return false;
}
self.reset_state();
true
}
fn check_line(&mut self, props: Properties) -> Boundary {
let state = self.line_state;
let lb = props.line_break();
use LineBreak::*;
let val = PAIR_TABLE[state.0 as usize][lb as usize];
// word-break: break-all
//
// Treat the NU, AL, and SA line breaking classes as ID.
let mode_val = if self.strength == WordBreakStrength::BreakAll {
let left = if matches!(state.1, Some(AL | NU | SA)) {
ID as usize
} else {
state.0 as usize
};
let right = if matches!(lb, AL | NU | SA) {
ID as usize
} else {
lb as usize
};
PAIR_TABLE[left][right]
} else {
val
};
let mut mode = if mode_val & MANDATORY_BREAK_BIT != 0 {
Boundary::Mandatory
} else if mode_val & ALLOWED_BREAK_BIT != 0 && state.1 != Some(ZWJ) {
Boundary::Line
} else {
Boundary::None
};
// word-break: keep-all
//
// Prohibit breaking between typographic letter units or the NU, AL, or
// AI, or ID classes.
// (See https://github.com/unicode-org/icu4x/blob/1e27279/components/segmenter/src/line.rs#L836-L840)
if let (
WordBreakStrength::KeepAll,
Some(AI | AL | ID | NU | HY | H2 | H3 | JL | JV | JT | CJ),
AI | AL | ID | NU | HY | H2 | H3 | JL | JV | JT | CJ,
) = (self.strength, state.1, lb)
{
mode = Boundary::None;
}
// Store the original value, not the modified one.
self.line_state = (val & !(ALLOWED_BREAK_BIT | MANDATORY_BREAK_BIT), Some(lb));
mode
}
fn next<I>(&mut self, iter: &mut I) -> Option<(Properties, Boundary)>
where
I: Iterator + Clone,
I::Item: Borrow<char>,
{
let props = iter.next()?.borrow().properties();
let mut boundary = self.check_line(props);
let word = self.check_word(props, iter);
if boundary as u16 == 0 && word {
boundary = Boundary::Word;
}
self.needs_bidi = self.needs_bidi || props.bidi_class().needs_resolution();
Some((props, boundary))
}
}
const ALLOWED_BREAK_BIT: u8 = 0x80;
const MANDATORY_BREAK_BIT: u8 = 0x40;
#[allow(non_upper_case_globals)]
const sot: u8 = 44;
#[rustfmt::skip]
const PAIR_TABLE: [[u8; 44]; 53] = [
[1,1,130,3,132,5,134,28,8,1,10,11,140,141,14,15,144,145,18,19,148,21,22,151,152,153,26,27,28,29,30,31,32,33,162,1,1,37,38,39,1,41,1,235,],
[1,1,130,3,132,5,134,28,8,1,10,11,140,141,14,15,144,145,18,19,148,21,22,151,152,153,26,27,28,29,30,31,32,33,162,1,1,37,38,39,1,41,1,235,],
[129,129,2,3,132,5,134,28,8,2,10,11,140,141,14,15,144,145,146,19,148,149,22,151,152,153,26,27,28,157,158,159,160,33,162,129,129,50,38,39,129,41,2,235,],
[129,129,130,3,132,5,134,28,8,3,10,11,140,141,14,143,144,145,146,19,148,149,22,151,152,153,26,27,28,157,158,159,160,33,162,129,129,37,38,39,129,41,3,235,],
[1,1,2,3,4,5,134,28,8,4,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1,1,37,38,39,1,41,4,235,],
[193,193,194,195,196,197,198,220,200,193,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,193,193,229,230,231,193,233,193,235,],
[129,129,130,131,132,5,134,156,8,6,10,11,140,141,14,15,144,145,146,147,148,149,22,151,152,153,26,27,156,157,158,159,160,33,162,129,129,37,38,39,129,41,6,235,],
[129,129,130,3,132,5,134,28,8,28,10,11,140,141,14,15,144,145,146,19,148,149,22,151,152,153,26,27,28,157,158,159,160,33,162,129,129,37,38,39,129,41,28,235,],
[129,129,130,3,132,5,134,28,8,8,10,11,140,141,14,15,144,145,146,19,148,149,22,151,152,153,26,27,28,157,158,31,32,33,162,129,129,48,38,39,129,41,8,235,],
[1,1,130,3,132,5,134,28,8,9,10,11,140,141,14,15,144,145,18,19,148,21,22,151,152,153,26,27,28,29,30,31,32,33,162,1,1,37,38,39,1,41,9,235,],
[1,1,130,3,132,5,134,28,8,10,10,11,140,141,14,15,144,145,18,19,148,149,22,151,152,153,26,27,28,29,158,31,32,33,162,1,1,49,38,39,1,41,10,235,],
[193,193,194,195,196,197,198,220,200,193,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,26,219,220,221,222,223,224,225,226,193,193,229,230,231,193,233,193,235,],
[129,129,130,3,132,5,134,28,8,12,10,11,140,13,14,15,144,145,146,19,148,21,22,151,152,153,26,27,28,157,158,31,160,33,162,129,129,37,38,39,129,41,12,235,],
[129,129,130,3,132,5,134,28,8,13,10,11,140,141,14,15,144,145,146,19,148,21,22,151,152,153,26,27,28,157,158,31,160,33,162,129,129,37,38,39,129,41,13,235,],
[129,129,130,3,132,5,134,28,8,14,10,11,140,141,14,15,144,145,146,19,148,21,22,151,152,153,26,27,28,157,158,159,160,33,162,129,129,37,38,39,129,41,14,235,],
[1,1,2,3,4,5,6,28,8,15,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1,1,37,38,39,1,41,15,235,],
[129,129,130,3,132,5,134,28,8,16,10,11,140,141,14,15,144,145,146,19,148,21,22,151,24,25,26,27,28,157,158,31,160,33,162,129,129,37,38,39,129,41,16,235,],
[129,129,130,3,132,5,134,28,8,17,10,11,140,141,14,15,144,145,146,19,148,21,22,151,24,153,26,27,28,157,158,31,160,33,162,129,129,37,38,39,129,41,17,235,],
[1,1,130,51,132,5,134,28,8,18,10,11,140,141,14,15,144,145,18,51,148,21,22,151,152,153,26,27,28,29,30,31,32,33,162,1,1,37,38,39,1,41,18,235,],
[129,129,130,3,132,5,134,28,8,19,10,11,140,141,14,143,144,145,146,19,148,149,22,151,152,153,26,27,28,29,158,159,160,33,162,129,129,37,38,39,129,41,19,235,],
[129,129,130,3,132,5,134,28,8,20,10,11,140,141,14,15,144,145,146,19,148,21,22,151,152,153,26,27,28,157,158,31,160,33,162,129,129,37,38,39,129,41,20,235,],
[129,129,130,3,132,5,134,28,8,21,10,11,140,141,14,15,144,145,146,19,148,21,22,151,152,153,26,27,28,157,158,159,160,33,162,129,129,37,38,39,129,41,21,235,],
[1,1,130,3,132,5,134,28,8,22,10,11,140,141,14,15,144,145,18,19,148,149,22,151,152,153,26,27,28,29,158,159,160,33,162,1,1,37,38,39,1,41,22,235,],
[129,129,130,3,132,5,134,28,8,23,10,11,140,141,14,15,16,17,146,19,148,21,22,23,152,25,26,27,28,157,158,31,160,33,162,129,129,37,38,39,129,41,23,235,],
[129,129,130,3,132,5,134,28,8,24,10,11,140,141,14,15,144,145,146,19,148,21,22,151,24,153,26,27,28,157,158,31,160,33,162,129,129,37,38,39,129,41,24,235,],
[129,129,130,3,132,5,134,28,8,25,10,11,140,141,14,15,144,145,146,19,148,21,22,151,24,25,26,27,28,157,158,31,160,33,162,129,129,37,38,39,129,41,25,235,],
[193,193,194,195,196,197,198,220,200,193,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,193,193,229,230,231,193,233,193,235,],
[193,193,194,195,196,197,198,220,200,193,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,193,193,229,230,231,193,233,193,235,],
[129,129,130,3,132,5,134,28,8,28,10,11,140,141,14,15,144,145,146,19,148,149,22,151,152,153,26,27,28,157,158,159,160,33,162,129,129,37,38,39,129,41,28,235,],
[1,1,130,3,132,5,134,28,8,29,10,11,140,141,14,15,144,145,18,19,148,21,22,151,152,153,26,27,28,29,30,31,32,33,162,1,1,37,38,39,1,41,29,235,],
[1,1,2,3,4,5,6,28,8,30,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1,1,46,38,39,1,41,30,235,],
[1,1,130,3,132,5,134,28,8,31,10,11,140,141,14,15,144,145,18,19,148,149,22,151,152,153,26,27,28,29,30,159,160,33,162,1,1,37,38,39,1,41,31,235,],
[1,1,130,3,132,5,134,28,8,32,10,11,12,13,14,15,16,17,18,19,20,149,22,23,24,25,26,27,28,29,30,159,160,33,162,1,1,37,38,39,1,41,32,235,],
[1,1,2,3,4,5,6,28,8,33,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1,1,47,38,39,1,41,33,235,],
[129,129,130,3,132,5,134,28,8,34,10,11,140,141,14,15,144,145,146,19,148,149,22,151,152,153,26,27,28,157,158,159,160,33,52,129,129,37,38,39,129,41,34,235,],
[1,1,130,3,132,5,134,28,8,1,10,11,140,141,14,15,144,145,18,19,148,21,22,151,152,153,26,27,28,29,30,31,32,33,162,1,1,37,38,39,1,41,1,235,],
[1,1,130,3,132,5,134,28,8,1,10,11,140,141,14,15,144,145,18,19,148,21,22,151,152,153,26,27,28,29,30,31,32,33,162,1,1,37,38,39,1,41,1,235,],
[129,129,130,131,132,5,134,156,8,129,10,11,140,141,14,143,144,145,146,147,148,149,22,151,152,153,26,27,156,157,158,159,160,161,162,129,129,37,38,39,129,41,129,235,],
[129,129,130,3,132,5,134,28,8,38,10,11,140,141,14,15,144,145,18,19,148,149,22,151,152,153,26,27,28,29,158,159,160,33,162,129,129,37,38,39,129,41,38,235,],
[1,1,2,3,4,5,6,28,8,39,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1,1,37,38,39,1,41,39,235,],
[1,1,130,3,132,5,134,28,8,1,10,11,140,141,14,15,144,145,18,19,148,21,22,151,152,153,26,27,28,29,30,31,32,33,162,1,1,37,38,39,1,41,1,235,],
[129,129,130,131,132,5,134,156,136,129,138,11,140,141,142,143,144,145,146,147,148,149,150,151,152,153,26,27,156,157,158,159,160,161,162,129,129,45,166,167,129,41,129,235,],
[1,1,130,3,132,5,134,28,8,42,10,11,140,141,14,15,144,145,18,19,148,21,22,151,152,153,26,27,28,29,30,31,32,33,162,1,1,37,38,39,1,41,42,235,],
[129,129,130,3,132,5,134,28,8,129,10,11,140,141,14,143,144,145,146,19,148,149,22,151,152,153,26,27,28,157,158,159,160,33,162,129,129,37,38,39,129,41,129,235,],
[1,1,2,3,4,5,6,28,8,1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1,1,37,38,39,1,41,1,43,],
[129,129,130,131,132,5,134,156,136,129,138,11,140,141,142,143,144,145,146,147,148,149,150,151,152,153,26,27,156,157,158,159,160,161,162,129,129,45,166,167,129,41,129,235,],
[1,1,2,3,4,5,6,28,8,1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1,1,46,38,39,1,41,1,235,],
[129,129,130,131,132,5,134,156,8,129,10,11,140,141,14,143,144,145,146,147,148,149,22,151,152,153,26,27,156,157,30,159,160,161,162,129,129,47,38,39,129,41,129,235,],
[129,129,130,131,132,5,134,28,8,129,10,11,140,141,14,143,144,145,146,147,148,149,22,151,152,153,26,27,28,157,158,159,160,161,162,129,129,48,38,39,129,41,129,235,],
[129,129,130,131,132,5,134,28,8,129,10,11,140,141,14,143,144,145,146,147,148,149,22,151,152,153,26,27,28,157,158,159,160,161,162,129,129,49,38,39,129,41,129,235,],
[129,129,2,131,132,5,134,156,8,129,10,11,140,141,14,143,144,145,146,147,148,149,22,151,152,153,26,27,156,157,158,159,160,161,162,129,129,50,38,39,129,41,129,235,],
[1,1,2,3,4,5,134,28,8,51,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,1,1,37,38,39,1,41,51,235,],
[129,129,130,3,132,5,134,28,8,52,10,11,140,141,14,15,144,145,146,19,148,149,22,151,152,153,26,27,28,157,158,159,160,33,162,129,129,37,38,39,129,41,52,235,],
];

83
vendor/swash/src/text/cluster/char.rs vendored Normal file
View File

@@ -0,0 +1,83 @@
use super::super::JoiningType;
use super::UserData;
use crate::GlyphId;
/// Character output from the cluster parser.
#[derive(Copy, Clone, Debug)]
pub struct Char {
/// The character.
pub ch: char,
/// Offset of the character in code units.
pub offset: u32,
/// Shaping class of the character.
pub shape_class: ShapeClass,
/// Joining type of the character.
pub joining_type: JoiningType,
/// True if the character is ignorable.
pub ignorable: bool,
/// True if the character should be considered when mapping glyphs.
pub contributes_to_shaping: bool,
/// Nominal glyph identifier.
pub glyph_id: GlyphId,
/// Arbitrary user data.
pub data: UserData,
}
impl Default for Char {
fn default() -> Self {
Self {
ch: '\0',
shape_class: ShapeClass::Base,
joining_type: JoiningType::U,
ignorable: false,
contributes_to_shaping: true,
glyph_id: 0,
data: 0,
offset: 0,
}
}
}
/// Shaping class of a character.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum ShapeClass {
/// Reph form.
Reph,
/// Pre-base form.
Pref,
/// Myanmar three character prefix.
Kinzi,
/// Base character.
Base,
/// Mark character.
Mark,
/// Halant modifier.
Halant,
/// Medial consonant Ra.
MedialRa,
/// Pre-base vowel modifier.
VMPre,
/// Pre-base dependent vowel.
VPre,
/// Below base dependent vowel.
VBlw,
/// Anusvara class.
Anusvara,
/// Zero width joiner.
Zwj,
/// Zero width non-joiner.
Zwnj,
/// Control character.
Control,
/// Variation selector.
Vs,
/// Other character.
Other,
}
impl Default for ShapeClass {
fn default() -> Self {
Self::Base
}
}

433
vendor/swash/src/text/cluster/cluster.rs vendored Normal file
View File

@@ -0,0 +1,433 @@
use super::super::{Codepoint as _, JoiningType};
use super::char::{Char, ShapeClass};
use super::token::Token;
use super::{ClusterInfo, UserData};
use crate::GlyphId;
use core::fmt;
use core::ops::Range;
/// The maximum number of characters in a single cluster.
pub const MAX_CLUSTER_SIZE: usize = 32;
/// Character cluster; output from the parser and input to the shaper.
#[derive(Copy, Clone)]
pub struct CharCluster {
info: ClusterInfo,
chars: [Char; MAX_CLUSTER_SIZE],
len: u8,
map_len: u8,
start: u32,
end: u32,
force_normalize: bool,
comp: Form,
decomp: Form,
form: FormKind,
best_ratio: f32,
}
impl CharCluster {
/// Creates a new empty cluster.
pub fn new() -> Self {
Self {
info: ClusterInfo(0),
chars: [DEFAULT_CHAR; MAX_CLUSTER_SIZE],
len: 0,
map_len: 0,
start: 0,
end: 0,
force_normalize: false,
comp: Form::new(),
decomp: Form::new(),
form: FormKind::Original,
best_ratio: 0.,
}
}
/// Returns the cluster information.
pub fn info(&self) -> ClusterInfo {
self.info
}
/// Returns the primary user data for the cluster.
pub fn user_data(&self) -> UserData {
self.chars[0].data
}
/// Returns the source range for the cluster in code units.
pub fn range(&self) -> SourceRange {
SourceRange {
start: self.start,
end: self.end,
}
}
/// Returns true if the cluster is empty.
pub fn is_empty(&self) -> bool {
self.len == 0
}
/// Returns the sequence of characters in the cluster.
pub fn chars(&self) -> &[Char] {
&self.chars[..self.len as usize]
}
/// Returns the currently mapped sequence of characters in the cluster.
pub fn mapped_chars(&self) -> &[Char] {
match self.form {
FormKind::Original => &self.chars[..self.len as usize],
FormKind::NFD => self.decomp.chars(),
FormKind::NFC => self.comp.chars(),
}
}
/// Applies a nominal glyph identifier mapping to the cluster, returning
/// a result indicating the status of the mapping.
pub fn map(&mut self, f: impl Fn(char) -> GlyphId) -> Status {
let len = self.len;
if len == 0 {
return Status::Complete;
}
let mut glyph_ids = [0u16; MAX_CLUSTER_SIZE];
let prev_ratio = self.best_ratio;
let mut ratio;
if self.force_normalize && self.composed().is_some() {
ratio = self.comp.map(&f, &mut glyph_ids, self.best_ratio);
if ratio > self.best_ratio {
self.best_ratio = ratio;
self.form = FormKind::NFC;
if ratio >= 1. {
return Status::Complete;
}
}
}
ratio = Mapper {
chars: &mut self.chars[..self.len as usize],
map_len: self.map_len.max(1),
}
.map(&f, &mut glyph_ids, self.best_ratio);
if ratio > self.best_ratio {
self.best_ratio = ratio;
self.form = FormKind::Original;
if ratio >= 1. {
return Status::Complete;
}
}
if len > 1 && self.decomposed().is_some() {
ratio = self.decomp.map(&f, &mut glyph_ids, self.best_ratio);
if ratio > self.best_ratio {
self.best_ratio = ratio;
self.form = FormKind::NFD;
if ratio >= 1. {
return Status::Complete;
}
}
if !self.force_normalize && self.composed().is_some() {
ratio = self.comp.map(&f, &mut glyph_ids, self.best_ratio);
if ratio > self.best_ratio {
self.best_ratio = ratio;
self.form = FormKind::NFC;
if ratio >= 1. {
return Status::Complete;
}
}
}
}
if self.best_ratio > prev_ratio {
Status::Keep
} else {
Status::Discard
}
}
/// Resets the cluster to the initial empty state.
pub fn clear(&mut self) {
self.info = ClusterInfo(0);
self.len = 0;
self.map_len = 0;
self.start = 0;
self.end = 0;
self.force_normalize = false;
self.comp.clear();
self.decomp.clear();
self.form = FormKind::Original;
self.best_ratio = 0.;
}
/// Returns the sequence of decomposed characters for the cluster.
fn decomposed(&mut self) -> Option<&[Char]> {
match self.decomp.state {
FormState::Invalid => None,
FormState::None => {
self.decomp.state = FormState::Invalid;
let mut i = 0;
for ch in &self.chars[..self.len as usize] {
let mut end = i;
let mut copy = *ch;
for c in ch.ch.decompose() {
if end == MAX_CLUSTER_SIZE {
return None;
}
copy.ch = c;
self.decomp.chars[end] = copy;
end += 1;
}
i = end;
}
if i == 0 {
return None;
}
self.decomp.len = i as u8;
self.decomp.state = FormState::Valid;
self.decomp.setup();
Some(self.decomp.chars())
}
FormState::Valid => Some(self.decomp.chars()),
}
}
/// Returns the sequence of composed characters for the cluster.
fn composed(&mut self) -> Option<&[Char]> {
match self.comp.state {
FormState::Invalid => None,
FormState::None => {
if self.decomposed().map(|chars| chars.len()).unwrap_or(0) == 0 {
self.comp.state = FormState::Invalid;
return None;
}
self.comp.state = FormState::Invalid;
let mut last = self.decomp.chars[0];
let mut i = 0;
for ch in &self.decomp.chars()[1..] {
if let Some(comp) = char::compose(last.ch, ch.ch) {
last.ch = comp;
} else {
self.comp.chars[i] = last;
i += 1;
last = *ch;
}
}
self.comp.chars[i] = last;
self.comp.len = i as u8 + 1;
self.comp.state = FormState::Valid;
self.comp.setup();
Some(self.comp.chars())
}
FormState::Valid => Some(self.comp.chars()),
}
}
}
impl Default for CharCluster {
fn default() -> Self {
Self::new()
}
}
/// Functions for cluster building.
impl CharCluster {
pub(super) fn info_mut(&mut self) -> &mut ClusterInfo {
&mut self.info
}
pub(super) fn len(&self) -> u8 {
self.len
}
pub(super) fn force_normalize(&mut self) {
self.force_normalize = true;
}
pub(super) fn push(&mut self, input: &Token, class: ShapeClass) {
let contributes_to_shaping = input.info.contributes_to_shaping();
self.chars[self.len as usize] = Char {
ch: input.ch,
shape_class: class,
joining_type: input.info.joining_type(),
ignorable: input.info.is_ignorable(),
contributes_to_shaping,
glyph_id: 0,
offset: input.offset,
data: input.data,
};
if self.len == 0 {
self.start = input.offset;
}
self.info.merge_boundary(input.info.boundary() as u16);
self.end = input.offset + input.len as u32;
self.len += 1;
self.map_len += contributes_to_shaping as u8;
}
/// This function records the attributes and range information for
/// a character but does not add it to the cluster. It is used when
/// characters such as emoji variation selectors are dropped from
/// shaping but should still be included in the cluster range.
pub(super) fn note_char(&mut self, input: &Token) {
if self.len == 0 {
self.start = input.offset;
}
self.info.merge_boundary(input.info.boundary() as u16);
self.end = input.offset + input.len as u32;
}
}
/// Iterative status of mapping a character cluster to nominal glyph identifiers.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Status {
/// Mapping should be skipped.
Discard,
/// The best mapping so far.
Keep,
/// Complete mapping.
Complete,
}
/// Source range of a cluster in code units.
#[derive(Copy, Clone)]
pub struct SourceRange {
pub start: u32,
pub end: u32,
}
impl SourceRange {
/// Converts the source range into a `usize` range.
pub fn to_range(self) -> Range<usize> {
self.start as usize..self.end as usize
}
}
impl From<SourceRange> for Range<usize> {
fn from(s: SourceRange) -> Self {
s.to_range()
}
}
impl fmt::Debug for SourceRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}..{}", self.start, self.end)
}
}
impl fmt::Display for SourceRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}..{}", self.start, self.end)
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[allow(clippy::upper_case_acronyms)]
enum FormKind {
Original,
NFD,
NFC,
}
#[derive(Copy, Clone, PartialEq, Eq)]
enum FormState {
None,
Valid,
Invalid,
}
#[derive(Copy, Clone)]
struct Form {
pub chars: [Char; MAX_CLUSTER_SIZE],
pub len: u8,
pub map_len: u8,
pub state: FormState,
}
impl Form {
fn new() -> Self {
Self {
chars: [DEFAULT_CHAR; MAX_CLUSTER_SIZE],
len: 0,
map_len: 0,
state: FormState::None,
}
}
fn clear(&mut self) {
self.len = 0;
self.map_len = 0;
self.state = FormState::None;
}
fn chars(&self) -> &[Char] {
&self.chars[..self.len as usize]
}
fn setup(&mut self) {
self.map_len = (self
.chars()
.iter()
.filter(|c| c.shape_class != ShapeClass::Control)
.count() as u8)
.max(1);
}
fn map(
&mut self,
f: &impl Fn(char) -> u16,
glyphs: &mut [u16; MAX_CLUSTER_SIZE],
best_ratio: f32,
) -> f32 {
Mapper {
chars: &mut self.chars[..self.len as usize],
map_len: self.map_len,
}
.map(f, glyphs, best_ratio)
}
}
struct Mapper<'a> {
chars: &'a mut [Char],
map_len: u8,
}
impl<'a> Mapper<'a> {
fn map(
&mut self,
f: &impl Fn(char) -> u16,
glyphs: &mut [u16; MAX_CLUSTER_SIZE],
best_ratio: f32,
) -> f32 {
if self.map_len == 0 {
return 1.;
}
let mut mapped = 0;
for (c, g) in self.chars.iter().zip(glyphs.iter_mut()) {
if !c.contributes_to_shaping {
*g = f(c.ch);
if self.map_len == 1 {
mapped += 1;
}
} else {
let gid = f(c.ch);
*g = gid;
if gid != 0 {
mapped += 1;
}
}
}
let ratio = mapped as f32 / self.map_len as f32;
if ratio > best_ratio {
for (ch, glyph) in self.chars.iter_mut().zip(glyphs) {
ch.glyph_id = *glyph;
}
}
ratio
}
}
const DEFAULT_CHAR: Char = Char {
ch: ' ',
shape_class: ShapeClass::Base,
joining_type: JoiningType::U,
ignorable: false,
contributes_to_shaping: true,
glyph_id: 0,
data: 0,
offset: 0,
};

466
vendor/swash/src/text/cluster/complex.rs vendored Normal file
View File

@@ -0,0 +1,466 @@
//! A complex cluster parser based on Microsoft's Universal Shaping Engine
//! specification.
use super::super::{Category, Codepoint, Script};
use super::unicode_data::{ClusterBreak, UseClass};
use super::{CharCluster, Emoji, ShapeClass, Token, Whitespace, MAX_CLUSTER_SIZE};
type Kind = UseClass;
pub struct ComplexState<I> {
chars: Tokens<I>,
cur: Token,
cur_kind: Kind,
cur_emoji: bool,
done: bool,
}
impl<I> ComplexState<I>
where
I: Iterator<Item = Token> + Clone,
{
pub fn new(script: Script, chars: I) -> Self {
let mut chars = Tokens::new(script, chars);
if let Some((first, kind, emoji)) = chars.by_ref().next() {
Self {
chars,
cur: first,
cur_kind: kind,
cur_emoji: emoji,
done: false,
}
} else {
Self {
chars,
cur: Token::default(),
cur_kind: UseClass::O,
cur_emoji: false,
done: true,
}
}
}
pub fn next(&mut self, cluster: &mut CharCluster) -> bool {
if self.done {
return false;
}
Parser::new(self, cluster).parse();
true
}
}
struct Parser<'a, I> {
s: &'a mut ComplexState<I>,
cluster: &'a mut CharCluster,
vt: bool,
}
impl<'a, I> Parser<'a, I>
where
I: Iterator<Item = Token> + Clone,
{
fn new(s: &'a mut ComplexState<I>, cluster: &'a mut CharCluster) -> Self {
Self {
s,
cluster,
vt: false,
}
}
fn parse(&mut self) -> Option<()> {
use UseClass::*;
if self.s.done {
return Some(());
}
if self.emoji() {
self.cluster.info_mut().set_emoji(Emoji::Default);
while self.emoji() {
self.accept_any_as(ShapeClass::Base)?;
if !self.parse_emoji_extension()? {
break;
}
}
return Some(());
}
match self.kind() {
O => {
// This is not in the USE spec, but added to support uniform
// clustering of CRLF across the parsers.
match self.s.cur.ch {
'\r' => {
self.cluster.info_mut().set_space(Whitespace::Newline);
self.accept_any_as(ShapeClass::Control)?;
if self.s.cur.ch == '\n' {
self.accept_any_as(ShapeClass::Control)?;
}
}
'\n' => {
self.cluster.info_mut().set_space(Whitespace::Newline);
self.accept_any_as(ShapeClass::Control)?;
}
_ => {
self.cluster.info_mut().set_space_from_char(self.s.cur.ch);
let class = match self.s.cur.info.category() {
Category::Format => match self.s.cur.ch as u32 {
0x200C => ShapeClass::Zwnj,
0x200D => ShapeClass::Zwj,
_ => ShapeClass::Control,
},
Category::Control => ShapeClass::Control,
_ => ShapeClass::Base,
};
self.accept_any_as(class)?;
}
}
}
IND | Rsv | WJ => {
self.accept_any_as(ShapeClass::Base)?;
self.accept_as(VS, ShapeClass::Vs)?;
}
R => {
self.accept_any_as(ShapeClass::Reph)?;
self.parse_standard(false)?;
}
CS => {
self.accept_any()?;
self.parse_standard(false)?;
}
B | GB => {
let is_potential_symbol = self.kind() == GB;
self.parse_standard(is_potential_symbol)?;
}
N => {
self.accept_any_as(ShapeClass::Base)?;
self.accept_as(VS, ShapeClass::Vs)?;
while self.parse_halant_number()? {}
}
S => {
self.accept_any_as(ShapeClass::Base)?;
self.accept_as(VS, ShapeClass::Vs)?;
self.accept_zero_or_many(SMAbv)?;
self.accept_zero_or_many(SMBlw)?;
}
_ => {
self.parse_standard(false)?;
}
}
None
}
fn parse_standard(&mut self, is_potential_symbol: bool) -> Option<()> {
use UseClass::*;
match self.kind() {
B | GB => {
self.accept_any_as(ShapeClass::Base)?;
self.parse_standard_tail(is_potential_symbol)?;
}
_ => {
self.cluster.info_mut().set_broken();
self.accept_any_as(self.kind().to_shape_class())?;
}
}
Some(())
}
fn parse_standard_tail(&mut self, is_potential_symbol: bool) -> Option<()> {
use UseClass::*;
self.accept_as(VS, ShapeClass::Vs)?;
let k = self.kind();
if is_potential_symbol && (k == SMAbv || k == SMBlw) {
self.accept_zero_or_many(SMAbv)?;
self.accept_zero_or_many(SMBlw)?;
return Some(());
}
self.accept_zero_or_many(CMAbv);
self.accept_zero_or_many(CMBlw);
while self.parse_halant_base()? {}
if self.vt {
return Some(());
}
self.accept(MPre)?;
self.accept(MAbv)?;
self.accept(MBlw)?;
self.accept(MBlw)?;
self.accept(MPst)?;
self.accept_zero_or_many_as(VPre, ShapeClass::VPre)?;
self.accept_zero_or_many(VAbv)?;
self.accept_zero_or_many(VBlw)?;
self.accept_zero_or_many(VPst)?;
while self.parse_vowel_modifier()? {}
self.accept_zero_or_many(FAbv)?;
self.accept_zero_or_many(FBlw)?;
self.accept_zero_or_many(FPst)?;
self.accept(FM)?;
Some(())
}
fn parse_vowel_modifier(&mut self) -> Option<bool> {
use UseClass::*;
Some(match self.kind() {
VMPre => {
self.accept_any_as(ShapeClass::VMPre)?;
true
}
VMAbv | VMBlw | VMPst => {
self.accept_any()?;
true
}
// Spec break: some scripts allow a virama as a vowel modifier and
// there are particular cases of split vowel characters that
// decompose into vowel + halant. Accept a halant here, but emit
// it as Other to avoid any effects on reordering.
H => {
self.accept_any()?;
true
}
_ => false,
})
}
fn parse_halant_base(&mut self) -> Option<bool> {
use UseClass::*;
self.vt = false;
match self.kind() {
SUB => {
self.accept_any()?;
self.accept_zero_or_many(CMAbv)?;
self.accept_zero_or_many(CMBlw)?;
return Some(true);
}
H => {
self.vt = true;
if self.s.chars.script == Script::Khmer && self.s.cur.ch == '\u{17d2}' {
self.accept_any_as(ShapeClass::Other)?;
} else {
self.accept_any_as(ShapeClass::Halant)?;
}
match self.kind() {
B => {
self.vt = false;
self.accept_any_as(ShapeClass::Base)?;
self.accept_as(VS, ShapeClass::Vs)?;
self.accept_zero_or_many(CMAbv)?;
self.accept_zero_or_many(CMBlw)?;
return Some(true);
}
_ => {
return Some(false);
}
}
}
_ => {}
}
Some(false)
}
fn parse_halant_number(&mut self) -> Option<bool> {
use UseClass::*;
match self.kind() {
HN => {
self.accept_any_as(ShapeClass::Halant)?;
match self.kind() {
N => {
self.accept_any_as(ShapeClass::Base)?;
self.accept_as(VS, ShapeClass::Vs)?;
Some(true)
}
_ => Some(false),
}
}
_ => None,
}
}
fn parse_emoji_extension(&mut self) -> Option<bool> {
use ClusterBreak::*;
loop {
match self.s.cur.info.cluster_break() {
EX => match self.s.cur.ch as u32 {
0x200C => self.accept_any_as(ShapeClass::Zwnj)?,
0xFE0F => {
self.cluster.info_mut().set_emoji(Emoji::Color);
self.cluster.note_char(&self.s.cur);
self.advance()?;
}
0xFE0E => {
self.cluster.info_mut().set_emoji(Emoji::Text);
self.cluster.note_char(&self.s.cur);
self.advance()?;
}
_ => self.accept_any_as(ShapeClass::Mark)?,
},
ZWJ => {
self.accept_any_as(ShapeClass::Zwj)?;
return Some(true);
}
_ => break,
}
}
Some(false)
}
#[inline(always)]
fn emoji(&self) -> bool {
self.s.cur_emoji
}
#[inline(always)]
fn kind(&self) -> Kind {
self.s.cur_kind
}
fn accept(&mut self, kind: Kind) -> Option<bool> {
self.accept_as(kind, ShapeClass::Other)
}
fn accept_as(&mut self, kind: Kind, as_class: ShapeClass) -> Option<bool> {
if self.s.cur_kind == kind {
self.accept_any_as(as_class)?;
Some(true)
} else {
Some(false)
}
}
fn accept_zero_or_many(&mut self, kind: Kind) -> Option<bool> {
let mut some = false;
while self.accept(kind)? {
some = true;
}
Some(some)
}
fn accept_zero_or_many_as(&mut self, kind: Kind, as_class: ShapeClass) -> Option<bool> {
let mut some = false;
while self.accept_as(kind, as_class)? {
some = true;
}
Some(some)
}
fn accept_any(&mut self) -> Option<()> {
self.cluster.push(&self.s.cur, ShapeClass::Other);
self.advance()?;
Some(())
}
fn accept_any_as(&mut self, as_class: ShapeClass) -> Option<()> {
self.cluster.push(&self.s.cur, as_class);
self.advance()?;
Some(())
}
fn advance(&mut self) -> Option<()> {
if self.cluster.len() as usize == MAX_CLUSTER_SIZE {
return None;
}
if let Some((input, kind, emoji)) = self.s.chars.next() {
self.s.cur = input;
self.s.cur_emoji = emoji;
self.s.cur_kind = kind;
if input.ch == '\u{34f}' {
self.accept_any_as(ShapeClass::Other)?;
}
Some(())
} else {
self.s.done = true;
None
}
}
}
impl UseClass {
pub fn to_shape_class(self) -> ShapeClass {
match self {
Self::B => ShapeClass::Base,
Self::H => ShapeClass::Halant,
Self::VPre => ShapeClass::VPre,
Self::VMPre => ShapeClass::VMPre,
Self::VBlw => ShapeClass::VBlw,
Self::R => ShapeClass::Reph,
Self::ZWNJ => ShapeClass::Zwnj,
Self::ZWJ => ShapeClass::Zwj,
_ => ShapeClass::Other,
}
}
}
#[derive(Clone)]
struct Tokens<I> {
iter: I,
decomp: [(Token, UseClass); 3],
decomp_len: u8,
decomp_offset: u8,
script: Script,
}
impl<I> Tokens<I> {
fn new(script: Script, iter: I) -> Self {
Self {
iter,
decomp: [(Token::default(), UseClass::O); 3],
decomp_len: 0,
decomp_offset: 0,
script,
}
}
}
impl<I> Iterator for Tokens<I>
where
I: Iterator<Item = Token> + Clone,
{
type Item = (Token, UseClass, bool);
fn next(&mut self) -> Option<Self::Item> {
if self.decomp_offset < self.decomp_len {
let (input, class) = self.decomp[self.decomp_offset as usize];
self.decomp_offset += 1;
Some((input, class, false))
} else {
let input = self.iter.next()?;
let (class, needs_decomp, emoji) = input.info.use_class();
if needs_decomp {
self.decomp_offset = 0;
self.decomp_len = 0;
for c in input.ch.decompose().chars() {
if self.decomp_len == 3 {
// shouldn't happen
break;
}
let props = c.properties();
let (class, ..) = props.use_class();
let c2 = Token {
ch: *c,
info: input.info.with_properties(props),
..input
};
self.decomp[self.decomp_len as usize] = (c2, class);
self.decomp_len += 1;
}
//self.decomp[..self.decomp_len as usize].reverse(); //.sort_unstable_by(|a, b| a.3.cmp(&b.3));
return self.next();
} else if self.script == Script::Khmer {
match input.ch as u32 {
0x17BE | 0x17BF | 0x17C0 | 0x17C4 | 0x17C5 => {
let a = '\u{17C1}';
let props = a.properties();
let a_class = props.use_class().0;
let a = Token {
ch: a,
info: input.info.with_properties(props),
..input
};
self.decomp[0] = (a, a_class);
self.decomp[1] = (input, class);
self.decomp_len = 2;
self.decomp_offset = 0;
return self.next();
}
_ => {}
}
}
Some((input, class, emoji))
}
}
}

236
vendor/swash/src/text/cluster/info.rs vendored Normal file
View File

@@ -0,0 +1,236 @@
use super::super::Properties;
use super::Boundary;
use core::fmt;
/// Information about a character including unicode properties and boundary
/// analysis.
#[derive(Copy, Clone, PartialEq, Eq, Default)]
pub struct CharInfo(pub(crate) Properties);
impl CharInfo {
/// Creates new character information from Unicode properties and
/// boundary analysis.
pub fn new(properties: Properties, boundary: Boundary) -> Self {
Self(properties.with_boundary(boundary as u16))
}
/// Returns the unicode properties for the character.
pub fn properties(self) -> Properties {
self.0
}
/// Returns the boundary state.
pub fn boundary(self) -> Boundary {
Boundary::from_raw(self.0.boundary())
}
pub(crate) fn with_properties(self, props: Properties) -> Self {
Self(props.with_boundary(self.0.boundary()))
}
}
impl core::ops::Deref for CharInfo {
type Target = Properties;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<char> for CharInfo {
fn from(c: char) -> Self {
Self(Properties::from(c))
}
}
impl From<CharInfo> for Properties {
fn from(a: CharInfo) -> Self {
a.0
}
}
impl From<&CharInfo> for Properties {
fn from(a: &CharInfo) -> Self {
a.0
}
}
impl From<Properties> for CharInfo {
fn from(p: Properties) -> Self {
Self(p)
}
}
impl From<&Properties> for CharInfo {
fn from(p: &Properties) -> Self {
Self(*p)
}
}
const BOUND_SHIFT: u16 = 14;
const SPACE_SHIFT: u16 = 1;
const EMOJI_SHIFT: u16 = 8;
const SPACE_MASK: u16 = 0b111;
const EMOJI_MASK: u16 = 0b11;
/// Information about a cluster including content properties and boundary analysis.
#[derive(Copy, Clone, PartialEq, Eq, Default)]
pub struct ClusterInfo(pub u16);
impl ClusterInfo {
/// Returns true if the cluster is missing an appropriate base
/// character.
pub fn is_broken(self) -> bool {
self.0 & 1 != 0
}
/// Returns true if the cluster is an emoji.
pub fn is_emoji(self) -> bool {
(self.0 >> EMOJI_SHIFT & EMOJI_MASK) != 0
}
/// Returns the emoji presentation mode of the cluster.
pub fn emoji(self) -> Emoji {
Emoji::from_raw(self.0 >> EMOJI_SHIFT & EMOJI_MASK)
}
/// Returns true if the cluster is whitespace.
pub fn is_whitespace(self) -> bool {
(self.0 >> SPACE_SHIFT & SPACE_MASK) != 0
}
/// Returns the whitespace content of the cluster.
pub fn whitespace(self) -> Whitespace {
Whitespace::from_raw(self.0 >> SPACE_SHIFT & SPACE_MASK)
}
/// Returns true if the cluster is a boundary.
pub fn is_boundary(self) -> bool {
(self.0 >> BOUND_SHIFT) != 0
}
/// Returns the boundary state of the cluster.
pub fn boundary(self) -> Boundary {
Boundary::from_raw(self.0 >> BOUND_SHIFT)
}
pub(super) fn set_broken(&mut self) {
self.0 |= 1;
}
pub(super) fn set_emoji(&mut self, emoji: Emoji) {
self.0 = self.0 & !(EMOJI_MASK << EMOJI_SHIFT) | (emoji as u16) << EMOJI_SHIFT;
}
pub(super) fn set_space(&mut self, space: Whitespace) {
self.0 = self.0 & !(SPACE_MASK << SPACE_SHIFT) | (space as u16) << SPACE_SHIFT;
}
#[inline]
pub(super) fn set_space_from_char(&mut self, ch: char) {
match ch {
' ' => self.set_space(Whitespace::Space),
'\u{a0}' => self.set_space(Whitespace::NoBreakSpace),
'\t' => self.set_space(Whitespace::Tab),
_ => {}
}
}
pub(super) fn merge_boundary(&mut self, boundary: u16) {
let bits = (self.0 >> BOUND_SHIFT).max(boundary) << BOUND_SHIFT;
self.0 = ((self.0 << 2) >> 2) | bits;
}
}
impl fmt::Debug for ClusterInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
let e = match self.emoji() {
Emoji::None => " ",
Emoji::Default => "E",
Emoji::Text => "T",
Emoji::Color => "C",
};
let s = match self.whitespace() {
Whitespace::None => " ",
Whitespace::Space => "s",
Whitespace::NoBreakSpace => "b",
Whitespace::Tab => "t",
Whitespace::Newline => "n",
Whitespace::Other => "o",
};
write!(f, "{}", if self.is_broken() { "!" } else { " " })?;
let b = match self.boundary() {
Boundary::Mandatory => "L",
Boundary::Line => "l",
Boundary::Word => "w",
_ => " ",
};
write!(f, "{}{}{}", e, s, b)
}
}
/// Presentation mode for an emoji cluster.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum Emoji {
/// Not an emoji.
None = 0,
/// Emoji with default presentation.
Default = 1,
/// Emoji with text presentation.
Text = 2,
/// Emoji with color presentation.
Color = 3,
}
impl Emoji {
#[inline]
fn from_raw(bits: u16) -> Self {
match bits & 0b11 {
0 => Self::None,
1 => Self::Default,
2 => Self::Text,
3 => Self::Color,
_ => Self::None,
}
}
}
/// Whitespace content of a cluster.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum Whitespace {
/// Not a space.
None = 0,
/// Standard space.
Space = 1,
/// Non-breaking space (U+00A0).
NoBreakSpace = 2,
/// Horizontal tab.
Tab = 3,
/// Newline (CR, LF, or CRLF).
Newline = 4,
/// Other space.
Other = 5,
}
impl Whitespace {
/// Returns true for space or no break space.
pub fn is_space_or_nbsp(self) -> bool {
matches!(self, Self::Space | Self::NoBreakSpace)
}
#[inline]
fn from_raw(bits: u16) -> Self {
match bits & 0b111 {
0 => Self::None,
1 => Self::Space,
2 => Self::NoBreakSpace,
3 => Self::Tab,
4 => Self::Newline,
5 => Self::Other,
_ => Self::None,
}
}
}

58
vendor/swash/src/text/cluster/mod.rs vendored Normal file
View File

@@ -0,0 +1,58 @@
/*!
Script aware cluster segmentation.
This module provides support for breaking text into clusters that are
appropriate for shaping with a given script. For most scripts, clusters are
equivalent to Unicode grapheme clusters. More complex scripts, however,
may produce shaping clusters that contain multiple graphemes.
*/
mod char;
#[allow(clippy::module_inception)]
mod cluster;
mod complex;
mod info;
mod myanmar;
mod parse;
mod simple;
mod token;
pub use self::{
char::{Char, ShapeClass},
cluster::{CharCluster, SourceRange, Status, MAX_CLUSTER_SIZE},
info::{CharInfo, ClusterInfo, Emoji, Whitespace},
parse::Parser,
token::Token,
};
use super::unicode_data;
/// Boundary type of a character or cluster.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum Boundary {
/// Not a boundary.
None = 0,
/// Start of a word.
Word = 1,
/// Potential line break.
Line = 2,
/// Mandatory line break.
Mandatory = 3,
}
impl Boundary {
pub(super) fn from_raw(raw: u16) -> Self {
match raw & 0b11 {
0 => Self::None,
1 => Self::Word,
2 => Self::Line,
3 => Self::Mandatory,
_ => Self::None,
}
}
}
/// Arbitrary user data that can be associated with a character throughout
/// the shaping pipeline.
pub type UserData = u32;

337
vendor/swash/src/text/cluster/myanmar.rs vendored Normal file
View File

@@ -0,0 +1,337 @@
//! Parser for Myanmar clusters.
use super::unicode_data::{Category, ClusterBreak, MyanmarClass};
use super::{CharCluster, Emoji, ShapeClass, Token, Whitespace, MAX_CLUSTER_SIZE};
type Kind = MyanmarClass;
pub struct MyanmarState<I> {
chars: I,
cur: Token,
cur_kind: Kind,
cur_emoji: bool,
done: bool,
}
impl<I> MyanmarState<I>
where
I: Iterator<Item = Token> + Clone,
{
pub fn new(mut chars: I) -> Self {
if let Some(first) = chars.by_ref().next() {
let (kind, emoji) = first.info.myanmar_class();
Self {
chars,
cur: first,
cur_kind: kind,
cur_emoji: emoji,
done: false,
}
} else {
Self {
chars,
cur: Token::default(),
cur_kind: MyanmarClass::O,
cur_emoji: false,
done: true,
}
}
}
pub fn next(&mut self, cluster: &mut CharCluster) -> bool {
if self.done {
return false;
}
Parser::new(self, cluster).parse();
true
}
}
struct Parser<'a, I> {
s: &'a mut MyanmarState<I>,
cluster: &'a mut CharCluster,
vt: bool,
}
impl<'a, I> Parser<'a, I>
where
I: Iterator<Item = Token> + Clone,
{
fn new(s: &'a mut MyanmarState<I>, cluster: &'a mut CharCluster) -> Self {
Self {
s,
cluster,
vt: false,
}
}
fn parse(&mut self) -> Option<()> {
use MyanmarClass::*;
if self.s.done {
return Some(());
}
if self.emoji() {
self.cluster.info_mut().set_emoji(Emoji::Default);
while self.emoji() {
self.accept_any_as(ShapeClass::Base)?;
if !self.parse_emoji_extension()? {
break;
}
}
return Some(());
}
match self.kind() {
O => {
// This is not in the Myanmar spec, but added to support uniform
// clustering of CRLF across the parsers.
match self.s.cur.ch {
'\r' => {
self.cluster.info_mut().set_space(Whitespace::Newline);
self.accept_any_as(ShapeClass::Control)?;
if self.s.cur.ch == '\n' {
self.accept_any_as(ShapeClass::Control)?;
}
}
'\n' => {
self.cluster.info_mut().set_space(Whitespace::Newline);
self.accept_any_as(ShapeClass::Control)?;
}
_ => {
self.cluster.info_mut().set_space_from_char(self.s.cur.ch);
let class = match self.s.cur.info.category() {
Category::Format => match self.s.cur.ch as u32 {
0x200C => ShapeClass::Zwnj,
0x200D => ShapeClass::Zwj,
_ => ShapeClass::Control,
},
Category::Control => ShapeClass::Control,
_ => ShapeClass::Base,
};
self.accept_any_as(class)?;
}
}
}
P | S | R | WJ | D0 => {
self.accept_any()?;
}
_ => {
match self.s.cur.ch as u32 {
0x1004 | 0x101B | 0x105A => {
let mut iter = self.s.chars.clone();
if let Some(b) = iter.next() {
if b.ch == '\u{103A}' {
if let Some(c) = iter.next() {
if c.ch == '\u{1039}' {
self.cluster.push(&self.s.cur, ShapeClass::Kinzi);
self.cluster.push(&b, ShapeClass::Kinzi);
self.cluster.push(&c, ShapeClass::Kinzi);
self.advance();
self.advance();
self.advance();
}
}
}
}
}
_ => {}
}
match self.kind() {
C | IV | D | DB => {
self.accept_any_as(ShapeClass::Base)?;
self.accept_as(VS, ShapeClass::Vs)?;
while self.parse_stacked_consonant_or_vowel()? {}
if self.vt {
return Some(());
}
self.accept_zero_or_many(As)?;
if self.accept(MY)? {
self.accept(As)?;
}
self.accept_as(MR, ShapeClass::MedialRa)?;
if self.accept(MW)? {
self.accept(MH)?;
self.accept(As)?;
} else if self.accept(MH)? {
self.accept(As)?;
}
self.accept_zero_or_many_as(VPre, ShapeClass::VPre)?;
self.accept_zero_or_many(VAbv)?;
self.accept_zero_or_many_as(VBlw, ShapeClass::VBlw)?;
self.accept_zero_or_many_as(A, ShapeClass::Anusvara)?;
if self.accept(DB)? {
self.accept(As)?;
}
while self.parse_post_base_vowel()? {}
while self.parse_pwo_tone_mark()? {}
self.accept_zero_or_many(V)?;
self.accept(J)?;
return Some(());
}
_ => {
self.cluster.info_mut().set_broken();
self.accept_any()?;
return Some(());
}
}
}
}
None
}
fn parse_stacked_consonant_or_vowel(&mut self) -> Option<bool> {
use MyanmarClass::*;
match self.kind() {
H => {
self.vt = true;
self.accept_any_as(ShapeClass::Halant)?;
match self.kind() {
C | IV => {
self.vt = false;
self.accept_any_as(ShapeClass::Base)?;
self.accept_as(VS, ShapeClass::Vs)?;
Some(true)
}
_ => Some(false),
}
}
_ => Some(false),
}
}
fn parse_post_base_vowel(&mut self) -> Option<bool> {
use MyanmarClass::*;
match self.kind() {
VPst => {
self.accept_any()?;
self.accept(MH)?;
self.accept_zero_or_many(As)?;
self.accept_zero_or_many(VAbv)?;
self.accept_zero_or_many_as(A, ShapeClass::Anusvara)?;
if self.accept(DB)? {
self.accept(As)?;
}
Some(true)
}
_ => Some(false),
}
}
fn parse_pwo_tone_mark(&mut self) -> Option<bool> {
use MyanmarClass::*;
match self.kind() {
PT => {
self.accept_any()?;
if self.accept(As)? {
self.accept_as(A, ShapeClass::Anusvara)?;
} else {
// This goes against the spec, but seems to be necessary to handle the actual
// example of a complex cluster here:
// https://docs.microsoft.com/en-us/typography/script-development/myanmar#well-formed-clusters
self.accept_zero_or_many_as(A, ShapeClass::Anusvara)?; // self.accept(A)?;
self.accept(DB)?;
self.accept(As)?;
}
Some(true)
}
_ => Some(false),
}
}
fn parse_emoji_extension(&mut self) -> Option<bool> {
use ClusterBreak::*;
loop {
match self.s.cur.info.cluster_break() {
EX => match self.s.cur.ch as u32 {
0x200C => self.accept_any_as(ShapeClass::Zwnj)?,
0xFE0F => {
self.cluster.info_mut().set_emoji(Emoji::Color);
self.cluster.note_char(&self.s.cur);
self.advance()?;
}
0xFE0E => {
self.cluster.info_mut().set_emoji(Emoji::Text);
self.cluster.note_char(&self.s.cur);
self.advance()?;
}
_ => self.accept_any_as(ShapeClass::Mark)?,
},
ZWJ => {
self.accept_any_as(ShapeClass::Zwj)?;
return Some(true);
}
_ => break,
}
}
Some(false)
}
#[inline(always)]
fn emoji(&self) -> bool {
self.s.cur_emoji
}
#[inline(always)]
fn kind(&self) -> Kind {
self.s.cur_kind
}
fn accept(&mut self, kind: Kind) -> Option<bool> {
self.accept_as(kind, ShapeClass::Other)
}
fn accept_as(&mut self, kind: Kind, as_class: ShapeClass) -> Option<bool> {
if self.s.cur_kind == kind {
self.accept_any_as(as_class)?;
Some(true)
} else {
Some(false)
}
}
fn accept_zero_or_many(&mut self, kind: Kind) -> Option<bool> {
let mut some = false;
while self.accept(kind)? {
some = true;
}
Some(some)
}
fn accept_zero_or_many_as(&mut self, kind: Kind, as_class: ShapeClass) -> Option<bool> {
let mut some = false;
while self.accept_as(kind, as_class)? {
some = true;
}
Some(some)
}
fn accept_any(&mut self) -> Option<()> {
self.cluster.push(&self.s.cur, ShapeClass::Other);
self.advance()?;
Some(())
}
fn accept_any_as(&mut self, as_class: ShapeClass) -> Option<()> {
self.cluster.push(&self.s.cur, as_class);
self.advance()?;
Some(())
}
fn advance(&mut self) -> Option<()> {
if self.cluster.len() as usize == MAX_CLUSTER_SIZE {
return None;
}
if let Some(input) = self.s.chars.next() {
let (kind, emoji) = input.info.myanmar_class();
self.s.cur = input;
self.s.cur_emoji = emoji;
self.s.cur_kind = kind;
if input.ch == '\u{34f}' {
self.accept_any()?;
}
Some(())
} else {
self.s.done = true;
None
}
}
}

53
vendor/swash/src/text/cluster/parse.rs vendored Normal file
View File

@@ -0,0 +1,53 @@
use super::{
super::Script, complex::ComplexState, myanmar::MyanmarState, simple::SimpleState, CharCluster,
Token,
};
/// Parser that accepts a sequence of characters and outputs character clusters.
pub struct Parser<I> {
inner: Inner<I>,
}
// enum Inner<I> {
// Simple(SimpleClusters<Filter<I>>),
// Myanmar(MyanmarClusters<Filter<I>>),
// Complex(ComplexClusters<Filter<I>>),
// }
enum Inner<I> {
Simple(SimpleState<I>),
Myanmar(MyanmarState<I>),
Complex(ComplexState<I>),
}
impl<I> Parser<I>
where
I: Iterator<Item = Token> + Clone,
{
/// Creates a new cluster parser for the specified script and iterator
/// over tokens.
pub fn new(script: Script, tokens: I) -> Self {
Self {
inner: if script.is_complex() {
if script == Script::Myanmar {
Inner::Myanmar(MyanmarState::new(tokens))
} else {
Inner::Complex(ComplexState::new(script, tokens))
}
} else {
Inner::Simple(SimpleState::new(tokens))
},
}
}
/// Parses the next cluster.
#[inline]
pub fn next(&mut self, cluster: &mut CharCluster) -> bool {
cluster.clear();
match self.inner {
Inner::Simple(ref mut c) => c.next(cluster),
Inner::Myanmar(ref mut c) => c.next(cluster),
Inner::Complex(ref mut c) => c.next(cluster),
}
}
}

235
vendor/swash/src/text/cluster/simple.rs vendored Normal file
View File

@@ -0,0 +1,235 @@
//! Simple cluster formation (unicode grapheme cluster algorithm).
use super::super::ClusterBreak;
use super::{CharCluster, Emoji, ShapeClass, Token, Whitespace, MAX_CLUSTER_SIZE};
pub struct SimpleState<I> {
chars: I,
cur: Token,
cur_kind: ClusterBreak,
cur_emoji: bool,
done: bool,
}
impl<I> SimpleState<I>
where
I: Iterator<Item = Token>,
{
pub fn new(mut chars: I) -> Self {
if let Some(first) = chars.by_ref().next() {
let (kind, emoji) = first.info.cluster_class();
Self {
chars,
cur: first,
cur_kind: kind,
cur_emoji: emoji,
done: false,
}
} else {
Self {
chars,
cur: Token::default(),
cur_kind: ClusterBreak::XX,
cur_emoji: false,
done: true,
}
}
}
pub fn next(&mut self, cluster: &mut CharCluster) -> bool {
if self.done {
return false;
}
Parser { s: self, cluster }.parse();
true
}
}
pub struct Parser<'a, I> {
s: &'a mut SimpleState<I>,
cluster: &'a mut CharCluster,
}
impl<'a, I> Parser<'a, I>
where
I: Iterator<Item = Token>,
{
fn parse(&mut self) -> Option<()> {
use ClusterBreak::*;
while self.accept(PP)? {}
if self.emoji() {
self.cluster.info_mut().set_emoji(Emoji::Default);
while self.emoji() {
self.accept_any()?;
if !self.parse_emoji_extension()? {
break;
}
}
} else {
match self.kind() {
CN => {
self.accept_any_as(ShapeClass::Control)?;
}
LF => {
self.cluster.info_mut().set_space(Whitespace::Newline);
self.accept_any_as(ShapeClass::Control)?;
}
CR => {
self.cluster.info_mut().set_space(Whitespace::Newline);
self.accept_any_as(ShapeClass::Control)?;
self.accept_as(LF, ShapeClass::Control)?;
}
L => {
self.accept_any()?;
match self.kind() {
L | V | LV | LVT => {
self.accept_any()?;
}
_ => {}
}
}
LV | V => {
self.accept_any()?;
match self.kind() {
V | T => {
self.accept_any()?;
}
_ => {}
}
}
LVT | T => {
self.accept_any()?;
self.accept(T)?;
}
RI => {
self.accept(RI)?;
}
EX | SM | ZWJ => {
self.cluster.info_mut().set_broken();
}
_ => {
self.cluster.info_mut().set_space_from_char(self.s.cur.ch);
self.accept_any()?;
}
}
}
while self.parse_extension()? {}
Some(())
}
fn parse_emoji_extension(&mut self) -> Option<bool> {
use ClusterBreak::*;
loop {
match self.kind() {
EX => match self.s.cur.ch as u32 {
0x200C => self.accept_any_as(ShapeClass::Zwnj)?,
0xFE0F => {
self.cluster.info_mut().set_emoji(Emoji::Color);
self.cluster.note_char(&self.s.cur);
self.advance()?;
}
0xFE0E => {
self.cluster.info_mut().set_emoji(Emoji::Text);
self.cluster.note_char(&self.s.cur);
self.advance()?;
}
_ => self.accept_any_as(ShapeClass::Mark)?,
},
ZWJ => {
self.accept_any_as(ShapeClass::Zwj)?;
return Some(true);
}
_ => break,
}
}
Some(false)
}
fn parse_extension(&mut self) -> Option<bool> {
use ClusterBreak::*;
Some(match self.kind() {
EX => {
if self.s.cur.ch as u32 == 0x200C {
self.accept_any_as(ShapeClass::Zwnj)?;
} else if self.s.cur.info.is_variation_selector() {
self.accept_any_as(ShapeClass::Vs)?;
} else {
self.cluster.force_normalize();
self.accept_any_as(ShapeClass::Mark)?;
}
true
}
SM => {
self.cluster.force_normalize();
self.accept_any_as(ShapeClass::Mark)?;
true
}
ZWJ => {
self.accept_any_as(ShapeClass::Zwj)?;
true
}
_ => false,
})
}
#[inline(always)]
fn emoji(&self) -> bool {
self.s.cur_emoji
}
#[inline(always)]
fn kind(&self) -> ClusterBreak {
self.s.cur_kind
}
fn accept(&mut self, kind: ClusterBreak) -> Option<bool> {
if self.s.cur_kind == kind {
self.accept_any()?;
Some(true)
} else {
Some(false)
}
}
fn accept_as(&mut self, kind: ClusterBreak, as_kind: ShapeClass) -> Option<bool> {
if self.s.cur_kind == kind {
self.accept_any_as(as_kind)?;
Some(true)
} else {
Some(false)
}
}
fn accept_any(&mut self) -> Option<()> {
self.push_cur();
self.advance()?;
Some(())
}
fn accept_any_as(&mut self, as_kind: ShapeClass) -> Option<()> {
self.cluster.push(&self.s.cur, as_kind);
self.advance()?;
Some(())
}
fn advance(&mut self) -> Option<()> {
if self.cluster.len() as usize == MAX_CLUSTER_SIZE {
return None;
}
if let Some(input) = self.s.chars.next() {
let (kind, emoji) = input.info.cluster_class();
self.s.cur = input;
self.s.cur_emoji = emoji;
self.s.cur_kind = kind;
Some(())
} else {
self.s.done = true;
None
}
}
#[inline]
fn push_cur(&mut self) {
self.cluster.push(&self.s.cur, ShapeClass::Base);
}
}

28
vendor/swash/src/text/cluster/token.rs vendored Normal file
View File

@@ -0,0 +1,28 @@
use super::{CharInfo, UserData};
/// Character input to the cluster parser.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Token {
/// The character.
pub ch: char,
/// Offset of the character in code units.
pub offset: u32,
/// Length of the character in code units.
pub len: u8,
/// Character information.
pub info: CharInfo,
/// Arbitrary user data.
pub data: UserData,
}
impl Default for Token {
fn default() -> Self {
Self {
ch: '\0',
offset: 0,
len: 1,
info: Default::default(),
data: 0,
}
}
}

187
vendor/swash/src/text/compose.rs vendored Normal file
View File

@@ -0,0 +1,187 @@
use super::unicode_data::{
compose_index, decompose_compat_index, decompose_index, COMPOSE0, COMPOSE1, COMPOSE1_COUNT,
DECOMPOSE, DECOMPOSE_COMPAT,
};
use core::char::from_u32_unchecked;
/// Decomposition of a character.
#[derive(Copy, Clone)]
pub struct Decompose {
inner: DecomposeInner,
len: u8,
cur: u8,
}
impl Decompose {
/// Returns the sequence of characters that represent the
/// decomposition.
pub fn chars(&self) -> &[char] {
match self.inner {
DecomposeInner::Slice(chars) => chars,
DecomposeInner::Array(ref chars, len) => &chars[..len as usize],
}
}
}
impl Iterator for Decompose {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
if self.cur >= self.len {
return None;
}
let item = self.chars()[self.cur as usize];
self.cur += 1;
Some(item)
}
}
#[derive(Copy, Clone)]
enum DecomposeInner {
Slice(&'static [char]),
Array([char; 3], u32),
}
impl DecomposeInner {
fn len(&self) -> u8 {
match self {
Self::Slice(s) => s.len() as u8,
Self::Array(_, len) => *len as u8,
}
}
}
impl From<DecomposeInner> for Decompose {
fn from(inner: DecomposeInner) -> Self {
Self {
inner,
len: inner.len(),
cur: 0,
}
}
}
pub fn compose_pair(a: char, b: char) -> Option<char> {
if let Some(c) = compose_hangul(a, b) {
return Some(c);
}
let l = pair_index(a as u32, &COMPOSE0[..])?;
let r = pair_index(b as u32, &COMPOSE1[..])?;
let c = compose_index(l * COMPOSE1_COUNT + r) as u32;
if c != 0 {
return Some(unsafe { core::char::from_u32_unchecked(c) });
}
None
}
fn pair_index(c: u32, table: &[(u32, u16, u16)]) -> Option<usize> {
let c = c as usize;
for entry in table {
let start = entry.0 as usize;
if start == 0 || c < start {
return None;
}
let end = start + entry.1 as usize;
if c <= end {
return Some(entry.2 as usize + (c - start));
}
}
None
}
const LBASE: u32 = 0x1100;
const VBASE: u32 = 0x1161;
const TBASE: u32 = 0x11A7;
const LCOUNT: u32 = 19;
const VCOUNT: u32 = 21;
const TCOUNT: u32 = 28;
const SBASE: u32 = 0xAC00;
const NCOUNT: u32 = VCOUNT * TCOUNT;
const SCOUNT: u32 = LCOUNT * NCOUNT;
fn is_hangul(c: char) -> bool {
let c = c as u32;
(SBASE..(SBASE + SCOUNT)).contains(&c)
}
fn compose_hangul(a: char, b: char) -> Option<char> {
let a = a as u32;
let b = b as u32;
if !(VBASE..(TBASE + TCOUNT)).contains(&b) {
return None;
}
if !(LBASE..(LBASE + LCOUNT)).contains(&a) && !(SBASE..(SBASE + SCOUNT)).contains(&a) {
return None;
}
if a >= SBASE {
if (a - SBASE) % TCOUNT == 0 {
Some(unsafe { from_u32_unchecked(a + (b - TBASE)) })
} else {
None
}
} else {
let li = a - LBASE;
let vi = b - VBASE;
Some(unsafe { from_u32_unchecked(SBASE + li * NCOUNT + vi * TCOUNT) })
}
}
fn decompose_hangul(c: char) -> DecomposeInner {
let si = c as u32 - SBASE;
let li = si / NCOUNT;
let mut chars = [' '; 3];
let mut len = 2;
unsafe {
chars[0] = from_u32_unchecked(LBASE + li);
let vi = (si % NCOUNT) / TCOUNT;
chars[1] = from_u32_unchecked(VBASE + vi);
let ti = si % TCOUNT;
if ti > 0 {
chars[2] = from_u32_unchecked(TBASE + ti);
len += 1;
}
}
DecomposeInner::Array(chars, len)
}
pub fn decompose(c: char) -> Decompose {
if c <= '\x7F' {
DecomposeInner::Array([c, ' ', ' '], 1).into()
} else if is_hangul(c) {
decompose_hangul(c).into()
} else {
let index = decompose_index(c as usize);
if index == 0 {
DecomposeInner::Array([c, ' ', ' '], 1).into()
} else {
let buf = &DECOMPOSE[index..];
let end = 1 + buf[0] as usize;
DecomposeInner::Slice(unsafe { &*(&buf[1..end] as *const [u32] as *const [char]) })
.into()
}
}
}
pub fn decompose_compat(c: char) -> Decompose {
if c <= '\x7F' {
DecomposeInner::Array([c, ' ', ' '], 1).into()
} else if is_hangul(c) {
decompose_hangul(c).into()
} else {
let index = decompose_compat_index(c as usize);
if index == 0 {
DecomposeInner::Array([c, ' ', ' '], 1).into()
} else if index == 1 {
let index = decompose_index(c as usize);
let buf = &DECOMPOSE[index..];
let end = 1 + buf[0] as usize;
DecomposeInner::Slice(unsafe { &*(&buf[1..end] as *const [u32] as *const [char]) })
.into()
} else {
let buf = &DECOMPOSE_COMPAT[index..];
let end = 1 + buf[0] as usize;
DecomposeInner::Slice(unsafe { &*(&buf[1..end] as *const [u32] as *const [char]) })
.into()
}
}
}

233
vendor/swash/src/text/lang.rs vendored Normal file
View File

@@ -0,0 +1,233 @@
use crate::{tag_from_bytes, Tag};
use core::fmt;
use super::lang_data::*;
/// Chinese, Japanese and Korean languages.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[repr(u8)]
pub enum Cjk {
None = 0,
Traditional = 1,
Simplified = 2,
Japanese = 3,
Korean = 4,
}
/// Representation of a language and its associated script and region.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Language {
language: [u8; 3],
script: [u8; 4],
region: [u8; 2],
lang_len: u8,
script_len: u8,
region_len: u8,
cjk: Cjk,
name_index: u16,
tag: Option<Tag>,
}
impl Language {
/// Parses a language tag.
pub fn parse(tag: &str) -> Option<Self> {
let mut lang = Self {
language: [0; 3],
region: [0; 2],
script: [0; 4],
lang_len: 0,
region_len: 0,
script_len: 0,
cjk: Cjk::None,
name_index: 0xFFFF,
tag: None,
};
let mut has_region = false;
let mut zh = false;
let mut lang_index = 0xFFFF;
for (i, part) in tag.split('-').enumerate() {
let bytes = part.as_bytes();
let len = bytes.len();
match i {
0 => {
match len {
2 => {
let a = bytes[0].to_ascii_lowercase();
let b = bytes[1].to_ascii_lowercase();
match (a, b) {
(b'z', b'h') => zh = true,
(b'j', b'a') => lang.cjk = Cjk::Japanese,
(b'k', b'o') => lang.cjk = Cjk::Korean,
_ => {}
};
lang.language[0] = a;
lang.language[1] = b;
lang.lang_len = 2;
let key = tag2(&[a, b]);
if let Ok(index) = LANG_BY_TAG2.binary_search_by(|x| x.0.cmp(&key)) {
lang_index = LANG_BY_TAG2.get(index)?.1;
}
}
3 => {
let a = bytes[0].to_ascii_lowercase();
let b = bytes[1].to_ascii_lowercase();
let c = bytes[2].to_ascii_lowercase();
zh = a == b'z' && b == b'h' && c == b'o';
lang.language[0] = a;
lang.language[1] = b;
lang.language[2] = c;
lang.lang_len = 3;
let key = tag3(&[a, b, c]);
if let Ok(index) = LANG_BY_TAG3.binary_search_by(|x| x.0.cmp(&key)) {
lang_index = LANG_BY_TAG3.get(index)?.1 as u16;
}
}
_ => return None,
};
}
1 => match len {
2 => {
let a = bytes[0].to_ascii_uppercase();
let b = bytes[1].to_ascii_uppercase();
lang.region[0] = a;
lang.region[1] = b;
lang.region_len = 2;
has_region = true;
}
4 => {
let a = bytes[0].to_ascii_uppercase();
let b = bytes[1].to_ascii_lowercase();
let c = bytes[2].to_ascii_lowercase();
let d = bytes[3].to_ascii_lowercase();
lang.script[0] = a;
lang.script[1] = b;
lang.script[2] = c;
lang.script[3] = d;
lang.script_len = 4;
}
_ => break,
},
2 => {
if has_region || len != 2 {
break;
}
let a = bytes[0].to_ascii_uppercase();
let b = bytes[1].to_ascii_uppercase();
lang.region[0] = a;
lang.region[1] = b;
lang.region_len = 2;
has_region = true;
}
_ => break,
}
}
lang.name_index = lang_index;
if lang_index != 0xFFFF {
lang.tag = Some(*LANG_TAGS.get(lang_index as usize)?);
} else if zh {
let (tag, cjk) = match lang.script().unwrap_or("") {
"Hant" => (tag_from_bytes(b"ZHT "), Cjk::Traditional),
"Hans" => (tag_from_bytes(b"ZHS "), Cjk::Simplified),
_ => (tag_from_bytes(b"ZHT "), Cjk::Traditional),
};
lang.tag = Some(tag);
lang.cjk = cjk;
lang.name_index = match LANG_TAGS.binary_search_by(|x| x.cmp(&tag)) {
Ok(index) => index as u16,
_ => 0xFFFF,
};
}
Some(lang)
}
/// Returns the language associated with the specified OpenType language
/// tag.
pub fn from_opentype(tag: Tag) -> Option<Self> {
if tag == tag_from_bytes(b"ZHT ") {
return Self::parse("zh-Hant");
} else if tag == tag_from_bytes(b"ZHS ") {
return Self::parse("zh-Hans");
}
let name_index = match LANG_TAGS.binary_search_by(|x| x.cmp(&tag)) {
Ok(index) => index,
_ => return None,
};
Self::parse(LANG_ENTRIES.get(name_index)?.1)
}
/// Returns the language component.
pub fn language(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(&self.language[..self.lang_len as usize]) }
}
/// Returns the script component.
pub fn script(&self) -> Option<&str> {
Some(if self.script_len == 4 {
unsafe { core::str::from_utf8_unchecked(&self.script) }
} else {
return None;
})
}
/// Returns the region component.
pub fn region(&self) -> Option<&str> {
Some(if self.region_len == 2 {
unsafe { core::str::from_utf8_unchecked(&self.region) }
} else {
return None;
})
}
/// Returns the CJK language.
pub fn cjk(&self) -> Cjk {
self.cjk
}
/// Returns the name of the language.
pub fn name(&self) -> Option<&'static str> {
LANG_ENTRIES.get(self.name_index as usize).map(|e| e.0)
}
/// Returns the associated OpenType language tag.
pub fn to_opentype(self) -> Option<Tag> {
self.tag
}
}
impl fmt::Display for Language {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.language())?;
if let Some(script) = self.script() {
write!(f, "-{}", script)?;
}
if let Some(region) = self.region() {
write!(f, "-{}", region)?;
}
if let Some(name) = self.name() {
write!(f, " ({})", name)?;
}
Ok(())
}
}
impl fmt::Debug for Language {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.language())?;
if let Some(script) = self.script() {
write!(f, "-{}", script)?;
}
if let Some(region) = self.region() {
write!(f, "-{}", region)?;
}
if let Some(tag) = self.tag {
let tag = tag.to_be_bytes();
if let Ok(s) = core::str::from_utf8(&tag) {
write!(f, " ({})", s)?;
}
}
if let Some(name) = self.name() {
write!(f, " \"{}\"", name)?;
}
Ok(())
}
}

177
vendor/swash/src/text/lang_data.rs vendored Normal file
View File

@@ -0,0 +1,177 @@
#![allow(dead_code)]
pub const fn tag2(b: &[u8; 2]) -> u16 {
(b[0] as u16) << 8 | b[1] as u16
}
pub const fn tag3(b: &[u8; 3]) -> u32 {
(b[0] as u32) << 16 | (b[1] as u32) << 8 | b[2] as u32
}
pub const fn tag4(b: &[u8; 4]) -> u32 {
(b[0] as u32) << 24 | (b[1] as u32) << 16 | (b[2] as u32) << 8 | b[3] as u32
}
use self::tag2 as t2;
use self::tag3 as t3;
use self::tag4 as t4;
pub const ZHT_INDEX: u16 = 711;
#[rustfmt::skip]
pub const LANG_TAGS: [u32; 715] = [
t4(b"ABA "), t4(b"ABK "), t4(b"ACH "), t4(b"ACR "), t4(b"ADY "), t4(b"AFK "), t4(b"AFR "), t4(b"AGW "), t4(b"AIO "), t4(b"AKA "), t4(b"ALS "), t4(b"ALT "), t4(b"ALT "), t4(b"AMH "), t4(b"ANG "), t4(b"ARA "), t4(b"ARG "), t4(b"ARI "), t4(b"ARK "), t4(b"ARK "), t4(b"ASM "), t4(b"AST "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "),
t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "),
t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"ATH "), t4(b"AVR "), t4(b"AWA "), t4(b"AYM "), t4(b"AZB "), t4(b"AZE "), t4(b"BAD "), t4(b"BAG "), t4(b"BAL "), t4(b"BAN "), t4(b"BAR "),
t4(b"BAU "), t4(b"BBC "), t4(b"BCH "), t4(b"BDY "), t4(b"BEL "), t4(b"BEM "), t4(b"BEN "), t4(b"BGC "), t4(b"BGQ "), t4(b"BGR "), t4(b"BHI "), t4(b"BHI "), t4(b"BHO "), t4(b"BIK "), t4(b"BIK "), t4(b"BIK "), t4(b"BIK "), t4(b"BIK "), t4(b"BIK "), t4(b"BIK "), t4(b"BIK "), t4(b"BIK "), t4(b"BIL "), t4(b"BIS "), t4(b"BJJ "),
t4(b"BKF "), t4(b"BLI "), t4(b"BLK "), t4(b"BLN "), t4(b"BLN "), t4(b"BLT "), t4(b"BMB "), t4(b"BOS "), t4(b"BPY "), t4(b"BRE "), t4(b"BRH "), t4(b"BRI "), t4(b"BRM "), t4(b"BRX "), t4(b"BSH "), t4(b"BSK "), t4(b"BTI "), t4(b"BTI "), t4(b"BTI "), t4(b"BTI "), t4(b"BTI "), t4(b"BTI "), t4(b"BTS "), t4(b"BUG "), t4(b"BYV "),
t4(b"CAK "), t4(b"CAT "), t4(b"CBK "), t4(b"CCHN"), t4(b"CCHN"), t4(b"CCHN"), t4(b"CCHN"), t4(b"CCHN"), t4(b"CCHN"), t4(b"CCHN"), t4(b"CCHN"), t4(b"CCHN"), t4(b"CCHN"), t4(b"CCHN"), t4(b"CCHN"), t4(b"CCHN"), t4(b"CCHN"), t4(b"CEB "), t4(b"CGG "), t4(b"CHA "), t4(b"CHE "), t4(b"CHG "), t4(b"CHH "), t4(b"CHI "), t4(b"CHK "),
t4(b"CHK0"), t4(b"CHO "), t4(b"CHP "), t4(b"CHR "), t4(b"CHU "), t4(b"CHY "), t4(b"CJA "), t4(b"CJM "), t4(b"CMR "), t4(b"CMR "), t4(b"CMR "), t4(b"CMR "), t4(b"COP "), t4(b"COR "), t4(b"COS "), t4(b"CRE "), t4(b"CRR "), t4(b"CRR "), t4(b"CRT "), t4(b"CSB "), t4(b"CSL "), t4(b"CSY "), t4(b"CTG "), t4(b"CUK "), t4(b"DAN "),
t4(b"DAR "), t4(b"DAX "), t4(b"DCR "), t4(b"DEU "), t4(b"DGO "), t4(b"DGR "), t4(b"DHG "), t4(b"DIQ "), t4(b"DIV "), t4(b"DJR "), t4(b"DJR0"), t4(b"DNG "), t4(b"DNJ "), t4(b"DNK "), t4(b"DRI "), t4(b"DUJ "), t4(b"DUJ "), t4(b"DUN "), t4(b"DZN "), t4(b"EBI "), t4(b"ECR "), t4(b"ECR "), t4(b"EDO "), t4(b"EFI "), t4(b"ELL "),
t4(b"EMK "), t4(b"ENG "), t4(b"ERZ "), t4(b"ESP "), t4(b"ESU "), t4(b"ETI "), t4(b"EUQ "), t4(b"EVK "), t4(b"EVN "), t4(b"EWE "), t4(b"FAN "), t4(b"FAN0"), t4(b"FAR "), t4(b"FAT "), t4(b"FIN "), t4(b"FJI "), t4(b"FLE "), t4(b"FMP "), t4(b"FNE "), t4(b"FNE "), t4(b"FON "), t4(b"FOS "), t4(b"FRA "), t4(b"FRC "), t4(b"FRI "),
t4(b"FRL "), t4(b"FRP "), t4(b"FTA "), t4(b"FUL "), t4(b"FUV "), t4(b"GAD "), t4(b"GAE "), t4(b"GAG "), t4(b"GAL "), t4(b"GAW "), t4(b"GEZ "), t4(b"GIH "), t4(b"GIL "), t4(b"GIL0"), t4(b"GKP "), t4(b"GLK "), t4(b"GMZ "), t4(b"GNN "), t4(b"GOG "), t4(b"GON "), t4(b"GRN "), t4(b"GRO "), t4(b"GUA "), t4(b"GUC "), t4(b"GUF "),
t4(b"GUJ "), t4(b"GUZ "), t4(b"HAI "), t4(b"HAL "), t4(b"HAL "), t4(b"HAR "), t4(b"HAU "), t4(b"HAW "), t4(b"HAY "), t4(b"HAZ "), t4(b"HBN "), t4(b"HER "), t4(b"HIL "), t4(b"HIN "), t4(b"HMA "), t4(b"HMN "), t4(b"HMO "), t4(b"HND "), t4(b"HND "), t4(b"HRI "), t4(b"HRV "), t4(b"HUN "), t4(b"HYE "), t4(b"HYE0"), t4(b"IBA "),
t4(b"IBB "), t4(b"IBO "), t4(b"IDO "), t4(b"IJO "), t4(b"ILE "), t4(b"ILO "), t4(b"INA "), t4(b"IND "), t4(b"ING "), t4(b"INU "), t4(b"IPK "), t4(b"IRI "), t4(b"IRT "), t4(b"ISL "), t4(b"ISM "), t4(b"ITA "), t4(b"IWR "), t4(b"JAM "), t4(b"JAN "), t4(b"JAV "), t4(b"JBO "), t4(b"JCT "), t4(b"JII "), t4(b"JUD "), t4(b"JUL "),
t4(b"KAB "), t4(b"KAB0"), t4(b"KAC "), t4(b"KAL "), t4(b"KAN "), t4(b"KAR "), t4(b"KAT "), t4(b"KAZ "), t4(b"KDE "), t4(b"KEA "), t4(b"KEB "), t4(b"KEK "), t4(b"KGE "), t4(b"KHA "), t4(b"KHK "), t4(b"KHM "), t4(b"KHS "), t4(b"KHT "), t4(b"KHV "), t4(b"KHW "), t4(b"KIK "), t4(b"KIR "), t4(b"KIS "), t4(b"KIS "), t4(b"KIU "),
t4(b"KJD "), t4(b"KJP "), t4(b"KJZ "), t4(b"KKN "), t4(b"KLM "), t4(b"KMB "), t4(b"KMN "), t4(b"KMO "), t4(b"KMS "), t4(b"KMZ "), t4(b"KNR "), t4(b"KOD "), t4(b"KOH "), t4(b"KOK "), t4(b"KOM "), t4(b"KON "), t4(b"KON0"), t4(b"KOP "), t4(b"KOR "), t4(b"KOS "), t4(b"KOZ "), t4(b"KPL "), t4(b"KRI "), t4(b"KRK "), t4(b"KRL "),
t4(b"KRM "), t4(b"KRT "), t4(b"KSH "), t4(b"KSH0"), t4(b"KSI "), t4(b"KSM "), t4(b"KSW "), t4(b"KUA "), t4(b"KUI "), t4(b"KUL "), t4(b"KUM "), t4(b"KUR "), t4(b"KUU "), t4(b"KUY "), t4(b"KYK "), t4(b"KYU "), t4(b"LAD "), t4(b"LAH "), t4(b"LAK "), t4(b"LAM "), t4(b"LAO "), t4(b"LAT "), t4(b"LAZ "), t4(b"LCR "), t4(b"LDK "),
t4(b"LEZ "), t4(b"LIJ "), t4(b"LIM "), t4(b"LIN "), t4(b"LIS "), t4(b"LJP "), t4(b"LKI "), t4(b"LMA "), t4(b"LMB "), t4(b"LMO "), t4(b"LMW "), t4(b"LOM "), t4(b"LRC "), t4(b"LRC "), t4(b"LRC "), t4(b"LRC "), t4(b"LSB "), t4(b"LSM "), t4(b"LTH "), t4(b"LTZ "), t4(b"LUA "), t4(b"LUB "), t4(b"LUG "), t4(b"LUH "), t4(b"LUO "),
t4(b"LVI "), t4(b"MAD "), t4(b"MAG "), t4(b"MAH "), t4(b"MAJ "), t4(b"MAK "), t4(b"MAL "), t4(b"MAM "), t4(b"MAN "), t4(b"MAP "), t4(b"MAR "), t4(b"MAW "), t4(b"MAW "), t4(b"MAW "), t4(b"MAW "), t4(b"MAW "), t4(b"MAW "), t4(b"MAW "), t4(b"MBN "), t4(b"MBO "), t4(b"MCH "), t4(b"MCR "), t4(b"MDE "), t4(b"MDR "), t4(b"MEN "),
t4(b"MER "), t4(b"MFA "), t4(b"MFE "), t4(b"MIN "), t4(b"MIZ "), t4(b"MKD "), t4(b"MKR "), t4(b"MKW "), t4(b"MLE "), t4(b"MLG "), t4(b"MLN "), t4(b"MLR "), t4(b"MLY "), t4(b"MND "), t4(b"MNG "), t4(b"MNI "), t4(b"MNK "), t4(b"MNK "), t4(b"MNK "), t4(b"MNK "), t4(b"MNK "), t4(b"MNK "), t4(b"MNK "), t4(b"MNX "), t4(b"MOH "),
t4(b"MOK "), t4(b"MON "), t4(b"MOS "), t4(b"MRI "), t4(b"MTH "), t4(b"MTS "), t4(b"MUN "), t4(b"MUS "), t4(b"MWL "), t4(b"MWW "), t4(b"MZN "), t4(b"NAG "), t4(b"NAN "), t4(b"NAP "), t4(b"NAS "), t4(b"NAU "), t4(b"NAV "), t4(b"NCR "), t4(b"NDB "), t4(b"NDB "), t4(b"NDC "), t4(b"NDG "), t4(b"NDS "), t4(b"NEP "), t4(b"NEW "),
t4(b"NGA "), t4(b"NHC "), t4(b"NIS "), t4(b"NIS "), t4(b"NIU "), t4(b"NKL "), t4(b"NKO "), t4(b"NLD "), t4(b"NOE "), t4(b"NOG "), t4(b"NOR "), t4(b"NOV "), t4(b"NSM "), t4(b"NSO "), t4(b"NTA "), t4(b"NTO "), t4(b"NYM "), t4(b"NYN "), t4(b"NZA "), t4(b"OCI "), t4(b"OCR "), t4(b"OJB "), t4(b"ORI "), t4(b"ORO "), t4(b"OSS "),
t4(b"PAA "), t4(b"PAG "), t4(b"PAL "), t4(b"PAM "), t4(b"PAN "), t4(b"PAP "), t4(b"PAP0"), t4(b"PAS "), t4(b"PAU "), t4(b"PCC "), t4(b"PCD "), t4(b"PDC "), t4(b"PGR "), t4(b"PHK "), t4(b"PIH "), t4(b"PIL "), t4(b"PLG "), t4(b"PLG "), t4(b"PLG "), t4(b"PLK "), t4(b"PMS "), t4(b"PNB "), t4(b"POH "), t4(b"PON "), t4(b"PRO "),
t4(b"PTG "), t4(b"PWO "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QIN "), t4(b"QUC "), t4(b"QUH "), t4(b"QUZ "), t4(b"QVI "),
t4(b"QWH "), t4(b"RAJ "), t4(b"RAR "), t4(b"RBU "), t4(b"RCR "), t4(b"REJ "), t4(b"RIA "), t4(b"RIF "), t4(b"RIT "), t4(b"RKW "), t4(b"RMS "), t4(b"RMY "), t4(b"ROM "), t4(b"ROY "), t4(b"RSY "), t4(b"RTM "), t4(b"RUA "), t4(b"RUN "), t4(b"RUP "), t4(b"RUS "), t4(b"SAD "), t4(b"SAN "), t4(b"SAS "), t4(b"SAT "), t4(b"SAY "),
t4(b"SCN "), t4(b"SCO "), t4(b"SCS "), t4(b"SEK "), t4(b"SEL "), t4(b"SGA "), t4(b"SGO "), t4(b"SGS "), t4(b"SHI "), t4(b"SHN "), t4(b"SIB "), t4(b"SID "), t4(b"SIG "), t4(b"SIG "), t4(b"SKS "), t4(b"SKY "), t4(b"SLA "), t4(b"SLA "), t4(b"SLV "), t4(b"SML "), t4(b"SMO "), t4(b"SNA "), t4(b"SNA0"), t4(b"SND "), t4(b"SNH "),
t4(b"SNK "), t4(b"SOG "), t4(b"SOP "), t4(b"SOT "), t4(b"SQI "), t4(b"SRB "), t4(b"SRD "), t4(b"SRK "), t4(b"SRR "), t4(b"SSL "), t4(b"SSM "), t4(b"STQ "), t4(b"SUK "), t4(b"SUN "), t4(b"SUR "), t4(b"SVA "), t4(b"SVE "), t4(b"SWA "), t4(b"SWK "), t4(b"SWZ "), t4(b"SXT "), t4(b"SXU "), t4(b"SYL "), t4(b"SYR "), t4(b"SYR "),
t4(b"SYR "), t4(b"SYR "), t4(b"SYR "), t4(b"SYR "), t4(b"SYRE"), t4(b"SYRE"), t4(b"SYRJ"), t4(b"SYRJ"), t4(b"SYRN"), t4(b"SYRN"), t4(b"SZL "), t4(b"TAB "), t4(b"TAJ "), t4(b"TAM "), t4(b"TAT "), t4(b"TCR "), t4(b"TDD "), t4(b"TEL "), t4(b"TET "), t4(b"TGL "), t4(b"TGN "), t4(b"TGR "), t4(b"TGY "), t4(b"THA "), t4(b"THT "),
t4(b"TIB "), t4(b"TIV "), t4(b"TKM "), t4(b"TMH "), t4(b"TMN "), t4(b"TNA "), t4(b"TNE "), t4(b"TNE "), t4(b"TNG "), t4(b"TOD "), t4(b"TOD0"), t4(b"TPI "), t4(b"TRK "), t4(b"TSG "), t4(b"TSJ "), t4(b"TUA "), t4(b"TUL "), t4(b"TUM "), t4(b"TUV "), t4(b"TVL "), t4(b"TWI "), t4(b"TYZ "), t4(b"TZM "), t4(b"TZO "), t4(b"UDM "),
t4(b"UKR "), t4(b"UMB "), t4(b"URD "), t4(b"USB "), t4(b"UYG "), t4(b"UZB "), t4(b"VEC "), t4(b"VEN "), t4(b"VIT "), t4(b"VOL "), t4(b"VRO "), t4(b"WAG "), t4(b"WAR "), t4(b"WCR "), t4(b"WEL "), t4(b"WLF "), t4(b"WLN "), t4(b"WTM "), t4(b"XBD "), t4(b"XHS "), t4(b"XJB "), t4(b"XKF "), t4(b"XOG "), t4(b"XPE "), t4(b"YAK "),
t4(b"YAO "), t4(b"YAP "), t4(b"YBA "), t4(b"YCR "), t4(b"YIM "), t4(b"ZEA "), t4(b"ZGH "), t4(b"ZHA "), t4(b"ZHH "), t4(b"ZHP "), t4(b"ZHS "), t4(b"ZHT "), t4(b"ZND "), t4(b"ZUL "), t4(b"ZZA "),
];
#[rustfmt::skip]
pub const LANG_BY_TAG2: [(u16, u16); 186] = [
(t2(b"aa"), 6), (t2(b"ab"), 1), (t2(b"af"), 5), (t2(b"ak"), 9), (t2(b"ak"), 670), (t2(b"am"), 13), (t2(b"an"), 16), (t2(b"ar"), 15), (t2(b"as"), 20), (t2(b"av"), 65), (t2(b"ay"), 67), (t2(b"az"), 69), (t2(b"ba"), 114),
(t2(b"be"), 79), (t2(b"bg"), 84), (t2(b"bi"), 98), (t2(b"bm"), 106), (t2(b"bn"), 81), (t2(b"bo"), 650), (t2(b"br"), 109), (t2(b"bs"), 107), (t2(b"ca"), 126), (t2(b"ce"), 145), (t2(b"ch"), 144), (t2(b"co"), 164), (t2(b"cr"), 703),
(t2(b"cr"), 165), (t2(b"cs"), 171), (t2(b"cu"), 170), (t2(b"cv"), 154), (t2(b"cy"), 689), (t2(b"da"), 174), (t2(b"de"), 178), (t2(b"dv"), 183), (t2(b"dz"), 193), (t2(b"ee"), 209), (t2(b"el"), 199), (t2(b"el"), 512), (t2(b"en"), 201),
(t2(b"eo"), 490), (t2(b"es"), 203), (t2(b"et"), 205), (t2(b"eu"), 206), (t2(b"fa"), 212), (t2(b"ff"), 228), (t2(b"fi"), 214), (t2(b"fj"), 215), (t2(b"fo"), 221), (t2(b"fr"), 222), (t2(b"fy"), 224), (t2(b"ga"), 287), (t2(b"ga"), 286),
(t2(b"gd"), 231), (t2(b"gl"), 233), (t2(b"gn"), 247), (t2(b"gu"), 250), (t2(b"gv"), 448), (t2(b"ha"), 256), (t2(b"he"), 291), (t2(b"hi"), 263), (t2(b"ho"), 266), (t2(b"hr"), 270), (t2(b"ht"), 252), (t2(b"hu"), 271), (t2(b"hy"), 273),
(t2(b"hy"), 272), (t2(b"hz"), 261), (t2(b"ia"), 281), (t2(b"id"), 282), (t2(b"ie"), 279), (t2(b"ig"), 276), (t2(b"ii"), 704), (t2(b"ik"), 285), (t2(b"io"), 277), (t2(b"is"), 288), (t2(b"it"), 290), (t2(b"iu"), 284), (t2(b"ja"), 293),
(t2(b"jv"), 294), (t2(b"ka"), 312), (t2(b"ka"), 306), (t2(b"kg"), 341), (t2(b"ki"), 320), (t2(b"kj"), 357), (t2(b"kk"), 307), (t2(b"kl"), 245), (t2(b"km"), 315), (t2(b"kn"), 304), (t2(b"ko"), 343), (t2(b"kr"), 335), (t2(b"ks"), 352),
(t2(b"ku"), 361), (t2(b"kv"), 339), (t2(b"kw"), 163), (t2(b"ky"), 321), (t2(b"la"), 371), (t2(b"lb"), 394), (t2(b"lg"), 397), (t2(b"li"), 377), (t2(b"ln"), 378), (t2(b"lo"), 370), (t2(b"lt"), 393), (t2(b"lu"), 396), (t2(b"lv"), 400),
(t2(b"mg"), 434), (t2(b"mh"), 403), (t2(b"mi"), 453), (t2(b"mk"), 430), (t2(b"ml"), 406), (t2(b"ml"), 436), (t2(b"mn"), 439), (t2(b"mr"), 410), (t2(b"ms"), 437), (t2(b"mt"), 455), (t2(b"my"), 112), (t2(b"na"), 465), (t2(b"nb"), 485),
(t2(b"nd"), 469), (t2(b"ne"), 473), (t2(b"ng"), 471), (t2(b"nl"), 482), (t2(b"nn"), 492), (t2(b"nr"), 468), (t2(b"nv"), 50), (t2(b"nv"), 466), (t2(b"ny"), 148), (t2(b"oc"), 494), (t2(b"oj"), 496), (t2(b"om"), 498), (t2(b"or"), 497),
(t2(b"os"), 499), (t2(b"pa"), 504), (t2(b"pi"), 502), (t2(b"pl"), 519), (t2(b"ps"), 507), (t2(b"pt"), 525), (t2(b"rm"), 560), (t2(b"rn"), 567), (t2(b"ro"), 562), (t2(b"ru"), 569), (t2(b"rw"), 566), (t2(b"sa"), 571), (t2(b"sc"), 606),
(t2(b"sd"), 598), (t2(b"se"), 487), (t2(b"sg"), 581), (t2(b"si"), 599), (t2(b"sk"), 590), (t2(b"sl"), 593), (t2(b"sm"), 595), (t2(b"sn"), 597), (t2(b"so"), 594), (t2(b"sq"), 604), (t2(b"sr"), 605), (t2(b"ss"), 619), (t2(b"st"), 603),
(t2(b"su"), 613), (t2(b"sv"), 616), (t2(b"sw"), 618), (t2(b"ta"), 638), (t2(b"te"), 642), (t2(b"tg"), 637), (t2(b"th"), 648), (t2(b"ti"), 647), (t2(b"tk"), 652), (t2(b"tl"), 644), (t2(b"tn"), 655), (t2(b"to"), 645), (t2(b"tr"), 662),
(t2(b"ts"), 663), (t2(b"tt"), 639), (t2(b"ty"), 649), (t2(b"ug"), 679), (t2(b"uk"), 675), (t2(b"ur"), 677), (t2(b"uz"), 680), (t2(b"ve"), 682), (t2(b"vi"), 683), (t2(b"vo"), 684), (t2(b"wa"), 691), (t2(b"wo"), 690), (t2(b"xh"), 694),
(t2(b"yi"), 297), (t2(b"yo"), 702), (t2(b"za"), 707), (t2(b"zu"), 713),
];
#[rustfmt::skip]
pub const LANG_BY_TAG3: [(u32, u32); 711] = [
(t3(b"aar"), 6), (t3(b"abk"), 1), (t3(b"abq"), 0), (t3(b"acf"), 210), (t3(b"ach"), 2), (t3(b"acr"), 3), (t3(b"ada"), 186), (t3(b"ady"), 4), (t3(b"afr"), 5), (t3(b"ahg"), 7), (t3(b"aht"), 33), (t3(b"aii"), 617), (t3(b"aii"), 628),
(t3(b"aio"), 8), (t3(b"aiw"), 17), (t3(b"aka"), 670), (t3(b"aka"), 9), (t3(b"alt"), 12), (t3(b"amf"), 260), (t3(b"amh"), 13), (t3(b"amw"), 627), (t3(b"ang"), 14), (t3(b"apj"), 46), (t3(b"apk"), 45), (t3(b"apl"), 47), (t3(b"apm"), 48),
(t3(b"apw"), 49), (t3(b"ara"), 15), (t3(b"arg"), 16), (t3(b"arn"), 409), (t3(b"asm"), 20), (t3(b"ast"), 21), (t3(b"atj"), 554), (t3(b"atv"), 11), (t3(b"ava"), 65), (t3(b"awa"), 66), (t3(b"aym"), 67), (t3(b"azb"), 68), (t3(b"aze"), 69),
(t3(b"bak"), 114), (t3(b"bal"), 101), (t3(b"bam"), 106), (t3(b"ban"), 73), (t3(b"bar"), 74), (t3(b"bbc"), 76), (t3(b"bci"), 75), (t3(b"bcl"), 94), (t3(b"bcq"), 77), (t3(b"bcr"), 53), (t3(b"bdy"), 78), (t3(b"bea"), 51), (t3(b"beb"), 116),
(t3(b"bel"), 79), (t3(b"bem"), 80), (t3(b"ben"), 81), (t3(b"bfq"), 70), (t3(b"bft"), 105), (t3(b"bfu"), 367), (t3(b"bfy"), 71), (t3(b"bgc"), 82), (t3(b"bgq"), 83), (t3(b"bgr"), 545), (t3(b"bhb"), 86), (t3(b"bhi"), 85), (t3(b"bho"), 87),
(t3(b"bik"), 92), (t3(b"bin"), 197), (t3(b"bis"), 98), (t3(b"bjj"), 99), (t3(b"bjt"), 104), (t3(b"bla"), 100), (t3(b"ble"), 103), (t3(b"blk"), 102), (t3(b"bln"), 95), (t3(b"bod"), 650), (t3(b"bos"), 107), (t3(b"bpy"), 108), (t3(b"bqi"), 388),
(t3(b"bra"), 111), (t3(b"bre"), 109), (t3(b"brh"), 110), (t3(b"brx"), 113), (t3(b"bsk"), 115), (t3(b"bto"), 96), (t3(b"bts"), 122), (t3(b"bug"), 123), (t3(b"bul"), 84), (t3(b"bum"), 117), (t3(b"bxp"), 118), (t3(b"bxr"), 553), (t3(b"byn"), 97),
(t3(b"byv"), 124), (t3(b"caf"), 54), (t3(b"caf"), 166), (t3(b"cak"), 125), (t3(b"cat"), 126), (t3(b"cbk"), 127), (t3(b"cbl"), 529), (t3(b"cco"), 140), (t3(b"ceb"), 142), (t3(b"ces"), 171), (t3(b"cfm"), 253), (t3(b"cgg"), 143), (t3(b"cha"), 144),
(t3(b"che"), 145), (t3(b"chj"), 139), (t3(b"chk"), 150), (t3(b"cho"), 151), (t3(b"chp"), 574), (t3(b"chp"), 152), (t3(b"chp"), 58), (t3(b"chq"), 138), (t3(b"chr"), 153), (t3(b"chu"), 170), (t3(b"chv"), 154), (t3(b"chy"), 155), (t3(b"chz"), 137),
(t3(b"cja"), 156), (t3(b"cjm"), 157), (t3(b"ckt"), 149), (t3(b"clc"), 55), (t3(b"cld"), 626), (t3(b"cle"), 136), (t3(b"cmr"), 534), (t3(b"cnb"), 528), (t3(b"cnh"), 544), (t3(b"cnk"), 531), (t3(b"cnl"), 135), (t3(b"cnt"), 141), (t3(b"cnw"), 543),
(t3(b"cop"), 162), (t3(b"coq"), 25), (t3(b"cor"), 163), (t3(b"cos"), 164), (t3(b"cpa"), 134), (t3(b"cre"), 165), (t3(b"cre"), 703), (t3(b"crh"), 168), (t3(b"crj"), 195), (t3(b"crk"), 688), (t3(b"crl"), 196), (t3(b"crm"), 421), (t3(b"crm"), 373),
(t3(b"crx"), 167), (t3(b"crx"), 64), (t3(b"csa"), 132), (t3(b"csb"), 169), (t3(b"csh"), 527), (t3(b"cso"), 131), (t3(b"csw"), 476), (t3(b"csw"), 467), (t3(b"csy"), 539), (t3(b"ctc"), 26), (t3(b"ctd"), 538), (t3(b"cte"), 130), (t3(b"ctg"), 172),
(t3(b"ctl"), 129), (t3(b"cts"), 93), (t3(b"cuc"), 128), (t3(b"cuk"), 173), (t3(b"cvn"), 133), (t3(b"cwd"), 640), (t3(b"cwd"), 177), (t3(b"cym"), 689), (t3(b"czt"), 542), (t3(b"dan"), 174), (t3(b"dao"), 533), (t3(b"dar"), 175), (t3(b"dax"), 176),
(t3(b"deu"), 178), (t3(b"dgo"), 179), (t3(b"dgr"), 59), (t3(b"dhd"), 416), (t3(b"dhg"), 181), (t3(b"din"), 188), (t3(b"diq"), 182), (t3(b"div"), 183), (t3(b"dje"), 184), (t3(b"djr"), 185), (t3(b"dng"), 192), (t3(b"dnj"), 187), (t3(b"doi"), 180),
(t3(b"dsb"), 391), (t3(b"dwu"), 190), (t3(b"dwy"), 191), (t3(b"dyu"), 299), (t3(b"dzo"), 193), (t3(b"efi"), 198), (t3(b"ell"), 199), (t3(b"ell"), 512), (t3(b"emk"), 200), (t3(b"emk"), 442), (t3(b"enf"), 218), (t3(b"eng"), 201), (t3(b"enh"), 656),
(t3(b"epo"), 490), (t3(b"est"), 205), (t3(b"esu"), 204), (t3(b"eto"), 119), (t3(b"eus"), 206), (t3(b"eve"), 208), (t3(b"evn"), 207), (t3(b"ewe"), 209), (t3(b"ewo"), 120), (t3(b"fan"), 211), (t3(b"fao"), 221), (t3(b"fas"), 212), (t3(b"fat"), 213),
(t3(b"fbl"), 91), (t3(b"fij"), 215), (t3(b"fil"), 515), (t3(b"fin"), 214), (t3(b"fmp"), 217), (t3(b"fon"), 220), (t3(b"fra"), 222), (t3(b"frc"), 223), (t3(b"frp"), 226), (t3(b"fry"), 224), (t3(b"fuf"), 227), (t3(b"ful"), 228), (t3(b"fur"), 225),
(t3(b"fuv"), 229), (t3(b"gaa"), 230), (t3(b"gag"), 232), (t3(b"gbm"), 234), (t3(b"gce"), 27), (t3(b"gez"), 235), (t3(b"gih"), 236), (t3(b"gil"), 238), (t3(b"gkp"), 239), (t3(b"gla"), 231), (t3(b"gld"), 462), (t3(b"gle"), 287), (t3(b"gle"), 286),
(t3(b"glg"), 233), (t3(b"glk"), 240), (t3(b"glv"), 448), (t3(b"gnn"), 242), (t3(b"gog"), 243), (t3(b"gon"), 244), (t3(b"grn"), 247), (t3(b"grt"), 246), (t3(b"gru"), 601), (t3(b"gsw"), 10), (t3(b"guc"), 248), (t3(b"guf"), 249), (t3(b"guj"), 250),
(t3(b"guk"), 241), (t3(b"guz"), 251), (t3(b"gwi"), 56), (t3(b"haa"), 57), (t3(b"har"), 269), (t3(b"hat"), 252), (t3(b"hau"), 256), (t3(b"haw"), 257), (t3(b"hay"), 258), (t3(b"haz"), 259), (t3(b"heb"), 291), (t3(b"her"), 261), (t3(b"hil"), 262),
(t3(b"hin"), 263), (t3(b"hlt"), 532), (t3(b"hmn"), 265), (t3(b"hmo"), 266), (t3(b"hnd"), 267), (t3(b"hne"), 147), (t3(b"hno"), 268), (t3(b"hoi"), 63), (t3(b"hoj"), 255), (t3(b"hrv"), 270), (t3(b"hsb"), 678), (t3(b"hun"), 271), (t3(b"hup"), 31),
(t3(b"hye"), 273), (t3(b"hye"), 272), (t3(b"iba"), 274), (t3(b"ibb"), 275), (t3(b"ibo"), 276), (t3(b"ido"), 277), (t3(b"igb"), 194), (t3(b"iii"), 704), (t3(b"ijc"), 278), (t3(b"iku"), 284), (t3(b"ile"), 279), (t3(b"ilo"), 280), (t3(b"ina"), 281),
(t3(b"ind"), 282), (t3(b"ing"), 44), (t3(b"inh"), 283), (t3(b"ipk"), 285), (t3(b"isl"), 288), (t3(b"ita"), 290), (t3(b"jam"), 292), (t3(b"jav"), 294), (t3(b"jbo"), 295), (t3(b"jct"), 296), (t3(b"jpn"), 293), (t3(b"kaa"), 348), (t3(b"kab"), 301),
(t3(b"kal"), 245), (t3(b"kam"), 330), (t3(b"kan"), 304), (t3(b"kas"), 352), (t3(b"kat"), 312), (t3(b"kat"), 306), (t3(b"kau"), 335), (t3(b"kaz"), 307), (t3(b"kbd"), 300), (t3(b"kca"), 314), (t3(b"kca"), 318), (t3(b"kca"), 316), (t3(b"kde"), 308),
(t3(b"kdr"), 350), (t3(b"kdt"), 363), (t3(b"kea"), 309), (t3(b"kek"), 311), (t3(b"kex"), 328), (t3(b"kfa"), 336), (t3(b"kfr"), 302), (t3(b"kfx"), 359), (t3(b"kfy"), 331), (t3(b"kha"), 354), (t3(b"khb"), 693), (t3(b"khm"), 315), (t3(b"kht"), 317),
(t3(b"khw"), 319), (t3(b"kik"), 320), (t3(b"kin"), 566), (t3(b"kir"), 321), (t3(b"kiu"), 324), (t3(b"kjd"), 325), (t3(b"kjh"), 313), (t3(b"kjp"), 326), (t3(b"kjz"), 327), (t3(b"kkz"), 30), (t3(b"kln"), 303), (t3(b"kmb"), 418), (t3(b"kmw"), 332),
(t3(b"kmz"), 334), (t3(b"koi"), 342), (t3(b"kok"), 338), (t3(b"kom"), 339), (t3(b"kon"), 341), (t3(b"kor"), 343), (t3(b"kos"), 344), (t3(b"koy"), 42), (t3(b"kpe"), 346), (t3(b"kpv"), 345), (t3(b"kpy"), 364), (t3(b"kqs"), 323), (t3(b"kqy"), 351),
(t3(b"krc"), 305), (t3(b"krc"), 72), (t3(b"kri"), 347), (t3(b"krl"), 349), (t3(b"kru"), 362), (t3(b"ksh"), 353), (t3(b"kss"), 322), (t3(b"ksw"), 356), (t3(b"ktb"), 310), (t3(b"ktu"), 340), (t3(b"ktw"), 43), (t3(b"kua"), 357), (t3(b"kum"), 360),
(t3(b"kur"), 361), (t3(b"kuu"), 38), (t3(b"kxc"), 333), (t3(b"kxu"), 358), (t3(b"kyu"), 365), (t3(b"lad"), 298), (t3(b"lao"), 370), (t3(b"lat"), 371), (t3(b"lav"), 400), (t3(b"lbe"), 368), (t3(b"lbj"), 374), (t3(b"lbl"), 90), (t3(b"lez"), 375),
(t3(b"lif"), 383), (t3(b"lij"), 376), (t3(b"lim"), 377), (t3(b"lin"), 378), (t3(b"lis"), 379), (t3(b"lit"), 393), (t3(b"ljp"), 380), (t3(b"lki"), 381), (t3(b"lld"), 366), (t3(b"lmn"), 369), (t3(b"lmo"), 384), (t3(b"lom"), 386), (t3(b"lrc"), 389),
(t3(b"ltz"), 394), (t3(b"lua"), 395), (t3(b"lub"), 396), (t3(b"lug"), 397), (t3(b"luo"), 399), (t3(b"lus"), 429), (t3(b"luy"), 398), (t3(b"luz"), 390), (t3(b"lzz"), 372), (t3(b"mad"), 401), (t3(b"mag"), 402), (t3(b"mah"), 403), (t3(b"mai"), 454),
(t3(b"mak"), 431), (t3(b"mal"), 436), (t3(b"mal"), 406), (t3(b"mam"), 407), (t3(b"man"), 447), (t3(b"mar"), 410), (t3(b"mbo"), 419), (t3(b"mct"), 121), (t3(b"mdf"), 450), (t3(b"mdr"), 423), (t3(b"mdy"), 433), (t3(b"men"), 422), (t3(b"mer"), 425),
(t3(b"mfa"), 426), (t3(b"mfe"), 427), (t3(b"mhr"), 382), (t3(b"min"), 428), (t3(b"mkd"), 430), (t3(b"mku"), 446), (t3(b"mkw"), 432), (t3(b"mlg"), 434), (t3(b"mlq"), 441), (t3(b"mlq"), 435), (t3(b"mlt"), 455), (t3(b"mnc"), 420), (t3(b"mni"), 440),
(t3(b"mnk"), 445), (t3(b"mnk"), 438), (t3(b"mns"), 408), (t3(b"mnw"), 451), (t3(b"moh"), 449), (t3(b"mon"), 439), (t3(b"mos"), 452), (t3(b"mpe"), 404), (t3(b"mrh"), 530), (t3(b"mri"), 453), (t3(b"mrj"), 264), (t3(b"msa"), 437), (t3(b"msc"), 443),
(t3(b"mtr"), 412), (t3(b"mus"), 457), (t3(b"mvb"), 23), (t3(b"mve"), 417), (t3(b"mwk"), 444), (t3(b"mwl"), 458), (t3(b"mwr"), 414), (t3(b"mww"), 459), (t3(b"mya"), 112), (t3(b"mym"), 424), (t3(b"myv"), 202), (t3(b"mzn"), 460), (t3(b"nag"), 461),
(t3(b"nap"), 463), (t3(b"nau"), 465), (t3(b"nav"), 466), (t3(b"nav"), 50), (t3(b"nbl"), 468), (t3(b"ndc"), 470), (t3(b"nde"), 469), (t3(b"ndo"), 471), (t3(b"nds"), 472), (t3(b"nep"), 473), (t3(b"new"), 474), (t3(b"nga"), 475), (t3(b"ngl"), 385),
(t3(b"ngo"), 620), (t3(b"niu"), 479), (t3(b"niv"), 237), (t3(b"njz"), 478), (t3(b"nld"), 482), (t3(b"nno"), 492), (t3(b"nob"), 485), (t3(b"nod"), 489), (t3(b"noe"), 483), (t3(b"nog"), 484), (t3(b"nov"), 486), (t3(b"nqo"), 481), (t3(b"nsk"), 464),
(t3(b"nso"), 488), (t3(b"nya"), 148), (t3(b"nym"), 491), (t3(b"nyn"), 480), (t3(b"nza"), 493), (t3(b"oci"), 494), (t3(b"oji"), 496), (t3(b"ojs"), 495), (t3(b"okm"), 337), (t3(b"ori"), 497), (t3(b"orm"), 498), (t3(b"oss"), 499), (t3(b"pag"), 501),
(t3(b"pam"), 503), (t3(b"pan"), 504), (t3(b"pap"), 506), (t3(b"pau"), 508), (t3(b"pcc"), 509), (t3(b"pcd"), 510), (t3(b"pce"), 516), (t3(b"pck"), 537), (t3(b"pdc"), 511), (t3(b"phk"), 513), (t3(b"pih"), 514), (t3(b"pli"), 502), (t3(b"pll"), 518),
(t3(b"plp"), 505), (t3(b"pms"), 520), (t3(b"pnb"), 521), (t3(b"poh"), 522), (t3(b"pol"), 519), (t3(b"pon"), 523), (t3(b"por"), 525), (t3(b"pro"), 524), (t3(b"prs"), 189), (t3(b"pus"), 507), (t3(b"pwo"), 526), (t3(b"quc"), 546), (t3(b"quh"), 547),
(t3(b"quz"), 548), (t3(b"qvi"), 549), (t3(b"qwh"), 550), (t3(b"raj"), 551), (t3(b"rar"), 552), (t3(b"rbb"), 517), (t3(b"rbl"), 89), (t3(b"rej"), 555), (t3(b"ria"), 556), (t3(b"rif"), 557), (t3(b"rit"), 558), (t3(b"rki"), 19), (t3(b"rkw"), 559),
(t3(b"rmy"), 561), (t3(b"rmz"), 18), (t3(b"rnl"), 254), (t3(b"roh"), 560), (t3(b"rom"), 563), (t3(b"ron"), 562), (t3(b"rtm"), 565), (t3(b"rue"), 564), (t3(b"run"), 567), (t3(b"rup"), 568), (t3(b"rus"), 569), (t3(b"rwr"), 415), (t3(b"sag"), 581),
(t3(b"sah"), 699), (t3(b"sam"), 500), (t3(b"san"), 571), (t3(b"sas"), 572), (t3(b"sat"), 573), (t3(b"sck"), 570), (t3(b"scn"), 575), (t3(b"sco"), 576), (t3(b"scs"), 60), (t3(b"scs"), 577), (t3(b"scs"), 591), (t3(b"seh"), 596), (t3(b"sek"), 52),
(t3(b"sel"), 579), (t3(b"sez"), 541), (t3(b"sga"), 580), (t3(b"sgs"), 582), (t3(b"sgw"), 146), (t3(b"shi"), 583), (t3(b"shn"), 584), (t3(b"sid"), 586), (t3(b"sin"), 599), (t3(b"sjd"), 355), (t3(b"sjo"), 585), (t3(b"skr"), 607), (t3(b"slk"), 590),
(t3(b"slv"), 593), (t3(b"sma"), 610), (t3(b"sme"), 487), (t3(b"smj"), 392), (t3(b"smn"), 289), (t3(b"smo"), 595), (t3(b"sms"), 589), (t3(b"sna"), 597), (t3(b"snd"), 598), (t3(b"snk"), 600), (t3(b"som"), 594), (t3(b"sop"), 602), (t3(b"sot"), 603),
(t3(b"spa"), 203), (t3(b"sqi"), 604), (t3(b"srd"), 606), (t3(b"srp"), 605), (t3(b"srr"), 608), (t3(b"srs"), 62), (t3(b"ssw"), 619), (t3(b"stq"), 611), (t3(b"stv"), 587), (t3(b"suk"), 612), (t3(b"sun"), 613), (t3(b"suq"), 614), (t3(b"sva"), 615),
(t3(b"swa"), 618), (t3(b"swb"), 158), (t3(b"swe"), 616), (t3(b"swv"), 411), (t3(b"sxu"), 621), (t3(b"syc"), 631), (t3(b"syc"), 629), (t3(b"syc"), 625), (t3(b"syc"), 633), (t3(b"syl"), 622), (t3(b"syr"), 632), (t3(b"syr"), 634), (t3(b"syr"), 624),
(t3(b"syr"), 630), (t3(b"szl"), 635), (t3(b"taa"), 35), (t3(b"tab"), 636), (t3(b"tah"), 649), (t3(b"tam"), 638), (t3(b"tat"), 639), (t3(b"tau"), 36), (t3(b"tcb"), 37), (t3(b"tce"), 39), (t3(b"tcp"), 540), (t3(b"tcy"), 666), (t3(b"tcz"), 536),
(t3(b"tdd"), 641), (t3(b"tel"), 642), (t3(b"tem"), 654), (t3(b"tet"), 643), (t3(b"tfn"), 34), (t3(b"tgj"), 477), (t3(b"tgk"), 637), (t3(b"tgl"), 644), (t3(b"tgx"), 32), (t3(b"tha"), 648), (t3(b"tht"), 41), (t3(b"tig"), 646), (t3(b"tir"), 647),
(t3(b"tiv"), 651), (t3(b"tmh"), 653), (t3(b"tod"), 660), (t3(b"toi"), 658), (t3(b"tol"), 28), (t3(b"ton"), 645), (t3(b"tpi"), 661), (t3(b"tru"), 623), (t3(b"tru"), 665), (t3(b"tsj"), 664), (t3(b"tsn"), 655), (t3(b"tso"), 663), (t3(b"ttm"), 40),
(t3(b"tuk"), 652), (t3(b"tum"), 667), (t3(b"tur"), 662), (t3(b"tuu"), 29), (t3(b"tvl"), 669), (t3(b"txc"), 22), (t3(b"tyv"), 668), (t3(b"tyz"), 671), (t3(b"tzm"), 672), (t3(b"tzo"), 673), (t3(b"ubl"), 88), (t3(b"udm"), 674), (t3(b"uig"), 679),
(t3(b"ukr"), 675), (t3(b"umb"), 676), (t3(b"unr"), 456), (t3(b"urd"), 677), (t3(b"uzb"), 680), (t3(b"vec"), 681), (t3(b"ven"), 682), (t3(b"vie"), 683), (t3(b"vls"), 216), (t3(b"vmw"), 405), (t3(b"vol"), 684), (t3(b"vro"), 685), (t3(b"war"), 687),
(t3(b"wbr"), 686), (t3(b"wlc"), 159), (t3(b"wle"), 588), (t3(b"wlk"), 24), (t3(b"wln"), 691), (t3(b"wni"), 160), (t3(b"wol"), 690), (t3(b"wry"), 413), (t3(b"wtm"), 692), (t3(b"xal"), 329), (t3(b"xal"), 659), (t3(b"xan"), 578), (t3(b"xho"), 694),
(t3(b"xjb"), 695), (t3(b"xkf"), 696), (t3(b"xog"), 697), (t3(b"xpe"), 698), (t3(b"xsl"), 592), (t3(b"xsl"), 609), (t3(b"xsl"), 61), (t3(b"yao"), 700), (t3(b"yap"), 701), (t3(b"yid"), 297), (t3(b"yor"), 702), (t3(b"yrk"), 657), (t3(b"yrk"), 219),
(t3(b"zdj"), 161), (t3(b"zea"), 705), (t3(b"zgh"), 706), (t3(b"zha"), 707), (t3(b"zne"), 712), (t3(b"zom"), 535), (t3(b"zul"), 713), (t3(b"zum"), 387), (t3(b"zza"), 714),
];
#[rustfmt::skip]
pub const LANG_ENTRIES: [(&'static str, &'static str); 715] = [
("Abaza", "abq"), ("Abkhazian", "ab"), ("Acoli", "ach"), ("Achi", "acr"), ("Adyghe", "ady"), ("Afrikaans", "af"), ("Afar", "aa"), ("Qimant", "ahg"), ("Aiton", "aio"), ("Akan", "ak"), ("Swiss German", "gsw"), ("Northern Altai", "atv"), ("Southern Altai", "alt"), ("Amharic", "am"), ("Old English (ca. 450-1100)", "ang"), ("Arabic", "ar"), ("Aragonese", "an"),
("Aari", "aiw"), ("Marma", "rmz"), ("Rakhine", "rki"), ("Assamese", "as"), ("Asturian", "ast"), ("Tsetsaut", "txc"), ("Mattole", "mvb"), ("Wailaki", "wlk"), ("Coquille", "coq"), ("Chetco", "ctc"), ("Galice", "gce"), ("Tolowa", "tol"), ("Tututni", "tuu"), ("Kaska", "kkz"), ("Hupa", "hup"), ("Tagish", "tgx"), ("Ahtena", "aht"),
("Tanaina", "tfn"), ("Lower Tanana", "taa"), ("Upper Tanana", "tau"), ("Tanacross", "tcb"), ("Upper Kuskokwim", "kuu"), ("Southern Tutchone", "tce"), ("Northern Tutchone", "ttm"), ("Tahltan", "tht"), ("Koyukon", "koy"), ("Kato", "ktw"), ("Degexit'an", "ing"), ("Kiowa Apache", "apk"), ("Jicarilla Apache", "apj"), ("Lipan Apache", "apl"), ("Mescalero-Chiricahua Apache", "apm"), ("Western Apache", "apw"), ("Navajo", "nv"),
("Beaver", "bea"), ("Sekani", "sek"), ("Babine", "bcr"), ("Southern Carrier", "caf"), ("Chilcotin", "clc"), ("Gwichʼin", "gwi"), ("Han", "haa"), ("Chipewyan", "chp"), ("Dogrib", "dgr"), ("North Slavey", "scs"), ("South Slavey", "xsl"), ("Sarsi", "srs"), ("Holikachuk", "hoi"), ("Carrier", "crx"), ("Avaric", "av"), ("Awadhi", "awa"), ("Aymara", "ay"),
("South Azerbaijani", "azb"), ("Azerbaijani", "az"), ("Badaga", "bfq"), ("Bagheli", "bfy"), ("Karachay-Balkar", "krc"), ("Balinese", "ban"), ("Bavarian", "bar"), ("Baoulé", "bci"), ("Batak Toba", "bbc"), ("Bench", "bcq"), ("Bandjalang", "bdy"), ("Belarusian", "be"), ("Bemba (Zambia)", "bem"), ("Bengali", "bn"), ("Haryanvi", "bgc"), ("Bagri", "bgq"), ("Bulgarian", "bg"),
("Bhilali", "bhi"), ("Bhili", "bhb"), ("Bhojpuri", "bho"), ("Buhi'non Bikol", "ubl"), ("Miraya Bikol", "rbl"), ("Libon Bikol", "lbl"), ("West Albay Bikol", "fbl"), ("Bikol", "bik"), ("Northern Catanduanes Bikol", "cts"), ("Central Bikol", "bcl"), ("Southern Catanduanes Bikol", "bln"), ("Rinconada Bikol", "bto"), ("Bilin", "byn"), ("Bislama", "bi"), ("Kanauji", "bjj"), ("Siksika", "bla"), ("Baluchi", "bal"),
("Pa'o Karen", "blk"), ("Balanta-Kentohe", "ble"), ("Balanta-Ganja", "bjt"), ("Balti", "bft"), ("Bambara", "bm"), ("Bosnian", "bs"), ("Bishnupriya", "bpy"), ("Breton", "br"), ("Brahui", "brh"), ("Braj", "bra"), ("Burmese", "my"), ("Bodo (India)", "brx"), ("Bashkir", "ba"), ("Burushaski", "bsk"), ("Bebele", "beb"), ("Bulu (Cameroon)", "bum"), ("Bebil", "bxp"),
("Eton (Cameroon)", "eto"), ("Ewondo", "ewo"), ("Mengisa", "mct"), ("Batak Simalungun", "bts"), ("Buginese", "bug"), ("Medumba", "byv"), ("Kaqchikel", "cak"), ("Catalan", "ca"), ("Chavacano", "cbk"), ("Usila Chinantec", "cuc"), ("Tlacoatzintepec Chinantec", "ctl"), ("Tepinapa Chinantec", "cte"), ("Sochiapam Chinantec", "cso"), ("Chiltepec Chinantec", "csa"), ("Valle Nacional Chinantec", "cvn"), ("Palantla Chinantec", "cpa"), ("Lalana Chinantec", "cnl"),
("Lealao Chinantec", "cle"), ("Ozumacín Chinantec", "chz"), ("Quiotepec Chinantec", "chq"), ("Ojitlán Chinantec", "chj"), ("Comaltepec Chinantec", "cco"), ("Tepetotutla Chinantec", "cnt"), ("Cebuano", "ceb"), ("Chiga", "cgg"), ("Chamorro", "ch"), ("Chechen", "ce"), ("Sebat Bet Gurage", "sgw"), ("Chhattisgarhi", "hne"), ("Nyanja", "ny"), ("Chukot", "ckt"), ("Chuukese", "chk"), ("Choctaw", "cho"), ("Chipewyan", "chp"),
("Cherokee", "chr"), ("Chuvash", "cv"), ("Cheyenne", "chy"), ("Western Cham", "cja"), ("Eastern Cham", "cjm"), ("Maore Comorian", "swb"), ("Mwali Comorian", "wlc"), ("Ndzwani Comorian", "wni"), ("Ngazidja Comorian", "zdj"), ("Coptic", "cop"), ("Cornish", "kw"), ("Corsican", "co"), ("Cree", "cr"), ("Southern Carrier", "caf"), ("Carrier", "crx"), ("Crimean Tatar", "crh"), ("Kashubian", "csb"),
("Church Slavic", "cu"), ("Czech", "cs"), ("Chittagonian", "ctg"), ("San Blas Kuna", "cuk"), ("Danish", "da"), ("Dargwa", "dar"), ("Dayi", "dax"), ("Woods Cree", "cwd"), ("German", "de"), ("Dogri (individual language)", "dgo"), ("Dogri (macrolanguage)", "doi"), ("Dhangu-Djangu", "dhg"), ("Dimli (individual language)", "diq"), ("Dhivehi", "dv"), ("Zarma", "dje"), ("Djambarrpuyngu", "djr"), ("Adangme", "ada"),
("Dan", "dnj"), ("Dinka", "din"), ("Dari", "prs"), ("Dhuwal", "dwu"), ("Dhuwaya", "dwy"), ("Dungan", "dng"), ("Dzongkha", "dz"), ("Ebira", "igb"), ("Southern East Cree", "crj"), ("Northern East Cree", "crl"), ("Bini", "bin"), ("Efik", "efi"), ("Modern Greek (1453-)", "el"), ("Eastern Maninkakan", "emk"), ("English", "en"), ("Erzya", "myv"), ("Spanish", "es"),
("Central Yupik", "esu"), ("Estonian", "et"), ("Basque", "eu"), ("Evenki", "evn"), ("Even", "eve"), ("Ewe", "ee"), ("Saint Lucian Creole French", "acf"), ("Fang (Equatorial Guinea)", "fan"), ("Persian", "fa"), ("Fanti", "fat"), ("Finnish", "fi"), ("Fijian", "fj"), ("Vlaams", "vls"), ("Fe'fe'", "fmp"), ("Forest Enets", "enf"), ("Nenets", "yrk"), ("Fon", "fon"),
("Faroese", "fo"), ("French", "fr"), ("Cajun French", "frc"), ("Western Frisian", "fy"), ("Friulian", "fur"), ("Arpitan", "frp"), ("Pular", "fuf"), ("Fulah", "ff"), ("Nigerian Fulfulde", "fuv"), ("Ga", "gaa"), ("Scottish Gaelic", "gd"), ("Gagauz", "gag"), ("Galician", "gl"), ("Garhwali", "gbm"), ("Geez", "gez"), ("Githabul", "gih"), ("Gilyak", "niv"),
("Gilbertese", "gil"), ("Guinea Kpelle", "gkp"), ("Gilaki", "glk"), ("Gumuz", "guk"), ("Gumatj", "gnn"), ("Gogo", "gog"), ("Gondi", "gon"), ("Kalaallisut", "kl"), ("Garo", "grt"), ("Guarani", "gn"), ("Wayuu", "guc"), ("Gupapuyngu", "guf"), ("Gujarati", "gu"), ("Gusii", "guz"), ("Haitian", "ht"), ("Falam Chin", "cfm"), ("Ranglong", "rnl"),
("Hadothi", "hoj"), ("Hausa", "ha"), ("Hawaiian", "haw"), ("Haya", "hay"), ("Hazaragi", "haz"), ("Hamer-Banna", "amf"), ("Herero", "hz"), ("Hiligaynon", "hil"), ("Hindi", "hi"), ("Western Mari", "mrj"), ("Hmong", "hmn"), ("Hiri Motu", "ho"), ("Southern Hindko", "hnd"), ("Northern Hindko", "hno"), ("Harari", "har"), ("Croatian", "hr"), ("Hungarian", "hu"),
("Armenian", "hy"), ("Armenian", "hy"), ("Iban", "iba"), ("Ibibio", "ibb"), ("Igbo", "ig"), ("Ido", "io"), ("Izon", "ijc"), ("Interlingue", "ie"), ("Iloko", "ilo"), ("Interlingua (International Auxiliary Language Association)", "ia"), ("Indonesian", "id"), ("Ingush", "inh"), ("Inuktitut", "iu"), ("Inupiaq", "ik"), ("Irish", "ga"), ("Irish", "ga"), ("Icelandic", "is"),
("Inari Sami", "smn"), ("Italian", "it"), ("Hebrew", "he"), ("Jamaican Creole English", "jam"), ("Japanese", "ja"), ("Javanese", "jv"), ("Lojban", "jbo"), ("Krymchak", "jct"), ("Yiddish", "yi"), ("Ladino", "lad"), ("Dyula", "dyu"), ("Kabardian", "kbd"), ("Kabyle", "kab"), ("Kachhi", "kfr"), ("Kalenjin", "kln"), ("Kannada", "kn"), ("Karachay-Balkar", "krc"),
("Georgian", "ka"), ("Kazakh", "kk"), ("Makonde", "kde"), ("Kabuverdianu", "kea"), ("Kambaata", "ktb"), ("Kekchí", "kek"), ("Georgian", "ka"), ("Khakas", "kjh"), ("Khanty", "kca"), ("Central Khmer", "km"), ("Khanty", "kca"), ("Khamti", "kht"), ("Khanty", "kca"), ("Khowar", "khw"), ("Kikuyu", "ki"), ("Kirghiz", "ky"), ("Southern Kisi", "kss"),
("Northern Kissi", "kqs"), ("Kirmanjki (individual language)", "kiu"), ("Southern Kiwai", "kjd"), ("Pwo Eastern Karen", "kjp"), ("Bumthangkha", "kjz"), ("Kukna", "kex"), ("Kalmyk", "xal"), ("Kamba (Kenya)", "kam"), ("Kumaoni", "kfy"), ("Komo (Democratic Republic of Congo)", "kmw"), ("Konso", "kxc"), ("Khorasani Turkish", "kmz"), ("Kanuri", "kr"), ("Kodava", "kfa"), ("Middle Korean (10th-16th cent.)", "okm"), ("Konkani (macrolanguage)", "kok"), ("Komi", "kv"),
("Kituba (Democratic Republic of Congo)", "ktu"), ("Kongo", "kg"), ("Komi-Permyak", "koi"), ("Korean", "ko"), ("Kosraean", "kos"), ("Komi-Zyrian", "kpv"), ("Kpelle", "kpe"), ("Krio", "kri"), ("Kara-Kalpak", "kaa"), ("Karelian", "krl"), ("Karaim", "kdr"), ("Koorete", "kqy"), ("Kashmiri", "ks"), ("Kölsch", "ksh"), ("Khasi", "kha"), ("Kildin Sami", "sjd"), ("S'gaw Karen", "ksw"),
("Kuanyama", "kj"), ("Kui (India)", "kxu"), ("Kullu Pahari", "kfx"), ("Kumyk", "kum"), ("Kurdish", "ku"), ("Kurukh", "kru"), ("Kuy", "kdt"), ("Koryak", "kpy"), ("Western Kayah", "kyu"), ("Ladin", "lld"), ("Gahri", "bfu"), ("Lak", "lbe"), ("Lambadi", "lmn"), ("Lao", "lo"), ("Latin", "la"), ("Laz", "lzz"), ("Moose Cree", "crm"),
("Ladakhi", "lbj"), ("Lezghian", "lez"), ("Ligurian", "lij"), ("Limburgan", "li"), ("Lingala", "ln"), ("Lisu", "lis"), ("Lampung Api", "ljp"), ("Laki", "lki"), ("Eastern Mari", "mhr"), ("Limbu", "lif"), ("Lombard", "lmo"), ("Lomwe", "ngl"), ("Loma (Liberia)", "lom"), ("Kumzari", "zum"), ("Bakhtiari", "bqi"), ("Northern Luri", "lrc"), ("Southern Luri", "luz"),
("Lower Sorbian", "dsb"), ("Lule Sami", "smj"), ("Lithuanian", "lt"), ("Luxembourgish", "lb"), ("Luba-Lulua", "lua"), ("Luba-Katanga", "lu"), ("Ganda", "lg"), ("Luyia", "luy"), ("Luo (Kenya and Tanzania)", "luo"), ("Latvian", "lv"), ("Madurese", "mad"), ("Magahi", "mag"), ("Marshallese", "mh"), ("Majang", "mpe"), ("Makhuwa", "vmw"), ("Malayalam", "ml"), ("Mam", "mam"),
("Mansi", "mns"), ("Mapudungun", "arn"), ("Marathi", "mr"), ("Shekhawati", "swv"), ("Mewari", "mtr"), ("Merwari", "wry"), ("Marwari", "mwr"), ("Marwari (India)", "rwr"), ("Dhundari", "dhd"), ("Marwari (Pakistan)", "mve"), ("Kimbundu", "kmb"), ("Mbo (Cameroon)", "mbo"), ("Manchu", "mnc"), ("Moose Cree", "crm"), ("Mende (Sierra Leone)", "men"), ("Mandar", "mdr"), ("Me'en", "mym"),
("Meru", "mer"), ("Pattani Malay", "mfa"), ("Morisyen", "mfe"), ("Minangkabau", "min"), ("Lushai", "lus"), ("Macedonian", "mk"), ("Makasar", "mak"), ("Kituba (Congo)", "mkw"), ("Male (Ethiopia)", "mdy"), ("Malagasy", "mg"), ("Western Maninkakan", "mlq"), ("Malayalam", "ml"), ("Malay (macrolanguage)", "ms"), ("Mandinka", "mnk"), ("Mongolian", "mn"), ("Manipuri", "mni"), ("Western Maninkakan", "mlq"),
("Eastern Maninkakan", "emk"), ("Sankaran Maninka", "msc"), ("Kita Maninkakan", "mwk"), ("Mandinka", "mnk"), ("Konyanka Maninka", "mku"), ("Mandingo", "man"), ("Manx", "gv"), ("Mohawk", "moh"), ("Moksha", "mdf"), ("Mon", "mnw"), ("Mossi", "mos"), ("Maori", "mi"), ("Maithili", "mai"), ("Maltese", "mt"), ("Mundari", "unr"), ("Creek", "mus"), ("Mirandese", "mwl"),
("Hmong Daw", "mww"), ("Mazanderani", "mzn"), ("Naga Pidgin", "nag"), ("Nanai", "gld"), ("Neapolitan", "nap"), ("Naskapi", "nsk"), ("Nauru", "na"), ("Navajo", "nv"), ("Swampy Cree", "csw"), ("South Ndebele", "nr"), ("North Ndebele", "nd"), ("Ndau", "ndc"), ("Ndonga", "ng"), ("Low German", "nds"), ("Nepali (macrolanguage)", "ne"), ("Newari", "new"), ("Ngbaka", "nga"),
("Swampy Cree", "csw"), ("Tagin", "tgj"), ("Nyishi", "njz"), ("Niuean", "niu"), ("Nyankole", "nyn"), ("N'Ko", "nqo"), ("Dutch", "nl"), ("Nimadi", "noe"), ("Nogai", "nog"), ("Norwegian Bokmål", "nb"), ("Novial", "nov"), ("Northern Sami", "se"), ("Pedi", "nso"), ("Northern Thai", "nod"), ("Esperanto", "eo"), ("Nyamwezi", "nym"), ("Norwegian Nynorsk", "nn"),
("Tigon Mbembe", "nza"), ("Occitan (post 1500)", "oc"), ("Severn Ojibwa", "ojs"), ("Ojibwa", "oj"), ("Oriya (macrolanguage)", "or"), ("Oromo", "om"), ("Ossetian", "os"), ("Samaritan Aramaic", "sam"), ("Pangasinan", "pag"), ("Pali", "pi"), ("Pampanga", "pam"), ("Panjabi", "pa"), ("Palpa", "plp"), ("Papiamento", "pap"), ("Pushto", "ps"), ("Palauan", "pau"), ("Bouyei", "pcc"),
("Picard", "pcd"), ("Pennsylvania German", "pdc"), ("Modern Greek (1453-)", "el"), ("Phake", "phk"), ("Pitcairn-Norfolk", "pih"), ("Filipino", "fil"), ("Ruching Palaung", "pce"), ("Rumai Palaung", "rbb"), ("Shwe Palaung", "pll"), ("Polish", "pl"), ("Piemontese", "pms"), ("Western Panjabi", "pnb"), ("Poqomchi'", "poh"), ("Pohnpeian", "pon"), ("Old Provençal (to 1500)", "pro"), ("Portuguese", "pt"), ("Pwo Western Karen", "pwo"),
("Asho Chin", "csh"), ("Chinbon Chin", "cnb"), ("Bualkhaw Chin", "cbl"), ("Mara Chin", "mrh"), ("Khumi Chin", "cnk"), ("Matu Chin", "hlt"), ("Daai Chin", "dao"), ("Mro-Khimi Chin", "cmr"), ("Zou", "zom"), ("Thado Chin", "tcz"), ("Paite Chin", "pck"), ("Tedim Chin", "ctd"), ("Siyin Chin", "csy"), ("Tawr Chin", "tcp"), ("Senthang Chin", "sez"), ("Zotung Chin", "czt"), ("Ngawn Chin", "cnw"),
("Hakha Chin", "cnh"), ("Bawm Chin", "bgr"), ("K'iche'", "quc"), ("South Bolivian Quechua", "quh"), ("Cusco Quechua", "quz"), ("Imbabura Highland Quichua", "qvi"), ("Huaylas Ancash Quechua", "qwh"), ("Rajasthani", "raj"), ("Rarotongan", "rar"), ("Russia Buriat", "bxr"), ("Atikamekw", "atj"), ("Rejang", "rej"), ("Riang (India)", "ria"), ("Tarifit", "rif"), ("Ritarungo", "rit"), ("Arakwal", "rkw"), ("Romansh", "rm"),
("Vlax Romani", "rmy"), ("Romanian", "ro"), ("Romany", "rom"), ("Rusyn", "rue"), ("Rotuman", "rtm"), ("Kinyarwanda", "rw"), ("Rundi", "rn"), ("Macedo-Romanian", "rup"), ("Russian", "ru"), ("Sadri", "sck"), ("Sanskrit", "sa"), ("Sasak", "sas"), ("Santali", "sat"), ("Chipewyan", "chp"), ("Sicilian", "scn"), ("Scots", "sco"), ("North Slavey", "scs"),
("Xamtanga", "xan"), ("Selkup", "sel"), ("Old Irish (to 900)", "sga"), ("Sango", "sg"), ("Samogitian", "sgs"), ("Tachelhit", "shi"), ("Shan", "shn"), ("Xibe", "sjo"), ("Sidamo", "sid"), ("Silt'e", "stv"), ("Wolane", "wle"), ("Skolt Sami", "sms"), ("Slovak", "sk"), ("North Slavey", "scs"), ("South Slavey", "xsl"), ("Slovenian", "sl"), ("Somali", "so"),
("Samoan", "sm"), ("Sena", "seh"), ("Shona", "sn"), ("Sindhi", "sd"), ("Sinhala", "si"), ("Soninke", "snk"), ("Kistane", "gru"), ("Songe", "sop"), ("Southern Sotho", "st"), ("Albanian", "sq"), ("Serbian", "sr"), ("Sardinian", "sc"), ("Saraiki", "skr"), ("Serer", "srr"), ("South Slavey", "xsl"), ("Southern Sami", "sma"), ("Saterfriesisch", "stq"),
("Sukuma", "suk"), ("Sundanese", "su"), ("Suri", "suq"), ("Svan", "sva"), ("Swedish", "sv"), ("Assyrian Neo-Aramaic", "aii"), ("Swahili (macrolanguage)", "sw"), ("Swati", "ss"), ("Ngoni", "ngo"), ("Upper Saxon", "sxu"), ("Sylheti", "syl"), ("Turoyo", "tru"), ("Syriac", "syr"), ("Classical Syriac", "syc"), ("Chaldean Neo-Aramaic", "cld"), ("Western Neo-Aramaic", "amw"), ("Assyrian Neo-Aramaic", "aii"),
("Classical Syriac", "syc"), ("Syriac", "syr"), ("Classical Syriac", "syc"), ("Syriac", "syr"), ("Classical Syriac", "syc"), ("Syriac", "syr"), ("Silesian", "szl"), ("Tabassaran", "tab"), ("Tajik", "tg"), ("Tamil", "ta"), ("Tatar", "tt"), ("Woods Cree", "cwd"), ("Tai Nüa", "tdd"), ("Telugu", "te"), ("Tetum", "tet"), ("Tagalog", "tl"), ("Tonga (Tonga Islands)", "to"),
("Tigre", "tig"), ("Tigrinya", "ti"), ("Thai", "th"), ("Tahitian", "ty"), ("Tibetan", "bo"), ("Tiv", "tiv"), ("Turkmen", "tk"), ("Tamashek", "tmh"), ("Timne", "tem"), ("Tswana", "tn"), ("Tundra Enets", "enh"), ("Nenets", "yrk"), ("Tonga (Zambia)", "toi"), ("Kalmyk", "xal"), ("Toma", "tod"), ("Tok Pisin", "tpi"), ("Turkish", "tr"),
("Tsonga", "ts"), ("Tshangla", "tsj"), ("Turoyo", "tru"), ("Tulu", "tcy"), ("Tumbuka", "tum"), ("Tuvinian", "tyv"), ("Tuvalu", "tvl"), ("Akan", "ak"), ("Tày", "tyz"), ("Central Atlas Tamazight", "tzm"), ("Tzotzil", "tzo"), ("Udmurt", "udm"), ("Ukrainian", "uk"), ("Umbundu", "umb"), ("Urdu", "ur"), ("Upper Sorbian", "hsb"), ("Uighur", "ug"),
("Uzbek", "uz"), ("Venetian", "vec"), ("Venda", "ve"), ("Vietnamese", "vi"), ("Volapük", "vo"), ("Võro", "vro"), ("Wagdi", "wbr"), ("Waray (Philippines)", "war"), ("Plains Cree", "crk"), ("Welsh", "cy"), ("Wolof", "wo"), ("Walloon", "wa"), ("Mewati", "wtm"), ("", "khb"), ("Xhosa", "xh"), ("Minjungbal", "xjb"), ("Khengkha", "xkf"),
("Soga", "xog"), ("Liberia Kpelle", "xpe"), ("Yakut", "sah"), ("Yao", "yao"), ("Yapese", "yap"), ("Yoruba", "yo"), ("Cree", "cr"), ("Sichuan Yi", "ii"), ("Zeeuws", "zea"), ("Standard Moroccan Tamazight", "zgh"), ("Zhuang", "za"), ("Chinese", "zh"), ("Chinese", "zh"), ("Chinese", "zh"), ("Chinese", "zh"), ("Zande (individual language)", "zne"), ("Zulu", "zu"),
("Zaza", "zza"),
];

19
vendor/swash/src/text/mod.rs vendored Normal file
View File

@@ -0,0 +1,19 @@
/*!
Character properties and textual analysis.
*/
// Avoid errors for generated Unicode data.
#![allow(clippy::upper_case_acronyms)]
mod analyze;
mod compose;
mod lang;
mod lang_data;
mod unicode;
mod unicode_data;
pub mod cluster;
pub use analyze::{analyze, Analyze, WordBreakStrength};
pub use lang::{Cjk, Language};
pub use unicode::*;

385
vendor/swash/src/text/unicode.rs vendored Normal file
View File

@@ -0,0 +1,385 @@
/*!
Unicode character properties.
*/
pub use super::compose::Decompose;
#[doc(inline)]
pub use super::unicode_data::{
BidiClass, Block, Category, ClusterBreak, JoiningType, LineBreak, Script, WordBreak,
UNICODE_VERSION,
};
use super::compose::{compose_pair, decompose, decompose_compat};
use super::unicode_data::{
get_record_index, MyanmarClass, Record, UseClass, BRACKETS, MIRRORS, RECORDS, SCRIPTS_BY_TAG,
SCRIPT_COMPLEXITY, SCRIPT_NAMES, SCRIPT_TAGS,
};
use crate::Tag;
use core::char::from_u32_unchecked;
const RECORD_MASK: u16 = 0x1FFF;
const BOUNDARY_SHIFT: u16 = 13;
/// Compact, constant time reference to Unicode properties for a character.
#[derive(Copy, Clone, PartialEq, Eq, Default)]
pub struct Properties(u16);
impl Properties {
fn new(ch: u32) -> Self {
Self(get_record_index(ch as usize) as u16)
}
/// Returns the category of the character.
pub fn category(self) -> Category {
self.record().category
}
/// Returns the unicode block that contains the character.
pub fn block(self) -> Block {
self.record().block
}
/// Returns the script to which the character belongs.
pub fn script(self) -> Script {
self.record().script
}
/// Returns the canonical combining class of the character.
pub fn combining_class(self) -> u8 {
self.record().combining_class
}
/// Returns the bidirectional type of the character.
pub fn bidi_class(self) -> BidiClass {
self.record().bidi_class
}
/// Returns the joining type of the character.
pub fn joining_type(self) -> JoiningType {
self.record().joining_type
}
/// Returns the cluster break property for the character.
pub fn cluster_break(self) -> ClusterBreak {
self.record().cluster_break
}
/// Returns the word break property for the character.
pub fn word_break(self) -> WordBreak {
self.record().word_break
}
/// Returns the line break property for the character.
pub fn line_break(self) -> LineBreak {
self.record().line_break
}
/// Returns true if the character is an emoji.
pub fn is_emoji(self) -> bool {
self.record().flags.is_emoji()
}
/// Returns true if the character is an extended pictographic symbol.
pub fn is_extended_pictographic(self) -> bool {
self.record().flags.is_extended_pictographic()
}
/// Returns true if the character is an opening bracket.
pub fn is_open_bracket(self) -> bool {
self.record().flags.is_open_bracket()
}
/// Returns true if the character is a closing bracket.
pub fn is_close_bracket(self) -> bool {
self.record().flags.is_close_bracket()
}
pub(crate) fn is_ignorable(self) -> bool {
self.record().flags.is_ignorable()
}
pub(crate) fn is_variation_selector(self) -> bool {
self.record().flags.is_variation_selector()
}
pub(crate) fn contributes_to_shaping(self) -> bool {
self.record().flags.contributes_to_shaping()
}
pub(crate) fn with_boundary(mut self, b: u16) -> Self {
self.set_boundary(b);
self
}
pub(crate) fn boundary(self) -> u16 {
self.0 >> BOUNDARY_SHIFT
}
pub(crate) fn set_boundary(&mut self, boundary: u16) {
self.0 = (self.0 & RECORD_MASK) | (boundary & 0b11) << BOUNDARY_SHIFT;
}
pub(crate) fn use_class(self) -> (UseClass, bool, bool) {
let r = self.record();
(
r.use_class,
r.flags.needs_decomp(),
r.flags.is_extended_pictographic(),
)
}
pub(crate) fn myanmar_class(self) -> (MyanmarClass, bool) {
let r = self.record();
(r.myanmar_class, r.flags.is_extended_pictographic())
}
pub(crate) fn cluster_class(self) -> (ClusterBreak, bool) {
let r = self.record();
(r.cluster_break, r.flags.is_extended_pictographic())
}
#[inline(always)]
fn record(self) -> &'static Record {
// SAFETY: The inner index can only be generated by the private
// constructor which produces an in-bounds record index.
unsafe { RECORDS.get_unchecked((self.0 & RECORD_MASK) as usize) }
}
}
impl From<char> for Properties {
fn from(ch: char) -> Self {
Self::new(ch as u32)
}
}
impl From<&'_ char> for Properties {
fn from(ch: &'_ char) -> Self {
Self::new(*ch as u32)
}
}
impl From<u32> for Properties {
fn from(ch: u32) -> Self {
Self::new(ch)
}
}
impl From<&'_ u32> for Properties {
fn from(ch: &'_ u32) -> Self {
Self::new(*ch)
}
}
/// Trait that exposes Unicode properties for the `char` type.
pub trait Codepoint: Sized + Copy {
/// Returns the codepoint properties.
fn properties(self) -> Properties;
/// Returns the category of the character.
fn category(self) -> Category {
self.properties().category()
}
/// Returns the unicode block that contains the character.
fn block(self) -> Block {
self.properties().block()
}
/// Returns the script to which the character belongs.
fn script(self) -> Script {
self.properties().script()
}
/// Returns the canonical combining class of the character.
fn combining_class(self) -> u8 {
self.properties().combining_class()
}
/// Returns the bidirectional type of the character.
fn bidi_class(self) -> BidiClass {
self.properties().bidi_class()
}
/// Returns the joining type of the character.
fn joining_type(self) -> JoiningType {
self.properties().joining_type()
}
/// Returns the cluster break property for the character.
fn cluster_break(self) -> ClusterBreak {
self.properties().cluster_break()
}
/// Returns the word break property for the character.
fn word_break(self) -> WordBreak {
self.properties().word_break()
}
/// Returns the line break property for the character.
fn line_break(self) -> LineBreak {
self.properties().line_break()
}
/// Returns true if the character is an emoji.
fn is_emoji(self) -> bool {
self.properties().is_emoji()
}
/// Returns true if the character is an extended pictographic symbol.
fn is_extended_pictographic(self) -> bool {
self.properties().is_extended_pictographic()
}
/// Returns the bracket type of the character.
fn bracket_type(self) -> BracketType;
/// If the character is a closing bracket, returns its opening bracket
/// pair.
fn opening_bracket(self) -> Option<char>;
/// If the character is an opening bracket, returns its closing bracket
/// pair.
fn closing_bracket(self) -> Option<char>;
/// Returns the mirror of the character, if any.
fn mirror(self) -> Option<char>;
/// Returns the composition of two characters, if any.
fn compose(a: char, b: char) -> Option<char>;
/// Returns the canonical decomposition of the character.
fn decompose(self) -> Decompose;
/// Returns the compatibility decomposition of the character.
fn decompose_compatible(self) -> Decompose;
}
impl Codepoint for char {
fn properties(self) -> Properties {
Properties::from(self)
}
fn bracket_type(self) -> BracketType {
match self.closing_bracket() {
Some(other) => BracketType::Open(other),
_ => match self.opening_bracket() {
Some(other) => BracketType::Close(other),
_ => BracketType::None,
},
}
}
fn opening_bracket(self) -> Option<char> {
let c = self as u32;
if let Ok(idx) = BRACKETS.binary_search_by(|x| (x.1 as u32).cmp(&c)) {
return Some(unsafe { from_u32_unchecked(BRACKETS[idx].0 as u32) });
}
None
}
fn closing_bracket(self) -> Option<char> {
let c = self as u32;
if let Ok(idx) = BRACKETS.binary_search_by(|x| (x.0 as u32).cmp(&c)) {
return Some(unsafe { from_u32_unchecked(BRACKETS[idx].1 as u32) });
}
None
}
fn mirror(self) -> Option<char> {
let c = self as u32;
if let Ok(idx) = MIRRORS.binary_search_by(|x| (x.0 as u32).cmp(&c)) {
return Some(unsafe { from_u32_unchecked(MIRRORS[idx].1 as u32) });
}
None
}
fn compose(a: char, b: char) -> Option<char> {
compose_pair(a, b)
}
fn decompose(self) -> Decompose {
decompose(self)
}
fn decompose_compatible(self) -> Decompose {
decompose_compat(self)
}
}
/// Bracket type of a character.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum BracketType {
/// Not a bracket.
None,
/// An opening bracket with the associated closing bracket.
Open(char),
/// A closing bracket with the associated opening bracket.
Close(char),
}
impl Script {
/// Returns the script associated with the specified OpenType language
/// tag.
pub fn from_opentype(tag: Tag) -> Option<Self> {
match SCRIPTS_BY_TAG.binary_search_by(|x| x.0.cmp(&tag)) {
Ok(index) => Some(SCRIPTS_BY_TAG[index].1),
_ => None,
}
}
/// Returns the name of the script.
pub fn name(self) -> &'static str {
SCRIPT_NAMES[self as usize]
}
/// Returns true if the script requires complex shaping.
pub fn is_complex(self) -> bool {
SCRIPT_COMPLEXITY[self as usize]
}
/// Returns true if the script has cursive joining.
pub fn is_joined(self) -> bool {
matches!(
self,
Script::Arabic
| Script::Mongolian
| Script::Syriac
| Script::Nko
| Script::PhagsPa
| Script::Mandaic
| Script::Manichaean
| Script::PsalterPahlavi
| Script::Adlam
)
}
/// Returns the script as an OpenType tag.
pub fn to_opentype(self) -> Tag {
SCRIPT_TAGS[self as usize]
}
}
impl WordBreak {
pub(crate) const fn mask(self) -> u32 {
1 << (self as u32)
}
}
impl BidiClass {
/// Returns the bidi class as a 32 bit bitmask.
pub const fn mask(self) -> u32 {
1 << (self as u32)
}
/// Returns true if the presence of this bidi class requires
/// resolution.
pub fn needs_resolution(self) -> bool {
use BidiClass::*;
const OVERRIDE_MASK: u32 = RLE.mask() | LRE.mask() | RLO.mask() | LRO.mask();
const ISOLATE_MASK: u32 = RLI.mask() | LRI.mask() | FSI.mask();
const EXPLICIT_MASK: u32 = OVERRIDE_MASK | ISOLATE_MASK;
const BIDI_MASK: u32 = EXPLICIT_MASK | R.mask() | AL.mask() | AN.mask();
self.mask() & BIDI_MASK != 0
}
}

5483
vendor/swash/src/text/unicode_data.rs vendored Normal file

File diff suppressed because it is too large Load Diff

315
vendor/swash/src/variation.rs vendored Normal file
View File

@@ -0,0 +1,315 @@
use super::internal::{var::*, RawFont};
use super::{
setting::Setting,
string::{LocalizedString, StringId},
FontRef, NormalizedCoord, Tag,
};
/// Proxy for rematerializing variations collections.
#[derive(Copy, Clone)]
pub struct VariationsProxy {
fvar: u32,
avar: u32,
len: usize,
}
impl VariationsProxy {
/// Creates a variations proxy from the specified font.
pub fn from_font(font: &FontRef) -> Self {
let fvar = font.table_offset(FVAR);
let table = Fvar::from_font(font).unwrap_or_else(|| Fvar::new(&[]));
let avar = font.table_offset(AVAR);
let len = table.axis_count() as usize;
Self { fvar, avar, len }
}
/// Materializes variations from the specified font. This proxy must have
/// been created by the same font.
pub fn materialize<'a>(&self, font: &FontRef<'a>) -> Variations<'a> {
let data = if self.fvar != 0 {
font.data.get(self.fvar as usize..).unwrap_or(&[])
} else {
&[]
};
Variations {
font: *font,
fvar: Fvar::new(data),
avar: self.avar,
len: self.len,
pos: 0,
}
}
}
/// Iterator over a collection of font variations.
#[derive(Copy, Clone)]
pub struct Variations<'a> {
font: FontRef<'a>,
fvar: Fvar<'a>,
avar: u32,
len: usize,
pos: usize,
}
impl<'a> Variations<'a> {
pub(crate) fn from_font(font: &FontRef<'a>) -> Self {
let fvar = Fvar::from_font(font).unwrap_or_else(|| Fvar::new(&[]));
let avar = font.table_offset(AVAR);
let len = fvar.axis_count() as usize;
Self {
font: *font,
fvar,
avar,
len,
pos: 0,
}
}
fn get(&self, index: usize) -> Option<Variation<'a>> {
let axis = self.fvar.get_axis(index as u16)?;
Some(Variation {
font: self.font,
axis,
avar: self.avar,
})
}
/// Searches for a variation with the specified tag.
///
/// ## Iteration behavior
/// This function searches the entire variation collection without regard
/// for the current state of the iterator.
pub fn find_by_tag(&self, tag: Tag) -> Option<Variation<'a>> {
for i in 0..self.len {
if let Some(var) = self.get(i) {
if var.tag() == tag {
return Some(var);
}
}
}
None
}
/// Returns an iterator over the set of normalized coordinates
/// corresponding to the specified variation settings.
pub fn normalized_coords<I>(&self, settings: I) -> impl Iterator<Item = NormalizedCoord> + Clone
where
I: IntoIterator,
I::Item: Into<Setting<f32>>,
{
let mut copy = *self;
copy.pos = 0;
let mut coords = [0i16; 32];
let len = self.len.min(32);
for setting in settings {
let val = setting.into();
let tag = val.tag;
for (var, coord) in copy.take(len).zip(coords.iter_mut()) {
if var.axis.tag == tag {
*coord = var.normalize(val.value);
}
}
}
(0..len).map(move |i| coords[i])
}
}
impl_iter!(Variations, Variation);
/// Axis of variation in a variable font.
#[derive(Copy, Clone)]
pub struct Variation<'a> {
font: FontRef<'a>,
axis: VarAxis,
avar: u32,
}
impl<'a> Variation<'a> {
/// Returns the index of the variation.
pub fn index(&self) -> usize {
self.axis.index as usize
}
/// Returns the tag that identifies the variation.
pub fn tag(&self) -> Tag {
self.axis.tag
}
/// Returns the name identifier for the variation.
pub fn name_id(&self) -> StringId {
StringId::Other(self.axis.name_id)
}
/// Returns the name for the variation, optionally for a
/// particular language.
pub fn name(&self, language: Option<&str>) -> Option<LocalizedString<'a>> {
self.font
.localized_strings()
.find_by_id(self.name_id(), language)
}
/// Returns true if the variation should be hidden from users.
pub fn is_hidden(&self) -> bool {
self.axis.is_hidden()
}
/// Returns the minimum value of the variation.
pub fn min_value(&self) -> f32 {
self.axis.min.to_f32()
}
/// Returns the maximum value of the variation.
pub fn max_value(&self) -> f32 {
self.axis.max.to_f32()
}
/// Returns the default value of the variation.
pub fn default_value(&self) -> f32 {
self.axis.default.to_f32()
}
/// Computes a normalized coordinate for the specified value.
pub fn normalize(&self, value: f32) -> NormalizedCoord {
let avar = if self.avar != 0 {
Some((self.font.data, self.avar))
} else {
None
};
self.axis.normalized_coord(value.into(), avar)
}
}
/// Iterator over a collection of named variation instances.
#[derive(Copy, Clone)]
pub struct Instances<'a> {
font: FontRef<'a>,
fvar: Fvar<'a>,
avar: u32,
len: usize,
pos: usize,
}
impl<'a> Instances<'a> {
pub(crate) fn from_font(font: &FontRef<'a>) -> Self {
let fvar = Fvar::from_font(font).unwrap_or_else(|| Fvar::new(&[]));
let avar = font.table_offset(AVAR);
Self {
font: *font,
fvar,
avar,
len: fvar.instance_count() as usize,
pos: 0,
}
}
fn get(&self, index: usize) -> Option<Instance<'a>> {
let inner = self.fvar.get_instance(index as u16)?;
Some(Instance {
parent: *self,
inner,
})
}
/// Searches for an instance with the specified name.
///
/// ## Iteration behavior
/// This function searches the entire instance collection without regard
/// for the current state of the iterator.
pub fn find_by_name(&self, name: &str) -> Option<Instance<'a>> {
let strings = self.font.localized_strings();
for i in 0..self.len {
if let Some(instance) = self.get(i) {
let id = instance.name_id();
for instance_name in strings.filter(|s| s.id() == id) {
if instance_name.chars().eq(name.chars()) {
return Some(instance);
}
}
}
}
None
}
/// Searches for an instance with the specified PostScript name.
///
/// ## Iteration behavior
/// This function searches the entire instance collection without regard
/// for the current state of the iterator.
pub fn find_by_postscript_name(&self, name: &str) -> Option<Instance<'a>> {
let strings = self.font.localized_strings();
for i in 0..self.len {
if let Some(instance) = self.get(i) {
if let Some(id) = instance.postscript_name_id() {
for instance_name in strings.filter(|s| s.id() == id) {
if instance_name.chars().eq(name.chars()) {
return Some(instance);
}
}
}
}
}
None
}
}
impl_iter!(Instances, Instance);
/// Named instance in a variable font.
#[derive(Copy, Clone)]
pub struct Instance<'a> {
parent: Instances<'a>,
inner: VarInstance<'a>,
}
impl<'a> Instance<'a> {
/// Returns the index of the instance.
pub fn index(&self) -> usize {
self.inner.index as usize
}
/// Returns the name identifier for the instance.
pub fn name_id(&self) -> StringId {
StringId::Other(self.inner.name_id)
}
/// Returns the name for the instance, optionally for a
/// particular language.
pub fn name(&self, language: Option<&str>) -> Option<LocalizedString<'a>> {
self.parent
.font
.localized_strings()
.find_by_id(self.name_id(), language)
}
/// Returns the PostScript name identifier for the instance.
pub fn postscript_name_id(&self) -> Option<StringId> {
self.inner.postscript_name_id.map(StringId::Other)
}
/// Returns the PostScript name for the instance, optionally for a
/// particular language.
pub fn postscript_name(&self, language: Option<&str>) -> Option<LocalizedString<'a>> {
self.parent
.font
.localized_strings()
.find_by_id(self.postscript_name_id()?, language)
}
/// Returns an iterator over the variation values of the instance.
pub fn values(&self) -> impl Iterator<Item = f32> + 'a {
self.inner.values.iter().map(|v| v.to_f32())
}
/// Returns an iterator over the normalized coordinates for the instance.
pub fn normalized_coords(&self) -> impl Iterator<Item = NormalizedCoord> + 'a {
let avar = if self.parent.avar != 0 {
Some((self.parent.font.data, self.parent.avar))
} else {
None
};
let fvar = self.parent.fvar;
(0..fvar.axis_count())
.map(move |i| fvar.get_axis(i).unwrap_or_default())
.zip(self.inner.values)
.map(move |(axis, value)| axis.normalized_coord(value, avar))
}
}