Find the width of a character

Let me start with an example to illustrate what I mean by "width":

let string1 = "abc"
let string2 = "甲乙丙"

Although both string1 and string2 contain the same number of characters, string2 takes 2x as much space as string1 does when printed in terminal, or anywhere on screen in monospaced fonts. So, each character in string2 has 2x the width of each in string1.

This becomes a problem if I want to generate a table with all cells well-aligned, because simply using each string's count property won't be enough:

   | abc |
---|-----|---
   | 甲乙丙 |

I considered assigning a width of 2 to a scalar if its Unicode.Scalar.Properties.isUnifiedIdeograph is true. However, a character is not always a scalar (because of combining characters), and there are also scalars like U+00A0 that has zero width.

Is there a way to find the width of a character in Swift?

“Monospace” becomes meaningless once you venture outside ASCII. Different fonts and terminals won’t agree with each other about how wide any particular string is.

When trying to hack together a table like that, it’s not just natural width you are fighting against, but plenty of other aspects like ligation, stacking and reordering (to name a few):

3 Likes

If you are satisfied with it working with a specific terminal emulator, you can look at their source code, and copy-paste their guesswork. That way your behavior will match. For example iterm2 has a giant list of characters.

3 Likes

As others said, monospace fonts don't do much between the texts with different East Asian Width. Most seem to use different "width" between narrow characters (e.g. basic latin) and half-width characters, so it'd misalign if you mix full-width and narrow characters:

12345678901234567890| // 20 chars
123456789012| // 12 chars and a bit

And most terminals don't use duospace fonts (below is Unifont Regular):

Screen Shot 2563-11-11 at 22.23.01

4 Likes

You don't really mention your use case. NSAttributedString().size tells you the dimensions of the string and that's valid on iOS and macOS on the screen in a view. That doesn't work for Terminal output though.

Yeah this is a really hard problem.

This blog post by the maintainer of HarfBuzz gives a good overview of what the various pieces are (e.g. FreeType, HarfBuzz, Pango) and what they do. It's a little bit old but still worth reading if you're curious about this topic. HarfBuzz is like the FOSS alternative of Apple’s CoreText, and is used by projects such as Android, Chromium, Gnome, and the PlayStation 4 UI.

It's kind of sad, because text is pretty fundamental for people to be able to work with computers, but it's so complex that you really do need a massive stack of libraries to do it correctly. The HarfBuzz documentation does a good job of explaining why things like "text shaping" are necessary and why they are so difficult.

2 Likes