links/svg.c

251 lines
5.7 KiB
C

#include "cfg.h"
#ifdef G
#include "links.h"
#ifdef HAVE_SVG
#include <cairo.h>
#include <librsvg/rsvg.h>
#ifdef HAVE_LIBRSVG_RSVG_CAIRO_H
#include <librsvg/rsvg-cairo.h>
#endif
#ifdef HAVE_LIBRSVG_LIBRSVG_FEATURES_H
#include <librsvg/librsvg-features.h>
#endif
#if !defined(LIBRSVG_CHECK_VERSION)
#define LIBRSVG_CHECK_VERSION(a,b,c) 0
#endif
#if LIBRSVG_CHECK_VERSION(2,40,0) && !LIBRSVG_CHECK_VERSION(2,40,16)
#define CURRENTCOLOR_HACK
#endif
#include "bits.h"
struct svg_decoder {
RsvgHandle *handle;
unsigned char *buffer;
int len;
};
void svg_start(struct cached_image *cimg)
{
struct svg_decoder *deco;
wait_for_fontconfig();
{
static unsigned rsvg_initialized = 0;
if (!rsvg_initialized) {
#if !defined(GLIB_DEPRECATED_IN_2_36)
g_type_init();
#endif
#if !LIBRSVG_CHECK_VERSION(2,36,0)
rsvg_init();
#endif
rsvg_initialized = 1;
}
}
deco = mem_alloc(sizeof(struct svg_decoder));
deco->buffer = init_str();
deco->len = 0;
cimg->decoder = deco;
deco->handle = rsvg_handle_new();
}
static int write_data(struct svg_decoder *deco, unsigned char *data, int length)
{
GError *er = NULL;
int h1 = close_std_handle(1);
int h2 = close_std_handle(2);
#ifdef HAVE_RSVG_HANDLE_READ_STREAM_SYNC
GInputStream *is = g_memory_input_stream_new_from_data((const void *)data, length, NULL);
if (!is)
return -1;
if (!rsvg_handle_read_stream_sync(deco->handle, is, NULL, &er)) {
g_error_free(er);
restore_std_handle(1, h1);
restore_std_handle(2, h2);
return -1;
}
#else
if (!rsvg_handle_write(deco->handle, (const guchar *)data, length, &er)) {
g_error_free(er);
restore_std_handle(1, h1);
restore_std_handle(2, h2);
return -1;
}
#endif
restore_std_handle(1, h1);
restore_std_handle(2, h2);
return 0;
}
void svg_restart(struct cached_image *cimg, unsigned char *data, int length)
{
struct svg_decoder *deco = (struct svg_decoder *)cimg->decoder;
add_bytes_to_str(&deco->buffer, &deco->len, data, length);
}
static void svg_hack_buffer(struct svg_decoder *deco)
{
#ifdef CURRENTCOLOR_HACK
#define find_string "\"currentColor\""
#define replace_string "\"black\""
unsigned char *new_buffer = init_str();
int new_len = 0;
unsigned char *ptr = deco->buffer;
while (1) {
int remaining = (int)(deco->buffer + deco->len - ptr);
unsigned char *f = memmem(ptr, remaining, find_string, strlen(cast_const_char find_string));
if (!f) {
if (!new_len) {
mem_free(new_buffer);
return;
}
add_bytes_to_str(&new_buffer, &new_len, ptr, remaining);
break;
} else {
add_bytes_to_str(&new_buffer, &new_len, ptr, f - ptr);
add_to_str(&new_buffer, &new_len, cast_uchar replace_string);
ptr = f + strlen(cast_const_char find_string);
}
}
mem_free(deco->buffer);
deco->buffer = new_buffer;
deco->len = new_len;
#endif
}
void svg_finish(struct cached_image *cimg)
{
struct svg_decoder *deco = (struct svg_decoder *)cimg->decoder;
RsvgDimensionData dim;
cairo_surface_t *surf;
cairo_t *cairo;
unsigned char *end_buffer, *p;
int h1, h2;
svg_hack_buffer(deco);
if (write_data(deco, deco->buffer, deco->len))
goto end;
#ifndef HAVE_RSVG_HANDLE_READ_STREAM_SYNC
{
h1 = close_std_handle(1);
h2 = close_std_handle(2);
GError *er = NULL;
if (!rsvg_handle_close(deco->handle, &er)) {
g_error_free(er);
restore_std_handle(1, h1);
restore_std_handle(2, h2);
goto end;
}
restore_std_handle(1, h1);
restore_std_handle(2, h2);
}
#endif
rsvg_handle_get_dimensions(deco->handle, &dim);
cimg->width = dim.width;
cimg->height = dim.height;
if (strstr(cast_const_char cimg->url, "/media/math/render/svg/")) {
cimg->width = (ssize_t)(cimg->width * 1.36);
cimg->height = (ssize_t)(cimg->height * 1.36);
}
cimg->buffer_bytes_per_pixel = 4;
cimg->red_gamma = cimg->green_gamma = cimg->blue_gamma = (float)sRGB_gamma;
cimg->strip_optimized = 0;
if (header_dimensions_known(cimg))
goto end;
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));
if (cairo_surface_status(surf))
goto end_surface;
cairo = cairo_create(surf);
if (cairo_status(cairo))
goto end_cairo;
if (dim.width && dim.height)
cairo_scale(cairo, (double)cimg->width / (double)dim.width, (double)cimg->height / (double)dim.height);
h1 = close_std_handle(1);
h2 = close_std_handle(2);
rsvg_handle_render_cairo(deco->handle, cairo);
cairo_surface_flush(surf);
restore_std_handle(1, h1);
restore_std_handle(2, h2);
end_buffer = cimg->buffer + cimg->width * cimg->height * cimg->buffer_bytes_per_pixel;
if (!big_endian) {
for (p = cimg->buffer; p < end_buffer; p += 4) {
#ifdef t4c
t4c t = *(t4c *)p;
t4c r = (t << 16) | (t >> 16);
*(t4c *)p = (t & 0xff00ff00U) | (r & 0xff00ffU);
#else
unsigned char c;
c = p[0];
p[0] = p[2];
p[2] = c;
#endif
}
} else {
for (p = cimg->buffer; p < end_buffer; p += 4) {
#ifdef t4c
t4c t = *(t4c *)p;
*(t4c *)p = (t << 8) | (t >> 24);
#else
unsigned char c;
c = p[0];
p[0] = p[1];
p[1] = p[2];
p[2] = p[3];
p[3] = c;
#endif
}
}
end_cairo:
cairo_destroy(cairo);
end_surface:
cairo_surface_destroy(surf);
end:
img_end(cimg);
}
void svg_destroy_decoder(struct cached_image *cimg)
{
struct svg_decoder *deco = (struct svg_decoder *)cimg->decoder;
g_object_unref(deco->handle);
mem_free(deco->buffer);
}
void add_svg_version(unsigned char **s, int *l)
{
add_to_str(s, l, cast_uchar "RSVG (");
#ifdef LIBRSVG_MAJOR_VERSION
add_num_to_str(s, l, LIBRSVG_MAJOR_VERSION);
#endif
#ifdef LIBRSVG_MINOR_VERSION
add_chr_to_str(s, l, '.');
add_num_to_str(s, l, LIBRSVG_MINOR_VERSION);
#endif
#ifdef LIBRSVG_MICRO_VERSION
add_chr_to_str(s, l, '.');
add_num_to_str(s, l, LIBRSVG_MICRO_VERSION);
#endif
add_chr_to_str(s, l, ')');
}
#endif
#endif