blob: abb8c15380b1930c6a5378178ae370c4084f1d6c [file] [log] [blame]
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -04001/*
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#ifdef CAIRO_HAS_SVG_SURFACE
33# include <cairo-svg.h>
34#endif
35#ifdef CAIRO_HAS_PDF_SURFACE
36# include <cairo-pdf.h>
37#endif
38#ifdef CAIRO_HAS_PS_SURFACE
39# include <cairo-ps.h>
40# if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0)
41# define HAS_EPS 1
42
43static cairo_surface_t *
44_cairo_eps_surface_create_for_stream (cairo_write_func_t write_func,
45 void *closure,
46 double width,
47 double height)
48{
49 cairo_surface_t *surface;
50
51 surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height);
52 cairo_ps_surface_set_eps (surface, TRUE);
53
54 return surface;
55}
56
57# else
58# undef HAS_EPS
59# endif
60#endif
61
62cairo_scaled_font_t *
63helper_cairo_create_scaled_font (const font_options_t *font_opts,
64 double font_size)
65{
66 hb_font_t *font = hb_font_reference (font_opts->get_font ());
67
68 cairo_font_face_t *cairo_face;
69 FT_Face ft_face = hb_ft_font_get_face (font);
70 if (!ft_face)
71 /* This allows us to get some boxes at least... */
72 cairo_face = cairo_toy_font_face_create ("@cairo:sans",
73 CAIRO_FONT_SLANT_NORMAL,
74 CAIRO_FONT_WEIGHT_NORMAL);
75 else
76 cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
77 cairo_matrix_t ctm, font_matrix;
78 cairo_font_options_t *font_options;
79
80 cairo_matrix_init_identity (&ctm);
81 cairo_matrix_init_scale (&font_matrix,
82 font_size, font_size);
83 font_options = cairo_font_options_create ();
84 cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
85 cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
86
87 cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face,
88 &font_matrix,
89 &ctm,
90 font_options);
91
92 cairo_font_options_destroy (font_options);
93 cairo_font_face_destroy (cairo_face);
94
95 static cairo_user_data_key_t key;
96 if (cairo_scaled_font_set_user_data (scaled_font,
97 &key,
98 (void *) font,
99 (cairo_destroy_func_t) hb_font_destroy))
100 hb_font_destroy (font);
101
102 return scaled_font;
103}
104
105
106struct finalize_closure_t {
107 void (*callback)(finalize_closure_t *);
108 cairo_surface_t *surface;
109 cairo_write_func_t write_func;
110 void *closure;
111};
112static cairo_user_data_key_t finalize_closure_key;
113
114#ifdef CAIRO_HAS_PNG_FUNCTIONS
115
116static void
117finalize_png (finalize_closure_t *closure)
118{
119 cairo_status_t status;
120 status = cairo_surface_write_to_png_stream (closure->surface,
121 closure->write_func,
122 closure->closure);
123 if (status != CAIRO_STATUS_SUCCESS)
124 fail (FALSE, "Failed to write output: %s",
125 cairo_status_to_string (status));
126}
127
128static cairo_surface_t *
129_cairo_png_surface_create_for_stream (cairo_write_func_t write_func,
130 void *closure,
131 double width,
132 double height,
133 cairo_content_t content)
134{
135 cairo_surface_t *surface;
136 int w = ceil (width);
137 int h = ceil (height);
138
139 switch (content) {
140 case CAIRO_CONTENT_ALPHA:
141 surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
142 break;
143 default:
144 case CAIRO_CONTENT_COLOR:
145 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
146 break;
147 case CAIRO_CONTENT_COLOR_ALPHA:
148 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
149 break;
150 }
151 cairo_status_t status = cairo_surface_status (surface);
152 if (status != CAIRO_STATUS_SUCCESS)
153 fail (FALSE, "Failed to create cairo surface: %s",
154 cairo_status_to_string (status));
155
156 finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1);
157 png_closure->callback = finalize_png;
158 png_closure->surface = surface;
159 png_closure->write_func = write_func;
160 png_closure->closure = closure;
161
162 if (cairo_surface_set_user_data (surface,
163 &finalize_closure_key,
164 (void *) png_closure,
165 (cairo_destroy_func_t) g_free))
166 g_free ((void *) closure);
167
168 return surface;
169}
170
171#endif
172
173static cairo_status_t
174stdio_write_func (void *closure,
175 const unsigned char *data,
176 unsigned int size)
177{
178 FILE *fp = (FILE *) closure;
179
180 while (size) {
181 size_t ret = fwrite (data, 1, size, fp);
182 size -= ret;
183 data += ret;
184 if (size && ferror (fp))
185 fail (FALSE, "Failed to write output: %s", strerror (errno));
186 }
187
188 return CAIRO_STATUS_SUCCESS;
189}
190
191cairo_t *
192helper_cairo_create_context (double w, double h,
193 view_options_t *view_opts,
194 output_options_t *out_opts)
195{
196 cairo_surface_t *(*constructor) (cairo_write_func_t write_func,
197 void *closure,
198 double width,
199 double height) = NULL;
200 cairo_surface_t *(*constructor2) (cairo_write_func_t write_func,
201 void *closure,
202 double width,
203 double height,
204 cairo_content_t content) = NULL;
205
206 const char *extension = out_opts->output_format;
207 if (!extension)
208 extension = "png";
209 if (0)
210 ;
211 #ifdef CAIRO_HAS_PNG_FUNCTIONS
212 else if (0 == strcasecmp (extension, "png"))
213 constructor2 = _cairo_png_surface_create_for_stream;
214 #endif
215 #ifdef CAIRO_HAS_SVG_SURFACE
216 else if (0 == strcasecmp (extension, "svg"))
217 constructor = cairo_svg_surface_create_for_stream;
218 #endif
219 #ifdef CAIRO_HAS_PDF_SURFACE
220 else if (0 == strcasecmp (extension, "pdf"))
221 constructor = cairo_pdf_surface_create_for_stream;
222 #endif
223 #ifdef CAIRO_HAS_PS_SURFACE
224 else if (0 == strcasecmp (extension, "ps"))
225 constructor = cairo_ps_surface_create_for_stream;
226 #ifdef HAS_EPS
227 else if (0 == strcasecmp (extension, "eps"))
228 constructor = _cairo_eps_surface_create_for_stream;
229 #endif
230 #endif
231
232
233 unsigned int fr, fg, fb, fa, br, bg, bb, ba;
234 br = bg = bb = ba = 255;
235 sscanf (view_opts->back + (*view_opts->back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
236 fr = fg = fb = 0; fa = 255;
237 sscanf (view_opts->fore + (*view_opts->fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
238
239 cairo_content_t content;
240 if (!view_opts->annotate && ba == 255 && br == bg && bg == bb && fr == fg && fg == fb)
241 content = CAIRO_CONTENT_ALPHA;
242 else if (ba == 255)
243 content = CAIRO_CONTENT_COLOR;
244 else
245 content = CAIRO_CONTENT_COLOR_ALPHA;
246
247 cairo_surface_t *surface;
248 FILE *f = out_opts->get_file_handle ();
249 if (constructor)
250 surface = constructor (stdio_write_func, f, w, h);
251 else if (constructor2)
252 surface = constructor2 (stdio_write_func, f, w, h, content);
253 else
254 fail (FALSE, "Unknown output format `%s'", extension);
255
256 cairo_t *cr = cairo_create (surface);
257 content = cairo_surface_get_content (surface);
258
259 switch (content) {
260 case CAIRO_CONTENT_ALPHA:
261 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
262 cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
263 cairo_paint (cr);
264 cairo_set_source_rgba (cr, 1., 1., 1.,
265 (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.)));
266 break;
267 default:
268 case CAIRO_CONTENT_COLOR:
269 case CAIRO_CONTENT_COLOR_ALPHA:
270 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
271 cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
272 cairo_paint (cr);
273 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
274 cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
275 break;
276 }
277
278 cairo_surface_destroy (surface);
279 return cr;
280}
281
282void
283helper_cairo_destroy_context (cairo_t *cr)
284{
285 finalize_closure_t *closure = (finalize_closure_t *)
286 cairo_surface_get_user_data (cairo_get_target (cr),
287 &finalize_closure_key);
288 if (closure)
289 closure->callback (closure);
290
291 cairo_status_t status = cairo_status (cr);
292 if (status != CAIRO_STATUS_SUCCESS)
293 fail (FALSE, "Failed: %s",
294 cairo_status_to_string (status));
295 cairo_destroy (cr);
296}
297
298
299void
300helper_cairo_line_from_buffer (helper_cairo_line_t *l,
301 hb_buffer_t *buffer,
302 const char *text,
303 unsigned int text_len,
304 double scale)
305{
306 memset (l, 0, sizeof (*l));
307
308 l->num_glyphs = hb_buffer_get_length (buffer);
309 hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, NULL);
310 hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, NULL);
311 l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1);
312
313 if (text) {
314 l->utf8 = g_strndup (text, text_len);
315 l->utf8_len = text_len;
316 l->num_clusters = l->num_glyphs ? 1 : 0;
317 for (unsigned int i = 1; i < l->num_glyphs; i++)
318 if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
319 l->num_clusters++;
320 l->clusters = cairo_text_cluster_allocate (l->num_clusters);
321 }
322
323 if ((l->num_glyphs && !l->glyphs) ||
324 (l->utf8_len && !l->utf8) ||
325 (l->num_clusters && !l->clusters))
326 {
327 l->finish ();
328 return;
329 }
330
331 hb_position_t x = 0, y = 0;
332 int i;
333 for (i = 0; i < (int) l->num_glyphs; i++)
334 {
335 l->glyphs[i].index = hb_glyph[i].codepoint;
336 l->glyphs[i].x = ( hb_position->x_offset + x) * scale;
337 l->glyphs[i].y = (-hb_position->y_offset + y) * scale;
338 x += hb_position->x_advance;
339 y += -hb_position->y_advance;
340
341 hb_position++;
342 }
343 l->glyphs[i].index = -1;
344 l->glyphs[i].x = x * scale;
345 l->glyphs[i].y = y * scale;
346
347 if (l->num_clusters) {
348 memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0]));
Behdad Esfahbod088c1e22011-09-20 14:43:55 -0400349 hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
Behdad Esfahbod8b8b1902011-09-19 16:41:17 -0400350 l->cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
351 unsigned int cluster = 0;
352 l->clusters[cluster].num_glyphs++;
353 if (backward) {
354 for (i = l->num_glyphs - 2; i >= 0; i--) {
355 if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) {
356 g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster);
357 l->clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i+1].cluster;
358 cluster++;
359 }
360 l->clusters[cluster].num_glyphs++;
361 }
362 l->clusters[cluster].num_bytes += text_len - hb_glyph[0].cluster;
363 } else {
364 for (i = 1; i < (int) l->num_glyphs; i++) {
365 if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) {
366 g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster);
367 l->clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i-1].cluster;
368 cluster++;
369 }
370 l->clusters[cluster].num_glyphs++;
371 }
372 l->clusters[cluster].num_bytes += text_len - hb_glyph[i - 1].cluster;
373 }
374 }
375}