The Paper Tape Project -- Paper Tape Font files =============================================== I have developed a portable and easy to use ASCII storage format for paper tape fonts. While paper tape fonts were formerly implemented as (perl) scripts, with C (and C++) that's no more practical, because setting up big nested hashes is not really possible. Apart from that, it would not be a good style to set up a giant header file with so much static data. I have considered using popular and approved data formats like XML or Jason instead of a "proprietary" new, perhaps even binary data format. On the one hand, using XML would have been really portable, but also very unhandy, because I would have to look for XML processors for Windows, and compiling would be get complex and even more complex. Therefore I've decided in favor of simple ASCII files with some not very strict layout. It's really obvious how it works: |***.*** |a You can write texts almost everywhere in the file, | .* * |a just the very first letters, which actually |***.*** |a *look* like a papertape (direction of movement, of | . | course, from top down), define the bytes from |***.*****|e that the letters are made up, which stand directly |* .* *|e next to the paper tape. |* .* *|e Yes, that's it! This file is a complete papertape |* . *|e font file, containing (until now) the letters a and e. Specification of Paper Tape files ================================= This is a typical paper tape file: ---- Start of example file ---- The Paper Tape Project -- PAPERTAPE_FONT file. The content of this file describes a paper tape font... See this small key how the document is structured: bits |identification 123.45678|char/string | . | |+++.+++++|m | . + |m | . + |m this is a comment. | . + |m |+++.+++++|m |+ . +|I |+++.+++++|I |+ . +|I | . | | . | | . | | . | |+++.+++++|0 Comments can be typed right anywhere, everything that doesn't look like a paper tape on the right place, is ignored. |+ . ++| <- even this one. |+ . +|0 |+++.+++++|0 | . | | . | =white_space this is a comment, too. | . | =white_space | . | ---- End of example file ---- As you see, paper tape files are quite funny ASCII files. Here are the rules you have to observe: * All lines are ignored, if there's not a papertape border in the very first position. The papertape border is defined as the pipe character, "|". * The papertape "flows" from top to down, like western european people write. The bit positions are also from left to right, like a real paper tape looks like, including the feed hole between bit 3 and 4. Therefore, as the key says, you read a paper tape byte like that: |123.45678| <- bit positions |012.34567| <- how computers count the bits e.g. | * . ** *| -> byte value = 2^1 + 2^4 + 2^5 + 2^7 = 178 = 0xbd * It's up to you which character you use for indicating an hole (logical 1). The only limitation is, that a logical 0 (not holed) MUST be an white space (no tab! Only the whitespace character). So all these bytes have the equal values: |+ +.++ ++| |h e.ll oo| |# #.## ##| || |.|| ||| (not recommended ;-) ) |( ).() ()| (also not recommendable) |* *.** **| * To allocate a byte to a character, you write that character directly after the papertape, like this: | * . ** *|r but not like this: | *.* ***| w That character is called the "ASCII identifier", because it shall be ASCII (8bit). You can write comments right after the identifier, because everything afterwards will be ignored. * There are three cases of bytes: * the normal byte |***.*****|p that belongs to an | .* *|p ASCII character: | . *** |p * bytes which have no | . | identifier at all | . | They are ignored. | . | * bytes that belong to |** . ****| special_character special characters. | *.* | special_character We give attention to special characters later. * Typically your paper tape letters span across multiple bytes. In Paper Tape files, you write next to *every* byte the character where it belongs to. This is read afterwards from up to down, like the virtual paper tape direction is: |***.*****|p | .* *|p | . *** |p | . | <- this line doesn't belong to p any more. Don't mix up with letters. Compare these examples: |***.*****|p |***.*****|p | .* *|p | .* *|p | . *** |p | . *** |p | . | | . | | . *|y | . *|y | . * |y | . * |p | .*** |y | .*** |y | . * |y | . * |y | . *|y | . *|y The typo at the right ("p" instead of "y") will overwrite the defined paper tape letter "p" above, because letters will always be read as blocks. Furthermore the first "y" line is overwritten, too, by the later 3 "y" lines. That is, due to the typo error, all previous definitions of "p" and "y" before are thrown away. Compare also these examples: |***.*****|p |***.*****|p | .* *|p | .* *|p this does nothing. | . | | . *** |p | . *** |p | . | | . | While the comment line on the left hand example is simply ignored, the white space byte on the right hand side is parsed and therefore the "p" value destroyed, like in the "y" and "p" example above. * Next to the normal ASCII characters, there are special characters. Principally, they are treated like ordinary characters, but they are not identified with an ASCII value, but with a full name: |***.*****|p | .* *|p This is an ordinary ASCII character | . *** |p | . | | . | =white_space This is typically used to define | . | =white_space how white_spaces have to look | . | =white_space like. They typically contain | . | only NULL bytes, of course ;-) | . | | . | =char_spacing And this defines how much space | . | is packed between characters. | . | Typically one NULL byte. | . | | *.* * | =secret_file |* .* ** | =secret_file The name of a special character | **.* ** | =secret_file may not contain whitespaces. |* *. *** | =secret_file Thereby normal text/comments | .**** | =secret_file can be written next to "special | . * | =secret_file character" bytes just like next | * . *** | =secret_file to ordinary bytes. |***.* ** | =secret_file |** . ** | =secret_file After all, the same limitiations |** .* ** | =secret_file exist like for normal multiple |** . *** | =secret_file byte definitions: Don't break in |* . * | =secret_file the flow, like | . | <- here, because this would | * .* | =secret_file truncate secret_file to <-this byte. * Take care that the papertapefont implementation can also write paper tape font files. If the utility program is called to change one character in your font file, it will read in (and parse) the font file and write back the parsed files (including the changes, of course). In this process, *ALL* your comments are *LOST*. The implementation cannot take care of your comments, this would be too complex. So if your font file is well commented, don't let the implementation write to it. Specification of the papertapefont.h library ============================================ I've developed a tiny stand alone C library which does not need auxillary libraries like Glib and which will compile at windows, too. It's quite simple to use that library. At first, there even exists a command line interface that implements most of the features. But here's how to use the C functions: The main "object like" structure is the PAPERTAPE_FONT structure. It can be built directly by parsing an already opened file: #include #include "papertapefont.h" FILE* font_file = fopen("/path/to/your/font.file", "r"); PAPERTAPE_FONT* font = papertape_font_new_from_file(font_file); Now the file has been parsed and every available letter is stored in RAM. So you can directly go and generate labels: int label_size; byte_t* label_bytes; label_bytes = papertape_font_get_label(font, "Hello World!", &label_size); // further use, e.g. with LOCHSTREIFEN*: LOCHSTREIFEN* papertape = lochstreifen_new(); lochstreifen_set_data(label_bytes, label_size); Of course you can check if your string can be fully generated, too: if(! papertape_font_string_is_printable(font, "{H4x05]!")) { printf("Sorry dude, the current font is not" "script kiddie compatible.\n"); } And after all, changing the papertape is fully implemented: bytes_t* user_generated; int len; let_user_generate_letter("Please make an 'A'", &user_generated, &len); papertape_font_set_char(font, 'A', user_generated, len); This will change the letter immediately. So the following output will give out "ABBA" on the papertape at the end (and not "ABAB"): bytes_t* output, buf_for_a, buf_for_b; int o_len, a_len, b_len; // print out "ab" on papertape output = papertape_font_get_label(font, "ab", &o_len); send_to_papertape_puncher(output, o_len); // flip a and b buf_for_a = papertape_font_get_label(font, "a", &a_len); buf_for_b = papertape_font_get_label(font, "b", &b_len); papertape_font_set_char(font, 'B', buf_for_a, a_len); papertape_font_set_char(font, 'A', buf_for_b, b_len); // print out "ab" on papertape... output = papertape_font_get_label(font, "ab", &o_len); send_to_papertape_puncher(output, o_len); If you feel destructive, you can also delete characters: // clean up all the mess by deleting everything!!111 papertape_font_del_char(font, 'a'); papertape_font_del_char(font, 'b'); if(!papertape_font_del_char(font, 'b')) printf("There is no more 'b'. Of course!"); output = papertape_font_get_label(font, "abba", &len); // this will give out 4 errors at stderr, because // no character can be generated. At last, after messing around you can save the whole changed thing (back) to a file. FILE* target = fopen("/existing/or/new.font.txt", "w"); papertape_font_write_to_file(font, target); Be aware that the following example will stripe out *ALL* comments out of your font file: FILE* needs_cleanup = fopen("font.txt", "r+"); PAPERTAPE_FONT* font = papertape_font_new_from_file(needs_cleanup); papertape_font_write_to_file(font, needs_cleanup); In the end, an issue that is a bit more complicated: Special characters. This includes white spaces, letter spacing, etc. The names for these special characters are noted in the papertapefont.h header file, so you can access them directly, too, if you want: byte_t *white = calloc(sizeof(byte_t), 100); papertape_font_set_special(font, PAPERTAPE_FONT_SPACE_NAME, white, 100); // about 1k of bytes: papertape_font_get_label(font, "A fairly long text ! ! ! ", &somewhere) // deletion sets the space to some kind of default value papertape_font_del_special(font, PAPERTAPE_FONT_SPACE_NAME); // so this is quite short: papertape_font_get_label(font, " s h o r t ", &somewhere); I think that's all, for the moment. Happy coding :-) -- Sven Köppel, 04.09.2008