source: projects/schriften/papertapefont.c @ 12

Last change on this file since 12 was 12, checked in by sven, 16 years ago

Since some time I wanted to develop some C routines for papertape handling.
These routines (just written, they compile fine, but not tested at all)
implement a complete own ASCII file scheme (very simple) and a nice C
interface, with some nice object like structures and a alphanumeralically
sorted single linked list in the background. No dependencies like Glib. Code
should compile on Windows, too (Yeah, I've even emulated an own getopts()
because stupid Windows has no getopts).

Handling PaperTape Font files is quite performant, by using the utility
program (as you can get by defining STANDALONE, e.g. with $ gcc ...
-DSTANDALONE=1) you can even edit the font file online or with external
binary editors, like the new GtkPaperTape (with Gtkmm in C++!) is supposed to
be.

Espacially very simple translation between existing font tools is possible,
as a side effect:

$ ./perl-font-tool.pl abcdefghijklmnopqrstuvwxyz123456789 | ./ctool -i font

-- Sven @ workstation dual screen 19" in la celeste

File size: 14.7 KB
Line 
1/**
2 * The Paper Tape Project -- Font subproject
3 * PaperTapeFont C Library / Stand alone frontend
4 *
5 * This file contains library routines (see papertapefont.h) for
6 * reading and writing "papertape font" files, as well as a
7 * stand alone program.
8 *
9 * To compile the CLI program, define STANDALONE, e.g.
10 * with
11 *
12 *   gcc papertapefont.c -DSTANDALONE=1
13 *
14 * Initially written in a few hours (500 lines code) between
15 * 02:00 and 06:00 at 04.09.2008
16 *
17 * (c) 2008 Sven Köppel
18 *
19 * This program is free software; you can redistribute
20 * it and/or modify it under the terms of the GNU General
21 * Public License as published by the Free Software
22 * Foundation; either version 3 of the License, or (at
23 * your option) any later version.
24 *
25 * This program is distributed in the hope that it will
26 * be useful, but WITHOUT ANY WARRANTY; without even the
27 * implied warranty of MERCHANTABILITY or FITNESS FOR A
28 * PARTICULAR PURPOSE. See the GNU General Public License
29 * for more details.
30 *
31 * You should have received a copy of the GNU General
32 * Public License along with this program; if not, see
33 * http://www.gnu.org/licenses/.
34 *
35 **/
36
37#include "papertapefont.h"
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41
42#define PAPERTAPE_FONT_MAX_LINE_LENGTH  100
43
44byte_t papertape_font_line2byte(const char *line);
45
46PAPERTAPE_FONT* papertape_font_new_from_file(FILE* fh) {
47        PAPERTAPE_FONT* font;
48        char line[PAPERTAPE_FONT_MAX_LINE_LENGTH];
49        char ascii_id;
50        byte_t* bytes;
51        int bytes_n;
52        int x;
53
54        // allocate new font object
55        font = malloc(sizeof(PAPERTAPE_FONT));
56        if(!font || !fh)
57                return NULL;
58
59        // set up as ensurance, against segfaults or simple fonts:
60        font->ascii_id = PAPERTAPE_FONT_CHARACTER_SPACING_ASCII_ID;
61        font->bytes = PAPERTAPE_FONT_CHARACTER_SPACING_DEFAULT_BYTES;
62        font->bytes_n = sizeof(PAPERTAPE_FONT_CHARACTER_SPACING_DEFAULT_BYTES);
63       
64        /*{
65                byte_t[] def_spacing = PAPERTAPE_FONT_CHARACTER_SPACING_DEFAULT_BYTES;
66                papertape_font_set_char(font, PAPERTAPE_FONT_CHARACTER_SPACING_ASCII_ID,
67                        def_spacing, sizeof(def_spacing));
68               
69        }*/
70
71        // now parse file.
72
73        // for each character package...
74        while(fgets(line, PAPERTAPE_FONT_MAX_LINE_LENGTH, fh)) {
75                // skip comment lines. They are only allowed at
76                // index positions, not in the data byte lines.
77                if(line[0] == '#')
78                        continue;
79
80                ascii_id = line[0];
81                bytes_n = atoi(&line[1]); // atoi will remove spaces
82                bytes = calloc(sizeof(byte_t), bytes_n); // allocate place for bytes
83
84                // parse the bytes notation
85                for(x=0; x < bytes_n; x++) {
86                        // break at end of line
87                        if(!fgets(line, PAPERTAPE_FONT_MAX_LINE_LENGTH, fh))
88                                break;
89                        // add parsed line to byte array
90                        bytes[x] = papertape_font_line2byte(line);
91                }
92
93                papertape_font_set_char(font, ascii_id, bytes, bytes_n);
94                // papertape_font_set_char copies the byte, so we free them here
95                free(bytes);
96        }
97}
98
99/**
100 * Parse a line to a byte. This is only used internally.
101 * Yeah, and it would be more performant to define this as a Macro, but that
102 * can be performed using gcc -O2, too.
103 **/
104byte_t papertape_font_line2byte(const char *line) {
105        byte_t value = 0;
106        #define isPunched(pos, add) if(line[pos] != ' ') { value += add; }
107        isPunched(0 ,  0);
108        isPunched(1 ,  1);
109        isPunched(2,   2);
110        isPunched(3,   4);
111        isPunched(4,   8);
112        isPunched(5,  16);
113        isPunched(6,  32);
114        isPunched(7,  64);
115        isPunched(8, 128);
116        return value;
117}
118
119
120/**
121 * This is the main function how to add or change a character in the papertape_font.
122 * Internally, the papertape_font works with a ascii ordered single linked list. So
123 * there's no other write access to that list than this function.
124 *
125 * @param font The Papertape Font object, e.g. made by papertape_font_new_...()
126 * @param ascii_id The ASCII value of the character you want to add.
127 * @param bytes Your byte array which is supposed to represent the character. The function copies it.
128 * @param bytes_n The length of your byte array (counting from 1, of course)
129 * @return 0 if successfully added, 1 otherwise. (It will never return 1 ;-) )
130 **/
131int papertape_font_set_char(PAPERTAPE_FONT* font, char ascii_id, const byte_t* bytes, int bytes_n) {
132        PAPERTAPE_FONT_CHAR *current;
133        PAPERTAPE_FONT_CHAR *next;
134       
135        // allocate the new character structure
136        PAPERTAPE_FONT_CHAR *buf;
137        buf = malloc(sizeof(PAPERTAPE_FONT_CHAR));
138
139        // copy all values to structure
140        buf->ascii_id = ascii_id;
141        buf->bytes_n = bytes_n;
142        buf->bytes = malloc(bytes_n);
143        memcpy(buf->bytes, bytes, bytes_n);
144        buf->next = NULL;
145
146        // Magic ascii id (typically a non printable character) for character spacing
147        if(ascii_id == PAPERTAPE_FONT_CHARACTER_SPACING_ASCII_ID) {
148                font->bytes = buf->bytes;
149                font->bytes_n = buf->bytes_n;
150                free(buf); // don't need it.
151                return 0;
152        }
153
154        // Start going throught the list
155        current = font; // Start with the font object as first list member.
156        while(current->next != NULL) {
157                if(current->next->ascii_id == ascii_id) {
158                        // the next one has the same ascii_id as we have (perhaps
159                        // we are 'c' and the next is 'c', too). So replace the next
160                        // one.
161                        next = current->next;  // copy the pointer somewhere else
162                        current->next = buf;   // replace the next pointer with our char
163                        free(next);            // delete the old next pointer.
164                        return 0;
165                } else if(current->next->ascii_id > ascii_id) {
166                        // the next one has a bigger ascii_id as we have (perhapse
167                        // we are 'c' while the next is 'e'). So pack our character
168                        // right here, between current and next.
169                        next = current->next;  // copy the pointer somewhere else
170                        current->next = buf;   // replace the next pointer with our char
171                        buf->next = next;      // pack the next pointer after our pointer
172                        return 0;
173                } else {
174                        // the next one has a lower ascii_id as we have, so we have
175                        // to go on throught the list
176                        current = current->next;
177                }
178        }
179
180        // we have reached the end of the list and haven't found any ascii_id biggger
181        // or equal to ours. So we simply add our character to the list.
182        current->next = buf;
183        return 0;
184}
185
186/**
187 * This is the main function from the papertape_font package. Using this function, you
188 * can render any ASCII string to a papertape label, using the current font.
189 *
190 * The return byte array will be malloced, and you'll have after calling the only
191 * link to that array. So feel free to free() it, if you don't need it any more.
192 *
193 * If there's a non printable character in your string, this function will print out an
194 * error message at stderr and skip that character. Use papertape_font_string_is_printable
195 * if you want to check wheter your string contains non printable characters.
196 *
197 * Internally speaken, this is the only read access function to the internal single
198 * linked font character list.
199 *
200 **/
201byte_t* papertape_font_get_label(PAPERTAPE_FONT* font, const char* string) {
202        PAPERTAPE_FONT_CHAR* current;
203        //int string_len = strlen(string);
204        int x; // to count throught the string
205        int size = 0; // the size of the output byte array
206        byte_t* output;
207        int pos; // current position in output array
208
209        // first step: Count the neccessary size for the output byte array
210        for(x=0; string[x] != '\0'; x++) {
211                // skip the font spacing at one character in the string
212                // (because it's only between the letters)
213                if(x != 0)
214                        size += font->bytes_n;
215
216                // go throught the list and try to find the current character.
217                current = font;
218                while(1) {
219                        if(current->ascii_id == string[x]) {
220                                size += current->bytes_n;
221                                break;
222                        } else if(current->next != NULL) {
223                                current = current->next;
224                                continue;
225                        } else {
226                                // We have reached the end of the character
227                                // list, but the current character is not available!
228                                fprintf(stderr, "papertape_font_get_label: Character \"%c\" in \"%s\" not printable\n",
229                                        string[x], string);
230                                break;
231                        }
232                }
233        }
234
235        // now malloc the output byte array and generate it
236        output = malloc(size);
237        for(x=0,pos=0; string[x] != '\0'; x++) {
238                // go throught the list, another time
239                current = font;
240                while(1) {
241                        if(current->ascii_id == string[x]) {
242                                memcpy(output+pos, current->bytes, current->bytes_n);
243                                pos += current->bytes_n;
244                                break;
245                        } else if(current->next != NULL) {
246                                current = current->next;
247                                continue;
248                        } // else: Character not found. Ignore it,
249                        //   because error already printed at first run.
250                }
251                // add the character spacer after each character
252                if(x+1 < strlen(string)) { // but not at the last character
253                        memcpy(output+pos, font->bytes, font->bytes_n);
254                        pos += font->bytes_n;
255                }
256        }
257
258        // we are done.
259        return output;
260}
261
262/**
263 * Checks if there's any non printable character in your string. Papertape fonts don't have
264 * to have bit masks for every character, so use this function to check your string.
265 * papertape_font_get_label will output errors to stderr if it finds unprintable
266 * characters.
267 *
268 * @returns 1 if all characters are printable, 0 otherwise.
269 **/
270int papertape_font_string_is_printable(PAPERTAPE_FONT *font, const char* string) {
271        PAPERTAPE_FONT_CHAR* current;
272        int x;
273
274        for(x=0; string[x] != '\0'; x++) {
275                current = font;
276                while(1) {
277                        if(current->ascii_id == string[x])
278                                // this character is clean.
279                                break;
280                        else if(current->next != NULL)
281                                // have not found character yet.
282                                current = current->next;
283                        else
284                                // at the end of list -- character not found!
285                                return 0;
286                }
287        }
288
289        return 1; // No non printable char found.
290}
291
292/**
293 * Write the current PAPERTAPE_FONT (back) out to a file.
294 * FILE mus be a writeable handle.
295 **/
296int papertape_font_write_to_file(PAPERTAPE_FONT* font, FILE *fh) {
297        PAPERTAPE_FONT_CHAR* current;
298        int x;
299
300        // Write first lines.
301        fputs(PAPERTAPE_FONT_WRITE_FIRST_LINES, fh);
302       
303        // now go throught list and write all characters out.
304        current = font;
305        while(1) {
306                // write index line
307                fprintf(fh, "%c %i\n", current->ascii_id, current->bytes_n);
308                // write data binary things.
309                for(x=0; x<current->bytes_n; x++) {
310                        #define paintPunched(check) fputc((current->bytes[x] & check) ?\
311                                fputc(PAPERTAPE_FONT_WRITE_LOGICAL_1_CHARACTER, fh) : fputc(' ', fh), fh)
312                        paintPunched(  0);
313                        paintPunched(  1);
314                        paintPunched(  2);
315                        paintPunched(  4);
316                        paintPunched(  8);
317                        paintPunched( 16);
318                        paintPunched( 32);
319                        paintPunched( 64);
320                        paintPunched(128);
321                        fputc('\n', fh);
322                }
323
324                // and go on. Or break, if at end.
325                if(current->next != NULL)
326                        current = current->next;
327                else
328                        break;
329        }
330}
331
332/* If we want it to be a standalone program */
333#ifdef STANDALONE
334int file_get_contents(FILE *stream, byte_t **content) {
335        byte_t buf[4096];
336        size_t bytes; // gerade eben eingelesene bytes
337        byte_t *str = NULL;
338        size_t total_bytes = 0;     // alle bis jetzt eingelesenen bytes
339        size_t total_allocated = 0;
340        byte_t *tmp; // fuers realloc
341
342        while(!feof(stream)) {
343                bytes = fread(buf, 1, sizeof(buf), stream);
344
345                while( (total_bytes + bytes) > total_allocated) {
346                        if(str)
347                                total_allocated *= 2;
348                        else
349                                total_allocated = bytes > sizeof(buf) ? bytes : sizeof(buf);
350
351                        tmp = realloc(str, total_allocated);
352
353                        if(tmp == NULL) {
354                                fprintf(stderr, "*** file_get_contents ERROR*** Could not reallocate\n");
355                                //return length; // Fehler - das eingelesene zumindest zurueckgeben.
356                                // nee, gar nichts zurückgeben. Das geht so nicht.
357                                return 0;
358                        }
359
360                        str = tmp;
361                } // while innen
362
363                memcpy(str + total_bytes, buf, bytes);
364                total_bytes += bytes;
365        } // while
366
367        if(total_allocated == 0)
368                str = malloc(1); // something empty. Just to be not NULL...
369        //str[total_bytes] = '\0'; // wenns ein string wäre.
370
371        *content = str;
372        return total_bytes;
373}
374
375int main(int argc, char **argv) {
376        enum {
377                RENDER_TEXT,
378                WRITE_CHAR,
379                EXPORT,
380                IMPORT,
381                NOTHING
382        } action;
383        action = NOTHING;
384        char *schematics = PAPERTAPE_FONT_SCHEMATICS_DEFAULT;
385        char *render_text;
386        char write_char;
387        char *font_name;
388        FILE *font_file;
389        PAPERTAPE_FONT *font;
390        int c;
391       
392        // poor man's getopt, for windows portability
393        for(c=1; c<argc; c++) {
394                if(strlen(argv[c]) == 2 && *argv[c] == '-') {
395                        argv[c]++; // shift "-" flag thingy.
396                        switch(*argv[c]) {
397                                case 'h':
398                                        fprintf(stderr, "Usage: %s [OPTIONS...] [A PAPERTAPE FONT FILE]\n"
399                                                "The Papertape font utilitie program.\n\n"
400                                                "   optional parameters:\n"
401                                                "   -h              display this help message\n"
402                                                "   -l [TEXT]       Render this text to STDOUT\n"
403                                                "   -w [ASCII ID]   Set the character with that ASCII ID, read from STDIN\n"
404                                                "   -o              Export the font file, according to schematics, to STDOUT\n"
405                                                "   -i              Import to font file (overwrite it), according to schematics, from STDIN\n\n"
406                                                "   -s [Sch string] Define own schematics for -i/-o. Default is:\n"
407                                                "                   %s\n",
408                                                argv[0], PAPERTAPE_FONT_SCHEMATICS_DEFAULT);
409                                        return 0;
410
411                                case 'l':
412                                        action = RENDER_TEXT;
413                                        render_text = argv[++c];
414                                        break;
415                               
416                                case 'w':
417                                        action = WRITE_CHAR;
418                                        write_char = *argv[++c];
419                                        break;
420                                       
421                                case 'o': action = EXPORT; break;
422                                case 'i': action = IMPORT;  break;
423                               
424                                case 's':
425                                        schematics = argv[++c];
426                        } // switch
427                } // if flag
428                else {
429                        // not flag but... Papertape Font file!
430                        font_name = argv[c];
431                }
432        } // for each argv
433
434        // Open Font File
435        if(font_name) {
436                char *mode;
437                switch(action) {
438                        case RENDER_TEXT:
439                        case EXPORT:       mode = "r";  break;
440                        case WRITE_CHAR:   mode = "rw"; break;
441                        case IMPORT:       mode = "w";  break;
442                        default:
443                                fprintf(stderr, "Error: No action specified. Call %s -h\n", argv[0]);
444                                return 1;
445                }
446       
447                font_file = fopen(font_name, mode);
448                if(!font_file) {
449                        perror("Opening font file");
450                        return 2;
451                }
452        } else {
453                fprintf(stderr, "Error: No font file specified. Call %s -h\n", argv[0]);
454        }
455       
456        // Stupid: papertapefont.c:431: Warnung: Zuweisung erzeugt Zeiger von Ganzzahl ohne Typkonvertierung
457        // Adding (PAPERTAPE_FONT*) did the job.
458        font = (PAPERTAPE_FONT*) papertape_new_from_file(font_file);
459        if(font == NULL) {
460                fprintf(stderr, "Error creating PAPERTAPE_FONT object\n");
461                return 3;
462        }
463
464        // Perform action
465        if(action == RENDER_TEXT && render_text != NULL) {
466                papertape_font_get_label(font, render_text);
467        } else if(action == WRITE_CHAR) {
468                byte_t *contents;
469                int bytes = file_get_contents(stdin, &contents);
470                printf("Read in %i bytes for letter %c (seen as byte: 0x%x)\n", bytes, write_char, write_char);
471                papertape_font_set_char(font, write_char, contents, bytes);
472        } else if(action == EXPORT) {
473                fprintf(stderr, "Printing file %s with schematics %s\n", font_name, schematics);
474                puts(papertape_font_get_label(font, schematics));
475        } else if(action == IMPORT) {
476                byte_t *contents;
477                int bytes, x;
478               
479                fprintf(stderr, "Reading from stdin, expecting schematics %s\n", schematics);
480                bytes = file_get_contents(stdin, &contents);
481               
482                fprintf(stderr, "To be implemented...\n");
483               
484                // Parse schematics
485                /*for(x=0; x<strlen(schematics); x++) {
486                        papertape_font_set_char(font, schematics[x],
487                }*/
488               
489        } else {
490                fprintf(stderr, "No correct call! See %s -h\n", argv[0]);
491        }
492        return 0;
493}
494#endif /* STANDALONE */
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