// Smolhershey utility program to produce a PostScript font specimen. // Copyright 02024 Kragen Javier Sitaker. See smolhershey.md for // license. #include #include #include #include #include "smolhershey.h" // Context data for our PostScript output. typedef struct { FILE *output; sh_point last; // Last point visited, to avoid redundant commands. int valid; // Boolean indicating whether last is valid. } ps_context; static void ps_draw_line(sh_point start, sh_point end, void *userdata) { ps_context *ctx = userdata; // Do we need to lift the pen? int lift_pen = (!ctx->valid || start.x != ctx->last.x || start.y != ctx->last.y); ctx->valid = 1; ctx->last = end; if (lift_pen) { fprintf(ctx->output, "\n%d %d moveto", start.x, start.y); } // Plot relative within each path, which delta-compresses so that // the output isn’t so bulky. fprintf(ctx->output, " %d %d rlineto", end.x - start.x, end.y - start.y); } // Read an entire text file into malloced memory. int slurp_file(u8 **buf, int *buflen, const char *name) { FILE *font_file = fopen(name, "r"); if (!font_file) return 0; fseek(font_file, 0L, SEEK_END); *buflen = ftell(font_file); rewind(font_file); *buf = malloc(*buflen); if (!*buf) { fclose(font_file); return 0; } // Read font data into memory. fread(*buf, 1, *buflen, font_file); fclose(font_file); return 1; } // Nobody has a Hershey font this big yet. Unicode isn’t this big. enum { max_glyphs = 131072 }; u8 *glyph_pointers[max_glyphs]; sh_font my_font = { .lines = glyph_pointers, .n = max_glyphs }; const char *page_prologue = "gsave 100 700 translate 1 -1 scale\n" "1 setlinecap 1 setlinejoin 1.5 setlinewidth"; int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "Usage: %s foo.jhf > foo-specimen.ps\n", argv[0]); return 1; } u8 *font_data; int font_data_length; if (!slurp_file(&font_data, &font_data_length, argv[1])) { perror(argv[1]); return 1; } int num_glyphs = sh_load_font(&my_font, font_data, font_data_length); if (num_glyphs > my_font.n) { fprintf(stderr, "Font allocation of %d glyphs wasn’t big enough‽\n", my_font.n); free(font_data); return 1; } ps_context ctx = { .output = stdout }; sh_gc gc = { // graphics context .font = &my_font, .draw_line = ps_draw_line, .userdata = &ctx, }; puts("%!"); puts(page_prologue); for (int i = 0; i < num_glyphs - 1; i++) { printf("\nnewpath"); sh_show(&gc, i); printf("\nstroke\n\n"); ctx.valid = 0; // can’t rlineto from the last point in the previous glyph // Wrap to next line if we’re about to run off the right edge. // These values are just guesses. if (gc.cp.x > 450) { gc.cp.x = 0; gc.cp.y += 32; // Similarly, wrap to next page if we’re about to run off the // bottom. if (gc.cp.y > 650) { puts("grestore showpage"); puts(page_prologue); gc.cp.y = 0; } } } puts("grestore showpage"); free(font_data); return 0; }