gg icon indicating copy to clipboard operation
gg copied to clipboard

emoji text not drawn. Is there an option for supporting emojis?

Open odeke-em opened this issue 8 years ago • 31 comments

When I modify a snippet text.go in examples/ ie

package main

import "github.com/fogleman/gg"

func main() {
    const S = 1024
    dc := gg.NewContext(S, S)
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    dc.SetRGB(0, 0, 0)
    if err := dc.LoadFontFace("/Library/Fonts/Arial.ttf", 96); err != nil {
        panic(err)
    }
    dc.DrawStringAnchored("😎  🔥  🙏🏾 ", S/2, S/2, 0.5, 0.5)
    dc.SavePNG("out.png")
}

I get an image out and the emojis don't appear. I wanted to ask if we have emoji support and if you might have any clues for how we could go around this. Thank you.

odeke-em avatar Apr 19 '16 22:04 odeke-em

I tried using this font: /System/Library/Fonts/Apple Color Emoji.ttf but then I get this error:

panic: freetype: unsupported TrueType feature: cmap encoding

I think this would involve a lot of work.

fogleman avatar Apr 19 '16 23:04 fogleman

@fogleman thanks for taking a look at this and for the hint of change of font. I'll hopefully dive into this issue too soon.

odeke-em avatar Apr 21 '16 07:04 odeke-em

@odeke-em and @fogleman, this does not appear to be font-specific. I'm able to reproduce using a number of open source fonts like Ubuntu Font Family or Xolonium, which is what I happen to be using for a project.

As a test I use unicode code point U+1F603 "Smiling Face With Open Mouth". Its []byte representation is [240 159 152 131].

antzucaro avatar Apr 12 '17 13:04 antzucaro

Could you please confirm whether this is relevant behaviour in golang/freetype? https://github.com/golang/freetype/blob/255de57f4e681ed92b2d55a483a27194dfa2b3dc/truetype/truetype.go#L118

haiitch avatar Apr 12 '17 20:04 haiitch

@htrob Modifying this freetype code with my font (Xolonium) still yields the same (blank) output.

antzucaro avatar Apr 13 '17 14:04 antzucaro

@antzucaro Ok, so it looks like it's really a golang/freetype problem. We should check what the freetype devs say in github.com/golang/freetype

haiitch avatar Apr 13 '17 23:04 haiitch

@htrob and @fogleman - looks like freetype won't implement this anytime soon according to this issue. The go-cairo library supports these emojis, so I suppose I'll have to change my renderer to that. That's a shame since I like gg's API better and it is pure Go.

antzucaro avatar Apr 14 '17 13:04 antzucaro

@antzucaro So go-cairo uses a different/own truetype renderer? I like gg a lot better too for the same reasons you do. I was also checking nanovgo, which supports truetype glyph rendering, trying to understand how it works.

nanovgo is primarily intended for live graphics on screen, which may be more useful and a flatter API for my particular application. nanovgo uses a reduced version of Fontstash, which is a truetype atlas renderer. It caches bitmap images of pre-rendered font types for live display on screen. https://github.com/memononen/fontstash

If a reduced version of a simpler library like fontstash handles these fonts correctly, it must not be extremely complicated code, it may be possible to grab or translate the relevant parts from Fontstash and fix the Go Freetype port.
I'd personally prefer to attempt that before trying to introduce a massive dependency like Cairo as a desperate last resort measure. In the meantime I'm going to see if I can abstract 2d drawing in such a way that I can use gg or alternatively nanovgo as a drop in replacement. 2d drawing apis inspired by HTML5 canvas are not wildly different, doing the same with Cairo would probably be a harder task as well.

haiitch avatar Apr 14 '17 17:04 haiitch

UPDATE: Fontstash actually uses another C-based truetype rendering library called stb, and in particular its subset stb_truetype.h https://github.com/nothings/stb

Faced with the option of a massive and complex C dependency like Cairo or a tiny C dependency like stb_truetype just for rendering some glyphs on screen, I'll probably go with the latter until Go Freetype can be fixed, and will try to abstract 2d drawing until a definitive All-Go solution can be devised.

I haven't looked yet, but maybe stb_truetype is simple enough that I can port it more or less literally at some point.

haiitch avatar Apr 14 '17 17:04 haiitch

