From d79895dc886d94d6eed5443ae270db2f558480bf Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Fri, 17 Mar 2023 15:05:10 +0100 Subject: [PATCH] Start work on Font roblox datatype --- .../lib-roblox/scripts/font_enum_map.luau | 84 ++++- .../lib-roblox/src/datatypes/conversion.rs | 7 +- .../lib-roblox/src/datatypes/types/enum.rs | 15 +- .../src/datatypes/types/enum_item.rs | 15 + .../lib-roblox/src/datatypes/types/enums.rs | 12 +- .../lib-roblox/src/datatypes/types/font.rs | 317 ++++++++++++++++++ .../lib-roblox/src/datatypes/types/mod.rs | 3 + packages/lib-roblox/src/lib.rs | 1 + 8 files changed, 437 insertions(+), 17 deletions(-) create mode 100644 packages/lib-roblox/src/datatypes/types/font.rs diff --git a/packages/lib-roblox/scripts/font_enum_map.luau b/packages/lib-roblox/scripts/font_enum_map.luau index 82b7f12..e3fe94d 100644 --- a/packages/lib-roblox/scripts/font_enum_map.luau +++ b/packages/lib-roblox/scripts/font_enum_map.luau @@ -2,15 +2,95 @@ local contents = "" -contents ..= "\nconst FONT_ENUM_MAP: &[(&str, Option<(&str, RbxFontWeight, RbxFontStyle)>)] = &[\n" +contents ..= "\ntype FontData = (&'static str, FontWeight, FontStyle);\n" + +-- FontWeight enum and implementation + +local function makeRustEnum(enum, numType: string) + local name = string.gsub(tostring(enum), "^Enum.", "") + + -- TODO: Use FromStr and AsStr traits for name functions + -- TODO: Match the naming convention that rbx-dom has for number/value functions + + local result = "" + + result ..= "\n#[derive(Debug, Clone, PartialEq)]" + result ..= "\npub(crate) enum " .. name .. " {" + for _, enum in enum:GetEnumItems() do + result ..= string.format("\n %s,", enum.Name) + end + result ..= "\n}\n" + + result ..= "\nimpl " .. name .. " {" + result ..= "\n pub fn into_name(self) -> &'static str {" + result ..= "\n match self {" + for _, enum in enum:GetEnumItems() do + result ..= string.format('\n Self::%s => "%s",', enum.Name, enum.Name) + end + result ..= "\n }" + result ..= "\n }" + result ..= "\n" + result ..= "\n pub fn from_name(name: impl AsRef) -> Option {" + result ..= "\n match name.as_ref() {" + for _, enum in enum:GetEnumItems() do + result ..= string.format('\n "%s" => Some(Self::%s),', enum.Name, enum.Name) + end + result ..= "\n _ => None," + result ..= "\n }" + result ..= "\n }" + result ..= "\n" + result ..= "\n pub fn into_num(self) -> " .. numType .. " {" + result ..= "\n match self {" + for _, enum in enum:GetEnumItems() do + result ..= string.format("\n Self::%s => %d,", enum.Name, enum.Value) + end + result ..= "\n }" + result ..= "\n }" + result ..= "\n" + result ..= "\n pub fn from_num(num: " .. numType .. ") -> Option {" + result ..= "\n match num {" + for _, enum in enum:GetEnumItems() do + result ..= string.format("\n %d => Some(Self::%s),", enum.Value, enum.Name) + end + result ..= "\n _ => None," + result ..= "\n }" + result ..= "\n }" + result ..= "\n}\n" + + result ..= "\nimpl fmt::Display for " .. name .. " {" + result ..= "\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {" + result ..= '\n write!(f, "{}", self.into_name())' + result ..= "\n }" + result ..= "\n}" + + return result +end + +contents ..= makeRustEnum(Enum.FontWeight, "u16") +contents ..= "\n" + +contents ..= makeRustEnum(Enum.FontStyle, "u8") +contents ..= "\n" + +-- Font constant map from enum to font data + +-- TODO: Fix formatting issues of output here + +local longestNameLen = 0 +for _, enum in Enum.Font:GetEnumItems() do + longestNameLen = math.max(longestNameLen, #enum.Name) +end + +contents ..= "\n#[rustfmt::skip]\nconst FONT_ENUM_MAP: &[(&str, Option)] = &[\n" for _, enum in Enum.Font:GetEnumItems() do if enum == Enum.Font.Unknown then contents ..= ' ("Unknown", None),\n' else local font = Font.fromEnum(enum) contents ..= string.format( - ' ("%s", Some(("%s", RbxFontWeight::%s, RbxFontStyle::%s))),\n', + ' ("%s",%s Some(("%s", FontWeight::%s, FontStyle::%s))),\n', enum.Name, + string.rep(" ", longestNameLen - #enum.Name), font.Family, font.Weight.Name, font.Style.Name diff --git a/packages/lib-roblox/src/datatypes/conversion.rs b/packages/lib-roblox/src/datatypes/conversion.rs index ac62908..82b0a80 100644 --- a/packages/lib-roblox/src/datatypes/conversion.rs +++ b/packages/lib-roblox/src/datatypes/conversion.rs @@ -128,9 +128,6 @@ impl<'lua> RbxVariantToLua<'lua> for LuaAnyUserData<'lua> { */ Ok(match variant.clone() { - // Not yet implemented datatypes - // Rbx::Font(_) => todo!(), - Rbx::Axes(value) => lua.create_userdata(Axes::from(value))?, Rbx::Faces(value) => lua.create_userdata(Faces::from(value))?, @@ -141,6 +138,8 @@ impl<'lua> RbxVariantToLua<'lua> for LuaAnyUserData<'lua> { Rbx::Color3uint8(value) => lua.create_userdata(Color3::from(value))?, Rbx::ColorSequence(value) => lua.create_userdata(ColorSequence::from(value))?, + Rbx::Font(value) => lua.create_userdata(Font::from(value))?, + Rbx::NumberRange(value) => lua.create_userdata(NumberRange::from(value))?, Rbx::NumberSequence(value) => lua.create_userdata(NumberSequence::from(value))?, @@ -200,6 +199,8 @@ impl<'lua> LuaToRbxVariant<'lua> for LuaAnyUserData<'lua> { RbxVariantType::Enum => convert::, + RbxVariantType::Font => convert::, + RbxVariantType::NumberRange => convert::, RbxVariantType::NumberSequence => convert::, diff --git a/packages/lib-roblox/src/datatypes/types/enum.rs b/packages/lib-roblox/src/datatypes/types/enum.rs index 070a0df..4682f23 100644 --- a/packages/lib-roblox/src/datatypes/types/enum.rs +++ b/packages/lib-roblox/src/datatypes/types/enum.rs @@ -15,6 +15,13 @@ pub struct Enum { pub(crate) desc: &'static EnumDescriptor<'static>, } +impl Enum { + pub(crate) fn from_name(name: impl AsRef) -> Option { + let db = rbx_reflection_database::get(); + db.enums.get(name.as_ref()).map(Enum::from) + } +} + impl LuaUserData for Enum { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { // Methods @@ -31,12 +38,8 @@ impl LuaUserData for Enum { .collect::>()) }); methods.add_meta_method(LuaMetaMethod::Index, |_, this, name: String| { - match this.desc.items.get(name.as_str()) { - Some(value) => Ok(EnumItem { - parent: this.clone(), - name: name.to_string(), - value: *value, - }), + match EnumItem::from_enum_and_name(this, &name) { + Some(item) => Ok(item), None => Err(LuaError::RuntimeError(format!( "The enum item '{}' does not exist for enum '{}'", name, this.desc.name diff --git a/packages/lib-roblox/src/datatypes/types/enum_item.rs b/packages/lib-roblox/src/datatypes/types/enum_item.rs index 1a148cc..7370760 100644 --- a/packages/lib-roblox/src/datatypes/types/enum_item.rs +++ b/packages/lib-roblox/src/datatypes/types/enum_item.rs @@ -19,6 +19,21 @@ pub struct EnumItem { } impl EnumItem { + pub(crate) fn from_enum_and_name(parent: &Enum, name: impl AsRef) -> Option { + let enum_name = name.as_ref(); + parent.desc.items.iter().find_map(|(name, v)| { + if *name == enum_name { + Some(Self { + parent: parent.clone(), + name: enum_name.to_string(), + value: *v, + }) + } else { + None + } + }) + } + /** Converts an instance property into an [`EnumItem`] datatype, if the property is known. diff --git a/packages/lib-roblox/src/datatypes/types/enums.rs b/packages/lib-roblox/src/datatypes/types/enums.rs index 6158e6d..b8f584d 100644 --- a/packages/lib-roblox/src/datatypes/types/enums.rs +++ b/packages/lib-roblox/src/datatypes/types/enums.rs @@ -25,16 +25,16 @@ impl LuaUserData for Enums { let db = rbx_reflection_database::get(); Ok(db.enums.values().map(Enum::from).collect::>()) }); - methods.add_meta_method(LuaMetaMethod::Index, |_, _, name: String| { - let db = rbx_reflection_database::get(); - match db.enums.get(name.as_str()) { - Some(desc) => Ok(Enum::from(desc)), + methods.add_meta_method( + LuaMetaMethod::Index, + |_, _, name: String| match Enum::from_name(&name) { + Some(e) => Ok(e), None => Err(LuaError::RuntimeError(format!( "The enum '{}' does not exist", name ))), - } - }); + }, + ); // Metamethods methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq); methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string); diff --git a/packages/lib-roblox/src/datatypes/types/font.rs b/packages/lib-roblox/src/datatypes/types/font.rs new file mode 100644 index 0000000..202ced4 --- /dev/null +++ b/packages/lib-roblox/src/datatypes/types/font.rs @@ -0,0 +1,317 @@ +use core::fmt; + +use mlua::prelude::*; +use rbx_dom_weak::types::{ + Font as RbxFont, FontStyle as RbxFontStyle, FontWeight as RbxFontWeight, +}; + +use super::{super::*, EnumItem}; + +/** + An implementation of the [Font](https://create.roblox.com/docs/reference/engine/datatypes/Font) Roblox datatype. + + This implements all documented properties, methods & constructors of the Font class as of March 2023. +*/ +#[derive(Debug, Clone, PartialEq)] +pub struct Font { + family: String, + weight: FontWeight, + style: FontStyle, +} + +impl Font { + pub(crate) fn from_enum(material_enum_item: &EnumItem) -> Option { + FONT_ENUM_MAP + .iter() + .find(|props| props.0 == material_enum_item.name && props.1.is_some()) + .map(|props| { + let props = props.1.as_ref().unwrap().clone(); + Font { + family: props.0.to_string(), + weight: props.1, + style: props.2, + } + }) + } + + pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { + datatype_table.set( + "fromEnum", + lua.create_function(|_, value: EnumItem| { + if value.parent.desc.name == "Font" { + match Font::from_enum(&value) { + Some(props) => Ok(props), + None => Err(LuaError::RuntimeError(format!( + "Found unknown Font '{}'", + value.name + ))), + } + } else { + Err(LuaError::RuntimeError(format!( + "Expected argument #1 to be a Font, got {}", + value.parent.desc.name + ))) + } + })?, + )?; + // TODO: Add fromName and fromId constructors + // TODO: Add "new" constructor + Ok(()) + } +} + +impl LuaUserData for Font { + fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("Family", |_, this| Ok(this.family.clone())); + // TODO: Getters & setters for weight, style + fields.add_field_method_get("Bold", |_, this| Ok(this.weight.clone().into_num() >= 600)); + fields.add_field_method_set("Bold", |_, this, value: bool| { + if value { + this.weight = FontWeight::Bold; + } else { + this.weight = FontWeight::Regular; + } + Ok(()) + }); + } + + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq); + methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string); + } +} + +impl fmt::Display for Font { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Font({}, {}, {})", self.family, self.weight, self.style) + } +} + +impl From for Font { + fn from(v: RbxFont) -> Self { + Self { + family: v.family, + weight: v.weight.into(), + style: v.style.into(), + } + } +} + +impl From for RbxFont { + fn from(v: Font) -> Self { + RbxFont { + family: v.family, + weight: v.weight.into(), + style: v.style.into(), + cached_face_id: None, + } + } +} + +impl From for FontWeight { + fn from(v: RbxFontWeight) -> Self { + FontWeight::from_num(v.as_u16()).expect("Missing font weight") + } +} + +impl From for RbxFontWeight { + fn from(v: FontWeight) -> Self { + RbxFontWeight::from_u16(v.into_num()).expect("Missing rbx font weight") + } +} + +impl From for FontStyle { + fn from(v: RbxFontStyle) -> Self { + FontStyle::from_num(v.as_u8()).expect("Missing font weight") + } +} + +impl From for RbxFontStyle { + fn from(v: FontStyle) -> Self { + RbxFontStyle::from_u8(v.into_num()).expect("Missing rbx font weight") + } +} + +/* + + NOTE: The font code below is all generated using the + font_enum_map script in the scripts dir next to src, + which can be ran in the Roblox Studio command bar + +*/ + +type FontData = (&'static str, FontWeight, FontStyle); + +#[derive(Debug, Clone, PartialEq)] +pub(crate) enum FontWeight { + Thin, + ExtraLight, + Light, + Regular, + Medium, + SemiBold, + Bold, + ExtraBold, + Heavy, +} + +impl FontWeight { + pub fn into_name(self) -> &'static str { + match self { + Self::Thin => "Thin", + Self::ExtraLight => "ExtraLight", + Self::Light => "Light", + Self::Regular => "Regular", + Self::Medium => "Medium", + Self::SemiBold => "SemiBold", + Self::Bold => "Bold", + Self::ExtraBold => "ExtraBold", + Self::Heavy => "Heavy", + } + } + + pub fn from_name(name: impl AsRef) -> Option { + match name.as_ref() { + "Thin" => Some(Self::Thin), + "ExtraLight" => Some(Self::ExtraLight), + "Light" => Some(Self::Light), + "Regular" => Some(Self::Regular), + "Medium" => Some(Self::Medium), + "SemiBold" => Some(Self::SemiBold), + "Bold" => Some(Self::Bold), + "ExtraBold" => Some(Self::ExtraBold), + "Heavy" => Some(Self::Heavy), + _ => None, + } + } + + pub fn into_num(self) -> u16 { + match self { + Self::Thin => 100, + Self::ExtraLight => 200, + Self::Light => 300, + Self::Regular => 400, + Self::Medium => 500, + Self::SemiBold => 600, + Self::Bold => 700, + Self::ExtraBold => 800, + Self::Heavy => 900, + } + } + + pub fn from_num(num: u16) -> Option { + match num { + 100 => Some(Self::Thin), + 200 => Some(Self::ExtraLight), + 300 => Some(Self::Light), + 400 => Some(Self::Regular), + 500 => Some(Self::Medium), + 600 => Some(Self::SemiBold), + 700 => Some(Self::Bold), + 800 => Some(Self::ExtraBold), + 900 => Some(Self::Heavy), + _ => None, + } + } +} + +impl fmt::Display for FontWeight { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.clone().into_name()) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub(crate) enum FontStyle { + Normal, + Italic, +} + +impl FontStyle { + pub fn into_name(self) -> &'static str { + match self { + Self::Normal => "Normal", + Self::Italic => "Italic", + } + } + + pub fn from_name(name: impl AsRef) -> Option { + match name.as_ref() { + "Normal" => Some(Self::Normal), + "Italic" => Some(Self::Italic), + _ => None, + } + } + + pub fn into_num(self) -> u8 { + match self { + Self::Normal => 0, + Self::Italic => 1, + } + } + + pub fn from_num(num: u8) -> Option { + match num { + 0 => Some(Self::Normal), + 1 => Some(Self::Italic), + _ => None, + } + } +} + +impl fmt::Display for FontStyle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.clone().into_name()) + } +} + +#[rustfmt::skip] +const FONT_ENUM_MAP: &[(&str, Option)] = &[ + ("Legacy", Some(("rbxasset://fonts/families/LegacyArial.json", FontWeight::Regular, FontStyle::Normal))), + ("Arial", Some(("rbxasset://fonts/families/Arial.json", FontWeight::Regular, FontStyle::Normal))), + ("ArialBold", Some(("rbxasset://fonts/families/Arial.json", FontWeight::Bold, FontStyle::Normal))), + ("SourceSans", Some(("rbxasset://fonts/families/SourceSansPro.json", FontWeight::Regular, FontStyle::Normal))), + ("SourceSansBold", Some(("rbxasset://fonts/families/SourceSansPro.json", FontWeight::Bold, FontStyle::Normal))), + ("SourceSansSemibold", Some(("rbxasset://fonts/families/SourceSansPro.json", FontWeight::SemiBold, FontStyle::Normal))), + ("SourceSansLight", Some(("rbxasset://fonts/families/SourceSansPro.json", FontWeight::Light, FontStyle::Normal))), + ("SourceSansItalic", Some(("rbxasset://fonts/families/SourceSansPro.json", FontWeight::Regular, FontStyle::Italic))), + ("Bodoni", Some(("rbxasset://fonts/families/AccanthisADFStd.json", FontWeight::Regular, FontStyle::Normal))), + ("Garamond", Some(("rbxasset://fonts/families/Guru.json", FontWeight::Regular, FontStyle::Normal))), + ("Cartoon", Some(("rbxasset://fonts/families/ComicNeueAngular.json", FontWeight::Regular, FontStyle::Normal))), + ("Code", Some(("rbxasset://fonts/families/Inconsolata.json", FontWeight::Regular, FontStyle::Normal))), + ("Highway", Some(("rbxasset://fonts/families/HighwayGothic.json", FontWeight::Regular, FontStyle::Normal))), + ("SciFi", Some(("rbxasset://fonts/families/Zekton.json", FontWeight::Regular, FontStyle::Normal))), + ("Arcade", Some(("rbxasset://fonts/families/PressStart2P.json", FontWeight::Regular, FontStyle::Normal))), + ("Fantasy", Some(("rbxasset://fonts/families/Balthazar.json", FontWeight::Regular, FontStyle::Normal))), + ("Antique", Some(("rbxasset://fonts/families/RomanAntique.json", FontWeight::Regular, FontStyle::Normal))), + ("Gotham", Some(("rbxasset://fonts/families/GothamSSm.json", FontWeight::Regular, FontStyle::Normal))), + ("GothamMedium", Some(("rbxasset://fonts/families/GothamSSm.json", FontWeight::Medium, FontStyle::Normal))), + ("GothamBold", Some(("rbxasset://fonts/families/GothamSSm.json", FontWeight::Bold, FontStyle::Normal))), + ("GothamBlack", Some(("rbxasset://fonts/families/GothamSSm.json", FontWeight::Heavy, FontStyle::Normal))), + ("AmaticSC", Some(("rbxasset://fonts/families/AmaticSC.json", FontWeight::Regular, FontStyle::Normal))), + ("Bangers", Some(("rbxasset://fonts/families/Bangers.json", FontWeight::Regular, FontStyle::Normal))), + ("Creepster", Some(("rbxasset://fonts/families/Creepster.json", FontWeight::Regular, FontStyle::Normal))), + ("DenkOne", Some(("rbxasset://fonts/families/DenkOne.json", FontWeight::Regular, FontStyle::Normal))), + ("Fondamento", Some(("rbxasset://fonts/families/Fondamento.json", FontWeight::Regular, FontStyle::Normal))), + ("FredokaOne", Some(("rbxasset://fonts/families/FredokaOne.json", FontWeight::Regular, FontStyle::Normal))), + ("GrenzeGotisch", Some(("rbxasset://fonts/families/GrenzeGotisch.json", FontWeight::Regular, FontStyle::Normal))), + ("IndieFlower", Some(("rbxasset://fonts/families/IndieFlower.json", FontWeight::Regular, FontStyle::Normal))), + ("JosefinSans", Some(("rbxasset://fonts/families/JosefinSans.json", FontWeight::Regular, FontStyle::Normal))), + ("Jura", Some(("rbxasset://fonts/families/Jura.json", FontWeight::Regular, FontStyle::Normal))), + ("Kalam", Some(("rbxasset://fonts/families/Kalam.json", FontWeight::Regular, FontStyle::Normal))), + ("LuckiestGuy", Some(("rbxasset://fonts/families/LuckiestGuy.json", FontWeight::Regular, FontStyle::Normal))), + ("Merriweather", Some(("rbxasset://fonts/families/Merriweather.json", FontWeight::Regular, FontStyle::Normal))), + ("Michroma", Some(("rbxasset://fonts/families/Michroma.json", FontWeight::Regular, FontStyle::Normal))), + ("Nunito", Some(("rbxasset://fonts/families/Nunito.json", FontWeight::Regular, FontStyle::Normal))), + ("Oswald", Some(("rbxasset://fonts/families/Oswald.json", FontWeight::Regular, FontStyle::Normal))), + ("PatrickHand", Some(("rbxasset://fonts/families/PatrickHand.json", FontWeight::Regular, FontStyle::Normal))), + ("PermanentMarker", Some(("rbxasset://fonts/families/PermanentMarker.json", FontWeight::Regular, FontStyle::Normal))), + ("Roboto", Some(("rbxasset://fonts/families/Roboto.json", FontWeight::Regular, FontStyle::Normal))), + ("RobotoCondensed", Some(("rbxasset://fonts/families/RobotoCondensed.json", FontWeight::Regular, FontStyle::Normal))), + ("RobotoMono", Some(("rbxasset://fonts/families/RobotoMono.json", FontWeight::Regular, FontStyle::Normal))), + ("Sarpanch", Some(("rbxasset://fonts/families/Sarpanch.json", FontWeight::Regular, FontStyle::Normal))), + ("SpecialElite", Some(("rbxasset://fonts/families/SpecialElite.json", FontWeight::Regular, FontStyle::Normal))), + ("TitilliumWeb", Some(("rbxasset://fonts/families/TitilliumWeb.json", FontWeight::Regular, FontStyle::Normal))), + ("Ubuntu", Some(("rbxasset://fonts/families/Ubuntu.json", FontWeight::Regular, FontStyle::Normal))), + ("Unknown", None), +]; diff --git a/packages/lib-roblox/src/datatypes/types/mod.rs b/packages/lib-roblox/src/datatypes/types/mod.rs index bc2ebce..6c6250d 100644 --- a/packages/lib-roblox/src/datatypes/types/mod.rs +++ b/packages/lib-roblox/src/datatypes/types/mod.rs @@ -8,6 +8,7 @@ mod r#enum; mod r#enum_item; mod r#enums; mod faces; +mod font; mod number_range; mod number_sequence; mod number_sequence_keypoint; @@ -30,6 +31,7 @@ pub use color3::Color3; pub use color_sequence::ColorSequence; pub use color_sequence_keypoint::ColorSequenceKeypoint; pub use faces::Faces; +pub use font::Font; pub use number_range::NumberRange; pub use number_sequence::NumberSequence; pub use number_sequence_keypoint::NumberSequenceKeypoint; @@ -98,6 +100,7 @@ mod tests { color_sequence_keypoint: "datatypes/ColorSequenceKeypoint", r#enum: "datatypes/Enum", faces: "datatypes/Faces", + font: "datatypes/Font", number_range: "datatypes/NumberRange", number_sequence: "datatypes/NumberSequence", number_sequence_keypoint: "datatypes/NumberSequenceKeypoint", diff --git a/packages/lib-roblox/src/lib.rs b/packages/lib-roblox/src/lib.rs index 99201ab..eb741f9 100644 --- a/packages/lib-roblox/src/lib.rs +++ b/packages/lib-roblox/src/lib.rs @@ -26,6 +26,7 @@ fn make_all_datatypes(lua: &Lua) -> LuaResult> { ("ColorSequence", make_dt(lua, ColorSequence::make_table)?), ("ColorSequenceKeypoint", make_dt(lua, ColorSequenceKeypoint::make_table)?), ("Faces", make_dt(lua, Faces::make_table)?), + ("Font", make_dt(lua, Font::make_table)?), ("NumberRange", make_dt(lua, NumberRange::make_table)?), ("NumberSequence", make_dt(lua, NumberSequence::make_table)?), ("NumberSequenceKeypoint", make_dt(lua, NumberSequenceKeypoint::make_table)?),