shithub: pdffs

Download patch

ref: 7dd87721538f0392ba840134ecf3e7468b5d4a3f
parent: b051539715907b60f5ccb8570f4665ad45b3fbaf
author: Noam Preil <noam@pixelhero.dev>
date: Tue Jun 1 14:57:54 EDT 2021

add preliminary heuristic-based text generation

--- a/array.c
+++ b/array.c
@@ -10,9 +10,9 @@
 	Object *o, *m;
 	int c, noel;
 
-	o = calloc(1, sizeof(*o));
-	o->pdf = pdf;
-	o->type = Oarray;
+	o = arraynew(pdf);
+	if(o == nil)
+		return nil;
 	Sgetc(s); /* throw away '[' */
 
 	for(noel = 0;;){
@@ -45,6 +45,18 @@
 	werrstr("array: %r");
 	pdfobjfree(o);
 	return nil;
+}
+
+Object *
+arraynew(Pdf *pdf)
+{
+	Object *o;
+	o = calloc(1, sizeof(*o));
+	if(o == nil)
+		return nil;
+	o->pdf = pdf;
+	o->type = Oarray;
+	return o;
 }
 
 int
--- a/main.c
+++ b/main.c
@@ -15,6 +15,38 @@
 	threadexitsall("usage");
 }
 
