#! /usr/bin/env perl

################################################
# Generate a README file for the Comprehensive #
# LaTeX Symbol List.                           #
#                                              #
# Author: Scott Pakin <scott+clsl@pakin.org>   #
################################################

use feature qw(fc);
use POSIX;
use warnings;
use strict;

###########################################################################

# Center a string within a given field length.
sub center_string ($$)
{
    my ($string, $fieldlen) = @_;
    my $prespaces = int (($fieldlen - length($string)) / 2);
    my $postspaces = $fieldlen - length($string) - $prespaces;

    return (" " x $prespaces) . $string . (" " x $postspaces);
}


# Format a list to a given number of columns.
sub format_list ($$@)
{
    my ($numcols, $textwidth, @list) = @_;
    my $itemwidth = int (($textwidth-4) / $numcols);

    my $col = 0;
    foreach my $item (@list) {
        print "    " if $col%$numcols==0;
        printf "%-${itemwidth}.${itemwidth}s", $item;
        print "\n" if ++$col%$numcols==0;
    }
    print "\n";
}


# Return the human-readable name of a PostScript font file.
sub find_pfb_name ($)
{
    my $pfb = $_[0];
    my $pfbfile = `kpsewhich $pfb`;
    die "${0}: Failed to locate $pfb\n" if !defined $pfbfile;
    chomp $pfbfile;
    local $/ = undef;
    open (PFBFILE, "<$pfbfile") || die "open(\"$pfbfile\"): $!\n";
    my $entirefile = <PFBFILE>;
    close PFBFILE or die "close(\"$pfbfile\"): $!\n";
    my $fullname;
    if ($entirefile =~ /FullName\s*\(([^\)]*)\)/) {
        # Best choice: FullName
        $fullname = $1;
        $fullname =~ s/\\(\d\d\d)/chr(oct($1))/ge;
        $fullname =~ s/^\s+//;
        $fullname =~ s/\s+$//;
    }
    elsif ($entirefile =~ m,FontName\s*/(\S+),) {
        # Tolerable: FontName
        $fullname = $1;
    }
    else {
        die "${pfbfile}: FullName not found\n";
    }
    return $fullname;
}


# Return the human-readable name of an OpenType or TrueType font file.
sub find_otf_name ($)
{
    my $otf = $_[0];
    my $otffile = `kpsewhich $otf`;
    die "${0}: Failed to locate $otf\n" if !defined $otffile;
    chomp $otffile;
    my %key_value;
    local $/ = "\n";
    open(OTFFILE, "otfinfo --info $otffile|") || die "open(\"$otffile\"): $!\n";
    while (my $ln = <OTFFILE>) {
        chomp $ln;
        next if $ln !~ /^([^:]+):\s*(.*)$/;
        $key_value{$1} = $2;
    }
    close OTFFILE or die "close(\"$otffile\"): $!\n";
    foreach my $key ("Full name",
                     "Preferred family",
                     "Family",
                     "PostScript name") {
        my $val = $key_value{$key};
        return $val if $val ne "";
    }
    die "${otffile}: Font information not found\n";
}


# Output all PostScript, TrueType, and OpenType font files and their
# associated, human-readable font name.
sub format_scalable_list ($\@)
{
    my $itemwidth = $_[0];
    foreach my $fname (@{$_[1]}) {
        my $fullname;
        if ($fname =~ /\.pf[ab]$/) {
            $fullname = find_pfb_name $fname;
        }
        elsif ($fname =~ /\.[ot]tf$/) {
            $fullname = find_otf_name $fname;
        }
        else {
            die "${0}: Unrecognized file type for $fname\n";
        }
        printf "    %-${itemwidth}.${itemwidth}s (%s)\n", $fname, $fullname;
    }
}


# Sort a list and discard duplicates.
sub sort_unique (@)
{
    my %uniq;
    foreach my $e (@_) {
        $uniq{$e} = 1;
    }
    return sort {fc($a) cmp fc($b)} keys %uniq;
}

###########################################################################

# Acquire the command-line arguments.  Round the symbol count to the
# nearest hundred.
die "Usage: $0 <.log file> <#symbols>\n" if $#ARGV<1;
my ($logfilename, $numsymbols) = @ARGV;
my $roundnumsymbols = int($numsymbols/100) * 100;

