Re: GNOME font library RFC



Lauris Kaplinski <lauris@ariman.ee> writes:

> Hello
> 
> I am planning to start moving gnome-font code from gnome-print into
> separate package (gnome-font?). The rough design idea is following:
> 
> Font server.
> This should keep an eye on hot directories and update its internal font
> list, if user drops/deletes some interesting (pfb, ttf) files. It acts as
> central font resource, distributing fonts to clients via CORBA.
> Possible additional functions:
> - It can generate GhostScript fontmap
> - It can act as X font server
> - It can somehow handle font URL-s
>
> Client side font library.
> Maintaining local copy of fonts during client lifetime. Doing all
> rendering, plus possibility to import/export raw font data (to include
> fonts with documents)

I would encourage you, if you are going to be putting a lot of effort
into designing something, is to conserve effort and fix the font problem
for Unix, and not just fix the font problem for GNOME. That is, make something
that can be used as the next generation font API for X.

Yes, that is a big job, but you already seem to have some pretty 
ambitious ideas.

For the more client-side stuff. I would encourage you to again save
effort and work on GnomeText-Font-Print / Pango integration. 

> The initial API draft for client (mostly copied from existing font API) is
> included in end of document. I am especially interested in special needs
> of pango-like multilanguage text handling.
> Also - maybe specific font should have rotation parameter - but then
> possibly rotation is not enough, and full affine transform would be
> better.
> I am not sure, how to deal with ligatures. I prefer, if font handles them
> internally, as there can be fonts with huge amounts of complex ligatures -
> but maybe user program should still have a way to access full
> ligature list.

Fonts cannot handle them internally (at least, not without very
sophisticated and complicated font systems, like Quickdraw GX's finite
state machines). This is because rendering many languages requires
interaction between the set of ligatures and linguistic information.

What the font needs to export is enough information to allow the
rendering system to pick the right ligatures and alternate glyphs.

The simple, and rather unsatisfactory, way of doing this is to simply
have predefined glyph encodings - this is how Pango deals with X
fonts.  For X, if Pango wants the form of an Alef glyph at the
beginning of the word, it knows that it is at position foo in a font
encoded with the mulearabic encoding.

The more sophisticated way of doing this is to include tables of information
that allow the rendering system to pick the ligatures. OpenType is
a well-developed example of doing this.

Robert Brady <rwb197@zepler.org> (the author of Pango's Devanagari shaper,
and gote), is working on a way specifying ligatures to use with X fonts
intermediate in complexity between these two extremes.

> API Draft
> 
> Problem 1. How to present available fonts in user-manageable way.

Have you read the Pango API's for this? Note that the general plan
has been that Pango will play a central role in the future of
GnomePrint / GnomeText. Raph hasn't had much time to give me feedback
but that is the way things are supposed to me moving.

The current Pango font API's can be modified (though for Pango 1.0,
that needs to be sooner rather than later), but it would be bad
if the API's of GnomePrint and Pango were subtly different and
incompatible.
 
> Proposal:
> 
> GnomeFontFamily
> Opaque
> 
> GList * gnome_font_get_family_list (const gchar * encoding...)
> 
> Arguments: NULL-terminated list of accepted encodings. NULL or "*" if don't
> care.
> Returns: list of available font families, referenced
> 
> All further subclassing is done with static strings
> 
> GList * gnome_font_family_get_weights (GnomeFontFamily * family, ...)
> GList * gnome_font_family_get_slants (GnomeFontFamily * family, ...)
> GList * gnome_font_family_get_stretches (GnomeFontFamily * family, ...)
> GList * gnome_font_family_get_variants (GnomeFontFamily * family, ...)
> GList * gnome_font_family_get_encodings (GnomeFontFamily * family, ...)

This looks like a rather inconvenient way of doing things - I have
to pick some axis to be the primary axis, and specify all the rest?
It doesn't seem to map very well to what people generally want to
do, which is either:

 - Find the closest match to some given specification
 - List all variants for a particular font.

Pango's API is much simpler.

void       pango_font_map_list_fonts    (PangoFontMap           *fontmap,
					 const gchar            *family,
					 PangoFontDescription ***descs,
					 int                    *n_descs);
