зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #2791 - Add basic support for web fonts. Synchronous loading only (from glennw:web-fonts)
Source-Repo: https://github.com/servo/servo Source-Revision: 541f22ade61a6abe7304e07f99d9ba73e7a609b1
This commit is contained in:
Родитель
9beb81f4d8
Коммит
5e3a90c040
|
@ -11,6 +11,8 @@ use std::collections::HashMap;
|
||||||
use sync::Arc;
|
use sync::Arc;
|
||||||
use font_template::{FontTemplate, FontTemplateDescriptor};
|
use font_template::{FontTemplate, FontTemplateDescriptor};
|
||||||
use platform::font_template::FontTemplateData;
|
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.
|
/// A list of font templates that make up a given font family.
|
||||||
struct FontFamily {
|
struct FontFamily {
|
||||||
|
@ -40,13 +42,35 @@ impl FontFamily {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a request is made for a font family that exists,
|
||||||
|
// pick the first valid font in the family if we failed
|
||||||
|
// to find an exact match for the descriptor.
|
||||||
|
for template in self.templates.mut_iter() {
|
||||||
|
let maybe_template = template.get();
|
||||||
|
if maybe_template.is_some() {
|
||||||
|
return maybe_template;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
None
|
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.
|
/// Commands that the FontContext sends to the font cache task.
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
GetFontTemplate(String, FontTemplateDescriptor, Sender<Reply>),
|
GetFontTemplate(String, FontTemplateDescriptor, Sender<Reply>),
|
||||||
|
AddWebFont(Vec<Url>, String, Sender<()>),
|
||||||
Exit(Sender<()>),
|
Exit(Sender<()>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +85,9 @@ struct FontCache {
|
||||||
port: Receiver<Command>,
|
port: Receiver<Command>,
|
||||||
generic_fonts: HashMap<String, String>,
|
generic_fonts: HashMap<String, String>,
|
||||||
local_families: HashMap<String, FontFamily>,
|
local_families: HashMap<String, FontFamily>,
|
||||||
|
web_families: HashMap<String, FontFamily>,
|
||||||
font_context: FontContextHandle,
|
font_context: FontContextHandle,
|
||||||
|
resource_task: ResourceTask,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontCache {
|
impl FontCache {
|
||||||
|
@ -72,7 +98,6 @@ impl FontCache {
|
||||||
match msg {
|
match msg {
|
||||||
GetFontTemplate(family, descriptor, result) => {
|
GetFontTemplate(family, descriptor, result) => {
|
||||||
let maybe_font_template = self.get_font_template(&family, &descriptor);
|
let maybe_font_template = self.get_font_template(&family, &descriptor);
|
||||||
|
|
||||||
let font_template = match maybe_font_template {
|
let font_template = match maybe_font_template {
|
||||||
Some(font_template) => font_template,
|
Some(font_template) => font_template,
|
||||||
None => self.get_last_resort_template(&descriptor),
|
None => self.get_last_resort_template(&descriptor),
|
||||||
|
@ -80,6 +105,25 @@ impl FontCache {
|
||||||
|
|
||||||
result.send(GetFontTemplateReply(font_template));
|
result.send(GetFontTemplateReply(font_template));
|
||||||
}
|
}
|
||||||
|
AddWebFont(urls, family_name, result) => {
|
||||||
|
for url in urls.iter() {
|
||||||
|
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) => {
|
Exit(result) => {
|
||||||
result.send(());
|
result.send(());
|
||||||
break;
|
break;
|
||||||
|
@ -105,9 +149,8 @@ impl FontCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_font_in_family<'a>(&'a mut self,
|
fn find_font_in_local_family<'a>(&'a mut self, family_name: &String, desc: &FontTemplateDescriptor)
|
||||||
family_name: &String,
|
-> Option<Arc<FontTemplateData>> {
|
||||||
desc: &FontTemplateDescriptor) -> Option<Arc<FontTemplateData>> {
|
|
||||||
// TODO(Issue #188): look up localized font family names if canonical name not found
|
// TODO(Issue #188): look up localized font family names if canonical name not found
|
||||||
// look up canonical name
|
// look up canonical name
|
||||||
if self.local_families.contains_key(family_name) {
|
if self.local_families.contains_key(family_name) {
|
||||||
|
@ -116,8 +159,7 @@ impl FontCache {
|
||||||
|
|
||||||
if s.templates.len() == 0 {
|
if s.templates.len() == 0 {
|
||||||
get_variations_for_family(family_name.as_slice(), |path| {
|
get_variations_for_family(family_name.as_slice(), |path| {
|
||||||
let template = FontTemplate::new(path.as_slice());
|
s.add_template(path.as_slice(), None);
|
||||||
s.templates.push(template);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,16 +177,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>> {
|
fn get_font_template(&mut self, family: &String, desc: &FontTemplateDescriptor) -> Option<Arc<FontTemplateData>> {
|
||||||
let transformed_family_name = self.transform_family(family);
|
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> {
|
fn get_last_resort_template(&mut self, desc: &FontTemplateDescriptor) -> Arc<FontTemplateData> {
|
||||||
let last_resort = get_last_resort_font_families();
|
let last_resort = get_last_resort_font_families();
|
||||||
|
|
||||||
for family in last_resort.iter() {
|
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() {
|
if maybe_font_in_family.is_some() {
|
||||||
return maybe_font_in_family.unwrap();
|
return maybe_font_in_family.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -162,7 +219,7 @@ pub struct FontCacheTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontCacheTask {
|
impl FontCacheTask {
|
||||||
pub fn new() -> FontCacheTask {
|
pub fn new(resource_task: ResourceTask) -> FontCacheTask {
|
||||||
let (chan, port) = channel();
|
let (chan, port) = channel();
|
||||||
|
|
||||||
spawn(proc() {
|
spawn(proc() {
|
||||||
|
@ -178,7 +235,9 @@ impl FontCacheTask {
|
||||||
port: port,
|
port: port,
|
||||||
generic_fonts: generic_fonts,
|
generic_fonts: generic_fonts,
|
||||||
local_families: HashMap::new(),
|
local_families: HashMap::new(),
|
||||||
|
web_families: HashMap::new(),
|
||||||
font_context: FontContextHandle::new(),
|
font_context: FontContextHandle::new(),
|
||||||
|
resource_task: resource_task,
|
||||||
};
|
};
|
||||||
|
|
||||||
cache.refresh_local_families();
|
cache.refresh_local_families();
|
||||||
|
@ -205,6 +264,12 @@ impl FontCacheTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_web_font(&mut self, urls: Vec<Url>, family: &str) {
|
||||||
|
let (response_chan, response_port) = channel();
|
||||||
|
self.chan.send(AddWebFont(urls, family.to_string(), response_chan));
|
||||||
|
response_port.recv();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn exit(&self) {
|
pub fn exit(&self) {
|
||||||
let (response_chan, response_port) = channel();
|
let (response_chan, response_port) = channel();
|
||||||
self.chan.send(Exit(response_chan));
|
self.chan.send(Exit(response_chan));
|
||||||
|
|
|
@ -34,7 +34,7 @@ fn create_scaled_font(backend: BackendType, template: &Arc<FontTemplateData>, pt
|
||||||
|
|
||||||
#[cfg(target_os="macos")]
|
#[cfg(target_os="macos")]
|
||||||
fn create_scaled_font(backend: BackendType, template: &Arc<FontTemplateData>, pt_size: f64) -> ScaledFont {
|
fn create_scaled_font(backend: BackendType, template: &Arc<FontTemplateData>, pt_size: f64) -> ScaledFont {
|
||||||
let cgfont = template.ctfont.copy_to_CGFont();
|
let cgfont = template.ctfont.get_ref().copy_to_CGFont();
|
||||||
ScaledFont::new(backend, &cgfont, pt_size as AzFloat)
|
ScaledFont::new(backend, &cgfont, pt_size as AzFloat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,21 +42,44 @@ impl PartialEq for FontTemplateDescriptor {
|
||||||
pub struct FontTemplate {
|
pub struct FontTemplate {
|
||||||
identifier: String,
|
identifier: String,
|
||||||
descriptor: Option<FontTemplateDescriptor>,
|
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!
|
||||||
|
is_valid: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Holds all of the template information for a font that
|
/// Holds all of the template information for a font that
|
||||||
/// is common, regardless of the number of instances of
|
/// is common, regardless of the number of instances of
|
||||||
/// this font handle per thread.
|
/// this font handle per thread.
|
||||||
impl FontTemplate {
|
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 {
|
FontTemplate {
|
||||||
identifier: identifier.to_string(),
|
identifier: identifier.to_string(),
|
||||||
descriptor: None,
|
descriptor: None,
|
||||||
data: None,
|
weak_ref: maybe_weak_ref,
|
||||||
|
strong_ref: maybe_strong_ref,
|
||||||
|
is_valid: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
/// Get the data for creating a font if it matches a given descriptor.
|
||||||
pub fn get_if_matches(&mut self, fctx: &FontContextHandle,
|
pub fn get_if_matches(&mut self, fctx: &FontContextHandle,
|
||||||
requested_desc: &FontTemplateDescriptor) -> Option<Arc<FontTemplateData>> {
|
requested_desc: &FontTemplateDescriptor) -> Option<Arc<FontTemplateData>> {
|
||||||
|
@ -74,19 +97,29 @@ impl FontTemplate {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let data = self.get_data();
|
if self.is_valid {
|
||||||
let handle = FontHandleMethods::new_from_template(fctx, data.clone(), None);
|
let data = self.get_data();
|
||||||
let handle: FontHandle = match handle {
|
let handle: Result<FontHandle, ()> = FontHandleMethods::new_from_template(fctx, data.clone(), None);
|
||||||
Ok(handle) => handle,
|
match handle {
|
||||||
Err(()) => fail!("TODO - Handle failure to create a font from template."),
|
Ok(handle) => {
|
||||||
};
|
let actual_desc = FontTemplateDescriptor::new(handle.boldness(),
|
||||||
let actual_desc = FontTemplateDescriptor::new(handle.boldness(),
|
handle.is_italic());
|
||||||
handle.is_italic());
|
let desc_match = actual_desc == *requested_desc;
|
||||||
let desc_match = actual_desc == *requested_desc;
|
|
||||||
|
|
||||||
self.descriptor = Some(actual_desc);
|
self.descriptor = Some(actual_desc);
|
||||||
if desc_match {
|
self.is_valid = true;
|
||||||
Some(data)
|
if desc_match {
|
||||||
|
Some(data)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(()) => {
|
||||||
|
self.is_valid = false;
|
||||||
|
debug!("Unable to create a font from template {}", self.identifier);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -94,11 +127,19 @@ impl FontTemplate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the data for creating a font.
|
||||||
|
pub fn get(&mut self) -> Option<Arc<FontTemplateData>> {
|
||||||
|
match self.is_valid {
|
||||||
|
true => Some(self.get_data()),
|
||||||
|
false => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the font template data. If any strong references still
|
/// Get the font template data. If any strong references still
|
||||||
/// exist, it will return a clone, otherwise it will load the
|
/// exist, it will return a clone, otherwise it will load the
|
||||||
/// font data and store a weak reference to it internally.
|
/// font data and store a weak reference to it internally.
|
||||||
pub fn get_data(&mut self) -> Arc<FontTemplateData> {
|
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(),
|
Some(ref data) => data.upgrade(),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
@ -106,8 +147,9 @@ impl FontTemplate {
|
||||||
match maybe_data {
|
match maybe_data {
|
||||||
Some(data) => data,
|
Some(data) => data,
|
||||||
None => {
|
None => {
|
||||||
let template_data = Arc::new(FontTemplateData::new(self.identifier.as_slice()));
|
assert!(self.strong_ref.is_none());
|
||||||
self.data = Some(template_data.downgrade());
|
let template_data = Arc::new(FontTemplateData::new(self.identifier.as_slice(), None));
|
||||||
|
self.weak_ref = Some(template_data.downgrade());
|
||||||
template_data
|
template_data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ extern crate servo_util = "util";
|
||||||
extern crate servo_msg = "msg";
|
extern crate servo_msg = "msg";
|
||||||
extern crate style;
|
extern crate style;
|
||||||
extern crate sync;
|
extern crate sync;
|
||||||
|
extern crate url = "url_";
|
||||||
|
|
||||||
// Eventually we would like the shaper to be pluggable, as many operating systems have their own
|
// 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.
|
// shapers. For now, however, this is a hard dependency.
|
||||||
|
|
|
@ -15,10 +15,17 @@ pub struct FontTemplateData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontTemplateData {
|
impl FontTemplateData {
|
||||||
pub fn new(identifier: &str) -> FontTemplateData {
|
pub fn new(identifier: &str, font_data: Option<Vec<u8>>) -> FontTemplateData {
|
||||||
// TODO: Handle file load failure!
|
let bytes = match font_data {
|
||||||
let mut file = File::open_mode(&Path::new(identifier), io::Open, io::Read).unwrap();
|
Some(bytes) => {
|
||||||
let bytes = file.read_to_end().unwrap();
|
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 {
|
FontTemplateData {
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
|
|
|
@ -15,10 +15,17 @@ pub struct FontTemplateData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontTemplateData {
|
impl FontTemplateData {
|
||||||
pub fn new(identifier: &str) -> FontTemplateData {
|
pub fn new(identifier: &str, font_data: Option<Vec<u8>>) -> FontTemplateData {
|
||||||
// TODO: Handle file load failure!
|
let bytes = match font_data {
|
||||||
let mut file = File::open_mode(&Path::new(identifier), io::Open, io::Read).unwrap();
|
Some(bytes) => {
|
||||||
let bytes = file.read_to_end().unwrap();
|
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 {
|
FontTemplateData {
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
|
|
|
@ -65,13 +65,17 @@ impl FontHandleMethods for FontHandle {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => 0.0
|
None => 0.0
|
||||||
};
|
};
|
||||||
let ct_result = core_text::font::new_from_name(template.identifier.as_slice(), size);
|
match template.ctfont {
|
||||||
ct_result.and_then(|ctfont| {
|
Some(ref ctfont) => {
|
||||||
Ok(FontHandle {
|
Ok(FontHandle {
|
||||||
font_data: template.clone(),
|
font_data: template.clone(),
|
||||||
ctfont: ctfont,
|
ctfont: ctfont.clone_with_font_size(size),
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
None => {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_template(&self) -> Arc<FontTemplateData> {
|
fn get_template(&self) -> Arc<FontTemplateData> {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
* 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 core_graphics::data_provider::CGDataProvider;
|
||||||
|
use core_graphics::font::CGFont;
|
||||||
use core_text::font::CTFont;
|
use core_text::font::CTFont;
|
||||||
use core_text;
|
use core_text;
|
||||||
|
|
||||||
|
@ -10,15 +12,28 @@ use core_text;
|
||||||
/// CTFont object is cached here for use by the
|
/// CTFont object is cached here for use by the
|
||||||
/// render functions that create CGFont references.
|
/// render functions that create CGFont references.
|
||||||
pub struct FontTemplateData {
|
pub struct FontTemplateData {
|
||||||
pub ctfont: CTFont,
|
pub ctfont: Option<CTFont>,
|
||||||
pub identifier: String,
|
pub identifier: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontTemplateData {
|
impl FontTemplateData {
|
||||||
pub fn new(identifier: &str) -> FontTemplateData {
|
pub fn new(identifier: &str, font_data: Option<Vec<u8>>) -> FontTemplateData {
|
||||||
let ctfont_result = core_text::font::new_from_name(identifier.as_slice(), 0.0);
|
let ctfont = match font_data {
|
||||||
|
Some(bytes) => {
|
||||||
|
let fontprov = CGDataProvider::from_buffer(bytes.as_slice());
|
||||||
|
let cgfont_result = CGFont::from_data_provider(fontprov);
|
||||||
|
match cgfont_result {
|
||||||
|
Ok(cgfont) => Some(core_text::font::new_from_CGFont(&cgfont, 0.0)),
|
||||||
|
Err(_) => None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
Some(core_text::font::new_from_name(identifier.as_slice(), 0.0).unwrap())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
FontTemplateData {
|
FontTemplateData {
|
||||||
ctfont: ctfont_result.unwrap(),
|
ctfont: ctfont,
|
||||||
identifier: identifier.to_string(),
|
identifier: identifier.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ use std::comm::{channel, Sender, Receiver};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use style::{AuthorOrigin, Stylesheet, Stylist};
|
use style::{AuthorOrigin, Stylesheet, Stylist};
|
||||||
|
use style::CSSFontFaceRule;
|
||||||
use sync::{Arc, Mutex};
|
use sync::{Arc, Mutex};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -447,7 +448,25 @@ impl LayoutTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_add_stylesheet(&mut self, sheet: Stylesheet) {
|
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) => {
|
||||||
|
let mut font_urls = vec!();
|
||||||
|
for source_line in font_face_rule.source_lines.iter() {
|
||||||
|
for source in source_line.sources.iter() {
|
||||||
|
font_urls.push(source.url.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.font_cache_task.add_web_font(font_urls, font_face_rule.family.as_slice());
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stylist.add_stylesheet(sheet, AuthorOrigin);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the flow tree root from the root node.
|
/// Retrieves the flow tree root from the root node.
|
||||||
|
|
|
@ -125,7 +125,7 @@ pub fn run(opts: opts::Opts) {
|
||||||
} else {
|
} else {
|
||||||
ImageCacheTask::new(resource_task.clone())
|
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(
|
let constellation_chan = Constellation::<layout::layout_task::LayoutTask>::start(
|
||||||
compositor_chan,
|
compositor_chan,
|
||||||
opts,
|
opts,
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* 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 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, UrlParser};
|
||||||
|
|
||||||
|
#[deriving(PartialEq)]
|
||||||
|
pub enum FontFaceFormat {
|
||||||
|
UnknownFormat,
|
||||||
|
WoffFormat,
|
||||||
|
TtfFormat,
|
||||||
|
SvgFormat,
|
||||||
|
EotFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FontFaceSource {
|
||||||
|
pub url: Url,
|
||||||
|
pub format_hints: Vec<FontFaceFormat>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FontFaceSourceLine {
|
||||||
|
pub sources: Vec<FontFaceSource>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FontFaceRule {
|
||||||
|
pub family: String,
|
||||||
|
pub source_lines: Vec<FontFaceSourceLine>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_font_face_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>, base_url: &Url) {
|
||||||
|
let mut maybe_family = None;
|
||||||
|
let mut source_lines = vec!();
|
||||||
|
|
||||||
|
if rule.prelude.as_slice().skip_whitespace().next().is_some() {
|
||||||
|
log_css_error(rule.location, "@font-face prelude contains unexpected characters");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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" => {
|
||||||
|
// FIXME(#2802): Share code with the font-family parser.
|
||||||
|
match one_component_value(value.as_slice()) {
|
||||||
|
Some(&String(ref string_value)) => {
|
||||||
|
maybe_family = Some(string_value.clone());
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
log_css_error(location, format!("Unsupported font-family string {:s}", name).as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"src" => {
|
||||||
|
let mut iter = value.as_slice().skip_whitespace();
|
||||||
|
let mut sources = vec!();
|
||||||
|
let mut syntax_error = false;
|
||||||
|
|
||||||
|
'outer: loop {
|
||||||
|
|
||||||
|
// url() or local() should be next
|
||||||
|
let maybe_url = match iter.next() {
|
||||||
|
Some(&URL(ref string_value)) => {
|
||||||
|
let maybe_url = UrlParser::new().base_url(base_url).parse(string_value.as_slice());
|
||||||
|
let url = maybe_url.unwrap_or_else(|_| Url::parse("about:invalid").unwrap());
|
||||||
|
Some(url)
|
||||||
|
},
|
||||||
|
Some(&Function(ref string_value, ref _values)) => {
|
||||||
|
match string_value.as_slice() {
|
||||||
|
"local" => {
|
||||||
|
log_css_error(location, "local font face is not supported yet - skipping");
|
||||||
|
None
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
log_css_error(location, format!("Unexpected token {}", string_value).as_slice());
|
||||||
|
syntax_error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
log_css_error(location, "Unsupported declaration type");
|
||||||
|
syntax_error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut next_token = iter.next();
|
||||||
|
|
||||||
|
match maybe_url {
|
||||||
|
Some(url) => {
|
||||||
|
let mut source = FontFaceSource {
|
||||||
|
url: url,
|
||||||
|
format_hints: vec!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// optional format, or comma to start loop again
|
||||||
|
match next_token {
|
||||||
|
Some(&Function(ref string_value, ref values)) => {
|
||||||
|
match string_value.as_slice() {
|
||||||
|
"format" => {
|
||||||
|
let mut format_iter = values.as_slice().skip_whitespace();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let fmt_token = format_iter.next();
|
||||||
|
match fmt_token {
|
||||||
|
Some(&String(ref format_hint)) => {
|
||||||
|
let hint = match format_hint.as_slice() {
|
||||||
|
"embedded-opentype" => EotFormat,
|
||||||
|
"woff" => WoffFormat,
|
||||||
|
"truetype" | "opentype" => TtfFormat,
|
||||||
|
"svg" => SvgFormat,
|
||||||
|
_ => UnknownFormat,
|
||||||
|
};
|
||||||
|
source.format_hints.push(hint);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
log_css_error(location, "Unexpected token");
|
||||||
|
syntax_error = true;
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let comma_token = format_iter.next();
|
||||||
|
match comma_token {
|
||||||
|
Some(&Comma) => {},
|
||||||
|
None => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
log_css_error(location, "Unexpected token");
|
||||||
|
syntax_error = true;
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
log_css_error(location,
|
||||||
|
format!("Unsupported token {}", string_value).as_slice());
|
||||||
|
syntax_error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next_token = iter.next();
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
sources.push(source);
|
||||||
|
},
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// after url or optional format, comes comma or end
|
||||||
|
match next_token {
|
||||||
|
Some(&Comma) => {},
|
||||||
|
None => break,
|
||||||
|
_ => {
|
||||||
|
log_css_error(location, "Unexpected token type");
|
||||||
|
syntax_error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !syntax_error && sources.len() > 0 {
|
||||||
|
let source_line = FontFaceSourceLine {
|
||||||
|
sources: sources
|
||||||
|
};
|
||||||
|
source_lines.push(source_line);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
log_css_error(location, format!("Unsupported declaration {:s}", name).as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if maybe_family.is_some() && source_lines.len() > 0 {
|
||||||
|
let font_face_rule = FontFaceRule {
|
||||||
|
family: maybe_family.unwrap(),
|
||||||
|
source_lines: source_lines,
|
||||||
|
};
|
||||||
|
parent_rules.push(CSSFontFaceRule(font_face_rule));
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ extern crate servo_util = "util";
|
||||||
|
|
||||||
|
|
||||||
// Public API
|
// 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::{Stylist, StylesheetOrigin, UserAgentOrigin, AuthorOrigin, UserOrigin};
|
||||||
pub use selector_matching::{MatchedProperty, matches_compound_selector};
|
pub use selector_matching::{MatchedProperty, matches_compound_selector};
|
||||||
pub use properties::{cascade, cascade_anonymous};
|
pub use properties::{cascade, cascade_anonymous};
|
||||||
|
@ -45,6 +45,7 @@ pub use selectors::{NamespaceConstraint, Selector, CompoundSelector, SimpleSelec
|
||||||
pub use selectors::{parse_selector_list};
|
pub use selectors::{parse_selector_list};
|
||||||
pub use namespaces::NamespaceMap;
|
pub use namespaces::NamespaceMap;
|
||||||
pub use media_queries::{MediaRule, MediaQueryList, MediaQuery, Device, MediaType, MediaQueryType};
|
pub use media_queries::{MediaRule, MediaQueryList, MediaQuery, Device, MediaType, MediaQueryType};
|
||||||
|
pub use font_face::{FontFaceFormat, FontFaceRule, FontFaceSource,FontFaceSourceLine, TtfFormat};
|
||||||
|
|
||||||
mod stylesheets;
|
mod stylesheets;
|
||||||
mod errors;
|
mod errors;
|
||||||
|
@ -55,3 +56,4 @@ mod namespaces;
|
||||||
mod node;
|
mod node;
|
||||||
mod media_queries;
|
mod media_queries;
|
||||||
mod parsing_utils;
|
mod parsing_utils;
|
||||||
|
mod font_face;
|
||||||
|
|
|
@ -16,6 +16,7 @@ use errors::{ErrorLoggerIterator, log_css_error};
|
||||||
use namespaces::{NamespaceMap, parse_namespace_rule};
|
use namespaces::{NamespaceMap, parse_namespace_rule};
|
||||||
use media_queries::{MediaRule, parse_media_rule};
|
use media_queries::{MediaRule, parse_media_rule};
|
||||||
use media_queries;
|
use media_queries;
|
||||||
|
use font_face::{FontFaceRule, parse_font_face_rule};
|
||||||
|
|
||||||
|
|
||||||
pub struct Stylesheet {
|
pub struct Stylesheet {
|
||||||
|
@ -28,6 +29,7 @@ pub struct Stylesheet {
|
||||||
pub enum CSSRule {
|
pub enum CSSRule {
|
||||||
CSSStyleRule(StyleRule),
|
CSSStyleRule(StyleRule),
|
||||||
CSSMediaRule(MediaRule),
|
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) {
|
parent_rules: &mut Vec<CSSRule>, namespaces: &NamespaceMap, base_url: &Url) {
|
||||||
match lower_name {
|
match lower_name {
|
||||||
"media" => parse_media_rule(rule, parent_rules, namespaces, base_url),
|
"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,
|
_ => log_css_error(rule.location,
|
||||||
format!("Unsupported at-rule: @{:s}", lower_name).as_slice())
|
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),
|
CSSStyleRule(ref rule) => callback(rule),
|
||||||
CSSMediaRule(ref rule) => if rule.media_queries.evaluate(device) {
|
CSSMediaRule(ref rule) => if rule.media_queries.evaluate(device) {
|
||||||
iter_style_rules(rule.rules.as_slice(), device, |s| callback(s))
|
iter_style_rules(rule.rules.as_slice(), device, |s| callback(s))
|
||||||
}
|
},
|
||||||
|
CSSFontFaceRule(_) => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче