#!/usr/bin/perl -w # Read raw font data from atari rom images, plus a few hand-drawn # "text bitmaps" from __DATA__. # Write 3 bitmap font "txt" files in the format txt2psf expects, then # runs txt2psf on them. Results in 3 fonts: # fauxtari-8.psf - 8x8 native size # fauxtari-16.psf - 16x16 scaled up # fauxtari-24.psf - 24x24 scaled up # The fiddly bits of this are getting the Unicode mappings correct. # ROM dumps are mapped to Atari address space at $C000, the regular # charset starts at $E000, so it's at offset $2000 (8192) in the # image. The XL international set is at $CC00, or offset $0C00 (3072). # In the Arabic ROM, the Arabic font takes the place of the standard # charset. All the charsets are 1K in size. # Arabic ROM, plus some info about it and a mention of the Hebrew ROM: # https://www.savetz.com/vintagecomputers/arabic65xe/ use bytes; $fontname = "fauxtari"; sub byte2line { my $t = shift; $t = sprintf("%08b", $t); $t =~ y/0/-/; $t =~ y/1/#/; return $t; } sub scale_line { my $line = shift; my $scale = shift; my $one = '#' x $scale; my $zero = '-' x $scale; $line =~ s/#/$one/g; $line =~ s/-/$zero/g; return ($line x $scale); } sub chr2output { my $codepoint = shift; my $bytes = shift; my $scale = shift || 1; my $unicode = ""; if(!ref $codepoint) { $codepoint = [ $codepoint ]; } $unicode .= sprintf("[%04x];", $_) for @$codepoint; my $result = sprintf("%%\nUnicode: %s\nBitmap: \\\n", $unicode); for(0..7) { my $byte = $bytes->[$_]; my $line = byte2line($byte); $result .= scale_line($line, $scale); $result .= " \\" unless $_ == 7; $result .= "\n"; } return $result; } sub internal2byte { my $t = shift; $t =~ y/-/0/; $t =~ y/#/1/; return pack("B*", $t); } sub psf2txt_header { my $charcount = shift; my $scale = shift; my $w = 8 * $scale; my $h = 8 * $scale; return <) { push @scale1, $_; $cnt++ if /^%/; if(/^\s*[-#]/) { push @scale2, scale_line($_, 2); push @scale3, scale_line($_, 3); } else { push @scale2, $_; push @scale3, $_; } } #warn "$cnt characters\n"; #warn "(padding to 256 characters)\n" unless $cnt >= 256; #warn "(padding to 512 characters)\n" unless $cnt >= 512; # 512 glyphs is the max for a PSF font. There can be more # *codepoints*, because the Unicode directory allows the same # glyph to represent multiple codepoints (e.g. space and # non-breaking space). if($cnt > 512) { die "$0: too many glyphs ($cnt > 512)"; } # The kernel refuses to load a font that isn't exactly # 256 or 512 glyphs, so we have to pad with junk glyphs. while($cnt < 512) { my $fake = "%\nUnicode: [0000];\nBitmap: "; push @scale1, $fake . (("-" x 8) x 8) . "\n"; push @scale2, $fake . (("-" x 16) x 16) . "\n"; push @scale3, $fake . (("-" x 24) x 24) . "\n"; $cnt++; } #warn "$cnt characters with padding\n"; # What is dupglyphs.pl for? # PSF allows the same glyph to represent multiple codepoints (e.g. # space and NBSP, or hyphen/soft-hyphen/en-dash). We only get 512 # glyphs max for a PSF, so we definitely want to use this. # BDF on the other hand doesn't allow this. So if the same glyph # data is to be used for multiple codepoints in BDF, the glyph data # has to be repeated for each codepoint. Bloats the font, but not # too much. The BDF is also what gets made into the TTF. I'm pretty # sure TTF supports multiple codepoints per glyph, but until I # learn some other way besides bitmapfont2ttf to generate the TTF, # it also will have dup glyph bloat. On the bright side, dupglyphs.pl # removes all the padding characters required by the psf font. sub mkfonts { my $px = shift; my $scaled_data = shift; my $scale = shift; open $fh, '>', "$fontname-$px.txt" or die $!; print $fh psf2txt_header($cnt, $scale); print $fh $_ for(@$scaled_data); close $fh; system("txt2psf $fontname-$px.txt $fontname-$px.psf"); system("perl dupglyphs.pl $fontname-$px.txt | perl sorttxtfont.pl | txt2psf | psf2bdf --defchar=0 --iso10646 --fontname=$fontname-$px | perl ./fixbdf.pl $px > $fontname-$px.bdf"); } mkfonts(8, \@scale1, 1); mkfonts(16, \@scale2, 2); mkfonts(24, \@scale3, 3); exit 0; __DATA__ % // dotted-box (no such glyph) Unicode: [0000]; Bitmap: \ -------- \ -##-###- \ -#------ \ ------#- \ -#------ \ ------#- \ -###-##- \ -------- % // backtick Unicode: [0060]; Bitmap: \ -------- \ --##---- \ --##---- \ ---##--- \ -------- \ -------- \ -------- \ -------- % // curlies Unicode: [007b]; Bitmap: \ ----##-- \ ---##--- \ ---##--- \ --##---- \ ---##--- \ ---##--- \ ----##-- \ -------- % Unicode: [007d]; Bitmap: \ --##---- \ ---##--- \ ---##--- \ ----##-- \ ---##--- \ ---##--- \ --##---- \ -------- % // tilde Unicode: [007e]; Bitmap: \ -------- \ -###--## \ ##-##-## \ ##--###- \ -------- \ -------- \ -------- \ -------- % // euro Unicode: [20ac]; Bitmap: \ ---####- \ --##---- \ -#####-- \ --##---- \ -#####-- \ --##---- \ ---####- \ -------- % // spanish left-quote Unicode: [00ab]; Bitmap: \ -------- \ -------- \ -------- \ -##--##- \ ##--##-- \ -##--##- \ -------- \ -------- % // spanish right-quote Unicode: [00bb]; Bitmap: \ -------- \ -------- \ -------- \ ##--##-- \ -##--##- \ ##--##-- \ -------- \ -------- % // spanish inverted question mark Unicode: [00bf]; Bitmap: \ -------- \ ---##--- \ -------- \ ---##--- \ ----##-- \ -##--##- \ --####-- \ -------- % // copyright Unicode: [00a9]; Bitmap: \ -#####-- \ #-----#- \ #--##-#- \ #-#---#- \ #--##-#- \ #-----#- \ -#####-- \ -------- % // registered Unicode: [00ae]; Bitmap: \ -#####-- \ #-----#- \ #-##--#- \ #-#-#-#- \ #-##--#- \ #-#-#-#- \ -#####-- \ -------- % // degrees Unicode: [00b0];[00ba]; Bitmap: \ -------- \ ---##--- \ --#--#-- \ ---##--- \ -------- \ -------- \ -------- \ -------- % // cents Unicode: [00a2]; Bitmap: \ -------- \ ---##--- \ --#####- \ -##----- \ -##----- \ --#####- \ ---##--- \ -------- % // "currency sign" // FIXME: not bold enough Unicode: [00a4]; Bitmap: \ -------- \ -#----#- \ --####-- \ --#--#-- \ --#--#-- \ --####-- \ -#----#- \ -------- % // yen Unicode: [00a5]; Bitmap: \ -##--##- \ -##--##- \ ---##--- \ -######- \ ---##--- \ -######- \ ---##--- \ -------- % // broken bar Unicode: [00a6]; Bitmap: \ -------- \ ---##--- \ ---##--- \ -------- \ ---##--- \ ---##--- \ ---##--- \ -------- % // section sign // FIXME: not bold enough Unicode: [00a7]; Bitmap: \ ---###-- \ --#----- \ ---##--- \ --#--#-- \ ---##--- \ -----#-- \ --###--- \ -------- % // diaresis (cha, cha, cha!) Unicode: [00a8]; Bitmap: \ -------- \ -##--##- \ -##--##- \ -------- \ -------- \ -------- \ -------- \ -------- % // feminine ordinal (superscript a) Unicode: [00aa]; Bitmap: \ -------- \ --####-- \ ----###- \ --##-##- \ ---####- \ -------- \ -------- \ -------- % // "not" sign Unicode: [00ac]; Bitmap: \ -------- \ -------- \ -------- \ -######- \ -----##- \ -------- \ -------- \ -------- % // macron Unicode: [00af]; Bitmap: \ -------- \ -######- \ -------- \ -------- \ -------- \ -------- \ -------- \ -------- % // plus-minus Unicode: [00b1]; Bitmap: \ -------- \ ---##--- \ -######- \ ---##--- \ -------- \ -######- \ -------- \ -------- % // superscript 2 // FIXME: not bold enough Unicode: [00b2]; Bitmap: \ -------- \ --##---- \ -#--#--- \ ---#---- \ --#----- \ -####--- \ -------- \ -------- % // FIXME: not bold enough // superscript 3 Unicode: [00b3]; Bitmap: \ -------- \ --##---- \ -#--#--- \ ---#---- \ -#--#--- \ --##---- \ -------- \ -------- % // acute Unicode: [00b4]; Bitmap: \ -------- \ ----##-- \ ---##--- \ -------- \ -------- \ -------- \ -------- \ -------- % // mu Unicode: [00b5]; Bitmap: \ -------- \ -------- \ -##--##- \ -##--##- \ -##--##- \ --#####- \ -----##- \ -------- % // paragraph Unicode: [00b6]; Bitmap: \ -------- \ --##-##- \ -###-##- \ -###-##- \ --##-##- \ ---#-##- \ ---#-##- \ -------- % // middle dot Unicode: [00b7]; Bitmap: \ -------- \ -------- \ -------- \ ---##--- \ ---##--- \ -------- \ -------- \ -------- % // superscript 1 Unicode: [00b9]; Bitmap: \ -------- \ ---##--- \ --###--- \ ---##--- \ --####-- \ -------- \ -------- \ -------- // FIXME: MISSING fractions, 00bc/00bd/00bc, ¼ ½ ¾, hard to do in 8x8 % // A with grave Unicode: [00c0]; Bitmap: \ -------- \ --##---- \ ---##--- \ --####-- \ -##--##- \ -######- \ -##--##- \ -------- % // A with acute Unicode: [00c1]; Bitmap: \ -------- \ ----##-- \ ---##--- \ --####-- \ -##--##- \ -######- \ -##--##- \ -------- % // A with circumflex Unicode: [00c2]; Bitmap: \ --####-- \ -#----#- \ ---##--- \ --####-- \ -##--##- \ -######- \ -##--##- \ -------- % // A with tilde Unicode: [00c3]; Bitmap: \ --##-##- \ -##-##-- \ ---##--- \ --####-- \ -##--##- \ -######- \ -##--##- \ -------- % // A with ring Unicode: [00c5]; Bitmap: \ ---##--- \ --#--#-- \ ---##--- \ --####-- \ -##--##- \ -######- \ -##--##- \ -------- % // AE ligature Unicode: [00c6]; Bitmap: \ -------- \ --#####- \ -####--- \ ##-##--- \ #######- \ ##-##--- \ ##-####- \ -------- // FIXME: this is hideous! % // C with cedilla Unicode: [00c7]; Bitmap: \ -------- \ --####-- \ -##--##- \ -##----- \ -##--##- \ --####-- \ ---##--- \ --##---- % // E with circumflex Unicode: [00ca]; Bitmap: \ --####-- \ -#----#- \ -######- \ -##----- \ -#####-- \ -##----- \ -######- \ -------- // FIXME: ugly! % // E with umlaut Unicode: [00cb]; Bitmap: \ --##-##- \ -------- \ -######- \ -##----- \ -#####-- \ -##----- \ -######- \ -------- % // I with grave Unicode: [00cc]; Bitmap: \ --##---- \ ---##--- \ -######- \ ---##--- \ ---##--- \ ---##--- \ -######- \ -------- % // I with acute Unicode: [00cd]; Bitmap: \ ----##-- \ ---##--- \ -######- \ ---##--- \ ---##--- \ ---##--- \ -######- \ -------- % // I with circumflex Unicode: [00ce]; Bitmap: \ --####-- \ -#----#- \ -######- \ ---##--- \ ---##--- \ ---##--- \ -######- \ -------- // FIXME: ugly! % // I with umlaut Unicode: [00cf]; Bitmap: \ --##-##- \ -------- \ -######- \ ---##--- \ ---##--- \ ---##--- \ -######- \ -------- % // capital eth Unicode: [00d0]; Bitmap: \ -------- \ -####--- \ -##-##-- \ -##--##- \ ####-##- \ -##-##-- \ -####--- \ -------- % // O with grave Unicode: [00d2]; Bitmap: \ -##----- \ --####-- \ -#-#-##- \ -##--##- \ -##--##- \ -##--##- \ --####-- \ -------- // FIXME: MISSING Ô Õ % // multiplication Unicode: [00d7]; Bitmap: \ -------- \ -##--##- \ --####-- \ ---##--- \ --####-- \ -##--##- \ -------- \ -------- // FIXME: MISSING Ø % // U with grave Unicode: [00d9]; Bitmap: \ --##---- \ ---##--- \ -##--##- \ -##--##- \ -##--##- \ -##--##- \ -######- \ -------- % // U with acute Unicode: [00da]; Bitmap: \ ----##-- \ ---##--- \ -##--##- \ -##--##- \ -##--##- \ -##--##- \ -######- \ -------- // FIXME: MISSING Û % // Y with acute Unicode: [00dd]; Bitmap: \ ----##-- \ ---##--- \ -##--##- \ -##--##- \ --####-- \ ---##--- \ ---##--- \ -------- % // capital thorn Unicode: [00de]; Bitmap: \ -------- \ -##----- \ -#####-- \ -##--##- \ -#####-- \ -##----- \ -##----- \ -------- % // a with circumflex Unicode: [00e2]; Bitmap: \ --####-- \ -#----#- \ -------- \ --####-- \ ----###- \ --##-##- \ ---####- \ -------- % // a with tilde Unicode: [00e3]; Bitmap: \ --##-##- \ -##-##-- \ -------- \ --####-- \ ----###- \ --##-##- \ ---####- // FIXME: touches bottom % // ae ligature Unicode: [00e6]; Bitmap: \ -------- \ -------- \ -###-##- \ ---##-## \ -######- \ ##-##--- \ -######- \ -------- // FIXME: fugly % // e with umlaut Unicode: [00eb]; Bitmap: \ -##--##- \ -------- \ --####-- \ -##--##- \ -######- \ -##----- \ --####-- \ -------- % // i with acute Unicode: [00ed]; Bitmap: \ ----##-- \ ---##--- \ -------- \ --###--- \ ---##--- \ ---##--- \ --####-- \ -------- % // lowercase eth Unicode: [00f0]; Bitmap: \ -##-#--- \ --##---- \ -#-##--- \ --####-- \ -##--##- \ -##--##- \ --####-- \ -------- % // o with tilde Unicode: [00f5]; Bitmap: \ --##-##- \ -##-##-- \ -------- \ --####-- \ -##--##- \ -##--##- \ --####-- \ -------- % // division Unicode: [00f7]; Bitmap: \ -------- \ ---##--- \ -------- \ -######- \ -------- \ ---##--- \ -------- \ -------- // FIXME: MISSING ø % // y with acute Unicode: [00fd]; Bitmap: \ ----##-- \ ---##--- \ -------- \ -##--##- \ -##--##- \ --#####- \ ----##-- \ -####--- % // lowercase thorn Unicode: [00fe]; Bitmap: \ -##----- \ -##----- \ -####--- \ -##-##-- \ -####--- \ -##----- \ -##----- \ -------- % // y with umlaut Unicode: [00ff]; Bitmap: \ -##--##- \ -------- \ -##--##- \ -##--##- \ -##--##- \ --#####- \ ----##-- \ -####--- % // left single curly quote Unicode: [2018]; Bitmap: \ -------- \ --##---- \ ---##--- \ ----##-- \ -------- \ -------- \ -------- \ -------- % // right single curly quote Unicode: [2019]; Bitmap: \ -------- \ ----##-- \ ---##--- \ --##---- \ -------- \ -------- \ -------- \ -------- % // left double curly quote Unicode: [201c]; Bitmap: \ -------- \ -##--##- \ -##--##- \ --##--## \ -------- \ -------- \ -------- \ -------- % // right double curly quote Unicode: [201d]; Bitmap: \ -------- \ -##--##- \ -##--##- \ ##--##-- \ -------- \ -------- \ -------- \ -------- % // spanish left single quote Unicode: [2039]; Bitmap: \ -------- \ -------- \ -------- \ ---##--- \ --##---- \ ---##--- \ -------- \ -------- % // spanish right single quote Unicode: [203a]; Bitmap: \ -------- \ -------- \ -------- \ ---##--- \ ----##-- \ ---##--- \ -------- \ -------- % // replacement character for missing glyphs Unicode: [fffd]; Bitmap: \ --####-- \ -##--##- \ -#-##-#- \ -###-##- \ -######- \ -###-##- \ --####-- \ --------