pangocairo-render.c 35.3 KB
Newer Older
Owen Taylor's avatar
Owen Taylor committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* Pango
 * pangocairo-render.c: Rendering routines to Cairo surfaces
 *
 * Copyright (C) 2004 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

22
#include "config.h"
23

24
25
#include <math.h>

Matthias Clasen's avatar
Matthias Clasen committed
26
#include "pango-font-private.h"
Owen Taylor's avatar
Owen Taylor committed
27
#include "pangocairo-private.h"
28
#include "pango-glyph-item.h"
29
#include "pango-impl-utils.h"
Owen Taylor's avatar
Owen Taylor committed
30
31
32
33
34
35
36
37
38
39
40
41

typedef struct _PangoCairoRendererClass PangoCairoRendererClass;

#define PANGO_CAIRO_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_CAIRO_RENDERER, PangoCairoRendererClass))
#define PANGO_IS_CAIRO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_CAIRO_RENDERER))
#define PANGO_CAIRO_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_CAIRO_RENDERER, PangoCairoRendererClass))

struct _PangoCairoRenderer
{
  PangoRenderer parent_instance;

  cairo_t *cr;
42
  gboolean do_path;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
43
  gboolean has_show_text_glyphs;
44
  double x_offset, y_offset;
45
46
47
48

  /* house-keeping options */
  gboolean is_cached_renderer;
  gboolean cr_had_current_point;
Owen Taylor's avatar
Owen Taylor committed
49
50
51
52
53
54
55
56
57
58
59
60
61
};

struct _PangoCairoRendererClass
{
  PangoRendererClass parent_class;
};

G_DEFINE_TYPE (PangoCairoRenderer, pango_cairo_renderer, PANGO_TYPE_RENDERER)

static void
set_color (PangoCairoRenderer *crenderer,
	   PangoRenderPart     part)
{
62
  PangoColor *color = pango_renderer_get_color ((PangoRenderer *) (crenderer), part);
63
64
  guint16 a = pango_renderer_get_alpha ((PangoRenderer *) (crenderer), part);
  gdouble red, green, blue, alpha;
65

66
67
  if (!a && !color)
    return;
68

Owen Taylor's avatar
Owen Taylor committed
69
  if (color)
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
    {
      red = color->red / 65535.;
      green = color->green / 65535.;
      blue = color->blue / 65535.;
      alpha = 1.;
    }
  else
    {
      cairo_pattern_t *pattern = cairo_get_source (crenderer->cr);

      if (pattern && cairo_pattern_get_type (pattern) == CAIRO_PATTERN_TYPE_SOLID)
        cairo_pattern_get_rgba (pattern, &red, &green, &blue, &alpha);
      else
        {
          red = 0.;
          green = 0.;
          blue = 0.;
          alpha = 1.;
        }
    }

  if (a)
    alpha = a / 65535.;

  cairo_set_source_rgba (crenderer->cr, red, green, blue, alpha);
Owen Taylor's avatar
Owen Taylor committed
95
}
96

97
/* note: modifies crenderer->cr without doing cairo_save/restore() */
98
99
100
101
102
103
static void
_pango_cairo_renderer_draw_frame (PangoCairoRenderer *crenderer,
				  double              x,
				  double              y,
				  double              width,
				  double              height,
104
105
				  double              line_width,
				  gboolean            invalid)
106
{
107
108
  cairo_t *cr = crenderer->cr;

109
110
111
112
113
114
115
  if (crenderer->do_path)
    {
      double d2 = line_width * .5, d = line_width;

      /* we draw an outer box in one winding direction and an inner one in the
       * opposite direction.  This works for both cairo windings rules.
       *
116
117
       * what we really want is cairo_stroke_to_path(), but that's not
       * implemented in cairo yet.
118
119
120
       */

      /* outer */
121
122
      cairo_rectangle (cr, x-d2, y-d2, width+d, height+d);

123
      /* inner */
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
      if (invalid)
        {
	  /* delicacies of computing the joint... this is REALLY slow */

	  double alpha, tan_alpha2, cos_alpha;
	  double sx, sy;

	  alpha = atan2 (height, width);

	  tan_alpha2 = tan (alpha * .5);
	  if (tan_alpha2 < 1e-5 || (sx = d2 / tan_alpha2, 2. * sx > width - d))
	    sx = (width - d) * .5;

	  cos_alpha = cos (alpha);
	  if (cos_alpha < 1e-5 || (sy = d2 / cos_alpha, 2. * sy > height - d))
	    sy = (height - d) * .5;

	  /* top triangle */
	  cairo_new_sub_path (cr);
	  cairo_line_to (cr, x+width-sx, y+d2);
	  cairo_line_to (cr, x+sx, y+d2);
	  cairo_line_to (cr, x+.5*width, y+.5*height-sy);
	  cairo_close_path (cr);

	  /* bottom triangle */
	  cairo_new_sub_path (cr);
	  cairo_line_to (cr, x+width-sx, y+height-d2);
	  cairo_line_to (cr, x+.5*width, y+.5*height+sy);
	  cairo_line_to (cr, x+sx, y+height-d2);
	  cairo_close_path (cr);


	  alpha = G_PI_2 - alpha;
	  tan_alpha2 = tan (alpha * .5);
	  if (tan_alpha2 < 1e-5 || (sy = d2 / tan_alpha2, 2. * sy > height - d))
Matthias Clasen's avatar
Matthias Clasen committed
159
	    sy = (height - d) * .5;
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180

	  cos_alpha = cos (alpha);
	  if (cos_alpha < 1e-5 || (sx = d2 / cos_alpha, 2. * sx > width - d))
	    sx = (width - d) * .5;

	  /* left triangle */
	  cairo_new_sub_path (cr);
	  cairo_line_to (cr, x+d2, y+sy);
	  cairo_line_to (cr, x+d2, y+height-sy);
	  cairo_line_to (cr, x+.5*width-sx, y+.5*height);
	  cairo_close_path (cr);

	  /* right triangle */
	  cairo_new_sub_path (cr);
	  cairo_line_to (cr, x+width-d2, y+sy);
	  cairo_line_to (cr, x+.5*width+sx, y+.5*height);
	  cairo_line_to (cr, x+width-d2, y+height-sy);
	  cairo_close_path (cr);
	}
      else
	cairo_rectangle (cr, x+width-d2, y+d2, - (width-d), height-d);
181
182
183
    }
  else
    {
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
      cairo_rectangle (cr, x, y, width, height);

      if (invalid)
        {
	  /* draw an X */

	  cairo_new_sub_path (cr);
	  cairo_move_to (cr, x, y);
	  cairo_rel_line_to (cr, width, height);

	  cairo_new_sub_path (cr);
	  cairo_move_to (cr, x + width, y);
	  cairo_rel_line_to (cr, -width, height);

	  cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
	}

      cairo_set_line_width (cr, line_width);
      cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
      cairo_set_miter_limit (cr, 2.);
      cairo_stroke (cr);
205
206
207
    }
}

