text_system.rs

  1use crate::{
  2    point, px, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontStyle,
  3    FontWeight, GlyphId, GlyphRasterizationParams, Pixels, PlatformTextSystem, Point, Result,
  4    ShapedGlyph, ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS,
  5};
  6use anyhow::anyhow;
  7use cocoa::appkit::{CGFloat, CGPoint};
  8use collections::HashMap;
  9use core_foundation::{
 10    array::CFIndex,
 11    attributed_string::{CFAttributedStringRef, CFMutableAttributedString},
 12    base::{CFRange, TCFType},
 13    string::CFString,
 14};
 15use core_graphics::{base::CGGlyph, color_space::CGColorSpace, context::CGContext};
 16use core_text::{font::CTFont, line::CTLine, string_attributes::kCTFontAttributeName};
 17use font_kit::{
 18    font::Font as FontKitFont,
 19    handle::Handle,
 20    hinting::HintingOptions,
 21    metrics::Metrics,
 22    properties::{Style as FontkitStyle, Weight as FontkitWeight},
 23    source::SystemSource,
 24    sources::mem::MemSource,
 25};
 26use parking_lot::{RwLock, RwLockUpgradableReadGuard};
 27use pathfinder_geometry::{
 28    rect::{RectF, RectI},
 29    transform2d::Transform2F,
 30    vector::{Vector2F, Vector2I},
 31};
 32use smallvec::SmallVec;
 33use std::{char, cmp, convert::TryFrom, ffi::c_void, sync::Arc};
 34
 35use super::open_type;
 36
 37#[allow(non_upper_case_globals)]
 38const kCGImageAlphaOnly: u32 = 7;
 39
 40pub struct MacTextSystem(RwLock<MacTextSystemState>);
 41
 42struct MacTextSystemState {
 43    memory_source: MemSource,
 44    system_source: SystemSource,
 45    fonts: Vec<FontKitFont>,
 46    font_selections: HashMap<Font, FontId>,
 47    font_ids_by_postscript_name: HashMap<String, FontId>,
 48    font_ids_by_family_name: HashMap<SharedString, SmallVec<[FontId; 4]>>,
 49    postscript_names_by_font_id: HashMap<FontId, String>,
 50}
 51
 52impl MacTextSystem {
 53    pub fn new() -> Self {
 54        Self(RwLock::new(MacTextSystemState {
 55            memory_source: MemSource::empty(),
 56            system_source: SystemSource::new(),
 57            fonts: Vec::new(),
 58            font_selections: HashMap::default(),
 59            font_ids_by_postscript_name: HashMap::default(),
 60            font_ids_by_family_name: HashMap::default(),
 61            postscript_names_by_font_id: HashMap::default(),
 62        }))
 63    }
 64}
 65
 66impl Default for MacTextSystem {
 67    fn default() -> Self {
 68        Self::new()
 69    }
 70}
 71
 72impl PlatformTextSystem for MacTextSystem {
 73    fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> Result<()> {
 74        self.0.write().add_fonts(fonts)
 75    }
 76
 77    fn all_font_families(&self) -> Vec<String> {
 78        self.0
 79            .read()
 80            .system_source
 81            .all_families()
 82            .expect("core text should never return an error")
 83    }
 84
 85    fn font_id(&self, font: &Font) -> Result<FontId> {
 86        let lock = self.0.upgradable_read();
 87        if let Some(font_id) = lock.font_selections.get(font) {
 88            Ok(*font_id)
 89        } else {
 90            let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
 91            let candidates = if let Some(font_ids) = lock.font_ids_by_family_name.get(&font.family)
 92            {
 93                font_ids.as_slice()
 94            } else {
 95                let font_ids = lock.load_family(&font.family, font.features)?;
 96                lock.font_ids_by_family_name
 97                    .insert(font.family.clone(), font_ids);
 98                lock.font_ids_by_family_name[&font.family].as_ref()
 99            };
100
101            let candidate_properties = candidates
102                .iter()
103                .map(|font_id| lock.fonts[font_id.0].properties())
104                .collect::<SmallVec<[_; 4]>>();
105
106            let ix = font_kit::matching::find_best_match(
107                &candidate_properties,
108                &font_kit::properties::Properties {
109                    style: font.style.into(),
110                    weight: font.weight.into(),
111                    stretch: Default::default(),
112                },
113            )?;
114
115            Ok(candidates[ix])
116        }
117    }
118
119    fn font_metrics(&self, font_id: FontId) -> FontMetrics {
120        self.0.read().fonts[font_id.0].metrics().into()
121    }
122
123    fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
124        Ok(self.0.read().fonts[font_id.0]
125            .typographic_bounds(glyph_id.into())?
126            .into())
127    }
128
129    fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
130        self.0.read().advance(font_id, glyph_id)
131    }
132
133    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
134        self.0.read().glyph_for_char(font_id, ch)
135    }
136
137    fn glyph_raster_bounds(
138        &self,
139        params: &GlyphRasterizationParams,
140    ) -> Result<Bounds<DevicePixels>> {
141        self.0.read().raster_bounds(params)
142    }
143
144    fn rasterize_glyph(
145        &self,
146        glyph_id: &GlyphRasterizationParams,
147    ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
148        self.0.read().rasterize_glyph(glyph_id)
149    }
150
151    fn layout_line(
152        &self,
153        text: &str,
154        font_size: Pixels,
155        font_runs: &[(usize, FontId)],
156    ) -> ShapedLine {
157        self.0.write().layout_line(text, font_size, font_runs)
158    }
159
160    fn wrap_line(
161        &self,
162        text: &str,
163        font_id: FontId,
164        font_size: Pixels,
165        width: Pixels,
166    ) -> Vec<usize> {
167        self.0.read().wrap_line(text, font_id, font_size, width)
168    }
169}
170
171impl MacTextSystemState {
172    fn add_fonts(&mut self, fonts: &[Arc<Vec<u8>>]) -> Result<()> {
173        self.memory_source.add_fonts(
174            fonts
175                .iter()
176                .map(|bytes| Handle::from_memory(bytes.clone(), 0)),
177        )?;
178        Ok(())
179    }
180
181    fn load_family(
182        &mut self,
183        name: &SharedString,
184        features: FontFeatures,
185    ) -> Result<SmallVec<[FontId; 4]>> {
186        let mut font_ids = SmallVec::new();
187        let family = self
188            .memory_source
189            .select_family_by_name(name.as_ref())
190            .or_else(|_| self.system_source.select_family_by_name(name.as_ref()))?;
191        for font in family.fonts() {
192            let mut font = font.load()?;
193            open_type::apply_features(&mut font, features);
194            let font_id = FontId(self.fonts.len());
195            font_ids.push(font_id);
196            let postscript_name = font.postscript_name().unwrap();
197            self.font_ids_by_postscript_name
198                .insert(postscript_name.clone(), font_id);
199            self.postscript_names_by_font_id
200                .insert(font_id, postscript_name);
201            self.fonts.push(font);
202        }
203        Ok(font_ids)
204    }
205
206    fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
207        Ok(self.fonts[font_id.0].advance(glyph_id.into())?.into())
208    }
209
210    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
211        self.fonts[font_id.0].glyph_for_char(ch).map(Into::into)
212    }
213
214    fn id_for_native_font(&mut self, requested_font: CTFont) -> FontId {
215        let postscript_name = requested_font.postscript_name();
216        if let Some(font_id) = self.font_ids_by_postscript_name.get(&postscript_name) {
217            *font_id
218        } else {
219            let font_id = FontId(self.fonts.len());
220            self.font_ids_by_postscript_name
221                .insert(postscript_name.clone(), font_id);
222            self.postscript_names_by_font_id
223                .insert(font_id, postscript_name);
224            self.fonts
225                .push(font_kit::font::Font::from_core_graphics_font(
226                    requested_font.copy_to_CGFont(),
227                ));
228            font_id
229        }
230    }
231
232    fn is_emoji(&self, font_id: FontId) -> bool {
233        self.postscript_names_by_font_id
234            .get(&font_id)
235            .map_or(false, |postscript_name| {
236                postscript_name == "AppleColorEmoji"
237            })
238    }
239
240    fn raster_bounds(&self, params: &GlyphRasterizationParams) -> Result<Bounds<DevicePixels>> {
241        let font = &self.fonts[params.font_id.0];
242        let scale = Transform2F::from_scale(params.scale_factor);
243        Ok(font
244            .raster_bounds(
245                params.glyph_id.into(),
246                params.font_size.into(),
247                scale,
248                HintingOptions::None,
249                font_kit::canvas::RasterizationOptions::GrayscaleAa,
250            )?
251            .into())
252    }
253
254    fn rasterize_glyph(
255        &self,
256        params: &GlyphRasterizationParams,
257    ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
258        let glyph_bounds = self.raster_bounds(params)?;
259
260        // let scale = Transform2F::from_scale(params.scale_factor);
261        // let glyph_bounds = font.raster_bounds(
262        //     params.glyph_id.into(),
263        //     params.font_size.into(),
264        //     scale,
265        //     HintingOptions::None,
266        //     font_kit::canvas::RasterizationOptions::GrayscaleAa,
267        // )?;
268
269        if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 {
270            Err(anyhow!("glyph bounds are empty"))
271        } else {
272            // Add an extra pixel when the subpixel variant isn't zero to make room for anti-aliasing.
273            let mut bitmap_size = glyph_bounds.size;
274            if params.subpixel_variant.x > 0 {
275                bitmap_size.width += DevicePixels(1);
276            }
277            if params.subpixel_variant.y > 0 {
278                bitmap_size.height += DevicePixels(1);
279            }
280
281            let mut bytes = vec![0; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize];
282            let cx = CGContext::create_bitmap_context(
283                Some(bytes.as_mut_ptr() as *mut _),
284                bitmap_size.width.0 as usize,
285                bitmap_size.height.0 as usize,
286                8,
287                bitmap_size.width.0 as usize,
288                &CGColorSpace::create_device_gray(),
289                kCGImageAlphaOnly,
290            );
291
292            // Move the origin to bottom left and account for scaling, this
293            // makes drawing text consistent with the font-kit's raster_bounds.
294            cx.translate(
295                -glyph_bounds.origin.x.0 as CGFloat,
296                (glyph_bounds.origin.y.0 + glyph_bounds.size.height.0) as CGFloat,
297            );
298            cx.scale(
299                params.scale_factor as CGFloat,
300                params.scale_factor as CGFloat,
301            );
302
303            let subpixel_shift = params
304                .subpixel_variant
305                .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32 / params.scale_factor);
306
307            cx.set_allows_font_subpixel_positioning(true);
308            cx.set_should_subpixel_position_fonts(true);
309            cx.set_allows_font_subpixel_quantization(false);
310            cx.set_should_subpixel_quantize_fonts(false);
311            self.fonts[params.font_id.0]
312                .native_font()
313                .clone_with_font_size(f32::from(params.font_size) as CGFloat)
314                .draw_glyphs(
315                    &[u32::from(params.glyph_id) as CGGlyph],
316                    &[CGPoint::new(
317                        subpixel_shift.x as CGFloat,
318                        subpixel_shift.y as CGFloat,
319                    )],
320                    cx,
321                );
322
323            Ok((bitmap_size.into(), bytes))
324        }
325    }
326
327    fn layout_line(
328        &mut self,
329        text: &str,
330        font_size: Pixels,
331        font_runs: &[(usize, FontId)],
332    ) -> ShapedLine {
333        // Construct the attributed string, converting UTF8 ranges to UTF16 ranges.
334        let mut string = CFMutableAttributedString::new();
335        {
336            string.replace_str(&CFString::new(text), CFRange::init(0, 0));
337            let utf16_line_len = string.char_len() as usize;
338
339            let mut ix_converter = StringIndexConverter::new(text);
340            for (run_len, font_id) in font_runs {
341                let utf8_end = ix_converter.utf8_ix + run_len;
342                let utf16_start = ix_converter.utf16_ix;
343
344                if utf16_start >= utf16_line_len {
345                    break;
346                }
347
348                ix_converter.advance_to_utf8_ix(utf8_end);
349                let utf16_end = cmp::min(ix_converter.utf16_ix, utf16_line_len);
350
351                let cf_range =
352                    CFRange::init(utf16_start as isize, (utf16_end - utf16_start) as isize);
353
354                let font: &FontKitFont = &self.fonts[font_id.0];
355                unsafe {
356                    string.set_attribute(
357                        cf_range,
358                        kCTFontAttributeName,
359                        &font.native_font().clone_with_font_size(font_size.into()),
360                    );
361                }
362
363                if utf16_end == utf16_line_len {
364                    break;
365                }
366            }
367        }
368
369        // Retrieve the glyphs from the shaped line, converting UTF16 offsets to UTF8 offsets.
370        let line = CTLine::new_with_attributed_string(string.as_concrete_TypeRef());
371
372        let mut runs = Vec::new();
373        for run in line.glyph_runs().into_iter() {
374            let attributes = run.attributes().unwrap();
375            let font = unsafe {
376                attributes
377                    .get(kCTFontAttributeName)
378                    .downcast::<CTFont>()
379                    .unwrap()
380            };
381            let font_id = self.id_for_native_font(font);
382
383            let mut ix_converter = StringIndexConverter::new(text);
384            let mut glyphs = Vec::new();
385            for ((glyph_id, position), glyph_utf16_ix) in run
386                .glyphs()
387                .iter()
388                .zip(run.positions().iter())
389                .zip(run.string_indices().iter())
390            {
391                let glyph_utf16_ix = usize::try_from(*glyph_utf16_ix).unwrap();
392                ix_converter.advance_to_utf16_ix(glyph_utf16_ix);
393                glyphs.push(ShapedGlyph {
394                    id: (*glyph_id).into(),
395                    position: point(position.x as f32, position.y as f32).map(px),
396                    index: ix_converter.utf8_ix,
397                    is_emoji: self.is_emoji(font_id),
398                });
399            }
400
401            runs.push(ShapedRun { font_id, glyphs })
402        }
403
404        let typographic_bounds = line.get_typographic_bounds();
405        ShapedLine {
406            width: typographic_bounds.width.into(),
407            ascent: typographic_bounds.ascent.into(),
408            descent: typographic_bounds.descent.into(),
409            runs,
410            font_size,
411            len: text.len(),
412        }
413    }
414
415    fn wrap_line(
416        &self,
417        text: &str,
418        font_id: FontId,
419        font_size: Pixels,
420        width: Pixels,
421    ) -> Vec<usize> {
422        let mut string = CFMutableAttributedString::new();
423        string.replace_str(&CFString::new(text), CFRange::init(0, 0));
424        let cf_range = CFRange::init(0, text.encode_utf16().count() as isize);
425        let font = &self.fonts[font_id.0];
426        unsafe {
427            string.set_attribute(
428                cf_range,
429                kCTFontAttributeName,
430                &font.native_font().clone_with_font_size(font_size.into()),
431            );
432
433            let typesetter = CTTypesetterCreateWithAttributedString(string.as_concrete_TypeRef());
434            let mut ix_converter = StringIndexConverter::new(text);
435            let mut break_indices = Vec::new();
436            while ix_converter.utf8_ix < text.len() {
437                let utf16_len = CTTypesetterSuggestLineBreak(
438                    typesetter,
439                    ix_converter.utf16_ix as isize,
440                    width.into(),
441                ) as usize;
442                ix_converter.advance_to_utf16_ix(ix_converter.utf16_ix + utf16_len);
443                if ix_converter.utf8_ix >= text.len() {
444                    break;
445                }
446                break_indices.push(ix_converter.utf8_ix as usize);
447            }
448            break_indices
449        }
450    }
451}
452
453#[derive(Clone)]
454struct StringIndexConverter<'a> {
455    text: &'a str,
456    utf8_ix: usize,
457    utf16_ix: usize,
458}
459
460impl<'a> StringIndexConverter<'a> {
461    fn new(text: &'a str) -> Self {
462        Self {
463            text,
464            utf8_ix: 0,
465            utf16_ix: 0,
466        }
467    }
468
469    fn advance_to_utf8_ix(&mut self, utf8_target: usize) {
470        for (ix, c) in self.text[self.utf8_ix..].char_indices() {
471            if self.utf8_ix + ix >= utf8_target {
472                self.utf8_ix += ix;
473                return;
474            }
475            self.utf16_ix += c.len_utf16();
476        }
477        self.utf8_ix = self.text.len();
478    }
479
480    fn advance_to_utf16_ix(&mut self, utf16_target: usize) {
481        for (ix, c) in self.text[self.utf8_ix..].char_indices() {
482            if self.utf16_ix >= utf16_target {
483                self.utf8_ix += ix;
484                return;
485            }
486            self.utf16_ix += c.len_utf16();
487        }
488        self.utf8_ix = self.text.len();
489    }
490}
491
492#[repr(C)]
493pub struct __CFTypesetter(c_void);
494
495pub type CTTypesetterRef = *const __CFTypesetter;
496
497#[link(name = "CoreText", kind = "framework")]
498extern "C" {
499    fn CTTypesetterCreateWithAttributedString(string: CFAttributedStringRef) -> CTTypesetterRef;
500
501    fn CTTypesetterSuggestLineBreak(
502        typesetter: CTTypesetterRef,
503        start_index: CFIndex,
504        width: f64,
505    ) -> CFIndex;
506}
507
508impl From<Metrics> for FontMetrics {
509    fn from(metrics: Metrics) -> Self {
510        FontMetrics {
511            units_per_em: metrics.units_per_em,
512            ascent: metrics.ascent,
513            descent: metrics.descent,
514            line_gap: metrics.line_gap,
515            underline_position: metrics.underline_position,
516            underline_thickness: metrics.underline_thickness,
517            cap_height: metrics.cap_height,
518            x_height: metrics.x_height,
519            bounding_box: metrics.bounding_box.into(),
520        }
521    }
522}
523
524impl From<RectF> for Bounds<f32> {
525    fn from(rect: RectF) -> Self {
526        Bounds {
527            origin: point(rect.origin_x(), rect.origin_y()),
528            size: size(rect.width(), rect.height()),
529        }
530    }
531}
532
533impl From<RectI> for Bounds<DevicePixels> {
534    fn from(rect: RectI) -> Self {
535        Bounds {
536            origin: point(DevicePixels(rect.origin_x()), DevicePixels(rect.origin_y())),
537            size: size(DevicePixels(rect.width()), DevicePixels(rect.height())),
538        }
539    }
540}
541
542impl From<Vector2I> for Size<DevicePixels> {
543    fn from(value: Vector2I) -> Self {
544        size(value.x().into(), value.y().into())
545    }
546}
547
548impl From<RectI> for Bounds<i32> {
549    fn from(rect: RectI) -> Self {
550        Bounds {
551            origin: point(rect.origin_x(), rect.origin_y()),
552            size: size(rect.width(), rect.height()),
553        }
554    }
555}
556
557impl From<Point<u32>> for Vector2I {
558    fn from(size: Point<u32>) -> Self {
559        Vector2I::new(size.x as i32, size.y as i32)
560    }
561}
562
563impl From<Vector2F> for Size<f32> {
564    fn from(vec: Vector2F) -> Self {
565        size(vec.x(), vec.y())
566    }
567}
568
569impl From<FontWeight> for FontkitWeight {
570    fn from(value: FontWeight) -> Self {
571        FontkitWeight(value.0)
572    }
573}
574
575impl From<FontStyle> for FontkitStyle {
576    fn from(style: FontStyle) -> Self {
577        match style {
578            FontStyle::Normal => FontkitStyle::Normal,
579            FontStyle::Italic => FontkitStyle::Italic,
580            FontStyle::Oblique => FontkitStyle::Oblique,
581        }
582    }
583}
584
585// #[cfg(test)]
586// mod tests {
587//     use super::*;
588//     use crate::AppContext;
589//     use font_kit::properties::{Style, Weight};
590//     use platform::FontSystem as _;
591
592//     #[crate::test(self, retries = 5)]
593//     fn test_layout_str(_: &mut AppContext) {
594//         // This is failing intermittently on CI and we don't have time to figure it out
595//         let fonts = FontSystem::new();
596//         let menlo = fonts.load_family("Menlo", &Default::default()).unwrap();
597//         let menlo_regular = RunStyle {
598//             font_id: fonts.select_font(&menlo, &Properties::new()).unwrap(),
599//             color: Default::default(),
600//             underline: Default::default(),
601//         };
602//         let menlo_italic = RunStyle {
603//             font_id: fonts
604//                 .select_font(&menlo, Properties::new().style(Style::Italic))
605//                 .unwrap(),
606//             color: Default::default(),
607//             underline: Default::default(),
608//         };
609//         let menlo_bold = RunStyle {
610//             font_id: fonts
611//                 .select_font(&menlo, Properties::new().weight(Weight::BOLD))
612//                 .unwrap(),
613//             color: Default::default(),
614//             underline: Default::default(),
615//         };
616//         assert_ne!(menlo_regular, menlo_italic);
617//         assert_ne!(menlo_regular, menlo_bold);
618//         assert_ne!(menlo_italic, menlo_bold);
619
620//         let line = fonts.layout_line(
621//             "hello world",
622//             16.0,
623//             &[(2, menlo_bold), (4, menlo_italic), (5, menlo_regular)],
624//         );
625//         assert_eq!(line.runs.len(), 3);
626//         assert_eq!(line.runs[0].font_id, menlo_bold.font_id);
627//         assert_eq!(line.runs[0].glyphs.len(), 2);
628//         assert_eq!(line.runs[1].font_id, menlo_italic.font_id);
629//         assert_eq!(line.runs[1].glyphs.len(), 4);
630//         assert_eq!(line.runs[2].font_id, menlo_regular.font_id);
631//         assert_eq!(line.runs[2].glyphs.len(), 5);
632//     }
633
634//     #[test]
635//     fn test_glyph_offsets() -> crate::Result<()> {
636//         let fonts = FontSystem::new();
637//         let zapfino = fonts.load_family("Zapfino", &Default::default())?;
638//         let zapfino_regular = RunStyle {
639//             font_id: fonts.select_font(&zapfino, &Properties::new())?,
640//             color: Default::default(),
641//             underline: Default::default(),
642//         };
643//         let menlo = fonts.load_family("Menlo", &Default::default())?;
644//         let menlo_regular = RunStyle {
645//             font_id: fonts.select_font(&menlo, &Properties::new())?,
646//             color: Default::default(),
647//             underline: Default::default(),
648//         };
649
650//         let text = "This is, m𐍈re 𐍈r less, Zapfino!𐍈";
651//         let line = fonts.layout_line(
652//             text,
653//             16.0,
654//             &[
655//                 (9, zapfino_regular),
656//                 (13, menlo_regular),
657//                 (text.len() - 22, zapfino_regular),
658//             ],
659//         );
660//         assert_eq!(
661//             line.runs
662//                 .iter()
663//                 .flat_map(|r| r.glyphs.iter())
664//                 .map(|g| g.index)
665//                 .collect::<Vec<_>>(),
666//             vec![0, 2, 4, 5, 7, 8, 9, 10, 14, 15, 16, 17, 21, 22, 23, 24, 26, 27, 28, 29, 36, 37],
667//         );
668//         Ok(())
669//     }
670
671//     #[test]
672//     #[ignore]
673//     fn test_rasterize_glyph() {
674//         use std::{fs::File, io::BufWriter, path::Path};
675
676//         let fonts = FontSystem::new();
677//         let font_ids = fonts.load_family("Fira Code", &Default::default()).unwrap();
678//         let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap();
679//         let glyph_id = fonts.glyph_for_char(font_id, 'G').unwrap();
680
681//         const VARIANTS: usize = 1;
682//         for i in 0..VARIANTS {
683//             let variant = i as f32 / VARIANTS as f32;
684//             let (bounds, bytes) = fonts
685//                 .rasterize_glyph(
686//                     font_id,
687//                     16.0,
688//                     glyph_id,
689//                     vec2f(variant, variant),
690//                     2.,
691//                     RasterizationOptions::Alpha,
692//                 )
693//                 .unwrap();
694
695//             let name = format!("/Users/as-cii/Desktop/twog-{}.png", i);
696//             let path = Path::new(&name);
697//             let file = File::create(path).unwrap();
698//             let w = &mut BufWriter::new(file);
699
700//             let mut encoder = png::Encoder::new(w, bounds.width() as u32, bounds.height() as u32);
701//             encoder.set_color(png::ColorType::Grayscale);
702//             encoder.set_depth(png::BitDepth::Eight);
703//             let mut writer = encoder.write_header().unwrap();
704//             writer.write_image_data(&bytes).unwrap();
705//         }
706//     }
707
708//     #[test]
709//     fn test_wrap_line() {
710//         let fonts = FontSystem::new();
711//         let font_ids = fonts.load_family("Helvetica", &Default::default()).unwrap();
712//         let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap();
713
714//         let line = "one two three four five\n";
715//         let wrap_boundaries = fonts.wrap_line(line, font_id, 16., 64.0);
716//         assert_eq!(wrap_boundaries, &["one two ".len(), "one two three ".len()]);
717
718//         let line = "aaa Ξ±Ξ±Ξ± βœ‹βœ‹βœ‹ πŸŽ‰πŸŽ‰πŸŽ‰\n";
719//         let wrap_boundaries = fonts.wrap_line(line, font_id, 16., 64.0);
720//         assert_eq!(
721//             wrap_boundaries,
722//             &["aaa Ξ±Ξ±Ξ± ".len(), "aaa Ξ±Ξ±Ξ± βœ‹βœ‹βœ‹ ".len(),]
723//         );
724//     }
725
726//     #[test]
727//     fn test_layout_line_bom_char() {
728//         let fonts = FontSystem::new();
729//         let font_ids = fonts.load_family("Helvetica", &Default::default()).unwrap();
730//         let style = RunStyle {
731//             font_id: fonts.select_font(&font_ids, &Default::default()).unwrap(),
732//             color: Default::default(),
733//             underline: Default::default(),
734//         };
735
736//         let line = "\u{feff}";
737//         let layout = fonts.layout_line(line, 16., &[(line.len(), style)]);
738//         assert_eq!(layout.len, line.len());
739//         assert!(layout.runs.is_empty());
740
741//         let line = "a\u{feff}b";
742//         let layout = fonts.layout_line(line, 16., &[(line.len(), style)]);
743//         assert_eq!(layout.len, line.len());
744//         assert_eq!(layout.runs.len(), 1);
745//         assert_eq!(layout.runs[0].glyphs.len(), 2);
746//         assert_eq!(layout.runs[0].glyphs[0].id, 68); // a
747//                                                      // There's no glyph for \u{feff}
748//         assert_eq!(layout.runs[0].glyphs[1].id, 69); // b
749//     }
750// }