// ============================================================
// PDF
//
// 纯 MoonBit 的 PDF 文本提取:
//   1. 扫描文件,解析间接对象(字典 / 数组 / 字符串 / 名称 / 数字 / 引用)
//   2. 遍历页树(Catalog → Pages → Kids),逐页取内容流
//   3. FlateDecode 解压交给 hustcer/fzip(unzlib_sync / inflate_sync)
//   4. 解析内容流文本算子(Tj / TJ / ' / "),按当前字体解码
//   5. 字体编码:CID 字体(Type0/Identity-H)走 ToUnicode CMap,
//      简单字体(TrueType/WinAnsi 等)按 Latin-1 直接映射
//
// 已知限制:仅实现 bfchar + 简单 bfrange 的 ToUnicode;不解析内联图片、
// 页面继承的 Resources(仅取页自身 /Resources)。
// ============================================================

///|
/// 读取 PDF 全部文本(各页按换行拼接)
pub fn read_pdf_text(path : String) -> String raise ReaderError {
  read_pdf_text_by_page(path).join("\n")
}

///|
/// 读取 PDF 每页文本(一页一个字符串,页内文本片段按换行拼接)
pub fn read_pdf_text_by_page(path : String) -> Array[String] raise ReaderError {
  let data = read_txt_by_byte(path).to_array()
  let doc = parse_pdf(data)
  let root_num = get_root(doc)
  let root_dict = match lookup(doc, root_num).value {
    PDict(kv) => kv
    _ => raise ReaderError::Parse("PDF Catalog 无效")
  }
  let pages_num = match dict_get(root_dict, "Pages") {
    Some(PRef(n)) => n
    _ => raise ReaderError::Parse("PDF 缺少 Pages")
  }
  let pages = collect_pages(doc, pages_num)
  let out : Array[String] = []
  for pnum in pages {
    let pobj = lookup(doc, pnum)
    let pdict = match pobj.value {
      PDict(kv) => kv
      _ => []
    }
    let fonts = build_page_fonts(doc, pdict)
    let content = get_content_bytes(doc, pdict)
    let runs = extract_text(content, fonts)
    out.push(runs.join("\n"))
  }
  out
}

// ============================================================
// 数据模型
// ============================================================

///|
/// PDF 值:动态类型
priv enum PdfValue {
  PStr(Bytes)
  PName(String)
  PArr(Array[PdfValue])
  PDict(Array[(String, PdfValue)])
  PRef(Int)
}

///|
/// 一个间接对象:值 + 可选的流字节
priv struct PdfObject {
  value : PdfValue
  stream : Bytes?
}

///|
/// 整个文档:对象表(按对象号线性查找,文件很小足够)
priv struct PdfDoc {
  objects : Array[(Int, PdfObject)]
}

///|
/// 字体描述:simple = 单字节编码;否则按 2 字节 CID 查 cid_map
priv struct PdfFont {
  simple : Bool
  cid_map : Array[(Int, Int)]
}

// ============================================================
// 文档解析:扫描对象
// ============================================================

///|
/// 扫描整个文件,按 "N G obj ... endobj" 找出所有间接对象
fn parse_pdf(data : Array[Byte]) -> PdfDoc raise ReaderError {
  let n = data.length()
  let objs : Array[(Int, PdfObject)] = []
  let mut i = 0
  while i < n {
    if !is_digit(data[i]) {
      i = i + 1
      continue
    }
    match try_obj_header(data, n, i) {
      Some((num, _gen, after)) => {
        let pos = @ref.new(after)
        match parse_value(data, n, pos) {
          None => i = pos.val
          Some(value) => {
            pdf_skip_ws(data, n, pos)
            let mut stream : Bytes? = None
            if matches_word(data, n, pos, "stream") {
              pos.val = pos.val + 6
              if pos.val < n && data[pos.val] == b'\r' {
                pos.val = pos.val + 1
              }
              if pos.val < n && data[pos.val] == b'\n' {
                pos.val = pos.val + 1
              }
              let start = pos.val
              let e = find_endstream(data, start, n)
              if e < 0 {
                raise ReaderError::Parse("PDF 流缺少 endstream")
              }
              let rawb = slice_bytes(data, start, e)
              let mut e2 = rawb.length()
              while e2 > 0 && (rawb[e2 - 1] == b'\r' || rawb[e2 - 1] == b'\n') {
                e2 = e2 - 1
              }
              stream = Some(Bytes::from_array(slice_bytes(rawb, 0, e2)))
              pos.val = e + 9
            }
            objs.push((num, { value, stream, }))
            i = pos.val
          }
        }
      }
      None => i = i + 1
    }
  }
  { objects: objs, }
}

