Bug 5495

Summary: Need cairo_scaled_font_text_extents
Product: cairo Reporter: Behdad Esfahbod <freedesktop>
Component: generalAssignee: Carl Worth <cworth>
Status: RESOLVED FIXED QA Contact: cairo-bugs mailing list <cairo-bugs>
Severity: blocker    
Priority: high CC: mlists
Version: 1.1.1   
Hardware: x86 (IA32)   
OS: Linux (All)   
Whiteboard:
i915 platform: i915 features:
Attachments: patch.
Updated patch
updated patch with correct indentation and better docs

Description Behdad Esfahbod 2006-01-04 17:57:43 UTC
Currently cairo provides the following functions:

  cairo_text_extents
  cairo_glyph_extents
  cairo_scaled_font_glyph_extents

In Pango, I need cairo_scaled_font_text_extents or cairo_text_to_glyphs.  While
the latter may have other uses in its own, the former does the job in my case. 
Here is the original report:

  http://bugzilla.gnome.org/show_bug.cgi?id=313551
Comment 1 Carl Worth 2006-01-06 12:15:03 UTC
I have had a note in the cairo TODO for quite a while:

    * cairo_text_glyphs:

        It would function as a sort of bridge between the toy and the
        real text APIs:

        >       void
        >       cairo_text_glyphs (cairo_t *cr, const unsigned char *utf8,
        >                          cairo_glyph_t *glyphs, int *num_glyphs);
        >
        > with num_glyphs as an input-output parameter. The behavior of this
        > function would be such that calling:
        >
        >       cairo_text_glyphs (cr, string, glyphs, &num_glyphs);
        >       cairo_show_glyphs (cr, glyphs, num_glyphs);
        >
        > would be equivalent too:
        >
        >       cairo_show_text (cr, string);
        >
        > as long as the original size of glyphs/num_glyphs was large
        > enough.

I've always liked this idea as it would allow for doing things such as
text-on-a-path while still interacting with the cairo API at the "toy" level of
text, not glyphs.

However, as it turns out, the semantics I defined above, (the sequence of
cairo_text_glyphs; cairo_show_glyphs being equivalent to cairo_show_text), looks
infeasible. The problem is that the cairo_glyph_t structure does not reference a
font, so it's inadequate for implementing any chars-to-glyph function that
references multiple fonts. (The original proposal for cairo_glyph_t did have a
font in it, but I think we removed it since it was regarded as wasteful since
pango would only call cairo_show_glyphs with the same font in every glyph).

I guess the chars-to-glyphs function could return a list of font,glyph-list
pairs, but that gets to be pretty awkward in a C API.

So, those are the current frustrations I'm having with the function I originally
wanted. (Meanwhile, Owen had other objections to a chars-to-glyphs function in
cairo, but I don't currently recall what they were.)

But back to your needs. It appears that any chars-to-glyphs function that
requires a cairo_t wouldn't help you very much, since you already have a
separate solution that does not require chars-to-glyphs but does require a dummy
cairo_t. Correct?

So, another possibility would be a public version of an existing internal
function in cairo:

cairo_status_t
_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
                                   double               x,
                                   double               y,
                                   const char          *utf8,
                                   cairo_glyph_t      **glyphs,
                                   int                 *num_glyphs)

This interface is obviously restricted to a single font, so it avoids the
font-merging problems of my desired cairo_text_glyphs function described above.

And it would be a rather simple change to make an appropriately modified public
interface to this. I'm open to suggestions on what the interface should look
like. Should it be exactly as above?

We generally try to stash status values in the object being acted upon, but we
also avoid doing that for functions that nominally are not modifiers of that
object. So this might be one of the rare cases where we actually return a status
value and expect the user to check in. Since this is off in the
already-"hard"-to-use scaled-font piece of cairo's API, I think I would be fine
with that decision.

Alternately, given that this function already exists in cairo, along with
cairo_scaled_font_glyph_extents, it doesn't look like it would be too hard to
implement cairo_scaled_font_text_extents instead. It probably should take an x,y
pair in contrast to cairo_text_extents (which gets the x,y from the path's
current point).

Anyway, there are some thoughts from me. Let's get a patch together and look at
it on the list. (Or if the various cairo_scaled_font_get_* functions from bug
#5496 give you a not-too-ugly solution in pango, then we can avoid making a
decision here.)

-Carl
Comment 2 Behdad Esfahbod 2006-01-08 02:09:41 UTC
Thanks Carl for the detailed reply.

I have mixed feelings about adding cairo_text_glyphs.  On one hand, I see it
useful for doing text on a path too, on the other hand, I like to see the toy
API as limited as possible, for reasons that we both know, but most users of
cairo don't know: that the toy API is not going to do i18n text anything near
what Pango does anytime, unless you duplicate/copy Pango into cairo.  So the
more limited it is, the more people will use Pango or another real text
rendering engine, and that makes their applications much more useful to the world.

Back to my need, when cairo_scaled_font_t getters are committed, I have a
solution using a dummy cairo_t.  I create a 0x0 image surface, that should work
good enough.  So, yes, anything needing a cairo_t is not going to help me more
here.  I personally am not satisfied with the that way, so I'm calling that ugly
and prefer adding cairo_scaled_font_text_extents, but if you really think it's
going to make new problems later, I'm not insisting on it.

I'm with you in implementing cairo_scaled_font_text_extents to fix this bug. 
Lets postpone exposing text_glyphs to when we exactly know where the toy api is
going.

I prefer:

cairo_public void
cairo_scaled_font_text_extents (cairo_scaled_font_t   *scaled_font,
				const char            *utf8, 
				cairo_text_extents_t  *extents);

