summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2014-03-18 16:37:19 +0100
committerTor Andersson <tor.andersson@artifex.com>2014-03-19 12:52:05 +0100
commit30bcbe60b93a0e4a697871f69e8367476796f27c (patch)
tree39c2fa6d826a866fe6a5692b6e3575956b41c44c /source
parent4c2715a0bcecfed6ebdfee901920631b09364d7e (diff)
downloadmupdf-30bcbe60b93a0e4a697871f69e8367476796f27c.tar.xz
Implement our own vsnprintf variant.
The primary motivator for this is so that we can print floating point values and get the full accuracy out, without having to print 1.5 as 1.5000000, and without getting 23e24 etc. We only support %c, %f, %d, %o, %x and %s currently. We only support the zero padding qualifier, for integers. We do support some extensions: %C turns values >=128 into UTF-8. %M prints a fz_matrix. %R prints a fz_rect. %P prints a fz_point. We also implement a fprintf variant on top of this to allow for consistent results when using fz_output. a
Diffstat (limited to 'source')
-rw-r--r--source/fitz/buffer.c44
-rw-r--r--source/fitz/output.c2
-rw-r--r--source/fitz/printf.c258
-rw-r--r--source/pdf/pdf-appearance.c2
4 files changed, 283 insertions, 23 deletions
diff --git a/source/fitz/buffer.c b/source/fitz/buffer.c
index 07ce9361..e9295a26 100644
--- a/source/fitz/buffer.c
+++ b/source/fitz/buffer.c
@@ -224,45 +224,47 @@ fz_buffer_printf(fz_context *ctx, fz_buffer *buffer, const char *fmt, ...)
int ret;
va_list args;
va_start(args, fmt);
-
ret = fz_buffer_vprintf(ctx, buffer, fmt, args);
-
va_end(args);
-
return ret;
}
int
fz_buffer_vprintf(fz_context *ctx, fz_buffer *buffer, const char *fmt, va_list old_args)
{
+ int slack;
int len;
+ va_list args;
+
+ slack = buffer->cap - buffer->len;
+#ifdef _MSC_VER /* Microsoft Visual C */
+ args = old_args;
+#else
+ va_copy(args, old_args);
+#endif
+ len = fz_vsnprintf((char *)buffer->data + buffer->len, slack, fmt, args);
+#ifndef _MSC_VER
+ va_end(args);
+#endif
- do
+ /* len = number of chars written, not including the terminating
+ * NULL, so len+1 > slack means "truncated". */
+ if (len+1 > slack)
{
- int slack = buffer->cap - buffer->len;
+ /* Grow the buffer and retry */
+ fz_ensure_buffer(ctx, buffer, buffer->len + len);
+ slack = buffer->cap - buffer->len;
- if (slack > 0)
- {
- va_list args;
#ifdef _MSC_VER /* Microsoft Visual C */
- args = old_args;
+ args = old_args;
#else
- va_copy(args, old_args);
+ va_copy(args, old_args);
#endif
- len = vsnprintf((char *)buffer->data + buffer->len, slack, fmt, args);
+ len = fz_vsnprintf((char *)buffer->data + buffer->len, slack, fmt, args);
#ifndef _MSC_VER
- va_end(args);
+ va_end(args);
#endif
- /* len = number of chars written, not including the terminating
- * NULL, so len+1 > slack means "truncated". MSVC differs here
- * and returns -1 for truncated. */
- if (len >= 0 && len+1 <= slack)
- break;
- }
- /* Grow the buffer and retry */
- fz_grow_buffer(ctx, buffer);
}
- while (1);
buffer->len += len;
diff --git a/source/fitz/output.c b/source/fitz/output.c
index 5be0dccf..fe63de92 100644
--- a/source/fitz/output.c
+++ b/source/fitz/output.c
@@ -11,7 +11,7 @@ file_printf(fz_output *out, const char *fmt, va_list ap)
{
FILE *file = (FILE *)out->opaque;
- return vfprintf(file, fmt, ap);
+ return fz_vfprintf(out->ctx, file, fmt, ap);
}
static int
diff --git a/source/fitz/printf.c b/source/fitz/printf.c
new file mode 100644
index 00000000..ef70aca1
--- /dev/null
+++ b/source/fitz/printf.c
@@ -0,0 +1,258 @@
+#include "mupdf/fitz.h"
+
+#ifdef _MSC_VER /* Microsoft Visual C */
+#define va_copy(a, oa) a=oa
+#define va_end(a)
+#endif
+
+struct fmtbuf
+{
+ char *p;
+ int s;
+ int n;
+};
+
+static void fmtputc(struct fmtbuf *out, int c)
+{
+ if (out->n < out->s)
+ out->p[out->n] = c;
+ ++(out->n);
+}
+
+/*
+ * Compute decimal integer m, exp such that:
+ * f = m*10^exp
+ * m is as short as possible with losing exactness
+ * assumes special cases (NaN, +Inf, -Inf) have been handled.
+ */
+static void fz_dtoa(float f, char *digits, int *exp, int *neg, int *ndigits)
+{
+ char buf[20], *s, *p, *e;
+ int n;
+
+ /* TODO: binary search */
+ for (n = 1; n < 9; ++n) {
+ sprintf(buf, "%+.*e", n, f);
+ if (strtof(buf, NULL) == f)
+ break;
+ }
+
+ *neg = (buf[0] == '-');
+
+ p = buf + 3;
+ e = strchr(p, 'e');
+ *exp = atoi(e + 1) - (e - p);
+
+ if (e[-1] == '0') {
+ --e;
+ ++(*exp);
+ }
+
+ s = digits;
+ *s++ = buf[1];
+ while (p < e)
+ *s++ = *p++;
+ *s = 0;
+
+ *ndigits = s - digits;
+}
+
+/*
+ * 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, neg, ndigits, point;
+
+ if (isnan(f)) f = 0;
+ if (isinf(f)) f = f < 0 ? -FLT_MAX : FLT_MAX;
+
+ fz_dtoa(f, digits, &exp, &neg, &ndigits);
+ point = exp + ndigits;
+
+ if (neg)
+ fmtputc(out, '-');
+
+ 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 fmtint(struct fmtbuf *out, int value, int z, int base)
+{
+ static const char *digits = "0123456789abcdef";
+ char buf[40];
+ unsigned int a;
+ int i;
+
+ if (value < 0)
+ {
+ fmtputc(out, '-');
+ a = -value;
+ }
+ else
+ a = value;
+
+ i = 0;
+ while (a) {
+ buf[i++] = digits[a % base];
+ a /= base;
+ }
+ while (i < z)
+ buf[i++] = '0';
+ while (i > 0)
+ fmtputc(out, buf[--i]);
+}
+
+int
+fz_vsnprintf(char *buffer, int space, const char *fmt, va_list args)
+{
+ struct fmtbuf out;
+ fz_matrix *m;
+ fz_rect *r;
+ fz_point *p;
+ int c, i, n, z;
+ double f;
+ char *s;
+
+ out.p = buffer;
+ out.s = space;
+ out.n = 0;
+
+ while ((c = *fmt++))
+ {
+ if (c == '%') {
+ c = *fmt++;
+ if (c == 0)
+ break;
+ z = 1;
+ if (c == '0' && fmt[0] && fmt[1]) {
+ z = *fmt++ - '0';
+ c = *fmt++;
+ }
+ switch (c) {
+ default:
+ fmtputc(&out, '%');
+ fmtputc(&out, c);
+ break;
+ case '%':
+ fmtputc(&out, '%');
+ break;
+ case 'M':
+ m = va_arg(args, fz_matrix*);
+ fmtfloat(&out, m->a); fmtputc(&out, ' ');
+ fmtfloat(&out, m->b); fmtputc(&out, ' ');
+ fmtfloat(&out, m->c); fmtputc(&out, ' ');
+ fmtfloat(&out, m->d); fmtputc(&out, ' ');
+ fmtfloat(&out, m->e); fmtputc(&out, ' ');
+ fmtfloat(&out, m->f);
+ break;
+ case 'R':
+ r = va_arg(args, fz_rect*);
+ fmtfloat(&out, r->x0); fmtputc(&out, ' ');
+ fmtfloat(&out, r->y0); fmtputc(&out, ' ');
+ fmtfloat(&out, r->x1); fmtputc(&out, ' ');
+ fmtfloat(&out, r->y1);
+ break;
+ case 'P':
+ p = va_arg(args, fz_point*);
+ fmtfloat(&out, p->x); fmtputc(&out, ' ');
+ fmtfloat(&out, p->y);
+ break;
+ case 'C':
+ c = va_arg(args, int);
+ if (c < 128)
+ fmtputc(&out, c);
+ else {
+ char buf[10];
+ 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 'f':
+ case 'g':
+ f = va_arg(args, double);
+ fmtfloat(&out, f);
+ break;
+ case 'x':
+ i = va_arg(args, int);
+ fmtint(&out, i, z, 16);
+ break;
+ case 'd':
+ i = va_arg(args, int);
+ fmtint(&out, i, z, 10);
+ break;
+ case 'o':
+ i = va_arg(args, int);
+ fmtint(&out, i, z, 8);
+ break;
+ case 's':
+ s = va_arg(args, char*);
+ if (!s)
+ s = "(null)";
+ while ((c = *s++))
+ fmtputc(&out, c);
+ break;
+ }
+ } else {
+ fmtputc(&out, c);
+ }
+ }
+
+ fmtputc(&out, 0);
+ return out.n - 1;
+}
+
+int
+fz_vfprintf(fz_context *ctx, FILE *file, const char *fmt, va_list old_args)
+{
+ char buffer[256];
+ int l;
+ va_list args;
+ char *b = buffer;
+
+ /* First try using our fixed size buffer */
+ va_copy(args, old_args);
+ l = fz_vsnprintf(buffer, sizeof buffer, fmt, args);
+ va_end(args);
+
+ /* If that failed, allocate the right size buffer dynamically */
+ if (l >= sizeof buffer)
+ {
+ b = fz_malloc(ctx, l + 1);
+ va_copy(args, old_args);
+ fz_vsnprintf(buffer, l + 1, fmt, args);
+ va_end(args);
+ }
+
+ l = fwrite(b, 1, l, file);
+
+ if (b != buffer)
+ fz_free(ctx, b);
+
+ return l;
+}
diff --git a/source/pdf/pdf-appearance.c b/source/pdf/pdf-appearance.c
index e1822331..2d1a6b54 100644
--- a/source/pdf/pdf-appearance.c
+++ b/source/pdf/pdf-appearance.c
@@ -56,7 +56,7 @@ static const char *fmt_q = "q\n";
static const char *fmt_W = "W\n";
static const char *fmt_n = "n\n";
static const char *fmt_BT = "BT\n";
-static const char *fmt_Tm = "%1.2f %1.2f %1.2f %1.2f %1.2f %1.2f Tm\n";
+static const char *fmt_Tm = "%f %f %f %f %f %f Tm\n";
static const char *fmt_Td = "%f %f Td\n";
static const char *fmt_Tj = " Tj\n";
static const char *fmt_ET = "ET\n";