+static void
+dumppage(Object *page)
+{
+	Page p;
+	pageinit(&p);
+	if(pagerender(&p, page))
+		fprint(1, "%s\n", p.text);
+	pagefree(&p);
+}
+
+static void
+dumppages(Object *pages)
+{
+	Object *page, *kids, *type;
+	int i, count;
+	kids = dictget(pages, "Kids");
+	count = arraylen(kids);
+	for(i = 0; i < count; i += 1){
+		page = arrayget(kids, i);
+		// Must be a dict, either Page or Pages
+		type = dictget(page, "Type");
+		// MUST be a name.
+		if(strcmp(type->name, "Pages") == 0)
+			dumppages(page);
+		else if(strcmp(type->name, "Page") == 0)
+			dumppage(page);
+		else
+			sysfatal("Unexpected page node type '%s'", type->name);
+	}
+}
+
+
 void
 threadmain(int argc, char **argv)
 {
@@ -49,6 +81,12 @@
 				sysfatal("write failed");
 			Sclose(s);
 			v = nil;
+			break;
+		}else if(argv[i][0] == '"' && argv[i][1] == 0 && v->type == Odict && strcmp(dictget(v, "Type")->name, "Page") == 0){
+			dumppage(v);
+			break;
+		}else if(argv[i][0] == '"' && argv[i][1] == 0 && v->type == Odict && strcmp(dictget(v, "Type")->name, "Pages") == 0){
+			dumppages(v);
 			break;
 		}else if(argv[i][0] == '*' && argv[i][1] == 0 && v->type == Odict){
 			for(k = 0; k < v->dict.nkv; k++)
--- a/op.c
+++ b/op.c
@@ -14,379 +14,395 @@
 
 struct Op {
 	char *s;
-	int (*f)(Op *op, Object *s);
+	int (*f)(Op *op, Page *p);
 	int argc;
 	int flags;
 };
 
 static int
-cobegin(Op *op, Object *s)
+cobegin(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-coend(Op *op, Object *s)
+coend(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-gspush(Op *op, Object *s)
+gspush(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-gspop(Op *op, Object *s)
+gspop(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-gsctm(Op *op, Object *s)
+gsctm(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-gswidth(Op *op, Object *s)
+gswidth(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-gscap(Op *op, Object *s)
+gscap(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-gsjoin(Op *op, Object *s)
+gsjoin(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-gsmiterlim(Op *op, Object *s)
+gsmiterlim(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-gsdash(Op *op, Object *s)
+gsdash(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-gsintent(Op *op, Object *s)
+gsintent(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-gsflatness(Op *op, Object *s)
+gsflatness(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-gsstate(Op *op, Object *s)
+gsstate(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-pcmove(Op *op, Object *s)
+pcmove(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-pcline(Op *op, Object *s)
+pcline(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-pccurve(Op *op, Object *s)
+pccurve(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-pcsubpath(Op *op, Object *s)
+pcsubpath(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-pcrect(Op *op, Object *s)
+pcrect(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-ppstroke(Op *op, Object *s)
+ppstroke(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-ppstrokec(Op *op, Object *s)
+ppstrokec(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-ppfill(Op *op, Object *s)
+ppfill(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-ppfills(Op *op, Object *s)
+ppfills(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-ppfillcfs(Op *op, Object *s)
+ppfillcfs(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-ppc(Op *op, Object *s)
+ppc(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-cpclip(Op *op, Object *s)
+cpclip(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-cspace(Op *op, Object *s)
+cspace(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-ccolour(Op *op, Object *s)
+ccolour(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-ccolour2(Op *op, Object *s)
+ccolour2(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-cgray(Op *op, Object *s)
+cgray(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-crgb(Op *op, Object *s)
+crgb(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-ccmyk(Op *op, Object *s)
+ccmyk(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-sshade(Op *op, Object *s)
+sshade(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-eoobject(Op *op, Object *s)
+eoobject(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-iibegin(Op *op, Object *s)
+iibegin(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-iidata(Op *op, Object *s)
+iidata(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-iiend(Op *op, Object *s)
+iiend(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-tsspace(Op *op, Object *s)
+tsspace(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-tswspace(Op *op, Object *s)
+tswspace(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-tshscale(Op *op, Object *s)
+tshscale(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-tslead(Op *op, Object *s)
+tslead(Op *op, Page *p)
 {
-	USED(op, s);
+	int d = arrayget(p->stack, 0)->num.d / 20;
+	while(d > 0){
+		d -= 1;
+		fprint(2, "\n");
+	}
+	USED(op, p);
 	return 0;
 }
 
 static int
-tsfontsz(Op *op, Object *s)
+tsfontsz(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-tsrendmode(Op *op, Object *s)
+tsrendmode(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-tsrise(Op *op, Object *s)
+tsrise(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-tobegin(Op *op, Object *s)
+tobegin(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-toend(Op *op, Object *s)
+toend(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-tpmove(Op *op, Object *s)
+tpmove(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-tpmatrix(Op *op, Object *s)
+tpmatrix(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-tpmove0(Op *op, Object *s)
+tpmove0(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
+	fprint(2, "\n");
 	return 0;
 }
 
 static int
-thshow(Op *op, Object *s)
+thshow(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-thshowarr(Op *op, Object *s)
+thshowarr(Op *op, Page *p)
 {
-	USED(op, s);
+	Object *arr = arrayget(p->stack, 0);
+	Object *o;
+	int i;
+	for(i = 0; i < arraylen(arr); i += 1){
+		o = arrayget(arr, i);
+		if(o->type == Ostr)
+			fprint(1, "%s", o->str);
+		else if(o->num.d < -14)
+			fprint(1, " ");
+	}
+	USED(op);
 	return 0;
 }
 
 static int
-t3width(Op *op, Object *s)
+t3width(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t3widthbb(Op *op, Object *s)
+t3widthbb(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4add(Op *op, Object *s)
+t4add(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 /*
 	double x;
 	x = objat(s+1, Onum)->num.d + objat(s+0, Onum)->num.d;
@@ -398,9 +414,9 @@
 }
 
 static int
-t4sub(Op *op, Object *s)
+t4sub(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 /*
 	double x;
 	x = objat(s+1, Onum)->num.d - objat(s+0, Onum)->num.d;
@@ -412,9 +428,9 @@
 }
 
 static int
-t4mul(Op *op, Object *s)
+t4mul(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 /*
 	double x;
 
@@ -427,9 +443,9 @@
 }
 
 static int
-t4div(Op *op, Object *s)
+t4div(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 /*
 	double x;
 
@@ -442,268 +458,268 @@
 }
 
 static int
-t4idiv(Op *op, Object *s)
+t4idiv(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4mod(Op *op, Object *s)
+t4mod(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4neg(Op *op, Object *s)
+t4neg(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4abs(Op *op, Object *s)
+t4abs(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4ceiling(Op *op, Object *s)
+t4ceiling(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4floor(Op *op, Object *s)
+t4floor(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4round(Op *op, Object *s)
+t4round(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4truncate(Op *op, Object *s)
+t4truncate(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4sqrt(Op *op, Object *s)
+t4sqrt(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4sin(Op *op, Object *s)
+t4sin(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4cos(Op *op, Object *s)
+t4cos(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4atan(Op *op, Object *s)
+t4atan(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4exp(Op *op, Object *s)
+t4exp(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4ln(Op *op, Object *s)
+t4ln(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4log(Op *op, Object *s)
+t4log(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4cvi(Op *op, Object *s)
+t4cvi(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4cvr(Op *op, Object *s)
+t4cvr(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4eq(Op *op, Object *s)
+t4eq(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4ne(Op *op, Object *s)
+t4ne(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4gt(Op *op, Object *s)
+t4gt(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4ge(Op *op, Object *s)
+t4ge(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4lt(Op *op, Object *s)
+t4lt(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4le(Op *op, Object *s)
+t4le(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4and(Op *op, Object *s)
+t4and(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4or(Op *op, Object *s)
+t4or(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4xor(Op *op, Object *s)
+t4xor(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4not(Op *op, Object *s)
+t4not(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4bitshift(Op *op, Object *s)
+t4bitshift(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4true(Op *op, Object *s)
+t4true(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4false(Op *op, Object *s)
+t4false(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4if(Op *op, Object *s)
+t4if(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4ifelse(Op *op, Object *s)
+t4ifelse(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4pop(Op *op, Object *s)
+t4pop(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4exch(Op *op, Object *s)
+t4exch(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4dup(Op *op, Object *s)
+t4dup(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4copy(Op *op, Object *s)
+t4copy(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4index(Op *op, Object *s)
+t4index(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
 static int
-t4roll(Op *op, Object *s)
+t4roll(Op *op, Page *p)
 {
-	USED(op, s);
+	USED(op, p);
 	return 0;
 }
 
@@ -856,18 +872,104 @@
 	{nil, nil, 0},
 };
 
+// If an op is found at the current position in the stream, the associated Op is
+// returned and the stream is advanced. Otherwise, nil is returned and the stream
+// is left unchanged.
 Op *
-opfind(char *name)
+opfind(Stream *s)
 {
 	int i;
+	uint len;
 	Op *op;
+	char *b = (char*)s->buf.b + s->buf.off;
 	i = 0;
-	op = &ops[0];
-	while(op->s != nil){
-		if(strcmp(op->s, name) == 0)
+	while(ops[i].s != nil){
+		op = &ops[i];
+		len = strlen(op->s);
+		if(strncmp(op->s, b, len) == 0 && (isws(b[len]) || isdelim(b[len]))){
+			s->buf.off += len;
 			return op;
+		}
 		i += 1;
-		op = &ops[i];
 	}
 	return nil;
+}
+
+void
+pageinit(Page *page)
+{
+	page->text = nil;
+	// Stack is per-content-stream, so we don't create it here
+	page->stack = nil;
+}
+
+void
+pagefree(Page *p)
+{
+	free(p->text);
+	pdfobjfree(p->stack);
+}
+
+static void
+stackreset(Object *stack)
+{
+	int i;
+	for(i = 0; i < stack->array.ne; i += 1)
+		pdfobjfree(stack->array.e[i]);
+	stack->array.ne = 0;
+	free(stack->array.e);
+	stack->array.e = nil;
+}
+
+static void
+pagerendercontent(Page *p, Object *content)
+{
+	Stream *s;
+	Object *o;
+	Op *op;
+	s = Sopen(content);
+	if(s == nil){
+		fprint(2, "%O\n", content);
+		sysfatal("%r");
+	}
+	p->stack = arraynew(content->pdf);
+	if(p->stack == nil)
+		return;
+	while(s->buf.off != s->buf.sz){
+		while(isws(s->buf.b[s->buf.off]) && s->buf.off != s->buf.sz)
+			s->buf.off += 1;
+		if(s->buf.off == s->buf.sz)
+			break;
+		op = opfind(s);
+		if(op != nil){
+			op->f(op, p);
+			stackreset(p->stack);
+		} else{
+			o = pdfobj(content->pdf, s);
+			if(o == nil){
+				fprint(2, "failed to read operand: %r\n");
+				break;
+			}
+			if(!arrayadd(p->stack, o)){
+				fprint(2, "Failed to push operand to stack: %r\n");
+				break;
+			}
+		}
+	}
+	fprint(1, "\n");
+	Sclose(s);
+}
+
+int
+pagerender(Page *p, Object *o)
+{
+	Object *content;
+	int i;
+	content = dictget(o, "Contents");
+	if(content->type == Oarray)
+		for(i = 0; i < arraylen(content); i += 1)
+			pagerendercontent(p, arrayget(content, i));
+	else if(content->type != Onull)
+		pagerendercontent(p, content);
+	return 0;
 }
--- a/pdf.h
+++ b/pdf.h
@@ -22,7 +22,12 @@
 #pragma incomplete PredictParms;
 typedef struct Stream Stream;
 typedef struct Xref Xref;
+typedef struct Page Page;
 
+struct Page {
+	Object *stack;
+	char *text;
+};
 
 struct Buffer {
 	uchar *b;
@@ -188,6 +193,7 @@
 int isutf8(char *s, int len);
 
 int arraylen(Object *o);
+Object *arraynew(Pdf *pdf);
 Object *arrayget(Object *o, int i);
 int arrayint(Object *o, int i);
 int arrayadd(Object *a, Object *o);
@@ -232,6 +238,10 @@
 int bufput(Buffer *b, uchar *d, int sz);
 int bufget(Buffer *b, uchar *d, int sz);
 void bufdump(Buffer *b);
+
+void pageinit(Page *p);
+int pagerender(Page *p, Object *o);
+void pagefree(Page *p);
 
 #pragma varargck type "O" Object*
 #pragma varargck type "T" Object*