Read the OS/2 metrics to determine the right line spacing
This commit is contained in:
parent
55e1502eb2
commit
6261c87b89
|
@ -117,7 +117,8 @@ fn main() {
|
|||
let space_advance = font.metrics_for_glyph(glyph_mapping.glyph_for(' ' as u32).unwrap())
|
||||
.unwrap()
|
||||
.advance_width as u32;
|
||||
let line_spacing = font.units_per_em() as u32;
|
||||
let line_spacing = (font.ascender() as i32 - font.descender() as i32 + font.line_gap() as i32)
|
||||
as u32;
|
||||
|
||||
let (mut current_x, mut current_y) = (0, line_spacing);
|
||||
for word in text.split_whitespace() {
|
||||
|
|
35
src/lib.rs
35
src/lib.rs
|
@ -10,8 +10,39 @@
|
|||
|
||||
//! A high-performance GPU rasterizer for OpenType fonts.
|
||||
//!
|
||||
//! Pathfinder rasterizes glyphs from a `.ttf`, `.ttc`, or `.otf` file loaded in memory to an atlas
|
||||
//! texture.
|
||||
//! ## Introduction
|
||||
//!
|
||||
//! Pathfinder is a fast, practical GPU-based rasterizer for OpenType fonts using OpenGL 4.3. It
|
||||
//! features:
|
||||
//!
|
||||
//! * Very low setup time. Glyph outlines can go from the `.otf` file to the GPU in a form ready
|
||||
//! for rasterization in less than a microsecond. There is no expensive tessellation or
|
||||
//! preprocessing step.
|
||||
//!
|
||||
//! * High quality antialiasing. Unlike techniques that rely on multisample antialiasing,
|
||||
//! Pathfinder computes exact fractional trapezoidal area coverage on a per-pixel basis.
|
||||
//!
|
||||
//! * Fast rendering, even at small pixel sizes. On typical systems, Pathfinder should easily
|
||||
//! exceed the performance of the best CPU rasterizers.
|
||||
//!
|
||||
//! * Low memory consumption. The only memory overhead over the glyph and outline storage itself is
|
||||
//! that of a coverage buffer which typically consumes somewhere between 4MB-16MB and can be
|
||||
//! discarded under memory pressure. Outlines are stored on-GPU in a compressed format and usually
|
||||
//! take up only a few dozen kilobytes.
|
||||
//!
|
||||
//! * Portability to most GPUs manufactured in the last few years, including integrated GPUs.
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! See `examples/generate-atlas.rs` for a simple example.
|
||||
//!
|
||||
//! Typically, the steps to use Pathfidner are:
|
||||
//!
|
||||
//! 1. Create a `Rasterizer` object. This holds the OpenGL state.
|
||||
//!
|
||||
//! 2. Open the font from disk (or elsewhere), and call `Font::new()` (or
|
||||
//! `Font::from_collection_index` in the case of a `.ttc` or `.dfont` collection) to load it.
|
||||
//!
|
||||
|
||||
#![cfg_attr(test, feature(test))]
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ use util::Jump;
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HheaTable {
|
||||
pub line_gap: i16,
|
||||
pub number_of_h_metrics: u16,
|
||||
}
|
||||
|
||||
|
@ -29,11 +30,17 @@ impl HheaTable {
|
|||
return Err(Error::UnsupportedHheaVersion)
|
||||
}
|
||||
|
||||
// Read the height-related metrics.
|
||||
let _ascender = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
||||
let _descender = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
||||
let line_gap = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
||||
|
||||
// Read the number of `hmtx` entries.
|
||||
try!(reader.jump(mem::size_of::<u16>() * 15).map_err(Error::eof));
|
||||
try!(reader.jump(mem::size_of::<u16>() * 12).map_err(Error::eof));
|
||||
let number_of_h_metrics = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
|
||||
|
||||
Ok(HheaTable {
|
||||
line_gap: line_gap,
|
||||
number_of_h_metrics: number_of_h_metrics,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ use otf::hhea::HheaTable;
|
|||
use otf::hmtx::{HmtxTable, HorizontalMetrics};
|
||||
use otf::kern::KernTable;
|
||||
use otf::loca::LocaTable;
|
||||
use otf::os_2::Os2Table;
|
||||
use outline::GlyphBounds;
|
||||
use std::mem;
|
||||
use std::u16;
|
||||
|
@ -31,6 +32,7 @@ mod hhea;
|
|||
mod hmtx;
|
||||
mod kern;
|
||||
mod loca;
|
||||
mod os_2;
|
||||
|
||||
const CMAP: u32 = ((b'c' as u32) << 24) |
|
||||
((b'm' as u32) << 16) |
|
||||
|
@ -60,6 +62,10 @@ const LOCA: u32 = ((b'l' as u32) << 24) |
|
|||
((b'o' as u32) << 16) |
|
||||
((b'c' as u32) << 8) |
|
||||
(b'a' as u32);
|
||||
const OS_2: u32 = ((b'O' as u32) << 24) |
|
||||
((b'S' as u32) << 16) |
|
||||
((b'/' as u32) << 8) |
|
||||
(b'2' as u32);
|
||||
const TTCF: u32 = ((b't' as u32) << 24) |
|
||||
((b't' as u32) << 16) |
|
||||
((b'c' as u32) << 8) |
|
||||
|
@ -90,6 +96,7 @@ pub struct Font<'a> {
|
|||
head: HeadTable,
|
||||
hhea: HheaTable,
|
||||
hmtx: HmtxTable<'a>,
|
||||
os_2: Os2Table,
|
||||
|
||||
glyf: Option<GlyfTable<'a>>,
|
||||
loca: Option<LocaTable<'a>>,
|
||||
|
@ -173,8 +180,8 @@ impl<'a> Font<'a> {
|
|||
|
||||
let (mut cmap_table, mut head_table) = (None, None);
|
||||
let (mut hhea_table, mut hmtx_table) = (None, None);
|
||||
let (mut glyf_table, mut loca_table) = (None, None);
|
||||
let mut kern_table = None;
|
||||
let (mut glyf_table, mut kern_table) = (None, None);
|
||||
let (mut loca_table, mut os_2_table) = (None, None);
|
||||
|
||||
for _ in 0..num_tables {
|
||||
let table_id = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
|
||||
|
@ -191,8 +198,9 @@ impl<'a> Font<'a> {
|
|||
HHEA => &mut hhea_table,
|
||||
HMTX => &mut hmtx_table,
|
||||
GLYF => &mut glyf_table,
|
||||
LOCA => &mut loca_table,
|
||||
KERN => &mut kern_table,
|
||||
LOCA => &mut loca_table,
|
||||
OS_2 => &mut os_2_table,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
|
@ -218,6 +226,7 @@ impl<'a> Font<'a> {
|
|||
head: try!(HeadTable::new(try!(head_table.ok_or(Error::RequiredTableMissing)))),
|
||||
hhea: try!(HheaTable::new(try!(hhea_table.ok_or(Error::RequiredTableMissing)))),
|
||||
hmtx: HmtxTable::new(try!(hmtx_table.ok_or(Error::RequiredTableMissing))),
|
||||
os_2: try!(Os2Table::new(try!(os_2_table.ok_or(Error::RequiredTableMissing)))),
|
||||
|
||||
glyf: glyf_table.map(GlyfTable::new),
|
||||
loca: loca_table,
|
||||
|
@ -383,6 +392,33 @@ impl<'a> Font<'a> {
|
|||
Some(kern) => kern.kerning_for_glyph_pair(left_glyph_id, right_glyph_id).unwrap_or(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the distance from the baseline to the top of the text box in font units.
|
||||
///
|
||||
/// The following expression computes the baseline-to-baseline height:
|
||||
/// `font.ascender() - font.descender() + font.line_gap()`.
|
||||
#[inline]
|
||||
pub fn ascender(&self) -> i16 {
|
||||
self.os_2.typo_ascender
|
||||
}
|
||||
|
||||
/// Returns the distance from the baseline to the bottom of the text box in font units.
|
||||
///
|
||||
/// The following expression computes the baseline-to-baseline height:
|
||||
/// `font.ascender() - font.descender() + font.line_gap()`.
|
||||
#[inline]
|
||||
pub fn descender(&self) -> i16 {
|
||||
self.os_2.typo_descender
|
||||
}
|
||||
|
||||
/// Returns the recommended extra gap between lines in font units.
|
||||
///
|
||||
/// The following expression computes the baseline-to-baseline height:
|
||||
/// `font.ascender() - font.descender() + font.line_gap()`.
|
||||
#[inline]
|
||||
pub fn line_gap(&self) -> i16 {
|
||||
self.os_2.typo_line_gap
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when parsing OpenType fonts.
|
||||
|
@ -412,6 +448,8 @@ pub enum Error {
|
|||
UnsupportedHeadVersion,
|
||||
/// We don't support the declared version of the font's horizontal metrics.
|
||||
UnsupportedHheaVersion,
|
||||
/// We don't support the declared version of the font's OS/2 and Windows table.
|
||||
UnsupportedOs2Version,
|
||||
/// A required table is missing.
|
||||
RequiredTableMissing,
|
||||
/// The glyph is a composite glyph.
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2017 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use otf::{Error, FontTable};
|
||||
use std::mem;
|
||||
use util::Jump;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Os2Table {
|
||||
pub typo_ascender: i16,
|
||||
pub typo_descender: i16,
|
||||
pub typo_line_gap: i16,
|
||||
}
|
||||
|
||||
impl Os2Table {
|
||||
pub fn new(table: FontTable) -> Result<Os2Table, Error> {
|
||||
let mut reader = table.bytes;
|
||||
|
||||
// We should be compatible with all versions. If this is greater than version 5, follow
|
||||
// Postel's law and hope for the best.
|
||||
let version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
|
||||
|
||||
// Skip to the line gap.
|
||||
try!(reader.jump(mem::size_of::<u16>() * 15).map_err(Error::eof));
|
||||
try!(reader.jump(10).map_err(Error::eof));
|
||||
if version == 0 {
|
||||
try!(reader.jump(mem::size_of::<u32>() * 2).map_err(Error::eof));
|
||||
} else {
|
||||
try!(reader.jump(mem::size_of::<u32>() * 5).map_err(Error::eof));
|
||||
}
|
||||
try!(reader.jump(mem::size_of::<u16>() * 3).map_err(Error::eof));
|
||||
|
||||
// Read the line spacing information.
|
||||
let typo_ascender = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
||||
let typo_descender = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
||||
let typo_line_gap = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
||||
|
||||
Ok(Os2Table {
|
||||
typo_ascender: typo_ascender,
|
||||
typo_descender: typo_descender,
|
||||
typo_line_gap: typo_line_gap,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue