Pango font API problems, gnome-print



One API freeze issue that has been lurking around under the surface
is the Pango Font API. Currently we have two fairly major problems:

 * GNOME-2.0 is currently shaping up to have two different  
   font APIs, one for Pango, and one for gnome-print. There is
   no excuse for this, and if we don't fix it, its going 
   to be a major blot on presenting GNOME as a coherent API set.

   Of course, it's fine for gnome-print to introduce additional
   font API that is specific to printing / postscript output; but
   this should be done as an extension to the Pango API, just
   as there is additional API for X11 fonts, for Xft fonts, and
   so forth.

 * Some Pango structures that will need to be improved in the future
   are not expandable in a binary or source compatible fashion.

Having spent some time reviewing the current gnome-print font APIs,
let me list the issues with the Pango font APIs which might cause
problems for replacing the gnome-print font APIs with the Pango
APIs.

 * There is no explicit representation of a "face". pango_context_list_fonts()
   does list all faces, but the representation is implicit - as a pattern
   that happens load that face.

 * There is no way of getting the original name of a face ... all
   we can get is the style parameters, which may not be one-to-one with
   the actual style names.

 * There is no way to get the foundry for a particular famiily. The original 
   idea was that for conflicting foundaries, if the backend wanted not
   to merge them, then it would report "Adobe Symbol" and "ITC Symbol"
   as the family names. Unfortunately, this means that the name of one
   font depends on other fonts on the system.

 * The set of metrics in PangoFontMetrics is quite limited. (It
   includes only ascent, descent, approximate_digit_width,
   approximate_char_width.)

 * There is no way of adding to the set of parameters that Pango uses
   to convert PangoFontDescription to PangoFont. You can't add
   a transform, or convert for a particular resolution ... the
   API is currently:

   PangoFont *pango_font_map_load_font (PangoFontMap               *fontmap,
					const PangoFontDescription *desc);

In addition, I would identify some other problems:

 * PangoFontDescription can be allocated on the stack and direct
   manipulation of members is allowed, so it can't be extended in 
   the future.

 * PangoFontDescription forces all of style/variant/weight/stretch to 
   be specified. This means that, for instance, gtk_widget_modify_font() 
   can't only change, say, the size while leaving the style unmodified.

   Also, relative sizes aren't possible in PangoFontDescription. This would
   also be useful for gtk_widget_modify_font().

 * PangoFontMetrics is allocated on the stack, so it can't be 
   extended in the future.


So, what can we do about these problems? I think the first thing
to do would be to make PangoFontMetrics and PangoFontDescription
heap allocated opaque structures.

So, we have:

  PangoMetrics *pango_context_load_metrics (PangoContext               *context,
				            const PangoFontDescription *desc,
					    PangoLanguage              *language);
  gint pango_metrics_get_ascent (PangoMetrics *metrics);

  void pango_metrics_ref   (PangoMetrics *metrics);
  void pango_metrics_unref (PangoMetrics *metrics);

Or, make PangoMetrics a GObject. And for PangoFontDescription:

  void        pango_font_description_new        (void);

  PangoWeight pango_font_description_get_weight   (PangoFontDescription *desc);
  void        pango_font_description_set_weight   (PangoFontDescription *desc,
                                                   PangoWeight           weight);
  [...]

By adding:

  void        pango_font_description_merge        (PangoFontDescription *desc,
                                                   PangoFontDescription *other);
  void        pango_font_description_unset_weight (PangoFontDescription *desc);
  [...]

We can can handle the possibility of having partial font descriptions.
With the change to PangoMetrics, adding additional metrics to PangoMetrics
becomes simply a question of figuring out what we need to add.


Handling the question of faces and foundaries is perhaps a bit
tricker. There are a number of possible strategies:

 A) Create PangoFontFamily and PangoFontFace objects, so you have something
  along the lines of:

  void        pango_list_families (PangoContext     *context,
                                   PangoFontFamily **families,
			           gint            **n_families);

  /* Full name including foundary */
  const char *pango_font_family_get_name         (PangoFontFamily  *family);
  /* Short unique name */
  const char *pango_font_family_get_display_name (PangoFontFamily  *family);
  /* Foundary name */
  const char *pango_font_family_get_foundary     (PangoFontFamily  *family);
  void        pango_font_family_list_faces       (PangoFontFamily  *family,
                                                  PangoFontFace   **faces,
                                                  gint            **n_faces);
  gboolean    pango_font_family_is_fixed_pitch   (PangoFontFamily  *family);
  [...]

  const char *          pango_font_face_get_name       (PangoFontFace *face);
  const char *          pango_font_face_get_style_name (PangoFontFace *face);
  PangoFontDescription *pango_font_face_describe       (PangoFontFace *face);


 This does pose potential memory management problems, which is why
 I avoided it in the first place, though the FontFace and FontFamily
 objects could potentially be about as lightweight as strings.

 Then, you'd probably want to add:

  void pango_font_description_set_face (PangoFontDescription *description,
                                        PangoFontFace        *face);
  void pango_font_description_set_family (PangoFontDescription *description,
                                          PangoFontFamily      *family);

 To allow exactly specifying the face or the family.
  
 B) Leave the representation of a family as a string, and of a face
 as a PangoFontDescription, but add some extra accessors along the
 lines of:

  char *pango_font_family_get_foundary  (PangoContext *context,
                                         const char *family);
  char *pango_font_family_get_full_name (PangoContext *context,
                                         const char *family);
  
  char *pango_font_face_get_style_name (PangoContext *context,
                                        PangoFontDescription *description);

 C) [ Somewhat between the previous two ] Add style_name, full_family_name
    string properties to PangoFontDescription, using 
   'full_family_name' as a unique identifier for the family, and 
   'full_family_name' + 'style_name' as unique identifier for 
    the face, then have:
    
  gboolean pango_font_family_is_fixed_pitch (PangoContext *context,
                                             const char   *family_name);
  PangoFontDescription *pango_font_face_describe (PangoContext *context,
                                                  const char   *family_name,
                                                  const char   *style name);

Finally, I intend to declare fairly large portions of the Pango API
to be evolving, and make no commitment to future source or binary
compatibility for those portions. In particular, I plan to do this
for:

 - The PangoFontClass structure and deriving from PangoFont
 - All of pango-fontmap.h
 - All of pango-engine.h

I think if we control the internal interfaces inside Pango, we can
maintain compatibility for the interfaces used by Pango - if 
necessary we can just make the external interfaces wrappers around
new extended interfaces.

I think the right way to mark things evolving is to surround them
in the header files with:

#ifndef PANGO_ENABLE_EVOLVING
#endif /* PANGO_ENABLE_EVOLVING */

Regards,
                                        Owen




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