How to output emoji to console in Node.js (on Windows)?
Asked Answered
T

4

20

On Windows, there's some basic emoji support in the console, so that I can get a monochrome glyph if I type, e.g. or 📜. I can output a string from PowerShell or a C# console application or Python and they all show those characters fine enough.

However, from Node.js, I can only get a couple of emoji to display (e.g. ), but not other (instead of 📜 I see ). However, if I throw a string with those characters, they display correctly.

console.log(' 📜 ☕ ');
throw ' 📜 ☕ ';

If I run the above script, the output is

 � ☕

C:\Code\emojitest\emojitest.js:2
throw ' 📜 ☕ '; 
^
 📜 ☕

Is there anyway that I can output those emojis correctly without throwing an error? Or is that exception happening outside of what's available to me through the standard Node.js APIs?

Tincal answered 18/5, 2017 at 18:5 Comment(9)
Hmm, you got a lot further than anybody else would. Finding a font that supports the glyphs is invariably the major obstacle. But � is an encoding problem, not entirely unusual since the scroll glyph is in the upper bit planes, the coffee cup is not. You probably should focus on why it is okay when it is rendered to stderr, like it will in a throw, but not to stdout.Dominoes
@HansPassant writing directly to stderr doesn't do anything different than writing directly to stdout, probably need to look into how Node.js is handling throwing…Tincal
Seems like a windows issue. It works fine on my mac.Zabrina
Now I feel a bit ridiculous. I just tried this example in Node.js 7.10.0 and it worked fine (I had been using the latest 6.x version), so it appears that this will no longer be an issue with stable Node.js once 8.x is released.Tincal
@bdukes: Maybe a daft question, but are you sure you were performing exactly the same test in exactly the same environment in exactly the same way? If so then my answer must be wrong, because the libuv behaviour I referred to hasn’t been changed between 6.x and 7.x. FWIW I get the same results with 6.x and 7.x.Gerontology
@BrianNixon, yes, good questions, but all I'm doing is switching node versions (from 7.10.0 to 6.10.3, via nvm) and running the same script: v7.10.0 🎵 I Have the Honour to Be Your Obedient Servant,🎵 ~ npm 📜🖋 ☕ ----------------- v6.10.3 � I Have the Honour to Be Your Obedient Servant,� ~ npm �� ☕Tincal
@BrianNixon, I did need to make sure that the file was saved with the right encoding, though, so that might explain your testsTincal
@bdukes: Well, I’m stumped. Maybe worth another question to see if somebody else can explain what’s going on…Gerontology
Which font did you use to display even the monochrome version of the coffee mug? On what console? Plain Windows, cmder, conemu...?Overvalue
G
17

What you want may not be possible without a change to libuv. When you (or the console) write to stdout or stderr on Windows and the stream is a TTY, libuv does its own conversion from UTF‑8 to UTF‑16. In doing so it explicitly refuses to output surrogate pairs, emitting instead the replacement character U+FFFD � for any codepoint beyond the BMP.

Here’s the culprit in uv/src/win/tty.c:

  /* We wouldn't mind emitting utf-16 surrogate pairs. Too bad, the */
  /* windows console doesn't really support UTF-16, so just emit the */
  /* replacement character. */
  if (utf8_codepoint > 0xffff) {
    utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
  }

The throw message appears correctly because Node lets Windows do the conversion from UTF‑8 to UTF‑16 with MultiByteToWideChar() (which does emit surrogate pairs) before writing the message to the console. (See PrintErrorString() in src/node.cc.)

Note: A pull request has been submitted to resolve this issue.

Gerontology answered 25/5, 2017 at 10:45 Comment(4)
Sounds good. The missing detail in the question is that the OP probably uses Win10. Lots of tinkering done on the console in that version, largely to expand support for the new Linux sub-system.Dominoes
So, a modified libuv (that would accept codepoints beyond BMP and use MultiByteToWideChar) would work on Windows 10? -- Sounds like a valid feature request :)Niersteiner
@Hugues Moreau: I assume that modifying libuv to output UTF‑16 surrogate pairs would allow display of non-BMP characters in Node streams in a Windows console that supports them. It’s not necessary to use MultiByteToWideChar(), particularly; deleting the line of code shown here then adding an else to the following block to emit a surrogate pair for the code point should be sufficient. But I haven’t got a build environment for Node.js set up (or the time or inclination today to create one!) to try it.Gerontology
@Hugues Moreau: Yes, it certainly seems worth taking up with the libuv developers to see if it’s a modification they’d consider. I don’t know the library well enough to judge how easy it would be to introduce a version check or configuration flag, or how much compatibility such a change might break, or even the rationale for it being implemented the way it is in the first place.Gerontology
M
3

There is an easy way to log Emojis in the console:

console.log("\u{1F9E1} Do what you love or love what you do!");

console.log("\u{1F3AF}");

Emoji code in Hex format reference: https://www.w3schools.com/charsets/ref_emoji.asp

Matt answered 8/6, 2023 at 3:25 Comment(0)
N
2

(Disclaimer: I don't have a solution, I explored what makes exception handling special with regards to printing emoji, with the tools I have on Windows 10 -- With some luck that might sched some light on the issue, and perhaps someone will recognize something and come up with a solution)

Looks like Node's exception reporting code for Windows calls to a different Windows API, that happens to support Unicode better.

Let's see with Node 7.10 sources:

ReportExceptionAppendExceptionLinePrintErrorString

In PrintErrorString, the Windows-specific section detects output type (tty/console or not): - For non-tty/console context it will print to stderr (e.g. if you redirect to a file) - In a cmd console (with no redirection), it will convert text with MultiByteToWideChar() and then pass that to WriteConsoleW().

If I run your program using ConEmu (easier than getting standard cmd to work with unicode & emoji -- yes I got a bit lazy here), I see something similar as what you saw: console.log fails to print emoji, but the emoji in exception message are printed OK (even the scroll glyph).

If I redirect all output to a file (node test.js > out.txt 2>&1, yes that works in Windows cmd too), I get "clean" Unicode in both cases.

So it seems when a program prints to stdout or stderr in a Windows console, the console does some (bad) reencoding work before printing. When the program uses Windows console API directly (doing the conversion itself with MultiByteToWideChar then write to console with WriteConsoleW()), the console shows the glorious unaltered emoji.

When a JS program uses console API to log stuff, maybe Node could try (on Windows) to detect console and do the same thing as it does for reporting exceptions. See @BrianNixon's answer that explains what is actually happening in libuv.

Niersteiner answered 25/5, 2017 at 10:51 Comment(0)
D
1

The next "Windows Terminal" (from Kayla Cinnamon) and the Microsoft/Terminal project should be able to display emojis.

This will be available starting June 2019. Through the use of the Consolas font, partial Unicode support will be provided.

The request is in progress in Microsoft/Terminal issue 387.
And Microsoft/Terminal issue 190 formally demands "Add emoji support to Windows Console".

But there are still issues (March 2019):

I updated my Win10 from 1803 to 1809 several days ago, and now all characters >= U+10000 (UTF-8 with 4 bytes or more) no longer display.
I have also tried the newest insider version(Windows 10 Insider Preview 18358.1 (19h1_release)), unfortunately, this bug still exists.

Dogbane answered 6/5, 2019 at 20:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.