///|
/// 尝试在 i 处解析 "N G obj" 头部,成功返回 (对象号, 代, 头部之后的位置)
fn try_obj_header(data : Array[Byte], n : Int, i : Int) -> (Int, Int, Int)? {
  let mut p = i
  let mut num = 0
  while p < n && is_digit(data[p]) {
    num = num * 10 + (data[p].to_int() - 48)
    p = p + 1
  }
  if p >= n || !is_ws_byte(data[p]) {
    return None
  }
  while p < n && is_ws_byte(data[p]) {
    p = p + 1
  }
  let mut gen = 0
  while p < n && is_digit(data[p]) {
    gen = gen * 10 + (data[p].to_int() - 48)
    p = p + 1
  }
  if p >= n || !is_ws_byte(data[p]) {
    return None
  }
  while p < n && is_ws_byte(data[p]) {
    p = p + 1
  }
  if p + 3 > n {
    return None
  }
  if data[p] == b'o' && data[p + 1] == b'b' && data[p + 2] == b'j' {
    Some((num, gen, p + 3))
  } else {
    None
  }
}

///|
/// 在 data[start..] 中查找 "endstream",返回其起始下标(未找到返回 -1)
fn find_endstream(data : Array[Byte], start : Int, n : Int) -> Int {
  let mut i = start
  while i + 8 < n {
    if data[i] == b'e' &&
      data[i + 1] == b'n' &&
      data[i + 2] == b'd' &&
      data[i + 3] == b's' &&
      data[i + 4] == b't' &&
      data[i + 5] == b'r' &&
      data[i + 6] == b'e' &&
      data[i + 7] == b'a' &&
      data[i + 8] == b'm' {
      return i
    }
    i = i + 1
  }
  -1
}

///|
/// 复制字节区间 [start, end) 为新的 Array[Byte]
fn slice_bytes(data : Array[Byte], start : Int, end : Int) -> Array[Byte] {
  let out : Array[Byte] = []
  let mut i = start
  while i < end && i < data.length() {
    out.push(data[i])
    i = i + 1
  }
  out
}

// ============================================================
// 值解析(递归下降)
// ============================================================

///|
fn parse_value(
  data : Array[Byte],
  n : Int,
  pos : Ref[Int],
) -> PdfValue? raise ReaderError {
  pdf_skip_ws(data, n, pos)
  if pos.val >= n {
    raise ReaderError::Parse("PDF 解析意外结束")
  }
  let c = data[pos.val]
  if c == b'<' {
    if pos.val + 1 < n && data[pos.val + 1] == b'<' {
      Some(parse_dict(data, n, pos))
    } else {
      Some(parse_hex_string(data, n, pos))
    }
  } else if c == b'(' {
    Some(parse_lit_string(data, n, pos))
  } else if c == b'[' {
    Some(parse_array(data, n, pos))
  } else if c == b'/' {
    Some(parse_name_value(data, n, pos))
  } else if c == b't' && matches_word(data, n, pos, "true") {
    pos.val = pos.val + 4
    None
  } else if c == b'f' && matches_word(data, n, pos, "false") {
    pos.val = pos.val + 5
    None
  } else if c == b'n' && matches_word(data, n, pos, "null") {
    pos.val = pos.val + 4
    None
  } else if c == b'-' || c == b'+' || c == b'.' || is_digit(c) {
    parse_number_or_ref(data, n, pos)
  } else {
    Some(parse_bare_name(data, n, pos))
  }
}

///|
/// 解析字典 << ... >>
fn parse_dict(
  data : Array[Byte],
  n : Int,
  pos : Ref[Int],
) -> PdfValue raise ReaderError {
  pos.val = pos.val + 2
  let kv : Array[(String, PdfValue)] = []
  pdf_skip_ws(data, n, pos)
  while pos.val < n {
    if data[pos.val] == b'>' && pos.val + 1 < n && data[pos.val + 1] == b'>' {
      pos.val = pos.val + 2
      return PDict(kv)
    }
    if data[pos.val] != b'/' {
      pos.val = pos.val + 1
      continue
    }
    let key = parse_name_string(data, n, pos)
    match parse_value(data, n, pos) {
      Some(value) => kv.push((key, value))
      None => ()
    }
    pdf_skip_ws(data, n, pos)
  }
  PDict(kv)
}

///|
/// 解析数组 [ ... ]
fn parse_array(
  data : Array[Byte],
  n : Int,
  pos : Ref[Int],
) -> PdfValue raise ReaderError {
  pos.val = pos.val + 1
  let arr : Array[PdfValue] = []
  pdf_skip_ws(data, n, pos)
  while pos.val < n && data[pos.val] != b']' {
    match parse_value(data, n, pos) {
      Some(v) => arr.push(v)
      None => ()
    }
    pdf_skip_ws(data, n, pos)
  }
  if pos.val < n && data[pos.val] == b']' {
    pos.val = pos.val + 1
  }
  PArr(arr)
}

///|
/// 解析十六进制字符串 <...>(保留原始字节)
fn parse_hex_string(data : Array[Byte], n : Int, pos : Ref[Int]) -> PdfValue {
  pos.val = pos.val + 1
  let buf : Array[Byte] = []
  let mut hi = -1
  while pos.val < n {
    let c = data[pos.val]
    if c == b'>' {
      pos.val = pos.val + 1
      break
    }
    if is_ws_byte(c) {
      pos.val = pos.val + 1
      continue
    }
    let v = hex_val(c)
    if v < 0 {
      pos.val = pos.val + 1
      continue
    }
    if hi < 0 {
      hi = v
    } else {
      buf.push((hi * 16 + v).to_byte())
      hi = -1
    }
    pos.val = pos.val + 1
  }
  if hi >= 0 {
    buf.push((hi * 16).to_byte())
  }
  PStr(Bytes::from_array(buf))
}

///|
/// 解析字面字符串 (...),处理反斜杠转义与括号嵌套
fn parse_lit_string(data : Array[Byte], n : Int, pos : Ref[Int]) -> PdfValue {
  pos.val = pos.val + 1
  let buf : Array[Byte] = []
  let mut depth = 1
  while pos.val < n && depth > 0 {
    let c = data[pos.val]
    if c == b'\\' {
      pos.val = pos.val + 1
      if pos.val >= n {
        break
      }
      let e = data[pos.val]
      let ei = e.to_int()
      if ei == 'n'.to_int() {
        buf.push(b'\n')
        pos.val = pos.val + 1
      } else if ei == 'r'.to_int() {
        buf.push(b'\r')
        pos.val = pos.val + 1
      } else if ei == 't'.to_int() {
        buf.push(b'\t')
        pos.val = pos.val + 1
      } else if ei == 'b'.to_int() {
        buf.push(b'\x08')
        pos.val = pos.val + 1
      } else if ei == 'f'.to_int() {
        buf.push(b'\x0c')
        pos.val = pos.val + 1
      } else if ei == '('.to_int() {
        buf.push(b'(')
        pos.val = pos.val + 1
      } else if ei == ')'.to_int() {
        buf.push(b')')
        pos.val = pos.val + 1
      } else if ei == '\\'.to_int() {
        buf.push(b'\\')
        pos.val = pos.val + 1
      } else if ei == '\n'.to_int() {
        pos.val = pos.val + 1
      } else if ei == '\r'.to_int() {
        pos.val = pos.val + 1
        if pos.val < n && data[pos.val] == b'\n' {
          pos.val = pos.val + 1
        }
      } else if ei >= '0'.to_int() && ei <= '7'.to_int() {
        let mut v = ei - '0'.to_int()
        let mut cnt = 1
        pos.val = pos.val + 1
        while cnt < 3 &&
              pos.val < n &&
              data[pos.val].to_int() >= '0'.to_int() &&
              data[pos.val].to_int() <= '7'.to_int() {
          v = v * 8 + (data[pos.val].to_int() - '0'.to_int())
          pos.val = pos.val + 1
          cnt = cnt + 1
        }
        buf.push(v.to_byte())
      } else {
        buf.push(e)
        pos.val = pos.val + 1
      }
    } else if c == b'(' {
      depth = depth + 1
      buf.push(c)
      pos.val = pos.val + 1
    } else if c == b')' {
      depth = depth - 1
      if depth > 0 {
        buf.push(c)
      }
      pos.val = pos.val + 1
    } else {
      buf.push(c)
      pos.val = pos.val + 1
    }
  }
  PStr(Bytes::from_array(buf))
}

///|
/// 读取名称(从 '/' 开始),返回 String(含 #xx 转义处理)
fn parse_name_string(data : Array[Byte], n : Int, pos : Ref[Int]) -> String {
  let buf = read_name_bytes(data, n, pos)
  @utf8.decode_lossy(Bytes::from_array(buf))
}

///|
fn parse_name_value(data : Array[Byte], n : Int, pos : Ref[Int]) -> PdfValue {
  let buf = read_name_bytes(data, n, pos)
  PName(@utf8.decode_lossy(Bytes::from_array(buf)))
}

///|
fn read_name_bytes(data : Array[Byte], n : Int, pos : Ref[Int]) -> Array[Byte] {
  pos.val = pos.val + 1
  let buf : Array[Byte] = []
  while pos.val < n && !is_delim(data[pos.val]) {
    let c = data[pos.val]
    if c == b'#' && pos.val + 2 < n {
      let h = hex_val(data[pos.val + 1]) * 16 + hex_val(data[pos.val + 2])
      if h >= 0 {
        buf.push(h.to_byte())
        pos.val = pos.val + 3
      } else {
        buf.push(c)
        pos.val = pos.val + 1
      }
    } else {
      buf.push(c)
      pos.val = pos.val + 1
    }
  }
  buf
}

///|
/// 内容流中的操作符是无斜杠的裸名称,此处按名称读取
fn parse_bare_name(data : Array[Byte], n : Int, pos : Ref[Int]) -> PdfValue {
  let buf : Array[Byte] = []
  while pos.val < n && !is_delim(data[pos.val]) {
    buf.push(data[pos.val])
    pos.val = pos.val + 1
  }
  PName(@utf8.decode_lossy(Bytes::from_array(buf)))
}

///|
/// 数字或引用(N G R):先尝试按引用解析,失败则按单个数字回退
fn parse_number_or_ref(
  data : Array[Byte],
  n : Int,
  pos : Ref[Int],
) -> PdfValue? {
  let save = pos.val
  match try_parse_int(data, n, pos) {
    Some(a) => {
      pdf_skip_ws(data, n, pos)
      match try_parse_int(data, n, pos) {
        Some(_) => {
          pdf_skip_ws(data, n, pos)
          if pos.val < n && data[pos.val] == b'R' {
            pos.val = pos.val + 1
            return Some(PRef(a))
          }
        }
        None => ()
      }
    }
    None => ()
  }
  pos.val = save
  parse_number_token(data, n, pos)
  None
}

///|
/// 尝试在 pos 处解析整数,成功则前进并返回 Some,否则位置不变
fn try_parse_int(data : Array[Byte], n : Int, pos : Ref[Int]) -> Int? {
  let mut p = pos.val
  let mut sign = 1
  if p < n && (data[p] == b'-' || data[p] == b'+') {
    if data[p] == b'-' {
      sign = -1
    }
    p = p + 1
  }
  if p >= n || !is_digit(data[p]) {
    return None
  }
  let mut v = 0
  while p < n && is_digit(data[p]) {
    v = v * 10 + (data[p].to_int() - 48)
    p = p + 1
  }
  pos.val = p
  Some(sign * v)
}