208
209
210
211
static void
_pango_cairo_renderer_draw_box_glyph (PangoCairoRenderer *crenderer,
				      PangoGlyphInfo     *gi,
				      double              cx,
212
213
				      double              cy,
				      gboolean            invalid)
214
215
216
{
  cairo_save (crenderer->cr);

217
218
219
220
221
  _pango_cairo_renderer_draw_frame (crenderer,
				    cx + 1.5,
				    cy + 1.5 - PANGO_UNKNOWN_GLYPH_HEIGHT,
				    (double)gi->geometry.width / PANGO_SCALE - 3.0,
				    PANGO_UNKNOWN_GLYPH_HEIGHT - 3.0,
222
223
				    1.0,
				    invalid);
224
225
226
227

  cairo_restore (crenderer->cr);
}

228
229
230
231
232
233
234
235
static void
_pango_cairo_renderer_draw_unknown_glyph (PangoCairoRenderer *crenderer,
					  PangoFont          *font,
					  PangoGlyphInfo     *gi,
					  double              cx,
					  double              cy)
{
  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
248
  cairo_save (crenderer->cr);

249
250
  ch = gi->glyph & ~PANGO_GLYPH_UNKNOWN_FLAG;
  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
257
258
      goto done;
    }

259
260
  if (G_UNLIKELY (invalid_input))
    {
261
      rows = hbi->rows;
262
263
      cols = 1;
    }
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
  else if (ch == 0x2423 ||
           g_unichar_type (ch) == G_UNICODE_SPACE_SEPARATOR)
    {
      /* We never want to show a hex box or other drawing for
       * space. If we want space to be visible, we replace 0x20
       * by 0x2423 (visible space).
       *
       * Since we don't want to rely on glyph availability,
       * we render a centered dot ourselves.
       */
      double x = cx + 0.5 *((double)gi->geometry.width / PANGO_SCALE);
      double y = cy + hbi->box_descent - 0.5 * hbi->box_height;

      cairo_new_sub_path (crenderer->cr);
      cairo_arc (crenderer->cr, x, y, 1.5 * hbi->line_width, 0, 2 * G_PI);
      cairo_close_path (crenderer->cr);
      cairo_fill (crenderer->cr);
      goto done;
    }
  else if (ch == '\t')
    {
      /* Since we don't want to rely on glyph availability,
       * we render an arrow like ↦ ourselves.
       */
      double y = cy + hbi->box_descent - 0.5 * hbi->box_height;
      double width = (double)gi->geometry.width / PANGO_SCALE;
      double offset = 0.2 * width;
      double x = cx + offset;
      double al = width - 2 * offset; /* arrow length */
      double tl = MIN (hbi->digit_width, 0.75 * al); /* tip length */
      double tw2 = 2.5 * hbi->line_width; /* tip width / 2 */
      double lw2 = 0.5 * hbi->line_width; /* line width / 2 */

      cairo_move_to (crenderer->cr, x - lw2, y - tw2);
      cairo_line_to (crenderer->cr, x + lw2, y - tw2);
      cairo_line_to (crenderer->cr, x + lw2, y - lw2);
      cairo_line_to (crenderer->cr, x + al - tl, y - lw2);
      cairo_line_to (crenderer->cr, x + al - tl, y - tw2);
      cairo_line_to (crenderer->cr, x + al,  y);
      cairo_line_to (crenderer->cr, x + al - tl, y + tw2);
      cairo_line_to (crenderer->cr, x + al - tl, y + lw2);
      cairo_line_to (crenderer->cr, x + lw2, y + lw2);
      cairo_line_to (crenderer->cr, x + lw2, y + tw2);
      cairo_line_to (crenderer->cr, x - lw2, y + tw2);
      cairo_close_path (crenderer->cr);
      cairo_fill (crenderer->cr);
      goto done;
    }
  else if (ch == '\n' || ch == 0x2028 || ch == 0x2029)
    {
      /* Since we don't want to rely on glyph availability,
       * we render an arrow like ↵ ourselves.
       */
      double width = (double)gi->geometry.width / PANGO_SCALE;
      double offset = 0.2 * width;
      double al = width - 2 * offset; /* arrow length */
      double tl = MIN (hbi->digit_width, 0.75 * al); /* tip length */
      double ah = al - 0.5 * tl; /* arrow height */
      double tw2 = 2.5 * hbi->line_width; /* tip width / 2 */
      double x = cx + offset;
      double y = cy - (hbi->box_height - al) / 2;
      double lw2 = 0.5 * hbi->line_width; /* line width / 2 */

      cairo_move_to (crenderer->cr, x, y);
      cairo_line_to (crenderer->cr, x + tl, y - tw2);
      cairo_line_to (crenderer->cr, x + tl, y - lw2);
      cairo_line_to (crenderer->cr, x + al - lw2, y - lw2);
      cairo_line_to (crenderer->cr, x + al - lw2, y - ah);
      cairo_line_to (crenderer->cr, x + al + lw2, y - ah);
      cairo_line_to (crenderer->cr, x + al + lw2, y + lw2);
      cairo_line_to (crenderer->cr, x + tl, y + lw2);
      cairo_line_to (crenderer->cr, x + tl, y + tw2);
      cairo_close_path (crenderer->cr);
      cairo_fill (crenderer->cr);
      goto done;
    }
