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 *
_cairo_eps_surface_create_for_stream(cairo_write_func_t write_func,void * closure,double width,double height)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
free_ft_library(void)67 void free_ft_library (void)
68 {
69 FT_Done_FreeType (ft_library);
70 }
71
72 cairo_scaled_font_t *
helper_cairo_create_scaled_font(const font_options_t * font_opts)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
helper_cairo_scaled_font_has_color(cairo_scaled_font_t * scaled_font)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
finalize_ansi(finalize_closure_t * closure)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 *
_cairo_ansi_surface_create_for_stream(cairo_write_func_t write_func,void * closure,double width,double height,cairo_content_t content)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
finalize_png(finalize_closure_t * closure)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 *
_cairo_png_surface_create_for_stream(cairo_write_func_t write_func,void * closure,double width,double height,cairo_content_t content)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
stdio_write_func(void * closure,const unsigned char * data,unsigned int size)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 *
helper_cairo_create_context(double w,double h,view_options_t * view_opts,output_options_t * out_opts,cairo_content_t content)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
helper_cairo_destroy_context(cairo_t * cr)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
helper_cairo_line_from_buffer(helper_cairo_line_t * l,hb_buffer_t * buffer,const char * text,unsigned int text_len,int scale_bits,hb_bool_t utf8_clusters)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