///|
/// 前进并吞掉一个数字字面量(整数 / 实数 / 科学计数),不返回其值
fn parse_number_token(data : Array[Byte], n : Int, pos : Ref[Int]) -> Unit {
  if pos.val < n && (data[pos.val] == b'-' || data[pos.val] == b'+') {
    pos.val = pos.val + 1
  }
  while pos.val < n && is_digit(data[pos.val]) {
    pos.val = pos.val + 1
  }
  if pos.val < n && data[pos.val] == b'.' {
    pos.val = pos.val + 1
    while pos.val < n && is_digit(data[pos.val]) {
      pos.val = pos.val + 1
    }
  }
  if pos.val < n && (data[pos.val] == b'e' || data[pos.val] == b'E') {
    pos.val = pos.val + 1
    if pos.val < n && (data[pos.val] == b'-' || data[pos.val] == b'+') {
      pos.val = pos.val + 1
    }
    while pos.val < n && is_digit(data[pos.val]) {
      pos.val = pos.val + 1
    }
  }
}

// ============================================================
// 字符级辅助
// ============================================================

///|
fn pdf_skip_ws(data : Array[Byte], n : Int, pos : Ref[Int]) -> Unit {
  while pos.val < n {
    let c = data[pos.val]
    if c == b' ' ||
      c == b'\n' ||
      c == b'\r' ||
      c == b'\t' ||
      c == b'\x0c' ||
      c == b'\x00' {
      pos.val = pos.val + 1
    } else if c == b'%' {
      while pos.val < n && data[pos.val] != b'\n' && data[pos.val] != b'\r' {
        pos.val = pos.val + 1
      }
    } else {
      break
    }
  }
}

///|
fn is_ws_byte(c : Byte) -> Bool {
  c == b' ' ||
  c == b'\n' ||
  c == b'\r' ||
  c == b'\t' ||
  c == b'\x0c' ||
  c == b'\x00'
}

///|
fn is_digit(c : Byte) -> Bool {
  let v = c.to_int()
  v >= '0'.to_int() && v <= '9'.to_int()
}

///|
fn is_delim(c : Byte) -> Bool {
  is_ws_byte(c) ||
  c == b'(' ||
  c == b')' ||
  c == b'<' ||
  c == b'>' ||
  c == b'[' ||
  c == b']' ||
  c == b'{' ||
  c == b'}' ||
  c == b'/' ||
  c == b'%'
}

///|
fn hex_val(c : Byte) -> Int {
  let v = c.to_int()
  if v >= '0'.to_int() && v <= '9'.to_int() {
    v - '0'.to_int()
  } else if v >= 'a'.to_int() && v <= 'f'.to_int() {
    v - 'a'.to_int() + 10
  } else if v >= 'A'.to_int() && v <= 'F'.to_int() {
    v - 'A'.to_int() + 10
  } else {
    -1
  }
}

///|
/// 判断 pos 处是否为单词 word 且后面是分隔符(不前进)
fn matches_word(
  data : Array[Byte],
  n : Int,
  pos : Ref[Int],
  word : String,
) -> Bool {
  let w = word.iter().to_array()
  let k = w.length()
  if pos.val + k > n {
    return false
  }
  let mut j = 0
  while j < k {
    if data[pos.val + j].to_int() != w[j].to_int() {
      return false
    }
    j = j + 1
  }
  if pos.val + k < n && !is_delim(data[pos.val + k]) {
    return false
  }
  true
}

// ============================================================
// 对象导航
// ============================================================

///|
fn lookup(doc : PdfDoc, num : Int) -> PdfObject raise ReaderError {
  for kv in doc.objects {
    let (n, o) = kv
    if n == num {
      return o
    }
  }
  raise ReaderError::Parse("PDF 未找到对象 " + num.to_string())
}

