Teach text::font how to find a glyph index on linux

This commit is contained in:
Brian Anderson 2012-06-14 18:47:36 -07:00
parent 3d4d7eabfe
commit c1af1b833c
3 changed files with 211 additions and 6 deletions

View file

@ -1,7 +1,19 @@
export font, create;
export font, create_test_font;
// FIXME: This probably needs to be an arc type so it can be
// shared by layout and the renderer
import libc::{ c_int, c_double };
import ptr::{ null, addr_of };
import azure::cairo::{
cairo_font_face_t,
cairo_scaled_font_t,
cairo_glyph_t,
CAIRO_STATUS_SUCCESS,
};
import azure::cairo::bindgen::{
cairo_font_face_destroy,
cairo_scaled_font_destroy,
cairo_scaled_font_text_to_glyphs,
cairo_glyph_free,
};
#[doc = "
A font handle. Layout can use this to calculate glyph metrics
@ -9,17 +21,210 @@ and the renderer can use it to render text.
"]
class font/& {
let fontbuf: [u8];
let cairo_font: *cairo_scaled_font_t;
let font_dtor: fn@();
new(+fontbuf: [u8]) {
let (cairo_font, font_dtor) = get_cairo_font(&fontbuf);
assert cairo_font.is_not_null();
self.fontbuf = fontbuf;
self.cairo_font = cairo_font;
self.font_dtor = font_dtor;
}
drop {
self.font_dtor();
}
fn buf() -> &self.[u8] {
&self.fontbuf
}
fn get_glyph_idx(codepoint: char) -> option<uint> {
#debug("getting glyph for codepoint %u", codepoint as uint);
let codepoint_str = str::from_char(codepoint);
let mut glyphs: *cairo_glyph_t = null();
let mut num_glyphs = 0 as c_int;
let status = str::as_c_str(codepoint_str) { |codepoint_buf|
cairo_scaled_font_text_to_glyphs(
self.cairo_font,
0.0 as c_double, 0.0 as c_double,
codepoint_buf, codepoint_str.len() as c_int,
addr_of(glyphs), addr_of(num_glyphs),
null(), null(), null()
)
};
ret if status == CAIRO_STATUS_SUCCESS {
// This might not be true, but at least we'll know if it isn't
assert num_glyphs == 1 as c_int;
let glyph_index = unsafe { *glyphs }.index as uint;
#debug("glyph index is %?", glyph_index);
cairo_glyph_free(glyphs);
some(glyph_index)
} else {
#error("cairo did not give me a glyph for %u", codepoint as uint);
none
}
}
}
fn create() -> font {
fn get_cairo_font(buf: &[u8]) -> (*cairo_scaled_font_t, fn@()) {
import libc::c_double;
import azure::cairo;
import cairo::{ cairo_matrix_t };
import cairo::bindgen::{
cairo_matrix_init_identity,
cairo_matrix_scale,
cairo_font_options_create,
cairo_font_options_destroy,
cairo_scaled_font_create,
cairo_scaled_font_destroy
};
import cairo::bindgen::cairo_scaled_font_create;
let mut (face, dtor) = get_cairo_face(buf);
let idmatrix: cairo_matrix_t = {
xx: 0 as c_double,
yx: 0 as c_double,
xy: 0 as c_double,
yy: 0 as c_double,
x0: 0 as c_double,
y0: 0 as c_double
};
cairo_matrix_init_identity(addr_of(idmatrix));
let fontmatrix = idmatrix;
cairo_matrix_scale(addr_of(fontmatrix),
300f as c_double, 400f as c_double);
let options = cairo_font_options_create();
let cfont = cairo_scaled_font_create(face, addr_of(fontmatrix),
addr_of(idmatrix), options);
cairo_font_options_destroy(options);
// FIXME: Need negative tests
if cfont.is_null() {
dtor();
fail "unable to create cairo scaled font";
}
dtor = fn@(move dtor) { cairo_scaled_font_destroy(cfont); dtor() };
(cfont, dtor)
}
#[cfg(target_os = "linux")]
fn get_cairo_face(buf: &[u8]) -> (*cairo_font_face_t, fn@()) {
import freetype = azure::freetype;
import freetype::{ FT_Error, FT_Library, FT_Face, FT_Long };
import freetype::bindgen::{
FT_Init_FreeType,
FT_Done_FreeType,
FT_New_Memory_Face,
FT_Done_Face
};
import azure::cairo_ft;
import cairo_ft::bindgen::cairo_ft_font_face_create_for_ft_face;
impl methods for FT_Error {
fn for_sure() { assert !self.failed() }
fn failed() -> bool { self != 0 as FT_Error }
}
let mut dtor = fn@() { };
let library: FT_Library = null();
// FIXME: Need tests for failure case
FT_Init_FreeType(addr_of(library)).for_sure();
dtor = fn@(move dtor) { FT_Done_FreeType(library).for_sure(); dtor() };
let face: FT_Face = null();
vec::as_buf(*buf) { |cbuf|
if FT_New_Memory_Face(library, cbuf, (*buf).len() as FT_Long,
0 as FT_Long, addr_of(face)).failed() {
dtor();
fail "unable to create FreeType face";
}
}
dtor = fn@(move dtor) { FT_Done_Face(face).for_sure(); dtor() };
let cface = cairo_ft_font_face_create_for_ft_face(face, 0 as c_int);
if cface.is_null() {
// FIXME: Need tests for failure case
dtor();
fail "unable to create cairo font face";
}
dtor = fn@(move dtor) { cairo_font_face_destroy(cface); dtor() };
(cface, dtor)
}
#[cfg(target_os = "macos")]
fn get_cairo_face(buf: &[u8]) -> (*cairo_font_face_t, fn@()) {
fail
}
fn create_test_font() -> font {
let buf = #include_bin("JosefinSans-SemiBold.ttf");
font(buf)
}
fn should_destruct_on_fail_without_leaking() {
#[test];
#[should_fail];
let _font = create_test_font();
fail;
}
fn should_get_glyph_indexes() {
#[test];
let font = create_test_font();
let glyph_idx = font.get_glyph_idx('w');
assert glyph_idx == some(40u);
}
fn should_be_threadsafe() {
#[test];
iter::repeat(10u) {||
task::spawn {||
create_test_font();
}
}
}
fn get_cairo_face_should_fail_and_not_leak_if_font_cant_be_created() {
#[test];
#[should_fail];
get_cairo_face(&[0u8, 1u8, 2u8, 3u8]);
}
fn get_cairo_face_should_return_a_new_face_and_dtor() {
#[test];
let buf = #include_bin("JosefinSans-SemiBold.ttf");
let (face, dtor) = get_cairo_face(&buf);
assert face.is_not_null();
dtor();
}
fn get_cairo_font_should_return_a_new_font_and_dtor() {
#[test];
let buf = #include_bin("JosefinSans-SemiBold.ttf");
let (font, dtor) = get_cairo_font(&buf);
assert font.is_not_null();
dtor();
}

View file

@ -139,6 +139,6 @@ fn hb_glyph_pos_to_servo_glyph_pos(hb_pos: hb_glyph_position_t) -> glyph_pos {
#[test]
fn test_shape_basic() {
let font = font::create();
let font = font::create_test_font();
shape_text2(&font, "firecracker");
}

View file

@ -17,7 +17,7 @@ class text_run {
line break positions.
"]
fn shape() {
let font = font::create();
let font = font::create_test_font();
self.glyphs = some(shape_text(&font, self.text));
}
}