///|
fn PdfDocument::pdf_text_font_is_embedded(
  self : PdfDocument,
  font : @syntax.PdfObject,
) -> Bool {
  match self.lookup_direct(pdf_text_font_descriptor_key_name, font) {
    Some(descriptor) =>
      pdf_text_dictionary_has_key(descriptor, pdf_text_font_file_key_name) ||
      pdf_text_dictionary_has_key(descriptor, pdf_text_font_file2_key_name) ||
      pdf_text_dictionary_has_key(descriptor, pdf_text_font_file3_key_name)
    None => false
  }
}

///|
fn PdfDocument::pdf_text_font_is_symbolic(
  self : PdfDocument,
  font : @syntax.PdfObject,
) -> Bool raise @core.PdfError {
  match self.lookup_direct(pdf_text_font_descriptor_key_name, font) {
    Some(descriptor) =>
      match self.lookup_direct(pdf_text_flags_key_name, descriptor) {
        Some(PdfInteger(flags)) => (flags & (1 << 3)) > 0
        _ => raise FontFlagsExpected
      }
    None => false
  }
}

///|
fn PdfDocument::pdf_text_default_base_encoding(
  self : PdfDocument,
  font : @syntax.PdfObject,
) -> PdfEncoding raise @core.PdfError {
  if self.pdf_text_font_is_embedded(font) ||
    self.pdf_text_font_is_symbolic(font) {
    PdfImplicitInFontFile
  } else {
    PdfStandardEncoding
  }
}

///|
fn PdfDocument::read_encoding_base(
  self : PdfDocument,
  encoding_dictionary : @syntax.PdfObject,
  font : @syntax.PdfObject,
) -> PdfEncoding raise @core.PdfError {
  match
    self.lookup_direct(pdf_text_base_encoding_key_name, encoding_dictionary) {
    Some(PdfNameObject(name)) =>
      match pdf_encoding_of_name(name) {
        Some(encoding) => encoding
        None => raise EncodingExpected
      }
    Some(_) => raise EncodingExpected
    None => self.pdf_text_default_base_encoding(font)
  }
}

///|
fn PdfDocument::read_encoding_differences(
  self : PdfDocument,
  differences : @syntax.PdfObject,
) -> Array[PdfEncodingDifference] raise @core.PdfError {
  match self.direct(differences) {
    PdfArray(values) => {
      let output : Array[PdfEncodingDifference] = Array(
        capacity=values.length(),
      )
      let mut next_code : Int? = None
      for value in values {
        match self.direct(value) {
          PdfInteger(code) => next_code = Some(code)
          PdfNameObject(name) =>
            match next_code {
              Some(code) => {
                output.push({ name, code, })
                next_code = Some(code + 1)
              }
              None => raise EncodingDifferencesMalformed
            }
          _ => raise EncodingDifferencesMalformed
        }
      }
      output
    }
    _ => raise EncodingDifferencesMalformed
  }
}

///|
fn pdf_text_is_true_type_name(name : @core.PdfName) -> Bool {
  name == pdf_text_true_type_name_value
}

///|
fn pdf_text_is_simple_font_subtype_name(name : @core.PdfName) -> Bool {
  name == pdf_text_type1_name ||
  name == pdf_text_mmtype1_name ||
  name == pdf_text_type3_name ||
  name == pdf_text_true_type_name_value
}

///|
fn pdf_text_is_standard14_subtype_name(name : @core.PdfName) -> Bool {
  name == pdf_text_type1_name || name == pdf_text_true_type_name_value
}

///|
/// Read the simple-font encoding from a font dictionary.
///
/// Name encodings are recognized directly. Encoding dictionaries combine
/// `/BaseEncoding` and `/Differences`; TrueType encodings additionally fall
/// back to StandardEncoding for undefined entries. Missing or unrecognized
/// encodings use `PdfImplicitInFontFile`.
pub fn PdfDocument::read_encoding(
  self : PdfDocument,
  font : @syntax.PdfObject,
) -> PdfEncoding raise @core.PdfError {
  match self.lookup_direct(pdf_text_encoding_key_name, font) {
    Some(PdfNameObject(name)) =>
      match pdf_encoding_of_name(name) {
        Some(encoding) => encoding
        None => PdfImplicitInFontFile
      }
    Some(PdfDictionary(_) as encoding_dictionary) =>
      match self.lookup_direct(pdf_text_subtype_key_name, font) {
        Some(PdfNameObject(subtype)) if pdf_text_is_simple_font_subtype_name(
            subtype,
          ) => {
          let base = self.read_encoding_base(encoding_dictionary, font)
          let encoding = match
            self.lookup_direct(
              pdf_text_differences_key_name, encoding_dictionary,
            ) {
            Some(differences) =>
              PdfCustomEncoding(
                base,
                self.read_encoding_differences(differences),
              )
            None => base
          }
          if pdf_text_is_true_type_name(subtype) {
            PdfFillUndefinedWithStandard(encoding)
          } else {
            encoding
          }
        }
        _ => raise FontSubtypeExpected
      }
    Some(_) => PdfImplicitInFontFile
    None => PdfImplicitInFontFile
  }
}