fogleman / gg

Go Graphics - 2D rendering in Go with a simple API.
https://godoc.org/github.com/fogleman/gg
MIT License
4.44k stars 358 forks source link

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

Open odeke-em opened 8 years ago

odeke-em commented 8 years ago

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.

fogleman commented 8 years ago

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.

odeke-em commented 8 years ago

@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.

antzucaro commented 7 years ago

@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].

haiitch commented 7 years ago

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

antzucaro commented 7 years ago

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

haiitch commented 7 years ago

@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

antzucaro commented 7 years ago

@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.

haiitch commented 7 years ago

@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 commented 7 years ago

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.

fogleman commented 7 years ago

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

fogleman commented 7 years ago

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.

haiitch commented 7 years ago

@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

antzucaro commented 7 years ago

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.

haiitch commented 7 years ago

@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!

nigeltao commented 7 years ago

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

Does that fix your issue?

fogleman commented 7 years ago

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

out

antzucaro commented 7 years ago

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

fogleman commented 7 years ago

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

freetype: unsupported TrueType feature: kern nTables: 3
haiitch commented 7 years ago

@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=

fogleman commented 7 years ago

@htrob That error occurs during font loading.

haiitch commented 7 years ago

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.

nigeltao commented 7 years ago

Xolonium-Regular.ttf now works for me.

antzucaro commented 7 years ago

@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.

fogleman commented 7 years ago

@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?

nigeltao commented 7 years ago

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 commented 7 years ago

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.

MichaelMonashev commented 6 years ago

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

hpoul commented 4 years ago

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? 🤔️

devnoname120 commented 2 years ago

@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")
hpoul commented 2 years ago

@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..

devnoname120 commented 2 years ago

@hpoul Thanks I ended up downloading all the emoji images from https://emojis.wiki/apple/ instead of generating them by myself.