docs: def html5_parse_legacy_color(input):
      """
      Apply the legacy color parsing algorithm from section 2.4.6 of
      HTML5.
  
      """
      # 1. Let input be the string being parsed.
      if not isinstance(input, unicode):
          raise ValueError(
              u"HTML5 legacy color parsing requires a Unicode string as input."
          )
  
      # 2. If input is the empty string, then return an error.
      if input == "":
          raise ValueError(
              u"HTML5 legacy color parsing forbids empty string as a value."
          )
  
      # 3. Strip leading and trailing whitespace from input.
      input = input.strip()
  
      # 4. If input is an ASCII case-insensitive match for the string
      #    "transparent", then return an error.
      if input.lower() == u"transparent":
          raise ValueError(
              u'HTML5 legacy color parsing forbids "transparent" as a value.'
          )
  
      # 5. If input is an ASCII case-insensitive match for one of the
      #    keywords listed in the SVG color keywords section of the CSS3
      #    Color specification, then return the simple color
      #    corresponding to that keyword.
      keyword_hex = CSS3_NAMES_TO_HEX.get(input.lower())
      if keyword_hex is not None:
          return html5_parse_simple_color(keyword_hex)
  
      # 6. If input is four characters long, and the first character in
      #    input is a "#" (U+0023) character, and the last three
      #    characters of input are all ASCII hex digits, then run these
      #    substeps:
      if len(input) == 4 and \
         input.startswith(u'#') and \
         all(c in string.hexdigits for c in input[1:]):
          # 1. Let result be a simple color.
          #
          # 2. Interpret the second character of input as a hexadecimal
          #    digit; let the red component of result be the resulting
          #    number multiplied by 17.
          #
          # 3. Interpret the third character of input as a hexadecimal
          #    digit; let the green component of result be the resulting
          #    number multiplied by 17.
          #
          # 4. Interpret the fourth character of input as a hexadecimal
          #    digit; let the blue component of result be the resulting
          #    number multiplied by 17.
          result = HTML5SimpleColor(
              int(input[1], 16) * 17,
              int(input[2], 16) * 17,
              int(input[3], 16) * 17
          )
  
          # 5. Return result.
          return result
  
      # 7. Replace any characters in input that have a Unicode code
      #    point greater than U+FFFF (i.e. any characters that are not
      #    in the basic multilingual plane) with the two-character
      #    string "00".
  
      # This one's a bit weird due to the existence of multiple internal
      # Unicode string representations in different versions and builds
      # of Python.
      #
      # From Python 2.2 through 3.2, Python could be compiled with
      # "narrow" or "wide" Unicode strings (see PEP 261). Narrow builds
      # handled Unicode strings with two-byte characters and surrogate
      # pairs for non-BMP code points. Wide builds handled Unicode
      # strings with four-byte characters and no surrogates. This means
      # ord() is only sufficient to identify a non-BMP character on a
      # wide build.
      #
      # Starting with Python 3.3, the internal string representation
      # (see PEP 393) is now dynamic, and Python chooses an encoding --
      # either latin-1, UCS-2 or UCS-4 -- wide enough to handle the
      # highest code point in the string.
      #
      # The code below bypasses all of that for a consistently effective
      # method: encode the string to little-endian UTF-32, then perform
      # a binary unpack of it as four-byte integers. Those integers will
      # be the Unicode code points, and from there filtering out non-BMP
      # code points is easy.
      encoded_input = input.encode('utf_32_le')
  
      # Format string is '<' (for little-endian byte order), then a
      # sequence of 'L' characters (for 4-byte unsigned long integer)
      # equal to the length of the original string, which is also
      # one-fourth the encoded length.  For example, for a six-character
      # input the generated format string will be '<LLLLLL'.
      format_string = '<' + ('L' * (int(len(encoded_input) / 4)))
      codepoints = struct.unpack(format_string, encoded_input)
      input = ''.join(u'00' if c > 0xffff
                      else unichr(c)
                      for c in codepoints)
  
      # 8. If input is longer than 128 characters, truncate input,
      #    leaving only the first 128 characters.
      if len(input) > 128:
          input = input[:128]
  
      # 9. If the first character in input is a "#" (U+0023) character,
      #    remove it.
      if input.startswith(u'#'):
          input = input[1:]
  
      # 10. Replace any character in input that is not an ASCII hex
      #     digit with the character "0" (U+0030).
      if any(c for c in input if c not in string.hexdigits):
          input = ''.join(c if c in string.hexdigits else u'0' for c in input)
  
      # 11. While input's length is zero or not a multiple of three,
      #     append a "0" (U+0030) character to input.
      while (len(input) == 0) or (len(input) % 3 != 0):
          input += u'0'
  
      # 12. Split input into three strings of equal length, to obtain
      #     three components. Let length be the length of those
      #     components (one third the length of input).
      length = int(len(input) / 3)
      red = input[:length]
      green = input[length:length*2]
      blue = input[length*2:]
  
      # 13. If length is greater than 8, then remove the leading
      #     length-8 characters in each component, and let length be 8.
      if length > 8:
          red, green, blue = (red[length-8:],
                              green[length-8:],
                              blue[length-8:])
          length = 8
  
      # 14. While length is greater than two and the first character in
      #     each component is a "0" (U+0030) character, remove that
      #     character and reduce length by one.
      while (length > 2) and (red[0] == u'0' and
                              green[0] == u'0' and
                              blue[0] == u'0'):
          red, green, blue = (red[1:],
                              green[1:],
                              blue[1:])
          length -= 1
  
      # 15. If length is still greater than two, truncate each
      #     component, leaving only the first two characters in each.
      if length > 2:
          red, green, blue = (red[:2],
                              green[:2],
                              blue[:2])
  
      # 16. Let result be a simple color.
      #
      # 17. Interpret the first component as a hexadecimal number; let
      #     the red component of result be the resulting number.
      #
      # 18. Interpret the second component as a hexadecimal number; let
      #     the green component of result be the resulting number.
      #
      # 19. Interpret the third component as a hexadecimal number; let
      #     the blue component of result be the resulting number.
      #
      # 20. Return result.
      return HTML5SimpleColor(
          int(red, 16),
          int(green, 16),
          int(blue, 16)
      )