[Question] How to use the latest FillString() and AddFont()?
sgon00 opened this issue ยท 15 comments
Hi, I am new to plot. I used some old version in the past and it's working very well.
Today, when I try to upgrade to the latest version (using go module), There are two problems in the code. I think it might because the API methods have been upgraded.
Some of my code:
require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
gonum.org/v1/plot v0.9.0
)
import (
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/vgimg"
)
c := vgimg.New(diagonal, diagonal)
fs, _ := vg.MakeFont(fontName, vg.Inch*vg.Length(fontInchSize))
c.FillString(fs, vg.Point{X: 0, Y: offset}, markText)
The error shows: cannot use fs (variable of type vg.Font) as font.Face value in argument to c.FillString
.
font, err := truetype.Parse(ttfBytes)
vg.AddFont(fontName, font)
The error shows: cannot use font (variable of type *truetype.Font) as *sfnt.Font value in argument to vg.AddFont
I don't know how to use the latest API.
How to convert fs
to font.Face? I tried fs.FontFace(96)
, but it didn't work.
How to add truetype.Font to vg.AddFont
? How to convert truetype.Font to sfnt.Font?
Can anyone plese help?
Thank you very much.
To get the font face, use fnt := hdlr.Fonts.Lookup(sty.Font, sty.Font.Size)
where hdlr
is a text.Handler
and sty
is your text style.
Rather than using truetype.Parse(ttfBytes)
, try sfnt.Parse(ttfBytes)
@kortschak Thank you very much for your quick reply. The sfnt.Parse(ttfBytes)
solution is clear. But I still don't know how to get font face. I am new to font coding. I tried to google some example code online, but failed to find any. Can you please show me a complete example how to get font face if that is not too much? OR show me some complete example code URLs? Thank you very much.
PS: I tried something stupid:
face, _ := opentype.NewFace(font, nil)
c.FillString(face, vg.Point{X: 0, Y: offset}, markText)
It has error message: cannot use face (variable of type font.Face) as font.Face value in argument to c.FillString
I am very confused. I can not use font.Face as font.Face?
Yeah, no worries. The API is new, I think we may need to have some more examples for how to use it effectively.
This is a basic use.
fnt := plot.DefaultTextHandler.Cache.Lookup(plot.DefaultFont, size)
If you look in the liberation.go file, you can see the fonts that are provided in the repo and also how the collection is constructed.
That's a pretty funny error. There are two font.Face
types in your program, opentype.NewFace
returns this and FillString
wants this. Unfortunately, there are only so many names, and sometime we have to share.
BTW It's easier to help if you provide full code. You can paste the code at https://play.golang.org.
Here's an example of using a 3rd-party font:
https://pkg.go.dev/gonum.org/v1/plot@v0.9.0/vg#example-package-AddFont
I am very sorry that I still can not make 0.9.0 work.
Some of my current code:
fontName := "ABC"
ttfBytes, err := ioutil.ReadFile("/System/Library/Fonts/Menlo.ttc")
font, _ := sfnt.Parse(ttfBytes)
vg.AddFont(fontName, font)
fs, _ := vg.MakeFont(fontName, vg.Inch*vg.Length(fontInchSize))
width := fs.Width(markText)
...
...
fnt := plot.DefaultTextHandler.Cache().Lookup(plot.DefaultFont, fontInchSize)
c.FillString(fnt, vg.Point{X: 0, Y: offset}, markText)
fs.Width
line will give me invalid memory address or nil pointer dereference
error.
It worked before when I use the old plot version.
If I change the fontName
to the built-in font name such as "Courier"
, the error will be gone. But I want to use the system font instead of FontMap list.
In the old version, I simply use c.FillString(fs, ...)
.
But in the new version, If I use fnt := plot.DefaultTextHandler.Cache().Lookup(plot.DefaultFont, fontInchSize)
and then c.FillString(fnt, ...)
, How can I use the custom ttc font?
I have read the example link provided by @sbinet. But I am still very lost.
I have the variable fs
created by vg.MakeFont
. Why can't I use it somehow in c.FillString
like before?
Thank you very much for your help.
as always in Go, please check the returned error values (that's why you get a nil
pointer as fs
.)
the new font system was put in place to allow for a much finer control of fonts (and to use golang.org/x/image/font/sfnt
instead of freetype
)
as hinted in the example, the way to add a new font is to populate the "font cache":
mincho := font.Font{Typeface: "Mincho"}
font.DefaultCache.Add([]font.Face{
{
Font: mincho,
Face: fontTTF,
},
})
if !font.DefaultCache.Has(mincho) {
log.Fatalf("no font %q!", mincho.Typeface)
}
ie: one adds a plot/font.Face
value to the default font cache, which can then be used by the whole set of gonum/plot
packages.
one can use that plot/font.Face
value with the plot/vg.Canvas.FillString
method, as before:
face := font.Face{
Font: mincho,
Face: fontTTF,
}
c.FillString(face, vg.Point{5,5}, "some text")
Thank you both very much for the help.
Finally, I made it work.
Sorry that I made one mistake above that I should NOT use ttc
font collection file, instead, I should use ttf
file. I should check the error in the line sfnt.Parse
which will produce a reasonable error message instead of a nil pointer error. This is my bad.
Btw, the new font system is way more complicated than before.
In the past, I just need to add the font once in vg.AddFont()
. That's all. I can then use the font in all methods such as vg.MakeFont()
and vgimg.Canvas.FillString()
,
But in the new version, I have to add the fonts twice. One by vg.AddFont()
so that I can call vg.MakeFont()
. Another by creating a font.Face, so that I can use vgimg.Canvas.FillString()
.
Anyway, thank you very much again and closing this question.
I believe that, as the example shown above might indicate, one doesn't need to use both vg.AddFont
and the "font cache" to add a new 3rd-party font to the gonum/plot font system.
could you please open another issue if that's not the case?
thanks.
Note to self: probably consider a migration path and delete vg.AddFont
, vg.MakeFont
and vg.Font
altogether (which were IIRC kept only for embedding fonts in PDF documents while there was no way to get back at the raw []byte
s)
@sbinet Thanks a lot for your reply. I was not saying that the font needs to add to "font cache" too. I mean the ttfBytes need to pass to both vg.AddFont
and font.Face
object. Maybe I am concerning too much. I just feel weird that I need to pass the same ttfBytes to two different places.
I used some of the codes from this gist: https://gist.github.com/linw1995/41e103f29e7dd97679c577a6f4830be8
Cheers.
and what I am saying is that no call to vg.AddFont
(nor vg.MakeFont
) should be needed.
so the raw []byte
representing the TTF font needs only to be passed once to the font cache.
one can then retrieve a font face (with the expected size) via plot/font.Cache.Lookup as Dan wrote earlier.
earlier on, I wrote:
face := font.Face{
Font: mincho,
Face: fontTTF,
}
c.FillString(face, vg.Point{5,5}, "some text")
but this could as well be written like:
face := font.DefaultCache.Lookup(font.Font{Typeface: "Mincho"}, 42)
c.FillString(face, vg.Point{5,5}, "some text")
@sbinet I posted the gist code that I studied from. (I am not using the same code, but they are similar). From the gist code, you can see that it needs to calculate the following:
fontStyle, _ := vg.MakeFont("Courier", vg.Inch*0.7)
markTextWidth := fontStyle.Width(markText)
unitText := markText
for markTextWidth <= diagonal {
markText += " " + unitText
markTextWidth = fontStyle.Width(markText)
}
.......
lineHeight := fontStyle.Extents().Height * 1
I don't know how to achieve the same if I don't call vg.AddFont
and vg.MakeFont
.
Thanks a lot.
The 'font.Face' type has the methods you are looking for: