#include "fitz_base.h" #include "fitz_stream.h" struct vap { va_list ap; }; static fz_error parseobj(fz_obj **obj, pdf_xref *xref, char **sp, struct vap *v); static inline int iswhite(int ch) { return ch == '\000' || ch == '\011' || ch == '\012' || ch == '\014' || ch == '\015' || ch == '\040'; } static inline int isdelim(int ch) { return ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '[' || ch == ']' || ch == '{' || ch == '}' || ch == '/' || ch == '%'; } static inline int isregular(int ch) { return !isdelim(ch) && !iswhite(ch) && ch != EOF; } static inline int fromhex(char ch) { if (ch >= '0' && ch <= '9') return ch - '0'; else if (ch >= 'A' && ch <= 'F') return ch - 'A' + 0xA; else if (ch >= 'a' && ch <= 'f') return ch - 'a' + 0xA; return 0; } static inline void skipwhite(char **sp) { char *s = *sp; while (iswhite(*s)) s ++; *sp = s; } static void parsekeyword(char **sp, char *b, char *eb) { char *s = *sp; while (b < eb && isregular(*s)) *b++ = *s++; *b++ = 0; *sp = s; } static fz_error parsename(fz_obj **obj, char **sp) { fz_error error; char buf[64]; char *s = *sp; char *p = buf; s ++; /* skip '/' */ while (p < buf + sizeof buf - 1 && isregular(*s)) *p++ = *s++; *p++ = 0; *sp = s; error = fz_newname(obj, buf); if (error) return fz_rethrow(error, "cannot create name"); return fz_okay; } static fz_error parsenumber(fz_obj **obj, char **sp) { fz_error error; char buf[32]; char *s = *sp; char *p = buf; while (p < buf + sizeof buf - 1) { if (s[0] == '-' || s[0] == '.' || (s[0] >= '0' && s[0] <= '9')) *p++ = *s++; else break; } *p++ = 0; *sp = s; if (strchr(buf, '.')) error = fz_newreal(obj, atof(buf)); else error = fz_newint(obj, atoi(buf)); if (error) return fz_rethrow(error, "cannot parse number"); return fz_okay; } static fz_error parsedict(fz_obj **obj, pdf_xref *xref, char **sp, struct vap *v) { fz_error error = fz_okay; fz_obj *dict = nil; fz_obj *key = nil; fz_obj *val = nil; char *s = *sp; error = fz_newdict(&dict, 8); if (error) return fz_rethrow(error, "cannot create dict"); s += 2; /* skip "<<" */ while (*s) { skipwhite(&s); /* end-of-dict marker >> */ if (*s == '>') { s ++; if (*s == '>') { s ++; break; } error = fz_throw("malformed >> marker"); goto cleanup; } /* non-name as key, bail */ if (*s != '/') { error = fz_throw("key is not a name"); goto cleanup; } error = parsename(&key, &s); if (error) { error = fz_rethrow(error, "cannot parse key"); goto cleanup; } skipwhite(&s); error = parseobj(&val, xref, &s, v); if (error) { error = fz_rethrow(error, "cannot parse value"); goto cleanup; } error = fz_dictput(dict, key, val); if (error) { error = fz_rethrow(error, "cannot insert dict entry"); goto cleanup; } fz_dropobj(val); val = nil; fz_dropobj(key); key = nil; } *obj = dict; *sp = s; return fz_okay; cleanup: if (val) fz_dropobj(val); if (key) fz_dropobj(key); if (dict) fz_dropobj(dict); *obj = nil; *sp = s; return error; /* already rethrown */ } static fz_error parsearray(fz_obj **obj, pdf_xref *xref, char **sp, struct vap *v) { fz_error error; fz_obj *a; fz_obj *o; char *s = *sp; error = fz_newarray(&a, 8); if (error) return fz_rethrow(error, "cannot create array"); s ++; /* skip '[' */ while (*s) { skipwhite(&s); if (*s == ']') { s ++; break; } error = parseobj(&o, xref, &s, v); if (error) { fz_dropobj(a); return fz_rethrow(error, "cannot parse item"); } error = fz_arraypush(a, o); if (error) { fz_dropobj(o); fz_dropobj(a); return fz_rethrow(error, "cannot add item to array"); } fz_dropobj(o); } *obj = a; *sp = s; return fz_okay; } static fz_error parsestring(fz_obj **obj, char **sp) { fz_error error; char buf[512]; char *s = *sp; char *p = buf; int balance = 1; int oct; s ++; /* skip '(' */ while (*s && p < buf + sizeof buf) { if (*s == '(') { balance ++; *p++ = *s++; } else if (*s == ')') { balance --; *p++ = *s++; } else if (*s == '\\') { s ++; if (*s >= '0' && *s <= '9') { oct = *s - '0'; s ++; if (*s >= '0' && *s <= '9') { oct = oct * 8 + (*s - '0'); s ++; if (*s >= '0' && *s <= '9') { oct = oct * 8 + (*s - '0'); s ++; } } *p++ = oct; } else switch (*s) { case 'n': *p++ = '\n'; s++; break; case 'r': *p++ = '\r'; s++; break; case 't': *p++ = '\t'; s++; break; case 'b': *p++ = '\b'; s++; break; case 'f': *p++ = '\f'; s++; break; default: *p++ = *s++; break; } } else *p++ = *s++; if (balance == 0) break; } *sp = s; error = fz_newstring(obj, buf, p - buf - 1); if (error) return fz_rethrow(error, "cannot create string"); return fz_okay; } static fz_error parsehexstring(fz_obj **obj, char **sp) { fz_error error; char buf[512]; char *s = *sp; char *p = buf; int a, b; s ++; /* skip '<' */ while (*s && p < buf + sizeof buf) { skipwhite(&s); if (*s == '>') { s ++; break; } a = *s++; if (*s == '\0') break; skipwhite(&s); if (*s == '>') { s ++; break; } b = *s++; *p++ = fromhex(a) * 16 + fromhex(b); } *sp = s; error = fz_newstring(obj, buf, p - buf); if (error) return fz_rethrow(error, "cannot create string"); return fz_okay; } static fz_error parseobj(fz_obj **obj, pdf_xref *xref, char **sp, struct vap *v) { fz_error error; char buf[32]; int num, gen, len; char *tmp; char *s = *sp; if (*s == '\0') return fz_throw("end of data"); skipwhite(&s); error = fz_okay; if (v != nil && *s == '%') { s ++; switch (*s) { case 'o': *obj = fz_keepobj(va_arg(v->ap, fz_obj*)); break; case 'b': error = fz_newbool(obj, va_arg(v->ap, int)); break; case 'i': error = fz_newint(obj, va_arg(v->ap, int)); break; case 'f': error = fz_newreal(obj, (float)va_arg(v->ap, double)); break; case 'n': error = fz_newname(obj, va_arg(v->ap, char*)); break; case 'r': num = va_arg(v->ap, int); gen = va_arg(v->ap, int); error = fz_newindirect(obj, num, gen, xref); break; case 's': tmp = va_arg(v->ap, char*); error = fz_newstring(obj, tmp, strlen(tmp)); break; case '#': tmp = va_arg(v->ap, char*); len = va_arg(v->ap, int); error = fz_newstring(obj, tmp, len); break; default: error = fz_throw("unknown format specifier in packobj: '%c'", *s); break; } if (error) error = fz_rethrow(error, "cannot create object for %% format"); s ++; } else if (*s == '/') { error = parsename(obj, &s); if (error) error = fz_rethrow(error, "cannot parse name"); } else if (*s == '(') { error = parsestring(obj, &s); if (error) error = fz_rethrow(error, "cannot parse string"); } else if (*s == '<') { if (s[1] == '<') { error = parsedict(obj, xref, &s, v); if (error) error = fz_rethrow(error, "cannot parse dict"); } else { error = parsehexstring(obj, &s); if (error) error = fz_rethrow(error, "cannot parse hex string"); } } else if (*s == '[') { error = parsearray(obj, xref, &s, v); if (error) error = fz_rethrow(error, "cannot parse array"); } else if (*s == '-' || *s == '.' || (*s >= '0' && *s <= '9')) { error = parsenumber(obj, &s); if (error) error = fz_rethrow(error, "cannot parse number"); } else if (isregular(*s)) { parsekeyword(&s, buf, buf + sizeof buf); if (strcmp("true", buf) == 0) { error = fz_newbool(obj, 1); if (error) error = fz_rethrow(error, "cannot create bool (true)"); } else if (strcmp("false", buf) == 0) { error = fz_newbool(obj, 0); if (error) error = fz_rethrow(error, "cannot create bool (false)"); } else if (strcmp("null", buf) == 0) { error = fz_newnull(obj); if (error) error = fz_rethrow(error, "cannot create null object"); } else error = fz_throw("undefined keyword %s", buf); } else error = fz_throw("syntax error: unknown byte 0x%d", *s); *sp = s; return error; /* already rethrown */ } fz_error fz_packobj(fz_obj **op, pdf_xref *xref, char *fmt, ...) { fz_error error; struct vap v; va_list ap; va_start(ap, fmt); va_copy(v.ap, ap); error = parseobj(op, xref, &fmt, &v); va_end(ap); if (error) return fz_rethrow(error, "cannot parse object"); return fz_okay; } fz_error fz_parseobj(fz_obj **op, pdf_xref *xref, char *str) { return parseobj(op, xref, &str, nil); }