Closes #20381 Release Notes: - Fixed password dialog for remote server showing ligatures
167 lines
5.2 KiB
Rust
167 lines
5.2 KiB
Rust
use std::sync::Arc;
|
|
|
|
use schemars::schema::{InstanceType, SchemaObject};
|
|
|
|
/// The OpenType features that can be configured for a given font.
|
|
#[derive(Default, Clone, Eq, PartialEq, Hash)]
|
|
pub struct FontFeatures(pub Arc<Vec<(String, u32)>>);
|
|
|
|
impl FontFeatures {
|
|
/// Disables `calt`.
|
|
pub fn disable_ligatures() -> Self {
|
|
Self(Arc::new(vec![("calt".into(), 0)]))
|
|
}
|
|
|
|
/// Get the tag name list of the font OpenType features
|
|
/// only enabled or disabled features are returned
|
|
pub fn tag_value_list(&self) -> &[(String, u32)] {
|
|
self.0.as_slice()
|
|
}
|
|
|
|
/// Returns whether the `calt` feature is enabled.
|
|
///
|
|
/// Returns `None` if the feature is not present.
|
|
pub fn is_calt_enabled(&self) -> Option<bool> {
|
|
self.0
|
|
.iter()
|
|
.find(|(feature, _)| feature == "calt")
|
|
.map(|(_, value)| *value == 1)
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Debug for FontFeatures {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let mut debug = f.debug_struct("FontFeatures");
|
|
for (tag, value) in self.tag_value_list() {
|
|
debug.field(tag, value);
|
|
}
|
|
|
|
debug.finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
|
#[serde(untagged)]
|
|
enum FeatureValue {
|
|
Bool(bool),
|
|
Number(serde_json::Number),
|
|
}
|
|
|
|
impl<'de> serde::Deserialize<'de> for FontFeatures {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
use serde::de::{MapAccess, Visitor};
|
|
use std::fmt;
|
|
|
|
struct FontFeaturesVisitor;
|
|
|
|
impl<'de> Visitor<'de> for FontFeaturesVisitor {
|
|
type Value = FontFeatures;
|
|
|
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
formatter.write_str("a map of font features")
|
|
}
|
|
|
|
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
|
|
where
|
|
M: MapAccess<'de>,
|
|
{
|
|
let mut feature_list = Vec::new();
|
|
|
|
while let Some((key, value)) =
|
|
access.next_entry::<String, Option<FeatureValue>>()?
|
|
{
|
|
if !is_valid_feature_tag(&key) {
|
|
log::error!("Incorrect font feature tag: {}", key);
|
|
continue;
|
|
}
|
|
if let Some(value) = value {
|
|
match value {
|
|
FeatureValue::Bool(enable) => {
|
|
if enable {
|
|
feature_list.push((key, 1));
|
|
} else {
|
|
feature_list.push((key, 0));
|
|
}
|
|
}
|
|
FeatureValue::Number(value) => {
|
|
if value.is_u64() {
|
|
feature_list.push((key, value.as_u64().unwrap() as u32));
|
|
} else {
|
|
log::error!(
|
|
"Incorrect font feature value {} for feature tag {}",
|
|
value,
|
|
key
|
|
);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(FontFeatures(Arc::new(feature_list)))
|
|
}
|
|
}
|
|
|
|
let features = deserializer.deserialize_map(FontFeaturesVisitor)?;
|
|
Ok(features)
|
|
}
|
|
}
|
|
|
|
impl serde::Serialize for FontFeatures {
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: serde::Serializer,
|
|
{
|
|
use serde::ser::SerializeMap;
|
|
|
|
let mut map = serializer.serialize_map(None)?;
|
|
|
|
for (tag, value) in self.tag_value_list() {
|
|
map.serialize_entry(tag, value)?;
|
|
}
|
|
|
|
map.end()
|
|
}
|
|
}
|
|
|
|
impl schemars::JsonSchema for FontFeatures {
|
|
fn schema_name() -> String {
|
|
"FontFeatures".into()
|
|
}
|
|
|
|
fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
|
let mut schema = SchemaObject::default();
|
|
schema.instance_type = Some(schemars::schema::SingleOrVec::Single(Box::new(
|
|
InstanceType::Object,
|
|
)));
|
|
{
|
|
let mut property = SchemaObject {
|
|
instance_type: Some(schemars::schema::SingleOrVec::Vec(vec![
|
|
InstanceType::Boolean,
|
|
InstanceType::Integer,
|
|
])),
|
|
..Default::default()
|
|
};
|
|
|
|
{
|
|
let mut number_constraints = property.number();
|
|
number_constraints.multiple_of = Some(1.0);
|
|
number_constraints.minimum = Some(0.0);
|
|
}
|
|
schema
|
|
.object()
|
|
.pattern_properties
|
|
.insert("[0-9a-zA-Z]{4}$".into(), property.into());
|
|
}
|
|
schema.into()
|
|
}
|
|
}
|
|
|
|
fn is_valid_feature_tag(tag: &str) -> bool {
|
|
tag.len() == 4 && tag.chars().all(|c| c.is_ascii_alphanumeric())
|
|
}
|