summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/fitz/svg-device.c184
1 files changed, 107 insertions, 77 deletions
diff --git a/source/fitz/svg-device.c b/source/fitz/svg-device.c
index 998e4019..b997653d 100644
--- a/source/fitz/svg-device.c
+++ b/source/fitz/svg-device.c
@@ -5,6 +5,7 @@ typedef struct svg_device_s svg_device;
typedef struct tile_s tile;
typedef struct font_s font;
typedef struct glyph_s glyph;
+typedef struct image_s image;
struct tile_s
{
@@ -29,6 +30,12 @@ struct font_s
glyph *sentlist;
};
+struct image_s
+{
+ int id;
+ fz_image *image;
+};
+
struct svg_device_s
{
fz_device super;
@@ -48,6 +55,10 @@ struct svg_device_s
int num_fonts;
int max_fonts;
font *fonts;
+
+ int num_images;
+ int max_images;
+ image *images;
};
/* SVG is awkward about letting us define things within symbol definitions
@@ -734,12 +745,90 @@ send_data_base64(fz_context *ctx, fz_output *out, fz_buffer *buffer)
}
}
+/* We spot repeated images, and send them just once using
+ * symbols. Unfortunately, for pathological files, such
+ * as the example in Bug695988, this can cause viewers to
+ * have conniptions. We therefore have a define that can
+ * be made to avoid this (SVG_SEND_REPEATED_IMAGES). */
+static void
+svg_send_image(fz_context *ctx, svg_device *sdev, fz_image *img)
+{
+ fz_output *out = sdev->out;
+ fz_compressed_buffer *buffer;
+#ifndef SVG_SEND_REPEATED_IMAGES
+ int i;
+ int id;
+
+ for (i = sdev->num_images-1; i >= 0; i--)
+ {
+ if (img == sdev->images[i].image)
+ break;
+ }
+ if (i >= 0)
+ id = sdev->images[i].id;
+ else
+ {
+ /* We need to send this image for the first time */
+ if (sdev->num_images == sdev->max_images)
+ {
+ int new_max = sdev->max_images * 2;
+ if (new_max == 0)
+ new_max = 32;
+ sdev->images = fz_resize_array(ctx, sdev->images, new_max, sizeof(image));
+ sdev->max_images = new_max;
+ }
+
+ id = sdev->id++;
+ out = start_def(ctx, sdev);
+ fz_printf(ctx, out, "<symbol id=\"im%d\">", id);
+#endif
+ fz_printf(ctx, out, "<image");
+ buffer = fz_compressed_image_buffer(ctx, img);
+ fz_printf(ctx, out, " width=\"%dpx\" height=\"%dpx\" xlink:href=\"data:", img->w, img->h);
+ switch (buffer == NULL ? FZ_IMAGE_JPX : buffer->params.type)
+ {
+ case FZ_IMAGE_PNG:
+ fz_printf(ctx, out, "image/png;base64,");
+ send_data_base64(ctx, out, buffer->buffer);
+ break;
+ case FZ_IMAGE_JPEG:
+ /* SVG cannot cope with CMYK images */
+ if (img->colorspace != fz_device_cmyk(ctx))
+ {
+ fz_printf(ctx, out, "image/jpeg;base64,");
+ send_data_base64(ctx, out, buffer->buffer);
+ break;
+ }
+ /*@fallthough@*/
+ default:
+ {
+ fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, img);
+ fz_printf(ctx, out, "image/png;base64,");
+ send_data_base64(ctx, out, buf);
+ fz_drop_buffer(ctx, buf);
+ break;
+ }
+ }
+ fz_printf(ctx, out, "\"/>");
+#ifndef SVG_SEND_REPEATED_IMAGES
+
+ fz_printf(ctx, out, "</symbol>");
+ out = end_def(ctx, sdev);
+
+ sdev->images[sdev->num_images].id = id;
+ sdev->images[sdev->num_images].image = fz_keep_image(ctx, img);
+ sdev->num_images++;
+ }
+
+ fz_printf(ctx, out, "<use x=\"0\" y=\"0\" xlink:href=\"#im%d\"/>", id);
+#endif
+}
+
static void
svg_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha)
{
svg_device *sdev = (svg_device*)dev;
fz_output *out = sdev->out;
- fz_compressed_buffer *buffer;
fz_matrix local_ctm = *ctm;
fz_matrix scale = { 0 };
@@ -748,34 +837,13 @@ svg_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_ma
scale.d = 1.0f / image->h;
fz_concat(&local_ctm, &scale, ctm);
+ fz_printf(ctx, out, "<g");
if (alpha != 1.0f)
- fz_printf(ctx, out, "<g opacity=\"%g\">", alpha);
- fz_printf(ctx, out, "<image");
+ fz_printf(ctx, out, " opacity=\"%g\"", alpha);
svg_dev_ctm(ctx, sdev, &local_ctm);
- buffer = fz_compressed_image_buffer(ctx, image);
- fz_printf(ctx, out, " width=\"%dpx\" height=\"%dpx\" xlink:href=\"data:", image->w, image->h);
- switch (buffer == NULL ? FZ_IMAGE_JPX : buffer->params.type)
- {
- case FZ_IMAGE_JPEG:
- fz_printf(ctx, out, "image/jpeg;base64,");
- send_data_base64(ctx, out, buffer->buffer);
- break;
- case FZ_IMAGE_PNG:
- fz_printf(ctx, out, "image/png;base64,");
- send_data_base64(ctx, out, buffer->buffer);
- break;
- default:
- {
- fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image);
- fz_printf(ctx, out, "image/png;base64,");
- send_data_base64(ctx, out, buf);
- fz_drop_buffer(ctx, buf);
- break;
- }
- }
- fz_printf(ctx, out, "\"/>\n");
- if (alpha != 1.0f)
- fz_printf(ctx, out, "</g>");
+ fz_printf(ctx, out, ">");
+ svg_send_image(ctx, sdev, image);
+ fz_printf(ctx, out, "</g>\n");
}
static void
@@ -783,7 +851,6 @@ svg_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_ma
{
svg_device *sdev = (svg_device*)dev;
fz_output *out = sdev->out;
-
fz_rect rect;
fz_irect bbox;
fz_pixmap *pix;
@@ -828,8 +895,6 @@ svg_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const
fz_colorspace *colorspace, const float *color, float alpha)
{
svg_device *sdev = (svg_device*)dev;
- fz_compressed_buffer *buffer;
-
fz_output *out;
fz_matrix local_ctm = *ctm;
fz_matrix scale = { 0 };
@@ -840,29 +905,9 @@ svg_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const
fz_concat(&local_ctm, &scale, ctm);
out = start_def(ctx, sdev);
- fz_printf(ctx, out, "<mask id=\"ma%d\"><image", mask);
- fz_printf(ctx, out, " width=\"%dpx\" height=\"%dpx\" xlink:href=\"data:", image->w, image->h);
- buffer = fz_compressed_image_buffer(ctx, image);
- switch (buffer == NULL ? FZ_IMAGE_JPX : buffer->params.type)
- {
- case FZ_IMAGE_JPEG:
- fz_printf(ctx, out, "image/jpeg;base64,");
- send_data_base64(ctx, out, buffer->buffer);
- break;
- case FZ_IMAGE_PNG:
- fz_printf(ctx, out, "image/png;base64,");
- send_data_base64(ctx, out, buffer->buffer);
- break;
- default:
- {
- fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image);
- fz_printf(ctx, out, "image/png;base64,");
- send_data_base64(ctx, out, buf);
- fz_drop_buffer(ctx, buf);
- break;
- }
- }
- fz_printf(ctx, out, "\"/></mask>\n");
+ fz_printf(ctx, out, "<mask id=\"ma%d\">", mask);
+ svg_send_image(ctx, sdev, image);
+ fz_printf(ctx, out, "</mask>\n");
out = end_def(ctx, sdev);
fz_printf(ctx, out, "<rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\"", image->w, image->h);
svg_dev_fill_color(ctx, sdev, colorspace, color, alpha);
@@ -878,37 +923,17 @@ svg_dev_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const
fz_matrix local_ctm = *ctm;
fz_matrix scale = { 0 };
int mask = sdev->id++;
- fz_compressed_buffer *buffer;
scale.a = 1.0f / image->w;
scale.d = 1.0f / image->h;
fz_concat(&local_ctm, &scale, ctm);
out = start_def(ctx, sdev);
- fz_printf(ctx, out, "<mask id=\"ma%d\"><image", mask);
+ fz_printf(ctx, out, "<mask id=\"ma%d\"><g", mask);
svg_dev_ctm(ctx, sdev, &local_ctm);
- fz_printf(ctx, out, " width=\"%dpx\" height=\"%dpx\" xlink:href=\"data:", image->w, image->h);
- buffer = fz_compressed_image_buffer(ctx, image);
- switch (buffer == NULL ? FZ_IMAGE_JPX : buffer->params.type)
- {
- case FZ_IMAGE_JPEG:
- fz_printf(ctx, out, "image/jpeg;base64,");
- send_data_base64(ctx, out, buffer->buffer);
- break;
- case FZ_IMAGE_PNG:
- fz_printf(ctx, out, "image/png;base64,");
- send_data_base64(ctx, out, buffer->buffer);
- break;
- default:
- {
- fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image);
- fz_printf(ctx, out, "image/png;base64,");
- send_data_base64(ctx, out, buf);
- fz_drop_buffer(ctx, buf);
- break;
- }
- }
- fz_printf(ctx, out, "\"/></mask>\n");
+ fz_printf(ctx, out, ">");
+ svg_send_image(ctx, sdev, image);
+ fz_printf(ctx, out, "</g></mask>\n");
out = end_def(ctx, sdev);
fz_printf(ctx, out, "<g mask=\"url(#ma%d)\">\n", mask);
}
@@ -1106,6 +1131,11 @@ svg_dev_drop_device(fz_context *ctx, fz_device *dev)
fz_free(ctx, sdev->fonts[i].sentlist);
}
fz_free(ctx, sdev->fonts);
+ for (i = 0; i < sdev->num_images; i++)
+ {
+ fz_drop_image(ctx, sdev->images[i].image);
+ }
+ fz_free(ctx, sdev->images);
}
fz_device *fz_new_svg_device(fz_context *ctx, fz_output *out, float page_width, float page_height)