blob: d931d1b7056e7c181e96c85b04ebc09ac083d731 [file] [log] [blame]
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -04001/*
2 * Copyright © 2011 Google, Inc.
3 *
4 * This is part of HarfBuzz, a text shaping library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 *
24 * Google Author(s): Behdad Esfahbod
25 */
26
Behdad Esfahbod17f40b72017-10-27 09:22:30 -060027#ifndef HELPER_CAIRO_HH
28#define HELPER_CAIRO_HH
29
Behdad Esfahbod93bc62e2021-08-07 13:13:58 -060030#include "view-options.hh"
31#include "output-options.hh"
Khaled Hosny6add69a2022-12-16 19:54:00 +020032#ifdef HAVE_CAIRO_FT
33# include "helper-cairo-ft.hh"
34#endif
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -040035
Behdad Esfahbod5d2df122022-02-03 17:18:54 -060036#include <cairo.h>
37#include <hb.h>
Matthias Clasen0d6ee462022-12-25 10:50:56 -050038#include <hb-cairo.h>
Behdad Esfahbodc5337c42021-08-06 19:19:50 -060039
40#include "helper-cairo-ansi.hh"
41#ifdef CAIRO_HAS_SVG_SURFACE
42# include <cairo-svg.h>
43#endif
44#ifdef CAIRO_HAS_PDF_SURFACE
45# include <cairo-pdf.h>
46#endif
47#ifdef CAIRO_HAS_PS_SURFACE
48# include <cairo-ps.h>
49# if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0)
50# define HAS_EPS 1
51
52static cairo_surface_t *
53_cairo_eps_surface_create_for_stream (cairo_write_func_t write_func,
54 void *closure,
55 double width,
56 double height)
57{
58 cairo_surface_t *surface;
59
60 surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height);
61 cairo_ps_surface_set_eps (surface, true);
62
63 return surface;
64}
65
66# else
67# undef HAS_EPS
68# endif
69#endif
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -040070
Behdad Esfahbod5d2df122022-02-03 17:18:54 -060071static inline bool
72helper_cairo_use_hb_draw (const font_options_t *font_opts)
Behdad Esfahbodc5337c42021-08-06 19:19:50 -060073{
Behdad Esfahbod6a3dec32022-02-08 17:39:16 -060074 const char *env = getenv ("HB_DRAW");
Behdad Esfahbod9cc9ffe2022-02-08 18:18:47 -060075 if (!env)
Behdad Esfahbod0e4f5792022-10-31 13:51:24 -060076 /* Older cairo had a bug in rendering COLRv0 fonts in
Behdad Esfahbodc54a7022023-01-21 14:07:41 -070077 * right-to-left direction as well as clipping issue
78 * with user-fonts.
79 *
80 * https://github.com/harfbuzz/harfbuzz/issues/4051 */
Behdad Esfahbod9cc9ffe2022-02-08 18:18:47 -060081 return cairo_version () >= CAIRO_VERSION_ENCODE (1, 17, 5);
Behdad Esfahbod0e4f5792022-10-31 13:51:24 -060082
Behdad Esfahbod9cc9ffe2022-02-08 18:18:47 -060083 return atoi (env);
Behdad Esfahbodc5337c42021-08-06 19:19:50 -060084}
Behdad Esfahbod6c0ebd02015-11-05 11:37:48 -080085
Behdad Esfahbodc5337c42021-08-06 19:19:50 -060086static inline cairo_scaled_font_t *
Behdad Esfahbode998cec2023-01-18 23:33:21 -070087helper_cairo_create_scaled_font (const font_options_t *font_opts,
88 const view_options_t *view_opts)
Behdad Esfahbodc5337c42021-08-06 19:19:50 -060089{
Matthias Clasen0d6ee462022-12-25 10:50:56 -050090 hb_font_t *font = font_opts->font;
91 bool use_hb_draw = true;
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -040092
Khaled Hosny6add69a2022-12-16 19:54:00 +020093#ifdef HAVE_CAIRO_FT
Matthias Clasen0d6ee462022-12-25 10:50:56 -050094 use_hb_draw = helper_cairo_use_hb_draw (font_opts);
Khaled Hosny6add69a2022-12-16 19:54:00 +020095#endif
Behdad Esfahbodc5337c42021-08-06 19:19:50 -060096
Matthias Clasen0d6ee462022-12-25 10:50:56 -050097
Khaled Hosny00060d92022-12-30 22:55:56 +020098 cairo_font_face_t *cairo_face = nullptr;
Behdad Esfahbod9f7538c2022-12-25 13:46:37 -070099 if (use_hb_draw)
Behdad Esfahbod5efb3bc2022-12-27 17:47:46 -0700100 {
Behdad Esfahbodcf001f62022-12-25 19:01:28 -0700101 cairo_face = hb_cairo_font_face_create_for_font (font);
Behdad Esfahbod5efb3bc2022-12-27 17:47:46 -0700102 hb_cairo_font_face_set_scale_factor (cairo_face, 1 << font_opts->subpixel_bits);
103 }
Behdad Esfahbod9f7538c2022-12-25 13:46:37 -0700104#ifdef HAVE_CAIRO_FT
105 else
106 cairo_face = helper_cairo_create_ft_font_face (font_opts);
107#endif
Matthias Clasen0d6ee462022-12-25 10:50:56 -0500108
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600109 cairo_matrix_t ctm, font_matrix;
110 cairo_font_options_t *font_options;
111
112 cairo_matrix_init_identity (&ctm);
113 cairo_matrix_init_scale (&font_matrix,
114 font_opts->font_size_x,
115 font_opts->font_size_y);
Behdad Esfahbodea993af2022-12-25 19:17:18 -0700116 if (!use_hb_draw)
117 font_matrix.xy = -font_opts->slant * font_opts->font_size_x;
Behdad Esfahbod5c558582022-02-07 18:54:16 -0600118
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600119 font_options = cairo_font_options_create ();
120 cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
121 cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
Behdad Esfahbod5847ec22023-01-18 22:37:54 -0700122#ifdef CAIRO_COLOR_PALETTE_DEFAULT
Behdad Esfahbod638e0ed2023-01-20 13:01:22 -0700123 cairo_font_options_set_color_palette (font_options, view_opts->palette);
124#endif
Behdad Esfahbodf21b15d2023-01-20 13:38:22 -0700125#ifdef HAVE_CAIRO_FONT_OPTIONS_GET_CUSTOM_PALETTE_COLOR
Behdad Esfahbode998cec2023-01-18 23:33:21 -0700126 if (view_opts->custom_palette)
Matthias Clasenc41892a2023-01-18 23:45:53 -0500127 {
Behdad Esfahbode998cec2023-01-18 23:33:21 -0700128 char **entries = g_strsplit (view_opts->custom_palette, ",", -1);
Behdad Esfahbod61719a82023-01-20 15:52:09 -0700129 unsigned idx = 0;
Behdad Esfahbod03e2e582023-01-20 11:24:35 -0700130 for (unsigned i = 0; entries[i]; i++)
Matthias Clasenc41892a2023-01-18 23:45:53 -0500131 {
Behdad Esfahbod61719a82023-01-20 15:52:09 -0700132 const char *p = strchr (entries[i], '=');
133 if (!p)
134 p = entries[i];
135 else
136 {
137 sscanf (entries[i], "%u", &idx);
138 p++;
139 }
140
Behdad Esfahbod03e2e582023-01-20 11:24:35 -0700141 unsigned fr, fg, fb, fa;
142 fr = fg = fb = fa = 0;
Behdad Esfahbod61719a82023-01-20 15:52:09 -0700143 if (parse_color (p, fr, fg,fb, fa))
144 cairo_font_options_set_custom_palette_color (font_options, idx, fr / 255., fg / 255., fb / 255., fa / 255.);
145
146 idx++;
Matthias Clasenc41892a2023-01-18 23:45:53 -0500147 }
Behdad Esfahbod52b78d52023-01-18 23:06:08 -0700148 g_strfreev (entries);
Matthias Clasenc41892a2023-01-18 23:45:53 -0500149 }
150#endif
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600151
152 cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face,
153 &font_matrix,
154 &ctm,
155 font_options);
156
157 cairo_font_options_destroy (font_options);
158 cairo_font_face_destroy (cairo_face);
159
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600160 return scaled_font;
161}
162
163static inline bool
164helper_cairo_scaled_font_has_color (cairo_scaled_font_t *scaled_font)
165{
Behdad Esfahbod5c3da762022-12-25 15:04:13 -0700166 hb_font_t *font = hb_cairo_font_face_get_font (cairo_scaled_font_get_font_face (scaled_font));
Behdad Esfahbod9f7538c2022-12-25 13:46:37 -0700167
Khaled Hosny6add69a2022-12-16 19:54:00 +0200168#ifdef HAVE_CAIRO_FT
Behdad Esfahbod5c3da762022-12-25 15:04:13 -0700169 if (!font)
Behdad Esfahbod5d2df122022-02-03 17:18:54 -0600170 return helper_cairo_ft_scaled_font_has_color (scaled_font);
Khaled Hosny6add69a2022-12-16 19:54:00 +0200171#endif
Matthias Clasen0d6ee462022-12-25 10:50:56 -0500172
Behdad Esfahbod5c3da762022-12-25 15:04:13 -0700173 hb_face_t *face = hb_font_get_face (font);
174
Matthias Clasen0d6ee462022-12-25 10:50:56 -0500175 return hb_ot_color_has_png (face) ||
176 hb_ot_color_has_layers (face) ||
177 hb_ot_color_has_paint (face);
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600178}
179
180
181enum class image_protocol_t {
182 NONE = 0,
183 ITERM2,
184 KITTY,
185};
186
187struct finalize_closure_t {
188 void (*callback)(finalize_closure_t *);
189 cairo_surface_t *surface;
190 cairo_write_func_t write_func;
191 void *closure;
192 image_protocol_t protocol;
193};
194static cairo_user_data_key_t finalize_closure_key;
195
196
197static void
198finalize_ansi (finalize_closure_t *closure)
199{
200 cairo_status_t status;
201 status = helper_cairo_surface_write_to_ansi_stream (closure->surface,
202 closure->write_func,
203 closure->closure);
204 if (status != CAIRO_STATUS_SUCCESS)
205 fail (false, "Failed to write output: %s",
206 cairo_status_to_string (status));
207}
208
209static cairo_surface_t *
210_cairo_ansi_surface_create_for_stream (cairo_write_func_t write_func,
211 void *closure,
212 double width,
213 double height,
214 cairo_content_t content,
215 image_protocol_t protocol HB_UNUSED)
216{
217 cairo_surface_t *surface;
218 int w = ceil (width);
219 int h = ceil (height);
220
221 switch (content) {
222 case CAIRO_CONTENT_ALPHA:
223 surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
224 break;
225 default:
226 case CAIRO_CONTENT_COLOR:
227 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
228 break;
229 case CAIRO_CONTENT_COLOR_ALPHA:
230 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
231 break;
232 }
233 cairo_status_t status = cairo_surface_status (surface);
234 if (status != CAIRO_STATUS_SUCCESS)
235 fail (false, "Failed to create cairo surface: %s",
236 cairo_status_to_string (status));
237
238 finalize_closure_t *ansi_closure = g_new0 (finalize_closure_t, 1);
239 ansi_closure->callback = finalize_ansi;
240 ansi_closure->surface = surface;
241 ansi_closure->write_func = write_func;
242 ansi_closure->closure = closure;
243
244 if (cairo_surface_set_user_data (surface,
245 &finalize_closure_key,
246 (void *) ansi_closure,
247 (cairo_destroy_func_t) g_free))
248 g_free ((void *) closure);
249
250 return surface;
251}
252
253
254#ifdef CAIRO_HAS_PNG_FUNCTIONS
255
256static cairo_status_t
257byte_array_write_func (void *closure,
258 const unsigned char *data,
259 unsigned int size)
260{
261 g_byte_array_append ((GByteArray *) closure, data, size);
262 return CAIRO_STATUS_SUCCESS;
263}
264
265static void
266finalize_png (finalize_closure_t *closure)
267{
268 cairo_status_t status;
269 GByteArray *bytes = nullptr;
270 GString *string;
271 gchar *base64;
272 size_t base64_len;
273
274 if (closure->protocol == image_protocol_t::NONE)
275 {
276 status = cairo_surface_write_to_png_stream (closure->surface,
277 closure->write_func,
278 closure->closure);
279 }
280 else
281 {
282 bytes = g_byte_array_new ();
283 status = cairo_surface_write_to_png_stream (closure->surface,
284 byte_array_write_func,
285 bytes);
286 }
287
288 if (status != CAIRO_STATUS_SUCCESS)
289 fail (false, "Failed to write output: %s",
290 cairo_status_to_string (status));
291
292 if (closure->protocol == image_protocol_t::NONE)
293 return;
294
295 base64 = g_base64_encode (bytes->data, bytes->len);
296 base64_len = strlen (base64);
297
298 string = g_string_new (NULL);
299 if (closure->protocol == image_protocol_t::ITERM2)
300 {
301 /* https://iterm2.com/documentation-images.html */
302 g_string_printf (string, "\033]1337;File=inline=1;size=%zu:%s\a\n",
303 base64_len, base64);
304 }
305 else if (closure->protocol == image_protocol_t::KITTY)
306 {
307#define CHUNK_SIZE 4096
308 /* https://sw.kovidgoyal.net/kitty/graphics-protocol.html */
309 for (size_t pos = 0; pos < base64_len; pos += CHUNK_SIZE)
310 {
311 size_t len = base64_len - pos;
312
313 if (pos == 0)
314 g_string_append (string, "\033_Ga=T,f=100,m=");
315 else
316 g_string_append (string, "\033_Gm=");
317
318 if (len > CHUNK_SIZE)
319 {
320 g_string_append (string, "1;");
321 g_string_append_len (string, base64 + pos, CHUNK_SIZE);
322 }
323 else
324 {
325 g_string_append (string, "0;");
326 g_string_append_len (string, base64 + pos, len);
327 }
328
329 g_string_append (string, "\033\\");
330 }
331 g_string_append (string, "\n");
332#undef CHUNK_SIZE
333 }
334
335 closure->write_func (closure->closure, (unsigned char *) string->str, string->len);
336
337 g_byte_array_unref (bytes);
338 g_free (base64);
339 g_string_free (string, TRUE);
340}
341
342static cairo_surface_t *
343_cairo_png_surface_create_for_stream (cairo_write_func_t write_func,
344 void *closure,
345 double width,
346 double height,
347 cairo_content_t content,
348 image_protocol_t protocol)
349{
350 cairo_surface_t *surface;
351 int w = ceil (width);
352 int h = ceil (height);
353
354 switch (content) {
355 case CAIRO_CONTENT_ALPHA:
356 surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
357 break;
358 default:
359 case CAIRO_CONTENT_COLOR:
360 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
361 break;
362 case CAIRO_CONTENT_COLOR_ALPHA:
363 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
364 break;
365 }
366 cairo_status_t status = cairo_surface_status (surface);
367 if (status != CAIRO_STATUS_SUCCESS)
368 fail (false, "Failed to create cairo surface: %s",
369 cairo_status_to_string (status));
370
371 finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1);
372 png_closure->callback = finalize_png;
373 png_closure->surface = surface;
374 png_closure->write_func = write_func;
375 png_closure->closure = closure;
376 png_closure->protocol = protocol;
377
378 if (cairo_surface_set_user_data (surface,
379 &finalize_closure_key,
380 (void *) png_closure,
381 (cairo_destroy_func_t) g_free))
382 g_free ((void *) closure);
383
384 return surface;
385}
386
387#endif
388
389static cairo_status_t
390stdio_write_func (void *closure,
391 const unsigned char *data,
392 unsigned int size)
393{
394 FILE *fp = (FILE *) closure;
395
396 while (size) {
397 size_t ret = fwrite (data, 1, size, fp);
398 size -= ret;
399 data += ret;
400 if (size && ferror (fp))
401 fail (false, "Failed to write output: %s", strerror (errno));
402 }
403
404 return CAIRO_STATUS_SUCCESS;
405}
406
407static const char *helper_cairo_supported_formats[] =
408{
409 "ansi",
410 #ifdef CAIRO_HAS_PNG_FUNCTIONS
411 "png",
412 #endif
413 #ifdef CAIRO_HAS_SVG_SURFACE
414 "svg",
415 #endif
416 #ifdef CAIRO_HAS_PDF_SURFACE
417 "pdf",
418 #endif
419 #ifdef CAIRO_HAS_PS_SURFACE
420 "ps",
421 #ifdef HAS_EPS
422 "eps",
423 #endif
424 #endif
425 nullptr
426};
427
Behdad Esfahbodc98773e2021-08-11 20:22:03 -0600428template <typename view_options_t,
Behdad Esfahbodc0b2f502022-02-18 12:29:14 -0600429 typename output_options_type>
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600430static inline cairo_t *
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -0400431helper_cairo_create_context (double w, double h,
432 view_options_t *view_opts,
Behdad Esfahbodc0b2f502022-02-18 12:29:14 -0600433 output_options_type *out_opts,
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600434 cairo_content_t content)
435{
436 cairo_surface_t *(*constructor) (cairo_write_func_t write_func,
437 void *closure,
438 double width,
439 double height) = nullptr;
440 cairo_surface_t *(*constructor2) (cairo_write_func_t write_func,
441 void *closure,
442 double width,
443 double height,
444 cairo_content_t content,
445 image_protocol_t protocol) = nullptr;
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -0400446
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600447 image_protocol_t protocol = image_protocol_t::NONE;
448 const char *extension = out_opts->output_format;
449 if (!extension) {
450#if HAVE_ISATTY
Behdad Esfahbod58bfe402021-08-11 19:48:28 -0600451 if (isatty (fileno (out_opts->out_fp)))
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600452 {
453#ifdef CAIRO_HAS_PNG_FUNCTIONS
454 const char *name;
455 /* https://gitlab.com/gnachman/iterm2/-/issues/7154 */
456 if ((name = getenv ("LC_TERMINAL")) != nullptr &&
457 0 == g_ascii_strcasecmp (name, "iTerm2"))
458 {
459 extension = "png";
460 protocol = image_protocol_t::ITERM2;
461 }
Wez Furlonge3548c22022-09-03 08:15:03 -0700462 else if ((name = getenv ("TERM_PROGRAM")) != nullptr &&
463 0 == g_ascii_strcasecmp (name, "WezTerm"))
464 {
465 extension = "png";
466 protocol = image_protocol_t::ITERM2;
467 }
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600468 else if ((name = getenv ("TERM")) != nullptr &&
469 0 == g_ascii_strcasecmp (name, "xterm-kitty"))
470 {
471 extension = "png";
472 protocol = image_protocol_t::KITTY;
473 }
474 else
475 extension = "ansi";
476#else
477 extension = "ansi";
478#endif
479 }
480 else
481#endif
482 {
483#ifdef CAIRO_HAS_PNG_FUNCTIONS
484 extension = "png";
485#else
486 extension = "ansi";
487#endif
488 }
489 }
490 if (0)
491 ;
492 else if (0 == g_ascii_strcasecmp (extension, "ansi"))
493 constructor2 = _cairo_ansi_surface_create_for_stream;
494 #ifdef CAIRO_HAS_PNG_FUNCTIONS
495 else if (0 == g_ascii_strcasecmp (extension, "png"))
496 constructor2 = _cairo_png_surface_create_for_stream;
497 #endif
498 #ifdef CAIRO_HAS_SVG_SURFACE
499 else if (0 == g_ascii_strcasecmp (extension, "svg"))
500 constructor = cairo_svg_surface_create_for_stream;
501 #endif
502 #ifdef CAIRO_HAS_PDF_SURFACE
503 else if (0 == g_ascii_strcasecmp (extension, "pdf"))
504 constructor = cairo_pdf_surface_create_for_stream;
505 #endif
506 #ifdef CAIRO_HAS_PS_SURFACE
507 else if (0 == g_ascii_strcasecmp (extension, "ps"))
508 constructor = cairo_ps_surface_create_for_stream;
509 #ifdef HAS_EPS
510 else if (0 == g_ascii_strcasecmp (extension, "eps"))
511 constructor = _cairo_eps_surface_create_for_stream;
512 #endif
513 #endif
514
515
516 unsigned int fr, fg, fb, fa, br, bg, bb, ba;
517 const char *color;
Behdad Esfahboddc4af472023-01-20 11:11:02 -0700518 br = bg = bb = ba = 255;
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600519 color = view_opts->back ? view_opts->back : DEFAULT_BACK;
Behdad Esfahbod03e2e582023-01-20 11:24:35 -0700520 parse_color (color, br, bg, bb, ba);
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600521 fr = fg = fb = 0; fa = 255;
522 color = view_opts->fore ? view_opts->fore : DEFAULT_FORE;
Behdad Esfahbod03e2e582023-01-20 11:24:35 -0700523 parse_color (color, fr, fg, fb, fa);
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600524
525 if (content == CAIRO_CONTENT_ALPHA)
526 {
Behdad Esfahbod91a174f2023-01-24 11:27:35 -0700527 if (view_opts->show_extents ||
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600528 br != bg || bg != bb ||
529 fr != fg || fg != fb)
530 content = CAIRO_CONTENT_COLOR;
531 }
532 if (ba != 255)
533 content = CAIRO_CONTENT_COLOR_ALPHA;
534
535 cairo_surface_t *surface;
Behdad Esfahbod58bfe402021-08-11 19:48:28 -0600536 FILE *f = out_opts->out_fp;
Behdad Esfahbodc5337c42021-08-06 19:19:50 -0600537 if (constructor)
538 surface = constructor (stdio_write_func, f, w, h);
539 else if (constructor2)
540 surface = constructor2 (stdio_write_func, f, w, h, content, protocol);
541 else
542 fail (false, "Unknown output format `%s'; supported formats are: %s%s",
543 extension,
544 g_strjoinv ("/", const_cast<char**> (helper_cairo_supported_formats)),
545 out_opts->explicit_output_format ? "" :
546 "\nTry setting format using --output-format");
547
548 cairo_t *cr = cairo_create (surface);
549 content = cairo_surface_get_content (surface);
550
551 switch (content) {
552 case CAIRO_CONTENT_ALPHA:
553 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
554 cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
555 cairo_paint (cr);
556 cairo_set_source_rgba (cr, 1., 1., 1.,
557 (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.)));
558 break;
559 default:
560 case CAIRO_CONTENT_COLOR:
561 case CAIRO_CONTENT_COLOR_ALPHA:
562 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
563 cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
564 cairo_paint (cr);
565 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
566 cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
567 break;
568 }
569
570 cairo_surface_destroy (surface);
571 return cr;
572}
573
574static inline void
575helper_cairo_destroy_context (cairo_t *cr)
576{
577 finalize_closure_t *closure = (finalize_closure_t *)
578 cairo_surface_get_user_data (cairo_get_target (cr),
579 &finalize_closure_key);
580 if (closure)
581 closure->callback (closure);
582
583 cairo_status_t status = cairo_status (cr);
584 if (status != CAIRO_STATUS_SUCCESS)
585 fail (false, "Failed: %s",
586 cairo_status_to_string (status));
587 cairo_destroy (cr);
588}
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -0400589
590
591struct helper_cairo_line_t {
Behdad Esfahbod74d29cd2022-12-29 18:11:41 -0700592 cairo_glyph_t *glyphs = nullptr;
593 unsigned int num_glyphs = 0;
594 char *utf8 = nullptr;
595 unsigned int utf8_len = 0;
596 cairo_text_cluster_t *clusters = nullptr;
597 unsigned int num_clusters = 0;
598 cairo_text_cluster_flags_t cluster_flags = (cairo_text_cluster_flags_t) 0;
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -0400599
Behdad Esfahbod74d29cd2022-12-29 18:11:41 -0700600 helper_cairo_line_t (const char *utf8_,
601 unsigned utf8_len_,
602 hb_buffer_t *buffer,
603 hb_bool_t utf8_clusters,
Behdad Esfahbod228a4152022-12-29 18:19:06 -0700604 unsigned subpixel_bits) :
Behdad Esfahbod74d29cd2022-12-29 18:11:41 -0700605 utf8 (utf8_ ? g_strndup (utf8_, utf8_len_) : nullptr),
606 utf8_len (utf8_len_)
607 {
608 hb_cairo_glyphs_from_buffer (buffer,
609 utf8_clusters,
Behdad Esfahbod228a4152022-12-29 18:19:06 -0700610 1 << subpixel_bits, 1 << subpixel_bits,
Behdad Esfahbod74d29cd2022-12-29 18:11:41 -0700611 0., 0.,
612 utf8, utf8_len,
613 &glyphs, &num_glyphs,
614 &clusters, &num_clusters,
615 &cluster_flags);
616 }
617
618 void finish ()
619 {
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -0400620 if (glyphs)
621 cairo_glyph_free (glyphs);
622 if (clusters)
623 cairo_text_cluster_free (clusters);
Behdad Esfahbod74d29cd2022-12-29 18:11:41 -0700624 g_free (utf8);
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -0400625 }
626
Behdad Esfahbod74d29cd2022-12-29 18:11:41 -0700627 void get_advance (double *x_advance, double *y_advance)
628 {
Behdad Esfahbod69b84a82012-04-12 15:50:40 -0400629 *x_advance = glyphs[num_glyphs].x;
630 *y_advance = glyphs[num_glyphs].y;
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -0400631 }
632};
633
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -0400634#endif