// Copyright 2026 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.

///|
suberror FontError {
  FaceTable(@face.FaceTableError)
  Parse(@sfnt.SfntError)
  Var(@ot_var.VarError)
  Cff(@ot_cff.CffError)
  Layout(@ot_tables.OtLayoutError)
  Color(@ot_color.ColorError)
  Aat(@aat.AatError)
  GlyphOutOfRange
} derive(Eq, Show, ToJson)

///|
/// Font ties a face to scale metrics.
pub struct Font {
  face : @face.Face
  mut scale_x : Int
  mut scale_y : Int
  mut ppem_x : Int
  mut ppem_y : Int
  mut head : @sfnt.HeadTable?
  mut maxp : @sfnt.MaxpTable?
  mut hhea : @sfnt.HheaTable?
  mut hmtx : @sfnt.HmtxTable?
  mut vhea : @sfnt.VheaTable?
  mut vmtx : @sfnt.VmtxTable?
  mut cmap : @sfnt.CmapTable?
  mut loca : @sfnt.LocaTable?
  mut glyf : @sfnt.GlyfTable?
  mut os2 : @sfnt.Os2Table?
  mut name : @sfnt.NameTable?
  mut post : @sfnt.PostTable?
  mut gasp : @sfnt.GaspTable?
  mut meta : @sfnt.MetaTable?
  mut vorg : @sfnt.VorgTable?
  mut hdmx : @sfnt.HdmxTable?
  mut stat : @sfnt.StatTable?
  mut base : @sfnt.BaseTable?
  mut jstf : @sfnt.JstfTable?
  mut math : @sfnt.MathTable?
  mut kern : @sfnt.KernTable?
  mut trak : @aat.TrakTable?
  mut morx : @aat.MorxTable?
  mut mort : @aat.MortTable?
  mut kerx : @aat.KerxTable?
  mut ankr : @aat.AnkrTable?
  mut bsln : @aat.BslnTable?
  mut feat : @aat.FeatTable?
  mut just : @aat.JustTable?
  mut ltag : @aat.LtagTable?
  mut opbd : @aat.OpbdTable?
  mut gsub : @ot_tables.GsubTable?
  mut gpos : @ot_tables.GposTable?
  mut gdef : @ot_tables.GdefTable?
  mut cpal : @ot_color.CpalTable?
  mut colr : @ot_color.ColrTable?
  mut hvar : @ot_var.HvarTable?
  mut vvar : @ot_var.HvarTable?
  mut mvar : @ot_var.MvarTable?
  mut fvar : @ot_var.FvarTable?
  mut avar : @ot_var.AvarTable?
  mut cvt : @sfnt.CvtTable?
  mut gvar : @ot_var.GvarTable?
  mut cvar : @ot_var.CvarTable?
  mut varc : @ot_var.VarcTable?
  mut cff1 : @ot_cff.Cff1Table?
  mut cff2 : @ot_cff.Cff2Table?
  mut var_coords_design : Array[Double]?
  mut var_coords_norm : Array[Int]?
} derive(Show, ToJson)

///|
let tag_head : @common.Tag = @common.Tag::from_chars('h', 'e', 'a', 'd')

///|
let tag_maxp : @common.Tag = @common.Tag::from_chars('m', 'a', 'x', 'p')

///|
let tag_hhea : @common.Tag = @common.Tag::from_chars('h', 'h', 'e', 'a')

///|
let tag_hmtx : @common.Tag = @common.Tag::from_chars('h', 'm', 't', 'x')

///|
let tag_vhea : @common.Tag = @common.Tag::from_chars('v', 'h', 'e', 'a')

///|
let tag_vmtx : @common.Tag = @common.Tag::from_chars('v', 'm', 't', 'x')

///|
let tag_cmap : @common.Tag = @common.Tag::from_chars('c', 'm', 'a', 'p')

///|
let tag_loca : @common.Tag = @common.Tag::from_chars('l', 'o', 'c', 'a')

///|
let tag_glyf : @common.Tag = @common.Tag::from_chars('g', 'l', 'y', 'f')

///|
let tag_os2 : @common.Tag = @common.Tag::from_chars('O', 'S', '/', '2')

///|
let tag_name : @common.Tag = @common.Tag::from_chars('n', 'a', 'm', 'e')

///|
let tag_post : @common.Tag = @common.Tag::from_chars('p', 'o', 's', 't')

///|
let tag_gasp : @common.Tag = @common.Tag::from_chars('g', 'a', 's', 'p')

///|
let tag_meta : @common.Tag = @common.Tag::from_chars('m', 'e', 't', 'a')

///|
let tag_vorg : @common.Tag = @common.Tag::from_chars('V', 'O', 'R', 'G')

///|
let tag_hdmx : @common.Tag = @common.Tag::from_chars('h', 'd', 'm', 'x')

///|
let tag_stat : @common.Tag = @common.Tag::from_chars('S', 'T', 'A', 'T')

///|
let tag_kern : @common.Tag = @common.Tag::from_chars('k', 'e', 'r', 'n')

///|
let tag_base : @common.Tag = @common.Tag::from_chars('B', 'A', 'S', 'E')

///|
let tag_jstf : @common.Tag = @common.Tag::from_chars('J', 'S', 'T', 'F')

///|
let tag_math : @common.Tag = @common.Tag::from_chars('M', 'A', 'T', 'H')

///|
let tag_gsub : @common.Tag = @common.Tag::from_chars('G', 'S', 'U', 'B')

///|
let tag_gpos : @common.Tag = @common.Tag::from_chars('G', 'P', 'O', 'S')

///|
let tag_gdef : @common.Tag = @common.Tag::from_chars('G', 'D', 'E', 'F')

///|
let tag_fvar : @common.Tag = @common.Tag::from_chars('f', 'v', 'a', 'r')

///|
let tag_avar : @common.Tag = @common.Tag::from_chars('a', 'v', 'a', 'r')

///|
let tag_hvar : @common.Tag = @common.Tag::from_chars('H', 'V', 'A', 'R')

///|
let tag_vvar : @common.Tag = @common.Tag::from_chars('V', 'V', 'A', 'R')

