1/* $Id$ $Revision$ */
2/* vim:set shiftwidth=4 ts=8: */
3
4/*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14#include "config.h"
15
16#include <stdlib.h>
17#include <string.h>
18#include "gvplugin_render.h"
19#include "agxbuf.h"
20#include "utils.h"
21#include "gvplugin_textlayout.h"
22
23#include <pango/pangocairo.h>
24#include "gvgetfontlist.h"
25#ifdef HAVE_PANGO_FC_FONT_LOCK_FACE
26#include <pango/pangofc-font.h>
27#endif
28
29#define N_NEW(n,t) (t*)malloc((n)*sizeof(t))
30
31static void pango_free_layout (void *layout)
32{
33 g_object_unref((PangoLayout*)layout);
34}
35
36static char* pango_psfontResolve (PostscriptAlias* pa)
37{
38 static char buf[1024];
39 strcpy(buf, pa->family);
40 strcat(buf, ",");
41 if (pa->weight) {
42 strcat(buf, " ");
43 strcat(buf, pa->weight);
44 }
45 if (pa->stretch) {
46 strcat(buf, " ");
47 strcat(buf, pa->stretch);
48 }
49 if (pa->style) {
50 strcat(buf, " ");
51 strcat(buf, pa->style);
52 }
53 return buf;
54}
55
56#define FONT_DPI 96.
57
58#define ENABLE_PANGO_MARKUP
59#ifdef ENABLE_PANGO_MARKUP
60#define FULL_MARKUP "<span weight=\"bold\" style=\"italic\" underline=\"single\"><sup><sub></sub></sup></span>"
61#endif
62
63static boolean pango_textlayout(textspan_t * span, char **fontpath)
64{
65 static char buf[1024]; /* returned in fontpath, only good until next call */
66 static PangoFontMap *fontmap;
67 static PangoContext *context;
68 static PangoFontDescription *desc;
69 static char *fontname;
70 static double fontsize;
71 static gv_font_map* gv_fmap;
72 char *fnt, *psfnt = NULL;
73 PangoLayout *layout;
74 PangoRectangle logical_rect;
75 cairo_font_options_t* options;
76 PangoFont *font;
77#ifdef ENABLE_PANGO_MARKUP
78 PangoAttrList *attrs;
79 GError *error = NULL;
80 int flags;
81#endif
82 char *text;
83 double textlayout_scale;
84 PostscriptAlias *pA;
85
86 if (!context) {
87 fontmap = pango_cairo_font_map_new();
88 gv_fmap = get_font_mapping(fontmap);
89#ifdef HAVE_PANGO_FONT_MAP_CREATE_CONTEXT
90 context = pango_font_map_create_context (fontmap);
91#else
92 context = pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP(fontmap));
93#endif
94 options=cairo_font_options_create();
95 cairo_font_options_set_antialias(options,CAIRO_ANTIALIAS_GRAY);
96 cairo_font_options_set_hint_style(options,CAIRO_HINT_STYLE_FULL);
97 cairo_font_options_set_hint_metrics(options,CAIRO_HINT_METRICS_ON);
98 cairo_font_options_set_subpixel_order(options,CAIRO_SUBPIXEL_ORDER_BGR);
99 pango_cairo_context_set_font_options(context, options);
100 pango_cairo_context_set_resolution(context, FONT_DPI);
101 cairo_font_options_destroy(options);
102 g_object_unref(fontmap);
103 }
104
105 if (!fontname || strcmp(fontname, span->font->name) != 0 || fontsize != span->font->size) {
106 fontname = span->font->name;
107 fontsize = span->font->size;
108 pango_font_description_free (desc);
109
110 pA = span->font->postscript_alias;
111 if (pA) {
112 psfnt = fnt = gv_fmap[pA->xfig_code].gv_font;
113 if(!psfnt)
114 psfnt = fnt = pango_psfontResolve (pA);
115 }
116 else
117 fnt = fontname;
118
119 desc = pango_font_description_from_string(fnt);
120 /* all text layout is done at a scale of FONT_DPI (nominaly 96.) */
121 pango_font_description_set_size (desc, (gint)(fontsize * PANGO_SCALE));
122
123 if (fontpath && (font = pango_font_map_load_font(fontmap, context, desc))) { /* -v support */
124 const char *fontclass;
125
126 fontclass = G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(font));
127
128 buf[0] = '\0';
129 if (psfnt) {
130 strcat(buf, "(ps:pango ");
131 strcat(buf, psfnt);
132 strcat(buf, ") ");
133 }
134 strcat(buf, "(");
135 strcat(buf, fontclass);
136 strcat(buf, ") ");
137#ifdef HAVE_PANGO_FC_FONT_LOCK_FACE
138 if (strcmp(fontclass, "PangoCairoFcFont") == 0) {
139 FT_Face face;
140 PangoFcFont *fcfont;
141 FT_Stream stream;
142 FT_StreamDesc streamdesc;
143 fcfont = PANGO_FC_FONT(font);
144 face = pango_fc_font_lock_face(fcfont);
145 if (face) {
146 strcat(buf, "\"");
147 strcat(buf, face->family_name);
148 strcat(buf, ", ");
149 strcat(buf, face->style_name);
150 strcat(buf, "\" ");
151
152 stream = face->stream;
153 if (stream) {
154 streamdesc = stream->pathname;
155 if (streamdesc.pointer)
156 strcat(buf, (char*)streamdesc.pointer);
157 else
158 strcat(buf, "*no pathname available*");
159 }
160 else
161 strcat(buf, "*no stream available*");
162 }
163 pango_fc_font_unlock_face(fcfont);
164 }
165 else
166#endif
167 {
168 PangoFontDescription *tdesc;
169 char *tfont;
170
171 tdesc = pango_font_describe(font);
172 tfont = pango_font_description_to_string(tdesc);
173 strcat(buf, "\"");
174 strcat(buf, tfont);
175 strcat(buf, "\" ");
176 g_free(tfont);
177 }
178 *fontpath = buf;
179 }
180 }
181
182#ifdef ENABLE_PANGO_MARKUP
183 if ((span->font) && (flags = span->font->flags)) {
184 unsigned char buf[BUFSIZ];
185 agxbuf xb;
186
187 agxbinit(&xb, BUFSIZ, buf);
188 agxbput(&xb,"<span");
189
190 if (flags & HTML_BF)
191 agxbput(&xb," weight=\"bold\"");
192 if (flags & HTML_IF)
193 agxbput(&xb," style=\"italic\"");
194 if (flags & HTML_UL)
195 agxbput(&xb," underline=\"single\"");
196 if (flags & HTML_S)
197 agxbput(&xb," strikethrough=\"true\"");
198 agxbput (&xb,">");
199
200 if (flags & HTML_SUP)
201 agxbput(&xb,"<sup>");
202 if (flags & HTML_SUB)
203 agxbput(&xb,"<sub>");
204
205 agxbput (&xb,xml_string0(span->str, TRUE));
206
207 if (flags & HTML_SUB)
208 agxbput(&xb,"</sub>");
209 if (flags & HTML_SUP)
210 agxbput(&xb,"</sup>");
211
212 agxbput (&xb,"</span>");
213 if (!pango_parse_markup (agxbuse(&xb), -1, 0, &attrs, &text, NULL, &error)) {
214 fprintf (stderr, "Error - pango_parse_markup: %s\n", error->message);
215 text = span->str;
216 attrs = NULL;
217 }
218 agxbfree (&xb);
219 }
220 else {
221 text = span->str;
222 attrs = NULL;
223 }
224#else
225 text = span->str;
226#endif
227
228 layout = pango_layout_new (context);
229 span->layout = (void *)layout; /* layout free with textspan - see labels.c */
230 span->free_layout = pango_free_layout; /* function for freeing pango layout */
231
232 pango_layout_set_text (layout, text, -1);
233 pango_layout_set_font_description (layout, desc);
234#ifdef ENABLE_PANGO_MARKUP
235 if (attrs)
236 pango_layout_set_attributes (layout, attrs);
237#endif
238
239 pango_layout_get_extents (layout, NULL, &logical_rect);
240
241 /* if pango doesn't like the font then it sets width=0 but height = garbage */
242 if (logical_rect.width == 0)
243 logical_rect.height = 0;
244
245 textlayout_scale = POINTS_PER_INCH / (FONT_DPI * PANGO_SCALE);
246 span->size.x = (int)(logical_rect.width * textlayout_scale + 1); /* round up so that width/height are never too small */
247 span->size.y = (int)(logical_rect.height * textlayout_scale + 1);
248
249 /* FIXME -- Horrible kluge !!! */
250
251 /* For now we are using pango for single line blocks only.
252 * The logical_rect.height seems to be too high from the font metrics on some platforms.
253 * Use an assumed height based on the point size.
254 */
255
256 span->size.y = (int)(span->font->size * 1.1 + .5);
257
258 /* The y offset from baseline to 0,0 of the bitmap representation */
259#if !defined(_WIN32) && defined PANGO_VERSION_MAJOR && (PANGO_VERSION_MAJOR >= 1)
260 span->yoffset_layout = pango_layout_get_baseline (layout) * textlayout_scale;
261#else
262 {
263 /* do it the hard way on rhel5/centos5 */
264 PangoLayoutIter *iter = pango_layout_get_iter (layout);
265 span->yoffset_layout = pango_layout_iter_get_baseline (iter) * textlayout_scale;
266 }
267#endif
268
269 /* The distance below midline for y centering of text strings */
270 span->yoffset_centerline = 0.2 * span->font->size;
271
272 if (logical_rect.width == 0)
273 return FALSE;
274 return TRUE;
275}
276
277static gvtextlayout_engine_t pango_textlayout_engine = {
278 pango_textlayout,
279};
280
281gvplugin_installed_t gvtextlayout_pango_types[] = {
282 {0, "textlayout", 10, &pango_textlayout_engine, NULL},
283 {0, NULL, 0, NULL, NULL}
284};
285