diff mupdf-source/source/fitz/printf.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/source/fitz/printf.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,803 @@
+// Copyright (C) 2004-2025 Artifex Software, Inc.
+//
+// This file is part of MuPDF.
+//
+// MuPDF is free software: you can redistribute it and/or modify it under the
+// terms of the GNU Affero General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or (at your option)
+// any later version.
+//
+// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+// details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
+//
+// Alternative licensing terms are available from the licensor.
+// For commercial licensing, see <https://www.artifex.com/> or contact
+// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
+// CA 94129, USA, for further information.
+
+#include "mupdf/fitz.h"
+
+#include <float.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#ifdef _MSC_VER
+#if _MSC_VER < 1500 /* MSVC 2008 */
+int snprintf(char *s, size_t n, const char *fmt, ...)
+{
+		int r;
+		va_list ap;
+		va_start(ap, fmt);
+		r = vsprintf(s, fmt, ap);
+		va_end(ap);
+		return r;
+}
+#else if _MSC_VER < 1900 /* MSVC 2015 */
+#define snprintf _snprintf
+#endif
+#endif
+
+static const char *fz_hex_digits = "0123456789abcdef";
+static const char *fz_hex_digits_UC = "0123456789ABCDEF";
+
+struct fmtbuf
+{
+	fz_context *ctx;
+	void *user;
+	void (*emit)(fz_context *ctx, void *user, int c);
+};
+
+static inline void fmtputc(struct fmtbuf *out, int c)
+{
+	out->emit(out->ctx, out->user, c);
+}
+
+/*
+ * Convert float to shortest possible string that won't lose precision, except:
+ * NaN to 0, +Inf to FLT_MAX, -Inf to -FLT_MAX.
+ */
+static void fmtfloat(struct fmtbuf *out, float f)
+{
+	char digits[40], *s = digits;
+	int exp, ndigits, point;
+
+	if (isnan(f)) f = 0;
+	if (isinf(f)) f = f < 0 ? -FLT_MAX : FLT_MAX;
+
+	if (signbit(f))
+		fmtputc(out, '-');
+
+	if (f == 0)
+	{
+		fmtputc(out, '0');
+		return;
+	}
+
+	ndigits = fz_grisu(f, digits, &exp);
+	point = exp + ndigits;
+
+	if (point <= 0)
+	{
+		fmtputc(out, '.');
+		while (point++ < 0)
+			fmtputc(out, '0');
+		while (ndigits-- > 0)
+			fmtputc(out, *s++);
+	}
+
+	else
+	{
+		while (ndigits-- > 0)
+		{
+			fmtputc(out, *s++);
+			if (--point == 0 && ndigits > 0)
+				fmtputc(out, '.');
+		}
+		while (point-- > 0)
+			fmtputc(out, '0');
+	}
+}
+
+static void fmtfloat_e(struct fmtbuf *out, double f, int w, int p)
+{
+	char buf[100], *s = buf;
+	snprintf(buf, sizeof buf, "%*.*e", w, p, f);
+	while (*s)
+		fmtputc(out, *s++);
+}
+
+static void fmtfloat_f(struct fmtbuf *out, double f, int w, int p)
+{
+	char buf[100], *s = buf;
+	snprintf(buf, sizeof buf, "%*.*f", w, p, f);
+	while (*s)
+		fmtputc(out, *s++);
+}
+
+static void fmtuint32(struct fmtbuf *out, unsigned int a, int s, int z, int w, int base, int q)
+{
+	char buf[40];
+	int i;
+	const char *hex_digits = fz_hex_digits;
+
+	if (base < 0)
+	{
+		base = -base;
+		hex_digits = fz_hex_digits_UC;
+	}
+
+	i = 0;
+	if (a == 0)
+		buf[i++] = '0';
+	while (a) {
+		buf[i++] = hex_digits[a % base];
+		a /= base;
+	}
+	if (s) {
+		if (z == '0')
+			while (i < w - 1)
+				buf[i++] = z;
+		buf[i++] = s;
+	}
+	while (i < w)
+		buf[i++] = z;
+	while (i > 0)
+	{
+		fmtputc(out, buf[--i]);
+		if (q && i != 0 && i % 3 == 0)
+			fmtputc(out, q);
+	}
+}
+
+static void fmtuint64(struct fmtbuf *out, uint64_t a, int s, int z, int w, int base, int q)
+{
+	char buf[80];
+	int i;
+	const char *hex_digits = fz_hex_digits;
+
+	if (base < 0)
+	{
+		base = -base;
+		hex_digits = fz_hex_digits_UC;
+	}
+
+	i = 0;
+	if (a == 0)
+		buf[i++] = '0';
+	while (a) {
+		buf[i++] = hex_digits[a % base];
+		a /= base;
+	}
+	if (s) {
+		if (z == '0')
+			while (i < w - 1)
+				buf[i++] = z;
+		buf[i++] = s;
+	}
+	while (i < w)
+		buf[i++] = z;
+	while (i > 0)
+	{
+		fmtputc(out, buf[--i]);
+		if (q && i != 0 && i % 3 == 0)
+			fmtputc(out, q);
+	}
+}
+
+static void fmtint32(struct fmtbuf *out, int value, int s, int z, int w, int base, int q)
+{
+	unsigned int a;
+
+	if (value < 0)
+	{
+		s = '-';
+		a = -value;
+	}
+	else if (s)
+	{
+		s = '+';
+		a = value;
+	}
+	else
+	{
+		s = 0;
+		a = value;
+	}
+	fmtuint32(out, a, s, z, w, base, q);
+}
+
+static void fmtint64(struct fmtbuf *out, int64_t value, int s, int z, int w, int base, int q)
+{
+	uint64_t a;
+
+	if (value < 0)
+	{
+		s = '-';
+		a = -value;
+	}
+	else if (s)
+	{
+		s = '+';
+		a = value;
+	}
+	else
+	{
+		s = 0;
+		a = value;
+	}
+	fmtuint64(out, a, s, z, w, base, q);
+}
+
+static void fmtquote(struct fmtbuf *out, const char *s, int sq, int eq, int verbatim)
+{
+	int i, n, c;
+	fmtputc(out, sq);
+	while (*s != 0) {
+		n = fz_chartorune(&c, s);
+		switch (c) {
+		default:
+			if (c < 32) {
+				fmtputc(out, '\\');
+				fmtputc(out, 'x');
+				fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
+				fmtputc(out, "0123456789ABCDEF"[(c)&15]);
+			} else if (c > 127) {
+				if (verbatim)
+				{
+					for (i = 0; i < n; ++i)
+						fmtputc(out, s[i]);
+				}
+				else if (c <= 0xffff)
+				{
+					fmtputc(out, '\\');
+					fmtputc(out, 'u');
+					fmtputc(out, "0123456789ABCDEF"[(c>>12)&15]);
+					fmtputc(out, "0123456789ABCDEF"[(c>>8)&15]);
+					fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
+					fmtputc(out, "0123456789ABCDEF"[(c)&15]);
+				}
+				else
+				{
+					/* Use a surrogate pair */
+					int hi = 0xd800 + ((c - 0x10000) >> 10);
+					int lo = 0xdc00 + ((c - 0x10000) & 0x3ff);
+					fmtputc(out, '\\');
+					fmtputc(out, 'u');
+					fmtputc(out, "0123456789ABCDEF"[(hi>>12)&15]);
+					fmtputc(out, "0123456789ABCDEF"[(hi>>8)&15]);
+					fmtputc(out, "0123456789ABCDEF"[(hi>>4)&15]);
+					fmtputc(out, "0123456789ABCDEF"[(hi)&15]);
+					fmtputc(out, '\\');
+					fmtputc(out, 'u');
+					fmtputc(out, "0123456789ABCDEF"[(lo>>12)&15]);
+					fmtputc(out, "0123456789ABCDEF"[(lo>>8)&15]);
+					fmtputc(out, "0123456789ABCDEF"[(lo>>4)&15]);
+					fmtputc(out, "0123456789ABCDEF"[(lo)&15]);
+				}
+			} else {
+				if (c == sq || c == eq)
+					fmtputc(out, '\\');
+				fmtputc(out, c);
+			}
+			break;
+		case '\\': fmtputc(out, '\\'); fmtputc(out, '\\'); break;
+		case '\b': fmtputc(out, '\\'); fmtputc(out, 'b'); break;
+		case '\f': fmtputc(out, '\\'); fmtputc(out, 'f'); break;
+		case '\n': fmtputc(out, '\\'); fmtputc(out, 'n'); break;
+		case '\r': fmtputc(out, '\\'); fmtputc(out, 'r'); break;
+		case '\t': fmtputc(out, '\\'); fmtputc(out, 't'); break;
+		}
+		s += n;
+	}
+	fmtputc(out, eq);
+}
+
+static void fmtquote_pdf(struct fmtbuf *out, const char *s, int sq, int eq)
+{
+	int c;
+	fmtputc(out, sq);
+	while ((c = (unsigned char)*s++) != 0) {
+		switch (c) {
+		default:
+			if (c < 32 || c > 127) {
+				fmtputc(out, '\\');
+				if (sq == '(')
+				{
+					fmtputc(out, '0' + ((c >> 6) & 7));
+					fmtputc(out, '0' + ((c >> 3) & 7));
+					fmtputc(out, '0' + ((c) & 7));
+				}
+				else
+				{
+					fmtputc(out, 'x');
+					fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
+					fmtputc(out, "0123456789ABCDEF"[(c)&15]);
+				}
+			} else {
+				if (c == sq || c == eq)
+					fmtputc(out, '\\');
+				fmtputc(out, c);
+			}
+			break;
+		case '\\': fmtputc(out, '\\'); fmtputc(out, '\\'); break;
+		case '\b': fmtputc(out, '\\'); fmtputc(out, 'b'); break;
+		case '\f': fmtputc(out, '\\'); fmtputc(out, 'f'); break;
+		case '\n': fmtputc(out, '\\'); fmtputc(out, 'n'); break;
+		case '\r': fmtputc(out, '\\'); fmtputc(out, 'r'); break;
+		case '\t': fmtputc(out, '\\'); fmtputc(out, 't'); break;
+		}
+	}
+	fmtputc(out, eq);
+}
+
+int
+fz_is_valid_xml_char(int c)
+{
+	if (c == 9 || c == 10 || c == 13)
+		return 1;
+	if (c < 32)
+		return 0;
+	if (c < 0xd800)
+		return 1;
+	if (c < 0xe000)
+		return 0;
+	if (c <= 0xfffd)
+		return 1;
+	if (c < 0x10000)
+		return 0;
+	if (c <= 0x10FFFF)
+		return 1;
+	return 0;
+}
+
+int
+fz_is_valid_xml_string(const char *s)
+{
+	int c, n;
+	while (*s != 0) {
+		n = fz_chartorune(&c, s);
+		if (!fz_is_valid_xml_char(c))
+			return 0;
+		s += n;
+	}
+	return 1;
+}
+
+int
+fz_range_limit_xml_char(int c)
+{
+	if (fz_is_valid_xml_char(c))
+		return c;
+	return 0xFFFD;
+}
+
+static void fmtquote_xml(struct fmtbuf *out, const char *s)
+{
+	int c, n;
+	fmtputc(out, '"');
+	while (*s != 0) {
+		n = fz_chartorune(&c, s);
+		switch (c) {
+		case '"':
+			fmtputc(out, '&');
+			fmtputc(out, 'q');
+			fmtputc(out, 'u');
+			fmtputc(out, 'o');
+			fmtputc(out, 't');
+			fmtputc(out, ';');
+			break;
+		case '&':
+			fmtputc(out, '&');
+			fmtputc(out, 'a');
+			fmtputc(out, 'm');
+			fmtputc(out, 'p');
+			fmtputc(out, ';');
+			break;
+		case '<':
+			fmtputc(out, '&');
+			fmtputc(out, 'l');
+			fmtputc(out, 't');
+			fmtputc(out, ';');
+			break;
+		case '>':
+			fmtputc(out, '&');
+			fmtputc(out, 'g');
+			fmtputc(out, 't');
+			fmtputc(out, ';');
+			break;
+		default:
+			c = fz_range_limit_xml_char(c);
+			if (c < 32 || c >= 127)
+			{
+				fmtputc(out, '&');
+				fmtputc(out, '#');
+				fmtputc(out, 'x');
+				if (c > 65535)
+				{
+					fmtputc(out, "0123456789ABCDEF"[(c>>20)&15]);
+					fmtputc(out, "0123456789ABCDEF"[(c>>16)&15]);
+				}
+				if (c > 255)
+				{
+					fmtputc(out, "0123456789ABCDEF"[(c>>12)&15]);
+					fmtputc(out, "0123456789ABCDEF"[(c>>8)&15]);
+				}
+				fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
+				fmtputc(out, "0123456789ABCDEF"[(c)&15]);
+				fmtputc(out, ';');
+			}
+			else
+				fmtputc(out, c);
+			break;
+		}
+		s += n;
+	}
+	fmtputc(out, '"');
+}
+
+static void fmtquote_hex(struct fmtbuf *out, const char *s)
+{
+	int c;
+	fmtputc(out, '"');
+	while ((c = *s++) != 0)
+	{
+		fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
+		fmtputc(out, "0123456789ABCDEF"[(c)&15]);
+	}
+	fmtputc(out, '"');
+}
+
+static void fmtname(struct fmtbuf *out, const char *s)
+{
+	int c;
+	fmtputc(out, '/');
+	while ((c = *s++) != 0) {
+		if (c <= 32 || c == '/' || c == '#') {
+			fmtputc(out, '#');
+			fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
+			fmtputc(out, "0123456789ABCDEF"[(c)&15]);
+		} else {
+			fmtputc(out, c);
+		}
+	}
+}
+
+void
+fz_format_string(fz_context *ctx, void *user, void (*emit)(fz_context *ctx, void *user, int c), const char *fmt, va_list args)
+{
+	struct fmtbuf out;
+	int c, s, z, p, w, q;
+	int32_t i32;
+	int64_t i64;
+	const char *str;
+	size_t bits;
+
+	out.ctx = ctx;
+	out.user = user;
+	out.emit = emit;
+
+	while ((c = *fmt++) != 0)
+	{
+		if (c == '%')
+		{
+			q = 0;
+			s = 0;
+			z = ' ';
+
+			/* flags */
+			while ((c = *fmt++) != 0)
+			{
+				/* plus sign */
+				if (c == '+')
+					s = 1;
+				/* space sign */
+				else if (c == ' ')
+					s = ' ';
+				/* zero padding */
+				else if (c == '0')
+					z = '0';
+				/* comma separators */
+				else if (c == '\'')
+					q = '\'';
+				else if (c == ',')
+					q = ',';
+				else if (c == '_')
+					q = '_';
+				/* TODO: '-' to left justify */
+				else
+					break;
+			}
+			if (c == 0)
+				break;
+
+			/* width */
+			w = 0;
+			if (c == '*') {
+				c = *fmt++;
+				w = va_arg(args, int);
+			} else {
+				while (c >= '0' && c <= '9') {
+					w = w * 10 + c - '0';
+					c = *fmt++;
+				}
+			}
+			if (c == 0)
+				break;
+
+			/* precision */
+			p = 6;
+			if (c == '.') {
+				c = *fmt++;
+				if (c == 0)
+					break;
+				if (c == '*') {
+					c = *fmt++;
+					p = va_arg(args, int);
+				} else {
+					if (c >= '0' && c <= '9')
+						p = 0;
+					while (c >= '0' && c <= '9') {
+						p = p * 10 + c - '0';
+						c = *fmt++;
+					}
+				}
+			}
+			if (c == 0)
+				break;
+
+			/* lengths */
+			bits = 0;
+			if (c == 'l') {
+				c = *fmt++;
+				bits = sizeof(int64_t) * 8;
+				if (c == 0)
+					break;
+			}
+			if (c == 't') {
+				c = *fmt++;
+				bits = sizeof(ptrdiff_t) * 8;
+				if (c == 0)
+					break;
+			}
+			if (c == 'z') {
+				c = *fmt++;
+				bits = sizeof(size_t) * 8;
+				if (c == 0)
+					break;
+			}
+
+			switch (c) {
+			default:
+				fmtputc(&out, '%');
+				fmtputc(&out, c);
+				break;
+			case '%':
+				fmtputc(&out, '%');
+				break;
+
+			case 'M':
+				{
+					fz_matrix *matrix = va_arg(args, fz_matrix*);
+					fmtfloat(&out, matrix->a); fmtputc(&out, ' ');
+					fmtfloat(&out, matrix->b); fmtputc(&out, ' ');
+					fmtfloat(&out, matrix->c); fmtputc(&out, ' ');
+					fmtfloat(&out, matrix->d); fmtputc(&out, ' ');
+					fmtfloat(&out, matrix->e); fmtputc(&out, ' ');
+					fmtfloat(&out, matrix->f);
+				}
+				break;
+			case 'R':
+				{
+					fz_rect *rect = va_arg(args, fz_rect*);
+					fmtfloat(&out, rect->x0); fmtputc(&out, ' ');
+					fmtfloat(&out, rect->y0); fmtputc(&out, ' ');
+					fmtfloat(&out, rect->x1); fmtputc(&out, ' ');
+					fmtfloat(&out, rect->y1);
+				}
+				break;
+			case 'P':
+				{
+					fz_point *point = va_arg(args, fz_point*);
+					fmtfloat(&out, point->x); fmtputc(&out, ' ');
+					fmtfloat(&out, point->y);
+				}
+				break;
+
+			case 'C': /* unicode char */
+				c = va_arg(args, int);
+				if (c < 128)
+					fmtputc(&out, c);
+				else {
+					char buf[10];
+					int i, n = fz_runetochar(buf, c);
+					for (i=0; i < n; ++i)
+						fmtputc(&out, buf[i]);
+				}
+				break;
+			case 'c':
+				c = va_arg(args, int);
+				fmtputc(&out, c);
+				break;
+			case 'e':
+				fmtfloat_e(&out, va_arg(args, double), w, p);
+				break;
+			case 'f':
+				fmtfloat_f(&out, va_arg(args, double), w, p);
+				break;
+			case 'g':
+				fmtfloat(&out, va_arg(args, double));
+				break;
+
+			case 'p':
+				bits = 8 * sizeof(void *);
+				z = '0';
+				fmtputc(&out, '0');
+				fmtputc(&out, 'x');
+				q = 0;
+				/* fallthrough */
+			case 'x':
+				if (bits == 64)
+				{
+					i64 = va_arg(args, int64_t);
+					fmtuint64(&out, i64, 0, z, w, 16, q);
+				}
+				else
+				{
+					i32 = va_arg(args, int);
+					fmtuint32(&out, i32, 0, z, w, 16, q);
+				}
+				break;
+			case 'X':
+				if (bits == 64)
+				{
+					i64 = va_arg(args, int64_t);
+					fmtuint64(&out, i64, 0, z, w, -16, q);
+				}
+				else
+				{
+					i32 = va_arg(args, int);
+					fmtuint32(&out, i32, 0, z, w, -16, q);
+				}
+				break;
+			case 'd':
+			case 'i':
+				if (bits == 64)
+				{
+					i64 = va_arg(args, int64_t);
+					fmtint64(&out, i64, s, z, w, 10, q);
+				}
+				else
+				{
+					i32 = va_arg(args, int);
+					fmtint32(&out, i32, s, z, w, 10, q);
+				}
+				break;
+			case 'u':
+				if (bits == 64)
+				{
+					i64 = va_arg(args, int64_t);
+					fmtuint64(&out, i64, 0, z, w, 10, q);
+				}
+				else
+				{
+					i32 = va_arg(args, int);
+					fmtuint32(&out, i32, 0, z, w, 10, q);
+				}
+				break;
+
+			case 's':
+				str = va_arg(args, const char*);
+				if (!str)
+					str = "(null)";
+				while ((c = *str++) != 0)
+					fmtputc(&out, c);
+				break;
+			case 'Q': /* quoted string (with verbatim unicode) */
+				str = va_arg(args, const char*);
+				if (!str) str = "";
+				fmtquote(&out, str, '"', '"', 1);
+				break;
+			case 'q': /* quoted string */
+				str = va_arg(args, const char*);
+				if (!str) str = "";
+				fmtquote(&out, str, '"', '"', 0);
+				break;
+			case '<': /* quoted string for xml */
+				str = va_arg(args, const char*);
+				if (!str) str = "";
+				fmtquote_xml(&out, str);
+				break;
+			case '>': /* hex string */
+				str = va_arg(args, const char*);
+				if (!str) str = "";
+				fmtquote_hex(&out, str);
+				break;
+			case '(': /* pdf string */
+				str = va_arg(args, const char*);
+				if (!str) str = "";
+				fmtquote_pdf(&out, str, '(', ')');
+				break;
+			case 'n': /* pdf name */
+				str = va_arg(args, const char*);
+				if (!str) str = "";
+				fmtname(&out, str);
+				break;
+			}
+		}
+		else
+		{
+			fmtputc(&out, c);
+		}
+	}
+}
+
+struct snprintf_buffer
+{
+	char *p;
+	size_t s, n;
+};
+
+static void snprintf_emit(fz_context *ctx, void *out_, int c)
+{
+	struct snprintf_buffer *out = out_;
+	if (out->n < out->s)
+		out->p[out->n] = c;
+	++(out->n);
+}
+
+size_t
+fz_vsnprintf(char *buffer, size_t space, const char *fmt, va_list args)
+{
+	struct snprintf_buffer out;
+	out.p = buffer;
+	out.s = space > 0 ? space - 1 : 0;
+	out.n = 0;
+
+	/* Note: using a NULL context is safe here */
+	fz_format_string(NULL, &out, snprintf_emit, fmt, args);
+	if (space > 0)
+		out.p[out.n < space ? out.n : space - 1] = '\0';
+
+	return out.n;
+}
+
+size_t
+fz_snprintf(char *buffer, size_t space, const char *fmt, ...)
+{
+	va_list ap;
+	struct snprintf_buffer out;
+	out.p = buffer;
+	out.s = space > 0 ? space - 1 : 0;
+	out.n = 0;
+
+	va_start(ap, fmt);
+	/* Note: using a NULL context is safe here */
+	fz_format_string(NULL, &out, snprintf_emit, fmt, ap);
+	if (space > 0)
+		out.p[out.n < space ? out.n : space - 1] = '\0';
+	va_end(ap);
+
+	return out.n;
+}
+
+char *
+fz_asprintf(fz_context *ctx, const char *fmt, ...)
+{
+	size_t len;
+	char *mem;
+	va_list ap;
+	va_start(ap, fmt);
+	len = fz_vsnprintf(NULL, 0, fmt, ap);
+	va_end(ap);
+	mem = Memento_label(fz_malloc(ctx, len+1), "asprintf");
+	va_start(ap, fmt);
+	fz_vsnprintf(mem, len+1, fmt, ap);
+	va_end(ap);
+	return mem;
+}