add multiline support
This commit is contained in:
		| @ -11,6 +11,8 @@ static char *fontfallbacks[] = { | |||||||
| static const char *fgcol = "#000000"; | static const char *fgcol = "#000000"; | ||||||
| static const char *bgcol = "#FFFFFF"; | static const char *bgcol = "#FFFFFF"; | ||||||
|  |  | ||||||
|  | static const float linespacing = 1.4; | ||||||
|  |  | ||||||
| /* how much screen estate is to be used at max for the content */ | /* how much screen estate is to be used at max for the content */ | ||||||
| static const float usablewidth = 0.75; | static const float usablewidth = 0.75; | ||||||
| static const float usableheight = 0.75; | static const float usableheight = 0.75; | ||||||
|  | |||||||
							
								
								
									
										62
									
								
								example
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								example
									
									
									
									
									
								
							| @ -1,14 +1,58 @@ | |||||||
| sent | sent | ||||||
| takahashi |  | ||||||
| why? | Origin: | ||||||
|  |      Takahashi | ||||||
|  |  | ||||||
|  | Why? | ||||||
|  | • PPTX sucks | ||||||
|  | • LATEX sucks | ||||||
|  | • PDF sucks | ||||||
|  |  | ||||||
|  | also: | ||||||
|  | terminal presentations | ||||||
|  | don't support images… | ||||||
|  |  | ||||||
| @nyan.png | @nyan.png | ||||||
|  | this text will not be displayed, since the @ at the start of the first line | ||||||
|  | makes this paragraph an image slide. | ||||||
|  |  | ||||||
| easy to use | easy to use | ||||||
| depends on Xlib, libpng |  | ||||||
|  | depends on | ||||||
|  | ♽ Xlib | ||||||
|  | ☢ libpng | ||||||
|  |  | ||||||
| ~1000 lines of code | ~1000 lines of code | ||||||
| how? |  | ||||||
| sent FILENAME | usage: | ||||||
| one slide per line | $ sent FILE1 [FILE2 …] | ||||||
|  |  | ||||||
|  | ▸ one slide per paragraph | ||||||
|  | ▸ lines starting with # are ignored | ||||||
|  | ▸ paragraphs starting with a @ line are png images | ||||||
|  | ▸ for an empty slide just use a \ as a paragraph | ||||||
|  |  | ||||||
| # This is a comment and will not be part of the presentation | # This is a comment and will not be part of the presentation | ||||||
| # The next line starts with a whitespace, it will not produce an image slide |  | ||||||
|  @FILE.png | # multiple empty lines between paragraphs are also ignored | ||||||
| thanks / questions? |  | ||||||
|  |  | ||||||
|  | # The following lines should produce | ||||||
|  | # one empty slide | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | \ | ||||||
|  | \ | ||||||
|  |  | ||||||
|  | \@this_line_actually_started_with_a_\.png | ||||||
|  | \#This line as well | ||||||
|  | ⇒ Prepend a backslash to kill behaviour of special characters | ||||||
|  |  | ||||||
|  | 😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏 | ||||||
|  | 😐😑😒😓😔😕😖😗😘😙😚😛😜😝😞😟 | ||||||
|  | 😠😡😢😣😥😦😧😨😩😪😫😭😮😯😰😱 | ||||||
|  | 😲😳😴😵😶😷😸😹😺😻😼😽😾😿🙀☠ | ||||||
|  |  | ||||||
|  | thanks | ||||||
|  | questions? | ||||||
|  | |||||||
							
								
								
									
										133
									
								
								sent.c
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								sent.c
									
									
									
									
									
								
							| @ -43,7 +43,8 @@ typedef struct { | |||||||
| } Image; | } Image; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
| 	char *text; | 	unsigned int linecount; | ||||||
|  | 	char **lines; | ||||||
| 	Image *img; | 	Image *img; | ||||||
| } Slide; | } Slide; | ||||||
|  |  | ||||||
| @ -85,7 +86,7 @@ static int pngprepare(Image *img); | |||||||
| static void pngscale(Image *img); | static void pngscale(Image *img); | ||||||
| static void pngdraw(Image *img); | static void pngdraw(Image *img); | ||||||
|  |  | ||||||
| static void getfontsize(char *str, unsigned int *width, unsigned int *height); | static void getfontsize(Slide *s, unsigned int *width, unsigned int *height); | ||||||
| static void cleanup(); | static void cleanup(); | ||||||
| static void eprintf(const char *, ...); | static void eprintf(const char *, ...); | ||||||
| static void die(const char *, ...); | static void die(const char *, ...); | ||||||
| @ -149,7 +150,7 @@ Image *pngopen(char *filename) | |||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
| 	if (!(img->info_ptr = png_create_info_struct(img->png_ptr)) | 	if (!(img->info_ptr = png_create_info_struct(img->png_ptr)) | ||||||
| 		|| setjmp(png_jmpbuf(img->png_ptr))) { | 	    || setjmp(png_jmpbuf(img->png_ptr))) { | ||||||
| 		pngfree(img); | 		pngfree(img); | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
| @ -315,28 +316,38 @@ void pngdraw(Image *img) | |||||||
| 	img->state |= DRAWN; | 	img->state |= DRAWN; | ||||||
| } | } | ||||||
|  |  | ||||||
| void getfontsize(char *str, unsigned int *width, unsigned int *height) | void getfontsize(Slide *s, unsigned int *width, unsigned int *height) | ||||||
| { | { | ||||||
| 	size_t i; | 	size_t i, j; | ||||||
|  | 	unsigned int curw, imax; | ||||||
|  | 	float lfac = linespacing * (s->linecount - 1) + 1; | ||||||
|  |  | ||||||
| 	for (i = 0; i < NUMFONTSCALES; i++) { | 	/* fit height */ | ||||||
| 		drw_setfontset(d, fonts[i]); | 	for (j = NUMFONTSCALES - 1; j >= 0; j--) | ||||||
| 		*height = fonts[i]->h; | 		if (fonts[j]->h * lfac <= xw.uh) | ||||||
| 		*width = drw_fontset_getwidth(d, str); |  | ||||||
| 		if (*width  > xw.uw || *height > xw.uh) |  | ||||||
| 			break; | 			break; | ||||||
|  | 	drw_setfontset(d, fonts[j]); | ||||||
|  |  | ||||||
|  | 	/* fit width */ | ||||||
|  | 	*width = 0; | ||||||
|  | 	for (i = 0; i < s->linecount; i++) { | ||||||
|  | 		curw = drw_fontset_getwidth(d, s->lines[i]); | ||||||
|  | 		if (curw >= *width) | ||||||
|  | 			imax = i; | ||||||
|  | 		while (j >= 0 && curw > xw.uw) { | ||||||
|  | 			drw_setfontset(d, fonts[--j]); | ||||||
|  | 			curw = drw_fontset_getwidth(d, s->lines[i]); | ||||||
|  | 		} | ||||||
|  | 		if (imax == i) | ||||||
|  | 			*width = curw; | ||||||
| 	} | 	} | ||||||
| 	if (i > 0) { | 	*height = fonts[j]->h * lfac; | ||||||
| 		drw_setfontset(d, fonts[i-1]); | 	*width += fonts[j]->h; | ||||||
| 		*height = fonts[i-1]->h; |  | ||||||
| 		*width = drw_fontset_getwidth(d, str); |  | ||||||
| 	} |  | ||||||
| 	*width += d->fonts->h; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void cleanup() | void cleanup() | ||||||
| { | { | ||||||
| 	unsigned int i; | 	unsigned int i, j; | ||||||
|  |  | ||||||
| 	for (i = 0; i < NUMFONTSCALES; i++) | 	for (i = 0; i < NUMFONTSCALES; i++) | ||||||
| 		drw_fontset_free(fonts[i]); | 		drw_fontset_free(fonts[i]); | ||||||
| @ -348,8 +359,9 @@ void cleanup() | |||||||
| 	XCloseDisplay(xw.dpy); | 	XCloseDisplay(xw.dpy); | ||||||
| 	if (slides) { | 	if (slides) { | ||||||
| 		for (i = 0; i < slidecount; i++) { | 		for (i = 0; i < slidecount; i++) { | ||||||
| 			if (slides[i].text) | 			for (j = 0; j < slides[i].linecount; j++) | ||||||
| 				free(slides[i].text); | 				free(slides[i].lines[j]); | ||||||
|  | 			free(slides[i].lines); | ||||||
| 			if (slides[i].img) | 			if (slides[i].img) | ||||||
| 				pngfree(slides[i].img); | 				pngfree(slides[i].img); | ||||||
| 		} | 		} | ||||||
| @ -394,27 +406,57 @@ void eprintf(const char *fmt, ...) | |||||||
| void load(FILE *fp) | void load(FILE *fp) | ||||||
| { | { | ||||||
| 	static size_t size = 0; | 	static size_t size = 0; | ||||||
|  | 	size_t blen, maxlines; | ||||||
| 	char buf[BUFSIZ], *p; | 	char buf[BUFSIZ], *p; | ||||||
| 	size_t i = slidecount; | 	Slide *s; | ||||||
|  |  | ||||||
| 	/* read each line from fp and add it to the item list */ | 	/* read each line from fp and add it to the item list */ | ||||||
| 	while (fgets(buf, sizeof(buf), fp)) { | 	while (1) { | ||||||
| 		if ((i+1) * sizeof(*slides) >= size) | 		if ((slidecount+1) * sizeof(*slides) >= size) | ||||||
| 			if (!(slides = realloc(slides, (size += BUFSIZ)))) | 			if (!(slides = realloc(slides, (size += BUFSIZ)))) | ||||||
| 				die("cannot realloc %u bytes:", size); | 				die("cannot realloc %u bytes:", size); | ||||||
| 		if (*buf == '#') |  | ||||||
| 			continue; | 		/* eat consecutive empty lines */ | ||||||
| 		if ((p = strchr(buf, '\n'))) | 		while ((p = fgets(buf, sizeof(buf), fp))) | ||||||
| 			*p = '\0'; | 			if (strcmp(buf, "\n") != 0 && buf[0] != '#') | ||||||
| 		if (!(slides[i].text = strdup(buf))) | 				break; | ||||||
| 			die("cannot strdup %u bytes:", strlen(buf)+1); | 		if (!p) | ||||||
| 		if (slides[i].text[0] == '@') | 			break; | ||||||
| 			slides[i].img = pngopen(slides[i].text + 1); |  | ||||||
| 		else | 		/* read one slide */ | ||||||
| 			slides[i].img = 0; | 		maxlines = 0; | ||||||
| 		i++; | 		memset((s = &slides[slidecount]), 0, sizeof(Slide)); | ||||||
|  | 		do { | ||||||
|  | 			if (buf[0] == '#') | ||||||
|  | 				continue; | ||||||
|  |  | ||||||
|  | 			/* grow lines array */ | ||||||
|  | 			if (s->linecount >= maxlines) { | ||||||
|  | 				maxlines = 2 * s->linecount + 1; | ||||||
|  | 				if (!(s->lines = realloc(s->lines, maxlines * sizeof(s->lines[0])))) | ||||||
|  | 					die("cannot realloc %u bytes:", maxlines * sizeof(s->lines[0])); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			blen = strlen(buf); | ||||||
|  | 			if (!(s->lines[s->linecount] = strdup(buf))) | ||||||
|  | 				die("cannot strdup:"); | ||||||
|  | 			if (s->lines[s->linecount][blen-1] == '\n') | ||||||
|  | 				s->lines[s->linecount][blen-1] = '\0'; | ||||||
|  |  | ||||||
|  | 			/* only make image slide if first line of a slide starts with @ */ | ||||||
|  | 			if (s->linecount == 0 && s->lines[0][0] == '@') { | ||||||
|  | 				memmove(s->lines[0], &s->lines[0][1], blen); | ||||||
|  | 				s->img = pngopen(s->lines[0]); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if (s->lines[s->linecount][0] == '\\') | ||||||
|  | 				memmove(s->lines[s->linecount], &s->lines[s->linecount][1], blen); | ||||||
|  | 			s->linecount++; | ||||||
|  | 		} while ((p = fgets(buf, sizeof(buf), fp)) && strcmp(buf, "\n") != 0); | ||||||
|  | 		slidecount++; | ||||||
|  | 		if (!p) | ||||||
|  | 			break; | ||||||
| 	} | 	} | ||||||
| 	slidecount = i; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void advance(const Arg *arg) | void advance(const Arg *arg) | ||||||
| @ -427,9 +469,9 @@ void advance(const Arg *arg) | |||||||
| 		idx = new_idx; | 		idx = new_idx; | ||||||
| 		xdraw(); | 		xdraw(); | ||||||
| 		if (slidecount > idx + 1 && slides[idx + 1].img && !pngread(slides[idx + 1].img)) | 		if (slidecount > idx + 1 && slides[idx + 1].img && !pngread(slides[idx + 1].img)) | ||||||
| 			die("could not read image %s", slides[idx + 1].text + 1); | 			die("could not read image %s", slides[idx + 1].lines[0]); | ||||||
| 		if (0 < idx && slides[idx - 1].img && !pngread(slides[idx - 1].img)) | 		if (0 < idx && slides[idx - 1].img && !pngread(slides[idx - 1].img)) | ||||||
| 			die("could not read image %s", slides[idx - 1].text + 1); | 			die("could not read image %s", slides[idx - 1].lines[0]); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -476,20 +518,27 @@ void usage() | |||||||
|  |  | ||||||
| void xdraw() | void xdraw() | ||||||
| { | { | ||||||
| 	unsigned int height, width; | 	unsigned int height, width, i; | ||||||
| 	Image *im = slides[idx].img; | 	Image *im = slides[idx].img; | ||||||
|  |  | ||||||
| 	getfontsize(slides[idx].text, &width, &height); | 	getfontsize(&slides[idx], &width, &height); | ||||||
| 	XClearWindow(xw.dpy, xw.win); | 	XClearWindow(xw.dpy, xw.win); | ||||||
|  |  | ||||||
| 	if (!im) { | 	if (!im) { | ||||||
| 		drw_rect(d, 0, 0, xw.w, xw.h, 1, 1); | 		drw_rect(d, 0, 0, xw.w, xw.h, 1, 1); | ||||||
| 		drw_text(d, (xw.w - width) / 2, (xw.h - height) / 2, width, height, slides[idx].text, 0); | 		for (i = 0; i < slides[idx].linecount; i++) | ||||||
|  | 			drw_text(d, | ||||||
|  | 			         (xw.w - width) / 2, | ||||||
|  | 			         (xw.h - height) / 2 + i * linespacing * d->fonts->h, | ||||||
|  | 			         width, | ||||||
|  | 			         d->fonts->h, | ||||||
|  | 			         slides[idx].lines[i], | ||||||
|  | 			         0); | ||||||
| 		drw_map(d, xw.win, 0, 0, xw.w, xw.h); | 		drw_map(d, xw.win, 0, 0, xw.w, xw.h); | ||||||
| 	} else if (!(im->state & LOADED) && !pngread(im)) { | 	} else if (!(im->state & LOADED) && !pngread(im)) { | ||||||
| 		eprintf("could not read image %s", slides[idx].text + 1); | 		eprintf("could not read image %s", slides[idx].lines[0]); | ||||||
| 	} else if (!(im->state & SCALED) && !pngprepare(im)) { | 	} else if (!(im->state & SCALED) && !pngprepare(im)) { | ||||||
| 		eprintf("could not prepare image %s for drawing", slides[idx].text + 1); | 		eprintf("could not prepare image %s for drawing", slides[idx].lines[0]); | ||||||
| 	} else if (!(im->state & DRAWN)) { | 	} else if (!(im->state & DRAWN)) { | ||||||
| 		pngdraw(im); | 		pngdraw(im); | ||||||
| 	} | 	} | ||||||
| @ -634,7 +683,7 @@ int main(int argc, char *argv[]) | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (!slides || !slides[0].text) | 	if (!slides || !slides[0].lines) | ||||||
| 		usage(); | 		usage(); | ||||||
|  |  | ||||||
| 	xinit(); | 	xinit(); | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user