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 */ |
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 | |
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 | */ |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
192 | static 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 | |
230 | static 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 | */ |
262 | void |
263 | fz_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 | |
495 | struct snprintf_buffer |
496 | { |
497 | char *p; |
498 | size_t s, n; |
499 | }; |
500 | |
501 | static 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 | */ |
512 | size_t |
513 | fz_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 | */ |
531 | size_t |
532 | fz_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 | |
550 | char * |
551 | fz_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 | |