ref: 1d93500ddcda77cd265d492c6c9094c0f5c7488f
dir: /object.c/
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <bio.h>
#include "pdf.h"
Object *pdfstring(Biobuf *b);
Object *pdfname(Biobuf *b);
Object *pdfarray(Pdf *pdf, Biobuf *b);
Object *pdfdict(Pdf *pdf, Biobuf *b);
Object null = {
.type = Onull,
};
static char *otypes[] = {
[Obool] = "bool",
[Onum] = "num",
[Ostr] = "str",
[Oname] = "name",
[Oarray] = "array",
[Odict] = "dict",
[Ostream] = "stream",
[Onull] = "null",
[Oindir] = "indir",
};
int
Tfmt(Fmt *f)
{
Object *o;
o = va_arg(f->args, Object*);
if(o == nil || o == &null)
return fmtprint(f, "null");
if(o->type < 0 || o->type >= nelem(otypes))
return fmtprint(f, "????");
return fmtprint(f, "%s", otypes[o->type]);
}
/* General function to parse an object of any type. */
Object *
pdfobj(Pdf *pdf, void *b)
{
Object *o, *o2;
vlong off;
int c, tf;
Xref xref;
char s[16];
o = o2 = nil;
do; while(isws(c = Bgetc(b)));
if(c < 0)
goto err;
switch(c){
case '<': /* dictionary or a string */
c = Bgetc(b);
if(c == '<'){
Bseek(b, -2, 1);
if((o = pdfdict(pdf, b)) != nil){
/* check for attached stream */
off = Boffset(b);
if(Bread(b, s, 7) == 7 && memcmp(s, "stream", 6) == 0 && isws(c = s[6])){
/* there IS a stream */
if(c == '\r' && (c = Bgetc(b)) < 0)
goto err;
if(c != '\n'){
werrstr("stream has no newline after dict");
goto err;
}
o->stream.off = Boffset(b);
o->type = Ostream;
o->stream.len = dictint(o, "Length");
return o;
}
Bseek(b, off, 0);
return o;
}
}
Bungetc(b);
/* fall through */
case '(':
Bungetc(b);
if((o = pdfstring(b)) != nil)
o->pdf = pdf;
return o;
case '/':
Bungetc(b);
if((o = pdfname(b)) != nil)
o->pdf = pdf;
return o;
case '[':
Bungetc(b);
if((o = pdfarray(pdf, b)) != nil)
o->pdf = pdf;
return o;
case 'n':
off = Boffset(b);
if(Bgetc(b) == 'u' && Bgetc(b) == 'l' && Bgetc(b) == 'l' && (isws(c = Bgetc(b)) || isdelim(c))){
Bungetc(b);
return &null;
}
Bseek(b, off, 0);
c = 'f';
goto unexpected;
case 't':
off = Boffset(b);
tf = 1;
if(Bgetc(b) == 'r' && Bgetc(b) == 'u' && Bgetc(b) == 'e' && (isws(c = Bgetc(b)) || isdelim(c)))
goto bool;
Bseek(b, off, 0);
c = 't';
goto unexpected;
case 'f':
off = Boffset(b);
tf = 0;
if(Bgetc(b) == 'a' && Bgetc(b) == 'l' && Bgetc(b) == 's' && Bgetc(b) == 'e' && (isws(c = Bgetc(b)) || isdelim(c)))
goto bool;
Bseek(b, off, 0);
c = 'f';
goto unexpected;
bool:
Bungetc(b);
if((o = calloc(1, sizeof(*o))) == nil)
goto err;
o->type = Obool;
o->pdf = pdf;
o->bool = tf;
return o;
default:
if(!isdigit(c)){
unexpected:
Bungetc(b);
werrstr("unexpected char '%c'", c);
goto err;
}
/* it could be a number or an indirect object */
Bungetc(b);
if((o = calloc(1, sizeof(*o))) == nil)
goto err;
o->pdf = pdf;
Bgetd(b, &o->num); /* get the first number */
off = Boffset(b); /* seek here if not an indirect object later */
if((o2 = pdfobj(pdf, b)) != nil && o2->type == Onum){ /* second object is number too */
do; while(isws(c = Bgetc(b)));
if(c < 0)
goto err;
if(c == 'R'){ /* indirect object */
o->type = Oindir;
o->indir.id = o->num;
o->indir.gen = o2->num;
pdfobjfree(o2);
return o;
}
if(c == 'o' && Bgetc(b) == 'b' && Bgetc(b) == 'j'){ /* object */
xref.id = o->num;
xref.gen = o2->num;
/* FIXME put into a map */
pdfobjfree(o);
pdfobjfree(o2);
if((o = pdfobj(pdf, b)) != nil)
return o;
o2 = nil;
}
}
/* just a number, go back and return it */
o->type = Onum;
if(Bseek(b, off, 0) != off){
werrstr("seek failed");
goto err;
}
return o;
}
err:
werrstr("object: %r");
pdfobjfree(o);
pdfobjfree(o2);
return nil;
}
void
pdfobjfree(Object *o)
{
int i;
if(o == nil)
return;
switch(o->type){
case Onull:
return;
case Obool:
case Onum:
case Ostr:
case Oname:
break;
case Oarray:
for(i = 0; i < o->array.ne; i++)
pdfobjfree(o->array.e[i]);
free(o->array.e);
break;
case Odict:
case Ostream:
for(i = 0; i < o->dict.nkv; i++){
free(o->dict.kv[i].key);
pdfobjfree(o->dict.kv[i].value);
}
free(o->dict.kv);
break;
case Oindir:
break;
}
free(o);
}