shithub: spit

Download patch

ref: 07e365f3e588fafe5b76f0ecb35250a3dddda476
parent: bdef1272330cf36855a68abd15d437077402f48e
author: qwx <qwx@sciops.net>
date: Wed Feb 18 18:59:48 EST 2026

add enterprise-grade features

- add -n to write slides out to images
- add slide numbers
- image autoscaling using resample(1) from sigrid's imgtools
- right justified text
- padding tweaks and fixes
- allow 0 padding and margins
- style: title line size, slide number size, paper size

todo: should use memdraw to render to file without initdraw
todo: small bug in image autoscaling wrt positioning

--- a/a.h
+++ b/a.h
@@ -12,8 +12,8 @@
 
 enum 
 {
-	Maxslides = 128,
-	Maxlines  = 16 
+	Maxslides = 256,
+	Maxlines  = 32, 
 };
 
 struct Lines
--- a/spit.c
+++ b/spit.c
@@ -8,6 +8,8 @@
 Sfont		*ftitle;
 char		*ftextname;
 Sfont		*ftext;
+Sfont		*frtext;
+Sfont		*fnum;
 char		*ffixedname;
 Sfont		*ffixed;
 Sdopts		 opts = {.prefilter = 0, .gamma = 1.0 };
@@ -24,7 +26,7 @@
 ladd(Lines *l, char *s)
 {
 	if(l->nlines == Maxlines)
-		sysfatal("too many lines");
+		sysfatal("slide %d: too many lines", nslides);
 	l->lines[l->nlines++] = strdup(s);
 }
 
@@ -45,18 +47,52 @@
 	threadexitsall("error");
 }
 
+void
+barf(Image *slide, int n)
+{
+	int fd;
+	char path[64];
+
+	snprint(path, sizeof path, "spit.%03d.bit", n);
+	if((fd = create(path, OWRITE, 0644)) < 0)
+		sysfatal("open: %r");
+	writeimage(fd, slide, 0);
+	close(fd);
+}
+
 Image*
 addslide(void)
 {
 	++nslides;
+	if(nodraw && nslides > 0){
+		barf(slides[0], nslides);
+		draw(slides[0], slides[0]->r, cols[Cbg], nil, ZP);
+		return slides[0];
+	}
 	if(nslides >= Maxslides)
 		sysfatal("too many slides");
-	slides[nslides] = allocimage(display, display->image->r, screen->chan, 0, coldefs[Cbg]);
+	slides[nslides] = allocimage(display, screenr, screen->chan, 0, coldefs[Cbg]);
 	if(slides[nslides] == nil)
 		sysfatal("allocimage: %r");
 	return slides[nslides];
 }
 
+void
+renderpagenum(Image *b)
+{
+	char num[8];
+	Point p;
+	Rectangle r;
+	Image *t;
+
+	snprint(num, sizeof num, "%3d", nslides+1);
+	r = pt_textrect(fnum, num);
+	t = pt_textdraw(fnum, num, r, &opts);
+	p = Pt(b->r.max.x - margin - Dx(r), b->r.max.y - margin - Dy(r));
+	draw(b, rectaddpt(r, p), cols[Cfg], t, ZP);
+	freeimage(t);
+}
+
 Point
 rendertitle(Image *b, Point p, char *s)
 {
@@ -63,27 +99,34 @@
 	Rectangle r;
 	Image *i;
 
+	while(*s == ' ' || *s == '\t')
+		s++;
+	if(*s == 0)
+		s = "";
 	r = pt_textrect(ftitle, s);
 	i = pt_textdraw(ftitle, s, r, &opts);
 	draw(b, rectaddpt(r, p), cols[Cfg], i, ZP);
 	freeimage(i);
 	p.y += Dy(r);
-	line(b, Pt(p.x, p.y), Pt(b->r.max.x - margin, p.y), 0, 0, 2, cols[Cfg], ZP);
-	p.y += Dy(r);
+	line(b, Pt(p.x, p.y), Pt(b->r.max.x - margin, p.y), 0, 0, ftitlelinesz, cols[Cfg], ZP);
+	//p.y += Dy(r);
 	return p;
 }
 
 Point
-rendertext(Image *b, Point p, char *s)
+rendertext(Image *b, Point p, char *s, int rjust)
 {
 	Rectangle r;
 	Image *i;
 
-	r = pt_textrect(ftext, s);
+	r = pt_textrect(rjust ? frtext : ftext, s);
 	if(strlen(s) > 0){
-		i = pt_textdraw(ftext, s, r, &opts);
-		draw(b, Rect(p.x, p.y, p.x+Dx(bol->r), p.y+Dy(bol->r)), bol, 0, ZP);
-		draw(b, rectaddpt(r, Pt(p.x + Dx(bol->r) + padding, p.y)), cols[Cfg], i, ZP);
+		i = pt_textdraw(rjust ? frtext : ftext, s, r, &opts);
+		if(rjust)
+			draw(b, rectaddpt(r, Pt(p.x + screenr.max.x - Dx(r) - margin, p.y)), cols[Cfg], i, ZP);
+		else
+			//draw(b, Rect(p.x, p.y, p.x+Dx(bol->r), p.y+Dy(bol->r)), bol, 0, ZP);
+			draw(b, rectaddpt(r, Pt(p.x + Dx(bol->r) + padding, p.y)), cols[Cfg], i, ZP);
 		freeimage(i);
 	}
 	p.y += Dy(r)*lineheight;
@@ -144,7 +187,6 @@
 	return p;
 }
 
