summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2016-07-19 14:32:25 +0100
committerRobin Watts <robin.watts@artifex.com>2016-09-16 17:29:06 +0100
commit3711d72c81e9cac0243c5f66628216e4256e645f (patch)
tree85867b2fab9a25c34d8fdd95268e36ef8dd335c1
parent1d2dfa7eb1cb98cc546d92818e4189a3760cc6c8 (diff)
downloadmupdf-3711d72c81e9cac0243c5f66628216e4256e645f.tar.xz
Bug 695988: Avoid sending image data multiple times.
If the same image is used repeatedly, then we currently send the data again and again. Instead, send each image as a reusable symbol and reuse it as required. This reduces the size of the output by over 50% for the example file, but unfortunately results in the example file killing both Firefox and Edge when we attempt to load it. The file seems entirely valid though, and this is a saving on sane files. There is a #define that can be made to revert to the old behaviour (SVG_SEND_REPEATED_IMAGES).
-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)