///|
/// 解析间接引用(只跟随一层)
fn resolve(doc : PdfDoc, v : PdfValue) -> PdfValue raise ReaderError {
  match v {
    PRef(n) => lookup(doc, n).value
    _ => v
  }
}

///|
/// 把值解析为间接对象(引用则取对象本身)
fn resolve_obj(doc : PdfDoc, v : PdfValue) -> PdfObject raise ReaderError {
  match v {
    PRef(n) => lookup(doc, n)
    _ => { value: v, stream: None, }
  }
}

///|
/// 取字典中 key 对应的字典(自动解析引用),不存在返回 None
fn field_dict(
  doc : PdfDoc,
  dict : Array[(String, PdfValue)],
  key : String,
) -> Array[(String, PdfValue)]? raise ReaderError {
  match dict_get(dict, key) {
    Some(v) =>
      match resolve(doc, v) {
        PDict(kv) => Some(kv)
        _ => None
      }
    None => None
  }
}

///|
fn dict_get(dict : Array[(String, PdfValue)], key : String) -> PdfValue? {
  for kv in dict {
    let (k, v) = kv
    if k == key {
      return Some(v)
    }
  }
  None
}

///|
fn dict_name(dict : Array[(String, PdfValue)], key : String) -> String? {
  match dict_get(dict, key) {
    Some(PName(n)) => Some(n)
    _ => None
  }
}

///|
/// 查找 Catalog 对象号
fn get_root(doc : PdfDoc) -> Int raise ReaderError {
  for kv in doc.objects {
    let (num, obj) = kv
    match obj.value {
      PDict(kv) =>
        match dict_name(kv, "Type") {
          Some("Catalog") => return num
          _ => ()
        }
      _ => ()
    }
  }
  raise ReaderError::Parse("PDF 未找到 Catalog")
}

///|
/// 遍历页树,收集所有 /Page 对象号
fn collect_pages(doc : PdfDoc, pages_num : Int) -> Array[Int] raise ReaderError {
  let out : Array[Int] = []
  let pobj = lookup(doc, pages_num)
  let pdict = match pobj.value {
    PDict(kv) => kv
    _ => raise ReaderError::Parse("PDF Pages 对象无效")
  }
  match dict_get(pdict, "Kids") {
    Some(PArr(kids)) =>
      for kid in kids {
        match kid {
          PRef(n) => {
            let k = lookup(doc, n)
            match k.value {
              PDict(kv) =>
                match dict_name(kv, "Type") {
                  Some("Page") => out.push(n)
                  Some("Pages") =>
                    for p in collect_pages(doc, n) {
                      out.push(p)
                    }
                  _ => ()
                }
              _ => ()
            }
          }
          _ => ()
        }
      }
    _ => ()
  }
  out
}

// ============================================================
// 流解码
// ============================================================

///|
/// 尝试按 zlib 解压,失败退回裸 DEFLATE,再失败返回原样
fn inflate(raw : Bytes) -> Bytes {
  let fa = raw.to_fixedarray()
  Bytes::from_array(@fzip.unzlib_sync(fa)) catch {
    _ => Bytes::from_array(@fzip.inflate_sync(fa)) catch { _ => raw }
  }
}

///|
fn decode_obj_stream(obj : PdfObject) -> Bytes {
  match obj.stream {
    None => b""
    Some(raw) => inflate(raw)
  }
}

///|
/// 取页内容流字节(支持单引用或引用数组,多段拼接)
fn get_content_bytes(
  doc : PdfDoc,
  page_dict : Array[(String, PdfValue)],
) -> Bytes raise ReaderError {
  match dict_get(page_dict, "Contents") {
    None => b""
    Some(c) =>
      match c {
        PArr(items) => {
          let mut all : Bytes = b""
          for it in items {
            all = all + decode_obj_stream(resolve_obj(doc, it))
          }
          all
        }
        _ => decode_obj_stream(resolve_obj(doc, c))
      }
  }
}

// ============================================================
// 字体与 ToUnicode CMap
// ============================================================

