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