void       pango_font_map_list_families (PangoFontMap           *fontmap,
					 gchar                ***families,
					 int                    *n_families);

Where, for pango_font_map_list_fonts(), if family is non-NULL, it lists
the available fonts for that particular family, and if family is NULL
it lists all fonts.

> Arguments: GnomeFontFamily
> NULL-terminated list of additional constraints, all expressed as string
> key-value pairs
> "weight", "slant", "stretch", "variant", "encoding"...
> Multiple pairs with same key are ANDed, except encodings, which are ORed
> Scalar arguments can include extra symbols:
> "+bold" - bold or thicker == ">=bold"
> ">book" - thicker than book
> Synonyms are resolved according to current family rules - i.e. usually
> "book" == "normal"
> Returns: New GList of static strings

Why would you want this complexity? The set of variants for a particular
font, is small. Why not just return them and let the client choose between
them if they want. 
 
> Convenience translations:
> 
> GnomeFontWeight gnome_font_string_to_weight (const gchar * weight)
> GnomeFontSlant gnome_font_string_to_weight (const gchar * slant)
> GnomeFontStretch gnome_font_string_to_weight (const gchar * stretch)
> GnomeFontVariant gnome_font_string_to_weight (const gchar * variant)
> GnomeFontEncoding gnome_font_string_to_encoding (const gchar * variant)
> 
> Opposite are family-dependent to allow correct value for synonyms
> 
> const gchar * gnome_font_weight_to_string (GnomeFontFamily * family,
> 	GnomeFontWeight weight)
> etc...
> 
> GnomeFontMap - unsized font
> Opaque

I took a simpler approach to this for Pango:

PangoFontDescription *pango_font_description_from_string (const char                  *str);
char *                pango_font_description_to_string   (const PangoFontDescription  *desc);

(look at the API docs on pango.org if you want a full description of the way
this works.)

This makes for a very nice user api.

 font_desc = pango_font_description_from_string ("Times Bold 24");

> GnomeFontMap * gnome_font_find (GnomeFontFamily * family,
> 	GnomeFontWeight weight,
> 	GnomeFontSlant slant,
> 	GnomeFontVariant variant,
> 	GnomeFontStretch stretch,
> 	GnomeFontEncoding encoding);

The name GnomeFontMap for this is unfortunate. A PangoFontmap is an object
that does lookups from PangoFontDescription to PangoFont.
 
> Arguments: font data. Anything (including family) can be NULL, in which case
> sensible default is loaded. Default values for all arguments are:
> "Helvetica", "Book", "Normal", "Normal", "Normal", "iso-8859-15"
> Returns: Referenced GnomeFontMap object

How can FontWeight, for instance, be NULL - it is a enumeration, right?
 
> Accessing members:
> GnomeFontFamily * gnome_font_map_get_family (GnomeFontMap * map)
> Returns: Referenced font family
> const gchar * gnome_font_map_get_family_as_string (GnomeFontMap * map)
> Return: Family name
> etc... (weight, slant, variant, stretch, encoding)
> 
> const gchar * gnome_font_map_get_ps_name (GnomeFontMap * map)
> Returns: PostScript name for font
>
> gdouble gnome_font_map_get_ascender (GnomeFontMap * map)
> gdouble gnome_font_map_get_descender (GnomeFontMap * map)
> gdouble gnome_font_map_get_underline_position (GnomeFontMap * map)
> gdouble gnome_font_map_get_underline_thickness (GnomeFontMap * map)

The metrics can be very different for different scripts. (Arabic has
much larger descenders than English). Pango has:

void                  pango_font_get_metrics       (PangoFont        *font,
						    const gchar      *lang,
						    PangoFontMetrics *metrics);

