helper-cairo.cc revision 6c0ebd02c99e7536975ba7194832a1f33abd7faf
14ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin/*
24ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * Copyright © 2011  Google, Inc.
34ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin *
44ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin *  This is part of HarfBuzz, a text shaping library.
54ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin *
64ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * Permission is hereby granted, without written agreement and without
74ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * license or royalty fees, to use, copy, modify, and distribute this
84ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * software and its documentation for any purpose, provided that the
94ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * above copyright notice and the following two paragraphs appear in
104ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * all copies of this software.
114ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin *
124ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
134ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
144ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
154ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
164ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * DAMAGE.
174ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin *
184ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
194ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
204ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
214ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
224ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
234ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin *
244ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin * Google Author(s): Behdad Esfahbod
254ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin */
264ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
274ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#include "helper-cairo.hh"
284ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
294ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#include <cairo-ft.h>
304ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#include <hb-ft.h>
314ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
324ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#include "helper-cairo-ansi.hh"
334ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#ifdef CAIRO_HAS_SVG_SURFACE
344ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#  include <cairo-svg.h>
354ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#endif
364ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#ifdef CAIRO_HAS_PDF_SURFACE
374ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#  include <cairo-pdf.h>
384ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#endif
394ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#ifdef CAIRO_HAS_PS_SURFACE
404ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#  include <cairo-ps.h>
414ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#  if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0)
424ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#    define HAS_EPS 1
434ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
444ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levinstatic cairo_surface_t *
454ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin_cairo_eps_surface_create_for_stream (cairo_write_func_t  write_func,
464ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin				      void               *closure,
474ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin				      double              width,
484ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin				      double              height)
49a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin{
504ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  cairo_surface_t *surface;
514ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
52a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height);
534ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  cairo_ps_surface_set_eps (surface, true);
544cee0af77b0275740f7f97ae5d778f60d5ac7f88Dmitry V. Levin
55a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  return surface;
564ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin}
574ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
58a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin#  else
594ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#    undef HAS_EPS
604ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#  endif
61a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin#endif
624ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
634ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
64a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levinstatic FT_Library ft_library;
654ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
664ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levinstatic inline
67a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levinvoid free_ft_library (void)
684ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin{
694ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  FT_Done_FreeType (ft_library);
70a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin}
714ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
724ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levincairo_scaled_font_t *
73a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levinhelper_cairo_create_scaled_font (const font_options_t *font_opts)
744ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin{
754ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  hb_font_t *font = hb_font_reference (font_opts->get_font ());
76a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
774ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  cairo_font_face_t *cairo_face;
784ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  FT_Face ft_face = hb_ft_font_get_face (font);
79a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  if (!ft_face)
80a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  {
81a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    if (!ft_library)
82a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    {
83a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin      FT_Init_FreeType (&ft_library);
84a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin#ifdef HAVE_ATEXIT
85a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin      atexit (free_ft_library);
864ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#endif
874ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin    }
880ed617bd66624cec6138102545d73b2e2346f1f6Dmitry V. Levin    FT_New_Face (ft_library,
894ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin		 font_opts->font_file,
904ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin		 font_opts->face_index,
91a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin		 &ft_face);
924ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  }
934ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  if (!ft_face)
944ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  {
9560fe8c139c6f2febefe595781812ddf0864a6ab8Denys Vlasenko    /* This allows us to get some boxes at least... */
96be284cae112d71a0073e24b34e16a63f31b32fd8Dmitry V. Levin    cairo_face = cairo_toy_font_face_create ("@cairo:sans",
974ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin					     CAIRO_FONT_SLANT_NORMAL,
98a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin					     CAIRO_FONT_WEIGHT_NORMAL);
99be284cae112d71a0073e24b34e16a63f31b32fd8Dmitry V. Levin  }
1004ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  else
101a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
1020a870586405ef425760d7681d5ac092bb022365eDmitry V. Levin  cairo_matrix_t ctm, font_matrix;
1030a870586405ef425760d7681d5ac092bb022365eDmitry V. Levin  cairo_font_options_t *font_options;
1040a870586405ef425760d7681d5ac092bb022365eDmitry V. Levin
1050a870586405ef425760d7681d5ac092bb022365eDmitry V. Levin  cairo_matrix_init_identity (&ctm);
1060a870586405ef425760d7681d5ac092bb022365eDmitry V. Levin  cairo_matrix_init_scale (&font_matrix,
1070a870586405ef425760d7681d5ac092bb022365eDmitry V. Levin			   font_opts->font_size_x,
1080a870586405ef425760d7681d5ac092bb022365eDmitry V. Levin			   font_opts->font_size_y);
109a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  font_options = cairo_font_options_create ();
1100a870586405ef425760d7681d5ac092bb022365eDmitry V. Levin  cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
111a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
1124ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
1134ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face,
1144ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin							       &font_matrix,
115a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin							       &ctm,
1164ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin							       font_options);
1174ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
118be284cae112d71a0073e24b34e16a63f31b32fd8Dmitry V. Levin  cairo_font_options_destroy (font_options);
1194ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  cairo_font_face_destroy (cairo_face);
1204ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
121a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  static cairo_user_data_key_t key;
1224ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  if (cairo_scaled_font_set_user_data (scaled_font,
1234ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin				       &key,
124be284cae112d71a0073e24b34e16a63f31b32fd8Dmitry V. Levin				       (void *) font,
1254ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin				       (cairo_destroy_func_t) hb_font_destroy))
1264ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin    hb_font_destroy (font);
127a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
128a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  return scaled_font;
1294ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin}
1304ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
131a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levinbool
1324ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levinhelper_cairo_scaled_font_has_color (cairo_scaled_font_t *scaled_font)
133a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin{
134a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  bool ret = false;
135a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin#ifdef FT_HAS_COLOR
136a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
137a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  if (ft_face)
1384ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  {
1394ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin    if (FT_HAS_COLOR (ft_face))
140be284cae112d71a0073e24b34e16a63f31b32fd8Dmitry V. Levin      ret = true;
1414ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin    cairo_ft_scaled_font_unlock_face (scaled_font);
1424ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  }
1434ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#endif
1444ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  return ret;
145a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin}
146a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
147a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
148a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levinstruct finalize_closure_t {
1494ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  void (*callback)(finalize_closure_t *);
1504ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  cairo_surface_t *surface;
151be284cae112d71a0073e24b34e16a63f31b32fd8Dmitry V. Levin  cairo_write_func_t write_func;
1524ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  void *closure;
1534ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin};
1544ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levinstatic cairo_user_data_key_t finalize_closure_key;
1554ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
156a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
157a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levinstatic void
158a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levinfinalize_ansi (finalize_closure_t *closure)
159a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin{
1604ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  cairo_status_t status;
1614ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  status = helper_cairo_surface_write_to_ansi_stream (closure->surface,
162be284cae112d71a0073e24b34e16a63f31b32fd8Dmitry V. Levin						      closure->write_func,
1634ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin						      closure->closure);
1644ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  if (status != CAIRO_STATUS_SUCCESS)
165a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    fail (false, "Failed to write output: %s",
166a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin	  cairo_status_to_string (status));
167a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin}
1682479ef0bceaa35ea353fd0ea372cf31d5eb8a216Dmitry V. Levin
1694ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levinstatic cairo_surface_t *
1704ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin_cairo_ansi_surface_create_for_stream (cairo_write_func_t write_func,
171be284cae112d71a0073e24b34e16a63f31b32fd8Dmitry V. Levin				       void *closure,
1724ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin				       double width,
173a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin				       double height,
174a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin				       cairo_content_t content)
175a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin{
1762479ef0bceaa35ea353fd0ea372cf31d5eb8a216Dmitry V. Levin  cairo_surface_t *surface;
1774ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  int w = ceil (width);
1784ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  int h = ceil (height);
17925caa318eedf38200cd7660320d95bf4662c6c5bDmitry V. Levin
180a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  switch (content) {
1814ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin    case CAIRO_CONTENT_ALPHA:
182a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin      surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
183a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin      break;
184a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    default:
185a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    case CAIRO_CONTENT_COLOR:
1864ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin      surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
1875bd67c86a93c658d258348e8f14af94fd45cbeb6Denys Vlasenko      break;
1884ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin    case CAIRO_CONTENT_COLOR_ALPHA:
189a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
1904ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin      break;
1914ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  }
192a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  cairo_status_t status = cairo_surface_status (surface);
193a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  if (status != CAIRO_STATUS_SUCCESS)
194a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    fail (false, "Failed to create cairo surface: %s",
1954ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin	  cairo_status_to_string (status));
1964ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
197a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  finalize_closure_t *ansi_closure = g_new0 (finalize_closure_t, 1);
1984ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  ansi_closure->callback = finalize_ansi;
199a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  ansi_closure->surface = surface;
200a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  ansi_closure->write_func = write_func;
201a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  ansi_closure->closure = closure;
2024ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
203a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  if (cairo_surface_set_user_data (surface,
204a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin				   &finalize_closure_key,
205a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin				   (void *) ansi_closure,
206a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin				   (cairo_destroy_func_t) g_free))
20761d62cf9481f100f76f1e8a2dfe131f638566633Denys Vlasenko    g_free ((void *) closure);
20861d62cf9481f100f76f1e8a2dfe131f638566633Denys Vlasenko
20961d62cf9481f100f76f1e8a2dfe131f638566633Denys Vlasenko  return surface;
21061d62cf9481f100f76f1e8a2dfe131f638566633Denys Vlasenko}
21161d62cf9481f100f76f1e8a2dfe131f638566633Denys Vlasenko
2124ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
2134ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin#ifdef CAIRO_HAS_PNG_FUNCTIONS
214be284cae112d71a0073e24b34e16a63f31b32fd8Dmitry V. Levin
215a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levinstatic void
216a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levinfinalize_png (finalize_closure_t *closure)
217a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin{
218a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  cairo_status_t status;
219a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  status = cairo_surface_write_to_png_stream (closure->surface,
220a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin					      closure->write_func,
2214ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin					      closure->closure);
222a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  if (status != CAIRO_STATUS_SUCCESS)
223be284cae112d71a0073e24b34e16a63f31b32fd8Dmitry V. Levin    fail (false, "Failed to write output: %s",
2244ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin	  cairo_status_to_string (status));
2254ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin}
2264ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
227a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levinstatic cairo_surface_t *
228a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin_cairo_png_surface_create_for_stream (cairo_write_func_t write_func,
229a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin				      void *closure,
230a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin				      double width,
231a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin				      double height,
232a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin				      cairo_content_t content)
233a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin{
234a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  cairo_surface_t *surface;
235a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  int w = ceil (width);
236a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  int h = ceil (height);
237a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
238a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  switch (content) {
2394ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin    case CAIRO_CONTENT_ALPHA:
240a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin      surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
241a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin      break;
2420a870586405ef425760d7681d5ac092bb022365eDmitry V. Levin    default:
243a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    case CAIRO_CONTENT_COLOR:
244a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin      surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
245a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin      break;
246a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    case CAIRO_CONTENT_COLOR_ALPHA:
247a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
248a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin      break;
2490a870586405ef425760d7681d5ac092bb022365eDmitry V. Levin  }
250a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  cairo_status_t status = cairo_surface_status (surface);
251a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  if (status != CAIRO_STATUS_SUCCESS)
252a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    fail (false, "Failed to create cairo surface: %s",
253a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin	  cairo_status_to_string (status));
254a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
2554ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1);
256be284cae112d71a0073e24b34e16a63f31b32fd8Dmitry V. Levin  png_closure->callback = finalize_png;
2575b88608b296c44c67ffa25900b8c2e496c2b41a4Mike Frysinger  png_closure->surface = surface;
2585b88608b296c44c67ffa25900b8c2e496c2b41a4Mike Frysinger  png_closure->write_func = write_func;
259a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  png_closure->closure = closure;
260a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
261a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  if (cairo_surface_set_user_data (surface,
262a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin				   &finalize_closure_key,
263a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin				   (void *) png_closure,
264a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin				   (cairo_destroy_func_t) g_free))
265a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    g_free ((void *) closure);
266a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
267a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  return surface;
268a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin}
269a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
2705b88608b296c44c67ffa25900b8c2e496c2b41a4Mike Frysinger#endif
271a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
2725b88608b296c44c67ffa25900b8c2e496c2b41a4Mike Frysingerstatic cairo_status_t
2735b88608b296c44c67ffa25900b8c2e496c2b41a4Mike Frysingerstdio_write_func (void                *closure,
274a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin		  const unsigned char *data,
275a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin		  unsigned int         size)
276a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin{
2774ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  FILE *fp = (FILE *) closure;
2784ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin
2794ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin  while (size) {
280a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    size_t ret = fwrite (data, 1, size, fp);
281a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    size -= ret;
282a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    data += ret;
283a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin    if (size && ferror (fp))
2844ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin      fail (false, "Failed to write output: %s", strerror (errno));
285a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  }
286a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
287a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin  return CAIRO_STATUS_SUCCESS;
2884ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levin}
289a0beac15389aa69135ce8f968c7f4e20e455940dDmitry V. Levin
2904ef6db489a8a51ae03ffb78b58c679162a39f3c3Dmitry V. Levinconst char *helper_cairo_supported_formats[] =
291{
292  "ansi",
293  #ifdef CAIRO_HAS_PNG_FUNCTIONS
294  "png",
295  #endif
296  #ifdef CAIRO_HAS_SVG_SURFACE
297  "svg",
298  #endif
299  #ifdef CAIRO_HAS_PDF_SURFACE
300  "pdf",
301  #endif
302  #ifdef CAIRO_HAS_PS_SURFACE
303  "ps",
304   #ifdef HAS_EPS
305    "eps",
306   #endif
307  #endif
308  NULL
309};
310
311cairo_t *
312helper_cairo_create_context (double w, double h,
313			     view_options_t *view_opts,
314			     output_options_t *out_opts,
315			     cairo_content_t content)
316{
317  cairo_surface_t *(*constructor) (cairo_write_func_t write_func,
318				   void *closure,
319				   double width,
320				   double height) = NULL;
321  cairo_surface_t *(*constructor2) (cairo_write_func_t write_func,
322				    void *closure,
323				    double width,
324				    double height,
325				    cairo_content_t content) = NULL;
326
327  const char *extension = out_opts->output_format;
328  if (!extension) {
329#if HAVE_ISATTY
330    if (isatty (fileno (out_opts->get_file_handle ())))
331      extension = "ansi";
332    else
333#endif
334    {
335#ifdef CAIRO_HAS_PNG_FUNCTIONS
336      extension = "png";
337#else
338      extension = "ansi";
339#endif
340    }
341  }
342  if (0)
343    ;
344    else if (0 == strcasecmp (extension, "ansi"))
345      constructor2 = _cairo_ansi_surface_create_for_stream;
346  #ifdef CAIRO_HAS_PNG_FUNCTIONS
347    else if (0 == strcasecmp (extension, "png"))
348      constructor2 = _cairo_png_surface_create_for_stream;
349  #endif
350  #ifdef CAIRO_HAS_SVG_SURFACE
351    else if (0 == strcasecmp (extension, "svg"))
352      constructor = cairo_svg_surface_create_for_stream;
353  #endif
354  #ifdef CAIRO_HAS_PDF_SURFACE
355    else if (0 == strcasecmp (extension, "pdf"))
356      constructor = cairo_pdf_surface_create_for_stream;
357  #endif
358  #ifdef CAIRO_HAS_PS_SURFACE
359    else if (0 == strcasecmp (extension, "ps"))
360      constructor = cairo_ps_surface_create_for_stream;
361   #ifdef HAS_EPS
362    else if (0 == strcasecmp (extension, "eps"))
363      constructor = _cairo_eps_surface_create_for_stream;
364   #endif
365  #endif
366
367
368  unsigned int fr, fg, fb, fa, br, bg, bb, ba;
369  const char *color;
370  br = bg = bb = 0; ba = 255;
371  color = view_opts->back ? view_opts->back : DEFAULT_BACK;
372  sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
373  fr = fg = fb = 0; fa = 255;
374  color = view_opts->fore ? view_opts->fore : DEFAULT_FORE;
375  sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
376
377  if (content == CAIRO_CONTENT_ALPHA)
378  {
379    if (view_opts->annotate ||
380	br != bg || bg != bb ||
381	fr != fg || fg != fb)
382      content = CAIRO_CONTENT_COLOR;
383  }
384  if (ba != 255)
385    content = CAIRO_CONTENT_COLOR_ALPHA;
386
387  cairo_surface_t *surface;
388  FILE *f = out_opts->get_file_handle ();
389  if (constructor)
390    surface = constructor (stdio_write_func, f, w, h);
391  else if (constructor2)
392    surface = constructor2 (stdio_write_func, f, w, h, content);
393  else
394    fail (false, "Unknown output format `%s'; supported formats are: %s%s",
395	  extension,
396	  g_strjoinv ("/", const_cast<char**> (helper_cairo_supported_formats)),
397	  out_opts->explicit_output_format ? "" :
398	  "\nTry setting format using --output-format");
399
400  cairo_t *cr = cairo_create (surface);
401  content = cairo_surface_get_content (surface);
402
403  switch (content) {
404    case CAIRO_CONTENT_ALPHA:
405      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
406      cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
407      cairo_paint (cr);
408      cairo_set_source_rgba (cr, 1., 1., 1.,
409			     (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.)));
410      break;
411    default:
412    case CAIRO_CONTENT_COLOR:
413    case CAIRO_CONTENT_COLOR_ALPHA:
414      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
415      cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
416      cairo_paint (cr);
417      cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
418      cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
419      break;
420  }
421
422  cairo_surface_destroy (surface);
423  return cr;
424}
425
426void
427helper_cairo_destroy_context (cairo_t *cr)
428{
429  finalize_closure_t *closure = (finalize_closure_t *)
430				cairo_surface_get_user_data (cairo_get_target (cr),
431							     &finalize_closure_key);
432  if (closure)
433    closure->callback (closure);
434
435  cairo_status_t status = cairo_status (cr);
436  if (status != CAIRO_STATUS_SUCCESS)
437    fail (false, "Failed: %s",
438	  cairo_status_to_string (status));
439  cairo_destroy (cr);
440}
441
442
443void
444helper_cairo_line_from_buffer (helper_cairo_line_t *l,
445			       hb_buffer_t         *buffer,
446			       const char          *text,
447			       unsigned int         text_len,
448			       int                  scale_bits,
449			       hb_bool_t            utf8_clusters)
450{
451  memset (l, 0, sizeof (*l));
452
453  l->num_glyphs = hb_buffer_get_length (buffer);
454  hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, NULL);
455  hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, NULL);
456  l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1);
457
458  if (text) {
459    l->utf8 = g_strndup (text, text_len);
460    l->utf8_len = text_len;
461    l->num_clusters = l->num_glyphs ? 1 : 0;
462    for (unsigned int i = 1; i < l->num_glyphs; i++)
463      if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
464	l->num_clusters++;
465    l->clusters = cairo_text_cluster_allocate (l->num_clusters);
466  }
467
468  if ((l->num_glyphs && !l->glyphs) ||
469      (l->utf8_len && !l->utf8) ||
470      (l->num_clusters && !l->clusters))
471  {
472    l->finish ();
473    return;
474  }
475
476  hb_position_t x = 0, y = 0;
477  int i;
478  for (i = 0; i < (int) l->num_glyphs; i++)
479  {
480    l->glyphs[i].index = hb_glyph[i].codepoint;
481    l->glyphs[i].x = scalbn ( hb_position->x_offset + x, scale_bits);
482    l->glyphs[i].y = scalbn (-hb_position->y_offset + y, scale_bits);
483    x +=  hb_position->x_advance;
484    y += -hb_position->y_advance;
485
486    hb_position++;
487  }
488  l->glyphs[i].index = -1;
489  l->glyphs[i].x = scalbn (x, scale_bits);
490  l->glyphs[i].y = scalbn (y, scale_bits);
491
492  if (l->num_clusters) {
493    memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0]));
494    hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
495    l->cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
496    unsigned int cluster = 0;
497    const char *start = l->utf8, *end;
498    l->clusters[cluster].num_glyphs++;
499    if (backward) {
500      for (i = l->num_glyphs - 2; i >= 0; i--) {
501	if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) {
502	  g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster);
503	  if (utf8_clusters)
504	    end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster;
505	  else
506	    end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i+1].cluster);
507	  l->clusters[cluster].num_bytes = end - start;
508	  start = end;
509	  cluster++;
510	}
511	l->clusters[cluster].num_glyphs++;
512      }
513      l->clusters[cluster].num_bytes = l->utf8 + text_len - start;
514    } else {
515      for (i = 1; i < (int) l->num_glyphs; i++) {
516	if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) {
517	  g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster);
518	  if (utf8_clusters)
519	    end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster;
520	  else
521	    end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i-1].cluster);
522	  l->clusters[cluster].num_bytes = end - start;
523	  start = end;
524	  cluster++;
525	}
526	l->clusters[cluster].num_glyphs++;
527      }
528      l->clusters[cluster].num_bytes = l->utf8 + text_len - start;
529    }
530  }
531}
532