///|
/// 构建页的字体表:字体名 → PdfFont
fn build_page_fonts(
  doc : PdfDoc,
  pdict : Array[(String, PdfValue)],
) -> Array[(String, PdfFont)] raise ReaderError {
  let out : Array[(String, PdfFont)] = []
  match field_dict(doc, pdict, "Resources") {
    Some(rd) =>
      match field_dict(doc, rd, "Font") {
        Some(fd) =>
          for kv in fd {
            let (name, fv) = kv
            out.push((name, build_font(doc, fv)))
          }
        None => ()
      }
    None => ()
  }
  out
}

///|
fn build_font(doc : PdfDoc, fv : PdfValue) -> PdfFont raise ReaderError {
  match resolve(doc, fv) {
    PDict(kv) => {
      let subtype = dict_name(kv, "Subtype")
      let encoding = dict_name(kv, "Encoding")
      let cmap = match dict_get(kv, "ToUnicode") {
        Some(tu) => parse_cmap_from(doc, tu)
        None => []
      }
      let is_cid = subtype == Some("Type0") ||
        encoding == Some("Identity-H") ||
        encoding == Some("Identity-V")
      { simple: !is_cid, cid_map: cmap, }
    }
    _ => { simple: true, cid_map: [], }
  }
}

///|
/// 从 ToUnicode 引用取流、解压、解析为 (cid, unicode) 映射
fn parse_cmap_from(
  doc : PdfDoc,
  tu : PdfValue,
) -> Array[(Int, Int)] raise ReaderError {
  let obj = resolve_obj(doc, tu)
  match obj.stream {
    None => []
    Some(raw) => {
      let text = @utf8.decode_lossy(inflate(raw))
      parse_cmap(text)
    }
  }
}

///|
/// 解析 ToUnicode CMap 文本中的 bfchar / bfrange
fn parse_cmap(text : String) -> Array[(Int, Int)] {
  let out : Array[(Int, Int)] = []
  let bf = cmap_region(text, "beginbfchar", "endbfchar")
  let ints = extract_hex_ints(bf)
  let mut i = 0
  while i + 1 < ints.length() {
    out.push((ints[i], ints[i + 1]))
    i = i + 2
  }
  // bfrange 只处理无数组的三元组形式   
  let br = cmap_region(text, "beginbfrange", "endbfrange")
  if !str_has(br, '[') {
    let rs = extract_hex_ints(br)
    let mut j = 0
    while j + 2 < rs.length() {
      let lo = rs[j]
      let hi = rs[j + 1]
      let dst = rs[j + 2]
      let mut k = 0
      while lo + k <= hi {
        out.push((lo + k, dst + k))
        k = k + 1
      }
      j = j + 3
    }
  }
  out
}

///|
fn cmap_region(text : String, start : String, end : String) -> String {
  let si = index_of(text, start)
  if si < 0 {
    return ""
  }
  let ei = index_of(text, end)
  if ei < 0 {
    return ""
  }
  text[si + start.length():ei].to_owned()
}

///|
fn index_of(s : String, sub : String) -> Int {
  let sc = s.iter().to_array()
  let pc = sub.iter().to_array()
  let n = sc.length()
  let m = pc.length()
  if m == 0 {
    return 0
  }
  let mut i = 0
  while i + m <= n {
    let mut j = 0
    while j < m && sc[i + j] == pc[j] {
      j = j + 1
    }
    if j == m {
      return i
    }
    i = i + 1
  }
  -1
}

///|
fn str_has(s : String, ch : Char) -> Bool {
  for c in s {
    if c == ch {
      return true
    }
  }
  false
}

///|
/// 提取字符串中所有  十六进制串对应的整数值
fn extract_hex_ints(s : String) -> Array[Int] {
  let out : Array[Int] = []
  let chars = s.iter().to_array()
  let n = chars.length()
  let mut i = 0
  while i < n {
    if chars[i] == '<' {
      let mut j = i + 1
      let mut v = 0
      let mut ok = false
      while j < n && chars[j] != '>' && hex_val_char(chars[j]) >= 0 {
        v = v * 16 + hex_val_char(chars[j])
        j = j + 1
        ok = true
      }
      if ok && j < n && chars[j] == '>' {
        out.push(v)
        i = j + 1
      } else {
        i = i + 1
      }
    } else {
      i = i + 1
    }
  }
  out
}

