// Copyright (C) 2004-2021 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 // // Alternative licensing terms are available from the licensor. // For commercial licensing, see or contact // Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, // CA 94945, U.S.A., +1(415)492-9861, for further information. #include "mupdf/fitz.h" #include #include #include #include #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"; 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) { char buf[40]; int i; i = 0; if (a == 0) buf[i++] = '0'; while (a) { buf[i++] = fz_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]); } static void fmtuint64(struct fmtbuf *out, uint64_t a, int s, int z, int w, int base) { char buf[80]; int i; i = 0; if (a == 0) buf[i++] = '0'; while (a) { buf[i++] = fz_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]); } static void fmtint32(struct fmtbuf *out, int value, int s, int z, int w, int base) { 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); } static void fmtint64(struct fmtbuf *out, int64_t value, int s, int z, int w, int base) { 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); } 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 { 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 { 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); } 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; 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 == '%') { 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'; /* 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'); /* fallthrough */ case 'x': if (bits == 64) { i64 = va_arg(args, int64_t); fmtuint64(&out, i64, 0, z, w, 16); } else { i32 = va_arg(args, int); fmtuint32(&out, i32, 0, z, w, 16); } break; case 'd': case 'i': if (bits == 64) { i64 = va_arg(args, int64_t); fmtint64(&out, i64, s, z, w, 10); } else { i32 = va_arg(args, int); fmtint32(&out, i32, s, z, w, 10); } break; case 'u': if (bits == 64) { i64 = va_arg(args, int64_t); fmtuint64(&out, i64, 0, z, w, 10); } else { i32 = va_arg(args, int); fmtuint32(&out, i32, 0, z, w, 10); } 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 '(': /* 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; }