340
341
342
343
344
345
  else if ((name = pango_get_ignorable_size (ch, &rows, &cols)))
    {
      /* Nothing else to do, we render 'default ignorable' chars
       * as hex box with their nick.
       */
    }
346
347
  else
    {
348
      /* Everything else gets a traditional hex box. */
349
      rows = hbi->rows;
350
351
      cols = (ch > 0xffff ? 6 : 4) / rows;
      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
358
  lsb = floor (lsb / hbi->pad_x) * hbi->pad_x;

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
365
366
367
368
				    hbi->line_width,
				    invalid_input);

  if (invalid_input)
    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
379
	  double x = x0 + col * (hbi->digit_width + hbi->pad_x);

380
381
382
          if (!p)
            goto done;

383
384
	  cairo_move_to (crenderer->cr, x, y);

385
          hexbox_string[0] = p[0];
386
387
388
389
390
391

	  if (crenderer->do_path)
	      cairo_text_path (crenderer->cr, hexbox_string);
	  else
	      cairo_show_text (crenderer->cr, hexbox_string);
	}
392
    }
393

394
done:
395
396
  cairo_restore (crenderer->cr);
}
397

398
399
400
401
402
#ifndef STACK_BUFFER_SIZE
#define STACK_BUFFER_SIZE (512 * sizeof (int))
#endif

#define STACK_ARRAY_LENGTH(T) (STACK_BUFFER_SIZE / sizeof(T))
Behdad Esfahbod's avatar
Behdad Esfahbod committed
403

