[1] | 1 | /** |
---|
| 2 | * Vollstaendig konfigurierbares Kommandozeilenfrontend fuer die |
---|
| 3 | * Lochstreifenzeichenroutine (Cairo verwendend). |
---|
| 4 | * siehe ./programm --help fuer Uebersicht der selbsterklaerenden |
---|
| 5 | * Parameter. |
---|
| 6 | * |
---|
| 7 | * Default ist Einlesen ueber stdin und Ausgabe auf stdout in PNG. |
---|
| 8 | * |
---|
| 9 | **/ |
---|
| 10 | |
---|
| 11 | #include <stdio.h> |
---|
| 12 | #include <stdlib.h> |
---|
| 13 | #include <string.h> |
---|
| 14 | #include <argp.h> |
---|
| 15 | |
---|
| 16 | #include "lochstreifen.h" |
---|
| 17 | |
---|
| 18 | LOCHSTREIFEN *l; |
---|
| 19 | int surface_type = 0; // 0 = PNG, 1 = SVG |
---|
| 20 | char *output_file; |
---|
| 21 | int verbosity = 0; |
---|
| 22 | |
---|
| 23 | #define DPRINTF(msg...) if(verbosity!=0) fprintf(stderr,msg); |
---|
| 24 | |
---|
| 25 | const char *argp_program_version = "Punch card visualisator - CLI frontend"; |
---|
| 26 | |
---|
| 27 | static struct argp_option options[] = { |
---|
| 28 | {"verbose", 'v', 0, 0, "Produce verbose output on stderr" }, |
---|
| 29 | //{"quiet", 'q', 0, 0, "Don't produce any output" }, |
---|
| 30 | //{"silent", 's', 0, OPTION_ALIAS }, |
---|
| 31 | |
---|
| 32 | {"output", 'o', "FILE", 0, "Output to FILE (instead of standard output)" }, |
---|
| 33 | {"image", 'i', 0, OPTION_ALIAS }, |
---|
| 34 | {"format", 'f', "SVG|PNG",0, "Set desired output image format (PNG or SVG)" }, |
---|
| 35 | {"", 0, 0, OPTION_DOC, ""}, |
---|
| 36 | |
---|
| 37 | {"Dimensions", 0, 0, OPTION_DOC, "Dimensions are integers without units"}, |
---|
| 38 | {"width", 'w', "NUM", 0, "Set desired width for output image (in px or points, according to output format)", 1 }, |
---|
| 39 | {"height", 'h', "NUM", 0, "Set desired height for output image (unit like width argument)", 1 }, |
---|
| 40 | {"diameter", 'd', "NUM", 0, "Set dimensions for output image by punched hole diameter (pixel)", 1 }, |
---|
| 41 | {"", 0, 0, OPTION_DOC, "",1}, |
---|
| 42 | |
---|
| 43 | {"Empty bytes", 1, 0, OPTION_DOC, "Bytes with value=0 which are not content of files", 1}, |
---|
| 44 | {"empty-start", -1, "NUM", 0, "Set number of empty bytes at beginning (default=0)", 2 }, |
---|
| 45 | {"empty-end", -2, "NUM", 0, "Set number of empty bytes at end (default=0)", 2 }, |
---|
| 46 | {"", 0, 0, OPTION_DOC, "",2}, |
---|
| 47 | |
---|
| 48 | {"Colors", 0, 0, OPTION_DOC, "Color format: #RGB[A], #RRGGBB[AA]", 2 }, |
---|
| 49 | {"hide-imagebg", -3, 0, 0, "Make the image background (space around paper tape) transparent", 3 }, |
---|
| 50 | {"color-imagebg", -4, "#RGBA", 0, "Set image background color", 3 }, |
---|
| 51 | {"hide-tapebg", -5, 0, 0, "Hide the paper tape background", 3 }, |
---|
| 52 | {"color-tapebg", -6, "#RGBA", 0, "Set tape background color", 3 }, |
---|
| 53 | {"hide-punched", -7, 0, 0, "Hide the holes (only the punched ones)", 3 }, |
---|
| 54 | {"color-punched", -8, "#RGBA", 0, "Set color of holes (punched bits)", 3 }, |
---|
| 55 | {"hide-notpunched", -9, 0, 0, "Hide the holes which aren't punched on real tapes", 3 }, |
---|
| 56 | {"color-notpunched",-10, "#RGBA", 0, "Set color of bits with boolean value \"false\"", 3 }, |
---|
| 57 | {"hide-feedholes", -11, 0, 0, "Hide the feed holes (which means they wouldn't be punched)", 3 }, |
---|
| 58 | {"color-feedholes", -12, "#RGBA", 0, "Set color of feed holes (the small ones)", 3 }, |
---|
| 59 | {"", 0, 0, OPTION_DOC, "",3}, |
---|
| 60 | |
---|
| 61 | {"Transformations and Rotations", 0, 0, OPTION_DOC, "", 3}, |
---|
| 62 | {"rotation", -13, "right|bottom|left|top", 0, "Rotation of punched tape (alternative short notation: 0=right, 1=bottom, 2=left, 3=top)", 4 }, |
---|
| 63 | {"reflection-byte-direction", -14, 0, 0, "Enables horizontal reflecion on the data axis (inverts data direction)", 4 }, |
---|
| 64 | {"reflection-bit-direction", -15, 0, 0, "Enables vertical reflection on the other axis (inverts bit direction)", 4 }, |
---|
| 65 | {"", 0, 0, OPTION_DOC, "",-2}, |
---|
| 66 | |
---|
| 67 | { 0 } |
---|
| 68 | }; |
---|
| 69 | |
---|
| 70 | char *strtolower(char *string) { |
---|
| 71 | // komisch, ich dachte echt, es gaebe dafuer eine Funktion in C... |
---|
| 72 | int x=0; |
---|
| 73 | for(;string[x];x++) string[x] = tolower(string[x]); |
---|
| 74 | |
---|
| 75 | return string; // ja, ich veraenderte den Ursprungsstring. Egal ;-) |
---|
| 76 | } |
---|
| 77 | |
---|
| 78 | cairo_pattern_t *hex2cairo_pattern(const char *string) { |
---|
| 79 | /** Kriegt aus #RRGGBBAA oder #RGBA eine Cairo-Surface */ |
---|
| 80 | char buf[2]; // zum temporaeren Speichern der einzelnen umzuwandelnden Zeichen |
---|
| 81 | buf[1] = '\0'; // nur ein Ein-Zeichen-String also |
---|
| 82 | int len = strlen(string); |
---|
| 83 | unsigned char numbers[8]; // rausgefundende Ziffern |
---|
| 84 | int numbers_count; // wie viele Ziffern rausgefunden |
---|
| 85 | cairo_pattern_t *pattern; |
---|
| 86 | |
---|
| 87 | // erst mal schauen, ob ne schoene Raute da ist und auf Laenge (entsprechendes Format) |
---|
| 88 | if(string[0] != '#' || (len != 4 && len != 5 && len != 7 && len != 9)) { |
---|
| 89 | fprintf(stderr, "Bad color: %s -- colors shall have format #RGB or #RRGGBB or #RGBA or #RRGGBBAA\n", string, len); |
---|
| 90 | exit(1); |
---|
| 91 | } |
---|
| 92 | string++; // das "#"-Zeichen braucht keiner mehr |
---|
| 93 | |
---|
| 94 | for(numbers_count=0; numbers_count < len-1; numbers_count++) { |
---|
| 95 | buf[0] = string[numbers_count]; |
---|
| 96 | numbers[numbers_count] = strtol(buf, NULL, 16); |
---|
| 97 | //printf("Read digit %s = 0x%x\n", buf, numbers[numbers_count]); |
---|
| 98 | } |
---|
| 99 | //numbers_count++; // wir zaehlen ab 1 -- komisch, tud er sowieso schon. |
---|
| 100 | |
---|
| 101 | if(numbers_count == 3 || numbers_count == 4) { |
---|
| 102 | // #RGB oder #RGBA |
---|
| 103 | pattern = cairo_pattern_create_rgba( |
---|
| 104 | (double)numbers[0] / (double)0xF, |
---|
| 105 | (double)numbers[1] / (double)0xF, |
---|
| 106 | (double)numbers[2] / (double)0xF, |
---|
| 107 | (numbers_count == 4) ? (double)numbers[3] / (double)0xF : 1 |
---|
| 108 | ); |
---|
| 109 | } else { |
---|
| 110 | // #RRGGBB oder #RRGGBBAA |
---|
| 111 | pattern = cairo_pattern_create_rgba( |
---|
| 112 | (double)(numbers[1] + numbers[0]*0x10) / (double)0xFF, |
---|
| 113 | (double)(numbers[3] + numbers[2]*0x10) / (double)0xFF, |
---|
| 114 | (double)(numbers[5] + numbers[4]*0x10) / (double)0xFF, |
---|
| 115 | (numbers_count == 8) ? (double)(numbers[7] + numbers[6]*0x10) / (double)0xFF : 1 |
---|
| 116 | ); |
---|
| 117 | } |
---|
| 118 | // Fuer Debug-Ausgaben: |
---|
| 119 | { double r,g,b,a; cairo_pattern_get_rgba(pattern, &r, &g, &b, &a); |
---|
| 120 | printf("Allocated color #%s as #%x%x%x, opacity %x\n", string, (unsigned int)(r*256), (unsigned int)(g*256), (unsigned int)(b*256), (unsigned int)(a*256)); } |
---|
| 121 | printf("Farbe fertig.\n"); |
---|
| 122 | return pattern; |
---|
| 123 | } |
---|
| 124 | |
---|
| 125 | error_t parse_option (int key, char *arg, struct argp_state *state) { |
---|
| 126 | //printf("OPTION %x (%c = %i)...\n", key, key, key); |
---|
| 127 | switch(key) { |
---|
| 128 | case 'v': |
---|
| 129 | verbosity = 1; |
---|
| 130 | break; |
---|
| 131 | case 'o': case 'i': |
---|
| 132 | // Ausgabedatei setzen. |
---|
| 133 | output_file = arg; |
---|
| 134 | break; |
---|
| 135 | case 'w': |
---|
| 136 | // Breite setzen. |
---|
| 137 | lochstreifen_set_d_by_width(l, atoi(arg)); |
---|
| 138 | break; |
---|
| 139 | case 'h': |
---|
| 140 | // Hoehe setzen. |
---|
| 141 | lochstreifen_set_d_by_height(l, atoi(arg)); |
---|
| 142 | break; |
---|
| 143 | case 'd': |
---|
| 144 | // Durchmesser setzen |
---|
| 145 | lochstreifen_set_d(l, atoi(arg)); |
---|
| 146 | break; |
---|
| 147 | case 'f': |
---|
| 148 | // Dateiformat setzen |
---|
| 149 | // keine Lust auf enums |
---|
| 150 | if(strcmp(strtolower(arg), "png") == 0) surface_type = 0; |
---|
| 151 | else if(strcmp(strtolower(arg), "svg") == 0) surface_type = 1; |
---|
| 152 | else argp_error(state, "Only PNG and SVG are supported as file formats.\n"); |
---|
| 153 | break; |
---|
| 154 | case -1: |
---|
| 155 | // Emptystart-Bytes |
---|
| 156 | l->empty_start = atoi(arg); |
---|
| 157 | break; |
---|
| 158 | case -2: |
---|
| 159 | // Emptyend-Bytes |
---|
| 160 | l->empty_end = atoi(arg); |
---|
| 161 | break; |
---|
| 162 | case -3: |
---|
| 163 | // hide imagebg |
---|
| 164 | l->hintergrund = NULL; |
---|
| 165 | break; |
---|
| 166 | case -4: |
---|
| 167 | // color imagebg |
---|
| 168 | l->hintergrund = hex2cairo_pattern(arg); |
---|
| 169 | break; |
---|
| 170 | case -5: |
---|
| 171 | // hide tape |
---|
| 172 | l->streifenbg = NULL; |
---|
| 173 | break; |
---|
| 174 | case -6: |
---|
| 175 | // color tapebg |
---|
| 176 | l->streifenbg = hex2cairo_pattern(arg); |
---|
| 177 | break; |
---|
| 178 | case -7: |
---|
| 179 | // hide punched |
---|
| 180 | l->punched = NULL; |
---|
| 181 | break; |
---|
| 182 | case -8: |
---|
| 183 | // color punched |
---|
| 184 | l->punched = hex2cairo_pattern(arg); |
---|
| 185 | break; |
---|
| 186 | case -9: |
---|
| 187 | // hide notpunched |
---|
| 188 | l->notpunched = NULL; |
---|
| 189 | break; |
---|
| 190 | case -10: |
---|
| 191 | // color notpunched |
---|
| 192 | l->notpunched = hex2cairo_pattern(arg); |
---|
| 193 | break; |
---|
| 194 | case -11: |
---|
| 195 | // hide feedholes |
---|
| 196 | l->fuehrung = NULL; |
---|
| 197 | break; |
---|
| 198 | case -12: |
---|
| 199 | // color fuerhung |
---|
| 200 | l->fuehrung = hex2cairo_pattern(arg); |
---|
| 201 | break; |
---|
| 202 | case -13: |
---|
| 203 | // rotation |
---|
| 204 | if(strcmp(arg, "right") == 0) arg = "0"; |
---|
| 205 | else if(strcmp(arg, "bottom") == 0) arg = "1"; |
---|
| 206 | else if(strcmp(arg, "left") == 0) arg = "2"; |
---|
| 207 | else if(strcmp(arg, "top") == 0) arg = "3"; |
---|
| 208 | arg[1] = '\0'; // String ist jetzt auf jeden Fall nur ein zeichen lang |
---|
| 209 | lochstreifen_set_direction(l, atoi(arg), -1, -1); |
---|
| 210 | break; |
---|
| 211 | case -14: |
---|
| 212 | // horizontal spiegeln |
---|
| 213 | lochstreifen_set_direction(l, -1, 1, -1); |
---|
| 214 | break; |
---|
| 215 | case -15: |
---|
| 216 | // vertikal spiegeln |
---|
| 217 | lochstreifen_set_direction(l, -1, -1, 1); |
---|
| 218 | break; |
---|
| 219 | //case ARGP_KEY_END: |
---|
| 220 | //printf("bla..."); |
---|
| 221 | default: |
---|
| 222 | return ARGP_ERR_UNKNOWN; |
---|
| 223 | } // switch |
---|
| 224 | return 0; // success |
---|
| 225 | } |
---|
| 226 | |
---|
| 227 | static struct argp argp = { options, parse_option, "[FILE TO READ FROM]", // Als Argument in der ersten Zeile |
---|
| 228 | "This program uses cairo to draw a punched tape as a PNG or SVG file. Any binary " // Hilfe oben |
---|
| 229 | "data are accepted via stdin or read in from the file given as argument. " |
---|
| 230 | "The produced image is written into stdout or in the filename given by -o." |
---|
| 231 | // mit \v danach koennte man ausfuehrliche Hilfe unten anzeigen. |
---|
| 232 | }; // static struct argp |
---|
| 233 | |
---|
| 234 | cairo_status_t* lochstreifen_out_closure(void *closure, unsigned char *data, unsigned int length) { |
---|
| 235 | // einfach nur in das uebergebene Dateihandle schreiben |
---|
| 236 | fwrite(data, length, 1, (FILE *)closure); |
---|
| 237 | return CAIRO_STATUS_SUCCESS; |
---|
| 238 | } |
---|
| 239 | |
---|
| 240 | int main(int argc, char *argv[]) { |
---|
| 241 | //unsigned char data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; |
---|
| 242 | /*int x; |
---|
| 243 | unsigned char data[256]; |
---|
| 244 | for(x=0;x<256;x++) { |
---|
| 245 | data[x] = (unsigned char) x; |
---|
| 246 | } |
---|
| 247 | draw_lochstreifen(256, data); |
---|
| 248 | return 0;*/ |
---|
| 249 | LOCHSTREIFEN *lochstreifen; |
---|
| 250 | cairo_t *cr; |
---|
| 251 | cairo_surface_t *surface; |
---|
| 252 | byte_t *data; |
---|
| 253 | int length; // laenge des *data-Arrays |
---|
| 254 | FILE *out; |
---|
| 255 | int input_argc; // Index der argv-Option, wo die zu handelnden Dateien stehen. |
---|
| 256 | l = lochstreifen_new(); |
---|
| 257 | lochstreifen = l; |
---|
| 258 | |
---|
| 259 | // Argumente parsen |
---|
| 260 | argp_parse(&argp, argc, argv, 0, &input_argc, NULL); |
---|
| 261 | |
---|
| 262 | //DPRINTF("Stop.\n"); |
---|
| 263 | //return 0; |
---|
| 264 | |
---|
| 265 | // Daten einlesen |
---|
| 266 | if(input_argc < argc) { |
---|
| 267 | FILE *fh; |
---|
| 268 | DPRINTF("Reading from file %s\n", argv[input_argc]); |
---|
| 269 | fh = fopen(argv[input_argc], "r"); |
---|
| 270 | |
---|
| 271 | if(fh == NULL) { |
---|
| 272 | perror("opening input file"); |
---|
| 273 | exit(1); |
---|
| 274 | } |
---|
| 275 | |
---|
| 276 | length = file_get_contents(fh, &data); |
---|
| 277 | fclose(fh); |
---|
| 278 | } else { |
---|
| 279 | DPRINTF("Reading from stdin, type [STRG]+[D] to generate paper tape from data\n"); |
---|
| 280 | length = file_get_contents(stdin, &data); |
---|
| 281 | } |
---|
| 282 | DPRINTF("Read in %d bytes\n", length); |
---|
| 283 | |
---|
| 284 | // Ausgabestream oeffnen |
---|
| 285 | if(output_file != NULL) { |
---|
| 286 | out = fopen(output_file, "w"); |
---|
| 287 | |
---|
| 288 | if(out == NULL) { |
---|
| 289 | perror("opening output file"); |
---|
| 290 | exit(1); |
---|
| 291 | } |
---|
| 292 | DPRINTF("File %s successfully opened for writing\n", output_file); |
---|
| 293 | } else { |
---|
| 294 | DPRINTF("Writing image data to stdout\n"); |
---|
| 295 | out = stdout; |
---|
| 296 | } |
---|
| 297 | |
---|
| 298 | /*int x; |
---|
| 299 | for(x=0; x<length; x++) { |
---|
| 300 | printf("%i von %i: 0x%x (=%c)\n", x, length, data[x], data[x]); |
---|
| 301 | }*/ |
---|
| 302 | |
---|
| 303 | lochstreifen_set_data(l, length, data, -1, -1); |
---|
| 304 | |
---|
| 305 | // Surface erstellen |
---|
| 306 | if(surface_type == 0) { // PNG |
---|
| 307 | cairo_status_t status; |
---|
| 308 | surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, |
---|
| 309 | lochstreifen_get_width(l), |
---|
| 310 | lochstreifen_get_height(l) |
---|
| 311 | ); |
---|
| 312 | cr = cairo_create(surface); |
---|
| 313 | lochstreifen_draw(l, cr); |
---|
| 314 | |
---|
| 315 | status = cairo_surface_write_to_png_stream(surface, (cairo_write_func_t)lochstreifen_out_closure, out); |
---|
| 316 | DPRINTF("paper tape PNG generated: %s\n", cairo_status_to_string(status)); |
---|
| 317 | } else if(surface_type == 1) { // SVG |
---|
| 318 | surface = (cairo_surface_t *)cairo_svg_surface_create_for_stream( |
---|
| 319 | lochstreifen_out_closure, out, |
---|
| 320 | (double)lochstreifen_get_width(l), |
---|
| 321 | (double)lochstreifen_get_height(l)); |
---|
| 322 | |
---|
| 323 | cr = cairo_create(surface); |
---|
| 324 | lochstreifen_draw(l, cr); |
---|
| 325 | DPRINTF("paper tape SVG generated: %s\n", cairo_status_to_string(cairo_surface_status(surface))); |
---|
| 326 | } else { |
---|
| 327 | fprintf(stderr, "Bad Surface Type %i", surface_type); |
---|
| 328 | return -42; |
---|
| 329 | } // if surface_type |
---|
| 330 | |
---|
| 331 | return 0; |
---|
| 332 | } // main |
---|