Graphviz  2.31.20130523.0446
plugin/pango/gvtextlayout_pango.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 #ifdef HAVE_CONFIG_H
00015 #include "config.h"
00016 #endif
00017 
00018 #include <stdlib.h>
00019 #include <string.h>
00020 #include "gvplugin_textlayout.h"
00021 
00022 #ifdef HAVE_PANGOCAIRO
00023 #include <pango/pangocairo.h>
00024 #include "gvgetfontlist.h"
00025 #ifdef HAVE_PANGO_FC_FONT_LOCK_FACE
00026 #include <pango/pangofc-font.h>
00027 #endif
00028 
00029 #define N_NEW(n,t)      (t*)malloc((n)*sizeof(t))
00030 
00031 static void pango_free_layout (void *layout)
00032 {
00033     g_object_unref((PangoLayout*)layout);
00034 }
00035 
00036 static char* pango_psfontResolve (PostscriptAlias* pa)
00037 {
00038     static char buf[1024];
00039     strcpy(buf, pa->family);
00040     strcat(buf, ",");
00041     if (pa->weight) {
00042         strcat(buf, " ");
00043         strcat(buf, pa->weight);
00044     }
00045     if (pa->stretch) {
00046         strcat(buf, " ");
00047         strcat(buf, pa->stretch);
00048     }
00049     if (pa->style) {
00050         strcat(buf, " ");
00051         strcat(buf, pa->style);
00052     }
00053     return buf;
00054 }
00055 
00056 #define FONT_DPI 96.
00057 
00058 #define ENABLE_PANGO_MARKUP
00059 #ifdef ENABLE_PANGO_MARKUP
00060 #define FULL_MARKUP "<span weight=\"bold\" style=\"italic\" underline=\"single\"><sup><sub></sub></sup></span>"
00061 #endif
00062 
00063 static boolean pango_textlayout(textpara_t * para, char **fontpath)
00064 {
00065     static char buf[1024];  /* returned in fontpath, only good until next call */
00066     static PangoFontMap *fontmap;
00067     static PangoContext *context;
00068     static PangoFontDescription *desc;
00069     static char *fontname;
00070     static double fontsize;
00071     static gv_font_map* gv_fmap;
00072     char *fnt, *psfnt = NULL;
00073     PangoLayout *layout;
00074     PangoRectangle logical_rect;
00075     cairo_font_options_t* options;
00076     PangoFont *font;
00077 #ifdef ENABLE_PANGO_MARKUP
00078     PangoAttrList *attrs;
00079     GError *error = NULL;
00080     int flags;
00081 #endif
00082     char *text;
00083     double textlayout_scale;
00084 
00085     if (!context) {
00086         fontmap = pango_cairo_font_map_new();
00087         gv_fmap = get_font_mapping(fontmap);
00088         context = pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP(fontmap));
00089         options=cairo_font_options_create();
00090         cairo_font_options_set_antialias(options,CAIRO_ANTIALIAS_GRAY);
00091         cairo_font_options_set_hint_style(options,CAIRO_HINT_STYLE_FULL);
00092         cairo_font_options_set_hint_metrics(options,CAIRO_HINT_METRICS_ON);
00093         cairo_font_options_set_subpixel_order(options,CAIRO_SUBPIXEL_ORDER_BGR);
00094         pango_cairo_context_set_font_options(context, options);
00095         pango_cairo_context_set_resolution(context, FONT_DPI);
00096         cairo_font_options_destroy(options);
00097         g_object_unref(fontmap);
00098     }
00099 
00100     if (!fontname || strcmp(fontname, para->fontname) != 0 || fontsize != para->fontsize) {
00101         fontname = para->fontname;
00102         fontsize = para->fontsize;
00103         pango_font_description_free (desc);
00104 
00105         if (para->postscript_alias) {
00106             psfnt = fnt = gv_fmap[para->postscript_alias->xfig_code].gv_font;
00107             if(!psfnt)
00108                 psfnt = fnt = pango_psfontResolve (para->postscript_alias);
00109         }
00110         else
00111             fnt = fontname;
00112 
00113         desc = pango_font_description_from_string(fnt);
00114         /* all text layout is done at a scale of FONT_DPI (nominaly 96.) */
00115         pango_font_description_set_size (desc, (gint)(fontsize * PANGO_SCALE));
00116 
00117         if (fontpath && (font = pango_font_map_load_font(fontmap, context, desc))) {  /* -v support */
00118             const char *fontclass;
00119 
00120             fontclass = G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(font));
00121 
00122             buf[0] = '\0';
00123             if (psfnt) {
00124                 strcat(buf, "(ps:pango  ");
00125                 strcat(buf, psfnt);
00126                 strcat(buf, ") ");
00127             }
00128             strcat(buf, "(");
00129             strcat(buf, fontclass);
00130             strcat(buf, ") ");
00131 #ifdef HAVE_PANGO_FC_FONT_LOCK_FACE
00132             if (strcmp(fontclass, "PangoCairoFcFont") == 0) {
00133                 FT_Face face;
00134                 PangoFcFont *fcfont;
00135                 FT_Stream stream;
00136                 FT_StreamDesc streamdesc;
00137                 fcfont = PANGO_FC_FONT(font);
00138                 face = pango_fc_font_lock_face(fcfont);
00139                 if (face) {
00140                     strcat(buf, "\"");
00141                     strcat(buf, face->family_name);
00142                     strcat(buf, ", ");
00143                     strcat(buf, face->style_name);
00144                     strcat(buf, "\" ");
00145     
00146                     stream = face->stream;
00147                     if (stream) {
00148                         streamdesc = stream->pathname;
00149                         if (streamdesc.pointer)
00150                             strcat(buf, (char*)streamdesc.pointer);
00151                         else
00152                             strcat(buf, "*no pathname available*");
00153                     }
00154                     else
00155                         strcat(buf, "*no stream available*");
00156                 }
00157                 pango_fc_font_unlock_face(fcfont);
00158             }
00159             else
00160 #endif
00161             {
00162                 PangoFontDescription *tdesc;
00163                 char *tfont;
00164                 
00165                 tdesc = pango_font_describe(font);
00166                 tfont = pango_font_description_to_string(tdesc);
00167                 strcat(buf, "\"");
00168                 strcat(buf, tfont);
00169                 strcat(buf, "\" ");
00170                 g_free(tfont);
00171             }
00172             *fontpath = buf;
00173         }
00174     }
00175 
00176 #ifdef ENABLE_PANGO_MARKUP
00177     if ((para->font) && (flags = para->font->flags)) {
00178         char* markup = N_NEW(strlen(para->str) + sizeof(FULL_MARKUP), char);
00179         strcpy(markup,"<span");
00180 
00181         if (flags & HTML_BF)
00182             strcat(markup," weight=\"bold\"");
00183         if (flags & HTML_IF)
00184             strcat(markup," style=\"italic\"");
00185         if (flags & HTML_UL)
00186             strcat(markup," underline=\"single\"");
00187         strcat (markup,">");
00188 
00189         if (flags & HTML_SUP)
00190             strcat(markup,"<sup>");
00191         if (flags & HTML_SUB)
00192             strcat(markup,"<sub>");
00193 
00194         strcat (markup,para->str);
00195 
00196         if (flags & HTML_SUB)
00197             strcat(markup,"</sub>");
00198         if (flags & HTML_SUP)
00199             strcat(markup,"</sup>");
00200 
00201         strcat (markup,"</span>");
00202         if (!pango_parse_markup (markup, -1, 0, &attrs, &text, NULL, &error)) {
00203             fprintf (stderr, "Error - pango_parse_markup: %s\n", error->message);
00204             text = para->str;
00205             attrs = NULL;
00206         }
00207         free (markup);
00208     }
00209     else {
00210         text = para->str;
00211         attrs = NULL;
00212     }
00213 #else
00214     text = para->str;
00215 #endif
00216 
00217     layout = pango_layout_new (context);
00218     para->layout = (void *)layout;    /* layout free with textpara - see labels.c */
00219     para->free_layout = pango_free_layout;    /* function for freeing pango layout */
00220 
00221     pango_layout_set_text (layout, text, -1);
00222     pango_layout_set_font_description (layout, desc);
00223 #ifdef ENABLE_PANGO_MARKUP
00224     if (attrs)
00225         pango_layout_set_attributes (layout, attrs);
00226 #endif
00227 
00228     pango_layout_get_extents (layout, NULL, &logical_rect);
00229 
00230     /* if pango doesn't like the font then it sets width=0 but height = garbage */
00231     if (logical_rect.width == 0)
00232         logical_rect.height = 0;
00233 
00234     textlayout_scale = POINTS_PER_INCH / (FONT_DPI * PANGO_SCALE);
00235     para->width = (int)(logical_rect.width * textlayout_scale + 1);    /* round up so that width/height are never too small */
00236     para->height = (int)(logical_rect.height * textlayout_scale + 1);
00237 
00238     /* FIXME  -- Horrible kluge !!! */
00239 
00240     /* For now we are using pango for single line blocks only.
00241      * The logical_rect.height seems to be too high from the font metrics on some platforms.
00242      * Use an assumed height based on the point size.
00243      */
00244 
00245     para->height = (int)(para->fontsize * 1.1 + .5);
00246 
00247     /* The y offset from baseline to 0,0 of the bitmap representation */
00248 #if defined PANGO_VERSION_MAJOR && (PANGO_VERSION_MAJOR >= 1)
00249     para->yoffset_layout = pango_layout_get_baseline (layout) * textlayout_scale;
00250 #else
00251     {
00252         /* do it the hard way on rhel5/centos5 */
00253         PangoLayoutIter *iter = pango_layout_get_iter (layout);
00254         para->yoffset_layout = pango_layout_iter_get_baseline (iter) * textlayout_scale;
00255     }
00256 #endif
00257 
00258     /* The distance below midline for y centering of text strings */
00259     para->yoffset_centerline = 0.2 * para->fontsize;
00260 
00261     if (logical_rect.width == 0)
00262         return FALSE;
00263     return TRUE;
00264 }
00265 
00266 static gvtextlayout_engine_t pango_textlayout_engine = {
00267     pango_textlayout,
00268 };
00269 #endif
00270 
00271 gvplugin_installed_t gvtextlayout_pango_types[] = {
00272 #ifdef HAVE_PANGOCAIRO
00273     {0, "textlayout", 10, &pango_textlayout_engine, NULL},
00274 #endif
00275     {0, NULL, 0, NULL, NULL}
00276 };