0

I'm having a hard time getting unicode to properly displayed in GUI.

I'm trying to type poker cards unicode inside emacs GUI, for instance. I would like to use font "Ubuntu Mono",

Ubuntu

NOT Symbola.

Symbola

Here's what's really weird:

  1. Symbola works fine in GUI, as long as I have it installed.
  2. If I remove Symbola from my system, GUI will only show ugly hex-code block, which is glyphless fallback.
  3. TUI emacs is fine and shows the non-Symbola version. (Needless to say can be properly displayed in terminal vim as well) Also, gedit and gvim works fine also. LibreOffice works fine as well.

To sum up, on my setting, I'm only experiencing problem with GUI emacs, which is really weird, since plenty of fonts installed on Ubuntu by default can display .

I'm wondering if there's some problem with my system setting or my build of emacs. Also, this link doesn't work for me. I'm getting display: no font available on my GUI.

             position: 1 of 1384 (0%), column: 0
            character:  (displayed as ) (codepoint 127145, #o370251, #x1f0a9)
              charset: unicode (Unicode (ISO10646))
code point in charset: 0x1F0A9
               script: playing-cards
               syntax: w    which means: word
             category: .:Base
             to input: type "C-x 8 RET 1f0a9" or "C-x 8 RET PLAYING CARD NINE OF SPADES"
          buffer code: #xF0 #x9F #x82 #xA9
            file code: #xF0 #x9F #x82 #xA9 (encoded by coding system utf-8-unix)
              display: no font available

Character code properties: customize what to show
  name: PLAYING CARD NINE OF SPADES
  general-category: So (Symbol, Other)
  decomposition: (127145) ('')

There is an overlay here:
 From 1 to 3
  face                 hl-line
  priority             -50
  window               #<window 3 on test.el>


There are text properties here:
  fontified            t

[back]

Tested environment:

Ubuntu 16.04 / 18.04
emacs 27.1
Spacemacs develop / emacs -Q

Edit:

A few details in the comment below.

When Symbola is installed, the following can be observed.

;; C-x C-e turns  into ugly hex-code
(set-fontset-font nil ? "Ubuntu Mono")

;; C-x C-e turns  into what's shown in the Symbola image
(set-fontset-font nil ? "Symbola")

;; This line actually works, but I have no idea why
(set-fontset-font nil 'playing-cards (font-spec :script 'playing-cards))
TerryTsao
  • 1,176
  • 4
  • 18

1 Answers1

5

Sorry, I'll rewrite my answer entirely from the beginning.

Sometimes, a script is a clue to find the need font for Emacs. You can check which script a codepoint belongs to by follwing:

(aref char-script-table ?)
playing-cards

By specifying the script in font-spec, you may find the fonts, but it often fails because the script that is not contained in script-representative-chars is not reliable, and font does not necessarily contain the glyph for specific codepoints.

(list-font (font-spec :script 'playing-cards))
(find-font (font-spec :script 'playing-cards))

font-spec function returns <font-spec> objects that is just an query of the fonts, and list-font and find-font function return <font-entity> objects that are abstract pointers to the installed fonts. Both types can be specified to set-frame-font and set-face-font functions, while only <font-spec> can be specified to set-fontset-font function. Emacs will not generate actual glyph images for fonts unless open-font function is called to <font-entity> object.

More assured way to find the font that contains the glyph for specific codepoint is just try to get the glyph from the candidate font.

(let ((font (open-font (find-font (font-spec :family "Ubuntu Mono")))))
  (font-get-glyphs font 0 1 [?]))
[nil] ; This font does not contain glyph for ''.

(let ((font (open-font (find-font (font-spec :family "DejaVu Sans")))))
  (font-get-glyphs font 0 1 [?]))
[[0 0 127145 5795 2 0 2 2 1 nil]]  ; This font does contain glyph for ''.

By cl-looping all installed font families (that can be obtained by (list-fonts (font-spec :weight 'normal))) for this test, I found that in my Linux system, there are only follwing three fonts that contain glyph for ''.

Noto Sans Symbols2 = [[0 0 127145 1646 1 0 2 2 0 nil]]
LastResort = [[0 0 127145 143 1 0 2 1 1 nil]]
DejaVu Sans = [[0 0 127145 5795 2 0 2 2 1 nil]]

Also, you can try cl-looping the above test from the codepoint #x0020 to #x3ffff to find the coverage of codepoints for specific font, such as "Ubuntu One", that is found to cover the following codepoint ranges.

00020-0007e
000a1-0024f
002bc-002c7
....
0f507-0f511
0f80b-0f81d

Therefore, it seems that in my Linux & Emacs system, "Ubuntu One" does not cotain the glyph for ''. It may be a fault of my Emacs font engine, or that my "Ubuntu One" font is different from yours...

Now you can use set-fontset-font to current fontset to specify the fonts for specific codepoints.

(set-fontset-font nil (#x1f0a0 . #x1f0ff) "Noto Sans Symbols2")
(set-fontset-font nil 'playing-cards "Noto Sans Symbols2")

or for default fallback fontset,

(set-fontset-font t 'playing-cards "Noto Sans Symbols2")

Note: there are (in some way) three layers of font API in Emacs.

  • Upper :: set-face-font, set-frame-font
  • Middle :: new-fontset, set-fontset-font, query-fontset
  • Lower :: find-font, open-font, font-get-glyphs

If you need to deal with single font, upper API can handle it. If you need to handle multiple fonts with codepoints in mind, middle API is needed. To access font directly, you may need to use lower layer API.

But lower API sometimes behaves differently by the operating systems, window systems or font engines.

If you access upper API, the fontset is automatically adjusted, and is named something like fontset-autoXX, which you can get by (face-attribute 'default :fontset).

dualism
  • 304
  • 1
  • 6
  • Thx for your reply. However, this doesn't work for me. I've tested with `(set-fontset-font nil ? "Ubuntu Mono")` . However, if I have Symbola installed, `(set-fontset-font nil ? "Symbola")` does work. In fact, when I C-xC-e on the two expressions, the ugly hex-code block would be turned on/off. Also, could you please explain more about `script-representative-chars` ? I couldn't find the script name `playing-cards` in its value. Could this be related? – TerryTsao Oct 23 '20 at 06:21
  • 1
    Update: After a few trial-and-errors, I find this expression actually works: `(set-fontset-font nil 'playing-cards (font-spec :script 'playing-cards))` . Obviously I have no idea why it works. Any chance you could explain a bit more? Thx. (If you could kindly update your answer a bit, I'll accept it.) – TerryTsao Oct 23 '20 at 07:14
  • At some point, please incorporate any relevant info into the question and answer. Comments can be deleted at any time. Q & A needs to stand on its own - it's what's searchable here and googlable, so it's what's most helpful to others. Thx. – Drew Oct 23 '20 at 16:54
  • Will do, thx. @Drew – TerryTsao Oct 24 '20 at 11:10
  • Very well explained. Thx a lot! – TerryTsao Oct 24 '20 at 12:35
  • Turns out fonts supported on my system is even one less than yours (no LastResort). I guess Ubuntu "Font Manager" gives me the Ubuntu Mono image in the OP for some other reasons. – TerryTsao Oct 24 '20 at 12:47
  • `(set-fontset-font nil (#x1f0a0 . #x1f0ff) "Noto Sans Symbols2")` should be: `(set-fontset-font nil '(#x1f0a0 . #x1f0ff) "Noto Sans Symbols2")` or the range tuple will be treated as a fn call – Iain Oct 16 '21 at 07:06