13 ms·
When monospace fonts aren't: The Unicode character width nightmare
- Kenji 11y agoThat reminds me of how I wanted to start a project with a name containing the German 'ö' character. Of course, as customary, I save my projects into folders with the name of the project. And, also of course, GCC issues instantly ensued when I tried to compile the C++ source of that project. What did I learn? Stay with ASCII for your code and names. It might be 2015 but apparently it's more important to put poop into unicode than to actually implement the thing in all the important software.
- gchpaco 11y agoDon't idly dismiss emoji. They're the first thing from outside the Basic Multilingual Plane that people actually want to use, and as a result the first thing to find all the bugs and hidden assumptions going from a 16 bit wide character set to a 20 bit wide character set. This helps people who actually needed access to the various CJK Ideograph Extensions, most of which are in Plane 2.
- gsnedders 11y agoNit-pick: 21-bit wide character set.
- jhallenworld 11y agoThanks to UTF-16, ugh.
- gchpaco 11y agoTruth, I dunno why I have 20 bits stuck in my head.
- Dylan16807 11y agoProbably because it barely goes over 20 bits. 20.09ish If you want to be cheeky you can argue that "hidden assumptions going from a 16 bit wide character set to a 20 bit wide character set" is still correct, because those hidden assumptions are part of UTF-16, and UTF-16 uses exactly 20 bits to express the astral planes, encoding them in a different way from the BMP.
- jepler 11y agoHave you tried this lately? On a Debian Jessie machine using the en_US.UTF-8 locale, I encountered no problems doing this, though I picked œ instead of ö. I did have a little bit of pain from git (git status showed \305\223 for œ) until I did "git config core.quotepath false". Not sure how this'll come through on ycombinator, so it's also on a pastebin for 24 hours: https://paste.debian.net/311370/ https://paste.debian.net/311370/ ~$ cd src ~/src$ mkdir œuvre ~/src$ cd œuvre ~/src/œuvre$ printf '#include <stdio.h>\nint main() {puts("hello œuvre");}\n' > œuvre.c ~/src/œuvre$ g++ œuvre.c -o œuvre ~/src/œuvre$ ./œuvre hello œuvre
- simoncion 11y agoI can also confirm success when compiling a file with a path "ömlaut/asdf.cpp". $ g++ --version | head -n 1 g++ (Gentoo Hardened 4.9.3 p1.1, pie-0.6.2) 4.9.3 $ uname -r -m 4.1.6-hardened i686 $ echo $LANG en_US.UTF-8 $ bash --version | head -n 1 GNU bash, version 4.3.42(1)-release (i686-pc-linux-gnu)
- eridius 11y agoThat's a bit different. Presumably you're running this in a terminal emulator. Terminal emulators (and programmers editors), if they're set to a monospace font, operate on a grid. For characters in the selected monospace font this is just like normal font rendering, but when fallback fonts are used, it still uses the layout from the original monospace font (i.e. the grid) instead of the layout from the fallback font. This means that the fallback font rendering doesn't screw with the columns. Naturally there's still the issue of properly identifying double-wide characters. But as long as you can correctly identify them (and for the ambiguous characters it generally treats them all the same, either as narrow or as wide depending on the software in question), you can simply render them with 2 cells instead of 1, and everything remains lined up. But the article here was showing monospaced text in a web browser, and a web browser doesn't do any of this (nor a rich text editor). Font fallback usually attempts to maintain properties like monospacing, but the fallback font may still have different metrics, meaning it won't line up with the monospaced text from the source font (and depending on the fonts available, if you have no monospaced font with asian glyphs it would have to fall back to a non-monospaced font). I suspect what's going on with the locale stuff here is simply that the locale affects the fallback list, and it ends up picking a different font that happens to have the right metrics to line up. But I can't say for certain that this is the explanation.
- GFK_of_xmaspast 11y ago" it's more important to put poop into unicode than to actually implement the thing in all the important software." I somehow suspect those aren't the same people.
- geofft 11y agoWere you using a UTF-8 locale? (Try running `locale`.)
- fred256 11y agoRelated, it always annoyed me in programming books when program listings using monospaced fonts used an "fi" ligature crammed into one character width.
- toothbrush 11y agoWow, that is an atrocity i have luckily never stumbled into—do you have examples?
- kylebgorman 11y agoDon't quote me on this but I think there may have been an example of this in the most recent Stroustrup C++ book?
- gpvos 11y agoHaving a "fi" or "ff" or similar typographic ligature at all in any monospace font is a grave mistake. (Typographic as opposed to ligatures that have been promoted to letters in certain languages, such as "ß").
- waqf 11y agoIn most languages there's not really much need to display source code in monospace, except to the extent that a previous programmer formatted code with that assumption. When you're trying to align lines of a block, you only need that a string of n initial spaces (or tabs) is the same width every time: you don't care if it is the same as a string of n arbitrary characters. This suffices for the compiler-enforced indentation rule in Python (though not, I think, for the indentation rule in Haskell). (I code in a variable-width font in Emacs and I work on shared codebases. The codebase style does have a couple alignment rules that don't make sense for variable-width fonts, but I just let Emacs enforce them and I'm happy that overall the code as I see it is easier on the eyes than it would be with fixed-width formatting.)
- gchpaco 11y agoAcme, too, defaults to variable width, and looks rather better for it. It's always a little awkward in Emacs because a lot of the system packaging (package.el, for example) assumes mono, but it can work.
- scythe 11y agoHow do you deal with the fact that in most variable-width fonts, the characters []{}():;'!.*,|`ijl are often too small/narrow to really notice? I've looked into programming with variable-width fonts, but I kept finding that monospace fonts are much easier on the eyes. It's hard to even look at typical C code in a variable-width font.
- waqf 11y agoI don't have a perfect solution, but using syntax highlighting together with a bold font weight goes a long way to make typos apparent.
- douche 11y agoThere are a few simple rules I have for code fonts: 1.) Monospace - I don't really go nuts about aligning things, but it is nice for looking at multi-line strings in code. 2.) Characters need to be distinct. l (lower-case ell), i (lower-case eye), I (upper-case eye) and 1 (one) cannot appear identical. 0, o and O shouldn't be the same - bonus if zero has a dot or line through the center. Visual Studio defaults to Consolas, which is nice.
- rspeer 11y agoA project I work on, "ftfy", deals with various Unicode issues. On the master branch, it's recently gained a module for aligning monospaced Unicode in a terminal: https://github.com/LuminosoInsight/python-ftfy/blob/master/ftfy/formatting.py https://github.com/LuminosoInsight/python-ftfy/blob/master/f... The result works pretty well in my gnome-terminal and Mac OS Terminal. You can probably see that GitHub in your web browser doesn't even come close to lining them up, though. And the problem can't be completely solved, because the standards have gaps, and there are some scripts where monospacing just isn't a thing.
- glandium 11y agoRelatedly, on GNU/Linux, depending on your locale, fontconfig configuration and characters in the strings displayed (no, I'm not making that up), your monospace font might end up not being a monospace font at all. Look at this nice ghex screenshot: http://i.imgur.com/z0Dp60H.png http://i.imgur.com/z0Dp60H.png
- breadbox 11y agoThis stuff is a nightmare if you're trying to write a nice-looking terminal application. AFAICT there is no reliable way to determine how many cells an arbitrary Unicode glyph will occupy when output to a terminal. None. You can use various wcwidth() functions as a first approximation, but you have to give up on things working in the general case, because there's no guarantee (in theory or in practice) that the terminal's font will actually honor the width defined by the standards. Hopefully this situation will slowly improve with the years, but given the level of neglect the terminal environment gets from standardization processes these days, I'm not entirely optimistic.
- mark-r 11y agoYou could always place each glyph individually, but that's likely to have subtle bugs too, while not being very performant.
- breadbox 11y agoI have used such a solution at times. But then you usually have the problem of overwriting half of a wide character, which is worse.
- jquast 11y agoThere is a method, using the report cursor position query to determine the current location of the cursor, you can then print question characters and re-read the loctation, the difference determining how many cells a character forwards the carriage.
- jhallenworld 11y agoI recently changed how JOE dealt with this. Originally it used Markus Kuhn's wcwidth function (http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c), but I've changed it to use the data in EastAsianWidth.txt: http://sourceforge.net/p/joe-editor/mercurial/ci/default/tree/joe/unicode.c#l510 http://sourceforge.net/p/joe-editor/mercurial/ci/default/tre... JOE uses 4-level radix trees for character classes. These work well because the leaf nodes are highly redundant and can be merged together. The resulting structure is often smaller than a binary tree. Character classes are also used for regular expressions, so there is code to build them on the fly from a list of ranges (it's tricky to do this efficiently). Anyway, I'm surprised that emoji are not double-wide characters. JOE is still missing Unicode normalization for string searches.
- kevin_thibedeau 11y agoThere is no way to standardize this. Just because legacy encodings provided for single cell and double width characters doesn't mean that all monospace fonts should have to conform to that scheme to support Asian characters. It is up to the font designer to decide how much advance to use and some fonts are designed with Latin characters the same width as the Chinese.
- arm 11y agoMany Chinese characters are completely unreadable at small sizes if you try to fit them in the width of a halfwidth character. It’s just not practical.
- masklinn 11y agoThe monospace font designer could integrate CJK logographs as double the standard width (that's essentially what CJK fonts do in reverse with halfwidth latin characters). CJK is monospace to start with though, I'd think cursive & highly ligatured scripts like arabic or brahmic scripts would be a bigger issue when designing monospace fonts (if you want something which doesn't look like utter shite) (though I guess Kufic — the very blocky arabic script found on the flags of iraq and iran — might be better source material than the more modern and cursive Naskh variations, it's commonly manipulated into tilings and brickwords already: https://en.wikipedia.org/wiki/Kufic#/media/File:Alijlas_kufi.png https://en.wikipedia.org/wiki/Kufic#/media/File:Alijlas_kufi...)
- mpweiher 11y agoSeems to work fine in OS X Terminal.
- gcb0 11y agogotta love how things catch up faster than you can complain about. the rendered text for me on firefox (actually, not even proper firefox, but iceweasel, on debian stable, which is almost a year behind firefox) align perfectly fine. but the picture labeled "firefox" is all out of whack.
- nsajko 11y agoIsn't the best general answer to assume mono is mono and put the font configuration responsibility on the user?
- douche 11y agoI was originally a history major, who had a hell of a professor that taught the history of pre-modern imperial China, so I ended up focusing in that area. One thing that baffled me was that characters never really fell out of usage in favor of a syllabary or alphabet the way similar ideographic systems have tended to elsewhere. Other countries in the Chinese cultural sphere, that originally imported Chinese script, developed alpha/syllabic replacements, like the Korean hangul and Japan's two kana systems. And it is not as though China was not exposed to alphabets - Buddhism, Tibet, and the various Turkic, Mongol and Manchurian tribes that bordered on (and not infrequently ruled over significant portions of China), all used variations and descendants of Semetic alphabets. It seemed like such a huge inefficiency and barrier to wide-spread literacy, to have to memorize at least a thousand or more characters, compared to 26-odd letters, 10 digits, and the 50 or so more common phonetic spelling rules, that could get you to an equivalent level of literacy in a Latin-based language. Once you've learned all the characters, and digested all the Confucian classics that make up the shared context of classic Chinese, you can be wonderfully succinct and expressive, but the learning curve to get there can be measured in decades, judging by accounts of prospective scholar-officials studying for the civil service examinations. It's perhaps the difference between becoming a proficient Vim or Emacs user, and popping open Notepad. To get back to Unicode, most of the hairiness could have been avoided if Chinese (and related regional variations) used a syllabic or alphabetic script - 100-200 characters vs ~80000 would make 2-byte wide chars sufficient to express nearly every non-dead script, with plenty of space for any number of poo or banana emoticons (if such a thing is really necessary, about which I have my doubts).
- microcolonel 11y agoThis seems more like a problem with GDI and/or DirectWrite(as well as how each browser is making use of them), less to do with chrome vs. firefox vs. IE vs. VS. Chrome on Chrome OS (using FreeType 2) properly aligns the text in that <pre>, as does firefox on GNU/Linux (also using FreeType 2, in addition to graphite). On FreeType with Chrome or Graphite, the full-width latin characters are also rendered with the correct weight and face. Something that Chrome and IE on Windows seem to get wrong in your screenshots.
- jquast 11y agoIf anybody here has serious expertise on this subject, please consider reviewing my python implementation of Markus Kuhn's wcwidth, updated for the latest Unicode Specification (programmatically by "python setup.py update"). https://github.com/jquast/wcwidth https://github.com/jquast/wcwidth