///|
fn BpmnPaint::font_name(self : BpmnPaint, name : String) -> String {
  let result = StringBuilder()
  result.write_char('"')
  let mut units = 1
  for c in name {
    let escaped = match c {
      '"' => "\\\""
      '\\' => "\\\\"
      '\n' => "\\a "
      '\r' => "\\d "
      _ => c.to_string()
    }
    if escaped.length() >
      self.limit - self.used - units - 2 - font_stack.length() {
      self.fail("bpmn/render-limit", "Font metadata exceeds the output budget")
      return ""
    }
    units += escaped.length()
    result.write_string(escaped)
  }
  result.write_string("\", " + font_stack)
  result.to_string()
}

///|
fn BpmnPaint::label(
  self : BpmnPaint,
  label : @bpmn_layout.Label,
) -> @writer.Node {
  let box = label.box
  let attrs = [("class", "bpmn-label"), ("data-owner", label.owner)]
  let raw = label.bounds
  let children = []
  let text_children = []
  if raw is Some(r) {
    children.push(
      self.rect(
        r.x.token,
        r.y.token,
        r.width.token,
        r.height.token,
        "none",
        fill=self.paint.color("bg"),
      ),
    )
  } else if label.mask {
    children.push(
      self.rect(
        bn(box.x),
        bn(box.y),
        bn(box.width),
        bn(box.height),
        "none",
        fill=self.paint.color("bg"),
      ),
    )
  }
  let x = box.x + box.width / 2.0
  let lines = label.text.split("\n").to_array()
  let height = lines.length().to_double() * (label.size + 4.0)
  let center_y = box.y + box.height / 2.0
  let family = label.font.name
    .map(name => self.font_name(name))
    .unwrap_or(font_stack)
  let color = label.family
    .map(f => self.paint.color("\{f.name()}-ink"))
    .unwrap_or(self.paint.color("text"))
  for i, line in lines {
    let y = center_y -
      height / 2.0 +
      label.size +
      i.to_double() * (label.size + 4.0)
    let attrs = [
      ("x", bn(x)),
      ("y", bn(y)),
      ("text-anchor", "middle"),
      ("font-family", family),
      ("font-size", label.font.size.map(n => n.token).unwrap_or("13")),
      ("font-weight", label.weight.to_string()),
      ("fill", color),
    ]
    if label.font.italic.unwrap_or(false) {
      attrs.push(("font-style", "italic"))
    }
    if label.font.underline.unwrap_or(false) ||
      label.font.strike_through.unwrap_or(false) {
      attrs.push(
        (
          "text-decoration",
          (if label.font.underline.unwrap_or(false) {
            "underline "
          } else {
            ""
          }) +
          (if label.font.strike_through.unwrap_or(false) {
            "line-through"
          } else {
            ""
          }),
        ),
      )
    }
    text_children.push(
      self.element("text", attrs~, children=[self.text(line.to_owned())]),
    )
  }
  if label.rotation == -90 {
    children.push(
      self.element(
        "g",
        attrs=[
          (
            "transform",
            "rotate(-90 \{bn(box.x+box.width/2.0)} \{bn(box.y+box.height/2.0)})",
          ),
        ],
        children=text_children,
      ),
    )
  } else {
    children.append(text_children)
  }
  self.element("g", attrs~, children~)
}