History

rewrite the API, support more object types and actual evaluation

Sigrid Haflínudóttir committed 6 years ago

Commit a080ae81 parent 34238e011 files · +494 −376Browse files

Unified · Split

11 files
  1. array.c +21 −24
  2. dict.c +41 −34
  3. eval.c +18 −7
  4. filter.c +1 −0
  5. main.c +9 −1
  6. misc.c +3 −2
  7. name.c +93 −65
  8. object.c +128 −68
  9. pdf.c +5 −9
  10. pdf.h +12 −38
  11. string.c +163 −128
array.c+21−24
@@ -1,40 +1,34 @@
1#include <u.h>1#include <u.h>
2#include <libc.h>2#include <libc.h>
3#include <ctype.h>3#include <bio.h>
4#include "pdf.h"4#include "pdf.h"
5 5
6/* 7.3.6 Array Objects */
7
6Object *8Object *
7pdfarray(char *p, char **e, int len)9pdfarray(Biobuf *b)
8{10{
9 Object *o, *m;11 Object *o, *m;
10 Object **a;12 Object **a;
11 13 int c, noel;
12 o = nil;
13 if(len < 3 || p[0] != '['){
14 werrstr("invalid array");
15 goto err;
16 }
17 p++;
18 len--;
19 14
20 o = calloc(1, sizeof(*o));15 o = calloc(1, sizeof(*o));
21 o->type = Oarray;16 o->type = Oarray;
22 for(; len >= 1;){17 Bgetc(b); /* throw away '[' */
23 for(; len > 0 && isws(*p); p++, len--);
24 if(len < 1){
25 werrstr("too short");
26 goto err;
27 }
28 18
29 if(p[0] == ']'){19 for(noel = 0;;){
30 p++;20 if((c = Bgetc(b)) < 0 || c == ']')
31 break;21 break;
22 if(noel){
23 werrstr("no ']'");
24 goto err;
32 }25 }
33 26
34 if((m = pdfobject(p, e, len)) == nil)27 Bungetc(b);
35 goto err;28 if((m = pdfobject(b)) == nil){
36 len -= *e - p;29 noel = 1;
37 p = *e;30 continue;
31 }
38 32
39 if((a = realloc(o->array.e, (o->array.ne+1)*sizeof(Object*))) == nil){33 if((a = realloc(o->array.e, (o->array.ne+1)*sizeof(Object*))) == nil){
40 freeobject(m);34 freeobject(m);
@@ -45,11 +39,14 @@ pdfarray(char *p, char **e, int len) · 4 unmodified lines
45 a[o->array.ne++] = m;39 a[o->array.ne++] = m;
46 }40 }
47 41
48 *e = p;42 if(c != ']'){
43 werrstr("no ']'");
44 goto err;
45 }
49 46
50 return o;47 return o;
51err:48err:
49 werrstr("array: %r");
52 freeobject(o);50 freeobject(o);
53
54 return nil;51 return nil;
55}52}
dict.c+41−34
@@ -1,70 +1,77 @@
1#include <u.h>1#include <u.h>
2#include <libc.h>2#include <libc.h>
3#include <ctype.h>3#include <bio.h>
4#include "pdf.h"4#include "pdf.h"
5 5
6/* 7.3.7 Dictionary Objects */
7
6Object *8Object *
7pdfdict(char *p, char **e, int len)9pdfdict(Biobuf *b)
8{10{
9 Object *o, *k, *v;11 Object *o, *k, *v;
10 KeyValue *kv;12 KeyValue *kv;
13 int c, nokey;
11 14
12 o = nil;15 /* skip '<<' */
13 if(len < 4 || p[0] != '<' || p[1] != '<'){16 Bseek(b, 2, 1);
14 werrstr("invalid dict");
15 goto err;
16 }
17 p += 2;
18 len -= 2;
19 17
18 k = v = nil;
20 o = calloc(1, sizeof(*o));19 o = calloc(1, sizeof(*o));
21 o->type = Odict;20 o->type = Odict;
22 for(; len >= 2;){21 for(nokey = 0;;){
23 for(; len > 2 && isws(*p); p++, len--);22 if((c = Bgetc(b)) < 0)
24 if(len < 1){23 goto err;
25 werrstr("too short");24 if(c == '>'){
25 if(Bgetc(b) == '>')
26 break;
27 werrstr("no '>>'");
26 goto err;28 goto err;
27 }29 }
28 30 if(nokey){
29 if(p[0] == '>' && p[1] == '>'){31 werrstr("no '>>'");
30 p += 2;32 goto err;
31 break;
32 }33 }
33 34
34 if((k = pdfobject(p, e, len)) == nil)35 Bungetc(b);
36 if((k = pdfobject(b)) == nil){
37 nokey = 1;
38 continue;
39 }
40 if((v = pdfobject(b)) == nil)
35 goto err;41 goto err;
36 if(k->type != Oname){42 if(k->type != Oname){
37 freeobject(k);
38 werrstr("expected name as a key");43 werrstr("expected name as a key");
39 goto err;44 goto err;
40 }45 }
41 len -= *e - p;
42 p = *e;
43 46
44 if((v = pdfobject(p, e, len)) == nil){47 if((kv = realloc(o->dict.kv, (o->dict.nkv+1)*sizeof(KeyValue))) == nil)
45 freeobject(k);
46 goto err;48 goto err;
47 }
48 len -= *e - p;
49 p = *e;
50
51 if((kv = realloc(o->dict.kv, (o->dict.nkv+1)*sizeof(KeyValue))) == nil){
52 freeobject(k);
53 freeobject(v);
54 goto err;
55 }
56 49
57 o->dict.kv = kv;50 o->dict.kv = kv;
58 kv[o->dict.nkv].key = strdup(k->name);51 kv[o->dict.nkv].key = strdup(k->name);
59 freeobject(k);52 freeobject(k);
60 kv[o->dict.nkv++].value = v;53 kv[o->dict.nkv++].value = v;
54 k = v = nil;
61 }55 }
62 56
63 *e = p;
64
65 return o;57 return o;
66err:58err:
67 freeobject(o);59 freeobject(o);
60 freeobject(k);
61 freeobject(v);
62 werrstr("dict: %r");
68 63
69 return nil;64 return nil;
70}65}
66
67Object *
68pdfdictget(Object *o, char *name)
69{
70 int i;
71
72 if(o == nil || o->type != Odict || name == nil)
73 return nil;
74 for(i = 0; i < o->dict.nkv && strcmp(name, o->dict.kv[i].key) != 0; i++);
75
76 return i < o->dict.nkv ? o->dict.kv[i].value : nil;
77}
eval.c+18−7
@@ -1,23 +1,34 @@
1#include <u.h>1#include <u.h>
2#include <libc.h>2#include <libc.h>
3#include <bio.h>
3#include "pdf.h"4#include "pdf.h"
4 5
5int6int
6pdfeval(Pdf *pdf, Object *o)7pdfeval(Pdf *pdf, Object *o)
7{8{
9 Object *d;
8 Xref *x;10 Xref *x;
9 int i;11 int i;
10 12
11 if(o->type != Oindir)13 if(o == nil || o->type != Oindir)
12 return 0;14 return 0;
13 15
14 for(i = 0; i < pdf->nxref; i++){16 for(i = 0; i < pdf->nxref && pdf->xref[i].id != o->indir.id; i++);
15 x = &pdf->xref[i];17 if(i >= pdf->nxref){
16 if(x->id == o->indir.id)18 werrstr("no object id %d in xref", o->indir.id);
17 return 0;19 return -1;
18 }20 }
21 x = &pdf->xref[i];
19 22
20 werrstr("no object id %d in xref", o->indir.id);23 if(Bseek(pdf->bio, x->off, 0) != x->off){
24 werrstr("xref seek failed");
25 return -1;
26 }
27 if((d = pdfobject(pdf->bio)) == nil)
28 return -1;
29 *o = *d;
30 memset(d, 0, sizeof(*d));
31 freeobject(d);
21 32
22 return -1;33 return 0;
23}34}
filter.c+1−0
@@ -1,5 +1,6 @@
1#include <u.h>1#include <u.h>
2#include <libc.h>2#include <libc.h>
3#include <bio.h>
3#include "pdf.h"4#include "pdf.h"
4 5
5/*6/*
main.c+9−1
@@ -1,8 +1,11 @@
1#include <u.h>1#include <u.h>
2#include <libc.h>2#include <libc.h>
3#include <thread.h>3#include <thread.h>
4#include <bio.h>
4#include "pdf.h"5#include "pdf.h"
5 6
7int mainstacksize = 32768;
8
6static void9static void
7usage(void)10usage(void)
8{11{
@@ -15,6 +18,7 @@ threadmain(int argc, char **argv) · 6 unmodified lines
15{18{
16 int fd;19 int fd;
17 Pdf *pdf;20 Pdf *pdf;
21 Object *v;
18 22
19 quotefmtinstall();23 quotefmtinstall();
20 24
@@ -40,7 +44,11 @@ threadmain(int argc, char **argv) · 19 unmodified lines
40 if((fd = open(argv[0], OREAD)) < 0)44 if((fd = open(argv[0], OREAD)) < 0)
41 sysfatal("%r");45 sysfatal("%r");
42 if((pdf = pdfopen(fd)) == nil)46 if((pdf = pdfopen(fd)) == nil)
43 sysfatal("pdfopen: %r");47 sysfatal("%r");
48 if((v = pdfdictget(pdf->info, "Creator")) != nil)
49 fprint(2, "creator: %s\n", v->str);
50 if((v = pdfdictget(pdf->info, "Producer")) != nil)
51 fprint(2, "producer: %s\n", v->str);
44 pdfclose(pdf);52 pdfclose(pdf);
45 53
46 threadexitsall(nil);54 threadexitsall(nil);
misc.c+3−2
@@ -1,10 +1,11 @@
1#include <u.h>1#include <u.h>
2#include <libc.h>2#include <libc.h>
3#include <bio.h>
3#include "pdf.h"4#include "pdf.h"
4 5
5/* 7.2.2 whitespace */6/* 7.2.2 whitespace */
6int7int
7isws(char c)8isws(int c)
8{9{
9 return /* \0 is missing on purpose */10 return /* \0 is missing on purpose */
10 c == '\t' || c == '\n' || c == '\f' || c == '\r' ||11 c == '\t' || c == '\n' || c == '\f' || c == '\r' ||
@@ -13,7 +14,7 @@ isws(char c) · 2 unmodified lines
13 14
14/* 7.2.2 delimeters */15/* 7.2.2 delimeters */
15int16int
16isdelim(char c)17isdelim(int c)
17{18{
18 return19 return
19 c == '(' || c == ')' || c == '<' || c == '>' ||20 c == '(' || c == ')' || c == '<' || c == '>' ||
name.c+93−65
@@ -1,104 +1,132 @@
1#include <u.h>1#include <u.h>
2#include <libc.h>2#include <libc.h>
3#include <bio.h>
3#include "pdf.h"4#include "pdf.h"
4 5
5int6/* 7.3.5 Name Objects */
6pdfname(char *p, char **e, int len)7
8Object *
9pdfname(Biobuf *b)
7{10{
8 int i, o;11 Object *o;
12 char *s, *r, hex[3];
13 int c, sz, maxsz;
9 14
10 if(len < 2){15 Bgetc(b); /* skip '/' */
11 werrstr("too short");
12 goto err;
13 }
14 16
15 if(p[0] != '/'){17 maxsz = 32;
16 werrstr("invalid first char");18 if((s = malloc(maxsz)) == nil)
17 goto err;19 goto err;
18 }
19 20
20 for(i = 1, o = 0; i < len; i++){21 for(sz = 0;;){
21 if(p[i] < '!' || p[i] > '~'){22 if((c = Bgetc(b)) < 0){
22 if(isws(p[i]) || isdelim(p[i]))23 if(c == Beof)
23 break;24 break;
24 werrstr("invalid char %02x", p[i]);
25 goto err;25 goto err;
26 }26 }
27 if(p[i] == '#'){27
28 i++;28 if(isws(c) || isdelim(c)){
29 if(i+2 > len){29 Bungetc(b);
30 werrstr("hex too short");30 break;
31 }
32 if(c < '!' || c > '~'){
33 werrstr("invalid char %02x", c);
34 goto err;
35 }
36 if(c == '#'){
37 if((c = Bgetc(b)) < 0)
31 goto err;38 goto err;
32 }39 hex[0] = c;
33 if(dec16((uchar*)p+o, 1, p+i, 2) != 1){40 if((c = Bgetc(b)) < 0)
41 goto err;
42 hex[1] = c;
43 if(dec16((uchar*)hex, 1, hex, 2) != 1){
34 werrstr("invalid hex");44 werrstr("invalid hex");
35 goto err;45 goto err;
36 }46 }
37 o++;47 c = hex[0];
38 i++;
39 }else{
40 p[o++] = p[i];
41 }48 }
49 if(sz+1 >= maxsz){
50 maxsz *= 2;
51 if((r = realloc(s, maxsz)) == nil)
52 goto err;
53 s = r;
54 }
55 s[sz++] = c;
42 }56 }
43 57
44 p[o] = 0;58 if((o = malloc(sizeof(*o) + sz + 1)) != nil){
45 *e = p + i;59 memmove(o->name, s, sz);
60 o->name[sz] = 0;
61 o->type = Oname;
62 free(s);
63 return o;
64 }
46 65
47 return o;
48err:66err:
49 werrstr("name: %r");67 werrstr("name: %r");
50 return -1;68 free(s);
69 return nil;
51}70}
52 71
53#ifdef TEST72#ifdef TEST
54static struct {73static struct {
55 char *i;74 char *in;
56 int len;75 char *out;
57 char *o;
58 int r;
59 int e;
60}t[] = {76}t[] = {
61 {"/SimpleName", 11, "SimpleName", 10},77 {"/SimpleName", "SimpleName"},
62 {"WrongName", 9, nil, -1},78 {"/.$()", ".$"},
63 {"/.$()", 5, ".$()", 4},79 {"/#30", "0"},
64 {"/#30", 4, "0", 1},80 {"/#3", nil},
65 {"/#3", 3, nil, -1},81 {"/#G0", nil},
66 {"/#G0", 4, nil, -1},82 {"/#", nil},
67 {"/#", 2, nil, -1},83 {"/Space Between", "Space"},
68 {"/Wrong Char", 11, nil, -1},84 {"/Two/Names", "Two"},
69 {"/\xff", 2, nil, -1},85 {"/\xff", nil,},
70};86};
71 87
88static char *s;
89static int off, n;
90
91static int
92rd(Biobufhdr *, void *data, long sz)
93{
94 if(sz > n-off)
95 sz = n-off;
96 memmove(data, s+off, sz);
97 off += sz;
98 return sz;
99}
100
72void101void
73test_pdfname(void)102test_pdfname(void)
74{103{
75 char *e;104 Object *o;
76 int i, r;105 Biobuf b;
106 int i;
77 107
78 fprint(2, "pdfname\n");108 fprint(2, "pdfname\n");
79 for(i = 0; i < nelem(t); i++){109 for(i = 0; i < nelem(t); i++){
110 s = t[i].in;
111 n = strlen(s);
112 off = 0;
113 Binit(&b, -1, OREAD);
114 Biofn(&b, rd);
115
80 fprint(2, "\t%d: ", i);116 fprint(2, "\t%d: ", i);
81 r = pdfname(t[i].i, &e, t[i].len);117 o = pdfname(&b);
82 if(r != t[i].r){118 if(o == nil && t[i].out != nil)
83 fprint(2, "expected r=%d, got %d", t[i].r, r);119 fprint(2, "ERROR: expected %q, got error: %r\n", t[i].out);
84 if(r < 0)120 else if(o != nil && t[i].out == nil)
85 fprint(2, " (%r)\n");121 fprint(2, "ERROR: expected error, got %q\n", o->name);
86 else122 else if(o == nil && t[i].out == nil)
87 fprint(2, "\n");123 fprint(2, "OK (%r)\n");
88 continue;124 else if(strcmp(o->name, t[i].out) != 0)
89 }else if(r >= 0){125 fprint(2, "ERROR: expected %q, got %q\n", t[i].out, o->name);
90 if(t[i].i+t[i].len != e){126 else
91 fprint(2, "expected e=%p, got %p\n", t[i].i+t[i].len, e);127 fprint(2, "OK\n");
92 continue;128 freeobject(o);
93 }else if(strcmp(t[i].o, t[i].i) != 0){129 Bterm(&b);
94 fprint(2, "expected %q, got %q\n", t[i].o, t[i].i);
95 continue;
96 }
97 }
98 fprint(2, "OK");
99 if(r < 0)
100 fprint(2, " (%r)");
101 fprint(2, "\n");
102 }130 }
103}131}
104#endif132#endif
object.c+128−68
@@ -1,105 +1,165 @@
1#include <u.h>1#include <u.h>
2#include <libc.h>2#include <libc.h>
3#include <ctype.h>3#include <ctype.h>
4#include <bio.h>
4#include "pdf.h"5#include "pdf.h"
5 6
7Object *pdfstring(Biobuf *b);
8Object *pdfname(Biobuf *b);
9Object *pdfarray(Biobuf *b);
10Object *pdfdict(Biobuf *b);
11
12static Object null = {
13 .type = Onull,
14};
15
16/* General function to parse an object of any type. */
6Object *17Object *
7pdfobject(char *p, char **e, int len)18pdfobject(void *b)
8{19{
9 Object *o;20 Object *o, *o2;
10 char *s, *p0;21 vlong off;
11 int sz, gen;22 int c, tf;
12
13 o = nil;
14 for(; len > 0 && isws(*p); p++, len--);
15 if(len < 1){
16 werrstr("too short");
17 goto err;
18 }
19 23
20 if(*p < 1){24 o = o2 = nil;
21 werrstr("unexpected non-ascii char");25 do; while(isws(c = Bgetc(b)));
26 if(c < 0)
22 goto err;27 goto err;
23 }
24 28
25 switch(*p){29 switch(c){
26 case '<': /* dictionary or a string */30 case '<': /* dictionary or a string */
27 if(len < 2){31 c = Bgetc(b);
28 werrstr("too short");32 if(c == '<'){
29 goto err;33 Bseek(b, -2, 1);
30 }34 return pdfdict(b);
31 if(p[1] == '<'){ /* dictionary */
32 o = pdfdict(p, e, len);
33 break;
34 }35 }
36 Bungetc(b);
35 /* fall through */37 /* fall through */
36 38
37 case '(': /* string */39 case '(':
38 if((sz = pdfstring(p, e, len)) < 0)40 Bungetc(b);
39 goto err;41 return pdfstring(b);
40 if((o = malloc(sizeof(*o)+sz+1)) != nil){
41 o->type = Ostr;
42 o->str = (char*)(o+1);
43 strcpy(o->str, p);
44 }
45 break;
46 42
47 case '/':43 case '/':
48 if((sz = pdfname(p, e, len)) < 0)44 Bungetc(b);
49 goto err;45 return pdfname(b);
50 if((o = malloc(sizeof(*o)+sz+1)) != nil){
51 o->type = Oname;
52 o->str = (char*)(o+1);
53 strcpy(o->str, p);
54 }
55 break;
56 46
57 case '[':47 case '[':
58 o = pdfarray(p, e, len);48 Bungetc(b);
59 break;49 return pdfarray(b);
50
51 case 'n':
52 off = Boffset(b);
53 if(Bgetc(b) == 'u' && Bgetc(b) == 'l' && Bgetc(b) == 'l' && (isws(c = Bgetc(b)) || isdelim(c))){
54 Bungetc(b);
55 return &null;
56 }
57 Bseek(b, off, 0);
58 c = 'f';
59 goto unexpected;
60
61 case 't':
62 off = Boffset(b);
63 tf = 1;
64 if(Bgetc(b) == 'r' && Bgetc(b) == 'u' && Bgetc(b) == 'e' && (isws(c = Bgetc(b)) || isdelim(c)))
65 goto bool;
66 Bseek(b, off, 0);
67 c = 't';
68 goto unexpected;
69
70 case 'f':
71 off = Boffset(b);
72 tf = 0;
73 if(Bgetc(b) == 'a' && Bgetc(b) == 'l' && Bgetc(b) == 's' && Bgetc(b) == 'e' && (isws(c = Bgetc(b)) || isdelim(c)))
74 goto bool;
75 Bseek(b, off, 0);
76 c = 'f';
77 goto unexpected;
78bool:
79 Bungetc(b);
80 if((o = malloc(sizeof(*o))) == nil)
81 goto err;
82 o->type = Obool;
83 o->bool = tf;
84 return o;
60 85
61 default:86 default:
62 if(isdigit(*p)){87 if((o = malloc(sizeof(*o))) == nil)
63 if((o = malloc(sizeof(*o))) != nil){88 goto err;
64 o->type = Onum;89 if(!isdigit(c)){
65 o->num = strtod(p, e);90unexpected:
66 sz = len - (*e - p);91 Bungetc(b);
67 p0 = *e;92 werrstr("unexpected char '%c'", c);
68 s = p0;93 goto err;
69 for(; sz > 0 && isws(*s); s++, sz--);94 }
70 if(sz > 0 && isdigit(*s)){95 /* it could be a number or an indirect object */
71 gen = strtod(s, &p);96 Bungetc(b);
72 sz -= (p - s);97 Bgetd(b, &o->num); /* get the first number */
73 s = p;98 off = Boffset(b); /* seek here if not an indirect object later */
74 for(; sz > 0 && isws(*s); s++, sz--);99
75 if(sz > 0 && *s == 'R'){ /* indirect object */100 if((o2 = pdfobject(b)) != nil && o2->type == Onum){ /* second object is number too */
76 s++;101 do; while(isws(c = Bgetc(b)));
77 o->type = Oindir;102 if(c < 0)
78 o->indir.id = o->num;103 goto err;
79 o->indir.gen = gen;104 if(c == 'R'){ /* indirect object */
80 p0 = s;105 o->type = Oindir;
81 }106 o->indir.id = o->num;
82 }107 o->indir.gen = o2->num;
83 *e = p0;108 freeobject(o2);
109 return o;
110 }
111 if(c == 'o' && Bgetc(b) == 'b' && Bgetc(b) == 'j'){ /* object */
112 freeobject(o2);
113 /* FIXME put into a map */
114 return pdfobject(b);
84 }115 }
85 break;
86 }116 }
87 werrstr("unexpected char %c", *p);
88 goto err;
89 }
90 117
91 if(o != nil)118 /* just a number, go back and return it */
119 o->type = Onum;
120 if(Bseek(b, off, 0) != off){
121 werrstr("seek failed");
122 goto err;
123 }
92 return o;124 return o;
125 }
126
93err:127err:
94 werrstr("object: %r");128 werrstr("object: %r");
95 freeobject(o);129 freeobject(o);
130 freeobject(o2);
96 return nil;131 return nil;
97}132}
98 133
99void134void
100freeobject(Object *o)135freeobject(Object *o)
101{136{
137 int i;
138
102 if(o == nil)139 if(o == nil)
103 return;140 return;
141
142 switch(o->type){
143 case Onull:
144 return;
145
146 case Obool:
147 case Onum:
148 case Ostr:
149 case Oname:
150 break;
151
152 case Oarray:
153 for(i = 0; i < o->array.ne; i++)
154 freeobject(o->array.e[i]);
155 free(o->array.e);
156 break;
157
158 case Odict:
159 case Ostream:
160 case Oindir:
161 break;
162 }
163
104 free(o);164 free(o);
105}165}
pdf.c+5−9
@@ -33,8 +33,8 @@ xrefread(Pdf *pdf, int xref0, int nxref) · 32 unmodified lines
33 /* store non-free objects only */33 /* store non-free objects only */
34 newnxref = pdf->nxref;34 newnxref = pdf->nxref;
35 for(e = s, i = 0; i < nxref; i++, e += 20){35 for(e = s, i = 0; i < nxref; i++, e += 20){
36 if(e[10] != ' ' || e[18] != '\r' || e[19] != '\n'){36 if(!isspace(e[10]) || !isspace(e[18]) || !isspace(e[19])){
37 werrstr("invalid xref line");37 werrstr("invalid xref line (%d/%d)", i, nxref);
38 goto err;38 goto err;
39 }39 }
40 xref.id = xref0 + i;40 xref.id = xref0 + i;
@@ -76,15 +76,11 @@ static int · 35 unmodified lines
76trailerread(Pdf *pdf)76trailerread(Pdf *pdf)
77{77{
78 int i;78 int i;
79 char *s, *e;
80 Object *o;79 Object *o;
81 KeyValue *kv;80 KeyValue *kv;
82 81
83 o = nil;82 if((o = pdfobject(pdf->bio)) == nil)
84 if((s = Brdstr(pdf->bio, 0, 1)) == nil || (o = pdfobject(s, &e, Blinelen(pdf->bio))) == nil)
85 goto err;83 goto err;
86 free(s);
87 s = nil;
88 84
89 if(o->type != Odict){85 if(o->type != Odict){
90 werrstr("isn't a dictionary");86 werrstr("isn't a dictionary");
@@ -92,7 +88,6 @@ trailerread(Pdf *pdf) · 1 unmodified line
92 }88 }
93 89
94 for(i = 0, kv = o->dict.kv; i < o->dict.nkv; i++, kv++){90 for(i = 0, kv = o->dict.kv; i < o->dict.nkv; i++, kv++){
95 fprint(2, "# %s %p\n", kv->key, kv->value);
96 if(strcmp(kv->key, "Root") == 0)91 if(strcmp(kv->key, "Root") == 0)
97 pdf->root = kv->value;92 pdf->root = kv->value;
98 else if(strcmp(kv->key, "Info") == 0)93 else if(strcmp(kv->key, "Info") == 0)
@@ -112,7 +107,6 @@ trailerread(Pdf *pdf) · 13 unmodified lines
112 return 0;107 return 0;
113err:108err:
114 freeobject(o);109 freeobject(o);
115 free(s);
116 return -1;110 return -1;
117}111}
118 112
@@ -127,6 +121,7 @@ pdfopen(int fd) · 8 unmodified lines
127 int xreftb; /* 7.5.4 xref table offset from the beginning of the file */121 int xreftb; /* 7.5.4 xref table offset from the beginning of the file */
128 int i, n, off;122 int i, n, off;
129 123
124 b = nil;
130 if((pdf = calloc(1, sizeof(*pdf))) == nil || (b = Bfdopen(fd, OREAD)) == nil)125 if((pdf = calloc(1, sizeof(*pdf))) == nil || (b = Bfdopen(fd, OREAD)) == nil)
131 goto err;126 goto err;
132 pdf->bio = b;127 pdf->bio = b;
@@ -204,6 +199,7 @@ badxref: · 71 unmodified lines
204 199
205 return pdf;200 return pdf;
206err:201err:
202 werrstr("pdfopen: %r [at %p]", (void*)Boffset(b));
207 pdfclose(pdf);203 pdfclose(pdf);
208 return nil;204 return nil;
209}205}
pdf.h+12−38
@@ -20,8 +20,11 @@ struct Object { · 19 unmodified lines
20 union {20 union {
21 int bool;21 int bool;
22 double num;22 double num;
23 char *str;23 struct {
24 char *name;24 int len;
25 char str[1];
26 };
27 char name[1];
25 28
26 struct {29 struct {
27 u32int id;30 u32int id;
@@ -50,8 +53,8 @@ struct Pdf { · 22 unmodified lines
50 Xref *xref;53 Xref *xref;
51 int nxref; /* 7.5.4 xref subsection number of objects */54 int nxref; /* 7.5.4 xref subsection number of objects */
52 55
53 Object *root; /* 7.5.5 root object */56 Object *root; /* 7.7.2 root object */
54 Object *info; /* 7.5.5 info dictionary */57 Object *info; /* 14.3.3 info dictionary */
55};58};
56 59
57struct Xref {60struct Xref {
@@ -63,45 +66,16 @@ struct Xref { · 5 unmodified lines
63Pdf *pdfopen(int fd);66Pdf *pdfopen(int fd);
64void pdfclose(Pdf *pdf);67void pdfclose(Pdf *pdf);
65 68
66/*69Object *pdfobject(void *b);
67 * General function to parse an object of any type.
68 */
69Object *pdfobject(char *p, char **e, int len);
70
71void freeobject(Object *o);70void freeobject(Object *o);
72 71
73/*
74 * 7.3.4 String Objects
75 *
76 * Rewrites the string in place with null termination and returns the
77 * length in bytes, without the null terminator.
78 * Returns < 0 if parsing failed.
79 * (*e) is advanced to the position after the string pointed by (p).
80 */
81int pdfstring(char *p, char **e, int len);
82
83/*
84 * 7.3.5 Name Objects
85 *
86 * Works the same way as pdfstring, but for name objects.
87 */
88int pdfname(char *p, char **e, int len);
89
90/*
91 * 7.3.6 Array Objects
92 */
93Object *pdfarray(char *p, char **e, int len);
94
95/*
96 * 7.3.7 Dictionary Objects
97 */
98Object *pdfdict(char *p, char **e, int len);
99
100/*72/*
101 * If the object is indirect, resolve it. Operation is not recursive, ie73 * If the object is indirect, resolve it. Operation is not recursive, ie
102 * values of a dictionary won't be resolved automatically.74 * values of a dictionary won't be resolved automatically.
103 */75 */
104int pdfeval(Pdf *pdf, Object *o);76int pdfeval(Pdf *pdf, Object *o);
105 77
106int isws(char c);78int isws(int c);
107int isdelim(char c);79int isdelim(int c);
80
81Object *pdfdictget(Object *o, char *name);
string.c+163−128
@@ -1,8 +1,11 @@
1#include <u.h>1#include <u.h>
2#include <libc.h>2#include <libc.h>
3#include <bio.h>
3#include "pdf.h"4#include "pdf.h"
4 5
5static int esc[] = {6/* 7.3.4 String Objects */
7
8static char esc[] = {
6 ['n'] = '\n',9 ['n'] = '\n',
7 ['r'] = '\r',10 ['r'] = '\r',
8 ['t'] = '\t',11 ['t'] = '\t',
@@ -14,175 +17,207 @@ static int esc[] = { · 5 unmodified lines
14 ['\n'] = -1,17 ['\n'] = -1,
15};18};
16 19
17static int20static Object *
18pdfstringhex(char *p, char **e, int len)21stringhex(Biobuf *b)
19{22{
20 int i;23 char *s;
24 Object *o;
25 int len, n;
21 26
22 for(i = 1; i < len; i += 1){27 if((s = Brdstr(b, '>', 0)) == nil)
23 if(p[i] == '>')28 return nil;
24 break;29 len = Blinelen(b) - 1;
30 if(s[len] != '>'){
31 werrstr("no '>'");
32 free(s);
33 return nil;
25 }34 }
26 if(i >= len){35 s[len] = '0'; /* the final zero may be missing */
27 werrstr("hex not closed");36 n = len/2;
28 return -1;37 o = nil;
29 }38 if(dec16((uchar*)s, n, s+1, len) != n){
30 p[i] = '0'; /* the final zero may be missing */
31 *e = p+i+1;
32 i = dec16((uchar*)p, i/2, p+1, i) == i/2 ? i/2 : -1;
33 if(i < 0)
34 werrstr("invalid hex");39 werrstr("invalid hex");
35 p[i] = 0;40 }else if((o = malloc(sizeof(*o) + n + 1)) != nil){
36 return i;41 memmove(o->str, s, n);
42 o->str[n] = 0;
43 o->len = n;
44 o->type = Ostr;
45 }
46
47 free(s);
48 return o;
37}49}
38 50
39int51Object *
40pdfstring(char *p, char **e, int len)52pdfstring(Biobuf *b)
41{53{
42 Rune r;54 Object *o;
43 int c, j, i, o, n, paren;55 char *s, *r;
44 char oct[4];56 char oct[4];
57 int i, c, paren, sz, maxsz;
45 58
46 if(len < 2){59 maxsz = 64;
47 werrstr("too short");60 if((s = malloc(maxsz)) == nil)
48 goto err;61 return nil;
49 }
50 62
51 paren = 0;63 for(paren = sz = 0;;){
52 for(i = o = 0; i < len;){64 if((c = Bgetc(b)) <= 0)
53 if((n = chartorune(&r, p+i)) == 1 && r == Runeerror){65 break;
54 werrstr("rune error at byte %d", n);
55 return -1;
56 }
57 66
58 if(i == 0){67 switch(c){
59 if(r == '('){68 case '<':
60 paren = 1;69 if(sz == 0){
61 i++;70 Bungetc(b);
62 continue;71 return stringhex(b);
63 }
64 if(r == '<'){
65 len = pdfstringhex(p, e, len);
66 if(len < 0)
67 goto err;
68 return len;
69 }72 }
70 werrstr("invalid first char");73 break;
71 goto err;
72 }
73 74
74 if(r == '\\'){75 case '(':
75 if(++i >= len){
76 werrstr("escaped char out of string len");
77 goto err;
78 }
79 if((n = chartorune(&r, p+i)) == 1 && r == Runeerror){
80 werrstr("rune error at byte %d", i);
81 goto err;
82 }
83 if(r >= '0' && r <= '9'){ /* octal */
84 n = 0;
85 for(j = 0; j < 3 && i < len && p[i] >= '0' && p[i] <= '9'; j++, i++)
86 oct[j] = p[i];
87 oct[j] = 0;
88 c = strtol(oct, nil, 8);
89 }else if(r >= nelem(esc) || (c = esc[r]) == 0){
90 werrstr("unknown escape char at byte %d", i);
91 goto err;
92 }
93 r = c;
94 i += n;
95 if(c < 0)
96 continue;
97 }else if(r == '('){
98 paren++;76 paren++;
99 i++;
100 continue;77 continue;
101 }else if(r == ')'){78
79 case ')':
102 paren--;80 paren--;
103 i++;81 if(paren < 1){
104 if(paren == 0)82 c = 0;
105 break;83 break;
84 }
106 continue;85 continue;
107 }else{86
108 i += n;87 case '\\':
88 if((c = Bgetc(b)) <= 0)
89 break;
90 if(c >= '0' && c <= '7'){ /* octal */
91 oct[0] = c;
92 for(i = 1; i < 3 && (c = Bgetc(b)) >= '0' && c <= '7'; i++)
93 oct[i] = c;
94 if(c <= 0)
95 break;
96 if(c < '0' || c > '7')
97 Bungetc(b);
98 oct[i] = 0;
99 c = strtol(oct, nil, 8);
100 }else if(c >= nelem(esc) || (c = esc[c]) == 0){
101 werrstr("unknown escape char %c", c);
102 goto err;
103 }else if(c < 0){
104 continue;
105 }
106 break;
107
108 default:
109 if(paren < 1){
110 werrstr("unexpected char '%c'", c);
111 goto err;
112 }
113 break;
109 }114 }
110 115
111 o += runetochar(p+o, &r);116 if(c <= 0)
117 break;
118 if(sz+1 > maxsz){
119 maxsz *= 2;
120 if((r = realloc(s, maxsz)) == nil)
121 goto err;
122 s = r;
123 }
124 s[sz++] = c;
112 }125 }
113 126
114 if(paren > 0){127 if(paren != 0){
115 werrstr("non-closed paren");128 werrstr("bad paren");
129 goto err;
130 }
131 if(c < 0){
132 werrstr("short");
116 goto err;133 goto err;
117 }134 }
118 135
119 p[o] = 0;136 if(c >= 0 && (o = malloc(sizeof(*o) + sz + 1)) != nil){
120 *e = p + i;137 memmove(o->str, s, sz);
138 o->str[sz] = 0;
139 o->len = sz;
140 o->type = Ostr;
141 free(s);
142 return o;
143 }
121 144
122 return o;
123err:145err:
146 free(s);
124 werrstr("string: %r");147 werrstr("string: %r");
125 return -1;148 return nil;
126}149}
127 150
128#ifdef TEST151#ifdef TEST
129static struct {152static struct {
130 char *i;153 char *in;
131 int len;154 char *out;
132 char *o;
133 int r;
134 int e;
135}t[] = {155}t[] = {
136 {"(simple string)", 15, "simple string", 13},156 {"", nil},
137 {"(non-closed paren", 17, nil, -1},157 {"(test, success)", "test, success"},
138 {"wrong first char", 16, nil, -1},158 {"(simple string)", "simple string"},
139 {"(parens((()((())))()))", 22, "parens", 6},159 {"(non-closed paren", nil},
140 {"(\\0053)", 7, "\x053", 2},160 {"wrong first char", nil},
141 {"(\\053)", 6, "+", 1},161 {"(parens((()((())))()))", "parens"},
142 {"(\\53)", 5, "+", 1},162 {"(\\0053)", "\x053"},
143 {"()", 2, "", 0},163 {"(\\053)", "+"},
144 {")", 1, nil, -1},164 {"(\\53)", "+"},
145 {"(\\)\\()", 6, ")(", 2},165 {"()", ""},
146 {"(\\\\)", 4, "\\", 1},166 {")", nil},
147 {"a", 1, nil, -1},167 {"(\\)\\()", ")("},
148 {"(1\\\n2)", 6, "12", 2},168 {"(\\\\)", "\\"},
149 {"<323130>", 8, "210", 3},169 {"a", nil},
150 {"<32313>", 7, "210", 3},170 {"(1\\\n2)", "12"},
151 {"<>", 2, "", 0},171 {"<323130>", "210"},
152 {"<", 1, nil, -1},172 {"<32313>", "210"},
153 {"<zz>", 4, nil, -1},173 {"<>", ""},
174 {"<", nil},
175 {"<zz>", nil},
176 {">", nil},
154};177};
155 178
179static char *s;
180static int off, n;
181
182static int
183rd(Biobufhdr *, void *data, long sz)
184{
185 if(sz > n-off)
186 sz = n-off;
187 memmove(data, s+off, sz);
188 off += sz;
189 return sz;
190}
191
156void192void
157test_pdfstring(void)193test_pdfstring(void)
158{194{
159 char *e;195 Object *o;
160 int i, r;196 Biobuf b;
197 int i;
161 198
162 fprint(2, "pdfstring\n");199 fprint(2, "pdfstring\n");
163 for(i = 0; i < nelem(t); i++){200 for(i = 0; i < nelem(t); i++){
201 s = t[i].in;
202 n = strlen(s);
203 off = 0;
204 Binit(&b, -1, OREAD);
205 Biofn(&b, rd);
206
164 fprint(2, "\t%d: ", i);207 fprint(2, "\t%d: ", i);
165 r = pdfstring(t[i].i, &e, t[i].len);208 o = pdfstring(&b);
166 if(r != t[i].r){209 if(o == nil && t[i].out != nil)
167 fprint(2, "expected r=%d, got %d", t[i].r, r);210 fprint(2, "ERROR: expected %q, got error: %r\n", t[i].out);
168 if(r < 0)211 else if(o != nil && t[i].out == nil)
169 fprint(2, " (%r)\n");212 fprint(2, "ERROR: expected error, got %q\n", o->str);
170 else213 else if(o == nil && t[i].out == nil)
171 fprint(2, "\n");214 fprint(2, "OK (%r)\n");
172 continue;215 else if(strcmp(o->str, t[i].out) != 0)
173 }else if(r >= 0){216 fprint(2, "ERROR: expected %q, got %q\n", t[i].out, o->str);
174 if(t[i].i+t[i].len != e){217 else
175 fprint(2, "expected e=%p, got %p\n", t[i].i+t[i].len, e);218 fprint(2, "OK\n");
176 continue;219 freeobject(o);
177 }else if(strcmp(t[i].o, t[i].i) != 0){220 Bterm(&b);
178 fprint(2, "expected %q, got %q\n", t[i].o, t[i].i);
179 continue;
180 }
181 }
182 fprint(2, "OK");
183 if(r < 0)
184 fprint(2, " (%r)");
185 fprint(2, "\n");
186 }221 }
187}222}
188#endif223#endif

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t