-
 Point
 rendercode(Image *b, Point p, Lines *lines)
 {
@@ -161,10 +203,11 @@
 			maxw = Dx(r[i]);
 	}
 	p.x += Dx(bol->r) + margin;
-	br = Rect(p.x, p.y, p.x + 1.5*maxw + 2*padding, p.y + maxh + 2*padding);
+	br = Rect(p.x, p.y, p.x + maxw + 4*padding, p.y + maxh + 2*padding);
 	draw(b, br, cols[Ccbg], nil, ZP);
 	border(b, br, 2, cols[Ccbord], ZP);
-	p.y += padding;
+	p.x += 2*padding;
+	p.y += 2*padding;
 	for(i = 0; i < lines->nlines; i++){
 		t = pt_textdraw(ffixed, lines->lines[i], r[i], &opts);
 		draw(b, rectaddpt(r[i], Pt(p.x+padding, p.y)), cols[Cfg], t, ZP);
@@ -171,22 +214,72 @@
 		freeimage(t);
 		p.y += Dy(r[i]);
 	}
-	p.x -= Dx(bol->r) + margin;
+	p.x -= Dx(bol->r) + margin - 2*padding;
 	return p;
 }
 
 Point
-renderimage(Image *b, Point p, char *f)
+renderimage(Image *b, Point p, char *f, int tile)
 {
 	Image *i;
-	int fd;
+	char dim[64], buf[1024];
+	double fx, fy;
+	int x, n, maxx, maxy, w, h, fd, pfd[2];
 
 	fd = open(f, OREAD);
 	if(fd <= 0)
 		sysfatal("open: %r");
 	i = readimage(display, fd, 0);
-	draw(b, rectaddpt(i->r, p), i, nil, ZP);
-	p.y += Dy(i->r) + margin;
+	maxy = screenr.max.y - margin;
+	maxx = screenr.max.x - margin;
+	h = maxy - p.y;
+	w = maxx - p.x;
+	if(tile)
+		w = (maxx - (tile - 1) * margin) / tile;
+	if((w < Dx(i->r) || h < Dy(i->r))){
+		if(pipe(pfd) < 0)
+			sysfatal("pipe: %r");
+		switch(fork()){
+		case -1: sysfatal("fork: %r");
+		case 0:
+			dup(pfd[0], 0);
+			dup(pfd[0], 1);
+			close(pfd[0]);
+			close(pfd[1]);
+			fx = (double)w / Dx(i->r);
+			fy = (double)h / Dy(i->r);
+			if(fx < fy){
+				snprint(dim, sizeof dim, "%f%%", fx*100);
+				execl("/bin/resample", "resample", "-f", "catmullrom", "-x", dim, nil);
+			}else{
+				snprint(dim, sizeof dim, "%f%%", fy*100);
+				execl("/bin/resample", "resample", "-f", "catmullrom", "-y", dim, nil);
+			}
+			sysfatal("execl: %r");
+		default:
+			close(pfd[0]);
+		}
+		seek(fd, 0, 0);
+		while((n = read(fd, buf, sizeof buf)) > 0)
+			if(write(pfd[1], buf, n) != n)
+				sysfatal("write: %r");
+		write(pfd[1], buf, 0);
+		freeimage(i);
+		if((i = readimage(display, pfd[1], 0)) == nil)
+			sysfatal("readimage: %r");
+		close(pfd[1]);
+	}
+	if(tile)
+		x = p.x;
+	else
+		x = (maxx - p.x) / 2 - Dx(i->r) / 2;
+	draw(b, rectaddpt(i->r, Pt(x, p.y)), i, nil, ZP);
+	if(tile)
+		p.x += Dx(i->r) + margin;
+	if(!tile || maxx - p.x <= margin){
+		p.x = margin;
+		p.y += Dy(i->r) + margin;
+	}
 	freeimage(i);
 	close(fd);
 	return p;
@@ -236,10 +329,10 @@
 		error(f, line, "empty style value");
 	if(strcmp(k, "margin") == 0){
 		margin = atoi(s);
-		if(margin == 0) error(f, line, "invalid 'margin' value");
+		if(margin < 0) error(f, line, "invalid 'margin' value");
 	}else if(strcmp(k, "padding") == 0){
 		padding = atoi(s);
-		if(padding == 0) error(f, line, "invalid 'padding' value");
+		if(padding < 0) error(f, line, "invalid 'padding' value");
 	}else if(strcmp(k, "lineheight") == 0){
 		lineheight = atof(s);
 		if(lineheight < 1.0) error(f, line, "invalid 'lineheight' value");
@@ -263,15 +356,31 @@
 		ftextname = strdup(s);
 	else if(strcmp(k, "text.size") == 0)
 		ftextsz = atof(s);
+	else if(strcmp(k, "rtext.size") == 0)
+		frtextsz = atof(s);
+	else if(strcmp(k, "num.size") == 0)
+		fnumsz = atof(s);
 	else if(strcmp(k, "fixed.font") == 0)
 		ffixedname = strdup(s);
 	else if(strcmp(k, "fixed.size") == 0)
 		ffixedsz = atof(s);
+	else if(strcmp(k, "title.linesize") == 0)
+		ftitlelinesz = atoi(s);
+	else if(strcmp(k, "screen.width") == 0)
+		screenr.max.x = atoi(s);
+	else if(strcmp(k, "screen.height") == 0)
+		screenr.max.y = atoi(s);
 	else
 		error(f, line, "unknown style key");		
 }
 
 void
+setpapersize(void)
+{
+	screenr = Rect(0, 0, Dx(screen->r), Dy(screen->r));
+}
+
+void
 initimages(void)
 {
 	Point p[4];
@@ -299,6 +408,8 @@
 		cols[i] = ealloccol(coldefs[i]);
 	ftitle = loadsfont(ftitlename, ftitlesz);
 	ftext  = loadsfont(ftextname ? ftextname : ftitlename, ftextsz);
+	frtext  = loadsfont(ftextname ? ftextname : ftitlename, frtextsz);
+	fnum  = loadsfont(ftextname ? ftextname : ftitlename, fnumsz);
 	ffixed = loadsfont(ffixedname ? ffixedname : ftitlename, ffixedsz);
 	initimages();
 }
@@ -308,9 +419,10 @@
 {
 	enum { Sstart, Scomment, Scontent, Slist, Squote, Scode };
 	Biobuf *bp;
-	char *l;
-	int s, ln;
+	char *l, *k;
+	int n, s, ln;
 	Image *b;
+	Rune r;
 	Point p;
 	Lines lines = {0};
 
@@ -339,12 +451,14 @@
 				continue;
 			}
 			if(l[0] != '#') error(f, ln, "expected title line");
+Title:
 			if(nslides == -1) /* all style parsed but not slide rendered yet */
 				loadstyle(f);
-Title:
+			else
+				renderpagenum(b);
 			p = Pt(margin, margin);
 			b = addslide();
-			p = rendertitle(b, p, l+2);
+			p = rendertitle(b, p, l+1);
 			s = Scontent;
 			break;
 		case Scomment:
@@ -363,12 +477,20 @@
 				s = Scode;
 				break;
 			}else if(l[0] == '!')