# Read an entire LaTeX log file.
open (LOGFILE, "<$logfilename") || die "open(): $!\nStopped";
undef $/;
my $logfile = <LOGFILE>;
$logfile =~ s/\n//g;
close LOGFILE;

# Get a list of .mf files (indirectly via corresponding .pk files), .pfb,
# .otf, and .ttf files referenced by the log file.
my @mffiles = sort_unique map {"$_.mf"} ($logfile =~ m!<(?:[^>]+/)?([^/<>.]+)\.\d+pk>!g);
my @scalable_files = sort_unique ($logfile =~ m!<(?:[^>]+/)?([^/<>.]+\.(?:pfb|pfa|otf|ttf))>!g);

# Tally the number of unique typefaces in @mffiles and @scalable_files.
my %allfonts;
foreach (@mffiles, @scalable_files) {
    my $fname = $_;
    $fname =~ s/\.(otf|ttf|pfb|pfa|mf)$//;
    $fname =~ s/\d+$//;
    $allfonts{$fname} = 1;
}
my $numtypefaces = keys %allfonts;

# Write the README header.
my $date = strftime "%d %B %Y", localtime;
$date =~ s/\b0+/ /g;
my $headerwidth = 40;
my $linewidth = 75;
my $centerspace = " " x int(($linewidth-$headerwidth)/2);
printf "%s+-%s-+\n", $centerspace, ("-" x ($headerwidth-4));
printf "%s| %s |\n",
    $centerspace,
    center_string("THE COMPREHENSIVE LATEX SYMBOL LIST", $headerwidth-4);
printf "%s| %s |\n",
    $centerspace,
    center_string("By Scott Pakin, scott+clsl\@pakin.org", $headerwidth-4);
printf "%s| %s |\n",
    $centerspace,
    center_string("", $headerwidth-4);
printf "%s| %s |\n",
    $centerspace,
    center_string($date, $headerwidth-4);
printf "%s+-%s-+\n", $centerspace, ("-" x ($headerwidth-4));

# Output some text.
print <<"EOF1";

The Comprehensive LaTeX Symbol List is an organized list of over $roundnumsymbols
symbols commonly available to LaTeX users.  Some of these symbols are
guaranteed to be available in every TeX distribution.  Others require
font files that come with many, but not all, TeX distributions.  The
rest require font files that must be downloaded explicitly from CTAN
(http://www.ctan.org/) and installed.  The Comprehensive LaTeX Symbol
List currently showcases symbols from $numtypefaces separate typefaces.

The same directory that contains this README file also contains (1)
prebuilt versions of the symbol list for both A4- and U.S. Letter-sized
paper (symbols-a4.pdf and symbols-letter.pdf), (2) raw font tables for
the various fonts used in the Comprehensive LaTeX Symbol List
(rawtables-a4.pdf and rawtables-letter.pdf), and (3) a text file named
SYMLIST that lists all symbols that appear in the symbols list, with one
column indicating the character or control sequence one would use in a
LaTeX document and one column indicating the actual control sequence
used internally by the Comprehensive LaTeX Symbol List,

If you'd like to build symbols.tex yourself (not recommended for normal
use), a Makefile and a number of helper scripts are provided.
symbols.tex tries to be smart about LaTeX packages; it omits tables of
symbols that require unavailable packages.  The "Document
Characteristics" table appearing near the end of the document lists the
packages that were unavailable during the build.

The prebuilt versions of the symbol list use primarily vector fonts,
which should look good at any resolution and on any output device.
However, some of the fonts used by symbols.tex are available only in a
METAFONT-generated bitmap format.  The prebuilt symbol lists utilize
1200 DPI versions of all of the bitmapped fonts as 1200 DPI is a
reasonably high printer resolution and therefore should obscure the
artifacts of bitmap rendering.

The following bitmapped fonts were used to typeset the symbol list:

EOF1

format_list (3, $linewidth, @mffiles);

print <<"EOF2";

For completeness, the following scalable fonts (some converted from
OpenType or TrueType to PostScript Type 1) were used to typeset the
symbol list:

EOF2

format_scalable_list (30, @scalable_files);
