1 #include "cfg.h"
2
3 #ifdef G
4 #include "links.h"
5
6 #ifdef HAVE_SVG
7
8 #include <cairo.h>
9 #include <librsvg/rsvg.h>
10 #ifdef HAVE_LIBRSVG_RSVG_CAIRO_H
11 #include <librsvg/rsvg-cairo.h>
12 #endif
13 #ifdef HAVE_LIBRSVG_LIBRSVG_FEATURES_H
14 #include <librsvg/librsvg-features.h>
15 #endif
16
17 #if !defined(LIBRSVG_CHECK_VERSION)
18 #define LIBRSVG_CHECK_VERSION(a,b,c) 0
19 #endif
20
21 #if LIBRSVG_CHECK_VERSION(2,40,0) && !LIBRSVG_CHECK_VERSION(2,40,16)
22 #define CURRENTCOLOR_HACK
23 #endif
24
25 #include "bits.h"
26
27 struct svg_decoder {
28 RsvgHandle *handle;
29 unsigned char *buffer;
30 int len;
31 };
32
svg_start(struct cached_image * cimg)33 void svg_start(struct cached_image *cimg)
34 {
35 struct svg_decoder *deco;
36
37 wait_for_fontconfig();
38
39 {
40 static unsigned rsvg_initialized = 0;
41 if (!rsvg_initialized) {
42 #if !defined(GLIB_DEPRECATED_IN_2_36)
43 g_type_init();
44 #endif
45 #if !LIBRSVG_CHECK_VERSION(2,36,0)
46 rsvg_init();
47 #endif
48 rsvg_initialized = 1;
49 }
50 }
51
52 deco = mem_alloc(sizeof(struct svg_decoder));
53 deco->buffer = init_str();
54 deco->len = 0;
55
56 cimg->decoder = deco;
57 deco->handle = rsvg_handle_new();
58 }
59
write_data(struct svg_decoder * deco,unsigned char * data,int length)60 static int write_data(struct svg_decoder *deco, unsigned char *data, int length)
61 {
62 GError *er = NULL;
63 int h1 = close_std_handle(1);
64 int h2 = close_std_handle(2);
65 #ifdef HAVE_RSVG_HANDLE_READ_STREAM_SYNC
66 GInputStream *is = g_memory_input_stream_new_from_data((const void *)data, length, NULL);
67 if (!is)
68 return -1;
69 if (!rsvg_handle_read_stream_sync(deco->handle, is, NULL, &er)) {
70 g_error_free(er);
71 restore_std_handle(1, h1);
72 restore_std_handle(2, h2);
73 return -1;
74 }
75 #else
76 if (!rsvg_handle_write(deco->handle, (const guchar *)data, length, &er)) {
77 g_error_free(er);
78 restore_std_handle(1, h1);
79 restore_std_handle(2, h2);
80 return -1;
81 }
82 #endif
83 restore_std_handle(1, h1);
84 restore_std_handle(2, h2);
85
86 return 0;
87 }
88
svg_restart(struct cached_image * cimg,unsigned char * data,int length)89 void svg_restart(struct cached_image *cimg, unsigned char *data, int length)
90 {
91 struct svg_decoder *deco = (struct svg_decoder *)cimg->decoder;
92 add_bytes_to_str(&deco->buffer, &deco->len, data, length);
93 }
94
svg_hack_buffer(struct svg_decoder * deco)95 static void svg_hack_buffer(struct svg_decoder *deco)
96 {
97 #ifdef CURRENTCOLOR_HACK
98 #define find_string "\"currentColor\""
99 #define replace_string "\"black\""
100 unsigned char *new_buffer = init_str();
101 int new_len = 0;
102 unsigned char *ptr = deco->buffer;
103 while (1) {
104 int remaining = (int)(deco->buffer + deco->len - ptr);
105 unsigned char *f = memmem(ptr, remaining, find_string, strlen(cast_const_char find_string));
106 if (!f) {
107 if (!new_len) {
108 mem_free(new_buffer);
109 return;
110 }
111 add_bytes_to_str(&new_buffer, &new_len, ptr, remaining);
112 break;
113 } else {
114 add_bytes_to_str(&new_buffer, &new_len, ptr, f - ptr);
115 add_to_str(&new_buffer, &new_len, cast_uchar replace_string);
116 ptr = f + strlen(cast_const_char find_string);
117 }
118 }
119 mem_free(deco->buffer);
120 deco->buffer = new_buffer;
121 deco->len = new_len;
122 #endif
123 }
124
svg_finish(struct cached_image * cimg)125 void svg_finish(struct cached_image *cimg)
126 {
127 struct svg_decoder *deco = (struct svg_decoder *)cimg->decoder;
128 RsvgDimensionData dim;
129 cairo_surface_t *surf;
130 cairo_t *cairo;
131 unsigned char *end_buffer, *p;
132 int h1, h2;
133
134 svg_hack_buffer(deco);
135 if (write_data(deco, deco->buffer, deco->len))
136 goto end;
137
138 #ifndef HAVE_RSVG_HANDLE_READ_STREAM_SYNC
139 {
140 h1 = close_std_handle(1);
141 h2 = close_std_handle(2);
142 GError *er = NULL;
143 if (!rsvg_handle_close(deco->handle, &er)) {
144 g_error_free(er);
145 restore_std_handle(1, h1);
146 restore_std_handle(2, h2);
147 goto end;
148 }
149 restore_std_handle(1, h1);
150 restore_std_handle(2, h2);
151 }
152 #endif
153
154 rsvg_handle_get_dimensions(deco->handle, &dim);
155
156 cimg->width = dim.width;
157 cimg->height = dim.height;
158 if (strstr(cast_const_char cimg->url, "/media/math/render/svg/")) {
159 cimg->width = (ssize_t)(cimg->width * 1.36);
160 cimg->height = (ssize_t)(cimg->height * 1.36);
161 }
162 cimg->buffer_bytes_per_pixel = 4;
163 cimg->red_gamma = cimg->green_gamma = cimg->blue_gamma = (float)sRGB_gamma;
164 cimg->strip_optimized = 0;
165 if (header_dimensions_known(cimg))
166 goto end;
167
168 surf = cairo_image_surface_create_for_data(cimg->buffer, CAIRO_FORMAT_ARGB32, (int)cimg->width, (int)cimg->height, (int)(cimg->width * cimg->buffer_bytes_per_pixel));
169 if (cairo_surface_status(surf))
170 goto end_surface;
171
172 cairo = cairo_create(surf);
173 if (cairo_status(cairo))
174 goto end_cairo;
175
176 if (dim.width && dim.height)
177 cairo_scale(cairo, (double)cimg->width / (double)dim.width, (double)cimg->height / (double)dim.height);
178
179 h1 = close_std_handle(1);
180 h2 = close_std_handle(2);
181 rsvg_handle_render_cairo(deco->handle, cairo);
182 cairo_surface_flush(surf);
183 restore_std_handle(1, h1);
184 restore_std_handle(2, h2);
185
186 end_buffer = cimg->buffer + cimg->width * cimg->height * cimg->buffer_bytes_per_pixel;
187 if (!big_endian) {
188 for (p = cimg->buffer; p < end_buffer; p += 4) {
189 #ifdef t4c
190 t4c t = *(t4c *)p;
191 t4c r = (t << 16) | (t >> 16);
192 *(t4c *)p = (t & 0xff00ff00U) | (r & 0xff00ffU);
193 #else
194 unsigned char c;
195 c = p[0];
196 p[0] = p[2];
197 p[2] = c;
198 #endif
199 }
200 } else {
201 for (p = cimg->buffer; p < end_buffer; p += 4) {
202 #ifdef t4c
203 t4c t = *(t4c *)p;
204 *(t4c *)p = (t << 8) | (t >> 24);
205 #else
206 unsigned char c;
207 c = p[0];
208 p[0] = p[1];
209 p[1] = p[2];
210 p[2] = p[3];
211 p[3] = c;
212 #endif
213 }
214 }
215
216 end_cairo:
217 cairo_destroy(cairo);
218 end_surface:
219 cairo_surface_destroy(surf);
220 end:
221 img_end(cimg);
222 }
223
svg_destroy_decoder(struct cached_image * cimg)224 void svg_destroy_decoder(struct cached_image *cimg)
225 {
226 struct svg_decoder *deco = (struct svg_decoder *)cimg->decoder;
227 g_object_unref(deco->handle);
228 mem_free(deco->buffer);
229 }
230
add_svg_version(unsigned char ** s,int * l)231 void add_svg_version(unsigned char **s, int *l)
232 {
233 add_to_str(s, l, cast_uchar "RSVG (");
234 #ifdef LIBRSVG_MAJOR_VERSION
235 add_num_to_str(s, l, LIBRSVG_MAJOR_VERSION);
236 #endif
237 #ifdef LIBRSVG_MINOR_VERSION
238 add_chr_to_str(s, l, '.');
239 add_num_to_str(s, l, LIBRSVG_MINOR_VERSION);
240 #endif
241 #ifdef LIBRSVG_MICRO_VERSION
242 add_chr_to_str(s, l, '.');
243 add_num_to_str(s, l, LIBRSVG_MICRO_VERSION);
244 #endif
245 add_chr_to_str(s, l, ')');
246 }
247
248 #endif
249
250 #endif
251