Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/harfbuzz/util/helper-cairo.hh @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /* | |
| 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 | |
| 27 #ifndef HELPER_CAIRO_HH | |
| 28 #define HELPER_CAIRO_HH | |
| 29 | |
| 30 #include "view-options.hh" | |
| 31 #include "output-options.hh" | |
| 32 #ifdef HAVE_CAIRO_FT | |
| 33 # include "helper-cairo-ft.hh" | |
| 34 #endif | |
| 35 #include "helper-cairo-user.hh" | |
| 36 | |
| 37 #include <cairo.h> | |
| 38 #include <hb.h> | |
| 39 | |
| 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 | |
| 52 static 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 | |
| 70 | |
| 71 static inline bool | |
| 72 helper_cairo_use_hb_draw (const font_options_t *font_opts) | |
| 73 { | |
| 74 const char *env = getenv ("HB_DRAW"); | |
| 75 if (!env) | |
| 76 #if 1 | |
| 77 /* Following branch disabled because we prefer our | |
| 78 * OpenType extensions working, ie going through hb-draw, | |
| 79 * over avoiding the obscure cairo bug. */ | |
| 80 return true; | |
| 81 #else | |
| 82 /* Older cairo had a bug in rendering COLRv0 fonts in | |
| 83 * right-to-left direction. */ | |
| 84 return cairo_version () >= CAIRO_VERSION_ENCODE (1, 17, 5); | |
| 85 #endif | |
| 86 | |
| 87 return atoi (env); | |
| 88 } | |
| 89 | |
| 90 static inline cairo_scaled_font_t * | |
| 91 helper_cairo_create_scaled_font (const font_options_t *font_opts) | |
| 92 { | |
| 93 hb_font_t *font = hb_font_reference (font_opts->font); | |
| 94 | |
| 95 #ifdef HAVE_CAIRO_FT | |
| 96 bool use_hb_draw = helper_cairo_use_hb_draw (font_opts); | |
| 97 cairo_font_face_t *cairo_face; | |
| 98 if (use_hb_draw) | |
| 99 cairo_face = helper_cairo_create_user_font_face (font_opts); | |
| 100 else | |
| 101 cairo_face = helper_cairo_create_ft_font_face (font_opts); | |
| 102 #else | |
| 103 cairo_font_face_t *cairo_face = helper_cairo_create_user_font_face (font_opts); | |
| 104 #endif | |
| 105 | |
| 106 cairo_matrix_t ctm, font_matrix; | |
| 107 cairo_font_options_t *font_options; | |
| 108 | |
| 109 cairo_matrix_init_identity (&ctm); | |
| 110 cairo_matrix_init_scale (&font_matrix, | |
| 111 font_opts->font_size_x, | |
| 112 font_opts->font_size_y); | |
| 113 #ifdef HAVE_CAIRO_FT | |
| 114 if (!use_hb_draw) | |
| 115 font_matrix.xy = -font_opts->slant * font_opts->font_size_x; | |
| 116 #endif | |
| 117 | |
| 118 font_options = cairo_font_options_create (); | |
| 119 cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE); | |
| 120 cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF); | |
| 121 | |
| 122 cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face, | |
| 123 &font_matrix, | |
| 124 &ctm, | |
| 125 font_options); | |
| 126 | |
| 127 cairo_font_options_destroy (font_options); | |
| 128 cairo_font_face_destroy (cairo_face); | |
| 129 | |
| 130 static cairo_user_data_key_t key; | |
| 131 if (cairo_scaled_font_set_user_data (scaled_font, | |
| 132 &key, | |
| 133 (void *) font, | |
| 134 (cairo_destroy_func_t) hb_font_destroy)) | |
| 135 hb_font_destroy (font); | |
| 136 | |
| 137 return scaled_font; | |
| 138 } | |
| 139 | |
| 140 static inline bool | |
| 141 helper_cairo_scaled_font_has_color (cairo_scaled_font_t *scaled_font) | |
| 142 { | |
| 143 #ifdef HAVE_CAIRO_FT | |
| 144 if (helper_cairo_user_font_face_has_data (cairo_scaled_font_get_font_face (scaled_font))) | |
| 145 return helper_cairo_user_scaled_font_has_color (scaled_font); | |
| 146 else | |
| 147 return helper_cairo_ft_scaled_font_has_color (scaled_font); | |
| 148 #else | |
| 149 return helper_cairo_user_scaled_font_has_color (scaled_font); | |
| 150 #endif | |
| 151 } | |
| 152 | |
| 153 | |
| 154 enum class image_protocol_t { | |
| 155 NONE = 0, | |
| 156 ITERM2, | |
| 157 KITTY, | |
| 158 }; | |
| 159 | |
| 160 struct finalize_closure_t { | |
| 161 void (*callback)(finalize_closure_t *); | |
| 162 cairo_surface_t *surface; | |
| 163 cairo_write_func_t write_func; | |
| 164 void *closure; | |
| 165 image_protocol_t protocol; | |
| 166 }; | |
| 167 static cairo_user_data_key_t finalize_closure_key; | |
| 168 | |
| 169 | |
| 170 static void | |
| 171 finalize_ansi (finalize_closure_t *closure) | |
| 172 { | |
| 173 cairo_status_t status; | |
| 174 status = helper_cairo_surface_write_to_ansi_stream (closure->surface, | |
| 175 closure->write_func, | |
| 176 closure->closure); | |
| 177 if (status != CAIRO_STATUS_SUCCESS) | |
| 178 fail (false, "Failed to write output: %s", | |
| 179 cairo_status_to_string (status)); | |
| 180 } | |
| 181 | |
| 182 static cairo_surface_t * | |
| 183 _cairo_ansi_surface_create_for_stream (cairo_write_func_t write_func, | |
| 184 void *closure, | |
| 185 double width, | |
| 186 double height, | |
| 187 cairo_content_t content, | |
| 188 image_protocol_t protocol HB_UNUSED) | |
| 189 { | |
| 190 cairo_surface_t *surface; | |
| 191 int w = ceil (width); | |
| 192 int h = ceil (height); | |
| 193 | |
| 194 switch (content) { | |
| 195 case CAIRO_CONTENT_ALPHA: | |
| 196 surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); | |
| 197 break; | |
| 198 default: | |
| 199 case CAIRO_CONTENT_COLOR: | |
| 200 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); | |
| 201 break; | |
| 202 case CAIRO_CONTENT_COLOR_ALPHA: | |
| 203 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); | |
| 204 break; | |
| 205 } | |
| 206 cairo_status_t status = cairo_surface_status (surface); | |
| 207 if (status != CAIRO_STATUS_SUCCESS) | |
| 208 fail (false, "Failed to create cairo surface: %s", | |
| 209 cairo_status_to_string (status)); | |
| 210 | |
| 211 finalize_closure_t *ansi_closure = g_new0 (finalize_closure_t, 1); | |
| 212 ansi_closure->callback = finalize_ansi; | |
| 213 ansi_closure->surface = surface; | |
| 214 ansi_closure->write_func = write_func; | |
| 215 ansi_closure->closure = closure; | |
| 216 | |
| 217 if (cairo_surface_set_user_data (surface, | |
| 218 &finalize_closure_key, | |
| 219 (void *) ansi_closure, | |
| 220 (cairo_destroy_func_t) g_free)) | |
| 221 g_free ((void *) closure); | |
| 222 | |
| 223 return surface; | |
| 224 } | |
| 225 | |
| 226 | |
| 227 #ifdef CAIRO_HAS_PNG_FUNCTIONS | |
| 228 | |
| 229 static cairo_status_t | |
| 230 byte_array_write_func (void *closure, | |
| 231 const unsigned char *data, | |
| 232 unsigned int size) | |
| 233 { | |
| 234 g_byte_array_append ((GByteArray *) closure, data, size); | |
| 235 return CAIRO_STATUS_SUCCESS; | |
| 236 } | |
| 237 | |
| 238 static void | |
| 239 finalize_png (finalize_closure_t *closure) | |
| 240 { | |
| 241 cairo_status_t status; | |
| 242 GByteArray *bytes = nullptr; | |
| 243 GString *string; | |
| 244 gchar *base64; | |
| 245 size_t base64_len; | |
| 246 | |
| 247 if (closure->protocol == image_protocol_t::NONE) | |
| 248 { | |
| 249 status = cairo_surface_write_to_png_stream (closure->surface, | |
| 250 closure->write_func, | |
| 251 closure->closure); | |
| 252 } | |
| 253 else | |
| 254 { | |
| 255 bytes = g_byte_array_new (); | |
| 256 status = cairo_surface_write_to_png_stream (closure->surface, | |
| 257 byte_array_write_func, | |
| 258 bytes); | |
| 259 } | |
| 260 | |
| 261 if (status != CAIRO_STATUS_SUCCESS) | |
| 262 fail (false, "Failed to write output: %s", | |
| 263 cairo_status_to_string (status)); | |
| 264 | |
| 265 if (closure->protocol == image_protocol_t::NONE) | |
| 266 return; | |
| 267 | |
| 268 base64 = g_base64_encode (bytes->data, bytes->len); | |
| 269 base64_len = strlen (base64); | |
| 270 | |
| 271 string = g_string_new (NULL); | |
| 272 if (closure->protocol == image_protocol_t::ITERM2) | |
| 273 { | |
| 274 /* https://iterm2.com/documentation-images.html */ | |
| 275 g_string_printf (string, "\033]1337;File=inline=1;size=%zu:%s\a\n", | |
| 276 base64_len, base64); | |
| 277 } | |
| 278 else if (closure->protocol == image_protocol_t::KITTY) | |
| 279 { | |
| 280 #define CHUNK_SIZE 4096 | |
| 281 /* https://sw.kovidgoyal.net/kitty/graphics-protocol.html */ | |
| 282 for (size_t pos = 0; pos < base64_len; pos += CHUNK_SIZE) | |
| 283 { | |
| 284 size_t len = base64_len - pos; | |
| 285 | |
| 286 if (pos == 0) | |
| 287 g_string_append (string, "\033_Ga=T,f=100,m="); | |
| 288 else | |
| 289 g_string_append (string, "\033_Gm="); | |
| 290 | |
| 291 if (len > CHUNK_SIZE) | |
| 292 { | |
| 293 g_string_append (string, "1;"); | |
| 294 g_string_append_len (string, base64 + pos, CHUNK_SIZE); | |
| 295 } | |
| 296 else | |
| 297 { | |
| 298 g_string_append (string, "0;"); | |
| 299 g_string_append_len (string, base64 + pos, len); | |
| 300 } | |
| 301 | |
| 302 g_string_append (string, "\033\\"); | |
| 303 } | |
| 304 g_string_append (string, "\n"); | |
| 305 #undef CHUNK_SIZE | |
| 306 } | |
| 307 | |
| 308 closure->write_func (closure->closure, (unsigned char *) string->str, string->len); | |
| 309 | |
| 310 g_byte_array_unref (bytes); | |
| 311 g_free (base64); | |
| 312 g_string_free (string, TRUE); | |
| 313 } | |
| 314 | |
| 315 static cairo_surface_t * | |
| 316 _cairo_png_surface_create_for_stream (cairo_write_func_t write_func, | |
| 317 void *closure, | |
| 318 double width, | |
| 319 double height, | |
| 320 cairo_content_t content, | |
| 321 image_protocol_t protocol) | |
| 322 { | |
| 323 cairo_surface_t *surface; | |
| 324 int w = ceil (width); | |
| 325 int h = ceil (height); | |
| 326 | |
| 327 switch (content) { | |
| 328 case CAIRO_CONTENT_ALPHA: | |
| 329 surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); | |
| 330 break; | |
| 331 default: | |
| 332 case CAIRO_CONTENT_COLOR: | |
| 333 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); | |
| 334 break; | |
| 335 case CAIRO_CONTENT_COLOR_ALPHA: | |
| 336 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); | |
| 337 break; | |
| 338 } | |
| 339 cairo_status_t status = cairo_surface_status (surface); | |
| 340 if (status != CAIRO_STATUS_SUCCESS) | |
| 341 fail (false, "Failed to create cairo surface: %s", | |
| 342 cairo_status_to_string (status)); | |
| 343 | |
| 344 finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1); | |
| 345 png_closure->callback = finalize_png; | |
| 346 png_closure->surface = surface; | |
| 347 png_closure->write_func = write_func; | |
| 348 png_closure->closure = closure; | |
| 349 png_closure->protocol = protocol; | |
| 350 | |
| 351 if (cairo_surface_set_user_data (surface, | |
| 352 &finalize_closure_key, | |
| 353 (void *) png_closure, | |
| 354 (cairo_destroy_func_t) g_free)) | |
| 355 g_free ((void *) closure); | |
| 356 | |
| 357 return surface; | |
| 358 } | |
| 359 | |
| 360 #endif | |
| 361 | |
| 362 static cairo_status_t | |
| 363 stdio_write_func (void *closure, | |
| 364 const unsigned char *data, | |
| 365 unsigned int size) | |
| 366 { | |
| 367 FILE *fp = (FILE *) closure; | |
| 368 | |
| 369 while (size) { | |
| 370 size_t ret = fwrite (data, 1, size, fp); | |
| 371 size -= ret; | |
| 372 data += ret; | |
| 373 if (size && ferror (fp)) | |
| 374 fail (false, "Failed to write output: %s", strerror (errno)); | |
| 375 } | |
| 376 | |
| 377 return CAIRO_STATUS_SUCCESS; | |
| 378 } | |
| 379 | |
| 380 static const char *helper_cairo_supported_formats[] = | |
| 381 { | |
| 382 "ansi", | |
| 383 #ifdef CAIRO_HAS_PNG_FUNCTIONS | |
| 384 "png", | |
| 385 #endif | |
| 386 #ifdef CAIRO_HAS_SVG_SURFACE | |
| 387 "svg", | |
| 388 #endif | |
| 389 #ifdef CAIRO_HAS_PDF_SURFACE | |
| 390 "pdf", | |
| 391 #endif | |
| 392 #ifdef CAIRO_HAS_PS_SURFACE | |
| 393 "ps", | |
| 394 #ifdef HAS_EPS | |
| 395 "eps", | |
| 396 #endif | |
| 397 #endif | |
| 398 nullptr | |
| 399 }; | |
| 400 | |
| 401 template <typename view_options_t, | |
| 402 typename output_options_type> | |
| 403 static inline cairo_t * | |
| 404 helper_cairo_create_context (double w, double h, | |
| 405 view_options_t *view_opts, | |
| 406 output_options_type *out_opts, | |
| 407 cairo_content_t content) | |
| 408 { | |
| 409 cairo_surface_t *(*constructor) (cairo_write_func_t write_func, | |
| 410 void *closure, | |
| 411 double width, | |
| 412 double height) = nullptr; | |
| 413 cairo_surface_t *(*constructor2) (cairo_write_func_t write_func, | |
| 414 void *closure, | |
| 415 double width, | |
| 416 double height, | |
| 417 cairo_content_t content, | |
| 418 image_protocol_t protocol) = nullptr; | |
| 419 | |
| 420 image_protocol_t protocol = image_protocol_t::NONE; | |
| 421 const char *extension = out_opts->output_format; | |
| 422 if (!extension) { | |
| 423 #if HAVE_ISATTY | |
| 424 if (isatty (fileno (out_opts->out_fp))) | |
| 425 { | |
| 426 #ifdef CAIRO_HAS_PNG_FUNCTIONS | |
| 427 const char *name; | |
| 428 /* https://gitlab.com/gnachman/iterm2/-/issues/7154 */ | |
| 429 if ((name = getenv ("LC_TERMINAL")) != nullptr && | |
| 430 0 == g_ascii_strcasecmp (name, "iTerm2")) | |
| 431 { | |
| 432 extension = "png"; | |
| 433 protocol = image_protocol_t::ITERM2; | |
| 434 } | |
| 435 else if ((name = getenv ("TERM_PROGRAM")) != nullptr && | |
| 436 0 == g_ascii_strcasecmp (name, "WezTerm")) | |
| 437 { | |
| 438 extension = "png"; | |
| 439 protocol = image_protocol_t::ITERM2; | |
| 440 } | |
| 441 else if ((name = getenv ("TERM")) != nullptr && | |
| 442 0 == g_ascii_strcasecmp (name, "xterm-kitty")) | |
| 443 { | |
| 444 extension = "png"; | |
| 445 protocol = image_protocol_t::KITTY; | |
| 446 } | |
| 447 else | |
| 448 extension = "ansi"; | |
| 449 #else | |
| 450 extension = "ansi"; | |
| 451 #endif | |
| 452 } | |
| 453 else | |
| 454 #endif | |
| 455 { | |
| 456 #ifdef CAIRO_HAS_PNG_FUNCTIONS | |
| 457 extension = "png"; | |
| 458 #else | |
| 459 extension = "ansi"; | |
| 460 #endif | |
| 461 } | |
| 462 } | |
| 463 if (0) | |
| 464 ; | |
| 465 else if (0 == g_ascii_strcasecmp (extension, "ansi")) | |
| 466 constructor2 = _cairo_ansi_surface_create_for_stream; | |
| 467 #ifdef CAIRO_HAS_PNG_FUNCTIONS | |
| 468 else if (0 == g_ascii_strcasecmp (extension, "png")) | |
| 469 constructor2 = _cairo_png_surface_create_for_stream; | |
| 470 #endif | |
| 471 #ifdef CAIRO_HAS_SVG_SURFACE | |
| 472 else if (0 == g_ascii_strcasecmp (extension, "svg")) | |
| 473 constructor = cairo_svg_surface_create_for_stream; | |
| 474 #endif | |
| 475 #ifdef CAIRO_HAS_PDF_SURFACE | |
| 476 else if (0 == g_ascii_strcasecmp (extension, "pdf")) | |
| 477 constructor = cairo_pdf_surface_create_for_stream; | |
| 478 #endif | |
| 479 #ifdef CAIRO_HAS_PS_SURFACE | |
| 480 else if (0 == g_ascii_strcasecmp (extension, "ps")) | |
| 481 constructor = cairo_ps_surface_create_for_stream; | |
| 482 #ifdef HAS_EPS | |
| 483 else if (0 == g_ascii_strcasecmp (extension, "eps")) | |
| 484 constructor = _cairo_eps_surface_create_for_stream; | |
| 485 #endif | |
| 486 #endif | |
| 487 | |
| 488 | |
| 489 unsigned int fr, fg, fb, fa, br, bg, bb, ba; | |
| 490 const char *color; | |
| 491 br = bg = bb = 0; ba = 255; | |
| 492 color = view_opts->back ? view_opts->back : DEFAULT_BACK; | |
| 493 sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba); | |
| 494 fr = fg = fb = 0; fa = 255; | |
| 495 color = view_opts->fore ? view_opts->fore : DEFAULT_FORE; | |
| 496 sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa); | |
| 497 | |
| 498 if (content == CAIRO_CONTENT_ALPHA) | |
| 499 { | |
| 500 if (view_opts->annotate || | |
| 501 br != bg || bg != bb || | |
| 502 fr != fg || fg != fb) | |
| 503 content = CAIRO_CONTENT_COLOR; | |
| 504 } | |
| 505 if (ba != 255) | |
| 506 content = CAIRO_CONTENT_COLOR_ALPHA; | |
| 507 | |
| 508 cairo_surface_t *surface; | |
| 509 FILE *f = out_opts->out_fp; | |
| 510 if (constructor) | |
| 511 surface = constructor (stdio_write_func, f, w, h); | |
| 512 else if (constructor2) | |
| 513 surface = constructor2 (stdio_write_func, f, w, h, content, protocol); | |
| 514 else | |
| 515 fail (false, "Unknown output format `%s'; supported formats are: %s%s", | |
| 516 extension, | |
| 517 g_strjoinv ("/", const_cast<char**> (helper_cairo_supported_formats)), | |
| 518 out_opts->explicit_output_format ? "" : | |
| 519 "\nTry setting format using --output-format"); | |
| 520 | |
| 521 cairo_t *cr = cairo_create (surface); | |
| 522 content = cairo_surface_get_content (surface); | |
| 523 | |
| 524 switch (content) { | |
| 525 case CAIRO_CONTENT_ALPHA: | |
| 526 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); | |
| 527 cairo_set_source_rgba (cr, 1., 1., 1., br / 255.); | |
| 528 cairo_paint (cr); | |
| 529 cairo_set_source_rgba (cr, 1., 1., 1., | |
| 530 (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.))); | |
| 531 break; | |
| 532 default: | |
| 533 case CAIRO_CONTENT_COLOR: | |
| 534 case CAIRO_CONTENT_COLOR_ALPHA: | |
| 535 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); | |
| 536 cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.); | |
| 537 cairo_paint (cr); | |
| 538 cairo_set_operator (cr, CAIRO_OPERATOR_OVER); | |
| 539 cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.); | |
| 540 break; | |
| 541 } | |
| 542 | |
| 543 cairo_surface_destroy (surface); | |
| 544 return cr; | |
| 545 } | |
| 546 | |
| 547 static inline void | |
| 548 helper_cairo_destroy_context (cairo_t *cr) | |
| 549 { | |
| 550 finalize_closure_t *closure = (finalize_closure_t *) | |
| 551 cairo_surface_get_user_data (cairo_get_target (cr), | |
| 552 &finalize_closure_key); | |
| 553 if (closure) | |
| 554 closure->callback (closure); | |
| 555 | |
| 556 cairo_status_t status = cairo_status (cr); | |
| 557 if (status != CAIRO_STATUS_SUCCESS) | |
| 558 fail (false, "Failed: %s", | |
| 559 cairo_status_to_string (status)); | |
| 560 cairo_destroy (cr); | |
| 561 } | |
| 562 | |
| 563 | |
| 564 struct helper_cairo_line_t { | |
| 565 cairo_glyph_t *glyphs; | |
| 566 unsigned int num_glyphs; | |
| 567 char *utf8; | |
| 568 unsigned int utf8_len; | |
| 569 cairo_text_cluster_t *clusters; | |
| 570 unsigned int num_clusters; | |
| 571 cairo_text_cluster_flags_t cluster_flags; | |
| 572 | |
| 573 void finish () { | |
| 574 if (glyphs) | |
| 575 cairo_glyph_free (glyphs); | |
| 576 if (clusters) | |
| 577 cairo_text_cluster_free (clusters); | |
| 578 if (utf8) | |
| 579 g_free (utf8); | |
| 580 } | |
| 581 | |
| 582 void get_advance (double *x_advance, double *y_advance) { | |
| 583 *x_advance = glyphs[num_glyphs].x; | |
| 584 *y_advance = glyphs[num_glyphs].y; | |
| 585 } | |
| 586 }; | |
| 587 | |
| 588 static inline void | |
| 589 helper_cairo_line_from_buffer (helper_cairo_line_t *l, | |
| 590 hb_buffer_t *buffer, | |
| 591 const char *text, | |
| 592 unsigned int text_len, | |
| 593 int scale_bits, | |
| 594 hb_bool_t utf8_clusters) | |
| 595 { | |
| 596 memset (l, 0, sizeof (*l)); | |
| 597 | |
| 598 l->num_glyphs = hb_buffer_get_length (buffer); | |
| 599 hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, nullptr); | |
| 600 hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, nullptr); | |
| 601 l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1); | |
| 602 | |
| 603 if (text) { | |
| 604 l->utf8 = g_strndup (text, text_len); | |
| 605 l->utf8_len = text_len; | |
| 606 l->num_clusters = l->num_glyphs ? 1 : 0; | |
| 607 for (unsigned int i = 1; i < l->num_glyphs; i++) | |
| 608 if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) | |
| 609 l->num_clusters++; | |
| 610 l->clusters = cairo_text_cluster_allocate (l->num_clusters); | |
| 611 } | |
| 612 | |
| 613 if ((l->num_glyphs && !l->glyphs) || | |
| 614 (l->utf8_len && !l->utf8) || | |
| 615 (l->num_clusters && !l->clusters)) | |
| 616 { | |
| 617 l->finish (); | |
| 618 return; | |
| 619 } | |
| 620 | |
| 621 hb_position_t x = 0, y = 0; | |
| 622 int i; | |
| 623 for (i = 0; i < (int) l->num_glyphs; i++) | |
| 624 { | |
| 625 l->glyphs[i].index = hb_glyph[i].codepoint; | |
| 626 l->glyphs[i].x = scalbn ((double) hb_position->x_offset + x, scale_bits); | |
| 627 l->glyphs[i].y = scalbn ((double) -hb_position->y_offset + y, scale_bits); | |
| 628 x += hb_position->x_advance; | |
| 629 y += -hb_position->y_advance; | |
| 630 | |
| 631 hb_position++; | |
| 632 } | |
| 633 l->glyphs[i].index = -1; | |
| 634 l->glyphs[i].x = scalbn ((double) x, scale_bits); | |
| 635 l->glyphs[i].y = scalbn ((double) y, scale_bits); | |
| 636 | |
| 637 if (l->num_clusters) { | |
| 638 memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0])); | |
| 639 hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer)); | |
| 640 l->cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0; | |
| 641 unsigned int cluster = 0; | |
| 642 const char *start = l->utf8, *end; | |
| 643 l->clusters[cluster].num_glyphs++; | |
| 644 if (backward) { | |
| 645 for (i = l->num_glyphs - 2; i >= 0; i--) { | |
| 646 if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) { | |
| 647 g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster); | |
| 648 if (utf8_clusters) | |
| 649 end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster; | |
| 650 else | |
| 651 end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i+1].cluster); | |
| 652 l->clusters[cluster].num_bytes = end - start; | |
| 653 start = end; | |
| 654 cluster++; | |
| 655 } | |
| 656 l->clusters[cluster].num_glyphs++; | |
| 657 } | |
| 658 l->clusters[cluster].num_bytes = l->utf8 + text_len - start; | |
| 659 } else { | |
| 660 for (i = 1; i < (int) l->num_glyphs; i++) { | |
| 661 if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) { | |
| 662 g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster); | |
| 663 if (utf8_clusters) | |
| 664 end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster; | |
| 665 else | |
| 666 end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i-1].cluster); | |
| 667 l->clusters[cluster].num_bytes = end - start; | |
| 668 start = end; | |
| 669 cluster++; | |
| 670 } | |
| 671 l->clusters[cluster].num_glyphs++; | |
| 672 } | |
| 673 l->clusters[cluster].num_bytes = l->utf8 + text_len - start; | |
| 674 } | |
| 675 } | |
| 676 } | |
| 677 | |
| 678 #endif |
