///|
// Writing composite (CID-keyed) fonts. `PdfFontCIDKeyed` was previously
// readable but not writable, so a document holding CJK text could be
// parsed and never reproduced.
//
// A composite font is two dictionaries: a `/Type0` root naming an
// encoding and pointing at a single descendant, and a `/CIDFontType2`
// descendant carrying the CID system info, the widths, and the font
// descriptor. As for simple fonts, the descriptor's `/FontFile2` is
// optional — a non-embedded CID font with a `/W` array is legal, and is
// how a document references an installed CJK face.

///|
let pdf_text_cid_font_type2_name : @core.PdfName = pdf_text_static_name(
  "/CIDFontType2",
)

///|
let pdf_text_cid_to_gid_map_key_name : @core.PdfName = pdf_text_static_name(
  "/CIDToGIDMap",
)

///|
let pdf_text_identity_name : @core.PdfName = pdf_text_static_name("/Identity")

///|
/// `/W` groups consecutive CIDs that share a width, which is both what
/// the format expects and much smaller than one entry per CID. Runs are
/// emitted as `first [w w w]`; a single CID is still a one-element run.
fn pdf_text_cid_widths_array(
  widths : ArrayView[(Int, Double)],
) -> @syntax.PdfObject {
  let entries : Array[@syntax.PdfObject] = []
  let mut index = 0
  while index < widths.length() {
    let (first_cid, _) = widths[index]
    // gather the run of consecutive CIDs starting here
    let run : Array[@syntax.PdfObject] = []
    let mut last = index
    while last < widths.length() {
      let (cid, width) = widths[last]
      if cid != first_cid + (last - index) {
        break
      }
      run.push(pdf_text_integer_of_double(width))
      last += 1
    }
    entries.push(PdfInteger(first_cid))
    entries.push(PdfArray(run))
    index = last
  }
  PdfArray(entries)
}

///|
/// The `/Encoding` entry of a `/Type0` font: a predefined CMap by name,
/// or a reference to an embedded CMap stream.
fn pdf_text_cid_encoding_object(
  encoding : PdfCMapEncoding,
) -> @syntax.PdfObject {
  match encoding {
    PdfPredefinedCMap(name) => PdfNameObject(name)
    PdfCMap(number) => PdfIndirect(number)
    PdfExternalCMap(number, _) => PdfIndirect(number)
  }
}

///|
/// Write a CID-keyed font and return the object number of its `/Type0`
/// dictionary.
fn PdfDocument::pdf_text_make_cid_keyed_font(
  self : PdfDocument,
  font : PdfCIDKeyedFont,
) -> @syntax.PdfObject {
  let composite = font.composite
  let descriptor = composite.cid_fontdescriptor
  let descriptor_entries : Array[(@core.PdfName, @syntax.PdfObject)] = [
    (pdf_text_type_key_name, PdfNameObject(pdf_text_font_descriptor_key_name)),
    (pdf_text_font_name_key_name, PdfNameObject(composite.cid_basefont)),
    (pdf_text_flags_key_name, PdfInteger(descriptor.flags)),
    (
      pdf_text_font_bbox_key_name,
      pdf_text_font_descriptor_bbox_object(descriptor.fontbbox),
    ),
    (
      pdf_text_italic_angle_key_name,
      pdf_text_integer_of_double(descriptor.italicangle),
    ),
    (pdf_text_ascent_key_name, pdf_text_integer_of_double(descriptor.ascent)),
    (pdf_text_descent_key_name, pdf_text_integer_of_double(descriptor.descent)),
    (
      pdf_text_cap_height_key_name,
      pdf_text_integer_of_double(descriptor.capheight),
    ),
    (pdf_text_stemv_key_name, pdf_text_integer_of_double(descriptor.stemv)),
  ]
  // as for simple fonts, an absent program means the consumer
  // substitutes the face; the widths still govern spacing
  match pdf_text_fontfile2_number(descriptor.fontfile) {
    Some(number) =>
      descriptor_entries.push(
        (pdf_text_font_file2_key_name, PdfIndirect(number)),
      )
    None => ()
  }
  let descriptor_number = self.add_object(PdfDictionary(descriptor_entries))
  let system_info = composite.cid_system_info
  let descendant_entries : Array[(@core.PdfName, @syntax.PdfObject)] = [
    (pdf_text_type_key_name, PdfNameObject(pdf_text_font_name)),
    (pdf_text_subtype_key_name, PdfNameObject(pdf_text_cid_font_type2_name)),
    (pdf_text_base_font_key_name, PdfNameObject(composite.cid_basefont)),
    (
      pdf_text_cid_system_info_key_name,
      PdfDictionary([
        (pdf_text_registry_key_name, PdfString(system_info.registry)),
        (pdf_text_ordering_key_name, PdfString(system_info.ordering)),
        (pdf_text_supplement_key_name, PdfInteger(system_info.supplement)),
      ]),
    ),
    (pdf_text_font_descriptor_key_name, PdfIndirect(descriptor_number)),
    (
      pdf_text_dw_key_name,
      pdf_text_integer_of_double(composite.cid_default_width),
    ),
    // Identity: a CID is its own glyph index, which is what Identity-H
    // encoding means and what a subsetter preserving glyph ids produces
    (pdf_text_cid_to_gid_map_key_name, PdfNameObject(pdf_text_identity_name)),
  ]
  if composite.cid_widths.length() > 0 {
    descendant_entries.push(
      (pdf_text_w_key_name, pdf_text_cid_widths_array(composite.cid_widths)),
    )
  }
  let descendant_number = self.add_object(PdfDictionary(descendant_entries))
  let root_entries : Array[(@core.PdfName, @syntax.PdfObject)] = [
    (pdf_text_type_key_name, PdfNameObject(pdf_text_font_name)),
    (pdf_text_subtype_key_name, PdfNameObject(pdf_text_type0_name)),
    (pdf_text_base_font_key_name, PdfNameObject(font.basefont)),
    (pdf_text_encoding_key_name, pdf_text_cid_encoding_object(font.encoding)),
    (
      pdf_text_descendant_fonts_key_name,
      PdfArray([PdfIndirect(descendant_number)]),
    ),
  ]
  // a composite font's ToUnicode maps two-byte codes, so it is written
  // from the descriptor when one is supplied
  match descriptor.tounicode {
    Some(tounicode) =>
      root_entries.push(
        (
          pdf_text_tounicode_key_name,
          PdfIndirect(self.pdf_text_write_tounicode_cid(tounicode)),
        ),
      )
    None => ()
  }
  PdfDictionary(root_entries)
}