const char * (UsageLines [] ) = { "p4label15: Reads text from standard input,", "writes P4 pbm to standard output using fixed-width.", "Usage: p4label15 (output image width) (output image height)", "Only ASCII characters are recognized.", "", "January 4, 2023. Latest at gopher://sdf.org/1/users/julianbr", }; const int NumUsageLines = sizeof (UsageLines)/sizeof (UsageLines [0] ); #define NUM_ROWS 15 #define LETTER_SPACING 1 #define LINE_SPACING 1 #define MARGIN 5 int MaxNumColumns; int (Outlines [] ) [NUM_ROWS] = { {0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0 }, /* ! */ {0, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* " */ {0, 0, 18, 18, 18, 63, 18, 18, 63, 18, 18, 18, 0, 0, 0 }, /* # */ {0, 8, 8, 62, 73, 72, 72, 62, 9, 9, 73, 62, 8, 8, 0 }, /* $ */ {0, 0, 50, 74, 52, 4, 8, 8, 16, 22, 41, 38, 0, 0, 0 }, /* % */ {0, 0, 12, 18, 18, 12, 24, 37, 34, 34, 34, 29, 0, 0, 0 }, /* & */ {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* ' */ {0, 0, 1, 2, 4, 4, 4, 4, 4, 4, 2, 1, 0, 0, 0 }, /* ( */ {0, 0, 4, 2, 1, 1, 1, 1, 1, 1, 2, 4, 0, 0, 0 }, /* ) */ {0, 0, 0, 0, 0, 18, 12, 63, 12, 18, 0, 0, 0, 0, 0 }, /* * */ {0, 0, 0, 0, 0, 4, 4, 31, 4, 4, 0, 0, 0, 0, 0 }, /* + */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0, 0 }, /* , */ {0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0 }, /* - */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, /* . */ {0, 0, 1, 1, 2, 2, 4, 4, 8, 8, 16, 16, 0, 0, 0 }, /* / */ {0, 0, 30, 33, 33, 35, 37, 41, 49, 33, 33, 30, 0, 0, 0 }, /* 0 */ {0, 0, 4, 12, 20, 4, 4, 4, 4, 4, 4, 31, 0, 0, 0 }, /* 1 */ {0, 0, 30, 33, 33, 1, 2, 4, 8, 16, 32, 63, 0, 0, 0 }, /* 2 */ {0, 0, 30, 33, 33, 1, 14, 1, 1, 33, 33, 30, 0, 0, 0 }, /* 3 */ {0, 0, 1, 3, 5, 9, 17, 33, 63, 1, 1, 1, 0, 0, 0 }, /* 4 */ {0, 0, 63, 32, 32, 32, 62, 1, 1, 1, 33, 30, 0, 0, 0 }, /* 5 */ {0, 0, 14, 16, 32, 32, 62, 33, 33, 33, 33, 30, 0, 0, 0 }, /* 6 */ {0, 0, 63, 1, 1, 2, 2, 4, 4, 8, 8, 8, 0, 0, 0 }, /* 7 */ {0, 0, 30, 33, 33, 33, 30, 33, 33, 33, 33, 30, 0, 0, 0 }, /* 8 */ {0, 0, 30, 33, 33, 33, 33, 31, 1, 1, 2, 28, 0, 0, 0 }, /* 9 */ {0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0 }, /* : */ {0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 2, 0, 0 }, /* ; */ {0, 0, 0, 1, 2, 4, 8, 16, 8, 4, 2, 1, 0, 0, 0 }, /* < */ {0, 0, 0, 0, 0, 0, 63, 0, 63, 0, 0, 0, 0, 0, 0 }, /* = */ {0, 0, 0, 16, 8, 4, 2, 1, 2, 4, 8, 16, 0, 0, 0 }, /* > */ {0, 0, 30, 33, 33, 33, 2, 4, 4, 0, 4, 4, 0, 0, 0 }, /* ? */ {0, 0, 62, 65, 79, 81, 81, 81, 83, 77, 64, 63, 0, 0, 0 }, /* @ */ {0, 0, 30, 33, 33, 33, 33, 63, 33, 33, 33, 33, 0, 0, 0 }, /* A */ {0, 0, 62, 33, 33, 33, 62, 33, 33, 33, 33, 62, 0, 0, 0 }, /* B */ {0, 0, 30, 33, 33, 32, 32, 32, 32, 33, 33, 30, 0, 0, 0 }, /* C */ {0, 0, 60, 34, 33, 33, 33, 33, 33, 33, 34, 60, 0, 0, 0 }, /* D */ {0, 0, 63, 32, 32, 32, 60, 32, 32, 32, 32, 63, 0, 0, 0 }, /* E */ {0, 0, 63, 32, 32, 32, 60, 32, 32, 32, 32, 32, 0, 0, 0 }, /* F */ {0, 0, 30, 33, 33, 32, 32, 39, 33, 33, 33, 30, 0, 0, 0 }, /* G */ {0, 0, 33, 33, 33, 33, 63, 33, 33, 33, 33, 33, 0, 0, 0 }, /* H */ {0, 0, 7, 2, 2, 2, 2, 2, 2, 2, 2, 7, 0, 0, 0 }, /* I */ {0, 0, 7, 2, 2, 2, 2, 2, 2, 34, 34, 28, 0, 0, 0 }, /* J */ {0, 0, 33, 34, 36, 40, 48, 48, 40, 36, 34, 33, 0, 0, 0 }, /* K */ {0, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 63, 0, 0, 0 }, /* L */ {0, 0, 65, 99, 85, 73, 73, 65, 65, 65, 65, 65, 0, 0, 0 }, /* M */ {0, 0, 33, 33, 33, 49, 41, 37, 35, 33, 33, 33, 0, 0, 0 }, /* N */ {0, 0, 30, 33, 33, 33, 33, 33, 33, 33, 33, 30, 0, 0, 0 }, /* O */ {0, 0, 62, 33, 33, 33, 33, 62, 32, 32, 32, 32, 0, 0, 0 }, /* P */ {0, 0, 30, 33, 33, 33, 33, 33, 33, 33, 37, 30, 1, 0, 0 }, /* Q */ {0, 0, 62, 33, 33, 33, 33, 62, 40, 36, 34, 33, 0, 0, 0 }, /* R */ {0, 0, 30, 33, 32, 32, 30, 1, 1, 33, 33, 30, 0, 0, 0 }, /* S */ {0, 0, 127, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0 }, /* T */ {0, 0, 33, 33, 33, 33, 33, 33, 33, 33, 33, 30, 0, 0, 0 }, /* U */ {0, 0, 33, 33, 33, 33, 33, 18, 18, 18, 12, 12, 0, 0, 0 }, /* V */ {0, 0, 65, 65, 65, 65, 65, 73, 73, 85, 99, 65, 0, 0, 0 }, /* W */ {0, 0, 33, 33, 18, 18, 12, 12, 18, 18, 33, 33, 0, 0, 0 }, /* X */ {0, 0, 65, 65, 34, 34, 20, 8, 8, 8, 8, 8, 0, 0, 0 }, /* Y */ {0, 0, 63, 1, 1, 2, 4, 8, 16, 32, 32, 63, 0, 0, 0 }, /* Z */ {0, 0, 7, 4, 4, 4, 4, 4, 4, 4, 4, 7, 0, 0, 0 }, /* [ */ {0, 0, 16, 16, 8, 8, 4, 4, 2, 2, 1, 1, 0, 0, 0 }, /* \ */ {0, 0, 7, 1, 1, 1, 1, 1, 1, 1, 1, 7, 0, 0, 0 }, /* ] */ {0, 0, 4, 10, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* ^ */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0 }, /* _ */ {2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* ` */ {0, 0, 0, 0, 0, 30, 1, 31, 33, 33, 33, 31, 0, 0, 0 }, /* a */ {0, 0, 32, 32, 32, 62, 33, 33, 33, 33, 33, 62, 0, 0, 0 }, /* b */ {0, 0, 0, 0, 0, 30, 33, 32, 32, 32, 33, 30, 0, 0, 0 }, /* c */ {0, 0, 1, 1, 1, 31, 33, 33, 33, 33, 33, 31, 0, 0, 0 }, /* d */ {0, 0, 0, 0, 0, 30, 33, 33, 63, 32, 32, 30, 0, 0, 0 }, /* e */ {0, 0, 7, 8, 8, 62, 8, 8, 8, 8, 8, 8, 0, 0, 0 }, /* f */ {0, 0, 0, 0, 0, 31, 33, 33, 33, 33, 33, 31, 1, 1, 30 }, /* g */ {0, 0, 32, 32, 32, 62, 33, 33, 33, 33, 33, 33, 0, 0, 0 }, /* h */ {0, 0, 2, 2, 0, 6, 2, 2, 2, 2, 2, 7, 0, 0, 0 }, /* i */ {0, 0, 2, 2, 0, 6, 2, 2, 2, 2, 2, 2, 34, 34, 14 }, /* j */ {0, 0, 32, 32, 32, 33, 34, 36, 56, 36, 34, 33, 0, 0, 0 }, /* k */ {0, 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 7, 0, 0, 0 }, /* l */ {0, 0, 0, 0, 0, 126, 73, 73, 73, 73, 73, 73, 0, 0, 0 }, /* m */ {0, 0, 0, 0, 0, 62, 33, 33, 33, 33, 33, 33, 0, 0, 0 }, /* n */ {0, 0, 0, 0, 0, 30, 33, 33, 33, 33, 33, 30, 0, 0, 0 }, /* o */ {0, 0, 0, 0, 0, 62, 33, 33, 33, 33, 33, 62, 32, 32, 32 }, /* p */ {0, 0, 0, 0, 0, 31, 33, 33, 33, 33, 33, 31, 1, 1, 1 }, /* q */ {0, 0, 0, 0, 0, 47, 48, 32, 32, 32, 32, 32, 0, 0, 0 }, /* r */ {0, 0, 0, 0, 0, 31, 32, 32, 30, 1, 1, 62, 0, 0, 0 }, /* s */ {0, 0, 8, 8, 8, 62, 8, 8, 8, 8, 8, 7, 0, 0, 0 }, /* t */ {0, 0, 0, 0, 0, 33, 33, 33, 33, 33, 33, 31, 0, 0, 0 }, /* u */ {0, 0, 0, 0, 0, 33, 33, 33, 18, 18, 12, 12, 0, 0, 0 }, /* v */ {0, 0, 0, 0, 0, 65, 65, 73, 73, 73, 73, 62, 0, 0, 0 }, /* w */ {0, 0, 0, 0, 0, 33, 33, 18, 12, 18, 33, 33, 0, 0, 0 }, /* x */ {0, 0, 0, 0, 0, 33, 33, 33, 33, 33, 33, 31, 1, 1, 30 }, /* y */ {0, 0, 0, 0, 0, 63, 2, 4, 8, 16, 32, 63, 0, 0, 0 }, /* z */ {0, 0, 3, 4, 4, 4, 8, 4, 4, 4, 4, 3, 0, 0, 0 }, /* { */ {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }, /* | */ {0, 0, 12, 2, 2, 2, 1, 2, 2, 2, 2, 12, 0, 0, 0 }, /* } */ {0, 12, 18, 18, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* degree */ {0, 49, 73, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* ~ */ }; int NumOutlines = sizeof (Outlines) / sizeof (Outlines [0] ); #include <stdlib.h> #include <stdio.h> void WriteLabel (unsigned char (* TextLine) [NUM_ROWS] , int width, int height) { int * Outline; int c, i, j, byte, NumTextLines, NumImageLines, NumChars, MaxNumChars, PowerOf2; int row, position, NumColumns; NumImageLines = 0; printf ("P4\n"); printf ("%d %d\n", width, height); for (i = 0; i < MARGIN; i++) { if (NumImageLines < height) { for (byte = 0; byte < (width + 7)/8; byte++) putchar (0); } NumImageLines++; } NumTextLines = 0; MaxNumChars = 0; c = getchar (); while (c != EOF) { NumChars = 0; for (i = 0; i < NUM_ROWS; i++) { for (byte = 0; byte < (width + 7)/8; byte++) TextLine [byte] [i] = 0; } while (c != '\n' && c != EOF) { if (c - '!' >= 0 && c - '!' < NumOutlines) { PowerOf2 = 1; NumColumns = 0; Outline = Outlines [c - '!']; for (i = 0; i < NUM_ROWS; i++) { row = Outline [i]; while (PowerOf2 <= row) { PowerOf2 *= 2; NumColumns++; } } if (NumColumns > MaxNumColumns) NumColumns = MaxNumColumns; for (i = 0; i < NumColumns; i++) { position = MARGIN + NumChars*(MaxNumColumns + LETTER_SPACING) + (MaxNumColumns - NumColumns)/2 + i; if (position < width) { byte = position/8; for (j = 0; j < NUM_ROWS; j++) { row = Outline [j]; if (row & (1 << (NumColumns - i - 1) ) ) TextLine [byte] [j] += (1 << (7 - position%8) ); } } } } NumChars++; c = getchar (); } for (i = 0; i < NUM_ROWS; i++) { if (NumImageLines < height) { for (byte = 0; byte < (width + 7)/8; byte++) putchar (TextLine [byte] [i] ); } NumImageLines++; } for (i = 0; i < LINE_SPACING; i++) { if (NumImageLines < height) { for (byte = 0; byte < (width + 7)/8; byte++) putchar (0); } NumImageLines++; } if (MaxNumChars < NumChars) MaxNumChars = NumChars; NumTextLines++; if (c != EOF) c = getchar (); } while (NumImageLines < height) { for (byte = 0; byte < (width + 7)/8; byte++) putchar (0); NumImageLines++; } fprintf (stderr, "p4label15: Fits"); fprintf (stderr, " %d", 2*MARGIN + MaxNumChars*MaxNumColumns + LETTER_SPACING*(MaxNumChars - 1) ); fprintf (stderr, "x%d,", 2*MARGIN + (NumTextLines - 1)*LINE_SPACING + NumTextLines*NUM_ROWS); fprintf (stderr, " writing %dx%d.\n", width, height); } int main (int argc, char * * argv) { unsigned char (* TextLine) [NUM_ROWS]; int i, j, width, height, PowerOf2, row; char c; PowerOf2 = 1; MaxNumColumns = 0; for (i = 0; i < NumOutlines; i++) { for (j = 0; j < NUM_ROWS; j++) { row = Outlines [i] [j]; while (PowerOf2 <= row) { PowerOf2 *= 2; MaxNumColumns++; } } } if (argc == 1) { for (i = 0; i < NumUsageLines; i++) printf ("%s\n", UsageLines [i] ); printf ("Each character %dx%d,", MaxNumColumns, NUM_ROWS); printf (" letter spacing %d, line spacing %d,", LETTER_SPACING, LINE_SPACING); printf (" margin %d.\n", MARGIN); } else if (argc == 3) { if ( sscanf (argv [1], "%d%c", & width, & c) != 1 || sscanf (argv [2], "%d%c", & height, & c) != 1 || width <= 0 || height <= 0) { fprintf (stderr, "p4label1528: Improper \"%s\" \"%s\",", argv [1], argv [2] ); fprintf (stderr, " expecting width and height.\n"); } else { TextLine = malloc (NUM_ROWS*((width + 7)/8) ); if (TextLine == NULL) fprintf (stderr, "***p4label15: Not enough memory.\n"); else { WriteLabel (TextLine, width, height); free (TextLine); } } } else printf ("Usage: %s: (width) (height)\n", argv [0] ); return 0; }