1 /* Pango
2  * pangocairo-fontmap.c: Cairo font handling
3  *
4  * Copyright (C) 2000-2005 Red Hat Software
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 #include "config.h"
23 
24 #include "pangocairo.h"
25 #include "pangocairo-private.h"
26 #include "pango-impl-utils.h"
27 
28 #include <string.h>
29 
30 typedef struct _PangoCairoContextInfo PangoCairoContextInfo;
31 
32 struct _PangoCairoContextInfo
33 {
34   double dpi;
35   gboolean set_options_explicit;
36 
37   cairo_font_options_t *set_options;
38   cairo_font_options_t *surface_options;
39   cairo_font_options_t *merged_options;
40 
41   PangoCairoShapeRendererFunc shape_renderer_func;
42   gpointer                    shape_renderer_data;
43   GDestroyNotify              shape_renderer_notify;
44 };
45 
46 static void
free_context_info(PangoCairoContextInfo * info)47 free_context_info (PangoCairoContextInfo *info)
48 {
49   if (info->set_options)
50     cairo_font_options_destroy (info->set_options);
51   if (info->surface_options)
52     cairo_font_options_destroy (info->surface_options);
53   if (info->merged_options)
54     cairo_font_options_destroy (info->merged_options);
55 
56   if (info->shape_renderer_notify)
57     info->shape_renderer_notify (info->shape_renderer_data);
58 
59   g_slice_free (PangoCairoContextInfo, info);
60 }
61 
62 static PangoCairoContextInfo *
get_context_info(PangoContext * context,gboolean create)63 get_context_info (PangoContext *context,
64 		  gboolean      create)
65 {
66   static GQuark context_info_quark; /* MT-safe */
67   PangoCairoContextInfo *info;
68 
69   if (G_UNLIKELY (!context_info_quark))
70     context_info_quark = g_quark_from_static_string ("pango-cairo-context-info");
71 
72 retry:
73   info = g_object_get_qdata (G_OBJECT (context), context_info_quark);
74 
75   if (G_UNLIKELY (!info) && create)
76     {
77       info = g_slice_new0 (PangoCairoContextInfo);
78       info->dpi = -1.0;
79 
80       if (!g_object_replace_qdata (G_OBJECT (context), context_info_quark, NULL,
81                                    info, (GDestroyNotify)free_context_info,
82                                    NULL))
83         {
84           free_context_info (info);
85           goto retry;
86         }
87     }
88 
89   return info;
90 }
91 
92 static void
_pango_cairo_update_context(cairo_t * cr,PangoContext * context)93 _pango_cairo_update_context (cairo_t      *cr,
94 			     PangoContext *context)
95 {
96   PangoCairoContextInfo *info;
97   cairo_matrix_t cairo_matrix;
98   cairo_surface_t *target;
99   PangoMatrix pango_matrix;
100   const PangoMatrix *current_matrix, identity_matrix = PANGO_MATRIX_INIT;
101   const cairo_font_options_t *merged_options;
102   cairo_font_options_t *old_merged_options;
103   gboolean changed = FALSE;
104 
105   info = get_context_info (context, TRUE);
106 
107   target = cairo_get_target (cr);
108 
109   if (!info->surface_options)
110     info->surface_options = cairo_font_options_create ();
111   cairo_surface_get_font_options (target, info->surface_options);
112   if (!info->set_options_explicit)
113   {
114     if (!info->set_options)
115       info->set_options = cairo_font_options_create ();
116     cairo_get_font_options (cr, info->set_options);
117   }
118 
119   old_merged_options = info->merged_options;
120   info->merged_options = NULL;
121 
122   merged_options = _pango_cairo_context_get_merged_font_options (context);
123 
124   if (old_merged_options)
125     {
126       if (!cairo_font_options_equal (merged_options, old_merged_options))
127 	changed = TRUE;
128       cairo_font_options_destroy (old_merged_options);
129       old_merged_options = NULL;
130     }
131   else
132     changed = TRUE;
133 
134   cairo_get_matrix (cr, &cairo_matrix);
135   pango_matrix.xx = cairo_matrix.xx;
136   pango_matrix.yx = cairo_matrix.yx;
137   pango_matrix.xy = cairo_matrix.xy;
138   pango_matrix.yy = cairo_matrix.yy;
139   pango_matrix.x0 = 0;
140   pango_matrix.y0 = 0;
141 
142   current_matrix = pango_context_get_matrix (context);
143   if (!current_matrix)
144     current_matrix = &identity_matrix;
145 
146   /* layout is matrix-independent if metrics-hinting is off.
147    * also ignore matrix translation offsets */
148   if ((cairo_font_options_get_hint_metrics (merged_options) != CAIRO_HINT_METRICS_OFF) &&
149       (0 != memcmp (&pango_matrix, current_matrix, sizeof (PangoMatrix))))
150     changed = TRUE;
151 
152   pango_context_set_matrix (context, &pango_matrix);
153 
154   if (changed)
155     pango_context_changed (context);
156 }
157 
158 /**
159  * pango_cairo_update_context:
160  * @cr: a Cairo context
161  * @context: a `PangoContext`, from a pangocairo font map
162  *
163  * Updates a `PangoContext` previously created for use with Cairo to
164  * match the current transformation and target surface of a Cairo
165  * context.
166  *
167  * If any layouts have been created for the context, it's necessary
168  * to call [method@Pango.Layout.context_changed] on those layouts.
169  *
170  * Since: 1.10
171  */
172 void
pango_cairo_update_context(cairo_t * cr,PangoContext * context)173 pango_cairo_update_context (cairo_t      *cr,
174 			    PangoContext *context)
175 {
176   g_return_if_fail (cr != NULL);
177   g_return_if_fail (PANGO_IS_CONTEXT (context));
178 
179   _pango_cairo_update_context (cr, context);
180 }
181 
182 /**
183  * pango_cairo_context_set_resolution:
184  * @context: a `PangoContext`, from a pangocairo font map
185  * @dpi: the resolution in "dots per inch". (Physical inches aren't actually
186  *   involved; the terminology is conventional.) A 0 or negative value
187  *   means to use the resolution from the font map.
188  *
189  * Sets the resolution for the context.
190  *
191  * This is a scale factor between points specified in a `PangoFontDescription`
192  * and Cairo units. The default value is 96, meaning that a 10 point font will
193  * be 13 units high. (10 * 96. / 72. = 13.3).
194  *
195  * Since: 1.10
196  */
197 void
pango_cairo_context_set_resolution(PangoContext * context,double dpi)198 pango_cairo_context_set_resolution (PangoContext *context,
199 				    double        dpi)
200 {
201   PangoCairoContextInfo *info = get_context_info (context, TRUE);
202   info->dpi = dpi;
203 }
204 
205 /**
206  * pango_cairo_context_get_resolution:
207  * @context: a `PangoContext`, from a pangocairo font map
208  *
209  * Gets the resolution for the context.
210  *
211  * See [func@PangoCairo.context_set_resolution]
212  *
213  * Return value: the resolution in "dots per inch". A negative value will
214  *   be returned if no resolution has previously been set.
215  *
216  * Since: 1.10
217  */
218 double
pango_cairo_context_get_resolution(PangoContext * context)219 pango_cairo_context_get_resolution (PangoContext *context)
220 {
221   PangoCairoContextInfo *info = get_context_info (context, FALSE);
222 
223   if (info)
224     return info->dpi;
225   else
226     return -1.0;
227 }
228 
229 /**
230  * pango_cairo_context_set_font_options:
231  * @context: a `PangoContext`, from a pangocairo font map
232  * @options: (nullable): a `cairo_font_options_t`, or %NULL to unset
233  *   any previously set options. A copy is made.
234  *
235  * Sets the font options used when rendering text with this context.
236  *
237  * These options override any options that [func@update_context]
238  * derives from the target surface.
239  *
240  * Since: 1.10
241  */
242 void
pango_cairo_context_set_font_options(PangoContext * context,const cairo_font_options_t * options)243 pango_cairo_context_set_font_options (PangoContext               *context,
244 				      const cairo_font_options_t *options)
245 {
246   PangoCairoContextInfo *info;
247 
248   g_return_if_fail (PANGO_IS_CONTEXT (context));
249 
250   info = get_context_info (context, TRUE);
251 
252   if (!info->set_options && !options)
253     return;
254 
255   if (info->set_options &&
256       options &&
257       cairo_font_options_equal (info->set_options, options))
258     return;
259 
260   if (info->set_options || options)
261     pango_context_changed (context);
262 
263  if (info->set_options)
264     cairo_font_options_destroy (info->set_options);
265 
266   if (options)
267   {
268     info->set_options = cairo_font_options_copy (options);
269     info->set_options_explicit = TRUE;
270   }
271   else
272   {
273     info->set_options = NULL;
274     info->set_options_explicit = FALSE;
275   }
276 
277   if (info->merged_options)
278     {
279       cairo_font_options_destroy (info->merged_options);
280       info->merged_options = NULL;
281     }
282 }
283 
284 /**
285  * pango_cairo_context_get_font_options:
286  * @context: a `PangoContext`, from a pangocairo font map
287  *
288  * Retrieves any font rendering options previously set with
289  * [func@PangoCairo.context_set_font_options].
290  *
291  * This function does not report options that are derived from
292  * the target surface by [func@update_context].
293  *
294  * Return value: (nullable): the font options previously set on the
295  *   context, or %NULL if no options have been set. This value is
296  *   owned by the context and must not be modified or freed.
297  *
298  * Since: 1.10
299  */
300 const cairo_font_options_t *
pango_cairo_context_get_font_options(PangoContext * context)301 pango_cairo_context_get_font_options (PangoContext *context)
302 {
303   PangoCairoContextInfo *info;
304 
305   g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL);
306 
307   info = get_context_info (context, FALSE);
308 
309   if (info)
310     return info->set_options;
311   else
312     return NULL;
313 }
314 
315 /**
316  * _pango_cairo_context_merge_font_options:
317  * @context: a `PangoContext`
318  * @options: a `cairo_font_options_t`
319  *
320  * Merge together options from the target surface and explicitly set
321  * on the context.
322  *
323  * Return value: the combined set of font options. This value is owned
324  *   by the context and must not be modified or freed.
325  */
326 const cairo_font_options_t *
_pango_cairo_context_get_merged_font_options(PangoContext * context)327 _pango_cairo_context_get_merged_font_options (PangoContext *context)
328 {
329   PangoCairoContextInfo *info = get_context_info (context, TRUE);
330 
331   if (!info->merged_options)
332     {
333       info->merged_options = cairo_font_options_create ();
334 
335       if (info->surface_options)
336 	cairo_font_options_merge (info->merged_options, info->surface_options);
337       if (info->set_options)
338 	cairo_font_options_merge (info->merged_options, info->set_options);
339     }
340 
341   return info->merged_options;
342 }
343 
344 /**
345  * pango_cairo_context_set_shape_renderer:
346  * @context: a `PangoContext`, from a pangocairo font map
347  * @func: (nullable): Callback function for rendering attributes of
348  *   type %PANGO_ATTR_SHAPE, or %NULL to disable shape rendering.
349  * @data: (nullable): User data that will be passed to @func.
350  * @dnotify: (nullable): Callback that will be called when the
351  *   context is freed to release @data
352  *
353  * Sets callback function for context to use for rendering attributes
354  * of type %PANGO_ATTR_SHAPE.
355  *
356  * See `PangoCairoShapeRendererFunc` for details.
357  *
358  * Since: 1.18
359  */
360 void
pango_cairo_context_set_shape_renderer(PangoContext * context,PangoCairoShapeRendererFunc func,gpointer data,GDestroyNotify dnotify)361 pango_cairo_context_set_shape_renderer (PangoContext                *context,
362 					PangoCairoShapeRendererFunc  func,
363 					gpointer                     data,
364 					GDestroyNotify               dnotify)
365 {
366   PangoCairoContextInfo *info;
367 
368   g_return_if_fail (PANGO_IS_CONTEXT (context));
369 
370   info  = get_context_info (context, TRUE);
371 
372   if (info->shape_renderer_notify)
373     info->shape_renderer_notify (info->shape_renderer_data);
374 
375   info->shape_renderer_func   = func;
376   info->shape_renderer_data   = data;
377   info->shape_renderer_notify = dnotify;
378 }
379 
380 /**
381  * pango_cairo_context_get_shape_renderer: (skip)
382  * @context: a `PangoContext`, from a pangocairo font map
383  * @data: Pointer to `gpointer` to return user data
384  *
385  * Sets callback function for context to use for rendering attributes
386  * of type %PANGO_ATTR_SHAPE.
387  *
388  * See `PangoCairoShapeRendererFunc` for details.
389  *
390  * Retrieves callback function and associated user data for rendering
391  * attributes of type %PANGO_ATTR_SHAPE as set by
392  * [func@PangoCairo.context_set_shape_renderer], if any.
393  *
394  * Return value: (transfer none) (nullable): the shape rendering callback
395  *   previously set on the context, or %NULL if no shape rendering callback
396  *   have been set.
397  *
398  * Since: 1.18
399  */
400 PangoCairoShapeRendererFunc
pango_cairo_context_get_shape_renderer(PangoContext * context,gpointer * data)401 pango_cairo_context_get_shape_renderer (PangoContext *context,
402                                         gpointer     *data)
403 {
404   PangoCairoContextInfo *info;
405 
406   g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL);
407 
408   info = get_context_info (context, FALSE);
409 
410   if (info)
411     {
412       if (data)
413         *data = info->shape_renderer_data;
414       return info->shape_renderer_func;
415     }
416   else
417     {
418       if (data)
419         *data = NULL;
420       return NULL;
421     }
422 }
423 
424 /**
425  * pango_cairo_create_context:
426  * @cr: a Cairo context
427  *
428  * Creates a context object set up to match the current transformation
429  * and target surface of the Cairo context.
430  *
431  * This context can then be
432  * used to create a layout using [ctor@Pango.Layout.new].
433  *
434  * This function is a convenience function that creates a context using
435  * the default font map, then updates it to @cr. If you just need to
436  * create a layout for use with @cr and do not need to access `PangoContext`
437  * directly, you can use [func@create_layout] instead.
438  *
439  * Return value: (transfer full): the newly created `PangoContext`
440  *
441  * Since: 1.22
442  */
443 PangoContext *
pango_cairo_create_context(cairo_t * cr)444 pango_cairo_create_context (cairo_t *cr)
445 {
446   PangoFontMap *fontmap;
447   PangoContext *context;
448 
449   g_return_val_if_fail (cr != NULL, NULL);
450 
451   fontmap = pango_cairo_font_map_get_default ();
452   context = pango_font_map_create_context (fontmap);
453   pango_cairo_update_context (cr, context);
454 
455   return context;
456 }
457 
458 /**
459  * pango_cairo_create_layout:
460  * @cr: a Cairo context
461  *
462  * Creates a layout object set up to match the current transformation
463  * and target surface of the Cairo context.
464  *
465  * This layout can then be used for text measurement with functions
466  * like [method@Pango.Layout.get_size] or drawing with functions like
467  * [func@show_layout]. If you change the transformation or target
468  * surface for @cr, you need to call [func@update_layout].
469  *
470  * This function is the most convenient way to use Cairo with Pango,
471  * however it is slightly inefficient since it creates a separate
472  * `PangoContext` object for each layout. This might matter in an
473  * application that was laying out large amounts of text.
474  *
475  * Return value: (transfer full): the newly created `PangoLayout`
476  *
477  * Since: 1.10
478  */
479 PangoLayout *
pango_cairo_create_layout(cairo_t * cr)480 pango_cairo_create_layout (cairo_t *cr)
481 {
482   PangoContext *context;
483   PangoLayout *layout;
484 
485   g_return_val_if_fail (cr != NULL, NULL);
486 
487   context = pango_cairo_create_context (cr);
488   layout = pango_layout_new (context);
489   g_object_unref (context);
490 
491   return layout;
492 }
493 
494 /**
495  * pango_cairo_update_layout:
496  * @cr: a Cairo context
497  * @layout: a `PangoLayout`, from [func@create_layout]
498  *
499  * Updates the private `PangoContext` of a `PangoLayout` created with
500  * [func@create_layout] to match the current transformation and target
501  * surface of a Cairo context.
502  *
503  * Since: 1.10
504  */
505 void
pango_cairo_update_layout(cairo_t * cr,PangoLayout * layout)506 pango_cairo_update_layout (cairo_t     *cr,
507 			   PangoLayout *layout)
508 {
509   g_return_if_fail (cr != NULL);
510   g_return_if_fail (PANGO_IS_LAYOUT (layout));
511 
512   _pango_cairo_update_context (cr, pango_layout_get_context (layout));
513 }
514 
515