summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/mf2pt1
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2011-08-04 17:47:47 +0000
committerKarl Berry <karl@freefriends.org>2011-08-04 17:47:47 +0000
commit823bec37813c80f4240bf3431a23c8e6af60f94f (patch)
tree7ac32545dad59a4769e13509d8ea96d5c4654291 /Master/texmf-dist/scripts/mf2pt1
parent1491bd20a6acc228027bb0ef69f72b48aa6a572d (diff)
new (to TL) script mf2pt1, other script consistencies (2aug11)
git-svn-id: svn://tug.org/texlive/trunk@23406 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/mf2pt1')
-rwxr-xr-xMaster/texmf-dist/scripts/mf2pt1/mf2pt1.pl1090
1 files changed, 1090 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/mf2pt1/mf2pt1.pl b/Master/texmf-dist/scripts/mf2pt1/mf2pt1.pl
new file mode 100755
index 00000000000..2119e7b6ab6
--- /dev/null
+++ b/Master/texmf-dist/scripts/mf2pt1/mf2pt1.pl
@@ -0,0 +1,1090 @@
+#! /usr/bin/perl
+
+##################################################
+# Convert stylized Metafont to PostScript Type 1 #
+# By Scott Pakin <scott+mf@pakin.org> #
+##################################################
+
+########################################################################
+# mf2pt1 #
+# Copyright (C) 2011 Scott Pakin #
+# #
+# This program may be distributed and/or modified under the conditions #
+# of the LaTeX Project Public License, either version 1.3c of this #
+# license or (at your option) any later version. #
+# #
+# The latest version of this license is in: #
+# #
+# http://www.latex-project.org/lppl.txt #
+# #
+# and version 1.3c or later is part of all distributions of LaTeX #
+# version 2006/05/20 or later. #
+########################################################################
+
+our $VERSION = "2.4.5"; # mf2pt1 version number
+require 5.6.1; # I haven't tested mf2pt1 with older Perl versions
+
+use File::Basename;
+use File::Spec;
+use Getopt::Long;
+use Pod::Usage;
+use Math::Trig;
+use warnings;
+use strict;
+
+# Define some common encoding vectors.
+my @standardencoding =
+ ((map {"_a$_"} (0..31)),
+ qw (space exclam quotedbl numbersign dollar percent ampersand
+ quoteright parenleft parenright asterisk plus comma hyphen
+ period slash zero one two three four five six seven eight
+ nine colon semicolon less equal greater question at A B C D E
+ F G H I J K L M N O P Q R S T U V W X Y Z bracketleft
+ backslash bracketright asciicircum underscore quoteleft a b c
+ d e f g h i j k l m n o p q r s t u v w x y z braceleft bar
+ braceright asciitilde),
+ (map {"_a$_"} (127..160)),
+ qw (exclamdown cent sterling fraction yen florin section currency
+ quotesingle quotedblleft guillemotleft guilsinglleft
+ guilsinglright fi fl _a176 endash dagger daggerdbl
+ periodcentered _a181 paragraph bullet quotesinglbase
+ quotedblbase quotedblright guillemotright ellipsis
+ perthousand _a190 questiondown _a192 grave acute circumflex
+ tilde macron breve dotaccent dieresis _a201 ring cedilla
+ _a204 hungarumlaut ogonek caron emdash),
+ (map {"_a$_"} (209..224)),
+ qw (AE _a226 ordfeminine _a228 _a229 _a230 _a231 Lslash Oslash OE
+ ordmasculine _a236 _a237 _a238 _a239 _a240 ae _a242 _a243
+ _a244 dotlessi _a246 _a247 lslash oslash oe germandbls _a252
+ _a253 _a254 _a255));
+my @isolatin1encoding =
+ ((map {"_a$_"} (0..31)),
+ qw (space exclam quotedbl numbersign dollar percent ampersand
+ quoteright parenleft parenright asterisk plus comma minus
+ period slash zero one two three four five six seven eight
+ nine colon semicolon less equal greater question at A B C D E
+ F G H I J K L M N O P Q R S T U V W X Y Z bracketleft
+ backslash bracketright asciicircum underscore quoteleft a b c
+ d e f g h i j k l m n o p q r s t u v w x y z braceleft bar
+ braceright asciitilde),
+ (map {"_a$_"} (128..143)),
+ qw (dotlessi grave acute circumflex tilde macron breve dotaccent
+ dieresis _a153 ring cedilla _a156 hungarumlaut ogonek
+ caron space exclamdown cent sterling currency yen brokenbar
+ section dieresis copyright ordfeminine guillemotleft
+ logicalnot hyphen registered macron degree plusminus
+ twosuperior threesuperior acute mu paragraph periodcentered
+ cedilla onesuperior ordmasculine guillemotright onequarter
+ onehalf threequarters questiondown Agrave Aacute Acircumflex
+ Atilde Adieresis Aring AE Ccedilla Egrave Eacute Ecircumflex
+ Edieresis Igrave Iacute Icircumflex Idieresis Eth Ntilde
+ Ograve Oacute Ocircumflex Otilde Odieresis multiply Oslash
+ Ugrave Uacute Ucircumflex Udieresis Yacute Thorn germandbls
+ agrave aacute acircumflex atilde adieresis aring ae ccedilla
+ egrave eacute ecircumflex edieresis igrave iacute icircumflex
+ idieresis eth ntilde ograve oacute ocircumflex otilde
+ odieresis divide oslash ugrave uacute ucircumflex udieresis
+ yacute thorn ydieresis));
+my @ot1encoding =
+ qw (Gamma Delta Theta Lambda Xi Pi Sigma Upsilon Phi
+ Psi Omega ff fi fl ffi ffl dotlessi dotlessj grave acute caron
+ breve macron ring cedilla germandbls ae oe oslash AE OE Oslash
+ suppress exclam quotedblright numbersign dollar percent
+ ampersand quoteright parenleft parenright asterisk plus comma
+ hyphen period slash zero one two three four five six seven
+ eight nine colon semicolon exclamdown equal questiondown
+ question at A B C D E F G H I J K L M N O P Q R S T U V W X Y
+ Z bracketleft quotedblleft bracketright circumflex dotaccent
+ quoteleft a b c d e f g h i j k l m n o p q r s t u v w x y z
+ endash emdash hungarumlaut tilde dieresis);
+my @t1encoding =
+ qw (grave acute circumflex tilde dieresis hungarumlaut ring caron
+ breve macron dotaccent cedilla ogonek quotesinglbase
+ guilsinglleft guilsinglright quotedblleft quotedblright
+ quotedblbase guillemotleft guillemotright endash emdash cwm
+ perthousand dotlessi dotlessj ff fi fl ffi ffl space exclam
+ quotedbl numbersign dollar percent ampersand quoteright
+ parenleft parenright asterisk plus comma hyphen period slash
+ zero one two three four five six seven eight nine colon
+ semicolon less equal greater question at A B C D E F G H I J K L
+ M N O P Q R S T U V W X Y Z bracketleft backslash bracketright
+ asciicircum underscore quoteleft a b c d e f g h i j k l m n o p
+ q r s t u v w x y z braceleft bar braceright asciitilde
+ sfthyphen Abreve Aogonek Cacute Ccaron Dcaron Ecaron Eogonek
+ Gbreve Lacute Lcaron Lslash Nacute Ncaron Eng Ohungarumlaut
+ Racute Rcaron Sacute Scaron Scedilla Tcaron Tcedilla
+ Uhungarumlaut Uring Ydieresis Zacute Zcaron Zdotaccent IJ
+ Idotaccent dcroat section abreve aogonek cacute ccaron dcaron
+ ecaron eogonek gbreve lacute lcaron lslash nacute ncaron eng
+ ohungarumlaut racute rcaron sacute scaron scedilla tcaron
+ tcedilla uhungarumlaut uring ydieresis zacute zcaron zdotaccent
+ ij exclamdown questiondown sterling Agrave Aacute Acircumflex
+ Atilde Adieresis Aring AE Ccedilla Egrave Eacute Ecircumflex
+ Edieresis Igrave Iacute Icircumflex Idieresis Eth Ntilde Ograve
+ Oacute Ocircumflex Otilde Odieresis OE Oslash Ugrave Uacute
+ Ucircumflex Udieresis Yacute Thorn SS agrave aacute acircumflex
+ atilde adieresis aring ae ccedilla egrave eacute ecircumflex
+ edieresis igrave iacute icircumflex idieresis eth ntilde ograve
+ oacute ocircumflex otilde odieresis oe oslash ugrave uacute
+ ucircumflex udieresis yacute thorn germandbls);
+
+# Define font parameters that the user can override.
+my $fontversion;
+my $creationdate;
+my $comment;
+my $familyname;
+my $weight;
+my $fullname;
+my $fixedpitch;
+my $italicangle;
+my $underlinepos;
+my $underlinethick;
+my $fontname;
+my $uniqueID;
+my $designsize;
+my ($mffile, $pt1file, $pfbfile, $ffscript);
+my $encoding;
+my $rounding;
+my $bpppix;
+
+# Define all of our other global variables.
+my $progname = basename $0, ".pl";
+my $mag;
+my @fontbbox;
+my @charbbox;
+my @charwd;
+my @glyphname;
+my @charfiles;
+my $filebase;
+my $filedir;
+my $filenoext;
+my $versionmsg = "mf2pt1 version $VERSION
+
+Copyright (C) 2011 Scott Pakin
+
+This program may be distributed and/or modified under the conditions
+of the LaTeX Project Public License, either version 1.3c of this
+license or (at your option) any later version.
+
+The latest version of this license is in:
+
+ http://www.latex-project.org/lppl.txt
+
+and version 1.3c or later is part of all distributions of LaTeX
+version 2006/05/20 or later.
+";
+
+
+######################################################################
+
+# The routines to compute the fractional approximation of a real number
+# are heavily based on code posted by Ben Tilly
+# <http://www.perlmonks.org/?node_id=26179> on Nov 16th, 2000, to the
+# PerlMonks list. See <http://www.perlmonks.org/index.pl?node_id=41961>.
+
+
+# Takes numerator/denominator pairs.
+# Returns a PS fraction string representation (with a trailing space).
+sub frac_string (@)
+{
+ my $res = "";
+
+ while (@_) {
+ my $n = shift;
+ my $d = shift;
+ $res .= $n . " ";
+ $res .= $d . " div " if $d > 1;
+ }
+
+ return $res;
+}
+
+
+# Takes a number.
+# Returns a numerator and denominator with the smallest denominator
+# so that the difference of the resulting fraction to the number is
+# smaller or equal to $rounding.
+sub frac_approx ($)
+{
+ my $num = shift;
+ my $f = ret_frac_iter ($num);
+
+ while (1) {
+ my ($n, $m) = $f->();
+ my $approx = $n / $m;
+ my $delta = abs ($num - $approx);
+ return ($n, $m) if ($delta <= $rounding);
+ }
+}
+
+
+# Takes a number, returns the best integer approximation and (in list
+# context) the error.
+sub best_int ($)
+{
+ my $x = shift;
+ my $approx = sprintf '%.0f', $x;
+ if (wantarray) {
+ return ($approx, $x - $approx);
+ }
+ else {
+ return $approx;
+ }
+}
+
+
+# Takes a numerator and denominator, in scalar context returns
+# the best fraction describing them, in list the numerator and
+# denominator.
+sub frac_standard ($$)
+{
+ my $n = best_int(shift);
+ my $m = best_int(shift);
+ my $k = gcd($n, $m);
+ $n /= $k;
+ $m /= $k;
+ if ($m < 0) {
+ $n *= -1;
+ $m *= -1;
+ }
+ if (wantarray) {
+ return ($n, $m);
+ }
+ else {
+ return "$n/$m";
+ }
+}
+
+
+# Euclidean algorithm for calculating a GCD.
+# Takes two integers, returns the greatest common divisor.
+sub gcd ($$)
+{
+ my ($n, $m) = @_;
+ while ($m) {
+ my $k = $n % $m;
+ ($n, $m) = ($m, $k);
+ }
+ return $n;
+}
+
+
+# Takes a list of terms in a continued fraction, and converts it
+# into a fraction.
+sub ints_to_frac (@)
+{
+ my ($n, $m) = (0, 1); # Start with 0
+ while (@_) {
+ my $k = pop;
+ if ($n) {
+ # Want frac for $k + 1/($n/$m)
+ ($n, $m) = frac_standard($k*$n + $m, $n);
+ }
+ else {
+ # Want $k
+ ($n, $m) = frac_standard($k, 1);
+ }
+ }
+ return frac_standard($n, $m);
+}
+
+
+# Takes a number, returns an anon sub which iterates through a set of
+# fractional approximations that converges very quickly to the number.
+sub ret_frac_iter ($)
+{
+ my $x = shift;
+ my $term_iter = ret_next_term_iter($x);
+ my @ints;
+ return sub {
+ push @ints, $term_iter->();
+ return ints_to_frac(@ints);
+ }
+}
+
+
+# Terms of a continued fraction converging on that number.
+sub ret_next_term_iter ($)
+{
+ my $x = shift;
+ return sub {
+ (my $n, $x) = best_int($x);
+ if (0 != $x) {
+ $x = 1/$x;
+ }
+ return $n;
+ }
+}
+
+######################################################################
+
+# Round a number to the nearest integer.
+sub round ($)
+{
+ return int($_[0] + 0.5*($_[0] <=> 0));
+}
+
+
+# Round a number to a given precision.
+sub prec ($)
+{
+ return round ($_[0] / $rounding) * $rounding;
+}
+
+
+# Set a variable's value to the first defined value in the given list.
+# If the variable was not previously defined and no value in the list
+# is defined, do nothing.
+sub assign_default (\$@)
+{
+ my $varptr = shift; # Pointer to variable to define
+ return if defined $$varptr && $$varptr ne "UNSPECIFIED";
+ foreach my $val (@_) {
+ next if !defined $val;
+ $$varptr = $val;
+ return;
+ }
+}
+
+
+# Print and execute a shell command. An environment variable with the
+# same name as the command overrides the command name. Return 1 on
+# success, 0 on failure. Optionally abort if the command fails, based
+# on the first argument to execute_command.
+sub execute_command ($@)
+{
+ my $abort_on_failure = shift;
+ my @command = @_;
+ $command[0] = $ENV{uc $command[0]} || $command[0];
+ my $prettyargs = join (" ", map {/[\\ ]/ ? "'$_'" : $_} @command);
+ print "Invoking \"$prettyargs\"...\n";
+ my $result = system @command;
+ die "${progname}: \"$prettyargs\" failed ($!)\n" if $result && $abort_on_failure;
+ return !$result;
+}
+
+
+# Output the font header.
+sub output_header ()
+{
+ # Show the initial boilerplate.
+ print OUTFILE <<"ENDHEADER";
+%!FontType1-1.0: $fontname $fontversion
+%%CreationDate: $creationdate
+% Font converted to Type 1 by mf2pt1, written by Scott Pakin.
+11 dict begin
+/FontInfo 11 dict dup begin
+/version ($fontversion) readonly def
+/Notice ($comment) readonly def
+/FullName ($fullname) readonly def
+/FamilyName ($familyname) readonly def
+/Weight ($weight) readonly def
+/ItalicAngle $italicangle def
+/isFixedPitch $fixedpitch def
+/UnderlinePosition $underlinepos def
+/UnderlineThickness $underlinethick def
+end readonly def
+/FontName /$fontname def
+ENDHEADER
+
+ # If we're not using an encoding that PostScript knows about, then
+ # create an encoding vector.
+ if ($encoding==\@standardencoding) {
+ print OUTFILE "/Encoding StandardEncoding def\n";
+ }
+ else {
+ print OUTFILE "/Encoding 256 array\n";
+ print OUTFILE "0 1 255 {1 index exch /.notdef put} for\n";
+ foreach my $charnum (0 .. $#{$encoding}) {
+ if ($encoding->[$charnum] && $encoding->[$charnum]!~/^_a\d+$/) {
+ print OUTFILE "dup $charnum /$encoding->[$charnum] put\n";
+ }
+ }
+ print OUTFILE "readonly def\n";
+ }
+
+ # Show the final boilerplate.
+ print OUTFILE <<"ENDHEADER";
+/PaintType 0 def
+/FontType 1 def
+/FontMatrix [0.001 0 0 0.001 0 0] readonly def
+/UniqueID $uniqueID def
+/FontBBox{@fontbbox}readonly def
+currentdict end
+currentfile eexec
+dup /Private 5 dict dup begin
+/RD{string currentfile exch readstring pop}executeonly def
+/ND{noaccess def}executeonly def
+/NP{noaccess put}executeonly def
+ENDHEADER
+}
+
+
+# Use MetaPost to generate one PostScript file per character. We
+# calculate the font bounding box from these characters and store them
+# in @fontbbox. If the input parameter is 1, set other font
+# parameters, too.
+sub get_bboxes ($)
+{
+ execute_command 1, ("mpost", "-mem=mf2pt1", "-progname=mpost",
+ "\\mode:=localfont; mag:=$mag; bpppix $bpppix; input $mffile");
+ opendir (CURDIR, ".") || die "${progname}: $! ($filedir)\n";
+ @charfiles = sort
+ { ($a=~ /\.(\d+)$/)[0] <=> ($b=~ /\.(\d+)$/)[0] }
+ grep /^$filebase.*\.\d+$/, readdir(CURDIR);
+ close CURDIR;
+ @fontbbox = (1000000, 1000000, -1000000, -1000000);
+ foreach my $psfile (@charfiles) {
+ # Read the character number from the output file's extension.
+ $psfile =~ /\.(\d+)$/;
+ my $charnum = $1;
+
+ # Process in turn each line of the current PostScript file.
+ my $havebbox = 0;
+ open (PSFILE, "<$psfile") || die "${progname}: $! ($psfile)\n";
+ while (<PSFILE>) {
+ my @tokens = split " ";
+ if ($tokens[0] eq "%%BoundingBox:") {
+ # Store the MetaPost-produced bounding box, just in case
+ # the given font doesn't use beginchar.
+ @tokens = ("%", "MF2PT1:", "glyph_dimensions", @tokens[1..4]);
+ $havebbox--;
+ }
+ next if $#tokens<1 || $tokens[1] ne "MF2PT1:";
+
+ # Process a "special" inserted into the generated PostScript.
+ MF2PT1_CMD:
+ {
+ # glyph_dimensions llx lly urx ury -- specified glyph dimensions
+ $tokens[2] eq "glyph_dimensions" && do {
+ my @bbox = @tokens[3..6];
+ $fontbbox[0]=$bbox[0] if $bbox[0]<$fontbbox[0];
+ $fontbbox[1]=$bbox[1] if $bbox[1]<$fontbbox[1];
+ $fontbbox[2]=$bbox[2] if $bbox[2]>$fontbbox[2];
+ $fontbbox[3]=$bbox[3] if $bbox[3]>$fontbbox[3];
+ $charbbox[$charnum] = \@bbox;
+ $havebbox++;
+ last MF2PT1_CMD;
+ };
+
+ # If all we want is the bounding box, exit the loop now.
+ last MF2PT1_CMD if !$_[0];
+
+ # glyph_name name -- glyph name
+ $tokens[2] eq "glyph_name" && do {
+ $glyphname[$charnum] = $tokens[3];
+ last MF2PT1_CMD;
+ };
+
+ # charwd wd -- character width as in TFM
+ $tokens[2] eq "charwd" && do {
+ $charwd[$charnum] = $tokens[3];
+ last MF2PT1_CMD;
+ };
+
+ # font_identifier name -- full font name
+ $tokens[2] eq "font_identifier" && do {
+ $fullname = $tokens[3];
+ last MF2PT1_CMD;
+ };
+
+ # font_size number -- font design size (pt, not bp)
+ $tokens[2] eq "font_size" && $tokens[3] && do {
+ $designsize = $tokens[3] * 72 / 72.27;
+ last MF2PT1_CMD;
+ };
+
+ # font_slant number -- italic amount
+ $tokens[2] eq "font_slant" && do {
+ $italicangle = 0 + rad2deg (atan(-$tokens[3]));
+ last MF2PT1_CMD;
+ };
+
+ # font_coding_scheme string -- font encoding
+ $tokens[2] eq "font_coding_scheme" && do {
+ $encoding = $tokens[3];
+ last MF2PT1_CMD;
+ };
+
+ # font_version string -- font version number (xxx.yyy)
+ $tokens[2] eq "font_version" && do {
+ $fontversion = $tokens[3];
+ last MF2PT1_CMD;
+ };
+
+ # font_comment string -- font comment notice
+ $tokens[2] eq "font_comment" && do {
+ $comment = join (" ", @tokens[3..$#tokens]);
+ last MF2PT1_CMD;
+ };
+
+ # font_family string -- font family name
+ $tokens[2] eq "font_family" && do {
+ $familyname = $tokens[3];
+ last MF2PT1_CMD;
+ };
+
+ # font_weight string -- font weight (e.g., "Book" or "Heavy")
+ $tokens[2] eq "font_weight" && do {
+ $weight = $tokens[3];
+ last MF2PT1_CMD;
+ };
+
+ # font_fixed_pitch number -- fixed width font (0=false, 1=true)
+ $tokens[2] eq "font_fixed_pitch" && do {
+ $fixedpitch = $tokens[3];
+ last MF2PT1_CMD;
+ };
+
+ # font_underline_position number -- vertical underline position
+ $tokens[2] eq "font_underline_position" && do {
+ # We store $underlinepos in points and later
+ # scale it by 1000/$designsize.
+ $underlinepos = $tokens[3];
+ last MF2PT1_CMD;
+ };
+
+ # font_underline_thickness number -- thickness of underline
+ $tokens[2] eq "font_underline_thickness" && do {
+ # We store $underlinethick in points and later
+ # scale it by 1000/$designsize.
+ $underlinethick = $tokens[3];
+ last MF2PT1_CMD;
+ };
+
+ # font_name string -- font name
+ $tokens[2] eq "font_name" && do {
+ $fontname = $tokens[3];
+ last MF2PT1_CMD;
+ };
+
+ # font_unique_id number (as string) -- globally unique font ID
+ $tokens[2] eq "font_unique_id" && do {
+ $uniqueID = 0+$tokens[3];
+ last MF2PT1_CMD;
+ };
+ }
+ }
+ close PSFILE;
+ if (!$havebbox) {
+ warn "${progname}: No beginchar in character $charnum; glyph dimensions are probably incorrect\n";
+ }
+ }
+}
+
+
+# Convert ordinary, MetaPost-produced PostScript files into Type 1
+# font programs.
+sub output_font_programs ()
+{
+ # Iterate over all the characters. We convert each one, line by
+ # line and token by token.
+ print "Converting PostScript graphics to Type 1 font programs...\n";
+ foreach my $psfile (@charfiles) {
+ # Initialize the font program.
+ $psfile =~ /\.(\d+)$/;
+ my $charnum = $1;
+ my $gname = $glyphname[$charnum] || $encoding->[$charnum];
+ my @fontprog;
+ push @fontprog, ("/$gname {",
+ frac_string (frac_approx ($charbbox[$charnum]->[0]),
+ frac_approx ($charwd[$charnum] * $mag))
+ . "hsbw");
+ my ($cpx, $cpy) =
+ ($charbbox[$charnum]->[0], 0); # Current point (PostScript)
+
+ # Iterate over every line in the current file.
+ open (PSFILE, "<$psfile") || die "${progname}: $! ($psfile)\n";
+ while (my $oneline=<PSFILE>) {
+ next if $oneline=~/^\%/;
+ next if $oneline=~/set/; # Fortunately, "set" never occurs on "good" lines.
+ my @arglist; # Arguments to current PostScript function
+
+ # Iterate over every token in the current line.
+ TOKENLOOP:
+ foreach my $token (split " ", $oneline) {
+ # Number: Round and push on the argument list.
+ $token =~ /^[-.\d]+$/ && do {
+ push @arglist, prec ($&);
+ next TOKENLOOP;
+ };
+
+ # curveto: Convert to vhcurveto, hvcurveto, or rrcurveto.
+ $token eq "curveto" && do {
+ my ($dx1, $dy1) = ($arglist[0] - $cpx,
+ $arglist[1] - $cpy);
+ my ($dx1n, $dx1d) = frac_approx ($dx1);
+ my ($dy1n, $dy1d) = frac_approx ($dy1);
+ $cpx += $dx1n / $dx1d;
+ $cpy += $dy1n / $dy1d;
+
+ my ($dx2, $dy2) = ($arglist[2] - $cpx,
+ $arglist[3] - $cpy);
+ my ($dx2n, $dx2d) = frac_approx ($dx2);
+ my ($dy2n, $dy2d) = frac_approx ($dy2);
+ $cpx += $dx2n / $dx2d;
+ $cpy += $dy2n / $dy2d;
+
+ my ($dx3, $dy3) = ($arglist[4] - $cpx,
+ $arglist[5] - $cpy);
+ my ($dx3n, $dx3d) = frac_approx ($dx3);
+ my ($dy3n, $dy3d) = frac_approx ($dy3);
+ $cpx += $dx3n / $dx3d;
+ $cpy += $dy3n / $dy3d;
+
+ if (!$dx1n && !$dy3n) {
+ push @fontprog, frac_string ($dy1n, $dy1d,
+ $dx2n, $dx2d,
+ $dy2n, $dy2d,
+ $dx3n, $dx3d)
+ . "vhcurveto";
+ }
+ elsif (!$dy1n && !$dx3n) {
+ push @fontprog, frac_string ($dx1n, $dx1d,
+ $dx2n, $dx2d,
+ $dy2n, $dy2d,
+ $dy3n, $dy3d)
+ . "hvcurveto";
+ }
+ else {
+ push @fontprog, frac_string ($dx1n, $dx1d,
+ $dy1n, $dy1d,
+ $dx2n, $dx2d,
+ $dy2n, $dy2d,
+ $dx3n, $dx3d,
+ $dy3n, $dy3d)
+ . "rrcurveto";
+ }
+ next TOKENLOOP;
+ };
+
+ # lineto: Convert to vlineto, hlineto, or rlineto.
+ $token eq "lineto" && do {
+ my ($dx, $dy) = ($arglist[0] - $cpx,
+ $arglist[1] - $cpy);
+ my ($dxn, $dxd) = frac_approx ($dx);
+ my ($dyn, $dyd) = frac_approx ($dy);
+ $cpx += $dxn / $dxd;
+ $cpy += $dyn / $dyd;
+
+ if (!$dxn) {
+ push @fontprog, frac_string ($dyn, $dyd)
+ . "vlineto" if $dyn;
+ }
+ elsif (!$dyn) {
+ push @fontprog, frac_string ($dxn, $dxd)
+ . "hlineto";
+ }
+ else {
+ push @fontprog, frac_string ($dxn, $dxd, $dyn, $dyd)
+ . "rlineto";
+ }
+ next TOKENLOOP;
+ };
+
+ # moveto: Convert to vmoveto, hmoveto, or rmoveto.
+ $token eq "moveto" && do {
+ my ($dx, $dy) = ($arglist[0] - $cpx,
+ $arglist[1] - $cpy);
+ my ($dxn, $dxd) = frac_approx ($dx);
+ my ($dyn, $dyd) = frac_approx ($dy);
+ $cpx += $dxn / $dxd;
+ $cpy += $dyn / $dyd;
+
+ if (!$dxn) {
+ push @fontprog, frac_string ($dyn, $dyd)
+ . "vmoveto";
+ }
+ elsif (!$dyn) {
+ push @fontprog, frac_string ($dxn, $dxd)
+ . "hmoveto";
+ }
+ else {
+ push @fontprog, frac_string ($dxn, $dxd, $dyn, $dyd)
+ . "rmoveto";
+ }
+ next TOKENLOOP;
+ };
+
+ # closepath: Output as is.
+ $token eq "closepath" && do {
+ push @fontprog, $token;
+ next TOKENLOOP;
+ };
+ }
+ }
+ close PSFILE;
+ push @fontprog, ("endchar",
+ "} ND");
+ print OUTFILE join ("\n\t", @fontprog), "\n";
+ }
+}
+
+
+# Output the final set of code for the Type 1 font.
+sub output_trailer ()
+{
+ print OUTFILE <<"ENDTRAILER";
+/.notdef {
+ 0 @{[frac_string (frac_approx ($fontbbox[2] - $fontbbox[0]))]} hsbw
+ endchar
+ } ND
+end
+end
+readonly put
+noaccess put
+dup/FontName get exch definefont pop
+mark currentfile closefile
+cleartomark
+ENDTRAILER
+}
+
+######################################################################
+
+# Parse the command line. Asterisks in the following represents
+# commands also defined by Plain Metafont.
+my %opthash = ();
+GetOptions (\%opthash,
+ "fontversion=s", # font_version
+ "comment=s", # font_comment
+ "family=s", # font_family
+ "weight=s", # font_weight
+ "fullname=s", # font_identifier (*)
+ "fixedpitch!", # font_fixed_pitch
+ "italicangle=f", # font_slant (*)
+ "underpos=f", # font_underline_position
+ "underthick=f", # font_underline_thickness
+ "name=s", # font_name
+ "uniqueid=i", # font_unique_id
+ "designsize=f", # font_size (*)
+ "encoding=s", # font_coding_scheme (*)
+ "rounding=f",
+ "bpppix=f",
+ "ffscript=s",
+ "h|help",
+ "V|version") || pod2usage(2);
+if (defined $opthash{"h"}) {
+ pod2usage(-verbose => 1,
+ -output => \*STDOUT, # Bug workaround for Pod::Usage
+ -exitval => "NOEXIT");
+ print "Please e-mail bug reports to scott+mf\@pakin.org.\n";
+ exit 1;
+}
+do {print $versionmsg; exit 1} if defined $opthash{"V"};
+pod2usage(2) if $#ARGV != 0;
+
+# Extract the filename from the command line.
+$mffile = $ARGV[0];
+my @fileparts = fileparse $mffile, ".mf";
+$filebase = $fileparts[0];
+$filedir = $fileparts[1];
+$filenoext = File::Spec->catfile ($filedir, $filebase);
+$pt1file = $filebase . ".pt1";
+$pfbfile = $filebase . ".pfb";
+
+assign_default $bpppix, $opthash{bpppix}, 0.02;
+
+# Make our first pass through the input, to set values for various options.
+$mag = 100; # Get a more precise bounding box.
+get_bboxes(1); # This might set $designsize.
+
+# Sanity-check the specified precision.
+assign_default $rounding, $opthash{rounding}, 1;
+if ($rounding<=0.0 || $rounding>1.0) {
+ die sprintf "%s: Invalid rounding amount \"%g\"; value must be a positive number no greater than 1.0\n", $progname, $rounding;
+}
+
+# Ensure that every user-definable parameter is assigned a value.
+assign_default $fontversion, $opthash{fontversion}, "001.000";
+assign_default $creationdate, scalar localtime;
+assign_default $comment, $opthash{comment}, "Font converted to Type 1 by mf2pt1, written by Scott Pakin.";
+assign_default $weight, $opthash{weight}, "Medium";
+assign_default $fixedpitch, $opthash{fixedpitch}, 0;
+assign_default $uniqueID, $opthash{uniqueid}, int(rand(1000000)) + 4000000;
+assign_default $designsize, $opthash{designsize};
+die "${progname}: a design size must be specified in $mffile or on the command line\n" if !defined $designsize;
+die "${progname}: the design size must be a positive number\n" if $designsize<=0.0;
+assign_default $underlinepos, $opthash{underpos}, -1;
+$underlinepos = round(1000*$underlinepos/$designsize);
+assign_default $underlinethick, $opthash{underthick}, 0.5;
+$underlinethick = round(1000*$underlinethick/$designsize);
+assign_default $fullname, $opthash{fullname}, $filebase;
+assign_default $familyname, $opthash{family}, $fullname;
+assign_default $italicangle, $opthash{italicangle}, 0;
+assign_default $fontname, $opthash{name}, "$familyname-$weight";
+$fontname =~ s/\s//g;
+assign_default $encoding, $opthash{encoding}, "standard";
+my $encoding_name = $encoding;
+ENCODING:
+{
+ if (-e $encoding) {
+ # Filenames take precedence over built-in encodings.
+ my @enc_array;
+ open (ENCFILE, "<$encoding") || die "${progname}: $! ($encoding)\n";
+ while (my $oneline = <ENCFILE>) {
+ $oneline =~ s/\%.*$//;
+ foreach my $word (split " ", $oneline) {
+ push @enc_array, substr($word, 1) if substr($word, 0, 1) eq "/";
+ }
+ }
+ close ENCFILE;
+ $encoding_name = substr (shift @enc_array, 1);
+ $encoding = \@enc_array;
+ last ENCODING;
+ }
+ $encoding=\@standardencoding, last ENCODING if $encoding eq "standard";
+ $encoding=\@isolatin1encoding, last ENCODING if $encoding eq "isolatin1";
+ $encoding=\@ot1encoding, last ENCODING if $encoding eq "ot1";
+ $encoding=\@t1encoding, last ENCODING if $encoding eq "t1";
+ $encoding=\@glyphname, last ENCODING if $encoding eq "asis";
+ warn "${progname}: Unknown encoding \"$encoding\"; using standard Adobe encoding\n";
+ $encoding=\@standardencoding; # Default to standard encoding
+}
+assign_default $fixedpitch, $opthash{fixedpitch}, 0;
+$fixedpitch = $fixedpitch ? "true" : "false";
+assign_default $ffscript, $opthash{ffscript};
+
+# Output the final values of all of our parameters.
+print "\n";
+print <<"PARAMVALUES";
+mf2pt1 is using the following font parameters:
+ font_version: $fontversion
+ font_comment: $comment
+ font_family: $familyname
+ font_weight: $weight
+ font_identifier: $fullname
+ font_fixed_pitch: $fixedpitch
+ font_slant: $italicangle
+ font_underline_position: $underlinepos
+ font_underline_thickness: $underlinethick
+ font_name: $fontname
+ font_unique_id: $uniqueID
+ font_size: $designsize (bp)
+ font_coding_scheme: $encoding_name
+PARAMVALUES
+ ;
+print "\n";
+
+# Scale by a factor of 1000/design size.
+$mag = 1000.0 / $designsize;
+get_bboxes(0);
+print "\n";
+
+# Output the font in disassembled format.
+open (OUTFILE, ">$pt1file") || die "${progname}: $! ($pt1file)\n";
+output_header();
+printf OUTFILE "2 index /CharStrings %d dict dup begin\n",
+ 1+scalar(grep {defined($_)} @charbbox);
+output_font_programs();
+output_trailer();
+close OUTFILE;
+unlink @charfiles;
+print "\n";
+
+# Convert from the disassembled font format to Type 1 binary format.
+if (!execute_command 0, ("t1asm", $pt1file, $pfbfile)) {
+ die "${progname}: You'll need either to install t1utils and rerun $progname or find another way to convert $pt1file to $pfbfile\n";
+ exit 1;
+}
+print "\n";
+unlink $pt1file;
+
+# Use FontForge to autohint the result.
+my $user_script = 0; # 1=script file was provided by the user; 0=created here
+if (defined $ffscript) {
+ # The user provided his own script.
+ $user_script = 1;
+}
+else {
+ # Create a FontForge script file.
+ $ffscript = $filebase . ".pe";
+ open (FFSCRIPT, ">$ffscript") || die "${progname}: $! ($ffscript)\n";
+ print FFSCRIPT <<'AUTOHINT';
+Open($1);
+SelectAll();
+RemoveOverlap();
+AddExtrema();
+Simplify(0, 2);
+CorrectDirection();
+Simplify(0, 2);
+RoundToInt();
+AutoHint();
+Generate($1);
+Quit(0);
+AUTOHINT
+ ;
+ close FFSCRIPT;
+}
+if (!execute_command 0, ("fontforge", "-script", $ffscript, $pfbfile)) {
+ warn "${progname}: You'll need to install FontForge if you want $pfbfile autohinted (not required, but strongly recommended)\n";
+}
+unlink $ffscript if !$user_script;
+print "\n";
+
+# Finish up.
+print "*** Successfully generated $pfbfile! ***\n";
+exit 0;
+
+######################################################################
+
+__END__
+
+=head1 NAME
+
+mf2pt1 - produce a PostScript Type 1 font program from a Metafont source
+
+
+=head1 SYNOPSIS
+
+mf2pt1
+[B<--help>]
+[B<--version>]
+[B<--comment>=I<string>]
+[B<--designsize>=I<number>]
+[B<--encoding>=I<encoding>]
+[B<--family>=I<name>]
+[B<-->[B<no>]B<fixedpitch>]
+[B<--fontversion>=I<MMM.mmm>]
+[B<--fullname>=I<name>]
+[B<--italicangle>=I<number>]
+[B<--name>=I<name>]
+[B<--underpos>=I<number>]
+[B<--underthick>=I<number>]
+[B<--uniqueid>=I<number>]
+[B<--weight>=I<weight>]
+[B<--rounding>=I<number>]
+[B<--bpppix>=I<number>]
+[B<--ffscript>=I<file.pe>]
+I<infile>.mf
+
+
+=head1 WARNING
+
+The B<mf2pt1> Info file is the main source of documentation for
+B<mf2pt1>. This man page is merely a brief summary.
+
+
+=head1 DESCRIPTION
+
+B<mf2pt1> facilitates producing PostScript Type 1 fonts from a
+Metafont source file. It is I<not>, as the name may imply, an
+automatic converter of arbitrary Metafont fonts to Type 1 format.
+B<mf2pt1> imposes a number of restrictions on the Metafont input. If
+these restrictions are met, B<mf2pt1> will produce valid Type 1
+output. (Actually, it produces "disassembled" Type 1; the B<t1asm>
+program from the B<t1utils> suite will convert this to a true Type 1
+font.)
+
+=head2 Usage
+
+ mf2pt1 myfont.mf
+
+=head1 OPTIONS
+
+Font parameters are best specified within a Metafont program. If
+necessary, though, command-line options can override any of these
+parameters. The B<mf2pt1> Info page, the primary source of B<mf2pt1>
+documentation, describes the following in greater detail.
+
+=over 4
+
+=item B<--help>
+
+Provide help on B<mf2pt1>'s command-line options.
+
+=item B<--version>
+
+Output the B<mf2pt1> version number, copyright, and license.
+
+=item B<--comment>=I<string>
+
+Include a font comment, usually a copyright notice.
+
+=item B<--designsize>=I<number>
+
+Specify the font design size in points.
+
+=item B<--encoding>=I<encoding>
+
+Designate the font encoding, either the name of a---typically
+F<.enc>---file which contains a PostScript font-encoding vector or one
+of C<standard> (the default), C<ot1>, C<t1>, or C<isolatin1>.
+
+=item B<--family>=I<name>
+
+Specify the font family.
+
+=item B<--fixedpitch>, B<--nofixedpitch>
+
+Assert that the font uses either monospaced (B<--fixedpitch>) or
+proportional (B<--nofixedpitch>) character widths.
+
+=item B<--fontversion>=I<MMM.mmm>
+
+Specify the font's major and minor version number.
+
+=item B<--fullname>=I<name>
+
+Designate the full font name (family plus modifiers).
+
+=item B<--italicangle>=I<number>
+
+Designate the italic angle in degrees counterclockwise from vertical.
+
+=item B<--name>=I<name>
+
+Provide the font name.
+
+=item B<--underpos>=I<number>
+
+Specify the vertical position of the underline in thousandths of the
+font height.
+
+=item B<--underthick>=I<number>
+
+Specify the thickness of the underline in thousandths of the font
+height.
+
+=item B<--uniqueid>=I<number>
+
+Specify a globally unique font identifier.
+
+=item B<--weight>=I<weight>
+
+Provide a description of the font weight (e.g., ``Heavy'').
+
+=item B<--rounding>=I<number>
+
+Specify the fraction of a font unit (0.0 < I<number> <= 1.0) to which
+to round coordinate values [default: 1.0].
+
+=item B<--bpppix>=I<number>
+
+Redefine the number of big points per pixel from 0.02 to I<number>.
+
+=item B<--ffscript>=I<file.pe>
+
+Name a script to pass to FontForge.
+
+=back
+
+
+=head1 FILES
+
+F<mf2pt1.mem> (which is generated from F<mf2pt1.mp> and F<mfplain.mp>)
+
+
+=head1 NOTES
+
+As stated in L</"WARNING">, the complete source of documentation for
+B<mf2pt1> is the Info page, not this man page.
+
+
+=head1 SEE ALSO
+
+mf(1), mpost(1), t1asm(1), fontforge(1)
+
+
+=head1 AUTHOR
+
+Scott Pakin, I<scott+mf@pakin.org>