#!@PERL@ # makevpl # (C) A. J. C. Duggan 1993 # Mix and match TeX's PL and VPL files # # FONTDIMEN actions are --- # -replacefd | -addfd # # CHARACTER actions are --- # -replace[ch|ic|wd|ht|dp|map] | # -add[ch|ic|wd|ht|dp|map] # (ch = character (all), ic = italic correction, wd = width, ht = height, # dp = depth, map = character map) # # mapfont FONTAT actions are --- # -normal | -at # # default actions are: # -addfd - -replacech - -normal # v1.1 20/9/93 AJCD # Fixed problems with scaling, removed redundant MAPFONTs # # v1.2 23/9/93 AJCD # Split into reusable packages ($prog) = ($0 =~ /([^\/]*)$/); unshift(@INC, "@PERLLIBDIR@"); # set perl include directory require 'paths.pl'; require 'parseenc.pl'; require 'parsepl.pl'; ############################################################################### # Auxiliary routines ############################################################################### # fatal(...) # causes a fatal error with the arguments given sub fatal { print STDERR "$prog: "; printf STDERR @_; print STDERR "\n"; exit 1; } # inrange(num, range) # tests if number is in the range given # range = num[,range] | [num]-[num][,range] | [] sub inrange { local($number, $range) = @_; foreach (split(/,/, $range)) { if (/^(0x?[\da-fA-F]+)/) { next if $number < oct($1); } elsif (/^(\d+)/) { next if $number < $1; } if (/(0x?[\da-fA-F]+)$/) { next if $number > oct($1); } elsif (/(\d+)$/) { next if $number > $1; } return 1; } 0; } # normalise(number) # return real normalised to design units of font sub normalise { local($string) = @_; $dunits = $designunits if !defined($dunits); $dsize = $designsize if !defined($dsize); $string = sprintf('R %.5f', &number($string)*$designsize*$dunits/($designunits*$dsize)) if $dsize*$designunits != $designsize*$dunits; $string; } ############################################################################### # Property list manipulation routines ############################################################################### # PL property functions # called by getpl with normal arguments as parameters sub checksum { # put checksum in MAPFONT local($name, $number) = @_; $fontprop{$fontname,0,FONTCHECKSUM} = "(FONTCHECKSUM $number)" if !defined($fontprop{$fontname,0,FONTCHECKSUM}); undef; } sub designunits { # save designunits local($name, $number) = @_; $designunits = &number($number); undef; } sub designsize { # save designsize local($name, $number) = @_; $fontprop{$fontname,0,FONTDSIZE} = "(FONTDSIZE $number)" if !defined($fontprop{$fontname,0,FONTDSIZE}); $designsize = &number($number); undef; } sub codingscheme { # keep original codingscheme local($name, $string) = @_; $fcoding = $string if !defined($fcoding); undef; } sub family { # keep original family local($name, $string) = @_; $ffamily = $string if !defined($ffamily); undef; } sub face { # keep original face local($name, $number) = @_; $fface = &number($number) if !defined($fface); undef; } sub header { # keep original header local($name, $byte, $value) = @_; local($index) = &number($byte); $header[$index] = "$byte $value" if !defined($header[$index]); undef; } # mapfont properties sub mapfontprop { # MAPFONT sub-properties join(' ', @_); } sub fontat { # fontat property is relative to designunits local($name, $number) = @_; $dunits = $designunits if !defined($dunits); $dsize = $designsize if !defined($dsize); if (!$scale) { $number = sprintf('R %.5f', &number($number)*$dunits/$designunits) if $dunits != $designunits; } else { local($fontat) = $scale*$dunits/$dsize; $number = sprintf('R %.5f', $fontat) if $fontat != $dunits; } "($name $number)"; } sub mapfont { # MAPFONT property local($name, $number, @map) = @_; local($value) = &number($number); foreach (@map) { # put MAPFONT properties into fontprops $fontprop{$fontname,$value,$1} = $_ if /^\((\w+)/; } undef; } # ligtable properties sub ligprop { join(' ', @_); } # addligs(left, skip, ligtable) # put entries into current font's ligtable for left-side character after # skipping given number of ligs & kerns sub addligs { local($left) = shift; local($skip) = shift; local($lastlabel) = !$skip; local($number); return if $lastlabel && defined($kernslike{$fontname,$left}); while ($skip && ($_ = shift)) { $skip-- if /KRN/ || /LIG/; } while (@_) { $_ = shift; last if (/STOP/); if (/SKIP (\S \S+)/) { &addligs($left, &number($1), @_); last; } elsif (/(\/?LIG\/?>?>?) (\S \S+) (\S \S+)/) { foreach $ligto (&mapsto(&number($2))) { if (!defined($ligtable{$fontname,$left,$ligto})) { ($number) = &mapsto(&number($3)); $ligtable{$fontname,$left,$ligto} = sprintf("($1 %s %s)", &charnum($ligto), &charnum($number)) if defined($number); print STDERR "$ligtable{$fontname,$left,$ligto}\n" if $debug; } } $lastlabel = 0; } elsif (/(KRN) (\S \S+) (\S \S+)/) { foreach $krnto (&mapsto(&number($2))) { if (!defined($ligtable{$fontname,$left,$krnto})) { $ligtable{$fontname,$left,$krnto} = sprintf("($1 %s %s)", &charnum($krnto), &normalise($3)); print STDERR "$ligtable{$fontname,$left,$krnto}\n" if $debug; } } $lastlabel = 0; } elsif (/LABEL BOUNDARYCHAR/) { $kernslike{$fontname,BOUNDARYCHAR} = $left if ($lastlabel); } elsif (/LABEL (\S \S+)/) { if ($lastlabel) { foreach (&mapsto(&number($1))) { $kernslike{$fontname,$_} = $left; } } } } } sub ligtable { # merge ligtables local($name) = shift; print STDERR '(Building ligature table...' if !$quiet; while (@_) { # run through program, putting LABELs in $_ = shift; if (/LABEL BOUNDARYCHAR/) { &addligs(BOUNDARYCHAR, 0, @_); } elsif (/LABEL (\S \S+)/) { foreach (&mapsto(&number($1))) { # add lig table for each remapping &addligs($_, 0, @_); } } } print STDERR 'done)' if !$quiet; } sub boundarychar { # keep original boundarychar local($name, $char) = @_; ($fbchar) = &mapsto(&number($char)) if !defined($fbchar); undef; } # list of fontdimen names -> fontdimen numbers %fontdimen = (SLANT, 1, SPACE, 2, STRETCH, 3, SHRINK, 4, XHEIGHT, 5, QUAD, 6, EXTRASPACE, 7, NUM1, 8, NUM2, 9, NUM3, 10, DENOM1, 11, DENOM2, 12, SUP1, 13, SUP2, 14, SUP3, 15, SUB1, 16, SUB2, 17, SUPDROP, 18, SUBDROP, 19, DELIM1, 20, DELIM2, 21, AXISHEIGHT, 22, DEFAULTRULETHICKNESS, 8, BIGOPSPACING1, 9, BIGOPSPACING2, 10, BIGOPSPACING3, 11, BIGOPSPACING4, 12, BIGOPSPACING5, 13 ); # fontdimen properties sub fontdimen { local($name, $number) = @_; local($index) = $fontdimen{$name}; $fontdimen[$index] = '('.join(' ', $name, &normalise($number)).')' if (!defined($fontdimen[$index]) && &inrange($index, $add{fd})) || &inrange($index, $sub{fd}); undef; } sub parameter { local($name, $indexstr, $number) = @_; local($index) = &number($indexstr); $fontdimen[$index] = join(' ', "($name", $indexstr, &normalise($number)).')' if (!defined($fontdimen[$index]) && &inrange($index, $add{fd})) || &inrange($index, $sub{fd}); undef; } # character properties %charprop = @thismap = (); sub charprop { local($name, $number) = @_; $charprop{$name} = "($name ".&normalise($number).')'; undef; } sub nextlarger { # deal with NEXTLARGER lists local($name, $number) = @_; print STDERR "$name $number : " if $debug; ($charprop{NEXTLARGER}) = &mapsto(&number($number)); undef $charprop{VARCHAR}; # mutually exclusive } sub varprop { # deal with VARCHAR lists local($name, $number) = @_; print STDERR "$name $number :" if $debug; ($charprop{$name}) = &mapsto(&number($number)); undef $charprop{NEXTLARGER}; # mutually exclusive } sub mapprop { # MAP sub-properties local($name) = shift; join(' ', "($name", @_).')'; # return property } sub mappropn { # normalised MAP sub-properties local($name) = shift; foreach (@_) { $_ = &normalise($_); } join(' ', "($name", @_).')'; # return property } sub map { # deal with MAP lists local($name); ($name, @thismap) = @_; undef; } sub character { # add or replace character properties local($name, $number) = @_; local($position) = &number($number); local($lastsel, $lastfont, $font) = 0; foreach $ch (&mapsto($position)) { $charic[$ch] = $charprop{CHARIC} if (!defined($charic[$ch]) && &inrange($position, $add{ic})) || &inrange($position, $sub{ic}); $charwd[$ch] = $charprop{CHARWD} if (!defined($charwd[$ch]) && &inrange($position, $add{wd})) || &inrange($position, $sub{wd}); $charht[$ch] = $charprop{CHARHT} if (!defined($charht[$ch]) && &inrange($position, $add{ht})) || &inrange($position, $sub{ht}); $chardp[$ch] = $charprop{CHARDP} if (!defined($chardp[$ch]) && &inrange($position, $add{dp})) || &inrange($position, $sub{dp}); if ((!defined($charmap[$ch]) && &inrange($position, $add{map})) || &inrange($position, $sub{map})) { @thismap = ("(SETCHAR $number)") if !@thismap; # make default MAP $charmap[$ch] = join("\n", '(MAP', @thismap, ')'); $charfrom[$ch] = $fontname; $nextlarger[$ch] = $charprop{NEXTLARGER} if defined($charprop{NEXTLARGER}); foreach $varprop (TOP, MID, BOT, REP) { $varchar{$ch,$varprop} = $charprop{$varprop} if defined($charprop{$varprop}); } } } %charprop = @thismap = (); undef; } ############################################################################### # Parsing and tokenisation ############################################################################### # list of property -> parameters_action # (N=number, S=string, P=property list, L=label or number) &plactions(CHECKSUM, checksum, DESIGNSIZE, designsize, DESIGNUNITS, designunits, CODINGSCHEME, codingscheme, FAMILY, family, FACE, family, SEVENBITSAFEFLAG, ignore, HEADER, header, BOUNDARYCHAR, boundarychar, VTITLE, ignore, COMMENT, ignore, FONTDIMEN, ignore, # FONTDIMEN properties follow SLANT, fontdimen, SPACE, fontdimen, STRETCH, fontdimen, SHRINK, fontdimen, XHEIGHT, fontdimen, QUAD, fontdimen, EXTRASPACE, fontdimen, NUM1, fontdimen, NUM2, fontdimen, NUM3, fontdimen, DENOM1, fontdimen, DENOM2, fontdimen, SUP1, fontdimen, SUP2, fontdimen, SUP3, fontdimen, SUB1, fontdimen, SUB2, fontdimen, SUPDROP, fontdimen, SUBDROP, fontdimen, DELIM1, fontdimen, DELIM2, fontdimen, AXISHEIGHT, fontdimen, DEFAULTRULETHICKNESS, fontdimen, BIGOPSPACING1, fontdimen, BIGOPSPACING2, fontdimen, BIGOPSPACING3, fontdimen, BIGOPSPACING4, fontdimen, BIGOPSPACING5, fontdimen, PARAMETER, parameter, LIGTABLE, ligtable, # LIGTABLE properties follow LABEL, ligprop, KRN, ligprop, STOP, ligprop, SKIP, ligprop, LIG, ligprop, '/LIG', ligprop, '/LIG>', ligprop, 'LIG/', ligprop, 'LIG/>', ligprop, '/LIG/', ligprop, '/LIG/>', ligprop, '/LIG/>>', ligprop, MAPFONT, mapfont, # MAPFONT properties follow FONTDSIZE, mapfontprop, FONTNAME, mapfontprop, FONTAREA, mapfontprop, FONTCHECKSUM, mapfontprop, FONTAT, fontat, CHARACTER, character, # CHARACTER properties follow CHARWD, charprop, CHARHT, charprop, CHARDP, charprop, CHARIC, charprop, NEXTLARGER, nextlarger, VARCHAR, ignore, # VARCHAR properties follow TOP, varprop, MID, varprop, BOT, varprop, REP, varprop, MAP, map, # MAP properties follow SELECTFONT, mapprop, SETCHAR, mapprop, SETRULE, mappropn, PUSH, mapprop, POP, mapprop, MOVERIGHT, mappropn, MOVELEFT, mappropn, MOVEUP, mappropn, MOVEDOWN, mappropn, SPECIAL, mapprop, SPECIALHEX, mapprop ); sub getpl { local($fontname) = &fontname(@_); $fontprop{$fontname,0,FONTNAME} = "(FONTNAME $fontname)"; &parsepl(@_); if ($scale && !defined($fontprop{$fontname,0,FONTAT})) { $dunits = $designunits if !defined($dunits); $dsize = $designsize if !defined($dsize); local($fontat) = $scale*$dunits/$dsize; $fontprop{$fontname,0,FONTAT} = sprintf("(FONTAT R %.5f)", $fontat) if $fontat != $dunits; } } ############################################################################### # output routines ############################################################################### # output VPL variables @charic = (); # italic corrections @charwd = (); # character widths @charht = (); # character heights @chardp = (); # character depths @charmap = (); # character map @nextlarger = (); # nextlarger lists %varchar = (); # number,property -> number %ligtable = (); # font,left,right -> lig/kern property %kernslike = (); # font,number -> number @fontdimen = (); # array of number -> property %mapfont = (); # number,property -> property %fontprop = (); # name,select,propname -> property @header = (); # header byte -> value @charfrom = (); # number -> font $mapfont = 0; # current MAPFONT number # fontmapsto(file, select) # maps a font selection number to the final output number and outputs MAPFONT sub fontmapsto { # return final MAPFONT of fontname,selectnum local($fn, $select) = @_; local($fontname) = $fontprop{$fn,$select,FONTNAME}; local($fontarea) = $fontprop{$fn,$select,FONTAREA}; local($fontat) = $fontprop{$fn,$select,FONTAT}; if (!defined($mapfont{$fontname,$fontarea,$fontat})) { local(@mapfont); foreach (FONTNAME, FONTAREA, FONTCHECKSUM, FONTAT, FONTDSIZE) { push(@mapfont, $fontprop{$fn,$select,$_}) if defined($fontprop{$fn,$select,$_}); } &expand("(MAPFONT D $mapfont", @mapfont, ')'); $mapfont{$fontname,$fontarea,$fontat} = $mapfont++; } $mapfont{$fontname,$fontarea,$fontat}; } # remap(file, map) # remaps font selections to the final output font sub remap { # remap fontmap to output fonts local($fn) = shift; local($lastsel, $lastfont, $font) = (0, 0); foreach (@_) { if (/\(SELECTFONT (.*)\)/) { $font = &fontmapsto($fn, $lastsel = &number($1)); if ($font != $lastfont) { # substitute mapped font $_ = "(SELECTFONT D $font)"; } else { # font same as last selected, and can be ignored $_ = ''; } $lastfont = $font; } elsif (/SETCHAR/) { $font = &fontmapsto($fn, $lastsel); $_ = "(SELECTFONT D $font)\n$_" if ($font != $lastfont); # substitute mapped font $lastfont = $font; } } join("\n", @_); } # makevpl() # print out tidied up vpl file sub makevpl { local(@proplist, $index, $temp); local($font, $right, $left, %outlig); # for ligtable construction print STDERR "Creating virtual property list\n" if !$quiet; print STDERR "...writing header\n" if !$quiet; print "(VTITLE created by $vtitle)\n", "(COMMENT $prog is (C) A. J. C. Duggan 1993)\n"; printf "(DESIGNSIZE R %.5f)\n", $dsize if defined($dsize); printf "(DESIGNUNITS R %.5f)\n", $dunits if defined($dunits); print "(FAMILY $ffamily)\n" if defined($ffamily); printf "(FACE O %o)\n", $fface if defined($fface); print "(CODINGSCHEME $fcoding)\n" if defined($fcoding); for ($index = 0; $index < @header; $index++) { # output header bytes print "(HEADER $header[$index])\n" if defined $header[$index]; } print STDERR "...writing font dimensions\n" if !$quiet; &expand('(FONTDIMEN', grep($_ ne undef, @fontdimen), ')') if @fontdimen; # output fontdimens print STDERR "...writing font mappings\n" if !$quiet; for ($index = 0; $index < 256; $index++) { # remap fonts $charmap[$index] = &remap($charfrom[$index], split("\n", $charmap[$index])) if defined($charmap[$index]); } print STDERR "...writing ligature table\n" if !$quiet; printf "(BOUNDARYCHAR %s)\n", &charnum($fbchar) if defined($fbchar); while (($_, $temp) = each %ligtable) { # ligtable mangling ($font, $left, $right) = split($;); $outlig{$left} .= "$temp\n" if ($left eq BOUNDARYCHAR || $charfrom[$left] eq $font) && ((defined($fbchar) && $right == $fbchar) || $charfrom[$right] eq $font); } @proplist = (); # clear output LIGTABLE foreach (keys(%outlig)) { while (($left, $right) = each %kernslike) { ($font, $left) = split($;, $left); if ($right eq $_ && ($left eq BOUNDARYCHAR || $charfrom[$left] eq $font)) { if ($left eq BOUNDARYCHAR) { push(@proplist, "(LABEL $left)"); } else { push(@proplist, sprintf("(LABEL %s)", &charnum($left))); } } } if ($_ eq BOUNDARYCHAR) { push(@proplist, "(LABEL $_)"); } else { push(@proplist, sprintf("(LABEL %s)", &charnum($_))); } push(@proplist, split("\n", $outlig{$_})); push(@proplist, '(STOP)'); } &expand('(LIGTABLE', @proplist, ')') if @proplist; # output ligtable print STDERR "...writing characters\n" if !$quiet; for ($index = 0; $index < 256; $index++) { # only output chars with MAPs @proplist = (); foreach (TOP, MID, BOT, REP) { push(@proplist, sprintf("($_ %s)", &charnum($temp))) if defined($temp = $varchar{$index,$_}) && defined($charmap[$temp]); } &expand(sprintf("(CHARACTER %s", &charnum($index)), sprintf("(COMMENT %s)", &encodeto($index)), $charwd[$index], $charht[$index], $chardp[$index], $charic[$index], defined($temp = $nextlarger[$index]) && defined($charmap[$temp]) ? sprintf("(NEXTLARGER %s)", &charnum($temp)) : '', @proplist ? ('(VARCHAR', @proplist, ')') : '', $charmap[$index], ')') if defined($charmap[$index]); } print STDERR "...done\n" if !$quiet; } ############################################################################### # Actions ############################################################################### # default actions %add = (fd, '-'); # additions %sub = (wd, '-', ht, '-', dp, '-', ic, '-', map, '-'); # substitutions $scale = 0; # scale ############################################################################### # Argument processing ############################################################################### $vtitle = join(' ', $prog, @ARGV); # set VTITLE to arguments $defaultenc = 'standard'; # default encoding while (@ARGV) { $_ = shift; ARGSW: { /^-quiet$/ && ($quiet = 1, last ARGSW); /^-defenc$/ && ($defaultenc = shift, last ARGSW); /^-enc$/ && (&getencoding(shift, &pathexpand($ENV{ENCPATH}, '.:@ENCODINGDIR@')), last ARGSW); /^-debug$/ && ($debug = 1, last ARGSW); /^-at$/ && ($scale = shift, last ARGSW); /^-normal$/ && ($scale = 0, last ARGSW); /^-replacech$/ && ($sub{wd} = $sub{ht} = $sub{dp} = $sub{ic} = $sub{map} = shift, $add{wd} = $add{ht} = $add{dp} = $add{ic} = $add{map} = '', last ARGSW); /^-addch$/ && ($sub{wd} = $sub{ht} = $sub{dp} = $sub{ic} = $sub{map} = '', $add{wd} = $add{ht} = $add{dp} = $add{ic} = $add{map} = shift, last ARGSW); /^-replace(ic|wd|ht|dp|fd|map)$/ && ($sub{$1} = shift, $add{$1} = '', last ARGSW); /^-add(ic|wd|ht|dp|fd|map)$/ && ($sub{$1} = '', $add{$1} = shift, last ARGSW); /^-size$/ && ($dsize = shift, last ARGSW); /^-units$/ && ($dunits = shift, last ARGSW); /^-family$/ && ($ffamily = shift, last ARGSW); /^-face$/ && ($fface = shift, last ARGSW); /^-coding$/ && ($fcoding = shift, last ARGSW); if (/^-/) { /^-v$/ && print STDERR "$prog release @RELEASE@\n"; print STDERR join("\n", "Usage: $prog [-quiet] [-defenc defaultenc] [-enc enc] [-at size] [-normal]", " [-addxx range|-replacexx range, xx=ch,fd,wd,ht,dp,ic,map]", " [-size num] [-units num] [-family str] [-face num] [-coding scheme]", " filename[:encoding]...\n"); exit 1; } else { # filename[:encoding] %defaultadd = %add if !defined(%defaultadd); # set default actions %defaultsub = %sub if !defined(%defaultsub); $defaultscale = $scale if !defined($defaultscale); ($file, $encoding) = split(/:/); &getencoding($encoding eq '' ? $defaultenc : $encoding, &pathexpand($ENV{ENCPATH}, '.:@ENCODINGDIR@')); &getpl($file); # process PL file %add = %defaultadd; # restore default actions %sub = %defaultsub; $scale = $defaultscale; } } } &fatal('no property list files specified') if !defined($file); &makevpl; # build and print final PL exit 0; # good termination