///|
let tag_mvar : @common.Tag = @common.Tag::from_chars('M', 'V', 'A', 'R')

///|
let tag_gvar : @common.Tag = @common.Tag::from_chars('g', 'v', 'a', 'r')

///|
let tag_cvar : @common.Tag = @common.Tag::from_chars('c', 'v', 'a', 'r')

///|
let tag_cvt : @common.Tag = @common.Tag::from_chars('c', 'v', 't', ' ')

///|
let tag_varc : @common.Tag = @common.Tag::from_chars('V', 'A', 'R', 'C')

///|
let tag_cff : @common.Tag = @common.Tag::from_chars('C', 'F', 'F', ' ')

///|
let tag_cff2 : @common.Tag = @common.Tag::from_chars('C', 'F', 'F', '2')

///|
let tag_cpal : @common.Tag = @common.Tag::from_chars('C', 'P', 'A', 'L')

///|
let tag_colr : @common.Tag = @common.Tag::from_chars('C', 'O', 'L', 'R')

///|
let tag_trak : @common.Tag = @common.Tag::from_chars('t', 'r', 'a', 'k')

///|
let tag_morx : @common.Tag = @common.Tag::from_chars('m', 'o', 'r', 'x')

///|
let tag_mort : @common.Tag = @common.Tag::from_chars('m', 'o', 'r', 't')

///|
let tag_kerx : @common.Tag = @common.Tag::from_chars('k', 'e', 'r', 'x')

///|
let tag_ankr : @common.Tag = @common.Tag::from_chars('a', 'n', 'k', 'r')

///|
let tag_bsln : @common.Tag = @common.Tag::from_chars('b', 's', 'l', 'n')

///|
let tag_feat : @common.Tag = @common.Tag::from_chars('f', 'e', 'a', 't')

///|
let tag_just : @common.Tag = @common.Tag::from_chars('j', 'u', 's', 't')

///|
let tag_ltag : @common.Tag = @common.Tag::from_chars('l', 't', 'a', 'g')

///|
let tag_opbd : @common.Tag = @common.Tag::from_chars('o', 'p', 'b', 'd')

///|
/// Create a font from a face.
pub fn Font::new(face : @face.Face) -> Font {
  Font::{
    face,
    scale_x: 0,
    scale_y: 0,
    ppem_x: 0,
    ppem_y: 0,
    head: None,
    maxp: None,
    hhea: None,
    hmtx: None,
    vhea: None,
    vmtx: None,
    cmap: None,
    loca: None,
    glyf: None,
    os2: None,
    name: None,
    post: None,
    gasp: None,
    meta: None,
    vorg: None,
    hdmx: None,
    stat: None,
    base: None,
    jstf: None,
    math: None,
    kern: None,
    trak: None,
    morx: None,
    mort: None,
    kerx: None,
    ankr: None,
    bsln: None,
    feat: None,
    just: None,
    ltag: None,
    opbd: None,
    gsub: None,
    gpos: None,
    gdef: None,
    cpal: None,
    colr: None,
    hvar: None,
    vvar: None,
    mvar: None,
    fvar: None,
    avar: None,
    cvt: None,
    gvar: None,
    cvar: None,
    varc: None,
    cff1: None,
    cff2: None,
    var_coords_design: None,
    var_coords_norm: None,
  }
}

///|
/// Create a font from raw bytes.
pub fn Font::from_bytes(bytes : Bytes) -> Font {
  Font::new(@face.Face::from_bytes(bytes))
}

///|
/// Access the face.
pub fn Font::get_face(self : Font) -> @face.Face {
  self.face
}

///|
/// Set scale values (in 26.6 fixed-point like HarfBuzz).
pub fn Font::set_scale(self : Font, x : Int, y : Int) -> Unit {
  self.scale_x = x
  self.scale_y = y
}

///|
/// Get scale values.
pub fn Font::get_scale(self : Font) -> (Int, Int) {
  (self.scale_x, self.scale_y)
}

///|
/// Set ppem values.
pub fn Font::set_ppem(self : Font, x : Int, y : Int) -> Unit {
  self.ppem_x = x
  self.ppem_y = y
}

///|
/// Get ppem values.
pub fn Font::get_ppem(self : Font) -> (Int, Int) {
  (self.ppem_x, self.ppem_y)
}

///|
fn Font::table_blob(
  self : Font,
  tag : @common.Tag,
) -> Result[@blob.Blob, FontError] {
  match self.face.reference_table(tag) {
    Err(err) => Err(FaceTable(err))
    Ok(blob) => Ok(blob)
  }
}

///|
fn Font::ensure_head(self : Font) -> Result[@sfnt.HeadTable, FontError] {
  match self.head {
    Some(head) => Ok(head)
    None =>
      match self.table_blob(tag_head) {
        Err(err) => Err(err)
        Ok(blob) =>
          match @sfnt.HeadTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(head) => {
              self.head = Some(head)
              self.face.set_upem(head.units_per_em)
              if self.scale_x == 0 {
                self.scale_x = head.units_per_em
              }
              if self.scale_y == 0 {
                self.scale_y = head.units_per_em
              }
              Ok(head)
            }
          }
      }
  }
}

///|
fn scale_value(value : Int, scale : Int, upem : Int) -> Int {
  if upem == 0 {
    return value
  }
  (value * scale) / upem
}

///|
fn Font::ensure_maxp(self : Font) -> Result[@sfnt.MaxpTable, FontError] {
  match self.maxp {
    Some(maxp) => Ok(maxp)
    None =>
      match self.table_blob(tag_maxp) {
        Err(err) => Err(err)
        Ok(blob) =>
          match @sfnt.MaxpTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(maxp) => {
              self.maxp = Some(maxp)
              self.face.set_glyph_count(maxp.num_glyphs)
              Ok(maxp)
            }
          }
      }
  }
}

///|
fn Font::ensure_hhea(self : Font) -> Result[@sfnt.HheaTable, FontError] {
  match self.hhea {
    Some(hhea) => Ok(hhea)
    None =>
      match self.table_blob(tag_hhea) {
        Err(err) => Err(err)
        Ok(blob) =>
          match @sfnt.HheaTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(hhea) => {
              self.hhea = Some(hhea)
              Ok(hhea)
            }
          }
      }
  }
}

