You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
7.2 KiB

4 years ago
  1. """
  2. This is an implementation of wcwidth() and wcswidth().
  3. Defined in IEEE Std 1002.1-2001.
  4. https://github.com/jquast/wcwidth
  5. from Markus Kuhn's C code at:
  6. http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
  7. This is an implementation of wcwidth() and wcswidth() (defined in
  8. IEEE Std 1002.1-2001) for Unicode.
  9. http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
  10. http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
  11. In fixed-width output devices, Latin characters all occupy a single
  12. "cell" position of equal width, whereas ideographic CJK characters
  13. occupy two such cells. Interoperability between terminal-line
  14. applications and (teletype-style) character terminals using the
  15. UTF-8 encoding requires agreement on which character should advance
  16. the cursor by how many cell positions. No established formal
  17. standards exist at present on which Unicode character shall occupy
  18. how many cell positions on character terminals. These routines are
  19. a first attempt of defining such behavior based on simple rules
  20. applied to data provided by the Unicode Consortium.
  21. For some graphical characters, the Unicode standard explicitly
  22. defines a character-cell width via the definition of the East Asian
  23. FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
  24. In all these cases, there is no ambiguity about which width a
  25. terminal shall use. For characters in the East Asian Ambiguous (A)
  26. class, the width choice depends purely on a preference of backward
  27. compatibility with either historic CJK or Western practice.
  28. Choosing single-width for these characters is easy to justify as
  29. the appropriate long-term solution, as the CJK practice of
  30. displaying these characters as double-width comes from historic
  31. implementation simplicity (8-bit encoded characters were displayed
  32. single-width and 16-bit ones double-width, even for Greek,
  33. Cyrillic, etc.) and not any typographic considerations.
  34. Much less clear is the choice of width for the Not East Asian
  35. (Neutral) class. Existing practice does not dictate a width for any
  36. of these characters. It would nevertheless make sense
  37. typographically to allocate two character cells to characters such
  38. as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
  39. represented adequately with a single-width glyph. The following
  40. routines at present merely assign a single-cell width to all
  41. neutral characters, in the interest of simplicity. This is not
  42. entirely satisfactory and should be reconsidered before
  43. establishing a formal standard in this area. At the moment, the
  44. decision which Not East Asian (Neutral) characters should be
  45. represented by double-width glyphs cannot yet be answered by
  46. applying a simple rule from the Unicode database content. Setting
  47. up a proper standard for the behavior of UTF-8 character terminals
  48. will require a careful analysis not only of each Unicode character,
  49. but also of each presentation form, something the author of these
  50. routines has avoided to do so far.
  51. http://www.unicode.org/unicode/reports/tr11/
  52. Markus Kuhn -- 2007-05-26 (Unicode 5.0)
  53. Permission to use, copy, modify, and distribute this software
  54. for any purpose and without fee is hereby granted. The author
  55. disclaims all warranties with regard to this software.
  56. Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
  57. """
  58. from __future__ import division
  59. from .table_wide import WIDE_EASTASIAN
  60. from .table_zero import ZERO_WIDTH
  61. def _bisearch(ucs, table):
  62. """
  63. Auxiliary function for binary search in interval table.
  64. :arg int ucs: Ordinal value of unicode character.
  65. :arg list table: List of starting and ending ranges of ordinal values,
  66. in form of ``[(start, end), ...]``.
  67. :rtype: int
  68. :returns: 1 if ordinal value ucs is found within lookup table, else 0.
  69. """
  70. lbound = 0
  71. ubound = len(table) - 1
  72. if ucs < table[0][0] or ucs > table[ubound][1]:
  73. return 0
  74. while ubound >= lbound:
  75. mid = (lbound + ubound) // 2
  76. if ucs > table[mid][1]:
  77. lbound = mid + 1
  78. elif ucs < table[mid][0]:
  79. ubound = mid - 1
  80. else:
  81. return 1
  82. return 0
  83. def wcwidth(wc):
  84. r"""
  85. Given one unicode character, return its printable length on a terminal.
  86. The wcwidth() function returns 0 if the wc argument has no printable effect
  87. on a terminal (such as NUL '\0'), -1 if wc is not printable, or has an
  88. indeterminate effect on the terminal, such as a control character.
  89. Otherwise, the number of column positions the character occupies on a
  90. graphic terminal (1 or 2) is returned.
  91. The following have a column width of -1:
  92. - C0 control characters (U+001 through U+01F).
  93. - C1 control characters and DEL (U+07F through U+0A0).
  94. The following have a column width of 0:
  95. - Non-spacing and enclosing combining characters (general
  96. category code Mn or Me in the Unicode database).
  97. - NULL (U+0000, 0).
  98. - COMBINING GRAPHEME JOINER (U+034F).
  99. - ZERO WIDTH SPACE (U+200B) through
  100. RIGHT-TO-LEFT MARK (U+200F).
  101. - LINE SEPERATOR (U+2028) and
  102. PARAGRAPH SEPERATOR (U+2029).
  103. - LEFT-TO-RIGHT EMBEDDING (U+202A) through
  104. RIGHT-TO-LEFT OVERRIDE (U+202E).
  105. - WORD JOINER (U+2060) through
  106. INVISIBLE SEPARATOR (U+2063).
  107. The following have a column width of 1:
  108. - SOFT HYPHEN (U+00AD) has a column width of 1.
  109. - All remaining characters (including all printable
  110. ISO 8859-1 and WGL4 characters, Unicode control characters,
  111. etc.) have a column width of 1.
  112. The following have a column width of 2:
  113. - Spacing characters in the East Asian Wide (W) or East Asian
  114. Full-width (F) category as defined in Unicode Technical
  115. Report #11 have a column width of 2.
  116. """
  117. # pylint: disable=C0103
  118. # Invalid argument name "wc"
  119. ucs = ord(wc)
  120. # NOTE: created by hand, there isn't anything identifiable other than
  121. # general Cf category code to identify these, and some characters in Cf
  122. # category code are of non-zero width.
  123. # pylint: disable=too-many-boolean-expressions
  124. # Too many boolean expressions in if statement (7/5)
  125. if (ucs == 0 or
  126. ucs == 0x034F or
  127. 0x200B <= ucs <= 0x200F or
  128. ucs == 0x2028 or
  129. ucs == 0x2029 or
  130. 0x202A <= ucs <= 0x202E or
  131. 0x2060 <= ucs <= 0x2063):
  132. return 0
  133. # C0/C1 control characters
  134. if ucs < 32 or 0x07F <= ucs < 0x0A0:
  135. return -1
  136. # combining characters with zero width
  137. if _bisearch(ucs, ZERO_WIDTH):
  138. return 0
  139. return 1 + _bisearch(ucs, WIDE_EASTASIAN)
  140. def wcswidth(pwcs, n=None):
  141. """
  142. Given a unicode string, return its printable length on a terminal.
  143. Return the width, in cells, necessary to display the first ``n``
  144. characters of the unicode string ``pwcs``. When ``n`` is None (default),
  145. return the length of the entire string.
  146. Returns ``-1`` if a non-printable character is encountered.
  147. """
  148. # pylint: disable=C0103
  149. # Invalid argument name "n"
  150. end = len(pwcs) if n is None else n
  151. idx = slice(0, end)
  152. width = 0
  153. for char in pwcs[idx]:
  154. wcw = wcwidth(char)
  155. if wcw < 0:
  156. return -1
  157. else:
  158. width += wcw
  159. return width