2 # use the -CA flag so @ARGV is interpreted as UTF-8
7 binmode STDOUT, ':utf8';
9 use lib 'ext/Font-TTF/lib';
12 my @name_strings = qw(
38 $name_strings{$name_strings[$_]} = $_ for 0..$#name_strings;
44 This tool provides a (relatively) simple way to manipulate the 'name' table of
45 TrueType/OpenType fonts.
48 $0 [options] [commands] [inputfile.ttf] [outputfile.ttf]
51 --verbose, -v print various details about the modifications made
53 Any sequence of the following commands:
54 --print print the font's current name strings
55 --set [name] [string] replace the name string's value
56 --append [name] [string] append to the name string's value
57 --subst [name] [string1] [string2] replace all occurrences of [string1]
58 with [string2] in the name string's value
60 "[name]" can be any of the following: (see the Name ID table on
61 http://www.microsoft.com/typography/otspec/name.htm for full explanations)
63 copyright, family, subfamily, unique-identifier, full-name, version,
64 postscript, trademark, manufacturer, designer, description, vendor-url,
65 designer-url, license, license-url, preferred-family, preferred-subfamily,
66 compatible-full, sample-text, postscript-cid, wws-family, wws-subfamily
74 my ($font, $id, $sub) = @_;
75 my $str = $font->{name}{strings}[$id];
77 for my $plat (0..$#$str) {
78 next unless $str->[$plat];
79 for my $enc (0..$#{$str->[$plat]}) {
80 next unless $str->[$plat][$enc];
81 for my $lang (keys %{$str->[$plat][$enc]}) {
82 next unless exists $str->[$plat][$enc]{$lang};
83 my $val = $sub->($str->[$plat][$enc]{$lang}, $plat, $enc, $lang);
84 $str->[$plat][$enc]{$lang} = $val;
90 warn "Can't find existing name string '$name_strings[$id]' ($id)\n";
97 $str =~ s/([\\"])/\\$1/g;
101 $str =~ s/([\x00-\x1f])/sprintf '\u%04X', ord $1/eg;
108 for my $nid (0..$#name_strings) {
109 my $name = $font->{name}->find_name($nid);
111 push @lines, json_string($name_strings[$nid]).': '.json_string($name);
116 print join ",\n\n", @lines;
122 if ($name =~ /^\d+$/ and $name < @name_strings) {
125 my $id = $name_strings{lc $name};
126 return $id if defined $id;
127 warn "Invalid name string identifier '$name'\n\n";
140 if ($_ eq '-v' or $_ eq '--verbose') {
142 } elsif ($_ eq '-p' or $_ eq '--print') {
144 push @commands, [ 'print' ];
145 } elsif ($_ eq '--set') {
146 @args >= 2 or help();
147 my $id = parse_id(shift @args);
148 my $val = shift @args;
149 push @commands, [ 'set', $id, $val ];
150 } elsif ($_ eq '--append') {
151 @args >= 2 or help();
152 my $id = parse_id(shift @args);
153 my $val = shift @args;
154 push @commands, [ 'append', $id, $val ];
155 } elsif ($_ eq '--subst') {
156 @args >= 3 or help();
157 my $id = parse_id(shift @args);
158 my $val1 = shift @args;
159 my $val2 = shift @args;
160 push @commands, [ 'subst', $id, $val1, $val2 ];
166 ($print and (@rest == 1 or @rest == 2)) or @rest == 2 or help();
168 my ($input_file, $output_file) = @rest;
170 my $font = Font::TTF::Font->open($input_file) or die "Error opening $input_file: $!";
174 for my $cmd (@commands) {
175 if ($cmd->[0] eq 'print') {
177 } elsif ($cmd->[0] eq 'set') {
179 modify_name($font, $id, sub {
180 my ($val, $plat, $enc, $lang) = @_;
181 print "Setting string $id (platform=$plat encoding=$enc lang=$lang)\n" if $verbose;
184 } elsif ($cmd->[0] eq 'append') {
186 modify_name($font, $id, sub {
187 my ($val, $plat, $enc, $lang) = @_;
188 print "Appending to string $id (platform=$plat encoding=$enc lang=$lang)\n" if $verbose;
189 return $val . $cmd->[2];
191 } elsif ($cmd->[0] eq 'subst') {
193 modify_name($font, $id, sub {
194 my ($val, $plat, $enc, $lang) = @_;
195 my $pat = quotemeta($cmd->[2]);
196 my $n = ($val =~ s/$pat/$cmd->[3]/g) || 0;
197 print "Substituting string $id (platform=$plat encoding=$enc lang=$lang) - $n match(es)\n" if $verbose;
198 warn "No match found for substitution on string '$name_strings[$id]'\n" if not $n;
206 $font->out($output_file) if $output_file;