///|
fn hex_val_char(c : Char) -> Int {
  let v = c.to_int()
  if v >= '0'.to_int() && v <= '9'.to_int() {
    v - '0'.to_int()
  } else if v >= 'a'.to_int() && v <= 'f'.to_int() {
    v - 'a'.to_int() + 10
  } else if v >= 'A'.to_int() && v <= 'F'.to_int() {
    v - 'A'.to_int() + 10
  } else {
    -1
  }
}

// ============================================================
// 内容流文本提取
// ============================================================

///|
/// 把内容流字节按 PDF 值切分成 token 序列
fn tokenize_content(content : Bytes) -> Array[PdfValue] raise ReaderError {
  let data = content.to_array()
  let n = data.length()
  let pos = @ref.new(0)
  let out : Array[PdfValue] = []
  while pos.val < n {
    pdf_skip_ws(data, n, pos)
    if pos.val >= n {
      break
    }
    match parse_value(data, n, pos) {
      Some(v) => out.push(v)
      None => ()
    }
  }
  out
}

///|
/// 提取内容流中的文本片段,按当前字体解码
fn extract_text(
  content : Bytes,
  fonts : Array[(String, PdfFont)],
) -> Array[String] raise ReaderError {
  let tokens = tokenize_content(content)
  let runs : Array[String] = []
  let mut current_font = "F1"
  let n = tokens.length()
  let mut i = 0
  while i < n {
    match tokens[i] {
      PName(op) =>
        if op == "Tf" && i >= 1 {
          match tokens[i - 1] {
            PName(fname) => current_font = fname
            _ => ()
          }
        } else if op == "Tj" && i >= 1 {
          match tokens[i - 1] {
            PStr(s) => runs.push(decode_with_font(fonts, current_font, s))
            _ => ()
          }
        } else if op == "TJ" && i >= 1 {
          match tokens[i - 1] {
            PArr(a) => {
              let mut sb = ""
              for el in a {
                match el {
                  PStr(s) => sb = sb + decode_with_font(fonts, current_font, s)
                  _ => ()
                }
              }
              if sb != "" {
                runs.push(sb)
              }
            }
            _ => ()
          }
        } else if (op == "'" || op == "\"") && i >= 1 {
          match tokens[i - 1] {
            PStr(s) => runs.push(decode_with_font(fonts, current_font, s))
            _ => ()
          }
        }
      _ => ()
    }
    i = i + 1
  }
  runs
}

///|
fn decode_with_font(
  fonts : Array[(String, PdfFont)],
  name : String,
  s : Bytes,
) -> String {
  let font = match find_font(fonts, name) {
    Some(f) => f
    None => { simple: true, cid_map: [], }
  }
  decode_run(font, s)
}

///|
fn find_font(fonts : Array[(String, PdfFont)], name : String) -> PdfFont? {
  for kv in fonts {
    let (n, f) = kv
    if n == name {
      return Some(f)
    }
  }
  None
}

///|
/// 按字体解码一段字节为字符串
fn decode_run(font : PdfFont, bytes : Bytes) -> String {
  let arr = bytes.to_array()
  let chars : Array[Char] = []
  if font.simple {
    for b in arr {
      chars.push(b.to_int().unsafe_to_char())
    }
  } else {
    let mut i = 0
    while i + 1 < arr.length() {
      let cid = (arr[i].to_int() << 8) | arr[i + 1].to_int()
      let uni = lookup_cid(font.cid_map, cid)
      if uni >= 0 {
        chars.push(uni.unsafe_to_char())
      }
      i = i + 2
    }
  }
  String::from_array(chars)
}

///|
fn lookup_cid(map : Array[(Int, Int)], cid : Int) -> Int {
  for kv in map {
    let (c, u) = kv
    if c == cid {
      return u
    }
  }
  -1
}