summaryrefslogtreecommitdiff
path: root/fonts/utilities/fontools/bin/autoinst
diff options
context:
space:
mode:
Diffstat (limited to 'fonts/utilities/fontools/bin/autoinst')
-rwxr-xr-xfonts/utilities/fontools/bin/autoinst3220
1 files changed, 3220 insertions, 0 deletions
diff --git a/fonts/utilities/fontools/bin/autoinst b/fonts/utilities/fontools/bin/autoinst
new file mode 100755
index 0000000000..fb295700f9
--- /dev/null
+++ b/fonts/utilities/fontools/bin/autoinst
@@ -0,0 +1,3220 @@
+#! /usr/bin/env perl
+
+=begin COPYRIGHT
+
+----------------------------------------------------------------------------
+
+ Copyright (C) 2005-2019 Marc Penninga.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to
+ Free Software Foundation, Inc.,
+ 59 Temple Place,
+ Suite 330,
+ Boston, MA 02111-1307,
+ USA
+
+----------------------------------------------------------------------------
+
+=end COPYRIGHT
+
+=cut
+
+use strict;
+use warnings;
+
+use Cwd ();
+use File::Path ();
+use File::Spec ();
+use Getopt::Long ();
+use Pod::Usage ();
+use POSIX ();
+
+my $VERSION = '20190712';
+
+my ($d, $m, $y) = (localtime time)[3 .. 5];
+my $TODAY = sprintf "%04d/%02d/%02d", $y + 1900, $m + 1, $d;
+
+=begin Comment
+
+ Some fontnames contain abbreviated words for width, weight and/or shape;
+ we unabbreviate these using the following table.
+
+ To avoid having to have this information in multiple places,
+ we also build a reversed table %ABBREV that maps 'full' forms
+ to all known abbreviations; we then inject these abbreviations into
+ the %NFSS_WEIGHT, %NFSS_WIDTH and %NFSS_SHAPE tables below,
+ so that they recognise the abbreviated forms as well as the full ones.
+
+=end Comment
+
+=cut
+
+my %UNABBREVIATE = (
+ cmp => 'compressed',
+ comp => 'compressed',
+ cond => 'condensed',
+ demi => 'demibold',
+ extcond => 'extracondensed',
+ hair => 'hairline',
+ incline => 'inclined',
+ it => 'italic',
+ ita => 'italic',
+ md => 'medium',
+ slant => 'slanted',
+ ultra => 'ultrablack',
+);
+my %ABBREV;
+while ( my ($k, $v) = each %UNABBREVIATE ) {
+ push @{$ABBREV{$v}}, $k;
+}
+for my $full (keys %ABBREV) {
+ push @{$ABBREV{$full}}, $full;
+}
+
+=begin Comment
+
+ LaTeX's NFSS contains a number of standard codes for weight and width:
+ - weight: ul, el, l, sl, m, sb, b, eb, ub
+ - width: uc, ec, c, sc, m, sx, x, ex, ux
+
+ These codes are not always a perfect match with the weights and widths
+ present in a font family; some families (especially sanserif ones)
+ contain more or different weights and widths, and the naming of those
+ weights and widths isn't always consistent between font families.
+ To handle this situation, we use a two-tiered approach:
+ 1. We install all fonts using a "series" name that is the concatenation
+ of whatever the font designer has chosen to call the weight and width
+ (but in all *lower*case).
+ 2. We add "ssub" rules to the .fd files that map the standard NFSS codes
+ to actual fonts.
+
+ In step 1, we follow NFSS in leaving out any occurrence of
+ the word "regular" unless *both* weight and width are Regular;
+ in that case, the 'series' attribute becomes "regular".
+
+ The two tables NFSS_WEIGHT and NFSS_WIDTH are used to control step 2.
+ It contains several entries of the form
+
+ sc => [ qw( semicondensed narrow ) ],
+
+ This should be read as follows: the NFSS code "sc" is mapped to
+ the *first* width on the right hand side present in the current family.
+
+ Please note that the tables contain empty keys instead of "m" for the
+ regular weight and width. NFSS actually combines weight and width into
+ a single "series" attribute; a weight or width of "m" is left out of
+ this combination (unless *both* weight and width are equal to "m"; then
+ the series becomes "m", but that's a special case we deal with later on).
+
+ In addition to the mapping of NFSS codes, the two mentioned tables are
+ also used in parsing the font's metadata to determine its weight and
+ width: any string that occurs on the right hand side is considered a
+ possible name to be searched for.
+
+ These tables can be extended to teach autoinst about new weights or
+ widths. Suppose your font family contains a "Hemibold" weight, that
+ you want mapped to the "sb" code. Then add the name "hemibold" to
+ the right hand side of the "sb" entry in the NFSS_WEIGHT table:
+
+ sb => [ qw( semibold demi demibold medium hemibold ) ],
+
+ In this case, since it's in last position, it's only mapped to "sb"
+ if none of the other fonts are present. Put it earlier in the list
+ to give it higher priority.
+
+ Note that autoinst converts all metadata to lowercase to avoid
+ inconsistent capitalization; so all entries in these tables should
+ be *lowercase* as well.
+
+ Technical notes:
+ - We define NFSS_WEIGHT and NFSS_WIDTH first as arrays
+ and then as hashtables; this allows us to use the array-variants
+ as an *ordered* (by weight/width) list of values.
+ - We exclude the Medium and Regular weights and widths
+ from the @WEIGHTS and @WIDTHS arrays to avoid false positives;
+ see the comments in the parse_basicinfo() function.
+
+=end Comment
+
+=cut
+
+my @NFSS_WEIGHT = (
+ ul => [ qw( ultralight thin hairline eight four two ) ],
+ el => [ qw( extralight ) ],
+ l => [ qw( light ) ],
+ sl => [ qw( semilight blond ) ],
+ '' => [ qw( regular text book ) ],
+ sb => [ qw( semibold demibold medium ) ],
+ b => [ qw( bold ) ],
+ eb => [ qw( extrabold ) ],
+ ub => [ qw( ultrabold black heavy extrablack ultrablack
+ ultraheavy poster super ) ],
+);
+my %NFSS_WEIGHT = @NFSS_WEIGHT;
+@NFSS_WEIGHT = grep { !ref } @NFSS_WEIGHT;
+
+# Add abbreviated forms, using the %ABBREV table constructed earlier
+for my $code (@NFSS_WEIGHT) {
+ $NFSS_WEIGHT{$code}
+ = [ map { @{ $ABBREV{$_} // [$_] } } @{$NFSS_WEIGHT{$code}} ];
+}
+
+my @WEIGHTS = grep { $_ !~ m/ regular | medium /xms }
+ map { @{$_} } values %NFSS_WEIGHT;
+
+my @NFSS_WIDTH = (
+ uc => [ qw( ultracondensed extracompressed ultracompressed ) ],
+ ec => [ qw( extracondensed compressed compact ) ],
+ c => [ qw( condensed ) ],
+ sc => [ qw( semicondensed narrow ) ],
+ '' => [ qw( regular ) ],
+ sx => [ qw( semiextended semiexpanded ) ],
+ x => [ qw( extended expanded wide ) ],
+ ex => [ ],
+ ux => [ ],
+);
+my %NFSS_WIDTH = @NFSS_WIDTH;
+@NFSS_WIDTH = grep { !ref } @NFSS_WIDTH;
+
+# Add abbreviated forms, using the %ABBREV table constructed earlier
+for my $code (@NFSS_WIDTH) {
+ $NFSS_WIDTH{$code}
+ = [ map { @{ $ABBREV{$_} // [$_] } } @{$NFSS_WIDTH{$code}} ];
+}
+
+my @WIDTHS = grep { $_ ne 'regular' } map { @{$_} } values %NFSS_WIDTH;
+
+=begin Comment
+
+ The NFSS_SHAPE table maps various shape names to NFSS codes.
+
+ Note that this table works the other way around compared to
+ the two previous tables; it directly maps shape names to NFSS codes.
+ The reason the other tables are a bit contorted is that there,
+ we might have multiple weighs or widths mapping to the same code,
+ and we need a way to prioritise some weights/widths over others.
+
+ For shapes, that's not the case; font families don't have both Slanted
+ and Oblique shapes, since these are different names for the same thing.
+
+ Like in the other NFSS_* tables, entries may be added to teach autoinst
+ about new shapes.
+
+=end Comment
+
+=cut
+
+my @NFSS_SHAPE = (
+ roman => 'n',
+ upright => 'n',
+ italic => 'it',
+ cursive => 'it',
+ kursiv => 'it',
+ inclined => 'sl',
+ oblique => 'sl',
+ slanted => 'sl',
+ romani => 'n', # Silentium has two roman shapes, but no italic;
+ romanii => 'it', # so we cheat by mapping the second roman to 'it'
+);
+my %NFSS_SHAPE = @NFSS_SHAPE;
+for my $full (keys %ABBREV) {
+ if (defined $NFSS_SHAPE{$full}) {
+ for my $abbrev ( @{$ABBREV{$full}} ) {
+ $NFSS_SHAPE{$abbrev} = $NFSS_SHAPE{$full};
+ }
+ }
+}
+
+=begin Comment
+
+ The %SHAPE table is used in deciding which font shapes
+ (normal, small caps, swash or textcomp) to generate.
+
+ Each key in this table names a shape; the corresponding value
+ is an anonymous hash with several key/value pairs:
+ code An anonymous hash with three possible keys:
+ 'n' -> the NFSS code to use for this variant shape
+ if the 'basic shape' is upright;
+ 'it' -> the NFSS code to use for this variant shape
+ if the 'basic shape' is italic
+ 'sl' -> the NFSS code to use for this variant shape
+ if the 'basic shape' is slanted (aka oblique);
+ If any entry is missing, the corresponding version
+ of this variant shape will not be built.
+ reqd A list of required OpenType features;
+ this shape is built if the font supports at least *one*
+ of these features.
+ nice A list of optional OpenType features;
+ these are used if the font supports them, but don't
+ prevent this shape from being built when missing.
+ extra Extra options passed to otftotfm when creating this shape.
+ name A string added to the name of the generated font,
+ to make it unique.
+
+ Textcomp is treated as a 'shape' even though it is technically
+ an encoding; that is just the easiest way to do things.
+
+=end Comment
+
+=cut
+
+my %SHAPE = (
+ normal => {
+ code => { n => 'n', it => 'it', sl => 'sl' },
+ reqd => [ ],
+ nice => [ 'kern', 'liga' ],
+ extra => '',
+ name => '',
+ },
+ smallcaps => {
+ code => { n => 'sc', it => 'scit', sl => 'scsl' },
+ reqd => [ 'smcp' ],
+ nice => [ 'kern', 'liga' ],
+ extra => '--unicoding="germandbls =: SSsmall"',
+ name => 'sc',
+ },
+ # '--feature=aalt' is in 'extra' instead of 'nice' because 'extra'
+ # comes last in the command line; else 'aalt' might be overridden
+ swash => {
+ code => { n => 'nw', it => 'sw' },
+ reqd => [ 'swsh' ],
+ nice => [ 'kern', 'liga', 'dlig' ],
+ extra => '--include-alternates="*.swash" --feature=aalt',
+ name => 'swash',
+ },
+ textcomp => {
+ code => { n => 'n', it => 'it', sl => 'sl' },
+ reqd => [ ],
+ nice => [ 'onum' ],
+ extra => '',
+ name => '',
+ },
+);
+
+=begin Comment
+
+ The next table is used to generate extra ssub rules in the .fd files
+ to map missing Slanted shapes to Italic and vice versa.
+
+=end Comment
+
+=cut
+
+my %SSUB_SHAPE = (
+ sl => 'it',
+ scsl => 'scit',
+ it => 'sl',
+ scit => 'scsl',
+);
+
+=begin Comment
+
+ The %STYLE table is used in deciding which figure styles to generate.
+ Each figure style (lining, oldstyle, tabular, proportional, superior,
+ inferior etc.) becomes a separate font family. We also treat Ornaments
+ as a figure style here; that's just the easiest way to handle them.
+
+ Each key in this table names a figure style; the corresponding
+ value is an anonymous hash with four key/value pairs:
+ reqd A list of required OpenType features; this style is built
+ if the font supports *all* these features.
+ nice A list of optional OpenType features;
+ these are used if the font supports them, but don't
+ prevent this style from being built when missing.
+ extra Extra options passed to otftotfm when creating this style.
+ shape An anonymous array of 'variant' shapes to build with
+ this figure style.
+
+ The 'reqd' and 'nice' subtables for the TLF, LF, TOsF and OsF styles
+ are empty; these are filled in at run time, depending on
+ which figure style is default for the current font.
+
+ The 'reqd' subtable for the Inf style is also empty; this may be filled
+ with 'subs', 'sinf' or 'dnom' depending on the -inferiors options.
+
+=end Comment
+
+=cut
+
+my %STYLE = (
+ TLF => {
+ reqd => [ ],
+ nice => [ ],
+ extra => '',
+ shapes => [ 'normal', 'smallcaps', 'swash', 'textcomp' ],
+ },
+ LF => {
+ reqd => [ ],
+ nice => [ ],
+ extra => '',
+ shapes => [ 'normal', 'smallcaps', 'swash', 'textcomp' ],
+ },
+ TOsF => {
+ reqd => [ ],
+ nice => [ ],
+ extra => '',
+ shapes => [ 'normal', 'smallcaps', 'swash', 'textcomp' ],
+ },
+ OsF => {
+ reqd => [ ],
+ nice => [ ],
+ extra => '',
+ shapes => [ 'normal', 'smallcaps', 'swash', 'textcomp' ],
+ },
+ Sup => {
+ reqd => [ 'sups' ],
+ nice => [ ],
+ extra => '--ligkern="* {KL} *"',
+ shapes => [ 'normal' ],
+ },
+ Inf => {
+ reqd => [ ],
+ nice => [ ],
+ extra => '--ligkern="* {KL} *"',
+ shapes => [ 'normal' ],
+ },
+ Numr => {
+ reqd => [ 'numr'],
+ nice => [ ],
+ extra => '--ligkern="* {KL} *"',
+ shapes => [ 'normal' ],
+ },
+ Dnom => {
+ reqd => [ 'dnom' ],
+ nice => [ ],
+ extra => '--ligkern="* {KL} *"',
+ shapes => [ 'normal' ],
+ },
+ Titl => {
+ reqd => [ 'titl' ],
+ nice => [ 'kern', 'liga' ],
+ extra => '',
+ shapes => [ 'normal' ],
+ },
+ Orn => {
+ reqd => [ 'ornm' ],
+ nice => [ ],
+ extra => '--ligkern="* {KL} *"',
+ shapes => [ 'normal' ],
+ },
+);
+
+=begin Comment
+
+ The %STYLE_DEFAULTS table is used to populate the 'reqd' and 'nice'
+ subtables of the %STYLE table, depending on the default figure style
+ for the current font. This is needed because some font families
+ provide only features for the non-default figure styles.
+
+ We therefore make the OpenType features that correspond
+ to the default figure style 'nice' (optional), so that this figure style
+ is always generated, even without OpenType features in the font;
+ the other figure style are only generated when the corresponding
+ features are present in the font.
+
+=end Comment
+
+=cut
+
+my %STYLE_DEFAULTS = (
+ lnum =>
+ sub {
+ push @{$STYLE{TLF}{nice}}, 'lnum';
+ push @{$STYLE{LF}{nice}}, 'lnum';
+ push @{$STYLE{TOsF}{reqd}}, 'onum';
+ push @{$STYLE{OsF}{reqd}}, 'onum';
+ },
+ onum =>
+ sub {
+ push @{$STYLE{TLF}{reqd}}, 'lnum';
+ push @{$STYLE{LF}{reqd}}, 'lnum';
+ push @{$STYLE{TOsF}{nice}}, 'onum';
+ push @{$STYLE{OsF}{nice}}, 'onum';
+ },
+ tnum =>
+ sub {
+ push @{$STYLE{TLF}{nice}}, 'tnum';
+ push @{$STYLE{TOsF}{nice}}, 'tnum';
+ push @{$STYLE{LF}{reqd}}, 'pnum';
+ push @{$STYLE{OsF}{reqd}}, 'pnum';
+ },
+ pnum =>
+ sub {
+ push @{$STYLE{TLF}{reqd}}, 'tnum';
+ push @{$STYLE{TOsF}{reqd}}, 'tnum';
+ push @{$STYLE{LF}{nice}}, 'pnum';
+ push @{$STYLE{OsF}{nice}}, 'pnum';
+ },
+);
+
+
+############################################################################
+
+
+sub main {
+ ARGV::parse_options();
+
+ my @fonts = map { Fontinfo::parse_fontinfo($_) } @ARGV;
+
+ create_logfile(@fonts);
+ Fontinfo::assert_unique(@fonts);
+
+ # We can only handle the '-inferiors=auto' option now,
+ # since this requires knowing which inferior figures this font supports;
+ # so we have to do the font info parsing first.
+ if ($ARGV{inferiors} eq 'auto') {
+ Fontinfo::handle_auto_inferiors(@fonts);
+ }
+
+ # For each font, figure out the styles, shapes and encodings to generate
+ my @worklist
+ = cleanup(expand_encodings(expand_shapes(expand_styles(@fonts))));
+
+ # Organize the worklist by family, encoding, style, series and shape
+ my %fddata;
+ for my $item (@worklist) {
+ my $family = $item->{font}{family};
+ my $encoding = $item->{encoding}[1];
+ my $style = $item->{style};
+ my $series = $item->{font}{series};
+ my $shape = $item->{fdshape};
+ my $minsize = $item->{font}{minsize};
+ my $maxsize = $item->{font}{maxsize};
+
+ push @{$fddata{$family}{$encoding}{$style}{$series}{$shape}},
+ [ $minsize, $maxsize, $item->{fontname} ];
+ }
+
+ # Decide to which weights and widths we'll map the standard NFSS codes
+ decide_nfss_mappings(@fonts);
+
+ log_worklist(@worklist);
+
+ if (!$ARGV{dryrun}) {
+ # Create the LaTeX support files
+ while (my ($fam, $famdata) = each %fddata) {
+ LaTeX::write_stylefile($fam, $famdata);
+ while (my ($enc, $encdata) = each %$famdata) {
+ while (my ($sty, $stydata) = each %$encdata) {
+ LaTeX::write_fdfile($fam, $enc, $sty, $stydata);
+ }
+ }
+ }
+
+ # Create (and execute) commands for otftotfm
+ make_commands(@worklist);
+ }
+
+ return;
+}
+
+#-----------------------------------------------------------------------
+# Return a list with all unique entries from the input
+#-----------------------------------------------------------------------
+sub uniq {
+ my %seen;
+ return grep { !$seen{$_}++ } @_;
+}
+
+#-----------------------------------------------------------------------
+# Determine which figure styles to create for each font;
+# return a list of (font, style) tuples
+#-----------------------------------------------------------------------
+sub expand_styles {
+ return map { my $font = $_;
+ map { { font => $font, style => $_ } }
+ grep { has_all_reqd_features($font, $STYLE{$_}) }
+ keys %STYLE
+ }
+ @_;
+}
+
+#-----------------------------------------------------------------------
+# Check if $font has all features mentioned in the 'reqd' subtable of $target
+#-----------------------------------------------------------------------
+sub has_all_reqd_features {
+ my ($font, $target) = @_;
+
+ return 0 unless defined $target;
+ for my $prereq (@{$target->{reqd}}) {
+ return 0 if !$font->{feature}{$prereq};
+ }
+ return 1;
+}
+
+#-----------------------------------------------------------------------
+# Determine which shapes to create for each (font, style) combination;
+# return a list of (font, style, shape) tuples
+#-----------------------------------------------------------------------
+sub expand_shapes {
+ return map { my ($font, $style) = @{$_}{qw(font style)};
+ map { { font => $font, style => $style, shape => $_ } }
+ grep { has_some_reqd_shapes($font, $SHAPE{$_}) }
+ @{$STYLE{$style}{shapes}};
+ }
+ @_;
+}
+
+#-----------------------------------------------------------------------
+# Check if $font has at least one of the features mentioned
+# in the 'reqd' subtable of $target
+#-----------------------------------------------------------------------
+sub has_some_reqd_shapes {
+ my ($font, $target) = @_;
+
+ return 0 unless defined $target->{code}{$font->{basicshape}};
+ return 1 unless @{$target->{reqd}};
+ return grep { $font->{feature}{$_} } @{$target->{reqd}};
+}
+
+#-----------------------------------------------------------------------
+# Determine which encodings to use for each (font, style, shape) combi;
+# return a list of (font, style, shape, encoding) tuples
+#-----------------------------------------------------------------------
+sub expand_encodings {
+ return
+ map { my ($font, $style, $shape) = @{$_}{qw(font style shape)};
+ my @encodings
+ = $shape eq 'textcomp' ? (['fontools_ts1', 'ts1'])
+ : $style eq 'Orn' ? ([Fontinfo::get_orn($font), 'u'])
+ : map { [lc $_, lc $_] } @{$ARGV{encoding}}
+ ;
+ map { { font => $font,
+ style => $style,
+ shape => $shape,
+ encoding => $_,
+ }
+ }
+ @encodings;
+ }
+ @_;
+}
+
+#-----------------------------------------------------------------------
+# Add some finishing touches to the work list
+#-----------------------------------------------------------------------
+sub cleanup {
+ my @worklist = @_;
+
+ WORKITEM:
+ for my $item (@worklist) {
+ my ($font, $style, $shape, $encoding)
+ = @{$item}{qw(font style shape encoding)};
+
+ # don't generate smallcaps version of TS1-encoded fonts,
+ # as these contain the same glyphs as the regular version
+ if ($font->{is_smallcaps}) {
+ if ($shape eq 'textcomp') {
+ $item = 0;
+ next WORKITEM;
+ }
+ else {
+ $shape = 'smallcaps';
+ }
+ }
+
+ # generate unique name for this font
+ $item->{fontname} = join '-', grep { $_ } $font->{name},
+ lc $style,
+ lc $SHAPE{$shape}{name},
+ lc $encoding->[1];
+
+ # look up NFSS code for font's shape
+ $item->{fdshape} = $SHAPE{$shape}{code}{$font->{basicshape}};
+
+ # compile list of OpenType features to use with this font
+ my %feature = map { ($_ => 1) }
+ grep { $font->{feature}{$_} }
+ ( @{$STYLE{$style}{reqd}},
+ @{$STYLE{$style}{nice}},
+ @{$SHAPE{$shape}{reqd}},
+ @{$SHAPE{$shape}{nice}},
+ );
+ if ($feature{lnum} && $feature{onum}) {
+ delete $feature{lnum};
+ }
+ $item->{features} = [ sort keys %feature ];
+
+ # add finishing touches to name and NFSS code of encoding
+ $item->{encoding}[0]
+ =~ s/\A (ot1|t1|ly1|lgr|t2[abc]|t3|ts3) \z/fontools_$1/xms;
+ $item->{encoding}[1] = uc $item->{encoding}[1];
+
+ $item->{cmdline} = make_cmdline($item);
+ }
+
+ return grep { $_ } @worklist;
+}
+
+#-----------------------------------------------------------------------
+# Modify NFSS_WEIGHT and _WIDTH tables: pick one choice for each NFSS code
+#-----------------------------------------------------------------------
+sub decide_nfss_mappings {
+ my ( %weight, %width );
+ for my $font (@_) {
+ $weight{ $font->{weight} } //= $font->{weight_class};
+ $width{ $font->{width} } //= $font->{width_class};
+ }
+
+ NFSSWEIGHT:
+ for my $nfssweight (@NFSS_WEIGHT) {
+ $NFSS_WEIGHT{$nfssweight}
+ = [ grep { $weight{$_} } @{$NFSS_WEIGHT{$nfssweight}} ];
+ }
+
+ # Some trickery to handle the case where the ul/ub codes are mapped
+ # but the el/eb codes are still empty. We try two things:
+ # 1. if there is a Thin (Heavy) weight and this is less extreme
+ # than the weight mapped to ul (ub), we map Thin (Heavy) to ul (ub)
+ # 2. otherwise we move the ul/ub weight to the el/eb position,
+ # unless that weight is the Ultralight/Ultrabold weight
+ if ( !$ARGV{el} and !$ARGV{ul} ) {
+ if ( @{$NFSS_WEIGHT{ul}} and !@{$NFSS_WEIGHT{el}} ) {
+ if ( $weight{thin}
+ and $weight{thin} > $weight{$NFSS_WEIGHT{ul}[0]} ) {
+ $NFSS_WEIGHT{el} = [ 'thin', ];
+ }
+ elsif ( $NFSS_WEIGHT{ul}[0] ne 'ultralight' ) {
+ $NFSS_WEIGHT{el} = [ shift @{$NFSS_WEIGHT{ul}} ];
+ }
+ }
+ }
+ if ( !$ARGV{eb} and !$ARGV{ub} ) {
+ if ( @{$NFSS_WEIGHT{ub}} and !@{$NFSS_WEIGHT{eb}} ) {
+ if ( $weight{heavy}
+ and $weight{heavy} < $weight{$NFSS_WEIGHT{ub}[0]}
+ ) {
+ $NFSS_WEIGHT{eb} = [ 'heavy', ]
+ unless @{$NFSS_WEIGHT{b}}
+ and $weight{$NFSS_WEIGHT{b}[0]} > $weight{heavy};
+ }
+ elsif ( $NFSS_WEIGHT{ub}[0] ne 'ultrabold' ) {
+ $NFSS_WEIGHT{eb} = [ shift @{$NFSS_WEIGHT{ub}} ];
+ }
+ }
+ }
+
+ # Special case: if we don't have Regular but we *do* have Medium,
+ # move Medium from the "sb" list to the "m" (i.e., Regular) one.
+ if ( !@{$NFSS_WEIGHT{''}} and $weight{medium} ) {
+ $NFSS_WEIGHT{''} = [ 'medium' ];
+ $NFSS_WEIGHT{sb}
+ = [ grep { $_ ne 'medium' } @{$NFSS_WEIGHT{medium}} ];
+ }
+
+ # Some more trickery to map the sl code to Book or Text (but of course
+ # only if sl is empty and Book/Text is lighter than Regular)
+ if ( !@{$NFSS_WEIGHT{sl}} ) {
+ $NFSS_WEIGHT{sl}
+ = [ grep { $weight{$_} < $weight{$NFSS_WEIGHT{''}[0]} }
+ @{$NFSS_WEIGHT{''}}
+ ];
+ }
+
+ NFSSWIDTH:
+ for my $nfsswidth (@NFSS_WIDTH) {
+ for my $width ( @{$NFSS_WIDTH{$nfsswidth}} ) {
+ if ( $width{$width} ) {
+ $NFSS_WIDTH{$nfsswidth} = [ $width ];
+ next NFSSWIDTH;
+ }
+ }
+ $NFSS_WIDTH{$nfsswidth} = [];
+ }
+}
+
+#-----------------------------------------------------------------------
+# Log all fonts, styles, encodings etc. we're going to create
+#-----------------------------------------------------------------------
+sub create_logfile {
+ my @fonts = @_;
+
+ if (-e $ARGV{logfile}) {
+ print "[INFO] file '$ARGV{logfile}' already exists;\n" .
+ " appending new log data to end\n\n";
+ }
+ open my $LOG, '>>', $ARGV{logfile}
+ or die "$0: cannot create $ARGV{logfile}: $!";
+
+ print {$LOG} <<"END_ARGUMENTS";
+
+
+############################################################################
+
+
+@{[ POSIX::strftime("[%F %T]", localtime time) ]} $0, version $VERSION
+
+ "$ARGV{cmdline}"
+
+----------------------------------------------------------------------------
+
+I'm using the following options:
+
+ encoding(s): @{[ join ', ', @{$ARGV{encoding}} ]}
+ NFSS: $ARGV{nfss} @{[ $ARGV{nfss} eq 'rm' ? '(serif)'
+ : $ARGV{nfss} eq 'sf' ? '(sanserif)'
+ : $ARGV{nfss} eq 'tt' ? '(typewriter)'
+ : '(unknown)'
+ ]}
+
+ lining: @{[ $ARGV{lining} ? 'yes' : 'no' ]}
+ oldstyle: @{[ $ARGV{oldstyle} ? 'yes' : 'no' ]}
+ proportional: @{[ $ARGV{proportional} ? 'yes' : 'no' ]}
+ tabular: @{[ $ARGV{tabular} ? 'yes' : 'no' ]}
+ ts1: @{[ $SHAPE{textcomp} ? 'yes' : 'no' ]}
+ smallcaps: @{[ $ARGV{smallcaps} ? 'yes' : 'no' ]}
+ swash: @{[ $ARGV{swash} ? 'yes' : 'no' ]}
+ titling: @{[ $ARGV{titling} ? 'yes' : 'no' ]}
+ superiors: @{[ $ARGV{superiors} ? 'yes' : 'no' ]}
+ inferiors: $ARGV{inferiors}
+ ornaments: @{[ $ARGV{ornaments} ? 'yes' : 'no' ]}
+ fractions: @{[ $ARGV{fractions} ? 'yes' : 'no' ]}
+ ligatures: @{[ $ARGV{ligatures} ? 'yes' : 'no' ]}
+
+ dry run/real: @{[ $ARGV{dryrun} ? 'dry run' : 'real' ]}
+ auto/manual: @{[ $ARGV{manual} ? 'manual' : 'auto' ]}
+ target: $ARGV{target}
+ extra: @{[ $ARGV{extra} || '<empty>' ]}
+
+ figurekern: @{[ $ARGV{figurekern} ? 'keep' : 'remove' ]}
+ mergewidths: @{[ $ARGV{mergewidths} ? 'yes' : 'no' ]}
+ mergeweights: @{[ $ARGV{mergeweights} ? 'yes' : 'no' ]}
+ mergeshapes: @{[ $ARGV{mergeshapes} ? 'yes' : 'no' ]}
+
+ nfssweight: @{[ join q{, }, @{$ARGV{nfssweight}} or '-' ]}
+ nfsswidth: @{[ join q{, }, @{$ARGV{nfsswidth}} or '-' ]}
+
+END_ARGUMENTS
+
+ my %fontfamilies = map { ( $_->{family} => 1 ) } @fonts;
+ my @fontfamilies = keys %fontfamilies;
+ if (scalar @fontfamilies > 1) {
+ warn <<"END_WARNING_MULTIPLE_FAMILIES";
+[WARNING] Your fonts seem to belong to multiple families:
+
+ @{[ join( "\n" . q{ } x 10, sort @fontfamilies ) ]}
+
+ "autoinst" performs best when installing one family at a time.
+ If you think your fonts should all be in the same family,
+ you might have found a bug in autoinst's font info parsing.
+
+ Please see the log file $ARGV{logfile} for details on how
+ the fonts' families, weights, widths and shapes were parsed.
+
+END_WARNING_MULTIPLE_FAMILIES
+
+ # increase verbosity if necessary, to log all relevant info
+ if ($ARGV{verbose} < 1) {
+ $ARGV{verbose} = 1;
+ }
+ }
+
+ $ARGV{logfile} = $LOG;
+
+ return if $ARGV{verbose} < 1;
+
+ print {$LOG} '-' x 76 . "\n\nResults of font info parsing:\n";
+
+ for my $font (@fonts) {
+ print {$LOG} <<"END_PARSE_FONT";
+
+ $font->{filename}
+ Name: $font->{name}
+ Family: $font->{family}
+ Weight: $font->{weight} ($font->{weight_class})
+ Width: $font->{width}
+ Shape: $font->{shape} @{[ $font->{is_smallcaps}
+ ? '(smallcaps)' : '' ]}
+ Size: $font->{minsize}-$font->{maxsize}
+ Features: @{[ join ', ', sort keys %{$font->{feature}} ]}
+END_PARSE_FONT
+ }
+}
+
+#-----------------------------------------------------------------------
+# Log all fonts, styles, encodings etc. we're going to create
+#-----------------------------------------------------------------------
+sub log_worklist {
+ my @worklist
+ = sort { $a->{font}{filename} cmp $b->{font}{filename}
+ || $a->{encoding}[1] cmp $b->{encoding}[1]
+ || $a->{style} cmp $b->{style}
+ } @_;
+
+ my $LOG = $ARGV{logfile};
+
+ print {$LOG} "\n" . '-' x 76 . "\n\nNFSS mappings:\n\n";
+ for my $weight (@NFSS_WEIGHT) {
+ printf {$LOG} " %-3s => %s\n",
+ $weight || 'm', $NFSS_WEIGHT{$weight}[0] || '';
+ }
+ printf {$LOG} "\n";
+ for my $width (@NFSS_WIDTH) {
+ printf {$LOG} " %-3s => %s\n",
+ $width || 'm', $NFSS_WIDTH{$width}[0] || '';
+ }
+ printf {$LOG} "\n";
+
+ return if $ARGV{verbose} < 1;
+
+ my ($prevfn, $prevsty, $prevenc) = ('') x 3;
+ my @cmds;
+ for my $item (@worklist) {
+ if ($prevfn ne $item->{font}{filename}) {
+ print {$LOG} <<"END_FONTINFO";
+
+ ------------------------------------------------------------------------
+
+ $item->{font}{filename}
+
+ Generating these encodings, figure styles and shapes:
+
+ ENC STYLE SHAPE FEATURES USED
+END_FONTINFO
+ }
+ if ($prevenc ne $item->{encoding}[1]
+ || $prevsty ne $item->{style}) {
+ print {$LOG} "\n";
+ }
+ printf {$LOG} " %-3s %-4s %-4s %s\n",
+ $item->{encoding}[1],
+ $item->{style},
+ $item->{fdshape},
+ join(', ', @{$item->{features}}),
+ ;
+ $prevfn = $item->{font}{filename};
+ $prevsty = $item->{style};
+ $prevenc = $item->{encoding}[1];
+
+ push @cmds, $item->{cmdline};
+ }
+
+ return if $ARGV{verbose} < 2;
+ print {$LOG} join "\n\n", @cmds;
+ print {$LOG} "\n";
+ close $LOG;
+}
+
+#-----------------------------------------------------------------------
+# Generate all otftotfm commands, and either save or execute them
+#-----------------------------------------------------------------------
+sub make_commands {
+ my @worklist = @_;
+
+ my @commands = map { $_->{cmdline} } @worklist;
+
+ # make sure the last command *does* call updmap
+ $commands[-1] =~ s/--no-updmap//xms if $ARGV{updmap};
+
+ if ($ARGV{manual}) {
+ open my $BAT, '>', 'autoinst.bat'
+ or die "[ERROR] Can't create 'autoinst.bat': $!";
+ print {$BAT} "$_\n" for @commands;
+ close $BAT;
+ }
+ else {
+ if (grep { system $_ } @commands) {
+ warn <<"END_OTFTOTFM_WARNING";
+[WARNING] one or more calls to 'otftotfm' failed;
+ please see the messages above.
+
+ Note that you can safely ignore any warnings
+ "bad UNICODING (‘germandbls’ has no encoding)"
+ or "bad ligature (‘ff’ has no encoding)"
+END_OTFTOTFM_WARNING
+ }
+ }
+
+ return;
+}
+
+# The official names for various coding schemes
+my %SCHEME = (
+ fontools_ot1 => 'TEX TEXT',
+ fontools_t1 => 'EXTENDED TEX FONT ENCODING - LATIN',
+ fontools_ts1 => 'TEX TEXT COMPANION SYMBOLS 1---TS1',
+ fontools_ly1 => 'TEX TYPEWRITER AND WINDOWS ANSI',
+ fontools_lgr => 'GREEK FONT ENCODING - LGR',
+ fontools_t2a => 'TEX CYRILLIC FONT ENCODING - T2A',
+ fontools_t2b => 'TEX CYRILLIC FONT ENCODING - T2B',
+ fontools_t2c => 'TEX CYRILLIC FONT ENCODING - T2C',
+ fontools_t3 => 'TEX IPA ENCODING',
+ fontools_ts3 => 'TEX IPA SYMBOL ENCODING',
+);
+
+#-----------------------------------------------------------------------
+# Generate a command line for otftotfm
+#-----------------------------------------------------------------------
+sub make_cmdline {
+ my $item = shift;
+
+ if ( !$ARGV{ligatures} ) {
+ @{$item->{features}} = grep { $_ ne 'liga' } @{$item->{features}};
+ }
+ return join ' ', 'otftotfm',
+ ($ARGV{manual} ? '--pl' : '--automatic'),
+ "--encoding=$item->{encoding}[0]",
+ set_targetdirs($item->{font}{family}),
+ '--no-updmap',
+ ($item->{font}{filename} =~ m/[.]ttf\z/xms
+ ? '--no-type1'
+ : ''),
+ ($SCHEME{$item->{encoding}[0]}
+ ? qq(--coding-scheme="$SCHEME{$item->{encoding}[0]}")
+ : ''),
+ (map { "--feature=$_" } @{$item->{features}}),
+ ($ARGV{ligatures}
+ ? ( '--ligkern="f i =: fi"',
+ '--ligkern="f l =: fl"',
+ '--ligkern="f f =: ff"',
+ '--ligkern="ff i =: ffi"',
+ '--ligkern="ff l =: ffl"' )
+ : ''),
+ $STYLE{$item->{style}}{extra},
+ $SHAPE{$item->{shape}}{extra},
+ $ARGV{extra},
+ qq("$item->{font}{filename}"),
+ $item->{fontname},
+ ;
+}
+
+#-----------------------------------------------------------------------
+# Return a string with all "directory" options for otftotfm set
+#-----------------------------------------------------------------------
+my @FILETYPES = qw(tfm vf type1 truetype);
+
+sub set_targetdirs {
+ my $family = shift;
+
+ my %dir = map { ($_ => File::Spec->catdir(
+ $ARGV{target},
+ 'fonts',
+ $_,
+ $ARGV{vendor},
+ $ARGV{typeface} || $family))
+ }
+ @FILETYPES;
+
+ $dir{$_} = File::Spec->catdir(
+ $ARGV{target}, 'fonts', $_, 'dvips', $ARGV{typeface} || $family)
+ for qw(enc map);
+
+ File::Path::make_path(values %dir) unless $ARGV{dryrun};
+
+ my $result = join ' ', map { "--${_}-directory=$dir{$_}" } @FILETYPES;
+ $result .= " --encoding-directory=$dir{enc} --map-file="
+ . File::Spec->catfile($dir{map}, "${family}.map");
+
+ return $result;
+}
+
+
+############################################################################
+
+
+package ARGV;
+
+my $USAGE =<<'END_USAGE';
+
+'autoinst' is a wrapper around Eddie Kohler's TypeTools
+(http://www.lcdf.org/type/), for installing OpenType fonts in LaTeX.
+
+Usage: autoinst [options] font[s]
+
+Possible options:
+ -encoding=ENC[,ENC]* Specify text encoding(s) (default: OT1,T1,LY1)
+
+ -(no)lining Toggle creation of lining digits
+ -(no)oldstyle Toggle creation of oldstyle digits
+ -(no)proportional Toggle creation of proportional digits
+ -(no)tabular Toggle creation of tabular digits
+ -(no)ts1 Toggle creation of TS1 fonts
+ -(no)smallcaps Toggle creation of smallcaps shape
+ -(no)swash Toggle creation of swash shape
+ -(no)titling Toggle creation of titling shape
+ -(no)superiors Toggle creation of fonts with superior characters
+ -noinferiors
+ -inferiors=[none|auto|subs|sinf|dnom]
+ Use this style for subscripts (see docs)
+ -(no)ornaments Toggle creation of ornament fonts
+ -(no)fractions Toggle creation of fonts with digits for fractions
+ -(no)ligatures Toggle manual addition of f-ligatures to font
+
+ -serif Install font as serif font
+ -sanserif Install font as sanserif font
+ -typewriter Install font as typewriter font
+
+ -defaultlining
+ -defaultoldstyle Specify which figure style should be
+ -defaulttabular considered 'default' for this font; see docs
+ -defaultproportional
+
+ -extra="TEXT" Add TEXT to the command for 'otftotfm'
+ -target="DIRECTORY" Install files into specified TEXMF tree
+ -vendor="VENDOR" Only used for naming directories
+ -typeface="TYPEFACE" Only used for naming directories
+ -(no)updmap Toggle running of updmap
+ -manual Manual mode (see documentation)
+
+ -(no)figurekern Keep or remove kerns between tabular figures
+ -(no)mergewidths Merge Condended/Extended subfamilies with main
+ -(no)mergeweights Merge separate weights with main family
+ -(no)mergeshapes Merge separate shapes with main family
+
+ -nfssweight=xx=yyyy Map the "xx" NFSS code to the "yyyy" weight
+ -nfsswidth=xx=yyyy Map the "xx" NFSS code to the "yyyy" width
+
+ -help Print this text and exit
+ -doc Print the complete documentation and exit
+ -dryrun Don't generate fonts, only log what would be done
+ -logfile="FILE" Write log to "FILE" (default: <fontfamily>.log)
+ -verbose Print more data to log file
+ (repeat for even higher verbosity)
+ -version Print version number and exit
+ font[s] The fonts (.otf or .ttf format) to install.
+
+Please report any bugs or suggestions to <marcpenninga@gmail.com>.
+END_USAGE
+
+# Default values for the command-line arguments
+%ARGV = (
+ encoding => 'OT1,T1,LY1',
+ textcomp => '2', # 0 = no, 1 = yes, 2 = ('T1' ? yes : no)
+ lining => '1', # 0 = no, 1 = yes
+ oldstyle => '1', # 0 = no, 1 = yes
+ proportional => '1', # 0 = no, 1 = yes
+ tabular => '1', # 0 = no, 1 = yes
+ smallcaps => '1', # 0 = no, 1 = yes
+ swash => '1', # 0 = no, 1 = yes
+ titling => '1', # 0 = no, 1 = yes
+ superiors => '1', # 0 = no, 1 = yes
+ inferiors => 'none', # values: none, auto, subs, sinf, dnom
+ ornaments => '1', # 0 = no, 1 = yes
+ fractions => '0', # 0 = no, 1 = yes
+ ligatures => '2', # 0 = no, 1 = yes, 2 = ('tt' ? no : yes)
+ nfss => '',
+ fig_height => 'lnum',
+ fig_width => 'tnum',
+ extra => '',
+ target => '',
+ vendor => 'lcdftools',
+ typeface => '',
+ updmap => '1', # 0 = no, 1 = yes
+ manual => '0', # 0 = no, 1 = yes
+ dryrun => '0', # 0 = no, 1 = yes
+ logfile => '',
+ figurekern => '1', # 0 = no, 1 = yes
+ mergewidths => '1', # 0 = no, 1 = yes
+ mergeweights => '1', # 0 = no, 1 = yes
+ mergeshapes => '1', # 0 = no, 1 = yes
+ verbose => 0,
+ nfsswidth => [],
+ nfssweight => [],
+);
+
+#-----------------------------------------------------------------------
+# Process command-line arguments
+#-----------------------------------------------------------------------
+sub parse_options {
+ $ARGV{cmdline} = join ' ', ($0, @ARGV);
+
+ Getopt::Long::GetOptions(
+ 'help|?' => sub { print $USAGE; exit },
+ 'version' => sub { print "$VERSION\n"; exit },
+ 'doc' => sub { Pod::Usage::pod2usage(-verbose => 2) },
+ 'encoding=s' => \$ARGV{encoding},
+ 'ts1!' => \$ARGV{textcomp},
+ 'lining!' => \$ARGV{lining},
+ 'oldstyle!' => \$ARGV{oldstyle},
+ 'proportional!' => \$ARGV{proportional},
+ 'tabular!' => \$ARGV{tabular},
+ 'smallcaps!' => \$ARGV{smallcaps},
+ 'swash!' => \$ARGV{swash},
+ 'titling!' => \$ARGV{titling},
+ 'superiors!' => \$ARGV{superiors},
+ 'noinferiors' => sub { $ARGV{inferiors} = 'none' },
+ 'inferiors:s' => \$ARGV{inferiors},
+ 'ornaments!' => \$ARGV{ornaments},
+ 'fractions!' => \$ARGV{fractions},
+ 'ligatures!' => \$ARGV{ligatures},
+ 'serif' => sub { $ARGV{nfss} = 'rm' },
+ 'sanserif' => sub { $ARGV{nfss} = 'sf' },
+ 'typewriter' => sub { $ARGV{nfss} = 'tt' },
+ 'defaultlining' => sub { $ARGV{fig_height} = 'lnum' },
+ 'defaultoldstyle' => sub { $ARGV{fig_height} = 'onum' },
+ 'defaulttabular' => sub { $ARGV{fig_width} = 'tnum' },
+ 'defaultproportional' => sub { $ARGV{fig_width} = 'pnum' },
+ 'extra=s' => \$ARGV{extra},
+ 'target=s' => \$ARGV{target},
+ 'vendor=s' => \$ARGV{vendor},
+ 'typeface=s' => \$ARGV{typeface},
+ 'updmap!' => \$ARGV{updmap},
+ 'dryrun' => \$ARGV{dryrun},
+ 'manual' => \$ARGV{manual},
+ 'figurekern!' => \$ARGV{figurekern},
+ 'mergewidths!' => \$ARGV{mergewidths},
+ 'mergeweights!' => \$ARGV{mergeweights},
+ 'mergeshapes!' => \$ARGV{mergeshapes},
+ 'mergesmallcaps!' => \$ARGV{mergeshapes},
+ 'logfile=s' => \$ARGV{logfile},
+ 'verbose+' => \$ARGV{verbose},
+ 'nfssweight=s%' => sub {
+ my ( $ignored, $key, $values ) = @_;
+ push @{$ARGV{nfssweight}},
+ "$key=$values";
+ my @values = split m/,/, lc $values;
+ $key = q{} if $key eq 'm';
+ $NFSS_WEIGHT{$key}
+ = [ @values, @{$NFSS_WEIGHT{$key}} ];
+ $ARGV{$key} = 'user-defined';
+ },
+ 'nfsswidth=s%' => sub {
+ my ( $ignored, $key, $values ) = @_;
+ my @values = split m/,/, lc $values;
+ push @{$ARGV{nfsswidth}},
+ "$key=$values";
+ $key = q{} if $key eq 'm';
+ $NFSS_WIDTH{$key}
+ = [ @values, @{$NFSS_WIDTH{$key}} ];
+ $ARGV{$key} = 'user-defined';
+ },
+ )
+ or die "$USAGE";
+
+ die "$USAGE" unless @ARGV;
+
+ delete $SHAPE{smallcaps} unless $ARGV{smallcaps};
+ delete $SHAPE{swash} unless $ARGV{swash};
+
+ delete $STYLE{Titl} unless $ARGV{titling};
+ delete $STYLE{Sup} unless $ARGV{superiors};
+ delete $STYLE{Orn} unless $ARGV{ornaments};
+ delete @STYLE{qw(Numr Dnom)} unless $ARGV{fractions};
+ $ARGV{inferiors} ||= 'auto';
+ if ($ARGV{inferiors} eq 'auto') { $STYLE{Inf}{reqd} = ['auto'] }
+ elsif ($ARGV{inferiors} eq 'subs') { $STYLE{Inf}{reqd} = ['subs'] }
+ elsif ($ARGV{inferiors} eq 'sinf') { $STYLE{Inf}{reqd} = ['sinf'] }
+ elsif ($ARGV{inferiors} eq 'dnom') { $STYLE{Inf}{reqd} = ['dnom'] }
+ else {
+ $ARGV{inferiors} = 'none';
+ delete $STYLE{Inf}
+ }
+
+ $ARGV{encoding} =~ s/\s+//xmsg;
+ my @textencodings = grep { $_ ne 'TS1' }
+ map { uc }
+ split /,/, $ARGV{encoding};
+ $ARGV{encoding} = \@textencodings;
+
+ # TS1-encoded fonts are generated if:
+ # - the user explicitly asked for TS1, or
+ # - the text encodings contain T1 and the user didn't turn off TS1
+ unless ( (grep { $_ eq 'T1' } @{$ARGV{encoding}} and $ARGV{textcomp})
+ or $ARGV{textcomp} == 1 )
+ {
+ delete $SHAPE{textcomp};
+ }
+
+ # Determine NFSS classification of this family, if the user
+ # didn't explicitly specify it
+ eval {
+ if ( !$ARGV{nfss} ) {
+ # EXPERIMENTAL: parse "post" table to see if font is fixed-width
+ my $cmd = qq(otfinfo --dump-table "post" "$ARGV[0]");
+ open my $otfinfo, '-|:raw', $cmd
+ or die "could not fork(): $!";
+ my $post_table = do { local $/; <$otfinfo> };
+ close $otfinfo
+ or die "'$cmd' failed";
+
+ my $is_fixed_pitch = unpack '@12N', $post_table;
+
+ $ARGV{nfss} = $is_fixed_pitch ? 'tt'
+ : $ARGV[0] =~ m/mono(?!type)/xmsi ? 'tt'
+ : $ARGV[0] =~ m/sans/xmsi ? 'sf'
+ : 'rm'
+ ;
+ }
+ } or warn "[WARNING] $@";
+
+ # If the user didn't explicitly say anything about ligatures,
+ # activate them unless the font is a typewriter font.
+ if ($ARGV{ligatures} == 2) {
+ $ARGV{ligatures} = $ARGV{nfss} ne 'tt' ? 1 : 0;
+ }
+ # Fix the %STYLE table to take 'default' figure styles into account.
+ $STYLE_DEFAULTS{$ARGV{fig_height}}();
+ $STYLE_DEFAULTS{$ARGV{fig_width}}();
+
+ delete @STYLE{qw(LF TLF)} unless $ARGV{lining};
+ delete @STYLE{qw(OsF TOsF)} unless $ARGV{oldstyle};
+ delete @STYLE{qw(LF OsF)} unless $ARGV{proportional};
+ delete @STYLE{qw(TLF TOsF)} unless $ARGV{tabular};
+
+ my $localtarget = File::Spec->catdir( Cwd->getcwd(), 'autoinst_output' );
+ if ($ARGV{manual}) {
+ warn "[WARNING] option '-target' overridden by '-manual'!\n"
+ if $ARGV{target};
+ $ARGV{target} = $localtarget;
+ $ARGV{updmap} = 0;
+ }
+ elsif (!$ARGV{target}) {
+ my $kpsepath = eval {
+ qx( kpsewhich -expand-var='\$TEXMFLOCAL:\$TEXMFHOME' );
+ };
+ if (!$kpsepath) {
+ warn <<"END_WARNING_KPSEWHICH";
+[WARNING] Call to "kpsewhich" failed.
+ Maybe your TeX system doesn't use the kpathsea library?
+
+ Consider using the "-target" command line option
+ to specify a TEXMF tree where autoinst should install all files.
+
+END_WARNING_KPSEWHICH
+ }
+ else {
+ for my $dir ( split m/[:;]/xms, substr $kpsepath, 0, -1 ) {
+ if (-w $dir) { $ARGV{target} = $dir; last }
+ if (-e $dir) { next }
+ my $par = File::Spec->catdir( $dir, File::Spec->updir() );
+ if (-w $par) { $ARGV{target} = $dir; last }
+ }
+ }
+ }
+
+ if (!$ARGV{target}) {
+ $ARGV{target} = $localtarget;
+ $ARGV{updmap} = 0;
+ warn <<"END_WARNING_DUMPING_FILES";
+[WARNING] No user-writable TEXMF-tree found!
+
+ I'm putting all generated files in "$ARGV{target}".
+
+ Please install these files into a suitable TEXMF directory,
+ update the filename database and run 'updmap' (or similar);
+ see your TeX installation's documentation.
+END_WARNING_DUMPING_FILES
+ }
+
+ if (!$ARGV{figurekern}) {
+ my @digits = qw(zero one two three four five six seven eight nine);
+ my $tkern
+ = join ' ', map { my $left = $_;
+ map { qq(--ligkern="$left {} $_") } @digits
+ }
+ @digits;
+
+ $STYLE{TLF}{extra} = $tkern;
+ $STYLE{TOsF}{extra} = $tkern;
+ }
+}
+
+
+############################################################################
+
+
+package Fontinfo;
+
+#-----------------------------------------------------------------------
+# Return ref to hash with all relevant info about this font
+#-----------------------------------------------------------------------
+sub parse_fontinfo {
+ my ($filename) = @_;
+
+ my $info = {
+ filename => $filename,
+ width => 'regular',
+ weight => 'regular',
+ shape => 'roman',
+ minsize => 0,
+ maxsize => 0,
+ is_smallcaps => 0,
+ weight_class => 0,
+ width_class => 0,
+ };
+
+ parse_basicinfo($info);
+ parse_sizeinfo($info);
+ parse_features($info);
+
+ return $info;
+}
+
+# table for converting digits in font names to text (safer for LaTeX)
+my @DIGITS = qw(Zero One Two Three Four Five Six Seven Eight Nine);
+
+#-----------------------------------------------------------------------
+# Get some basic info (family, weight, width, shape) about this font
+#-----------------------------------------------------------------------
+sub parse_basicinfo {
+ my $info = shift;
+
+ my $cmd = qq(otfinfo --info "$info->{filename}");
+ open my $otfinfo, '-|', $cmd
+ or die "[ERROR] could not fork(): $!";
+ my %data = map { my ($k,$v) = m/\A\s* ([^:]+?) \s*:\s* ([^\r\n]+)/xms;
+ $k =~ s/\s+//xmsg;
+ $v =~ s/\s+//xmsg;
+ (lc $k => $v);
+ }
+ grep { m/\A\s* [^:]+? \s*:\s* [^\r\n]+/xms } <$otfinfo>;
+ close $otfinfo
+ or die "[ERROR] '$cmd' failed";
+
+ $data{family} = $data{preferredfamily} || $data{family};
+ $data{subfamily} = $data{preferredsubfamily} || $data{subfamily};
+ $data{fullname} =~ s/\A$data{family}//xms;
+ $data{fullname} = lc $data{fullname};
+
+ # clean up family name (it's used in LaTeX command names)
+ $data{family} =~ s/\A(?: Adobe | DTL | FF | ITC | LT | MT)//xms;
+ $data{family} =~ s/(?: LT | MT)(?: Std | Pro )\z//xms;
+ $data{family} =~ s/(?<= Kievit ) OT//xms;
+ $data{family} =~ s/ Std \z//xms;
+ $data{family} =~ s/(\d)/$DIGITS[$1]/xmsge;
+ $data{family} =~ s/[^A-Za-z]+//xmsg;
+
+ # Sometimes the relevant info is in Fullname, sometimes in Subfamily;
+ # so we need to test against both
+ my $fullinfo = lc "$data{subfamily} | $data{fullname}";
+
+ # We need to be careful when parsing the font info; in particular
+ # we must parse 'UltraCondensed' as 'Regular' weight
+ # and 'UltraCondensed' width, not as 'Ultra' weight and 'Condensed' width.
+ # The following rules should prevent accidents:
+ # 1. Search for matching widths before matching weights
+ # (as none of the widths is a proper substring of some weight)
+ # 2. Remove any recognised search string from the 'fullinfo'
+ # 3. Test the weights 'medium' and 'regular' *last*, since these strings
+ # may also occur in Subfamily without indicating the weight;
+ # so we only take them to mean weight if we find no other hit.
+ for my $width ( mqrs(@WIDTHS) ) {
+ if ($fullinfo =~ m/${width}/xms) {
+ $info->{width} = $width;
+ my $widths = join '|', mqrs(@WIDTHS);
+ $fullinfo =~ s/${widths}//gxmsi;
+ last;
+ }
+ }
+ for my $weight ( mqrs(@WEIGHTS), qw(medium regular) ) {
+ if ($fullinfo =~ m/${weight}/xms) {
+ $info->{weight} = $weight;
+ my $weights = join '|', mqrs(@WEIGHTS), qw(medium regular);
+ $fullinfo =~ s/${weights}//gxmsi;
+ last;
+ }
+ }
+ for my $shape ( mqrs( keys %NFSS_SHAPE ) ) {
+ if ($fullinfo =~ m/${shape}/xms) {
+ $info->{shape} = $shape;
+ my $shapes = join '|', mqrs( keys %NFSS_SHAPE );
+ $fullinfo =~ s/${shapes}//gxmsi;
+ last;
+ }
+ }
+
+ # In many font families, each font is in a subfamily of its own;
+ # so we remove width, weight and shape from the 'subfamily' value.
+ my $subst;
+ $data{subfamily} =~ s/$info->{width}//xmsi;
+ $data{subfamily} =~ s/$info->{weight}//xmsi;
+ $data{subfamily} =~ s/$info->{shape}//xmsi;
+
+ $info->{name} = $data{postscriptname};
+ $info->{family} = $data{family};
+ $info->{subfamily} = $data{subfamily};
+
+ # Take care to unabbreviate weight and width; CondensedUltra fonts
+ # might end up as 'ultracondensed' instead of 'ultrablackcondensed'!
+ if (exists $UNABBREVIATE{ $info->{width} }) {
+ $info->{width} = $UNABBREVIATE{ $info->{width} };
+ }
+ if (exists $UNABBREVIATE{ $info->{weight} }) {
+ $info->{weight} = $UNABBREVIATE{ $info->{weight} };
+ }
+ if (exists $UNABBREVIATE{ $info->{shape} }) {
+ $info->{shape} = $UNABBREVIATE{ $info->{shape} };
+ }
+
+ if ($ARGV{mergeshapes}) {
+ my $shapes = join '|', mqrs( qw(it italic) );
+ if ( $info->{family} =~ m/(.+?) (${shapes}) \z/xmsi
+ and ( $info->{shape} eq 'regular'
+ or $info->{shape} eq ( $UNABBREVIATE{ lc($2) } // lc($2) )
+ )
+ ) {
+ $info->{family} = $1;
+ $info->{shape} = $UNABBREVIATE{ lc($2) } // lc($2);
+ }
+
+ $shapes = join '|', mqrs( qw(sc smallcaps) );
+ if ( $info->{family} =~ m/(.+?) (${shapes}) \z/xmsi ) {
+ $info->{family} = $1;
+ $info->{is_smallcaps} = 1;
+ }
+ }
+
+ # Some font families put different widths into separate families;
+ # we provide an option to merge these with the 'main' font family.
+ if ($ARGV{mergewidths}) {
+ my $widths = join '|', mqrs(@WIDTHS);
+ if ( $info->{family} =~ m/(.+?) (${widths}) \z/xmsi
+ and ( $info->{width} eq 'regular'
+ or $info->{width} eq ( $UNABBREVIATE{ lc($2) } // lc($2) )
+ )
+ ) {
+ $info->{family} = $1;
+ $info->{width} = $UNABBREVIATE{ lc($2) } // lc($2);
+ }
+ }
+
+ # Some font families put extreme weights into separate families;
+ # we provide an option to merge these with the 'main' font family.
+ if ($ARGV{mergeweights}) {
+ my $weights = join '|', mqrs(@WEIGHTS), qw(medium regular);
+ if ( $info->{family} =~ m/(.+?) (${weights}) \z/xmsi
+ and ( $info->{weight} eq 'regular'
+ or $info->{weight} eq ( $UNABBREVIATE{ lc($2) } // lc($2) )
+ )
+ ) {
+ $info->{family} = $1;
+ $info->{weight} = $UNABBREVIATE{ lc($2) } // lc($2);
+ }
+ }
+
+ # Fix confusion about "Text": weight vs part of familyname.
+ # In the latter case, we strip it off anyway.
+ $info->{family} =~ s/text \z//xmsi;
+
+ if ( !$ARGV{logfile} ) {
+ $ARGV{logfile} = sprintf "%s.log", lc $info->{family}
+ }
+
+ $info->{basicshape} = $NFSS_SHAPE{$info->{shape}};
+
+ # We define 'series' as 'weight + width'. This matches NFSS,
+ # but contradicts how most fonts are named (which is 'width + weight').
+ $info->{series}
+ = ($info->{width} eq 'regular') ? $info->{weight}
+ : ($info->{weight} eq 'regular') ? $info->{width}
+ : $info->{weight} . $info->{width}
+ ;
+
+ # EXPERIMENTAL: we extract the usWeightClass and usWidthClass
+ # properties from the font's OS/2 table
+ eval {
+ my $cmd = qq(otfinfo --dump-table "OS/2" "$info->{filename}");
+ open $otfinfo, '-|:raw', $cmd
+ or die "could not fork(): $!";
+ my $os2_table = do { local $/; <$otfinfo> };
+ close $otfinfo
+ or die "'$cmd' failed";
+ @{$info}{qw(weight_class width_class)} = unpack '@4n @6n', $os2_table;
+ } or warn "[WARNING] $@";
+
+ return;
+}
+
+#-----------------------------------------------------------------------
+# Re-order argument list so that longer strings come before shorter ones
+#
+# This is needed when matching a string against a list of patterns
+# where some of the patterns may be substrings of other patterns;
+# then we want the *longest* matching pattern, so we test the patterns
+# in descending order of length.
+#-----------------------------------------------------------------------
+sub mqrs {
+ return map { quotemeta } reverse sort { length($a) <=> length($b) } @_;
+}
+
+#-----------------------------------------------------------------------
+# Fill the 'feature' field in the 'info' struct with a list of
+# all features this font supports
+#-----------------------------------------------------------------------
+sub parse_features {
+ my $info = shift;
+
+ my $cmd = qq(otfinfo --features "$info->{filename}");
+ open my $otfinfo, '-|', $cmd
+ or die "[ERROR] could not fork(): $!";
+ %{$info->{feature}} = map { (substr($_, 0, 4) => 1) } <$otfinfo>;
+ close $otfinfo
+ or die "[ERROR] '$cmd' failed";
+
+ $cmd = qq(otfinfo --tables "$info->{filename}");
+ open $otfinfo, '-|', $cmd
+ or die "[ERROR] could not fork(): $!";
+ $info->{feature}{kern} = 1 if grep { m/\d+ \s+ kern/xms } <$otfinfo>;
+ close $otfinfo
+ or die "[ERROR] '$cmd' failed";
+
+ return;
+}
+
+#-----------------------------------------------------------------------
+# Fill the 'minsize' and 'maxsize' fields in the 'info' struct
+# with limits of optical design size range of this font
+#-----------------------------------------------------------------------
+sub parse_sizeinfo {
+ my $info = shift;
+
+ my $cmd = qq(otfinfo --optical-size "$info->{filename}");
+ open my $otfinfo, '-|', $cmd
+ or die "[ERROR] could not fork(): $!";
+
+ if (my ($minsize, $maxsize)
+ = <$otfinfo> =~ m/[(] ([\d.]+) \s* pt, \s*
+ ([\d.]+) \s* pt \s* []]/xms
+ ) {
+ # fix some known bugs
+ if ($info->{name} eq 'GaramondPremrPro-It'
+ && $minsize == 6 && $maxsize == 8.9)
+ {
+ ($minsize, $maxsize) = (8.9, 14.9);
+ }
+ elsif ($info->{family} eq 'KeplerStd'
+ && $info->{subfamily} =~ m/Caption/xms
+ && $minsize == 8.9 && $maxsize == 13.9)
+ {
+ ($minsize, $maxsize) = (6, 8.9);
+ }
+ elsif ($info->{family} eq 'KeplerStd'
+ && $info->{subfamily} =~ m/Subhead/xms
+ && $minsize == 8.9 && $maxsize == 13.9)
+ {
+ ($minsize, $maxsize) = (13.9, 23);
+ }
+ elsif ($info->{family} eq 'KeplerStd'
+ && $info->{subfamily} =~ m/Display/xms
+ && $minsize == 8.9 && $maxsize == 13.9)
+ {
+ ($minsize, $maxsize) = (23, 72);
+ }
+
+ @{$info}{qw(minsize maxsize)} = ($minsize, $maxsize);
+ }
+ close $otfinfo
+ or die "[ERROR] '$cmd' failed";
+
+ return;
+}
+
+
+# Error messages, used in assert_unique()
+my $ERR_DETAIL =<<'END_ERR_DETAIL';
+[ERROR] I've parsed both %s
+ and %s as
+
+ Family: %s
+ Weight: %s
+ Width: %s
+ Shape: %s
+ Size: %s-%s
+
+END_ERR_DETAIL
+
+my $ERR_PARSE =<<'END_ERR_PARSE';
+[ERROR] I failed to parse all fonts in a unique way;
+ presumably some fonts have unusual widths, weights or shapes.
+
+ Try one of the following:
+ - Run 'autoinst' on a smaller set of fonts,
+ omitting the ones that weren't parsed correctly;
+ - Add the missing widths, weights or shapes to the tables
+ 'NFSS_WIDTH', 'NFSS_WEIGHT' or 'NFSS_SHAPE' near the top
+ of the source code;
+
+ Please also send a bug report to the author.
+
+END_ERR_PARSE
+
+#-----------------------------------------------------------------------
+# Assert all parsed font infos are unique
+#-----------------------------------------------------------------------
+sub assert_unique {
+ my @fonts = @_;
+
+ # These attributes should uniquely identify each font
+ my @attributes
+ = qw(family weight width shape minsize maxsize is_smallcaps);
+
+ my (%seen, $err_details);
+ for my $font (@fonts) {
+ my $key = join "\x00", @{$font}{ @attributes };
+
+ if ($seen{$key}) {
+ $err_details .= sprintf $ERR_DETAIL,
+ $seen{$key}{filename},
+ $font->{filename},
+ @{$font}{ @attributes };
+ }
+ else {
+ $seen{$key} = $font;
+ }
+ }
+
+ # Die with detailed error message if the font infos aren't unique
+ if ($err_details) {
+ die $err_details, $ERR_PARSE;
+ }
+
+ return 1;
+}
+
+#-----------------------------------------------------------------------
+# Generate (font-specific) encoding vector for ornament glyphs
+#-----------------------------------------------------------------------
+sub get_orn {
+ my ($font) = @_;
+
+ my $fontfile = $font->{filename};
+ my $enc_name = $font->{family} . '_orn';
+
+ if (not -e "$enc_name.enc") {
+ # Default ornament names: 'orn.' plus three digits
+ my @encoding = map { sprintf "orn.%03d", $_ } 1 .. 256;
+
+ my $cmd = qq(otfinfo --glyphs "$font->{filename}");
+ open my $otfinfo, '-|', $cmd
+ or die "[ERROR] could not fork(): $!";
+ chop(my @glyphnames = <$otfinfo>);
+ close $otfinfo
+ or die "[ERROR] '$cmd' failed";
+
+ # Test for some known alternative names (probably not exhaustive)
+ my @ornaments
+ = sort grep { m/\A (?: orn|u2022[.]|word[.]|hand|bullet[.])
+ | [.]orn \z/xms
+ }
+ @glyphnames;
+
+ @encoding[0 .. $#ornaments] = @ornaments;
+
+ open my $ORN, '>', "$enc_name.enc"
+ or die "[ERROR] Can't create '$enc_name.enc': $!";
+
+ print {$ORN} "/$font->{family}OrnamentEncoding [\n";
+ map { print {$ORN} " /$_\n" } @encoding[0 .. 255];
+ print {$ORN} "] def\n";
+ close $ORN;
+ }
+
+ return $enc_name;
+}
+
+#-----------------------------------------------------------------------
+# Handle the -inferiors=auto option
+#-----------------------------------------------------------------------
+sub handle_auto_inferiors {
+ FONT:
+ for my $font (@_) {
+ for my $inf (qw(subs sinf dnom)) {
+ if (exists $font->{feature}{$inf}) {
+ $ARGV{inferiors} = $inf;
+ $STYLE{Inf}{reqd} = [$inf];
+ last FONT;
+ }
+ }
+ }
+
+ # If we didn't find any inferior figures, delete the 'Inf' entry
+ # from the %STYLE table to indicate we don't want to generate this style.
+ delete $STYLE{Inf} if $ARGV{inferiors} eq 'auto';
+}
+
+
+############################################################################
+
+
+package LaTeX;
+
+#-----------------------------------------------------------------------
+# Create a LaTeX style file
+#-----------------------------------------------------------------------
+sub write_stylefile {
+ my ($fam, $data) = @_;
+
+ my %seen = %{ get_keys($data) };
+
+ my $fn = sprintf "%s.sty", $fam;
+ my $dir = File::Spec->catdir(
+ $ARGV{target}, 'tex', 'latex', $ARGV{typeface} || $fam);
+ File::Path::make_path($dir);
+ $fn = File::Spec->catfile($dir, $fn);
+ open my $STY, '>', $fn
+ or die "[ERROR] Can't create '$fn': $!";
+
+ print {$STY} <<"END_STY_HEADER";
+%% Generated by autoinst on $TODAY
+%%
+\\NeedsTeXFormat{LaTeX2e}
+\\ProvidesPackage{$fam}
+ [$TODAY (autoinst) Style file for $fam.]
+
+END_STY_HEADER
+
+ my $enc = join ',', grep { $_ ne 'OT1' } @{$ARGV{encoding}};
+
+ print {$STY} "\\RequirePackage[$enc]{fontenc}\n" if $enc;
+ print {$STY} "\\RequirePackage{textcomp}\n" if $seen{TS1};
+
+ print {$STY} <<'END_STY_FONTAXES_START';
+\IfFileExists{mweights.sty}{\RequirePackage{mweights}}{}
+\IfFileExists{fontaxes.sty}{
+ \RequirePackage{fontaxes}
+END_STY_FONTAXES_START
+
+
+ if ($seen{nw} or $seen{sw}) {
+ print {$STY} <<'END_STY_FONTAXES_SW';
+ \DeclareRobustCommand\swshape{\not@math@alphabet\swshape\relax
+ \fontprimaryshape\itdefault\fontsecondaryshape\swdefault\selectfont}
+ \fa@naming@exception{shape}{{n}{sw}}{nw}
+ \fa@naming@exception{shape}{{it}{sw}}{sw}
+
+END_STY_FONTAXES_SW
+ }
+
+ if ($seen{Sup}) {
+ print {$STY} <<'END_STY_FONTAXES_SUP';
+ \fa@naming@exception{figures}{{superior}{proportional}}{Sup}
+ \fa@naming@exception{figures}{{superior}{tabular}}{Sup}
+ \def\sufigures{\@nomath\sufigures
+ \fontfigurestyle{superior}\selectfont}
+ \DeclareTextFontCommand{\textsu}{\sufigures}
+ \let\textsuperior\textsu
+
+END_STY_FONTAXES_SUP
+ }
+
+ if ($seen{Inf}) {
+ print {$STY} <<'END_STY_FONTAXES_INF';
+ \fa@naming@exception{figures}{{inferior}{proportional}}{Inf}
+ \fa@naming@exception{figures}{{inferior}{tabular}}{Inf}
+ \def\infigures{\@nomath\infigures
+ \fontfigurestyle{inferior}\selectfont}
+ \DeclareTextFontCommand{\textin}{\infigures}
+ \let\textinferior\textin
+
+END_STY_FONTAXES_INF
+ }
+
+ if ($seen{Titl}) {
+ print {$STY} <<'END_STY_FONTAXES_TITL';
+ \fa@naming@exception{figures}{{titlingshape}{proportional}}{Titl}
+ \fa@naming@exception{figures}{{titlingshape}{tabular}}{Titl}
+ \def\tlshape{\@nomath\tlshape
+ \fontfigurestyle{titlingshape}\selectfont}
+ \DeclareTextFontCommand{\texttl}{\tlshape}
+ \let\texttitling\texttl
+
+END_STY_FONTAXES_TITL
+ }
+
+ if ($seen{Orn}) {
+ print {$STY} <<'END_STY_FONTAXES_ORN';
+ \fa@naming@exception{figures}{{ornament}{proportional}}{Orn}
+ \fa@naming@exception{figures}{{ornament}{tabular}}{Orn}
+ \def\ornaments{\@nomath\ornaments
+ \fontencoding{U}\fontfigurestyle{ornament}\selectfont}
+ \DeclareTextFontCommand{\textornaments}{\ornaments}
+ \providecommand{\ornament}[1]{\textornaments{\char##1}}
+
+END_STY_FONTAXES_ORN
+ }
+
+ if ($seen{Numr}) {
+ print {$STY} <<'END_STY_FONTAXES_NUMR';
+ \fa@naming@exception{figures}{{numerators}{proportional}}{Numr}
+ \fa@naming@exception{figures}{{numerators}{tabular}}{Numr}
+
+END_STY_FONTAXES_NUMR
+ }
+
+ if ($seen{Dnom}) {
+ print {$STY} <<'END_STY_FONTAXES_DNOM';
+ \fa@naming@exception{figures}{{denominators}{proportional}}{Dnom}
+ \fa@naming@exception{figures}{{denominators}{tabular}}{Dnom}
+
+END_STY_FONTAXES_DNOM
+ }
+
+ print {$STY} "}{}\n\n";
+
+ print {$STY} <<"END_STY_XKEYVAL";
+\\IfFileExists{xkeyval.sty}{
+ \\newcommand*{\\$fam\@scale}{1}
+ \\RequirePackage{xkeyval}
+ \\DeclareOptionX{scale}{\\renewcommand*{\\$fam\@scale}{##1}}
+ \\DeclareOptionX{scaled}{\\renewcommand*{\\$fam\@scale}{##1}}
+}{
+ \\let\\DeclareOptionX\\DeclareOption
+ \\let\\ExecuteOptionsX\\ExecuteOptions
+ \\let\\ProcessOptionsX\\ProcessOptions
+}
+
+END_STY_XKEYVAL
+
+ if ($seen{LF} or $seen{TLF}) {
+ print {$STY}
+ "\\DeclareOptionX{lining}{\\edef\\$fam\@figurestyle{LF}}\n";
+ }
+ if ($seen{OsF} or $seen{TOsF}) {
+ print {$STY}
+ "\\DeclareOptionX{oldstyle}{\\edef\\$fam\@figurestyle{OsF}}\n";
+ }
+ if ($seen{TLF} or $seen{TOsF}) {
+ print {$STY}
+ "\\DeclareOptionX{tabular}{\\edef\\$fam\@figurealign{T}}\n";
+ }
+ if ($seen{LF} or $seen{OsF}) {
+ print {$STY}
+ "\\DeclareOptionX{proportional}{\\edef\\$fam\@figurealign{}}\n";
+ }
+
+ print {$STY} <<"END_STY_MAINFONT";
+\\DeclareOptionX{mainfont}{
+ \\renewcommand{\\familydefault}{\\$ARGV{nfss}default}
+}
+END_STY_MAINFONT
+
+ my $defaults
+ = $seen{OsF} ? 'oldstyle,proportional'
+ : $seen{TOsF} ? 'oldstyle,tabular'
+ : $seen{LF} ? 'lining,proportional'
+ : $seen{TLF} ? 'lining,tabular'
+ : die "[ERROR] Internal bug, please report!";
+
+ my $default_bold;
+ for my $series (qw(heavy black extrabold demibold semibold bold)) {
+ if ( $seen{$series} ) {
+ print {$STY}
+ "\\DeclareOptionX{$series}{\\edef\\bfseries\@$ARGV{nfss}",
+ "{$series}}\n";
+ $default_bold = $series;
+ }
+ }
+ $defaults .= ",$default_bold" if $default_bold;
+
+ my $default_regular;
+ for my $series (qw(medium book text regular)) {
+ if ( $seen{$series} ) {
+ print {$STY}
+ "\\DeclareOptionX{$series}{\\edef\\mdseries\@$ARGV{nfss}",
+ "{$series}}\n";
+ $default_regular = $series;
+ }
+ }
+ $defaults .= ",$default_regular" if $default_regular;
+
+ print {$STY} <<"END_STYLE_REST";
+\\ExecuteOptionsX{$defaults}
+\\ProcessOptionsX\\relax
+
+\\renewcommand*
+ {\\$ARGV{nfss}default}
+ {$fam-\\$fam\@figurealign\\$fam\@figurestyle}
+
+\\endinput
+END_STYLE_REST
+
+ close $STY;
+
+ return;
+}
+
+#-----------------------------------------------------------------------
+# Walk a nested dictionary, return lookup table with all keys
+#-----------------------------------------------------------------------
+sub get_keys {
+ my $dict = shift;
+ my $seen = shift || {};
+
+ while (my ($k, $v) = each %$dict) {
+ $seen->{$k} = 1;
+ get_keys($v, $seen) if ref $v eq 'HASH';
+ }
+
+ return $seen;
+}
+
+#-----------------------------------------------------------------------
+# Create a .fd file for NFSS
+#-----------------------------------------------------------------------
+sub write_fdfile {
+ my ($fam, $enc, $sty, $data) = @_;
+
+ my $fn = sprintf "%s%s-%s.fd", $enc, $fam, $sty;
+ my $dir = File::Spec->catdir(
+ $ARGV{target}, 'tex', 'latex', $ARGV{typeface} || $fam);
+ File::Path::make_path($dir);
+ $fn = File::Spec->catfile($dir, $fn);
+ open my $FD, '>', $fn
+ or die "[ERROR] Can't create '$fn': $!";
+
+ print {$FD} <<"END_FD_HEADER";
+%% Generated by autoinst on $TODAY
+%%
+\\ProvidesFile{${enc}${fam}-${sty}.fd}
+ [$TODAY (autoinst) Font definitions for ${enc}/${fam}-${sty}.]
+
+\\expandafter\\ifx\\csname ${fam}\@scale\\endcsname\\relax
+ \\let\\${fam}\@\@scale\\\@empty
+\\else
+ \\edef\\${fam}\@\@scale{s*[\\csname ${fam}\@scale\\endcsname]}%
+\\fi
+
+\\DeclareFontFamily{${enc}}{${fam}-${sty}}{@{[
+ $ARGV{nfss} eq 'tt' ? '\hyphenchar\font=-1' : ""
+]}}
+
+END_FD_HEADER
+
+ while (my ($series, $fdseries) = each %$data) {
+ print {$FD} "\n% ---- $series ----\n\n";
+ while (my ($shape, $fdshape) = each %$fdseries) {
+ print {$FD}
+ "\\DeclareFontShape{$enc}{${fam}-${sty}}{$series}{$shape}{\n";
+ my @sizes = sort { $a->[0] <=> $b->[0] }
+ @{$fdshape};
+ $sizes[0][0] = $sizes[-1][1] = '';
+ $sizes[$_][0] = $sizes[$_ - 1][1] for (1 .. $#sizes);
+ for my $size (@sizes) {
+ print {$FD} " <$size->[0]-$size->[1]> ",
+ "\\${fam}\@\@scale $size->[2]\n";
+ }
+ print {$FD} "}{}\n\n";
+ }
+
+ # ssub italic for missing slanted, or vice versa
+ while (my ($shape, $replace) = each %SSUB_SHAPE) {
+ if (!exists $fdseries->{$shape} && exists $fdseries->{$replace}) {
+ print {$FD} <<"END_SSUB_SHAPE";
+\\DeclareFontShape{$enc}{${fam}-${sty}}{$series}{$shape}{
+ <-> ssub * ${fam}-${sty}/${series}/${replace}
+}{}
+
+END_SSUB_SHAPE
+ $fdseries->{$shape} = 1;
+ }
+ }
+ }
+
+ print {$FD} <<"END_COMMENT";
+%
+% Extra 'ssub' rules to map the standard NFSS codes to our fancy names
+%
+END_COMMENT
+ my %seen;
+ NFSSWEIGHT:
+ for my $nfssweight (@NFSS_WEIGHT) {
+ NFSSWIDTH:
+ for my $nfsswidth (@NFSS_WIDTH) {
+ my $nfssseries = ( $nfssweight . $nfsswidth) || 'm';
+
+ for my $weight ( @{$NFSS_WEIGHT{$nfssweight}} ) {
+ $weight = '' if $weight eq 'regular';
+ for my $width ( @{$NFSS_WIDTH{$nfsswidth}} ) {
+ $width = '' if $width eq 'regular';
+ my $series = ( $weight . $width ) || 'regular';
+ if ( exists $data->{$series} ) {
+ print {$FD} "\n% $nfssseries --> $series\n\n";
+ for my $shape ( keys %{$data->{$series}} ) {
+ print {$FD} <<"END_SSUB_SERIES";
+\\DeclareFontShape{$enc}{${fam}-${sty}}{$nfssseries}{$shape}{
+ <-> ssub * ${fam}-${sty}/${series}/${shape}
+}{}
+
+END_SSUB_SERIES
+ $seen{$nfssseries}{$shape} = 1;
+ }
+ next NFSSWIDTH;
+ }
+ }
+ }
+ }
+ }
+
+ # Add ssub rules to map bx to b
+ for my $shape ( keys %{$seen{b}} ) {
+ if ( !exists $seen{bx}{$shape} ) {
+ print {$FD} <<"END_SSUB_BX";
+\\DeclareFontShape{$enc}{${fam}-${sty}}{bx}{$shape}{
+ <-> ssub * ${fam}-${sty}/b/${shape}
+}{}
+
+END_SSUB_BX
+ }
+ }
+
+ print {$FD} "\\endinput\n";
+ close $FD;
+
+ return;
+}
+
+
+############################################################################
+
+
+package main;
+
+main();
+
+
+__END__
+
+
+############################################################################
+
+
+ To create the documentation:
+
+ pod2man --center="Marc Penninga" --release="fontools" --section=1 \
+ autoinst - | groff -Tps -man - | ps2pdf - autoinst.pdf
+
+
+=pod
+
+=head1 NAME
+
+autoinst - wrapper around the F<LCDF TypeTools>,
+for installing and using OpenType fonts in LaTeX.
+
+
+=head1 SYNOPSIS
+
+B<autoinst> [I<options>] B<fontfile(s)>
+
+
+=head1 DESCRIPTION
+
+Eddie Kohler's I<LCDF TypeTools> are superb tools for installing
+OpenType fonts in LaTeX, but they can be hard to use:
+they need many, often long, command lines
+and don't generate the F<fd> and F<sty> files LaTeX needs.
+B<autoinst> simplifies the use of the I<TypeTools> for font installation
+by generating and executing all commands for I<otftotfm>
+and by creating and installing all necessary F<fd> and F<sty> files.
+
+Given a family of font files (in F<otf> or F<ttf> format),
+B<autoinst> will create several LaTeX font families:
+
+=over 2
+
+=over 3
+
+=item -
+
+Four text families (with lining and oldstyle digits,
+each in both tabular and proportional variants),
+all with the following shapes:
+
+=over 2
+
+=over 8
+
+=item I<n>
+
+Roman (i.e., upright) text
+
+=item I<it>, I<sl>
+
+Italic and slanted (sometimes called oblique) text
+
+=item I<sc>
+
+Small caps
+
+=item I<scit>, I<scsl>
+
+Italic and slanted small caps
+
+=item I<sw>
+
+Swash
+
+=item I<nw>
+
+"Upright swash"
+
+=back
+
+=back
+
+=item -
+
+For each T1-encoded text family:
+a family of TS1-encoded symbol fonts, in roman, italic and slanted shapes.
+
+=item -
+
+Families with superiors, inferiors, numerators and denominators,
+in roman, italic and slanted shapes.
+
+=item -
+
+Families with "Titling" characters;
+these "... replace the default glyphs
+with corresponding forms designed specifically for titling.
+These may be all-capital and/or larger on the body,
+and adjusted for viewing at larger sizes"
+(according to the OpenType Specification).
+
+=item -
+
+An ornament family, also in roman, italic and slanted shapes.
+
+=back
+
+=back
+
+Of course, if your fonts don't contain italics, oldstyle digits, small caps
+etc., the corresponding shapes and families are not created.
+In addition, the creation of most families and shapes can be controlled
+by the user (see L</"COMMAND-LINE OPTIONS"> below).
+
+These families use the I<FontPro> project's naming scheme:
+I<< <FontFamily>-<Suffix> >>, where I<< <Suffix> >> is:
+
+=over 8
+
+=item I<LF>
+
+proportional (i.e., figures have varying widths) lining figures
+
+=item I<TLF>
+
+tabular (i.e., all figures have the same width) lining figures
+
+=item I<OsF>
+
+proportional oldstyle figures
+
+=item I<TOsF>
+
+tabular oldstyle figures
+
+=item I<Sup>
+
+superior characters (note that most fonts have only an incomplete set of
+superior characters: digits, some punctuation and the letters I<abdeilmnorst>;
+normal forms are used for other characters)
+
+=item I<Inf>
+
+inferior characters; usually only digits and some punctuation,
+normal forms for other characters
+
+=item I<Titl>
+
+Titling characters; see above.
+
+=item I<Orn>
+
+ornaments
+
+=item I<Numr>
+
+numerators
+
+=item I<Dnom>
+
+denominators
+
+=back
+
+The individual fonts are named I<< <FontName>-<suffix>-<shape>-<enc> >>,
+where I<< <suffix> >> is the same as above (but in lowercase),
+I<< <shape> >> is either empty, "sc" or "swash",
+and I<< <enc> >> is the encoding (also in lowercase).
+A typical name in this scheme would be "FiraSans-Light-osf-sc-ly1".
+
+
+=head2 About the log file
+
+B<autoinst> writes some info about what it thinks it's doing to a log file.
+By default this is called F<< <fontfamily>.log >>,
+but this choice can be overridden by the user;
+see the I<-logfile> command-line option in L</"COMMAND-LINE OPTIONS"> below.
+If this log file already exists, B<autoinst> will append its data to the end
+rather than overwrite it.
+Use the I<-verbose> command-line option to ask for more detailed info.
+
+
+=head2 A note for MiKTeX users
+
+Automatically installing the fonts into a suitable TEXMF tree
+(as B<autoinst> tries to do by default) only works for TeX-installations
+that use the F<kpathsea> library; with TeX distributions that implement
+their own directory searching (such as MiKTeX), B<autoinst> will complain
+that it cannot find the F<kpsewhich> program and move all generated files
+into a subdirectory C<./autoinst_output/> of the current directory.
+If you use such a TeX distribution, you should either move these files
+to their correct destinations by hand, or use the I<-target> option
+(see L</"COMMAND-LINE OPTIONS"> below) to manually specify a TEXMF tree.
+
+Also, some OpenType fonts contain so many kerning pairs that the resulting
+F<pl> and F<vpl> files are too big for MiKTeX's F<pltotf> and F<vptovf>;
+the versions that come with W32TeX (F<http://www.w32tex.org>)
+and TeXLive (F<http://tug.org/texlive>) don't seem to have this problem.
+
+
+=head2 A note for MacTeX users
+
+By default, B<autoinst> will try to install all generated files into
+the $TEXMFLOCAL tree; when this directory isn't user-writable,
+it will use the $TEXMFHOME tree instead. Unfortunately, MacTeX's version
+of C<updmap-sys> (which is called behind the scenes) doesn't search
+in $TEXMFHOME, and hence MacTeX will not find the new fonts.
+
+To remedy this, either run B<autoinst> as root (so that it can install
+everything into $TEXMFLOCAL) or manually run C<updmap -user> to tell
+TeX about the files in $TEXMFHOME.
+The latter option does, however, have some caveats;
+see F<https://tug.org/texlive/scripts-sys-user.html>.
+
+
+=head2 Using the fonts in your LaTeX documents
+
+B<autoinst> generates a style file for using the fonts in LaTeX documents,
+named F<< <FontFamily>.sty >>. This style file also takes care of loading
+the F<fontenc> and F<textcomp> packages.
+To use the fonts, add the command C<<< \usepackage{I<< <FontFamily> >>} >>>
+to the preamble of your document.
+
+This style file defines a number of options:
+
+=over 4
+
+=item C<mainfont>
+
+Redefine C<\familydefault> to make this font the main font
+for the document.
+This is a no-op if the font is installed as a serif font;
+but if the font is installed as a sanserif or typewriter font,
+this option saves you from having to redefine C<\familydefault>
+yourself.
+
+=item C<lining>, C<oldstyle>, C<tabular>, C<proportional>
+
+Choose which figure style to use.
+The defaults are "oldstyle" and "proportional" (if available).
+
+=item C<<< scale=I<< <number> >> >>>
+
+Scale the font by a factor of I<< <number> >>.
+E.g., to increase the size of the font by 5%, use
+C<<< \usepackage[scale=1.05]{I<< <FontFamily> >>} >>>.
+May also be spelled C<scaled>.
+
+This option is only available when you have the F<xkeyval> package installed.
+
+=item C<medium>, C<book>, C<text>, C<regular>
+
+Select the weight that LaTeX will use as the "regular" weight;
+the default is C<regular>.
+
+=item C<heavy>, C<black>, C<extrabold>, C<demibold>, C<semibold>, C<bold>
+
+Select the weight that LaTeX will use as the "bold" weight;
+the default is C<bold>.
+
+=back
+
+The previous two groups of options will only work if
+you have the F<mweights> package installed.
+
+The style file will also try to load the F<fontaxes> package
+(on CTAN), which gives easy access to various font shapes and styles.
+Using the machinery set up by F<fontaxes>, the generated style file
+defines a number of commands (which take the text to be typeset as argument)
+and declarations (which don't take arguments, but affect all text up to
+the end of the current group) to access titling, superior and inferior
+characters:
+
+
+ DECLARATION COMMAND SHORT FORM OF COMMAND
+
+ \tlshape \texttitling \texttl
+ \sufigures \textsuperior \textsu
+ \infigures \textinferior \textin
+
+
+In addition, the C<\swshape> and C<\textsw> commands are redefined to place
+swash on F<fontaxes>' secondary shape axis (F<fontaxes> places it on the
+primary shape axis) to make them behave properly when nested, so that
+C<\swshape\upshape> will give upright swash.
+
+There are no commands for accessing the numerator and denominator
+fonts; these can be selected using F<fontaxes>' standard commands,
+e.g., C<\fontfigurestyle{numerator}\selectfont>.
+
+The style file also provides a command C<<< \ornament{I<< <number> >>} >>>,
+where C<<< I<< <number> >> >>> is a number from 0 to the total number of
+ornaments minus one. Ornaments are always typeset using the current family,
+series and shape. A list of all ornaments in a font can be created by
+running LaTeX on the file F<nfssfont.tex> (part of a standard
+LaTeX installation) and supplying the name of the ornament font.
+
+To access ornament glyphs, B<autoinst> creates a font-specific encoding file
+F<< <FontFamily>_orn.enc >>,
+but only if that file doesn't yet exist in the current directory.
+This is a deliberate feature that allows you to provide your own encoding
+vector, e.g. if your fonts use non-standard glyph names for ornaments.
+
+These commands are only generated for existing shapes and number styles;
+no commands are generated for shapes and styles that don't exist,
+or whose generation was turned off by the user.
+Also these commands are built on top of F<fontaxes>, so if that package
+cannot be found, you're limited to using the lower-level commands from
+standard NFSS (C<\fontfamily>, C<\fontseries>, C<\fontshape> etc.).
+
+
+=head2 On the choice of text encoding
+
+By default, B<autoinst> generates text fonts with OT1, T1 and LY1
+encodings, and the generated style files use LY1 as the default text encoding.
+LY1 has been chosen over T1 because it has some empty slots to accommodate
+the additional ligatures found in many OpenType fonts.
+Other encodings can be chosen using the I<-encoding> option
+(see L</"COMMAND-LINE OPTIONS"> below).
+
+
+=head2 NFSS codes
+
+LaTeX's New Font Selection System (NFSS)
+identifies fonts by a combination of family,
+series (the concatenation of weight and width), shape and size.
+B<autoinst> parses the font's metadata
+(more precisely: the output of C<otfinfo --info>)
+to determine these parameters.
+When this fails (usually because the font family contains uncommon weights,
+widths or shapes),
+B<autoinst> ends up with different fonts having the I<same> values
+for these font parameters; such fonts cannot be used in NFSS,
+since there's no way distinguish them.
+When B<autoinst> detects such a situation, it will print an error message
+and abort.
+If that happens, either rerun B<autoinst> on a smaller set of fonts,
+or add the missing widths, weights and shapes to the tables C<NFSS_WIDTH>,
+C<NFSS_WEIGHT> and C<NFSS_SHAPE>, near the top of the source code.
+Please also send a bug report (see L<AUTHOR> below).
+
+The mapping of shapes to NFSS codes is done using the following table:
+
+ SHAPE CODE
+ -------------------------------- ----
+ Roman, Upright n
+ Italic, Cursive, Kursive it
+ Oblique, Slant(ed), Incline(d) sl
+
+(I<Exception:> Adobe Silentium Pro contains two Roman shapes;
+we map the first of these to "n", for the second one we (ab)use the "it" code
+as this family doesn't contain an Italic shape.)
+
+The mapping of weights and widths to NFSS codes is a more complex,
+two-step proces.
+In the first step, all fonts are assigned a "series" name that is simply
+the concatenation of its weight and width
+(after expanding any abbreviations and converting to lowercase).
+A font with "Cond" width and "Ultra" weight will then be known
+as "ultrablackcondensed".
+
+In the second step, B<autoinst> tries to map all combinations of NFSS codes
+(ul, el, l, sl, m, sb, b, eb and ub for weights;
+uc, ec, c, sc, m, sx, x, ex and ux for widths) to actual fonts.
+Of course, not all 81 combinations of these NFSS weights and widths will map
+to existing fonts;
+and conversely it may not be possible to assign every existing font
+a unique code in a sane way (especially for the weights, some font families
+offer more choices or finer granularity than NFSS's codes can handle;
+e.g., Fira Sans contains fifteen(!) different weights,
+including an additional "Medium" weight between Regular and Semibold).
+
+B<autoinst> tries hard to ensure that the most common NFSS codes
+(and high-level commands such as C<\bfseries>,
+which are built on top of those codes) will "just work".
+
+To see exactly which NFSS codes map to which fonts, see the log file
+(pro tip: run B<autoinst> with the I<-dryrun> option
+to check the chosen mapping beforehand).
+The I<-nfssweight> and I<-nfsswidth> command-line options can be used
+to finetune the mapping between NFSS codes and fonts.
+
+To access specific weights or widths,
+one can always use the C<\fontseries> command with the full series name
+(i.e., C<\fontseries{demibold}\selectfont>).
+
+
+=head1 COMMAND-LINE OPTIONS
+
+B<autoinst> tries hard to do The Right Thing (TM) by default,
+so you usually won't really need these options;
+but most aspects of its operation can be fine-tuned if you want to.
+
+You may use either one or two dashes before options,
+and option names may be shortened to a unique prefix
+(e.g., B<-encoding> may be abbreviated to B<-enc> or even B<-en>,
+but B<-e> is ambiguous (it may mean either B<-encoding> or B<-extra>)).
+
+
+=over 4
+
+=item B<-version>
+
+Print B<autoinst>'s version number and exit.
+
+=item B<-help>
+
+Print a (relatively) short help text and exit.
+
+=item B<-dryrun>
+
+Don't generate output; just parse input fonts and write
+a log file saying what B<autoinst> would have done.
+
+=item B<-logfile>=I<filename>
+
+Write log data to F<filename> instead of the default F<< <fontfamily>.log >>.
+If the file already exists, B<autoinst> appends to it;
+it doesn't overwrite an existing file.
+
+=item B<-verbose>
+
+Add more details to the log file. Repeat this option for even more info.
+
+=item B<-encoding>=I<encoding[,encoding]>
+
+Generate the specified encoding(s) for the text fonts.
+Multiple encodings may be specified as a comma-separated list:
+C<-encoding=OT1,T1,LY1> (without spaces!).
+The style file passes these to F<fontenc> in the specified order,
+so the I<last> one will become the default text encoding of your document.
+
+The default choice of encodings is "OT1,T1,LY1".
+For each encoding, a file F<< <encoding>.enc >> (in all I<lowercase>!)
+should be somewhere where F<otftotfm> can find it. Suitable encoding files
+for OT1, T1/TS1, LY1, LGR, T2A/B/C and T3/TS3 come with B<autoinst>.
+(These files are called F<fontools_ot1.enc> etc. to avoid name clashes
+with other packages; the "fontools_" prefix may be omitted.)
+
+=item B<-ts1>/B<-nots1>
+
+Control the creation of TS1-encoded fonts. The default is B<-ts1>
+if the text encodings (see I<-encoding> above) include T1,
+B<-nots1> otherwise.
+
+=item B<-serif>/B<-sanserif>/B<-typewriter>
+
+Install the font as a serif, sanserif or typewriter font, respectively.
+This changes how you access the font in LaTeX:
+with C<\rmfamily>/C<\textrm>, C<\sffamily>/C<\textsf>
+or C<\ttfamily>/C<\texttt>.
+
+Installing the font as a typewriter font will cause two further changes:
+it will - by default - turn off the use of f-ligatures
+(though this can be overridden with the I<-ligatures> option),
+and it will disable hyphenation for this font.
+This latter effect cannot be disabled in B<autoinst>;
+if you want typewriter text to be hyphenated, use the F<hyphenat> package.
+
+If none of these options is specified, B<autoinst> tries to guess:
+if the font's filename contains the string "mono"
+or if the field C<isFixedPitch> in the font's I<post> table is True,
+it will select B<-typewriter>;
+else if the filename contains "sans" it selects B<-sanserif>;
+and otherwise it will opt for B<-serif>.
+
+=item B<-lining>/B<-nolining>
+
+Control the creation of fonts with lining figures. The default is
+B<-lining>.
+
+=item B<-oldstyle>/B<-nooldstyle>
+
+Control the creation of fonts with oldstyle figures. The default is
+B<-oldstyle>.
+
+=item B<-proportional>/B<-noproportional>
+
+Control the creation of fonts with proportional figures. The default is
+B<-proportional>.
+
+=item B<-tabular>/B<-notabular>
+
+Control the creation of fonts with tabular figures. The default is
+B<-tabular>.
+
+=item B<-smallcaps>/B<-nosmallcaps>
+
+Control the creation of small caps fonts. The default is
+B<-smallcaps>.
+
+=item B<-swash>/B<-noswash>
+
+Control the creation of swash fonts. The default is B<-swash>.
+
+=item B<-titling>/B<-notitling>
+
+Control the creation of titling families. The default is B<-titling>.
+
+=item B<-superiors>/B<-nosuperiors>
+
+Control the creation of fonts with superior characters.
+The default is B<-superiors>.
+
+=item B<-noinferiors>
+
+=item B<-inferiors> [= B<none> | B<auto> | B<subs> | B<sinf> | B<dnom> ]
+
+The OpenType standard defines several kinds of digits that might be used
+as inferiors or subscripts: "Subscripts" (OpenType feature "subs"),
+"Scientific Inferiors" ("sinf"), and "Denominators" ("dnom").
+This option allows the user to determine which of these styles B<autoinst>
+should use for the inferior characters.
+Alternatively, the value "auto" tells B<autoinst> to use the first value
+in "subs", "sinf" or "dnom" that is supported by the font.
+Saying just B<-inferiors> is equivalent to B<-inferiors=auto>;
+otherwise the default is B<-noinferiors>.
+
+I<< If you specify a style of inferiors that isn't present in the font,
+B<autoinst> will fall back to its default behaviour of not creating fonts
+with inferiors at all; it won't try to substitute one of the other styles. >>
+
+=item B<-fractions>/B<-nofractions>
+
+Control the creation of fonts with numerators and denominators.
+The default is B<-nofractions>.
+
+=item B<-ligatures>/B<-noligatures>
+
+Some fonts create glyphs for the standard f-ligatures (ff, fi, fl, ffi, ffl),
+but don't provide a "liga" feature to access these.
+This option tells B<autoinst> to add extra C<LIGKERN> rules to
+the generated fonts to enable the use of these ligatures.
+The default is B<-ligatures>,
+unless the user specified the I<ligatures> option.
+
+Specify B<-noligatures> to disable the generation of ligatures even for fonts
+that do contain a "liga" feature.
+
+=item B<-defaultlining>/B<-defaultoldstyle>
+
+=item B<-defaulttabular>/B<-defaultproportional>
+
+Tell B<autoinst> which figure style is the current font family's default
+(i.e., which figures you get when you don't specify any OpenType features).
+
+I<Don't use these options unless you are certain you need them!>
+They are only needed for fonts that don't provide OpenType features
+for their default figure style; and even in that case,
+B<autoinst>'s default values (B<-defaultlining> and B<-defaulttabular>)
+are usually correct.
+
+=item B<-nofigurekern>
+
+Some fonts provide kerning pairs for tabular figures.
+This is very probably not what you want
+(e.g., numbers in tables won't line up exactly).
+This option adds extra I< --ligkern> options
+to the commands for I<otftotfm> to suppress such kerns.
+Note that this option leads to very long commands (it adds
+one hundred I< --ligkern> options), which may cause problems on some systems.
+
+=item B<-mergewidths>/B<-nomergewidths>,
+B<-mergeweights>/B<-nomergeweights>,
+B<-mergeshapes>/B<-nomergeshapes>
+
+Some font put different widths, weights or shapes (e.g., small caps)
+in separate families.
+These options tell B<autoinst> to merge those separate families into
+the main family.
+Since this is usually desirable, they are all enabled by default.
+
+In earlier versions, B<-mergeshapes> was called B<-mergesmallcaps>;
+for reasons of backward compatibility, that option is still supported.
+
+=item B<-nfssweight>=I<code>=I<weight>, B<-nfsswidth>=I<code>=I<width>
+
+Map the NFSS code I<code> to the given weight or width,
+overriding the built-in tables.
+Each of these options may be given multiple times,
+to override more than one NFSS code.
+Example: to map the "ul" code to the "Thin" weight,
+use C<-nfssweight=ul=thin>.
+To inhibit the use of the "ul" code completely,
+use C<-nfssweight=ul=>.
+
+=item B<-extra>=I<text>
+
+Append I<text> as extra options to the command lines for I<otftotfm>.
+To prevent I<text> from accidentily being interpreted as options
+to B<autoinst>, it should be properly quoted.
+
+=item B<-manual>
+
+Manual mode; for users who want to post-process the generated files
+and commands. By default, B<autoinst> immediately executes all
+F<otftotfm> commands it generates;
+in manual mode, these are instead written to a file F<autoinst.bat>.
+Furthermore it tells F<otftotfm> to generate human readable (and editable)
+F<pl/vpl> files instead of the default F<tfm/vf> ones,
+and to place all generated files in a subdirectory C<./autoinst_output/>
+of the current directory, rather than install them into your TeX installation.
+
+When using this option, you need to execute the following manual steps after
+B<autoinst> has finished:
+
+=over 2
+
+=item - run F<pltotf> and F<vptovf> on the generated F<pl> and F<vf> files,
+to convert them to F<tfm/vf> format;
+
+=item - move all generated files to a proper TEXMF tree,
+and, if necessary, update the filename database;
+
+=item - tell TeX about the new F<map> file
+(usually by running C<updmap> or similar).
+
+=back
+
+Note that some options (I<-target>, I<-vendor> and I<-typeface>,
+I<-[no]updmap>) are meaningless, and hence ignored, in manual mode.
+
+=item B<-target>=I<DIRECTORY>
+
+Install all generated files into the TEXMF tree at I<DIRECTORY>.
+
+By default, B<autoinst> searches the $TEXMFLOCAL and $TEXMFHOME trees
+and installs all files into the first user-writable TEXMF tree it finds.
+If B<autoinst> cannot find such a user-writable directory
+(which shouldn't happen, since $TEXMFHOME is supposed to be user-writable)
+it will print a warning message and put all files into the subdirectory
+C<./autoinst_output/> of the current directory.
+It's then up to the user to move the generated files to a better location
+and update all relevant databases
+(usually by calling F<texhash> and F<updmap>).
+
+=item B<-vendor>=I<VENDOR>
+
+=item B<-typeface>=I<TYPEFACE>
+
+These options are equivalent to F<otftotfm>'s I< --vendor> and I< --typeface>
+options: they change the "vendor" and "typeface" parts of the names of the
+subdirectories in the TEXMF tree where generated files will be stored.
+The default values are "lcdftools" and the font's FontFamily name.
+
+Note that these options change I<only> directory names,
+not the names of any generated files.
+
+=item B<-updmap>/B<-noupdmap>
+
+Control whether or not F<updmap> is called after the last call to F<otftotfm>.
+The default is B<-updmap>.
+
+=back
+
+
+=head1 SEE ALSO
+
+Eddie Kohler's B<TypeTools> (F<http://www.lcdf.org/type>).
+
+B<Perl> can be obtained from F<http://www.perl.org>;
+it is included in most Linux distributions.
+For Windows, try ActivePerl (F<http://www.activestate.com>)
+or Strawberry Perl (F<http://strawberryperl.com>).
+
+B<XeTeX> (F<http://www.tug.org/xetex>) and
+B<LuaTeX> (F<http://www.luatex.org>)
+are Unicode-aware TeX engines that can use OpenType fonts directly,
+without any (La)TeX-specific support files.
+
+The B<FontPro> project (F<https://github.com/sebschub/FontPro>)
+offers very complete LaTeX support (even for typesetting maths) for
+Adobe's Minion Pro, Myriad Pro and Cronos Pro font families.
+
+
+=head1 AUTHOR
+
+Marc Penninga (F<marcpenninga@gmail.com>)
+
+When sending a bug report, please give as much relevant information as
+possible; this usually includes (but may not be limited to) the log file
+(please add the I<-verbose> command-line option, for extra info).
+If you see any error messages, please include these I<verbatim>;
+don't paraphase.
+
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005-2019 Marc Penninga.
+
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published
+by the Free Software Foundation, either version 2 of the License,
+or (at your option) any later version.
+A copy of the text of the GNU General Public License is included in
+the I<fontools> distribution; see the file F<GPLv2.txt>.
+
+
+=head1 DISCLAIMER
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+
+=head1 VERSION
+
+This document describes B<autoinst> version 20190712.
+
+
+=head1 RECENT CHANGES
+
+(See the source for the full story, all the way back to 2005.)
+
+=over 12
+
+=item I<2019-07-12>
+
+Replaced single quotes in calls to F<otfinfo> with double quotes,
+as they caused problems on Windows 10.
+
+=item I<2019-06-25>
+
+=over 3
+
+=item -
+
+Added the I<-mergeweights> and I<-mergeshapes> options,
+and improved I<-mergewidths>.
+
+=item -
+
+Improved the parsing of fonts' widths and weights.
+
+=item -
+
+Improved the mapping of widths and weights to NFSS codes.
+
+=item -
+
+Changed logging code so that that results of font info parsing
+are always logged, even (especially!) when parsing fails.
+
+=item -
+
+Added a warning when installing fonts from multiple families.
+
+=item -
+
+Added simple recognition for sanserif and typewriter fonts.
+
+=item -
+
+Fixed error checking after calls to F<otfinfo>
+(B<autoinst> previously only checked whether C<fork()> was successful,
+not whether the actual call to F<otfinfo> worked).
+
+=item -
+
+Fixed a bug in the I<-inferiors> option;
+when used without a (supposedly optional) value,
+it would silently gobble the next option instead.
+
+=back
+
+=item I<2019-05-22>
+
+Added the I<mainfont> option to the generated F<sty> files.
+Prevented hyphenation for typewriter fonts
+(added C<\hyphenchar\font=-1> to the C<\DeclareFontFamily> declarations).
+Added the I<-version> option.
+
+=item I<2019-05-17>
+
+Changed the way the F<-ligatures> option works:
+F<-ligatures> enables f-ligatures (even without a "liga" feature),
+F<-noligatures> now disables f-ligatures (overriding a "liga" feature).
+
+=item I<2019-05-11>
+
+Separate small caps families are now also recognised when the family name
+ends with "SC" (previously B<autoinst> only looked for "SmallCaps").
+
+=item I<2019-04-22>
+
+Fixed a bug in the generation of swash shapes.
+
+=item I<2019-04-19>
+
+Fixed a bug that affected -mergesmallcaps with multiple encodings.
+
+=item I<2019-04-16>
+
+Added the <-mergesmallcaps> option, to handle cases where
+the small caps fonts are in separate font families.
+Titling shape is now treated as a separate family instead of a distinct shape;
+it is generated only for fonts with the 'titl' feature.
+Only add f-ligatures to fonts when explicitly asked to (I<-ligatures>).
+
+=item I<2019-04-11>
+
+Tried to make the log file more relevant.
+Added the I<-nfssweight> and I<-nfsswidth> options,
+and finetuned the automatic mapping between fonts and NFSS codes.
+Changed the name of the generated log file to F<< <fontfamily>.log >>,
+and revived the I<-logfile> option to allow overriding this choice.
+Made I<-mergewidths> the default (instead of I<-nomergewidths>).
+
+=item I<2019-04-01>
+
+Fine-tuned the decision where to put generated files;
+in particular, create $TEXMFHOME if it doesn't already exist
+and $TEXMFLOCAL isn't user-writable.
+
+In manual mode, or when we can't find a user-writable TEXMF tree,
+put all generated files into a subdirectory C<./autoinst_output/>
+instead of all over the current working directory.
+
+Added "auto" value to the I<inferiors> option,
+to tell B<autoinst> to use whatever inferior characters are available.
+
+=item I<2019-03-14>
+
+Overhauled the mapping of fonts (more specifically of weights and widths;
+the mapping of shapes didn't change) to NFSS codes.
+Instead of inventing our own codes to deal with every possible weight
+and width out there, we now create "long" codes based on the names
+in the font metadata.
+Then we add "ssub" rules to the F<fd> files to map the standard NFSS codes
+to our fancy names (see the section B<NFSS codes>;
+based on discussions with Frank Mittelbach and Bob Tennent).
+
+=back
+
+
+=begin Really_old_history
+
+=over 12
+
+=item I<2018-08-10>
+
+Added encoding files for LGR and T2A/B/C to I<fontools>.
+
+=item I<2018-03-26>
+
+Added the I<-(no)mergewidths> option; tried to improve the documentation.
+
+=item I<2018-03-26>
+
+Added the "Text" weight and the I<-(no)mergewidths> option.
+Changed the NFSS codes for "Thin" and "Book" to "i" and "o", respectively.
+Tried to improve the documentation.
+
+=item I<2018-01-09>
+
+Added the "sl" weight for font families (such as Fira Sans) that contain both
+"Book" and "Regular" weights (reported by Bob Tennent).
+Added the "Two", "Four", "Eight" and "Hair" weights (for Fira Sans).
+
+=item I<2017-06-16>
+
+Changed the I<-inferiors> option from a binary yes-or-no choice to allow
+the user to choose one of the "subs", "sinf" and "dnom" features.
+B<autoinst> now always creates a log file.
+
+=item I<2017-03-21>
+
+Updated the F<fontools_ot1.enc> encoding file to include the "Lslash"
+and "lslash" glyphs (thanks to Bob Tennent).
+
+=item I<2015-11-22>
+
+Bugfix: LaTeX doesn't like command names with dashes in it.
+
+=item I<2015-05-13>
+
+Fixed an error message that mixed up width and weight.
+
+=item I<2014-04-04>
+
+Fixed a bug in the font info parsing code.
+
+=item I<2014-01-21>
+
+"Oblique" or "slanted" fonts are now mapped to NFSS code "sl" instead
+of "it"; added "ssub" rules to the F<fd> files to substitute slanted fonts
+for italic ones if the latter are missing. Fixed a few bugs.
+
+=item I<2014-01-03>
+
+Added the I<-dryrun> and I<-logfile> options; changed which info is logged.
+Added the I<-lining>, I<-oldstyle>, I<-tabular> and I<-proportional>
+options; the old options with those names have been renamed to
+I<-defaultlining>, I<-defaultoldstyle> etc.
+
+=item I<2013-10-31>
+
+The previous change required Perl v5.14 or newer;
+now it also works with older versions.
+
+=item I<2013-10-01>
+
+Added the I<-lining>, I<-oldstyle>, I<-tabular> and I<-proportional>
+command line options.
+
+=item I<2013-07-25>
+
+The generated F<sty> files now use the I<mweights> package instead of
+redefining C<\mddefault> and C<\bfdefault>.
+Added C<scale> as an alias for the package option C<scaled>.
+
+=item I<2013-02-06>
+
+Bugfix: the directory names for map and encoding files contained
+the "vendor" instead of the "typeface".
+
+=item I<2013-01-03>
+
+Added extra "ssub" rules to the F<fd> files that substitute "b" for "bx".
+Verbose mode now also prints all generated commands before they're executed.
+
+=item I<2012-10-25>
+
+Added extra "ssub" rules to the F<fd> files that substitute italic
+shapes for slanted ones.
+
+=item I<2012-09-25>
+
+Added the I<-vendor>, I<-typeface> and I<-(no)updmap> command line options.
+
+=item I<2012-07-06>
+
+Documentation update.
+
+=item I<2012-03-06>
+
+Implemented the "splitting the font family into multiple subfamilies"
+emergency strategy when font info parsing fails.
+Added recognition for a number of unusual widths, weights and shapes.
+
+=item I<2012-02-29>
+
+Fixed a bug in the font parsing code,
+where possible widths, weights and shapes where tested in the wrong order;
+this led to "ExtraLight" fonts being recognised as "Light".
+Added recognition for "Narrow" and "Wide" widths.
+Also added the I<-(no)figurekern> command-line option.
+
+=item I<2012-02-01>
+
+Reorganised the code, and fixed some bugs in the process.
+Added the I<-target> command-line option.
+Made B<autoinst> install the F<fd> and F<sty> files in
+the same TEXMF tree as the other generated files.
+Generate OT1, T1 and LY1 encoded text fonts by default.
+Made I<-titling> a default option (instead of I<-notitling>).
+Updated the documentation.
+
+=item I<2011-06-15>
+
+Fixed the font info parsing code for some fonts that are
+too lazy to spell out "Italic" in full.
+
+=item I<2010-04-29>
+
+The I< --feature=kern> option is only used for fonts that
+contain either a I<kern> feature or a I<kern> table.
+Font feature selection commands in the F<sty> file are only
+generated for shapes and figure styles that are supported
+by the current font, and whose generation has not been
+turned off using the command-line options.
+Fixed the font info parsing to work with the Condensed fonts
+in the Minion Pro family.
+
+=item I<2010-04-23>
+
+Always provide the I< --feature=kern> option to F<otftotfm>,
+even if the font doesn't have a I<kern> feature;
+this will make F<otftotfm> use the I<kern> table if present.
+For fonts without a I<liga> feature, add I< --ligkern>
+options for the common f-ligatures to the F<otftotfm> command line,
+so that any ligatures present in the font will still be used.
+Bug fix: the generated F<sty> files now work for font families
+with names containing digits.
+
+=item I<2009-04-09>
+
+Prefixed the filenames of the included encoding files with
+I<fontools_>, to prevent name clashes with other packages.
+
+=item I<2009-04-06>
+
+A small patch to the C<get_orn> subroutine:
+it now also recognises the I<bullet.xxx> ornament glyphs
+in Adobe Kepler Pro.
+
+=item I<2007-08-07>
+
+Fixed a small bug with filename quoting on Windows.
+
+=item I<2007-07-31>
+
+Changed the tables that map weights and widths to NFSS codes:
+in some extended families, different combinations of
+weight and width were mapped to the same series.
+Added a work-around for incorrect size info in some Kepler fonts.
+Fixed a small bug in the generated commands for otftotfm
+(sometimes, the "onum" feature was included twice).
+Added encoding file for OT1 to the I<fontools> collection.
+
+=item I<2007-07-27>
+
+Two bugfixes: a closing brace was missing in the generated style file,
+and the NFSS series was formed as "width plus weight" instead of the reverse.
+
+=item I<2007-06-10>
+
+Bugfix: silently replacing \DeclareOption, \ProcessOptions and
+\ExecuteOptions with their counterparts from the xkeyval package
+caused problems for some other packages.
+
+=item I<2007-06-04>
+
+Added the I< --no-updmap> option to all generated commands for F<otftotfm>
+(except the last); this should yield a significant speed-up for large
+families (suggested by Steven E. Harris).
+Tweaked the font info parsing to work around a bug in the naming of
+some FontFont fonts, where every font is in a family of its own.
+Added the "scaled" option (including the loading of F<xkeyval>)
+to the generated style file.
+Extended the output of the I<-verbose> option.
+
+=item I<2007-02-08>
+
+Yet Another Complete Rewrite.
+The code is now much more readable and more flexible;
+the program can now handle fonts from different families,
+as well as multiple text encodings.
+Rewrote the font info parsing code to work for Arno Pro.
+
+=item I<2006-10-11>
+
+The program determines the fonts' weights, widths and shapes by parsing
+the output from C<otfinfo --info> instead of the font filename.
+This should make B<autoinst> work for non-Adobe fonts.
+Filenames with spaces now work as well.
+
+=item I<2006-08-31>
+
+Made the generated style files try to include "fontaxes.sty";
+changed the names of the generated fonts and families
+(to make the previous change possible);
+added command-line options for most font styles and shapes;
+tweaked the filename parsing code for Cronos Pro and Gill Sans Pro;
+added runtime generation of encoding vectors for ornament fonts
+(because GaramondPremier's ornament names differ from other fonts);
+changed the NFSS-code for italic small caps and titling to "scit" and "tlit"
+(to work with F<fontaxes>);
+and edited (and hopefully improved) the documentation.
+
+=item I<2005-10-03>
+
+When creating LY1, T1, OT1 or TS1 encoded fonts, the I<-coding-scheme>
+option is added to the commands for F<otftotfm>; this should make the
+generated F<pl> and F<vpl> files acceptable to I<fontinst>.
+Also elaborated the documentation somewhat and fixed a small bug.
+
+=item I<2005-09-22>
+
+Added check to see if filename parsing succeeded;
+updated the filename parsing code to cater for GaramondPremier Pro,
+Silentium Pro and some non-Adobe fonts;
+added the I<-sanserif> and I<-typewriter> options and hacked the
+style files to support using several different font families in one document.
+
+=item I<2005-09-12>
+
+Cleaned up the code (it now runs under the F<strict> and F<warnings> pragmas);
+fixed a (rather obscure) bug that occurred when creating TS1-encoded
+fonts for families with multiple optical masters and oldstyle digits;
+added the I<medium, semibold> etc. options to the style file;
+and improved the layout of the generated files.
+
+=item I<2005-08-11>
+
+The generated commands weren't actually executed, only printed...
+Also added a small hack to cater for fonts
+(such as some recent versions of MinionPro)
+that contain swash characters but don't provide a "swsh" feature.
+
+=item I<2005-08-10>
+
+Dropped the "fontname" scheme in favor of a more verbose naming scheme,
+since many filenames were still more than eight characters long anyway.
+Added F<nfssext.sty>-like commands to the generated style file.
+Changed the default encoding to LY1 and added the "inferior" shape.
+
+=item I<2005-08-01>
+
+Rewrote (and hopefully improved) the user interface;
+changed the program to by default execute the generated F<otftotfm> command
+lines rather than writing them to a file;
+added automatic determination of the "fontname" code for the font family;
+changed the NFSS code for italic small caps to "si"; added titling shapes;
+changed the generated style
+file to include an interface for the ornaments and to load Lehman's NFSS
+extensions F<nfssext.sty> if this is installed; corrected the "fontname" codes
+for OT1, T1, LY1 and user-specific encodings; extended the output generated by
+the I<-verbose> option; and rewrote and extended the documentation.
+
+=item I<2005-06-16>
+
+Did some more finetuning to the filename-parsing code.
+
+=item I<2005-05-31>
+
+Generate correct fontname for OT1-encoded fonts.
+
+=item I<2005-05-18>
+
+Tried to make the filename-parsing code a bit more robust by adding several
+weights and widths; changed the error that's displayed when filename parsing
+fails; commented the code.
+
+=item I<2005-04-29>
+
+Rewrote large parts of the code (yes it I<was> even worse).
+
+=item I<2005-04-18>
+
+Changed default text-encoding to T1, added TS1.
+
+=item I<2005-03-29>
+
+Added support for font families with multiple widths.
+
+=item I<2005-03-15>
+
+First version.
+
+=back
+
+=end Really_old_history