History

Significantly improved text output

Noam Preil committed 5 years ago

Commit b574ea61 parent 8a7f9b45 files · +508 −162Browse files

Unified · Split

5 files
  1. main.c +19 −9
  2. misc.c +1 −0
  3. object.c +74 −40
  4. op.c +363 −102
  5. pdf.h +51 −11
main.c+19−9
@@ -17,17 +17,21 @@ usage(void) · 16 unmodified lines
1717 threadexitsall("usage");
1818 }
1919
20−static void
20+static int
2121 dumppage(Object *page)
2222 {
23+ int ret;
2324 Page p;
24− pageinit(&p);
25− if(pagerender(&p, page) && p.buf.sz != 0)
26− write(1, p.buf.b, p.buf.sz);
25+ pageinit(&p, page);
26+ ret = pagerender(&p);
27+ if(ret)
28+ if(p.buf.sz != 0)
29+ fprint(1, "%s", (char*)p.buf.b);
2730 pagefree(&p);
31+ return ret;
2832 }
2933
30−static void
34+static int
3135 dumppages(Object *pages)
3236 {
3337 Object *page, *kids, *type;
@@ -39,13 +43,19 @@ dumppages(Object *pages) · 5 unmodified lines
3943 // Must be a dict, either Page or Pages
4044 type = dictget(page, "Type");
4145 // MUST be a name.
42− if(strcmp(type->name, "Pages") == 0)
43− dumppages(page);
44− else if(strcmp(type->name, "Page") == 0)
45− dumppage(page);
46+ if(strcmp(type->name, "Pages") == 0){
47+ if(!dumppages(page))
48+ return 0;
49+ }
50+ else if(strcmp(type->name, "Page") == 0){
51+ if(!dumppage(page))
52+ return 0;
53+ print("\n");
54+ }
4655 else
4756 sysfatal("Unexpected page node type '%s'", type->name);
4857 }
58+ return 1;
4959 }
5060
5161
misc.c+1−0
@@ -50,6 +50,7 @@ Ofmt(Fmt *f) · 49 unmodified lines
5050 return fmtprint(f, "%g", o->num.d);
5151
5252 case Ostr:
53+ case Oop:
5354 if(isutf8(o->str, o->len))
5455 return fmtprint(f, "%q", o->str);
5556 return fmtprint(f, "<%.*H>", o->len, o->str);
object.c+74−40
@@ -8,21 +8,90 @@ Object *pdfname(Stream *s); · 7 unmodified lines
88 Object *pdfarray(Pdf *pdf, Stream *s);
99 Object *pdfdict(Pdf *pdf, Stream *s);
1010
11+/* returns 1 if str is at the beginning of the stream, and
12+ is followed either by whitespace or, if delim is 1,
13+ a delimiter.
14+ strlen(str) must be in (0, 16)
15+ on match, the stream seeks to right after the string.
16+ otherwise, the stream position is unchanged. */
17+static int
18+sismatch(Stream *s, char *str, int delim)
19+{
20+ long len = strlen(str);
21+ vlong off = Soffset(s);
22+ char b[16];
23+ if(len == 0 || len > 16)
24+ return 0;
25+ if(Sread(s, b, len + 1) == len + 1 && memcmp(b, str, len) == 0 && (isws(b[len]) || (delim && isdelim(b[len])))){
26+ Sungetc(s);
27+ return 1;
28+ }
29+
30+ Sseek(s, off, 0);
31+ return 0;
32+}
33+
34+char *
35+suntilend(Stream *s)
36+{
37+ int sz, c, full = 0;
38+ char buf[8];
39+ for(sz = 0; sz < 7;){
40+ c = Sgetc(s);
41+ if(c < 0)
42+ break;
43+ if(isws(c) || isdelim(c)){
44+ Sungetc(s);
45+ full = 1;
46+ break;
47+ }
48+ buf[sz] = c;
49+ sz += 1;
50+ }
51+ if(!full)
52+ return nil;
53+ buf[sz] = 0;
54+ return strdup(buf);
55+}
56+
1157 /* General function to parse an object of any type. */
1258 Object *
1359 pdfobj(Pdf *pdf, Stream *s)
1460 {
1561 Object *o, *o2;
1662 vlong off;
17− int c, tf;
63+ int c;
1864 Xref xref;
19− char b[16];
2065
2166 o = o2 = nil;
2267 do; while(isws(c = Sgetc(s)));
2368 if(c < 0)
2469 goto err;
2570
71+ if(isascii(c) && isalpha(c)){
72+ Sungetc(s);
73+ // bool, null, or op
74+ if(sismatch(s, "null", 1)){
75+ fprint(1, "NULL\n");
76+ return &null;
77+ }
78+ if((o = calloc(1, sizeof(*o))) == nil)
79+ goto err;
80+ o->type = Obool;
81+ o->pdf = pdf;
82+ if(sismatch(s, "true", 1)){
83+ o->bool = 1;
84+ return o;
85+ }
86+ if(sismatch(s, "false", 1)){
87+ o->bool = 0;
88+ return o;
89+ }
90+ o->type = Oop;
91+ o->str = suntilend(s);
92+ return o;
93+ }
94+
2695 switch(c){
2796 case '<': /* dictionary or a string */
2897 c = Sgetc(s);
@@ -33,7 +102,8 @@ pdfobj(Pdf *pdf, Stream *s) · 4 unmodified lines
33102 off = Soffset(s);
34103 do; while(isws(Sgetc(s)));
35104 Sungetc(s);
36− if(Sread(s, b, 7) == 7 && memcmp(b, "stream", 6) == 0 && isws(c = b[6])){
105+ if(sismatch(s, "stream", 0)){
106+ c = Sgetc(s);
37107 /* there IS a stream */
38108 if(c == '\r' && (c = Sgetc(s)) < 0)
39109 goto err;
@@ -71,45 +141,8 @@ pdfobj(Pdf *pdf, Stream *s) · 31 unmodified lines
71141 o->pdf = pdf;
72142 return o;
73143
74− case 'n':
75− off = Soffset(s);
76− if(Sgetc(s) == 'u' && Sgetc(s) == 'l' && Sgetc(s) == 'l' && (isws(c = Sgetc(s)) || isdelim(c))){
77− Sungetc(s);
78− return &null;
79− }
80− Sseek(s, off, 0);
81− c = 'n';
82− goto unexpected;
83−
84− case 't':
85− off = Soffset(s);
86− tf = 1;
87− if(Sgetc(s) == 'r' && Sgetc(s) == 'u' && Sgetc(s) == 'e' && (isws(c = Sgetc(s)) || isdelim(c)))
88− goto bool;
89− Sseek(s, off, 0);
90− c = 't';
91− goto unexpected;
92−
93− case 'f':
94− off = Soffset(s);
95− tf = 0;
96− if(Sgetc(s) == 'a' && Sgetc(s) == 'l' && Sgetc(s) == 's' && Sgetc(s) == 'e' && (isws(c = Sgetc(s)) || isdelim(c)))
97− goto bool;
98− Sseek(s, off, 0);
99− c = 'f';
100− goto unexpected;
101−bool:
102− Sungetc(s);
103− if((o = calloc(1, sizeof(*o))) == nil)
104− goto err;
105− o->type = Obool;
106− o->pdf = pdf;
107− o->bool = tf;
108− return o;
109−
110144 default:
111145 if(!isdigit(c) && c != '-'){
112−unexpected:
113146 Sungetc(s);
114147 werrstr("unexpected char '%c' at %#x+%#x (%d left)", c, Sobjoffset(s), Soffset(s), Ssize(s));
115148 goto err;
@@ -178,6 +211,7 @@ pdfobjfree(Object *o) · 62 unmodified lines
178211 return;
179212
180213 case Ostr:
214+ case Oop:
181215 case Oname:
182216 free(o->str);
183217 break;
op.c+363−102
@@ -12,6 +12,30 @@ enum { · 11 unmodified lines
1212
1313 typedef struct Op Op;
1414
15+static void
16+matidentity(double *arr)
17+{
18+ double src[6] = {
19+ 1, 0,
20+ 0, 1,
21+ 0, 0
22+ };
23+ memcpy(arr, src, sizeof(double) * 6);
24+}
25+
26+static void
27+matmult(double *m1, double *m2, double *out)
28+{
29+ double result[6];
30+ result[0] = m1[0] * m2[0] + m1[1] * m2[2];
31+ result[1] = m1[0] * m2[1] + m1[1] * m2[3];
32+ result[2] = m1[2] * m2[0] + m1[3] * m2[2];
33+ result[3] = m1[2] * m2[1] + m1[3] * m2[3];
34+ result[4] = m1[4] * m2[0] + m1[5] * m2[2] + m2[4];
35+ result[5] = m1[4] * m2[1] + m1[5] * m2[3] + m2[5];
36+ memcpy(out, result, sizeof(double) * 6);
37+}
38+
1539 struct Op {
1640 char *s;
1741 int (*f)(Op *op, Page *p);
@@ -19,6 +43,16 @@ struct Op { · 1 unmodified line
1943 int flags;
2044 };
2145
46+static int
47+flagless(Op *op)
48+{
49+ if(op->flags != 0){
50+ fprint(2, "Op '%s' expected no flags\n", op->s);
51+ return 0;
52+ }
53+ return 1;
54+}
55+
2256 static int
2357 cobegin(Op *op, Page *p)
2458 {
@@ -36,29 +70,54 @@ coend(Op *op, Page *p) · 11 unmodified lines
3670 static int
3771 gspush(Op *op, Page *p)
3872 {
39− USED(op, p);
40− return 0;
73+ USED(op);
74+ GS *r = realloc(p->GS, sizeof(GS) * (p->nGS + 1));
75+ if(r == nil)
76+ return 0;
77+ p->GS = r;
78+ p->nGS += 1;
79+ p->GSactive = &p->GS[p->nGS - 1];
80+ *(p->GSactive) = p->GS[p->nGS - 2];
81+ return 1;
4182 }
4283
4384 static int
4485 gspop(Op *op, Page *p)
4586 {
46− USED(op, p);
47− return 0;
87+ USED(op);
88+ GS *r = realloc(p->GS, sizeof(GS) * (p->nGS - 1));
89+ if(r == nil)
90+ return 0;
91+ p->GS = r;
92+ p->nGS -= 1;
93+ p->GSactive = &p->GS[p->nGS - 1];
94+ return 1;
4895 }
4996
97+/* six parameters give the inputs a,b,c,d,e,f for the matrix
98+ [a b 0]
99+ [c d 0]
100+ [e f 1]
101+ That matrix should be premultiplied with the current matrix
102+ newCTM = input x oldCTM
103+ (8.3.4)
104+ */
50105 static int
51106 gsctm(Op *op, Page *p)
52107 {
53− USED(op, p);
54− return 0;
108+ double input[6];
109+ int i;
110+ for(i = 0; i < 6; i += 1)
111+ input[i] = arrayget(p->stack, i)->num.d;
112+ matmult(input, p->GSactive->CTM, p->GSactive->CTM);
113+ return flagless(op);
55114 }
56115
57116 static int
58117 gswidth(Op *op, Page *p)
59118 {
60− USED(op, p);
61− return 0;
119+ p->GSactive->LW = arrayget(p->stack, 0)->num.i;
120+ return flagless(op);
62121 }
63122
64123 static int
@@ -99,8 +158,8 @@ gsintent(Op *op, Page *p) · 34 unmodified lines
99158 static int
100159 gsflatness(Op *op, Page *p)
101160 {
102− USED(op, p);
103− return 0;
161+ p->GSactive->FL = arrayget(p->stack, 0)->num.d;
162+ return flagless(op);
104163 }
105164
106165 static int
@@ -114,21 +173,21 @@ static int · 7 unmodified lines
114173 pcmove(Op *op, Page *p)
115174 {
116175 USED(op, p);
117− return 0;
176+ return 1;
118177 }
119178
120179 static int
121180 pcline(Op *op, Page *p)
122181 {
123182 USED(op, p);
124− return 0;
183+ return 1;
125184 }
126185
127186 static int
128187 pccurve(Op *op, Page *p)
129188 {
130189 USED(op, p);
131− return 0;
190+ return 1;
132191 }
133192
134193 static int
@@ -149,7 +208,7 @@ static int · 14 unmodified lines
149208 ppstroke(Op *op, Page *p)
150209 {
151210 USED(op, p);
152− return 0;
211+ return 1;
153212 }
154213
155214 static int
@@ -218,8 +277,21 @@ ccolour2(Op *op, Page *p) · 62 unmodified lines
218277 static int
219278 cgray(Op *op, Page *p)
220279 {
221− USED(op, p);
222− return 0;
280+ int value = 255 * arrayget(p->stack, 0)->num.d;
281+ int i;
282+ u32int *color;
283+ if(op->flags & Nonstroking){
284+ color = &p->GSactive->NSC;
285+ p->GSactive->NSCS = DeviceGray;
286+ } else{
287+ color = &p->GSactive->SC;
288+ p->GSactive->SCS = DeviceGray;
289+ }
290+ *color = 0;
291+ for(i = 0; i < 3; i += 1)
292+ *color = (*color | value) << 8;
293+ *color |= 255;
294+ return 1;
223295 }
224296
225297 static int
@@ -295,21 +367,51 @@ tshscale(Op *op, Page *p) · 69 unmodified lines
295367 static int
296368 tslead(Op *op, Page *p)
297369 {
298− int d = arrayget(p->stack, 0)->num.d / 20;
299− while(d > 0){
300− d -= 1;
301− if(bufput(&p->buf, (uchar*)"\n", 1) == -1)
302− sysfatal("OOM");
370+ p->TS.TL = arrayget(p->stack, 0)->num.d;
371+ return flagless(op);
372+}
373+
374+static int
375+fontwidths(Page *p)
376+{
377+ Object *o;
378+ int i;
379+ if(p->GSactive->Font.widths != nil)
380+ free(p->GSactive->Font.widths);
381+ o = dictget(p->GSactive->Font.font, "FirstChar");
382+ if(o == nil)
383+ return 1;
384+ p->GSactive->Font.first = o->num.i;
385+ p->GSactive->Font.last = dictget(p->GSactive->Font.font, "LastChar")->num.i;
386+ p->GSactive->Font.widths = malloc(sizeof(int) * (p->GSactive->Font.last - p->GSactive->Font.first + 1));
387+ if(p->GSactive->Font.widths == nil){
388+ print("Failed to allocate for (%d, %d): %d\n", p->GSactive->Font.first, p->GSactive->Font.last, p->GSactive->Font.last - p->GSactive->Font.first + 1);
389+ return 1;
303390 }
304− USED(op, p);
305− return 0;
391+ o = dictget(p->GSactive->Font.font, "Widths");
392+ if(o == nil)
393+ return 0;
394+ for(i = 0; i < arraylen(o); i += 1)
395+ p->GSactive->Font.widths[i] = arrayget(o, i)->num.i;
396+ o = dictget(p->GSactive->Font.font, "FontDescriptor");
397+ p->GSactive->Font.defwidth = dictget(o, "MissingWidth")->num.i;
398+ return 1;
306399 }
307400
308401 static int
309402 tsfontsz(Op *op, Page *p)
310403 {
311− USED(op, p);
312− return 0;
404+ char *name = arrayget(p->stack, 0)->name;
405+ p->GSactive->Font.font = dictget(dictget(dictget(p->obj, "Resources"), "Font"), name);
406+ if(p->GSactive->Font.font == nil){
407+ werrstr("Font not found: '%s'", name);
408+ return 0;
409+ }
410+ p->GSactive->Font.enc = dictget(p->GSactive->Font.font, "Encoding");
411+ if(p->GSactive->Font.enc)
412+ p->GSactive->Font.enc = dictget(p->GSactive->Font.enc, "Differences");
413+ p->GSactive->Font.size = arrayget(p->stack, 1)->num.d;
414+ return fontwidths(p) && flagless(op);
313415 }
314416
315417 static int
@@ -329,15 +431,39 @@ tsrise(Op *op, Page *p) · 13 unmodified lines
329431 static int
330432 tobegin(Op *op, Page *p)
331433 {
332− USED(op, p);
333− return 0;
434+ if(p->TS.inobj){
435+ werrstr("Text objects must not be nested");
436+ return 0;
437+ }
438+ matidentity(p->TS.Tm);
439+ matidentity(p->TS.Tlm);
440+ p->TS.inobj = 1;
441+ p->GSactive->Font.font = nil;
442+ return flagless(op);
334443 }
335444
336445 static int
337446 toend(Op *op, Page *p)
338447 {
339− USED(op, p);
340− return 0;
448+ if(!p->TS.inobj){
449+ werrstr("ET found without BT");
450+ return 0;
451+ }
452+ p->TS.inobj = 0;
453+ return flagless(op);
454+}
455+
456+static int
457+tmove(Page *p, double x, double y, int tlm)
458+{
459+ double shift[6] = {1, 0, 0, 1, x, y};
460+ if(tlm){
461+ matmult(shift, p->TS.Tlm, p->TS.Tlm);
462+ memcpy(p->TS.Tm, p->TS.Tlm, sizeof(double) * 6);
463+ } else{
464+ matmult(shift, p->TS.Tm, p->TS.Tm);
465+ }
466+ return 1;
341467 }
342468
343469 static int
@@ -346,62 +472,141 @@ tpmove(Op *op, Page *p) · 2 unmodified lines
346472 Object *x, *y;
347473 x = arrayget(p->stack, 0);
348474 y = arrayget(p->stack, 1);
349− if(y->num.d != 0){
350− if(bufput(&p->buf, (uchar*)"\n", 1) == -1)
351− sysfatal("OOM");
352− }
353− else if(x->num.d < 50)
354− if(bufput(&p->buf, (uchar*)" ", 1) == -1)
355− sysfatal("OOM");
356− USED(op, p);
357− return 0;
475+ if(op->flags & Leading)
476+ p->TS.TL = -y->num.d;
477+ return tmove(p, x->num.d, y->num.d, 1);
358478 }
359479
360480 static int
361481 tpmatrix(Op *op, Page *p)
362482 {
363− USED(op, p);
364− return 0;
483+ int i;
484+ for(i = 0; i < 6; i += 1){
485+ p->TS.Tm[i] = arrayget(p->stack, i)->num.d;
486+ p->TS.Tlm[i] = p->TS.Tm[i];
487+ }
488+ return flagless(op);
365489 }
366490
367491 static int
368492 tpmove0(Op *op, Page *p)
369493 {
370− USED(op, p);
371− if(bufput(&p->buf, (uchar*)"\n", 1) == -1)
372− sysfatal("OOM");
373− return 0;
494+ return tmove(p, 0, 0 - p->TS.TL, 1) && flagless(op);
495+}
496+
497+static int
498+writepatched(Page *p, uchar c)
499+{
500+ int i, len, d = 0;
501+ Object *o;
502+ if(p->GSactive->Font.enc != nil){
503+ len = arraylen(p->GSactive->Font.enc);
504+ for(i = 0; i < len; i += 1){
505+ o = arrayget(p->GSactive->Font.enc, i);
506+ if(o->type == Onum)
507+ d = o->num.i;
508+ else if(d == c){
509+ if(strcmp(o->name, "endash") == 0)
510+ return bufput(&p->buf, (uchar*)"-", 1) == 1;
511+ if(strcmp(o->name, "fi") == 0)
512+ return bufput(&p->buf, (uchar*)"fi", 2) == 2;
513+ if(strcmp(o->name, "ff") == 0)
514+ return bufput(&p->buf, (uchar*)"ff", 2) == 2;
515+ if(strcmp(o->name, "ffi") == 0)
516+ return bufput(&p->buf, (uchar*)"ffi", 3) == 3;
517+ if(strcmp(o->name, "bullet") == 0)
518+ return bufput(&p->buf, (uchar*)"•", strlen("•")) == 3;
519+ if(strcmp(o->name, "quotedblleft") == 0)
520+ return bufput(&p->buf, (uchar*)"\"", 1) == 1;
521+ if(strcmp(o->name, "quotedblright") == 0)
522+ return bufput(&p->buf, (uchar*)"\"", 1) == 1;
523+ if(strcmp(o->name, "quoteleft") == 0)
524+ return bufput(&p->buf, (uchar*)"'", 1) == 1;
525+ if(strcmp(o->name, "quoteright") == 0)
526+ return bufput(&p->buf, (uchar*)"'", 1) == 1;
527+ fprint(2, "TODO: recognize glyph name '%s'\n", o->name);
528+ return 1;
529+ } else
530+ d += 1;
531+ }
532+ }
533+ return bufput(&p->buf, (uchar*)&c, 1) == 1;
534+}
535+
536+/* Renders one character / glyph and updates the text state */
537+static int
538+tchar(Page *p, ulong c)
539+{
540+ double Trm[6] = {p->GSactive->Font.size, 0, 0, p->GSactive->Font.size, 0, 0};
541+ double tx;
542+ int i;
543+ matmult(Trm, p->TS.Tm, Trm);
544+ matmult(Trm, p->GSactive->CTM, Trm);
545+ tx = p->GSactive->Font.size / 1000;
546+ if(c >= p->GSactive->Font.first && c <= p->GSactive->Font.last)
547+ tx = tx * (double)p->GSactive->Font.widths[c - p->GSactive->Font.first];
548+ else
549+ tx = tx * (double)p->GSactive->Font.defwidth;
550+ // Check if whitespace is needed
551+ if(p->buf.sz > 1){
552+ if(p->TS.y != Trm[5]){
553+ for(i = 0; i < (int)((p->TS.y - Trm[5]) / p->GSactive->Font.size); i += 1)
554+ if(bufput(&p->buf, (uchar*)"\n", 1) != 1)
555+ return 0;
556+ }
557+ if(Trm[4] - p->TS.x > 2.5){
558+ if(bufput(&p->buf, (uchar*)" ", 1) != 1)
559+ return 0;
560+ }
561+ }
562+ if(!writepatched(p, c) || !tmove(p, tx, 0, 0))
563+ return 0;
564+ p->TS.x = Trm[4] + tx;
565+ p->TS.y = Trm[5];
566+ return 1;
567+}
568+
569+static int
570+tstr(Page *p, char *str, ulong len)
571+{
572+ ulong i;
573+ for(i = 0; i < len; i += 1)
574+ if(!tchar(p, str[i]))
575+ return 0;
576+ return 1;
374577 }
375578
376579 static int
377580 thshow(Op *op, Page *p)
378581 {
582+ if(op->flags != 0){
583+ fprint(2, "TODO: thshow != Tj\n");
584+ return 0;
585+ }
379586 Object *o = arrayget(p->stack, 0);
380− if(bufput(&p->buf, (uchar*)o->str, o->len) == -1)
381− sysfatal("OOM");
382− USED(op);
383− return 0;
587+ if(!tstr(p, o->str, o->len))
588+ return 0;
589+ return 1;
384590 }
385591
386592 static int
387593 thshowarr(Op *op, Page *p)
388594 {
389− Object *arr = arrayget(p->stack, 0);
390− Object *o;
595+ Object *o, *arr = arrayget(p->stack, 0);
391596 int i;
392597 for(i = 0; i < arraylen(arr); i += 1){
393598 o = arrayget(arr, i);
394599 if(o->type == Ostr){
395− if(bufput(&p->buf, (uchar*)o->str, o->len) == -1)
396− sysfatal("OOM");
600+ if(!tstr(p, o->str, o->len))
601+ return 0;
397602 }
398− else if(o->num.d < -150){
399− if(bufput(&p->buf, (uchar*)" ", 1) == -1)
400− sysfatal("OOM");
603+ else{
604+ double shift = 0 - (p->GSactive->Font.size * o->num.d / 1000);
605+ if(!tmove(p, shift, 0, 0))
606+ return 0;
401607 }
402608 }
403− USED(op);
404− return 0;
609+ return flagless(op);
405610 }
406611
407612 static int
@@ -746,7 +951,7 @@ static int · 338 unmodified lines
746951 opignore(Op *op, Page *p)
747952 {
748953 USED(op, p);
749− return 1;
954+ return 0;
750955 }
751956
752957 static Op ops[] = {
@@ -833,8 +1038,8 @@ static Op ops[] = { · 80 unmodified lines
8331038 /* 9.4.2 Text position operators */
8341039 {"Td", tpmove, 2,}, /* move, next line */
8351040 {"TD", tpmove, 2, Leading,}, /* move, next line, leading */
836− {"Tm", tpmatrix, 6,}, /* (line) matrix */
837− {"T*", tpmove0, 0, Leading,}, /* move, next line, leading */
1041+ {"Tm", tpmatrix, 6,}, /* set Tm and Tlm */
1042+ {"T*", tpmove0, 0,}, /* move, next line, leading */
8381043
8391044 /* 9.4.3 Text showing operators */
8401045 {"Tj", thshow, 1,}, /* show string */
@@ -902,56 +1107,92 @@ static Op ops[] = { · 61 unmodified lines
9021107 {nil, nil, 0,},
9031108 };
9041109
905−// If an op is found at the current position in the stream, the associated Op is
906−// returned and the stream is advanced. Otherwise, nil is returned and the stream
907−// is left unchanged.
9081110 Op *
909−opfind(Stream *s)
1111+opfind(char *name)
9101112 {
911− int i;
912− uint len;
1113+ int i = 0;
9131114 Op *op;
914− char *b = (char*)s->buf.b + s->buf.off;
915− i = 0;
9161115 while(ops[i].s != nil){
9171116 op = &ops[i];
918− len = strlen(op->s);
919− if(strncmp(op->s, b, len) == 0 && (isws(b[len]) || isdelim(b[len]))){
920− s->buf.off += len;
1117+ if(strcmp(op->s, name) == 0)
9211118 return op;
922− }
9231119 i += 1;
9241120 }
9251121 return nil;
9261122 }
9271123
9281124 void
929−pageinit(Page *page)
1125+pageinit(Page *page, Object *o)
9301126 {
9311127 bufinit(&page->buf, 0, 0);
9321128 // Stack is per-content-stream, so we don't create it here
9331129 page->stack = nil;
1130+ page->obj = o;
1131+ page->TS.inobj = 0;
1132+ page->TS.x = 0;
1133+ page->TS.y = 0;
9341134 }
9351135
9361136 void
937−pagefree(Page *p)
1137+gsinit(Page *p, GS *gs)
1138+{
1139+ USED(p);
1140+ /* todo: actually initialize the full state */
1141+ /* CTM maps user coords to device coords.
1142+ TODO: use mediabox and screen info to init CTM
1143+ */
1144+ matidentity(gs->CTM);
1145+ gs->LW = 1;
1146+ gs->LC = 0;
1147+ gs->LJ = 0;
1148+ gs->ML = 10;
1149+ gs->SCS = gs->NSCS = DeviceGray;
1150+ // Alpha is lowest byte; this is (0, 0, 0, 255) == black
1151+ gs->SC = gs->NSC = 255;
1152+ gs->Font.font = nil;
1153+ gs->Font.enc = nil;
1154+ gs->Font.widths = nil;
1155+}
1156+
1157+void
1158+gsfree(GS gs)
9381159 {
939− buffree(&p->buf);
940− pdfobjfree(p->stack);
1160+ free(gs.Font.widths);
1161+ pdfobjfree(gs.Font.font);
1162+ gs.Font.font = nil;
1163+ gs.Font.enc = nil;
1164+ gs.Font.widths = nil;
9411165 }
9421166
943−static void
944−stackreset(Object *stack)
1167+void
1168+pagegsclean(Page *p)
9451169 {
9461170 int i;
947− for(i = 0; i < stack->array.ne; i += 1)
948− pdfobjfree(stack->array.e[i]);
949− stack->array.ne = 0;
950− free(stack->array.e);
951− stack->array.e = nil;
1171+ p->GSactive = nil;
1172+ for(i = 0; i < p->nGS; i += 1)
1173+ gsfree(p->GS[i]);
1174+ free(p->GS);
1175+ p->GS = nil;
1176+ p->nGS = 0;
9521177 }
9531178
954−static void
1179+static int
1180+stackreset(Page *p)
1181+{
1182+ pdfobjfree(p->stack);
1183+ p->stack = arraynew(p->obj->pdf);
1184+ return p->stack != nil;
1185+}
1186+
1187+void
1188+pagefree(Page *p)
1189+{
1190+ buffree(&p->buf);
1191+ pdfobjfree(p->stack);
1192+ pagegsclean(p);
1193+}
1194+
1195+static int
9551196 pagerendercontent(Page *p, Object *content)
9561197 {
9571198 Stream *s;
@@ -964,43 +1205,63 @@ pagerendercontent(Page *p, Object *content) · 6 unmodified lines
9641205 }
9651206 p->stack = arraynew(content->pdf);
9661207 if(p->stack == nil)
967− return;
1208+ return 0;
9681209 while(s->buf.off != s->buf.sz){
9691210 while(isws(s->buf.b[s->buf.off]) && s->buf.off != s->buf.sz)
9701211 s->buf.off += 1;
9711212 if(s->buf.off == s->buf.sz)
9721213 break;
973− op = opfind(s);
974− if(op != nil){
975− op->f(op, p);
976− stackreset(p->stack);
977− } else{
978− o = pdfobj(content->pdf, s);
979− if(o == nil){
980− fprint(2, "failed to read operand: %r\n");
981− break;
1214+ o = pdfobj(content->pdf, s);
1215+ if(o == nil)
1216+ return 0;
1217+ if(o->type == Oop){
1218+ op = opfind(o->str);
1219+ if(op == nil){
1220+ fprint(2, "Unknown op: %s\n", o->str);
1221+ pdfobjfree(o);
1222+ return 0;
1223+ }
1224+ pdfobjfree(o);
1225+ if(!op->f(op, p)){
1226+ fprint(2, "'%s' failed!\n", op->s);
1227+ return 0;
9821228 }
1229+ if(!stackreset(p))
1230+ return 0;
1231+ } else{
9831232 if(!arrayadd(p->stack, o)){
9841233 fprint(2, "Failed to push operand to stack: %r\n");
985− break;
1234+ return 0;
9861235 }
9871236 }
9881237 }
989− if(bufput(&p->buf, (uchar*)"\n", 1) == -1)
990− sysfatal("OOM");
1238+ if(bufput(&p->buf, (uchar*)"\n\0", 2) != 2)
1239+ return 0;
9911240 Sclose(s);
1241+ return 1;
9921242 }
9931243
9941244 int
995−pagerender(Page *p, Object *o)
1245+pagerender(Page *p)
9961246 {
9971247 Object *content;
9981248 int i;
999− content = dictget(o, "Contents");
1000− if(content->type == Oarray)
1249+ p->nGS = 1;
1250+ p->GS = malloc(sizeof(GS));
1251+ if(p->GS == nil){
1252+ werrstr("Out of memory");
1253+ return 0;
1254+ }
1255+ gsinit(p, p->GS);
1256+ content = dictget(p->obj, "Contents");
1257+ if(content->type == Oarray){
10011258 for(i = 0; i < arraylen(content); i += 1)
1002− pagerendercontent(p, arrayget(content, i));
1259+ if(!pagerendercontent(p, arrayget(content, i)))
1260+ return 0;
1261+ }
10031262 else if(content->type != Onull)
1004− pagerendercontent(p, content);
1263+ if(!pagerendercontent(p, content))
1264+ return 0;
1265+ pagegsclean(p);
10051266 return 1;
10061267 }
pdf.h+51−11
@@ -8,10 +8,12 @@ enum { · 7 unmodified lines
88 Ostream, /* 7.3.8 */
99 Onull, /* 7.3.9 */
1010 Oindir, /* 7.3.10 */
11+ Oop, /* 7.8.2 */
1112 };
1213
1314 typedef struct Buffer Buffer;
1415 typedef struct Filter Filter;
16+typedef struct TS TS;
1517 typedef struct GS GS;
1618 typedef struct GSD GSD;
1719 typedef struct GSFont GSFont;
@@ -35,11 +37,6 @@ struct Buffer { · 17 unmodified lines
3537 void *(*memimage)(Buffer *b);
3638 };
3739
38−struct Page {
39− Object *stack;
40− Buffer buf;
41−};
42−
4340 struct Filter {
4441 char *name;
4542 int (*readall)(void *aux, Buffer *bi, Buffer *bo);
@@ -104,22 +101,65 @@ struct GSD { · 58 unmodified lines
104101 struct GSFont {
105102 Object *font;
106103 double size;
104+ Object *enc; /* TODO: drop enc, use the encoding table */
105+ struct{
106+ // If a character c is in [first, last], replace it with values[c], which may be multibyte.
107+ int first, last;
108+ char **values;
109+ } encoding;
110+ struct{
111+ int first, last;
112+ int *widths;
113+ int defwidth;
114+ };
107115 };
108116
117+/* Color spaces; 8.6.3 / table 61 */
118+typedef enum ColorSpace {
119+ DeviceGray, DeviceRGB, DeviceCMYK, /* Device family */
120+ CalGray, CalRGB, Lab, ICCBased, /* CIE-based family */
121+ Pattern, Indexed, Separation, DeviceN, /* Special family */
122+} ColorSpace;
123+
109124 struct GS {
125+ double CTM[6]; /* current transformation matrix ; 8.3 */
110126 Object *BG, *UCR, *UCR2, *TR, *TR2, *HT, *BM, *SMask, *UseBlackPTComp, *HTO;
111127 int LW, LC, LJ, ML, RI, OP, op, OPM, SA, AIS, TK;
112− double SM, CA, ca;
113− struct {
114− GSFont *Font;
115− int nFont;
128+ double SM, CA, ca, FL;
129+ struct{ /* coloring info */
130+ ColorSpace SCS, NSCS; /* stroking color space and nonstroking color space */
131+ u32int SC, NSC;
116132 };
133+ GSFont Font;
117134 struct {
118135 GSD *d;
119136 int nd;
120137 };
121138 };
122139
140+struct TS {
141+ double Tm[6]; /* text matrix */
142+ double Tlm[6]; /* text line matrix */
143+ /* Tracks if we're in a text object; nesting is verboten */
144+ int inobj;
145+ double TL;
146+ /* Temporary, for pdf2txt functionality: tracks the last character's position so we know whether whitespace is needed */
147+ double x, y;
148+};
149+
150+struct Page {
151+ Object *obj;
152+ Object *stack;
153+ Buffer buf;
154+ /* The graphical state stack. GSactive is always a shortcut for the top of the stack, GS[nGS - 1] */
155+ struct{
156+ GS *GS;
157+ GS *GSactive;
158+ int nGS;
159+ };
160+ TS TS;
161+};
162+
123163 struct Pdf {
124164 Stream *s;
125165 Xref *xref;
@@ -246,8 +286,8 @@ int bufput(Buffer *b, uchar *d, int sz); · 120 unmodified lines
246286 int bufget(Buffer *b, uchar *d, int sz);
247287 void bufdump(Buffer *b);
248288
249−void pageinit(Page *p);
250−int pagerender(Page *p, Object *o);
289+void pageinit(Page *p, Object *o);
290+int pagerender(Page *p);
251291 void pagefree(Page *p);
252292
253293 #pragma varargck type "O" Object*

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