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 | * powerful cross platform working 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 | * This program uses only basis libc functions (not even getopt), |
---|
15 | * so you can compile it with any M$ Windows C compiler, too. |
---|
16 | * See the document "FONT_FILES.txt" in the source code directory |
---|
17 | * for an introducion and manual. |
---|
18 | * |
---|
19 | * Initially written in a few hours (500 lines code) between |
---|
20 | * 02:00 and 06:00 at 04.09.2008 |
---|
21 | * Improved and multiple times rewritten the next day to |
---|
22 | * 1000 lines ;-) |
---|
23 | * |
---|
24 | * (c) 2008 Sven Köppel |
---|
25 | * |
---|
26 | * This program is free software; you can redistribute |
---|
27 | * it and/or modify it under the terms of the GNU General |
---|
28 | * Public License as published by the Free Software |
---|
29 | * Foundation; either version 3 of the License, or (at |
---|
30 | * your option) any later version. |
---|
31 | * |
---|
32 | * This program is distributed in the hope that it will |
---|
33 | * be useful, but WITHOUT ANY WARRANTY; without even the |
---|
34 | * implied warranty of MERCHANTABILITY or FITNESS FOR A |
---|
35 | * PARTICULAR PURPOSE. See the GNU General Public License |
---|
36 | * for more details. |
---|
37 | * |
---|
38 | * You should have received a copy of the GNU General |
---|
39 | * Public License along with this program; if not, see |
---|
40 | * http://www.gnu.org/licenses/. |
---|
41 | * |
---|
42 | **/ |
---|
43 | |
---|
44 | #include "papertapefont.h" |
---|
45 | #include <stdio.h> |
---|
46 | #include <stdlib.h> |
---|
47 | #include <string.h> |
---|
48 | |
---|
49 | byte_t papertape_font_line2byte(const char *line); |
---|
50 | void papertape_font_byte2line(const byte_t byte, FILE *write); |
---|
51 | |
---|
52 | /** |
---|
53 | * Allocate a new PAPERTAPE_FONT object, without any character. |
---|
54 | * You can use papertape_font_new_from_file() if you want to create |
---|
55 | * a PAPERTAPE_FONT object from file or use papertape_font_import_from_file() |
---|
56 | * or papertape_font_import_from_schematics() to fill your PAPERTAPE_FONT |
---|
57 | * object. |
---|
58 | **/ |
---|
59 | // implemented as a Macro due to simplicity. |
---|
60 | |
---|
61 | /** |
---|
62 | * Load a new papertape font from the file called "filename". This |
---|
63 | * is some more luxurious than calling the single functions. |
---|
64 | * @return allocated font object or NULL if not possible. |
---|
65 | **/ |
---|
66 | PAPERTAPE_FONT* papertape_font_new_from_file(const char* filename) { |
---|
67 | FILE* file; |
---|
68 | PAPERTAPE_FONT* font = papertape_font_new(); |
---|
69 | if(!font) |
---|
70 | return NULL; |
---|
71 | |
---|
72 | file = fopen(filename, "r"); |
---|
73 | if(!file) { |
---|
74 | perror("Opening papertape font file"); |
---|
75 | return NULL; |
---|
76 | } |
---|
77 | |
---|
78 | papertape_font_load_file(font, file); |
---|
79 | return font; |
---|
80 | } |
---|
81 | |
---|
82 | /** |
---|
83 | * This will load all characters found in the filehandle (which must be readable). |
---|
84 | * Characters loaded here will overwrite previous characters, if present. |
---|
85 | **/ |
---|
86 | void papertape_font_load_file(PAPERTAPE_FONT* font, FILE* fh) { |
---|
87 | #define MAX_LINE 100 |
---|
88 | #define MAX_BYTES 30 |
---|
89 | #define MAX_NAME 30 |
---|
90 | |
---|
91 | // Buffer for the line string currently read in |
---|
92 | char line[MAX_LINE]; |
---|
93 | // Buffer for the name string currently read in |
---|
94 | char name[MAX_NAME] = ""; |
---|
95 | // Buffer for last name string read in |
---|
96 | char last_name[MAX_NAME] = ""; |
---|
97 | // Buffer for the bytes currently read in |
---|
98 | byte_t bytes[MAX_BYTES]; |
---|
99 | // Real number of bytes already read in |
---|
100 | int bytes_n = 0; |
---|
101 | // the current ascii_id |
---|
102 | char ascii_id = 0; |
---|
103 | // the last ascii_id |
---|
104 | char last_ascii_id = 0; |
---|
105 | // if we haven't already read in one byte from the new |
---|
106 | // character, this is set to 1. This will force reading in the |
---|
107 | // current byte. |
---|
108 | int new_character = 1; |
---|
109 | |
---|
110 | // for each character package... |
---|
111 | while(fgets(line, MAX_LINE, fh) != NULL) { |
---|
112 | /* |
---|
113 | * the layout of a typical * the layout of a typical long |
---|
114 | * ASCII line: * special line: |
---|
115 | * * |
---|
116 | * |123.45678|x * |123.45678| =named_thing comments |
---|
117 | * 012345678901 * 01234567890123456789012345678... |
---|
118 | * * || |
---|
119 | * length: 12 characters. * 12 chars-^^- char no. 12 |
---|
120 | * * |
---|
121 | */ |
---|
122 | |
---|
123 | /* remove "\n" lines at end. We don't need them. */ |
---|
124 | if(line[strlen(line)-1] == '\n') |
---|
125 | line[strlen(line)-1] = '\0'; |
---|
126 | |
---|
127 | /* skip lines which are not a paper tape or which have no id */ |
---|
128 | if(line[0] != PAPERTAPE_FONT_BORDER || strlen(line) < 12) |
---|
129 | continue; |
---|
130 | |
---|
131 | ascii_id = line[11]; |
---|
132 | //fprintf(stderr, "Read in line ascii_id 0x%x (%c)\n", ascii_id, ascii_id); |
---|
133 | |
---|
134 | // strlen: to avoid segfaults |
---|
135 | if(strlen(line) > (11 + strlen(PAPERTAPE_FONT_SPECIAL_ID)) && |
---|
136 | strncmp(line+11, PAPERTAPE_FONT_SPECIAL_ID, strlen(PAPERTAPE_FONT_SPECIAL_ID)) == 0) { |
---|
137 | // we have a special value with a full name |
---|
138 | ascii_id = 0; |
---|
139 | int z; |
---|
140 | // copy name |
---|
141 | strncpy(name, &line[11+strlen(PAPERTAPE_FONT_SPECIAL_ID)], MAX_NAME); |
---|
142 | // truncate name to only one word |
---|
143 | for(z=0; !(name[z] == ' ' || name[z] == '\0' || name[z] == '\t'); z++); |
---|
144 | name[z] = '\0'; |
---|
145 | } |
---|
146 | |
---|
147 | // if we are still at the same byte like the last one... |
---|
148 | if(ascii_id == last_ascii_id || //new_character || |
---|
149 | ( ascii_id == 0 && (strcmp(name, last_name) == 0) ) |
---|
150 | ) { |
---|
151 | //fprintf(stderr, "Save line for 0x%x as no. %i\n", ascii_id, bytes_n+1); |
---|
152 | // ...then add this byte to the current char byte array |
---|
153 | if(bytes_n < MAX_BYTES) |
---|
154 | // only store if enough place in bytes[] array |
---|
155 | bytes[bytes_n++] = papertape_font_line2byte(line); |
---|
156 | // first byte stored, no more new character |
---|
157 | new_character = 0; |
---|
158 | } else { |
---|
159 | // we are at a new char, so let's push the last char to the list |
---|
160 | if(last_ascii_id == 0) |
---|
161 | papertape_font_set_special(font, last_name, bytes, bytes_n); |
---|
162 | else |
---|
163 | papertape_font_set_char(font, last_ascii_id, bytes, bytes_n); |
---|
164 | |
---|
165 | // store first byte from new character |
---|
166 | bytes[0] = papertape_font_line2byte(line); |
---|
167 | bytes_n = 1; |
---|
168 | // and tell the next loop that it has to read in. |
---|
169 | new_character = 1; |
---|
170 | } |
---|
171 | |
---|
172 | // Don't force to read in any more. |
---|
173 | //if(last_ascii_id == ascii_id) |
---|
174 | // new_character = 0; |
---|
175 | last_ascii_id = ascii_id; |
---|
176 | strcpy(last_name, name); |
---|
177 | } // while file |
---|
178 | |
---|
179 | // we are done, and there's still one character left to push to the list |
---|
180 | if(last_ascii_id == 0) |
---|
181 | papertape_font_set_special(font, last_name, bytes, bytes_n); |
---|
182 | else |
---|
183 | papertape_font_set_char(font, last_ascii_id, bytes, bytes_n); |
---|
184 | |
---|
185 | // rewind the file handle (if writing is called on the same file, etc.) |
---|
186 | rewind(fh); |
---|
187 | } |
---|
188 | |
---|
189 | /** |
---|
190 | * Load Papertape font characters from binary array in order which is given |
---|
191 | * by the "schematics" string. |
---|
192 | * |
---|
193 | **/ |
---|
194 | void papertape_font_load_schematics(PAPERTAPE_FONT* font, const char *schematics, const byte_t *contents, int content_len) { |
---|
195 | int schematics_len = strlen(schematics); |
---|
196 | int schematics_x; |
---|
197 | int content_offset, content_start, content_end; |
---|
198 | for(schematics_x=0, content_offset=0; schematics_x < schematics_len; schematics_x++) { |
---|
199 | // find start of current character |
---|
200 | for(content_start = content_offset; content_start < content_len && |
---|
201 | contents[content_start] == 0; content_start++); |
---|
202 | // find end of current character |
---|
203 | for(content_end = content_start; content_end < content_len && |
---|
204 | contents[content_end] != 0; content_end++); |
---|
205 | // make new character between content_start and content_end |
---|
206 | papertape_font_set_char(font, schematics[schematics_x], |
---|
207 | contents + content_start, content_end - content_start); |
---|
208 | // save offset for next character |
---|
209 | content_offset = content_end + 1; |
---|
210 | } |
---|
211 | } |
---|
212 | |
---|
213 | |
---|
214 | |
---|
215 | /** |
---|
216 | * Parse a line to a byte. This is only used internally. |
---|
217 | * Yeah, and it would be more performant to define this as a Macro, but that |
---|
218 | * can be performed using gcc -O2, too. |
---|
219 | **/ |
---|
220 | byte_t papertape_font_line2byte(const char *line) { |
---|
221 | byte_t value = 0; |
---|
222 | #define isPunched(pos, add) if(line[pos] != ' ') { value += add; } |
---|
223 | /* left border */ |
---|
224 | isPunched(1 , 1); |
---|
225 | isPunched(2, 2); |
---|
226 | isPunched(3, 4); |
---|
227 | /* feed hole */ |
---|
228 | isPunched(5, 8); |
---|
229 | isPunched(6, 16); |
---|
230 | isPunched(7, 32); |
---|
231 | isPunched(8, 64); |
---|
232 | isPunched(9, 128); |
---|
233 | /* right border */ |
---|
234 | return value; |
---|
235 | } |
---|
236 | |
---|
237 | /** |
---|
238 | * This is the invert function of papertape_font_line2byte. It writes to |
---|
239 | * file handle "write" a line, but without Newline! |
---|
240 | * |
---|
241 | **/ |
---|
242 | void papertape_font_byte2line(const byte_t byte, FILE *write) { |
---|
243 | #define paintPunched(check) fputc((byte & check) ?\ |
---|
244 | PAPERTAPE_FONT_WRITE_LOGICAL_1 : ' ', write) |
---|
245 | fputc(PAPERTAPE_FONT_BORDER, write); /* left border */ |
---|
246 | paintPunched( 1); /* bit 1 */ |
---|
247 | paintPunched( 2); /* bit 2 */ |
---|
248 | paintPunched( 4); /* bit 3 */ |
---|
249 | fputc(PAPERTAPE_FONT_FEED_HOLE, write); /* feed hole */ |
---|
250 | paintPunched( 8); /* bit 4 */ |
---|
251 | paintPunched( 16); /* bit 5 */ |
---|
252 | paintPunched( 32); /* bit 6 */ |
---|
253 | paintPunched( 64); /* bit 7 */ |
---|
254 | paintPunched(128); /* bit 8 */ |
---|
255 | fputc(PAPERTAPE_FONT_BORDER, write); /* right border */ |
---|
256 | } |
---|
257 | |
---|
258 | |
---|
259 | /** |
---|
260 | * This function adds or changes special characters in the papertape font. |
---|
261 | * These characters are typically white spaces or word spacings. |
---|
262 | * |
---|
263 | * @param font The Papertape Font object, e.g. made by papertape_font_new_...() |
---|
264 | * @param name The Name ID |
---|
265 | * @param bytes Your byte array which is supposed to represent the character. The function copies it. |
---|
266 | * @param bytes_n The length of your byte array (counting from 1, of course) |
---|
267 | * @return 0 if successfully added, 1 otherwise. (It will never return 1 ;-) ) |
---|
268 | **/ |
---|
269 | PAPERTAPE_FONT_CHAR* papertape_font_set_special(PAPERTAPE_FONT* font, const char *name, const byte_t* bytes, int bytes_n) { |
---|
270 | // allocate the new character structure |
---|
271 | PAPERTAPE_FONT_CHAR *buf; |
---|
272 | buf = (PAPERTAPE_FONT_CHAR*) malloc(sizeof(PAPERTAPE_FONT_CHAR)); |
---|
273 | |
---|
274 | // copy all values to structure |
---|
275 | buf->ascii_id = 0x0; |
---|
276 | buf->bytes_n = bytes_n; |
---|
277 | buf->bytes = (byte_t*) malloc(bytes_n); |
---|
278 | memcpy(buf->bytes, bytes, bytes_n); |
---|
279 | buf->name = (char*) malloc(strlen(name)); |
---|
280 | strcpy(buf->name, name); |
---|
281 | |
---|
282 | // Pack it at the very start |
---|
283 | buf->next = (font->next != NULL) ? font->next : NULL; |
---|
284 | font->next = buf; |
---|
285 | |
---|
286 | return buf; |
---|
287 | } |
---|
288 | |
---|
289 | /** |
---|
290 | * This is the main function how to add or change a character in the papertape_font. |
---|
291 | * Internally, the papertape_font works with a ascii ordered single linked list. So |
---|
292 | * there's no other write access to that list than this function. |
---|
293 | * |
---|
294 | * If you want to set/add characters with special long names, use papertape_font_set_special_char. |
---|
295 | * |
---|
296 | * @param font The Papertape Font object, e.g. made by papertape_font_new_...() |
---|
297 | * @param ascii_id The ASCII value of the character you want to add. |
---|
298 | * @param bytes Your byte array which is supposed to represent the character. The function copies it. |
---|
299 | * @param bytes_n The length of your byte array (counting from 1, of course) |
---|
300 | * @return A link to the generated character. You don't need it, anyway. |
---|
301 | **/ |
---|
302 | PAPERTAPE_FONT_CHAR* papertape_font_set_char(PAPERTAPE_FONT* font, char ascii_id, const byte_t* bytes, int bytes_n) { |
---|
303 | PAPERTAPE_FONT_CHAR *current; |
---|
304 | PAPERTAPE_FONT_CHAR *next; |
---|
305 | |
---|
306 | // allocate the new character structure |
---|
307 | PAPERTAPE_FONT_CHAR *buf; |
---|
308 | buf = (PAPERTAPE_FONT_CHAR*) malloc(sizeof(PAPERTAPE_FONT_CHAR)); |
---|
309 | |
---|
310 | // copy all values to structure |
---|
311 | buf->ascii_id = ascii_id; |
---|
312 | buf->bytes_n = bytes_n; |
---|
313 | buf->bytes = (byte_t*) malloc(bytes_n); |
---|
314 | memcpy(buf->bytes, bytes, bytes_n); |
---|
315 | buf->name = NULL; |
---|
316 | buf->next = NULL; |
---|
317 | |
---|
318 | // Start going throught the list |
---|
319 | current = font; // Start with the font object as first list member. |
---|
320 | while(current->next != NULL) { |
---|
321 | if(current->next->ascii_id == ascii_id) { |
---|
322 | // the next one has the same ascii_id as we have (perhaps |
---|
323 | // we are 'c' and the next is 'c', too). So replace the next |
---|
324 | // one. |
---|
325 | next = current->next; // copy the pointer somewhere else |
---|
326 | current->next = buf; // replace the next pointer with our char |
---|
327 | free(next); // delete the old next pointer. |
---|
328 | return buf; |
---|
329 | } else if(current->next->ascii_id > ascii_id) { |
---|
330 | // the next one has a bigger ascii_id as we have (perhapse |
---|
331 | // we are 'c' while the next is 'e'). So pack our character |
---|
332 | // right here, between current and next. |
---|
333 | next = current->next; // copy the pointer somewhere else |
---|
334 | current->next = buf; // replace the next pointer with our char |
---|
335 | buf->next = next; // pack the next pointer after our pointer |
---|
336 | return buf; |
---|
337 | } else { |
---|
338 | // the next one has a lower ascii_id as we have, so we have |
---|
339 | // to go on throught the list |
---|
340 | current = current->next; |
---|
341 | } |
---|
342 | } |
---|
343 | |
---|
344 | // we have reached the end of the list and haven't found any ascii_id biggger |
---|
345 | // or equal to ours. So we simply add our character to the list. |
---|
346 | current->next = buf; |
---|
347 | return buf; |
---|
348 | } |
---|
349 | |
---|
350 | /** |
---|
351 | * This is an auxillary function to access the linked list. It's not supposed to be used |
---|
352 | * from outside -- use highlevel functions like set_char or get_label. |
---|
353 | * @returns Link inside the list, NULL if not found. |
---|
354 | **/ |
---|
355 | PAPERTAPE_FONT_CHAR* papertape_font_low_get_char(PAPERTAPE_FONT* font, char ascii_id) { |
---|
356 | PAPERTAPE_FONT_CHAR* current; |
---|
357 | current = font; |
---|
358 | while(current->next != NULL) { |
---|
359 | if(current->next->ascii_id == ascii_id) { |
---|
360 | // we have found the right character entry. |
---|
361 | return current->next; |
---|
362 | } else |
---|
363 | current = current->next; |
---|
364 | } |
---|
365 | return NULL; |
---|
366 | } |
---|
367 | |
---|
368 | /** |
---|
369 | * This is just like papertape_font_low_get_char, but for special characters. |
---|
370 | * Ah, one special thing: Special characters are always valid, even if not defined. |
---|
371 | **/ |
---|
372 | PAPERTAPE_FONT_CHAR* papertape_font_low_get_special(PAPERTAPE_FONT* font, const char* name) { |
---|
373 | PAPERTAPE_FONT_CHAR* current; |
---|
374 | byte_t default_bytes[] = { 0 }; |
---|
375 | current = font; |
---|
376 | while(current->next != NULL) { |
---|
377 | if(current->next->name != NULL && |
---|
378 | strcmp(current->next->name, name) == 0) { |
---|
379 | // we have found the right character entry. |
---|
380 | return current->next; |
---|
381 | } else |
---|
382 | current = current->next; |
---|
383 | } |
---|
384 | // no special character found. We'll generate one. |
---|
385 | return papertape_font_set_special(font, name, default_bytes, 1); |
---|
386 | } |
---|
387 | |
---|
388 | |
---|
389 | /** |
---|
390 | * I don't know why one wanted to delete a character, but by using this function you can |
---|
391 | * do exactly that. |
---|
392 | * @returns 1 if successful deleted, 0 if not found in list. |
---|
393 | **/ |
---|
394 | int papertape_font_del_char(PAPERTAPE_FONT* font, char ascii_id) { |
---|
395 | // we cannot use papertape_font_low_get_char, because this would need |
---|
396 | // a double linked list. |
---|
397 | PAPERTAPE_FONT_CHAR* current; |
---|
398 | current = font; |
---|
399 | while(current->next != NULL) { |
---|
400 | if(current->next->ascii_id == ascii_id) { |
---|
401 | // we have found the right character entry. |
---|
402 | if(current->next->next != NULL) { |
---|
403 | // we are somewhere in the list, so we have to rewrite |
---|
404 | // the "next" pointer. |
---|
405 | current->next = current->next->next; |
---|
406 | } |
---|
407 | free(current->next); |
---|
408 | return 1; |
---|
409 | } else |
---|
410 | current = current->next; |
---|
411 | } |
---|
412 | return 0; |
---|
413 | } |
---|
414 | |
---|
415 | /** |
---|
416 | * This is the same like for normal characters, only for special characters. |
---|
417 | **/ |
---|
418 | int papertape_font_del_special(PAPERTAPE_FONT* font, const char* name) { |
---|
419 | PAPERTAPE_FONT_CHAR* current; |
---|
420 | current = font; |
---|
421 | while(current->next != NULL) { |
---|
422 | if(current->next->name != NULL && |
---|
423 | strcmp(current->next->name, name) == 0) { |
---|
424 | // we have found the right character entry. |
---|
425 | if(current->next->next != NULL) { |
---|
426 | // we are somewhere in the list, so we have to rewrite |
---|
427 | // the "next" pointer. |
---|
428 | current->next = current->next->next; |
---|
429 | } |
---|
430 | free(current->next); |
---|
431 | return 1; |
---|
432 | } else |
---|
433 | current = current->next; |
---|
434 | } |
---|
435 | return 0; |
---|
436 | } |
---|
437 | |
---|
438 | /** |
---|
439 | * This is the main function from the papertape_font package. Using this function, you |
---|
440 | * can render any ASCII string to a papertape label, using the current font. |
---|
441 | * |
---|
442 | * The return byte array will be malloced, and you'll have after calling the only |
---|
443 | * link to that array. So feel free to free() it, if you don't need it any more. |
---|
444 | * |
---|
445 | * If there's a non printable character in your string, this function will print out an |
---|
446 | * error message at stderr and skip that character. Use papertape_font_string_is_printable |
---|
447 | * if you want to check wheter your string contains non printable characters. |
---|
448 | * |
---|
449 | * Internally speaken, this is the only read access function to the internal single |
---|
450 | * linked font character list. |
---|
451 | * |
---|
452 | **/ |
---|
453 | byte_t* papertape_font_get_label(PAPERTAPE_FONT* font, const char* string, int* size) { |
---|
454 | PAPERTAPE_FONT_CHAR* character_spacing = papertape_font_low_get_special(font, PAPERTAPE_FONT_CHARACTER_SPACING_NAME); |
---|
455 | int x; // to count throught the string |
---|
456 | *size = 0; // the size of the output byte array |
---|
457 | byte_t* output; |
---|
458 | int output_pos; // current position in output array |
---|
459 | int clen = strlen(string)*2 - 1; // length of characters stack array |
---|
460 | // dynamic stack arrays are ISO C99, compare http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Variable-Length.html |
---|
461 | PAPERTAPE_FONT_CHAR *characters[ clen ]; |
---|
462 | int cpos = 0; // current position in character array |
---|
463 | |
---|
464 | // pack the font char array together |
---|
465 | for(x=0; string[x] != '\0'; x++) { |
---|
466 | // find white spaces |
---|
467 | if(string[x] == ' ') |
---|
468 | characters[cpos] = papertape_font_low_get_special(font, PAPERTAPE_FONT_WHITE_SPACE_NAME); |
---|
469 | else |
---|
470 | characters[cpos] = papertape_font_low_get_char(font, string[x]); |
---|
471 | |
---|
472 | if(characters[cpos] == NULL) { |
---|
473 | fprintf(stderr, "papertape_font_get_label: Character \"%c\" at position %i in \"%s\" not printable\n", |
---|
474 | string[x], x+1, string); |
---|
475 | // tell the malloc loop (see below) to loop not over the complete |
---|
476 | // characters[] array, because one character is missing. That is, |
---|
477 | // pop the array for 1 value. |
---|
478 | clen--; |
---|
479 | // don't increase cpos, we want to overwrite this character in the |
---|
480 | // next loop. |
---|
481 | continue; |
---|
482 | } else if(characters[cpos]->bytes == NULL) { |
---|
483 | // for all possibilities... this character is empty. |
---|
484 | // Make sure bytes_n is it, too. |
---|
485 | if(characters[cpos]->bytes_n != 0) |
---|
486 | fprintf(stderr, "papertape_font_get_label: Bad font parsed! Character 0x%x (%c) not really empty!\n", |
---|
487 | characters[cpos]->ascii_id, characters[cpos]->ascii_id); |
---|
488 | } |
---|
489 | |
---|
490 | *size += characters[cpos]->bytes_n; |
---|
491 | cpos++; |
---|
492 | |
---|
493 | // add the characters spacing after each character |
---|
494 | if(x+1 < strlen(string)) { // but not at the last character |
---|
495 | characters[cpos] = character_spacing; |
---|
496 | *size += characters[cpos]->bytes_n; |
---|
497 | cpos++; |
---|
498 | } |
---|
499 | } |
---|
500 | |
---|
501 | // now malloc the output byte array and generate it |
---|
502 | output = (byte_t*) malloc(*size); |
---|
503 | for(cpos=0,output_pos=0; cpos < clen && output_pos < *size; output_pos += characters[cpos++]->bytes_n) |
---|
504 | memcpy(output+output_pos, characters[cpos]->bytes, characters[cpos]->bytes_n); |
---|
505 | |
---|
506 | // we are done. |
---|
507 | return output; |
---|
508 | } |
---|
509 | |
---|
510 | /** |
---|
511 | * Checks if there's any non printable character in your string. Papertape fonts don't have |
---|
512 | * to have bit masks for every character, so use this function to check your string. |
---|
513 | * papertape_font_get_label will output errors to stderr if it finds unprintable |
---|
514 | * characters. |
---|
515 | * |
---|
516 | * @returns 1 if all characters are printable, 0 otherwise. |
---|
517 | **/ |
---|
518 | int papertape_font_string_is_printable(PAPERTAPE_FONT *font, const char* string) { |
---|
519 | int x; |
---|
520 | |
---|
521 | for(x=0; string[x] != '\0'; x++) { |
---|
522 | if(string[x] == ' ') |
---|
523 | // white spaces are always printable |
---|
524 | continue; |
---|
525 | if(papertape_font_low_get_char(font, string[x]) == NULL) |
---|
526 | // character not found! |
---|
527 | return 0; |
---|
528 | } |
---|
529 | |
---|
530 | return 1; // No non printable char found. |
---|
531 | } |
---|
532 | |
---|
533 | /** |
---|
534 | * Write the current PAPERTAPE_FONT (back) out to a file. |
---|
535 | * FILE mus be a writeable handle. |
---|
536 | **/ |
---|
537 | int papertape_font_write_to_file(PAPERTAPE_FONT* font, FILE *fh) { |
---|
538 | PAPERTAPE_FONT_CHAR* current; |
---|
539 | int x; |
---|
540 | |
---|
541 | // Write first lines. |
---|
542 | fputs(PAPERTAPE_FONT_WRITE_FIRST_LINES, fh); |
---|
543 | |
---|
544 | // now go throught list and write all characters out. |
---|
545 | current = font; |
---|
546 | while(1) { |
---|
547 | // write bytes and identifiers |
---|
548 | for(x=0; x<current->bytes_n; x++) { |
---|
549 | papertape_font_byte2line(current->bytes[x], fh); |
---|
550 | if(current->ascii_id == 0) { |
---|
551 | fputs(PAPERTAPE_FONT_SPECIAL_ID, fh); |
---|
552 | fputs(current->name, fh); |
---|
553 | } else |
---|
554 | fputc(current->ascii_id, fh); |
---|
555 | fputc('\n', fh); |
---|
556 | } |
---|
557 | |
---|
558 | // and go on. Or break, if at end. |
---|
559 | if(current->next != NULL) { |
---|
560 | // write a neat NULL byte as eye candy seperation |
---|
561 | papertape_font_byte2line(0x0, fh); |
---|
562 | fputc('\n', fh); |
---|
563 | current = current->next; |
---|
564 | } else |
---|
565 | break; |
---|
566 | } |
---|
567 | |
---|
568 | return 1; // could be void. |
---|
569 | } |
---|
570 | /** |
---|
571 | * This will print a dump of the papertape font linked list, just for debugging purpose, |
---|
572 | * to the file handle. It's just fine to give "stderr" at the file handle. |
---|
573 | **/ |
---|
574 | void papertape_font_dump(PAPERTAPE_FONT *font, FILE* fh) { |
---|
575 | PAPERTAPE_FONT_CHAR* current; |
---|
576 | int ram_bytes = 0; |
---|
577 | int i = 0; |
---|
578 | int x; |
---|
579 | |
---|
580 | current = font; |
---|
581 | while(1) { |
---|
582 | ram_bytes += sizeof(PAPERTAPE_FONT_CHAR); |
---|
583 | if(current->ascii_id == 0 && current->name == NULL) |
---|
584 | fprintf(fh, "%i. char: Special character, no name (broken), %i bytes allocated\n", i++, current->bytes_n); |
---|
585 | else if(current->ascii_id == 0) { |
---|
586 | fprintf(fh, "%i. char: Special character, name \"%s\", %i bytes allocated\n", i++, current->name, current->bytes_n); |
---|
587 | ram_bytes += strlen(current->name)+1; |
---|
588 | } else |
---|
589 | fprintf(fh, "%i. char: \"%c\" (0x%x), %i bytes allocated\n", i++, current->ascii_id, current->ascii_id, current->bytes_n); |
---|
590 | if(current->bytes == NULL) { |
---|
591 | fprintf(fh, "No bytes allocated!\n"); |
---|
592 | } else { |
---|
593 | ram_bytes += current->bytes_n; |
---|
594 | for(x=0; x < current->bytes_n; x++) { |
---|
595 | papertape_font_byte2line(current->bytes[x], fh); |
---|
596 | fputc('\n', fh); |
---|
597 | } |
---|
598 | } |
---|
599 | |
---|
600 | if(current->next == NULL) { |
---|
601 | fprintf(fh, "End of list.\n"); |
---|
602 | fprintf(fh, "Statistics: %i entries, %i total bytes in heap\n", i, ram_bytes); |
---|
603 | return; |
---|
604 | } else |
---|
605 | current = current->next; |
---|
606 | } |
---|
607 | } |
---|
608 | |
---|
609 | /** |
---|
610 | * The main() method (and auxillary helper functions) is only compiled when the switch |
---|
611 | * "STANDALONE" is defined. If not, we'll only get the simple library. |
---|
612 | * |
---|
613 | **/ |
---|
614 | #ifdef STANDALONE |
---|
615 | |
---|
616 | /** |
---|
617 | * A helper method for the standalone main(): Read contents of a stream |
---|
618 | * in a dynamically allocated heap byte array. |
---|
619 | **/ |
---|
620 | int file_get_contents(FILE *stream, byte_t **content) { |
---|
621 | byte_t buf[4096]; |
---|
622 | size_t bytes; // gerade eben eingelesene bytes |
---|
623 | byte_t *str = NULL; |
---|
624 | size_t total_bytes = 0; // alle bis jetzt eingelesenen bytes |
---|
625 | size_t total_allocated = 0; |
---|
626 | byte_t *tmp; // fuers realloc |
---|
627 | |
---|
628 | while(!feof(stream)) { |
---|
629 | bytes = fread(buf, 1, sizeof(buf), stream); |
---|
630 | |
---|
631 | while( (total_bytes + bytes) > total_allocated) { |
---|
632 | if(str) |
---|
633 | total_allocated *= 2; |
---|
634 | else |
---|
635 | total_allocated = bytes > sizeof(buf) ? bytes : sizeof(buf); |
---|
636 | |
---|
637 | tmp = realloc(str, total_allocated); |
---|
638 | |
---|
639 | if(tmp == NULL) { |
---|
640 | fprintf(stderr, "*** file_get_contents ERROR*** Could not reallocate\n"); |
---|
641 | //return length; // Fehler - das eingelesene zumindest zurueckgeben. |
---|
642 | // nee, gar nichts zurückgeben. Das geht so nicht. |
---|
643 | return 0; |
---|
644 | } |
---|
645 | |
---|
646 | str = tmp; |
---|
647 | } // while innen |
---|
648 | |
---|
649 | memcpy(str + total_bytes, buf, bytes); |
---|
650 | total_bytes += bytes; |
---|
651 | } // while |
---|
652 | |
---|
653 | if(total_allocated == 0) |
---|
654 | str = (byte_t*) malloc(1); // something empty. Just to be not NULL... |
---|
655 | //str[total_bytes] = '\0'; // wenns ein string wäre. |
---|
656 | |
---|
657 | *content = str; |
---|
658 | return total_bytes; |
---|
659 | } |
---|
660 | |
---|
661 | /** |
---|
662 | * The main method. We are using an own parameter system with extensive help messages. |
---|
663 | * No so much parameter flexibility, but it should be enough :-) |
---|
664 | **/ |
---|
665 | int main(int argc, char **argv) { |
---|
666 | // Instead of poor getopt emulation, an own parameter system like "svn" CLI client has it |
---|
667 | if(argc < 3 || strcasecmp(argv[1], "help") == 0) { |
---|
668 | // argc == 1: Wrong call like "./font", missing subcommand. |
---|
669 | // Wrong call (like "./font label"), or help call (like "./font help"). |
---|
670 | // Display help. Or startup help system. |
---|
671 | if(argc < 3) { |
---|
672 | fprintf(stderr, "Usage: %s <subcommand> [subcommand option] <font file>\n" |
---|
673 | "The Papertape font utility program.\n" |
---|
674 | "Type '%s help <subcommand>' for help on a specific subcommand.\n" |
---|
675 | "The <subcommand> and <font file> parameters are mandatory\n" |
---|
676 | "\n" |
---|
677 | "Available subcommands:\n" |
---|
678 | " help Starts up this help system\n" |
---|
679 | " label Prints out a complete label, given as the option\n" |
---|
680 | " set Sets the character with the ASCII ID (e.g. 0x20), reads from STDIN\n" |
---|
681 | " del Deletes the character with the ASCII ID from the font file\n" |
---|
682 | " dump Parses the font file and prints out the result, for debugging\n" |
---|
683 | " export Exports the font file to STDOUT, according to schematics\n" |
---|
684 | " import Imports the font file, according to schematics\n" |
---|
685 | " Default schematics: %s\n", |
---|
686 | argv[0], argv[0], PAPERTAPE_FONT_SCHEMATICS_DEFAULT); |
---|
687 | } |
---|
688 | |
---|
689 | if(argc >= 3) { |
---|
690 | // verbose Help system. |
---|
691 | if(strcasecmp(argv[2], "label") == 0) { |
---|
692 | fprintf(stderr, "label: Print out the compiled bytes for the translated label.\n" |
---|
693 | "Expects String as subcommand option.\n\n" |
---|
694 | "Example calls:\n" |
---|
695 | " Prints out yor text to STDOUT.\n" |
---|
696 | " $ %s label \"The text you want to label\" your-font-file.ptf:\n" |
---|
697 | " The same with \"hello world\". Upper/lowercase produces the same result, btw.\n" |
---|
698 | " $ %s label hello\\ world your-font-file.ptf:\n" |
---|
699 | " This writes the output to the file hallo-welt.bin:\n" |
---|
700 | " $ %s label hallo\\ Welt your-font-file.ptf > hallo-welt.bin\n" |
---|
701 | " This will punch out all files in the directory with the right label before\n" |
---|
702 | " $ for x in *; do %s label \"$x\" ../fonts/your.ptf | cat - \"$x\" | ./punch; done;\n", |
---|
703 | argv[0], argv[0], argv[0], argv[0]); |
---|
704 | } else if(strcasecmp(argv[2], "set") == 0) { |
---|
705 | fprintf(stderr, "set: Changes or adds a character in the font file\n" |
---|
706 | "Expects the wanted character as hex or character as subcommand option.\n\n" |
---|
707 | "Example calls:\n" |
---|
708 | " This will create a new font file that contains the character 'a':\n" |
---|
709 | " $ cat how-a-should-look-like.bin | %s set a new-font-file.ptf\n" |
---|
710 | " This will overwrite the current value of the character 'a':\n" |
---|
711 | " $ cat a-new-look-for-letter-a.bin | %s set a new-font-file.ptf\n" |
---|
712 | " This will set the blank space character:\n" |
---|
713 | " $ echo -en \"\\0\" | %s set \" \" existing-font-file.ptf\n" |
---|
714 | " This will do exactly the same like above:\n" |
---|
715 | " $ echo -en \"\\0\" | %s set 0x20 existing-font-file.ptf\n", |
---|
716 | argv[0], argv[0], argv[0], argv[0]); |
---|
717 | } else if(strcasecmp(argv[2], "del") == 0) { |
---|
718 | fprintf(stderr, "del: Deletes one or more characters from a font file\n" |
---|
719 | "Expects one character as subcommand option.\n\n" |
---|
720 | "Example calls:\n" |
---|
721 | " Delete a 'm' from your font file:\n" |
---|
722 | " $ %s del m your-font-file.ptf\n", |
---|
723 | argv[0]); |
---|
724 | } else if(strcasecmp(argv[2], "dump") == 0) { |
---|
725 | fprintf(stderr, "dump: Parse a font file and print out the results to stdout. This is\n" |
---|
726 | "pracitcal for detecting syntax errors in the font file.\n\n" |
---|
727 | "Example calls:\n" |
---|
728 | " Get a dump:\n" |
---|
729 | " $ %s dump your-font-file.ptf\n" |
---|
730 | " Save it somewhere:\n" |
---|
731 | " $ %s dump your-font-file.ptf > dump.txt\n", |
---|
732 | argv[0], argv[0]); |
---|
733 | } else if(strcasecmp(argv[2], "export") == 0|| strcasecmp(argv[2], "import") == 0) { |
---|
734 | fprintf(stderr, "import/export: Converting font files to complete binary files on the fly\n" |
---|
735 | "You can give a scheme optionally as subcommand option.\n" |
---|
736 | "This is the default scheme:\n" |
---|
737 | " %s\n\n" |
---|
738 | "Example calls:\n" |
---|
739 | " Export an existing font file, edit the file with an hex editor and import it again:\n" |
---|
740 | " $ %s export your-font-file.ptf > font-file-export.bin\n" |
---|
741 | " $ khexdump font-file-export.bin\n" |
---|
742 | " [ some GUI editing, and so on...]\n" |
---|
743 | " $ cat font-file-export.bin | %s import your-font-file.ptf\n" |
---|
744 | " Import to a new font file from existin g other font mechanism:\n" |
---|
745 | " $ SCHEME=\"abcdefgh...4567890 ()<>\"\n" |
---|
746 | " $ perl another-font.pl \"$SCHEME\" | %s import \"%s\" importet-font-file.ptf\n" |
---|
747 | " Make a new font file which only contains the digits of the old file:\n" |
---|
748 | " $ %s export \"0123456789\" existing.ptf | %s import \"0123456789\" new.ptf\n", |
---|
749 | PAPERTAPE_FONT_SCHEMATICS_DEFAULT, argv[0], argv[0], argv[0], argv[0], argv[0], argv[0]); |
---|
750 | } else { |
---|
751 | fprintf(stderr, "'%s': unknown subcommand. Type '%s help' for a list of valid commands.\n", |
---|
752 | argv[2], argv[0]); |
---|
753 | } |
---|
754 | } // verbose help |
---|
755 | return 0; |
---|
756 | } // Help messages |
---|
757 | |
---|
758 | if(strcasecmp(argv[1], "export") == 0 || strcasecmp(argv[1], "label") == 0) { |
---|
759 | // export or label. |
---|
760 | int size; |
---|
761 | byte_t *bytes; |
---|
762 | PAPERTAPE_FONT *font; |
---|
763 | |
---|
764 | if(! (font=papertape_font_new_from_file(argc == 4 ? argv[3] : argv[2]) )) { |
---|
765 | fprintf(stderr, "Error creating Papertape font.\n"); |
---|
766 | return 3; |
---|
767 | } |
---|
768 | |
---|
769 | bytes = papertape_font_get_label(font, argc == 4 ? argv[2] : PAPERTAPE_FONT_SCHEMATICS_DEFAULT, &size); |
---|
770 | fwrite(bytes, size, sizeof(byte_t), stdout); |
---|
771 | return 0; |
---|
772 | } else if(strcasecmp(argv[1], "set") == 0 || strcasecmp(argv[1], "del") == 0) { |
---|
773 | // set and delete |
---|
774 | char character; |
---|
775 | FILE *font_file; |
---|
776 | PAPERTAPE_FONT *font; |
---|
777 | |
---|
778 | // at first: get the character |
---|
779 | if(argc == 3) { |
---|
780 | fprintf(stderr, "%s: Too few arguments! Missing character. Type '%s help %s' for usage.\n", |
---|
781 | argv[1], argv[0], argv[1]); |
---|
782 | return 1; |
---|
783 | } |
---|
784 | if(strlen(argv[2]) > 1) |
---|
785 | // A Number like "255", 0x20 or 080. strol will parse it. |
---|
786 | character = (char)strtol(argv[2], NULL, 0); |
---|
787 | else |
---|
788 | // only one character in ASCII. |
---|
789 | character = argv[2][0]; |
---|
790 | |
---|
791 | // open file for reading and WRITING. |
---|
792 | font_file = fopen(argv[3], "r+"); |
---|
793 | if(!font_file) { |
---|
794 | perror("Opening font file for writing"); |
---|
795 | return 2; |
---|
796 | } |
---|
797 | font = papertape_font_new(); |
---|
798 | papertape_font_load_file(font, font_file); |
---|
799 | |
---|
800 | if(strcasecmp(argv[1], "set")) { |
---|
801 | // change/add character |
---|
802 | byte_t *contents; |
---|
803 | int bytes = file_get_contents(stdin, &contents); |
---|
804 | printf("Read in %i bytes for letter %c (seen as byte: 0x%x)\n", bytes, character, character); |
---|
805 | papertape_font_set_char(font, character, contents, bytes); |
---|
806 | printf("Set character %c successfully.\n", character); |
---|
807 | } else { |
---|
808 | // delete character |
---|
809 | printf(papertape_font_del_char(font, character) ? |
---|
810 | "Deleted %c successfully from file %s\n" : |
---|
811 | "Could not find %c in file %s!\n", |
---|
812 | character, argv[3]); |
---|
813 | } |
---|
814 | |
---|
815 | // write papertape file. |
---|
816 | papertape_font_write_to_file(font, font_file); |
---|
817 | return 0; |
---|
818 | } else if(strcasecmp(argv[1], "dump") == 0) { |
---|
819 | PAPERTAPE_FONT* font = papertape_font_new_from_file(argv[2]); |
---|
820 | printf("This is a dump from the internal single linked ordered list,\n" |
---|
821 | "created from the file '%s':\n\n", argv[2]); |
---|
822 | papertape_font_dump(font, stdout); |
---|
823 | return 0; |
---|
824 | } else if(strcasecmp(argv[1], "import") == 0) { |
---|
825 | // run an import. |
---|
826 | PAPERTAPE_FONT *font = papertape_font_new(); |
---|
827 | FILE* font_file; |
---|
828 | byte_t *contents; |
---|
829 | int content_len; |
---|
830 | char *schematics; |
---|
831 | |
---|
832 | // open file for DESTROYING WRITING. |
---|
833 | font_file = fopen(argc == 4 ? argv[3] : argv[2], "w"); |
---|
834 | if(font_file == NULL) { |
---|
835 | perror("Opening font file for writing"); |
---|
836 | return 2; |
---|
837 | } |
---|
838 | |
---|
839 | schematics = argc == 4 ? argv[2] : PAPERTAPE_FONT_SCHEMATICS_DEFAULT; |
---|
840 | |
---|
841 | fprintf(stderr, "Reading from stdin, expecting schematics %s\n", schematics); |
---|
842 | content_len = file_get_contents(stdin, &contents); |
---|
843 | // debug: print it out! |
---|
844 | // fwrite(contents, 1, content_len, stdout); exit(0); |
---|
845 | |
---|
846 | // Parse schematics |
---|
847 | papertape_font_load_schematics(font, schematics, contents, content_len); |
---|
848 | |
---|
849 | fprintf(stderr, "Readed from stdin successfully. Here you get a dump:\n"); |
---|
850 | papertape_font_dump(font, stdout); |
---|
851 | |
---|
852 | // that's it, we are done. Write the papertape font. |
---|
853 | papertape_font_write_to_file(font, font_file); |
---|
854 | return 0; |
---|
855 | } else { |
---|
856 | fprintf(stderr, "'%s': unknown subcommand. Type '%s help' for a list of valid commands.\n", |
---|
857 | argv[2], argv[0]); |
---|
858 | return 1; |
---|
859 | } |
---|
860 | } // main |
---|
861 | #endif /* STANDALONE */ |
---|