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 */
10int 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
24static const char *fz_hex_digits = "0123456789abcdef";
25
26struct fmtbuf
27{
28 fz_context *ctx;
29 void *user;
30 void (*emit)(fz_context *ctx, void *user, int c);
31};
32
33static 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 */
42static 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
84static 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
92static 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
100static 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
124static 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
148static 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
170static 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
192static void fmtquote(struct fmtbuf *out, const char *s, int sq, int eq)
193{
194 int c;
195 fmtputc(out, sq);
196 while ((c = *s++) != 0) {
197 switch (c) {
198 default:
199 if (c < 32 || c > 127) {
200 fmtputc(out, '\\');
201 if (sq == '(')
202 {
203 fmtputc(out, '0' + ((c >> 6) & 7));
204 fmtputc(out, '0' + ((c >> 3) & 7));
205 fmtputc(out, '0' + ((c) & 7));
206 }
207 else
208 {
209 fmtputc(out, 'x');
210 fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
211 fmtputc(out, "0123456789ABCDEF"[(c)&15]);
212 }
213 } else {
214 if (c == sq || c == eq)
215 fmtputc(out, '\\');
216 fmtputc(out, c);
217 }
218 break;
219 case '\\': fmtputc(out, '\\'); fmtputc(out, '\\'); break;
220 case '\b': fmtputc(out, '\\'); fmtputc(out, 'b'); break;
221 case '\f': fmtputc(out, '\\'); fmtputc(out, 'f'); break;
222 case '\n': fmtputc(out, '\\'); fmtputc(out, 'n'); break;
223 case '\r': fmtputc(out, '\\'); fmtputc(out, 'r'); break;
224 case '\t': fmtputc(out, '\\'); fmtputc(out, 't'); break;
225 }
226 }
227 fmtputc(out, eq);
228}
229
230static void fmtname(struct fmtbuf *out, const char *s)
231{
232 int c;
233 fmtputc(out, '/');
234 while ((c = *s++) != 0) {
235 if (c <= 32 || c == '/' || c == '#') {
236 fmtputc(out, '#');
237 fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
238 fmtputc(out, "0123456789ABCDEF"[(c)&15]);
239 } else {
240 fmtputc(out, c);
241 }
242 }
243}
244
245/*
246 Our customised 'printf'-like string formatter.
247 Takes %c, %d, %s, %u, %x, as usual.
248 Modifiers are not supported except for zero-padding ints (e.g. %02d, %03u, %04x, etc).
249 %g output in "as short as possible hopefully lossless non-exponent" form,
250 see fz_ftoa for specifics.
251 %f and %e output as usual.
252 %C outputs a utf8 encoded int.
253 %M outputs a fz_matrix*. %R outputs a fz_rect*. %P outputs a fz_point*.
254 %n outputs a PDF name (with appropriate escaping).
255 %q and %( output escaped strings in C/PDF syntax.
256 %l{d,u,x} indicates that the values are int64_t.
257 %z{d,u,x} indicates that the value is a size_t.
258
259 user: An opaque pointer that is passed to the emit function.
260 emit: A function pointer called to emit output bytes as the string is being formatted.
261*/
262void
263fz_format_string(fz_context *ctx, void *user, void (*emit)(fz_context *ctx, void *user, int c), const char *fmt, va_list args)
264{
265 struct fmtbuf out;
266 int c, s, z, p, w;
267 int32_t i32;
268 int64_t i64;
269 const char *str;
270 size_t bits;
271
272 out.ctx = ctx;
273 out.user = user;
274 out.emit = emit;
275
276 while ((c = *fmt++) != 0)
277 {
278 if (c == '%')
279 {
280 s = 0;
281 z = ' ';
282
283 /* flags */
284 while ((c = *fmt++) != 0)
285 {
286 /* plus sign */
287 if (c == '+')
288 s = 1;
289 /* space sign */
290 else if (c == ' ')
291 s = ' ';
292 /* zero padding */
293 else if (c == '0')
294 z = '0';
295 /* TODO: '-' to left justify */
296 else
297 break;
298 }
299 if (c == 0)
300 break;
301
302 /* width */
303 w = 0;
304 if (c == '*') {
305 c = *fmt++;
306 w = va_arg(args, int);
307 } else {
308 while (c >= '0' && c <= '9') {
309 w = w * 10 + c - '0';
310 c = *fmt++;
311 }
312 }
313 if (c == 0)
314 break;
315
316 /* precision */
317 p = 6;
318 if (c == '.') {
319 c = *fmt++;
320 if (c == 0)
321 break;
322 if (c == '*') {
323 c = *fmt++;
324 p = va_arg(args, int);
325 } else {
326 if (c >= '0' && c <= '9')
327 p = 0;
328 while (c >= '0' && c <= '9') {
329 p = p * 10 + c - '0';
330 c = *fmt++;
331 }
332 }
333 }
334 if (c == 0)
335 break;
336
337 /* lengths */
338 bits = 0;
339 if (c == 'l') {
340 c = *fmt++;
341 bits = sizeof(int64_t) * 8;
342 if (c == 0)
343 break;
344 }
345 if (c == 't') {
346 c = *fmt++;
347 bits = sizeof(ptrdiff_t) * 8;
348 if (c == 0)
349 break;
350 }
351 if (c == 'z') {
352 c = *fmt++;
353 bits = sizeof(size_t) * 8;
354 if (c == 0)
355 break;
356 }
357
358 switch (c) {
359 default:
360 fmtputc(&out, '%');
361 fmtputc(&out, c);
362 break;
363 case '%':
364 fmtputc(&out, '%');
365 break;
366
367 case 'M':
368 {
369 fz_matrix *matrix = va_arg(args, fz_matrix*);
370 fmtfloat(&out, matrix->a); fmtputc(&out, ' ');
371 fmtfloat(&out, matrix->b); fmtputc(&out, ' ');
372 fmtfloat(&out, matrix->c); fmtputc(&out, ' ');
373 fmtfloat(&out, matrix->d); fmtputc(&out, ' ');
374 fmtfloat(&out, matrix->e); fmtputc(&out, ' ');
375 fmtfloat(&out, matrix->f);
376 }
377 break;
378 case 'R':
379 {
380 fz_rect *rect = va_arg(args, fz_rect*);
381 fmtfloat(&out, rect->x0); fmtputc(&out, ' ');
382 fmtfloat(&out, rect->y0); fmtputc(&out, ' ');
383 fmtfloat(&out, rect->x1); fmtputc(&out, ' ');
384 fmtfloat(&out, rect->y1);
385 }
386 break;
387 case 'P':
388 {
389 fz_point *point = va_arg(args, fz_point*);
390 fmtfloat(&out, point->x); fmtputc(&out, ' ');
391 fmtfloat(&out, point->y);
392 }
393 break;
394
395 case 'C': /* unicode char */
396 c = va_arg(args, int);
397 if (c < 128)
398 fmtputc(&out, c);
399 else {
400 char buf[10];
401 int i, n = fz_runetochar(buf, c);
402 for (i=0; i < n; ++i)
403 fmtputc(&out, buf[i]);
404 }
405 break;
406 case 'c':
407 c = va_arg(args, int);
408 fmtputc(&out, c);
409 break;
410
411 case 'e':
412 fmtfloat_e(&out, va_arg(args, double), w, p);
413 break;
414 case 'f':
415 fmtfloat_f(&out, va_arg(args, double), w, p);
416 break;
417 case 'g':
418 fmtfloat(&out, va_arg(args, double));
419 break;
420
421 case 'p':
422 bits = 8 * sizeof(void *);
423 w = 2 * sizeof(void *);
424 fmtputc(&out, '0');
425 fmtputc(&out, 'x');
426 /* fallthrough */
427 case 'x':
428 if (bits == 64)
429 {
430 i64 = va_arg(args, int64_t);
431 fmtuint64(&out, i64, 0, z, w, 16);
432 }
433 else
434 {
435 i32 = va_arg(args, int);
436 fmtuint32(&out, i32, 0, z, w, 16);
437 }
438 break;
439 case 'd':
440 if (bits == 64)
441 {
442 i64 = va_arg(args, int64_t);
443 fmtint64(&out, i64, s, z, w, 10);
444 }
445 else
446 {
447 i32 = va_arg(args, int);
448 fmtint32(&out, i32, s, z, w, 10);
449 }
450 break;
451 case 'u':
452 if (bits == 64)
453 {
454 i64 = va_arg(args, int64_t);
455 fmtuint64(&out, i64, 0, z, w, 10);
456 }
457 else
458 {
459 i32 = va_arg(args, int);
460 fmtuint32(&out, i32, 0, z, w, 10);
461 }
462 break;
463
464 case 's':
465 str = va_arg(args, const char*);
466 if (!str)
467 str = "(null)";
468 while ((c = *str++) != 0)
469 fmtputc(&out, c);
470 break;
471 case 'q': /* quoted string */
472 str = va_arg(args, const char*);
473 if (!str) str = "";
474 fmtquote(&out, str, '"', '"');
475 break;
476 case '(': /* pdf string */
477 str = va_arg(args, const char*);
478 if (!str) str = "";
479 fmtquote(&out, str, '(', ')');
480 break;
481 case 'n': /* pdf name */
482 str = va_arg(args, const char*);
483 if (!str) str = "";
484 fmtname(&out, str);
485 break;
486 }
487 }
488 else
489 {
490 fmtputc(&out, c);
491 }
492 }
493}
494
495struct snprintf_buffer
496{
497 char *p;
498 size_t s, n;
499};
500
501static void snprintf_emit(fz_context *ctx, void *out_, int c)
502{
503 struct snprintf_buffer *out = out_;
504 if (out->n < out->s)
505 out->p[out->n] = c;
506 ++(out->n);
507}
508
509/*
510 A vsnprintf work-alike, using our custom formatter.
511*/
512size_t
513fz_vsnprintf(char *buffer, size_t space, const char *fmt, va_list args)
514{
515 struct snprintf_buffer out;
516 out.p = buffer;
517 out.s = space > 0 ? space - 1 : 0;
518 out.n = 0;
519
520 /* Note: using a NULL context is safe here */
521 fz_format_string(NULL, &out, snprintf_emit, fmt, args);
522 if (space > 0)
523 out.p[out.n < space ? out.n : space - 1] = '\0';
524
525 return out.n;
526}
527
528/*
529 The non va_list equivalent of fz_vsnprintf.
530*/
531size_t
532fz_snprintf(char *buffer, size_t space, const char *fmt, ...)
533{
534 va_list ap;
535 struct snprintf_buffer out;
536 out.p = buffer;
537 out.s = space > 0 ? space - 1 : 0;
538 out.n = 0;
539
540 va_start(ap, fmt);
541 /* Note: using a NULL context is safe here */
542 fz_format_string(NULL, &out, snprintf_emit, fmt, ap);
543 if (space > 0)
544 out.p[out.n < space ? out.n : space - 1] = '\0';
545 va_end(ap);
546
547 return out.n;
548}
549
550char *
551fz_asprintf(fz_context *ctx, const char *fmt, ...)
552{
553 int len;
554 char *mem;
555 va_list ap;
556 va_start(ap, fmt);
557 len = fz_vsnprintf(NULL, 0, fmt, ap);
558 va_end(ap);
559 mem = fz_malloc(ctx, len+1);
560 va_start(ap, fmt);
561 fz_vsnprintf(mem, len+1, fmt, ap);
562 va_end(ap);
563 return mem;
564}
565