diff options
-rw-r--r-- | src/components/gfx/font_cache_task.rs | 70 | ||||
-rw-r--r-- | src/components/gfx/font_template.rs | 64 | ||||
-rw-r--r-- | src/components/gfx/gfx.rs | 1 | ||||
-rw-r--r-- | src/components/gfx/platform/android/font_template.rs | 15 | ||||
-rw-r--r-- | src/components/gfx/platform/linux/font_template.rs | 15 | ||||
-rw-r--r-- | src/components/gfx/platform/macos/font_template.rs | 18 | ||||
-rw-r--r-- | src/components/layout/layout_task.rs | 23 | ||||
-rw-r--r-- | src/components/main/servo.rs | 2 | ||||
-rw-r--r-- | src/components/style/font_face.rs | 112 | ||||
-rw-r--r-- | src/components/style/style.rs | 4 | ||||
-rw-r--r-- | src/components/style/stylesheets.rs | 6 |
11 files changed, 288 insertions, 42 deletions
diff --git a/src/components/gfx/font_cache_task.rs b/src/components/gfx/font_cache_task.rs index 55e2dad003e..1dd71f9f1f8 100644 --- a/src/components/gfx/font_cache_task.rs +++ b/src/components/gfx/font_cache_task.rs @@ -11,6 +11,8 @@ use std::collections::HashMap; use sync::Arc; use font_template::{FontTemplate, FontTemplateDescriptor}; use platform::font_template::FontTemplateData; +use servo_net::resource_task::{ResourceTask, load_whole_resource}; +use url::Url; /// A list of font templates that make up a given font family. struct FontFamily { @@ -42,11 +44,23 @@ impl FontFamily { None } + + fn add_template(&mut self, identifier: &str, maybe_data: Option<Vec<u8>>) { + for template in self.templates.iter() { + if template.identifier() == identifier { + return; + } + } + + let template = FontTemplate::new(identifier, maybe_data); + self.templates.push(template); + } } /// Commands that the FontContext sends to the font cache task. pub enum Command { GetFontTemplate(String, FontTemplateDescriptor, Sender<Reply>), + AddWebFont(Url, String, Sender<()>), Exit(Sender<()>), } @@ -61,7 +75,9 @@ struct FontCache { port: Receiver<Command>, generic_fonts: HashMap<String, String>, local_families: HashMap<String, FontFamily>, + web_families: HashMap<String, FontFamily>, font_context: FontContextHandle, + resource_task: ResourceTask, } impl FontCache { @@ -80,6 +96,23 @@ impl FontCache { result.send(GetFontTemplateReply(font_template)); } + AddWebFont(url, family_name, result) => { + let maybe_resource = load_whole_resource(&self.resource_task, url.clone()); + match maybe_resource { + Ok((_, bytes)) => { + if !self.web_families.contains_key(&family_name) { + let family = FontFamily::new(); + self.web_families.insert(family_name.clone(), family); + } + let family = self.web_families.get_mut(&family_name); + family.add_template(format!("{}", url).as_slice(), Some(bytes)); + }, + Err(msg) => { + fail!(msg); + } + } + result.send(()); + } Exit(result) => { result.send(()); break; @@ -105,9 +138,8 @@ impl FontCache { } } - fn find_font_in_family<'a>(&'a mut self, - family_name: &String, - desc: &FontTemplateDescriptor) -> Option<Arc<FontTemplateData>> { + fn find_font_in_local_family<'a>(&'a mut self, family_name: &String, desc: &FontTemplateDescriptor) + -> Option<Arc<FontTemplateData>> { // TODO(Issue #188): look up localized font family names if canonical name not found // look up canonical name if self.local_families.contains_key(family_name) { @@ -116,8 +148,7 @@ impl FontCache { if s.templates.len() == 0 { get_variations_for_family(family_name.as_slice(), |path| { - let template = FontTemplate::new(path.as_slice()); - s.templates.push(template); + s.add_template(path.as_slice(), None); }); } @@ -135,16 +166,31 @@ impl FontCache { } } + fn find_font_in_web_family<'a>(&'a mut self, family_name: &String, desc: &FontTemplateDescriptor) + -> Option<Arc<FontTemplateData>> { + if self.web_families.contains_key(family_name) { + let family = self.web_families.get_mut(family_name); + let maybe_font = family.find_font_for_style(desc, &self.font_context); + maybe_font + } else { + None + } + } + fn get_font_template(&mut self, family: &String, desc: &FontTemplateDescriptor) -> Option<Arc<FontTemplateData>> { let transformed_family_name = self.transform_family(family); - self.find_font_in_family(&transformed_family_name, desc) + let mut maybe_template = self.find_font_in_web_family(&transformed_family_name, desc); + if maybe_template.is_none() { + maybe_template = self.find_font_in_local_family(&transformed_family_name, desc); + } + maybe_template } fn get_last_resort_template(&mut self, desc: &FontTemplateDescriptor) -> Arc<FontTemplateData> { let last_resort = get_last_resort_font_families(); for family in last_resort.iter() { - let maybe_font_in_family = self.find_font_in_family(family, desc); + let maybe_font_in_family = self.find_font_in_local_family(family, desc); if maybe_font_in_family.is_some() { return maybe_font_in_family.unwrap(); } @@ -162,7 +208,7 @@ pub struct FontCacheTask { } impl FontCacheTask { - pub fn new() -> FontCacheTask { + pub fn new(resource_task: ResourceTask) -> FontCacheTask { let (chan, port) = channel(); spawn(proc() { @@ -178,7 +224,9 @@ impl FontCacheTask { port: port, generic_fonts: generic_fonts, local_families: HashMap::new(), + web_families: HashMap::new(), font_context: FontContextHandle::new(), + resource_task: resource_task, }; cache.refresh_local_families(); @@ -205,6 +253,12 @@ impl FontCacheTask { } } + pub fn add_web_font(&mut self, url: Url, family: &str) { + let (response_chan, response_port) = channel(); + self.chan.send(AddWebFont(url, family.to_string(), response_chan)); + response_port.recv(); + } + pub fn exit(&self) { let (response_chan, response_port) = channel(); self.chan.send(Exit(response_chan)); diff --git a/src/components/gfx/font_template.rs b/src/components/gfx/font_template.rs index 12753da68a1..c25a104a06b 100644 --- a/src/components/gfx/font_template.rs +++ b/src/components/gfx/font_template.rs @@ -42,21 +42,42 @@ impl PartialEq for FontTemplateDescriptor { pub struct FontTemplate { identifier: String, descriptor: Option<FontTemplateDescriptor>, - data: Option<Weak<FontTemplateData>>, + weak_ref: Option<Weak<FontTemplateData>>, + strong_ref: Option<Arc<FontTemplateData>>, // GWTODO: Add code path to unset the strong_ref for web fonts! } /// Holds all of the template information for a font that /// is common, regardless of the number of instances of /// this font handle per thread. impl FontTemplate { - pub fn new(identifier: &str) -> FontTemplate { + pub fn new(identifier: &str, maybe_bytes: Option<Vec<u8>>) -> FontTemplate { + let maybe_data = match maybe_bytes { + Some(_) => Some(FontTemplateData::new(identifier, maybe_bytes)), + None => None, + }; + + let maybe_strong_ref = match maybe_data { + Some(data) => Some(Arc::new(data)), + None => None, + }; + + let maybe_weak_ref = match maybe_strong_ref { + Some(ref strong_ref) => Some(strong_ref.downgrade()), + None => None, + }; + FontTemplate { identifier: identifier.to_string(), descriptor: None, - data: None, + weak_ref: maybe_weak_ref, + strong_ref: maybe_strong_ref, } } + pub fn identifier<'a>(&'a self) -> &'a str { + self.identifier.as_slice() + } + /// Get the data for creating a font if it matches a given descriptor. pub fn get_if_matches(&mut self, fctx: &FontContextHandle, requested_desc: &FontTemplateDescriptor) -> Option<Arc<FontTemplateData>> { @@ -75,20 +96,24 @@ impl FontTemplate { }, None => { let data = self.get_data(); - let handle = FontHandleMethods::new_from_template(fctx, data.clone(), None); - let handle: FontHandle = match handle { - Ok(handle) => handle, - Err(()) => fail!("TODO - Handle failure to create a font from template."), - }; - let actual_desc = FontTemplateDescriptor::new(handle.boldness(), - handle.is_italic()); - let desc_match = actual_desc == *requested_desc; + let handle: Result<FontHandle, ()> = FontHandleMethods::new_from_template(fctx, data.clone(), None); + match handle { + Ok(handle) => { + let actual_desc = FontTemplateDescriptor::new(handle.boldness(), + handle.is_italic()); + let desc_match = actual_desc == *requested_desc; - self.descriptor = Some(actual_desc); - if desc_match { - Some(data) - } else { - None + self.descriptor = Some(actual_desc); + if desc_match { + Some(data) + } else { + None + } + } + Err(()) => { + debug!("Unable to create a font from template {}", self.identifier); + None + } } } } @@ -98,7 +123,7 @@ impl FontTemplate { /// exist, it will return a clone, otherwise it will load the /// font data and store a weak reference to it internally. pub fn get_data(&mut self) -> Arc<FontTemplateData> { - let maybe_data = match self.data { + let maybe_data = match self.weak_ref { Some(ref data) => data.upgrade(), None => None, }; @@ -106,8 +131,9 @@ impl FontTemplate { match maybe_data { Some(data) => data, None => { - let template_data = Arc::new(FontTemplateData::new(self.identifier.as_slice())); - self.data = Some(template_data.downgrade()); + assert!(self.strong_ref.is_none()); + let template_data = Arc::new(FontTemplateData::new(self.identifier.as_slice(), None)); + self.weak_ref = Some(template_data.downgrade()); template_data } } diff --git a/src/components/gfx/gfx.rs b/src/components/gfx/gfx.rs index 4a7c4e217b2..d9510350d83 100644 --- a/src/components/gfx/gfx.rs +++ b/src/components/gfx/gfx.rs @@ -31,6 +31,7 @@ extern crate servo_util = "util"; extern crate servo_msg = "msg"; extern crate style; extern crate sync; +extern crate url; // Eventually we would like the shaper to be pluggable, as many operating systems have their own // shapers. For now, however, this is a hard dependency. diff --git a/src/components/gfx/platform/android/font_template.rs b/src/components/gfx/platform/android/font_template.rs index d1a821a0bf3..e8c058ee9ac 100644 --- a/src/components/gfx/platform/android/font_template.rs +++ b/src/components/gfx/platform/android/font_template.rs @@ -15,10 +15,17 @@ pub struct FontTemplateData { } impl FontTemplateData { - pub fn new(identifier: &str) -> FontTemplateData { - // TODO: Handle file load failure! - let mut file = File::open_mode(&Path::new(identifier), io::Open, io::Read).unwrap(); - let bytes = file.read_to_end().unwrap(); + pub fn new(identifier: &str, font_data: Option<Vec<u8>>) -> FontTemplateData { + let bytes = match font_data { + Some(bytes) => { + bytes + }, + None => { + // TODO: Handle file load failure! + let mut file = File::open_mode(&Path::new(identifier), io::Open, io::Read).unwrap(); + file.read_to_end().unwrap() + }, + }; FontTemplateData { bytes: bytes, diff --git a/src/components/gfx/platform/linux/font_template.rs b/src/components/gfx/platform/linux/font_template.rs index e00a1018317..663ea64ab29 100644 --- a/src/components/gfx/platform/linux/font_template.rs +++ b/src/components/gfx/platform/linux/font_template.rs @@ -15,10 +15,17 @@ pub struct FontTemplateData { } impl FontTemplateData { - pub fn new(identifier: &str) -> FontTemplateData { - // TODO: Handle file load failure! - let mut file = File::open_mode(&Path::new(identifier), io::Open, io::Read).unwrap(); - let bytes = file.read_to_end().unwrap(); + pub fn new(identifier: &str, font_data: Option<Vec<u8>>) -> FontTemplateData { + let bytes = match font_data { + Some(bytes) => { + bytes + }, + None => { + // TODO: Handle file load failure! + let mut file = File::open_mode(&Path::new(identifier), io::Open, io::Read).unwrap(); + file.read_to_end().unwrap() + }, + }; FontTemplateData { bytes: bytes, diff --git a/src/components/gfx/platform/macos/font_template.rs b/src/components/gfx/platform/macos/font_template.rs index bda41186da3..44d4e3f8acd 100644 --- a/src/components/gfx/platform/macos/font_template.rs +++ b/src/components/gfx/platform/macos/font_template.rs @@ -2,6 +2,8 @@ * 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/. */ +use core_graphics::data_provider::CGDataProvider; +use core_graphics::font::CGFont; use core_text::font::CTFont; use core_text; @@ -15,10 +17,20 @@ pub struct FontTemplateData { } impl FontTemplateData { - pub fn new(identifier: &str) -> FontTemplateData { - let ctfont_result = core_text::font::new_from_name(identifier.as_slice(), 0.0); + pub fn new(identifier: &str, font_data: Option<Vec<u8>>) -> FontTemplateData { + let ctfont = match font_data { + Some(bytes) => { + let fontprov = CGDataProvider::from_buffer(bytes.as_slice()); + let cgfont = CGFont::from_data_provider(fontprov); + core_text::font::new_from_CGFont(&cgfont, 0.0) + }, + None => { + core_text::font::new_from_name(identifier.as_slice(), 0.0).unwrap() + } + }; + FontTemplateData { - ctfont: ctfont_result.unwrap(), + ctfont: ctfont, identifier: identifier.to_string(), } } diff --git a/src/components/layout/layout_task.rs b/src/components/layout/layout_task.rs index bed0096ff69..eb7cfade084 100644 --- a/src/components/layout/layout_task.rs +++ b/src/components/layout/layout_task.rs @@ -60,6 +60,8 @@ use std::mem; use std::ptr; use std::task::TaskBuilder; use style::{AuthorOrigin, Stylesheet, Stylist}; +use style::CSSFontFaceRule; +use style::TtfFormat; use sync::{Arc, Mutex}; use url::Url; @@ -450,7 +452,26 @@ impl LayoutTask { } fn handle_add_stylesheet(&mut self, sheet: Stylesheet) { - self.stylist.add_stylesheet(sheet, AuthorOrigin) + // Find all font-face rules and notify the font cache of them. + // GWTODO: Need to handle unloading web fonts (when we handle unloading stylesheets!) + // GWTODO: Need to handle font-face nested within media rules. + for rule in sheet.rules.iter() { + match rule { + &CSSFontFaceRule(ref font_face_rule) => { + for source in font_face_rule.sources.iter() { + match source.format { + TtfFormat => { + self.font_cache_task.add_web_font(source.url.clone(), font_face_rule.family.as_slice()); + }, + _ => {} + } + } + }, + _ => {} + } + } + + self.stylist.add_stylesheet(sheet, AuthorOrigin); } /// Retrieves the flow tree root from the root node. diff --git a/src/components/main/servo.rs b/src/components/main/servo.rs index 944df69bd43..1227b80471c 100644 --- a/src/components/main/servo.rs +++ b/src/components/main/servo.rs @@ -124,7 +124,7 @@ pub fn run(opts: opts::Opts) { } else { ImageCacheTask::new(resource_task.clone()) }; - let font_cache_task = FontCacheTask::new(); + let font_cache_task = FontCacheTask::new(resource_task.clone()); let constellation_chan = Constellation::<layout::layout_task::LayoutTask>::start( compositor_chan, opts, diff --git a/src/components/style/font_face.rs b/src/components/style/font_face.rs new file mode 100644 index 00000000000..f572d58b8df --- /dev/null +++ b/src/components/style/font_face.rs @@ -0,0 +1,112 @@ +use cssparser::ast::*; +use cssparser::parse_declaration_list; +use errors::{ErrorLoggerIterator, log_css_error}; +use std::ascii::StrAsciiExt; +use parsing_utils::one_component_value; +use stylesheets::{CSSRule, CSSFontFaceRule}; +use url::Url; +use servo_util::url::parse_url; + +#[deriving(PartialEq)] +pub enum FontFaceFormat { + UnknownFormat, + WoffFormat, + TtfFormat, + SvgFormat, + EotFormat, +} + +pub struct FontFaceSource { + pub url: Url, + pub format: FontFaceFormat, +} + +pub struct FontFaceRule { + pub family: String, + pub sources: Vec<FontFaceSource>, +} + +pub fn parse_font_face_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>, base_url: &Url) { + let mut font_face_rule = FontFaceRule { + family: "".to_string(), + sources: vec!() + }; + + let block = match rule.block { + Some(block) => block, + None => { + log_css_error(rule.location, "Invalid @font-face rule"); + return + } + }; + + for item in ErrorLoggerIterator(parse_declaration_list(block.move_iter())) { + match item { + DeclAtRule(rule) => log_css_error( + rule.location, format!("Unsupported at-rule in declaration list: @{:s}", rule.name).as_slice()), + Declaration(Declaration{ location: location, name: name, value: value, important: _}) => { + + let name_lower = name.as_slice().to_ascii_lower(); + match name_lower.as_slice() { + "font-family" => { + match one_component_value(value.as_slice()) { + Some(&String(ref string_value)) => { + font_face_rule.family = string_value.clone(); + }, + _ => { + log_css_error(location, format!("Unsupported font-family string {:s}", name).as_slice()); + } + } + }, + "src" => { + for component_value in value.as_slice().skip_whitespace() { + match component_value { + &URL(ref string_value) => { + let font_url = parse_url(string_value.as_slice(), Some(base_url.clone())); + let src = FontFaceSource { url: font_url, format: UnknownFormat }; + font_face_rule.sources.push(src); + }, + &Function(ref string_value, ref values) => { + match string_value.as_slice() { + "format" => { + let format = one_component_value(values.as_slice()).and_then(|c| { + match c { + &String(ref format_string) => Some(format_string.as_slice().to_ascii_lower()), + _ => None, + } + }); + match font_face_rule.sources.mut_last() { + Some(source) => { + source.format = match format.unwrap_or("".to_string()).as_slice() { + "embedded-opentype" => EotFormat, + "woff" => WoffFormat, + "truetype" | "opentype" => TtfFormat, + "svg" => SvgFormat, + _ => UnknownFormat, + } + } + None => {} + }; + }, + "local" => { + log_css_error(location, "local font face not supported yet!"); + }, + _ => { + log_css_error(location, format!("Unsupported declaration {}", string_value).as_slice()); + } + } + }, + _ => {} + } + } + }, + _ => { + log_css_error(location, format!("Unsupported declaration {:s}", name).as_slice()); + } + } + } + } + } + + parent_rules.push(CSSFontFaceRule(font_face_rule)); +}
\ No newline at end of file diff --git a/src/components/style/style.rs b/src/components/style/style.rs index 276b831cc06..b34e5559dbb 100644 --- a/src/components/style/style.rs +++ b/src/components/style/style.rs @@ -32,7 +32,7 @@ extern crate servo_util = "util"; // Public API -pub use stylesheets::{Stylesheet, CSSRule, StyleRule}; +pub use stylesheets::{Stylesheet, CSSRule, StyleRule, CSSFontFaceRule}; pub use selector_matching::{Stylist, StylesheetOrigin, UserAgentOrigin, AuthorOrigin, UserOrigin}; pub use selector_matching::{MatchedProperty, matches_compound_selector}; pub use properties::{cascade, cascade_anonymous}; @@ -47,6 +47,7 @@ pub use selectors::{NamespaceConstraint, Selector, CompoundSelector, SimpleSelec pub use selectors::{parse_selector_list}; pub use namespaces::NamespaceMap; pub use media_queries::{MediaRule, MediaQueryList, MediaQuery, Device, MediaType, MediaQueryType}; +pub use font_face::{FontFaceFormat, FontFaceRule, FontFaceSource, TtfFormat}; mod stylesheets; mod errors; @@ -57,3 +58,4 @@ mod namespaces; mod node; mod media_queries; mod parsing_utils; +mod font_face; diff --git a/src/components/style/stylesheets.rs b/src/components/style/stylesheets.rs index 8992477de00..a1e78bc2a18 100644 --- a/src/components/style/stylesheets.rs +++ b/src/components/style/stylesheets.rs @@ -16,6 +16,7 @@ use errors::{ErrorLoggerIterator, log_css_error}; use namespaces::{NamespaceMap, parse_namespace_rule}; use media_queries::{MediaRule, parse_media_rule}; use media_queries; +use font_face::{FontFaceRule, parse_font_face_rule}; pub struct Stylesheet { @@ -28,6 +29,7 @@ pub struct Stylesheet { pub enum CSSRule { CSSStyleRule(StyleRule), CSSMediaRule(MediaRule), + CSSFontFaceRule(FontFaceRule), } @@ -143,6 +145,7 @@ pub fn parse_nested_at_rule(lower_name: &str, rule: AtRule, parent_rules: &mut Vec<CSSRule>, namespaces: &NamespaceMap, base_url: &Url) { match lower_name { "media" => parse_media_rule(rule, parent_rules, namespaces, base_url), + "font-face" => parse_font_face_rule(rule, parent_rules, base_url), _ => log_css_error(rule.location, format!("Unsupported at-rule: @{:s}", lower_name).as_slice()) } @@ -156,7 +159,8 @@ pub fn iter_style_rules<'a>(rules: &[CSSRule], device: &media_queries::Device, CSSStyleRule(ref rule) => callback(rule), CSSMediaRule(ref rule) => if rule.media_queries.evaluate(device) { iter_style_rules(rule.rules.as_slice(), device, |s| callback(s)) - } + }, + CSSFontFaceRule(_) => {}, } } } |