///|
fn Font::ensure_hmtx(self : Font) -> Result[@sfnt.HmtxTable, FontError] {
  match self.hmtx {
    Some(hmtx) => Ok(hmtx)
    None => {
      let maxp = match self.ensure_maxp() {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      let hhea = match self.ensure_hhea() {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      match self.table_blob(tag_hmtx) {
        Err(err) => Err(err)
        Ok(blob) =>
          match
            @sfnt.HmtxTable::parse(
              blob.as_view(),
              maxp.num_glyphs,
              hhea.number_of_hmetrics,
            ) {
            Err(err) => Err(Parse(err))
            Ok(hmtx) => {
              self.hmtx = Some(hmtx)
              Ok(hmtx)
            }
          }
      }
    }
  }
}

///|
fn Font::ensure_vhea(self : Font) -> Result[@sfnt.VheaTable, FontError] {
  match self.vhea {
    Some(vhea) => Ok(vhea)
    None =>
      match self.table_blob(tag_vhea) {
        Err(err) => Err(err)
        Ok(blob) =>
          match @sfnt.VheaTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(vhea) => {
              self.vhea = Some(vhea)
              Ok(vhea)
            }
          }
      }
  }
}

///|
///|
fn Font::ensure_cmap(self : Font) -> Result[@sfnt.CmapTable, FontError] {
  match self.cmap {
    Some(cmap) => Ok(cmap)
    None =>
      match self.table_blob(tag_cmap) {
        Err(err) => Err(err)
        Ok(blob) =>
          match @sfnt.CmapTable::parse(blob) {
            Err(err) => Err(Parse(err))
            Ok(cmap) => {
              self.cmap = Some(cmap)
              Ok(cmap)
            }
          }
      }
  }
}

///|
fn Font::ensure_loca(self : Font) -> Result[@sfnt.LocaTable, FontError] {
  match self.loca {
    Some(loca) => Ok(loca)
    None => {
      let head = match self.ensure_head() {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      let maxp = match self.ensure_maxp() {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      match self.table_blob(tag_loca) {
        Err(err) => Err(err)
        Ok(blob) =>
          match
            @sfnt.LocaTable::parse(
              blob.as_view(),
              maxp.num_glyphs,
              head.index_to_loc_format,
            ) {
            Err(err) => Err(Parse(err))
            Ok(loca) => {
              self.loca = Some(loca)
              Ok(loca)
            }
          }
      }
    }
  }
}

///|
fn Font::ensure_glyf(self : Font) -> Result[@sfnt.GlyfTable, FontError] {
  match self.glyf {
    Some(glyf) => Ok(glyf)
    None =>
      match self.table_blob(tag_glyf) {
        Err(err) => Err(err)
        Ok(blob) =>
          match @sfnt.GlyfTable::parse(blob) {
            Err(err) => Err(Parse(err))
            Ok(glyf) => {
              self.glyf = Some(glyf)
              Ok(glyf)
            }
          }
      }
  }
}

///|
pub fn Font::os2(self : Font) -> Result[@sfnt.Os2Table?, FontError] {
  match self.os2 {
    Some(os2) => Ok(Some(os2))
    None =>
      match self.face.reference_table_optional(tag_os2) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.Os2Table::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(os2) => {
              self.os2 = Some(os2)
              Ok(Some(os2))
            }
          }
      }
  }
}

///|
pub fn Font::name(self : Font) -> Result[@sfnt.NameTable?, FontError] {
  match self.name {
    Some(name) => Ok(Some(name))
    None =>
      match self.face.reference_table_optional(tag_name) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.NameTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(name) => {
              self.name = Some(name)
              Ok(Some(name))
            }
          }
      }
  }
}

///|
pub fn Font::post(self : Font) -> Result[@sfnt.PostTable?, FontError] {
  match self.post {
    Some(post) => Ok(Some(post))
    None =>
      match self.face.reference_table_optional(tag_post) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.PostTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(post) => {
              self.post = Some(post)
              Ok(Some(post))
            }
          }
      }
  }
}

///|
pub fn Font::gasp(self : Font) -> Result[@sfnt.GaspTable?, FontError] {
  match self.gasp {
    Some(gasp) => Ok(Some(gasp))
    None =>
      match self.face.reference_table_optional(tag_gasp) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.GaspTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(gasp) => {
              self.gasp = Some(gasp)
              Ok(Some(gasp))
            }
          }
      }
  }
}

///|
pub fn Font::meta(self : Font) -> Result[@sfnt.MetaTable?, FontError] {
  match self.meta {
    Some(meta) => Ok(Some(meta))
    None =>
      match self.face.reference_table_optional(tag_meta) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.MetaTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(meta) => {
              self.meta = Some(meta)
              Ok(Some(meta))
            }
          }
      }
  }
}

///|
pub fn Font::vorg(self : Font) -> Result[@sfnt.VorgTable?, FontError] {
  match self.vorg {
    Some(vorg) => Ok(Some(vorg))
    None =>
      match self.face.reference_table_optional(tag_vorg) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.VorgTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(vorg) => {
              self.vorg = Some(vorg)
              Ok(Some(vorg))
            }
          }
      }
  }
}

///|
pub fn Font::hdmx(self : Font) -> Result[@sfnt.HdmxTable?, FontError] {
  match self.hdmx {
    Some(hdmx) => Ok(Some(hdmx))
    None =>
      match self.face.reference_table_optional(tag_hdmx) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.HdmxTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(hdmx) => {
              self.hdmx = Some(hdmx)
              Ok(Some(hdmx))
            }
          }
      }
  }
}

///|
pub fn Font::stat(self : Font) -> Result[@sfnt.StatTable?, FontError] {
  match self.stat {
    Some(stat) => Ok(Some(stat))
    None =>
      match self.face.reference_table_optional(tag_stat) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.StatTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(stat) => {
              self.stat = Some(stat)
              Ok(Some(stat))
            }
          }
      }
  }
}

