// Copyright 2025 International Digital Economy Academy
//
// 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.
///|
/// Minimal glyf loader for TrueType hinting.
///
/// This intentionally supports only simple glyphs for now (sufficient for the
/// hint parity fixtures).
const TAG_HEAD_TT : UInt = 0x68656164 // "head"
///|
const TAG_LOCA_TT : UInt = 0x6C6F6361 // "loca"
///|
const TAG_MAXP_TT2 : UInt = 0x6D617870 // "maxp"
///|
const TAG_GLYF_TT : UInt = 0x676C7966 // "glyf"
///|
const TAG_HHEA_TT : UInt = 0x68686561 // "hhea"
///|
const TAG_HMTX_TT : UInt = 0x686D7478 // "hmtx"
///|
const TAG_OS2_TT : UInt = 0x4F532F32 // "OS/2"
///|
fn tt2_read_u16_be(view : BytesView, offset : Int) -> Int? {
if offset < 0 || offset + 2 > view.length() {
None
} else {
let b0 = view.at(offset).to_int()
let b1 = view.at(offset + 1).to_int()
Some((b0 << 8) | b1)
}
}
///|
fn tt2_read_i16_be(view : BytesView, offset : Int) -> Int? {
match tt2_read_u16_be(view, offset) {
None => None
Some(u) => Some(if u >= 0x8000 { u - 0x10000 } else { u })
}
}
///|
fn tt2_read_u32_be(view : BytesView, offset : Int) -> UInt? {
if offset < 0 || offset + 4 > view.length() {
None
} else {
let b0 = view.at(offset).to_uint()
let b1 = view.at(offset + 1).to_uint()
let b2 = view.at(offset + 2).to_uint()
let b3 = view.at(offset + 3).to_uint()
Some((b0 << 24) | (b1 << 16) | (b2 << 8) | b3)
}
}
///|
fn tt2_scale_funits_to_26_6(value : Int, scale_bits : Int) -> Int {
let v = value.to_int64() * scale_bits.to_int64()
let adj = 0x8000 - (if v < 0 { 1 } else { 0 })
((v + adj.to_int64()) >> 16).to_int()
}
///|
/// Scales a font-unit coordinate plus a 16.16 gvar delta into 26.6 bits.
fn tt2_scale_funits_plus_delta_fixed_to_26_6(
value : Int,
delta_bits : Int,
scale_bits : Int,
) -> Int {
// Match fontations' scaling path for gvar deltas:
// - convert delta 16.16 -> 26.6 (Fixed::to_f26dot6 rounding)
// - add to value in 26.6
// - multiply by 16.16 scale (stored in `scale_bits`)
// - undo the extra 26.6 shift (F26Dot6::to_i32 rounding)
let delta_26_6 = ((delta_bits.to_int64() + (0x200).to_int64()) >> 10).to_int()
let base_26_6 = (value << 6) + delta_26_6
let tmp = tt_hint_mul_16_16(base_26_6, scale_bits)
((tmp.to_int64() + (32).to_int64()) >> 6).to_int()
}
///|
fn tt2_round_grid_26_6(bits : Int) -> Int {
if bits >= 0 {
tt_hint_round_26(bits).max(0)
} else {
(-tt_hint_round_26(-bits)).min(0)
}
}
///|
fn tt2_os2_vmetrics(font : @moon_skrifa.FontRef) -> (Int, Int) {
// Matches fontations Outlines::new: use OS/2 sTypoAscender/Descender,
// defaulting to 0 if the table is missing.
match font.table(TAG_OS2_TT) {
None => (0, 0)
Some(os2) => {
// Offsets: sTypoAscender @68, sTypoDescender @70.
// Use 0 when the table is too short.
let asc = tt2_read_i16_be(os2, 68).unwrap_or(0)
let desc = tt2_read_i16_be(os2, 70).unwrap_or(0)
(asc, desc)
}
}
}
///|
fn tt2_glyph_slice(
font : @moon_skrifa.FontRef,
gid : @moon_skrifa.GlyphId,
) -> Result[BytesView, DrawError] {
let head = match font.table(TAG_HEAD_TT) {
None => return Err(Read)
Some(v) => v
}
let loca = match font.table(TAG_LOCA_TT) {
None => return Err(Read)
Some(v) => v
}
let glyf = match font.table(TAG_GLYF_TT) {
None => return Err(Read)
Some(v) => v
}
let maxp = match font.table(TAG_MAXP_TT2) {
None => return Err(Read)
Some(v) => v
}
let num_glyphs = match tt2_read_u16_be(maxp, 4) {
None => return Err(Read)
Some(v) => v
}
let idx = gid.to_uint64().to_int()
if idx < 0 || idx >= num_glyphs {
return Err(GlyphNotFound(gid))
}
let index_to_loc_format = match tt2_read_i16_be(head, 50) {
None => return Err(Read)
Some(v) => v
}
let (start, end) = if index_to_loc_format == 0 {
let need = (num_glyphs + 1) * 2
if loca.length() < need {
return Err(Read)
}
let o0 = match tt2_read_u16_be(loca, idx * 2) {
None => return Err(Read)
Some(v) => v
}
let o0 = o0 * 2
let o1 = match tt2_read_u16_be(loca, (idx + 1) * 2) {
None => return Err(Read)
Some(v) => v
}
let o1 = o1 * 2
(o0, o1)
} else {
let need = (num_glyphs + 1) * 4
if loca.length() < need {
return Err(Read)
}
let o0_u = match tt2_read_u32_be(loca, idx * 4) {
None => return Err(Read)
Some(v) => v
}
let o1_u = match tt2_read_u32_be(loca, (idx + 1) * 4) {
None => return Err(Read)
Some(v) => v
}
let o0 = o0_u.to_uint64().to_int()
let o1 = o1_u.to_uint64().to_int()
(o0, o1)
}
if start < 0 || end < start || end > glyf.length() {
Err(Read)
} else {
Ok(glyf[start:end])
}
}
///|
fn tt2_hmtx_metrics(
font : @moon_skrifa.FontRef,
gid : @moon_skrifa.GlyphId,
) -> (Int, Int)? {
let hhea = match font.table(TAG_HHEA_TT) {
None => return None
Some(v) => v
}
let hmtx = match font.table(TAG_HMTX_TT) {
None => return None
Some(v) => v
}
let num_hmetrics = tt2_read_u16_be(hhea, 34).unwrap_or(0)
let idx = gid.to_uint64().to_int()
if idx < 0 {
return None
}
if num_hmetrics <= 0 {
return None
}
let mut aw = 0
let mut lsb = 0
if idx < num_hmetrics {
let off = idx * 4
if off + 4 > hmtx.length() {
return None
}
aw = tt2_read_u16_be(hmtx, off).unwrap_or(0)
lsb = tt2_read_i16_be(hmtx, off + 2).unwrap_or(0)
} else {
let last_off = (num_hmetrics - 1) * 4
if last_off + 4 > hmtx.length() {
return None
}
aw = tt2_read_u16_be(hmtx, last_off).unwrap_or(0)
let lsb_off = num_hmetrics * 4 + (idx - num_hmetrics) * 2
if lsb_off + 2 > hmtx.length() {
return None
}
lsb = tt2_read_i16_be(hmtx, lsb_off).unwrap_or(0)
}
Some((aw, lsb))
}
///|
fn tt2_decode_simple_glyph_for_hinting(
glyph : BytesView,
scale_bits : Int,
advance : Int,
lsb : Int,
os2_ascent : Int,
os2_descent : Int,
) -> (TtZone, BytesView)? {
if glyph.length() < 10 {
return None
}
let number_of_contours = tt2_read_i16_be(glyph, 0).unwrap_or(0)
if number_of_contours < 0 {
return None
}
let x_min = tt2_read_i16_be(glyph, 2).unwrap_or(0)
let mut off = 10
let contours : Array[Int] = Array::new()
if number_of_contours > 0 {
for _ in 0.. glyph.length() {
return None
}
let instr = glyph[off:off + instruction_len]
off = off + instruction_len
let num_points = if contours.is_empty() {
0
} else {
let last_end = contours.at(contours.length() - 1)
last_end + 1
}
if num_points < 0 {
return None
}
// Decode flags.
let point_flags : Array[Int] = Array::new()
while point_flags.length() < num_points {
if off >= glyph.length() {
return None
}
let flag = glyph.at(off).to_int()
off = off + 1
point_flags.push(flag)
if (flag & 0x08) != 0 {
if off >= glyph.length() {
return None
}
let repeat = glyph.at(off).to_int()
off = off + 1
for _ in 0..= glyph.length() {
return None
}
let b = glyph.at(off).to_int()
off = off + 1
dxs.push(if same { b } else { -b })
} else if same {
dxs.push(0)
} else {
let d = tt2_read_i16_be(glyph, off).unwrap_or(0)
off = off + 2
dxs.push(d)
}
}
// Decode y deltas.
let dys : Array[Int] = Array::new()
for i in 0..= glyph.length() {
return None
}
let b = glyph.at(off).to_int()
off = off + 1
dys.push(if same { b } else { -b })
} else if same {
dys.push(0)
} else {
let d = tt2_read_i16_be(glyph, off).unwrap_or(0)
off = off + 2
dys.push(d)
}
}
// Build point arrays (+ phantom points).
let total_points = num_points + 4
let unscaled : Array[TtPoint] = Array::make(total_points, { x: 0, y: 0 })
let original : Array[TtPoint] = Array::make(total_points, { x: 0, y: 0 })
let points : Array[TtPoint] = Array::make(total_points, { x: 0, y: 0 })
let flags : Array[Int] = Array::make(total_points, 0)
let mut x = 0
let mut y = 0
for i in 0.. 0 {
for i in 0..<4 {
let p = points.at(num_points + i)
points.set(num_points + i, {
x: tt2_round_grid_26_6(p.x),
y: tt2_round_grid_26_6(p.y),
})
}
}
let zone = TtZone::{ unscaled, original, points, flags, contours }
Some((zone, instr))
}
///|
///|
const TT2_COMPOSITE_USE_MY_METRICS : Int = 0x0200
///|
priv enum Tt2Anchor {
Offset(Int, Int)
Point(Int, Int)
}
///|
priv struct Tt2Component {
flags : Int
gid : @moon_skrifa.GlyphId
anchor : Tt2Anchor
xx16 : Int
yx16 : Int
xy16 : Int
yy16 : Int
have_xform : Bool
}
///|
fn tt2_read_f2dot14_bits(view : BytesView, offset : Int) -> Int? {
tt2_read_i16_be(view, offset)
}
///|
fn tt2_f2dot14_to_16_16(bits : Int) -> Int {
bits * 4
}
///|
fn tt2_apply_xform_point(p : TtPoint, comp : Tt2Component) -> TtPoint {
if !comp.have_xform {
return p
}
let x = p.x
let y = p.y
let nx = tt_hint_mul_16_16(x, comp.xx16) + tt_hint_mul_16_16(y, comp.xy16)
let ny = tt_hint_mul_16_16(x, comp.yx16) + tt_hint_mul_16_16(y, comp.yy16)
{ x: nx, y: ny }
}
///|
fn tt2_abs_i32(v : Int) -> Int {
if v < 0 {
-v
} else {
v
}
}
///|
/// FreeType's guess for hypot(a, b) where inputs are 16.16 fixed values.
fn tt2_hypot_guess_16_16(a : Int, b : Int) -> Int {
let aa = tt2_abs_i32(a)
let bb = tt2_abs_i32(b)
if aa > bb {
(aa.to_int64() + (((3).to_int64() * bb.to_int64()) >> 3)).to_int()
} else {
(bb.to_int64() + (((3).to_int64() * aa.to_int64()) >> 3)).to_int()
}
}
///|
fn tt2_component_offset_bits(
comp : Tt2Component,
scale_bits : Int,
is_hinted : Bool,
delta_x : Int,
delta_y : Int,
) -> (Int, Int) {
let mut dx = 0
let mut dy = 0
match comp.anchor {
Offset(x, y) => {
dx = x
dy = y
}
_ => ()
}
if comp.have_xform &&
(
comp.flags &
(COMPOSITE_SCALED_COMPONENT_OFFSET | COMPOSITE_UNSCALED_COMPONENT_OFFSET)
) ==
COMPOSITE_SCALED_COMPONENT_OFFSET {
// FreeType's guess-based scaling for composite offsets.
//
// Match fontations-reference: x = Fixed::from_bits(x) * hypot(xx, xy).
dx = tt_hint_mul_16_16(dx, tt2_hypot_guess_16_16(comp.xx16, comp.xy16))
dy = tt_hint_mul_16_16(dy, tt2_hypot_guess_16_16(comp.yy16, comp.yx16))
}
// For composite gvar deltas, FreeType/fontations round off fractional parts
// and apply them in font units.
dx = dx + delta_x
dy = dy + delta_y
let dx_bits = tt2_scale_funits_to_26_6(dx, scale_bits)
let mut dy_bits = tt2_scale_funits_to_26_6(dy, scale_bits)
if is_hinted && (comp.flags & COMPOSITE_ROUND_XY_TO_GRID) != 0 {
// Only round the y-coordinate, per FreeType.
dy_bits = tt2_round_grid_26_6(dy_bits)
}
(dx_bits, dy_bits)
}
///|
fn tt2_zone_outline_count(zone : TtZone) -> Int? {
if zone.contours.is_empty() {
Some(0)
} else {
let last_end = zone.contours.at(zone.contours.length() - 1)
if last_end < 0 {
None
} else {
Some(last_end + 1)
}
}
}
///|
fn tt2_zone_phantoms(
zone : TtZone,
outline_points : Int,
) -> (TtPoint, TtPoint, TtPoint, TtPoint)? {
if outline_points < 0 || zone.points.length() < outline_points + 4 {
return None
}
Some(
(
zone.points.at(outline_points),
zone.points.at(outline_points + 1),
zone.points.at(outline_points + 2),
zone.points.at(outline_points + 3),
),
)
}
///|
fn tt2_load_glyph_zone_for_hinting_impl(
font : @moon_skrifa.FontRef,
gid : @moon_skrifa.GlyphId,
coords : ArrayView[@moon_skrifa.NormalizedCoord],
scale_bits : Int,
tt : TtHintInstance,
fpgm : BytesView,
prep : BytesView,
is_pedantic : Bool,
depth : Int,
) -> Result[TtZone, DrawError] {
if depth > 16 {
return Err(RecursionLimitExceeded(gid))
}
let glyph = match tt2_glyph_slice(font, gid) {
Err(e) => return Err(e)
Ok(v) => v
}
// Empty glyph (loca start == end) is valid. We still synthesize the four
// phantom points to compute metrics, matching upstream.
if glyph.length() == 0 {
let (aw, lsb) = tt2_hmtx_metrics(font, gid).unwrap_or((0, 0))
let (os2_ascent, os2_descent) = tt2_os2_vmetrics(font)
let phantom0_x = 0 - lsb
let phantom1_x = phantom0_x + aw
let unscaled : Array[TtPoint] = Array::make(4, { x: 0, y: 0 })
let original : Array[TtPoint] = Array::make(4, { x: 0, y: 0 })
let points : Array[TtPoint] = Array::make(4, { x: 0, y: 0 })
let flags : Array[Int] = Array::make(4, 0)
unscaled.set(0, { x: phantom0_x, y: 0 })
unscaled.set(1, { x: phantom1_x, y: 0 })
unscaled.set(2, { x: 0, y: os2_ascent })
unscaled.set(3, { x: 0, y: os2_descent })
for i in 0..<4 {
let p = unscaled.at(i)
let sx = tt2_scale_funits_to_26_6(p.x, scale_bits)
let sy = tt2_scale_funits_to_26_6(p.y, scale_bits)
let sp = TtPoint::{ x: sx, y: sy }
original.set(i, sp)
points.set(i, sp)
flags.set(i, 0)
}
return Ok({ unscaled, original, points, flags, contours: Array::new() })
}
if glyph.length() < 10 {
return Err(Read)
}
let number_of_contours = match tt2_read_i16_be(glyph, 0) {
None => return Err(Read)
Some(v) => v
}
let x_min = match tt2_read_i16_be(glyph, 2) {
None => return Err(Read)
Some(v) => v
}
let (aw, lsb) = tt2_hmtx_metrics(font, gid).unwrap_or((0, 0))
let (os2_ascent, os2_descent) = tt2_os2_vmetrics(font)
if number_of_contours >= 0 {
let (zone0, code) = match
tt2_decode_simple_glyph_for_hinting(
glyph, scale_bits, aw, lsb, os2_ascent, os2_descent,
) {
None => return Err(Read)
Some(v) => v
}
// Apply gvar deltas (including phantom points) for hinted glyph loading.
if !coords.is_empty() {
let point_count = match tt2_zone_outline_count(zone0) {
None => return Err(Read)
Some(v) => v
}
let xs : Array[Double] = Array::new()
let ys : Array[Double] = Array::new()
for i in 0.. ()
Some((dx_bits, dy_bits)) => {
let total_points = point_count + 4
// Recompute scaled points from unscaled + delta (preserving fractional parts),
// then round phantom points if we will execute instructions.
for i in 0.. 0 {
// For hinting, adjust unscaled points by rounding off the deltas.
zone0.unscaled.set(i, {
x: u.x + gvar_fixed16_16_to_i32(dx_bits.at(i)),
y: u.y + gvar_fixed16_16_to_i32(dy_bits.at(i)),
})
}
}
if code.length() > 0 {
for i in 0..<4 {
let p = zone0.points.at(point_count + i)
zone0.points.set(point_count + i, {
x: tt2_round_grid_26_6(p.x),
y: tt2_round_grid_26_6(p.y),
})
}
}
}
}
}
if code.length() == 0 {
return Ok(zone0)
}
match
tt.hint_glyph(
fpgm,
prep,
code,
zone0,
false,
is_pedantic,
tt_fvar_axis_count(font),
coords,
) {
Err(e) => Err(HintingFailed(e))
Ok(z) => Ok(z)
}
} else {
// Composite glyph: load components, apply transforms/offsets, then run
// composite instructions (if present) with is_composite=true.
let mut off = 10
let comps : Array[Tt2Component] = Array::new()
let mut flags_last = 0
while true {
flags_last = match tt2_read_u16_be(glyph, off) {
None => return Err(Read)
Some(v) => v
}
let comp_gid_i = match tt2_read_u16_be(glyph, off + 2) {
None => return Err(Read)
Some(v) => v
}
off = off + 4
let arg_words = (flags_last & COMPOSITE_ARG_1_AND_2_ARE_WORDS) != 0
let args_xy = (flags_last & COMPOSITE_ARGS_ARE_XY_VALUES) != 0
let anchor = if arg_words {
if args_xy {
let v1 = match tt2_read_i16_be(glyph, off) {
None => return Err(Read)
Some(v) => v
}
let v2 = match tt2_read_i16_be(glyph, off + 2) {
None => return Err(Read)
Some(v) => v
}
off = off + 4
Tt2Anchor::Offset(v1, v2)
} else {
let v1 = match tt2_read_u16_be(glyph, off) {
None => return Err(Read)
Some(v) => v
}
let v2 = match tt2_read_u16_be(glyph, off + 2) {
None => return Err(Read)
Some(v) => v
}
off = off + 4
Point(v1, v2)
}
} else {
if off + 2 > glyph.length() {
return Err(Read)
}
let b1 = glyph.at(off).to_int()
let b2 = glyph.at(off + 1).to_int()
off = off + 2
if args_xy {
let v1 = if b1 >= 128 { b1 - 256 } else { b1 }
let v2 = if b2 >= 128 { b2 - 256 } else { b2 }
Offset(v1, v2)
} else {
Point(b1, b2)
}
}
let mut xx14 = 0x4000
let mut yy14 = 0x4000
let mut xy14 = 0
let mut yx14 = 0
let mut have_xform = false
if (flags_last & COMPOSITE_WE_HAVE_A_SCALE) != 0 {
let s = match tt2_read_f2dot14_bits(glyph, off) {
None => return Err(Read)
Some(v) => v
}
off = off + 2
xx14 = s
yy14 = s
have_xform = true
} else if (flags_last & COMPOSITE_WE_HAVE_AN_X_AND_Y_SCALE) != 0 {
let sx = match tt2_read_f2dot14_bits(glyph, off) {
None => return Err(Read)
Some(v) => v
}
let sy = match tt2_read_f2dot14_bits(glyph, off + 2) {
None => return Err(Read)
Some(v) => v
}
off = off + 4
xx14 = sx
yy14 = sy
have_xform = true
} else if (flags_last & COMPOSITE_WE_HAVE_A_TWO_BY_TWO) != 0 {
let a = match tt2_read_f2dot14_bits(glyph, off) {
None => return Err(Read)
Some(v) => v
}
let b = match tt2_read_f2dot14_bits(glyph, off + 2) {
None => return Err(Read)
Some(v) => v
}
let c = match tt2_read_f2dot14_bits(glyph, off + 4) {
None => return Err(Read)
Some(v) => v
}
let d = match tt2_read_f2dot14_bits(glyph, off + 6) {
None => return Err(Read)
Some(v) => v
}
off = off + 8
xx14 = a
yx14 = b
xy14 = c
yy14 = d
have_xform = true
}
comps.push({
flags: flags_last,
gid: GlyphId(comp_gid_i.to_uint16()),
anchor,
xx16: tt2_f2dot14_to_16_16(xx14),
yx16: tt2_f2dot14_to_16_16(yx14),
xy16: tt2_f2dot14_to_16_16(xy14),
yy16: tt2_f2dot14_to_16_16(yy14),
have_xform,
})
if (flags_last & COMPOSITE_MORE_COMPONENTS) == 0 {
break
}
}
let ins_len = if (flags_last & COMPOSITE_WE_HAVE_INSTRUCTIONS) != 0 {
if off + 2 > glyph.length() {
return Err(Read)
}
let n = match tt2_read_u16_be(glyph, off) {
None => return Err(Read)
Some(v) => v
}
off = off + 2
if n < 0 || off + n > glyph.length() {
return Err(Read)
}
n
} else {
0
}
let ins = if ins_len > 0 {
glyph[off:off + ins_len]
} else {
glyph[off:off]
}
// Precompute composite gvar deltas (component offsets + phantoms).
let comp_deltas = match
glyf_gvar_composite_hint_deltas(font, gid, coords, comps.length()) {
None => None
Some((dx, dy)) => Some((dx, dy))
}
let phantom0_x = x_min - lsb
let phantom1_x = phantom0_x + aw
// Match fontations/FreeType vmetrics shape (see glyf scaler).
let phantom2_y = os2_ascent
let phantom3_y = os2_descent
// Apply gvar phantom deltas in font units before scaling (rounded).
let mut ph0_x_f = phantom0_x
let mut ph0_y_f = 0
let mut ph1_x_f = phantom1_x
let mut ph1_y_f = 0
let mut ph2_x_f = 0
let mut ph2_y_f = phantom2_y
let mut ph3_x_f = 0
let mut ph3_y_f = phantom3_y
match comp_deltas {
None => ()
Some((dx_bits, dy_bits)) => {
let base = comps.length()
if base + 3 < dx_bits.length() && base + 3 < dy_bits.length() {
ph0_x_f = ph0_x_f + gvar_fixed16_16_to_i32(dx_bits.at(base))
ph0_y_f = ph0_y_f + gvar_fixed16_16_to_i32(dy_bits.at(base))
ph1_x_f = ph1_x_f + gvar_fixed16_16_to_i32(dx_bits.at(base + 1))
ph1_y_f = ph1_y_f + gvar_fixed16_16_to_i32(dy_bits.at(base + 1))
ph2_x_f = ph2_x_f + gvar_fixed16_16_to_i32(dx_bits.at(base + 2))
ph2_y_f = ph2_y_f + gvar_fixed16_16_to_i32(dy_bits.at(base + 2))
ph3_x_f = ph3_x_f + gvar_fixed16_16_to_i32(dx_bits.at(base + 3))
ph3_y_f = ph3_y_f + gvar_fixed16_16_to_i32(dy_bits.at(base + 3))
}
}
}
// Scale to 26.6.
let mut ph0 = TtPoint::{
x: tt2_scale_funits_to_26_6(ph0_x_f, scale_bits),
y: tt2_scale_funits_to_26_6(ph0_y_f, scale_bits),
}
let mut ph1 = TtPoint::{
x: tt2_scale_funits_to_26_6(ph1_x_f, scale_bits),
y: tt2_scale_funits_to_26_6(ph1_y_f, scale_bits),
}
let mut ph2 = TtPoint::{
x: tt2_scale_funits_to_26_6(ph2_x_f, scale_bits),
y: tt2_scale_funits_to_26_6(ph2_y_f, scale_bits),
}
let mut ph3 = TtPoint::{
x: tt2_scale_funits_to_26_6(ph3_x_f, scale_bits),
y: tt2_scale_funits_to_26_6(ph3_y_f, scale_bits),
}
let points_out : Array[TtPoint] = Array::new()
let flags_out : Array[Int] = Array::new()
let contours_out : Array[Int] = Array::new()
let mut base_point = 0
for i in 0.. return Err(e)
Ok(z) => z
}
let outline_n = match tt2_zone_outline_count(zone_c) {
None => return Err(Read)
Some(v) => v
}
let (cph0, cph1, cph2, cph3) = match
tt2_zone_phantoms(zone_c, outline_n) {
None => return Err(Read)
Some(v) => v
}
if (comp.flags & TT2_COMPOSITE_USE_MY_METRICS) != 0 {
ph0 = cph0
ph1 = cph1
ph2 = cph2
ph3 = cph3
}
// Extract component outline points and flags.
let comp_points : Array[TtPoint] = Array::new()
let comp_flags : Array[Int] = Array::new()
for j in 0.. {
let mut dx = 0
let mut dy = 0
match comp_deltas {
None => ()
Some((dx_bits, dy_bits)) =>
if i < dx_bits.length() && i < dy_bits.length() {
dx = gvar_fixed16_16_to_i32(dx_bits.at(i))
dy = gvar_fixed16_16_to_i32(dy_bits.at(i))
}
}
tt2_component_offset_bits(comp, scale_bits, true, dx, dy)
}
Point(base, component) => {
if base < 0 || base >= points_out.length() {
return Err(InvalidAnchorPoint(gid, base.to_uint16()))
}
if component < 0 || component >= comp_points.length() {
return Err(InvalidAnchorPoint(gid, component.to_uint16()))
}
let bp = points_out.at(base)
let cp = comp_points.at(component)
(bp.x - cp.x, bp.y - cp.y)
}
}
if ox != 0 || oy != 0 {
for j in 0..= 0 {
contours_out.push(end_ix + base_point)
}
}
base_point = points_out.length()
}
let total_points = points_out.length() + 4
let unscaled : Array[TtPoint] = Array::make(total_points, { x: 0, y: 0 })
let original : Array[TtPoint] = Array::make(total_points, { x: 0, y: 0 })
let points : Array[TtPoint] = Array::make(total_points, { x: 0, y: 0 })
let flags : Array[Int] = Array::make(total_points, 0)
for i in 0.. 0 {
for i in 0..<4 {
let p = points.at(points_out.length() + i)
points.set(points_out.length() + i, {
x: tt2_round_grid_26_6(p.x),
y: tt2_round_grid_26_6(p.y),
})
}
}
let zone0 = TtZone::{
unscaled,
original,
points,
flags,
contours: contours_out,
}
if ins.length() == 0 {
return Ok(zone0)
}
match
tt.hint_glyph(
fpgm,
prep,
ins,
zone0,
true,
is_pedantic,
tt_fvar_axis_count(font),
coords,
) {
Err(e) => Err(HintingFailed(e))
Ok(z) => Ok(z)
}
}
}
///|
fn tt_load_glyph_zone_for_hinting(
font : @moon_skrifa.FontRef,
gid : @moon_skrifa.GlyphId,
coords : ArrayView[@moon_skrifa.NormalizedCoord],
scale_bits : Int,
tt : TtHintInstance,
fpgm : BytesView,
prep : BytesView,
is_pedantic : Bool,
) -> Result[TtZone, DrawError] {
let maxp = match font.table(TAG_MAXP_TT2) {
None => return Err(Read)
Some(v) => v
}
let num_glyphs = tt2_read_u16_be(maxp, 4).unwrap_or(0)
if num_glyphs <= 0 {
return Err(Read)
}
tt2_load_glyph_zone_for_hinting_impl(
font, gid, coords, scale_bits, tt, fpgm, prep, is_pedantic, 0,
)
}