diff options
-rw-r--r-- | src/components/gfx/font.rs | 1 | ||||
-rw-r--r-- | src/components/gfx/gfx.rc | 6 | ||||
-rw-r--r-- | src/components/gfx/platform/android/font.rs | 331 | ||||
-rw-r--r-- | src/components/gfx/platform/android/font_context.rs | 60 | ||||
-rw-r--r-- | src/components/gfx/platform/android/font_list.rs | 205 | ||||
-rw-r--r-- | src/components/gfx/platform/mod.rs | 7 |
6 files changed, 607 insertions, 3 deletions
diff --git a/src/components/gfx/font.rs b/src/components/gfx/font.rs index c8b2680dbfb..99901cdc643 100644 --- a/src/components/gfx/font.rs +++ b/src/components/gfx/font.rs @@ -353,6 +353,7 @@ impl Font { } #[cfg(target_os="linux")] + #[cfg(target_os="android")] fn create_azure_font(&self) -> ScaledFont { let freetype_font = self.handle.face; let size = self.style.pt_size as AzFloat; diff --git a/src/components/gfx/gfx.rc b/src/components/gfx/gfx.rc index 98cdee6232a..eddeb32e970 100644 --- a/src/components/gfx/gfx.rc +++ b/src/components/gfx/gfx.rc @@ -20,9 +20,9 @@ extern mod servo_msg (name = "msg"); // shapers. For now, however, this is a hard dependency. extern mod harfbuzz; -// Linux-specific library dependencies -#[cfg(target_os="linux")] extern mod fontconfig; -#[cfg(target_os="linux")] extern mod freetype; +// Linux and Android-specific library dependencies +#[cfg(target_os="linux")] #[cfg(target_os="android")] extern mod fontconfig; +#[cfg(target_os="linux")] #[cfg(target_os="android")] extern mod freetype; // Mac OS-specific library dependencies #[cfg(target_os="macos")] extern mod core_foundation; diff --git a/src/components/gfx/platform/android/font.rs b/src/components/gfx/platform/android/font.rs new file mode 100644 index 00000000000..aff744cae2d --- /dev/null +++ b/src/components/gfx/platform/android/font.rs @@ -0,0 +1,331 @@ +/* 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/. */ + +extern mod freetype; + +use font::{CSSFontWeight, FontHandleMethods, FontMetrics, FontTableMethods}; +use font::{FontTableTag, FractionalPixel, SpecifiedFontStyle, UsedFontStyle, FontWeight100}; +use font::{FontWeight200, FontWeight300, FontWeight400, FontWeight500, FontWeight600}; +use font::{FontWeight700, FontWeight800, FontWeight900}; +use geometry::Au; +use geometry; +use platform::font_context::FontContextHandle; +use text::glyph::GlyphIndex; +use text::util::{float_to_fixed, fixed_to_float}; + +use freetype::freetype::{FT_Get_Char_Index, FT_Get_Postscript_Name}; +use freetype::freetype::{FT_Load_Glyph, FT_Set_Char_Size}; +use freetype::freetype::{FT_New_Face, FT_Get_Sfnt_Table}; +use freetype::freetype::{FT_New_Memory_Face, FT_Done_Face}; +use freetype::freetype::{FTErrorMethods, FT_F26Dot6, FT_Face, FT_FaceRec}; +use freetype::freetype::{FT_GlyphSlot, FT_Library, FT_Long, FT_ULong}; +use freetype::freetype::{FT_STYLE_FLAG_ITALIC, FT_STYLE_FLAG_BOLD}; +use freetype::freetype::{FT_SizeRec, FT_UInt, FT_Size_Metrics}; +use freetype::freetype::{ft_sfnt_os2}; +use freetype::tt_os2::TT_OS2; + +use std::cast; +use std::ptr; +use std::str; + +fn float_to_fixed_ft(f: float) -> i32 { + float_to_fixed(6, f) +} + +fn fixed_to_float_ft(f: i32) -> float { + fixed_to_float(6, f) +} + +pub struct FontTable { + bogus: () +} + +impl FontTableMethods for FontTable { + fn with_buffer(&self, _blk: &fn(*u8, uint)) { + fail!() + } +} + +enum FontSource { + FontSourceMem(~[u8]), + FontSourceFile(~str) +} + +pub struct FontHandle { + // The font binary. This must stay valid for the lifetime of the font, + // if the font is created using FT_Memory_Face. + source: FontSource, + face: FT_Face, + handle: FontContextHandle +} + +#[unsafe_destructor] +impl Drop for FontHandle { + fn drop(&self) { + assert!(self.face.is_not_null()); + unsafe { + if !FT_Done_Face(self.face).succeeded() { + fail!(~"FT_Done_Face failed"); + } + } + } +} + +impl FontHandleMethods for FontHandle { + fn new_from_buffer(fctx: &FontContextHandle, + buf: ~[u8], + style: &SpecifiedFontStyle) + -> Result<FontHandle, ()> { + let ft_ctx: FT_Library = fctx.ctx.ctx; + if ft_ctx.is_null() { return Err(()); } + + let face_result = do buf.as_imm_buf |bytes: *u8, len: uint| { + create_face_from_buffer(ft_ctx, bytes, len, style.pt_size) + }; + + // TODO: this could be more simply written as result::chain + // and moving buf into the struct ctor, but cant' move out of + // captured binding. + return match face_result { + Ok(face) => { + let handle = FontHandle { + face: face, + source: FontSourceMem(buf), + handle: *fctx + }; + Ok(handle) + } + Err(()) => Err(()) + }; + + fn create_face_from_buffer(lib: FT_Library, + cbuf: *u8, cbuflen: uint, pt_size: float) + -> Result<FT_Face, ()> { + + unsafe { + let mut face: FT_Face = ptr::null(); + let face_index = 0 as FT_Long; + let result = FT_New_Memory_Face(lib, cbuf, cbuflen as FT_Long, + face_index, ptr::to_mut_unsafe_ptr(&mut face)); + + if !result.succeeded() || face.is_null() { + return Err(()); + } + if FontHandle::set_char_size(face, pt_size).is_ok() { + Ok(face) + } else { + Err(()) + } + } + } + } + + // an identifier usable by FontContextHandle to recreate this FontHandle. + fn face_identifier(&self) -> ~str { + /* FT_Get_Postscript_Name seems like a better choice here, but it + doesn't give usable results for fontconfig when deserializing. */ + unsafe { str::raw::from_c_str((*self.face).family_name) } + } + fn family_name(&self) -> ~str { + unsafe { str::raw::from_c_str((*self.face).family_name) } + } + fn face_name(&self) -> ~str { + unsafe { str::raw::from_c_str(FT_Get_Postscript_Name(self.face)) } + } + fn is_italic(&self) -> bool { + unsafe { (*self.face).style_flags & FT_STYLE_FLAG_ITALIC != 0 } + } + fn boldness(&self) -> CSSFontWeight { + let default_weight = FontWeight400; + if unsafe { (*self.face).style_flags & FT_STYLE_FLAG_BOLD == 0 } { + default_weight + } else { + unsafe { + let os2 = FT_Get_Sfnt_Table(self.face, ft_sfnt_os2) as *TT_OS2; + let valid = os2.is_not_null() && (*os2).version != 0xffff; + if valid { + let weight =(*os2).usWeightClass; + match weight { + 1 | 100..199 => FontWeight100, + 2 | 200..299 => FontWeight200, + 3 | 300..399 => FontWeight300, + 4 | 400..499 => FontWeight400, + 5 | 500..599 => FontWeight500, + 6 | 600..699 => FontWeight600, + 7 | 700..799 => FontWeight700, + 8 | 800..899 => FontWeight800, + 9 | 900..999 => FontWeight900, + _ => default_weight + } + } else { + default_weight + } + } + } + } + + fn clone_with_style(&self, + fctx: &FontContextHandle, + style: &UsedFontStyle) -> Result<FontHandle, ()> { + match self.source { + FontSourceMem(ref buf) => { + FontHandleMethods::new_from_buffer(fctx, buf.clone(), style) + } + FontSourceFile(ref file) => { + FontHandle::new_from_file(fctx, (*file).clone(), style) + } + } + } + + fn glyph_index(&self, + codepoint: char) -> Option<GlyphIndex> { + assert!(self.face.is_not_null()); + unsafe { + let idx = FT_Get_Char_Index(self.face, codepoint as FT_ULong); + return if idx != 0 as FT_UInt { + Some(idx as GlyphIndex) + } else { + debug!("Invalid codepoint: %?", codepoint); + None + }; + } + } + + fn glyph_h_advance(&self, + glyph: GlyphIndex) -> Option<FractionalPixel> { + assert!(self.face.is_not_null()); + unsafe { + let res = FT_Load_Glyph(self.face, glyph as FT_UInt, 0); + if res.succeeded() { + let void_glyph = (*self.face).glyph; + let slot: FT_GlyphSlot = cast::transmute(void_glyph); + assert!(slot.is_not_null()); + debug!("metrics: %?", (*slot).metrics); + let advance = (*slot).metrics.horiAdvance; + debug!("h_advance for %? is %?", glyph, advance); + let advance = advance as i32; + return Some(fixed_to_float_ft(advance) as FractionalPixel); + } else { + debug!("Unable to load glyph %?. reason: %?", glyph, res); + return None; + } + } + } + + fn get_metrics(&self) -> FontMetrics { + /* TODO(Issue #76): complete me */ + let face = self.get_face_rec(); + + let underline_size = self.font_units_to_au(face.underline_thickness as float); + let underline_offset = self.font_units_to_au(face.underline_position as float); + let em_size = self.font_units_to_au(face.units_per_EM as float); + let ascent = self.font_units_to_au(face.ascender as float); + let descent = self.font_units_to_au(face.descender as float); + let max_advance = self.font_units_to_au(face.max_advance_width as float); + + return FontMetrics { + underline_size: underline_size, + underline_offset: underline_offset, + leading: geometry::from_pt(0.0), //FIXME + x_height: geometry::from_pt(0.0), //FIXME + em_size: em_size, + ascent: ascent, + descent: -descent, // linux font's seem to use the opposite sign from mac + max_advance: max_advance + } + } + + fn get_table_for_tag(&self, _: FontTableTag) -> Option<FontTable> { + None + } +} + +impl<'self> FontHandle { + fn set_char_size(face: FT_Face, pt_size: float) -> Result<(), ()>{ + let char_width = float_to_fixed_ft(pt_size) as FT_F26Dot6; + let char_height = float_to_fixed_ft(pt_size) as FT_F26Dot6; + let h_dpi = 72; + let v_dpi = 72; + + unsafe { + let result = FT_Set_Char_Size(face, char_width, char_height, h_dpi, v_dpi); + if result.succeeded() { Ok(()) } else { Err(()) } + } + } + + pub fn new_from_file(fctx: &FontContextHandle, file: ~str, + style: &SpecifiedFontStyle) -> Result<FontHandle, ()> { + unsafe { + let ft_ctx: FT_Library = fctx.ctx.ctx; + if ft_ctx.is_null() { return Err(()); } + + let mut face: FT_Face = ptr::null(); + let face_index = 0 as FT_Long; + do file.to_c_str().with_ref |file_str| { + FT_New_Face(ft_ctx, file_str, + face_index, ptr::to_mut_unsafe_ptr(&mut face)); + } + if face.is_null() { + return Err(()); + } + if FontHandle::set_char_size(face, style.pt_size).is_ok() { + Ok(FontHandle { + source: FontSourceFile(file), + face: face, + handle: *fctx + }) + } else { + Err(()) + } + } + } + + pub fn new_from_file_unstyled(fctx: &FontContextHandle, file: ~str) + -> Result<FontHandle, ()> { + unsafe { + let ft_ctx: FT_Library = fctx.ctx.ctx; + if ft_ctx.is_null() { return Err(()); } + + let mut face: FT_Face = ptr::null(); + let face_index = 0 as FT_Long; + do file.to_c_str().with_ref |file_str| { + FT_New_Face(ft_ctx, file_str, + face_index, ptr::to_mut_unsafe_ptr(&mut face)); + } + if face.is_null() { + return Err(()); + } + + Ok(FontHandle { + source: FontSourceFile(file), + face: face, + handle: *fctx + }) + } + } + + fn get_face_rec(&'self self) -> &'self FT_FaceRec { + unsafe { + &(*self.face) + } + } + + fn font_units_to_au(&self, value: float) -> Au { + let face = self.get_face_rec(); + + // face.size is a *c_void in the bindings, presumably to avoid + // recursive structural types + let size: &FT_SizeRec = unsafe { cast::transmute(&(*face.size)) }; + let metrics: &FT_Size_Metrics = &(*size).metrics; + + let em_size = face.units_per_EM as float; + let x_scale = (metrics.x_ppem as float) / em_size as float; + + // If this isn't true then we're scaling one of the axes wrong + assert!(metrics.x_ppem == metrics.y_ppem); + + return geometry::from_frac_px(value * x_scale); + } +} + diff --git a/src/components/gfx/platform/android/font_context.rs b/src/components/gfx/platform/android/font_context.rs new file mode 100644 index 00000000000..5a828ee7438 --- /dev/null +++ b/src/components/gfx/platform/android/font_context.rs @@ -0,0 +1,60 @@ +/* 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 font::UsedFontStyle; +use platform::font::FontHandle; +use font_context::FontContextHandleMethods; +use platform::font_list::path_from_identifier; + +use freetype::freetype::{FTErrorMethods, FT_Library}; +use freetype::freetype::{FT_Done_FreeType, FT_Init_FreeType}; + +use std::ptr; + +struct FreeTypeLibraryHandle { + ctx: FT_Library, +} + +impl Drop for FreeTypeLibraryHandle { + fn drop(&self) { + assert!(self.ctx.is_not_null()); + unsafe { + FT_Done_FreeType(self.ctx); + } + } +} + +pub struct FontContextHandle { + ctx: @FreeTypeLibraryHandle, +} + +impl FontContextHandle { + pub fn new() -> FontContextHandle { + unsafe { + let ctx: FT_Library = ptr::null(); + let result = FT_Init_FreeType(ptr::to_unsafe_ptr(&ctx)); + if !result.succeeded() { fail!(); } + + FontContextHandle { + ctx: @FreeTypeLibraryHandle { ctx: ctx }, + } + } + } +} + +impl FontContextHandleMethods for FontContextHandle { + fn clone(&self) -> FontContextHandle { + FontContextHandle { ctx: self.ctx } + } + + fn create_font_from_identifier(&self, name: ~str, style: UsedFontStyle) + -> Result<FontHandle, ()> { + debug!("Creating font handle for %s", name); + do path_from_identifier(name, &style).chain |file_name| { + debug!("Opening font face %s", file_name); + FontHandle::new_from_file(self, file_name, &style) + } + } +} + diff --git a/src/components/gfx/platform/android/font_list.rs b/src/components/gfx/platform/android/font_list.rs new file mode 100644 index 00000000000..a598cebb316 --- /dev/null +++ b/src/components/gfx/platform/android/font_list.rs @@ -0,0 +1,205 @@ +/* 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/. */ + +extern mod freetype; +extern mod fontconfig; + +use fontconfig::fontconfig::{ + FcChar8, FcResultMatch, FcSetSystem, FcPattern, + FcResultNoMatch, FcMatchPattern, FC_SLANT_ITALIC, FC_WEIGHT_BOLD +}; +use fontconfig::fontconfig::{ + FcConfigGetCurrent, FcConfigGetFonts, FcPatternGetString, + FcPatternDestroy, FcFontSetDestroy, FcConfigSubstitute, + FcDefaultSubstitute, FcPatternCreate, FcPatternAddString, FcPatternAddInteger, + FcFontMatch, FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy, + FcObjectSetAdd, FcPatternGetInteger +}; + + +use font::{FontHandleMethods, UsedFontStyle}; +use font_context::FontContextHandleMethods; +use font_list::{FontEntry, FontFamily, FontFamilyMap}; +use platform::font::FontHandle; +use platform::font_context::FontContextHandle; + +use std::hashmap::HashMap; +use std::libc; +use std::libc::{c_int, c_char}; +use std::ptr; +use std::str; + +pub struct FontListHandle { + fctx: FontContextHandle, +} + +impl FontListHandle { + pub fn new(fctx: &FontContextHandle) -> FontListHandle { + FontListHandle { fctx: fctx.clone() } + } + + pub fn get_available_families(&self) -> FontFamilyMap { + let mut family_map : FontFamilyMap = HashMap::new(); + unsafe { + let config = FcConfigGetCurrent(); + let fontSet = FcConfigGetFonts(config, FcSetSystem); + for i in range(0, (*fontSet).nfont as int) { + let font = (*fontSet).fonts.offset(i); + let family: *FcChar8 = ptr::null(); + let mut v: c_int = 0; + do "family".to_c_str().with_ref |FC_FAMILY| { + while FcPatternGetString(*font, FC_FAMILY, v, &family) == FcResultMatch { + let family_name = str::raw::from_c_str(family as *c_char); + debug!("Creating new FontFamily for family: %s", family_name); + let new_family = @mut FontFamily::new(family_name); + family_map.insert(family_name, new_family); + v += 1; + } + } + } + } + return family_map; + } + + pub fn load_variations_for_family(&self, family: @mut FontFamily) { + debug!("getting variations for %?", family); + unsafe { + let config = FcConfigGetCurrent(); + let font_set = FcConfigGetFonts(config, FcSetSystem); + let font_set_array_ptr = ptr::to_unsafe_ptr(&font_set); + let pattern = FcPatternCreate(); + assert!(pattern.is_not_null()); + do "family".to_c_str().with_ref |FC_FAMILY| { + do family.family_name.to_c_str().with_ref |family_name| { + let ok = FcPatternAddString(pattern, FC_FAMILY, family_name as *FcChar8); + assert!(ok != 0); + } + } + + let object_set = FcObjectSetCreate(); + assert!(object_set.is_not_null()); + + do "file".to_c_str().with_ref |FC_FILE| { + FcObjectSetAdd(object_set, FC_FILE); + } + do "index".to_c_str().with_ref |FC_INDEX| { + FcObjectSetAdd(object_set, FC_INDEX); + } + + let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set); + + debug!("found %? variations", (*matches).nfont); + + for i in range(0, (*matches).nfont as int) { + let font = (*matches).fonts.offset(i); + let file = do "file".to_c_str().with_ref |FC_FILE| { + let file: *FcChar8 = ptr::null(); + if FcPatternGetString(*font, FC_FILE, 0, &file) == FcResultMatch { + str::raw::from_c_str(file as *libc::c_char) + } else { + fail!(); + } + }; + let index = do "index".to_c_str().with_ref |FC_INDEX| { + let index: libc::c_int = 0; + if FcPatternGetInteger(*font, FC_INDEX, 0, &index) == FcResultMatch { + index + } else { + fail!(); + } + }; + + debug!("variation file: %?", file); + debug!("variation index: %?", index); + + let font_handle = FontHandle::new_from_file_unstyled(&self.fctx, + file); + let font_handle = font_handle.unwrap(); + + debug!("Creating new FontEntry for face: %s", font_handle.face_name()); + let entry = @FontEntry::new(font_handle); + family.entries.push(entry); + } + + FcFontSetDestroy(matches); + FcPatternDestroy(pattern); + FcObjectSetDestroy(object_set); + } + } + + pub fn get_last_resort_font_families() -> ~[~str] { + ~[~"Roboto"] + } +} + +struct AutoPattern { + pattern: *FcPattern +} + +impl Drop for AutoPattern { + fn drop(&self) { + unsafe { + FcPatternDestroy(self.pattern); + } + } +} + +pub fn path_from_identifier(name: ~str, style: &UsedFontStyle) -> Result<~str, ()> { + unsafe { + let config = FcConfigGetCurrent(); + let wrapper = AutoPattern { pattern: FcPatternCreate() }; + let pattern = wrapper.pattern; + let res = do "family".to_c_str().with_ref |FC_FAMILY| { + do name.to_c_str().with_ref |family| { + FcPatternAddString(pattern, FC_FAMILY, family as *FcChar8) + } + }; + if res != 1 { + debug!("adding family to pattern failed"); + return Err(()); + } + + if style.italic { + let res = do "slant".to_c_str().with_ref |FC_SLANT| { + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC) + }; + if res != 1 { + debug!("adding slant to pattern failed"); + return Err(()); + } + } + if style.weight.is_bold() { + let res = do "weight".to_c_str().with_ref |FC_WEIGHT| { + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD) + }; + if res != 1 { + debug!("adding weight to pattern failed"); + return Err(()); + } + } + + if FcConfigSubstitute(config, pattern, FcMatchPattern) != 1 { + debug!("substitution failed"); + return Err(()); + } + FcDefaultSubstitute(pattern); + let result = FcResultNoMatch; + let result_wrapper = AutoPattern { pattern: FcFontMatch(config, pattern, &result) }; + let result_pattern = result_wrapper.pattern; + if result != FcResultMatch && result_pattern.is_null() { + debug!("obtaining match to pattern failed"); + return Err(()); + } + + let file: *FcChar8 = ptr::null(); + let res = do "file".to_c_str().with_ref |FC_FILE| { + FcPatternGetString(result_pattern, FC_FILE, 0, &file) + }; + if res != FcResultMatch { + debug!("getting filename for font failed"); + return Err(()); + } + Ok(str::raw::from_c_str(file as *c_char)) + } +} diff --git a/src/components/gfx/platform/mod.rs b/src/components/gfx/platform/mod.rs index 2d8fb4f9ce7..34870cc2f2d 100644 --- a/src/components/gfx/platform/mod.rs +++ b/src/components/gfx/platform/mod.rs @@ -4,6 +4,7 @@ #[cfg(target_os="linux")] pub use platform::linux::{font, font_context, font_list}; #[cfg(target_os="macos")] pub use platform::macos::{font, font_context, font_list}; +#[cfg(target_os="android")] pub use platform::android::{font, font_context, font_list}; #[cfg(target_os="linux")] pub mod linux { @@ -19,3 +20,9 @@ pub mod macos { pub mod font_list; } +#[cfg(target_os="android")] +pub mod android { + pub mod font; + pub mod font_context; + pub mod font_list; +} |