///|
pub fn Font::vhea(self : Font) -> Result[@sfnt.VheaTable?, FontError] {
  match self.vhea {
    Some(vhea) => Ok(Some(vhea))
    None =>
      match self.face.reference_table_optional(tag_vhea) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.VheaTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(vhea) => {
              self.vhea = Some(vhea)
              Ok(Some(vhea))
            }
          }
      }
  }
}

///|
pub fn Font::vmtx(self : Font) -> Result[@sfnt.VmtxTable?, FontError] {
  match self.vmtx {
    Some(vmtx) => Ok(Some(vmtx))
    None =>
      match self.face.reference_table_optional(tag_vmtx) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) => {
          let maxp = match self.ensure_maxp() {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          let vhea = match self.ensure_vhea() {
            Err(err) => return Err(err)
            Ok(value) => value
          }
          match
            @sfnt.VmtxTable::parse(
              blob.as_view(),
              maxp.num_glyphs,
              vhea.number_of_vmetrics,
            ) {
            Err(err) => Err(Parse(err))
            Ok(vmtx) => {
              self.vmtx = Some(vmtx)
              Ok(Some(vmtx))
            }
          }
        }
      }
  }
}

///|
pub fn Font::base(self : Font) -> Result[@sfnt.BaseTable?, FontError] {
  match self.base {
    Some(base) => Ok(Some(base))
    None =>
      match self.face.reference_table_optional(tag_base) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.BaseTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(base) => {
              self.base = Some(base)
              Ok(Some(base))
            }
          }
      }
  }
}

///|
pub fn Font::jstf(self : Font) -> Result[@sfnt.JstfTable?, FontError] {
  match self.jstf {
    Some(jstf) => Ok(Some(jstf))
    None =>
      match self.face.reference_table_optional(tag_jstf) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.JstfTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(jstf) => {
              self.jstf = Some(jstf)
              Ok(Some(jstf))
            }
          }
      }
  }
}

///|
pub fn Font::math(self : Font) -> Result[@sfnt.MathTable?, FontError] {
  match self.math {
    Some(math) => Ok(Some(math))
    None =>
      match self.face.reference_table_optional(tag_math) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.MathTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(math) => {
              self.math = Some(math)
              Ok(Some(math))
            }
          }
      }
  }
}

///|
pub fn Font::gsub(self : Font) -> Result[@ot_tables.GsubTable?, FontError] {
  match self.gsub {
    Some(gsub) => Ok(Some(gsub))
    None =>
      match self.face.reference_table_optional(tag_gsub) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_tables.GsubTable::parse(blob) {
            Err(err) => Err(Layout(err))
            Ok(gsub) => {
              self.gsub = Some(gsub)
              Ok(Some(gsub))
            }
          }
      }
  }
}

///|
pub fn Font::kern(self : Font) -> Result[@sfnt.KernTable?, FontError] {
  match self.kern {
    Some(kern) => Ok(Some(kern))
    None =>
      match self.face.reference_table_optional(tag_kern) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.KernTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(kern) => {
              self.kern = Some(kern)
              Ok(Some(kern))
            }
          }
      }
  }
}

///|
pub fn Font::trak(self : Font) -> Result[@aat.TrakTable?, FontError] {
  match self.trak {
    Some(trak) => Ok(Some(trak))
    None =>
      match self.face.reference_table_optional(tag_trak) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @aat.TrakTable::parse(blob.as_view()) {
            Err(err) => Err(Aat(err))
            Ok(trak) => {
              self.trak = Some(trak)
              Ok(Some(trak))
            }
          }
      }
  }
}

///|
pub fn Font::morx(self : Font) -> Result[@aat.MorxTable?, FontError] {
  match self.morx {
    Some(morx) => Ok(Some(morx))
    None =>
      match self.face.reference_table_optional(tag_morx) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) => {
          let maxp = match self.ensure_maxp() {
            Err(err) => return Err(err)
            Ok(maxp) => maxp
          }
          match @aat.MorxTable::parse(blob.as_view(), maxp.num_glyphs) {
            Err(err) => Err(Aat(err))
            Ok(morx) => {
              self.morx = Some(morx)
              Ok(Some(morx))
            }
          }
        }
      }
  }
}

///|
pub fn Font::mort(self : Font) -> Result[@aat.MortTable?, FontError] {
  match self.mort {
    Some(mort) => Ok(Some(mort))
    None =>
      match self.face.reference_table_optional(tag_mort) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) => {
          let maxp = match self.ensure_maxp() {
            Err(err) => return Err(err)
            Ok(maxp) => maxp
          }
          match @aat.MortTable::parse(blob.as_view(), maxp.num_glyphs) {
            Err(err) => Err(Aat(err))
            Ok(mort) => {
              self.mort = Some(mort)
              Ok(Some(mort))
            }
          }
        }
      }
  }
}

///|
pub fn Font::kerx(self : Font) -> Result[@aat.KerxTable?, FontError] {
  match self.kerx {
    Some(kerx) => Ok(Some(kerx))
    None =>
      match self.face.reference_table_optional(tag_kerx) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) => {
          let maxp = match self.ensure_maxp() {
            Err(err) => return Err(err)
            Ok(maxp) => maxp
          }
          match @aat.KerxTable::parse(blob.as_view(), maxp.num_glyphs) {
            Err(err) => Err(Aat(err))
            Ok(kerx) => {
              self.kerx = Some(kerx)
              Ok(Some(kerx))
            }
          }
        }
      }
  }
}

///|
pub fn Font::ankr(self : Font) -> Result[@aat.AnkrTable?, FontError] {
  match self.ankr {
    Some(ankr) => Ok(Some(ankr))
    None =>
      match self.face.reference_table_optional(tag_ankr) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) => {
          let maxp = match self.ensure_maxp() {
            Err(err) => return Err(err)
            Ok(maxp) => maxp
          }
          match @aat.AnkrTable::parse(blob.as_view(), maxp.num_glyphs) {
            Err(err) => Err(Aat(err))
            Ok(ankr) => {
              self.ankr = Some(ankr)
              Ok(Some(ankr))
            }
          }
        }
      }
  }
}

