// Smolhershey example program producing HP-GL output. Copyright // 02024 Kragen Javier Sitaker. See smolhershey.md for license. // // Mostly the same as smolhersheyexample.c, but in some ways it is a // more complete example, showing how to use userdata. // // hp2xx from the hp2xx package can be used to visualize the output // (./smolhersheyhpgl | hp2xx -), and hp2xx -m png can convert it to a // PNG. It looks better with a wider pen (-p 5); I did some // brute-force antialiasing with hp2xx -p 5 -d 600 -m png and using // ImageMagick to reduce the resolution with convert -geometry // 596x596, then cleaned it up with optipng, and checked in the result // as smolhersheyhpgl.png. #include #include #include #include #include "smolhershey.h" // Context data for our HP-GL output. This is an example of how to // use userdata. typedef struct { FILE *output; // http://cmp.felk.cvut.cz/cmp/courses/ROB/labsmaterial/other/hpgl.html says: // “One irritant to HPGL is that there are two different coordinate // systems in use. Small-format plotters, including A- and B-size // plotters, locate the origin at the lower-left corner; // large-format plotters, including D- and E-size plotters, located // the origin at the center of the media.” float x_origin, y_origin, // Starting point. scale, // In 25μm units, 1016 per inch. angle; // In radians. sh_point last; // Last point visited, to avoid redundant commands. int valid; // Boolean indicating whether last is valid. } hpgl_context; static inline sh_point hpgl_xform(hpgl_context *ctx, sh_point p) { // Note that because Hershey fonts have Y increasing down, rather // than up, we reverse the signs on the Y coordinates from p. double s = sin(ctx->angle), c = cos(ctx->angle); return (sh_point) { .x = ctx->x_origin + ctx->scale * (c * p.x + s * p.y), .y = ctx->y_origin + ctx->scale * (s * p.x - c * p.y), }; } static void hpgl_draw_line(sh_point start, sh_point end, void *userdata) { hpgl_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; start = hpgl_xform(ctx, start); end = hpgl_xform(ctx, end); if (lift_pen) { fprintf(ctx->output, "PU;PA%d,%d;PD;\n", start.x, start.y); } // Plot relative within each path, which delta-compresses so that // the output isn’t so bulky. fprintf(ctx->output, " PR%d,%d;\n", 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; // Get file size. fseek(font_file, 0L, SEEK_END); *buflen = ftell(font_file); rewind(font_file); // Allocate memory for font data. *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; } int main(int argc, char **argv) { // Load Hershey font data from a file. char *font_file_path = "/usr/share/hershey-fonts/cursive.jhf"; u8 *font_data; int font_data_length; if (!slurp_file(&font_data, &font_data_length, font_file_path)) { int err = errno; perror(font_file_path); if (err == ENOENT) { fprintf(stderr, "Maybe: sudo apt install hershey-fonts-data # or equivalent\n"); } return 1; } // Allocate space for glyph pointers (assuming 96 glyphs + EOF). u8 *glyph_pointers[97]; // One extra for EOF // Initialize sh_font structure with one extra slot for EOF. sh_font my_font = { .lines = glyph_pointers, .n = 97 }; int num_glyphs = sh_load_font(&my_font, font_data, font_data_length); if (num_glyphs > my_font.n) { fprintf(stderr, "Font allocation wasn’t big enough.\n"); free(font_data); return 1; } hpgl_context ctx = { .output = stdout, .scale = 14, .x_origin = 1016, .y_origin = 1016, // 1", 1" .angle = 3.14159 / 3, // 60° }; // Initialize graphics context. sh_gc gc = { .font = &my_font, .draw_line = hpgl_draw_line, .userdata = &ctx, }; char *text_to_render = argc == 1 ? "Hello, World!" : argv[1]; for (char *p = text_to_render; *p; p++) { sh_show(&gc, *p - ' '); ctx.valid = 0; // ensure PU before next glyph } free(font_data); return 0; } /* The program’s output, which hp2xx at least renders reasonably, is: PU;PA1004,1135;PD; PR-26,-17; PR-31,2; PR-13,7; PR-17,26; PR2,31; PR7,12; PR26,18; PR31,-2; PR25,-14; PR41,-40; PR59,-67; PR34,-52; PR11,-38; PR-14,-24; PR-20,-5; PR-24,14; PU;PA1065,1100;PD; PR26,130; PR2,32; PR-3,50; PR-10,38; PR-18,26; PR-12,7; PR-19,-5; PR-7,-12; PR10,-38; PR35,-52; PR59,-67; PR53,-47; PR36,-21; PR20,5; PR7,13; PR1,31; PR-5,19; PR-22,45; PU;PA1275,1270;PD; PR2,31; PR-5,19; PR-17,26; PR-24,14; PR-19,-5; PR-7,-12; PR-2,-31; PR17,-27; PR36,-21; PR31,-1; PR27,17; PR14,24; PR1,31; PR-5,19; PR-22,46; PR-22,45; PR-40,71; PR-17,26; PR-30,34; PR-24,14; PR-19,-6; PR-2,-31; PR17,-26; PR42,-40; PR78,-61; PR72,-42; PR20,5; PR7,12; PR1,31; PR-5,19; PR-22,46; PR-22,45; PR-40,71; PR-17,26; PR-30,34; PR-24,14; PR-19,-6; PR-2,-31; PR17,-26; PR42,-40; PR78,-61; PR72,-42; PR20,5; PR7,12; PR1,31; PR-5,19; PR-22,46; PU;PA1408,1694;PD; PR-14,-24; PR-2,-31; PR5,-19; PR17,-26; PR24,-14; PR31,-2; PR27,17; PR14,24; PR1,32; PR-5,19; PR-17,26; PR-24,14; PR-31,2; PR-26,-18; PR5,-19; PR24,-14; PR31,-2; PR26,18; PR21,36; PR2,31; PR-5,19; PU;PA1559,1817;PD; PR5,-19; PR-19,-5; PR-5,19; PR19,5; PR24,-14; PR11,-38; PU;PA1606,2178;PD; PR-26,-17; PR-31,2; PR-13,7; PR-17,26; PR2,31; PR7,12; PR26,17; PR31,-1; PR37,-21; PR175,-118; PU;PA1612,2385;PD; PR185,-269; PU;PA1612,2385;PD; PR241,-172; PU;PA1696,2530;PD; PR-2,-31; PR16,-57; PR27,-65; PR52,-78; PR64,-86; PU;PA1870,2495;PD; PR-14,-25; PR-2,-31; PR5,-19; PR17,-26; PR24,-14; PR31,-2; PR27,17; PR14,25; PR1,31; PR-5,19; PR-17,26; PR-24,14; PR-31,2; PR-26,-17; PR5,-19; PR24,-14; PR31,-2; PR26,17; PR21,36; PR2,32; PR-5,19; PR-22,45; PR-18,26; PR25,-14; PR21,36; PR19,6; PR24,-14; PR29,-34; PR12,-7; PR20,6; PR7,12; PR1,31; PR-5,19; PR-22,45; PR-22,46; PR-40,71; PR-17,26; PR-30,33; PR-24,14; PR-19,-5; PR-2,-31; PR17,-26; PR42,-40; PR78,-62; PR72,-42; PR20,6; PR7,12; PR1,31; PR-5,19; PR-22,45; PU;PA2172,2934;PD; PR-31,2; PR-26,-17; PR-14,-24; PR-2,-31; PR5,-20; PR17,-26; PR24,-14; PR31,-2; PR27,18; PR14,24; PR1,31; PR-17,26; PR-176,199; PU;PA2143,2968;PD; PR53,-48; PR36,-21; PR20,6; PR7,12; PR1,31; PR-5,19; PR-22,45; PU;PA2074,3185;PD; PR170,-98; PU;PA2304,3052;PD; PR5,-19; PR20,5; PR-6,19; PR-19,-5; */