docs: def draw(title, subtitle, author, cover_width=400, cover_height=600):
      """
      Main drawing function, which generates a cover of the given dimension and
      renders title, author, and graphics.
      """
  
      # Based on some initial constants and the title+author strings, generate a base
      # background color and a shape color to draw onto the background. Try to keep
      # these two colors somewhat compatible with each other by varying only their hue.
      def processColors():
          base_saturation = 100
          base_brightness = 90
          color_distance = 100
          invert = True
  
          counts = len(title) + len(author)
          color_seed = int(_map(_clip(counts, 2, 80), 2, 80, 10, 360))
          shape_color = Image.colorHSB(color_seed, base_saturation, base_brightness-(counts % 20))
          base_color = Image.colorHSB((
              color_seed + color_distance) % 360,
              base_saturation,
              base_brightness
          )
          if invert:
              shape_color, base_color = base_color, shape_color
          if (counts % 10) == 0:
              shape_color, base_color = base_color, shape_color
          return shape_color, base_color
  
  
      # Fill the background of the image with white.
      def drawBackground():
          fill = Image.colorRGB(255, 255, 255)
          cover_image.rect(0, 0, cover_width, cover_height, fill)
  
  
      # Draw the actual artwork for the cover. Given the length of the title string,
      # generate an appropriate sized grid and draw C64 PETSCII into each of the cells.
      # https://www.c64-wiki.com/index.php/PETSCII
      # https://en.wikipedia.org/wiki/PETSCII#/media/File:PET_Keyboard.svg
      def drawArtwork():
          artwork_start_x = 0
          artwork_start_y = cover_height - cover_width
  
          grid_count, grid_total, grid_size = breakGrid()
          cover_image.rect(0, 0, cover_width, cover_height * cover_margin / 100, base_color)
          cover_image.rect(0, 0 + artwork_start_y, cover_width, cover_width, base_color)
          c64_title = c64Convert()
          for c, i in zip(itertools.cycle(c64_title), range(0, grid_total)):
              grid_x = int(i % grid_count)
              grid_y = int(i / grid_count)
              x = grid_x * grid_size + artwork_start_x
              y = grid_y * grid_size + artwork_start_y
              drawShape(c, x, y, grid_size)
  
  
      # Compute the graphics grid size based on the length of the book title.
      def breakGrid():
          min_title = 2
          max_title = 60
          length = _clip(len(title), min_title, max_title)
  
          grid_count = int(_map(length, min_title, max_title, 2, 11))
          grid_total = grid_count * grid_count
          grid_size = cover_width / grid_count
          return grid_count, grid_total, grid_size
  
  
      # Given the title of the book, filter through its characters and ensure
      # that only a certain range is used for the title; characters outside of
      # that range are replaced with a somewhat random character.
      def c64Convert():
          c64_letters = " qQwWeErRtTyYuUiIoOpPaAsSdDfFgGhHjJkKlL:zZxXcCvVbBnNmM,;?<>@[]1234567890.=-+*/"
          c64_title = ""
          for c in title:
              if c in c64_letters:
                  c64_title += c
              else:
                  c64_title += c64_letters[ord(c) % len(c64_letters)]
          return c64_title
  
  
      # Given an alphabetic character from the book's title string and the x, y
      # coordinates and size of the cell within the cover grid, draw a PETSCII
      # shape into that cell.
      def drawShape(c, x, y, s):
          shape_thickness = 10
          thick = int(s * shape_thickness / 100)
          if c in "qQ":
              cover_image.ellipse(x, y, s, s, shape_color)
          elif c in "wW":
              cover_image.ellipse(x, y, s, s, shape_color)
              cover_image.ellipse(x+thick, y+thick, s-(thick*2), s-(thick*2), base_color)
          elif c in "eE":
              cover_image.rect(x, y+thick, s, thick, shape_color)
          elif c in "rR":
              cover_image.rect(x, y+s-(thick*2), s, thick, shape_color)
          elif c in "tT":
              cover_image.rect(x+thick, y, thick, s, shape_color)
          elif c in "yY":
              cover_image.rect(x+s-(thick*2), y, thick, s, shape_color)
          elif c in "uU":
              cover_image.arc(x, y, 2*s, 2*s, 180, 270, shape_color, thick)
          elif c in "iI":
              cover_image.arc(x-s, y, 2*s, 2*s, 270, 360, shape_color, thick)
          elif c in "oO":
              cover_image.rect(x, y, s, thick, shape_color)
              cover_image.rect(x, y, thick, s, shape_color)
          elif c in "pP":
              cover_image.rect(x, y, s, thick, shape_color)
              cover_image.rect(x+s-thick, y, thick, s, shape_color)
          elif c in "aA":
              cover_image.triangle(x, y+s, x+(s/2), y, x+s, y+s, shape_color)
          elif c in "sS":
              cover_image.triangle(x, y, x+(s/2), y+s, x+s, y, shape_color)
          elif c in "dD":
              cover_image.rect(x, y+(thick*2), s, thick, shape_color)
          elif c in "fF":
              cover_image.rect(x, y+s-(thick*3), s, thick, shape_color)
          elif c in "gG":
              cover_image.rect(x+(thick*2), y, thick, s, shape_color)
          elif c in "hH":
              cover_image.rect(x+s-(thick*3), y, thick, s, shape_color)
          elif c in "jJ":
              cover_image.arc(x, y-s, 2*s, 2*s, 90, 180, shape_color, thick)
          elif c in "kK":
              cover_image.arc(x-s, y-s, 2*s, 2*s, 0, 90, shape_color, thick)
          elif c in "lL":
              cover_image.rect(x, y, thick, s, shape_color)
              cover_image.rect(x, y+s-thick, s, thick, shape_color)
          elif c == ":":
              cover_image.rect(x+s-thick, y, thick, s, shape_color)
              cover_image.rect(x, y+s-thick, s, thick, shape_color)
          elif c in "zZ":
              cover_image.triangle(x, y+(s/2), x+(s/2), y, x+s, y+(s/2), shape_color)
              cover_image.triangle(x, y+(s/2), x+(s/2), y+s, x+s, y+(s/2), shape_color)
          elif c in "xX":
              cover_image.ellipse(x+(s/2), y+(s/3), thick*2, thick*2, shape_color)
              cover_image.ellipse(x+(s/3), y+s-(s/3), thick*2, thick*2, shape_color)
              cover_image.ellipse(x+s-(s/3), y+s-(s/3), thick*2, thick*2, shape_color)
          elif c in "cC":
              cover_image.rect(x, y+(thick*3), s, thick, shape_color)
          elif c in "vV":
              cover_image.rect(x, y, s, s, shape_color)
              cover_image.triangle(x+thick, y, x+(s/2), y+(s/2)-thick, x+s-thick, y, base_color)
              cover_image.triangle(x, y+thick, x+(s/2)-thick, y+(s/2), x, y+s-thick, base_color)
              cover_image.triangle(x+thick, y+s, x+(s/2), y+(s/2)+thick, x+s-thick, y+s, base_color)
              cover_image.triangle(x+s, y+thick, x+s, y+s-thick, x+(s/2)+thick, y+(s/2), base_color)
          elif c in "bB":
              cover_image.rect(x+(thick*3), y, thick, s, shape_color)
          elif c in "nN":
              cover_image.rect(x, y, s, s, shape_color)
              cover_image.triangle(x, y, x+s-thick, y, x, y+s-thick, base_color)
              cover_image.triangle(x+thick, y+s, x+s, y+s, x+s, y+thick, base_color)
          elif c in "mM":
              cover_image.rect(x, y, s, s, shape_color)
              cover_image.triangle(x+thick, y, x+s, y, x+s, y+s-thick, base_color)
              cover_image.triangle(x, y+thick, x, y+s, x+s-thick, y + s, base_color)
          elif c == ",":
              cover_image.rect(x+(s/2), y+(s/2), s/2, s/2, shape_color)
          elif c == ";":
              cover_image.rect(x, y+(s/2), s/2, s/2, shape_color)
          elif c == "?":
              cover_image.rect(x, y, s/2, s/2, shape_color)
              cover_image.rect(x+(s/2), y+(s/2), s/2, s/2, shape_color)
          elif c == "<":
              cover_image.rect(x+(s/2), y, s/2, s/2, shape_color)
          elif c == ">":
              cover_image.rect(x, y, s/2, s/2, shape_color)
          elif c == "@":
              cover_image.rect(x, y+(s/2)-(thick/2), s, thick, shape_color)
          elif c == "[":
              cover_image.rect(x+(s/2)-(thick/2), y, thick, s, shape_color)
          elif c == "]":
              cover_image.rect(x, y+(s/2)-(thick/2), s, thick, shape_color)
              cover_image.rect(x+(s/2)-(thick/2), y, thick, s, shape_color)
          elif c == "0":
              cover_image.rect(x+(s/2)-(thick/2), y+(s/2)-(thick/2), thick, s/2+thick/2, shape_color)
              cover_image.rect(x+(s/2)-(thick/2), y+(s/2)-(thick/2), s/2+thick/2, thick, shape_color)
          elif c == "1":
              cover_image.rect(x, y+(s/2)-(thick/2), s, thick, shape_color)
              cover_image.rect(x+(s/2)-(thick/2), y, thick, s/2+thick/2, shape_color)
          elif c == "2":
              cover_image.rect(x, y+(s/2)-(thick/2), s, thick, shape_color)
              cover_image.rect(x+(s/2)-(thick/2), y+(s/2)-(thick/2), thick, s/2+thick/2, shape_color)
          elif c == "3":
              cover_image.rect(x, y+(s/2)-(thick/2), s/2+thick/2, thick, shape_color)
              cover_image.rect(x+(s/2)-(thick/2), y, thick, s, shape_color)
          elif c == "4":
              cover_image.rect(x, y, thick*2, s, shape_color)
          elif c == "5":
              cover_image.rect(x, y, thick*3, s, shape_color)
          elif c == "6":
              cover_image.rect(x+s-(thick*3), y, thick*3, s, shape_color)
          elif c == "7":
              cover_image.rect(x, y, s, thick*2, shape_color)
          elif c == "8":
              cover_image.rect(x, y, s, thick*3, shape_color)
          elif c == "9":
              cover_image.rect(x, y+s-(thick*3), s, thick*3, shape_color)
          elif c == ".":
              cover_image.rect(x+(s/2)-(thick/2), y+(s/2)-(thick/2), thick, s/2+thick/2, shape_color)
              cover_image.rect(x, y+(s/2)-(thick/2), s/2+thick/2, thick, shape_color)
          elif c == "=":
              cover_image.rect(x+(s/2)-(thick/2), y, thick, s/2+thick/2, shape_color)
              cover_image.rect(x, y+(s/2)-(thick/2), s/2, thick, shape_color)
          elif c == "-":
              cover_image.rect(x+(s/2)-(thick/2), y, thick, s/2+thick/2, shape_color)
              cover_image.rect(x+(s/2)-(thick/2), y+(s/2)-(thick/2), s/2+thick/2, thick, shape_color)
          elif c == "+":
              cover_image.rect(x+(s/2)-(thick/2), y+(s/2)-(thick/2), s/2+thick/2, thick, shape_color)
              cover_image.rect(x+(s/2)-(thick/2), y, thick, s, shape_color)
          elif c == "*":
              cover_image.rect(x+s-(thick*2), y, thick*2, s, shape_color)
          elif c == "/":
              cover_image.rect(x, y+s-(thick*2), s, thick*2, shape_color)
          elif c == " ":
              cover_image.rect(x, y, s, s, base_color)
          else:
              assert not "Implement."
  
      # If the text is long, use a smaller font size.
      def scale_font(text, font_name, font_properties):
          (font_size, font_slant, font_weight) = font_properties
          w = len(text) * font_size
          if w > cover_width * 3:   #This is an empirical, unintelligent, heuristic.
              return  (font_size * 0.8, font_slant, font_weight)
          elif w < cover_width :
              return  (font_size * 1.2, font_slant, font_weight)
          else:
              return font_properties
  
      # return a font appropriate for the text. Uses Noto CJK if text contains CJK, otherwise
      # Noto Sans.
      
      def select_font(text):
          for char in text:
              if ord(char) >= 0x4E00:
                  return 'Noto Sans CJK SC'
          return 'Noto Sans'
  
      # Allocate fonts for the title and the author, and draw the text.
      
      def drawText():
          fill = Image.colorRGB(50, 50, 50)
  
          title_font_size = cover_width * 0.08
          subtitle_font_size = cover_width * 0.05
          title_font_properties = (title_font_size, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
          subtitle_font_properties = (subtitle_font_size, cairo.FONT_SLANT_NORMAL,
                                       cairo.FONT_WEIGHT_NORMAL)
          title_font_family = select_font(title)
          subtitle_font_family = select_font(subtitle)
          title_font_properties = scale_font(title, title_font_family, title_font_properties)
          subtitle_font_properties = scale_font(
              subtitle,
              subtitle_font_family,
              subtitle_font_properties
          )
          title_font = cover_image.font(title_font_family, title_font_properties)
          subtitle_font = cover_image.font(subtitle_font_family, subtitle_font_properties)
          title_height = (cover_height - cover_width - (cover_height * cover_margin / 100)) * 0.75
  
          x = cover_height * cover_margin / 100
          y = cover_height * cover_margin / 100 * 2
          width = cover_width - (2 * cover_height * cover_margin / 100)
          height = title_height
          title_lines, font_height = cover_image.text(title, x, y, width, height, fill, title_font)
          if subtitle:
              y = min(
                  y + font_height * title_lines * cover_height,
                  title_height - subtitle_font_properties[0]
              )
  
              cover_image.text(subtitle, x, y, width, height, fill, subtitle_font)
  
          author_font_size = cover_width * 0.07
          author_font_properties = (
              author_font_size,
              cairo.FONT_SLANT_NORMAL,
              cairo.FONT_WEIGHT_NORMAL
          )
          author_font = cover_image.font(select_font(author), author_font_properties)
          author_height = (cover_height - cover_width - (cover_height * cover_margin / 100)) * 0.25
          x = cover_height * cover_margin / 100
          y = title_height
          width = cover_width - (2 * cover_height * cover_margin / 100)
          height = author_height
          cover_image.text(author, x, y, width, height, fill, author_font)
  
      # Create the new cover image.
      cover_margin = 2
      cover_image = Image(cover_width, cover_height)
  
      # Draw the book cover.
      shape_color, base_color = processColors()
      drawBackground()
      drawArtwork()
      drawText()
  
      # Return the cover Image instance.
      return cover_image