| // Copyright 2015 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //! A simple renderer for TrueType fonts |
| |
| use std::collections::HashMap; |
| use std::fmt; |
| use std::fmt::{Debug, Display, Formatter}; |
| use std::result::Result; |
| |
| use geom::{affine_pt, Affine, Point}; |
| use raster::Raster; |
| |
| #[derive(PartialEq, Eq, Hash)] |
| struct Tag(u32); |
| |
| impl Tag { |
| fn from_str(s: &str) -> Tag { |
| Tag(get_u32(s.as_bytes(), 0).unwrap()) |
| } |
| } |
| |
| impl Display for Tag { |
| fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
| let &Tag(tag) = self; |
| let buf = vec![ |
| ((tag >> 24) & 0xff) as u8, |
| ((tag >> 16) & 0xff) as u8, |
| ((tag >> 8) & 0xff) as u8, |
| (tag & 0xff) as u8, |
| ]; |
| f.write_str(&String::from_utf8(buf).unwrap()) |
| } |
| } |
| |
| fn get_u16(data: &[u8], off: usize) -> Option<u16> { |
| if off + 1 > data.len() { |
| None |
| } else { |
| Some(((data[off] as u16) << 8) | data[off + 1] as u16) |
| } |
| } |
| |
| fn get_i16(data: &[u8], off: usize) -> Option<i16> { |
| get_u16(data, off).map(|x| x as i16) |
| } |
| |
| fn get_f2_14(data: &[u8], off: usize) -> Option<f32> { |
| get_i16(data, off).map(|x| x as f32 * (1.0 / (1 << 14) as f32)) |
| } |
| |
| fn get_u32(data: &[u8], off: usize) -> Option<u32> { |
| if off + 3 > data.len() { |
| None |
| } else { |
| Some( |
| ((data[off] as u32) << 24) |
| | ((data[off + 1] as u32) << 16) |
| | ((data[off + 2] as u32) << 8) |
| | data[off + 3] as u32, |
| ) |
| } |
| } |
| |
| // TODO: be consistent, use newtype or one-field struct everywhere |
| struct Head<'a>(&'a [u8]); |
| |
| impl<'a> Head<'a> { |
| fn index_to_loc_format(&self) -> i16 { |
| get_i16(self.0, 50).unwrap() |
| } |
| |
| fn units_per_em(&self) -> u16 { |
| get_u16(self.0, 18).unwrap() |
| } |
| } |
| |
| struct Maxp<'a> { |
| data: &'a [u8], |
| } |
| |
| impl<'a> Maxp<'a> { |
| fn num_glyphs(&self) -> u16 { |
| get_u16(self.data, 4).unwrap() |
| } |
| } |
| |
| struct Loca<'a>(&'a [u8]); |
| |
| impl<'a> Loca<'a> { |
| fn get_off(&self, glyph_ix: u16, fmt: i16) -> Option<u32> { |
| if fmt != 0 { |
| get_u32(self.0, glyph_ix as usize * 4) |
| } else { |
| get_u16(self.0, glyph_ix as usize * 2).map(|raw| raw as u32 * 2) |
| } |
| } |
| } |
| |
| struct EncodingRecord<'a>(&'a [u8]); |
| |
| impl<'a> EncodingRecord<'a> { |
| fn get_platform_id(&self) -> u16 { |
| get_u16(self.0, 0).unwrap() |
| } |
| |
| fn get_encoding_id(&self) -> u16 { |
| get_u16(self.0, 2).unwrap() |
| } |
| |
| fn get_offset(&self) -> u32 { |
| get_u32(self.0, 4).unwrap() |
| } |
| } |
| |
| impl<'a> Debug for EncodingRecord<'a> { |
| fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
| f.debug_struct("EncodingRecord") |
| .field("platformID", &self.get_platform_id()) |
| .field("encodingID", &self.get_encoding_id()) |
| .field("offset", &self.get_offset()) |
| .finish() |
| } |
| } |
| |
| struct Encoding<'a>(&'a [u8]); |
| |
| impl<'a> Encoding<'a> { |
| fn get_format(&self) -> u16 { |
| get_u16(self.0, 0).unwrap() |
| } |
| |
| fn get_length(&self) -> u16 { |
| get_u16(self.0, 2).unwrap() |
| } |
| |
| fn get_language(&self) -> u16 { |
| get_u16(self.0, 4).unwrap() |
| } |
| } |
| |
| impl<'a> Debug for Encoding<'a> { |
| fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
| f.debug_struct("Encoding") |
| .field("format", &self.get_format()) |
| .field("length", &self.get_length()) |
| .field("language", &self.get_language()) |
| .finish() |
| } |
| } |
| |
| struct EncodingFormat4<'a>(&'a [u8]); |
| |
| impl<'a> EncodingFormat4<'a> { |
| fn get_format(&self) -> u16 { |
| get_u16(self.0, 0).unwrap() |
| } |
| |
| fn get_length(&self) -> u16 { |
| get_u16(self.0, 2).unwrap() |
| } |
| |
| fn get_language(&self) -> u16 { |
| get_u16(self.0, 4).unwrap() |
| } |
| |
| fn get_seg_count_x_2(&self) -> u16 { |
| get_u16(self.0, 6).unwrap() |
| } |
| |
| fn get_seg_count(&self) -> u16 { |
| self.get_seg_count_x_2() / 2 |
| } |
| |
| fn get_search_range(&self) -> u16 { |
| get_u16(self.0, 8).unwrap() |
| } |
| |
| fn get_entry_selector(&self) -> u16 { |
| get_u16(self.0, 10).unwrap() |
| } |
| |
| fn get_range_shift(&self) -> u16 { |
| get_u16(self.0, 12).unwrap() |
| } |
| |
| fn get_u16_vec(&self, start_position: u16, count: u16) -> Vec<u16> { |
| let mut result = vec![]; |
| let mut vec_position = start_position; |
| let limit = vec_position + 2 * count; |
| while vec_position < limit { |
| result.push(get_u16(self.0, vec_position as usize).unwrap()); |
| vec_position += 2; |
| } |
| result |
| } |
| |
| fn get_i16_vec(&self, start_position: u16, count: u16) -> Vec<i16> { |
| let mut result = vec![]; |
| let mut vec_position = start_position; |
| let limit = vec_position + 2 * count; |
| while vec_position < limit { |
| result.push(get_i16(self.0, vec_position as usize).unwrap()); |
| vec_position += 2; |
| } |
| result |
| } |
| |
| fn get_end_counts_position() -> u16 { |
| 14 |
| } |
| |
| fn get_end_counts(&self) -> Vec<u16> { |
| let seg_count = self.get_seg_count(); |
| self.get_u16_vec(Self::get_end_counts_position(), seg_count) |
| } |
| |
| fn get_start_counts_position(seg_count: u16) -> u16 { |
| Self::get_end_counts_position() + 2 + 2 * seg_count |
| } |
| |
| fn get_start_counts(&self) -> Vec<u16> { |
| let seg_count = self.get_seg_count(); |
| self.get_u16_vec(Self::get_start_counts_position(seg_count), seg_count) |
| } |
| |
| fn get_id_deltas_position(seg_count: u16) -> u16 { |
| Self::get_start_counts_position(seg_count) + 2 * seg_count |
| } |
| |
| fn get_id_deltas(&self) -> Vec<i16> { |
| let seg_count = self.get_seg_count(); |
| self.get_i16_vec(Self::get_id_deltas_position(seg_count), seg_count) |
| } |
| |
| fn get_id_range_offset_position(seg_count: u16) -> u16 { |
| Self::get_id_deltas_position(seg_count) + 2 * seg_count |
| } |
| |
| fn get_id_range_offsets(&self) -> Vec<u16> { |
| let seg_count = self.get_seg_count(); |
| self.get_u16_vec(Self::get_id_range_offset_position(seg_count), seg_count) |
| } |
| |
| fn extract_glyph_id( |
| &self, code_point: u16, start_value: u16, seg_count: u16, seg_index: u16, |
| ) -> Option<u16> { |
| let data = self.0; |
| let seg_index_pos = 2 * seg_index; |
| let id_range_offset_pos = Self::get_id_range_offset_position(seg_count) + seg_index_pos; |
| let id_range_offset_value = get_u16(data, id_range_offset_pos as usize).unwrap(); |
| let id_delta_pos = Self::get_id_deltas_position(seg_count) + seg_index_pos; |
| let id_delta = get_i16(data, id_delta_pos as usize).unwrap(); |
| if id_range_offset_value == 0 { |
| Some((code_point as i16 + id_delta) as u16) |
| } else { |
| let delta = (code_point - start_value) * 2; |
| let pos = id_range_offset_pos.wrapping_add(delta) + id_range_offset_value; |
| let glyph_array_value = get_u16(data, pos as usize).unwrap(); |
| if glyph_array_value == 0 { |
| return None; |
| } |
| let glyph_index = (glyph_array_value as i16).wrapping_add(id_delta); |
| Some(glyph_index as u16) |
| } |
| } |
| |
| pub fn lookup_glyph_id(&self, code_point: u16) -> Option<u16> { |
| let end_counts_position = Self::get_end_counts_position(); |
| let seg_count = self.get_seg_count(); |
| let mut size = seg_count - 1; |
| let mut index = size / 2; |
| while size > 0 { |
| let search = end_counts_position + index * 2; |
| let end_value = get_u16(self.0, search as usize).unwrap(); |
| if end_value >= code_point { |
| let start_pos = Self::get_start_counts_position(seg_count) + 2 * index; |
| let start_value = get_u16(self.0, start_pos as usize).unwrap(); |
| if start_value > code_point { |
| size /= 2; |
| index -= size; |
| } else { |
| return self.extract_glyph_id(code_point, start_value, seg_count, index); |
| } |
| } else { |
| size /= 2; |
| index += size; |
| } |
| } |
| None |
| } |
| } |
| |
| impl<'a> Debug for EncodingFormat4<'a> { |
| fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
| f.debug_struct("EncodingFormat4") |
| .field("format", &self.get_format()) |
| .field("length", &self.get_length()) |
| .field("language", &self.get_language()) |
| .field("segCountX2", &self.get_seg_count_x_2()) |
| .field("searchRange", &self.get_search_range()) |
| .field("entrySelector", &self.get_entry_selector()) |
| .field("rangeShift", &self.get_range_shift()) |
| .field("endCounts", &self.get_end_counts()) |
| .field("startCounts", &self.get_start_counts()) |
| .field("idDeltas", &self.get_id_deltas()) |
| .field("idRangeOffsets", &self.get_id_range_offsets()) |
| .finish() |
| } |
| } |
| |
| struct Cmap<'a>(&'a [u8]); |
| |
| impl<'a> Cmap<'a> { |
| fn get_version(&self) -> u16 { |
| get_u16(self.0, 0).unwrap() |
| } |
| |
| fn get_num_tables(&self) -> u16 { |
| get_u16(self.0, 2).unwrap() |
| } |
| |
| fn get_encoding_record(&self, index: u16) -> Option<EncodingRecord<'a>> { |
| if index >= self.get_num_tables() { |
| return None; |
| } |
| let enc_offset = (index * 8 + 4) as usize; |
| let encoding_data = &self.0[enc_offset as usize..(enc_offset + 12) as usize]; |
| Some(EncodingRecord(encoding_data)) |
| } |
| |
| fn get_encoding_records(&self) -> Vec<EncodingRecord> { |
| let mut encodings = vec![]; |
| for i in 0..self.get_num_tables() { |
| encodings.push(self.get_encoding_record(i).unwrap()); |
| } |
| encodings |
| } |
| |
| fn get_encoding(&self, index: u16) -> Option<Encoding<'a>> { |
| if index >= self.get_num_tables() { |
| return None; |
| } |
| let record = self.get_encoding_record(index).unwrap(); |
| let subtable_len = get_u16(self.0, (record.get_offset() + 2) as usize).unwrap() as u32; |
| let encoding_data = |
| &self.0[record.get_offset() as usize..(record.get_offset() + subtable_len) as usize]; |
| Some(Encoding(encoding_data)) |
| } |
| |
| fn get_encoding_format_4_at(&self, index: u16) -> Option<EncodingFormat4<'a>> { |
| let encoding = self.get_encoding(index); |
| if encoding.is_none() || encoding.unwrap().get_format() != 4 { |
| return None; |
| } |
| let record = self.get_encoding_record(index).unwrap(); |
| let subtable_len = get_u16(self.0, (record.get_offset() + 2) as usize).unwrap() as u32; |
| let encoding_data = |
| &self.0[record.get_offset() as usize..(record.get_offset() + subtable_len) as usize]; |
| Some(EncodingFormat4(encoding_data)) |
| } |
| |
| fn get_encodings(&self) -> Vec<Encoding> { |
| let mut encodings = vec![]; |
| for i in 0..self.get_num_tables() { |
| encodings.push(self.get_encoding(i).unwrap()); |
| } |
| encodings |
| } |
| |
| pub fn find_format_4_encoding(&self) -> Option<u16> { |
| for index in 0..self.get_num_tables() { |
| let encoding = self.get_encoding(index); |
| if let Some(encoding) = encoding { |
| if encoding.get_format() == 4 { |
| return Some(index); |
| } |
| } |
| } |
| None |
| } |
| } |
| |
| impl<'a> Debug for Cmap<'a> { |
| fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
| f.debug_struct("Cmap") |
| .field("version", &self.get_version()) |
| .field("numTables", &self.get_num_tables()) |
| .field("encodingRecords", &self.get_encoding_records()) |
| .field("encodings", &self.get_encodings()) |
| .finish() |
| } |
| } |
| |
| fn get_bbox_raw(data: &[u8]) -> (i16, i16, i16, i16) { |
| ( |
| get_i16(data, 2).unwrap(), |
| get_i16(data, 4).unwrap(), |
| get_i16(data, 6).unwrap(), |
| get_i16(data, 8).unwrap(), |
| ) |
| } |
| |
| enum Glyph<'a> { |
| Empty, |
| Simple(SimpleGlyph<'a>), |
| Compound(CompoundGlyph<'a>), |
| } |
| |
| struct SimpleGlyph<'a> { |
| data: &'a [u8], |
| } |
| |
| impl<'a> SimpleGlyph<'a> { |
| fn number_of_contours(&self) -> i16 { |
| get_i16(self.data, 0).unwrap() |
| } |
| |
| fn bbox(&self) -> (i16, i16, i16, i16) { |
| get_bbox_raw(self.data) |
| } |
| |
| fn points(&self) -> GlyphPoints<'a> { |
| let data = self.data; |
| let n_contours = self.number_of_contours(); |
| let insn_len_off = 10 + 2 * n_contours as usize; |
| let n_points = get_u16(data, insn_len_off - 2).unwrap() as usize + 1; |
| let insn_len = get_u16(data, insn_len_off).unwrap(); // insn_len |
| let flags_ix = insn_len_off + insn_len as usize + 2; |
| let mut flags_size = 0; |
| let mut x_size = 0; |
| let mut points_remaining = n_points; |
| while points_remaining > 0 { |
| let flag = data[flags_ix as usize + flags_size]; |
| let repeat_count = if (flag & 8) == 0 { |
| 1 |
| } else { |
| flags_size += 1; |
| data[flags_ix as usize + flags_size] as usize + 1 |
| }; |
| flags_size += 1; |
| match flag & 0x12 { |
| 0x02 | 0x12 => x_size += repeat_count, |
| 0x00 => x_size += 2 * repeat_count, |
| _ => (), |
| } |
| points_remaining -= repeat_count; |
| } |
| let x_ix = flags_ix + flags_size; |
| let y_ix = x_ix + x_size; |
| GlyphPoints { |
| data: data, |
| x: 0, |
| y: 0, |
| points_remaining: n_points, |
| last_flag: 0, |
| flag_repeats_remaining: 0, |
| flags_ix: flags_ix, |
| x_ix: x_ix, |
| y_ix: y_ix, |
| } |
| } |
| |
| fn contour_sizes(&self) -> ContourSizes { |
| let n_contours = self.number_of_contours(); |
| ContourSizes { |
| data: self.data, |
| contours_remaining: n_contours as usize, |
| ix: 10, |
| offset: -1, |
| } |
| } |
| } |
| |
| struct GlyphPoints<'a> { |
| data: &'a [u8], |
| x: i16, |
| y: i16, |
| points_remaining: usize, |
| last_flag: u8, |
| flag_repeats_remaining: u8, |
| flags_ix: usize, |
| x_ix: usize, |
| y_ix: usize, |
| } |
| |
| impl<'a> Iterator for GlyphPoints<'a> { |
| type Item = (bool, i16, i16); |
| fn next(&mut self) -> Option<(bool, i16, i16)> { |
| if self.points_remaining == 0 { |
| None |
| } else { |
| if self.flag_repeats_remaining == 0 { |
| self.last_flag = self.data[self.flags_ix]; |
| if (self.last_flag & 8) == 0 { |
| self.flags_ix += 1; |
| } else { |
| self.flag_repeats_remaining = self.data[self.flags_ix + 1]; |
| self.flags_ix += 2; |
| } |
| } else { |
| self.flag_repeats_remaining -= 1; |
| } |
| let flag = self.last_flag; |
| //println!("flag={:02x}, flags_ix={}, x_ix={}, ({}) y_ix={} ({})", |
| // flag, self.flags_ix, self.x_ix, self.data.get(self.x_ix), self.y_ix, |
| // self.data.get(self.y_ix)); |
| match flag & 0x12 { |
| 0x02 => { |
| self.x -= self.data[self.x_ix] as i16; |
| self.x_ix += 1; |
| } |
| 0x00 => { |
| self.x += get_i16(self.data, self.x_ix).unwrap(); |
| self.x_ix += 2; |
| } |
| 0x12 => { |
| self.x += self.data[self.x_ix] as i16; |
| self.x_ix += 1; |
| } |
| _ => (), |
| } |
| match flag & 0x24 { |
| 0x04 => { |
| self.y -= self.data[self.y_ix] as i16; |
| self.y_ix += 1; |
| } |
| 0x00 => { |
| self.y += get_i16(self.data, self.y_ix).unwrap(); |
| self.y_ix += 2; |
| } |
| 0x24 => { |
| self.y += self.data[self.y_ix] as i16; |
| self.y_ix += 1; |
| } |
| _ => (), |
| } |
| self.points_remaining -= 1; |
| Some(((self.last_flag & 1) != 0, self.x, self.y)) |
| } |
| } |
| |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| ( |
| self.points_remaining as usize, |
| Some(self.points_remaining as usize), |
| ) |
| } |
| } |
| |
| struct ContourSizes<'a> { |
| data: &'a [u8], |
| contours_remaining: usize, |
| ix: usize, |
| offset: i32, |
| } |
| |
| impl<'a> Iterator for ContourSizes<'a> { |
| type Item = usize; |
| fn next(&mut self) -> Option<(usize)> { |
| if self.contours_remaining == 0 { |
| None |
| } else { |
| let ret = get_u16(self.data, self.ix).unwrap() as i32 - self.offset; |
| self.offset += ret; |
| self.ix += 2; |
| self.contours_remaining -= 1; |
| Some(ret as usize) |
| } |
| } |
| |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| (self.contours_remaining, Some(self.contours_remaining)) |
| } |
| } |
| |
| struct CompoundGlyph<'a> { |
| data: &'a [u8], |
| } |
| |
| struct Components<'a> { |
| data: &'a [u8], |
| more: bool, |
| ix: usize, |
| } |
| |
| const ARG_1_AND_2_ARE_WORDS: u16 = 1; |
| const WE_HAVE_A_SCALE: u16 = 1 << 3; |
| const MORE_COMPONENTS: u16 = 1 << 5; |
| const WE_HAVE_AN_X_AND_Y_SCALE: u16 = 1 << 6; |
| const WE_HAVE_A_TWO_BY_TWO: u16 = 1 << 7; |
| |
| impl<'a> Iterator for Components<'a> { |
| type Item = (u16, Affine); |
| fn next(&mut self) -> Option<(u16, Affine)> { |
| if !self.more { |
| return None; |
| } |
| let flags = get_u16(self.data, self.ix).unwrap(); |
| self.ix += 2; |
| let glyph_index = get_u16(self.data, self.ix).unwrap(); |
| self.ix += 2; |
| let arg1; |
| let arg2; |
| if (flags & ARG_1_AND_2_ARE_WORDS) != 0 { |
| arg1 = get_i16(self.data, self.ix).unwrap(); |
| self.ix += 2; |
| arg2 = get_i16(self.data, self.ix).unwrap(); |
| self.ix += 2; |
| } else { |
| arg1 = self.data[self.ix] as i16; |
| self.ix += 1; |
| arg2 = self.data[self.ix] as i16; |
| self.ix += 1; |
| } |
| let mut a = 1.0; |
| let mut b = 0.0; |
| let mut c = 0.0; |
| let mut d = 1.0; |
| if (flags & WE_HAVE_A_TWO_BY_TWO) != 0 { |
| a = get_f2_14(self.data, self.ix).unwrap(); |
| self.ix += 2; |
| b = get_f2_14(self.data, self.ix).unwrap(); |
| self.ix += 2; |
| c = get_f2_14(self.data, self.ix).unwrap(); |
| self.ix += 2; |
| d = get_f2_14(self.data, self.ix).unwrap(); |
| self.ix += 2; |
| } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0 { |
| a = get_f2_14(self.data, self.ix).unwrap(); |
| self.ix += 2; |
| d = get_f2_14(self.data, self.ix).unwrap(); |
| self.ix += 2; |
| } else if (flags & WE_HAVE_A_SCALE) != 0 { |
| a = get_f2_14(self.data, self.ix).unwrap(); |
| self.ix += 2; |
| d = a; |
| } |
| // TODO: handle non-ARGS_ARE_XY_VALUES case |
| let x = arg1 as f32; |
| let y = arg2 as f32; |
| let z = Affine::new(a, b, c, d, x, y); |
| self.more = (flags & MORE_COMPONENTS) != 0; |
| Some((glyph_index, z)) |
| } |
| } |
| |
| impl<'a> CompoundGlyph<'a> { |
| fn bbox(&self) -> (i16, i16, i16, i16) { |
| get_bbox_raw(self.data) |
| } |
| |
| fn components(&self) -> Components { |
| Components { |
| data: self.data, |
| ix: 10, |
| more: true, |
| } |
| } |
| } |
| |
| pub struct Font<'a> { |
| _version: u32, |
| _tables: HashMap<Tag, &'a [u8]>, |
| head: Head<'a>, |
| maxp: Maxp<'a>, |
| cmap: Option<Cmap<'a>>, |
| loca: Option<Loca<'a>>, |
| glyf: Option<&'a [u8]>, |
| encoding_index: Option<u16>, |
| } |
| |
| struct Metrics { |
| l: i32, |
| t: i32, |
| r: i32, |
| b: i32, |
| } |
| |
| impl Metrics { |
| fn width(&self) -> usize { |
| (self.r - self.l) as usize |
| } |
| |
| fn height(&self) -> usize { |
| (self.b - self.t) as usize |
| } |
| } |
| |
| impl<'a> Font<'a> { |
| fn metrics_and_affine( |
| &self, xmin: i16, ymin: i16, xmax: i16, ymax: i16, size: u32, |
| ) -> (Metrics, Affine) { |
| let ppem = self.head.units_per_em(); |
| let scale = (size as f32) / (ppem as f32); |
| let l = (xmin as f32 * scale).floor() as i32; |
| let t = (ymax as f32 * -scale).floor() as i32; |
| let r = (xmax as f32 * scale).ceil() as i32; |
| let b = (ymin as f32 * -scale).ceil() as i32; |
| let metrics = Metrics { |
| l: l, |
| t: t, |
| r: r, |
| b: b, |
| }; |
| let z = Affine::new(scale, 0.0, 0.0, -scale, -l as f32, -t as f32); |
| (metrics, z) |
| } |
| |
| fn render_glyph_inner(&self, raster: &mut Raster, z: &Affine, glyph: &Glyph) { |
| match *glyph { |
| Glyph::Simple(ref s) => { |
| let mut p = s.points(); |
| for n in s.contour_sizes() { |
| //println!("n = {}", n); |
| //let v = path_from_pts(p.by_ref().take(n)).collect::<Vec<_>>(); |
| //println!("size = {}", v.len()); |
| draw_path(raster, z, &mut path_from_pts(p.by_ref().take(n))); |
| } |
| } |
| Glyph::Compound(ref c) => { |
| for (glyph_index, affine) in c.components() { |
| //println!("component {} {:?}", glyph_index, affine); |
| let concat = Affine::concat(z, &affine); |
| if let Some(component_glyph) = self.get_glyph(glyph_index) { |
| self.render_glyph_inner(raster, &concat, &component_glyph); |
| } |
| } |
| } |
| _ => { |
| println!("unhandled glyph case"); |
| } |
| } |
| } |
| |
| pub fn render_glyph(&self, glyph_id: u16, size: u32) -> Option<GlyphBitmap> { |
| let glyph = self.get_glyph(glyph_id); |
| match glyph { |
| Some(Glyph::Simple(ref s)) => { |
| let (xmin, ymin, xmax, ymax) = s.bbox(); |
| let (metrics, z) = self.metrics_and_affine(xmin, ymin, xmax, ymax, size); |
| let mut raster = Raster::new(metrics.width(), metrics.height()); |
| //dump_glyph(SimpleGlyph(s)); |
| self.render_glyph_inner(&mut raster, &z, glyph.as_ref().unwrap()); |
| //None |
| Some(GlyphBitmap { |
| width: metrics.width(), |
| height: metrics.height(), |
| left: metrics.l, |
| top: metrics.t, |
| data: raster.get_bitmap(), |
| }) |
| } |
| Some(Glyph::Compound(ref c)) => { |
| let (xmin, ymin, xmax, ymax) = c.bbox(); |
| let (metrics, z) = self.metrics_and_affine(xmin, ymin, xmax, ymax, size); |
| let mut raster = Raster::new(metrics.width(), metrics.height()); |
| self.render_glyph_inner(&mut raster, &z, glyph.as_ref().unwrap()); |
| Some(GlyphBitmap { |
| width: metrics.width(), |
| height: metrics.height(), |
| left: metrics.l, |
| top: metrics.t, |
| data: raster.get_bitmap(), |
| }) |
| } |
| _ => { |
| println!("glyph {} error", glyph_id); |
| None |
| } |
| } |
| } |
| |
| fn get_glyph(&self, glyph_ix: u16) -> Option<Glyph> { |
| if glyph_ix >= self.maxp.num_glyphs() { |
| return None; |
| } |
| let fmt = self.head.index_to_loc_format(); |
| match self.loca { |
| Some(ref loca) => match ( |
| loca.get_off(glyph_ix, fmt), |
| loca.get_off(glyph_ix + 1, fmt), |
| self.glyf, |
| ) { |
| (Some(off0), Some(off1), Some(glyf)) => if off0 == off1 { |
| Some(Glyph::Empty) |
| } else { |
| let glyph_data = &glyf[off0 as usize..off1 as usize]; |
| if get_i16(glyph_data, 0) == Some(-1) { |
| Some(Glyph::Compound(CompoundGlyph { data: glyph_data })) |
| } else { |
| Some(Glyph::Simple(SimpleGlyph { data: glyph_data })) |
| } |
| }, |
| (_, _, _) => None, |
| }, |
| None => None, |
| } |
| } |
| |
| pub fn lookup_glyph_id(&self, code_point: u32) -> Option<u16> { |
| match self.encoding_index { |
| Some(encoding_index) => { |
| if code_point > u16::max_value() as u32 { |
| return None; |
| } |
| |
| self.cmap |
| .as_ref() |
| .unwrap() |
| .get_encoding_format_4_at(encoding_index) |
| .unwrap() |
| .lookup_glyph_id(code_point as u16) |
| } |
| None => None, |
| } |
| } |
| } |
| |
| #[derive(Debug)] |
| enum PathOp { |
| MoveTo(Point), |
| LineTo(Point), |
| QuadTo(Point, Point), |
| } |
| |
| use self::PathOp::{LineTo, MoveTo, QuadTo}; |
| |
| struct BezPathOps<T> { |
| inner: T, |
| first_oncurve: Option<Point>, |
| first_offcurve: Option<Point>, |
| last_offcurve: Option<Point>, |
| alldone: bool, |
| closing: bool, |
| } |
| |
| fn path_from_pts<T: Iterator>(inner: T) -> BezPathOps<T> { |
| BezPathOps { |
| inner: inner, |
| first_oncurve: None, |
| first_offcurve: None, |
| last_offcurve: None, |
| alldone: false, |
| closing: false, |
| } |
| } |
| |
| impl<I> Iterator for BezPathOps<I> |
| where |
| I: Iterator<Item = (bool, i16, i16)>, |
| { |
| type Item = PathOp; |
| fn next(&mut self) -> Option<PathOp> { |
| loop { |
| if self.closing { |
| if self.alldone { |
| return None; |
| } else { |
| match (self.first_offcurve, self.last_offcurve) { |
| (None, None) => { |
| self.alldone = true; |
| return Some(LineTo(self.first_oncurve.unwrap())); |
| } |
| (None, Some(last_offcurve)) => { |
| self.alldone = true; |
| return Some(QuadTo(last_offcurve, self.first_oncurve.unwrap())); |
| } |
| (Some(first_offcurve), None) => { |
| self.alldone = true; |
| return Some(QuadTo(first_offcurve, self.first_oncurve.unwrap())); |
| } |
| (Some(first_offcurve), Some(last_offcurve)) => { |
| self.last_offcurve = None; |
| return Some(QuadTo( |
| last_offcurve, |
| Point::lerp(0.5, &last_offcurve, &first_offcurve), |
| )); |
| } |
| } |
| } |
| } else { |
| match self.inner.next() { |
| None => { |
| self.closing = true; |
| } |
| Some((oncurve, x, y)) => { |
| let p = Point::new(x, y); |
| if self.first_oncurve.is_none() { |
| if oncurve { |
| self.first_oncurve = Some(p); |
| return Some(MoveTo(p)); |
| } else { |
| match self.first_offcurve { |
| None => self.first_offcurve = Some(p), |
| Some(first_offcurve) => { |
| let midp = Point::lerp(0.5, &first_offcurve, &p); |
| self.first_oncurve = Some(midp); |
| self.last_offcurve = Some(p); |
| return Some(MoveTo(midp)); |
| } |
| } |
| } |
| } else { |
| match (self.last_offcurve, oncurve) { |
| (None, false) => self.last_offcurve = Some(p), |
| (None, true) => return Some(LineTo(p)), |
| (Some(last_offcurve), false) => { |
| self.last_offcurve = Some(p); |
| return Some(QuadTo( |
| last_offcurve, |
| Point::lerp(0.5, &last_offcurve, &p), |
| )); |
| } |
| (Some(last_offcurve), true) => { |
| self.last_offcurve = None; |
| return Some(QuadTo(last_offcurve, p)); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| #[derive(Debug)] |
| pub enum FontError { |
| Invalid, |
| } |
| |
| pub fn parse(data: &[u8]) -> Result<Font, FontError> { |
| if data.len() < 12 { |
| return Err(FontError::Invalid); |
| } |
| let version = get_u32(data, 0).unwrap(); |
| let num_tables = get_u16(data, 4).unwrap() as usize; |
| let _search_range = get_u16(data, 6).unwrap(); |
| let _entry_selector = get_u16(data, 8).unwrap(); |
| let _range_shift = get_u16(data, 10).unwrap(); |
| let mut tables = HashMap::new(); |
| for i in 0..num_tables { |
| let header = &data[12 + i * 16..12 + (i + 1) * 16]; |
| let tag = get_u32(header, 0).unwrap(); |
| let _check_sum = get_u32(header, 4).unwrap(); |
| let offset = get_u32(header, 8).unwrap(); |
| let length = get_u32(header, 12).unwrap(); |
| let table_data = &data[offset as usize..(offset + length) as usize]; |
| //println!("{}: {}", Tag(tag), table_data.len()); |
| tables.insert(Tag(tag), table_data); |
| } |
| let head = Head(*tables.get(&Tag::from_str("head")).unwrap()); // todo: don't fail |
| let maxp = Maxp { |
| data: *tables.get(&Tag::from_str("maxp")).unwrap(), |
| }; |
| let loca = tables.get(&Tag::from_str("loca")).map(|&data| Loca(data)); |
| let glyf = tables.get(&Tag::from_str("glyf")).map(|&data| data); |
| let cmap = tables.get(&Tag::from_str("cmap")).map(|&data| Cmap(data)); |
| let encoding_index = cmap.as_ref().and_then(|cmap| cmap.find_format_4_encoding()); |
| let f = Font { |
| _version: version, |
| _tables: tables, |
| head: head, |
| maxp: maxp, |
| loca: loca, |
| cmap: cmap, |
| glyf: glyf, |
| encoding_index: encoding_index, |
| }; |
| //println!("version = {:x}", version); |
| Ok(f) |
| } |
| |
| /* |
| fn dump_glyph(g: Glyph) { |
| match g { |
| Glyph::Empty => println!("empty"), |
| Glyph::Simple(s) => { |
| //println!("{} contours", s.number_of_contours()) |
| let mut p = s.points(); |
| for n in s.contour_sizes() { |
| for _ in 0..n { |
| println!("{:?}", p.next().unwrap()); |
| } |
| println!("z"); |
| } |
| let mut p = s.points(); |
| for n in s.contour_sizes() { |
| for pathop in path_from_pts(p.by_ref().take(n)) { |
| println!("{:?}", pathop); |
| } |
| } |
| }, |
| _ => println!("other") |
| } |
| } |
| */ |
| |
| /* |
| fn dump(data: Vec<u8>) { |
| println!("length is {}", data.len()); |
| match parse(&data) { |
| Ok(font) => { |
| println!("numGlyphs = {}", font.maxp.num_glyphs()); |
| for i in 0.. font.maxp.num_glyphs() { |
| println!("glyph {}", i); |
| match font.get_glyph(i) { |
| Some(g) => dump_glyph(g), |
| None => println!("glyph {} error", i) |
| } |
| } |
| }, |
| _ => () |
| } |
| } |
| */ |
| |
| fn draw_path<I: Iterator<Item = PathOp>>(r: &mut Raster, z: &Affine, path: &mut I) { |
| let mut lastp = Point::new(0i16, 0i16); |
| for op in path { |
| match op { |
| MoveTo(p) => lastp = p, |
| LineTo(p) => { |
| r.draw_line(&affine_pt(z, &lastp), &affine_pt(z, &p)); |
| lastp = p |
| } |
| QuadTo(p1, p2) => { |
| r.draw_quad( |
| &affine_pt(z, &lastp), |
| &affine_pt(z, &p1), |
| &affine_pt(z, &p2), |
| ); |
| lastp = p2; |
| } |
| } |
| } |
| } |
| |
| pub struct GlyphBitmap { |
| pub width: usize, |
| pub height: usize, |
| pub left: i32, |
| pub top: i32, |
| pub data: Vec<u8>, |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| |
| use font::parse; |
| |
| static FONT_DATA: &'static [u8] = |
| include_bytes!("../fonts/notomono-hinted/NotoMono-Regular.ttf"); |
| |
| #[test] |
| fn test_cmap_format_4() { |
| let font = parse(&FONT_DATA).unwrap(); |
| let cmap = font.cmap.as_ref().unwrap(); |
| assert!(cmap.get_encoding_record(cmap.get_num_tables()).is_none()); |
| assert!(cmap.get_encoding(cmap.get_num_tables()).is_none()); |
| assert_eq!(font.lookup_glyph_id('A' as u32).unwrap(), 36); |
| assert_eq!(font.lookup_glyph_id(0x3c8).unwrap(), 405); |
| assert_eq!(font.lookup_glyph_id(0xfffd).unwrap(), 589); |
| assert_eq!(font.lookup_glyph_id(0x232B).is_none(), true); |
| assert_eq!(font.lookup_glyph_id(0x1000232B).is_none(), true); |
| // test for panics |
| for i in 0..0x1ffff { |
| font.lookup_glyph_id(i); |
| } |
| } |
| } |