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 {
     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