///|
pub fn Font::bsln(self : Font) -> Result[@aat.BslnTable?, FontError] {
  match self.bsln {
    Some(bsln) => Ok(Some(bsln))
    None =>
      match self.face.reference_table_optional(tag_bsln) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) => {
          let maxp = match self.ensure_maxp() {
            Err(err) => return Err(err)
            Ok(maxp) => maxp
          }
          match @aat.BslnTable::parse(blob.as_view(), maxp.num_glyphs) {
            Err(err) => Err(Aat(err))
            Ok(bsln) => {
              self.bsln = Some(bsln)
              Ok(Some(bsln))
            }
          }
        }
      }
  }
}

///|
pub fn Font::feat(self : Font) -> Result[@aat.FeatTable?, FontError] {
  match self.feat {
    Some(feat) => Ok(Some(feat))
    None =>
      match self.face.reference_table_optional(tag_feat) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @aat.FeatTable::parse(blob.as_view()) {
            Err(err) => Err(Aat(err))
            Ok(feat) => {
              self.feat = Some(feat)
              Ok(Some(feat))
            }
          }
      }
  }
}

///|
pub fn Font::just(self : Font) -> Result[@aat.JustTable?, FontError] {
  match self.just {
    Some(just) => Ok(Some(just))
    None =>
      match self.face.reference_table_optional(tag_just) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) => {
          let maxp = match self.ensure_maxp() {
            Err(err) => return Err(err)
            Ok(maxp) => maxp
          }
          match @aat.JustTable::parse(blob.as_view(), maxp.num_glyphs) {
            Err(err) => Err(Aat(err))
            Ok(just) => {
              self.just = Some(just)
              Ok(Some(just))
            }
          }
        }
      }
  }
}

///|
pub fn Font::ltag(self : Font) -> Result[@aat.LtagTable?, FontError] {
  match self.ltag {
    Some(ltag) => Ok(Some(ltag))
    None =>
      match self.face.reference_table_optional(tag_ltag) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @aat.LtagTable::parse(blob.as_view()) {
            Err(err) => Err(Aat(err))
            Ok(ltag) => {
              self.ltag = Some(ltag)
              Ok(Some(ltag))
            }
          }
      }
  }
}

///|
pub fn Font::opbd(self : Font) -> Result[@aat.OpbdTable?, FontError] {
  match self.opbd {
    Some(opbd) => Ok(Some(opbd))
    None =>
      match self.face.reference_table_optional(tag_opbd) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) => {
          let maxp = match self.ensure_maxp() {
            Err(err) => return Err(err)
            Ok(maxp) => maxp
          }
          match @aat.OpbdTable::parse(blob.as_view(), maxp.num_glyphs) {
            Err(err) => Err(Aat(err))
            Ok(opbd) => {
              self.opbd = Some(opbd)
              Ok(Some(opbd))
            }
          }
        }
      }
  }
}

///|
pub fn Font::gpos(self : Font) -> Result[@ot_tables.GposTable?, FontError] {
  match self.gpos {
    Some(gpos) => Ok(Some(gpos))
    None =>
      match self.face.reference_table_optional(tag_gpos) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_tables.GposTable::parse(blob) {
            Err(err) => Err(Layout(err))
            Ok(gpos) => {
              self.gpos = Some(gpos)
              Ok(Some(gpos))
            }
          }
      }
  }
}

///|
pub fn Font::gdef(self : Font) -> Result[@ot_tables.GdefTable?, FontError] {
  match self.gdef {
    Some(gdef) => Ok(Some(gdef))
    None =>
      match self.face.reference_table_optional(tag_gdef) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_tables.GdefTable::parse(blob) {
            Err(err) => Err(Layout(err))
            Ok(gdef) => {
              self.gdef = Some(gdef)
              Ok(Some(gdef))
            }
          }
      }
  }
}

///|
pub fn Font::cpal(self : Font) -> Result[@ot_color.CpalTable?, FontError] {
  match self.cpal {
    Some(cpal) => Ok(Some(cpal))
    None =>
      match self.face.reference_table_optional(tag_cpal) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_color.CpalTable::parse(blob.as_view()) {
            Err(err) => Err(Color(err))
            Ok(cpal) => {
              self.cpal = Some(cpal)
              Ok(Some(cpal))
            }
          }
      }
  }
}

///|
pub fn Font::colr(self : Font) -> Result[@ot_color.ColrTable?, FontError] {
  match self.colr {
    Some(colr) => Ok(Some(colr))
    None =>
      match self.face.reference_table_optional(tag_colr) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_color.ColrTable::parse(blob.as_view()) {
            Err(err) => Err(Color(err))
            Ok(colr) => {
              self.colr = Some(colr)
              Ok(Some(colr))
            }
          }
      }
  }
}

///|
pub fn Font::hvar(self : Font) -> Result[@ot_var.HvarTable?, FontError] {
  match self.hvar {
    Some(hvar) => Ok(Some(hvar))
    None =>
      match self.face.reference_table_optional(tag_hvar) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_var.HvarTable::parse(blob.as_view()) {
            Err(err) => Err(Var(err))
            Ok(hvar) => {
              self.hvar = Some(hvar)
              Ok(Some(hvar))
            }
          }
      }
  }
}
///|
pub fn Font::fvar(self : Font) -> Result[@ot_var.FvarTable?, FontError] {
  match self.fvar {
    Some(fvar) => Ok(Some(fvar))
    None =>
      match self.face.reference_table_optional(tag_fvar) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_var.FvarTable::parse(blob.as_view()) {
            Err(err) => Err(Var(err))
            Ok(fvar) => {
              self.fvar = Some(fvar)
              Ok(Some(fvar))
            }
          }
      }
  }
}

///|
pub fn Font::vvar(self : Font) -> Result[@ot_var.HvarTable?, FontError] {
  match self.vvar {
    Some(vvar) => Ok(Some(vvar))
    None =>
      match self.face.reference_table_optional(tag_vvar) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_var.HvarTable::parse(blob.as_view()) {
            Err(err) => Err(Var(err))
            Ok(vvar) => {
              self.vvar = Some(vvar)
              Ok(Some(vvar))
            }
          }
      }
  }
}

