3 ##################################################
4 # Convert stylized Metafont to PostScript Type 1 #
5 # By Scott Pakin <scott+mf@pakin.org> #
6 ##################################################
8 ########################################################################
10 # Copyright (C) 2007 Scott Pakin #
12 # This program may be distributed and/or modified under the conditions #
13 # of the LaTeX Project Public License, either version 1.3c of this #
14 # license or (at your option) any later version. #
16 # The latest version of this license is in: #
18 # http://www.latex-project.org/lppl.txt #
20 # and version 1.3c or later is part of all distributions of LaTeX #
21 # version 2006/05/20 or later. #
22 ########################################################################
24 our $VERSION = "2.4.2"; # mf2pt1 version number
25 require 5.6.1; # I haven't tested mf2pt1 with older Perl versions
35 # Define some common encoding vectors.
36 my @standardencoding =
37 ((map {"_a$_"} (0..31)),
38 qw (space exclam quotedbl numbersign dollar percent ampersand
39 quoteright parenleft parenright asterisk plus comma hyphen
40 period slash zero one two three four five six seven eight
41 nine colon semicolon less equal greater question at A B C D E
42 F G H I J K L M N O P Q R S T U V W X Y Z bracketleft
43 backslash bracketright asciicircum underscore quoteleft a b c
44 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
45 braceright asciitilde),
46 (map {"_a$_"} (127..160)),
47 qw (exclamdown cent sterling fraction yen florin section currency
48 quotesingle quotedblleft guillemotleft guilsinglleft
49 guilsinglright fi fl _a176 endash dagger daggerdbl
50 periodcentered _a181 paragraph bullet quotesinglbase
51 quotedblbase quotedblright guillemotright ellipsis
52 perthousand _a190 questiondown _a192 grave acute circumflex
53 tilde macron breve dotaccent dieresis _a201 ring cedilla
54 _a204 hungarumlaut ogonek caron emdash),
55 (map {"_a$_"} (209..224)),
56 qw (AE _a226 ordfeminine _a228 _a229 _a230 _a231 Lslash Oslash OE
57 ordmasculine _a236 _a237 _a238 _a239 _a240 ae _a242 _a243
58 _a244 dotlessi _a246 _a247 lslash oslash oe germandbls _a252
60 my @isolatin1encoding =
61 ((map {"_a$_"} (0..31)),
62 qw (space exclam quotedbl numbersign dollar percent ampersand
63 quoteright parenleft parenright asterisk plus comma minus
64 period slash zero one two three four five six seven eight
65 nine colon semicolon less equal greater question at A B C D E
66 F G H I J K L M N O P Q R S T U V W X Y Z bracketleft
67 backslash bracketright asciicircum underscore quoteleft a b c
68 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
69 braceright asciitilde),
70 (map {"_a$_"} (128..143)),
71 qw (dotlessi grave acute circumflex tilde macron breve dotaccent
72 dieresis _a153 ring cedilla _a156 hungarumlaut ogonek
73 caron space exclamdown cent sterling currency yen brokenbar
74 section dieresis copyright ordfeminine guillemotleft
75 logicalnot hyphen registered macron degree plusminus
76 twosuperior threesuperior acute mu paragraph periodcentered
77 cedilla onesuperior ordmasculine guillemotright onequarter
78 onehalf threequarters questiondown Agrave Aacute Acircumflex
79 Atilde Adieresis Aring AE Ccedilla Egrave Eacute Ecircumflex
80 Edieresis Igrave Iacute Icircumflex Idieresis Eth Ntilde
81 Ograve Oacute Ocircumflex Otilde Odieresis multiply Oslash
82 Ugrave Uacute Ucircumflex Udieresis Yacute Thorn germandbls
83 agrave aacute acircumflex atilde adieresis aring ae ccedilla
84 egrave eacute ecircumflex edieresis igrave iacute icircumflex
85 idieresis eth ntilde ograve oacute ocircumflex otilde
86 odieresis divide oslash ugrave uacute ucircumflex udieresis
87 yacute thorn ydieresis));
89 qw (Gamma Delta Theta Lambda Xi Pi Sigma Upsilon Phi
90 Psi Omega ff fi fl ffi ffl dotlessi dotlessj grave acute caron
91 breve macron ring cedilla germandbls ae oe oslash AE OE Oslash
92 suppress exclam quotedblright numbersign dollar percent
93 ampersand quoteright parenleft parenright asterisk plus comma
94 hyphen period slash zero one two three four five six seven
95 eight nine colon semicolon exclamdown equal questiondown
96 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
97 Z bracketleft quotedblleft bracketright circumflex dotaccent
98 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
99 endash emdash hungarumlaut tilde dieresis);
101 qw (grave acute circumflex tilde dieresis hungarumlaut ring caron
102 breve macron dotaccent cedilla ogonek quotesinglbase
103 guilsinglleft guilsinglright quotedblleft quotedblright
104 quotedblbase guillemotleft guillemotright endash emdash cwm
105 perthousand dotlessi dotlessj ff fi fl ffi ffl space exclam
106 quotedbl numbersign dollar percent ampersand quoteright
107 parenleft parenright asterisk plus comma hyphen period slash
108 zero one two three four five six seven eight nine colon
109 semicolon less equal greater question at A B C D E F G H I J K L
110 M N O P Q R S T U V W X Y Z bracketleft backslash bracketright
111 asciicircum underscore quoteleft a b c d e f g h i j k l m n o p
112 q r s t u v w x y z braceleft bar braceright asciitilde
113 sfthyphen Abreve Aogonek Cacute Ccaron Dcaron Ecaron Eogonek
114 Gbreve Lacute Lcaron Lslash Nacute Ncaron Eng Ohungarumlaut
115 Racute Rcaron Sacute Scaron Scedilla Tcaron Tcedilla
116 Uhungarumlaut Uring Ydieresis Zacute Zcaron Zdotaccent IJ
117 Idotaccent dcroat section abreve aogonek cacute ccaron dcaron
118 ecaron eogonek gbreve lacute lcaron lslash nacute ncaron eng
119 ohungarumlaut racute rcaron sacute scaron scedilla tcaron
120 tcedilla uhungarumlaut uring ydieresis zacute zcaron zdotaccent
121 ij exclamdown questiondown sterling Agrave Aacute Acircumflex
122 Atilde Adieresis Aring AE Ccedilla Egrave Eacute Ecircumflex
123 Edieresis Igrave Iacute Icircumflex Idieresis Eth Ntilde Ograve
124 Oacute Ocircumflex Otilde Odieresis OE Oslash Ugrave Uacute
125 Ucircumflex Udieresis Yacute Thorn SS agrave aacute acircumflex
126 atilde adieresis aring ae ccedilla egrave eacute ecircumflex
127 edieresis igrave iacute icircumflex idieresis eth ntilde ograve
128 oacute ocircumflex otilde odieresis oe oslash ugrave uacute
129 ucircumflex udieresis yacute thorn germandbls);
131 # Define font parameters that the user can override.
145 my ($mffile, $pt1file, $pfbfile, $ffscript);
150 # Define all of our other global variables.
151 my $progname = basename $0, ".pl";
161 my $versionmsg = "mf2pt1 version $VERSION
163 Copyright (C) 2007 Scott Pakin
165 This program may be distributed and/or modified under the conditions
166 of the LaTeX Project Public License, either version 1.3c of this
167 license or (at your option) any later version.
169 The latest version of this license is in:
171 http://www.latex-project.org/lppl.txt
173 and version 1.3c or later is part of all distributions of LaTeX
174 version 2006/05/20 or later.
178 ######################################################################
180 # The routines to compute the fractional approximation of a real number
181 # are heavily based on code posted by Ben Tilly
182 # <http://www.perlmonks.org/?node_id=26179> on Nov 16th, 2000, to the
183 # PerlMonks list. See <http://www.perlmonks.org/index.pl?node_id=41961>.
186 # Takes numerator/denominator pairs.
187 # Returns a PS fraction string representation (with a trailing space).
196 $res .= $d . " div " if $d > 1;
204 # Returns a numerator and denominator with the smallest denominator
205 # so that the difference of the resulting fraction to the number is
206 # smaller or equal to $rounding.
210 my $f = ret_frac_iter ($num);
213 my ($n, $m) = $f->();
214 my $approx = $n / $m;
215 my $delta = abs ($num - $approx);
216 return ($n, $m) if ($delta <= $rounding);
221 # Takes a number, returns the best integer approximation and (in list
222 # context) the error.
226 my $approx = sprintf '%.0f', $x;
228 return ($approx, $x - $approx);
236 # Takes a numerator and denominator, in scalar context returns
237 # the best fraction describing them, in list the numerator and
239 sub frac_standard ($$)
241 my $n = best_int(shift);
242 my $m = best_int(shift);
259 # Euclidean algorithm for calculating a GCD.
260 # Takes two integers, returns the greatest common divisor.
272 # Takes a list of terms in a continued fraction, and converts it
276 my ($n, $m) = (0, 1); # Start with 0
280 # Want frac for $k + 1/($n/$m)
281 ($n, $m) = frac_standard($k*$n + $m, $n);
285 ($n, $m) = frac_standard($k, 1);
288 return frac_standard($n, $m);
292 # Takes a number, returns an anon sub which iterates through a set of
293 # fractional approximations that converges very quickly to the number.
294 sub ret_frac_iter ($)
297 my $term_iter = ret_next_term_iter($x);
300 push @ints, $term_iter->();
301 return ints_to_frac(@ints);
306 # Terms of a continued fraction converging on that number.
307 sub ret_next_term_iter ($)
311 (my $n, $x) = best_int($x);
319 ######################################################################
321 # Round a number to the nearest integer.
324 return int($_[0] + 0.5*($_[0] <=> 0));
328 # Round a number to a given precision.
331 return round ($_[0] / $rounding) * $rounding;
335 # Set a variable's value to the first defined value in the given list.
336 # If the variable was not previously defined and no value in the list
337 # is defined, do nothing.
338 sub assign_default (\$@)
340 my $varptr = shift; # Pointer to variable to define
341 return if defined $$varptr && $$varptr ne "UNSPECIFIED";
342 foreach my $val (@_) {
343 next if !defined $val;
350 # Print and execute a shell command. An environment variable with the
351 # same name as the command overrides the command name. Return 1 on
352 # success, 0 on failure. Optionally abort if the command fails, based
353 # on the first argument to execute_command.
354 sub execute_command ($@)
356 my $abort_on_failure = shift;
358 $command[0] = $ENV{uc $command[0]} || $command[0];
359 my $prettyargs = join (" ", map {/[\\ ]/ ? "'$_'" : $_} @command);
360 print "Invoking \"$prettyargs\"...\n";
361 my $result = system @command;
362 die "${progname}: \"$prettyargs\" failed ($!)\n" if $result && $abort_on_failure;
367 # Output the font header.
370 # Show the initial boilerplate.
371 print OUTFILE <<"ENDHEADER";
372 %!FontType1-1.0: $fontname $fontversion
373 %%CreationDate: $creationdate
374 % Font converted to Type 1 by mf2pt1, written by Scott Pakin.
376 /FontInfo 11 dict dup begin
377 /version ($fontversion) readonly def
378 /Notice ($comment) readonly def
379 /FullName ($fullname) readonly def
380 /FamilyName ($familyname) readonly def
381 /Weight ($weight) readonly def
382 /ItalicAngle $italicangle def
383 /isFixedPitch $fixedpitch def
384 /UnderlinePosition $underlinepos def
385 /UnderlineThickness $underlinethick def
387 /FontName /$fontname def
390 # If we're not using an encoding that PostScript knows about, then
391 # create an encoding vector.
392 if ($encoding==\@standardencoding) {
393 print OUTFILE "/Encoding StandardEncoding def\n";
396 print OUTFILE "/Encoding 256 array\n";
397 print OUTFILE "0 1 255 {1 index exch /.notdef put} for\n";
398 foreach my $charnum (0 .. $#{$encoding}) {
399 if ($encoding->[$charnum] && $encoding->[$charnum]!~/^_a\d+$/) {
400 print OUTFILE "dup $charnum /$encoding->[$charnum] put\n";
403 print OUTFILE "readonly def\n";
406 # Show the final boilerplate.
407 print OUTFILE <<"ENDHEADER";
410 /FontMatrix [0.001 0 0 0.001 0 0] readonly def
411 /UniqueID $uniqueID def
412 /FontBBox{@fontbbox}readonly def
415 dup /Private 5 dict dup begin
416 /RD{string currentfile exch readstring pop}executeonly def
417 /ND{noaccess def}executeonly def
418 /NP{noaccess put}executeonly def
423 # Use MetaPost to generate one PostScript file per character. We
424 # calculate the font bounding box from these characters and store them
425 # in @fontbbox. If the input parameter is 1, set other font
429 execute_command 1, ("mpost", "-mem=mf2pt1", "-progname=mpost",
430 "\\mode:=localfont; mag:=$mag; bpppix $bpppix; input $mffile");
431 opendir (CURDIR, ".") || die "${progname}: $! ($filedir)\n";
432 @charfiles = grep /^$filebase.*\.\d+$/, readdir(CURDIR);
434 @fontbbox = (1000000, 1000000, -1000000, -1000000);
435 foreach my $psfile (@charfiles) {
436 # Read the character number from the output file's extension.
437 $psfile =~ /\.(\d+)$/;
440 # Process in turn each line of the current PostScript file.
442 open (PSFILE, "<$psfile") || die "${progname}: $! ($psfile)\n";
444 my @tokens = split " ";
445 if ($tokens[0] eq "%%BoundingBox:") {
446 # Store the MetaPost-produced bounding box, just in case
447 # the given font doesn't use beginchar.
448 @tokens = ("%", "MF2PT1:", "glyph_dimensions", @tokens[1..4]);
451 next if $#tokens<1 || $tokens[1] ne "MF2PT1:";
453 # Process a "special" inserted into the generated PostScript.
456 # glyph_dimensions llx lly urx ury -- specified glyph dimensions
457 $tokens[2] eq "glyph_dimensions" && do {
458 my @bbox = @tokens[3..6];
459 $fontbbox[0]=$bbox[0] if $bbox[0]<$fontbbox[0];
460 $fontbbox[1]=$bbox[1] if $bbox[1]<$fontbbox[1];
461 $fontbbox[2]=$bbox[2] if $bbox[2]>$fontbbox[2];
462 $fontbbox[3]=$bbox[3] if $bbox[3]>$fontbbox[3];
463 $charbbox[$charnum] = \@bbox;
468 # If all we want is the bounding box, exit the loop now.
469 last MF2PT1_CMD if !$_[0];
471 # glyph_name name -- glyph name
472 $tokens[2] eq "glyph_name" && do {
473 $glyphname[$charnum] = $tokens[3];
477 # charwd wd -- character width as in TFM
478 $tokens[2] eq "charwd" && do {
479 $charwd[$charnum] = $tokens[3];
483 # font_identifier name -- full font name
484 $tokens[2] eq "font_identifier" && do {
485 $fullname = $tokens[3];
489 # font_size number -- font design size (pt, not bp)
490 $tokens[2] eq "font_size" && $tokens[3] && do {
491 $designsize = $tokens[3] * 72 / 72.27;
495 # font_slant number -- italic amount
496 $tokens[2] eq "font_slant" && do {
497 $italicangle = 0 + rad2deg (atan(-$tokens[3]));
501 # font_coding_scheme string -- font encoding
502 $tokens[2] eq "font_coding_scheme" && do {
503 $encoding = $tokens[3];
507 # font_version string -- font version number (xxx.yyy)
508 $tokens[2] eq "font_version" && do {
509 $fontversion = $tokens[3];
513 # font_comment string -- font comment notice
514 $tokens[2] eq "font_comment" && do {
515 $comment = join (" ", @tokens[3..$#tokens]);
519 # font_family string -- font family name
520 $tokens[2] eq "font_family" && do {
521 $familyname = $tokens[3];
525 # font_weight string -- font weight (e.g., "Book" or "Heavy")
526 $tokens[2] eq "font_weight" && do {
527 $weight = $tokens[3];
531 # font_fixed_pitch number -- fixed width font (0=false, 1=true)
532 $tokens[2] eq "font_fixed_pitch" && do {
533 $fixedpitch = $tokens[3];
537 # font_underline_position number -- vertical underline position
538 $tokens[2] eq "font_underline_position" && do {
539 # We store $underlinepos in points and later
540 # scale it by 1000/$designsize.
541 $underlinepos = $tokens[3];
545 # font_underline_thickness number -- thickness of underline
546 $tokens[2] eq "font_underline_thickness" && do {
547 # We store $underlinethick in points and later
548 # scale it by 1000/$designsize.
549 $underlinethick = $tokens[3];
553 # font_name string -- font name
554 $tokens[2] eq "font_name" && do {
555 $fontname = $tokens[3];
559 # font_unique_id number (as string) -- globally unique font ID
560 $tokens[2] eq "font_unique_id" && do {
561 $uniqueID = 0+$tokens[3];
568 warn "${progname}: No beginchar in character $charnum; glyph dimensions are probably incorrect\n";
574 # Convert ordinary, MetaPost-produced PostScript files into Type 1
576 sub output_font_programs ()
578 # Iterate over all the characters. We convert each one, line by
579 # line and token by token.
580 print "Converting PostScript graphics to Type 1 font programs...\n";
581 foreach my $psfile (@charfiles) {
582 # Initialize the font program.
583 $psfile =~ /\.(\d+)$/;
585 my $gname = $glyphname[$charnum] || $encoding->[$charnum];
587 push @fontprog, ("/$gname {",
588 frac_string (frac_approx ($charbbox[$charnum]->[0]),
589 frac_approx ($charwd[$charnum] * $mag))
592 ($charbbox[$charnum]->[0], 0); # Current point (PostScript)
594 # Iterate over every line in the current file.
595 open (PSFILE, "<$psfile") || die "${progname}: $! ($psfile)\n";
596 while (my $oneline=<PSFILE>) {
597 next if $oneline=~/^\%/;
598 next if $oneline=~/set/; # Fortunately, "set" never occurs on "good" lines.
599 my @arglist; # Arguments to current PostScript function
601 # Iterate over every token in the current line.
603 foreach my $token (split " ", $oneline) {
604 # Number: Round and push on the argument list.
605 $token =~ /^[-.\d]+$/ && do {
606 push @arglist, prec ($&);
610 # curveto: Convert to vhcurveto, hvcurveto, or rrcurveto.
611 $token eq "curveto" && do {
612 my ($dx1, $dy1) = ($arglist[0] - $cpx,
614 my ($dx1n, $dx1d) = frac_approx ($dx1);
615 my ($dy1n, $dy1d) = frac_approx ($dy1);
616 $cpx += $dx1n / $dx1d;
617 $cpy += $dy1n / $dy1d;
619 my ($dx2, $dy2) = ($arglist[2] - $cpx,
621 my ($dx2n, $dx2d) = frac_approx ($dx2);
622 my ($dy2n, $dy2d) = frac_approx ($dy2);
623 $cpx += $dx2n / $dx2d;
624 $cpy += $dy2n / $dy2d;
626 my ($dx3, $dy3) = ($arglist[4] - $cpx,
628 my ($dx3n, $dx3d) = frac_approx ($dx3);
629 my ($dy3n, $dy3d) = frac_approx ($dy3);
630 $cpx += $dx3n / $dx3d;
631 $cpy += $dy3n / $dy3d;
633 if (!$dx1n && !$dy3n) {
634 push @fontprog, frac_string ($dy1n, $dy1d,
640 elsif (!$dy1n && !$dx3n) {
641 push @fontprog, frac_string ($dx1n, $dx1d,
648 push @fontprog, frac_string ($dx1n, $dx1d,
659 # lineto: Convert to vlineto, hlineto, or rlineto.
660 $token eq "lineto" && do {
661 my ($dx, $dy) = ($arglist[0] - $cpx,
663 my ($dxn, $dxd) = frac_approx ($dx);
664 my ($dyn, $dyd) = frac_approx ($dy);
669 push @fontprog, frac_string ($dyn, $dyd)
673 push @fontprog, frac_string ($dxn, $dxd)
677 push @fontprog, frac_string ($dxn, $dxd, $dyn, $dyd)
683 # moveto: Convert to vmoveto, hmoveto, or rmoveto.
684 $token eq "moveto" && do {
685 my ($dx, $dy) = ($arglist[0] - $cpx,
687 my ($dxn, $dxd) = frac_approx ($dx);
688 my ($dyn, $dyd) = frac_approx ($dy);
693 push @fontprog, frac_string ($dyn, $dyd)
697 push @fontprog, frac_string ($dxn, $dxd)
701 push @fontprog, frac_string ($dxn, $dxd, $dyn, $dyd)
707 # closepath: Output as is.
708 $token eq "closepath" && do {
709 push @fontprog, $token;
715 push @fontprog, ("endchar",
717 print OUTFILE join ("\n\t", @fontprog), "\n";
722 # Output the final set of code for the Type 1 font.
723 sub output_trailer ()
725 print OUTFILE <<"ENDTRAILER";
727 0 @{[$fontbbox[2]-$fontbbox[0]]} hsbw
734 dup/FontName get exch definefont pop
735 mark currentfile closefile
740 ######################################################################
742 # Parse the command line. Asterisks in the following represents
743 # commands also defined by Plain Metafont.
745 GetOptions (\%opthash,
746 "fontversion=s", # font_version
747 "comment=s", # font_comment
748 "family=s", # font_family
749 "weight=s", # font_weight
750 "fullname=s", # font_identifier (*)
751 "fixedpitch!", # font_fixed_pitch
752 "italicangle=f", # font_slant (*)
753 "underpos=f", # font_underline_position
754 "underthick=f", # font_underline_thickness
755 "name=s", # font_name
756 "uniqueid=i", # font_unique_id
757 "designsize=f", # font_size (*)
758 "encoding=s", # font_coding_scheme (*)
763 "V|version") || pod2usage(2);
764 if (defined $opthash{"h"}) {
765 pod2usage(-verbose => 1,
766 -output => \*STDOUT, # Bug workaround for Pod::Usage
767 -exitval => "NOEXIT");
768 print "Please e-mail bug reports to scott+mf\@pakin.org.\n";
771 do {print $versionmsg; exit 1} if defined $opthash{"V"};
772 pod2usage(2) if $#ARGV != 0;
774 # Extract the filename from the command line.
776 my @fileparts = fileparse $mffile, ".mf";
777 $filebase = $fileparts[0];
778 $filedir = $fileparts[1];
779 $filenoext = File::Spec->catfile ($filedir, $filebase);
780 $pt1file = $filebase . ".pt1";
781 $pfbfile = $filebase . ".pfb";
783 assign_default $bpppix, $opthash{bpppix}, 0.02;
785 # Make our first pass through the input, to set values for various options.
786 $mag = 100; # Get a more precise bounding box.
787 get_bboxes(1); # This might set $designsize.
789 # Sanity-check the specified precision.
790 assign_default $rounding, $opthash{rounding}, 1;
791 if ($rounding<=0.0 || $rounding>1.0) {
792 die sprintf "%s: Invalid rounding amount \"%g\"; value must be a positive number no greater than 1.0\n", $progname, $rounding;
795 # Ensure that every user-definable parameter is assigned a value.
796 assign_default $fontversion, $opthash{fontversion}, "001.000";
797 assign_default $creationdate, scalar localtime;
798 assign_default $comment, $opthash{comment}, "Font converted to Type 1 by mf2pt1, written by Scott Pakin.";
799 assign_default $weight, $opthash{weight}, "Medium";
800 assign_default $fixedpitch, $opthash{fixedpitch}, 0;
801 assign_default $uniqueID, $opthash{uniqueid}, int(rand(1000000)) + 4000000;
802 assign_default $designsize, $opthash{designsize};
803 die "${progname}: a design size must be specified in $mffile or on the command line\n" if !defined $designsize;
804 die "${progname}: the design size must be a positive number\n" if $designsize<=0.0;
805 assign_default $underlinepos, $opthash{underpos}, -1;
806 $underlinepos = round(1000*$underlinepos/$designsize);
807 assign_default $underlinethick, $opthash{underthick}, 0.5;
808 $underlinethick = round(1000*$underlinethick/$designsize);
809 assign_default $fullname, $opthash{fullname}, $filebase;
810 assign_default $familyname, $opthash{family}, $fullname;
811 assign_default $italicangle, $opthash{italicangle}, 0;
812 assign_default $fontname, $opthash{name}, "$familyname-$weight";
813 $fontname =~ s/\s//g;
814 assign_default $encoding, $opthash{encoding}, "standard";
815 my $encoding_name = $encoding;
819 # Filenames take precedence over built-in encodings.
821 open (ENCFILE, "<$encoding") || die "${progname}: $! ($encoding)\n";
822 while (my $oneline = <ENCFILE>) {
823 $oneline =~ s/\%.*$//;
824 foreach my $word (split " ", $oneline) {
825 push @enc_array, substr($word, 1) if substr($word, 0, 1) eq "/";
829 $encoding_name = substr (shift @enc_array, 1);
830 $encoding = \@enc_array;
833 $encoding=\@standardencoding, last ENCODING if $encoding eq "standard";
834 $encoding=\@isolatin1encoding, last ENCODING if $encoding eq "isolatin1";
835 $encoding=\@ot1encoding, last ENCODING if $encoding eq "ot1";
836 $encoding=\@t1encoding, last ENCODING if $encoding eq "t1";
837 $encoding=\@glyphname, last ENCODING if $encoding eq "asis";
838 warn "${progname}: Unknown encoding \"$encoding\"; using standard Adobe encoding\n";
839 $encoding=\@standardencoding; # Default to standard encoding
841 assign_default $fixedpitch, $opthash{fixedpitch}, 0;
842 $fixedpitch = $fixedpitch ? "true" : "false";
843 assign_default $ffscript, $opthash{ffscript};
845 # Output the final values of all of our parameters.
847 print <<"PARAMVALUES";
848 mf2pt1 is using the following font parameters:
849 font_version: $fontversion
850 font_comment: $comment
851 font_family: $familyname
853 font_identifier: $fullname
854 font_fixed_pitch: $fixedpitch
855 font_slant: $italicangle
856 font_underline_position: $underlinepos
857 font_underline_thickness: $underlinethick
859 font_unique_id: $uniqueID
860 font_size: $designsize (bp)
861 font_coding_scheme: $encoding_name
866 # Scale by a factor of 1000/design size.
867 $mag = 1000.0 / $designsize;
871 # Output the font in disassembled format.
872 open (OUTFILE, ">$pt1file") || die "${progname}: $! ($pt1file)\n";
874 printf OUTFILE "2 index /CharStrings %d dict dup begin\n",
875 1+scalar(grep {defined($_)} @charbbox);
876 output_font_programs();
882 # Convert from the disassembled font format to Type 1 binary format.
883 if (!execute_command 0, ("t1asm", $pt1file, $pfbfile)) {
884 die "${progname}: You'll need either to install t1utils and rerun $progname or find another way to convert $pt1file to $pfbfile\n";
890 # Use FontForge to autohint the result.
891 my $user_script = 0; # 1=script file was provided by the user; 0=created here
892 if (defined $ffscript) {
893 # The user provided his own script.
897 # Create a FontForge script file.
898 $ffscript = $filebase . ".pe";
899 open (FFSCRIPT, ">$ffscript") || die "${progname}: $! ($ffscript)\n";
900 print FFSCRIPT <<'AUTOHINT';
916 if (!execute_command 0, ("fontforge", "-script", $ffscript, $pfbfile)) {
917 warn "${progname}: You'll need to install FontForge if you want $pfbfile autohinted (not required, but strongly recommended)\n";
919 unlink $ffscript if !$user_script;
923 print "*** Successfully generated $pfbfile! ***\n";
926 ######################################################################
932 mf2pt1 - produce a PostScript Type 1 font program from a Metafont source
940 [B<--comment>=I<string>]
941 [B<--designsize>=I<number>]
942 [B<--encoding>=I<encoding>]
943 [B<--family>=I<name>]
944 [B<-->[B<no>]B<fixedpitch>]
945 [B<--fontversion>=I<MMM.mmm>]
946 [B<--fullname>=I<name>]
947 [B<--italicangle>=I<number>]
949 [B<--underpos>=I<number>]
950 [B<--underthick>=I<number>]
951 [B<--uniqueid>=I<number>]
952 [B<--weight>=I<weight>]
953 [B<--rounding>=I<number>]
954 [B<--bpppix>=I<number>]
955 [B<--ffscript>=I<file.pe>]
961 The B<mf2pt1> Info file is the main source of documentation for
962 B<mf2pt1>. This man page is merely a brief summary.
967 B<mf2pt1> facilitates producing PostScript Type 1 fonts from a
968 Metafont source file. It is I<not>, as the name may imply, an
969 automatic converter of arbitrary Metafont fonts to Type 1 format.
970 B<mf2pt1> imposes a number of restrictions on the Metafont input. If
971 these restrictions are met, B<mf2pt1> will produce valid Type 1
972 output. (Actually, it produces "disassembled" Type 1; the B<t1asm>
973 program from the B<t1utils> suite will convert this to a true Type 1
982 Font parameters are best specified within a Metafont program. If
983 necessary, though, command-line options can override any of these
984 parameters. The B<mf2pt1> Info page, the primary source of B<mf2pt1>
985 documentation, describes the following in greater detail.
991 Provide help on B<mf2pt1>'s command-line options.
995 Output the B<mf2pt1> version number, copyright, and license.
997 =item B<--comment>=I<string>
999 Include a font comment, usually a copyright notice.
1001 =item B<--designsize>=I<number>
1003 Specify the font design size in points.
1005 =item B<--encoding>=I<encoding>
1007 Designate the font encoding, either the name of a---typically
1008 F<.enc>---file which contains a PostScript font-encoding vector or one
1009 of C<standard> (the default), C<ot1>, C<t1>, or C<isolatin1>.
1011 =item B<--family>=I<name>
1013 Specify the font family.
1015 =item B<--fixedpitch>, B<--nofixedpitch>
1017 Assert that the font uses either monospaced (B<--fixedpitch>) or
1018 proportional (B<--nofixedpitch>) character widths.
1020 =item B<--fontversion>=I<MMM.mmm>
1022 Specify the font's major and minor version number.
1024 =item B<--fullname>=I<name>
1026 Designate the full font name (family plus modifiers).
1028 =item B<--italicangle>=I<number>
1030 Designate the italic angle in degrees counterclockwise from vertical.
1032 =item B<--name>=I<name>
1034 Provide the font name.
1036 =item B<--underpos>=I<number>
1038 Specify the vertical position of the underline in thousandths of the
1041 =item B<--underthick>=I<number>
1043 Specify the thickness of the underline in thousandths of the font
1046 =item B<--uniqueid>=I<number>
1048 Specify a globally unique font identifier.
1050 =item B<--weight>=I<weight>
1052 Provide a description of the font weight (e.g., ``Heavy'').
1054 =item B<--rounding>=I<number>
1056 Specify the fraction of a font unit (0.0 < I<number> <= 1.0) to which
1057 to round coordinate values [default: 1.0].
1059 =item B<--bpppix>=I<number>
1061 Redefine the number of big points per pixel from 0.02 to I<number>.
1063 =item B<--ffscript>=I<file.pe>
1065 Name a script to pass to FontForge.
1072 F<mf2pt1.mem> (which is generated from F<mf2pt1.mp> and F<mfplain.mp>)
1077 As stated in L</"WARNING">, the complete source of documentation for
1078 B<mf2pt1> is the Info page, not this man page.
1083 mf(1), mpost(1), t1asm(1), fontforge(1)
1088 Scott Pakin, I<scott+mf@pakin.org>