1 /* Pango
2  * pango-renderer.h: Base class for rendering
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 #include <stdlib.h>
24 
25 #include "pango-renderer.h"
26 #include "pango-impl-utils.h"
27 #include "pango-layout-private.h"
28 
29 #define N_RENDER_PARTS 5
30 
31 #define PANGO_IS_RENDERER_FAST(renderer) (renderer != NULL)
32 #define IS_VALID_PART(part) ((guint)part < N_RENDER_PARTS)
33 
34 typedef struct _LineState LineState;
35 typedef struct _Point Point;
36 
37 struct _Point
38 {
39   double x, y;
40 };
41 
42 struct _LineState
43 {
44   PangoUnderline underline;
45   PangoRectangle underline_rect;
46 
47   gboolean strikethrough;
48   PangoRectangle strikethrough_rect;
49   int strikethrough_glyphs;
50 
51   PangoOverline  overline;
52   PangoRectangle overline_rect;
53 
54   int logical_rect_end;
55 };
56 
57 struct _PangoRendererPrivate
58 {
59   PangoColor color[N_RENDER_PARTS];
60   gboolean color_set[N_RENDER_PARTS];
61   guint16 alpha[N_RENDER_PARTS];
62 
63   PangoLayoutLine *line;
64   LineState *line_state;
65   PangoOverline overline;
66 };
67 
68 static void pango_renderer_finalize                     (GObject          *gobject);
69 static void pango_renderer_default_draw_glyphs          (PangoRenderer    *renderer,
70                                                          PangoFont        *font,
71                                                          PangoGlyphString *glyphs,
72                                                          int               x,
73                                                          int               y);
74 static void pango_renderer_default_draw_glyph_item      (PangoRenderer    *renderer,
75                                                          const char       *text,
76                                                          PangoGlyphItem   *glyph_item,
77                                                          int               x,
78                                                          int               y);
79 static void pango_renderer_default_draw_rectangle       (PangoRenderer    *renderer,
80                                                          PangoRenderPart   part,
81                                                          int               x,
82                                                          int               y,
83                                                          int               width,
84                                                          int               height);
85 static void pango_renderer_default_draw_error_underline (PangoRenderer    *renderer,
86                                                          int               x,
87                                                          int               y,
88                                                          int               width,
89                                                          int               height);
90 static void pango_renderer_default_prepare_run          (PangoRenderer    *renderer,
91                                                          PangoLayoutRun   *run);
92 
93 static void pango_renderer_prepare_run (PangoRenderer  *renderer,
94                                         PangoLayoutRun *run);
95 
96 static void
to_device(PangoMatrix * matrix,double x,double y,Point * result)97 to_device (PangoMatrix *matrix,
98            double       x,
99            double       y,
100            Point       *result)
101 {
102   if (matrix)
103     {
104       result->x = (x * matrix->xx + y * matrix->xy) / PANGO_SCALE + matrix->x0;
105       result->y = (x * matrix->yx + y * matrix->yy) / PANGO_SCALE + matrix->y0;
106     }
107   else
108     {
109       result->x = x / PANGO_SCALE;
110       result->y = y / PANGO_SCALE;
111     }
112 }
113 
G_DEFINE_ABSTRACT_TYPE_WITH_CODE(PangoRenderer,pango_renderer,G_TYPE_OBJECT,G_ADD_PRIVATE (PangoRenderer))114 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PangoRenderer, pango_renderer, G_TYPE_OBJECT,
115                                   G_ADD_PRIVATE (PangoRenderer))
116 
117 static void
118 pango_renderer_class_init (PangoRendererClass *klass)
119 {
120   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
121 
122   klass->draw_glyphs = pango_renderer_default_draw_glyphs;
123   klass->draw_glyph_item = pango_renderer_default_draw_glyph_item;
124   klass->draw_rectangle = pango_renderer_default_draw_rectangle;
125   klass->draw_error_underline = pango_renderer_default_draw_error_underline;
126   klass->prepare_run = pango_renderer_default_prepare_run;
127 
128   gobject_class->finalize = pango_renderer_finalize;
129 }
130 
131 static void
pango_renderer_init(PangoRenderer * renderer)132 pango_renderer_init (PangoRenderer *renderer)
133 {
134   renderer->priv = pango_renderer_get_instance_private (renderer);
135   renderer->matrix = NULL;
136 }
137 
138 static void
pango_renderer_finalize(GObject * gobject)139 pango_renderer_finalize (GObject *gobject)
140 {
141   PangoRenderer *renderer = PANGO_RENDERER (gobject);
142 
143   if (renderer->matrix)
144     pango_matrix_free (renderer->matrix);
145 
146   G_OBJECT_CLASS (pango_renderer_parent_class)->finalize (gobject);
147 }
148 
149 /**
150  * pango_renderer_draw_layout:
151  * @renderer: a `PangoRenderer`
152  * @layout: a `PangoLayout`
153  * @x: X position of left edge of baseline, in user space coordinates
154  *   in Pango units.
155  * @y: Y position of left edge of baseline, in user space coordinates
156  *   in Pango units.
157  *
158  * Draws @layout with the specified `PangoRenderer`.
159  *
160  * This is equivalent to drawing the lines of the layout, at their
161  * respective positions relative to @x, @y.
162  *
163  * Since: 1.8
164  */
165 void
pango_renderer_draw_layout(PangoRenderer * renderer,PangoLayout * layout,int x,int y)166 pango_renderer_draw_layout (PangoRenderer *renderer,
167                             PangoLayout   *layout,
168                             int            x,
169                             int            y)
170 {
171   PangoLayoutIter iter;
172 
173   g_return_if_fail (PANGO_IS_RENDERER (renderer));
174   g_return_if_fail (PANGO_IS_LAYOUT (layout));
175 
176   /* We only change the matrix if the renderer isn't already
177    * active.
178    */
179   if (!renderer->active_count)
180     {
181       PangoContext *context = pango_layout_get_context (layout);
182       pango_renderer_set_matrix (renderer,
183                                  pango_context_get_matrix (context));
184     }
185 
186   pango_renderer_activate (renderer);
187 
188   _pango_layout_get_iter (layout, &iter);
189 
190   do
191     {
192       PangoRectangle   logical_rect;
193       PangoLayoutLine *line;
194       int              baseline;
195 
196       line = pango_layout_iter_get_line_readonly (&iter);
197 
198       pango_layout_iter_get_line_extents (&iter, NULL, &logical_rect);
199       baseline = pango_layout_iter_get_baseline (&iter);
200 
201       pango_renderer_draw_layout_line (renderer,
202                                        line,
203                                        x + logical_rect.x,
204                                        y + baseline);
205     }
206   while (pango_layout_iter_next_line (&iter));
207 
208   _pango_layout_iter_destroy (&iter);
209 
210   pango_renderer_deactivate (renderer);
211 }
212 
213 static void
draw_underline(PangoRenderer * renderer,LineState * state)214 draw_underline (PangoRenderer *renderer,
215                 LineState     *state)
216 {
217   PangoRectangle *rect = &state->underline_rect;
218   PangoUnderline underline = state->underline;
219 
220   state->underline = PANGO_UNDERLINE_NONE;
221 
222   switch (underline)
223     {
224     case PANGO_UNDERLINE_NONE:
225       break;
226     case PANGO_UNDERLINE_DOUBLE:
227     case PANGO_UNDERLINE_DOUBLE_LINE:
228       pango_renderer_draw_rectangle (renderer,
229                                      PANGO_RENDER_PART_UNDERLINE,
230                                      rect->x,
231                                      rect->y + 2 * rect->height,
232                                      rect->width,
233                                      rect->height);
234       G_GNUC_FALLTHROUGH;
235     case PANGO_UNDERLINE_SINGLE:
236     case PANGO_UNDERLINE_LOW:
237     case PANGO_UNDERLINE_SINGLE_LINE:
238       pango_renderer_draw_rectangle (renderer,
239                                      PANGO_RENDER_PART_UNDERLINE,
240                                      rect->x,
241                                      rect->y,
242                                      rect->width,
243                                      rect->height);
244       break;
245     case PANGO_UNDERLINE_ERROR:
246     case PANGO_UNDERLINE_ERROR_LINE:
247       pango_renderer_draw_error_underline (renderer,
248                                            rect->x,
249                                            rect->y,
250                                            rect->width,
251                                            3 * rect->height);
252       break;
253     }
254 }
255 
256 static void
draw_overline(PangoRenderer * renderer,LineState * state)257 draw_overline (PangoRenderer *renderer,
258                LineState     *state)
259 {
260   PangoRectangle *rect = &state->overline_rect;
261   PangoOverline overline = state->overline;
262 
263   state->overline = PANGO_OVERLINE_NONE;
264 
265   switch (overline)
266     {
267     case PANGO_OVERLINE_NONE:
268       break;
269     case PANGO_OVERLINE_SINGLE:
270       pango_renderer_draw_rectangle (renderer,
271                                      PANGO_RENDER_PART_OVERLINE,
272                                      rect->x,
273                                      rect->y,
274                                      rect->width,
275                                      rect->height);
276       break;
277     }
278 }
279 
280 static void
draw_strikethrough(PangoRenderer * renderer,LineState * state)281 draw_strikethrough (PangoRenderer *renderer,
282                     LineState     *state)
283 {
284   PangoRectangle *rect = &state->strikethrough_rect;
285   int num_glyphs = state->strikethrough_glyphs;
286 
287   if (state->strikethrough && num_glyphs > 0)
288     pango_renderer_draw_rectangle (renderer,
289                                    PANGO_RENDER_PART_STRIKETHROUGH,
290                                    rect->x,
291                                    rect->y / num_glyphs,
292                                    rect->width,
293                                    rect->height / num_glyphs);
294 
295   state->strikethrough = FALSE;
296   state->strikethrough_glyphs = 0;
297   rect->x += rect->width;
298   rect->width = 0;
299   rect->y = 0;
300   rect->height = 0;
301 }
302 
303 static void
handle_line_state_change(PangoRenderer * renderer,PangoRenderPart part)304 handle_line_state_change (PangoRenderer  *renderer,
305                           PangoRenderPart part)
306 {
307   LineState *state = renderer->priv->line_state;
308   if (!state)
309     return;
310 
311   if (part == PANGO_RENDER_PART_UNDERLINE &&
312       state->underline != PANGO_UNDERLINE_NONE)
313     {
314       PangoRectangle *rect = &state->underline_rect;
315 
316       rect->width = state->logical_rect_end - rect->x;
317       draw_underline (renderer, state);
318       state->underline = renderer->underline;
319       rect->x = state->logical_rect_end;
320       rect->width = 0;
321     }
322 
323   if (part == PANGO_RENDER_PART_OVERLINE &&
324       state->overline != PANGO_OVERLINE_NONE)
325     {
326       PangoRectangle *rect = &state->overline_rect;
327 
328       rect->width = state->logical_rect_end - rect->x;
329       draw_overline (renderer, state);
330       state->overline = renderer->priv->overline;
331       rect->x = state->logical_rect_end;
332       rect->width = 0;
333     }
334 
335   if (part == PANGO_RENDER_PART_STRIKETHROUGH &&
336       state->strikethrough)
337     {
338       PangoRectangle *rect = &state->strikethrough_rect;
339 
340       rect->width = state->logical_rect_end - rect->x;
341       draw_strikethrough (renderer, state);
342       state->strikethrough = renderer->strikethrough;
343     }
344 }
345 
346 static void
add_underline(PangoRenderer * renderer,LineState * state,PangoFontMetrics * metrics,int base_x,int base_y,PangoRectangle * ink_rect,PangoRectangle * logical_rect)347 add_underline (PangoRenderer    *renderer,
348                LineState        *state,
349                PangoFontMetrics *metrics,
350                int               base_x,
351                int               base_y,
352                PangoRectangle   *ink_rect,
353                PangoRectangle   *logical_rect)
354 {
355   PangoRectangle *current_rect = &state->underline_rect;
356   PangoRectangle new_rect;
357 
358   int underline_thickness = pango_font_metrics_get_underline_thickness (metrics);
359   int underline_position = pango_font_metrics_get_underline_position (metrics);
360 
361   new_rect.x = base_x + logical_rect->x;
362   new_rect.width = logical_rect->width;
363   new_rect.height = underline_thickness;
364   new_rect.y = base_y;
365 
366   switch (renderer->underline)
367     {
368     case PANGO_UNDERLINE_NONE:
369       g_assert_not_reached ();
370       break;
371     case PANGO_UNDERLINE_SINGLE:
372     case PANGO_UNDERLINE_DOUBLE:
373     case PANGO_UNDERLINE_ERROR:
374       new_rect.y -= underline_position;
375       break;
376     case PANGO_UNDERLINE_LOW:
377       new_rect.y += ink_rect->y + ink_rect->height + underline_thickness;
378       break;
379     case PANGO_UNDERLINE_SINGLE_LINE:
380     case PANGO_UNDERLINE_DOUBLE_LINE:
381     case PANGO_UNDERLINE_ERROR_LINE:
382       new_rect.y -= underline_position;
383       if (state->underline == renderer->underline)
384         {
385           new_rect.y = MAX (current_rect->y, new_rect.y);
386           new_rect.height = MAX (current_rect->height, new_rect.height);
387           current_rect->y = new_rect.y;
388           current_rect->height = new_rect.height;
389         }
390       break;
391     }
392 
393   if (renderer->underline == state->underline &&
394       new_rect.y == current_rect->y &&
395       new_rect.height == current_rect->height)
396     {
397       current_rect->width = new_rect.x + new_rect.width - current_rect->x;
398     }
399   else
400     {
401       draw_underline (renderer, state);
402 
403       *current_rect = new_rect;
404       state->underline = renderer->underline;
405     }
406 }
407 
408 static void
add_overline(PangoRenderer * renderer,LineState * state,PangoFontMetrics * metrics,int base_x,int base_y,PangoRectangle * ink_rect,PangoRectangle * logical_rect)409 add_overline (PangoRenderer    *renderer,
410               LineState        *state,
411               PangoFontMetrics *metrics,
412               int               base_x,
413               int               base_y,
414               PangoRectangle   *ink_rect,
415               PangoRectangle   *logical_rect)
416 {
417   PangoRectangle *current_rect = &state->overline_rect;
418   PangoRectangle new_rect;
419   int underline_thickness = pango_font_metrics_get_underline_thickness (metrics);
420   int ascent = pango_font_metrics_get_ascent (metrics);
421 
422   new_rect.x = base_x + logical_rect->x;
423   new_rect.width = logical_rect->width;
424   new_rect.height = underline_thickness;
425   new_rect.y = base_y;
426 
427   switch (renderer->priv->overline)
428     {
429     case PANGO_OVERLINE_NONE:
430       g_assert_not_reached ();
431       break;
432     case PANGO_OVERLINE_SINGLE:
433       new_rect.y -= ascent;
434       if (state->overline == renderer->priv->overline)
435         {
436           new_rect.y = MIN (current_rect->y, new_rect.y);
437           new_rect.height = MAX (current_rect->height, new_rect.height);
438           current_rect->y = new_rect.y;
439           current_rect->height = new_rect.height;
440         }
441       break;
442     }
443 
444   if (renderer->priv->overline == state->overline &&
445       new_rect.y == current_rect->y &&
446       new_rect.height == current_rect->height)
447     {
448       current_rect->width = new_rect.x + new_rect.width - current_rect->x;
449     }
450   else
451     {
452       draw_overline (renderer, state);
453 
454       *current_rect = new_rect;
455       state->overline = renderer->priv->overline;
456     }
457 }
458 
459 static void
add_strikethrough(PangoRenderer * renderer,LineState * state,PangoFontMetrics * metrics,int base_x,int base_y,PangoRectangle * ink_rect G_GNUC_UNUSED,PangoRectangle * logical_rect,int num_glyphs)460 add_strikethrough (PangoRenderer    *renderer,
461                    LineState        *state,
462                    PangoFontMetrics *metrics,
463                    int               base_x,
464                    int               base_y,
465                    PangoRectangle   *ink_rect G_GNUC_UNUSED,
466                    PangoRectangle   *logical_rect,
467                    int               num_glyphs)
468 {
469   PangoRectangle *current_rect = &state->strikethrough_rect;
470   PangoRectangle new_rect;
471 
472   int strikethrough_thickness = pango_font_metrics_get_strikethrough_thickness (metrics);
473   int strikethrough_position = pango_font_metrics_get_strikethrough_position (metrics);
474 
475   new_rect.x = base_x + logical_rect->x;
476   new_rect.width = logical_rect->width;
477   new_rect.y = (base_y - strikethrough_position) * num_glyphs;
478   new_rect.height = strikethrough_thickness * num_glyphs;
479 
480   if (state->strikethrough)
481     {
482       current_rect->width = new_rect.x + new_rect.width - current_rect->x;
483       current_rect->y += new_rect.y;
484       current_rect->height += new_rect.height;
485       state->strikethrough_glyphs += num_glyphs;
486     }
487   else
488     {
489       *current_rect = new_rect;
490       state->strikethrough = TRUE;
491       state->strikethrough_glyphs = num_glyphs;
492     }
493 }
494 
495 static void
get_item_properties(PangoItem * item,gint * rise,PangoAttrShape ** shape_attr)496 get_item_properties (PangoItem       *item,
497                      gint            *rise,
498                      PangoAttrShape **shape_attr)
499 {
500   GSList *l;
501 
502   if (rise)
503     *rise = 0;
504 
505   if (shape_attr)
506     *shape_attr = NULL;
507 
508   for (l = item->analysis.extra_attrs; l; l = l->next)
509     {
510       PangoAttribute *attr = l->data;
511 
512       switch ((int) attr->klass->type)
513         {
514         case PANGO_ATTR_SHAPE:
515           if (shape_attr)
516             *shape_attr = (PangoAttrShape *)attr;
517           break;
518 
519         case PANGO_ATTR_RISE:
520           if (rise)
521             *rise = ((PangoAttrInt *)attr)->value;
522           break;
523 
524         default:
525           break;
526         }
527     }
528 }
529 
530 static void
draw_shaped_glyphs(PangoRenderer * renderer,PangoGlyphString * glyphs,PangoAttrShape * attr,int x,int y)531 draw_shaped_glyphs (PangoRenderer    *renderer,
532                     PangoGlyphString *glyphs,
533                     PangoAttrShape   *attr,
534                     int               x,
535                     int               y)
536 {
537   PangoRendererClass *class = PANGO_RENDERER_GET_CLASS (renderer);
538   int i;
539 
540   if (!class->draw_shape)
541     return;
542 
543   for (i = 0; i < glyphs->num_glyphs; i++)
544     {
545       PangoGlyphInfo *gi = &glyphs->glyphs[i];
546 
547       class->draw_shape (renderer, attr, x, y);
548 
549       x += gi->geometry.width;
550     }
551 }
552 
553 
554 /**
555  * pango_renderer_draw_layout_line:
556  * @renderer: a `PangoRenderer`
557  * @line: a `PangoLayoutLine`
558  * @x: X position of left edge of baseline, in user space coordinates
559  *   in Pango units.
560  * @y: Y position of left edge of baseline, in user space coordinates
561  *   in Pango units.
562  *
563  * Draws @line with the specified `PangoRenderer`.
564  *
565  * This draws the glyph items that make up the line, as well as
566  * shapes, backgrounds and lines that are specified by the attributes
567  * of those items.
568  *
569  * Since: 1.8
570  */
571 void
pango_renderer_draw_layout_line(PangoRenderer * renderer,PangoLayoutLine * line,int x,int y)572 pango_renderer_draw_layout_line (PangoRenderer   *renderer,
573                                  PangoLayoutLine *line,
574                                  int              x,
575                                  int              y)
576 {
577   int x_off = 0;
578   int glyph_string_width;
579   LineState state;
580   GSList *l;
581   gboolean got_overall = FALSE;
582   PangoRectangle overall_rect;
583   const char *text;
584 
585   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
586 
587   /* We only change the matrix if the renderer isn't already
588    * active.
589    */
590   if (!renderer->active_count)
591     pango_renderer_set_matrix (renderer,
592                                G_LIKELY (line->layout) ?
593                                pango_context_get_matrix
594                                (pango_layout_get_context (line->layout)) :
595                                NULL);
596 
597   pango_renderer_activate (renderer);
598 
599   renderer->priv->line = line;
600   renderer->priv->line_state = &state;
601 
602   state.underline = PANGO_UNDERLINE_NONE;
603   state.overline = PANGO_OVERLINE_NONE;
604   state.strikethrough = FALSE;
605 
606   text = G_LIKELY (line->layout) ? pango_layout_get_text (line->layout) : NULL;
607 
608   for (l = line->runs; l; l = l->next)
609     {
610       PangoFontMetrics *metrics;
611       gint rise;
612       PangoLayoutRun *run = l->data;
613       PangoAttrShape *shape_attr;
614       PangoRectangle ink_rect, *ink = NULL;
615       PangoRectangle logical_rect, *logical = NULL;
616 
617       if (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE)
618         logical = &logical_rect;
619 
620       pango_renderer_prepare_run (renderer, run);
621 
622       get_item_properties (run->item, &rise, &shape_attr);
623 
624       if (shape_attr)
625         {
626           ink = &ink_rect;
627           logical = &logical_rect;
628           _pango_shape_get_extents (run->glyphs->num_glyphs,
629                                     &shape_attr->ink_rect,
630                                     &shape_attr->logical_rect,
631                                     ink,
632                                     logical);
633           glyph_string_width = logical->width;
634         }
635       else
636         {
637           if (renderer->underline != PANGO_UNDERLINE_NONE ||
638               renderer->priv->overline != PANGO_OVERLINE_NONE ||
639               renderer->strikethrough)
640             {
641               ink = &ink_rect;
642               logical = &logical_rect;
643             }
644           if (G_UNLIKELY (ink || logical))
645             pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
646                                         ink, logical);
647           if (logical)
648             glyph_string_width = logical_rect.width;
649           else
650             glyph_string_width = pango_glyph_string_get_width (run->glyphs);
651         }
652 
653       state.logical_rect_end = x + x_off + glyph_string_width;
654 
655       if (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE)
656         {
657           gboolean is_hinted = ((logical_rect.y | logical_rect.height) & (PANGO_SCALE - 1)) == 0;
658           int adjustment = logical_rect.y + logical_rect.height / 2;
659 
660           if (is_hinted)
661             adjustment = PANGO_UNITS_ROUND (adjustment);
662 
663           rise += adjustment;
664         }
665 
666 
667       if (renderer->priv->color_set[PANGO_RENDER_PART_BACKGROUND])
668         {
669           if (!got_overall)
670             {
671               pango_layout_line_get_extents (line, NULL, &overall_rect);
672               got_overall = TRUE;
673             }
674 
675           pango_renderer_draw_rectangle (renderer,
676                                          PANGO_RENDER_PART_BACKGROUND,
677                                          x + x_off,
678                                          y + overall_rect.y,
679                                          glyph_string_width,
680                                          overall_rect.height);
681         }
682 
683       if (shape_attr)
684         {
685           draw_shaped_glyphs (renderer, run->glyphs, shape_attr, x + x_off, y - rise);
686         }
687       else
688         {
689           pango_renderer_draw_glyph_item (renderer,
690                                           text,
691                                           run,
692                                           x + x_off, y - rise);
693         }
694 
695       if (renderer->underline != PANGO_UNDERLINE_NONE ||
696           renderer->priv->overline != PANGO_OVERLINE_NONE ||
697           renderer->strikethrough)
698         {
699           metrics = pango_font_get_metrics (run->item->analysis.font,
700                                             run->item->analysis.language);
701 
702           if (renderer->underline != PANGO_UNDERLINE_NONE)
703             add_underline (renderer, &state,metrics,
704                            x + x_off, y - rise,
705                            ink, logical);
706 
707           if (renderer->priv->overline != PANGO_OVERLINE_NONE)
708             add_overline (renderer, &state,metrics,
709                            x + x_off, y - rise,
710                            ink, logical);
711 
712           if (renderer->strikethrough)
713             add_strikethrough (renderer, &state, metrics,
714                                x + x_off, y - rise,
715                                ink, logical, run->glyphs->num_glyphs);
716 
717           pango_font_metrics_unref (metrics);
718         }
719 
720       if (renderer->underline == PANGO_UNDERLINE_NONE &&
721           state.underline != PANGO_UNDERLINE_NONE)
722         draw_underline (renderer, &state);
723 
724       if (renderer->priv->overline == PANGO_OVERLINE_NONE &&
725           state.overline != PANGO_OVERLINE_NONE)
726         draw_overline (renderer, &state);
727 
728       if (!renderer->strikethrough && state.strikethrough)
729         draw_strikethrough (renderer, &state);
730 
731       x_off += glyph_string_width;
732     }
733 
734   /* Finish off any remaining underlines
735    */
736   draw_underline (renderer, &state);
737   draw_overline (renderer, &state);
738   draw_strikethrough (renderer, &state);
739 
740   renderer->priv->line_state = NULL;
741   renderer->priv->line = NULL;
742 
743   pango_renderer_deactivate (renderer);
744 }
745 
746 /**
747  * pango_renderer_draw_glyphs:
748  * @renderer: a `PangoRenderer`
749  * @font: a `PangoFont`
750  * @glyphs: a `PangoGlyphString`
751  * @x: X position of left edge of baseline, in user space coordinates
752  *   in Pango units.
753  * @y: Y position of left edge of baseline, in user space coordinates
754  *   in Pango units.
755  *
756  * Draws the glyphs in @glyphs with the specified `PangoRenderer`.
757  *
758  * Since: 1.8
759  */
760 void
pango_renderer_draw_glyphs(PangoRenderer * renderer,PangoFont * font,PangoGlyphString * glyphs,int x,int y)761 pango_renderer_draw_glyphs (PangoRenderer    *renderer,
762                             PangoFont        *font,
763                             PangoGlyphString *glyphs,
764                             int               x,
765                             int               y)
766 {
767   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
768 
769   pango_renderer_activate (renderer);
770 
771   PANGO_RENDERER_GET_CLASS (renderer)->draw_glyphs (renderer, font, glyphs, x, y);
772 
773   pango_renderer_deactivate (renderer);
774 }
775 
776 static void
pango_renderer_default_draw_glyphs(PangoRenderer * renderer,PangoFont * font,PangoGlyphString * glyphs,int x,int y)777 pango_renderer_default_draw_glyphs (PangoRenderer    *renderer,
778                                     PangoFont        *font,
779                                     PangoGlyphString *glyphs,
780                                     int               x,
781                                     int               y)
782 {
783   int i;
784   int x_position = 0;
785 
786   for (i = 0; i < glyphs->num_glyphs; i++)
787     {
788       PangoGlyphInfo *gi = &glyphs->glyphs[i];
789       Point p;
790 
791       to_device (renderer->matrix,
792                  x + x_position + gi->geometry.x_offset,
793                  y +              gi->geometry.y_offset,
794                  &p);
795 
796       pango_renderer_draw_glyph (renderer, font, gi->glyph, p.x, p.y);
797 
798       x_position += gi->geometry.width;
799     }
800 }
801 
802 /**
803  * pango_renderer_draw_glyph_item:
804  * @renderer: a `PangoRenderer`
805  * @text: (nullable): the UTF-8 text that @glyph_item refers to
806  * @glyph_item: a `PangoGlyphItem`
807  * @x: X position of left edge of baseline, in user space coordinates
808  *   in Pango units
809  * @y: Y position of left edge of baseline, in user space coordinates
810  *   in Pango units
811  *
812  * Draws the glyphs in @glyph_item with the specified `PangoRenderer`,
813  * embedding the text associated with the glyphs in the output if the
814  * output format supports it.
815  *
816  * This is useful for rendering text in PDF.
817  *
818  * Note that this method does not handle attributes in @glyph_item.
819  * If you want colors, shapes and lines handled automatically according
820  * to those attributes, you need to use pango_renderer_draw_layout_line()
821  * or pango_renderer_draw_layout().
822  *
823  * Note that @text is the start of the text for layout, which is then
824  * indexed by `glyph_item->item->offset`.
825  *
826  * If @text is %NULL, this simply calls [method@Pango.Renderer.draw_glyphs].
827  *
828  * The default implementation of this method simply falls back to
829  * [method@Pango.Renderer.draw_glyphs].
830  *
831  * Since: 1.22
832  */
833 void
pango_renderer_draw_glyph_item(PangoRenderer * renderer,const char * text,PangoGlyphItem * glyph_item,int x,int y)834 pango_renderer_draw_glyph_item (PangoRenderer  *renderer,
835                                 const char     *text,
836                                 PangoGlyphItem *glyph_item,
837                                 int             x,
838                                 int             y)
839 {
840   if (!text)
841     {
842       pango_renderer_draw_glyphs (renderer,
843                                   glyph_item->item->analysis.font,
844                                   glyph_item->glyphs,
845                                   x, y);
846       return;
847     }
848 
849   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
850 
851   pango_renderer_activate (renderer);
852 
853   PANGO_RENDERER_GET_CLASS (renderer)->draw_glyph_item (renderer, text, glyph_item, x, y);
854 
855   pango_renderer_deactivate (renderer);
856 }
857 
858 static void
pango_renderer_default_draw_glyph_item(PangoRenderer * renderer,const char * text G_GNUC_UNUSED,PangoGlyphItem * glyph_item,int x,int y)859 pango_renderer_default_draw_glyph_item (PangoRenderer  *renderer,
860                                         const char     *text G_GNUC_UNUSED,
861                                         PangoGlyphItem *glyph_item,
862                                         int             x,
863                                         int             y)
864 {
865   pango_renderer_draw_glyphs (renderer,
866                               glyph_item->item->analysis.font,
867                               glyph_item->glyphs,
868                               x, y);
869 }
870 
871 /**
872  * pango_renderer_draw_rectangle:
873  * @renderer: a `PangoRenderer`
874  * @part: type of object this rectangle is part of
875  * @x: X position at which to draw rectangle, in user space coordinates
876  *   in Pango units
877  * @y: Y position at which to draw rectangle, in user space coordinates
878  *   in Pango units
879  * @width: width of rectangle in Pango units
880  * @height: height of rectangle in Pango units
881  *
882  * Draws an axis-aligned rectangle in user space coordinates with the
883  * specified `PangoRenderer`.
884  *
885  * This should be called while @renderer is already active.
886  * Use [method@Pango.Renderer.activate] to activate a renderer.
887  *
888  * Since: 1.8
889  */
890 void
pango_renderer_draw_rectangle(PangoRenderer * renderer,PangoRenderPart part,int x,int y,int width,int height)891 pango_renderer_draw_rectangle (PangoRenderer   *renderer,
892                                PangoRenderPart  part,
893                                int              x,
894                                int              y,
895                                int              width,
896                                int              height)
897 {
898   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
899   g_return_if_fail (IS_VALID_PART (part));
900   g_return_if_fail (renderer->active_count > 0);
901 
902   PANGO_RENDERER_GET_CLASS (renderer)->draw_rectangle (renderer, part, x, y, width, height);
903 }
904 
905 static int
compare_points(const void * a,const void * b)906 compare_points (const void *a,
907                 const void *b)
908 {
909   const Point *pa = a;
910   const Point *pb = b;
911 
912   if (pa->y < pb->y)
913     return -1;
914   else if (pa->y > pb->y)
915     return 1;
916   else if (pa->x < pb->x)
917     return -1;
918   else if (pa->x > pb->x)
919     return 1;
920   else
921     return 0;
922 }
923 
924 static void
draw_rectangle(PangoRenderer * renderer,PangoMatrix * matrix,PangoRenderPart part,int x,int y,int width,int height)925 draw_rectangle (PangoRenderer   *renderer,
926                 PangoMatrix     *matrix,
927                 PangoRenderPart  part,
928                 int              x,
929                 int              y,
930                 int              width,
931                 int              height)
932 {
933   Point points[4];
934 
935   /* Convert the points to device coordinates, and sort
936    * in ascending Y order. (Ordering by X for ties)
937    */
938   to_device (matrix, x, y, &points[0]);
939   to_device (matrix, x + width, y, &points[1]);
940   to_device (matrix, x, y + height, &points[2]);
941   to_device (matrix, x + width, y + height, &points[3]);
942 
943   qsort (points, 4, sizeof (Point), compare_points);
944 
945   /* There are essentially three cases. (There is a fourth
946    * case where trapezoid B is degenerate and we just have
947    * two triangles, but we don't need to handle it separately.)
948    *
949    *     1            2             3
950    *
951    *     ______       /\           /\
952    *    /     /      /A \         /A \
953    *   /  B  /      /____\       /____\
954    *  /_____/      /  B  /       \  B  \
955    *              /_____/         \_____\
956    *              \ C  /           \ C  /
957    *               \  /             \  /
958    *                \/               \/
959    */
960   if (points[0].y == points[1].y)
961     {
962      /* Case 1 (pure shear) */
963       pango_renderer_draw_trapezoid (renderer, part,                                      /* B */
964                                      points[0].y, points[0].x, points[1].x,
965                                      points[2].y, points[2].x, points[3].x);
966     }
967   else if (points[1].x < points[2].x)
968     {
969       /* Case 2 */
970       double tmp_width = ((points[2].x - points[0].x) * (points[1].y - points[0].y)) / (points[2].y - points[0].y);
971       double base_width = tmp_width + points[0].x - points[1].x;
972 
973       pango_renderer_draw_trapezoid (renderer, part,                                      /* A */
974                                      points[0].y, points[0].x, points[0].x,
975                                      points[1].y, points[1].x, points[1].x + base_width);
976       pango_renderer_draw_trapezoid (renderer, part,                                      /* B */
977                                      points[1].y, points[1].x, points[1].x + base_width,
978                                      points[2].y, points[2].x - base_width, points[2].x);
979       pango_renderer_draw_trapezoid (renderer, part,                                      /* C */
980                                      points[2].y, points[2].x - base_width, points[2].x,
981                                      points[3].y, points[3].x, points[3].x);
982     }
983   else
984     {
985       /* case 3 */
986       double tmp_width = ((points[0].x - points[2].x) * (points[1].y - points[0].y)) / (points[2].y - points[0].y);
987       double base_width = tmp_width + points[1].x - points[0].x;
988 
989       pango_renderer_draw_trapezoid (renderer, part,                                     /* A */
990                                      points[0].y, points[0].x, points[0].x,
991                                      points[1].y,  points[1].x - base_width, points[1].x);
992       pango_renderer_draw_trapezoid (renderer, part,                                     /* B */
993                                      points[1].y, points[1].x - base_width, points[1].x,
994                                      points[2].y, points[2].x, points[2].x + base_width);
995       pango_renderer_draw_trapezoid (renderer, part,                                     /* C */
996                                      points[2].y, points[2].x, points[2].x + base_width,
997                                      points[3].y, points[3].x, points[3].x);
998     }
999 }
1000 
1001 static void
pango_renderer_default_draw_rectangle(PangoRenderer * renderer,PangoRenderPart part,int x,int y,int width,int height)1002 pango_renderer_default_draw_rectangle (PangoRenderer  *renderer,
1003                                        PangoRenderPart part,
1004                                        int             x,
1005                                        int             y,
1006                                        int             width,
1007                                        int             height)
1008 {
1009   draw_rectangle (renderer, renderer->matrix, part, x, y, width, height);
1010 }
1011 
1012 /**
1013  * pango_renderer_draw_error_underline:
1014  * @renderer: a `PangoRenderer`
1015  * @x: X coordinate of underline, in Pango units in user coordinate system
1016  * @y: Y coordinate of underline, in Pango units in user coordinate system
1017  * @width: width of underline, in Pango units in user coordinate system
1018  * @height: height of underline, in Pango units in user coordinate system
1019  *
1020  * Draw a squiggly line that approximately covers the given rectangle
1021  * in the style of an underline used to indicate a spelling error.
1022  *
1023  * The width of the underline is rounded to an integer number
1024  * of up/down segments and the resulting rectangle is centered
1025  * in the original rectangle.
1026  *
1027  * This should be called while @renderer is already active.
1028  * Use [method@Pango.Renderer.activate] to activate a renderer.
1029  *
1030  * Since: 1.8
1031  */
1032 void
pango_renderer_draw_error_underline(PangoRenderer * renderer,int x,int y,int width,int height)1033 pango_renderer_draw_error_underline (PangoRenderer *renderer,
1034                                      int            x,
1035                                      int            y,
1036                                      int            width,
1037                                      int            height)
1038 {
1039   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1040   g_return_if_fail (renderer->active_count > 0);
1041 
1042   PANGO_RENDERER_GET_CLASS (renderer)->draw_error_underline (renderer, x, y, width, height);
1043 }
1044 
1045 /* We are drawing an error underline that looks like one of:
1046  *
1047  *  /\      /\      /\        /\      /\               -
1048  * /  \    /  \    /  \      /  \    /  \              |
1049  * \   \  /\   \  /   /      \   \  /\   \             |
1050  *  \   \/B \   \/ C /        \   \/B \   \            | height = HEIGHT_SQUARES * square
1051  *   \ A \  /\ A \  /          \ A \  /\ A \           |
1052  *    \   \/  \   \/            \   \/  \   \          |
1053  *     \  /    \  /              \  /    \  /          |
1054  *      \/      \/                \/      \/           -
1055  *      |---|
1056  *    unit_width = (HEIGHT_SQUARES - 1) * square
1057  *
1058  * To do this conveniently, we work in a coordinate system where A,B,C
1059  * are axis aligned rectangles. (If fonts were square, the diagrams
1060  * would be clearer)
1061  *
1062  *             (0,0)
1063  *              /\      /\
1064  *             /  \    /  \
1065  *            /\  /\  /\  /
1066  *           /  \/  \/  \/
1067  *          /    \  /\  /
1068  *      Y axis    \/  \/
1069  *                 \  /\
1070  *                  \/  \
1071  *                       \ X axis
1072  *
1073  * Note that the long side in this coordinate system is HEIGHT_SQUARES + 1
1074  * units long
1075  *
1076  * The diagrams above are shown with HEIGHT_SQUARES an integer, but
1077  * that is actually incidental; the value 2.5 below seems better than
1078  * either HEIGHT_SQUARES=3 (a little long and skinny) or
1079  * HEIGHT_SQUARES=2 (a bit short and stubby)
1080  */
1081 
1082 #define HEIGHT_SQUARES 2.5
1083 
1084 static void
get_total_matrix(PangoMatrix * total,const PangoMatrix * global,int x,int y,int square)1085 get_total_matrix (PangoMatrix       *total,
1086                   const PangoMatrix *global,
1087                   int                x,
1088                   int                y,
1089                   int                square)
1090 {
1091   PangoMatrix local;
1092   gdouble scale = 0.5 * square;
1093 
1094   /* The local matrix translates from the axis aligned coordinate system
1095    * to the original user space coordinate system.
1096    */
1097   local.xx = scale;
1098   local.xy = - scale;
1099   local.yx = scale;
1100   local.yy = scale;
1101   local.x0 = 0;
1102   local.y0 = 0;
1103 
1104   *total = *global;
1105   pango_matrix_concat (total, &local);
1106 
1107   total->x0 = (global->xx * x + global->xy * y) / PANGO_SCALE + global->x0;
1108   total->y0 = (global->yx * x + global->yy * y) / PANGO_SCALE + global->y0;
1109 }
1110 
1111 static void
pango_renderer_default_draw_error_underline(PangoRenderer * renderer,int x,int y,int width,int height)1112 pango_renderer_default_draw_error_underline (PangoRenderer *renderer,
1113                                              int            x,
1114                                              int            y,
1115                                              int            width,
1116                                              int            height)
1117 {
1118   int square;
1119   int unit_width;
1120   int width_units;
1121   const PangoMatrix identity = PANGO_MATRIX_INIT;
1122   const PangoMatrix *matrix;
1123   double dx, dx0, dy0;
1124   PangoMatrix total;
1125   int i;
1126 
1127   if (width <= 0 || height <= 0)
1128     return;
1129 
1130   square = height / HEIGHT_SQUARES;
1131   unit_width = (HEIGHT_SQUARES - 1) * square;
1132   width_units = (width + unit_width / 2) / unit_width;
1133 
1134   x += (width - width_units * unit_width) / 2;
1135 
1136   if (renderer->matrix)
1137     matrix = renderer->matrix;
1138   else
1139     matrix = &identity;
1140 
1141   get_total_matrix (&total, matrix, x, y, square);
1142   dx = unit_width * 2;
1143   dx0 = (matrix->xx * dx) / PANGO_SCALE;
1144   dy0 = (matrix->yx * dx) / PANGO_SCALE;
1145 
1146   i = (width_units - 1) / 2;
1147   while (TRUE)
1148     {
1149       draw_rectangle (renderer, &total, PANGO_RENDER_PART_UNDERLINE, /* A */
1150                       0,                      0,
1151                       HEIGHT_SQUARES * 2 - 1, 1);
1152 
1153       if (i <= 0)
1154         break;
1155       i--;
1156 
1157       draw_rectangle (renderer, &total, PANGO_RENDER_PART_UNDERLINE, /* B */
1158                       HEIGHT_SQUARES * 2 - 2, - (HEIGHT_SQUARES * 2 - 3),
1159                       1,                      HEIGHT_SQUARES * 2 - 3);
1160 
1161       total.x0 += dx0;
1162       total.y0 += dy0;
1163     }
1164   if (width_units % 2 == 0)
1165     {
1166       draw_rectangle (renderer, &total, PANGO_RENDER_PART_UNDERLINE, /* C */
1167                       HEIGHT_SQUARES * 2 - 2, - (HEIGHT_SQUARES * 2 - 2),
1168                       1,                      HEIGHT_SQUARES * 2 - 2);
1169     }
1170 }
1171 
1172 /**
1173  * pango_renderer_draw_trapezoid:
1174  * @renderer: a `PangoRenderer`
1175  * @part: type of object this trapezoid is part of
1176  * @y1_: Y coordinate of top of trapezoid
1177  * @x11: X coordinate of left end of top of trapezoid
1178  * @x21: X coordinate of right end of top of trapezoid
1179  * @y2: Y coordinate of bottom of trapezoid
1180  * @x12: X coordinate of left end of bottom of trapezoid
1181  * @x22: X coordinate of right end of bottom of trapezoid
1182  *
1183  * Draws a trapezoid with the parallel sides aligned with the X axis
1184  * using the given `PangoRenderer`; coordinates are in device space.
1185  *
1186  * Since: 1.8
1187  */
1188 void
pango_renderer_draw_trapezoid(PangoRenderer * renderer,PangoRenderPart part,double y1_,double x11,double x21,double y2,double x12,double x22)1189 pango_renderer_draw_trapezoid (PangoRenderer   *renderer,
1190                                PangoRenderPart  part,
1191                                double           y1_,
1192                                double           x11,
1193                                double           x21,
1194                                double           y2,
1195                                double           x12,
1196                                double           x22)
1197 {
1198   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1199   g_return_if_fail (renderer->active_count > 0);
1200 
1201   if (PANGO_RENDERER_GET_CLASS (renderer)->draw_trapezoid)
1202     PANGO_RENDERER_GET_CLASS (renderer)->draw_trapezoid (renderer, part,
1203                                                          y1_, x11, x21,
1204                                                          y2, x12, x22);
1205 }
1206 
1207 /**
1208  * pango_renderer_draw_glyph:
1209  * @renderer: a `PangoRenderer`
1210  * @font: a `PangoFont`
1211  * @glyph: the glyph index of a single glyph
1212  * @x: X coordinate of left edge of baseline of glyph
1213  * @y: Y coordinate of left edge of baseline of glyph
1214  *
1215  * Draws a single glyph with coordinates in device space.
1216  *
1217  * Since: 1.8
1218  */
1219 void
pango_renderer_draw_glyph(PangoRenderer * renderer,PangoFont * font,PangoGlyph glyph,double x,double y)1220 pango_renderer_draw_glyph (PangoRenderer *renderer,
1221                            PangoFont     *font,
1222                            PangoGlyph     glyph,
1223                            double         x,
1224                            double         y)
1225 {
1226   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1227   g_return_if_fail (renderer->active_count > 0);
1228 
1229   if (glyph == PANGO_GLYPH_EMPTY) /* glyph PANGO_GLYPH_EMPTY never renders */
1230     return;
1231 
1232   if (PANGO_RENDERER_GET_CLASS (renderer)->draw_glyph)
1233     PANGO_RENDERER_GET_CLASS (renderer)->draw_glyph (renderer, font, glyph, x, y);
1234 }
1235 
1236 /**
1237  * pango_renderer_activate:
1238  * @renderer: a `PangoRenderer`
1239  *
1240  * Does initial setup before rendering operations on @renderer.
1241  *
1242  * [method@Pango.Renderer.deactivate] should be called when done drawing.
1243  * Calls such as [method@Pango.Renderer.draw_layout] automatically
1244  * activate the layout before drawing on it.
1245  *
1246  * Calls to [method@Pango.Renderer.activate] and
1247  * [method@Pango.Renderer.deactivate] can be nested and the
1248  * renderer will only be initialized and deinitialized once.
1249  *
1250  * Since: 1.8
1251  */
1252 void
pango_renderer_activate(PangoRenderer * renderer)1253 pango_renderer_activate (PangoRenderer *renderer)
1254 {
1255   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1256 
1257   renderer->active_count++;
1258   if (renderer->active_count == 1)
1259     {
1260       if (PANGO_RENDERER_GET_CLASS (renderer)->begin)
1261         PANGO_RENDERER_GET_CLASS (renderer)->begin (renderer);
1262     }
1263 }
1264 
1265 /**
1266  * pango_renderer_deactivate:
1267  * @renderer: a `PangoRenderer`
1268  *
1269  * Cleans up after rendering operations on @renderer.
1270  *
1271  * See docs for [method@Pango.Renderer.activate].
1272  *
1273  * Since: 1.8
1274  */
1275 void
pango_renderer_deactivate(PangoRenderer * renderer)1276 pango_renderer_deactivate (PangoRenderer *renderer)
1277 {
1278   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1279   g_return_if_fail (renderer->active_count > 0);
1280 
1281   if (renderer->active_count == 1)
1282     {
1283       if (PANGO_RENDERER_GET_CLASS (renderer)->end)
1284         PANGO_RENDERER_GET_CLASS (renderer)->end (renderer);
1285     }
1286   renderer->active_count--;
1287 }
1288 
1289 /**
1290  * pango_renderer_set_color:
1291  * @renderer: a `PangoRenderer`
1292  * @part: the part to change the color of
1293  * @color: (nullable): the new color or %NULL to unset the current color
1294  *
1295  * Sets the color for part of the rendering.
1296  *
1297  * Also see [method@Pango.Renderer.set_alpha].
1298  *
1299  * Since: 1.8
1300  */
1301 void
pango_renderer_set_color(PangoRenderer * renderer,PangoRenderPart part,const PangoColor * color)1302 pango_renderer_set_color (PangoRenderer    *renderer,
1303                           PangoRenderPart   part,
1304                           const PangoColor *color)
1305 {
1306   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1307   g_return_if_fail (IS_VALID_PART (part));
1308 
1309   if ((!color && !renderer->priv->color_set[part]) ||
1310       (color && renderer->priv->color_set[part] &&
1311        renderer->priv->color[part].red == color->red &&
1312        renderer->priv->color[part].green == color->green &&
1313        renderer->priv->color[part].blue == color->blue))
1314     return;
1315 
1316   pango_renderer_part_changed (renderer, part);
1317 
1318   if (color)
1319     {
1320       renderer->priv->color_set[part] = TRUE;
1321       renderer->priv->color[part] = *color;
1322     }
1323   else
1324     {
1325       renderer->priv->color_set[part] = FALSE;
1326     }
1327 }
1328 
1329 /**
1330  * pango_renderer_get_color:
1331  * @renderer: a `PangoRenderer`
1332  * @part: the part to get the color for
1333  *
1334  * Gets the current rendering color for the specified part.
1335  *
1336  * Return value: (transfer none) (nullable): the color for the
1337  *   specified part, or %NULL if it hasn't been set and should be
1338  *   inherited from the environment.
1339  *
1340  * Since: 1.8
1341  */
1342 PangoColor *
pango_renderer_get_color(PangoRenderer * renderer,PangoRenderPart part)1343 pango_renderer_get_color (PangoRenderer   *renderer,
1344                           PangoRenderPart  part)
1345 {
1346   g_return_val_if_fail (PANGO_IS_RENDERER_FAST (renderer), NULL);
1347   g_return_val_if_fail (IS_VALID_PART (part), NULL);
1348 
1349   if (renderer->priv->color_set[part])
1350     return &renderer->priv->color[part];
1351   else
1352     return NULL;
1353 }
1354 
1355 /**
1356  * pango_renderer_set_alpha:
1357  * @renderer: a `PangoRenderer`
1358  * @part: the part to set the alpha for
1359  * @alpha: an alpha value between 1 and 65536, or 0 to unset the alpha
1360  *
1361  * Sets the alpha for part of the rendering.
1362  *
1363  * Note that the alpha may only be used if a color is
1364  * specified for @part as well.
1365  *
1366  * Since: 1.38
1367  */
1368 void
pango_renderer_set_alpha(PangoRenderer * renderer,PangoRenderPart part,guint16 alpha)1369 pango_renderer_set_alpha (PangoRenderer   *renderer,
1370                           PangoRenderPart  part,
1371                           guint16          alpha)
1372 {
1373   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1374   g_return_if_fail (IS_VALID_PART (part));
1375 
1376   if ((!alpha && !renderer->priv->alpha[part]) ||
1377       (alpha && renderer->priv->alpha[part] &&
1378        renderer->priv->alpha[part] == alpha))
1379     return;
1380 
1381   pango_renderer_part_changed (renderer, part);
1382 
1383   renderer->priv->alpha[part] = alpha;
1384 }
1385 
1386 /**
1387  * pango_renderer_get_alpha:
1388  * @renderer: a `PangoRenderer`
1389  * @part: the part to get the alpha for
1390  *
1391  * Gets the current alpha for the specified part.
1392  *
1393  * Return value: the alpha for the specified part,
1394  *   or 0 if it hasn't been set and should be
1395  *   inherited from the environment.
1396  *
1397  * Since: 1.38
1398  */
1399 guint16
pango_renderer_get_alpha(PangoRenderer * renderer,PangoRenderPart part)1400 pango_renderer_get_alpha (PangoRenderer   *renderer,
1401                           PangoRenderPart  part)
1402 {
1403   g_return_val_if_fail (PANGO_IS_RENDERER_FAST (renderer), 0);
1404   g_return_val_if_fail (IS_VALID_PART (part), 0);
1405 
1406   return renderer->priv->alpha[part];
1407 }
1408 
1409 /**
1410  * pango_renderer_part_changed:
1411  * @renderer: a `PangoRenderer`
1412  * @part: the part for which rendering has changed.
1413  *
1414  * Informs Pango that the way that the rendering is done
1415  * for @part has changed.
1416  *
1417  * This should be called if the rendering changes in a way that would
1418  * prevent multiple pieces being joined together into one drawing call.
1419  * For instance, if a subclass of `PangoRenderer` was to add a stipple
1420  * option for drawing underlines, it needs to call
1421  *
1422  * ```
1423  * pango_renderer_part_changed (render, PANGO_RENDER_PART_UNDERLINE);
1424  * ```
1425  *
1426  * When the stipple changes or underlines with different stipples
1427  * might be joined together. Pango automatically calls this for
1428  * changes to colors. (See [method@Pango.Renderer.set_color])
1429  *
1430  * Since: 1.8
1431  */
1432 void
pango_renderer_part_changed(PangoRenderer * renderer,PangoRenderPart part)1433 pango_renderer_part_changed (PangoRenderer   *renderer,
1434                              PangoRenderPart  part)
1435 {
1436   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1437   g_return_if_fail (IS_VALID_PART (part));
1438   g_return_if_fail (renderer->active_count > 0);
1439 
1440   handle_line_state_change (renderer, part);
1441 
1442   if (PANGO_RENDERER_GET_CLASS (renderer)->part_changed)
1443     PANGO_RENDERER_GET_CLASS (renderer)->part_changed (renderer, part);
1444 }
1445 
1446 /**
1447  * pango_renderer_prepare_run:
1448  * @renderer: a `PangoRenderer`
1449  * @run: a `PangoLayoutRun`
1450  *
1451  * Set up the state of the `PangoRenderer` for rendering @run.
1452  *
1453  * Since: 1.8
1454  */
1455 static void
pango_renderer_prepare_run(PangoRenderer * renderer,PangoLayoutRun * run)1456 pango_renderer_prepare_run (PangoRenderer  *renderer,
1457                             PangoLayoutRun *run)
1458 {
1459   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1460 
1461   PANGO_RENDERER_GET_CLASS (renderer)->prepare_run (renderer, run);
1462 }
1463 
1464 static void
pango_renderer_default_prepare_run(PangoRenderer * renderer,PangoLayoutRun * run)1465 pango_renderer_default_prepare_run (PangoRenderer  *renderer,
1466                                     PangoLayoutRun *run)
1467 {
1468   PangoColor *fg_color = NULL;
1469   PangoColor *bg_color = NULL;
1470   PangoColor *underline_color = NULL;
1471   PangoColor *overline_color = NULL;
1472   PangoColor *strikethrough_color = NULL;
1473   guint16 fg_alpha = 0;
1474   guint16 bg_alpha = 0;
1475   GSList *l;
1476 
1477   renderer->underline = PANGO_UNDERLINE_NONE;
1478   renderer->priv->overline = PANGO_OVERLINE_NONE;
1479   renderer->strikethrough = FALSE;
1480 
1481   for (l = run->item->analysis.extra_attrs; l; l = l->next)
1482     {
1483       PangoAttribute *attr = l->data;
1484 
1485       switch ((int) attr->klass->type)
1486         {
1487         case PANGO_ATTR_UNDERLINE:
1488           renderer->underline = ((PangoAttrInt *)attr)->value;
1489           break;
1490 
1491         case PANGO_ATTR_OVERLINE:
1492           renderer->priv->overline = ((PangoAttrInt *)attr)->value;
1493           break;
1494 
1495         case PANGO_ATTR_STRIKETHROUGH:
1496           renderer->strikethrough = ((PangoAttrInt *)attr)->value;
1497           break;
1498 
1499         case PANGO_ATTR_FOREGROUND:
1500           fg_color = &((PangoAttrColor *)attr)->color;
1501           break;
1502 
1503         case PANGO_ATTR_BACKGROUND:
1504           bg_color = &((PangoAttrColor *)attr)->color;
1505           break;
1506 
1507         case PANGO_ATTR_UNDERLINE_COLOR:
1508           underline_color = &((PangoAttrColor *)attr)->color;
1509           break;
1510 
1511         case PANGO_ATTR_OVERLINE_COLOR:
1512           overline_color = &((PangoAttrColor *)attr)->color;
1513           break;
1514 
1515         case PANGO_ATTR_STRIKETHROUGH_COLOR:
1516           strikethrough_color = &((PangoAttrColor *)attr)->color;
1517           break;
1518 
1519         case PANGO_ATTR_FOREGROUND_ALPHA:
1520           fg_alpha = ((PangoAttrInt *)attr)->value;
1521           break;
1522 
1523         case PANGO_ATTR_BACKGROUND_ALPHA:
1524           bg_alpha = ((PangoAttrInt *)attr)->value;
1525           break;
1526 
1527         default:
1528           break;
1529         }
1530     }
1531 
1532   if (!underline_color)
1533     underline_color = fg_color;
1534 
1535   if (!overline_color)
1536     overline_color = fg_color;
1537 
1538   if (!strikethrough_color)
1539     strikethrough_color = fg_color;
1540 
1541   pango_renderer_set_color (renderer, PANGO_RENDER_PART_FOREGROUND, fg_color);
1542   pango_renderer_set_color (renderer, PANGO_RENDER_PART_BACKGROUND, bg_color);
1543   pango_renderer_set_color (renderer, PANGO_RENDER_PART_UNDERLINE, underline_color);
1544   pango_renderer_set_color (renderer, PANGO_RENDER_PART_STRIKETHROUGH, strikethrough_color);
1545   pango_renderer_set_color (renderer, PANGO_RENDER_PART_OVERLINE, overline_color);
1546 
1547   pango_renderer_set_alpha (renderer, PANGO_RENDER_PART_FOREGROUND, fg_alpha);
1548   pango_renderer_set_alpha (renderer, PANGO_RENDER_PART_BACKGROUND, bg_alpha);
1549   pango_renderer_set_alpha (renderer, PANGO_RENDER_PART_UNDERLINE, fg_alpha);
1550   pango_renderer_set_alpha (renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_alpha);
1551   pango_renderer_set_alpha (renderer, PANGO_RENDER_PART_OVERLINE, fg_alpha);
1552 }
1553 
1554 /**
1555  * pango_renderer_set_matrix:
1556  * @renderer: a `PangoRenderer`
1557  * @matrix: (nullable): a `PangoMatrix`, or %NULL to unset any existing matrix
1558  *  (No matrix set is the same as setting the identity matrix.)
1559  *
1560  * Sets the transformation matrix that will be applied when rendering.
1561  *
1562  * Since: 1.8
1563  */
1564 void
pango_renderer_set_matrix(PangoRenderer * renderer,const PangoMatrix * matrix)1565 pango_renderer_set_matrix (PangoRenderer     *renderer,
1566                            const PangoMatrix *matrix)
1567 {
1568   g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer));
1569 
1570   pango_matrix_free (renderer->matrix);
1571   renderer->matrix = pango_matrix_copy (matrix);
1572 }
1573 
1574 /**
1575  * pango_renderer_get_matrix:
1576  * @renderer: a `PangoRenderer`
1577  *
1578  * Gets the transformation matrix that will be applied when
1579  * rendering.
1580  *
1581  * See [method@Pango.Renderer.set_matrix].
1582  *
1583  * Return value: (nullable): the matrix, or %NULL if no matrix has
1584  *   been set (which is the same as the identity matrix). The returned
1585  *   matrix is owned by Pango and must not be modified or freed.
1586  *
1587  * Since: 1.8
1588  */
1589 const PangoMatrix *
pango_renderer_get_matrix(PangoRenderer * renderer)1590 pango_renderer_get_matrix (PangoRenderer *renderer)
1591 {
1592   g_return_val_if_fail (PANGO_IS_RENDERER (renderer), NULL);
1593 
1594   return renderer->matrix;
1595 }
1596 
1597 /**
1598  * pango_renderer_get_layout:
1599  * @renderer: a `PangoRenderer`
1600  *
1601  * Gets the layout currently being rendered using @renderer.
1602  *
1603  * Calling this function only makes sense from inside a subclass's
1604  * methods, like in its draw_shape vfunc, for example.
1605  *
1606  * The returned layout should not be modified while still being
1607  * rendered.
1608  *
1609  * Return value: (transfer none) (nullable): the layout, or %NULL if
1610  *   no layout is being rendered using @renderer at this time.
1611  *
1612  * Since: 1.20
1613  */
1614 PangoLayout *
pango_renderer_get_layout(PangoRenderer * renderer)1615 pango_renderer_get_layout (PangoRenderer *renderer)
1616 {
1617   if (G_UNLIKELY (renderer->priv->line == NULL))
1618     return NULL;
1619 
1620   return renderer->priv->line->layout;
1621 }
1622 
1623 /**
1624  * pango_renderer_get_layout_line:
1625  * @renderer: a `PangoRenderer`
1626  *
1627  * Gets the layout line currently being rendered using @renderer.
1628  *
1629  * Calling this function only makes sense from inside a subclass's
1630  * methods, like in its draw_shape vfunc, for example.
1631  *
1632  * The returned layout line should not be modified while still being
1633  * rendered.
1634  *
1635  * Return value: (transfer none) (nullable): the layout line, or %NULL
1636  *   if no layout line is being rendered using @renderer at this time.
1637  *
1638  * Since: 1.20
1639  */
1640 PangoLayoutLine *
pango_renderer_get_layout_line(PangoRenderer * renderer)1641 pango_renderer_get_layout_line (PangoRenderer *renderer)
1642 {
1643   return renderer->priv->line;
1644 }
1645