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 | |
31 | static void pango_free_layout (void *layout) |
32 | { |
33 | g_object_unref((PangoLayout*)layout); |
34 | } |
35 | |
36 | static 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 | |
63 | static 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 | |
277 | static gvtextlayout_engine_t pango_textlayout_engine = { |
278 | pango_textlayout, |
279 | }; |
280 | |
281 | gvplugin_installed_t gvtextlayout_pango_types[] = { |
282 | {0, "textlayout" , 10, &pango_textlayout_engine, NULL}, |
283 | {0, NULL, 0, NULL, NULL} |
284 | }; |
285 | |