1 /* pangox.c: Routines for handling X fonts
2 *
3 * Copyright (C) 1999 Red Hat Software
4 * Copyright (C) 2000 SuSE Linux Ltd
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21
22 #include <string.h>
23 #include <stdlib.h>
24 #include <math.h>
25
26 #include <X11/Xlib.h>
27
28
29 #include "pango-impl-utils.h"
30 #include "pangox-private.h"
31
32 #define PANGO_TYPE_X_FONT (pango_x_font_get_type ())
33 #define PANGO_X_FONT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_TYPE_X_FONT, PangoXFont))
34 #define PANGO_X_FONT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_X_FONT, PangoXFontClass))
35 #define PANGO_X_IS_FONT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_TYPE_X_FONT))
36 #define PANGO_X_IS_FONT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_X_FONT))
37 #define PANGO_X_FONT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_X_FONT, PangoXFontClass))
38
39 typedef struct _PangoXFontClass PangoXFontClass;
40 typedef struct _PangoXMetricsInfo PangoXMetricsInfo;
41 typedef struct _PangoXContextInfo PangoXContextInfo;
42
43 struct _PangoXSubfontInfo
44 {
45 char *xlfd;
46 XFontStruct *font_struct;
47 gboolean is_1byte;
48 int range_byte1;
49 int range_byte2;
50 };
51
52 struct _PangoXMetricsInfo
53 {
54 const char *sample_str;
55 PangoFontMetrics *metrics;
56 };
57
58 struct _PangoXContextInfo
59 {
60 PangoGetGCFunc get_gc_func;
61 PangoFreeGCFunc free_gc_func;
62 };
63
64 struct _PangoXFontClass
65 {
66 PangoFontClass parent_class;
67 };
68
69 static PangoFontClass *parent_class; /* Parent class structure for PangoXFont */
70
71 static void pango_x_font_class_init (PangoXFontClass *class);
72 static void pango_x_font_init (PangoXFont *xfont);
73 static void pango_x_font_dispose (GObject *object);
74 static void pango_x_font_finalize (GObject *object);
75
76 static PangoFontDescription *pango_x_font_describe (PangoFont *font);
77 static PangoCoverage * pango_x_font_get_coverage (PangoFont *font,
78 PangoLanguage *language);
79 static PangoEngineShape * pango_x_font_find_shaper (PangoFont *font,
80 PangoLanguage *language,
81 guint32 ch);
82 static void pango_x_font_get_glyph_extents (PangoFont *font,
83 PangoGlyph glyph,
84 PangoRectangle *ink_rect,
85 PangoRectangle *logical_rect);
86 static PangoFontMetrics * pango_x_font_get_metrics (PangoFont *font,
87 PangoLanguage *language);
88 static PangoFontMap * pango_x_font_get_font_map (PangoFont *font);
89
90 static PangoXSubfontInfo * pango_x_find_subfont (PangoFont *font,
91 PangoXSubfont subfont_index);
92 static XCharStruct * pango_x_get_per_char (PangoFont *font,
93 PangoXSubfontInfo *subfont,
94 guint16 char_index);
95 static gboolean pango_x_find_glyph (PangoFont *font,
96 PangoGlyph glyph,
97 PangoXSubfontInfo **subfont_return,
98 XCharStruct **charstruct_return);
99 static XFontStruct * pango_x_get_font_struct (PangoFont *font,
100 PangoXSubfontInfo *info);
101
102 static void pango_x_get_item_properties (PangoItem *item,
103 PangoUnderline *uline,
104 PangoAttrColor *fg_color,
105 gboolean *fg_set,
106 PangoAttrColor *bg_color,
107 gboolean *bg_set);
108
109 static inline PangoXSubfontInfo *
pango_x_find_subfont(PangoFont * font,PangoXSubfont subfont_index)110 pango_x_find_subfont (PangoFont *font,
111 PangoXSubfont subfont_index)
112 {
113 PangoXFont *xfont = (PangoXFont *)font;
114
115 if (subfont_index < 1 || subfont_index > xfont->n_subfonts)
116 return NULL;
117
118 return xfont->subfonts[subfont_index-1];
119 }
120
121 static void
pango_x_make_font_struct(PangoFont * font,PangoXSubfontInfo * info)122 pango_x_make_font_struct (PangoFont *font, PangoXSubfontInfo *info)
123 {
124 PangoXFont *xfont = (PangoXFont *)font;
125 PangoXFontCache *cache;
126
127 cache = pango_x_font_map_get_font_cache (xfont->fontmap);
128
129 info->font_struct = pango_x_font_cache_load (cache, info->xlfd);
130 if (!info->font_struct)
131 {
132 g_warning ("Cannot load font for XLFD '%s\n", info->xlfd);
133
134 /* Prevent a segfault, but probably not much more */
135 info->font_struct = pango_x_font_cache_load (cache, "fixed");
136 }
137
138 info->is_1byte = (info->font_struct->min_byte1 == 0 && info->font_struct->max_byte1 == 0);
139 info->range_byte1 = info->font_struct->max_byte1 - info->font_struct->min_byte1 + 1;
140 info->range_byte2 = info->font_struct->max_char_or_byte2 - info->font_struct->min_char_or_byte2 + 1;
141 }
142
143 static inline XFontStruct *
pango_x_get_font_struct(PangoFont * font,PangoXSubfontInfo * info)144 pango_x_get_font_struct (PangoFont *font, PangoXSubfontInfo *info)
145 {
146 if (!info->font_struct)
147 pango_x_make_font_struct (font, info);
148
149 return info->font_struct;
150 }
151
152 static void
free_context_info(PangoXContextInfo * info)153 free_context_info (PangoXContextInfo *info)
154 {
155 g_slice_free (PangoXContextInfo, info);
156 }
157
158 static PangoXContextInfo *
get_context_info(PangoContext * context)159 get_context_info (PangoContext *context)
160 {
161 PangoXContextInfo *info;
162 static GQuark quark = 0;
163
164 if (G_UNLIKELY (!quark))
165 quark = g_quark_from_static_string ("pango-x-info");
166
167 info = g_object_get_qdata (G_OBJECT (context), quark);
168
169 if (G_UNLIKELY (!info))
170 {
171 info = g_slice_new (PangoXContextInfo);
172 info->get_gc_func = NULL;
173 info->free_gc_func = NULL;
174 g_object_set_qdata_full (G_OBJECT (context),
175 quark,
176 info, (GDestroyNotify)free_context_info);
177 }
178
179 return info;
180 }
181
182 /**
183 * pango_x_get_context:
184 * @display: an X display (As returned by XOpenDisplay().)
185 *
186 * Retrieves a #PangoContext appropriate for rendering with X fonts on the
187 * given display.
188 *
189 * Return value: the new #PangoContext.
190 *
191 * Deprecated: 1.22: Use pango_x_font_map_for_display() followed by
192 * pango_font_map_create_context() instead.
193 **/
194 PangoContext *
pango_x_get_context(Display * display)195 pango_x_get_context (Display *display)
196 {
197 return pango_font_map_create_context (pango_x_font_map_for_display (display));
198 }
199
200 /**
201 * pango_x_context_set_funcs:
202 * @context: a #PangoContext.
203 * @get_gc_func: function called to create a new GC for a given color.
204 * @free_gc_func: function called to free a GC created with @get_gc_func.
205 *
206 * Sets the functions that will be used to get GC's in various colors when
207 * rendering layouts with this context.
208 **/
209 void
pango_x_context_set_funcs(PangoContext * context,PangoGetGCFunc get_gc_func,PangoFreeGCFunc free_gc_func)210 pango_x_context_set_funcs (PangoContext *context,
211 PangoGetGCFunc get_gc_func,
212 PangoFreeGCFunc free_gc_func)
213 {
214 PangoXContextInfo *info;
215
216 g_return_if_fail (context != NULL);
217
218 info = get_context_info (context);
219
220 info->get_gc_func = get_gc_func;
221 info->free_gc_func = free_gc_func;
222 }
223
224 static GType
pango_x_font_get_type(void)225 pango_x_font_get_type (void)
226 {
227 static GType object_type = 0;
228
229 if (G_UNLIKELY (!object_type))
230 {
231 const GTypeInfo object_info =
232 {
233 sizeof (PangoXFontClass),
234 (GBaseInitFunc) NULL,
235 (GBaseFinalizeFunc) NULL,
236 (GClassInitFunc) pango_x_font_class_init,
237 NULL, /* class_finalize */
238 NULL, /* class_data */
239 sizeof (PangoXFont),
240 0, /* n_preallocs */
241 (GInstanceInitFunc) pango_x_font_init,
242 NULL /* value_table */
243 };
244
245 object_type = g_type_register_static (PANGO_TYPE_FONT,
246 I_("PangoXFont"),
247 &object_info, 0);
248 }
249
250 return object_type;
251 }
252
253 static void
pango_x_font_init(PangoXFont * xfont)254 pango_x_font_init (PangoXFont *xfont)
255 {
256 xfont->subfonts_by_charset = g_hash_table_new (g_str_hash, g_str_equal);
257
258 xfont->n_subfonts = 0;
259 xfont->max_subfonts = 1;
260
261 xfont->subfonts = g_new (PangoXSubfontInfo *, xfont->max_subfonts);
262
263 xfont->metrics_by_lang = NULL;
264
265 xfont->size = -1;
266 xfont->xface = NULL;
267 }
268
269 static void
pango_x_font_class_init(PangoXFontClass * class)270 pango_x_font_class_init (PangoXFontClass *class)
271 {
272 GObjectClass *object_class = G_OBJECT_CLASS (class);
273 PangoFontClass *font_class = PANGO_FONT_CLASS (class);
274
275 parent_class = g_type_class_peek_parent (class);
276
277 object_class->finalize = pango_x_font_finalize;
278 object_class->dispose = pango_x_font_dispose;
279
280 font_class->describe = pango_x_font_describe;
281 font_class->get_coverage = pango_x_font_get_coverage;
282 /* font_class->find_shaper = pango_x_font_find_shaper; */
283 font_class->get_glyph_extents = pango_x_font_get_glyph_extents;
284 font_class->get_metrics = pango_x_font_get_metrics;
285 font_class->get_font_map = pango_x_font_get_font_map;
286 }
287
288 PangoXFont *
pango_x_font_new(PangoFontMap * fontmap,const char * spec,int size)289 pango_x_font_new (PangoFontMap *fontmap, const char *spec, int size)
290 {
291 PangoXFont *result;
292
293 g_return_val_if_fail (fontmap != NULL, NULL);
294 g_return_val_if_fail (spec != NULL, NULL);
295
296 result = g_object_new (PANGO_TYPE_X_FONT, NULL);
297
298 g_assert (result->fontmap == NULL);
299 result->fontmap = fontmap;
300 g_object_add_weak_pointer (G_OBJECT (result->fontmap), (gpointer *) (gpointer) &result->fontmap);
301
302 result->display = pango_x_fontmap_get_display (fontmap);
303
304 result->fonts = g_strsplit(spec, ",", -1);
305 for (result->n_fonts = 0; result->fonts[result->n_fonts]; result->n_fonts++)
306 ; /* Nothing */
307
308 result->size = size;
309
310 return result;
311 }
312
313 /**
314 * pango_x_load_font:
315 * @display: the X display.
316 * @spec: a comma-separated list of XLFD's.
317 *
318 * Loads up a logical font based on a "fontset" style text
319 * specification. This is not remotely useful (Pango API's generally
320 * work in terms of #PangoFontDescription) and the result may not
321 * work correctly in all circumstances. Use of this function should
322 * be avoided.
323 *
324 * Returns: a new #PangoFont.
325 */
326 PangoFont *
pango_x_load_font(Display * display,const char * spec)327 pango_x_load_font (Display *display,
328 const char *spec)
329 {
330 PangoXFont *result;
331
332 g_return_val_if_fail (display != NULL, NULL);
333 g_return_val_if_fail (spec != NULL, NULL);
334
335 result = pango_x_font_new (pango_x_font_map_for_display (display), spec, -1);
336
337 return (PangoFont *)result;
338 }
339
340
341 #define FLUSH \
342 G_STMT_START { \
343 if (charcount) \
344 { \
345 XDrawString16 (display, d, gc, \
346 glyph_x0, glyph_y0, \
347 xcharbuffer, charcount); \
348 charcount = 0; \
349 } \
350 } G_STMT_END
351
352
353 /**
354 * pango_x_render:
355 * @display: the X display.
356 * @d: the drawable on which to draw string.
357 * @gc: the graphics context.
358 * @font: the font in which to draw the string.
359 * @glyphs: the glyph string to draw.
360 * @x: the x position of start of string (in pixels).
361 * @y: the y position of baseline (in pixels).
362 *
363 * Renders a #PangoGlyphString onto an X drawable.
364 */
365 void
pango_x_render(Display * display,Drawable d,GC gc,PangoFont * font,PangoGlyphString * glyphs,int x,int y)366 pango_x_render (Display *display,
367 Drawable d,
368 GC gc,
369 PangoFont *font,
370 PangoGlyphString *glyphs,
371 int x,
372 int y)
373 {
374 Font old_fid = None;
375 XFontStruct *fs;
376 int i;
377 int x_off = 0;
378
379 /*
380 * We collect the characters in this buffer as long as the font does not
381 * change. At that time, or when the buffer runs full, or at the end,
382 * then we empty the buffer.
383 */
384 XChar2b xcharbuffer[1000];
385 int glyph_x0 = 0, expected_x = 0; /* x/y initializations are to quiet GCC */
386 int glyph_y0 = 0;
387 int charcount = 0;
388
389 g_return_if_fail (display != NULL);
390 g_return_if_fail (glyphs != NULL);
391
392 for (i=0; i<glyphs->num_glyphs; i++)
393 {
394 PangoGlyph glyph = glyphs->glyphs[i].glyph;
395 int glyph_x = x + PANGO_PIXELS (x_off + glyphs->glyphs[i].geometry.x_offset);
396 int glyph_y = y + PANGO_PIXELS (glyphs->glyphs[i].geometry.y_offset);
397
398 /* Clip glyphs into the X coordinate range; we really
399 * want to clip glyphs with an ink rect outside the
400 * [0,32767] x [0,32767] rectangle but looking up
401 * the ink rect here would be a noticeable speed hit.
402 * This is close enough.
403 */
404 if (!(glyph != PANGO_GLYPH_EMPTY &&
405 glyph_x >= -16384 && glyph_x <= 32767 &&
406 glyph_y >= -16384 && glyph_y <= 32767))
407 goto next_glyph;
408
409 if (G_LIKELY ((glyph & PANGO_GLYPH_UNKNOWN_FLAG) == 0))
410 {
411 guint16 index = PANGO_X_GLYPH_INDEX (glyph);
412 guint16 subfont_index = PANGO_X_GLYPH_SUBFONT (glyph);
413 PangoXSubfontInfo *subfont;
414
415 subfont = pango_x_find_subfont (font, subfont_index);
416 if (subfont)
417 {
418 fs = pango_x_get_font_struct (font, subfont);
419 if (!fs)
420 continue;
421
422 if (fs->fid != old_fid)
423 {
424 FLUSH;
425 XSetFont (display, gc, fs->fid);
426 old_fid = fs->fid;
427 }
428
429 if (charcount == G_N_ELEMENTS (xcharbuffer) ||
430 (charcount > 0 && (glyph_y != glyph_y0 ||
431 glyph_x != expected_x)))
432 FLUSH;
433
434 if (charcount == 0)
435 {
436 glyph_x0 = glyph_x;
437 glyph_y0 = glyph_y;
438 }
439 xcharbuffer[charcount].byte1 = index / 256;
440 xcharbuffer[charcount].byte2 = index % 256;
441
442 expected_x = glyph_x + XTextWidth16 (fs, &xcharbuffer[charcount], 1);
443
444 charcount++;
445 } else
446 goto unknown_glyph;
447 } else {
448 PangoFontMetrics *metrics;
449 int x1, y1, x2, y2; /* rectangle the character should go inside. */
450 int baseline;
451 int stroke_thick;
452 gunichar wc;
453 gboolean invalid_input;
454
455 unknown_glyph:
456 FLUSH;
457
458 if (font)
459 metrics = pango_font_get_metrics (font, NULL);
460 else
461 metrics = NULL;
462
463 if (metrics)
464 {
465 y1 = glyph_y - PANGO_PIXELS (metrics->ascent);
466 y2 = y1 + PANGO_PIXELS (metrics->ascent + metrics->descent);
467 }
468 else
469 {
470 y2 = glyph_y;
471 y1 = y2 - PANGO_UNKNOWN_GLYPH_HEIGHT;
472 }
473
474 x1 = glyph_x;
475 x2 = x1 + PANGO_PIXELS (glyphs->glyphs[i].geometry.width);
476
477 baseline = glyph_y;
478 stroke_thick = MAX ((int) (0.5 + 0.025 * (y2 - y1)), 1);
479
480 if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
481 wc = glyph & ~PANGO_GLYPH_UNKNOWN_FLAG;
482 else
483 wc = 0;
484 invalid_input = glyph == PANGO_GLYPH_INVALID_INPUT || wc > 0x10FFFF;
485
486 switch (wc)
487 {
488 case '\n':
489 case '\r':
490 case 0x2028: /* Line separator */
491 case 0x2029: /* Paragraph separator */
492 {
493 /* Draw a carriage-return thingy */
494 PangoRectangle up_stroke;
495 PangoRectangle across_stroke;
496
497 int hborder = (x2 - x1) * 0.1;
498 int arrow_height = 0.25 * (y2 - y1);
499 int top_border = (y2 - y1) * 0.25;
500
501 int arrow_x, arrow_width, tmp_height;
502
503 /* Draw the arrow-head */
504
505 tmp_height = (stroke_thick % 2 == 0) ? 2 : 1; /* Starting height */
506 arrow_height = 2 * ((1 + arrow_height - tmp_height) / 2) + tmp_height; /* Force symmetry */
507 arrow_width = 2 + arrow_height - tmp_height;
508
509 for (arrow_x = x1 + hborder; arrow_x < x1 + hborder + arrow_width; arrow_x++)
510 {
511 XDrawLine (display, d, gc,
512 arrow_x,
513 baseline - stroke_thick + (stroke_thick - tmp_height) / 2,
514 arrow_x,
515 baseline - stroke_thick + (stroke_thick - tmp_height) / 2 + tmp_height - 1);
516
517 if ((arrow_x - x1 - hborder) % 2 == 1)
518 tmp_height += 2;
519 }
520
521 across_stroke.x = arrow_x;
522 across_stroke.width = x2 - hborder - arrow_x - stroke_thick;
523 across_stroke.y = baseline - stroke_thick;
524 across_stroke.height = stroke_thick;
525
526 XFillRectangle (display, d, gc,
527 across_stroke.x, across_stroke.y,
528 across_stroke.width, across_stroke.height);
529
530 up_stroke.x = across_stroke.x + across_stroke.width;
531 up_stroke.width = stroke_thick;
532 up_stroke.y = y1 + top_border;
533 up_stroke.height = baseline - up_stroke.y;
534
535 XFillRectangle (display, d, gc,
536 up_stroke.x, up_stroke.y,
537 up_stroke.width, up_stroke.height);
538 }
539 break;
540
541 default:
542 {
543 /* Perhaps we should draw the box-with-numbers as in the
544 * other backends, though we have no guarantee of having
545 * an appropriate size of font. Right now, we just
546 * draw an empty box. (To draw the box-with-numbers.
547 * the backends would have to be changed to use
548 * pango_x_font_get_unknown_glyph() rather than
549 * pango_x_get_unknown_glyph().
550 */
551
552 int xspace = MAX ((int) (0.5 + 0.1 * (x2 - x1)), 1);
553 int yspace = MAX ((int) (0.5 + 0.1 * (y2 - y1)), 1);
554
555 x1 += xspace;
556 x2 -= xspace;
557 y1 += yspace;
558 y2 -= yspace;
559
560 XFillRectangle (display, d, gc,
561 x1, y1,
562 x2 - x1, stroke_thick);
563 XFillRectangle (display, d, gc,
564 x1, y1 + stroke_thick,
565 stroke_thick, y2 - y1 - 2 * stroke_thick);
566 XFillRectangle (display, d, gc,
567 x2 - stroke_thick, y1 + stroke_thick,
568 stroke_thick, y2 - y1 - 2 * stroke_thick);
569 XFillRectangle (display, d, gc,
570 x1, y2 - stroke_thick,
571 x2 - x1, stroke_thick);
572 if (invalid_input)
573 {
574 XDrawLine (display, d, gc,
575 x1, y1,
576 x2-1, y2-1);
577 XDrawLine (display, d, gc,
578 x2-1, y1,
579 x1, y2-1);
580 }
581
582 break;
583 }
584 }
585
586 pango_font_metrics_unref (metrics);
587 }
588
589 next_glyph:
590 x_off += glyphs->glyphs[i].geometry.width;
591 }
592 FLUSH;
593 }
594
595 #undef FLUSH
596
597 static void
pango_x_font_get_glyph_extents(PangoFont * font,PangoGlyph glyph,PangoRectangle * ink_rect,PangoRectangle * logical_rect)598 pango_x_font_get_glyph_extents (PangoFont *font,
599 PangoGlyph glyph,
600 PangoRectangle *ink_rect,
601 PangoRectangle *logical_rect)
602 {
603 XCharStruct *cs;
604 PangoXSubfontInfo *subfont;
605
606 if (glyph == PANGO_GLYPH_EMPTY)
607 {
608 if (ink_rect)
609 ink_rect->x = ink_rect->width = ink_rect->y = ink_rect->height = 0;
610 if (logical_rect)
611 logical_rect->x = logical_rect->width = logical_rect->y = logical_rect->height = 0;
612 return;
613 }
614 if ((glyph & PANGO_GLYPH_UNKNOWN_FLAG) == 0 && pango_x_find_glyph (font, glyph, &subfont, &cs))
615 {
616 if (ink_rect)
617 {
618 ink_rect->x = PANGO_SCALE * cs->lbearing;
619 ink_rect->width = PANGO_SCALE * (cs->rbearing - cs->lbearing);
620 ink_rect->y = PANGO_SCALE * -cs->ascent;
621 ink_rect->height = PANGO_SCALE * (cs->ascent + cs->descent);
622 }
623 if (logical_rect)
624 {
625 logical_rect->x = 0;
626 logical_rect->width = PANGO_SCALE * cs->width;
627 logical_rect->y = - PANGO_SCALE * subfont->font_struct->ascent;
628 logical_rect->height = PANGO_SCALE * (subfont->font_struct->ascent + subfont->font_struct->descent);
629 }
630 }
631 else
632 {
633 PangoFontMetrics *metrics;
634 gunichar wc;
635 gdouble width_factor;
636 int w;
637
638 if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
639 wc = glyph & (~PANGO_GLYPH_UNKNOWN_FLAG);
640 else
641 wc = 0;
642
643 switch (wc)
644 {
645 case '\n':
646 case '\r':
647 case 0x2028: /* Line separator */
648 case 0x2029: /* Paragraph separator */
649 {
650 #define MAGIC_FACTOR 1.2
651
652 /* carriage-return thingy */
653 width_factor = MAGIC_FACTOR;
654 break;
655 }
656 default:
657 {
658 /* Unknown glyph square */
659 width_factor = 1.0;
660 }
661 }
662
663 metrics = pango_font_get_metrics (font, NULL);
664
665 if (metrics)
666 {
667 w = metrics->approximate_char_width * width_factor;
668 w = PANGO_SCALE * PANGO_PIXELS (w);
669
670 if (ink_rect)
671 {
672 ink_rect->x = PANGO_SCALE;
673 ink_rect->width = w - 2 * PANGO_SCALE;
674 ink_rect->y = - (metrics->ascent - PANGO_SCALE);
675 ink_rect->height = metrics->ascent + metrics->descent - 2 * PANGO_SCALE;
676 }
677 if (logical_rect)
678 {
679 logical_rect->x = 0;
680 logical_rect->width = w;
681 logical_rect->y = - metrics->ascent;
682 logical_rect->height = metrics->ascent + metrics->descent;
683 }
684
685 pango_font_metrics_unref (metrics);
686 }
687 else
688 {
689 if (ink_rect)
690 ink_rect->x = ink_rect->y = ink_rect->height = ink_rect->width = 0;
691 if (logical_rect)
692 logical_rect->x = logical_rect->y = logical_rect->height = logical_rect->width = 0;
693 }
694 }
695 }
696
697 static gboolean
get_int_prop(Atom atom,XFontStruct * fs,int * val)698 get_int_prop (Atom atom,
699 XFontStruct *fs,
700 int *val)
701 {
702 int i;
703
704 *val = 0;
705
706 i = 0;
707 while (i < fs->n_properties)
708 {
709 if (fs->properties[i].name == atom)
710 {
711 *val = fs->properties[i].card32;
712 return TRUE;
713 }
714
715 ++i;
716 }
717
718 return FALSE;
719 }
720
721 /* Call @func with each glyph resulting from shaping @string with each
722 * glyph. This duplicates quite a bit of code from pango_itemize. This
723 * function should die and we should simply add the ability to specify
724 * particular fonts when itemizing.
725 */
726 static void
itemize_string_foreach(PangoFont * font,PangoLanguage * language,const char * str,void (* func)(PangoFont * font,PangoGlyphInfo * glyph_info,gpointer data),gpointer data)727 itemize_string_foreach (PangoFont *font,
728 PangoLanguage *language,
729 const char *str,
730 void (*func) (PangoFont *font,
731 PangoGlyphInfo *glyph_info,
732 gpointer data),
733 gpointer data)
734 {
735 const char *start, *p;
736 PangoGlyphString *glyph_str = pango_glyph_string_new ();
737 PangoEngineShape *shaper, *last_shaper;
738 int last_level;
739 int i;
740 guint8 *embedding_levels;
741 PangoDirection base_dir = PANGO_DIRECTION_LTR;
742 gboolean finished = FALSE;
743
744 embedding_levels = pango_log2vis_get_embedding_levels (str, -1, &base_dir);
745
746 last_shaper = NULL;
747 last_level = 0;
748
749 i = 0;
750 p = start = str;
751 while (*p || !finished)
752 {
753 gunichar wc;
754
755 if (*p)
756 {
757 wc = g_utf8_get_char (p);
758 shaper = pango_font_find_shaper (font, language, wc);
759 }
760 else
761 {
762 finished = TRUE;
763 shaper = NULL;
764 }
765
766 if (p > start &&
767 (finished ||
768 (shaper != last_shaper || last_level != embedding_levels[i])))
769 {
770 PangoAnalysis analysis = { NULL };
771 int j;
772
773 analysis.shape_engine = last_shaper;
774 analysis.font = font;
775 analysis.language = language;
776 analysis.level = last_level;
777
778 pango_shape (start, p - start, &analysis, glyph_str);
779
780 for (j = 0; j < glyph_str->num_glyphs; j++)
781 (*func) (font, &glyph_str->glyphs[j], data);
782
783 start = p;
784 }
785
786 if (!finished)
787 {
788 p = g_utf8_next_char (p);
789
790 last_shaper = shaper;
791 last_level = embedding_levels[i];
792 i++;
793 }
794 }
795
796 pango_glyph_string_free (glyph_str);
797 g_free (embedding_levels);
798 }
799
800 /* Get composite font metrics for all subfonts in list
801 */
802 static void
get_font_metrics_from_subfonts(PangoFont * font,GSList * subfonts,PangoFontMetrics * metrics)803 get_font_metrics_from_subfonts (PangoFont *font,
804 GSList *subfonts,
805 PangoFontMetrics *metrics)
806 {
807 PangoXFont *xfont = (PangoXFont *)font;
808 GSList *tmp_list = subfonts;
809 gboolean first = TRUE;
810 int total_avg_widths = 0;
811 int n_avg_widths = 0;
812 Atom avg_width_atom;
813
814 avg_width_atom = pango_x_fontmap_atom_from_name (xfont->fontmap,
815 "AVERAGE_WIDTH");
816
817 metrics->ascent = 0;
818 metrics->descent = 0;
819
820 while (tmp_list)
821 {
822 PangoXSubfontInfo *subfont = pango_x_find_subfont (font, GPOINTER_TO_UINT (tmp_list->data));
823
824 if (subfont)
825 {
826 XFontStruct *fs = pango_x_get_font_struct (font, subfont);
827 gint avg_width = 0;
828
829 if (fs)
830 {
831 if (first)
832 {
833 metrics->ascent = fs->ascent * PANGO_SCALE;
834 metrics->descent = fs->descent * PANGO_SCALE;
835 first = FALSE;
836 }
837 else
838 {
839 metrics->ascent = MAX (fs->ascent * PANGO_SCALE, metrics->ascent);
840 metrics->descent = MAX (fs->descent * PANGO_SCALE, metrics->descent);
841 }
842
843 if (get_int_prop (avg_width_atom, fs, &avg_width))
844 {
845 /* convert decipoints --> Pango units.
846 * Resolution is in (points * PANGO_SCALE) / pixel,
847 * avg_width in decipoints.
848 * We want pixels * PANGO_SCALE
849 */
850
851 /* Convert to points * PANGO_SCALE */
852 avg_width *= PANGO_SCALE / (double) 10.0;
853 /* Convert to pixels * PANGO_SCALE */
854 avg_width *= (PANGO_SCALE / PANGO_X_FONT_MAP (PANGO_X_FONT (font)->fontmap)->resolution);
855 }
856 else
857 {
858 avg_width = PANGO_SCALE * ((fs->min_bounds.width + fs->max_bounds.width) / 2);
859 }
860 }
861
862 if (avg_width)
863 {
864 total_avg_widths += avg_width;
865 n_avg_widths += 1;
866 }
867 }
868 else
869 g_warning ("Invalid subfont %d in get_font_metrics_from_subfonts", GPOINTER_TO_UINT (tmp_list->data));
870
871 tmp_list = tmp_list->next;
872 }
873
874 /* This is pretty darn bogus. */
875 if (n_avg_widths)
876 metrics->approximate_char_width = total_avg_widths / n_avg_widths;
877 else
878 metrics->approximate_char_width = PANGO_UNKNOWN_GLYPH_WIDTH * PANGO_SCALE;
879 if (metrics->ascent + metrics->descent == 0)
880 {
881 metrics->ascent = PANGO_UNKNOWN_GLYPH_HEIGHT * PANGO_SCALE;
882 metrics->descent = 0;
883 }
884 }
885
886 static void
get_subfonts_foreach(PangoFont * font,PangoGlyphInfo * glyph_info,gpointer data)887 get_subfonts_foreach (PangoFont *font,
888 PangoGlyphInfo *glyph_info,
889 gpointer data)
890 {
891 GSList **subfonts = data;
892 PangoGlyph glyph = glyph_info->glyph;
893 PangoXSubfont subfont;
894
895 if (glyph == PANGO_GLYPH_EMPTY)
896 return;
897
898 /* Use an arbitrary subfont for unknown glyphs...*/
899 if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
900 {
901 if (((PangoXFont *)font)->n_subfonts > 0)
902 glyph = PANGO_X_MAKE_GLYPH (1, 0);
903 else
904 return;
905 }
906
907 subfont = PANGO_X_GLYPH_SUBFONT (glyph);
908 if (!g_slist_find (*subfonts, GUINT_TO_POINTER ((guint)subfont)))
909 *subfonts = g_slist_prepend (*subfonts, GUINT_TO_POINTER ((guint)subfont));
910 }
911
912 /* Get composite font metrics for all subfonts resulting from shaping
913 * string str with the given font
914 */
915 static void
get_font_metrics_from_string(PangoFont * font,PangoLanguage * language,const char * str,PangoFontMetrics * metrics)916 get_font_metrics_from_string (PangoFont *font,
917 PangoLanguage *language,
918 const char *str,
919 PangoFontMetrics *metrics)
920 {
921 GSList *subfonts = NULL;
922
923 itemize_string_foreach (font, language, str, get_subfonts_foreach, &subfonts);
924 get_font_metrics_from_subfonts (font, subfonts, metrics);
925 g_slist_free (subfonts);
926 }
927
928 static void
average_width_foreach(PangoFont * font G_GNUC_UNUSED,PangoGlyphInfo * glyph_info,gpointer data)929 average_width_foreach (PangoFont *font G_GNUC_UNUSED,
930 PangoGlyphInfo *glyph_info,
931 gpointer data)
932 {
933 int *width = data;
934
935 *width += glyph_info->geometry.width;
936 }
937
938 /* Get composite font metrics for all subfonts resulting from shaping
939 * string str with the given font
940 */
941 static gdouble
get_total_width_for_string(PangoFont * font,PangoLanguage * language,const char * str)942 get_total_width_for_string (PangoFont *font,
943 PangoLanguage *language,
944 const char *str)
945 {
946 int width = 0;
947
948 itemize_string_foreach (font, language, str, average_width_foreach, &width);
949
950 return width;
951 }
952
953 static PangoFontMetrics *
pango_x_font_get_metrics(PangoFont * font,PangoLanguage * language)954 pango_x_font_get_metrics (PangoFont *font,
955 PangoLanguage *language)
956 {
957 PangoXMetricsInfo *info = NULL; /* Quiet gcc */
958 PangoXFont *xfont = (PangoXFont *)font;
959 GSList *tmp_list;
960
961 const char *sample_str = pango_language_get_sample_string (language);
962
963 tmp_list = xfont->metrics_by_lang;
964 while (tmp_list)
965 {
966 info = tmp_list->data;
967
968 if (info->sample_str == sample_str) /* We _don't_ need strcmp */
969 break;
970
971 tmp_list = tmp_list->next;
972 }
973
974 if (!tmp_list)
975 {
976 PangoFontMetrics *metrics;
977
978 info = g_slice_new0 (PangoXMetricsInfo);
979
980 xfont->metrics_by_lang = g_slist_prepend (xfont->metrics_by_lang, info);
981
982 info->sample_str = sample_str;
983 metrics = pango_font_metrics_new ();
984
985 get_font_metrics_from_string (font, language, sample_str, metrics);
986
987 metrics->approximate_digit_width = get_total_width_for_string (font, language, "0123456789") / 10;
988
989 info->metrics = metrics;
990 }
991
992 return pango_font_metrics_ref (info->metrics);
993 }
994
995 static PangoFontMap *
pango_x_font_get_font_map(PangoFont * font)996 pango_x_font_get_font_map (PangoFont *font)
997 {
998 PangoXFont *xfont = (PangoXFont *)font;
999
1000 return xfont->fontmap;
1001 }
1002
1003 /* Compare the tail of a to b */
1004 static gboolean
match_end(const char * a,const char * b)1005 match_end (const char *a, const char *b)
1006 {
1007 size_t len_a = strlen (a);
1008 size_t len_b = strlen (b);
1009
1010 if (len_b > len_a)
1011 return FALSE;
1012 else
1013 return (strcmp (a + len_a - len_b, b) == 0);
1014 }
1015
1016 /* Substitute in a charset into an XLFD. Return the
1017 * (g_malloc'd) new name, or %NULL if the XLFD cannot
1018 * match the charset
1019 */
1020 static char *
name_for_charset(char * xlfd,char * charset)1021 name_for_charset (char *xlfd, char *charset)
1022 {
1023 char *p;
1024 char *dash_charset = g_strconcat ("-", charset, NULL);
1025 char *result = NULL;
1026 int ndashes = 0;
1027
1028 for (p = xlfd; *p; p++)
1029 if (*p == '-')
1030 ndashes++;
1031
1032 if (ndashes == 14) /* Complete XLFD */
1033 {
1034 if (match_end (xlfd, "-*-*"))
1035 {
1036 result = g_malloc (strlen (xlfd) - 4 + strlen (dash_charset) + 1);
1037 strncpy (result, xlfd, strlen (xlfd) - 4);
1038 strcpy (result + strlen (xlfd) - 4, dash_charset);
1039 }
1040 if (match_end (xlfd, dash_charset))
1041 result = g_strdup (xlfd);
1042 }
1043 else if (ndashes == 13)
1044 {
1045 if (match_end (xlfd, "-*"))
1046 {
1047 result = g_malloc (strlen (xlfd) - 2 + strlen (dash_charset) + 1);
1048 strncpy (result, xlfd, strlen (xlfd) - 2);
1049 strcpy (result + strlen (xlfd) - 2, dash_charset);
1050 }
1051 if (match_end (xlfd, dash_charset))
1052 result = g_strdup (xlfd);
1053 }
1054 else
1055 {
1056 if (match_end (xlfd, "*"))
1057 {
1058 result = g_malloc (strlen (xlfd) + strlen (dash_charset) + 1);
1059 strcpy (result, xlfd);
1060 strcpy (result + strlen (xlfd), dash_charset);
1061 }
1062 if (match_end (xlfd, dash_charset))
1063 result = g_strdup (xlfd);
1064 }
1065
1066 g_free (dash_charset);
1067 return result;
1068 }
1069
1070 static PangoXSubfont
pango_x_insert_subfont(PangoFont * font,const char * xlfd)1071 pango_x_insert_subfont (PangoFont *font, const char *xlfd)
1072 {
1073 PangoXFont *xfont = (PangoXFont *)font;
1074 PangoXSubfontInfo *info;
1075
1076 info = g_slice_new (PangoXSubfontInfo);
1077
1078 info->xlfd = g_strdup (xlfd);
1079 info->font_struct = NULL;
1080
1081 xfont->n_subfonts++;
1082
1083 if (xfont->n_subfonts > xfont->max_subfonts)
1084 {
1085 xfont->max_subfonts *= 2;
1086 xfont->subfonts = g_renew (PangoXSubfontInfo *, xfont->subfonts, xfont->max_subfonts);
1087 }
1088
1089 xfont->subfonts[xfont->n_subfonts - 1] = info;
1090
1091 return xfont->n_subfonts;
1092 }
1093
1094 /**
1095 * pango_x_list_subfonts:
1096 * @font: a #PangoFont.
1097 * @charsets: the charsets to list subfonts for.
1098 * @n_charsets: the number of charsets in @charsets.
1099 * @subfont_ids: location to store a pointer to an array of subfont IDs for each found subfont;
1100 * the result must be freed using g_free().
1101 * @subfont_charsets: location to store a pointer to an array of subfont IDs for each found subfont;
1102 * the result must be freed using g_free().
1103 *
1104 * Lists the subfonts of a given font. The result is ordered first by charset,
1105 * and then within each charset, by the order of fonts in the font specification.
1106 *
1107 * Return value: length of the arrays stored in @subfont_ids and
1108 * @subfont_charsets.
1109 **/
1110 int
pango_x_list_subfonts(PangoFont * font,char ** charsets,int n_charsets,PangoXSubfont ** subfont_ids,int ** subfont_charsets)1111 pango_x_list_subfonts (PangoFont *font,
1112 char **charsets,
1113 int n_charsets,
1114 PangoXSubfont **subfont_ids,
1115 int **subfont_charsets)
1116 {
1117 PangoXFont *xfont = (PangoXFont *)font;
1118 PangoXSubfont **subfont_lists;
1119 PangoFontMap *fontmap;
1120 int i, j;
1121 int n_subfonts = 0;
1122
1123 g_return_val_if_fail (font != NULL, 0);
1124 g_return_val_if_fail (n_charsets == 0 || charsets != NULL, 0);
1125
1126 fontmap = pango_x_font_map_for_display (xfont->display);
1127
1128 subfont_lists = g_new (PangoXSubfont *, n_charsets);
1129
1130 for (j=0; j<n_charsets; j++)
1131 {
1132 subfont_lists[j] = g_hash_table_lookup (xfont->subfonts_by_charset, charsets[j]);
1133 if (!subfont_lists[j])
1134 {
1135 subfont_lists[j] = g_new (PangoXSubfont, xfont->n_fonts);
1136
1137 for (i = 0; i < xfont->n_fonts; i++)
1138 {
1139 PangoXSubfont subfont = 0;
1140 char *xlfd;
1141
1142 if (xfont->size == -1)
1143 {
1144 xlfd = name_for_charset (xfont->fonts[i], charsets[j]);
1145
1146 if (xlfd)
1147 {
1148 int count;
1149 char **names = XListFonts (xfont->display, xlfd, 1, &count);
1150 if (count > 0)
1151 subfont = pango_x_insert_subfont (font, names[0]);
1152
1153 XFreeFontNames (names);
1154 g_free (xlfd);
1155 }
1156 }
1157 else
1158 {
1159 xlfd = pango_x_make_matching_xlfd (fontmap, xfont->fonts[i], charsets[j], xfont->size);
1160 if (xlfd)
1161 {
1162 subfont = pango_x_insert_subfont (font, xlfd);
1163 g_free (xlfd);
1164 }
1165 }
1166
1167 subfont_lists[j][i] = subfont;
1168 }
1169
1170 g_hash_table_insert (xfont->subfonts_by_charset, g_strdup (charsets[j]), subfont_lists[j]);
1171 }
1172
1173 for (i = 0; i < xfont->n_fonts; i++)
1174 if (subfont_lists[j][i])
1175 n_subfonts++;
1176 }
1177
1178 *subfont_ids = g_new (PangoXSubfont, n_subfonts);
1179 *subfont_charsets = g_new (int, n_subfonts);
1180
1181 n_subfonts = 0;
1182
1183 for (j=0; j<n_charsets; j++)
1184 for (i=0; i<xfont->n_fonts; i++)
1185 if (subfont_lists[j][i])
1186 {
1187 (*subfont_ids)[n_subfonts] = subfont_lists[j][i];
1188 (*subfont_charsets)[n_subfonts] = j;
1189 n_subfonts++;
1190 }
1191
1192 g_free (subfont_lists);
1193
1194 return n_subfonts;
1195 }
1196
1197 /**
1198 * pango_x_has_glyph:
1199 * @font: a #PangoFont which must be from the X backend.
1200 * @glyph: the index of a glyph in the font. (Formed
1201 * using the #PANGO_X_MAKE_GLYPH macro)
1202 *
1203 * Checks if the given glyph is present in a X font.
1204 *
1205 * Return value: %TRUE if the glyph is present.
1206 **/
1207 gboolean
pango_x_has_glyph(PangoFont * font,PangoGlyph glyph)1208 pango_x_has_glyph (PangoFont *font,
1209 PangoGlyph glyph)
1210 {
1211 PangoXSubfontInfo *subfont;
1212 XCharStruct *cs;
1213
1214 guint16 char_index = PANGO_X_GLYPH_INDEX (glyph);
1215 guint16 subfont_index = PANGO_X_GLYPH_SUBFONT (glyph);
1216
1217 subfont = pango_x_find_subfont (font, subfont_index);
1218 if (!subfont)
1219 return FALSE;
1220
1221 cs = pango_x_get_per_char (font, subfont, char_index);
1222
1223 if (cs && (cs->lbearing != cs->rbearing || cs->width != 0))
1224 return TRUE;
1225 else
1226 return FALSE;
1227 }
1228
1229 /**
1230 * pango_x_font_subfont_xlfd:
1231 * @font: a #PangoFont which must be from the X backend.
1232 * @subfont_id: the id of a subfont within the font.
1233 *
1234 * Determines the X Logical Font Description for the specified
1235 * subfont.
1236 *
1237 * Return value: A newly-allocated string containing the XLFD for the
1238 * subfont. This string must be freed with g_free().
1239 **/
1240 char *
pango_x_font_subfont_xlfd(PangoFont * font,PangoXSubfont subfont_id)1241 pango_x_font_subfont_xlfd (PangoFont *font,
1242 PangoXSubfont subfont_id)
1243 {
1244 PangoXSubfontInfo *subfont;
1245
1246 g_return_val_if_fail (font != NULL, NULL);
1247 g_return_val_if_fail (PANGO_X_IS_FONT (font), NULL);
1248
1249 subfont = pango_x_find_subfont (font, subfont_id);
1250 if (!subfont)
1251 {
1252 g_warning ("pango_x_font_subfont_xlfd: Invalid subfont_id specified");
1253 return NULL;
1254 }
1255
1256 return g_strdup (subfont->xlfd);
1257 }
1258
1259 static void
pango_x_font_dispose(GObject * object)1260 pango_x_font_dispose (GObject *object)
1261 {
1262 PangoXFont *xfont = PANGO_X_FONT (object);
1263
1264 /* If the font is not already in the freed-fonts cache, add it,
1265 * if it is already there, do nothing and the font will be
1266 * freed.
1267 */
1268 if (!xfont->in_cache && xfont->fontmap)
1269 pango_x_fontmap_cache_add (xfont->fontmap, xfont);
1270
1271 G_OBJECT_CLASS (parent_class)->dispose (object);
1272 }
1273
1274
1275 static void
subfonts_foreach(gpointer key,gpointer value,gpointer data G_GNUC_UNUSED)1276 subfonts_foreach (gpointer key, gpointer value, gpointer data G_GNUC_UNUSED)
1277 {
1278 g_free (key);
1279 g_free (value);
1280 }
1281
1282 static void
free_metrics_info(PangoXMetricsInfo * info)1283 free_metrics_info (PangoXMetricsInfo *info)
1284 {
1285 pango_font_metrics_unref (info->metrics);
1286 g_slice_free (PangoXMetricsInfo, info);
1287 }
1288
1289 static void
pango_x_font_finalize(GObject * object)1290 pango_x_font_finalize (GObject *object)
1291 {
1292 PangoXFont *xfont = (PangoXFont *)object;
1293 PangoXFontCache *cache = pango_x_font_map_get_font_cache (xfont->fontmap);
1294
1295 int i;
1296
1297 for (i=0; i<xfont->n_subfonts; i++)
1298 {
1299 PangoXSubfontInfo *info = xfont->subfonts[i];
1300
1301 g_free (info->xlfd);
1302
1303 if (info->font_struct)
1304 pango_x_font_cache_unload (cache, info->font_struct);
1305
1306 g_slice_free (PangoXSubfontInfo, info);
1307 }
1308
1309 g_free (xfont->subfonts);
1310
1311 g_hash_table_foreach (xfont->subfonts_by_charset, subfonts_foreach, NULL);
1312 g_hash_table_destroy (xfont->subfonts_by_charset);
1313
1314 g_slist_foreach (xfont->metrics_by_lang, (GFunc)free_metrics_info, NULL);
1315 g_slist_free (xfont->metrics_by_lang);
1316
1317 if (xfont->xface)
1318 pango_x_face_remove (xfont->xface, (PangoFont *)xfont);
1319
1320 g_assert (xfont->fontmap != NULL);
1321 g_object_remove_weak_pointer (G_OBJECT (xfont->fontmap), (gpointer *) (gpointer) &xfont->fontmap);
1322 xfont->fontmap = NULL;
1323
1324 g_strfreev (xfont->fonts);
1325
1326 G_OBJECT_CLASS (parent_class)->finalize (object);
1327 }
1328
1329 static PangoFontDescription *
pango_x_font_describe(PangoFont * font)1330 pango_x_font_describe (PangoFont *font)
1331 {
1332 /* FIXME: this doesn't work for fonts from pango_x_font_load()
1333 */
1334 PangoXFont *xfont = (PangoXFont *)font;
1335
1336 if (xfont->xface)
1337 {
1338 PangoFontDescription *desc = pango_font_face_describe (PANGO_FONT_FACE (xfont->xface));
1339 pango_font_description_set_size (desc, xfont->size);
1340
1341 return desc;
1342 }
1343 else
1344 return NULL;
1345 }
1346
1347 PangoMap *
pango_x_get_shaper_map(PangoLanguage * language)1348 pango_x_get_shaper_map (PangoLanguage *language)
1349 {
1350 static guint engine_type_id = 0;
1351 static guint render_type_id = 0;
1352
1353 if (engine_type_id == 0)
1354 {
1355 engine_type_id = g_quark_from_static_string (PANGO_ENGINE_TYPE_SHAPE);
1356 render_type_id = g_quark_from_static_string (PANGO_RENDER_TYPE_X);
1357 }
1358
1359 return pango_find_map (language, engine_type_id, render_type_id);
1360 }
1361
1362 static PangoCoverage *
pango_x_font_get_coverage(PangoFont * font,PangoLanguage * language)1363 pango_x_font_get_coverage (PangoFont *font,
1364 PangoLanguage *language)
1365 {
1366 PangoXFont *xfont = (PangoXFont *)font;
1367
1368 return pango_x_face_get_coverage (xfont->xface, font, language);
1369 }
1370
1371 static PangoEngineShape *
pango_x_font_find_shaper(PangoFont * font G_GNUC_UNUSED,PangoLanguage * language,guint32 ch)1372 pango_x_font_find_shaper (PangoFont *font G_GNUC_UNUSED,
1373 PangoLanguage *language,
1374 guint32 ch)
1375 {
1376 static PangoEngineShape *shaper;
1377 if (g_once_init_enter (&shaper))
1378 g_once_init_leave (&shaper,
1379 _pango_basic_x_script_engine_create("BasicScriptEngineXCompat"));
1380 return shaper;
1381 }
1382
1383 /* Utility functions */
1384
1385 static XCharStruct *
pango_x_get_per_char(PangoFont * font,PangoXSubfontInfo * subfont,guint16 char_index)1386 pango_x_get_per_char (PangoFont *font,
1387 PangoXSubfontInfo *subfont,
1388 guint16 char_index)
1389 {
1390 XFontStruct *fs;
1391
1392 int index;
1393 int byte1;
1394 int byte2;
1395
1396 fs = pango_x_get_font_struct (font, subfont);
1397 if (!fs)
1398 return NULL;
1399
1400 if (subfont->is_1byte)
1401 {
1402 index = (int)char_index - fs->min_char_or_byte2;
1403 if (index < 0 || index >= subfont->range_byte2)
1404 return NULL;
1405 }
1406 else
1407 {
1408 byte1 = (int)(char_index / 256) - fs->min_byte1;
1409 if (byte1 < 0 || byte1 >= subfont->range_byte1)
1410 return NULL;
1411
1412 byte2 = (int)(char_index % 256) - fs->min_char_or_byte2;
1413 if (byte2 < 0 || byte2 >= subfont->range_byte2)
1414 return NULL;
1415
1416 index = byte1 * subfont->range_byte2 + byte2;
1417 }
1418
1419 if (fs->per_char)
1420 return &fs->per_char[index];
1421 else
1422 return &fs->min_bounds;
1423 }
1424
1425 static gboolean
pango_x_find_glyph(PangoFont * font,PangoGlyph glyph,PangoXSubfontInfo ** subfont_return,XCharStruct ** charstruct_return)1426 pango_x_find_glyph (PangoFont *font,
1427 PangoGlyph glyph,
1428 PangoXSubfontInfo **subfont_return,
1429 XCharStruct **charstruct_return)
1430 {
1431 PangoXSubfontInfo *subfont;
1432 XCharStruct *cs;
1433
1434 guint16 char_index = PANGO_X_GLYPH_INDEX (glyph);
1435 guint16 subfont_index = PANGO_X_GLYPH_SUBFONT (glyph);
1436
1437 subfont = pango_x_find_subfont (font, subfont_index);
1438 if (!subfont)
1439 return FALSE;
1440
1441 cs = pango_x_get_per_char (font, subfont, char_index);
1442
1443 if (cs && (cs->lbearing != cs->rbearing || cs->width != 0))
1444 {
1445 if (subfont_return)
1446 *subfont_return = subfont;
1447
1448 if (charstruct_return)
1449 *charstruct_return = cs;
1450
1451 return TRUE;
1452 }
1453 else
1454 return FALSE;
1455 }
1456
1457 /**
1458 * pango_x_get_unknown_glyph:
1459 * @font: a #PangoFont.
1460 *
1461 * Returns the index of a glyph suitable for drawing unknown characters;
1462 * you should generally use PANGO_GET_UNKNOWN_GLYPH() instead,
1463 * since that may return a glyph that provides a better representation
1464 * of a particular char. (E.g., by showing hex digits, or a glyph
1465 * representative of a certain Unicode range.)
1466 *
1467 * Return value: a glyph index into @font.
1468 **/
1469 PangoGlyph
pango_x_get_unknown_glyph(PangoFont * font G_GNUC_UNUSED)1470 pango_x_get_unknown_glyph (PangoFont *font G_GNUC_UNUSED)
1471 {
1472 return PANGO_GET_UNKNOWN_GLYPH (0);
1473 }
1474
1475 /**
1476 * pango_x_render_layout_line:
1477 * @display: the X display.
1478 * @drawable: the drawable on which to draw.
1479 * @gc: GC to use for uncolored drawing.
1480 * @line: a #PangoLayoutLine.
1481 * @x: the x position of start of string (in pixels).
1482 * @y: the y position of baseline (in pixels).
1483 *
1484 * Renders a #PangoLayoutLine onto an X drawable.
1485 */
1486 void
pango_x_render_layout_line(Display * display,Drawable drawable,GC gc,PangoLayoutLine * line,int x,int y)1487 pango_x_render_layout_line (Display *display,
1488 Drawable drawable,
1489 GC gc,
1490 PangoLayoutLine *line,
1491 int x,
1492 int y)
1493 {
1494 GSList *tmp_list = line->runs;
1495 PangoRectangle overall_rect;
1496 PangoRectangle logical_rect;
1497 PangoRectangle ink_rect;
1498 PangoContext *context = pango_layout_get_context (line->layout);
1499 PangoXContextInfo *info = get_context_info (context);
1500
1501 int x_off = 0;
1502
1503 pango_layout_line_get_extents (line,NULL, &overall_rect);
1504
1505 while (tmp_list)
1506 {
1507 PangoUnderline uline = PANGO_UNDERLINE_NONE;
1508 PangoLayoutRun *run = tmp_list->data;
1509 PangoAttrColor fg_color, bg_color;
1510 gboolean fg_set, bg_set;
1511 GC fg_gc;
1512
1513 tmp_list = tmp_list->next;
1514
1515 pango_x_get_item_properties (run->item, &uline, &fg_color, &fg_set, &bg_color, &bg_set);
1516
1517 if (fg_set && info->get_gc_func)
1518 fg_gc = info->get_gc_func (context, &fg_color.color, gc);
1519 else
1520 fg_gc = gc;
1521
1522 if (uline == PANGO_UNDERLINE_NONE)
1523 pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
1524 NULL, &logical_rect);
1525 else
1526 pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
1527 &ink_rect, &logical_rect);
1528
1529 if (bg_set && info->get_gc_func)
1530 {
1531 GC bg_gc = info->get_gc_func (context, &bg_color.color, gc);
1532
1533 XFillRectangle (display, drawable, bg_gc,
1534 x + (x_off + logical_rect.x) / PANGO_SCALE,
1535 y + overall_rect.y / PANGO_SCALE,
1536 logical_rect.width / PANGO_SCALE,
1537 overall_rect.height / PANGO_SCALE);
1538
1539 if (info->free_gc_func)
1540 info->free_gc_func (context, bg_gc);
1541 }
1542
1543 pango_x_render (display, drawable, fg_gc, run->item->analysis.font, run->glyphs,
1544 x + x_off / PANGO_SCALE, y);
1545
1546 switch (uline)
1547 {
1548 case PANGO_UNDERLINE_NONE:
1549 break;
1550 case PANGO_UNDERLINE_DOUBLE:
1551 XDrawLine (display, drawable, fg_gc,
1552 x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 4,
1553 x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 4);
1554 /* Fall through */
1555 case PANGO_UNDERLINE_SINGLE:
1556 XDrawLine (display, drawable, fg_gc,
1557 x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 2,
1558 x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 2);
1559 break;
1560 case PANGO_UNDERLINE_ERROR:
1561 {
1562 int point_x;
1563 int counter = 0;
1564 int end_x = x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE;
1565
1566 for (point_x = x + PANGO_PIXELS (x_off + ink_rect.x) - 1;
1567 point_x <= end_x;
1568 point_x += 2)
1569 {
1570 if (counter)
1571 XDrawLine (display, drawable, gc,
1572 point_x, y + 2, MIN (point_x + 1, end_x), y + 2);
1573 else
1574 XDrawLine (display, drawable, gc,
1575 point_x, y + 3, MIN (point_x + 1, end_x), y + 3);
1576
1577 counter = (counter + 1) % 2;
1578 }
1579 }
1580 break;
1581 case PANGO_UNDERLINE_LOW:
1582 XDrawLine (display, drawable, fg_gc,
1583 x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 2,
1584 x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 2);
1585 break;
1586 }
1587
1588 if (fg_set && info->get_gc_func && info->free_gc_func)
1589 info->free_gc_func (context, fg_gc);
1590
1591 x_off += logical_rect.width;
1592 }
1593 }
1594
1595 /**
1596 * pango_x_render_layout:
1597 * @display: the X display.
1598 * @drawable: the drawable on which to draw.
1599 * @gc: GC to use for uncolored drawing.
1600 * @layout: a #PangoLayout.
1601 * @x: the x position of the left of the layout (in pixels).
1602 * @y: the y position of the top of the layout (in pixels).
1603 *
1604 * Renders a #PangoLayout onto an X drawable.
1605 */
1606 void
pango_x_render_layout(Display * display,Drawable drawable,GC gc,PangoLayout * layout,int x,int y)1607 pango_x_render_layout (Display *display,
1608 Drawable drawable,
1609 GC gc,
1610 PangoLayout *layout,
1611 int x,
1612 int y)
1613 {
1614 PangoLayoutIter *iter;
1615
1616 g_return_if_fail (display != NULL);
1617 g_return_if_fail (PANGO_IS_LAYOUT (layout));
1618
1619 iter = pango_layout_get_iter (layout);
1620
1621 do
1622 {
1623 PangoRectangle logical_rect;
1624 PangoLayoutLine *line;
1625 int baseline;
1626
1627 line = pango_layout_iter_get_line_readonly (iter);
1628
1629 pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
1630 baseline = pango_layout_iter_get_baseline (iter);
1631
1632 pango_x_render_layout_line (display, drawable, gc,
1633 line,
1634 x + PANGO_PIXELS (logical_rect.x),
1635 y + PANGO_PIXELS (baseline));
1636 }
1637 while (pango_layout_iter_next_line (iter));
1638
1639 pango_layout_iter_free (iter);
1640 }
1641
1642 /* This utility function is duplicated here and in pango-layout.c; should it be
1643 * public? Trouble is - what is the appropriate set of properties?
1644 */
1645 static void
pango_x_get_item_properties(PangoItem * item,PangoUnderline * uline,PangoAttrColor * fg_color,gboolean * fg_set,PangoAttrColor * bg_color,gboolean * bg_set)1646 pango_x_get_item_properties (PangoItem *item,
1647 PangoUnderline *uline,
1648 PangoAttrColor *fg_color,
1649 gboolean *fg_set,
1650 PangoAttrColor *bg_color,
1651 gboolean *bg_set)
1652 {
1653 GSList *tmp_list = item->analysis.extra_attrs;
1654
1655 if (fg_set)
1656 *fg_set = FALSE;
1657
1658 if (bg_set)
1659 *bg_set = FALSE;
1660
1661 while (tmp_list)
1662 {
1663 PangoAttribute *attr = tmp_list->data;
1664
1665 switch ((int) attr->klass->type)
1666 {
1667 case PANGO_ATTR_UNDERLINE:
1668 if (uline)
1669 *uline = ((PangoAttrInt *)attr)->value;
1670 break;
1671
1672 case PANGO_ATTR_FOREGROUND:
1673 if (fg_color)
1674 *fg_color = *((PangoAttrColor *)attr);
1675 if (fg_set)
1676 *fg_set = TRUE;
1677
1678 break;
1679
1680 case PANGO_ATTR_BACKGROUND:
1681 if (bg_color)
1682 *bg_color = *((PangoAttrColor *)attr);
1683 if (bg_set)
1684 *bg_set = TRUE;
1685
1686 break;
1687
1688 default:
1689 break;
1690 }
1691 tmp_list = tmp_list->next;
1692 }
1693 }
1694
1695 /**
1696 * pango_x_apply_ligatures:
1697 * @font: unused
1698 * @subfont: unused
1699 * @glyphs: unused
1700 * @n_glyphs: unused
1701 * @clusters: unused
1702 *
1703 * Previously did subfont-specific ligation. Now a no-op.
1704 *
1705 * Return value: %FALSE, always.
1706 */
1707 gboolean
pango_x_apply_ligatures(PangoFont * font G_GNUC_UNUSED,PangoXSubfont subfont_id G_GNUC_UNUSED,gunichar ** glyphs G_GNUC_UNUSED,int * n_glyphs G_GNUC_UNUSED,int ** clusters G_GNUC_UNUSED)1708 pango_x_apply_ligatures (PangoFont *font G_GNUC_UNUSED,
1709 PangoXSubfont subfont_id G_GNUC_UNUSED,
1710 gunichar **glyphs G_GNUC_UNUSED,
1711 int *n_glyphs G_GNUC_UNUSED,
1712 int **clusters G_GNUC_UNUSED)
1713 {
1714 return FALSE;
1715 }
1716
1717 /**
1718 * pango_x_find_first_subfont:
1719 * @font: A #PangoFont.
1720 * @rfont: A pointer to a #PangoXSubfont.
1721 * @charsets: An array of charsets.
1722 * @n_charsets: The number of charsets in @charsets.
1723 *
1724 * Looks for subfonts with the @charset charset,
1725 * in @font, and puts the first one in *@rfont.
1726 *
1727 * Return value: %TRUE if *@rfont now contains a font.
1728 */
1729 gboolean
pango_x_find_first_subfont(PangoFont * font,char ** charsets,int n_charsets,PangoXSubfont * rfont)1730 pango_x_find_first_subfont (PangoFont *font,
1731 char **charsets,
1732 int n_charsets,
1733 PangoXSubfont *rfont)
1734 {
1735 int n_subfonts;
1736 gboolean result = FALSE;
1737 PangoXSubfont *subfonts;
1738 int *subfont_charsets;
1739
1740 g_return_val_if_fail (font, 0);
1741 g_return_val_if_fail (charsets, 0);
1742 g_return_val_if_fail (rfont, 0);
1743
1744 n_subfonts = pango_x_list_subfonts (font, charsets, n_charsets,
1745 &subfonts, &subfont_charsets);
1746
1747 if (n_subfonts > 0)
1748 {
1749 *rfont = subfonts[0];
1750 result = TRUE;
1751 }
1752
1753 g_free (subfonts);
1754 g_free (subfont_charsets);
1755 return result;
1756 }
1757
1758 /**
1759 * pango_x_fallback_shape:
1760 * @font: A #PangoFont.
1761 * @glyphs: A pointer to a #PangoGlyphString.
1762 * @text: UTF-8 string.
1763 * @n_chars: Number of UTF-8 seqs in @text.
1764 *
1765 * This is a simple fallback shaper, that can be used
1766 * if no subfont that supports a given script is found.
1767 * For every character in @text, it puts the unknown glyph.
1768 */
1769 void
pango_x_fallback_shape(PangoFont * font,PangoGlyphString * glyphs,const char * text,int n_chars)1770 pango_x_fallback_shape (PangoFont *font,
1771 PangoGlyphString *glyphs,
1772 const char *text,
1773 int n_chars)
1774 {
1775 PangoGlyph unknown_glyph = pango_x_get_unknown_glyph (font);
1776 PangoRectangle logical_rect;
1777 const char *p;
1778 int i;
1779
1780 g_return_if_fail (font);
1781 g_return_if_fail (glyphs);
1782 g_return_if_fail (text);
1783 g_return_if_fail (n_chars >= 0);
1784
1785 pango_font_get_glyph_extents (font, unknown_glyph, NULL, &logical_rect);
1786 pango_glyph_string_set_size (glyphs, n_chars);
1787 p = text;
1788 for (i = 0; i < n_chars; i++)
1789 {
1790 glyphs->glyphs[i].glyph = unknown_glyph;
1791
1792 glyphs->glyphs[i].geometry.x_offset = 0;
1793 glyphs->glyphs[i].geometry.y_offset = 0;
1794 glyphs->glyphs[i].geometry.width = logical_rect.width;
1795
1796 glyphs->log_clusters[i] = p - text;
1797
1798 p = g_utf8_next_char (p);
1799 }
1800 }
1801
1802 /**
1803 * pango_x_font_get_unknown_glyph:
1804 * @font: a #PangoFont.
1805 * @wc: the Unicode character for which a glyph is needed.
1806 *
1807 * Returns the index of a glyph suitable for drawing @wc as an
1808 * unknown character.
1809 *
1810 * Use PANGO_GET_UNKNOWN_GLYPH() instead.
1811 *
1812 * Return value: a glyph index into @font.
1813 */
1814 PangoGlyph
pango_x_font_get_unknown_glyph(PangoFont * font G_GNUC_UNUSED,gunichar wc)1815 pango_x_font_get_unknown_glyph (PangoFont *font G_GNUC_UNUSED,
1816 gunichar wc)
1817 {
1818 return PANGO_GET_UNKNOWN_GLYPH (wc);
1819 }
1820