///|
pub fn Font::mvar(self : Font) -> Result[@ot_var.MvarTable?, FontError] {
  match self.mvar {
    Some(mvar) => Ok(Some(mvar))
    None =>
      match self.face.reference_table_optional(tag_mvar) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_var.MvarTable::parse(blob.as_view()) {
            Err(err) => Err(Var(err))
            Ok(mvar) => {
              self.mvar = Some(mvar)
              Ok(Some(mvar))
            }
          }
      }
  }
}

///|
pub fn Font::gvar(self : Font) -> Result[@ot_var.GvarTable?, FontError] {
  match self.gvar {
    Some(gvar) => Ok(Some(gvar))
    None =>
      match self.face.reference_table_optional(tag_gvar) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_var.GvarTable::parse(blob.as_view()) {
            Err(err) => Err(Var(err))
            Ok(gvar) => {
              self.gvar = Some(gvar)
              Ok(Some(gvar))
            }
          }
      }
  }
}

///|
pub fn Font::cvar(self : Font) -> Result[@ot_var.CvarTable?, FontError] {
  match self.cvar {
    Some(cvar) => Ok(Some(cvar))
    None => {
      let fvar = match self.fvar() {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      let axis_count = match fvar {
        None => return Ok(None)
        Some(value) => value.axes().length()
      }
      match self.face.reference_table_optional(tag_cvar) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_var.CvarTable::parse(blob.as_view(), axis_count) {
            Err(err) => Err(Var(err))
            Ok(cvar) => {
              self.cvar = Some(cvar)
              Ok(Some(cvar))
            }
          }
      }
    }
  }
}

///|
pub fn Font::varc(self : Font) -> Result[@ot_var.VarcTable?, FontError] {
  match self.varc {
    Some(varc) => Ok(Some(varc))
    None =>
      match self.face.reference_table_optional(tag_varc) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_var.VarcTable::parse(blob.as_view()) {
            Err(err) => Err(Var(err))
            Ok(varc) => {
              self.varc = Some(varc)
              Ok(Some(varc))
            }
          }
      }
  }
}

///|
pub fn Font::cff1(self : Font) -> Result[@ot_cff.Cff1Table?, FontError] {
  match self.cff1 {
    Some(cff1) => Ok(Some(cff1))
    None =>
      match self.face.reference_table_optional(tag_cff) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_cff.Cff1Table::parse(blob.as_view()) {
            Err(err) => Err(Cff(err))
            Ok(cff1) => {
              self.cff1 = Some(cff1)
              Ok(Some(cff1))
            }
          }
      }
  }
}

///|
pub fn Font::cff2(self : Font) -> Result[@ot_cff.Cff2Table?, FontError] {
  match self.cff2 {
    Some(cff2) => Ok(Some(cff2))
    None =>
      match self.face.reference_table_optional(tag_cff2) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_cff.Cff2Table::parse(blob.as_view()) {
            Err(err) => Err(Cff(err))
            Ok(cff2) => {
              self.cff2 = Some(cff2)
              Ok(Some(cff2))
            }
          }
      }
  }
}

///|
pub fn Font::varc_components(
  self : Font,
  glyph : UInt,
) -> Result[Array[@ot_var.VarcComponent]?, FontError] {
  let varc = match self.varc() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  match varc {
    None => Ok(None)
    Some(varc) => {
      let coords : Array[Int] = match self.var_coords_norm {
        None => []
        Some(value) => value
      }
      match varc.glyph_components(glyph.reinterpret_as_int(), coords, coords) {
        Err(err) => Err(Var(err))
        Ok(value) => Ok(Some(value))
      }
    }
  }
}

///|
pub fn Font::mvar_delta(self : Font, tag : @common.Tag) -> Result[Double, FontError] {
  match self.var_coords_norm {
    None => Ok(0.0)
    Some(coords) =>
      match self.mvar() {
        Err(err) => Err(err)
        Ok(None) => Ok(0.0)
        Ok(Some(mvar)) => Ok(mvar.get_var(tag, coords))
      }
  }
}

///|
pub fn Font::avar(self : Font) -> Result[@ot_var.AvarTable?, FontError] {
  match self.avar {
    Some(avar) => Ok(Some(avar))
    None =>
      match self.face.reference_table_optional(tag_avar) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @ot_var.AvarTable::parse(blob.as_view()) {
            Err(err) => Err(Var(err))
            Ok(avar) => {
              self.avar = Some(avar)
              Ok(Some(avar))
            }
          }
      }
  }
}

///|
pub fn Font::cvt(self : Font) -> Result[@sfnt.CvtTable?, FontError] {
  match self.cvt {
    Some(cvt) => Ok(Some(cvt))
    None =>
      match self.face.reference_table_optional(tag_cvt) {
        Err(err) => Err(FaceTable(err))
        Ok(None) => Ok(None)
        Ok(Some(blob)) =>
          match @sfnt.CvtTable::parse(blob.as_view()) {
            Err(err) => Err(Parse(err))
            Ok(cvt) => {
              self.cvt = Some(cvt)
              Ok(Some(cvt))
            }
          }
      }
  }
}

///|
pub fn Font::cvt_values(self : Font) -> Result[Array[Int]?, FontError] {
  let cvt = match self.cvt() {
    Err(err) => return Err(err)
    Ok(None) => return Ok(None)
    Ok(Some(value)) => value
  }
  let base = cvt.values().to_array()
  match self.var_coords_norm {
    None => Ok(Some(base))
    Some(coords) =>
      match self.cvar() {
        Err(err) => Err(err)
        Ok(None) => Ok(Some(base))
        Ok(Some(cvar)) =>
          match cvar.apply_to_cvt(base, coords) {
            Err(err) => Err(Var(err))
            Ok(values) => Ok(Some(values))
          }
      }
  }
}

///|
/// Map a codepoint to a glyph id.
pub fn Font::glyph_for_codepoint(
  self : Font,
  codepoint : UInt,
) -> Result[UInt?, FontError] {
  match self.ensure_cmap() {
    Err(err) => Err(err)
    Ok(cmap) =>
      match cmap.glyph_for(codepoint) {
        Err(err) => Err(Parse(err))
        Ok(value) => Ok(value)
      }
  }
}

