helper-cairo.hh 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  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. #ifndef HELPER_CAIRO_HH
  27. #define HELPER_CAIRO_HH
  28. #include "view-options.hh"
  29. #include "output-options.hh"
  30. #ifdef HAVE_CAIRO_FT
  31. # include "helper-cairo-ft.hh"
  32. #endif
  33. #include <cairo.h>
  34. #include <hb.h>
  35. #include <hb-cairo.h>
  36. #include "helper-cairo-ansi.hh"
  37. #ifdef CAIRO_HAS_SVG_SURFACE
  38. # include <cairo-svg.h>
  39. #endif
  40. #ifdef CAIRO_HAS_PDF_SURFACE
  41. # include <cairo-pdf.h>
  42. #endif
  43. #ifdef CAIRO_HAS_PS_SURFACE
  44. # include <cairo-ps.h>
  45. # if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0)
  46. # define HAS_EPS 1
  47. static cairo_surface_t *
  48. _cairo_eps_surface_create_for_stream (cairo_write_func_t write_func,
  49. void *closure,
  50. double width,
  51. double height)
  52. {
  53. cairo_surface_t *surface;
  54. surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height);
  55. cairo_ps_surface_set_eps (surface, true);
  56. return surface;
  57. }
  58. # else
  59. # undef HAS_EPS
  60. # endif
  61. #endif
  62. #ifdef CAIRO_HAS_SCRIPT_SURFACE
  63. # include <cairo-script.h>
  64. #endif
  65. static inline bool
  66. helper_cairo_use_hb_draw (const font_options_t *font_opts)
  67. {
  68. const char *env = getenv ("HB_DRAW");
  69. if (!env)
  70. /* Older cairo had a bug in rendering COLRv0 fonts in
  71. * right-to-left direction as well as clipping issue
  72. * with user-fonts.
  73. *
  74. * https://github.com/harfbuzz/harfbuzz/issues/4051 */
  75. return cairo_version () >= CAIRO_VERSION_ENCODE (1, 17, 5);
  76. return atoi (env);
  77. }
  78. static inline cairo_scaled_font_t *
  79. helper_cairo_create_scaled_font (const font_options_t *font_opts,
  80. const view_options_t *view_opts)
  81. {
  82. hb_font_t *font = font_opts->font;
  83. bool use_hb_draw = true;
  84. #ifdef HAVE_CAIRO_FT
  85. use_hb_draw = helper_cairo_use_hb_draw (font_opts);
  86. #endif
  87. cairo_font_face_t *cairo_face = nullptr;
  88. if (use_hb_draw)
  89. {
  90. cairo_face = hb_cairo_font_face_create_for_font (font);
  91. hb_cairo_font_face_set_scale_factor (cairo_face, 1 << font_opts->subpixel_bits);
  92. }
  93. #ifdef HAVE_CAIRO_FT
  94. else
  95. cairo_face = helper_cairo_create_ft_font_face (font_opts);
  96. #endif
  97. cairo_matrix_t ctm, font_matrix;
  98. cairo_font_options_t *font_options;
  99. cairo_matrix_init_identity (&ctm);
  100. cairo_matrix_init_scale (&font_matrix,
  101. font_opts->font_size_x,
  102. font_opts->font_size_y);
  103. if (!use_hb_draw)
  104. font_matrix.xy = -font_opts->slant * font_opts->font_size_x;
  105. font_options = cairo_font_options_create ();
  106. cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
  107. cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
  108. #ifdef CAIRO_COLOR_PALETTE_DEFAULT
  109. cairo_font_options_set_color_palette (font_options, view_opts->palette);
  110. #endif
  111. #ifdef HAVE_CAIRO_FONT_OPTIONS_GET_CUSTOM_PALETTE_COLOR
  112. if (view_opts->custom_palette)
  113. {
  114. char **entries = g_strsplit (view_opts->custom_palette, ",", -1);
  115. unsigned idx = 0;
  116. for (unsigned i = 0; entries[i]; i++)
  117. {
  118. const char *p = strchr (entries[i], '=');
  119. if (!p)
  120. p = entries[i];
  121. else
  122. {
  123. sscanf (entries[i], "%u", &idx);
  124. p++;
  125. }
  126. unsigned fr, fg, fb, fa;
  127. fr = fg = fb = fa = 0;
  128. if (parse_color (p, fr, fg,fb, fa))
  129. cairo_font_options_set_custom_palette_color (font_options, idx, fr / 255., fg / 255., fb / 255., fa / 255.);
  130. idx++;
  131. }
  132. g_strfreev (entries);
  133. }
  134. #endif
  135. cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face,
  136. &font_matrix,
  137. &ctm,
  138. font_options);
  139. cairo_font_options_destroy (font_options);
  140. cairo_font_face_destroy (cairo_face);
  141. return scaled_font;
  142. }
  143. static inline bool
  144. helper_cairo_scaled_font_has_color (cairo_scaled_font_t *scaled_font)
  145. {
  146. hb_font_t *font = hb_cairo_font_face_get_font (cairo_scaled_font_get_font_face (scaled_font));
  147. #ifdef HAVE_CAIRO_FT
  148. if (!font)
  149. return helper_cairo_ft_scaled_font_has_color (scaled_font);
  150. #endif
  151. hb_face_t *face = hb_font_get_face (font);
  152. return hb_ot_color_has_png (face) ||
  153. hb_ot_color_has_layers (face) ||
  154. hb_ot_color_has_paint (face);
  155. }
  156. enum class image_protocol_t {
  157. NONE = 0,
  158. ITERM2,
  159. KITTY,
  160. };
  161. struct finalize_closure_t {
  162. void (*callback)(finalize_closure_t *);
  163. cairo_surface_t *surface;
  164. cairo_write_func_t write_func;
  165. void *closure;
  166. image_protocol_t protocol;
  167. };
  168. static cairo_user_data_key_t finalize_closure_key;
  169. static void
  170. finalize_ansi (finalize_closure_t *closure)
  171. {
  172. cairo_status_t status;
  173. status = helper_cairo_surface_write_to_ansi_stream (closure->surface,
  174. closure->write_func,
  175. closure->closure);
  176. if (status != CAIRO_STATUS_SUCCESS)
  177. fail (false, "Failed to write output: %s",
  178. cairo_status_to_string (status));
  179. }
  180. static cairo_surface_t *
  181. _cairo_ansi_surface_create_for_stream (cairo_write_func_t write_func,
  182. void *closure,
  183. double width,
  184. double height,
  185. cairo_content_t content,
  186. image_protocol_t protocol HB_UNUSED)
  187. {
  188. cairo_surface_t *surface;
  189. int w = ceil (width);
  190. int h = ceil (height);
  191. switch (content) {
  192. case CAIRO_CONTENT_ALPHA:
  193. surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
  194. break;
  195. default:
  196. case CAIRO_CONTENT_COLOR:
  197. surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
  198. break;
  199. case CAIRO_CONTENT_COLOR_ALPHA:
  200. surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
  201. break;
  202. }
  203. cairo_status_t status = cairo_surface_status (surface);
  204. if (status != CAIRO_STATUS_SUCCESS)
  205. fail (false, "Failed to create cairo surface: %s",
  206. cairo_status_to_string (status));
  207. finalize_closure_t *ansi_closure = g_new0 (finalize_closure_t, 1);
  208. ansi_closure->callback = finalize_ansi;
  209. ansi_closure->surface = surface;
  210. ansi_closure->write_func = write_func;
  211. ansi_closure->closure = closure;
  212. if (cairo_surface_set_user_data (surface,
  213. &finalize_closure_key,
  214. (void *) ansi_closure,
  215. (cairo_destroy_func_t) g_free))
  216. g_free ((void *) closure);
  217. return surface;
  218. }
  219. #ifdef CAIRO_HAS_PNG_FUNCTIONS
  220. static cairo_status_t
  221. byte_array_write_func (void *closure,
  222. const unsigned char *data,
  223. unsigned int size)
  224. {
  225. g_byte_array_append ((GByteArray *) closure, data, size);
  226. return CAIRO_STATUS_SUCCESS;
  227. }
  228. static void
  229. finalize_png (finalize_closure_t *closure)
  230. {
  231. cairo_status_t status;
  232. GByteArray *bytes = nullptr;
  233. GString *string;
  234. gchar *base64;
  235. size_t base64_len;
  236. if (closure->protocol == image_protocol_t::NONE)
  237. {
  238. status = cairo_surface_write_to_png_stream (closure->surface,
  239. closure->write_func,
  240. closure->closure);
  241. }
  242. else
  243. {
  244. bytes = g_byte_array_new ();
  245. status = cairo_surface_write_to_png_stream (closure->surface,
  246. byte_array_write_func,
  247. bytes);
  248. }
  249. if (status != CAIRO_STATUS_SUCCESS)
  250. fail (false, "Failed to write output: %s",
  251. cairo_status_to_string (status));
  252. if (closure->protocol == image_protocol_t::NONE)
  253. return;
  254. base64 = g_base64_encode (bytes->data, bytes->len);
  255. base64_len = strlen (base64);
  256. string = g_string_new (NULL);
  257. if (closure->protocol == image_protocol_t::ITERM2)
  258. {
  259. /* https://iterm2.com/documentation-images.html */
  260. g_string_printf (string, "\033]1337;File=inline=1;size=%zu:%s\a\n",
  261. base64_len, base64);
  262. }
  263. else if (closure->protocol == image_protocol_t::KITTY)
  264. {
  265. #define CHUNK_SIZE 4096
  266. /* https://sw.kovidgoyal.net/kitty/graphics-protocol.html */
  267. for (size_t pos = 0; pos < base64_len; pos += CHUNK_SIZE)
  268. {
  269. size_t len = base64_len - pos;
  270. if (pos == 0)
  271. g_string_append (string, "\033_Ga=T,f=100,m=");
  272. else
  273. g_string_append (string, "\033_Gm=");
  274. if (len > CHUNK_SIZE)
  275. {
  276. g_string_append (string, "1;");
  277. g_string_append_len (string, base64 + pos, CHUNK_SIZE);
  278. }
  279. else
  280. {
  281. g_string_append (string, "0;");
  282. g_string_append_len (string, base64 + pos, len);
  283. }
  284. g_string_append (string, "\033\\");
  285. }
  286. g_string_append (string, "\n");
  287. #undef CHUNK_SIZE
  288. }
  289. closure->write_func (closure->closure, (unsigned char *) string->str, string->len);
  290. g_byte_array_unref (bytes);
  291. g_free (base64);
  292. g_string_free (string, TRUE);
  293. }
  294. static cairo_surface_t *
  295. _cairo_png_surface_create_for_stream (cairo_write_func_t write_func,
  296. void *closure,
  297. double width,
  298. double height,
  299. cairo_content_t content,
  300. image_protocol_t protocol)
  301. {
  302. cairo_surface_t *surface;
  303. int w = ceil (width);
  304. int h = ceil (height);
  305. switch (content) {
  306. case CAIRO_CONTENT_ALPHA:
  307. surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
  308. break;
  309. default:
  310. case CAIRO_CONTENT_COLOR:
  311. surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
  312. break;
  313. case CAIRO_CONTENT_COLOR_ALPHA:
  314. surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
  315. break;
  316. }
  317. cairo_status_t status = cairo_surface_status (surface);
  318. if (status != CAIRO_STATUS_SUCCESS)
  319. fail (false, "Failed to create cairo surface: %s",
  320. cairo_status_to_string (status));
  321. finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1);
  322. png_closure->callback = finalize_png;
  323. png_closure->surface = surface;
  324. png_closure->write_func = write_func;
  325. png_closure->closure = closure;
  326. png_closure->protocol = protocol;
  327. if (cairo_surface_set_user_data (surface,
  328. &finalize_closure_key,
  329. (void *) png_closure,
  330. (cairo_destroy_func_t) g_free))
  331. g_free ((void *) closure);
  332. return surface;
  333. }
  334. #endif
  335. #ifdef CAIRO_HAS_SCRIPT_SURFACE
  336. static cairo_surface_t *
  337. _cairo_script_surface_create_for_stream (cairo_write_func_t write_func,
  338. void *closure,
  339. double width,
  340. double height,
  341. cairo_content_t content,
  342. image_protocol_t protocol HB_UNUSED)
  343. {
  344. cairo_device_t *script = cairo_script_create_for_stream (write_func, closure);
  345. cairo_surface_t *surface = cairo_script_surface_create (script, content, width, height);
  346. cairo_device_destroy (script);
  347. return surface;
  348. }
  349. #endif
  350. static cairo_status_t
  351. stdio_write_func (void *closure,
  352. const unsigned char *data,
  353. unsigned int size)
  354. {
  355. FILE *fp = (FILE *) closure;
  356. while (size) {
  357. size_t ret = fwrite (data, 1, size, fp);
  358. size -= ret;
  359. data += ret;
  360. if (size && ferror (fp))
  361. fail (false, "Failed to write output: %s", strerror (errno));
  362. }
  363. return CAIRO_STATUS_SUCCESS;
  364. }
  365. static const char *helper_cairo_supported_formats[] =
  366. {
  367. "ansi",
  368. #ifdef CAIRO_HAS_PNG_FUNCTIONS
  369. "png",
  370. #endif
  371. #ifdef CAIRO_HAS_SVG_SURFACE
  372. "svg",
  373. #endif
  374. #ifdef CAIRO_HAS_PDF_SURFACE
  375. "pdf",
  376. #endif
  377. #ifdef CAIRO_HAS_PS_SURFACE
  378. "ps",
  379. #ifdef HAS_EPS
  380. "eps",
  381. #endif
  382. #endif
  383. #ifdef CAIRO_HAS_SCRIPT_SURFACE
  384. "script",
  385. #endif
  386. nullptr
  387. };
  388. template <typename view_options_t,
  389. typename output_options_type>
  390. static inline cairo_t *
  391. helper_cairo_create_context (double w, double h,
  392. view_options_t *view_opts,
  393. output_options_type *out_opts,
  394. cairo_content_t content)
  395. {
  396. cairo_surface_t *(*constructor) (cairo_write_func_t write_func,
  397. void *closure,
  398. double width,
  399. double height) = nullptr;
  400. cairo_surface_t *(*constructor2) (cairo_write_func_t write_func,
  401. void *closure,
  402. double width,
  403. double height,
  404. cairo_content_t content,
  405. image_protocol_t protocol) = nullptr;
  406. image_protocol_t protocol = image_protocol_t::NONE;
  407. const char *extension = out_opts->output_format;
  408. if (!extension) {
  409. #if HAVE_ISATTY
  410. if (isatty (fileno (out_opts->out_fp)))
  411. {
  412. #ifdef CAIRO_HAS_PNG_FUNCTIONS
  413. const char *name;
  414. /* https://gitlab.com/gnachman/iterm2/-/issues/7154 */
  415. if ((name = getenv ("LC_TERMINAL")) != nullptr &&
  416. 0 == g_ascii_strcasecmp (name, "iTerm2"))
  417. {
  418. extension = "png";
  419. protocol = image_protocol_t::ITERM2;
  420. }
  421. else if ((name = getenv ("TERM_PROGRAM")) != nullptr &&
  422. 0 == g_ascii_strcasecmp (name, "WezTerm"))
  423. {
  424. extension = "png";
  425. protocol = image_protocol_t::ITERM2;
  426. }
  427. else if ((name = getenv ("TERM")) != nullptr &&
  428. 0 == g_ascii_strcasecmp (name, "xterm-kitty"))
  429. {
  430. extension = "png";
  431. protocol = image_protocol_t::KITTY;
  432. }
  433. else
  434. extension = "ansi";
  435. #else
  436. extension = "ansi";
  437. #endif
  438. }
  439. else
  440. #endif
  441. {
  442. #ifdef CAIRO_HAS_PNG_FUNCTIONS
  443. extension = "png";
  444. #else
  445. extension = "ansi";
  446. #endif
  447. }
  448. }
  449. if (0)
  450. ;
  451. else if (0 == g_ascii_strcasecmp (extension, "ansi"))
  452. constructor2 = _cairo_ansi_surface_create_for_stream;
  453. #ifdef CAIRO_HAS_PNG_FUNCTIONS
  454. else if (0 == g_ascii_strcasecmp (extension, "png"))
  455. constructor2 = _cairo_png_surface_create_for_stream;
  456. #endif
  457. #ifdef CAIRO_HAS_SVG_SURFACE
  458. else if (0 == g_ascii_strcasecmp (extension, "svg"))
  459. constructor = cairo_svg_surface_create_for_stream;
  460. #endif
  461. #ifdef CAIRO_HAS_PDF_SURFACE
  462. else if (0 == g_ascii_strcasecmp (extension, "pdf"))
  463. constructor = cairo_pdf_surface_create_for_stream;
  464. #endif
  465. #ifdef CAIRO_HAS_PS_SURFACE
  466. else if (0 == g_ascii_strcasecmp (extension, "ps"))
  467. constructor = cairo_ps_surface_create_for_stream;
  468. #ifdef HAS_EPS
  469. else if (0 == g_ascii_strcasecmp (extension, "eps"))
  470. constructor = _cairo_eps_surface_create_for_stream;
  471. #endif
  472. #ifdef CAIRO_HAS_SCRIPT_SURFACE
  473. else if (0 == g_ascii_strcasecmp (extension, "script"))
  474. constructor2 = _cairo_script_surface_create_for_stream;
  475. #endif
  476. #endif
  477. unsigned int fr, fg, fb, fa, br, bg, bb, ba;
  478. const char *color;
  479. br = bg = bb = ba = 255;
  480. color = view_opts->back ? view_opts->back : DEFAULT_BACK;
  481. parse_color (color, br, bg, bb, ba);
  482. fr = fg = fb = 0; fa = 255;
  483. color = view_opts->fore ? view_opts->fore : DEFAULT_FORE;
  484. parse_color (color, fr, fg, fb, fa);
  485. if (content == CAIRO_CONTENT_ALPHA)
  486. {
  487. if (view_opts->show_extents ||
  488. br != bg || bg != bb ||
  489. fr != fg || fg != fb)
  490. content = CAIRO_CONTENT_COLOR;
  491. }
  492. if (ba != 255)
  493. content = CAIRO_CONTENT_COLOR_ALPHA;
  494. cairo_surface_t *surface;
  495. FILE *f = out_opts->out_fp;
  496. if (constructor)
  497. surface = constructor (stdio_write_func, f, w, h);
  498. else if (constructor2)
  499. surface = constructor2 (stdio_write_func, f, w, h, content, protocol);
  500. else
  501. fail (false, "Unknown output format `%s'; supported formats are: %s%s",
  502. extension,
  503. g_strjoinv ("/", const_cast<char**> (helper_cairo_supported_formats)),
  504. out_opts->explicit_output_format ? "" :
  505. "\nTry setting format using --output-format");
  506. cairo_t *cr = cairo_create (surface);
  507. content = cairo_surface_get_content (surface);
  508. switch (content) {
  509. case CAIRO_CONTENT_ALPHA:
  510. cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
  511. cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
  512. cairo_paint (cr);
  513. cairo_set_source_rgba (cr, 1., 1., 1.,
  514. (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.)));
  515. break;
  516. default:
  517. case CAIRO_CONTENT_COLOR:
  518. case CAIRO_CONTENT_COLOR_ALPHA:
  519. cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
  520. cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
  521. cairo_paint (cr);
  522. cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
  523. break;
  524. }
  525. cairo_surface_destroy (surface);
  526. return cr;
  527. }
  528. static inline void
  529. helper_cairo_destroy_context (cairo_t *cr)
  530. {
  531. finalize_closure_t *closure = (finalize_closure_t *)
  532. cairo_surface_get_user_data (cairo_get_target (cr),
  533. &finalize_closure_key);
  534. if (closure)
  535. closure->callback (closure);
  536. cairo_status_t status = cairo_status (cr);
  537. if (status != CAIRO_STATUS_SUCCESS)
  538. fail (false, "Failed: %s",
  539. cairo_status_to_string (status));
  540. cairo_destroy (cr);
  541. }
  542. struct helper_cairo_line_t {
  543. cairo_glyph_t *glyphs = nullptr;
  544. unsigned int num_glyphs = 0;
  545. char *utf8 = nullptr;
  546. unsigned int utf8_len = 0;
  547. cairo_text_cluster_t *clusters = nullptr;
  548. unsigned int num_clusters = 0;
  549. cairo_text_cluster_flags_t cluster_flags = (cairo_text_cluster_flags_t) 0;
  550. helper_cairo_line_t (const char *utf8_,
  551. unsigned utf8_len_,
  552. hb_buffer_t *buffer,
  553. hb_bool_t utf8_clusters,
  554. unsigned subpixel_bits) :
  555. utf8 (utf8_ ? g_strndup (utf8_, utf8_len_) : nullptr),
  556. utf8_len (utf8_len_)
  557. {
  558. hb_cairo_glyphs_from_buffer (buffer,
  559. utf8_clusters,
  560. 1 << subpixel_bits, 1 << subpixel_bits,
  561. 0., 0.,
  562. utf8, utf8_len,
  563. &glyphs, &num_glyphs,
  564. &clusters, &num_clusters,
  565. &cluster_flags);
  566. }
  567. void finish ()
  568. {
  569. if (glyphs)
  570. cairo_glyph_free (glyphs);
  571. if (clusters)
  572. cairo_text_cluster_free (clusters);
  573. g_free (utf8);
  574. }
  575. void get_advance (double *x_advance, double *y_advance)
  576. {
  577. *x_advance = glyphs[num_glyphs].x;
  578. *y_advance = glyphs[num_glyphs].y;
  579. }
  580. };
  581. #endif