///|
fn pdf_content_text_state_name_text(name : @core.PdfName) -> String {
  @ascii.decode_lossy(name.bytes())
}

///|
fn PdfContentTextState::content_text_move(
  self : PdfContentTextState,
  tx : Double,
  ty : Double,
) -> PdfContentTextState {
  let line_matrix = @geometry.transform_matrix_compose(
    self.line_matrix,
    @geometry.transform_translate(tx, ty),
  )
  { ..self, text_matrix: line_matrix, line_matrix, }
}

///|
/// Convert text state to the layout input used by cpdfcontent glyph placement.
pub fn PdfContentTextState::content_text_layout(
  self : PdfContentTextState,
  ctm : @geometry.TransformMatrix,
) -> PdfContentTextLayout {
  {
    font_size: self.font_size,
    horizontal_scaling: self.horizontal_scaling,
    character_spacing: self.character_spacing,
    word_spacing: self.word_spacing,
    rise: self.rise,
    ctm,
    text_matrix: self.text_matrix,
  }
}

///|
/// Apply cpdfcontent text-state operators that do not require font resources.
pub fn PdfContentTextState::content_apply_text_state_op(
  self : PdfContentTextState,
  op : @content.PdfContentOp,
) -> PdfContentTextState {
  match op {
    Op_BT =>
      {
        ..self,
        text_matrix: @geometry.transform_identity_matrix,
        line_matrix: @geometry.transform_identity_matrix,
      }
    Op_ET => self
    Op_Tc(spacing) => { ..self, character_spacing: spacing, }
    Op_Tw(spacing) => { ..self, word_spacing: spacing, }
    Op_Tz(scale) => { ..self, horizontal_scaling: scale / 100.0, }
    Op_TL(leading) => { ..self, leading, }
    Op_Tf(font, size) =>
      { ..self, font: pdf_content_text_state_name_text(font), font_size: size, }
    Op_Tr(mode) => { ..self, rendering_mode: mode, }
    Op_Ts(rise) => { ..self, rise, }
    Op_Td(tx, ty) => self.content_text_move(tx, ty)
    Op_TD(tx, ty) =>
      self
      .content_apply_text_state_op(Op_TL(-ty))
      .content_apply_text_state_op(Op_Td(tx, ty))
    Op_Tm(matrix) => { ..self, text_matrix: matrix, line_matrix: matrix, }
    Op_Tstar => self.content_apply_text_state_op(Op_TD(0.0, -self.leading))
    Op_quote(_) => self.content_apply_text_state_op(Op_Tstar)
    Op_double_quote(word_spacing, character_spacing, _) =>
      self
      .content_apply_text_state_op(Op_Tw(word_spacing))
      .content_apply_text_state_op(Op_Tc(character_spacing))
      .content_apply_text_state_op(Op_Tstar)
    _ => self
  }
}

///|
/// Compatibility wrapper for `PdfContentTextState::content_apply_text_state_op`.
pub fn pdf_content_apply_text_state_op(
  state : PdfContentTextState,
  op : @content.PdfContentOp,
) -> PdfContentTextState {
  state.content_apply_text_state_op(op)
}

///|
fn pdf_content_empty_text_op_result(
  state : PdfContentTextState,
) -> PdfContentTextOpResult {
  { text_state: state, glyphs: [], }
}

///|
fn pdf_content_text_op_with_layout_result(
  state : PdfContentTextState,
  result : PdfContentTextLayoutResult,
) -> PdfContentTextOpResult {
  {
    text_state: { ..state, text_matrix: result.text_matrix, },
    glyphs: result.glyphs,
  }
}

///|
/// Apply cpdfcontent text-state and text-showing operators with a known font.
pub fn PdfContentTextState::content_apply_text_op(
  self : PdfContentTextState,
  font : PdfFont,
  ctm : @geometry.TransformMatrix,
  op : @content.PdfContentOp,
) -> PdfContentTextOpResult {
  match op {
    Op_Tj(text) =>
      pdf_content_text_op_with_layout_result(
        self,
        font.content_text_layout_of_bytes(text, self.content_text_layout(ctm)),
      )
    Op_TJ(elements) =>
      pdf_content_text_op_with_layout_result(
        self,
        font.content_text_layout_of_tj_array(
          match elements {
            PdfArray(values) => values
            _ => []
          },
          self.content_text_layout(ctm),
        ),
      )
    Op_quote(text) =>
      self
      .content_apply_text_state_op(Op_Tstar)
      .content_apply_text_op(font, ctm, Op_Tj(text))
    Op_double_quote(word_spacing, character_spacing, text) =>
      self
      .content_apply_text_state_op(Op_Tw(word_spacing))
      .content_apply_text_state_op(Op_Tc(character_spacing))
      .content_apply_text_op(font, ctm, Op_quote(text))
    _ => pdf_content_empty_text_op_result(self.content_apply_text_state_op(op))
  }
}

///|
/// Compatibility wrapper for `PdfContentTextState::content_apply_text_op`.
pub fn pdf_content_apply_text_op(
  state : PdfContentTextState,
  font : PdfFont,
  ctm : @geometry.TransformMatrix,
  op : @content.PdfContentOp,
) -> PdfContentTextOpResult {
  state.content_apply_text_op(font, ctm, op)
}