source: projects/visualisator/cli.c @ 6

Last change on this file since 6 was 6, checked in by sven, 11 years ago

Completed documentation (everything in english) and licence issues (everything is
GPLv3 now).

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