File webp-upstream-697749.patch of Package mupdf

diff --git a/include/mupdf/fitz/compressed-buffer.h b/include/mupdf/fitz/compressed-buffer.h
index 80458f11..64202fd6 100644
--- a/include/mupdf/fitz/compressed-buffer.h
+++ b/include/mupdf/fitz/compressed-buffer.h
@@ -125,10 +125,10 @@ fz_stream *fz_open_image_decomp_stream_from_buffer(fz_context *ctx, fz_compresse
 fz_stream *fz_open_image_decomp_stream(fz_context *ctx, fz_stream *, fz_compression_params *, int *l2factor);
 
 /**
-	Recognise image format strings in the first 8 bytes from image
+	Recognise image format strings in the first 12 bytes from image
 	data.
 */
-int fz_recognize_image_format(fz_context *ctx, unsigned char p[8]);
+int fz_recognize_image_format(fz_context *ctx, unsigned char p[12]);
 
 /**
 	Map from FZ_IMAGE_* value to string.
@@ -167,6 +167,7 @@ enum
 	FZ_IMAGE_PNM,
 	FZ_IMAGE_TIFF,
 	FZ_IMAGE_PSD,
+	FZ_IMAGE_WEBP,
 };
 
 /**
diff --git a/scripts/wrap/make_cppyy.py b/scripts/wrap/make_cppyy.py
index 89be2fec..91396c33 100644
--- a/scripts/wrap/make_cppyy.py
+++ b/scripts/wrap/make_cppyy.py
@@ -763,7 +763,7 @@ def make_cppyy(
                     ''')
 
             # Provide an overload for mfz_recognize_image_format(), because
-            # the default unsigned char p[8] causes problems.
+            # the default unsigned char p[12] causes problems.
             #
             cppyy.cppdef(f'''
                     namespace mupdf
diff --git a/source/cbz/mucbz.c b/source/cbz/mucbz.c
index e2f99530..e877490b 100644
--- a/source/cbz/mucbz.c
+++ b/source/cbz/mucbz.c
@@ -49,6 +49,7 @@ static const char *cbz_ext_list[] = {
 	".tif",
 	".tiff",
 	".wdp",
+	".webp",
 	NULL
 };
 
diff --git a/source/cbz/muimg.c b/source/cbz/muimg.c
index 69d1f990..78c07017 100644
--- a/source/cbz/muimg.c
+++ b/source/cbz/muimg.c
@@ -191,7 +191,7 @@ img_open_document(fz_context *ctx, const fz_document_handler *handler, fz_stream
 		len = fz_buffer_storage(ctx, doc->buffer, &data);
 
 		fmt = FZ_IMAGE_UNKNOWN;
-		if (len >= 8)
+		if (len >= 12)
 			fmt = fz_recognize_image_format(ctx, data);
 		if (fmt == FZ_IMAGE_TIFF)
 		{
@@ -236,7 +236,7 @@ img_open_document(fz_context *ctx, const fz_document_handler *handler, fz_stream
 static int
 img_recognize_content(fz_context *ctx, const fz_document_handler *handler, fz_stream *stream, fz_archive *dir, void **state, fz_document_recognize_state_free_fn **free_state)
 {
-	unsigned char data[8];
+	unsigned char data[12];
 	size_t n;
 	int fmt;
 
@@ -248,9 +248,9 @@ img_recognize_content(fz_context *ctx, const fz_document_handler *handler, fz_st
 	if (free_state)
 		*free_state = NULL;
 
-	n = fz_read(ctx, stream, data, 8);
+	n = fz_read(ctx, stream, data, 12);
 
-	if (n != 8)
+	if (n != 12)
 		return 0;
 
 	fmt = fz_recognize_image_format(ctx, data);
diff --git a/source/fitz/image-imp.h b/source/fitz/image-imp.h
index 06db2afc..ee6db1c5 100644
--- a/source/fitz/image-imp.h
+++ b/source/fitz/image-imp.h
@@ -32,6 +32,7 @@ fz_pixmap *fz_load_gif(fz_context *ctx, const unsigned char *data, size_t size);
 fz_pixmap *fz_load_bmp(fz_context *ctx, const unsigned char *data, size_t size);
 fz_pixmap *fz_load_pnm(fz_context *ctx, const unsigned char *data, size_t size);
 fz_pixmap *fz_load_jbig2(fz_context *ctx, const unsigned char *data, size_t size);
+fz_pixmap *fz_load_webp(fz_context *ctx, const unsigned char *data, size_t size);
 
 void fz_load_jpeg_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace, uint8_t *orientation);
 void fz_load_jpx_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace);
@@ -43,5 +44,6 @@ void fz_load_gif_info(fz_context *ctx, const unsigned char *data, size_t size, i
 void fz_load_bmp_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace);
 void fz_load_pnm_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace);
 void fz_load_jbig2_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace);
+void fz_load_webp_info(fz_context *ctx, const unsigned char *data, size_t size, int *w, int *h, int *xres, int *yres, fz_colorspace **cspace);
 
 #endif
diff --git a/source/fitz/image.c b/source/fitz/image.c
index e898aa42..915ddf28 100644
--- a/source/fitz/image.c
+++ b/source/fitz/image.c
@@ -794,6 +794,9 @@ compressed_image_get_pixmap(fz_context *ctx, fz_image *image_, fz_irect *subarea
 	case FZ_IMAGE_BMP:
 		tile = fz_load_bmp(ctx, image->buffer->buffer->data, image->buffer->buffer->len);
 		break;
+	case FZ_IMAGE_WEBP:
+		tile = fz_load_webp(ctx, image->buffer->buffer->data, image->buffer->buffer->len);
+		break;
 	case FZ_IMAGE_TIFF:
 		tile = fz_load_tiff(ctx, image->buffer->buffer->data, image->buffer->buffer->len);
 		break;
@@ -1295,7 +1298,7 @@ fz_lookup_image_type(const char *type)
 }
 
 int
-fz_recognize_image_format(fz_context *ctx, unsigned char p[8])
+fz_recognize_image_format(fz_context *ctx, unsigned char p[12])
 {
 	if (p[0] == 'P' && p[1] >= '1' && p[1] <= '7')
 		return FZ_IMAGE_PNM;
@@ -1328,6 +1331,9 @@ fz_recognize_image_format(fz_context *ctx, unsigned char p[8])
 		return FZ_IMAGE_JBIG2;
 	if (p[0] == '8' && p[1] == 'B' && p[2] == 'P' && p[3] == 'S')
 		return FZ_IMAGE_PSD;
+	if (p[0] == 'R' && p[1] == 'I' && p[2] == 'F' && p[3] == 'F' &&
+		p[8] == 'W' && p[9] == 'E' && p[10] == 'B' && p[11] == 'P')
+		return FZ_IMAGE_WEBP;
 	return FZ_IMAGE_UNKNOWN;
 }
 
@@ -1344,7 +1350,7 @@ fz_new_image_from_buffer(fz_context *ctx, fz_buffer *buffer)
 	int bpc;
 	uint8_t orientation = 0;
 
-	if (len < 8)
+	if (len < 12)
 		fz_throw(ctx, FZ_ERROR_FORMAT, "unknown image file format");
 
 	type = fz_recognize_image_format(ctx, buf);
@@ -1382,6 +1388,9 @@ fz_new_image_from_buffer(fz_context *ctx, fz_buffer *buffer)
 		fz_load_jbig2_info(ctx, buf, len, &w, &h, &xres, &yres, &cspace);
 		bpc = 1;
 		break;
+	case FZ_IMAGE_WEBP:
+		fz_load_webp_info(ctx, buf, len, &w, &h, &xres, &yres, &cspace);
+		break;
 	default:
 		fz_throw(ctx, FZ_ERROR_FORMAT, "unknown image file format");
 	}
diff --git a/source/fitz/load-webp.c b/source/fitz/load-webp.c
new file mode 100644
index 00000000..1ab554da
--- /dev/null
+++ b/source/fitz/load-webp.c
@@ -0,0 +1,280 @@
+#include "mupdf/fitz.h"
+
+#ifdef HAVE_WEBP
+
+#include "pixmap-imp.h"
+
+#include <math.h>
+#include <string.h>
+#include <limits.h>
+
+#include <webp/decode.h>
+#include <webp/demux.h>
+#include <webp/types.h>
+
+struct info
+{
+	int width, height;
+	int xres, yres;
+	uint8_t orientation;
+	int pages;
+	fz_colorspace *cs;
+};
+
+/* Returns true if <x> can be represented as an integer without overflow.
+ *
+ * We can't use comparisons such as 'return x < INT_MAX' because INT_MAX is
+ * not safely convertible to float - it ends up as INT_MAX+1 so the comparison
+ * doesn't do what we want.
+ *
+ * Instead we do a round-trip conversion and return true if this differs by
+ * less than 1. This relies on high adjacent float values that differ by more
+ * than 1, actually being exact integers, so the round-trip doesn't change the
+ * value.
+ */
+static int float_can_be_int(float x)
+{
+	return fabsf(x - (float)(int) x) < 1;
+}
+
+static uint8_t exif_orientation_to_mupdf[9] = { 0, 1, 5, 3, 7, 6, 4, 8, 2 };
+
+static inline int read_value(const unsigned char *data, int bytes, int is_big_endian)
+{
+	int value = 0;
+	if (!is_big_endian)
+		data += bytes;
+	for (; bytes > 0; bytes--)
+		value = (value << 8) | (is_big_endian ? *data++ : *--data);
+	return value;
+}
+
+static int extract_exif_resolution(WebPData* chunk,
+	int *xres, int *yres, uint8_t *orientation)
+{
+	int is_big_endian, orient;
+	const unsigned char *data;
+	unsigned int offset, ifd_len, res_type = 0;
+	float x_res = 0, y_res = 0;
+
+	if (!chunk || chunk->size < 14)
+		return 0;
+	data = (const unsigned char *)chunk->bytes;
+	if (read_value(data, 4, 1) != 0x45786966 /* Exif */ || read_value(data + 4, 2, 1) != 0x0000)
+		return 0;
+	if (read_value(data + 6, 4, 1) == 0x49492A00)
+		is_big_endian = 0;
+	else if (read_value(data + 6, 4, 1) == 0x4D4D002A)
+		is_big_endian = 1;
+	else
+		return 0;
+
+	offset = read_value(data + 10, 4, is_big_endian) + 6;
+	if (offset < 14 || offset > chunk->size - 2)
+		return 0;
+	ifd_len = read_value(data + offset, 2, is_big_endian);
+	for (offset += 2; ifd_len > 0 && offset + 12 < chunk->size; ifd_len--, offset += 12)
+	{
+		int tag = read_value(data + offset, 2, is_big_endian);
+		int type = read_value(data + offset + 2, 2, is_big_endian);
+		int count = read_value(data + offset + 4, 4, is_big_endian);
+		unsigned int value_off = read_value(data + offset + 8, 4, is_big_endian) + 6;
+		switch (tag)
+		{
+		case 0x112:
+			if (type == 3 && count == 1) {
+				orient = read_value(data + offset + 8, 2, is_big_endian);
+				if (orient >= 1 && orient <= 8 && orientation)
+					*orientation = exif_orientation_to_mupdf[orient];
+			}
+			break;
+		case 0x11A:
+			if (type == 5 && value_off > offset && value_off <= chunk->size - 8)
+				x_res = 1.0f * read_value(data + value_off, 4, is_big_endian) / read_value(data + value_off + 4, 4, is_big_endian);
+			break;
+		case 0x11B:
+			if (type == 5 && value_off > offset && value_off <= chunk->size - 8)
+				y_res = 1.0f * read_value(data + value_off, 4, is_big_endian) / read_value(data + value_off + 4, 4, is_big_endian);
+			break;
+		case 0x128:
+			if (type == 3 && count == 1)
+				res_type = read_value(data + offset + 8, 2, is_big_endian);
+			break;
+		}
+	}
+
+	if (x_res <= 0 || !float_can_be_int(x_res) || y_res <= 0 || !float_can_be_int(y_res))
+		return 0;
+	if (res_type == 2)
+	{
+		*xres = (int)x_res;
+		*yres = (int)y_res;
+	}
+	else if (res_type == 3)
+	{
+		*xres = (int)(x_res * 254 / 100);
+		*yres = (int)(y_res * 254 / 100);
+	}
+	else
+	{
+		*xres = 0;
+		*yres = 0;
+	}
+	return 1;
+}
+
+static fz_colorspace *extract_icc_profile(fz_context *ctx, WebPData* chunk, fz_colorspace *colorspace)
+{
+#if FZ_ENABLE_ICC
+	fz_buffer *buf = NULL;
+
+	fz_var(buf);
+
+	if (!chunk || !chunk->bytes || chunk->size == 0)
+		return colorspace;
+
+	fz_try(ctx)
+	{
+		buf = fz_new_buffer_from_copied_data(ctx, chunk->bytes, chunk->size);
+		if (buf)
+		{
+			fz_colorspace *icc = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_NONE, 0, NULL, buf);
+			fz_drop_colorspace(ctx, colorspace);
+			colorspace = icc;
+		}
+	}
+	fz_always(ctx)
+		fz_drop_buffer(ctx, buf);
+	fz_catch(ctx)
+		fz_warn(ctx, "ignoring embedded ICC profile in JPEG");
+
+	return colorspace;
+#else
+	return colorspace;
+#endif
+}
+
+static fz_pixmap *
+webp_read_image(fz_context *ctx, struct info *info, const unsigned char *p, size_t total, int only_metadata)
+{
+	fz_pixmap *image = NULL;
+
+	fz_try(ctx)
+	{
+		struct WebPBitstreamFeatures features;
+		WebPData webp_data;
+		WebPDemuxer* demux = NULL;
+
+		if (WebPGetFeatures(p, total, &features) != VP8_STATUS_OK)
+			fz_throw(ctx, FZ_ERROR_GENERIC, "unable to extract webp features");
+
+		info->width = features.width;
+		info->height = features.height;
+		info->xres = 72;
+		info->yres = 72;
+		info->cs = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
+
+		webp_data.bytes = p;
+		webp_data.size = total;
+		demux = WebPDemux(&webp_data);
+
+		if (demux != NULL)
+		{
+			if (WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS) & EXIF_FLAG)
+			{
+				WebPChunkIterator chunk_iter;
+				if (WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter))
+				{
+					extract_exif_resolution(&chunk_iter.chunk, &info->xres, &info->yres, &info->orientation);
+				}
+				WebPDemuxReleaseChunkIterator(&chunk_iter);
+			}
+			if (WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS) & ICCP_FLAG)
+			{
+				WebPChunkIterator chunk_iter;
+				if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter))
+				{
+					info->cs = extract_icc_profile(ctx, &chunk_iter.chunk, info->cs);
+				}
+				WebPDemuxReleaseChunkIterator(&chunk_iter);
+			}
+
+			WebPDemuxDelete(demux);
+		}
+
+		if (!only_metadata)
+		{
+			uint8_t* rgba = features.has_alpha ? WebPDecodeRGBA(p, total, NULL, NULL) : WebPDecodeRGB(p, total, NULL, NULL);
+
+			if (rgba == NULL)
+				fz_throw(ctx, FZ_ERROR_GENERIC, "failed decoding webp image");
+
+			image = fz_new_pixmap(ctx, info->cs, info->width, info->height, NULL, features.has_alpha);
+			image->xres = info->xres;
+			image->yres = info->yres;
+
+			fz_clear_pixmap(ctx, image);
+			fz_unpack_tile(ctx, image, rgba, image->n, 8, image->stride, 1);
+
+			WebPFree(rgba);
+		}
+	}
+	fz_catch(ctx)
+		fz_rethrow(ctx);
+
+	return image;
+}
+
+fz_pixmap *
+fz_load_webp(fz_context *ctx, const unsigned char *p, size_t total)
+{
+	fz_pixmap *image;
+	struct info info;
+
+	fz_try(ctx)
+	{
+		image = webp_read_image(ctx, &info, p, total, 0);
+	}
+	fz_always(ctx)
+		fz_drop_colorspace(ctx, info.cs);
+	fz_catch(ctx)
+		fz_rethrow(ctx);
+
+	return image;
+}
+
+void
+fz_load_webp_info(fz_context *ctx, const unsigned char *p, size_t total, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep)
+{
+	struct info info;
+
+	fz_try(ctx)
+		webp_read_image(ctx, &info, p, total, 1);
+	fz_catch(ctx)
+	{
+		fz_drop_colorspace(ctx, info.cs);
+		fz_rethrow(ctx);
+	}
+
+	*cspacep = info.cs;
+	*wp = info.width;
+	*hp = info.height;
+	*xresp = info.xres;
+	*yresp = info.xres;
+}
+
+#else /* HAVE_WEBP */
+
+fz_pixmap *
+fz_load_webp(fz_context *ctx, const unsigned char *p, size_t total)
+{
+	fz_throw(ctx, FZ_ERROR_GENERIC, "WebP codec is not available");
+}
+
+void
+fz_load_webp_info(fz_context *ctx, const unsigned char *p, size_t total, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep)
+{
+	fz_throw(ctx, FZ_ERROR_GENERIC, "WebP codec is not available");
+}
+
+#endif /* HAVE_WEBP */
diff --git a/source/html/mobi.c b/source/html/mobi.c
index e0a076d1..e586eddb 100644
--- a/source/html/mobi.c
+++ b/source/html/mobi.c
@@ -318,7 +318,7 @@ fz_extract_html_from_mobi(fz_context *ctx, fz_buffer *mobi)
 		for (i = extra; i < n; ++i)
 		{
 			uint32_t size = offsets[i+1] - offsets[i];
-			if (size > 8)
+			if (size > 12)
 			{
 				unsigned char *data = mobi->data + offsets[i];
 				if (fz_recognize_image_format(ctx, data))
openSUSE Build Service is sponsored by