Where lang is the standard "en_US"-style string. If you really want
to handle fine typography, you need to handle baseline adjustment as
well. (See the OpenType spec, or Lunde's book for information about this)

> Accessing glyphs
> 
> gint gnome_font_map_glyph_from_unicode (GnomeFontMap * map, gint unicode)

This is nowhere near sufficient. In the big picture, one-to-one char
to glyph mappings are only a small part of what is necessary. 
 
> gint gnome_font_map_glyph_from_char (GnomeFontMap * map, gint ch)

Char? in what encoding?

> Glyph operations (all sizes are in 0.001 units)
> 
> gdouble gnome_font_map_glyph_ascender (GnomeFontMap * map, gint glyph)
> gdouble gnome_font_map_glyph_descender (GnomeFontMap * map, gint glyph)
> gdouble gnome_font_map_glyph_lbearing (GnomeFontMap * map, gint glyph)
> gdouble gnome_font_map_glyph_rbearing (GnomeFontMap * map, gint glyph)
> ArtPoint * gnome_font_map_glyph_advance (GnomeFontMap * map, gint glyph,
> 	ArtPoint * advance)

The Pango API here is:

void pango_font_get_glyph_extents (PangoFont        *font,
                                   PangoGlyph        glyph,
                                   PangoRectangle   *ink_rect,
                                   PangoRectangle   *logical_rect);

/* Macros to translate from extents rectangles to ascent/descent/lbearing/rbearing
 */
#define PANGO_ASCENT(rect) (-(rect).y)
#define PANGO_DESCENT(rect) ((rect).y + (rect).height)
#define PANGO_LBEARING(rect) ((rect).x)
#define PANGO_RBEARING(rect) ((rect).x + (rect).width)

 1) You actually need all 8 values upon occasion, and having a function
    with 8 arguments is unmanagable.

 2) Getting metrics is actually a bottlneck in many applications; getting multiple 
    metrics at once can be a distinct optimization.

> ArtDRect * gnome_font_map_glyph_bbox (GnomeFontMap * map, gint glyph,
> 	ArtDRect * bbox)
> const ArtBpath * gnome_font_map_glyph_outline (GnomeFontMap * map,
> 	gint glyph)
> 
> Text operations
> 
> GnomeGlyphList * gnome_font_map_translate (GnomeFontMap * map,
> 	const gchar * text)
> Returns: List (array?) of glyphs, corresponding to given character. This
> function resolves all ligatures that font support

This is nowhere near sufficient. Pango is all about doing this. Just 
create a font system that Pango can use.
 
> ArtPoint * gnome_font_map_text_advance (GnomeFontMap * map,
> 	const gchar * text, ArtPoint * advance)
> ArtDRect * gnome_font_map_text_bbox (GnomeFontMap * map, const gchar * text,
> 	ArtDRect * bbox)

> gdouble gnome_font_map_text_position (GnomeFontMap * map,
> 	const gchar * text, gint position, gboolean inside_ligature)

Again, not enough. Don't try to do this yourself. Take advantage
of Pango.

> gdouble gnome_font_map_glyph_kerning (GnomeFontMap * map, gint glyph1,
> 	gint glyph2)
> 
> Misc
> 
> gchar * gnome_font_map_get_ps_font (GnomeFontMap * map, gint * length)
> Returns: PostScript (type?) font, suitable for sending to printer
 
Hmmm, but, what do you do with the result? What's the encoding?
What if the font map maps to more than one font? Wouldn't it be
better to have a function to render to postscript.
 
> GnomeFont - sized font variant, suitable for rendering
> Opaque

I'm quite confused by the presence a bunch of functions duplicated
between GnomeFont and GnomeFontmap; I don't think that is an
intuitive API, and I don't quite undersand what the implications
are of the duplication - what is the relation between the  metrics
of the unsized font and the metrics of the sized font?

(Pango has no way of getting metrics for an unsized font - for X
using bitmap fonts, there are huge deviations from linear scaling,
and it wouldn't really make any sense at all.)

> GnomeFont * gnome_font_find (GnomeFontFamily * family, gdouble size,
> 	GnomeFontWeight weight,
> 	GnomeFontSlant slant,
> 	GnomeFontVariant variant,
> 	GnomeFontStretch stretch,
> 	GnomeFontEncoding encoding);
> GnomeFont * gnome_font_get_from_map (GnomeFontMap * map, gdouble size)
> 
> Accessing members:
> 
> GnomeFontFamily * gnome_font_map_get_family (GnomeFont * font)
> Returns: Referenced font family
> const gchar * gnome_font_get_family_as_string (GnomeFont * font)
> Return: Family name
> etc... (weight, slant, variant, stretch, encoding)
> GnomeFontMap * gnome_font_get_map (GnomeFont * font)
> 
> const gchar * gnome_font_get_ps_name (GnomeFont * font)
> Returns: PostScript name for font
> const gchar * gnome_font_get_x_name (GnomeFont * font)
> gdouble gnome_font_get_size (GnomeFont * font)
> 
> gint gnome_font_get_ascender (GnomeFont * font)
> gint gnome_font_get_descender (GnomeFont * font)
> gint gnome_font_get_underline_position (GnomeFont * font)
> gint gnome_font_get_underline_thickness (GnomeFont * font)
> 
> Accessing glyphs
> 
> gint gnome_font_glyph_from_unicode (GnomeFont * font, gint unicode)
> gint gnome_font_glyph_from_char (GnomeFont * font, gint ch)
> 
> Glyph operations (all sizes are in units (usually pixels))
> 
> gint gnome_font_glyph_ascender (GnomeFont * font, gint glyph)
> gint gnome_font_glyph_descender (GnomeFont * font, gint glyph)
> gint gnome_font_glyph_lbearing (GnomeFont * font, gint glyph)
> gint gnome_font_glyph_rbearing (GnomeFont * font, gint glyph)
> GdkPoint * gnome_font_glyph_advance (GnomeFont * font, gint glyph,
> 	GdkPoint * advance)
> ArtIRect * gnome_font_glyph_bbox (GnomeFont * font, gint glyph,
> 	ArtIRect * bbox)
> 
> Text operations
> 
> GnomeGlyphList * gnome_font_translate (GnomeFont * font,
> 	const gchar * text)
> Returns: List (array?) of glyphs, corresponding to given character. This
> function resolves all ligatures that font support
> 
> GdkPoint * gnome_font_text_advance (GnomeFont * font,
> 	const gchar * text, GdkPoint * advance)
> ArtIRect * gnome_font_text_bbox (GnomeFont * font, const gchar * text,
> 	ArtIRect * bbox)

> gint gnome_font_text_position (GnomeFont * font,
> 	const gchar * text, gint position, gboolean inside_ligature)
> gint gnome_font_glyph_kerning (GnomeFont * font, gint glyph1,
> 	gint glyph2)
 
> Rendering
> 
> gnome_font_render_glyph_graymap (GnomeFont * font, gint glyph,
> 	guchar * pixels, gint width, gint height, gint rowstride)
> gnome_font_render_glyph_rgb (GnomeFont * font, gint glyph,
> 	guchar * pixels, gint width, gint height, gint rowstride,
> 	guint32 color)
> gnome_font_render_glyph_rgba (GnomeFont * font, gint glyph,
> 	guchar * pixels, gint width, gint height, gint rowstride,
> 	guint32 color)
> gnome_font_render_glyph_pixmap_and_mask (GnomeFont * font, gint glyph,
> 	GdkWindow * window, GdkPixmap ** pixmap, GdkBitmap ** bitmap)

You probably need to think about sub-pixel positionging here.

> gnome_font_render_text_graymap (GnomeFont * font, const gchar * text,
> 	guchar * pixels, gint width, gint height, gint rowstride)
> gnome_font_render_glyph_rgb (GnomeFont * font, const gchar * text,
> 	guchar * pixels, gint width, gint height, gint rowstride,
> 	guint32 color)
> gnome_font_render_text_rgba (GnomeFont * font, const gchar * text,
> 	guchar * pixels, gint width, gint height, gint rowstride,
> 	guint32 color)
> gnome_font_render_text_pixmap_and_mask (GnomeFont * font, const gchar * text,
> 	GdkWindow * window, GdkPixmap ** pixmap, GdkBitmap ** bitmap)

These should all be done in conjunction with Pango, not separately.

> GdkFont * gnome_font_get_gdk_font (GnomeFont * font)
> NB! this function should invoke X font lookup, to which (hopefully)
> server-side XFS responds, rendering to X exact representation of given
> font.

Pretty irrelevant in a few months. GdkFont is deprecated in the next
release of GTK+. With Pango, a "font" does not map to a single X font
in any particularly straightforward way.
 
> const gchar * gnome_font_get_uri (GnomeFont * font)
> Returns URI, if present, from where client can fetch that font

Is there a point in including this function at this point? What does the
client get from the URI?

Hope these comments are useful.
                                        Owen




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]