aboutsummaryrefslogtreecommitdiffstats
path: root/components/fonts/platform/windows
diff options
context:
space:
mode:
Diffstat (limited to 'components/fonts/platform/windows')
-rw-r--r--components/fonts/platform/windows/font.rs276
-rw-r--r--components/fonts/platform/windows/font_list.rs379
2 files changed, 655 insertions, 0 deletions
diff --git a/components/fonts/platform/windows/font.rs b/components/fonts/platform/windows/font.rs
new file mode 100644
index 00000000000..8dd2d78bb1f
--- /dev/null
+++ b/components/fonts/platform/windows/font.rs
@@ -0,0 +1,276 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+// NOTE: https://www.chromium.org/directwrite-font-proxy has useful
+// information for an approach that we'll likely need to take when the
+// renderer moves to a sandboxed process.
+
+use std::cmp::{max, min};
+use std::fmt;
+use std::io::Cursor;
+use std::ops::Deref;
+use std::sync::Arc;
+
+use app_units::Au;
+use dwrote::{FontFace, FontFile};
+use log::{debug, warn};
+use style::computed_values::font_stretch::T as StyleFontStretch;
+use style::computed_values::font_weight::T as StyleFontWeight;
+use style::values::computed::font::FontStyle as StyleFontStyle;
+use truetype::tables::WindowsMetrics;
+use truetype::value::Read;
+use webrender_api::FontInstanceFlags;
+
+use crate::{
+ ot_tag, FontIdentifier, FontMetrics, FontTableMethods, FontTableTag, FontTemplateDescriptor,
+ FractionalPixel, GlyphId, PlatformFontMethods,
+};
+
+// 1em = 12pt = 16px, assuming 72 points per inch and 96 px per inch
+fn pt_to_px(pt: f64) -> f64 {
+ pt / 72. * 96.
+}
+fn em_to_px(em: f64) -> f64 {
+ em * 16.
+}
+fn au_from_em(em: f64) -> Au {
+ Au::from_f64_px(em_to_px(em))
+}
+fn au_from_pt(pt: f64) -> Au {
+ Au::from_f64_px(pt_to_px(pt))
+}
+
+pub struct FontTable {
+ data: Vec<u8>,
+}
+
+impl FontTable {
+ pub fn wrap(data: &[u8]) -> FontTable {
+ FontTable {
+ data: data.to_vec(),
+ }
+ }
+}
+
+impl FontTableMethods for FontTable {
+ fn buffer(&self) -> &[u8] {
+ &self.data
+ }
+}
+
+#[derive(Debug)]
+pub struct PlatformFont {
+ face: Nondebug<FontFace>,
+ /// A reference to this data used to create this [`PlatformFont`], ensuring the
+ /// data stays alive of the lifetime of this struct.
+ _data: Arc<Vec<u8>>,
+ em_size: f32,
+ du_to_px: f32,
+ scaled_du_to_px: f32,
+}
+
+// Based on information from the Skia codebase, it seems that DirectWrite APIs from
+// Windows 10 and beyond are thread safe. If problems arise from this, we can protect the
+// platform font with a Mutex.
+// See https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/src/ports/SkScalerContext_win_dw.cpp;l=56;bpv=0;bpt=1.
+unsafe impl Sync for PlatformFont {}
+unsafe impl Send for PlatformFont {}
+
+struct Nondebug<T>(T);
+
+impl<T> fmt::Debug for Nondebug<T> {
+ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+ Ok(())
+ }
+}
+
+impl<T> Deref for Nondebug<T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ &self.0
+ }
+}
+
+impl PlatformFontMethods for PlatformFont {
+ fn new_from_data(
+ _font_identifier: FontIdentifier,
+ data: Arc<Vec<u8>>,
+ face_index: u32,
+ pt_size: Option<Au>,
+ ) -> Result<Self, &'static str> {
+ let font_file = FontFile::new_from_data(data.clone()).ok_or("Could not create FontFile")?;
+ let face = font_file
+ .create_face(face_index, dwrote::DWRITE_FONT_SIMULATIONS_NONE)
+ .map_err(|_| "Could not create FontFace")?;
+
+ let pt_size = pt_size.unwrap_or(au_from_pt(12.));
+ let du_per_em = face.metrics().metrics0().designUnitsPerEm as f32;
+
+ let em_size = pt_size.to_f32_px() / 16.;
+ let design_units_per_pixel = du_per_em / 16.;
+
+ let design_units_to_pixels = 1. / design_units_per_pixel;
+ let scaled_design_units_to_pixels = em_size / design_units_per_pixel;
+
+ Ok(PlatformFont {
+ face: Nondebug(face),
+ _data: data,
+ em_size,
+ du_to_px: design_units_to_pixels,
+ scaled_du_to_px: scaled_design_units_to_pixels,
+ })
+ }
+
+ fn descriptor(&self) -> FontTemplateDescriptor {
+ // We need the font (DWriteFont) in order to be able to query things like
+ // the family name, face name, weight, etc. On Windows 10, the
+ // DWriteFontFace3 interface provides this on the FontFace, but that's only
+ // available on Win10+.
+ //
+ // Instead, we do the parsing work using the truetype crate for raw fonts.
+ // We're just extracting basic info, so this is sufficient for now.
+ //
+ // The `dwrote` APIs take SFNT table tags in a reversed byte order, which
+ // is why `u32::swap_bytes()` is called here.
+ let windows_metrics_bytes = self
+ .face
+ .get_font_table(u32::swap_bytes(ot_tag!('O', 'S', '/', '2')));
+ if windows_metrics_bytes.is_none() {
+ warn!("Could not find OS/2 table in font.");
+ return FontTemplateDescriptor::default();
+ }
+
+ let mut cursor = Cursor::new(windows_metrics_bytes.as_ref().unwrap());
+ let Ok(table) = WindowsMetrics::read(&mut cursor) else {
+ warn!("Could not read OS/2 table in font.");
+ return FontTemplateDescriptor::default();
+ };
+
+ let (weight_val, width_val, italic_bool) = match table {
+ WindowsMetrics::Version0(ref m) => {
+ (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
+ },
+ WindowsMetrics::Version1(ref m) => {
+ (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
+ },
+ WindowsMetrics::Version2(ref m) |
+ WindowsMetrics::Version3(ref m) |
+ WindowsMetrics::Version4(ref m) => {
+ (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
+ },
+ WindowsMetrics::Version5(ref m) => {
+ (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
+ },
+ };
+
+ let weight = StyleFontWeight::from_float(weight_val as f32);
+ let stretch = match min(9, max(1, width_val)) {
+ 1 => StyleFontStretch::ULTRA_CONDENSED,
+ 2 => StyleFontStretch::EXTRA_CONDENSED,
+ 3 => StyleFontStretch::CONDENSED,
+ 4 => StyleFontStretch::SEMI_CONDENSED,
+ 5 => StyleFontStretch::NORMAL,
+ 6 => StyleFontStretch::SEMI_EXPANDED,
+ 7 => StyleFontStretch::EXPANDED,
+ 8 => StyleFontStretch::EXTRA_EXPANDED,
+ 9 => StyleFontStretch::ULTRA_CONDENSED,
+ _ => {
+ warn!("Unknown stretch size.");
+ StyleFontStretch::NORMAL
+ },
+ };
+
+ let style = if italic_bool {
+ StyleFontStyle::ITALIC
+ } else {
+ StyleFontStyle::NORMAL
+ };
+
+ FontTemplateDescriptor::new(weight, stretch, style)
+ }
+
+ fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
+ let glyph = self.face.get_glyph_indices(&[codepoint as u32])[0];
+ if glyph == 0 {
+ return None;
+ }
+ Some(glyph as GlyphId)
+ }
+
+ fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
+ if glyph == 0 {
+ return None;
+ }
+
+ let gm = self.face.get_design_glyph_metrics(&[glyph as u16], false)[0];
+ let f = (gm.advanceWidth as f32 * self.scaled_du_to_px) as FractionalPixel;
+
+ Some(f)
+ }
+
+ /// Can this font do basic horizontal LTR shaping without Harfbuzz?
+ fn can_do_fast_shaping(&self) -> bool {
+ // TODO copy CachedKernTable from the MacOS X implementation to
+ // somehwere global and use it here. We could also implement the
+ // IDirectWriteFontFace1 interface and use the glyph kerning pair
+ // methods there.
+ false
+ }
+
+ fn glyph_h_kerning(&self, _: GlyphId, _: GlyphId) -> FractionalPixel {
+ 0.0
+ }
+
+ fn metrics(&self) -> FontMetrics {
+ let dm = self.face.metrics().metrics0();
+
+ let au_from_du = |du| -> Au { Au::from_f32_px(du as f32 * self.du_to_px) };
+ let au_from_du_s = |du| -> Au { Au::from_f32_px(du as f32 * self.scaled_du_to_px) };
+
+ // anything that we calculate and don't just pull out of self.face.metrics
+ // is pulled out here for clarity
+ let leading = dm.ascent - dm.capHeight;
+
+ let zero_horizontal_advance = self
+ .glyph_index('0')
+ .and_then(|idx| self.glyph_h_advance(idx))
+ .map(Au::from_f64_px);
+ let ic_horizontal_advance = self
+ .glyph_index('\u{6C34}')
+ .and_then(|idx| self.glyph_h_advance(idx))
+ .map(Au::from_f64_px);
+
+ let metrics = FontMetrics {
+ underline_size: au_from_du(dm.underlineThickness as i32),
+ underline_offset: au_from_du_s(dm.underlinePosition as i32),
+ strikeout_size: au_from_du(dm.strikethroughThickness as i32),
+ strikeout_offset: au_from_du_s(dm.strikethroughPosition as i32),
+ leading: au_from_du_s(leading as i32),
+ x_height: au_from_du_s(dm.xHeight as i32),
+ em_size: au_from_em(self.em_size as f64),
+ ascent: au_from_du_s(dm.ascent as i32),
+ descent: au_from_du_s(dm.descent as i32),
+ max_advance: au_from_pt(0.0), // FIXME
+ average_advance: au_from_pt(0.0), // FIXME
+ line_gap: au_from_du_s((dm.ascent + dm.descent + dm.lineGap as u16) as i32),
+ zero_horizontal_advance,
+ ic_horizontal_advance,
+ };
+ debug!("Font metrics (@{} pt): {:?}", self.em_size * 12., metrics);
+ metrics
+ }
+
+ fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
+ // dwrote (and presumably the Windows APIs) accept a reversed version of the table
+ // tag bytes, which means that `u32::swap_bytes` must be called here in order to
+ // use a byte order compatible with the rest of Servo.
+ self.face
+ .get_font_table(u32::swap_bytes(tag))
+ .map(|bytes| FontTable { data: bytes })
+ }
+
+ fn webrender_font_instance_flags(&self) -> FontInstanceFlags {
+ FontInstanceFlags::empty()
+ }
+}
diff --git a/components/fonts/platform/windows/font_list.rs b/components/fonts/platform/windows/font_list.rs
new file mode 100644
index 00000000000..31c1d631160
--- /dev/null
+++ b/components/fonts/platform/windows/font_list.rs
@@ -0,0 +1,379 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use std::hash::Hash;
+use std::sync::Arc;
+
+use base::text::{unicode_plane, UnicodeBlock, UnicodeBlockMethod};
+use dwrote::{Font, FontCollection, FontDescriptor, FontStretch, FontStyle};
+use malloc_size_of_derive::MallocSizeOf;
+use serde::{Deserialize, Serialize};
+use style::values::computed::{FontStyle as StyleFontStyle, FontWeight as StyleFontWeight};
+use style::values::specified::font::FontStretchKeyword;
+
+use crate::{
+ EmojiPresentationPreference, FallbackFontSelectionOptions, FontTemplate, FontTemplateDescriptor,
+};
+
+pub static SANS_SERIF_FONT_FAMILY: &str = "Arial";
+
+pub fn system_default_family(_: &str) -> Option<String> {
+ Some("Verdana".to_owned())
+}
+
+pub fn for_each_available_family<F>(mut callback: F)
+where
+ F: FnMut(String),
+{
+ let system_fc = FontCollection::system();
+ for family in system_fc.families_iter() {
+ callback(family.name());
+ }
+}
+
+/// An identifier for a local font on a Windows system.
+#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
+pub struct LocalFontIdentifier {
+ /// The FontDescriptor of this font.
+ #[ignore_malloc_size_of = "dwrote does not support MallocSizeOf"]
+ pub font_descriptor: Arc<FontDescriptor>,
+}
+
+impl LocalFontIdentifier {
+ pub fn index(&self) -> u32 {
+ FontCollection::system()
+ .get_font_from_descriptor(&self.font_descriptor)
+ .map_or(0, |font| font.create_font_face().get_index())
+ }
+
+ pub(crate) fn read_data_from_file(&self) -> Vec<u8> {
+ let font = FontCollection::system()
+ .get_font_from_descriptor(&self.font_descriptor)
+ .unwrap();
+ let face = font.create_font_face();
+ let files = face.get_files();
+ assert!(!files.is_empty());
+ files[0].get_font_file_bytes()
+ }
+}
+
+impl Eq for LocalFontIdentifier {}
+
+impl Hash for LocalFontIdentifier {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.font_descriptor.family_name.hash(state);
+ self.font_descriptor.weight.to_u32().hash(state);
+ self.font_descriptor.stretch.to_u32().hash(state);
+ self.font_descriptor.style.to_u32().hash(state);
+ }
+}
+
+pub fn for_each_variation<F>(family_name: &str, mut callback: F)
+where
+ F: FnMut(FontTemplate),
+{
+ let system_fc = FontCollection::system();
+ if let Some(family) = system_fc.get_font_family_by_name(family_name) {
+ let count = family.get_font_count();
+ for i in 0..count {
+ let font = family.get_font(i);
+ let template_descriptor = (&font).into();
+ let local_font_identifier = LocalFontIdentifier {
+ font_descriptor: Arc::new(font.to_descriptor()),
+ };
+ callback(FontTemplate::new_for_local_font(
+ local_font_identifier,
+ template_descriptor,
+ ))
+ }
+ }
+}
+
+// Based on gfxWindowsPlatform::GetCommonFallbackFonts() in Gecko
+pub fn fallback_font_families(options: FallbackFontSelectionOptions) -> Vec<&'static str> {
+ let mut families = Vec::new();
+ if options.presentation_preference == EmojiPresentationPreference::Emoji {
+ families.push("Segoe UI Emoji");
+ }
+
+ families.push("Arial");
+ match unicode_plane(options.character) {
+ // https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane
+ 0 => {
+ if let Some(block) = options.character.block() {
+ match block {
+ UnicodeBlock::CyrillicSupplement |
+ UnicodeBlock::Armenian |
+ UnicodeBlock::Hebrew => {
+ families.push("Estrangelo Edessa");
+ families.push("Cambria");
+ },
+
+ UnicodeBlock::Arabic | UnicodeBlock::ArabicSupplement => {
+ families.push("Microsoft Uighur");
+ },
+
+ UnicodeBlock::Syriac => {
+ families.push("Estrangelo Edessa");
+ },
+
+ UnicodeBlock::Thaana => {
+ families.push("MV Boli");
+ },
+
+ UnicodeBlock::NKo => {
+ families.push("Ebrima");
+ },
+
+ UnicodeBlock::Devanagari | UnicodeBlock::Bengali => {
+ families.push("Nirmala UI");
+ families.push("Utsaah");
+ families.push("Aparajita");
+ },
+
+ UnicodeBlock::Gurmukhi |
+ UnicodeBlock::Gujarati |
+ UnicodeBlock::Oriya |
+ UnicodeBlock::Tamil |
+ UnicodeBlock::Telugu |
+ UnicodeBlock::Kannada |
+ UnicodeBlock::Malayalam |
+ UnicodeBlock::Sinhala |
+ UnicodeBlock::Lepcha |
+ UnicodeBlock::OlChiki |
+ UnicodeBlock::CyrillicExtendedC |
+ UnicodeBlock::SundaneseSupplement |
+ UnicodeBlock::VedicExtensions => {
+ families.push("Nirmala UI");
+ },
+
+ UnicodeBlock::Thai => {
+ families.push("Leelawadee UI");
+ },
+
+ UnicodeBlock::Lao => {
+ families.push("Lao UI");
+ },
+
+ UnicodeBlock::Myanmar |
+ UnicodeBlock::MyanmarExtendedA |
+ UnicodeBlock::MyanmarExtendedB => {
+ families.push("Myanmar Text");
+ },
+
+ UnicodeBlock::HangulJamo |
+ UnicodeBlock::HangulJamoExtendedA |
+ UnicodeBlock::HangulSyllables |
+ UnicodeBlock::HangulJamoExtendedB |
+ UnicodeBlock::HangulCompatibilityJamo => {
+ families.push("Malgun Gothic");
+ },
+
+ UnicodeBlock::Ethiopic |
+ UnicodeBlock::EthiopicSupplement |
+ UnicodeBlock::EthiopicExtended |
+ UnicodeBlock::EthiopicExtendedA => {
+ families.push("Nyala");
+ },
+
+ UnicodeBlock::Cherokee => {
+ families.push("Plantagenet Cherokee");
+ },
+
+ UnicodeBlock::UnifiedCanadianAboriginalSyllabics |
+ UnicodeBlock::UnifiedCanadianAboriginalSyllabicsExtended => {
+ families.push("Euphemia");
+ families.push("Segoe UI");
+ },
+
+ UnicodeBlock::Khmer | UnicodeBlock::KhmerSymbols => {
+ families.push("Khmer UI");
+ families.push("Leelawadee UI");
+ },
+
+ UnicodeBlock::Mongolian => {
+ families.push("Mongolian Baiti");
+ },
+
+ UnicodeBlock::TaiLe => {
+ families.push("Microsoft Tai Le");
+ },
+
+ UnicodeBlock::NewTaiLue => {
+ families.push("Microsoft New Tai Lue");
+ },
+
+ UnicodeBlock::Buginese |
+ UnicodeBlock::TaiTham |
+ UnicodeBlock::CombiningDiacriticalMarksExtended => {
+ families.push("Leelawadee UI");
+ },
+
+ UnicodeBlock::GeneralPunctuation |
+ UnicodeBlock::SuperscriptsandSubscripts |
+ UnicodeBlock::CurrencySymbols |
+ UnicodeBlock::CombiningDiacriticalMarksforSymbols |
+ UnicodeBlock::LetterlikeSymbols |
+ UnicodeBlock::NumberForms |
+ UnicodeBlock::Arrows |
+ UnicodeBlock::MathematicalOperators |
+ UnicodeBlock::MiscellaneousTechnical |
+ UnicodeBlock::ControlPictures |
+ UnicodeBlock::OpticalCharacterRecognition |
+ UnicodeBlock::EnclosedAlphanumerics |
+ UnicodeBlock::BoxDrawing |
+ UnicodeBlock::BlockElements |
+ UnicodeBlock::GeometricShapes |
+ UnicodeBlock::MiscellaneousSymbols |
+ UnicodeBlock::Dingbats |
+ UnicodeBlock::MiscellaneousMathematicalSymbolsA |
+ UnicodeBlock::SupplementalArrowsA |
+ UnicodeBlock::SupplementalArrowsB |
+ UnicodeBlock::MiscellaneousMathematicalSymbolsB |
+ UnicodeBlock::SupplementalMathematicalOperators |
+ UnicodeBlock::MiscellaneousSymbolsandArrows |
+ UnicodeBlock::Glagolitic |
+ UnicodeBlock::LatinExtendedC |
+ UnicodeBlock::Coptic => {
+ families.push("Segoe UI");
+ families.push("Segoe UI Symbol");
+ families.push("Cambria");
+ families.push("Meiryo");
+ families.push("Lucida Sans Unicode");
+ families.push("Ebrima");
+ },
+
+ UnicodeBlock::GeorgianSupplement |
+ UnicodeBlock::Tifinagh |
+ UnicodeBlock::CyrillicExtendedA |
+ UnicodeBlock::SupplementalPunctuation |
+ UnicodeBlock::CJKRadicalsSupplement |
+ UnicodeBlock::KangxiRadicals |
+ UnicodeBlock::IdeographicDescriptionCharacters => {
+ families.push("Segoe UI");
+ families.push("Segoe UI Symbol");
+ families.push("Meiryo");
+ },
+
+ UnicodeBlock::BraillePatterns => {
+ families.push("Segoe UI Symbol");
+ },
+
+ UnicodeBlock::CJKSymbolsandPunctuation |
+ UnicodeBlock::Hiragana |
+ UnicodeBlock::Katakana |
+ UnicodeBlock::Bopomofo |
+ UnicodeBlock::Kanbun |
+ UnicodeBlock::BopomofoExtended |
+ UnicodeBlock::CJKStrokes |
+ UnicodeBlock::KatakanaPhoneticExtensions |
+ UnicodeBlock::CJKUnifiedIdeographs => {
+ families.push("Microsoft YaHei");
+ families.push("Yu Gothic");
+ },
+
+ UnicodeBlock::EnclosedCJKLettersandMonths => {
+ families.push("Malgun Gothic");
+ },
+
+ UnicodeBlock::YijingHexagramSymbols => {
+ families.push("Segoe UI Symbol");
+ },
+
+ UnicodeBlock::YiSyllables | UnicodeBlock::YiRadicals => {
+ families.push("Microsoft Yi Baiti");
+ families.push("Segoe UI");
+ },
+
+ UnicodeBlock::Vai |
+ UnicodeBlock::CyrillicExtendedB |
+ UnicodeBlock::Bamum |
+ UnicodeBlock::ModifierToneLetters |
+ UnicodeBlock::LatinExtendedD => {
+ families.push("Ebrima");
+ families.push("Segoe UI");
+ families.push("Cambria Math");
+ },
+
+ UnicodeBlock::SylotiNagri |
+ UnicodeBlock::CommonIndicNumberForms |
+ UnicodeBlock::Phagspa |
+ UnicodeBlock::Saurashtra |
+ UnicodeBlock::DevanagariExtended => {
+ families.push("Microsoft PhagsPa");
+ families.push("Nirmala UI");
+ },
+
+ UnicodeBlock::KayahLi | UnicodeBlock::Rejang | UnicodeBlock::Javanese => {
+ families.push("Malgun Gothic");
+ families.push("Javanese Text");
+ families.push("Leelawadee UI");
+ },
+
+ UnicodeBlock::AlphabeticPresentationForms => {
+ families.push("Microsoft Uighur");
+ families.push("Gabriola");
+ families.push("Sylfaen");
+ },
+
+ UnicodeBlock::ArabicPresentationFormsA |
+ UnicodeBlock::ArabicPresentationFormsB => {
+ families.push("Traditional Arabic");
+ families.push("Arabic Typesetting");
+ },
+
+ UnicodeBlock::VariationSelectors |
+ UnicodeBlock::VerticalForms |
+ UnicodeBlock::CombiningHalfMarks |
+ UnicodeBlock::CJKCompatibilityForms |
+ UnicodeBlock::SmallFormVariants |
+ UnicodeBlock::HalfwidthandFullwidthForms |
+ UnicodeBlock::Specials => {
+ families.push("Microsoft JhengHei");
+ },
+
+ _ => {},
+ }
+ }
+ },
+
+ // https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Multilingual_Plane
+ 1 => {
+ families.push("Segoe UI Symbol");
+ families.push("Ebrima");
+ families.push("Nirmala UI");
+ families.push("Cambria Math");
+ },
+
+ _ => {},
+ }
+
+ families.push("Arial Unicode MS");
+ families
+}
+
+impl From<&Font> for FontTemplateDescriptor {
+ fn from(font: &Font) -> Self {
+ let style = match font.style() {
+ FontStyle::Normal => StyleFontStyle::NORMAL,
+ FontStyle::Oblique => StyleFontStyle::OBLIQUE,
+ FontStyle::Italic => StyleFontStyle::ITALIC,
+ };
+ let weight = StyleFontWeight::from_float(font.weight().to_u32() as f32);
+ let stretch = match font.stretch() {
+ FontStretch::Undefined => FontStretchKeyword::Normal,
+ FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed,
+ FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed,
+ FontStretch::Condensed => FontStretchKeyword::Condensed,
+ FontStretch::SemiCondensed => FontStretchKeyword::SemiCondensed,
+ FontStretch::Normal => FontStretchKeyword::Normal,
+ FontStretch::SemiExpanded => FontStretchKeyword::SemiExpanded,
+ FontStretch::Expanded => FontStretchKeyword::Expanded,
+ FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded,
+ FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded,
+ }
+ .compute();
+ FontTemplateDescriptor::new(weight, stretch, style)
+ }
+}