1 #include "mupdf/fitz.h"
2
3 #include <float.h>
4 #include <math.h>
5 #include <stdarg.h>
6 #include <stdio.h>
7
8 #ifdef _MSC_VER
9 #if _MSC_VER < 1500 /* MSVC 2008 */
snprintf(char * s,size_t n,const char * fmt,...)10 int snprintf(char *s, size_t n, const char *fmt, ...)
11 {
12 int r;
13 va_list ap;
14 va_start(ap, fmt);
15 r = vsprintf(s, fmt, ap);
16 va_end(ap);
17 return r;
18 }
19 #else if _MSC_VER < 1900 /* MSVC 2015 */
20 #define snprintf _snprintf
21 #endif
22 #endif
23
24 static const char *fz_hex_digits = "0123456789abcdef";
25
26 struct fmtbuf
27 {
28 fz_context *ctx;
29 void *user;
30 void (*emit)(fz_context *ctx, void *user, int c);
31 };
32
fmtputc(struct fmtbuf * out,int c)33 static inline void fmtputc(struct fmtbuf *out, int c)
34 {
35 out->emit(out->ctx, out->user, c);
36 }
37
38 /*
39 * Convert float to shortest possible string that won't lose precision, except:
40 * NaN to 0, +Inf to FLT_MAX, -Inf to -FLT_MAX.
41 */
fmtfloat(struct fmtbuf * out,float f)42 static void fmtfloat(struct fmtbuf *out, float f)
43 {
44 char digits[40], *s = digits;
45 int exp, ndigits, point;
46
47 if (isnan(f)) f = 0;
48 if (isinf(f)) f = f < 0 ? -FLT_MAX : FLT_MAX;
49
50 if (signbit(f))
51 fmtputc(out, '-');
52
53 if (f == 0)
54 {
55 fmtputc(out, '0');
56 return;
57 }
58
59 ndigits = fz_grisu(f, digits, &exp);
60 point = exp + ndigits;
61
62 if (point <= 0)
63 {
64 fmtputc(out, '.');
65 while (point++ < 0)
66 fmtputc(out, '0');
67 while (ndigits-- > 0)
68 fmtputc(out, *s++);
69 }
70
71 else
72 {
73 while (ndigits-- > 0)
74 {
75 fmtputc(out, *s++);
76 if (--point == 0 && ndigits > 0)
77 fmtputc(out, '.');
78 }
79 while (point-- > 0)
80 fmtputc(out, '0');
81 }
82 }
83
fmtfloat_e(struct fmtbuf * out,double f,int w,int p)84 static void fmtfloat_e(struct fmtbuf *out, double f, int w, int p)
85 {
86 char buf[100], *s = buf;
87 snprintf(buf, sizeof buf, "%*.*e", w, p, f);
88 while (*s)
89 fmtputc(out, *s++);
90 }
91
fmtfloat_f(struct fmtbuf * out,double f,int w,int p)92 static void fmtfloat_f(struct fmtbuf *out, double f, int w, int p)
93 {
94 char buf[100], *s = buf;
95 snprintf(buf, sizeof buf, "%*.*f", w, p, f);
96 while (*s)
97 fmtputc(out, *s++);
98 }
99
fmtuint32(struct fmtbuf * out,unsigned int a,int s,int z,int w,int base)100 static void fmtuint32(struct fmtbuf *out, unsigned int a, int s, int z, int w, int base)
101 {
102 char buf[40];
103 int i;
104
105 i = 0;
106 if (a == 0)
107 buf[i++] = '0';
108 while (a) {
109 buf[i++] = fz_hex_digits[a % base];
110 a /= base;
111 }
112 if (s) {
113 if (z == '0')
114 while (i < w - 1)
115 buf[i++] = z;
116 buf[i++] = s;
117 }
118 while (i < w)
119 buf[i++] = z;
120 while (i > 0)
121 fmtputc(out, buf[--i]);
122 }
123
fmtuint64(struct fmtbuf * out,uint64_t a,int s,int z,int w,int base)124 static void fmtuint64(struct fmtbuf *out, uint64_t a, int s, int z, int w, int base)
125 {
126 char buf[80];
127 int i;
128
129 i = 0;
130 if (a == 0)
131 buf[i++] = '0';
132 while (a) {
133 buf[i++] = fz_hex_digits[a % base];
134 a /= base;
135 }
136 if (s) {
137 if (z == '0')
138 while (i < w - 1)
139 buf[i++] = z;
140 buf[i++] = s;
141 }
142 while (i < w)
143 buf[i++] = z;
144 while (i > 0)
145 fmtputc(out, buf[--i]);
146 }
147
fmtint32(struct fmtbuf * out,int value,int s,int z,int w,int base)148 static void fmtint32(struct fmtbuf *out, int value, int s, int z, int w, int base)
149 {
150 unsigned int a;
151
152 if (value < 0)
153 {
154 s = '-';
155 a = -value;
156 }
157 else if (s)
158 {
159 s = '+';
160 a = value;
161 }
162 else
163 {
164 s = 0;
165 a = value;
166 }
167 fmtuint32(out, a, s, z, w, base);
168 }
169
fmtint64(struct fmtbuf * out,int64_t value,int s,int z,int w,int base)170 static void fmtint64(struct fmtbuf *out, int64_t value, int s, int z, int w, int base)
171 {
172 uint64_t a;
173
174 if (value < 0)
175 {
176 s = '-';
177 a = -value;
178 }
179 else if (s)
180 {
181 s = '+';
182 a = value;
183 }
184 else
185 {
186 s = 0;
187 a = value;
188 }
189 fmtuint64(out, a, s, z, w, base);
190 }
191
fmtquote(struct fmtbuf * out,const char * s,int sq,int eq,int verbatim)192 static void fmtquote(struct fmtbuf *out, const char *s, int sq, int eq, int verbatim)
193 {
194 int i, n, c;
195 fmtputc(out, sq);
196 while (*s != 0) {
197 n = fz_chartorune(&c, s);
198 switch (c) {
199 default:
200 if (c < 32) {
201 fmtputc(out, '\\');
202 fmtputc(out, 'x');
203 fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
204 fmtputc(out, "0123456789ABCDEF"[(c)&15]);
205 } else if (c > 127) {
206 if (verbatim)
207 {
208 for (i = 0; i < n; ++i)
209 fmtputc(out, s[i]);
210 }
211 else
212 {
213 fmtputc(out, '\\');
214 fmtputc(out, 'u');
215 fmtputc(out, "0123456789ABCDEF"[(c>>12)&15]);
216 fmtputc(out, "0123456789ABCDEF"[(c>>8)&15]);
217 fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
218 fmtputc(out, "0123456789ABCDEF"[(c)&15]);
219 }
220 } else {
221 if (c == sq || c == eq)
222 fmtputc(out, '\\');
223 fmtputc(out, c);
224 }
225 break;
226 case '\\': fmtputc(out, '\\'); fmtputc(out, '\\'); break;
227 case '\b': fmtputc(out, '\\'); fmtputc(out, 'b'); break;
228 case '\f': fmtputc(out, '\\'); fmtputc(out, 'f'); break;
229 case '\n': fmtputc(out, '\\'); fmtputc(out, 'n'); break;
230 case '\r': fmtputc(out, '\\'); fmtputc(out, 'r'); break;
231 case '\t': fmtputc(out, '\\'); fmtputc(out, 't'); break;
232 }
233 s += n;
234 }
235 fmtputc(out, eq);
236 }
237
fmtquote_pdf(struct fmtbuf * out,const char * s,int sq,int eq)238 static void fmtquote_pdf(struct fmtbuf *out, const char *s, int sq, int eq)
239 {
240 int c;
241 fmtputc(out, sq);
242 while ((c = (unsigned char)*s++) != 0) {
243 switch (c) {
244 default:
245 if (c < 32 || c > 127) {
246 fmtputc(out, '\\');
247 if (sq == '(')
248 {
249 fmtputc(out, '0' + ((c >> 6) & 7));
250 fmtputc(out, '0' + ((c >> 3) & 7));
251 fmtputc(out, '0' + ((c) & 7));
252 }
253 else
254 {
255 fmtputc(out, 'x');
256 fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
257 fmtputc(out, "0123456789ABCDEF"[(c)&15]);
258 }
259 } else {
260 if (c == sq || c == eq)
261 fmtputc(out, '\\');
262 fmtputc(out, c);
263 }
264 break;
265 case '\\': fmtputc(out, '\\'); fmtputc(out, '\\'); break;
266 case '\b': fmtputc(out, '\\'); fmtputc(out, 'b'); break;
267 case '\f': fmtputc(out, '\\'); fmtputc(out, 'f'); break;
268 case '\n': fmtputc(out, '\\'); fmtputc(out, 'n'); break;
269 case '\r': fmtputc(out, '\\'); fmtputc(out, 'r'); break;
270 case '\t': fmtputc(out, '\\'); fmtputc(out, 't'); break;
271 }
272 }
273 fmtputc(out, eq);
274 }
275
fmtname(struct fmtbuf * out,const char * s)276 static void fmtname(struct fmtbuf *out, const char *s)
277 {
278 int c;
279 fmtputc(out, '/');
280 while ((c = *s++) != 0) {
281 if (c <= 32 || c == '/' || c == '#') {
282 fmtputc(out, '#');
283 fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
284 fmtputc(out, "0123456789ABCDEF"[(c)&15]);
285 } else {
286 fmtputc(out, c);
287 }
288 }
289 }
290
291 void
fz_format_string(fz_context * ctx,void * user,void (* emit)(fz_context * ctx,void * user,int c),const char * fmt,va_list args)292 fz_format_string(fz_context *ctx, void *user, void (*emit)(fz_context *ctx, void *user, int c), const char *fmt, va_list args)
293 {
294 struct fmtbuf out;
295 int c, s, z, p, w;
296 int32_t i32;
297 int64_t i64;
298 const char *str;
299 size_t bits;
300
301 out.ctx = ctx;
302 out.user = user;
303 out.emit = emit;
304
305 while ((c = *fmt++) != 0)
306 {
307 if (c == '%')
308 {
309 s = 0;
310 z = ' ';
311
312 /* flags */
313 while ((c = *fmt++) != 0)
314 {
315 /* plus sign */
316 if (c == '+')
317 s = 1;
318 /* space sign */
319 else if (c == ' ')
320 s = ' ';
321 /* zero padding */
322 else if (c == '0')
323 z = '0';
324 /* TODO: '-' to left justify */
325 else
326 break;
327 }
328 if (c == 0)
329 break;
330
331 /* width */
332 w = 0;
333 if (c == '*') {
334 c = *fmt++;
335 w = va_arg(args, int);
336 } else {
337 while (c >= '0' && c <= '9') {
338 w = w * 10 + c - '0';
339 c = *fmt++;
340 }
341 }
342 if (c == 0)
343 break;
344
345 /* precision */
346 p = 6;
347 if (c == '.') {
348 c = *fmt++;
349 if (c == 0)
350 break;
351 if (c == '*') {
352 c = *fmt++;
353 p = va_arg(args, int);
354 } else {
355 if (c >= '0' && c <= '9')
356 p = 0;
357 while (c >= '0' && c <= '9') {
358 p = p * 10 + c - '0';
359 c = *fmt++;
360 }
361 }
362 }
363 if (c == 0)
364 break;
365
366 /* lengths */
367 bits = 0;
368 if (c == 'l') {
369 c = *fmt++;
370 bits = sizeof(int64_t) * 8;
371 if (c == 0)
372 break;
373 }
374 if (c == 't') {
375 c = *fmt++;
376 bits = sizeof(ptrdiff_t) * 8;
377 if (c == 0)
378 break;
379 }
380 if (c == 'z') {
381 c = *fmt++;
382 bits = sizeof(size_t) * 8;
383 if (c == 0)
384 break;
385 }
386
387 switch (c) {
388 default:
389 fmtputc(&out, '%');
390 fmtputc(&out, c);
391 break;
392 case '%':
393 fmtputc(&out, '%');
394 break;
395
396 case 'M':
397 {
398 fz_matrix *matrix = va_arg(args, fz_matrix*);
399 fmtfloat(&out, matrix->a); fmtputc(&out, ' ');
400 fmtfloat(&out, matrix->b); fmtputc(&out, ' ');
401 fmtfloat(&out, matrix->c); fmtputc(&out, ' ');
402 fmtfloat(&out, matrix->d); fmtputc(&out, ' ');
403 fmtfloat(&out, matrix->e); fmtputc(&out, ' ');
404 fmtfloat(&out, matrix->f);
405 }
406 break;
407 case 'R':
408 {
409 fz_rect *rect = va_arg(args, fz_rect*);
410 fmtfloat(&out, rect->x0); fmtputc(&out, ' ');
411 fmtfloat(&out, rect->y0); fmtputc(&out, ' ');
412 fmtfloat(&out, rect->x1); fmtputc(&out, ' ');
413 fmtfloat(&out, rect->y1);
414 }
415 break;
416 case 'P':
417 {
418 fz_point *point = va_arg(args, fz_point*);
419 fmtfloat(&out, point->x); fmtputc(&out, ' ');
420 fmtfloat(&out, point->y);
421 }
422 break;
423
424 case 'C': /* unicode char */
425 c = va_arg(args, int);
426 if (c < 128)
427 fmtputc(&out, c);
428 else {
429 char buf[10];
430 int i, n = fz_runetochar(buf, c);
431 for (i=0; i < n; ++i)
432 fmtputc(&out, buf[i]);
433 }
434 break;
435 case 'c':
436 c = va_arg(args, int);
437 fmtputc(&out, c);
438 break;
439
440 case 'e':
441 fmtfloat_e(&out, va_arg(args, double), w, p);
442 break;
443 case 'f':
444 fmtfloat_f(&out, va_arg(args, double), w, p);
445 break;
446 case 'g':
447 fmtfloat(&out, va_arg(args, double));
448 break;
449
450 case 'p':
451 bits = 8 * sizeof(void *);
452 z = '0';
453 fmtputc(&out, '0');
454 fmtputc(&out, 'x');
455 /* fallthrough */
456 case 'x':
457 if (bits == 64)
458 {
459 i64 = va_arg(args, int64_t);
460 fmtuint64(&out, i64, 0, z, w, 16);
461 }
462 else
463 {
464 i32 = va_arg(args, int);
465 fmtuint32(&out, i32, 0, z, w, 16);
466 }
467 break;
468 case 'd':
469 case 'i':
470 if (bits == 64)
471 {
472 i64 = va_arg(args, int64_t);
473 fmtint64(&out, i64, s, z, w, 10);
474 }
475 else
476 {
477 i32 = va_arg(args, int);
478 fmtint32(&out, i32, s, z, w, 10);
479 }
480 break;
481 case 'u':
482 if (bits == 64)
483 {
484 i64 = va_arg(args, int64_t);
485 fmtuint64(&out, i64, 0, z, w, 10);
486 }
487 else
488 {
489 i32 = va_arg(args, int);
490 fmtuint32(&out, i32, 0, z, w, 10);
491 }
492 break;
493
494 case 's':
495 str = va_arg(args, const char*);
496 if (!str)
497 str = "(null)";
498 while ((c = *str++) != 0)
499 fmtputc(&out, c);
500 break;
501 case 'Q': /* quoted string (with verbatim unicode) */
502 str = va_arg(args, const char*);
503 if (!str) str = "";
504 fmtquote(&out, str, '"', '"', 1);
505 break;
506 case 'q': /* quoted string */
507 str = va_arg(args, const char*);
508 if (!str) str = "";
509 fmtquote(&out, str, '"', '"', 0);
510 break;
511 case '(': /* pdf string */
512 str = va_arg(args, const char*);
513 if (!str) str = "";
514 fmtquote_pdf(&out, str, '(', ')');
515 break;
516 case 'n': /* pdf name */
517 str = va_arg(args, const char*);
518 if (!str) str = "";
519 fmtname(&out, str);
520 break;
521 }
522 }
523 else
524 {
525 fmtputc(&out, c);
526 }
527 }
528 }
529
530 struct snprintf_buffer
531 {
532 char *p;
533 size_t s, n;
534 };
535
snprintf_emit(fz_context * ctx,void * out_,int c)536 static void snprintf_emit(fz_context *ctx, void *out_, int c)
537 {
538 struct snprintf_buffer *out = out_;
539 if (out->n < out->s)
540 out->p[out->n] = c;
541 ++(out->n);
542 }
543
544 size_t
fz_vsnprintf(char * buffer,size_t space,const char * fmt,va_list args)545 fz_vsnprintf(char *buffer, size_t space, const char *fmt, va_list args)
546 {
547 struct snprintf_buffer out;
548 out.p = buffer;
549 out.s = space > 0 ? space - 1 : 0;
550 out.n = 0;
551
552 /* Note: using a NULL context is safe here */
553 fz_format_string(NULL, &out, snprintf_emit, fmt, args);
554 if (space > 0)
555 out.p[out.n < space ? out.n : space - 1] = '\0';
556
557 return out.n;
558 }
559
560 size_t
fz_snprintf(char * buffer,size_t space,const char * fmt,...)561 fz_snprintf(char *buffer, size_t space, const char *fmt, ...)
562 {
563 va_list ap;
564 struct snprintf_buffer out;
565 out.p = buffer;
566 out.s = space > 0 ? space - 1 : 0;
567 out.n = 0;
568
569 va_start(ap, fmt);
570 /* Note: using a NULL context is safe here */
571 fz_format_string(NULL, &out, snprintf_emit, fmt, ap);
572 if (space > 0)
573 out.p[out.n < space ? out.n : space - 1] = '\0';
574 va_end(ap);
575
576 return out.n;
577 }
578
579 char *
fz_asprintf(fz_context * ctx,const char * fmt,...)580 fz_asprintf(fz_context *ctx, const char *fmt, ...)
581 {
582 size_t len;
583 char *mem;
584 va_list ap;
585 va_start(ap, fmt);
586 len = fz_vsnprintf(NULL, 0, fmt, ap);
587 va_end(ap);
588 mem = Memento_label(fz_malloc(ctx, len+1), "asprintf");
589 va_start(ap, fmt);
590 fz_vsnprintf(mem, len+1, fmt, ap);
591 va_end(ap);
592 return mem;
593 }
594