///|
/// Map a codepoint + variation selector to a glyph id, if supported.
pub fn Font::variation_glyph_for(
  self : Font,
  codepoint : UInt,
  selector : UInt,
) -> Result[(Bool, UInt?), FontError] {
  match self.ensure_cmap() {
    Err(err) => Err(err)
    Ok(cmap) =>
      match cmap.variation_mapping(codepoint, selector) {
        Err(err) => Err(Parse(err))
        Ok(value) => Ok(value)
      }
  }
}

///|
pub fn Font::glyph_name(
  self : Font,
  glyph : UInt,
) -> Result[String?, FontError] {
  let maxp = match self.ensure_maxp() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  let index = glyph.reinterpret_as_int()
  if index < 0 || index >= maxp.num_glyphs {
    return Ok(None)
  }
  let post = match self.post() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  match post {
    Some(post) =>
      match post.glyph_name(index) {
        Some(name) => return Ok(Some(name))
        None => ()
      }
    None => ()
  }
  let cff1 = match self.cff1() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  match cff1 {
    None => Ok(None)
    Some(cff1) =>
      match cff1.glyph_name(index) {
        Err(err) => Err(Cff(err))
        Ok(value) => Ok(value)
      }
  }
}

///|
pub fn Font::glyph_from_name(
  self : Font,
  name : String,
) -> Result[UInt?, FontError] {
  if name == "" {
    return Ok(None)
  }
  let maxp = match self.ensure_maxp() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  let post = match self.post() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  match post {
    Some(post) =>
      match post.glyph_from_name(name) {
        None => ()
        Some(glyph) => if glyph >= 0 && glyph < maxp.num_glyphs {
          return Ok(Some(glyph.reinterpret_as_uint()))
        }
      }
    None => ()
  }
  let cff1 = match self.cff1() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  match cff1 {
    None => Ok(None)
    Some(cff1) =>
      match cff1.glyph_from_name(name) {
        Err(err) => Err(Cff(err))
        Ok(None) => Ok(None)
        Ok(Some(glyph)) =>
          if glyph < 0 || glyph >= maxp.num_glyphs {
            Ok(None)
          } else {
            Ok(Some(glyph.reinterpret_as_uint()))
          }
      }
  }
}

