Number of columns text occupies.
Counts UTF-8 code points and skips ANSI escape sequences, which take
no space on screen. Continuation bytes, those with the top two bits
10, do not start a character and so do not count. A byte that is
not valid UTF-8 counts as one, so invalid input degrades to the byte
count rather than to something arbitrary.
One column per code point: an East Asian wide character occupies two and is counted once here. Getting that right needs a width table, and is out of scope.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | text |
Text to measure. |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer(kind=default_int), | private | :: | byte | ||||
| integer(kind=default_int), | private | :: | i | ||||
| integer(kind=default_int), | private | :: | skip |
pure function display_width(text) result(width) !! Number of columns `text` occupies. !! !! Counts UTF-8 code points and skips ANSI escape sequences, which take !! no space on screen. Continuation bytes, those with the top two bits !! `10`, do not start a character and so do not count. A byte that is !! not valid UTF-8 counts as one, so invalid input degrades to the byte !! count rather than to something arbitrary. !! !! One column per code point: an East Asian wide character occupies two !! and is counted once here. Getting that right needs a width table, and !! is out of scope. character(len=*), intent(in) :: text !! Text to measure. integer(default_int) :: width integer(default_int) :: i, byte, skip width = 0_default_int i = 1_default_int do while (i <= len(text, kind=default_int)) skip = escape_length(text, i) if (skip > 0_default_int) then i = i + skip cycle end if byte = iachar(text(i:i), default_int) if (byte < 128_default_int .or. byte >= 192_default_int) then width = width + 1_default_int end if i = i + 1_default_int end do end function display_width