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 
     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 				 double font_size)
     75 {
     76   hb_font_t *font = hb_font_reference (font_opts->get_font ());
     77 
     78   cairo_font_face_t *cairo_face;
     79   FT_Face ft_face = hb_ft_font_get_face (font);
     80   if (!ft_face)
     81   {
     82     if (!ft_library)
     83     {
     84       FT_Init_FreeType (&ft_library);
     85 #ifdef HAVE_ATEXIT
     86       atexit (free_ft_library);
     87 #endif
     88     }
     89     FT_New_Face (ft_library,
     90 		 font_opts->font_file,
     91 		 font_opts->face_index,
     92 		 &ft_face);
     93   }
     94   if (!ft_face)
     95   {
     96     /* This allows us to get some boxes at least... */
     97     cairo_face = cairo_toy_font_face_create ("@cairo:sans",
     98 					     CAIRO_FONT_SLANT_NORMAL,
     99 					     CAIRO_FONT_WEIGHT_NORMAL);
    100   }
    101   else
    102     cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
    103   cairo_matrix_t ctm, font_matrix;
    104   cairo_font_options_t *font_options;
    105 
    106   cairo_matrix_init_identity (&ctm);
    107   cairo_matrix_init_scale (&font_matrix,
    108 			   font_size, font_size);
    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 			       double               scale,
    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 = ( hb_position->x_offset + x) * scale;
    460     l->glyphs[i].y = (-hb_position->y_offset + y) * scale;
    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 = x * scale;
    468   l->glyphs[i].y = y * scale;
    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 }
    510