Finish implementing first cut at font matching. Closes #174.

This commit is contained in:
Brian J. Burg 2012-11-12 19:24:35 -08:00
parent ebb9392587
commit 0d135a4fa5
8 changed files with 89 additions and 34 deletions

@ -1 +1 @@
Subproject commit 3e5e34f4f46598a85465f4fb754ccaff04a98e4c Subproject commit 7fcbf5112ec163bdc14f096ddd9a45ef744ca6a6

@ -1 +1 @@
Subproject commit dab5176896750f512410e24e33852ccaf13dbf08 Subproject commit 4c38ba65d4929ba418317c9fd9d16a0aa83945c9

View file

@ -23,6 +23,8 @@ pub type FontHandle/& = quartz::font::QuartzFontHandle;
pub type FontHandle/& = freetype::font::FreeTypeFontHandle; pub type FontHandle/& = freetype::font::FreeTypeFontHandle;
pub trait FontHandleMethods { pub trait FontHandleMethods {
// an identifier usable by FontContextHandle to recreate this FontHandle.
pure fn face_identifier() -> ~str;
pure fn family_name() -> ~str; pure fn family_name() -> ~str;
pure fn face_name() -> ~str; pure fn face_name() -> ~str;
pure fn is_italic() -> bool; pure fn is_italic() -> bool;
@ -165,17 +167,17 @@ pub impl FontDescriptor : cmp::Eq {
} }
pub impl FontDescriptor { pub impl FontDescriptor {
static pure fn new(style: &UsedFontStyle, selector: &FontSelector) -> FontDescriptor { static pure fn new(style: UsedFontStyle, selector: FontSelector) -> FontDescriptor {
FontDescriptor { FontDescriptor {
style: copy *style, style: move style,
selector: copy *selector, selector: move selector,
} }
} }
} }
// A FontSelector is a platform-specific strategy for serializing face names. // A FontSelector is a platform-specific strategy for serializing face names.
pub enum FontSelector { pub enum FontSelector {
SelectorPlatformName(~str), SelectorPlatformIdentifier(~str),
SelectorStubDummy, // aka, use Josephin Sans SelectorStubDummy, // aka, use Josephin Sans
} }
@ -183,8 +185,8 @@ pub enum FontSelector {
pub impl FontSelector : cmp::Eq { pub impl FontSelector : cmp::Eq {
pure fn eq(other: &FontSelector) -> bool { pure fn eq(other: &FontSelector) -> bool {
match (&self, other) { match (&self, other) {
(&SelectorPlatformIdentifier(a), &SelectorPlatformIdentifier(b)) => a == b,
(&SelectorStubDummy, &SelectorStubDummy) => true, (&SelectorStubDummy, &SelectorStubDummy) => true,
(&SelectorPlatformName(a), &SelectorPlatformName(b)) => a == b,
_ => false _ => false
} }
} }
@ -275,7 +277,21 @@ impl Font {
}); });
} }
static fn new_from_handle(fctx: &FontContext, handle: &FontHandle, static fn new_from_adopted_handle(_fctx: &FontContext, handle: FontHandle,
style: &SpecifiedFontStyle, backend: BackendType) -> @Font {
let metrics = handle.get_metrics();
@Font {
handle : move handle,
azure_font: None,
shaper: None,
style: copy *style,
metrics: move metrics,
backend: backend,
}
}
static fn new_from_existing_handle(fctx: &FontContext, handle: &FontHandle,
style: &SpecifiedFontStyle, backend: BackendType) -> Result<@Font,()> { style: &SpecifiedFontStyle, backend: BackendType) -> Result<@Font,()> {
// TODO(Issue #179): convert between specified and used font style here? // TODO(Issue #179): convert between specified and used font style here?
@ -284,14 +300,7 @@ impl Font {
Err(()) => return Err(()) Err(()) => return Err(())
}; };
return Ok(@Font { return Ok(Font::new_from_adopted_handle(fctx, move styled_handle, style, backend));
handle : move styled_handle,
azure_font: None,
shaper: None,
style: copy *style,
metrics: handle.get_metrics(),
backend: backend,
});
} }
priv fn get_shaper(@self) -> @Shaper { priv fn get_shaper(@self) -> @Shaper {
@ -454,7 +463,7 @@ pub impl Font : FontMethods {
fn get_descriptor() -> FontDescriptor { fn get_descriptor() -> FontDescriptor {
// TODO(Issue #174): implement by-platform-name FontSelectors, // TODO(Issue #174): implement by-platform-name FontSelectors,
// probably by adding such an API to FontHandle. // probably by adding such an API to FontHandle.
FontDescriptor::new(&font_context::dummy_style(), &SelectorStubDummy) FontDescriptor::new(copy self.style, SelectorPlatformIdentifier(self.handle.face_identifier()))
} }
fn glyph_index(codepoint: char) -> Option<GlyphIndex> { fn glyph_index(codepoint: char) -> Option<GlyphIndex> {

View file

@ -1,5 +1,5 @@
use font::{Font, FontDescriptor, FontGroup, FontStyle, SelectorPlatformName, SelectorStubDummy}; use font::{Font, FontDescriptor, FontGroup, FontStyle, SelectorPlatformIdentifier, SelectorStubDummy};
use font::{SpecifiedFontStyle}; use font::{SpecifiedFontStyle, UsedFontStyle};
use font_list::FontList; use font_list::FontList;
use native::FontHandle; use native::FontHandle;
use util::cache; use util::cache;
@ -27,14 +27,18 @@ pub fn dummy_style() -> FontStyle {
} }
} }
// TODO(Issue #163): this is a workaround for static methods and
// typedefs not working well together. It should be removed.
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
type FontContextHandle/& = quartz::font_context::QuartzFontContextHandle; type FontContextHandle/& = quartz::font_context::QuartzFontContextHandle;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
type FontContextHandle/& = freetype::font_context::FreeTypeFontContextHandle; type FontContextHandle/& = freetype::font_context::FreeTypeFontContextHandle;
trait FontContextHandleMethods {
fn create_font_from_identifier(~str, UsedFontStyle) -> Result<FontHandle, ()>;
}
// TODO(Issue #163): this is a workaround for static methods, traits,
// and typedefs not working well together. It should be removed.
pub impl FontContextHandle { pub impl FontContextHandle {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
static pub fn new() -> FontContextHandle { static pub fn new() -> FontContextHandle {
@ -91,16 +95,10 @@ pub impl FontContext {
} }
} }
// TODO:(Issue #196): cache font groups on the font context.
priv fn create_font_group(style: &SpecifiedFontStyle) -> @FontGroup { priv fn create_font_group(style: &SpecifiedFontStyle) -> @FontGroup {
// TODO(Issue #174): implement by-platform-name FontSelectors
let desc = FontDescriptor::new(&font_context::dummy_style(), &SelectorStubDummy);
let fonts = DVec(); let fonts = DVec();
match self.get_font_by_descriptor(&desc) {
Ok(instance) => fonts.push(instance),
Err(()) => {}
}
// TODO(Issue #193): make iteration over 'font-family' more robust. // TODO(Issue #193): make iteration over 'font-family' more robust.
for str::split_char_each(style.families, ',') |family| { for str::split_char_each(style.families, ',') |family| {
let family_name = str::trim(family); let family_name = str::trim(family);
@ -108,13 +106,23 @@ pub impl FontContext {
let result = list.find_font_in_family(family_name, style); let result = list.find_font_in_family(family_name, style);
do result.iter |font_entry| { do result.iter |font_entry| {
let instance = Font::new_from_handle(&self, &font_entry.handle, style, self.backend); // TODO(Issue #203): route this instantion through FontContext's Font instance cache.
let instance = Font::new_from_existing_handle(&self, &font_entry.handle, style, self.backend);
do result::iter(&instance) |font: &@Font| { fonts.push(*font); } do result::iter(&instance) |font: &@Font| { fonts.push(*font); }
}; };
} }
// TODO(Issue #194): attach a fallback font to the font list, // TODO(Issue #194): *always* attach a fallback font to the
// so that this assertion will never fail. // font list, so that this assertion will never fail.
// assert fonts.len() > 0;
if fonts.len() == 0 {
let desc = FontDescriptor::new(font_context::dummy_style(), SelectorStubDummy);
match self.get_font_by_descriptor(&desc) {
Ok(instance) => fonts.push(instance),
Err(()) => {}
}
}
assert fonts.len() > 0; assert fonts.len() > 0;
// TODO(Issue #179): Split FontStyle into specified and used styles // TODO(Issue #179): Split FontStyle into specified and used styles
let used_style = copy *style; let used_style = copy *style;
@ -128,7 +136,12 @@ pub impl FontContext {
Font::new_from_buffer(&self, test_font_bin(), &desc.style, self.backend) Font::new_from_buffer(&self, test_font_bin(), &desc.style, self.backend)
}, },
// TODO(Issue #174): implement by-platform-name font selectors. // TODO(Issue #174): implement by-platform-name font selectors.
SelectorPlatformName(_) => { fail ~"FontContext::create_font_instance() can't yet handle SelectorPlatformName." } SelectorPlatformIdentifier(identifier) => {
let result_handle = self.handle.create_font_from_identifier(identifier, copy desc.style);
result::chain(move result_handle, |handle| {
Ok(Font::new_from_adopted_handle(&self, move handle, &desc.style, self.backend))
})
}
}; };
} }
} }

