helper-cairo.hh 19 KB

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