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 |
47 | free_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 | |
62 | static PangoCairoContextInfo * |
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 (string: "pango-cairo-context-info" ); |
71 | |
72 | retry: |
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 | |
92 | static 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 | */ |
172 | void |
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 |
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 |
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 |
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 (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 | */ |
300 | const cairo_font_options_t * |
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 * |
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 | */ |
360 | void |
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 |
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 * |
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 * |
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 (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 | */ |
505 | void |
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, context: pango_layout_get_context (layout)); |
513 | } |
514 | |
515 | |