///|
/// Font header (head table)
struct FontHead {
units_per_em : Int
index_to_loc_format : Int // 0 = short, 1 = long
} derive(Eq, Debug)
///|
pub extend FontHead with Debug::{to_repr}
///|
pub extend FontHead with Eq::{not_equal, equal}
///|
/// Horizontal header (hhea table)
struct FontHhea {
ascent : Int
descent : Int
line_gap : Int
num_h_metrics : Int
} derive(Eq, Debug)
///|
pub extend FontHhea with Debug::{to_repr}
///|
pub extend FontHhea with Eq::{not_equal, equal}
///|
/// Horizontal metrics
struct LongHorMetric {
advance_width : Int
lsb : Int // left side bearing
} derive(Eq, Debug)
///|
pub extend LongHorMetric with Debug::{to_repr}
///|
pub extend LongHorMetric with Eq::{not_equal, equal}
///|
/// Simple glyph point
struct GlyphPoint {
x : Int
y : Int
on_curve : Bool
} derive(Eq, Debug)
///|
pub extend GlyphPoint with Debug::{to_repr}
///|
pub extend GlyphPoint with Eq::{not_equal, equal}
///|
/// Glyph contour
struct GlyphContour {
points : Array[GlyphPoint]
} derive(Debug)
///|
pub extend GlyphContour with Debug::{to_repr}
///|
/// Simple glyph data
struct SimpleGlyph {
contours : Array[GlyphContour]
xmin : Int
ymin : Int
xmax : Int
ymax : Int
} derive(Debug)
///|
pub extend SimpleGlyph with Debug::{to_repr}
///|
/// A component of a composite glyph
struct GlyphComponent {
glyph_index : Int
x_offset : Int
y_offset : Int
scale_x : Double
scale_y : Double
scale_xy : Double
scale_yx : Double
} derive(Debug)
///|
pub extend GlyphComponent with Debug::{to_repr}
///|
/// Glyph data (simple or composite)
enum GlyphData {
Simple(SimpleGlyph)
Composite(Array[GlyphComponent])
Empty
} derive(Debug)
///|
pub extend GlyphData with Debug::{to_repr}
///|
/// Table record in font file
struct TableRecord {
tag : String
checksum : UInt
offset : UInt
length : UInt
} derive(Eq, Debug)
///|
pub extend TableRecord with Debug::{to_repr}
///|
pub extend TableRecord with Eq::{not_equal, equal}
///|
/// Kerning pair
struct KernPair {
left : Int
right : Int
value : Int
} derive(Eq, Debug)
///|
pub extend KernPair with Debug::{to_repr}
///|
pub extend KernPair with Eq::{not_equal, equal}
///|
/// Parsed font data
struct Font {
head : FontHead
hhea : FontHhea
hmtx : Array[LongHorMetric]
cmap : Map[Int, Int] // char code -> glyph index
glyphs : Array[GlyphData]
glyph_count : Int
kern : Array[KernPair] // kerning pairs
} derive(Debug)
///|
pub extend Font with Debug::{to_repr}
///|
/// Get font metrics
fn Font::get_metrics(self : Font) -> (Int, Int, Int) {
(self.hhea.ascent, self.hhea.descent, self.hhea.line_gap)
}
///|
/// Get units per em
fn Font::get_units_per_em(self : Font) -> Int {
self.head.units_per_em
}
///|
/// Map character to glyph index
fn Font::char_to_glyph(self : Font, char_code : Int) -> Int {
match self.cmap.get(char_code) {
Some(idx) => idx
None => 0 // .notdef glyph
}
}
///|
/// Return whether this font maps a Unicode codepoint to a drawable glyph.
fn Font::has_glyph_for_char(self : Font, char_code : Int) -> Bool {
self.char_to_glyph(char_code) > 0
}
///|
/// Get glyph data, resolving composite glyphs
fn Font::get_glyph(self : Font, glyph_id : Int) -> GlyphData {
guard glyph_id >= 0 && glyph_id < self.glyph_count else { return Empty }
let data = self.glyphs[glyph_id]
match data {
Composite(components) => resolve_composite_glyph(self, components)
_ => data
}
}
///|
/// Resolve a composite glyph by combining component glyphs
fn resolve_composite_glyph(
font : Font,
components : Array[GlyphComponent],
) -> GlyphData {
let all_contours = []
for component in components {
let glyph_data = font.get_glyph(component.glyph_index)
match glyph_data {
Simple(simple) =>
// Transform and add contours
for contour in simple.contours {
let transformed_points = []
for point in contour.points {
let x = point.x.to_double() * component.scale_x +
point.y.to_double() * component.scale_xy +
component.x_offset.to_double()
let y = point.x.to_double() * component.scale_yx +
point.y.to_double() * component.scale_y +
component.y_offset.to_double()
transformed_points.push({
x: x.to_int(),
y: y.to_int(),
on_curve: point.on_curve,
})
}
all_contours.push({ points: transformed_points, })
}
_ => () // Skip empty or unresolved composite glyphs
}
}
guard all_contours.length() > 0 else { return Empty }
// Calculate bounding box from contours
let mut xmin = all_contours[0].points[0].x
let mut ymin = all_contours[0].points[0].y
let mut xmax = xmin
let mut ymax = ymin
for contour in all_contours {
for point in contour.points {
if point.x < xmin {
xmin = point.x
}
if point.y < ymin {
ymin = point.y
}
if point.x > xmax {
xmax = point.x
}
if point.y > ymax {
ymax = point.y
}
}
}
Simple({ contours: all_contours, xmin, ymin, xmax, ymax, })
}
///|
/// Get horizontal metrics for a glyph
fn Font::get_h_metrics(self : Font, glyph_id : Int) -> (Int, Int) {
let idx = if glyph_id < self.hmtx.length() {
glyph_id
} else {
self.hmtx.length() - 1
}
let m = self.hmtx[idx]
(m.advance_width, m.lsb)
}
///|
/// Get kerning value between two glyphs
fn Font::get_kerning(self : Font, left_glyph : Int, right_glyph : Int) -> Int {
for pair in self.kern {
if pair.left == left_glyph && pair.right == right_glyph {
return pair.value
}
}
0
}