Thanks for looking into this everyone. Maybe we can figure something out if we keep digging.

fogleman avatar Apr 14 '17 19:04 fogleman

The Apple Color Emoji font has a cmap encoding that isn't listed here:

https://github.com/golang/freetype/blob/master/truetype/truetype.go#L60

It has a value of 0x00000004, which indicates:

Unicode 2.0 and onwards semantics, Unicode full repertoire (cmap subtable formats 0, 4, 6, 10, 12).

Here are some relevant pages:

https://www.microsoft.com/typography/otspec/name.htm

https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html

I don't know much about TTF, so this is all I know so far.

fogleman avatar Apr 14 '17 19:04 fogleman

@antzucaro Xolonium works just fine with nanovgo (truetype rendered by Fontstash, using stb_truetype.h), which I may be using for my GUI toolkit, in addition to gg. Not native Go, but stb_truetype is a tiny dependency. Maybe the font renderer can be made pluggable in fogleman/gg until a complete Go solution is found? https://s30.postimg.org/cfkmwjhgh/Screenshot_at_2017-04-14_20_36_57.png

haiitch avatar Apr 14 '17 23:04 haiitch

Thanks for checking, @htrob! I will have to do a more thorough comparison with Cairo, which is easily installed on my system. If it could be plugged into gg, however, that would be a huge win.

antzucaro avatar Apr 15 '17 00:04 antzucaro

@antzucaro You're welcome. I looked a little more into this, and turns out that nanovgo's truetype rendering is not done with the original Fontstash/stb_truetype, but a reduced version written in Go dubbed fontstash_mini (the almost identical project name was misleading). I didn't look too deep, but it seems to be all native Go, and it renders correctly all fonts I've used so far (as shown in previous screenshot). https://github.com/shibukawa/nanovgo/blob/master/fontstashmini/truetype/truetype.go

This definitely seems like something that could be integrated in fogleman/gg, if it deals with problematic fonts that golang/freetype is never going to be able to handle. I'm actually doing this for myself, trying to make sure I'm not going to be stuck into a dead end if I build something bigger on top of golang/freetype.

I used Antigrain in the past but that was a bit of an overkill for my most basic 2d drawing needs, and fogleman/gg is a really useful tool that in my opinion strikes the right balance of output quality, API size, and ease of use, but it's always good to make sure it's applied to the right kind of task.

Hope this helps. Cheers!

haiitch avatar Apr 15 '17 01:04 haiitch

I just pushed https://github.com/golang/freetype/commit/b12e98108289d6367ecd1a819deee597b2c3fa57

Does that fix your issue?

nigeltao avatar Apr 20 '17 01:04 nigeltao

@nigeltao Thanks! Here I rendered "\U0001F1E6\U0001F1E7\U0001F1E8"

out

fogleman avatar Apr 20 '17 02:04 fogleman

@fogleman @nigeltao - unfortunately it does not work w/ Xolonium modifying this example.

antzucaro avatar Apr 21 '17 16:04 antzucaro

I tried that font and indeed it produces this new error:

freetype: unsupported TrueType feature: kern nTables: 3

fogleman avatar Apr 21 '17 16:04 fogleman

@fogleman, have you been able to identify whether this bug happens during font loading/interpreting or is it something that happens when attempting to render it?

I'm trying to understand what fontstash_mini does differently from golang/freetype. this seems to be the relevant part in fontstash_mini that deals with tables https://github.com/shibukawa/nanovgo/search?utf8=%E2%9C%93&q=tables&type=

This appears to be the relevant part in golang/freetype https://github.com/golang/freetype/search?utf8=%E2%9C%93&q=ntables&type=

haiitch avatar Apr 22 '17 20:04 haiitch

@htrob That error occurs during font loading.

fogleman avatar Apr 22 '17 23:04 fogleman

I think the comparison between two distinct font loading libraries is helpful.

For what I see in the links above, the difference between fontstash_mini and golang/freetype when loading truetype tables is that freetype doesn't want to deal with the specific case of the type of table that contains kerning information. Instead of ignoring this type of table, golang/freetype just halts execution saying that kerning tables aren't supported. fontstash_mini appears to ignore the tables it doesn't know how to peruse, and continues execution without dropping the ball.

Do you guys agree this is a correct diagnostic?

