gfx: Clamp the font size we supply to Core Text to 0.01pt.

Core Text treats a font size of 0.0 as 12.0, which is obviously not what
we want.

Improves Twitter.
Improves Reddit /r/rust.

Closes #10492.
This commit is contained in:
Patrick Walton 2016-04-08 17:37:22 -07:00
parent b214205ba9
commit 18fbf8cf30
6 changed files with 70 additions and 17 deletions

View file

@ -38,7 +38,7 @@ fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFo
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont { fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont {
let cgfont = template.ctfont().as_ref().unwrap().copy_to_CGFont(); let cgfont = template.ctfont(pt_size.to_f64_px()).as_ref().unwrap().copy_to_CGFont();
ScaledFont::new(BackendType::Skia, &cgfont, pt_size.to_f32_px()) ScaledFont::new(BackendType::Skia, &cgfont, pt_size.to_f32_px())
} }

View file

@ -72,7 +72,7 @@ impl FontHandleMethods for FontHandle {
Some(s) => s.to_f64_px(), Some(s) => s.to_f64_px(),
None => 0.0 None => 0.0
}; };
match template.ctfont() { match template.ctfont(size) {
Some(ref ctfont) => { Some(ref ctfont) => {
Ok(FontHandle { Ok(FontHandle {
font_data: template.clone(), font_data: template.clone(),

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::Au;
use core_graphics::data_provider::CGDataProvider; use core_graphics::data_provider::CGDataProvider;
use core_graphics::font::CGFont; use core_graphics::font::CGFont;
use core_text; use core_text;
@ -9,6 +10,7 @@ use core_text::font::CTFont;
use serde::de::{Error, Visitor}; use serde::de::{Error, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::collections::HashMap;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::ops::Deref; use std::ops::Deref;
@ -26,7 +28,7 @@ pub struct FontTemplateData {
/// `CTFont` instances over and over. It can always be recreated from the `identifier` and/or /// `CTFont` instances over and over. It can always be recreated from the `identifier` and/or
/// `font_data` fields. /// `font_data` fields.
/// ///
/// When sending a `FontTemplateData` instance across processes, this will be set to `None` on /// When sending a `FontTemplateData` instance across processes, this will be cleared out on
/// the other side, because `CTFont` instances cannot be sent across processes. This is /// the other side, because `CTFont` instances cannot be sent across processes. This is
/// harmless, however, because it can always be recreated. /// harmless, however, because it can always be recreated.
ctfont: CachedCTFont, ctfont: CachedCTFont,
@ -41,29 +43,38 @@ unsafe impl Sync for FontTemplateData {}
impl FontTemplateData { impl FontTemplateData {
pub fn new(identifier: Atom, font_data: Option<Vec<u8>>) -> FontTemplateData { pub fn new(identifier: Atom, font_data: Option<Vec<u8>>) -> FontTemplateData {
FontTemplateData { FontTemplateData {
ctfont: CachedCTFont(Mutex::new(None)), ctfont: CachedCTFont(Mutex::new(HashMap::new())),
identifier: identifier.to_owned(), identifier: identifier.to_owned(),
font_data: font_data font_data: font_data
} }
} }
/// Retrieves the Core Text font instance, instantiating it if necessary. /// Retrieves the Core Text font instance, instantiating it if necessary.
pub fn ctfont(&self) -> Option<CTFont> { pub fn ctfont(&self, pt_size: f64) -> Option<CTFont> {
let mut ctfont = self.ctfont.lock().unwrap(); let mut ctfonts = self.ctfont.lock().unwrap();
if ctfont.is_none() { let pt_size_key = Au::from_f64_px(pt_size);
*ctfont = match self.font_data { if !ctfonts.contains_key(&pt_size_key) {
// If you pass a zero font size to one of the Core Text APIs, it'll replace it with
// 12.0. We don't want that! (Issue #10492.)
let clamped_pt_size = pt_size.max(0.01);
let ctfont = match self.font_data {
Some(ref bytes) => { Some(ref bytes) => {
let fontprov = CGDataProvider::from_buffer(bytes); let fontprov = CGDataProvider::from_buffer(bytes);
let cgfont_result = CGFont::from_data_provider(fontprov); let cgfont_result = CGFont::from_data_provider(fontprov);
match cgfont_result { match cgfont_result {
Ok(cgfont) => Some(core_text::font::new_from_CGFont(&cgfont, 0.0)), Ok(cgfont) => {
Some(core_text::font::new_from_CGFont(&cgfont, clamped_pt_size))
}
Err(_) => None Err(_) => None
} }
} }
None => core_text::font::new_from_name(&*self.identifier, 0.0).ok(), None => core_text::font::new_from_name(&*self.identifier, clamped_pt_size).ok(),
};
if let Some(ctfont) = ctfont {
ctfonts.insert(pt_size_key, ctfont);
} }
} }
ctfont.as_ref().map(|ctfont| (*ctfont).clone()) ctfonts.get(&pt_size_key).map(|ctfont| (*ctfont).clone())
} }
/// Returns a clone of the data in this font. This may be a hugely expensive /// Returns a clone of the data in this font. This may be a hugely expensive
@ -75,7 +86,7 @@ impl FontTemplateData {
None => {} None => {}
} }
let path = Url::parse(&*self.ctfont() let path = Url::parse(&*self.ctfont(0.0)
.expect("No Core Text font available!") .expect("No Core Text font available!")
.url() .url()
.expect("No URL for Core Text font!") .expect("No URL for Core Text font!")
@ -96,16 +107,16 @@ impl FontTemplateData {
/// Returns the native font that underlies this font template, if applicable. /// Returns the native font that underlies this font template, if applicable.
pub fn native_font(&self) -> Option<CGFont> { pub fn native_font(&self) -> Option<CGFont> {
self.ctfont().map(|ctfont| ctfont.copy_to_CGFont()) self.ctfont(0.0).map(|ctfont| ctfont.copy_to_CGFont())
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct CachedCTFont(Mutex<Option<CTFont>>); pub struct CachedCTFont(Mutex<HashMap<Au, CTFont>>);
impl Deref for CachedCTFont { impl Deref for CachedCTFont {
type Target = Mutex<Option<CTFont>>; type Target = Mutex<HashMap<Au, CTFont>>;
fn deref(&self) -> &Mutex<Option<CTFont>> { fn deref(&self) -> &Mutex<HashMap<Au, CTFont>> {
&self.0 &self.0
} }
} }
@ -126,7 +137,7 @@ impl Deserialize for CachedCTFont {
#[inline] #[inline]
fn visit_none<E>(&mut self) -> Result<CachedCTFont, E> where E: Error { fn visit_none<E>(&mut self) -> Result<CachedCTFont, E> where E: Error {
Ok(CachedCTFont(Mutex::new(None))) Ok(CachedCTFont(Mutex::new(HashMap::new())))
} }
} }

View file

@ -2423,6 +2423,18 @@
"url": "/_mozilla/css/inline_element_border_a.html" "url": "/_mozilla/css/inline_element_border_a.html"
} }
], ],
"css/inline_font_size_zero_a.html": [
{
"path": "css/inline_font_size_zero_a.html",
"references": [
[
"/_mozilla/css/inline_font_size_zero_ref.html",
"=="
]
],
"url": "/_mozilla/css/inline_font_size_zero_a.html"
}
],
"css/inline_hypothetical_box_a.html": [ "css/inline_hypothetical_box_a.html": [
{ {
"path": "css/inline_hypothetical_box_a.html", "path": "css/inline_hypothetical_box_a.html",
@ -8923,6 +8935,18 @@
"url": "/_mozilla/css/inline_element_border_a.html" "url": "/_mozilla/css/inline_element_border_a.html"
} }
], ],
"css/inline_font_size_zero_a.html": [
{
"path": "css/inline_font_size_zero_a.html",
"references": [
[
"/_mozilla/css/inline_font_size_zero_ref.html",
"=="
]
],
"url": "/_mozilla/css/inline_font_size_zero_a.html"
}
],
"css/inline_hypothetical_box_a.html": [ "css/inline_hypothetical_box_a.html": [
{ {
"path": "css/inline_hypothetical_box_a.html", "path": "css/inline_hypothetical_box_a.html",

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title></title>
<link rel="match" href="inline_font_size_zero_ref.html">
<div style="width: 600px; font-size: 0;">
<div style="display: inline-block; width: 200px; background: blue; height: 50px;"></div>
<div style="display: inline-block; width: 400px; background: green; height: 50px;"></div>
</div>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title></title>
<div style="width: 600px;">
<div style="display: inline-block; width: 200px; background: blue; height: 50px;"></div><!--
--><div style="display: inline-block; width: 400px; background: green; height: 50px;"></div>
</div>