View file

@ -1,4 +1,4 @@
use font::{CSSFontWeight, SpecifiedFontStyle}; use font::{CSSFontWeight, SpecifiedFontStyle, UsedFontStyle};
use native::FontHandle; use native::FontHandle;
use dvec::DVec; use dvec::DVec;
@ -59,6 +59,10 @@ pub impl FontList {
do family.iter |fam| { do family.iter |fam| {
result = fam.find_font_for_style(style); result = fam.find_font_for_style(style);
} }
let decision = if result.is_some() { "Found" } else { "Couldn't find" };
debug!("FontList: %s font face in family[%?] matching style: %?", decision, style, family_name);
return result; return result;
} }
@ -66,6 +70,9 @@ pub impl FontList {
// look up canonical name // look up canonical name
let family = self.family_map.find(&str::from_slice(family_name)); let family = self.family_map.find(&str::from_slice(family_name));
let decision = if family.is_some() { "Found" } else { "Couldn't find" };
debug!("FontList: %s font family with name=%s", decision, family_name);
// 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
return family; return family;
} }

View file

@ -188,5 +188,9 @@ pub impl QuartzFontHandle : FontHandleMethods {
Some(data.copy_to_buf()) Some(data.copy_to_buf())
}); });
} }
pure fn face_identifier() -> ~str {
self.ctfont.postscript_name()
}
} }

View file

@ -1,3 +1,14 @@
extern mod core_foundation;
extern mod core_graphics;
extern mod core_text;
use ct = core_text;
use ct::font::CTFont;
use gfx_font::{FontHandle, UsedFontStyle};
use font::QuartzFontHandle;
use gfx_font_context::FontContextHandleMethods;
pub struct QuartzFontContextHandle { pub struct QuartzFontContextHandle {
ctx: (), ctx: (),
@ -10,3 +21,12 @@ pub impl QuartzFontContextHandle {
QuartzFontContextHandle { ctx: () } QuartzFontContextHandle { ctx: () }
} }
} }
pub impl QuartzFontContextHandle : FontContextHandleMethods {
fn create_font_from_identifier(name: ~str, style: UsedFontStyle) -> Result<FontHandle, ()> {
let ctfont_result = CTFont::new_from_name(move name, style.pt_size);
do result::chain(move ctfont_result) |ctfont| {
QuartzFontHandle::new_from_CTFont(&self, move ctfont)
}
}
}

View file

@ -92,3 +92,5 @@ pub mod util {
} }
use gfx_font = font; use gfx_font = font;
use gfx_font_context = font_context;
use gfx_font_list = font_list;