font_kit/loaders/
directwrite.rs

1// font-kit/src/loaders/directwrite.rs
2//
3// Copyright © 2018 The Pathfinder Project Developers.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! A loader that uses the Windows DirectWrite API to load and rasterize fonts.
12
13use byteorder::{BigEndian, ReadBytesExt};
14use dwrote::CustomFontCollectionLoaderImpl;
15use dwrote::Font as DWriteFont;
16use dwrote::FontCollection as DWriteFontCollection;
17use dwrote::FontFace as DWriteFontFace;
18use dwrote::FontFallback as DWriteFontFallback;
19use dwrote::FontFile as DWriteFontFile;
20use dwrote::FontMetrics as DWriteFontMetrics;
21use dwrote::FontStyle as DWriteFontStyle;
22use dwrote::GlyphOffset as DWriteGlyphOffset;
23use dwrote::GlyphRunAnalysis as DWriteGlyphRunAnalysis;
24use dwrote::InformationalStringId as DWriteInformationalStringId;
25use dwrote::OutlineBuilder as DWriteOutlineBuilder;
26use dwrote::{DWRITE_TEXTURE_ALIASED_1x1, DWRITE_TEXTURE_CLEARTYPE_3x1};
27use dwrote::{DWRITE_GLYPH_RUN, DWRITE_MEASURING_MODE_NATURAL};
28use dwrote::{DWRITE_RENDERING_MODE_ALIASED, DWRITE_RENDERING_MODE_NATURAL};
29use pathfinder_geometry::line_segment::LineSegment2F;
30use pathfinder_geometry::rect::{RectF, RectI};
31use pathfinder_geometry::transform2d::Transform2F;
32use pathfinder_geometry::vector::{Vector2F, Vector2I};
33use std::borrow::Cow;
34use std::ffi::OsString;
35use std::fmt::{self, Debug, Formatter};
36use std::fs::File;
37use std::io::{self, Read, Seek, SeekFrom};
38use std::os::windows::ffi::OsStringExt;
39use std::os::windows::io::AsRawHandle;
40use std::path::{Path, PathBuf};
41use std::sync::{Arc, Mutex};
42use winapi::shared::minwindef::{FALSE, MAX_PATH};
43use winapi::um::dwrite::DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
44use winapi::um::dwrite::DWRITE_READING_DIRECTION;
45use winapi::um::dwrite::DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
46use winapi::um::fileapi;
47
48use crate::canvas::{Canvas, Format, RasterizationOptions};
49use crate::error::{FontLoadingError, GlyphLoadingError};
50use crate::file_type::FileType;
51use crate::handle::Handle;
52use crate::hinting::HintingOptions;
53use crate::loader::{FallbackFont, FallbackResult, Loader};
54use crate::metrics::Metrics;
55use crate::outline::{OutlineBuilder, OutlineSink};
56use crate::properties::{Properties, Stretch, Style, Weight};
57
58const ERROR_BOUND: f32 = 0.0001;
59
60const OPENTYPE_TABLE_TAG_HEAD: u32 = 0x68656164;
61
62/// DirectWrite's representation of a font.
63#[allow(missing_debug_implementations)]
64pub struct NativeFont {
65    /// The native DirectWrite font object.
66    pub dwrite_font: DWriteFont,
67    /// The native DirectWrite font face object.
68    pub dwrite_font_face: DWriteFontFace,
69}
70
71/// A loader that uses the Windows DirectWrite API to load and rasterize fonts.
72pub struct Font {
73    dwrite_font: DWriteFont,
74    dwrite_font_face: DWriteFontFace,
75    cached_data: Mutex<Option<Arc<Vec<u8>>>>,
76}
77
78struct MyTextAnalysisSource {
79    text_utf16_len: u32,
80    locale: String,
81}
82
83impl dwrote::TextAnalysisSourceMethods for MyTextAnalysisSource {
84    fn get_locale_name<'a>(&'a self, text_pos: u32) -> (Cow<'a, str>, u32) {
85        (self.locale.as_str().into(), self.text_utf16_len - text_pos)
86    }
87
88    fn get_paragraph_reading_direction(&self) -> DWRITE_READING_DIRECTION {
89        DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
90    }
91}
92
93impl Font {
94    fn from_dwrite_font_file(
95        font_file: DWriteFontFile,
96        mut font_index: u32,
97        font_data: Option<Arc<Vec<u8>>>,
98    ) -> Result<Font, FontLoadingError> {
99        let collection_loader = CustomFontCollectionLoaderImpl::new(&[font_file.clone()]);
100        let collection = DWriteFontCollection::from_loader(collection_loader);
101        let families = collection.families_iter();
102        for family in families {
103            for family_font_index in 0..family.get_font_count() {
104                if font_index > 0 {
105                    font_index -= 1;
106                    continue;
107                }
108                let Ok(dwrite_font) = family.font(family_font_index) else {
109                    continue;
110                };
111                let dwrite_font_face = dwrite_font.create_font_face();
112                return Ok(Font {
113                    dwrite_font,
114                    dwrite_font_face,
115                    cached_data: Mutex::new(font_data),
116                });
117            }
118        }
119        Err(FontLoadingError::NoSuchFontInCollection)
120    }
121
122    /// Loads a font from raw font data (the contents of a `.ttf`/`.otf`/etc. file).
123    ///
124    /// If the data represents a collection (`.ttc`/`.otc`/etc.), `font_index` specifies the index
125    /// of the font to load from it. If the data represents a single font, pass 0 for `font_index`.
126    pub fn from_bytes(font_data: Arc<Vec<u8>>, font_index: u32) -> Result<Font, FontLoadingError> {
127        let font_file =
128            DWriteFontFile::new_from_data(font_data.clone()).ok_or(FontLoadingError::Parse)?;
129        Font::from_dwrite_font_file(font_file, font_index, Some(font_data))
130    }
131
132    /// Loads a font from a `.ttf`/`.otf`/etc. file.
133    ///
134    /// If the file is a collection (`.ttc`/`.otc`/etc.), `font_index` specifies the index of the
135    /// font to load from it. If the file represents a single font, pass 0 for `font_index`.
136    pub fn from_file(file: &mut File, font_index: u32) -> Result<Font, FontLoadingError> {
137        unsafe {
138            let mut path = vec![0; MAX_PATH + 1];
139            let path_len = fileapi::GetFinalPathNameByHandleW(
140                file.as_raw_handle(),
141                path.as_mut_ptr(),
142                path.len() as u32 - 1,
143                0,
144            );
145            if path_len == 0 {
146                return Err(FontLoadingError::Io(io::Error::last_os_error()));
147            }
148            path.truncate(path_len as usize);
149            Font::from_path(PathBuf::from(OsString::from_wide(&path)), font_index)
150        }
151    }
152
153    /// Loads a font from the path to a `.ttf`/`.otf`/etc. file.
154    ///
155    /// If the file is a collection (`.ttc`/`.otc`/etc.), `font_index` specifies the index of the
156    /// font to load from it. If the file represents a single font, pass 0 for `font_index`.
157    #[inline]
158    pub fn from_path<P: AsRef<Path>>(path: P, font_index: u32) -> Result<Font, FontLoadingError> {
159        let font_file = DWriteFontFile::new_from_path(path).ok_or(FontLoadingError::Parse)?;
160        Font::from_dwrite_font_file(font_file, font_index, None)
161    }
162
163    /// Creates a font from a native API handle.
164    #[inline]
165    pub unsafe fn from_native_font(native_font: NativeFont) -> Font {
166        Font {
167            dwrite_font: native_font.dwrite_font,
168            dwrite_font_face: native_font.dwrite_font_face,
169            cached_data: Mutex::new(None),
170        }
171    }
172
173    /// Loads the font pointed to by a handle.
174    #[inline]
175    pub fn from_handle(handle: &Handle) -> Result<Self, FontLoadingError> {
176        <Self as Loader>::from_handle(handle)
177    }
178
179    /// Determines whether a blob of raw font data represents a supported font, and, if so, what
180    /// type of font it is.
181    pub fn analyze_bytes(font_data: Arc<Vec<u8>>) -> Result<FileType, FontLoadingError> {
182        match DWriteFontFile::analyze_data(font_data) {
183            0 => Err(FontLoadingError::Parse),
184            1 => Ok(FileType::Single),
185            font_count => Ok(FileType::Collection(font_count)),
186        }
187    }
188
189    /// Determines whether a file represents a supported font, and, if so, what type of font it is.
190    pub fn analyze_file(file: &mut File) -> Result<FileType, FontLoadingError> {
191        let mut font_data = vec![];
192        file.seek(SeekFrom::Start(0))
193            .map_err(FontLoadingError::Io)?;
194        match file.read_to_end(&mut font_data) {
195            Err(io_error) => Err(FontLoadingError::Io(io_error)),
196            Ok(_) => Font::analyze_bytes(Arc::new(font_data)),
197        }
198    }
199
200    /// Returns the wrapped native font handle.
201    pub fn native_font(&self) -> NativeFont {
202        NativeFont {
203            dwrite_font: self.dwrite_font.clone(),
204            dwrite_font_face: self.dwrite_font_face.clone(),
205        }
206    }
207
208    /// Determines whether a path points to a supported font, and, if so, what type of font it is.
209    #[inline]
210    pub fn analyze_path<P: AsRef<Path>>(path: P) -> Result<FileType, FontLoadingError> {
211        <Self as Loader>::analyze_path(path)
212    }
213
214    /// Returns the PostScript name of the font. This should be globally unique.
215    #[inline]
216    pub fn postscript_name(&self) -> Option<String> {
217        let dwrite_font = &self.dwrite_font;
218        dwrite_font.informational_string(DWriteInformationalStringId::PostscriptName)
219    }
220
221    /// Returns the full name of the font (also known as "display name" on macOS).
222    #[inline]
223    pub fn full_name(&self) -> String {
224        let dwrite_font = &self.dwrite_font;
225        dwrite_font
226            .informational_string(DWriteInformationalStringId::FullName)
227            .unwrap_or_else(|| dwrite_font.family_name())
228    }
229
230    /// Returns the name of the font family.
231    #[inline]
232    pub fn family_name(&self) -> String {
233        self.dwrite_font.family_name()
234    }
235
236    /// Returns true if and only if the font is monospace (fixed-width).
237    #[inline]
238    pub fn is_monospace(&self) -> bool {
239        self.dwrite_font.is_monospace().unwrap_or(false)
240    }
241
242    /// Returns the values of various font properties, corresponding to those defined in CSS.
243    pub fn properties(&self) -> Properties {
244        let dwrite_font = &self.dwrite_font;
245        Properties {
246            style: style_for_dwrite_style(dwrite_font.style()),
247            stretch: Stretch(Stretch::MAPPING[(dwrite_font.stretch() as usize) - 1]),
248            weight: Weight(dwrite_font.weight().to_u32() as f32),
249        }
250    }
251
252    /// Returns the usual glyph ID for a Unicode character.
253    ///
254    /// Be careful with this function; typographically correct character-to-glyph mapping must be
255    /// done using a *shaper* such as HarfBuzz. This function is only useful for best-effort simple
256    /// use cases like "what does character X look like on its own".
257    pub fn glyph_for_char(&self, character: char) -> Option<u32> {
258        let chars = [character as u32];
259        self.dwrite_font_face
260            .get_glyph_indices(&chars)
261            .into_iter()
262            .next()
263            .and_then(|g| {
264                // 0 means the char is not present in the font per
265                // https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritefontface-getglyphindices
266                if g != 0 {
267                    Some(g as u32)
268                } else {
269                    None
270                }
271            })
272    }
273
274    /// Returns the number of glyphs in the font.
275    ///
276    /// Glyph IDs range from 0 inclusive to this value exclusive.
277    #[inline]
278    pub fn glyph_count(&self) -> u32 {
279        self.dwrite_font_face.get_glyph_count() as u32
280    }
281
282    /// Sends the vector path for a glyph to a path builder.
283    ///
284    /// If `hinting_mode` is not None, this function performs grid-fitting as requested before
285    /// sending the hinding outlines to the builder.
286    ///
287    /// TODO(pcwalton): What should we do for bitmap glyphs?
288    pub fn outline<S>(
289        &self,
290        glyph_id: u32,
291        _: HintingOptions,
292        sink: &mut S,
293    ) -> Result<(), GlyphLoadingError>
294    where
295        S: OutlineSink,
296    {
297        let outline_sink = OutlineCanonicalizer::new();
298        self.dwrite_font_face.get_glyph_run_outline(
299            self.metrics().units_per_em as f32,
300            &[glyph_id as u16],
301            None,
302            None,
303            false,
304            false,
305            Box::new(outline_sink.clone()),
306        );
307        outline_sink
308            .0
309            .lock()
310            .unwrap()
311            .builder
312            .take_outline()
313            .copy_to(&mut *sink);
314        Ok(())
315    }
316
317    /// Returns the boundaries of a glyph in font units.
318    pub fn typographic_bounds(&self, glyph_id: u32) -> Result<RectF, GlyphLoadingError> {
319        let metrics = self
320            .dwrite_font_face
321            .get_design_glyph_metrics(&[glyph_id as u16], false);
322
323        let metrics = &metrics[0];
324        let advance_width = metrics.advanceWidth as i32;
325        let advance_height = metrics.advanceHeight as i32;
326        let left_side_bearing = metrics.leftSideBearing as i32;
327        let right_side_bearing = metrics.rightSideBearing as i32;
328        let top_side_bearing = metrics.topSideBearing as i32;
329        let bottom_side_bearing = metrics.bottomSideBearing as i32;
330        let vertical_origin_y = metrics.verticalOriginY as i32;
331
332        let y_offset = vertical_origin_y + bottom_side_bearing - advance_height;
333        let width = advance_width - (left_side_bearing + right_side_bearing);
334        let height = advance_height - (top_side_bearing + bottom_side_bearing);
335
336        Ok(RectI::new(
337            Vector2I::new(left_side_bearing, y_offset),
338            Vector2I::new(width, height),
339        )
340        .to_f32())
341    }
342
343    /// Returns the distance from the origin of the glyph with the given ID to the next, in font
344    /// units.
345    pub fn advance(&self, glyph_id: u32) -> Result<Vector2F, GlyphLoadingError> {
346        let metrics = self
347            .dwrite_font_face
348            .get_design_glyph_metrics(&[glyph_id as u16], false);
349        let metrics = &metrics[0];
350        Ok(Vector2F::new(metrics.advanceWidth as f32, 0.0))
351    }
352
353    /// Returns the amount that the given glyph should be displaced from the origin.
354    pub fn origin(&self, glyph: u32) -> Result<Vector2F, GlyphLoadingError> {
355        let metrics = self
356            .dwrite_font_face
357            .get_design_glyph_metrics(&[glyph as u16], false);
358        Ok(Vector2I::new(
359            metrics[0].leftSideBearing,
360            metrics[0].verticalOriginY + metrics[0].bottomSideBearing,
361        )
362        .to_f32())
363    }
364
365    /// Retrieves various metrics that apply to the entire font.
366    pub fn metrics(&self) -> Metrics {
367        let dwrite_font = &self.dwrite_font;
368
369        // Unfortunately, the bounding box info is Windows 8 only, so we need a fallback. First,
370        // try to grab it from the font. If that fails, we try the `head` table. If there's no
371        // `head` table, we give up.
372        match dwrite_font.metrics() {
373            DWriteFontMetrics::Metrics1(metrics) => Metrics {
374                units_per_em: metrics.designUnitsPerEm as u32,
375                ascent: metrics.ascent as f32,
376                descent: -(metrics.descent as f32),
377                line_gap: metrics.lineGap as f32,
378                cap_height: metrics.capHeight as f32,
379                x_height: metrics.xHeight as f32,
380                underline_position: metrics.underlinePosition as f32,
381                underline_thickness: metrics.underlineThickness as f32,
382                bounding_box: RectI::new(
383                    Vector2I::new(metrics.glyphBoxLeft as i32, metrics.glyphBoxBottom as i32),
384                    Vector2I::new(
385                        metrics.glyphBoxRight as i32 - metrics.glyphBoxLeft as i32,
386                        metrics.glyphBoxTop as i32 - metrics.glyphBoxBottom as i32,
387                    ),
388                )
389                .to_f32(),
390            },
391            DWriteFontMetrics::Metrics0(metrics) => {
392                let bounding_box = match self
393                    .dwrite_font_face
394                    .get_font_table(OPENTYPE_TABLE_TAG_HEAD.swap_bytes())
395                {
396                    Some(head) => {
397                        let mut reader = &head[36..];
398                        let x_min = reader.read_i16::<BigEndian>().unwrap();
399                        let y_min = reader.read_i16::<BigEndian>().unwrap();
400                        let x_max = reader.read_i16::<BigEndian>().unwrap();
401                        let y_max = reader.read_i16::<BigEndian>().unwrap();
402                        RectI::new(
403                            Vector2I::new(x_min as i32, y_min as i32),
404                            Vector2I::new(x_max as i32 - x_min as i32, y_max as i32 - y_min as i32),
405                        )
406                        .to_f32()
407                    }
408                    None => RectF::default(),
409                };
410                Metrics {
411                    units_per_em: metrics.designUnitsPerEm as u32,
412                    ascent: metrics.ascent as f32,
413                    descent: -(metrics.descent as f32),
414                    line_gap: metrics.lineGap as f32,
415                    cap_height: metrics.capHeight as f32,
416                    x_height: metrics.xHeight as f32,
417                    underline_position: metrics.underlinePosition as f32,
418                    underline_thickness: metrics.underlineThickness as f32,
419                    bounding_box,
420                }
421            }
422        }
423    }
424
425    /// Returns a handle to this font, if possible.
426    ///
427    /// This is useful if you want to open the font with a different loader.
428    #[inline]
429    pub fn handle(&self) -> Option<Handle> {
430        <Self as Loader>::handle(self)
431    }
432
433    /// Attempts to return the raw font data (contents of the font file).
434    ///
435    /// If this font is a member of a collection, this function returns the data for the entire
436    /// collection.
437    pub fn copy_font_data(&self) -> Option<Arc<Vec<u8>>> {
438        let mut font_data = self.cached_data.lock().unwrap();
439        if font_data.is_none() {
440            let files = self.dwrite_font_face.get_files();
441            // FIXME(pcwalton): Is this right? When can a font have multiple files?
442            if let Some(file) = files.get(0) {
443                *font_data = Some(Arc::new(file.get_font_file_bytes()))
444            }
445        }
446        (*font_data).clone()
447    }
448
449    /// Returns the pixel boundaries that the glyph will take up when rendered using this loader's
450    /// rasterizer at the given size and origin.
451    #[inline]
452    pub fn raster_bounds(
453        &self,
454        glyph_id: u32,
455        point_size: f32,
456        transform: Transform2F,
457        hinting_options: HintingOptions,
458        rasterization_options: RasterizationOptions,
459    ) -> Result<RectI, GlyphLoadingError> {
460        let dwrite_analysis = self.build_glyph_analysis(
461            glyph_id,
462            point_size,
463            transform,
464            hinting_options,
465            rasterization_options,
466        )?;
467
468        let texture_type = match rasterization_options {
469            RasterizationOptions::Bilevel => DWRITE_TEXTURE_ALIASED_1x1,
470            RasterizationOptions::GrayscaleAa | RasterizationOptions::SubpixelAa => {
471                DWRITE_TEXTURE_CLEARTYPE_3x1
472            }
473        };
474
475        let texture_bounds = dwrite_analysis.get_alpha_texture_bounds(texture_type)?;
476        let texture_width = texture_bounds.right - texture_bounds.left;
477        let texture_height = texture_bounds.bottom - texture_bounds.top;
478
479        Ok(RectI::new(
480            Vector2I::new(texture_bounds.left, texture_bounds.top),
481            Vector2I::new(texture_width, texture_height),
482        ))
483    }
484
485    /// Rasterizes a glyph to a canvas with the given size and origin.
486    ///
487    /// Format conversion will be performed if the canvas format does not match the rasterization
488    /// options. For example, if bilevel (black and white) rendering is requested to an RGBA
489    /// surface, this function will automatically convert the 1-bit raster image to the 32-bit
490    /// format of the canvas. Note that this may result in a performance penalty, depending on the
491    /// loader.
492    ///
493    /// If `hinting_options` is not None, the requested grid fitting is performed.
494    pub fn rasterize_glyph(
495        &self,
496        canvas: &mut Canvas,
497        glyph_id: u32,
498        point_size: f32,
499        transform: Transform2F,
500        hinting_options: HintingOptions,
501        rasterization_options: RasterizationOptions,
502    ) -> Result<(), GlyphLoadingError> {
503        // TODO(pcwalton): This is woefully incomplete. See WebRender's code for a more complete
504        // implementation.
505
506        let dwrite_analysis = self.build_glyph_analysis(
507            glyph_id,
508            point_size,
509            transform,
510            hinting_options,
511            rasterization_options,
512        )?;
513
514        let texture_type = match rasterization_options {
515            RasterizationOptions::Bilevel => DWRITE_TEXTURE_ALIASED_1x1,
516            RasterizationOptions::GrayscaleAa | RasterizationOptions::SubpixelAa => {
517                DWRITE_TEXTURE_CLEARTYPE_3x1
518            }
519        };
520
521        // TODO(pcwalton): Avoid a copy in some cases by writing directly to the canvas.
522        let texture_bounds = dwrite_analysis.get_alpha_texture_bounds(texture_type)?;
523        let texture_width = texture_bounds.right - texture_bounds.left;
524        let texture_height = texture_bounds.bottom - texture_bounds.top;
525
526        // 'Returns an empty rectangle if there are no glyphs of the specified texture type.'
527        // https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwriteglyphrunanalysis-getalphatexturebounds
528        if texture_width == 0 || texture_height == 0 {
529            return Ok(());
530        }
531
532        let texture_format = if texture_type == DWRITE_TEXTURE_ALIASED_1x1 {
533            Format::A8
534        } else {
535            Format::Rgb24
536        };
537        let texture_bits_per_pixel = texture_format.bits_per_pixel();
538        let texture_bytes_per_pixel = texture_bits_per_pixel as usize / 8;
539        let texture_size = Vector2I::new(texture_width, texture_height);
540        let texture_stride = texture_width as usize * texture_bytes_per_pixel;
541
542        let mut texture_bytes =
543            dwrite_analysis.create_alpha_texture(texture_type, texture_bounds)?;
544        canvas.blit_from(
545            Vector2I::new(texture_bounds.left, texture_bounds.top),
546            &mut texture_bytes,
547            texture_size,
548            texture_stride,
549            texture_format,
550        );
551
552        Ok(())
553    }
554
555    /// Returns true if and only if the font loader can perform hinting in the requested way.
556    ///
557    /// Some APIs support only rasterizing glyphs with hinting, not retrieving hinted outlines. If
558    /// `for_rasterization` is false, this function returns true if and only if the loader supports
559    /// retrieval of hinted *outlines*. If `for_rasterization` is true, this function returns true
560    /// if and only if the loader supports *rasterizing* hinted glyphs.
561    pub fn supports_hinting_options(
562        &self,
563        hinting_options: HintingOptions,
564        for_rasterization: bool,
565    ) -> bool {
566        match (hinting_options, for_rasterization) {
567            (HintingOptions::None, _)
568            | (HintingOptions::Vertical(_), true)
569            | (HintingOptions::VerticalSubpixel(_), true) => true,
570            (HintingOptions::Vertical(_), false)
571            | (HintingOptions::VerticalSubpixel(_), false)
572            | (HintingOptions::Full(_), _) => false,
573        }
574    }
575
576    fn build_glyph_analysis(
577        &self,
578        glyph_id: u32,
579        point_size: f32,
580        transform: Transform2F,
581        _hinting_options: HintingOptions,
582        rasterization_options: RasterizationOptions,
583    ) -> Result<DWriteGlyphRunAnalysis, GlyphLoadingError> {
584        unsafe {
585            let glyph_id = glyph_id as u16;
586            let advance = 0.0;
587            let offset = DWriteGlyphOffset {
588                advanceOffset: 0.0,
589                ascenderOffset: 0.0,
590            };
591            let glyph_run = DWRITE_GLYPH_RUN {
592                fontFace: self.dwrite_font_face.as_ptr(),
593                fontEmSize: point_size,
594                glyphCount: 1,
595                glyphIndices: &glyph_id,
596                glyphAdvances: &advance,
597                glyphOffsets: &offset,
598                isSideways: FALSE,
599                bidiLevel: 0,
600            };
601
602            let rendering_mode = match rasterization_options {
603                RasterizationOptions::Bilevel => DWRITE_RENDERING_MODE_ALIASED,
604                RasterizationOptions::GrayscaleAa | RasterizationOptions::SubpixelAa => {
605                    DWRITE_RENDERING_MODE_NATURAL
606                }
607            };
608
609            Ok(DWriteGlyphRunAnalysis::create(
610                &glyph_run,
611                1.0,
612                Some(dwrote::DWRITE_MATRIX {
613                    m11: transform.m11(),
614                    m12: transform.m12(),
615                    m21: transform.m21(),
616                    m22: transform.m22(),
617                    dx: transform.vector.x(),
618                    dy: transform.vector.y(),
619                }),
620                rendering_mode,
621                DWRITE_MEASURING_MODE_NATURAL,
622                0.0,
623                0.0,
624            )?)
625        }
626    }
627
628    /// Get font fallback results for the given text and locale.
629    ///
630    /// The `locale` argument is a language tag such as `"en-US"` or `"zh-Hans-CN"`.
631    ///
632    /// Note: on Windows 10, the result is a single font.
633    fn get_fallbacks(&self, text: &str, locale: &str) -> FallbackResult<Font> {
634        let sys_fallback = DWriteFontFallback::get_system_fallback();
635        if sys_fallback.is_none() {
636            unimplemented!("Need Windows 7 method for font fallbacks")
637        }
638        let text_utf16: Vec<u16> = text.encode_utf16().collect();
639        let text_utf16_len = text_utf16.len() as u32;
640        let number_subst =
641            dwrote::NumberSubstitution::new(DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, locale, true);
642        let text_analysis_source = MyTextAnalysisSource {
643            text_utf16_len,
644            locale: locale.to_owned(),
645        };
646        let text_analysis = dwrote::TextAnalysisSource::from_text_and_number_subst(
647            Box::new(text_analysis_source),
648            text_utf16.into(),
649            number_subst,
650        );
651        let sys_fallback = sys_fallback.unwrap();
652        // TODO: I think the MapCharacters can take a null pointer, update
653        // dwrote to accept an optional collection. This appears to be what
654        // blink does.
655        let collection = DWriteFontCollection::get_system(false);
656        let fallback_result = sys_fallback.map_characters(
657            &text_analysis,
658            0,
659            text_utf16_len,
660            &collection,
661            Some(&self.dwrite_font.family_name()),
662            self.dwrite_font.weight(),
663            self.dwrite_font.style(),
664            self.dwrite_font.stretch(),
665        );
666        let valid_len = convert_len_utf16_to_utf8(text, fallback_result.mapped_length);
667        let fonts = if let Some(dwrite_font) = fallback_result.mapped_font {
668            let dwrite_font_face = dwrite_font.create_font_face();
669            let font = Font {
670                dwrite_font,
671                dwrite_font_face,
672                cached_data: Mutex::new(None),
673            };
674            let fallback_font = FallbackFont {
675                font,
676                scale: fallback_result.scale,
677            };
678            vec![fallback_font]
679        } else {
680            vec![]
681        };
682        FallbackResult { fonts, valid_len }
683    }
684
685    /// Returns the raw contents of the OpenType table with the given tag.
686    ///
687    /// Tags are four-character codes. A list of tags can be found in the [OpenType specification].
688    ///
689    /// [OpenType specification]: https://docs.microsoft.com/en-us/typography/opentype/spec/
690    pub fn load_font_table(&self, table_tag: u32) -> Option<Box<[u8]>> {
691        self.dwrite_font_face
692            .get_font_table(table_tag.swap_bytes())
693            .map(|v| v.into())
694    }
695}
696
697// There might well be a more efficient impl that doesn't fully decode the text,
698// just looks at the utf-8 bytes.
699fn convert_len_utf16_to_utf8(text: &str, len_utf16: usize) -> usize {
700    let mut l_utf8 = 0;
701    let mut l_utf16 = 0;
702    let mut chars = text.chars();
703    while l_utf16 < len_utf16 {
704        if let Some(c) = chars.next() {
705            l_utf8 += c.len_utf8();
706            l_utf16 += c.len_utf16();
707        } else {
708            break;
709        }
710    }
711    l_utf8
712}
713
714impl Clone for Font {
715    #[inline]
716    fn clone(&self) -> Font {
717        Font {
718            dwrite_font: self.dwrite_font.clone(),
719            dwrite_font_face: self.dwrite_font_face.clone(),
720            cached_data: Mutex::new((*self.cached_data.lock().unwrap()).clone()),
721        }
722    }
723}
724
725impl Debug for Font {
726    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
727        self.family_name().fmt(fmt)
728    }
729}
730
731impl Loader for Font {
732    type NativeFont = NativeFont;
733
734    #[inline]
735    fn from_bytes(font_data: Arc<Vec<u8>>, font_index: u32) -> Result<Self, FontLoadingError> {
736        Font::from_bytes(font_data, font_index)
737    }
738
739    #[inline]
740    fn from_file(file: &mut File, font_index: u32) -> Result<Font, FontLoadingError> {
741        Font::from_file(file, font_index)
742    }
743
744    fn from_path<P>(path: P, font_index: u32) -> Result<Self, FontLoadingError>
745    where
746        P: AsRef<Path>,
747    {
748        Font::from_path(path, font_index)
749    }
750
751    #[inline]
752    unsafe fn from_native_font(native_font: Self::NativeFont) -> Self {
753        Font::from_native_font(native_font)
754    }
755
756    #[inline]
757    fn analyze_bytes(font_data: Arc<Vec<u8>>) -> Result<FileType, FontLoadingError> {
758        Font::analyze_bytes(font_data)
759    }
760
761    #[inline]
762    fn analyze_file(file: &mut File) -> Result<FileType, FontLoadingError> {
763        Font::analyze_file(file)
764    }
765
766    #[inline]
767    fn native_font(&self) -> Self::NativeFont {
768        self.native_font()
769    }
770
771    #[inline]
772    fn postscript_name(&self) -> Option<String> {
773        self.postscript_name()
774    }
775
776    #[inline]
777    fn full_name(&self) -> String {
778        self.full_name()
779    }
780
781    #[inline]
782    fn family_name(&self) -> String {
783        self.family_name()
784    }
785
786    #[inline]
787    fn is_monospace(&self) -> bool {
788        self.is_monospace()
789    }
790
791    #[inline]
792    fn properties(&self) -> Properties {
793        self.properties()
794    }
795
796    #[inline]
797    fn glyph_for_char(&self, character: char) -> Option<u32> {
798        self.glyph_for_char(character)
799    }
800
801    #[inline]
802    fn glyph_count(&self) -> u32 {
803        self.glyph_count()
804    }
805
806    #[inline]
807    fn outline<S>(
808        &self,
809        glyph_id: u32,
810        hinting: HintingOptions,
811        sink: &mut S,
812    ) -> Result<(), GlyphLoadingError>
813    where
814        S: OutlineSink,
815    {
816        self.outline(glyph_id, hinting, sink)
817    }
818
819    #[inline]
820    fn typographic_bounds(&self, glyph_id: u32) -> Result<RectF, GlyphLoadingError> {
821        self.typographic_bounds(glyph_id)
822    }
823
824    #[inline]
825    fn advance(&self, glyph_id: u32) -> Result<Vector2F, GlyphLoadingError> {
826        self.advance(glyph_id)
827    }
828
829    #[inline]
830    fn origin(&self, origin: u32) -> Result<Vector2F, GlyphLoadingError> {
831        self.origin(origin)
832    }
833
834    #[inline]
835    fn metrics(&self) -> Metrics {
836        self.metrics()
837    }
838
839    #[inline]
840    fn supports_hinting_options(
841        &self,
842        hinting_options: HintingOptions,
843        for_rasterization: bool,
844    ) -> bool {
845        self.supports_hinting_options(hinting_options, for_rasterization)
846    }
847
848    #[inline]
849    fn copy_font_data(&self) -> Option<Arc<Vec<u8>>> {
850        self.copy_font_data()
851    }
852
853    #[inline]
854    fn rasterize_glyph(
855        &self,
856        canvas: &mut Canvas,
857        glyph_id: u32,
858        point_size: f32,
859        transform: Transform2F,
860        hinting_options: HintingOptions,
861        rasterization_options: RasterizationOptions,
862    ) -> Result<(), GlyphLoadingError> {
863        self.rasterize_glyph(
864            canvas,
865            glyph_id,
866            point_size,
867            transform,
868            hinting_options,
869            rasterization_options,
870        )
871    }
872
873    #[inline]
874    fn get_fallbacks(&self, text: &str, locale: &str) -> FallbackResult<Self> {
875        self.get_fallbacks(text, locale)
876    }
877
878    #[inline]
879    fn load_font_table(&self, table_tag: u32) -> Option<Box<[u8]>> {
880        self.load_font_table(table_tag)
881    }
882}
883
884#[derive(Clone)]
885struct OutlineCanonicalizer(Arc<Mutex<OutlineCanonicalizerInfo>>);
886
887struct OutlineCanonicalizerInfo {
888    builder: OutlineBuilder,
889    last_position: Vector2F,
890}
891
892impl OutlineCanonicalizer {
893    fn new() -> OutlineCanonicalizer {
894        OutlineCanonicalizer(Arc::new(Mutex::new(OutlineCanonicalizerInfo {
895            builder: OutlineBuilder::new(),
896            last_position: Vector2F::default(),
897        })))
898    }
899}
900
901impl DWriteOutlineBuilder for OutlineCanonicalizer {
902    fn move_to(&mut self, to_x: f32, to_y: f32) {
903        let to = Vector2F::new(to_x, -to_y);
904
905        let mut this = self.0.lock().unwrap();
906        this.last_position = to;
907        this.builder.move_to(to);
908    }
909
910    fn line_to(&mut self, to_x: f32, to_y: f32) {
911        let to = Vector2F::new(to_x, -to_y);
912
913        let mut this = self.0.lock().unwrap();
914        this.last_position = to;
915        this.builder.line_to(to);
916    }
917
918    fn close(&mut self) {
919        let mut this = self.0.lock().unwrap();
920        this.builder.close();
921    }
922
923    fn curve_to(
924        &mut self,
925        ctrl0_x: f32,
926        ctrl0_y: f32,
927        ctrl1_x: f32,
928        ctrl1_y: f32,
929        to_x: f32,
930        to_y: f32,
931    ) {
932        let ctrl = LineSegment2F::new(
933            Vector2F::new(ctrl0_x, -ctrl0_y),
934            Vector2F::new(ctrl1_x, -ctrl1_y),
935        );
936        let to = Vector2F::new(to_x, -to_y);
937
938        // This might be a degree-elevated quadratic curve. Try to detect that.
939        // See Sederberg § 2.6, "Distance Between Two Bézier Curves".
940        let mut this = self.0.lock().unwrap();
941        let baseline = LineSegment2F::new(this.last_position, to);
942        let approx_ctrl = LineSegment2F((ctrl * 3.0).0 - baseline.0) * 0.5;
943        let delta_ctrl = (approx_ctrl.to() - approx_ctrl.from()) * 2.0;
944        let max_error = delta_ctrl.length() / 6.0;
945
946        if max_error < ERROR_BOUND {
947            // Round to nearest 0.5.
948            let approx_ctrl = (approx_ctrl.midpoint() * 2.0).round() * 0.5;
949            this.builder.quadratic_curve_to(approx_ctrl, to);
950        } else {
951            this.builder.cubic_curve_to(ctrl, to);
952        }
953
954        this.last_position = to;
955    }
956}
957
958fn style_for_dwrite_style(style: DWriteFontStyle) -> Style {
959    match style {
960        DWriteFontStyle::Normal => Style::Normal,
961        DWriteFontStyle::Oblique => Style::Oblique,
962        DWriteFontStyle::Italic => Style::Italic,
963    }
964}