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