Owen Taylor's avatar
Owen Taylor committed
404
static void
405
406
407
408
409
410
411
412
413
414
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)
Owen Taylor's avatar
Owen Taylor committed
415
{
416
  PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
Owen Taylor's avatar
Owen Taylor committed
417

418
  int i, count;
Owen Taylor's avatar
Owen Taylor committed
419
420
  int x_position = 0;
  cairo_glyph_t *cairo_glyphs;
421
  cairo_glyph_t stack_glyphs[STACK_ARRAY_LENGTH (cairo_glyph_t)];
422
423
  double base_x = crenderer->x_offset + (double)x / PANGO_SCALE;
  double base_y = crenderer->y_offset + (double)y / PANGO_SCALE;
Owen Taylor's avatar
Owen Taylor committed
424

Behdad Esfahbod's avatar
Behdad Esfahbod committed
425
  cairo_save (crenderer->cr);
426
  if (!crenderer->do_path)
Behdad Esfahbod's avatar
Behdad Esfahbod committed
427
    set_color (crenderer, PANGO_RENDER_PART_FOREGROUND);
Owen Taylor's avatar
Owen Taylor committed
428

429
  if (!_pango_cairo_font_install (font, crenderer->cr))
430
431
432
433
434
435
436
    {
      for (i = 0; i < glyphs->num_glyphs; i++)
	{
	  PangoGlyphInfo *gi = &glyphs->glyphs[i];

	  if (gi->glyph != PANGO_GLYPH_EMPTY)
	    {
437
438
439
440
	      double cx = base_x + (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
	      double cy = gi->geometry.y_offset == 0 ?
			  base_y :
			  base_y + (double)(gi->geometry.y_offset) / PANGO_SCALE;
441
442

	      _pango_cairo_renderer_draw_unknown_glyph (crenderer, font, gi, cx, cy);
443
	    }
444
445
446
447
448
449
	  x_position += gi->geometry.width;
	}

      goto done;
    }

450
  if (glyphs->num_glyphs > (int) G_N_ELEMENTS (stack_glyphs))
Owen Taylor's avatar
Owen Taylor committed
451
452
453
454
    cairo_glyphs = g_new (cairo_glyph_t, glyphs->num_glyphs);
  else
    cairo_glyphs = stack_glyphs;

455
  count = 0;
Owen Taylor's avatar
Owen Taylor committed
456
457
458
459
  for (i = 0; i < glyphs->num_glyphs; i++)
    {
      PangoGlyphInfo *gi = &glyphs->glyphs[i];

460
      if (gi->glyph != PANGO_GLYPH_EMPTY)
461
462
463
	{
	  double cx = base_x + (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
	  double cy = gi->geometry.y_offset == 0 ?
464
465
		      base_y :
		      base_y + (double)(gi->geometry.y_offset) / PANGO_SCALE;
466

467
	  if (gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG)
468
469
470
471
472
473
            {
              if (gi->glyph == (0x20 | PANGO_GLYPH_UNKNOWN_FLAG))
                ; /* no hex boxes for space, please */
              else
	        _pango_cairo_renderer_draw_unknown_glyph (crenderer, font, gi, cx, cy);
            }
474
475
476
477
478
479
480
481
	  else
	    {
	      cairo_glyphs[count].index = gi->glyph;
	      cairo_glyphs[count].x = cx;
	      cairo_glyphs[count].y = cy;
	      count++;
	    }
	}
Owen Taylor's avatar
Owen Taylor committed
482
483
484
      x_position += gi->geometry.width;
    }

485
  if (G_UNLIKELY (crenderer->do_path))
486
    cairo_glyph_path (crenderer->cr, cairo_glyphs, count);
487
  else
488
489
490
491
492
    if (G_UNLIKELY (clusters))
      cairo_show_text_glyphs (crenderer->cr,
			      text, text_len,
			      cairo_glyphs, count,
			      clusters, num_clusters,
Behdad Esfahbod's avatar
Behdad Esfahbod committed
493
			      backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : 0);
494
495
496
497
    else
      cairo_show_glyphs (crenderer->cr, cairo_glyphs, count);

  if (cairo_glyphs != stack_glyphs)
Owen Taylor's avatar
Owen Taylor committed
498
499
    g_free (cairo_glyphs);

500
done:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
501
  cairo_restore (crenderer->cr);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
502
}
503

504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
static void
pango_cairo_renderer_draw_glyphs (PangoRenderer     *renderer,
				  PangoFont         *font,
				  PangoGlyphString  *glyphs,
				  int                x,
				  int                y)
{
  pango_cairo_renderer_show_text_glyphs (renderer,
					 NULL, 0,
					 glyphs,
					 NULL, 0,
					 FALSE,
					 font,
					 x, y);
}

Behdad Esfahbod's avatar
Behdad Esfahbod committed
520
521
522
523
524
525
526
527
static void
pango_cairo_renderer_draw_glyph_item (PangoRenderer     *renderer,
				      const char        *text,
				      PangoGlyphItem    *glyph_item,
				      int                x,
				      int                y)
{
  PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
528
529
530
531
  PangoFont          *font      = glyph_item->item->analysis.font;
  PangoGlyphString   *glyphs    = glyph_item->glyphs;
  PangoItem          *item      = glyph_item->item;
  gboolean            backward  = (item->analysis.level & 1) != 0;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
532

533
534
535
536
  PangoGlyphItemIter   iter;
  cairo_text_cluster_t *cairo_clusters;
  cairo_text_cluster_t stack_clusters[STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
  int num_clusters;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
537
538
539

  if (!crenderer->has_show_text_glyphs || crenderer->do_path)
    {
540
541
542
543
544
545
546
      pango_cairo_renderer_show_text_glyphs (renderer,
					     NULL, 0,
					     glyphs,
					     NULL, 0,
					     FALSE,
					     font,
					     x, y);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
547
548
549
      return;
    }

550
551
  if (glyphs->num_glyphs > (int) G_N_ELEMENTS (stack_clusters))
    cairo_clusters = g_new (cairo_text_cluster_t, glyphs->num_glyphs);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
552
  else
553
    cairo_clusters = stack_clusters;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
554

555
556
  num_clusters = 0;
  if (pango_glyph_item_iter_init_start (&iter, glyph_item, text))
Behdad Esfahbod's avatar
Behdad Esfahbod committed
557
    {
558
559
560
561
562
563
564
      do {
        int num_bytes, num_glyphs, i;

        num_bytes  = iter.end_index - iter.start_index;
        num_glyphs = backward ? iter.start_glyph - iter.end_glyph : iter.end_glyph - iter.start_glyph;

	if (num_bytes < 1)
Matthias Clasen's avatar
Matthias Clasen committed
565
	  g_warning ("pango_cairo_renderer_draw_glyph_item: bad cluster has num_bytes %d", num_bytes);
566
567
568
569
	if (num_glyphs < 1)
	  g_warning ("pango_cairo_renderer_draw_glyph_item: bad cluster has num_glyphs %d", num_glyphs);

	/* Discount empty and unknown glyphs */
Behdad Esfahbod's avatar
Behdad Esfahbod committed
570
571
	for (i = MIN (iter.start_glyph, iter.end_glyph+1);
	     i < MAX (iter.start_glyph+1, iter.end_glyph);
572
573
574
575
576
577
578
579
580
581
582
583
584
	     i++)
	  {
	    PangoGlyphInfo *gi = &glyphs->glyphs[i];

	    if (gi->glyph == PANGO_GLYPH_EMPTY ||
		gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG)
	      num_glyphs--;
	  }

        cairo_clusters[num_clusters].num_bytes  = num_bytes;
        cairo_clusters[num_clusters].num_glyphs = num_glyphs;
        num_clusters++;
      } while (pango_glyph_item_iter_next_cluster (&iter));
Behdad Esfahbod's avatar
Behdad Esfahbod committed
585
586
    }

587
588
589
590
591
592
593
  pango_cairo_renderer_show_text_glyphs (renderer,
					 text + item->offset, item->length,
					 glyphs,
					 cairo_clusters, num_clusters,
					 backward,
					 font,
					 x, y);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
594

595
596
  if (cairo_clusters != stack_clusters)
    g_free (cairo_clusters);
Owen Taylor's avatar
Owen Taylor committed
597
598
599
600
601
602
603
604
605
606
}

static void
pango_cairo_renderer_draw_rectangle (PangoRenderer     *renderer,
				     PangoRenderPart    part,
				     int                x,
				     int                y,
				     int                width,
				     int                height)
{
607
  PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
Owen Taylor's avatar
Owen Taylor committed
608

609
610
611
  if (!crenderer->do_path)
    {
      cairo_save (crenderer->cr);
Owen Taylor's avatar
Owen Taylor committed
612

613
614
      set_color (crenderer, part);
    }
Owen Taylor's avatar
Owen Taylor committed
615
616

  cairo_rectangle (crenderer->cr,
617
		   crenderer->x_offset + (double)x / PANGO_SCALE,
618
		   crenderer->y_offset + (double)y / PANGO_SCALE,
Owen Taylor's avatar
Owen Taylor committed
619
		   (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
620

621
  if (!crenderer->do_path)
622
623
    {
      cairo_fill (crenderer->cr);
624

625
626
      cairo_restore (crenderer->cr);
    }
Owen Taylor's avatar
Owen Taylor committed
627
628
}

629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
static void
pango_cairo_renderer_draw_trapezoid (PangoRenderer     *renderer,
				     PangoRenderPart    part,
				     double             y1_,
				     double             x11,
				     double             x21,
				     double             y2,
				     double             x12,
				     double             x22)
{
  PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
  cairo_t *cr;
  double x, y;

  cr = crenderer->cr;

  cairo_save (cr);

  if (!crenderer->do_path)
    set_color (crenderer, part);

  x = crenderer->x_offset,
  y = crenderer->y_offset;
  cairo_user_to_device_distance (cr, &x, &y);
  cairo_identity_matrix (cr);
  cairo_translate (cr, x, y);

  cairo_move_to (cr, x11, y1_);
  cairo_line_to (cr, x21, y1_);
  cairo_line_to (cr, x22, y2);
  cairo_line_to (cr, x12, y2);
  cairo_close_path (cr);

  if (!crenderer->do_path)
    cairo_fill (cr);

  cairo_restore (cr);
}

Owen Taylor's avatar
Owen Taylor committed
668
669
670
671
672
673
/* Draws an error underline that looks like one of:
 *              H       E                H
 *     /\      /\      /\        /\      /\               -
 *   A/  \    /  \    /  \     A/  \    /  \              |
 *    \   \  /    \  /   /D     \   \  /    \             |
 *     \   \/  C   \/   /        \   \/   C  \            | height = HEIGHT_SQUARES * square
674
 *      \      /\  F   /          \  F   /\   \           |
Owen Taylor's avatar
Owen Taylor committed
675
676
677
 *       \    /  \    /            \    /  \   \G         |
 *        \  /    \  /              \  /    \  /          |
 *         \/      \/                \/      \/           -
678
 *         B                         B
Behdad Esfahbod's avatar
Minor.    
Behdad Esfahbod committed
679
680
 *         |---|
 *       unit_width = (HEIGHT_SQUARES - 1) * square
Owen Taylor's avatar
Owen Taylor committed
681
682
683
684
685
686
687
688
 *
 * The x, y, width, height passed in give the desired bounding box;
 * x/width are adjusted to make the underline a integer number of units
 * wide.
 */
#define HEIGHT_SQUARES 2.5

static void
689
690
691
692
693
draw_error_underline (cairo_t *cr,
		      double   x,
		      double   y,
		      double   width,
		      double   height)
Owen Taylor's avatar
Owen Taylor committed
694
695
696
{
  double square = height / HEIGHT_SQUARES;
  double unit_width = (HEIGHT_SQUARES - 1) * square;
697
  double double_width = 2 * unit_width;
Owen Taylor's avatar
Owen Taylor committed
698
699
  int width_units = (width + unit_width / 2) / unit_width;
  double y_top, y_bottom;
700
  double x_left, x_middle, x_right;
Owen Taylor's avatar
Owen Taylor committed
701
702
  int i;

703
  x += (width - width_units * unit_width) / 2;
704

705
706
  y_top = y;
  y_bottom = y + height;
707

Owen Taylor's avatar
Owen Taylor committed
708
  /* Bottom of squiggle */
709
710
  x_middle = x + unit_width;
  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)
Owen Taylor's avatar
Owen Taylor committed
713
714
    {
      cairo_line_to (cr, x_middle, y_bottom); /* B */
715
      cairo_line_to (cr, x_right, y_top + square); /* C */
716

717
718
      x_middle += double_width;
      x_right  += double_width;
Owen Taylor's avatar
Owen Taylor committed
719
    }
720
721
722
723
724
725
726
727
  cairo_line_to (cr, x_middle, y_bottom); /* B */

  if (i + 1 == width_units)
    cairo_line_to (cr, x_middle + square / 2, y_bottom - square / 2); /* G */
  else if (i + 2 == width_units) {
    cairo_line_to (cr, x_right + square / 2, y_top + square / 2); /* D */
    cairo_line_to (cr, x_right, y_top); /* E */
  }
728

Owen Taylor's avatar
Owen Taylor committed
729
  /* Top of squiggle */
730
731
  x_left = x_middle - unit_width;
  for (; i >= 0; i -= 2)
Owen Taylor's avatar
Owen Taylor committed
732
    {
733
      cairo_line_to (cr, x_middle, y_bottom - square); /* F */
Owen Taylor's avatar
Owen Taylor committed
734
      cairo_line_to (cr, x_left, y_top);   /* H */
735
736
737

      x_left   -= double_width;
      x_middle -= double_width;
Owen Taylor's avatar
Owen Taylor committed
738
    }
739
740
741
742
743
744
745
746
747
}

static void
pango_cairo_renderer_draw_error_underline (PangoRenderer *renderer,
					   int            x,
					   int            y,
					   int            width,
					   int            height)
{
748
  PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
749
750
751
752
753
754
755
  cairo_t *cr = crenderer->cr;

  if (!crenderer->do_path)
    {
      cairo_save (cr);

      set_color (crenderer, PANGO_RENDER_PART_UNDERLINE);
756

757
758
759
760
761
762
763
      cairo_new_path (cr);
    }

  draw_error_underline (cr,
			crenderer->x_offset + (double)x / PANGO_SCALE,
			crenderer->y_offset + (double)y / PANGO_SCALE,
			(double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
764

765
766
767
  if (!crenderer->do_path)
    {
      cairo_fill (cr);
Owen Taylor's avatar
Owen Taylor committed
768

769
770
      cairo_restore (cr);
    }
Owen Taylor's avatar
Owen Taylor committed
771
772
}

Behdad Esfahbod's avatar
New API    
Behdad Esfahbod committed
773
774
775
776
777
778
779
780
static void
pango_cairo_renderer_draw_shape (PangoRenderer  *renderer,
				 PangoAttrShape *attr,
				 int             x,
				 int             y)
{
  PangoCairoRenderer *crenderer = (PangoCairoRenderer *) (renderer);
  cairo_t *cr = crenderer->cr;
781
782
783
784
785
786
787
788
789
  PangoLayout *layout;
  PangoCairoShapeRendererFunc shape_renderer;
  gpointer                    shape_renderer_data;
  double base_x, base_y;

  layout = pango_renderer_get_layout (renderer);

  if (!layout)
  	return;
Behdad Esfahbod's avatar
New API    
Behdad Esfahbod committed
790

791
792
793
794
  shape_renderer = pango_cairo_context_get_shape_renderer (pango_layout_get_context (layout),
							   &shape_renderer_data);

  if (!shape_renderer)
Behdad Esfahbod's avatar
New API    
Behdad Esfahbod committed
795
796
    return;

797
798
799
  base_x = crenderer->x_offset + (double)x / PANGO_SCALE;
  base_y = crenderer->y_offset + (double)y / PANGO_SCALE;

Behdad Esfahbod's avatar
New API    
Behdad Esfahbod committed
800
801
802
803
804
805
  cairo_save (cr);
  if (!crenderer->do_path)
    set_color (crenderer, PANGO_RENDER_PART_FOREGROUND);

  cairo_move_to (cr, base_x, base_y);

806
  shape_renderer (cr, attr, crenderer->do_path, shape_renderer_data);
Behdad Esfahbod's avatar
New API    
Behdad Esfahbod committed
807
808
809
810

  cairo_restore (cr);
}

Owen Taylor's avatar
Owen Taylor committed
811
static void
Behdad Esfahbod's avatar
Behdad Esfahbod committed
812
pango_cairo_renderer_init (PangoCairoRenderer *renderer G_GNUC_UNUSED)
Owen Taylor's avatar
Owen Taylor committed
813
814
815
816
817
818
819
820
821
{
}

static void
pango_cairo_renderer_class_init (PangoCairoRendererClass *klass)
{
  PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);

  renderer_class->draw_glyphs = pango_cairo_renderer_draw_glyphs;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
822
  renderer_class->draw_glyph_item = pango_cairo_renderer_draw_glyph_item;
Owen Taylor's avatar
Owen Taylor committed
823
  renderer_class->draw_rectangle = pango_cairo_renderer_draw_rectangle;
824
  renderer_class->draw_trapezoid = pango_cairo_renderer_draw_trapezoid;
Owen Taylor's avatar
Owen Taylor committed
825
  renderer_class->draw_error_underline = pango_cairo_renderer_draw_error_underline;
Behdad Esfahbod's avatar
New API    
Behdad Esfahbod committed
826
  renderer_class->draw_shape = pango_cairo_renderer_draw_shape;
Owen Taylor's avatar
Owen Taylor committed
827
828
}

Behdad Esfahbod's avatar
Behdad Esfahbod committed
829
static PangoCairoRenderer *cached_renderer = NULL; /* MT-safe */
830
G_LOCK_DEFINE_STATIC (cached_renderer);
831
832

static PangoCairoRenderer *
833
acquire_renderer (void)
834
835
836
{
  PangoCairoRenderer *renderer;

837
  if (G_LIKELY (G_TRYLOCK (cached_renderer)))
838
839
    {
      if (G_UNLIKELY (!cached_renderer))
840
841
842
843
        {
	  cached_renderer = g_object_new (PANGO_TYPE_CAIRO_RENDERER, NULL);
	  cached_renderer->is_cached_renderer = TRUE;
	}
844
845
846
847

      renderer = cached_renderer;
    }
  else
848
849
850
    {
      renderer = g_object_new (PANGO_TYPE_CAIRO_RENDERER, NULL);
    }
851
852
853
854

  return renderer;
}

855
static void
856
release_renderer (PangoCairoRenderer *renderer)
857
{
858
  if (G_LIKELY (renderer->is_cached_renderer))
859
860
861
    {
      renderer->cr = NULL;
      renderer->do_path = FALSE;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
862
      renderer->has_show_text_glyphs = FALSE;
863
864
865
      renderer->x_offset = 0.;
      renderer->y_offset = 0.;

866
      G_UNLOCK (cached_renderer);
867
868
869
870
871
    }
  else
    g_object_unref (renderer);
}

872
873
874
875
876
static void
save_current_point (PangoCairoRenderer *renderer)
{
  renderer->cr_had_current_point = cairo_has_current_point (renderer->cr);
  cairo_get_current_point (renderer->cr, &renderer->x_offset, &renderer->y_offset);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
877
878

  /* abuse save_current_point() to cache cairo_has_show_text_glyphs() result */
Behdad Esfahbod's avatar
Behdad Esfahbod committed
879
  renderer->has_show_text_glyphs = cairo_surface_has_show_text_glyphs (cairo_get_target (renderer->cr));
880
881
882
883
884
885
886
887
888
889
890
891
}

static void
restore_current_point (PangoCairoRenderer *renderer)
{
  if (renderer->cr_had_current_point)
    /* XXX should do cairo_set_current_point() when we have that function */
    cairo_move_to (renderer->cr, renderer->x_offset, renderer->y_offset);
  else
    cairo_new_sub_path (renderer->cr);
}

892
893
894
895
896
897
898
899
900

/* convenience wrappers using the default renderer */


static void
_pango_cairo_do_glyph_string (cairo_t          *cr,
			      PangoFont        *font,
			      PangoGlyphString *glyphs,
			      gboolean          do_path)
Owen Taylor's avatar
Owen Taylor committed
901
{
902
  PangoCairoRenderer *crenderer = acquire_renderer ();
903
  PangoRenderer *renderer = (PangoRenderer *) crenderer;
Owen Taylor's avatar
Owen Taylor committed
904
905

  crenderer->cr = cr;
906
  crenderer->do_path = do_path;
907
  save_current_point (crenderer);
908
909
910
911
912
913
914

  if (!do_path)
    {
      /* unset all part colors, since when drawing just a glyph string,
       * prepare_run() isn't called.
       */

915
916
917
918
919
920
      pango_renderer_activate (renderer);

      pango_renderer_set_color (renderer, PANGO_RENDER_PART_FOREGROUND, NULL);
      pango_renderer_set_color (renderer, PANGO_RENDER_PART_BACKGROUND, NULL);
      pango_renderer_set_color (renderer, PANGO_RENDER_PART_UNDERLINE, NULL);
      pango_renderer_set_color (renderer, PANGO_RENDER_PART_STRIKETHROUGH, NULL);
921
      pango_renderer_set_color (renderer, PANGO_RENDER_PART_OVERLINE, NULL);
922
    }
923

Owen Taylor's avatar
Owen Taylor committed
924
  pango_renderer_draw_glyphs (renderer, font, glyphs, 0, 0);
925
926
927
928
929

  if (!do_path)
    {
      pango_renderer_deactivate (renderer);
    }
930

931
932
933
  restore_current_point (crenderer);

  release_renderer (crenderer);
Owen Taylor's avatar
Owen Taylor committed
934
935
}

Behdad Esfahbod's avatar
Behdad Esfahbod committed
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
static void
_pango_cairo_do_glyph_item (cairo_t          *cr,
			    const char       *text,
			    PangoGlyphItem   *glyph_item,
			    gboolean          do_path)
{
  PangoCairoRenderer *crenderer = acquire_renderer ();
  PangoRenderer *renderer = (PangoRenderer *) crenderer;

  crenderer->cr = cr;
  crenderer->do_path = do_path;
  save_current_point (crenderer);

  if (!do_path)
    {
      /* unset all part colors, since when drawing just a glyph string,
       * prepare_run() isn't called.
       */

      pango_renderer_activate (renderer);

      pango_renderer_set_color (renderer, PANGO_RENDER_PART_FOREGROUND, NULL);
      pango_renderer_set_color (renderer, PANGO_RENDER_PART_BACKGROUND, NULL);
      pango_renderer_set_color (renderer, PANGO_RENDER_PART_UNDERLINE, NULL);
      pango_renderer_set_color (renderer, PANGO_RENDER_PART_STRIKETHROUGH, NULL);
961
      pango_renderer_set_color (renderer, PANGO_RENDER_PART_OVERLINE, NULL);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
962
963
964
965
966
967
968
969
970
971
972
973
974
975
    }

  pango_renderer_draw_glyph_item (renderer, text, glyph_item, 0, 0);

  if (!do_path)
    {
      pango_renderer_deactivate (renderer);
    }

  restore_current_point (crenderer);

  release_renderer (crenderer);
}

976
977
978
979
static void
_pango_cairo_do_layout_line (cairo_t          *cr,
			     PangoLayoutLine  *line,
			     gboolean          do_path)
Owen Taylor's avatar
Owen Taylor committed
980
{
981
  PangoCairoRenderer *crenderer = acquire_renderer ();
982
  PangoRenderer *renderer = (PangoRenderer *) crenderer;
Owen Taylor's avatar
Owen Taylor committed
983
984

  crenderer->cr = cr;
985
  crenderer->do_path = do_path;
986
  save_current_point (crenderer);
987

Owen Taylor's avatar
Owen Taylor committed
988
  pango_renderer_draw_layout_line (renderer, line, 0, 0);
989

990
991
992
  restore_current_point (crenderer);

  release_renderer (crenderer);
Owen Taylor's avatar
Owen Taylor committed
993
994
}

995
996
997
998
static void
_pango_cairo_do_layout (cairo_t     *cr,
			PangoLayout *layout,
			gboolean     do_path)
Owen Taylor's avatar
Owen Taylor committed
999
{
1000
  PangoCairoRenderer *crenderer = acquire_renderer ();
1001
  PangoRenderer *renderer = (PangoRenderer *) crenderer;
1002

Owen Taylor's avatar
Owen Taylor committed
1003
  crenderer->cr = cr;
1004
  crenderer->do_path = do_path;
1005
  save_current_point (crenderer);
1006

Owen Taylor's avatar
Owen Taylor committed
1007
  pango_renderer_draw_layout (renderer, layout, 0, 0);
1008

1009
1010
1011
  restore_current_point (crenderer);

  release_renderer (crenderer);
Owen Taylor's avatar
Owen Taylor committed
1012
}
1013

1014
1015
static void
_pango_cairo_do_error_underline (cairo_t *cr,
1016
1017
1018
1019
1020
				 double   x,
				 double   y,
				 double   width,
				 double   height,
				 gboolean do_path)
1021
{
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1022
1023
1024
1025
1026
  /* We don't use a renderer here, for a simple reason:
   * the only renderer we can get is the default renderer, that
   * is all implemented here, so we shortcircuit and make our
   * life way easier.
   */
1027

Behdad Esfahbod's avatar
Behdad Esfahbod committed
1028
1029
  if (!do_path)
    cairo_new_path (cr);
1030

Behdad Esfahbod's avatar
Behdad Esfahbod committed
1031
  draw_error_underline (cr, x, y, width, height);
1032

Behdad Esfahbod's avatar
Behdad Esfahbod committed
1033
1034
  if (!do_path)
    cairo_fill (cr);
1035
}
1036
1037
1038
1039
1040
1041
1042
1043


/* public wrapper of above to show or append path */


/**
 * pango_cairo_show_glyph_string:
 * @cr: a Cairo context
1044
1045
 * @font: a `PangoFont` from a `PangoCairoFontMap`
 * @glyphs: a `PangoGlyphString`
1046
 *
1047
 * Draws the glyphs in @glyphs in the specified cairo context.
Matthias Clasen's avatar
Matthias Clasen committed
1048
 *
1049
1050
1051
1052
 * The origin of the glyphs (the left edge of the baseline) will
 * be drawn at the current point of the cairo context.
 *
 * Since: 1.10
1053
 */
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
void
pango_cairo_show_glyph_string (cairo_t          *cr,
			       PangoFont        *font,
			       PangoGlyphString *glyphs)
{
  g_return_if_fail (cr != NULL);
  g_return_if_fail (glyphs != NULL);

  _pango_cairo_do_glyph_string (cr, font, glyphs, FALSE);
}

Behdad Esfahbod's avatar
Behdad Esfahbod committed
1065
1066
1067
1068
1069

/**
 * pango_cairo_show_glyph_item:
 * @cr: a Cairo context
 * @text: the UTF-8 text that @glyph_item refers to
1070
 * @glyph_item: a `PangoGlyphItem`
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1071
1072
 *
 * Draws the glyphs in @glyph_item in the specified cairo context,
Matthias Clasen's avatar
Matthias Clasen committed
1073
 *
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1074
1075
 * embedding the text associated with the glyphs in the output if the
 * output format supports it (PDF for example), otherwise it acts
1076
 * similar to [func@show_glyph_string].
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1077
1078
1079
1080
1081
 *
 * The origin of the glyphs (the left edge of the baseline) will
 * be drawn at the current point of the cairo context.
 *
 * Note that @text is the start of the text for layout, which is then
1082
 * indexed by `glyph_item->item->offset`.
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1083
 *
Behdad Esfahbod's avatar
Oops.    
Behdad Esfahbod committed
1084
 * Since: 1.22
1085
 */
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
void
pango_cairo_show_glyph_item (cairo_t          *cr,
			     const char       *text,
			     PangoGlyphItem   *glyph_item)
{
  g_return_if_fail (cr != NULL);
  g_return_if_fail (text != NULL);
  g_return_if_fail (glyph_item != NULL);

  _pango_cairo_do_glyph_item (cr, text, glyph_item, FALSE);
}

1098
1099
1100
/**
 * pango_cairo_show_layout_line:
 * @cr: a Cairo context
1101
 * @line: a `PangoLayoutLine`
1102
 *
1103
 * Draws a `PangoLayoutLine` in the specified cairo context.
Matthias Clasen's avatar
Matthias Clasen committed
1104
 *
1105
1106
1107
1108
 * The origin of the glyphs (the left edge of the line) will
 * be drawn at the current point of the cairo context.
 *
 * Since: 1.10
1109
 */
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
void
pango_cairo_show_layout_line (cairo_t          *cr,
			      PangoLayoutLine  *line)
{
  g_return_if_fail (cr != NULL);
  g_return_if_fail (line != NULL);

  _pango_cairo_do_layout_line (cr, line, FALSE);
}

/**
 * pango_cairo_show_layout:
 * @cr: a Cairo context
 * @layout: a Pango layout
1124
 *
1125
 * Draws a `PangoLayout` in the specified cairo context.
Matthias Clasen's avatar
Matthias Clasen committed
1126
 *
1127
 * The top-left corner of the `PangoLayout` will be drawn
1128
1129
1130
 * at the current point of the cairo context.
 *
 * Since: 1.10
1131
 */
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
void
pango_cairo_show_layout (cairo_t     *cr,
			 PangoLayout *layout)
{
  g_return_if_fail (cr != NULL);
  g_return_if_fail (PANGO_IS_LAYOUT (layout));

  _pango_cairo_do_layout (cr, layout, FALSE);
}

/**
 * pango_cairo_show_error_underline:
 * @cr: a Cairo context
 * @x: The X coordinate of one corner of the rectangle
 * @y: The Y coordinate of one corner of the rectangle
 * @width: Non-negative width of the rectangle
 * @height: Non-negative height of the rectangle
1149
 *
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1150
1151
 * Draw a squiggly line in the specified cairo context that approximately
 * covers the given rectangle in the style of an underline used to indicate a
Matthias Clasen's avatar
Matthias Clasen committed
1152
1153
1154
 * spelling error.
 *
 * The width of the underline is rounded to an integer
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1155
 * number of up/down segments and the resulting rectangle is centered in the
Matthias Clasen's avatar
Matthias Clasen committed
1156
 * original rectangle.
1157
1158
 *
 * Since: 1.14
1159
 */
1160
1161
void
pango_cairo_show_error_underline (cairo_t *cr,
1162
1163
1164
1165
				  double  x,
				  double  y,
				  double  width,
				  double  height)
1166
{
1167
  g_return_if_fail (cr != NULL);
1168
1169
1170
1171
  g_return_if_fail ((width >= 0) && (height >= 0));

  _pango_cairo_do_error_underline (cr, x, y, width, height, FALSE);
}
1172

1173
/**
1174
 * pango_cairo_glyph_string_path:
1175
 * @cr: a Cairo context
1176
1177
 * @font: a `PangoFont` from a `PangoCairoFontMap`
 * @glyphs: a `PangoGlyphString`
1178
 *
1179
 * Adds the glyphs in @glyphs to the current path in the specified
Matthias Clasen's avatar
Matthias Clasen committed
1180
1181
1182
 * cairo context.
 *
 * The origin of the glyphs (the left edge of the baseline)
1183
1184
1185
 * will be at the current point of the cairo context.
 *
 * Since: 1.10
1186
 */
1187
1188
1189
1190
1191
1192
1193
1194
void
pango_cairo_glyph_string_path (cairo_t          *cr,
			       PangoFont        *font,
			       PangoGlyphString *glyphs)
{
  g_return_if_fail (cr != NULL);
  g_return_if_fail (glyphs != NULL);

1195
  _pango_cairo_do_glyph_string (cr, font, glyphs, TRUE);
1196
1197
1198
1199
1200
}

/**
 * pango_cairo_layout_line_path:
 * @cr: a Cairo context
1201
 * @line: a `PangoLayoutLine`
1202
 *
1203
 * Adds the text in `PangoLayoutLine` to the current path in the
Matthias Clasen's avatar
Matthias Clasen committed
1204
1205
 * specified cairo context.
 *
1206
1207
 * The origin of the glyphs (the left edge of the line) will be
 * at the current point of the cairo context.
1208
1209
 *
 * Since: 1.10
1210