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