diff options
Diffstat (limited to 'fonts/utilities/fontools/bin/autoinst')
-rwxr-xr-x | fonts/utilities/fontools/bin/autoinst | 3220 |
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 |