///|
pub fn Font::set_variations(
  self : Font,
  variations : Array[@ot_var.AxisCoord],
) -> Result[Unit, FontError] {
  let fvar = match self.fvar() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  match fvar {
    None => {
      self.var_coords_design = None
      self.var_coords_norm = None
      Ok(())
    }
    Some(fvar) => {
      let axis_count = fvar.axes.length()
      let design_coords : Array[Double] = Array::make(axis_count, 0.0)
      for i in 0.. ()
          Some(index) => if index >= 0 && index < axis_count {
            design_coords[index] = variation.value
          }
        }
      }
      let mut normalized = fvar.normalize_design_coords(design_coords.copy())
      match self.avar() {
        Err(err) => return Err(err)
        Ok(None) => ()
        Ok(Some(avar)) => normalized = avar.map_coords_16_16(normalized)
      }
      for i in 0.. Array[Int] {
  match self.var_coords_norm {
    None => []
    Some(coords) => coords.copy()
  }
}

///|
/// Return glyph horizontal advance.
pub fn Font::glyph_h_advance(
  self : Font,
  glyph : UInt,
) -> Result[Int, FontError] {
  let head = match self.ensure_head() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  let hmtx = match self.ensure_hmtx() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  let index = glyph.reinterpret_as_int()
  if index < 0 || index >= hmtx.advances.length() {
    return Err(GlyphOutOfRange)
  }
  let mut advance = hmtx.advances[index]
  match self.var_coords_norm {
    None => ()
    Some(coords) => {
      let hvar = match self.hvar() {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      match hvar {
        Some(hvar) => {
          let delta = hvar.advance_delta(glyph, coords)
          advance = advance + delta.round().to_int()
        }
        None =>
          match self.gvar() {
            Err(err) => return Err(err)
            Ok(None) => ()
            Ok(Some(_)) =>
              match self.glyph_gvar_advance(glyph, coords, false) {
                Err(err) => return Err(err)
                Ok(None) => ()
                Ok(Some(value)) => advance = value
              }
          }
      }
    }
  }
  Ok(scale_value(advance, self.scale_x, head.units_per_em))
}

///|
/// Return glyph bounds from glyf header (font units).
pub fn Font::glyph_bounds(
  self : Font,
  glyph : UInt,
) -> Result[@sfnt.GlyphBounds?, FontError] {
  let coords : Array[Int] = match self.var_coords_norm {
    None => []
    Some(value) => value
  }
  self.glyph_bounds_with_coords(glyph, coords, 8)
}

fn Font::glyph_bounds_with_coords(
  self : Font,
  glyph : UInt,
  coords : Array[Int],
  depth : Int,
) -> Result[@sfnt.GlyphBounds?, FontError] {
  if depth <= 0 {
    return self.glyph_bounds_raw(glyph, coords)
  }
  let varc = match self.varc() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  match varc {
    None => self.glyph_bounds_raw(glyph, coords)
    Some(varc) => {
      let components = match varc.glyph_components(glyph.reinterpret_as_int(), coords, coords) {
        Err(err) => return Err(Var(err))
        Ok(value) => value
      }
      if components.is_empty() {
        return self.glyph_bounds_raw(glyph, coords)
      }
      let mut min_x : Double? = None
      let mut min_y : Double? = None
      let mut max_x : Double? = None
      let mut max_y : Double? = None
      for comp in components {
        if !comp.visible {
          continue
        }
        if comp.glyph < 0 {
          continue
        }
        let bounds = match self.glyph_bounds_with_coords(
          comp.glyph.reinterpret_as_uint(),
          comp.coords,
          depth - 1,
        ) {
          Err(err) => return Err(err)
          Ok(value) => value
        }
        match bounds {
          None => ()
          Some(bounds) => {
            let (x0, y0, x1, y1) = comp.transform.apply_to_bounds(
              bounds.x_min.to_double(),
              bounds.y_min.to_double(),
              bounds.x_max.to_double(),
              bounds.y_max.to_double(),
            )
            let next_min_x = match min_x {
              None => x0
              Some(v) => if x0 < v { x0 } else { v }
            }
            let next_min_y = match min_y {
              None => y0
              Some(v) => if y0 < v { y0 } else { v }
            }
            let next_max_x = match max_x {
              None => x1
              Some(v) => if x1 > v { x1 } else { v }
            }
            let next_max_y = match max_y {
              None => y1
              Some(v) => if y1 > v { y1 } else { v }
            }
            min_x = Some(next_min_x)
            min_y = Some(next_min_y)
            max_x = Some(next_max_x)
            max_y = Some(next_max_y)
          }
        }
      }
      match (min_x, min_y, max_x, max_y) {
        (Some(min_x), Some(min_y), Some(max_x), Some(max_y)) => {
          let x_min = @math.floor(min_x).to_int()
          let y_min = @math.floor(min_y).to_int()
          let x_max = @math.ceil(max_x).to_int()
          let y_max = @math.ceil(max_y).to_int()
          Ok(Some(@sfnt.GlyphBounds::new(-1, x_min, y_min, x_max, y_max)))
        }
        _ => self.glyph_bounds_raw(glyph, coords)
      }
    }
  }
}

fn Font::glyph_bounds_raw(
  self : Font,
  glyph : UInt,
  coords : Array[Int],
) -> Result[@sfnt.GlyphBounds?, FontError] {
  let maxp = match self.ensure_maxp() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  let index = glyph.reinterpret_as_int()
  if index < 0 || index >= maxp.num_glyphs {
    return Err(GlyphOutOfRange)
  }
  let loca_blob = match self.face.reference_table_optional(tag_loca) {
    Err(err) => return Err(FaceTable(err))
    Ok(value) => value
  }
  let glyf_blob = match self.face.reference_table_optional(tag_glyf) {
    Err(err) => return Err(FaceTable(err))
    Ok(value) => value
  }
  if loca_blob is None || glyf_blob is None {
    let cff2_bounds = match self.glyph_bounds_cff2(index, coords) {
      Err(err) => return Err(err)
      Ok(value) => value
    }
    match cff2_bounds {
      Some(value) => return Ok(Some(value))
      None => return self.glyph_bounds_cff1(index)
    }
  }
  let loca = match self.ensure_loca() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  let glyf = match self.ensure_glyf() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  let offsets = loca.offsets
  if index + 1 >= offsets.length() {
    return Err(GlyphOutOfRange)
  }
  let start = offsets[index]
  let end = offsets[index + 1]
  let length = end - start
  if !coords.is_empty() {
    let gvar = match self.gvar() {
      Err(err) => return Err(err)
      Ok(value) => value
    }
    match gvar {
      None => ()
      Some(_) => {
        let points = match self.glyph_points_with_gvar(glyph, coords) {
          Err(err) => return Err(err)
          Ok(value) => value
        }
        match points {
          None => ()
          Some(points) =>
            match glyph_bounds_from_points_for_font(points) {
              None => ()
              Some(bounds) => return Ok(Some(bounds))
            }
        }
      }
    }
  }
  match glyf.glyph_bounds(start, length) {
    Err(err) => Err(Parse(err))
    Ok(bounds) => Ok(bounds)
  }
}

fn Font::glyph_bounds_cff1(
  self : Font,
  glyph : Int,
) -> Result[@sfnt.GlyphBounds?, FontError] {
  let cff1 = match self.cff1() {
    Err(err) => return Err(err)
    Ok(None) => return Ok(None)
    Ok(Some(value)) => value
  }
  let bounds = match cff1.glyph_bounds(glyph) {
    Err(err) => return Err(Cff(err))
    Ok(value) => value
  }
  match bounds {
    None => Ok(None)
    Some(bounds) => {
      let x_min = @math.floor(bounds.x_min).to_int()
      let y_min = @math.floor(bounds.y_min).to_int()
      let x_max = @math.ceil(bounds.x_max).to_int()
      let y_max = @math.ceil(bounds.y_max).to_int()
      Ok(Some(@sfnt.GlyphBounds::new(-1, x_min, y_min, x_max, y_max)))
    }
  }
}

fn Font::glyph_bounds_cff2(
  self : Font,
  glyph : Int,
  coords : Array[Int],
) -> Result[@sfnt.GlyphBounds?, FontError] {
  let cff2 = match self.cff2() {
    Err(err) => return Err(err)
    Ok(None) => return Ok(None)
    Ok(Some(value)) => value
  }
  let bounds = match cff2.glyph_bounds(glyph, coords) {
    Err(err) => return Err(Cff(err))
    Ok(value) => value
  }
  match bounds {
    None => Ok(None)
    Some(bounds) => {
      let x_min = @math.floor(bounds.x_min).to_int()
      let y_min = @math.floor(bounds.y_min).to_int()
      let x_max = @math.ceil(bounds.x_max).to_int()
      let y_max = @math.ceil(bounds.y_max).to_int()
      Ok(Some(@sfnt.GlyphBounds::new(-1, x_min, y_min, x_max, y_max)))
    }
  }
}

///|
/// Return glyph vertical advance.
pub fn Font::glyph_v_advance(
  self : Font,
  glyph : UInt,
) -> Result[Int, FontError] {
  let head = match self.ensure_head() {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  let vmtx = match self.vmtx() {
    Err(err) => return Err(err)
    Ok(None) => return Ok(0)
    Ok(Some(value)) => value
  }
  let index = glyph.reinterpret_as_int()
  if index < 0 || index >= vmtx.advances.length() {
    return Err(GlyphOutOfRange)
  }
  let mut advance = vmtx.advances[index]
  match self.var_coords_norm {
    None => ()
    Some(coords) => {
      let vvar = match self.vvar() {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      match vvar {
        Some(vvar) => {
          let delta = vvar.advance_delta(glyph, coords)
          advance = advance + delta.round().to_int()
        }
        None =>
          match self.gvar() {
            Err(err) => return Err(err)
            Ok(None) => ()
            Ok(Some(_)) =>
              match self.glyph_gvar_advance(glyph, coords, true) {
                Err(err) => return Err(err)
                Ok(None) => ()
                Ok(Some(value)) => advance = value
              }
          }
      }
    }
  }
  Ok(-scale_value(advance, self.scale_y, head.units_per_em))
}