text: """Helper functions and classes for Cairo (drawing engine used by the
  canvas)."""
  from math import pi
  
  import cairo
  
  
  def text_extents(cr, text, font=None, multiline=False, padding=1):
      """Simple way to determine the size of a piece of text."""
      if not text:
          return 0, 0
      if font:
          cr.save()
          text_set_font(cr, font)
  
      if multiline:
          width, height = 0, 0
          for line in text.split("\n"):
              _x_bear, _y_bear, w, h, _x_adv, _y_adv = cr.text_extents(line)
              width = max(width, w)
              height += h + padding
      else:
          _x_bear, _y_bear, width, height, _x_adv, _y_adv = cr.text_extents(text)
          # width, height = width + x_bearing, height + y_bearing
  
      if font:
          cr.restore()
      return width, height
  
  
  def text_center(cr, x, y, text):
      text_align(cr, x, y, text, align_x=0, align_y=0)
  
  
  def text_align(cr, x, y, text, align_x=0, align_y=0, padding_x=0, padding_y=0):
      """Draw text relative to (x, y).
  
      x, y - coordinates
      text - text to print (utf8)
      align_x - -1 (top), 0 (middle), 1 (bottom)
      align_y - -1 (left), 0 (center), 1 (right)
      padding_x - padding (extra offset), always > 0
      padding_y - padding (extra offset), always > 0
      """
      if not text:
          return
  
      x_bear, y_bear, w, h, _x_adv, _y_adv = cr.text_extents(text)
      if align_x == 0:
          x = 0.5 - (w / 2 + x_bear) + x
      elif align_x < 0:
          x = -(w + x_bear) + x - padding_x
      else:
          x = x + padding_x
      if align_y == 0:
          y = 0.5 - (h / 2 + y_bear) + y
      elif align_y < 0:
          y = -(h + y_bear) + y - padding_y
      else:
          y = -y_bear + y + padding_y
      cr.move_to(x, y)
      cr.show_text(text)
  
  
  def text_multiline(cr, x, y, text):
      """Draw a string of text with embedded newlines.
  
      cr - cairo context
      x - leftmost x
      y - topmost y
      text - text to draw
      """
      if not text:
          return
      for line in text.split("\n"):
          _x_bear, _y_bear, _w, h, _x_adv, _y_adv = cr.text_extents(text)
          y += h
          cr.move_to(x, y)
          cr.show_text(line)
  
  
  def text_underline(cr, x, y, text, offset=1.5):
      """Draw text with underline."""
      x_bear, y_bear, w, h, x_adv, y_adv = cr.text_extents(text)
      cr.move_to(x, y - y_bear)
      cr.show_text(text)
      cr.move_to(x, y - y_bear + offset)
      cr.set_line_width(1.0)
      cr.rel_line_to(x_adv, 0)
      cr.stroke()
  
  
  def text_set_font(cr, font):
      """Set the font from a string.
  
      E.g. 'sans 10' or 'sans italic bold 12' only restriction is that the
      font name should be the first option and the font size as last
      argument
      """
      font = font.split()
      cr.select_font_face(
          font[0],
          "italic" in font and cairo.FONT_SLANT_ITALIC or cairo.FONT_SLANT_NORMAL,
          "bold" in font and cairo.FONT_WEIGHT_BOLD or cairo.FONT_WEIGHT_NORMAL,
      )
      cr.set_font_size(float(font[-1]))
  
  
  def path_ellipse(cr, x, y, width, height, angle=0):
      """Draw an ellipse.
  
      x      - center x
      y      - center y
      width  - width of ellipse  (in x direction when angle=0)
      height - height of ellipse (in y direction when angle=0)
      angle  - angle in radians to rotate, clockwise
      """
      cr.save()
      cr.translate(x, y)
      cr.rotate(angle)
      cr.scale(width / 2.0, height / 2.0)
      cr.move_to(1.0, 0.0)
      cr.arc(0.0, 0.0, 1.0, 0.0, 2.0 * pi)
      cr.restore()
  