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
30typedef struct _PangoCairoContextInfo PangoCairoContextInfo;
31
32struct _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
46static void
47free_context_info (PangoCairoContextInfo *info)
48{
49 if (info->set_options)
50 cairo_font_options_destroy (options: info->set_options);
51 if (info->surface_options)
52 cairo_font_options_destroy (options: info->surface_options);
53 if (info->merged_options)
54 cairo_font_options_destroy (options: 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
62static PangoCairoContextInfo *
63get_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 (string: "pango-cairo-context-info");
71
72retry:
73 info = g_object_get_qdata (G_OBJECT (context), quark: 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), quark: context_info_quark, NULL,
81 newval: info, destroy: (GDestroyNotify)free_context_info,
82 NULL))
83 {
84 free_context_info (info);
85 goto retry;
86 }
87 }
88
89 return info;
90}
91
92static void
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 (surface: target, options: 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, options: 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 (options: merged_options, other: old_merged_options))
127 changed = TRUE;
128 cairo_font_options_destroy (options: old_merged_options);
129 old_merged_options = NULL;
130 }
131 else
132 changed = TRUE;
133
134 cairo_get_matrix (cr, matrix: &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 (options: merged_options) != CAIRO_HINT_METRICS_OFF) &&
149 (0 != memcmp (s1: &pango_matrix, s2: current_matrix, n: sizeof (PangoMatrix))))
150 changed = TRUE;
151
152 pango_context_set_matrix (context, matrix: &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 */
172void
173pango_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 */
197void
198pango_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 */
218double
219pango_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 */
242void
243pango_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 (options: info->set_options, other: 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 (options: info->set_options);
265
266 if (options)
267 {
268 info->set_options = cairo_font_options_copy (original: 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 (options: 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 */
300const cairo_font_options_t *
301pango_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 */
326const cairo_font_options_t *
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 (options: info->merged_options, other: info->surface_options);
337 if (info->set_options)
338 cairo_font_options_merge (options: info->merged_options, other: 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 */
360void
361pango_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 */
400PangoCairoShapeRendererFunc
401pango_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 */
443PangoContext *
444pango_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 */
479PangoLayout *
480pango_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 (object: 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 */
505void
506pango_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, context: pango_layout_get_context (layout));
513}
514
515

source code of gtk/subprojects/pango/pango/pangocairo-context.c