Like cairo_scaled_font_glyph_extents, and I'd say it should use (0,0) as (x,y),
but that's your decision.

behdad
Comment 3 Carl Worth 2006-01-10 07:28:25 UTC
(In reply to comment #2)
> So the more limited it is, the more people will use Pango or another real text
> rendering engine, and that makes their applications much more useful to the world.

OK. You, Owen, and Keith have all raised the same concern about this function. I
fold. I won't add it, and we can all rest assured that people won't mistake
cairo's toy API for anything but a toy.

> cairo_public void
> cairo_scaled_font_text_extents (cairo_scaled_font_t   *scaled_font,
> 				const char            *utf8, 
> 				cairo_text_extents_t  *extents);
> 
> Like cairo_scaled_font_glyph_extents, and I'd say it should use (0,0) as (x,y),
> but that's your decision.

As a rule, I do like fewer arguments rather than more. But I am a bit concerned
that there might be a missing x,y offset here. There's no cairo_t here so the
user couldn't just work around it with something like cairo_translate, right?

But I also put a lot of trust in you as one of the primary users of this API.
Are you quite convinced that nobody would ever need anything but (0,0) here?
Comment 4 Behdad Esfahbod 2006-01-10 21:53:09 UTC
Isn't the (x,y) simply added to the x,y in the extents?  I mean, if there's no
transformation involved, I prefer using (0,0).  But if dropping (x,y) means
somebody would need to create a dummy cairo_t, set ctm, etc, then I withdraw.
Comment 5 Carl Worth 2006-01-11 03:10:03 UTC
> Isn't the (x,y) simply added to the x,y in the extents?

Uhm, yeah, I guess so.

In that case, please move forward with your API as proposed. It looks good.

-Carl

Comment 6 Behdad Esfahbod 2006-01-14 21:22:17 UTC
Created attachment 4345 [details] [review]
patch.

Patch for cairo_scaled_font_text_extents.
Well, I reworked the pango patch and the "create dummy cairo_t" approach works
perfectly now, just that it's ugly.  I will switch to this function when 1.2 is
out...
Comment 7 Behdad Esfahbod 2006-01-20 16:47:22 UTC
Created attachment 4403 [details] [review]
Updated patch

Oops, forgot to free the glyphs array.	Fixed.
Comment 8 Carl Worth 2006-01-21 11:03:04 UTC
[copying my recent post to cairo@cairographics.org which reviews the latest
patch here]

The indentation for the patch needs to be changed from two spaces to
cairo's standard of four spaces (see cairo/CODING_STYLE for more
details).

Beyond that, I would like to see a little bit more detail in the
documentation to describe what the returned extents actually are,
(that is how they correspond to something that might be drawn). I
recognize that you modelled the new documentation from the existing
documentation for cairo_scaled_font_glyph_extents---it should be fixed
as well.

For example, the documentation for cairo_text_extents has the
following paragraph:

    * Gets the extents for a string of text. The extents describe a
    * user-space rectangle that encloses the "inked" portion of the text,
    * (as it would be drawn by cairo_show_text()). Additionally, the
    * x_advance and y_advance values indicate the amount by which the
    * current point would be advanced by cairo_show_text().

This makes very clear the strong association between the values
returned by cairo_text_extents and the effects of cairo_show_text.
(There's also a second paragraph about whitespace character which you
may just want to copy verbatim.)

For cairo_scaled_font_text_extents, the first paragraph is a bit
trickier to write since the drawing operation to reference is
cairo_show_text, but only if the cairo_t is using the same scaled
font. Maybe the right way to describe the operation would be to have
the literal code necessary to achieve the same effect. Maybe something
like:

        cairo_font_face_t *font_face;
        cairo_matrix_t font_matrix, ctm;
        cairo_font_options_t font_options;

        font_face = cairo_scaled_font_get_font_face (scaled_font);
        cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix);
        cairo_scaled_font_get_ctm (scaled_font, &ctm);
        cairo_scaled_font_get_font_options (scaled_font, &font_options);

        cairo_set_font_face (cr, font_face);
        cairo_set_font_matrix (cr, &font_matrix);
        cairo_set_matrix (cr, &ctm);
        cairo_set_font_options (cr, &font_options);

        cairo_show_text (cr, text);

Though, now that I type that all out, maybe it would be easier to just
describe it with text, saying something like "as it would be drawn by
cairo_show_text if the cairo graphics state were set to the same
font_face, font_matrix, ctm, and font_options as @scaled_font).

Anyway, I'll let you take a whack at the complete paragraph.

Does anybody see any problem with adding this new
cairo_scaled_font_text_extents function? I've been convinced that this
makes sense *instead of* ever adding any sort of text_to_glyphs
functionality within cairo.
Comment 9 Behdad Esfahbod 2006-01-22 03:08:47 UTC
Created attachment 4423 [details] [review]
updated patch with correct indentation and better docs

Ok, attaching a final patch with better documentation and correct indentation.
Comment 10 Carl Worth 2006-01-22 21:13:00 UTC
Looks good to me. Please commit!

-Carl
Comment 11 Behdad Esfahbod 2006-01-22 21:29:58 UTC
2006-01-22  Behdad Esfahbod  <behdad@behdad.org>

        * src/cairo.h, doc/public/cairo-sections.txt: Add
        cairo_scaled_font_text_extents.

        * src/cairo-scaled-font.c (cairo_scaled_font_text_extents): New
        function.

        * src/cairo-scaled-font.c (cairo_scaled_font_glyph_extents): Improve
        documentation.

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.