comparison mupdf-source/source/fitz/printf.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2025 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24
25 #include <float.h>
26 #include <math.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29
30 #ifdef _MSC_VER
31 #if _MSC_VER < 1500 /* MSVC 2008 */
32 int snprintf(char *s, size_t n, const char *fmt, ...)
33 {
34 int r;
35 va_list ap;
36 va_start(ap, fmt);
37 r = vsprintf(s, fmt, ap);
38 va_end(ap);
39 return r;
40 }
41 #else if _MSC_VER < 1900 /* MSVC 2015 */
42 #define snprintf _snprintf
43 #endif
44 #endif
45
46 static const char *fz_hex_digits = "0123456789abcdef";
47 static const char *fz_hex_digits_UC = "0123456789ABCDEF";
48
49 struct fmtbuf
50 {
51 fz_context *ctx;
52 void *user;
53 void (*emit)(fz_context *ctx, void *user, int c);
54 };
55
56 static inline void fmtputc(struct fmtbuf *out, int c)
57 {
58 out->emit(out->ctx, out->user, c);
59 }
60
61 /*
62 * Convert float to shortest possible string that won't lose precision, except:
63 * NaN to 0, +Inf to FLT_MAX, -Inf to -FLT_MAX.
64 */
65 static void fmtfloat(struct fmtbuf *out, float f)
66 {
67 char digits[40], *s = digits;
68 int exp, ndigits, point;
69
70 if (isnan(f)) f = 0;
71 if (isinf(f)) f = f < 0 ? -FLT_MAX : FLT_MAX;
72
73 if (signbit(f))
74 fmtputc(out, '-');
75
76 if (f == 0)
77 {
78 fmtputc(out, '0');
79 return;
80 }
81
82 ndigits = fz_grisu(f, digits, &exp);
83 point = exp + ndigits;
84
85 if (point <= 0)
86 {
87 fmtputc(out, '.');
88 while (point++ < 0)
89 fmtputc(out, '0');
90 while (ndigits-- > 0)
91 fmtputc(out, *s++);
92 }
93
94 else
95 {
96 while (ndigits-- > 0)
97 {
98 fmtputc(out, *s++);
99 if (--point == 0 && ndigits > 0)
100 fmtputc(out, '.');
101 }
102 while (point-- > 0)
103 fmtputc(out, '0');
104 }
105 }
106
107 static void fmtfloat_e(struct fmtbuf *out, double f, int w, int p)
108 {
109 char buf[100], *s = buf;
110 snprintf(buf, sizeof buf, "%*.*e", w, p, f);
111 while (*s)
112 fmtputc(out, *s++);
113 }
114
115 static void fmtfloat_f(struct fmtbuf *out, double f, int w, int p)
116 {
117 char buf[100], *s = buf;
118 snprintf(buf, sizeof buf, "%*.*f", w, p, f);
119 while (*s)
120 fmtputc(out, *s++);
121 }
122
123 static void fmtuint32(struct fmtbuf *out, unsigned int a, int s, int z, int w, int base, int q)
124 {
125 char buf[40];
126 int i;
127 const char *hex_digits = fz_hex_digits;
128
129 if (base < 0)
130 {
131 base = -base;
132 hex_digits = fz_hex_digits_UC;
133 }
134
135 i = 0;
136 if (a == 0)
137 buf[i++] = '0';
138 while (a) {
139 buf[i++] = hex_digits[a % base];
140 a /= base;
141 }
142 if (s) {
143 if (z == '0')
144 while (i < w - 1)
145 buf[i++] = z;
146 buf[i++] = s;
147 }
148 while (i < w)
149 buf[i++] = z;
150 while (i > 0)
151 {
152 fmtputc(out, buf[--i]);
153 if (q && i != 0 && i % 3 == 0)
154 fmtputc(out, q);
155 }
156 }
157
158 static void fmtuint64(struct fmtbuf *out, uint64_t a, int s, int z, int w, int base, int q)
159 {
160 char buf[80];
161 int i;
162 const char *hex_digits = fz_hex_digits;
163
164 if (base < 0)
165 {
166 base = -base;
167 hex_digits = fz_hex_digits_UC;
168 }
169
170 i = 0;
171 if (a == 0)
172 buf[i++] = '0';
173 while (a) {
174 buf[i++] = hex_digits[a % base];
175 a /= base;
176 }
177 if (s) {
178 if (z == '0')
179 while (i < w - 1)
180 buf[i++] = z;
181 buf[i++] = s;
182 }
183 while (i < w)
184 buf[i++] = z;
185 while (i > 0)
186 {
187 fmtputc(out, buf[--i]);
188 if (q && i != 0 && i % 3 == 0)
189 fmtputc(out, q);
190 }
191 }
192
193 static void fmtint32(struct fmtbuf *out, int value, int s, int z, int w, int base, int q)
194 {
195 unsigned int a;
196
197 if (value < 0)
198 {
199 s = '-';
200 a = -value;
201 }
202 else if (s)
203 {
204 s = '+';
205 a = value;
206 }
207 else
208 {
209 s = 0;
210 a = value;
211 }
212 fmtuint32(out, a, s, z, w, base, q);
213 }
214
215 static void fmtint64(struct fmtbuf *out, int64_t value, int s, int z, int w, int base, int q)
216 {
217 uint64_t a;
218
219 if (value < 0)
220 {
221 s = '-';
222 a = -value;
223 }
224 else if (s)
225 {
226 s = '+';
227 a = value;
228 }
229 else
230 {
231 s = 0;
232 a = value;
233 }
234 fmtuint64(out, a, s, z, w, base, q);
235 }
236
237 static void fmtquote(struct fmtbuf *out, const char *s, int sq, int eq, int verbatim)
238 {
239 int i, n, c;
240 fmtputc(out, sq);
241 while (*s != 0) {
242 n = fz_chartorune(&c, s);
243 switch (c) {
244 default:
245 if (c < 32) {
246 fmtputc(out, '\\');
247 fmtputc(out, 'x');
248 fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
249 fmtputc(out, "0123456789ABCDEF"[(c)&15]);
250 } else if (c > 127) {
251 if (verbatim)
252 {
253 for (i = 0; i < n; ++i)
254 fmtputc(out, s[i]);
255 }
256 else if (c <= 0xffff)
257 {
258 fmtputc(out, '\\');
259 fmtputc(out, 'u');
260 fmtputc(out, "0123456789ABCDEF"[(c>>12)&15]);
261 fmtputc(out, "0123456789ABCDEF"[(c>>8)&15]);
262 fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
263 fmtputc(out, "0123456789ABCDEF"[(c)&15]);
264 }
265 else
266 {
267 /* Use a surrogate pair */
268 int hi = 0xd800 + ((c - 0x10000) >> 10);
269 int lo = 0xdc00 + ((c - 0x10000) & 0x3ff);
270 fmtputc(out, '\\');
271 fmtputc(out, 'u');
272 fmtputc(out, "0123456789ABCDEF"[(hi>>12)&15]);
273 fmtputc(out, "0123456789ABCDEF"[(hi>>8)&15]);
274 fmtputc(out, "0123456789ABCDEF"[(hi>>4)&15]);
275 fmtputc(out, "0123456789ABCDEF"[(hi)&15]);
276 fmtputc(out, '\\');
277 fmtputc(out, 'u');
278 fmtputc(out, "0123456789ABCDEF"[(lo>>12)&15]);
279 fmtputc(out, "0123456789ABCDEF"[(lo>>8)&15]);
280 fmtputc(out, "0123456789ABCDEF"[(lo>>4)&15]);
281 fmtputc(out, "0123456789ABCDEF"[(lo)&15]);
282 }
283 } else {
284 if (c == sq || c == eq)
285 fmtputc(out, '\\');
286 fmtputc(out, c);
287 }
288 break;
289 case '\\': fmtputc(out, '\\'); fmtputc(out, '\\'); break;
290 case '\b': fmtputc(out, '\\'); fmtputc(out, 'b'); break;
291 case '\f': fmtputc(out, '\\'); fmtputc(out, 'f'); break;
292 case '\n': fmtputc(out, '\\'); fmtputc(out, 'n'); break;
293 case '\r': fmtputc(out, '\\'); fmtputc(out, 'r'); break;
294 case '\t': fmtputc(out, '\\'); fmtputc(out, 't'); break;
295 }
296 s += n;
297 }
298 fmtputc(out, eq);
299 }
300
301 static void fmtquote_pdf(struct fmtbuf *out, const char *s, int sq, int eq)
302 {
303 int c;
304 fmtputc(out, sq);
305 while ((c = (unsigned char)*s++) != 0) {
306 switch (c) {
307 default:
308 if (c < 32 || c > 127) {
309 fmtputc(out, '\\');
310 if (sq == '(')
311 {
312 fmtputc(out, '0' + ((c >> 6) & 7));
313 fmtputc(out, '0' + ((c >> 3) & 7));
314 fmtputc(out, '0' + ((c) & 7));
315 }
316 else
317 {
318 fmtputc(out, 'x');
319 fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
320 fmtputc(out, "0123456789ABCDEF"[(c)&15]);
321 }
322 } else {
323 if (c == sq || c == eq)
324 fmtputc(out, '\\');
325 fmtputc(out, c);
326 }
327 break;
328 case '\\': fmtputc(out, '\\'); fmtputc(out, '\\'); break;
329 case '\b': fmtputc(out, '\\'); fmtputc(out, 'b'); break;
330 case '\f': fmtputc(out, '\\'); fmtputc(out, 'f'); break;
331 case '\n': fmtputc(out, '\\'); fmtputc(out, 'n'); break;
332 case '\r': fmtputc(out, '\\'); fmtputc(out, 'r'); break;
333 case '\t': fmtputc(out, '\\'); fmtputc(out, 't'); break;
334 }
335 }
336 fmtputc(out, eq);
337 }
338
339 int
340 fz_is_valid_xml_char(int c)
341 {
342 if (c == 9 || c == 10 || c == 13)
343 return 1;
344 if (c < 32)
345 return 0;
346 if (c < 0xd800)
347 return 1;
348 if (c < 0xe000)
349 return 0;
350 if (c <= 0xfffd)
351 return 1;
352 if (c < 0x10000)
353 return 0;
354 if (c <= 0x10FFFF)
355 return 1;
356 return 0;
357 }
358
359 int
360 fz_is_valid_xml_string(const char *s)
361 {
362 int c, n;
363 while (*s != 0) {
364 n = fz_chartorune(&c, s);
365 if (!fz_is_valid_xml_char(c))
366 return 0;
367 s += n;
368 }
369 return 1;
370 }
371
372 int
373 fz_range_limit_xml_char(int c)
374 {
375 if (fz_is_valid_xml_char(c))
376 return c;
377 return 0xFFFD;
378 }
379
380 static void fmtquote_xml(struct fmtbuf *out, const char *s)
381 {
382 int c, n;
383 fmtputc(out, '"');
384 while (*s != 0) {
385 n = fz_chartorune(&c, s);
386 switch (c) {
387 case '"':
388 fmtputc(out, '&');
389 fmtputc(out, 'q');
390 fmtputc(out, 'u');
391 fmtputc(out, 'o');
392 fmtputc(out, 't');
393 fmtputc(out, ';');
394 break;
395 case '&':
396 fmtputc(out, '&');
397 fmtputc(out, 'a');
398 fmtputc(out, 'm');
399 fmtputc(out, 'p');
400 fmtputc(out, ';');
401 break;
402 case '<':
403 fmtputc(out, '&');
404 fmtputc(out, 'l');
405 fmtputc(out, 't');
406 fmtputc(out, ';');
407 break;
408 case '>':
409 fmtputc(out, '&');
410 fmtputc(out, 'g');
411 fmtputc(out, 't');
412 fmtputc(out, ';');
413 break;
414 default:
415 c = fz_range_limit_xml_char(c);
416 if (c < 32 || c >= 127)
417 {
418 fmtputc(out, '&');
419 fmtputc(out, '#');
420 fmtputc(out, 'x');
421 if (c > 65535)
422 {
423 fmtputc(out, "0123456789ABCDEF"[(c>>20)&15]);
424 fmtputc(out, "0123456789ABCDEF"[(c>>16)&15]);
425 }
426 if (c > 255)
427 {
428 fmtputc(out, "0123456789ABCDEF"[(c>>12)&15]);
429 fmtputc(out, "0123456789ABCDEF"[(c>>8)&15]);
430 }
431 fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
432 fmtputc(out, "0123456789ABCDEF"[(c)&15]);
433 fmtputc(out, ';');
434 }
435 else
436 fmtputc(out, c);
437 break;
438 }
439 s += n;
440 }
441 fmtputc(out, '"');
442 }
443
444 static void fmtquote_hex(struct fmtbuf *out, const char *s)
445 {
446 int c;
447 fmtputc(out, '"');
448 while ((c = *s++) != 0)
449 {
450 fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
451 fmtputc(out, "0123456789ABCDEF"[(c)&15]);
452 }
453 fmtputc(out, '"');
454 }
455
456 static void fmtname(struct fmtbuf *out, const char *s)
457 {
458 int c;
459 fmtputc(out, '/');
460 while ((c = *s++) != 0) {
461 if (c <= 32 || c == '/' || c == '#') {
462 fmtputc(out, '#');
463 fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]);
464 fmtputc(out, "0123456789ABCDEF"[(c)&15]);
465 } else {
466 fmtputc(out, c);
467 }
468 }
469 }
470
471 void
472 fz_format_string(fz_context *ctx, void *user, void (*emit)(fz_context *ctx, void *user, int c), const char *fmt, va_list args)
473 {
474 struct fmtbuf out;
475 int c, s, z, p, w, q;
476 int32_t i32;
477 int64_t i64;
478 const char *str;
479 size_t bits;
480
481 out.ctx = ctx;
482 out.user = user;
483 out.emit = emit;
484
485 while ((c = *fmt++) != 0)
486 {
487 if (c == '%')
488 {
489 q = 0;
490 s = 0;
491 z = ' ';
492
493 /* flags */
494 while ((c = *fmt++) != 0)
495 {
496 /* plus sign */
497 if (c == '+')
498 s = 1;
499 /* space sign */
500 else if (c == ' ')
501 s = ' ';
502 /* zero padding */
503 else if (c == '0')
504 z = '0';
505 /* comma separators */
506 else if (c == '\'')
507 q = '\'';
508 else if (c == ',')
509 q = ',';
510 else if (c == '_')
511 q = '_';
512 /* TODO: '-' to left justify */
513 else
514 break;
515 }
516 if (c == 0)
517 break;
518
519 /* width */
520 w = 0;
521 if (c == '*') {
522 c = *fmt++;
523 w = va_arg(args, int);
524 } else {
525 while (c >= '0' && c <= '9') {
526 w = w * 10 + c - '0';
527 c = *fmt++;
528 }
529 }
530 if (c == 0)
531 break;
532
533 /* precision */
534 p = 6;
535 if (c == '.') {
536 c = *fmt++;
537 if (c == 0)
538 break;
539 if (c == '*') {
540 c = *fmt++;
541 p = va_arg(args, int);
542 } else {
543 if (c >= '0' && c <= '9')
544 p = 0;
545 while (c >= '0' && c <= '9') {
546 p = p * 10 + c - '0';
547 c = *fmt++;
548 }
549 }
550 }
551 if (c == 0)
552 break;
553
554 /* lengths */
555 bits = 0;
556 if (c == 'l') {
557 c = *fmt++;
558 bits = sizeof(int64_t) * 8;
559 if (c == 0)
560 break;
561 }
562 if (c == 't') {
563 c = *fmt++;
564 bits = sizeof(ptrdiff_t) * 8;
565 if (c == 0)
566 break;
567 }
568 if (c == 'z') {
569 c = *fmt++;
570 bits = sizeof(size_t) * 8;
571 if (c == 0)
572 break;
573 }
574
575 switch (c) {
576 default:
577 fmtputc(&out, '%');
578 fmtputc(&out, c);
579 break;
580 case '%':
581 fmtputc(&out, '%');
582 break;
583
584 case 'M':
585 {
586 fz_matrix *matrix = va_arg(args, fz_matrix*);
587 fmtfloat(&out, matrix->a); fmtputc(&out, ' ');
588 fmtfloat(&out, matrix->b); fmtputc(&out, ' ');
589 fmtfloat(&out, matrix->c); fmtputc(&out, ' ');
590 fmtfloat(&out, matrix->d); fmtputc(&out, ' ');
591 fmtfloat(&out, matrix->e); fmtputc(&out, ' ');
592 fmtfloat(&out, matrix->f);
593 }
594 break;
595 case 'R':
596 {
597 fz_rect *rect = va_arg(args, fz_rect*);
598 fmtfloat(&out, rect->x0); fmtputc(&out, ' ');
599 fmtfloat(&out, rect->y0); fmtputc(&out, ' ');
600 fmtfloat(&out, rect->x1); fmtputc(&out, ' ');
601 fmtfloat(&out, rect->y1);
602 }
603 break;
604 case 'P':
605 {
606 fz_point *point = va_arg(args, fz_point*);
607 fmtfloat(&out, point->x); fmtputc(&out, ' ');
608 fmtfloat(&out, point->y);
609 }
610 break;
611
612 case 'C': /* unicode char */
613 c = va_arg(args, int);
614 if (c < 128)
615 fmtputc(&out, c);
616 else {
617 char buf[10];
618 int i, n = fz_runetochar(buf, c);
619 for (i=0; i < n; ++i)
620 fmtputc(&out, buf[i]);
621 }
622 break;
623 case 'c':
624 c = va_arg(args, int);
625 fmtputc(&out, c);
626 break;
627 case 'e':
628 fmtfloat_e(&out, va_arg(args, double), w, p);
629 break;
630 case 'f':
631 fmtfloat_f(&out, va_arg(args, double), w, p);
632 break;
633 case 'g':
634 fmtfloat(&out, va_arg(args, double));
635 break;
636
637 case 'p':
638 bits = 8 * sizeof(void *);
639 z = '0';
640 fmtputc(&out, '0');
641 fmtputc(&out, 'x');
642 q = 0;
643 /* fallthrough */
644 case 'x':
645 if (bits == 64)
646 {
647 i64 = va_arg(args, int64_t);
648 fmtuint64(&out, i64, 0, z, w, 16, q);
649 }
650 else
651 {
652 i32 = va_arg(args, int);
653 fmtuint32(&out, i32, 0, z, w, 16, q);
654 }
655 break;
656 case 'X':
657 if (bits == 64)
658 {
659 i64 = va_arg(args, int64_t);
660 fmtuint64(&out, i64, 0, z, w, -16, q);
661 }
662 else
663 {
664 i32 = va_arg(args, int);
665 fmtuint32(&out, i32, 0, z, w, -16, q);
666 }
667 break;
668 case 'd':
669 case 'i':
670 if (bits == 64)
671 {
672 i64 = va_arg(args, int64_t);
673 fmtint64(&out, i64, s, z, w, 10, q);
674 }
675 else
676 {
677 i32 = va_arg(args, int);
678 fmtint32(&out, i32, s, z, w, 10, q);
679 }
680 break;
681 case 'u':
682 if (bits == 64)
683 {
684 i64 = va_arg(args, int64_t);
685 fmtuint64(&out, i64, 0, z, w, 10, q);
686 }
687 else
688 {
689 i32 = va_arg(args, int);
690 fmtuint32(&out, i32, 0, z, w, 10, q);
691 }
692 break;
693
694 case 's':
695 str = va_arg(args, const char*);
696 if (!str)
697 str = "(null)";
698 while ((c = *str++) != 0)
699 fmtputc(&out, c);
700 break;
701 case 'Q': /* quoted string (with verbatim unicode) */
702 str = va_arg(args, const char*);
703 if (!str) str = "";
704 fmtquote(&out, str, '"', '"', 1);
705 break;
706 case 'q': /* quoted string */
707 str = va_arg(args, const char*);
708 if (!str) str = "";
709 fmtquote(&out, str, '"', '"', 0);
710 break;
711 case '<': /* quoted string for xml */
712 str = va_arg(args, const char*);
713 if (!str) str = "";
714 fmtquote_xml(&out, str);
715 break;
716 case '>': /* hex string */
717 str = va_arg(args, const char*);
718 if (!str) str = "";
719 fmtquote_hex(&out, str);
720 break;
721 case '(': /* pdf string */
722 str = va_arg(args, const char*);
723 if (!str) str = "";
724 fmtquote_pdf(&out, str, '(', ')');
725 break;
726 case 'n': /* pdf name */
727 str = va_arg(args, const char*);
728 if (!str) str = "";
729 fmtname(&out, str);
730 break;
731 }
732 }
733 else
734 {
735 fmtputc(&out, c);
736 }
737 }
738 }
739
740 struct snprintf_buffer
741 {
742 char *p;
743 size_t s, n;
744 };
745
746 static void snprintf_emit(fz_context *ctx, void *out_, int c)
747 {
748 struct snprintf_buffer *out = out_;
749 if (out->n < out->s)
750 out->p[out->n] = c;
751 ++(out->n);
752 }
753
754 size_t
755 fz_vsnprintf(char *buffer, size_t space, const char *fmt, va_list args)
756 {
757 struct snprintf_buffer out;
758 out.p = buffer;
759 out.s = space > 0 ? space - 1 : 0;
760 out.n = 0;
761
762 /* Note: using a NULL context is safe here */
763 fz_format_string(NULL, &out, snprintf_emit, fmt, args);
764 if (space > 0)
765 out.p[out.n < space ? out.n : space - 1] = '\0';
766
767 return out.n;
768 }
769
770 size_t
771 fz_snprintf(char *buffer, size_t space, const char *fmt, ...)
772 {
773 va_list ap;
774 struct snprintf_buffer out;
775 out.p = buffer;
776 out.s = space > 0 ? space - 1 : 0;
777 out.n = 0;
778
779 va_start(ap, fmt);
780 /* Note: using a NULL context is safe here */
781 fz_format_string(NULL, &out, snprintf_emit, fmt, ap);
782 if (space > 0)
783 out.p[out.n < space ? out.n : space - 1] = '\0';
784 va_end(ap);
785
786 return out.n;
787 }
788
789 char *
790 fz_asprintf(fz_context *ctx, const char *fmt, ...)
791 {
792 size_t len;
793 char *mem;
794 va_list ap;
795 va_start(ap, fmt);
796 len = fz_vsnprintf(NULL, 0, fmt, ap);
797 va_end(ap);
798 mem = Memento_label(fz_malloc(ctx, len+1), "asprintf");
799 va_start(ap, fmt);
800 fz_vsnprintf(mem, len+1, fmt, ap);
801 va_end(ap);
802 return mem;
803 }