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