-				p = renderimage(b, p, l+2);
+				p = renderimage(b, p, l+2, 0);
 			else if(l[0] == ';'){
 				s = Scomment;
 				break;
-			}else
-				p = rendertext(b, p, l);
+			}else{
+				n = chartorune(&r, l);
+				if(r == L'→')
+					p = rendertext(b, p, l+n+1, 1);
+				else if(r == L'¡'){
+					n = strtol(l+n+1, &k, 10);
+					p = renderimage(b, p, k+1, n);
+				}else
+					p = rendertext(b, p, l, 0);
+			}
 			break;
 		case Slist:
 			if(l[0] != '-'){
@@ -402,25 +524,10 @@
 	}
 	if(nslides == -1)
 		error(f, ln, "no slides parsed");
+	renderpagenum(b);
 }
 
 void
-barf(void)
-{
-	int fd;
-	char path[64];
-	Image **i;
-
-	for(i=slides; i<slides+nslides+1; i++){
-		snprint(path, sizeof path, "spit.%03zd.bit", i - slides);
-		if((fd = create(path, OWRITE, 0644)) < 0)
-			sysfatal("open: %r");
-		writeimage(fd, *i, 0);
-		close(fd);
-	}
-}
-
-void
 redraw(void)
 {
 	draw(screen, screen->r, slides[curslide], nil, ZP);
@@ -520,10 +627,10 @@
 	alts[Ekeyboard].c = kc->c;
 	memimageinit();
 	fullscreen = 0;
-	screenr = screen->r;
+	setpapersize();
 	render(f);
 	if(nodraw){
-		barf();
+		addslide();
 		threadexitsall(nil);
 	}
 	resize();
--- a/style.h
+++ b/style.h
@@ -10,8 +10,15 @@
 /* slide title font size */
 float	ftitlesz		= 120.0;
 
+/* slide title line size */
+int	ftitlelinesz		= 2;
+
 /* text font size */
 float	ftextsz			= 96.0;
+float	frtextsz		= 96.0;
+
+/* page number font size */
+float	fnumsz			= 64.0;
 
 /* fixed font size */
 float	ffixedsz		= 72.0;
--