/usr/share/texmf-texlive/scripts/fontools/pfm2kpx is in texlive-font-utils 2009-10ubuntu1.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | #!/usr/bin/perl
=pod
=head1 NAME
pfm2kpx - extract correct kerning information from F<pfm>
(Printer Font Metrics) files
=head1 SYNOPSIS
pfm2kpx [options] I<pfm>
=head1 DESCRIPTION
Many PostScript Type1 fonts contain only
F<pfb> and F<pfm> files, but no F<afm> (Adobe Font Metrics) files. These can be
generated with I<pf2afm> (which comes with I<GhostScript>), but
this might not yield all kerning pairs from the F<pfm> file. The reason
for this is that I<pf2afm> interprets C<dfCharSet = 0x00> to mean that
the Adobe StandardEncoding has been used, whereas many fonts (or font
creation tools) instead use the Windows ANSI (aka codepage 1252) encoding.
(You know you've hit this problem when B<pf2afm> complains "C<.notdef
character ocurred among kern pairs -- you'd better check the resulting
AFM file>".) In this case, the resulting F<afm> is incomplete and contains
wrong kerning pairs.
B<pfm2kpx> extracts the correct kerning pairs from such F<pfm> files and
repairs the F<afm> (if necessary creating it first by running I<pf2afm>).
If for some reason it can't update the F<afm>, it prints the kerning pairs to
C<stdout> instead.
When I<pf2afm> doesn't complain about the kerning pairs, B<pfm2kpx>
thinks the resulting F<afm> file is fine and quits; this can be
changed by specifying the B<-f> option (see below).
=head1 OPTIONS
=over 4
=item B<-f>
Force updating of the F<afm> file, even if I<pf2afm> doesn't complain.
=back
=head1 SEE ALSO
F<pf2afm> (part of GhostScript), F<afm2afm>, F<autoinst>, F<cmap2enc>,
F<font2afm>, F<ot2kpx>.
=head1 AUTHOR
Marc Penninga <marc@penninga.info>
=head1 HISTORY
=over 12
=item I<2005-02-17>
First version
=item I<2005-02-18>
Added C<binmode PFM> to make B<pfm2kpx> work on Windows platforms
=item I<2005-03-08>
Input files searched via B<kpsewhich> (where available)
=item I<2005-03-14>
Rewrote some of the code to make it more robust, added the B<-f> option
=item I<2005-03-15>
Input files searched using B<kpsewhich> or B<findtexmf>
=item I<2005-04-29>
Improved the documentation
=item I<2005-05-24>
Bugfix.
=item I<2005-07-29>
Some updates to the documentation.
=back
=cut
##############################################################################
@winansi = qw(
.notdef .notdef .notdef .notdef
.notdef .notdef .notdef .notdef
.notdef .notdef .notdef .notdef
.notdef .notdef .notdef .notdef
.notdef .notdef .notdef .notdef
.notdef .notdef .notdef .notdef
.notdef .notdef .notdef .notdef
.notdef .notdef .notdef .notdef
space exclam quotedbl numbersign
dollar percent ampersand quotesingle
parenleft parenright asterisk plus
comma hyphen period slash
zero one two three
four five six seven
eight nine colon semicolon
less equal greater question
at A B C
D E F G
H I J K
L M N O
P Q R S
T U V W
X Y Z bracketleft
backslash bracketright asciicircum underscore
grave a b c
d e f g
h i j k
l m n o
p q r s
t u v w
x y z braceleft
bar braceright asciitilde .notdef
Euro .notdef quotesinglbase florin
quotedblbase ellipsis dagger daggerdbl
circumflex perthousand Scaron guilsinglleft
OE .notdef Zcaron .notdef
.notdef quoteleft quoteright quotedblleft
quotedblright bullet endash emdash
tilde trademark scaron guilsignlright
oe .notdef zcaron Ydieresis
.notdef exclamdown cent sterling
currency yen brokenbar section
dieresis copyright ordfeminine guillemotleft
logicalnot .notdef registered macron
degree plusminus twosuperior threesuperior
acute mu paragraph periodcentered
cedilla onesuperior ordmasculine guillemotright
onequarter onehalf threequarters questiondown
Agrave Aacute Acircumflex Atilde
Adieresis Aring AE Ccedilla
Egrave Eacute Ecircumflex Edieresis
Igrave Iacute Icircumflex Idieresis
Eth Ntilde Ograve Oacute
Ocircumflex Otilde Odieresis multiply
Oslash Ugrave Uacute Ucircumflex
Udieresis Yacute Thorn germandbls
agrave aacute acircumflex atilde
adieresis aring ae ccedilla
egrave eacute ecircumflex edieresis
igrave iacute icircumflex idieresis
eth ntilde ograve oacute
ocircumflex otilde odieresis divide
oslash ugrave uacute ucircumflex
udieresis yacute thorn ydieresis
);
sub getword($) {
my @bytes = reverse(split(//, substr($_[0], 0, 2)));
my $r = 0;
for (@bytes) {
$r = ($r << 8) + unpack "C", $_;
}
return $r >= 32768 ? $r - 65536 : $r;
}
sub getdword($) {
my @bytes = reverse(split(//, substr($_[0], 0, 4)));
my $r = 0;
for (@bytes) {
$r = ($r << 8) + unpack "C", $_;
}
return $r;
}
##############################################################################
use Getopt::Std;
use integer;
use warnings; no warnings qw(uninitialized);
getopts "f", \%options;
$0 =~ s!.*/!!;
die "Usage: $0 pfmfile\n" if @ARGV != 1;
($base = $ARGV[0]) =~ s!.*/|\.pfm!!g;
unless (-e "${base}.afm") {
$errmsg = `pf2afm '${base}.pfb'`;
if ($errmsg !~ /\.notdef character ocurred among kern pairs/ and
not exists $options{f})
{
warn "Warning: output from <pf2afm> seems OK; skipping `$ARGV[0]'.\n",
" If you disagree, rerun $0 with the `-f' option\n";
exit 0;
}
}
if ((chop($fn = `kpsewhich '$ARGV[0]' 2>&1`) and -e $fn) or
(chop($fn = `findtexmf '$ARGV[0]' 2>&1`) and -e $fn))
{
open PFM, "<$fn" or die "Error: can't open `$fn' - $!\n";
}
else {
open PFM, "<$ARGV[0]" or
die "Error: can't open `$ARGV[0]' - $!\n";
}
binmode PFM;
{
local $/;
$pfm = <PFM>;
}
$dfCharSet = unpack "C", substr($pfm, 85, 1);
if ($dfCharSet != 0) {
die "Error: `dfCharSet' is $dfCharSet, not 0\n";
}
$dfPairKernTable = getdword(substr $pfm, 131, 4);
$etmKernPairs = getword(substr $pfm, 195, 2);
if ($dfPairKernTable == 0 or $etmKernPairs == 0) {
warn "Warning: no kerning pairs found in `$ARGV[0]'\n";
exit;
}
$pairkern = substr $pfm, $dfPairKernTable;
$n = getword(substr $pairkern, 0, 2);
if ($n != $etmKernPairs) {
warn "Warning: incorrect number of kerning pairs in `$ARGV[0]';\n",
" please check the resulting AFM file!\n";
}
for $i (0 .. $n - 1) {
$pair = substr $pairkern, 2 + 4 * $i, 4;
$left = unpack "C", substr($pair, 0, 1);
$right = unpack "C", substr($pair, 1, 1);
$kern = getword(substr($pair, 2, 2));
push @KPX, "KPX $winansi[$left] $winansi[$right] $kern\n";
}
if (open AFM, "<${base}.afm") {
{
local $/;
$afm = <AFM>;
}
$afm =~ s/StartKernData.*//s;
$afm =~ s/\r\n*/\n/gs;
$time = localtime;
$afm =~ s/(?<=Comment ).*?$/Converted at $time by $0 from $ARGV[0]/m;
if (open AFM, ">${base}.afm") {select AFM}
else {
warn "Warning: can't create `${base}.afm' - $!\n" .
" printing to <STDOUT> instead\n";
}
print <<EOF;
${afm}
StartKernData
StartKernPairs $n
@{KPX}EndKernPairs
EndKernData
EndFontMetrics
EOF
}
else {
warn "Warning: file `${base}.afm' not found, " .
"printing only kerning pairs\n";
print "StartKernData\nStartKernPairs $n\n";
map print, @KPX;
print "EndKernPairs\nEndKernData\n";
}
__END__
|