If I'm reading this correctly, all that needs to be done is to make golang/freetype ignore kerning tables (and other tables it doesn't know how to handle) without halting execution. We don't really need golang/freetype to implement advanced kerning features, we only need programmes not to halt arbitrarily. It doesn't make any sense to halt execution if you can proceed with degraded behaviour, unless you're developing a font validation tool, or some other font management tool. In which case, you would benefit from better font and rendering library capability detection anyway, instead of just halting. Or at least a poor error code as an int, but not halting.

The downside of patching golang/freetype to fix this is that it may require vendoring golang/freetype until they incorporate the behaviour change, if they ever do.

haiitch avatar Apr 23 '17 17:04 haiitch

Xolonium-Regular.ttf now works for me.

nigeltao avatar Apr 29 '17 00:04 nigeltao

@nigeltao I pulled in the latest freetype and re-ran the example code cited above, inserting unicode code point U+1F603 "Smiling Face With Open Mouth" into the the "looking glass" text snippet. It still does not render them.

antzucaro avatar Apr 29 '17 14:04 antzucaro

@nigeltao @antzucaro Indeed the font loads and works now, for the most part. Here I rendered the first unicode plane with Xolonium-Regular:

out

All the other planes just render boxes.

Try it yourself with this example code:

https://github.com/fogleman/gg/blob/master/examples/unicode.go

BTW, @nigeltao I tried adding this function to gg:

func (dc *Context) HasRune(r rune) bool {
	_, _, _, _, ok := dc.fontFace.Glyph(fixp(0, 0), r)
	return ok
}

But it seems to always return true even if it just renders a box or renders nothing. Is that expected behavior?

fogleman avatar Apr 29 '17 18:04 fogleman

Yeah, that's more or less expected for now.

https://www.microsoft.com/typography/OTSPEC/cmap.htm says that "Character codes that do not correspond to any glyph in the font should be mapped to glyph index 0. The glyph at this location must be a special glyph representing a missing character, commonly known as .notdef."

It makes sense (to me) when considering a single font face in isolation, but I'd like to reconsider that design decision (Glyph returns ok == true) when we have a good idea of how to set text in multiple font faces, e.g. this run of text uses a Latin font, this run of text uses a Chinese font. But I don't have a good idea for how to do that yet. It probably involves a higher level "Text Layout" data structure, and API to create and manipulate them.

nigeltao avatar May 04 '17 00:05 nigeltao

For your immediate problem, you might be able to check

return dc.font.Index(r) != 0

and note that that's a method on the Font, not the Face. You might need to add a dc.font field.

nigeltao avatar May 04 '17 01:05 nigeltao

I still see boxes instead of Unicode symbols after go run unicode.go . How can I help to fix it?

MichaelMonashev avatar Jan 10 '18 21:01 MichaelMonashev

It seems right now drawing emoji kind of works - I got it to work by simply loading emoji font using freetype:

Screenshot 2020-09-05 at 12 26 58

But any idea how I could get colors to work? I'm obviously clueless about fonts. I could kind of found out that color fonts are it's own thing :-) and supported by sfnt. And there is also a library to parse sfnt which worked as well (x/image/font/sfnt). and with a recent patch https://go-review.googlesource.com/c/image/+/240897/ I was also able to get a font.Face from it.

Unfortunately the font.Face instance still rendered in the default color, instead of the emoji color. Any idea at which place this could go wrong? while parsing the ttf, creating the font.Face from the sfnt.Font when using gg dc.DrawString? 🤔️

hpoul avatar Sep 05 '20 10:09 hpoul

@hpoul Which font did you use? With Xolonium-Regular.ttf I still get an empty box trying to print 😀.

imgWidth := 64
imgHeight := 64

dc := gg.NewContext(imgWidth, imgHeight)
if err := dc.LoadFontFace("Xolonium-Regular.ttf", 64); err != nil {
	panic(err)
}

dc.SetColor(color.Black)
dc.DrawString("😀", 0, 64)

_ = dc.SavePNG("out.png")

devnoname120 avatar Jun 19 '22 10:06 devnoname120

@devnoname120 i've tried noto emoji font and noto color emoji font https://github.com/googlefonts/noto-emoji#noto-emoji .. but i never got colors to work.. So I abandoned it completely..

hpoul avatar Jun 19 '22 14:06 hpoul