view-cairo.cc revision da4a2a1426ee3aa9d9678ec12c9ba4dfcba0bcf8
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#include "view-cairo.hh" 28 29 30#ifdef CAIRO_HAS_SVG_SURFACE 31# include <cairo-svg.h> 32#endif 33#ifdef CAIRO_HAS_PDF_SURFACE 34# include <cairo-pdf.h> 35#endif 36#ifdef CAIRO_HAS_PS_SURFACE 37# include <cairo-ps.h> 38# if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0) 39# define HAS_EPS 1 40 41static cairo_surface_t * 42_cairo_eps_surface_create_for_stream (cairo_write_func_t write_func, 43 void *closure, 44 double width, 45 double height) 46{ 47 cairo_surface_t *surface; 48 49 surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height); 50 cairo_ps_surface_set_eps (surface, TRUE); 51 52 return surface; 53} 54 55# else 56# undef HAS_EPS 57# endif 58#endif 59 60struct line_t { 61 cairo_glyph_t *glyphs; 62 unsigned int num_glyphs; 63 char *utf8; 64 unsigned int utf8_len; 65 cairo_text_cluster_t *clusters; 66 unsigned int num_clusters; 67 cairo_text_cluster_flags_t cluster_flags; 68 69 void finish (void) { 70 if (glyphs) 71 cairo_glyph_free (glyphs); 72 if (clusters) 73 cairo_text_cluster_free (clusters); 74 if (utf8) 75 g_free (utf8); 76 } 77}; 78 79void 80view_cairo_t::init (const font_options_t *font_opts) 81{ 82 lines = g_array_new (FALSE, FALSE, sizeof (line_t)); 83 scale = double (font_opts->font_size) / hb_face_get_upem (hb_font_get_face (font_opts->get_font ())); 84} 85 86void 87view_cairo_t::consume_line (hb_buffer_t *buffer, 88 const char *text, 89 unsigned int text_len) 90{ 91 line_t l = {0}; 92 93 l.num_glyphs = hb_buffer_get_length (buffer); 94 hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, NULL); 95 hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, NULL); 96 l.glyphs = cairo_glyph_allocate (l.num_glyphs + 1); 97 l.utf8 = g_strndup (text, text_len); 98 l.utf8_len = text_len; 99 l.num_clusters = l.num_glyphs ? 1 : 0; 100 for (unsigned int i = 1; i < l.num_glyphs; i++) 101 if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) 102 l.num_clusters++; 103 l.clusters = cairo_text_cluster_allocate (l.num_clusters); 104 105 if ((l.num_glyphs && !l.glyphs) || 106 (l.utf8_len && !l.utf8) || 107 (l.num_clusters && !l.clusters)) 108 { 109 l.finish (); 110 return; 111 } 112 113 hb_position_t x = 0, y = 0; 114 int i; 115 for (i = 0; i < (int) l.num_glyphs; i++) 116 { 117 l.glyphs[i].index = hb_glyph[i].codepoint; 118 l.glyphs[i].x = ( hb_position->x_offset + x) * scale; 119 l.glyphs[i].y = (-hb_position->y_offset + y) * scale; 120 x += hb_position->x_advance; 121 y += -hb_position->y_advance; 122 123 hb_position++; 124 } 125 l.glyphs[i].index = 0; 126 l.glyphs[i].x = x; 127 l.glyphs[i].y = y; 128 129 memset ((void *) l.clusters, 0, l.num_clusters * sizeof (l.clusters[0])); 130 bool backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer)); 131 l.cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0; 132 unsigned int cluster = 0; 133 if (!l.num_glyphs) 134 goto done; 135 l.clusters[cluster].num_glyphs++; 136 if (backward) { 137 for (i = l.num_glyphs - 2; i >= 0; i--) { 138 if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) { 139 g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster); 140 l.clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i+1].cluster; 141 cluster++; 142 } 143 l.clusters[cluster].num_glyphs++; 144 } 145 l.clusters[cluster].num_bytes += text_len - hb_glyph[0].cluster; 146 } else { 147 for (i = 1; i < (int) l.num_glyphs; i++) { 148 if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) { 149 g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster); 150 l.clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i-1].cluster; 151 cluster++; 152 } 153 l.clusters[cluster].num_glyphs++; 154 } 155 l.clusters[cluster].num_bytes += text_len - hb_glyph[i - 1].cluster; 156 } 157 158done: 159 g_array_append_val (lines, l); 160} 161 162void 163view_cairo_t::finish (const font_options_t *font_opts) 164{ 165 render (font_opts); 166 167 for (unsigned int i = 0; i < lines->len; i++) { 168 line_t &line = g_array_index (lines, line_t, i); 169 line.finish (); 170 } 171 172 g_array_unref (lines); 173} 174 175double 176view_cairo_t::line_width (unsigned int i) 177{ 178 line_t &line = g_array_index (lines, line_t, i); 179 return line.glyphs[line.num_glyphs].x * scale; 180} 181 182void 183view_cairo_t::get_surface_size (cairo_scaled_font_t *scaled_font, 184 double *w, double *h) 185{ 186 cairo_font_extents_t font_extents; 187 188 cairo_scaled_font_extents (scaled_font, &font_extents); 189 190 *h = font_extents.ascent 191 + font_extents.descent 192 + ((int) lines->len - 1) * font_extents.height; 193 *w = 0; 194 for (unsigned int i = 0; i < lines->len; i++) 195 *w = MAX (*w, line_width (i)); 196 197 *w += margin.l + margin.r; 198 *h += margin.t + margin.b; 199} 200 201cairo_scaled_font_t * 202view_cairo_t::create_scaled_font (const font_options_t *font_opts) 203{ 204 hb_font_t *font = hb_font_reference (font_opts->get_font ()); 205 206 cairo_font_face_t *cairo_face; 207 FT_Face ft_face = hb_ft_font_get_face (font); 208 if (!ft_face) 209 /* This allows us to get some boxes at least... */ 210 cairo_face = cairo_toy_font_face_create ("sans", 211 CAIRO_FONT_SLANT_NORMAL, 212 CAIRO_FONT_WEIGHT_NORMAL); 213 else 214 cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0); 215 cairo_matrix_t ctm, font_matrix; 216 cairo_font_options_t *font_options; 217 218 cairo_matrix_init_identity (&ctm); 219 cairo_matrix_init_scale (&font_matrix, 220 font_opts->font_size, font_opts->font_size); 221 font_options = cairo_font_options_create (); 222 cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE); 223 cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF); 224 225 cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face, 226 &font_matrix, 227 &ctm, 228 font_options); 229 230 cairo_font_options_destroy (font_options); 231 cairo_font_face_destroy (cairo_face); 232 233 static cairo_user_data_key_t key; 234 if (cairo_scaled_font_set_user_data (scaled_font, 235 &key, 236 (void *) font, 237 (cairo_destroy_func_t) hb_font_destroy)) 238 hb_font_destroy (font); 239 240 return scaled_font; 241} 242 243struct finalize_closure_t { 244 void (*callback)(finalize_closure_t *); 245 cairo_surface_t *surface; 246 cairo_write_func_t write_func; 247 void *closure; 248}; 249static cairo_user_data_key_t finalize_closure_key; 250 251#ifdef CAIRO_HAS_PNG_FUNCTIONS 252 253static void 254finalize_png (finalize_closure_t *closure) 255{ 256 cairo_status_t status; 257 status = cairo_surface_write_to_png_stream (closure->surface, 258 closure->write_func, 259 closure->closure); 260 if (status != CAIRO_STATUS_SUCCESS) 261 fail (FALSE, "Failed to write output: %s", 262 cairo_status_to_string (status)); 263} 264 265static cairo_surface_t * 266_cairo_png_surface_create_for_stream (cairo_write_func_t write_func, 267 void *closure, 268 double width, 269 double height, 270 cairo_content_t content) 271{ 272 cairo_surface_t *surface; 273 int w = ceil (width); 274 int h = ceil (height); 275 276 switch (content) { 277 case CAIRO_CONTENT_ALPHA: 278 surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); 279 break; 280 default: 281 case CAIRO_CONTENT_COLOR: 282 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); 283 break; 284 case CAIRO_CONTENT_COLOR_ALPHA: 285 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); 286 break; 287 } 288 cairo_status_t status = cairo_surface_status (surface); 289 if (status != CAIRO_STATUS_SUCCESS) 290 fail (FALSE, "Failed to create cairo surface: %s", 291 cairo_status_to_string (status)); 292 293 finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1); 294 png_closure->callback = finalize_png; 295 png_closure->surface = surface; 296 png_closure->write_func = write_func; 297 png_closure->closure = closure; 298 299 if (cairo_surface_set_user_data (surface, 300 &finalize_closure_key, 301 (void *) png_closure, 302 (cairo_destroy_func_t) g_free)) 303 g_free ((void *) closure); 304 305 return surface; 306} 307 308#endif 309 310void 311view_cairo_t::render (const font_options_t *font_opts) 312{ 313 cairo_scaled_font_t *scaled_font = create_scaled_font (font_opts); 314 double w, h; 315 get_surface_size (scaled_font, &w, &h); 316 cairo_t *cr = create_context (w, h); 317 cairo_set_scaled_font (cr, scaled_font); 318 cairo_scaled_font_destroy (scaled_font); 319 320 draw (cr); 321 322 finalize_closure_t *closure = (finalize_closure_t *) 323 cairo_surface_get_user_data (cairo_get_target (cr), 324 &finalize_closure_key); 325 if (closure) 326 closure->callback (closure); 327 328 cairo_status_t status = cairo_status (cr); 329 if (status != CAIRO_STATUS_SUCCESS) 330 fail (FALSE, "Failed: %s", 331 cairo_status_to_string (status)); 332 cairo_destroy (cr); 333} 334 335static cairo_status_t 336stdio_write_func (void *closure, 337 const unsigned char *data, 338 unsigned int size) 339{ 340 FILE *fp = (FILE *) closure; 341 342 while (size) { 343 size_t ret = fwrite (data, 1, size, fp); 344 size -= ret; 345 data += ret; 346 if (size && ferror (fp)) 347 fail (FALSE, "Failed to write output: %s", strerror (errno)); 348 } 349 350 return CAIRO_STATUS_SUCCESS; 351} 352 353cairo_t * 354view_cairo_t::create_context (double w, double h) 355{ 356 cairo_surface_t *(*constructor) (cairo_write_func_t write_func, 357 void *closure, 358 double width, 359 double height) = NULL; 360 cairo_surface_t *(*constructor2) (cairo_write_func_t write_func, 361 void *closure, 362 double width, 363 double height, 364 cairo_content_t content) = NULL; 365 366 const char *extension = output_format; 367 if (!extension) 368 extension = "png"; 369 if (0) 370 ; 371 #ifdef CAIRO_HAS_PNG_FUNCTIONS 372 else if (0 == strcasecmp (extension, "png")) 373 constructor2 = _cairo_png_surface_create_for_stream; 374 #endif 375 #ifdef CAIRO_HAS_SVG_SURFACE 376 else if (0 == strcasecmp (extension, "svg")) 377 constructor = cairo_svg_surface_create_for_stream; 378 #endif 379 #ifdef CAIRO_HAS_PDF_SURFACE 380 else if (0 == strcasecmp (extension, "pdf")) 381 constructor = cairo_pdf_surface_create_for_stream; 382 #endif 383 #ifdef CAIRO_HAS_PS_SURFACE 384 else if (0 == strcasecmp (extension, "ps")) 385 constructor = cairo_ps_surface_create_for_stream; 386 #ifdef HAS_EPS 387 else if (0 == strcasecmp (extension, "eps")) 388 constructor = _cairo_eps_surface_create_for_stream; 389 #endif 390 #endif 391 392 393 unsigned int fr, fg, fb, fa, br, bg, bb, ba; 394 br = bg = bb = ba = 255; 395 sscanf (back + (*back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba); 396 fr = fg = fb = 0; fa = 255; 397 sscanf (fore + (*fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa); 398 399 cairo_content_t content; 400 if (!annotate && ba == 255 && br == bg && bg == bb && fr == fg && fg == fb) 401 content = CAIRO_CONTENT_ALPHA; 402 else if (ba == 255) 403 content = CAIRO_CONTENT_COLOR; 404 else 405 content = CAIRO_CONTENT_COLOR_ALPHA; 406 407 cairo_surface_t *surface; 408 FILE *f = get_file_handle (); 409 if (constructor) 410 surface = constructor (stdio_write_func, f, w, h); 411 else if (constructor2) 412 surface = constructor2 (stdio_write_func, f, w, h, content); 413 else 414 fail (FALSE, "Unknown output format `%s'", extension); 415 416 cairo_t *cr = cairo_create (surface); 417 content = cairo_surface_get_content (surface); 418 419 switch (content) { 420 case CAIRO_CONTENT_ALPHA: 421 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 422 cairo_set_source_rgba (cr, 1., 1., 1., br / 255.); 423 cairo_paint (cr); 424 cairo_set_source_rgba (cr, 1., 1., 1., 425 (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.))); 426 break; 427 default: 428 case CAIRO_CONTENT_COLOR: 429 case CAIRO_CONTENT_COLOR_ALPHA: 430 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 431 cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.); 432 cairo_paint (cr); 433 cairo_set_operator (cr, CAIRO_OPERATOR_OVER); 434 cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.); 435 break; 436 } 437 438 cairo_surface_destroy (surface); 439 return cr; 440} 441 442void 443view_cairo_t::draw (cairo_t *cr) 444{ 445 cairo_save (cr); 446 447 cairo_font_extents_t font_extents; 448 cairo_font_extents (cr, &font_extents); 449 cairo_translate (cr, margin.l, margin.t); 450 for (unsigned int i = 0; i < lines->len; i++) 451 { 452 line_t &l = g_array_index (lines, line_t, i); 453 454 if (i) 455 cairo_translate (cr, 0, line_space); 456 457 cairo_translate (cr, 0, font_extents.ascent); 458 459 if (annotate) { 460 cairo_save (cr); 461 462 /* Draw actual glyph origins */ 463 cairo_set_source_rgba (cr, 1., 0., 0., .5); 464 cairo_set_line_width (cr, 5); 465 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); 466 for (unsigned i = 0; i < l.num_glyphs; i++) { 467 cairo_move_to (cr, l.glyphs[i].x, l.glyphs[i].y); 468 cairo_rel_line_to (cr, 0, 0); 469 } 470 cairo_stroke (cr); 471 472 cairo_restore (cr); 473 } 474 475 if (cairo_surface_get_type (cairo_get_target (cr)) == CAIRO_SURFACE_TYPE_IMAGE) { 476 /* cairo_show_glyphs() doesn't support subpixel positioning */ 477 cairo_glyph_path (cr, l.glyphs, l.num_glyphs); 478 cairo_fill (cr); 479 } else 480 cairo_show_text_glyphs (cr, 481 l.utf8, l.utf8_len, 482 l.glyphs, l.num_glyphs, 483 l.clusters, l.num_clusters, 484 l.cluster_flags); 485 486 cairo_translate (cr, 0, font_extents.height - font_extents.ascent); 487 } 488 489 cairo_restore (cr); 490} 491