///|
/// Layout text into positioned glyphs with kerning
pub fn TTFont::layout_text(
  self : TTFont,
  text : String,
  font_size : Double,
) -> Array[GlyphPosition] {
  let scale = font_size / self.units_per_em.to_double()
  let codepoints = string_to_codepoints(text)
  let positions : Array[GlyphPosition] = []
  let mut x = 0.0
  let mut prev_gid = -1
  for cp in codepoints {
    let gid = self.glyph_index(cp)
    // Apply kerning
    if prev_gid >= 0 {
      let kern = self.kern_pair(prev_gid, gid)
      x += kern.to_double() * scale
    }
    let advance = if gid < self.advance_widths.length() {
      self.advance_widths[gid]
    } else {
      0
    }
    let x_advance = advance.to_double() * scale
    positions.push({
      codepoint: cp,
      glyph_id: gid,
      x_offset: x,
      y_offset: 0.0,
      x_advance,
    })
    x += x_advance
    prev_gid = gid
  }
  positions
}

///|
/// Measure total text width
pub fn TTFont::measure_text(
  self : TTFont,
  text : String,
  font_size : Double,
) -> Double {
  let scale = font_size / self.units_per_em.to_double()
  let codepoints = string_to_codepoints(text)
  let mut width = 0.0
  let mut prev_gid = -1
  for cp in codepoints {
    let gid = self.glyph_index(cp)
    if prev_gid >= 0 {
      let kern = self.kern_pair(prev_gid, gid)
      width += kern.to_double() * scale
    }
    let advance = if gid < self.advance_widths.length() {
      self.advance_widths[gid]
    } else {
      0
    }
    width += advance.to_double() * scale
    prev_gid = gid
  }
  width
}

///|
/// Layout text vertically (top-to-bottom) into positioned glyphs
pub fn TTFont::layout_text_vertical(
  self : TTFont,
  text : String,
  font_size : Double,
) -> Array[GlyphPosition] {
  let scale = font_size / self.units_per_em.to_double()
  let codepoints = string_to_codepoints(text)
  let positions : Array[GlyphPosition] = []
  let mut y = 0.0
  for cp in codepoints {
    let gid = self.glyph_index(cp)
    let advance_height = match self.vertical {
      Some(vm) =>
        if gid < vm.advance_heights.length() {
          vm.advance_heights[gid]
        } else {
          self.units_per_em
        }
      None => self.units_per_em
    }
    let origin_y = self.vert_origin_y(gid)
    let y_advance = advance_height.to_double() * scale
    positions.push({
      codepoint: cp,
      glyph_id: gid,
      x_offset: origin_y.to_double() * scale,
      y_offset: y,
      x_advance: y_advance,
    })
    y += y_advance
  }
  positions
}

///|
/// Measure total text height for vertical layout
pub fn TTFont::measure_text_vertical(
  self : TTFont,
  text : String,
  font_size : Double,
) -> Double {
  let scale = font_size / self.units_per_em.to_double()
  let codepoints = string_to_codepoints(text)
  let mut height = 0.0
  for cp in codepoints {
    let gid = self.glyph_index(cp)
    let advance_height = match self.vertical {
      Some(vm) =>
        if gid < vm.advance_heights.length() {
          vm.advance_heights[gid]
        } else {
          self.units_per_em
        }
      None => self.units_per_em
    }
    height += advance_height.to_double() * scale
  }
  height
}

///|
/// Convert String to array of Unicode codepoints (handling surrogate pairs)
fn string_to_codepoints(s : String) -> Array[Int] {
  let result : Array[Int] = []
  let len = s.length()
  let mut i = 0
  while i < len {
    let c = s[i].to_int()
    if c >= 0xD800 && c <= 0xDBFF && i + 1 < len {
      let lo = s[i + 1].to_int()
      if lo >= 0xDC00 && lo <= 0xDFFF {
        let cp = 0x10000 + ((c - 0xD800) << 10) + (lo - 0xDC00)
        result.push(cp)
        i += 2
        continue
      }
    }
    result.push(c)
    i += 1
  }
  result
}