1 /* Pango
2  * pangocairo-render.c: Rendering routines to Cairo surfaces
3  *
4  * Copyright (C) 2004 Red Hat, Inc.
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 "config.h"
23 
24 #include <math.h>
25 
26 #include "pango-font-private.h"
27 #include "pangocairo-private.h"
28 #include "pango-glyph-item.h"
29 #include "pango-impl-utils.h"
30 
31 typedef struct _PangoCairoRendererClass PangoCairoRendererClass;
32 
33 #define PANGO_CAIRO_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_CAIRO_RENDERER, PangoCairoRendererClass))
34 #define PANGO_IS_CAIRO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_CAIRO_RENDERER))
35 #define PANGO_CAIRO_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_CAIRO_RENDERER, PangoCairoRendererClass))
36 
37 struct _PangoCairoRenderer
38 {
39   PangoRenderer parent_instance;
40 
41   cairo_t *cr;
42   gboolean do_path;
43   gboolean has_show_text_glyphs;
44   double x_offset, y_offset;
45 
46   /* house-keeping options */
47   gboolean is_cached_renderer;
48   gboolean cr_had_current_point;
49 };
50 
51 struct _PangoCairoRendererClass
52 {
53   PangoRendererClass parent_class;
54 };
55 
G_DEFINE_TYPE(PangoCairoRenderer,pango_cairo_renderer,PANGO_TYPE_RENDERER)56 G_DEFINE_TYPE (PangoCairoRenderer, pango_cairo_renderer, PANGO_TYPE_RENDERER)
57 
58 static void
59 set_color (PangoCairoRenderer *crenderer,
60 	   PangoRenderPart     part)
61 {
62   PangoColor *color = pango_renderer_get_color ((PangoRenderer *) (crenderer), part);
63   guint16 a = pango_renderer_get_alpha ((PangoRenderer *) (crenderer), part);
64   gdouble red, green, blue, alpha;
65 
66   if (!a && !color)
67     return;
68 
69   if (color)
70     {
71       red = color->red / 65535.;
72       green = color->green / 65535.;
73       blue = color->blue / 65535.;
74       alpha = 1.;
75     }
76   else
77     {
78       cairo_pattern_t *pattern = cairo_get_source (crenderer->cr);
79 
80       if (pattern && cairo_pattern_get_type (pattern) == CAIRO_PATTERN_TYPE_SOLID)
81         cairo_pattern_get_rgba (pattern, &red, &green, &blue, &alpha);
82       else
83         {
84           red = 0.;
85           green = 0.;
86           blue = 0.;
87           alpha = 1.;
88         }
89     }
90 
91   if (a)
92     alpha = a / 65535.;
93 
94   cairo_set_source_rgba (crenderer->cr, red, green, blue, alpha);
95 }
96 
97 /* note: modifies crenderer->cr without doing cairo_save/restore() */
98 static void
_pango_cairo_renderer_draw_frame(PangoCairoRenderer * crenderer,double x,double y,double width,double height,double line_width,gboolean invalid)99 _pango_cairo_renderer_draw_frame (PangoCairoRenderer *crenderer,
100 				  double              x,
101 				  double              y,
102 				  double              width,
103 				  double              height,
104 				  double              line_width,
105 				  gboolean            invalid)
106 {
107   cairo_t *cr = crenderer->cr;
108 
109   if (crenderer->do_path)
110     {
111       double d2 = line_width * .5, d = line_width;
112 
113       /* we draw an outer box in one winding direction and an inner one in the
114        * opposite direction.  This works for both cairo windings rules.
115        *
116        * what we really want is cairo_stroke_to_path(), but that's not
117        * implemented in cairo yet.
118        */
119 
120       /* outer */
121       cairo_rectangle (cr, x-d2, y-d2, width+d, height+d);
122 
123       /* inner */
124       if (invalid)
125         {
126 	  /* delicacies of computing the joint... this is REALLY slow */
127 
128 	  double alpha, tan_alpha2, cos_alpha;
129 	  double sx, sy;
130 
131 	  alpha = atan2 (height, width);
132 
133 	  tan_alpha2 = tan (alpha * .5);
134 	  if (tan_alpha2 < 1e-5 || (sx = d2 / tan_alpha2, 2. * sx > width - d))
135 	    sx = (width - d) * .5;
136 
137 	  cos_alpha = cos (alpha);
138 	  if (cos_alpha < 1e-5 || (sy = d2 / cos_alpha, 2. * sy > height - d))
139 	    sy = (height - d) * .5;
140 
141 	  /* top triangle */
142 	  cairo_new_sub_path (cr);
143 	  cairo_line_to (cr, x+width-sx, y+d2);
144 	  cairo_line_to (cr, x+sx, y+d2);
145 	  cairo_line_to (cr, x+.5*width, y+.5*height-sy);
146 	  cairo_close_path (cr);
147 
148 	  /* bottom triangle */
149 	  cairo_new_sub_path (cr);
150 	  cairo_line_to (cr, x+width-sx, y+height-d2);
151 	  cairo_line_to (cr, x+.5*width, y+.5*height+sy);
152 	  cairo_line_to (cr, x+sx, y+height-d2);
153 	  cairo_close_path (cr);
154 
155 
156 	  alpha = G_PI_2 - alpha;
157 	  tan_alpha2 = tan (alpha * .5);
158 	  if (tan_alpha2 < 1e-5 || (sy = d2 / tan_alpha2, 2. * sy > height - d))
159 	    sy = (height - d) * .5;
160 
161 	  cos_alpha = cos (alpha);
162 	  if (cos_alpha < 1e-5 || (sx = d2 / cos_alpha, 2. * sx > width - d))
163 	    sx = (width - d) * .5;
164 
165 	  /* left triangle */
166 	  cairo_new_sub_path (cr);
167 	  cairo_line_to (cr, x+d2, y+sy);
168 	  cairo_line_to (cr, x+d2, y+height-sy);
169 	  cairo_line_to (cr, x+.5*width-sx, y+.5*height);
170 	  cairo_close_path (cr);
171 
172 	  /* right triangle */
173 	  cairo_new_sub_path (cr);
174 	  cairo_line_to (cr, x+width-d2, y+sy);
175 	  cairo_line_to (cr, x+.5*width+sx, y+.5*height);
176 	  cairo_line_to (cr, x+width-d2, y+height-sy);
177 	  cairo_close_path (cr);
178 	}
179       else
180 	cairo_rectangle (cr, x+width-d2, y+d2, - (width-d), height-d);
181     }
182   else
183     {
184       cairo_rectangle (cr, x, y, width, height);
185 
186       if (invalid)
187         {
188 	  /* draw an X */
189 
190 	  cairo_new_sub_path (cr);
191 	  cairo_move_to (cr, x, y);
192 	  cairo_rel_line_to (cr, width, height);
193 
194 	  cairo_new_sub_path (cr);
195 	  cairo_move_to (cr, x + width, y);
196 	  cairo_rel_line_to (cr, -width, height);
197 
198 	  cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
199 	}
200 
201       cairo_set_line_width (cr, line_width);
202       cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
203       cairo_set_miter_limit (cr, 2.);
204       cairo_stroke (cr);
205     }
206 }
207 
208 static void
_pango_cairo_renderer_draw_box_glyph(PangoCairoRenderer * crenderer,PangoGlyphInfo * gi,double cx,double cy,gboolean invalid)209 _pango_cairo_renderer_draw_box_glyph (PangoCairoRenderer *crenderer,
210 				      PangoGlyphInfo     *gi,
211 				      double              cx,
212 				      double              cy,
213 				      gboolean            invalid)
214 {
215   cairo_save (crenderer->cr);
216 
217   _pango_cairo_renderer_draw_frame (crenderer,
218 				    cx + 1.5,
219 				    cy + 1.5 - PANGO_UNKNOWN_GLYPH_HEIGHT,
220 				    (double)gi->geometry.width / PANGO_SCALE - 3.0,
221 				    PANGO_UNKNOWN_GLYPH_HEIGHT - 3.0,
222 				    1.0,
223 				    invalid);
224 
225   cairo_restore (crenderer->cr);
226 }
227 
228 static void
_pango_cairo_renderer_draw_unknown_glyph(PangoCairoRenderer * crenderer,PangoFont * font,PangoGlyphInfo * gi,double cx,double cy)229 _pango_cairo_renderer_draw_unknown_glyph (PangoCairoRenderer *crenderer,
230 					  PangoFont          *font,
231 					  PangoGlyphInfo     *gi,
232 					  double              cx,
233 					  double              cy)
234 {
235   char buf[7];
236   double x0, y0;
237   int row, col;
238   int rows, cols;
239   double width, lsb;
240   char hexbox_string[2] = { 0, 0 };
241   PangoCairoFontHexBoxInfo *hbi;
242   gunichar ch;
243   gboolean invalid_input;
244   const char *p;
245   const char *name;
246 
247   cairo_save (crenderer->cr);
248 
249   ch = gi->glyph & ~PANGO_GLYPH_UNKNOWN_FLAG;
250   invalid_input = G_UNLIKELY (gi->glyph == PANGO_GLYPH_INVALID_INPUT || ch > 0x10FFFF);
251 
252   hbi = _pango_cairo_font_get_hex_box_info ((PangoCairoFont *)font);
253   if (!hbi || !_pango_cairo_font_install ((PangoFont *)(hbi->font), crenderer->cr))
254     {
255       _pango_cairo_renderer_draw_box_glyph (crenderer, gi, cx, cy, invalid_input);
256       goto done;
257     }
258 
259   if (G_UNLIKELY (invalid_input))
260     {
261       rows = hbi->rows;
262       cols = 1;
263     }
264   else if (ch == 0x2423 ||
265            g_unichar_type (ch) == G_UNICODE_SPACE_SEPARATOR)
266     {
267       /* We never want to show a hex box or other drawing for
268        * space. If we want space to be visible, we replace 0x20
269        * by 0x2423 (visible space).
270        *
271        * Since we don't want to rely on glyph availability,
272        * we render a centered dot ourselves.
273        */
274       double x = cx + 0.5 *((double)gi->geometry.width / PANGO_SCALE);
275       double y = cy + hbi->box_descent - 0.5 * hbi->box_height;
276 
277       cairo_new_sub_path (crenderer->cr);
278       cairo_arc (crenderer->cr, x, y, 1.5 * hbi->line_width, 0, 2 * G_PI);
279       cairo_close_path (crenderer->cr);
280       cairo_fill (crenderer->cr);
281       goto done;
282     }
283   else if (ch == '\t')
284     {
285       /* Since we don't want to rely on glyph availability,
286        * we render an arrow like ↦ ourselves.
287        */
288       double y = cy + hbi->box_descent - 0.5 * hbi->box_height;
289       double width = (double)gi->geometry.width / PANGO_SCALE;
290       double offset = 0.2 * width;
291       double x = cx + offset;
292       double al = width - 2 * offset; /* arrow length */
293       double tl = MIN (hbi->digit_width, 0.75 * al); /* tip length */
294       double tw2 = 2.5 * hbi->line_width; /* tip width / 2 */
295       double lw2 = 0.5 * hbi->line_width; /* line width / 2 */
296 
297       cairo_move_to (crenderer->cr, x - lw2, y - tw2);
298       cairo_line_to (crenderer->cr, x + lw2, y - tw2);
299       cairo_line_to (crenderer->cr, x + lw2, y - lw2);
300       cairo_line_to (crenderer->cr, x + al - tl, y - lw2);
301       cairo_line_to (crenderer->cr, x + al - tl, y - tw2);
302       cairo_line_to (crenderer->cr, x + al,  y);
303       cairo_line_to (crenderer->cr, x + al - tl, y + tw2);
304       cairo_line_to (crenderer->cr, x + al - tl, y + lw2);
305       cairo_line_to (crenderer->cr, x + lw2, y + lw2);
306       cairo_line_to (crenderer->cr, x + lw2, y + tw2);
307       cairo_line_to (crenderer->cr, x - lw2, y + tw2);
308       cairo_close_path (crenderer->cr);
309       cairo_fill (crenderer->cr);
310       goto done;
311     }
312   else if (ch == '\n' || ch == 0x2028 || ch == 0x2029)
313     {
314       /* Since we don't want to rely on glyph availability,
315        * we render an arrow like ↵ ourselves.
316        */
317       double width = (double)gi->geometry.width / PANGO_SCALE;
318       double offset = 0.2 * width;
319       double al = width - 2 * offset; /* arrow length */
320       double tl = MIN (hbi->digit_width, 0.75 * al); /* tip length */
321       double ah = al - 0.5 * tl; /* arrow height */
322       double tw2 = 2.5 * hbi->line_width; /* tip width / 2 */
323       double x = cx + offset;
324       double y = cy - (hbi->box_height - al) / 2;
325       double lw2 = 0.5 * hbi->line_width; /* line width / 2 */
326 
327       cairo_move_to (crenderer->cr, x, y);
328       cairo_line_to (crenderer->cr, x + tl, y - tw2);
329       cairo_line_to (crenderer->cr, x + tl, y - lw2);
330       cairo_line_to (crenderer->cr, x + al - lw2, y - lw2);
331       cairo_line_to (crenderer->cr, x + al - lw2, y - ah);
332       cairo_line_to (crenderer->cr, x + al + lw2, y - ah);
333       cairo_line_to (crenderer->cr, x + al + lw2, y + lw2);
334       cairo_line_to (crenderer->cr, x + tl, y + lw2);
335       cairo_line_to (crenderer->cr, x + tl, y + tw2);
336       cairo_close_path (crenderer->cr);
337       cairo_fill (crenderer->cr);
338       goto done;
339     }
340   else if ((name = pango_get_ignorable_size (ch, &rows, &cols)))
341     {
342       /* Nothing else to do, we render 'default ignorable' chars
343        * as hex box with their nick.
344        */
345     }
346   else
347     {
348       /* Everything else gets a traditional hex box. */
349       rows = hbi->rows;
350       cols = (ch > 0xffff ? 6 : 4) / rows;
351       g_snprintf (buf, sizeof(buf), (ch > 0xffff) ? "%06X" : "%04X", ch);
352       name = buf;
353     }
354 
355   width = (3 * hbi->pad_x + cols * (hbi->digit_width + hbi->pad_x));
356   lsb = ((double)gi->geometry.width / PANGO_SCALE - width) * .5;
357   lsb = floor (lsb / hbi->pad_x) * hbi->pad_x;
358 
359   _pango_cairo_renderer_draw_frame (crenderer,
360 				    cx + lsb + .5 * hbi->pad_x,
361 				    cy + hbi->box_descent - hbi->box_height + hbi->pad_y * 0.5,
362 				    width - hbi->pad_x,
363 				    (hbi->box_height - hbi->pad_y),
364 				    hbi->line_width,
365 				    invalid_input);
366 
367   if (invalid_input)
368     goto done;
369 
370   x0 = cx + lsb + hbi->pad_x * 2;
371   y0 = cy + hbi->box_descent - hbi->pad_y * 2 - ((hbi->rows - rows) * hbi->digit_height / 2);
372 
373   for (row = 0, p = name; row < rows; row++)
374     {
375       double y = y0 - (rows - 1 - row) * (hbi->digit_height + hbi->pad_y);
376       for (col = 0; col < cols; col++, p++)
377 	{
378 	  double x = x0 + col * (hbi->digit_width + hbi->pad_x);
379 
380           if (!p)
381             goto done;
382 
383 	  cairo_move_to (crenderer->cr, x, y);
384 
385           hexbox_string[0] = p[0];
386 
387 	  if (crenderer->do_path)
388 	      cairo_text_path (crenderer->cr, hexbox_string);
389 	  else
390 	      cairo_show_text (crenderer->cr, hexbox_string);
391 	}
392     }
393 
394 done:
395   cairo_restore (crenderer->cr);
396 }
397 
398 #ifndef STACK_BUFFER_SIZE
399 #define STACK_BUFFER_SIZE (512 * sizeof (int))
400 #endif
401 
402 #define STACK_ARRAY_LENGTH(T) (STACK_BUFFER_SIZE / sizeof(T))
403 
404 static void
pango_cairo_renderer_show_text_glyphs(PangoRenderer * renderer,const char * text,int text_len,PangoGlyphString * glyphs,cairo_text_cluster_t * clusters,int num_clusters,gboolean backward,PangoFont * font,int x,int y)405 pango_cairo_renderer_show_text_glyphs (PangoRenderer        *renderer,
406 				       const char           *text,
407 				       int                   text_len,
408 				       PangoGlyphString     *glyphs,
409 				       cairo_text_cluster_t *clusters,
410 				       int                   num_clusters,
411 				       gboolean              backward,
412 				       PangoFont            *font,
413 				       int                   x,
414 				       int                   y)
415 {
416   PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
417 
418   int i, count;
419   int x_position = 0;
420   cairo_glyph_t *cairo_glyphs;
421   cairo_glyph_t stack_glyphs[STACK_ARRAY_LENGTH (cairo_glyph_t)];
422   double base_x = crenderer->x_offset + (double)x / PANGO_SCALE;
423   double base_y = crenderer->y_offset + (double)y / PANGO_SCALE;
424 
425   cairo_save (crenderer->cr);
426   if (!crenderer->do_path)
427     set_color (crenderer, PANGO_RENDER_PART_FOREGROUND);
428 
429   if (!_pango_cairo_font_install (font, crenderer->cr))
430     {
431       for (i = 0; i < glyphs->num_glyphs; i++)
432 	{
433 	  PangoGlyphInfo *gi = &glyphs->glyphs[i];
434 
435 	  if (gi->glyph != PANGO_GLYPH_EMPTY)
436 	    {
437 	      double cx = base_x + (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
438 	      double cy = gi->geometry.y_offset == 0 ?
439 			  base_y :
440 			  base_y + (double)(gi->geometry.y_offset) / PANGO_SCALE;
441 
442 	      _pango_cairo_renderer_draw_unknown_glyph (crenderer, font, gi, cx, cy);
443 	    }
444 	  x_position += gi->geometry.width;
445 	}
446 
447       goto done;
448     }
449 
450   if (glyphs->num_glyphs > (int) G_N_ELEMENTS (stack_glyphs))
451     cairo_glyphs = g_new (cairo_glyph_t, glyphs->num_glyphs);
452   else
453     cairo_glyphs = stack_glyphs;
454 
455   count = 0;
456   for (i = 0; i < glyphs->num_glyphs; i++)
457     {
458       PangoGlyphInfo *gi = &glyphs->glyphs[i];
459 
460       if (gi->glyph != PANGO_GLYPH_EMPTY)
461 	{
462 	  double cx = base_x + (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
463 	  double cy = gi->geometry.y_offset == 0 ?
464 		      base_y :
465 		      base_y + (double)(gi->geometry.y_offset) / PANGO_SCALE;
466 
467 	  if (gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG)
468             {
469               if (gi->glyph == (0x20 | PANGO_GLYPH_UNKNOWN_FLAG))
470                 ; /* no hex boxes for space, please */
471               else
472 	        _pango_cairo_renderer_draw_unknown_glyph (crenderer, font, gi, cx, cy);
473             }
474 	  else
475 	    {
476 	      cairo_glyphs[count].index = gi->glyph;
477 	      cairo_glyphs[count].x = cx;
478 	      cairo_glyphs[count].y = cy;
479 	      count++;
480 	    }
481 	}
482       x_position += gi->geometry.width;
483     }
484 
485   if (G_UNLIKELY (crenderer->do_path))
486     cairo_glyph_path (crenderer->cr, cairo_glyphs, count);
487   else
488     if (G_UNLIKELY (clusters))
489       cairo_show_text_glyphs (crenderer->cr,
490 			      text, text_len,
491 			      cairo_glyphs, count,
492 			      clusters, num_clusters,
493 			      backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : 0);
494     else
495       cairo_show_glyphs (crenderer->cr, cairo_glyphs, count);
496 
497   if (cairo_glyphs != stack_glyphs)
498     g_free (cairo_glyphs);
499 
500 done:
501   cairo_restore (crenderer->cr);
502 }
503 
504 static void
pango_cairo_renderer_draw_glyphs(PangoRenderer * renderer,PangoFont * font,PangoGlyphString * glyphs,int x,int y)505 pango_cairo_renderer_draw_glyphs (PangoRenderer     *renderer,
506 				  PangoFont         *font,
507 				  PangoGlyphString  *glyphs,
508 				  int                x,
509 				  int                y)
510 {
511   pango_cairo_renderer_show_text_glyphs (renderer,
512 					 NULL, 0,
513 					 glyphs,
514 					 NULL, 0,
515 					 FALSE,
516 					 font,
517 					 x, y);
518 }
519 
520 static void
pango_cairo_renderer_draw_glyph_item(PangoRenderer * renderer,const char * text,PangoGlyphItem * glyph_item,int x,int y)521 pango_cairo_renderer_draw_glyph_item (PangoRenderer     *renderer,
522 				      const char        *text,
523 				      PangoGlyphItem    *glyph_item,
524 				      int                x,
525 				      int                y)
526 {
527   PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
528   PangoFont          *font      = glyph_item->item->analysis.font;
529   PangoGlyphString   *glyphs    = glyph_item->glyphs;
530   PangoItem          *item      = glyph_item->item;
531   gboolean            backward  = (item->analysis.level & 1) != 0;
532 
533   PangoGlyphItemIter   iter;
534   cairo_text_cluster_t *cairo_clusters;
535   cairo_text_cluster_t stack_clusters[STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
536   int num_clusters;
537 
538   if (!crenderer->has_show_text_glyphs || crenderer->do_path)
539     {
540       pango_cairo_renderer_show_text_glyphs (renderer,
541 					     NULL, 0,
542 					     glyphs,
543 					     NULL, 0,
544 					     FALSE,
545 					     font,
546 					     x, y);
547       return;
548     }
549 
550   if (glyphs->num_glyphs > (int) G_N_ELEMENTS (stack_clusters))
551     cairo_clusters = g_new (cairo_text_cluster_t, glyphs->num_glyphs);
552   else
553     cairo_clusters = stack_clusters;
554 
555   num_clusters = 0;
556   if (pango_glyph_item_iter_init_start (&iter, glyph_item, text))
557     {
558       do {
559         int num_bytes, num_glyphs, i;
560 
561         num_bytes  = iter.end_index - iter.start_index;
562         num_glyphs = backward ? iter.start_glyph - iter.end_glyph : iter.end_glyph - iter.start_glyph;
563 
564 	if (num_bytes < 1)
565 	  g_warning ("pango_cairo_renderer_draw_glyph_item: bad cluster has num_bytes %d", num_bytes);
566 	if (num_glyphs < 1)
567 	  g_warning ("pango_cairo_renderer_draw_glyph_item: bad cluster has num_glyphs %d", num_glyphs);
568 
569 	/* Discount empty and unknown glyphs */
570 	for (i = MIN (iter.start_glyph, iter.end_glyph+1);
571 	     i < MAX (iter.start_glyph+1, iter.end_glyph);
572 	     i++)
573 	  {
574 	    PangoGlyphInfo *gi = &glyphs->glyphs[i];
575 
576 	    if (gi->glyph == PANGO_GLYPH_EMPTY ||
577 		gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG)
578 	      num_glyphs--;
579 	  }
580 
581         cairo_clusters[num_clusters].num_bytes  = num_bytes;
582         cairo_clusters[num_clusters].num_glyphs = num_glyphs;
583         num_clusters++;
584       } while (pango_glyph_item_iter_next_cluster (&iter));
585     }
586 
587   pango_cairo_renderer_show_text_glyphs (renderer,
588 					 text + item->offset, item->length,
589 					 glyphs,
590 					 cairo_clusters, num_clusters,
591 					 backward,
592 					 font,
593 					 x, y);
594 
595   if (cairo_clusters != stack_clusters)
596     g_free (cairo_clusters);
597 }
598 
599 static void
pango_cairo_renderer_draw_rectangle(PangoRenderer * renderer,PangoRenderPart part,int x,int y,int width,int height)600 pango_cairo_renderer_draw_rectangle (PangoRenderer     *renderer,
601 				     PangoRenderPart    part,
602 				     int                x,
603 				     int                y,
604 				     int                width,
605 				     int                height)
606 {
607   PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
608 
609   if (!crenderer->do_path)
610     {
611       cairo_save (crenderer->cr);
612 
613       set_color (crenderer, part);
614     }
615 
616   cairo_rectangle (crenderer->cr,
617 		   crenderer->x_offset + (double)x / PANGO_SCALE,
618 		   crenderer->y_offset + (double)y / PANGO_SCALE,
619 		   (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
620 
621   if (!crenderer->do_path)
622     {
623       cairo_fill (crenderer->cr);
624 
625       cairo_restore (crenderer->cr);
626     }
627 }
628 
629 static void
pango_cairo_renderer_draw_trapezoid(PangoRenderer * renderer,PangoRenderPart part,double y1_,double x11,double x21,double y2,double x12,double x22)630 pango_cairo_renderer_draw_trapezoid (PangoRenderer     *renderer,
631 				     PangoRenderPart    part,
632 				     double             y1_,
633 				     double             x11,
634 				     double             x21,
635 				     double             y2,
636 				     double             x12,
637 				     double             x22)
638 {
639   PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
640   cairo_t *cr;
641   double x, y;
642 
643   cr = crenderer->cr;
644 
645   cairo_save (cr);
646 
647   if (!crenderer->do_path)
648     set_color (crenderer, part);
649 
650   x = crenderer->x_offset,
651   y = crenderer->y_offset;
652   cairo_user_to_device_distance (cr, &x, &y);
653   cairo_identity_matrix (cr);
654   cairo_translate (cr, x, y);
655 
656   cairo_move_to (cr, x11, y1_);
657   cairo_line_to (cr, x21, y1_);
658   cairo_line_to (cr, x22, y2);
659   cairo_line_to (cr, x12, y2);
660   cairo_close_path (cr);
661 
662   if (!crenderer->do_path)
663     cairo_fill (cr);
664 
665   cairo_restore (cr);
666 }
667 
668 /* Draws an error underline that looks like one of:
669  *              H       E                H
670  *     /\      /\      /\        /\      /\               -
671  *   A/  \    /  \    /  \     A/  \    /  \              |
672  *    \   \  /    \  /   /D     \   \  /    \             |
673  *     \   \/  C   \/   /        \   \/   C  \            | height = HEIGHT_SQUARES * square
674  *      \      /\  F   /          \  F   /\   \           |
675  *       \    /  \    /            \    /  \   \G         |
676  *        \  /    \  /              \  /    \  /          |
677  *         \/      \/                \/      \/           -
678  *         B                         B
679  *         |---|
680  *       unit_width = (HEIGHT_SQUARES - 1) * square
681  *
682  * The x, y, width, height passed in give the desired bounding box;
683  * x/width are adjusted to make the underline a integer number of units
684  * wide.
685  */
686 #define HEIGHT_SQUARES 2.5
687 
688 static void
draw_error_underline(cairo_t * cr,double x,double y,double width,double height)689 draw_error_underline (cairo_t *cr,
690 		      double   x,
691 		      double   y,
692 		      double   width,
693 		      double   height)
694 {
695   double square = height / HEIGHT_SQUARES;
696   double unit_width = (HEIGHT_SQUARES - 1) * square;
697   double double_width = 2 * unit_width;
698   int width_units = (width + unit_width / 2) / unit_width;
699   double y_top, y_bottom;
700   double x_left, x_middle, x_right;
701   int i;
702 
703   x += (width - width_units * unit_width) / 2;
704 
705   y_top = y;
706   y_bottom = y + height;
707 
708   /* Bottom of squiggle */
709   x_middle = x + unit_width;
710   x_right  = x + double_width;
711   cairo_move_to (cr, x - square / 2, y_top + square / 2); /* A */
712   for (i = 0; i < width_units-2; i += 2)
713     {
714       cairo_line_to (cr, x_middle, y_bottom); /* B */
715       cairo_line_to (cr, x_right, y_top + square); /* C */
716 
717       x_middle += double_width;
718       x_right  += double_width;
719     }
720   cairo_line_to (cr, x_middle, y_bottom); /* B */
721 
722   if (i + 1 == width_units)
723     cairo_line_to (cr, x_middle + square / 2, y_bottom - square / 2); /* G */
724   else if (i + 2 == width_units) {
725     cairo_line_to (cr, x_right + square / 2, y_top + square / 2); /* D */
726     cairo_line_to (cr, x_right, y_top); /* E */
727   }
728 
729   /* Top of squiggle */
730   x_left = x_middle - unit_width;
731   for (; i >= 0; i -= 2)
732     {
733       cairo_line_to (cr, x_middle, y_bottom - square); /* F */
734       cairo_line_to (cr, x_left, y_top);   /* H */
735 
736       x_left   -= double_width;
737       x_middle -= double_width;
738     }
739 }
740 
741 static void
pango_cairo_renderer_draw_error_underline(PangoRenderer * renderer,int x,int y,int width,int height)742 pango_cairo_renderer_draw_error_underline (PangoRenderer *renderer,
743 					   int            x,
744 					   int            y,
745 					   int            width,
746 					   int            height)
747 {
748   PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
749   cairo_t *cr = crenderer->cr;
750 
751   if (!crenderer->do_path)
752     {
753       cairo_save (cr);
754 
755       set_color (crenderer, PANGO_RENDER_PART_UNDERLINE);
756 
757       cairo_new_path (cr);
758     }
759 
760   draw_error_underline (cr,
761 			crenderer->x_offset + (double)x / PANGO_SCALE,
762 			crenderer->y_offset + (double)y / PANGO_SCALE,
763 			(double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
764 
765   if (!crenderer->do_path)
766     {
767       cairo_fill (cr);
768 
769       cairo_restore (cr);
770     }
771 }
772 
773 static void
pango_cairo_renderer_draw_shape(PangoRenderer * renderer,PangoAttrShape * attr,int x,int y)774 pango_cairo_renderer_draw_shape (PangoRenderer  *renderer,
775 				 PangoAttrShape *attr,
776 				 int             x,
777 				 int             y)
778 {
779   PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
780   cairo_t *cr = crenderer->cr;
781   PangoLayout *layout;
782   PangoCairoShapeRendererFunc shape_renderer;
783   gpointer                    shape_renderer_data;
784   double base_x, base_y;
785 
786   layout = pango_renderer_get_layout (renderer);
787 
788   if (!layout)
789   	return;
790 
791   shape_renderer = pango_cairo_context_get_shape_renderer (pango_layout_get_context (layout),
792 							   &shape_renderer_data);
793 
794   if (!shape_renderer)
795     return;
796 
797   base_x = crenderer->x_offset + (double)x / PANGO_SCALE;
798   base_y = crenderer->y_offset + (double)y / PANGO_SCALE;
799 
800   cairo_save (cr);
801   if (!crenderer->do_path)
802     set_color (crenderer, PANGO_RENDER_PART_FOREGROUND);
803 
804   cairo_move_to (cr, base_x, base_y);
805 
806   shape_renderer (cr, attr, crenderer->do_path, shape_renderer_data);
807 
808   cairo_restore (cr);
809 }
810 
811 static void
pango_cairo_renderer_init(PangoCairoRenderer * renderer G_GNUC_UNUSED)812 pango_cairo_renderer_init (PangoCairoRenderer *renderer G_GNUC_UNUSED)
813 {
814 }
815 
816 static void
pango_cairo_renderer_class_init(PangoCairoRendererClass * klass)817 pango_cairo_renderer_class_init (PangoCairoRendererClass *klass)
818 {
819   PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
820 
821   renderer_class->draw_glyphs = pango_cairo_renderer_draw_glyphs;
822   renderer_class->draw_glyph_item = pango_cairo_renderer_draw_glyph_item;
823   renderer_class->draw_rectangle = pango_cairo_renderer_draw_rectangle;
824   renderer_class->draw_trapezoid = pango_cairo_renderer_draw_trapezoid;
825   renderer_class->draw_error_underline = pango_cairo_renderer_draw_error_underline;
826   renderer_class->draw_shape = pango_cairo_renderer_draw_shape;
827 }
828 
829 static PangoCairoRenderer *cached_renderer = NULL; /* MT-safe */
830 G_LOCK_DEFINE_STATIC (cached_renderer);
831 
832 static PangoCairoRenderer *
acquire_renderer(void)833 acquire_renderer (void)
834 {
835   PangoCairoRenderer *renderer;
836 
837   if (G_LIKELY (G_TRYLOCK (cached_renderer)))
838     {
839       if (G_UNLIKELY (!cached_renderer))
840         {
841 	  cached_renderer = g_object_new (PANGO_TYPE_CAIRO_RENDERER, NULL);
842 	  cached_renderer->is_cached_renderer = TRUE;
843 	}
844 
845       renderer = cached_renderer;
846     }
847   else
848     {
849       renderer = g_object_new (PANGO_TYPE_CAIRO_RENDERER, NULL);
850     }
851 
852   return renderer;
853 }
854 
855 static void
release_renderer(PangoCairoRenderer * renderer)856 release_renderer (PangoCairoRenderer *renderer)
857 {
858   if (G_LIKELY (renderer->is_cached_renderer))
859     {
860       renderer->cr = NULL;
861       renderer->do_path = FALSE;
862       renderer->has_show_text_glyphs = FALSE;
863       renderer->x_offset = 0.;
864       renderer->y_offset = 0.;
865 
866       G_UNLOCK (cached_renderer);
867     }
868   else
869     g_object_unref (renderer);
870 }
871 
872 static void
save_current_point(PangoCairoRenderer * renderer)873 save_current_point (PangoCairoRenderer *renderer)
874 {
875   renderer->cr_had_current_point = cairo_has_current_point (renderer->cr);
876   cairo_get_current_point (renderer->cr, &renderer->x_offset, &renderer->y_offset);
877 
878   /* abuse save_current_point() to cache cairo_has_show_text_glyphs() result */
879   renderer->has_show_text_glyphs = cairo_surface_has_show_text_glyphs (cairo_get_target (renderer->cr));
880 }
881 
882 static void
restore_current_point(PangoCairoRenderer * renderer)883 restore_current_point (PangoCairoRenderer *renderer)
884 {
885   if (renderer->cr_had_current_point)
886     /* XXX should do cairo_set_current_point() when we have that function */
887     cairo_move_to (renderer->cr, renderer->x_offset, renderer->y_offset);
888   else
889     cairo_new_sub_path (renderer->cr);
890 }
891 
892 
893 /* convenience wrappers using the default renderer */
894 
895 
896 static void
_pango_cairo_do_glyph_string(cairo_t * cr,PangoFont * font,PangoGlyphString * glyphs,gboolean do_path)897 _pango_cairo_do_glyph_string (cairo_t          *cr,
898 			      PangoFont        *font,
899 			      PangoGlyphString *glyphs,
900 			      gboolean          do_path)
901 {
902   PangoCairoRenderer *crenderer = acquire_renderer ();
903   PangoRenderer *renderer = (PangoRenderer *) crenderer;
904 
905   crenderer->cr = cr;
906   crenderer->do_path = do_path;
907   save_current_point (crenderer);
908 
909   if (!do_path)
910     {
911       /* unset all part colors, since when drawing just a glyph string,
912        * prepare_run() isn't called.
913        */
914 
915       pango_renderer_activate (renderer);
916 
917       pango_renderer_set_color (renderer, PANGO_RENDER_PART_FOREGROUND, NULL);
918       pango_renderer_set_color (renderer, PANGO_RENDER_PART_BACKGROUND, NULL);
919       pango_renderer_set_color (renderer, PANGO_RENDER_PART_UNDERLINE, NULL);
920       pango_renderer_set_color (renderer, PANGO_RENDER_PART_STRIKETHROUGH, NULL);
921       pango_renderer_set_color (renderer, PANGO_RENDER_PART_OVERLINE, NULL);
922     }
923 
924   pango_renderer_draw_glyphs (renderer, font, glyphs, 0, 0);
925 
926   if (!do_path)
927     {
928       pango_renderer_deactivate (renderer);
929     }
930 
931   restore_current_point (crenderer);
932 
933   release_renderer (crenderer);
934 }
935 
936 static void
_pango_cairo_do_glyph_item(cairo_t * cr,const char * text,PangoGlyphItem * glyph_item,gboolean do_path)937 _pango_cairo_do_glyph_item (cairo_t          *cr,
938 			    const char       *text,
939 			    PangoGlyphItem   *glyph_item,
940 			    gboolean          do_path)
941 {
942   PangoCairoRenderer *crenderer = acquire_renderer ();
943   PangoRenderer *renderer = (PangoRenderer *) crenderer;
944 
945   crenderer->cr = cr;
946   crenderer->do_path = do_path;
947   save_current_point (crenderer);
948 
949   if (!do_path)
950     {
951       /* unset all part colors, since when drawing just a glyph string,
952        * prepare_run() isn't called.
953        */
954 
955       pango_renderer_activate (renderer);
956 
957       pango_renderer_set_color (renderer, PANGO_RENDER_PART_FOREGROUND, NULL);
958       pango_renderer_set_color (renderer, PANGO_RENDER_PART_BACKGROUND, NULL);
959       pango_renderer_set_color (renderer, PANGO_RENDER_PART_UNDERLINE, NULL);
960       pango_renderer_set_color (renderer, PANGO_RENDER_PART_STRIKETHROUGH, NULL);
961       pango_renderer_set_color (renderer, PANGO_RENDER_PART_OVERLINE, NULL);
962     }
963 
964   pango_renderer_draw_glyph_item (renderer, text, glyph_item, 0, 0);
965 
966   if (!do_path)
967     {
968       pango_renderer_deactivate (renderer);
969     }
970 
971   restore_current_point (crenderer);
972 
973   release_renderer (crenderer);
974 }
975 
976 static void
_pango_cairo_do_layout_line(cairo_t * cr,PangoLayoutLine * line,gboolean do_path)977 _pango_cairo_do_layout_line (cairo_t          *cr,
978 			     PangoLayoutLine  *line,
979 			     gboolean          do_path)
980 {
981   PangoCairoRenderer *crenderer = acquire_renderer ();
982   PangoRenderer *renderer = (PangoRenderer *) crenderer;
983 
984   crenderer->cr = cr;
985   crenderer->do_path = do_path;
986   save_current_point (crenderer);
987 
988   pango_renderer_draw_layout_line (renderer, line, 0, 0);
989 
990   restore_current_point (crenderer);
991 
992   release_renderer (crenderer);
993 }
994 
995 static void
_pango_cairo_do_layout(cairo_t * cr,PangoLayout * layout,gboolean do_path)996 _pango_cairo_do_layout (cairo_t     *cr,
997 			PangoLayout *layout,
998 			gboolean     do_path)
999 {
1000   PangoCairoRenderer *crenderer = acquire_renderer ();
1001   PangoRenderer *renderer = (PangoRenderer *) crenderer;
1002 
1003   crenderer->cr = cr;
1004   crenderer->do_path = do_path;
1005   save_current_point (crenderer);
1006 
1007   pango_renderer_draw_layout (renderer, layout, 0, 0);
1008 
1009   restore_current_point (crenderer);
1010 
1011   release_renderer (crenderer);
1012 }
1013 
1014 static void
_pango_cairo_do_error_underline(cairo_t * cr,double x,double y,double width,double height,gboolean do_path)1015 _pango_cairo_do_error_underline (cairo_t *cr,
1016 				 double   x,
1017 				 double   y,
1018 				 double   width,
1019 				 double   height,
1020 				 gboolean do_path)
1021 {
1022   /* We don't use a renderer here, for a simple reason:
1023    * the only renderer we can get is the default renderer, that
1024    * is all implemented here, so we shortcircuit and make our
1025    * life way easier.
1026    */
1027 
1028   if (!do_path)
1029     cairo_new_path (cr);
1030 
1031   draw_error_underline (cr, x, y, width, height);
1032 
1033   if (!do_path)
1034     cairo_fill (cr);
1035 }
1036 
1037 
1038 /* public wrapper of above to show or append path */
1039 
1040 
1041 /**
1042  * pango_cairo_show_glyph_string:
1043  * @cr: a Cairo context
1044  * @font: a `PangoFont` from a `PangoCairoFontMap`
1045  * @glyphs: a `PangoGlyphString`
1046  *
1047  * Draws the glyphs in @glyphs in the specified cairo context.
1048  *
1049  * The origin of the glyphs (the left edge of the baseline) will
1050  * be drawn at the current point of the cairo context.
1051  *
1052  * Since: 1.10
1053  */
1054 void
pango_cairo_show_glyph_string(cairo_t * cr,PangoFont * font,PangoGlyphString * glyphs)1055 pango_cairo_show_glyph_string (cairo_t          *cr,
1056 			       PangoFont        *font,
1057 			       PangoGlyphString *glyphs)
1058 {
1059   g_return_if_fail (cr != NULL);
1060   g_return_if_fail (glyphs != NULL);
1061 
1062   _pango_cairo_do_glyph_string (cr, font, glyphs, FALSE);
1063 }
1064 
1065 
1066 /**
1067  * pango_cairo_show_glyph_item:
1068  * @cr: a Cairo context
1069  * @text: the UTF-8 text that @glyph_item refers to
1070  * @glyph_item: a `PangoGlyphItem`
1071  *
1072  * Draws the glyphs in @glyph_item in the specified cairo context,
1073  *
1074  * embedding the text associated with the glyphs in the output if the
1075  * output format supports it (PDF for example), otherwise it acts
1076  * similar to [func@show_glyph_string].
1077  *
1078  * The origin of the glyphs (the left edge of the baseline) will
1079  * be drawn at the current point of the cairo context.
1080  *
1081  * Note that @text is the start of the text for layout, which is then
1082  * indexed by `glyph_item->item->offset`.
1083  *
1084  * Since: 1.22
1085  */
1086 void
pango_cairo_show_glyph_item(cairo_t * cr,const char * text,PangoGlyphItem * glyph_item)1087 pango_cairo_show_glyph_item (cairo_t          *cr,
1088 			     const char       *text,
1089 			     PangoGlyphItem   *glyph_item)
1090 {
1091   g_return_if_fail (cr != NULL);
1092   g_return_if_fail (text != NULL);
1093   g_return_if_fail (glyph_item != NULL);
1094 
1095   _pango_cairo_do_glyph_item (cr, text, glyph_item, FALSE);
1096 }
1097 
1098 /**
1099  * pango_cairo_show_layout_line:
1100  * @cr: a Cairo context
1101  * @line: a `PangoLayoutLine`
1102  *
1103  * Draws a `PangoLayoutLine` in the specified cairo context.
1104  *
1105  * The origin of the glyphs (the left edge of the line) will
1106  * be drawn at the current point of the cairo context.
1107  *
1108  * Since: 1.10
1109  */
1110 void
pango_cairo_show_layout_line(cairo_t * cr,PangoLayoutLine * line)1111 pango_cairo_show_layout_line (cairo_t          *cr,
1112 			      PangoLayoutLine  *line)
1113 {
1114   g_return_if_fail (cr != NULL);
1115   g_return_if_fail (line != NULL);
1116 
1117   _pango_cairo_do_layout_line (cr, line, FALSE);
1118 }
1119 
1120 /**
1121  * pango_cairo_show_layout:
1122  * @cr: a Cairo context
1123  * @layout: a Pango layout
1124  *
1125  * Draws a `PangoLayout` in the specified cairo context.
1126  *
1127  * The top-left corner of the `PangoLayout` will be drawn
1128  * at the current point of the cairo context.
1129  *
1130  * Since: 1.10
1131  */
1132 void
pango_cairo_show_layout(cairo_t * cr,PangoLayout * layout)1133 pango_cairo_show_layout (cairo_t     *cr,
1134 			 PangoLayout *layout)
1135 {
1136   g_return_if_fail (cr != NULL);
1137   g_return_if_fail (PANGO_IS_LAYOUT (layout));
1138 
1139   _pango_cairo_do_layout (cr, layout, FALSE);
1140 }
1141 
1142 /**
1143  * pango_cairo_show_error_underline:
1144  * @cr: a Cairo context
1145  * @x: The X coordinate of one corner of the rectangle
1146  * @y: The Y coordinate of one corner of the rectangle
1147  * @width: Non-negative width of the rectangle
1148  * @height: Non-negative height of the rectangle
1149  *
1150  * Draw a squiggly line in the specified cairo context that approximately
1151  * covers the given rectangle in the style of an underline used to indicate a
1152  * spelling error.
1153  *
1154  * The width of the underline is rounded to an integer
1155  * number of up/down segments and the resulting rectangle is centered in the
1156  * original rectangle.
1157  *
1158  * Since: 1.14
1159  */
1160 void
pango_cairo_show_error_underline(cairo_t * cr,double x,double y,double width,double height)1161 pango_cairo_show_error_underline (cairo_t *cr,
1162 				  double  x,
1163 				  double  y,
1164 				  double  width,
1165 				  double  height)
1166 {
1167   g_return_if_fail (cr != NULL);
1168   g_return_if_fail ((width >= 0) && (height >= 0));
1169 
1170   _pango_cairo_do_error_underline (cr, x, y, width, height, FALSE);
1171 }
1172 
1173 /**
1174  * pango_cairo_glyph_string_path:
1175  * @cr: a Cairo context
1176  * @font: a `PangoFont` from a `PangoCairoFontMap`
1177  * @glyphs: a `PangoGlyphString`
1178  *
1179  * Adds the glyphs in @glyphs to the current path in the specified
1180  * cairo context.
1181  *
1182  * The origin of the glyphs (the left edge of the baseline)
1183  * will be at the current point of the cairo context.
1184  *
1185  * Since: 1.10
1186  */
1187 void
pango_cairo_glyph_string_path(cairo_t * cr,PangoFont * font,PangoGlyphString * glyphs)1188 pango_cairo_glyph_string_path (cairo_t          *cr,
1189 			       PangoFont        *font,
1190 			       PangoGlyphString *glyphs)
1191 {
1192   g_return_if_fail (cr != NULL);
1193   g_return_if_fail (glyphs != NULL);
1194 
1195   _pango_cairo_do_glyph_string (cr, font, glyphs, TRUE);
1196 }
1197 
1198 /**
1199  * pango_cairo_layout_line_path:
1200  * @cr: a Cairo context
1201  * @line: a `PangoLayoutLine`
1202  *
1203  * Adds the text in `PangoLayoutLine` to the current path in the
1204  * specified cairo context.
1205  *
1206  * The origin of the glyphs (the left edge of the line) will be
1207  * at the current point of the cairo context.
1208  *
1209  * Since: 1.10
1210  */
1211 void
pango_cairo_layout_line_path(cairo_t * cr,PangoLayoutLine * line)1212 pango_cairo_layout_line_path (cairo_t          *cr,
1213 			      PangoLayoutLine  *line)
1214 {
1215   g_return_if_fail (cr != NULL);
1216   g_return_if_fail (line != NULL);
1217 
1218   _pango_cairo_do_layout_line (cr, line, TRUE);
1219 }
1220 
1221 /**
1222  * pango_cairo_layout_path:
1223  * @cr: a Cairo context
1224  * @layout: a Pango layout
1225  *
1226  * Adds the text in a `PangoLayout` to the current path in the
1227  * specified cairo context.
1228  *
1229  * The top-left corner of the `PangoLayout` will be at the
1230  * current point of the cairo context.
1231  *
1232  * Since: 1.10
1233  */
1234 void
pango_cairo_layout_path(cairo_t * cr,PangoLayout * layout)1235 pango_cairo_layout_path (cairo_t     *cr,
1236 			 PangoLayout *layout)
1237 {
1238   g_return_if_fail (cr != NULL);
1239   g_return_if_fail (PANGO_IS_LAYOUT (layout));
1240 
1241   _pango_cairo_do_layout (cr, layout, TRUE);
1242 }
1243 
1244 /**
1245  * pango_cairo_error_underline_path:
1246  * @cr: a Cairo context
1247  * @x: The X coordinate of one corner of the rectangle
1248  * @y: The Y coordinate of one corner of the rectangle
1249  * @width: Non-negative width of the rectangle
1250  * @height: Non-negative height of the rectangle
1251  *
1252  * Add a squiggly line to the current path in the specified cairo context that
1253  * approximately covers the given rectangle in the style of an underline used
1254  * to indicate a spelling error.
1255  *
1256  * The width of the underline is rounded to an integer number of up/down
1257  * segments and the resulting rectangle is centered in the original rectangle.
1258  *
1259  * Since: 1.14
1260  */
1261 void
pango_cairo_error_underline_path(cairo_t * cr,double x,double y,double width,double height)1262 pango_cairo_error_underline_path (cairo_t *cr,
1263 				  double   x,
1264 				  double   y,
1265 				  double   width,
1266 				  double   height)
1267 {
1268   g_return_if_fail (cr != NULL);
1269   g_return_if_fail ((width >= 0) && (height >= 0));
1270 
1271   _pango_cairo_do_error_underline (cr, x, y, width, height, TRUE);
1272 }
1273