| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803 |
- // 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;
- }
|