[cairo] User font feature
Behdad Esfahbod
behdad at cs.toronto.edu
Sun Apr 9 14:51:19 PDT 2006
On Fri, 7 Apr 2006, Kristian Høgsberg wrote:
> cairo_public cairo_font_face_t *
> cairo_user_font_face_create (cairo_ucs4_to_index_func_t ucs4_to_index,
> cairo_get_glyph_metrics_func_t get_glyph_metrics,
> cairo_render_glyph_func_t render_glyph,
> cairo_get_glyph_path_func_t get_glyph_path);
>
> Using this API the application can create a font face given a set of
> call backs. The font face can be used as any other font face and
> works with cairo_show_text() and cairo_show_glyphs() etc. The idea is
> that when cairo needs metrics or the glyph bitmap it calls out to the
> application provided callbacks. The glyphs bitmaps are cached and
> evicted as needed etc. behind the scenes as for the other cairo font
> face types.
>
> While this API is sufficient to fix the poppler performance problem,
> there's a few other issues to consider:
Is it? I don't think so. It doesn't get any data, so you end up
needing one set of functions per font!
> - PS and PDF Type3 fonts and SVG fonts allow for ARGB glyphs. That
> is, a glyph is either an alpha mask that is used in a mask operation
> with the current source when painting the glyph (as in cairo today) or
> it's a full ARGB image that just gets composited into the destination
> when painting the glyph. I believe it's not too difficult to extend
> the glyph cache to also handle ARGB glyphs, we just need to inspect
> the surface contents for each glyph surface and either mask it or
> composite it as we paint the glyphs.
ARGB glyphs are pretty interesting, for example you can create a
Valentine's font that draw little red hearts instead of dots for
i and j :).
> - Behdad suggests using the text_to_glyphs API instead of
> ucs4_to_index, which makes it possible for the user font
> implementation to substitute combined glyphs if necessary (I think
> that was the motivation, anyway).
If you need anything other than a toy api, you need this, for
kerning, for ligatures, for non-Latin shaping, and a lot more.
> - We probably need a callback when a scaled font is created creation
> to allow the user font implementation to attach per-scaled font data
> and a callback to free that data. Most of the built in font backends
> extend cairo_scaled_font_t with backend specific data, after all.
True. I sat down and wrote my idea of what the API may look
like. The main problem is that since font_face_t and
scaled_font_t are opaque, the user font cannot embed them. So
what I came up with was having the user font return a void* that
the user-font backend will put in the font_face_t/scaled_font_t
and pass to user functions on subsequent calles.
> - The render_glyph callback needs to take a cairo_t instead as
> suggested by Behdad. This way we can render alpha masks for the
> glyphs we do now, but it will also be possible to render a user font
> glyph into an SVG glyph or Type3 glyph. The glyph path isn't
> sufficient for outputting SVG or Type3 fonts as the source user glyphs
> may be bitmap or ARGB glyphs. Passing a cairo_t to the render_glyph
> callback also let's us set the font matrix as the ctm for that cairo_t
> and we can specify that the callback must render the glyph in a unit
> sqare. Not sure this works so well, though, if the application wants
> to do hinting or pixel grid snapping.
I won't say a unit square. You draw like it's a 1pt font, as the
font matrix is really just a scale. I have added a scaled_font
level to your design to deal with hinting too.
> - We still need the get_glyph_path callback for the cairo_text_path()
> case. Alternatively, this could be done by passing a meta surface to
> render_glyph and then extracting the path from that. Of course that
> isn't always possible - again, bitmap fonts and ARGB glyphs.
>
> But anyway, that's a head up on the user-font work. Feedback welcome.
>
> cheers,
> Kristian
Ok, here is my take. It may be a bit over-engineered, but is
feature-complete. It does handle all my needs for user-font in
Pango, and can easily handle yours. Some design decisions and
goals first:
- One of the goals was to not have to expose any cairo structs
that is not already exposed.
- Next, if you have a set of functions to render say SVG <font>
tags, you shouldn't have to pass them to cairo every time you
create such a font. So I added the idea of a user_font_class,
from which you create new font_faces.
- And of course to be able to do hinting and other interesting
stuff, you create an scaled_font from a font_face.
- We may use a struct instead of passing ten methods to a
function, that works too.
/* cu stands for cairo_user here */
cairo_public cairo_user_font_class_t *
cairo_user_font_class_create (cairo_text_to_glyph_func_t,
cu_font_face_create_func_t,
cu_font_face_destroy_func_t,
cu_scaled_font_create_func_t,
cu_scaled_font_destroy_func_t,
cu_scaled_font_extents_func_t,
cu_scaled_font_glyph_extents_func_t,
cu_scaled_font_show_glyph_func_t,
cu_scaled_font_glyph_path_func_t,
cu_font_class_destroy_func_t,
void *class_data);
cairo_public cairo_user_font_class_t *
cairo_user_font_class_reference (cairo_user_font_class_t *);
cairo_public void
cairo_user_font_class_destroy (cairo_user_font_class_t *);
cairo_public cairo_font_face_t *
cairo_user_font_face_create (cairo_user_font_class_t *,
void *font_face_data);
cairo_public cairo_user_font_class_t *
cairo_user_font_face_get_class (cairo_user_font_t *);
/* convenience */
cairo_public cairo_user_font_class_t *
cairo_user_scaled_font_get_class (cairo_user_font_t *);
where:
- cu_font_face_create_func_t:
If this is not NULL, it will be passed the class and the font_data,
and should return a void* that will be attached to the font_face and
passed to all cu_font_face_* and cu_scaled_font_* instead of the
cairo_font_face_t * which is opaque and has no getters anyway. (or
maybe passing that around is still useful? I don't think so at this
time.)
- cu_scaled_font_create_func_t:
If this is not NULL, it should return a void* that is attached to
the scaled font and passed to all cu_scaled_font_ functions in
excess to the cairo_scaled_font_t *.
- cu_scaled_font_extents_func_t:
If this is NULL, the extents will be set to zero and the status on
the scaled font will be set to CAIRO_STATUS_NOT_SUPPORTED
(interesting there's no such an status currently.)
- cu_scaled_font_glyph_extents_func_t:
This takes the cairo_scaled_font_t and returns scaled maybe-hinted
extents.
- cu_scaled_font_glyph_path_func_t:
Like show_glyph, but only appends subpaths to current path. If is
NULL, the backend may return NOT_SUPPORTED or use show_glyph and
surface analysis to emulate it.
- cu_scaled_font_show_glyph_func_t:
This takes the scaled font (and its data) and a cairo_t, and uses
other cairo operations to draw the glyph. The cairo_t has the
font_matrix applied to it already. The show glyph may also set
color or other parameters, but of course it should save/restore
appropriately. It may also use other font functions, like the
toy API (consider a user-font to draw hexboxes for example), or
even other user fonts. It returns a boolean: whether the glyph
should be cached or not. This can be used to implement fonts
that have slightly different shapes for each glyph every time
shown. (to simulate hand-writing for example.)
It's up to the user-font backend to appropriately create a
surface of proper type, and to cache them. For example, for the
PS/PDF surfaces, the noncached glyphs will be directed to the
output while the cached glyphs will be stuffed into Type3 fonts.
Each glyph may be a bitmap or drawing instructions depending on
whether the surface had to fallback to image or not. There are
a few things about this API that I'm not comfortable with:
* If the glyph is to not be cached, we may as well draw it
directly on the target surface, but with this design we can't.
* The user-font backend may need to call get_glyph_extents to
create the proper bitmap surface to pass to show_glyph. While not
infeasible, I don't think it's optimal.
* And of course, you don't have a way to know whether the glyph
is going to be ARGB or not.
Cheers,
behdad
More information about the cairo
mailing list