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 "helper-cairo.hh" 28 29 #include <cairo-ft.h> 30 #include <hb-ft.h> 31 32 #include "helper-cairo-ansi.hh" 33 #ifdef CAIRO_HAS_SVG_SURFACE 34 # include <cairo-svg.h> 35 #endif 36 #ifdef CAIRO_HAS_PDF_SURFACE 37 # include <cairo-pdf.h> 38 #endif 39 #ifdef CAIRO_HAS_PS_SURFACE 40 # include <cairo-ps.h> 41 # if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0) 42 # define HAS_EPS 1 43 44 static cairo_surface_t * 45 _cairo_eps_surface_create_for_stream (cairo_write_func_t write_func, 46 void *closure, 47 double width, 48 double height) 49 { 50 cairo_surface_t *surface; 51 52 surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height); 53 cairo_ps_surface_set_eps (surface, true); 54 55 return surface; 56 } 57 58 # else 59 # undef HAS_EPS 60 # endif 61 #endif 62 63 64 static FT_Library ft_library; 65 66 static inline 67 void free_ft_library (void) 68 { 69 FT_Done_FreeType (ft_library); 70 } 71 72 cairo_scaled_font_t * 73 helper_cairo_create_scaled_font (const font_options_t *font_opts) 74 { 75 hb_font_t *font = hb_font_reference (font_opts->get_font ()); 76 77 cairo_font_face_t *cairo_face; 78 /* We cannot use the FT_Face from hb_font_t, as doing so will confuse hb_font_t because 79 * cairo will reset the face size. As such, create new face... */ 80 FT_Face ft_face = NULL;//hb_ft_font_get_face (font); 81 if (!ft_face) 82 { 83 if (!ft_library) 84 { 85 FT_Init_FreeType (&ft_library); 86 #ifdef HAVE_ATEXIT 87 atexit (free_ft_library); 88 #endif 89 } 90 FT_New_Face (ft_library, 91 font_opts->font_file, 92 font_opts->face_index, 93 &ft_face); 94 } 95 if (!ft_face) 96 { 97 /* This allows us to get some boxes at least... */ 98 cairo_face = cairo_toy_font_face_create ("@cairo:sans", 99 CAIRO_FONT_SLANT_NORMAL, 100 CAIRO_FONT_WEIGHT_NORMAL); 101 } 102 else 103 cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0); 104 cairo_matrix_t ctm, font_matrix; 105 cairo_font_options_t *font_options; 106 107 cairo_matrix_init_identity (&ctm); 108 cairo_matrix_init_scale (&font_matrix, 109 font_opts->font_size_x, 110 font_opts->font_size_y); 111 font_options = cairo_font_options_create (); 112 cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE); 113 cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF); 114 115 cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face, 116 &font_matrix, 117 &ctm, 118 font_options); 119 120 cairo_font_options_destroy (font_options); 121 cairo_font_face_destroy (cairo_face); 122 123 static cairo_user_data_key_t key; 124 if (cairo_scaled_font_set_user_data (scaled_font, 125 &key, 126 (void *) font, 127 (cairo_destroy_func_t) hb_font_destroy)) 128 hb_font_destroy (font); 129 130 return scaled_font; 131 } 132 133 bool 134 helper_cairo_scaled_font_has_color (cairo_scaled_font_t *scaled_font) 135 { 136 bool ret = false; 137 #ifdef FT_HAS_COLOR 138 FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font); 139 if (ft_face) 140 { 141 if (FT_HAS_COLOR (ft_face)) 142 ret = true; 143 cairo_ft_scaled_font_unlock_face (scaled_font); 144 } 145 #endif 146 return ret; 147 } 148 149 150 struct finalize_closure_t { 151 void (*callback)(finalize_closure_t *); 152 cairo_surface_t *surface; 153 cairo_write_func_t write_func; 154 void *closure; 155 }; 156 static cairo_user_data_key_t finalize_closure_key; 157 158 159 static void 160 finalize_ansi (finalize_closure_t *closure) 161 { 162 cairo_status_t status; 163 status = helper_cairo_surface_write_to_ansi_stream (closure->surface, 164 closure->write_func, 165 closure->closure); 166 if (status != CAIRO_STATUS_SUCCESS) 167 fail (false, "Failed to write output: %s", 168 cairo_status_to_string (status)); 169 } 170 171 static cairo_surface_t * 172 _cairo_ansi_surface_create_for_stream (cairo_write_func_t write_func, 173 void *closure, 174 double width, 175 double height, 176 cairo_content_t content) 177 { 178 cairo_surface_t *surface; 179 int w = ceil (width); 180 int h = ceil (height); 181 182 switch (content) { 183 case CAIRO_CONTENT_ALPHA: 184 surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); 185 break; 186 default: 187 case CAIRO_CONTENT_COLOR: 188 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); 189 break; 190 case CAIRO_CONTENT_COLOR_ALPHA: 191 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); 192 break; 193 } 194 cairo_status_t status = cairo_surface_status (surface); 195 if (status != CAIRO_STATUS_SUCCESS) 196 fail (false, "Failed to create cairo surface: %s", 197 cairo_status_to_string (status)); 198 199 finalize_closure_t *ansi_closure = g_new0 (finalize_closure_t, 1); 200 ansi_closure->callback = finalize_ansi; 201 ansi_closure->surface = surface; 202 ansi_closure->write_func = write_func; 203 ansi_closure->closure = closure; 204 205 if (cairo_surface_set_user_data (surface, 206 &finalize_closure_key, 207 (void *) ansi_closure, 208 (cairo_destroy_func_t) g_free)) 209 g_free ((void *) closure); 210 211 return surface; 212 } 213 214 215 #ifdef CAIRO_HAS_PNG_FUNCTIONS 216 217 static void 218 finalize_png (finalize_closure_t *closure) 219 { 220 cairo_status_t status; 221 status = cairo_surface_write_to_png_stream (closure->surface, 222 closure->write_func, 223 closure->closure); 224 if (status != CAIRO_STATUS_SUCCESS) 225 fail (false, "Failed to write output: %s", 226 cairo_status_to_string (status)); 227 } 228 229 static cairo_surface_t * 230 _cairo_png_surface_create_for_stream (cairo_write_func_t write_func, 231 void *closure, 232 double width, 233 double height, 234 cairo_content_t content) 235 { 236 cairo_surface_t *surface; 237 int w = ceil (width); 238 int h = ceil (height); 239 240 switch (content) { 241 case CAIRO_CONTENT_ALPHA: 242 surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); 243 break; 244 default: 245 case CAIRO_CONTENT_COLOR: 246 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); 247 break; 248 case CAIRO_CONTENT_COLOR_ALPHA: 249 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); 250 break; 251 } 252 cairo_status_t status = cairo_surface_status (surface); 253 if (status != CAIRO_STATUS_SUCCESS) 254 fail (false, "Failed to create cairo surface: %s", 255 cairo_status_to_string (status)); 256 257 finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1); 258 png_closure->callback = finalize_png; 259 png_closure->surface = surface; 260 png_closure->write_func = write_func; 261 png_closure->closure = closure; 262 263 if (cairo_surface_set_user_data (surface, 264 &finalize_closure_key, 265 (void *) png_closure, 266 (cairo_destroy_func_t) g_free)) 267 g_free ((void *) closure); 268 269 return surface; 270 } 271 272 #endif 273 274 static cairo_status_t 275 stdio_write_func (void *closure, 276 const unsigned char *data, 277 unsigned int size) 278 { 279 FILE *fp = (FILE *) closure; 280 281 while (size) { 282 size_t ret = fwrite (data, 1, size, fp); 283 size -= ret; 284 data += ret; 285 if (size && ferror (fp)) 286 fail (false, "Failed to write output: %s", strerror (errno)); 287 } 288 289 return CAIRO_STATUS_SUCCESS; 290 } 291 292 const char *helper_cairo_supported_formats[] = 293 { 294 "ansi", 295 #ifdef CAIRO_HAS_PNG_FUNCTIONS 296 "png", 297 #endif 298 #ifdef CAIRO_HAS_SVG_SURFACE 299 "svg", 300 #endif 301 #ifdef CAIRO_HAS_PDF_SURFACE 302 "pdf", 303 #endif 304 #ifdef CAIRO_HAS_PS_SURFACE 305 "ps", 306 #ifdef HAS_EPS 307 "eps", 308 #endif 309 #endif 310 NULL 311 }; 312 313 cairo_t * 314 helper_cairo_create_context (double w, double h, 315 view_options_t *view_opts, 316 output_options_t *out_opts, 317 cairo_content_t content) 318 { 319 cairo_surface_t *(*constructor) (cairo_write_func_t write_func, 320 void *closure, 321 double width, 322 double height) = NULL; 323 cairo_surface_t *(*constructor2) (cairo_write_func_t write_func, 324 void *closure, 325 double width, 326 double height, 327 cairo_content_t content) = NULL; 328 329 const char *extension = out_opts->output_format; 330 if (!extension) { 331 #if HAVE_ISATTY 332 if (isatty (fileno (out_opts->get_file_handle ()))) 333 extension = "ansi"; 334 else 335 #endif 336 { 337 #ifdef CAIRO_HAS_PNG_FUNCTIONS 338 extension = "png"; 339 #else 340 extension = "ansi"; 341 #endif 342 } 343 } 344 if (0) 345 ; 346 else if (0 == g_ascii_strcasecmp (extension, "ansi")) 347 constructor2 = _cairo_ansi_surface_create_for_stream; 348 #ifdef CAIRO_HAS_PNG_FUNCTIONS 349 else if (0 == g_ascii_strcasecmp (extension, "png")) 350 constructor2 = _cairo_png_surface_create_for_stream; 351 #endif 352 #ifdef CAIRO_HAS_SVG_SURFACE 353 else if (0 == g_ascii_strcasecmp (extension, "svg")) 354 constructor = cairo_svg_surface_create_for_stream; 355 #endif 356 #ifdef CAIRO_HAS_PDF_SURFACE 357 else if (0 == g_ascii_strcasecmp (extension, "pdf")) 358 constructor = cairo_pdf_surface_create_for_stream; 359 #endif 360 #ifdef CAIRO_HAS_PS_SURFACE 361 else if (0 == g_ascii_strcasecmp (extension, "ps")) 362 constructor = cairo_ps_surface_create_for_stream; 363 #ifdef HAS_EPS 364 else if (0 == g_ascii_strcasecmp (extension, "eps")) 365 constructor = _cairo_eps_surface_create_for_stream; 366 #endif 367 #endif 368 369 370 unsigned int fr, fg, fb, fa, br, bg, bb, ba; 371 const char *color; 372 br = bg = bb = 0; ba = 255; 373 color = view_opts->back ? view_opts->back : DEFAULT_BACK; 374 sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba); 375 fr = fg = fb = 0; fa = 255; 376 color = view_opts->fore ? view_opts->fore : DEFAULT_FORE; 377 sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa); 378 379 if (content == CAIRO_CONTENT_ALPHA) 380 { 381 if (view_opts->annotate || 382 br != bg || bg != bb || 383 fr != fg || fg != fb) 384 content = CAIRO_CONTENT_COLOR; 385 } 386 if (ba != 255) 387 content = CAIRO_CONTENT_COLOR_ALPHA; 388 389 cairo_surface_t *surface; 390 FILE *f = out_opts->get_file_handle (); 391 if (constructor) 392 surface = constructor (stdio_write_func, f, w, h); 393 else if (constructor2) 394 surface = constructor2 (stdio_write_func, f, w, h, content); 395 else 396 fail (false, "Unknown output format `%s'; supported formats are: %s%s", 397 extension, 398 g_strjoinv ("/", const_cast<char**> (helper_cairo_supported_formats)), 399 out_opts->explicit_output_format ? "" : 400 "\nTry setting format using --output-format"); 401 402 cairo_t *cr = cairo_create (surface); 403 content = cairo_surface_get_content (surface); 404 405 switch (content) { 406 case CAIRO_CONTENT_ALPHA: 407 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 408 cairo_set_source_rgba (cr, 1., 1., 1., br / 255.); 409 cairo_paint (cr); 410 cairo_set_source_rgba (cr, 1., 1., 1., 411 (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.))); 412 break; 413 default: 414 case CAIRO_CONTENT_COLOR: 415 case CAIRO_CONTENT_COLOR_ALPHA: 416 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 417 cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.); 418 cairo_paint (cr); 419 cairo_set_operator (cr, CAIRO_OPERATOR_OVER); 420 cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.); 421 break; 422 } 423 424 cairo_surface_destroy (surface); 425 return cr; 426 } 427 428 void 429 helper_cairo_destroy_context (cairo_t *cr) 430 { 431 finalize_closure_t *closure = (finalize_closure_t *) 432 cairo_surface_get_user_data (cairo_get_target (cr), 433 &finalize_closure_key); 434 if (closure) 435 closure->callback (closure); 436 437 cairo_status_t status = cairo_status (cr); 438 if (status != CAIRO_STATUS_SUCCESS) 439 fail (false, "Failed: %s", 440 cairo_status_to_string (status)); 441 cairo_destroy (cr); 442 } 443 444 445 void 446 helper_cairo_line_from_buffer (helper_cairo_line_t *l, 447 hb_buffer_t *buffer, 448 const char *text, 449 unsigned int text_len, 450 int scale_bits, 451 hb_bool_t utf8_clusters) 452 { 453 memset (l, 0, sizeof (*l)); 454 455 l->num_glyphs = hb_buffer_get_length (buffer); 456 hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, NULL); 457 hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, NULL); 458 l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1); 459 460 if (text) { 461 l->utf8 = g_strndup (text, text_len); 462 l->utf8_len = text_len; 463 l->num_clusters = l->num_glyphs ? 1 : 0; 464 for (unsigned int i = 1; i < l->num_glyphs; i++) 465 if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) 466 l->num_clusters++; 467 l->clusters = cairo_text_cluster_allocate (l->num_clusters); 468 } 469 470 if ((l->num_glyphs && !l->glyphs) || 471 (l->utf8_len && !l->utf8) || 472 (l->num_clusters && !l->clusters)) 473 { 474 l->finish (); 475 return; 476 } 477 478 hb_position_t x = 0, y = 0; 479 int i; 480 for (i = 0; i < (int) l->num_glyphs; i++) 481 { 482 l->glyphs[i].index = hb_glyph[i].codepoint; 483 l->glyphs[i].x = scalbn ((double) hb_position->x_offset + x, scale_bits); 484 l->glyphs[i].y = scalbn ((double) -hb_position->y_offset + y, scale_bits); 485 x += hb_position->x_advance; 486 y += -hb_position->y_advance; 487 488 hb_position++; 489 } 490 l->glyphs[i].index = -1; 491 l->glyphs[i].x = scalbn ((double) x, scale_bits); 492 l->glyphs[i].y = scalbn ((double) y, scale_bits); 493 494 if (l->num_clusters) { 495 memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0])); 496 hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer)); 497 l->cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0; 498 unsigned int cluster = 0; 499 const char *start = l->utf8, *end; 500 l->clusters[cluster].num_glyphs++; 501 if (backward) { 502 for (i = l->num_glyphs - 2; i >= 0; i--) { 503 if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) { 504 g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster); 505 if (utf8_clusters) 506 end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster; 507 else 508 end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i+1].cluster); 509 l->clusters[cluster].num_bytes = end - start; 510 start = end; 511 cluster++; 512 } 513 l->clusters[cluster].num_glyphs++; 514 } 515 l->clusters[cluster].num_bytes = l->utf8 + text_len - start; 516 } else { 517 for (i = 1; i < (int) l->num_glyphs; i++) { 518 if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) { 519 g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster); 520 if (utf8_clusters) 521 end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster; 522 else 523 end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i-1].cluster); 524 l->clusters[cluster].num_bytes = end - start; 525 start = end; 526 cluster++; 527 } 528 l->clusters[cluster].num_glyphs++; 529 } 530 l->clusters[cluster].num_bytes = l->utf8 + text_len - start; 531 } 532 } 533 } 534