5 # possible problems finder
6 # (c)2004,2005 Stepan Roh (PUBLIC DOMAIN)
7 # usage: ./problems.pl [-l <0|1|2|3>] sfd_files+
9 # detected problems (higher levels contain lower levels):
11 # monospaced font (with Mono in name) without indication in Panose (and vice-versa)
12 # glyphs in monospaced face with different width
13 # not normalized file (looks for WinInfo, DisplaySize, HStem, VStem, Ref, KernsSLIF, different position than encoding,
14 # unordered glyphs or H or M flag)
15 # glyphs without width or with negative width
17 # combining marks with non-zero width in non-monospaced fonts
18 # missing point numbers in splines
20 # colorized glyphs with content
22 # different set of mapped content glyphs (first SFD file specified on command line is taken as an etalon)
24 # ligature referencing colorized or missing glyphs
25 # ligature in colorized glyph (due to bug in FF <20050502 it causes problems on Mac OS X)
26 # ligature in empty glyph
28 sub process_sfd_file
($$);
30 # glyph name => ( 'dec_enc' => dec_enc, 'hex_enc' => hex_enc )
33 %problems_counter = ();
35 sub process_sfd_file
($$) {
36 local ($sfd_file, $max_level) = @_;
39 my ($level, $problem, @args) = @_;
41 if ($level <= $max_level) {
42 print $sfd_file, ': [', $level, '] ', $problem, ': ', @args, "\n";
43 $problems_counter{'['.$level.'] '.$problem}++;
50 return (($dec_enc >= 0x0300) && ($dec_enc <= 0x036F))
51 || (($dec_enc >= 0x1DC0) && ($dec_enc <= 0x1DFF))
52 || (($dec_enc >= 0x20D0) && ($dec_enc <= 0x20FF))
53 || (($dec_enc >= 0xFE20) && ($dec_enc <= 0xFE2F));
61 my ($fontname, $panose, $is_mono_name, $is_mono_panose) = ('', '', 0, 0);
67 my %content_glyphs = ();
68 my @ligature_refs = ();
71 my $in_spline_set = 0;
72 open (SFD
, $sfd_file) || die "Unable to open $sfd_file : $!\n";
74 if (/^StartChar:\s*(\S+)\s*$/) {
85 } elsif (/^Colour:\s*(\S+)\s*/) {
87 } elsif (/^Flags:\s*(\S+)\s*/) {
89 if ($flags =~ /([MH])/) {
90 problem
(0, 'not normalized: '.$1.' flag', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''), ': flags=', $flags);
92 } elsif (/^Encoding:\s*(\d+)\s*((?:-|\d)+)\s*(\d+)\s*$/) {
95 $hex_enc = sprintf ('%04x', $2);
98 problem
(0, 'not normalized: glyph position differs from encoding', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''), ': position=', $3);
100 if ($dec_enc <= $prev_enc) {
101 problem
(0, 'not normalized: unordered glyphs', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''), ': previous=', $prev_enc);
103 $prev_enc = $dec_enc;
104 } elsif (/^Width:\s*(\S+)\s*/) {
106 } elsif (/^Ligature:\s*\S*\s*\S*\s*\S*\s*(.*?)\s*$/) {
107 @ligature_refs = split(/\s+/, $1);
109 } elsif (/^Fore\s*$/) {
112 } elsif (/^EndSplineSet\s*$/) {
114 } elsif ($in_spline_set && !/.+,.+,.+$/) {
115 problem
(0, 'point numbers missing', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''));
118 problem
(0, 'not normalized: old-style "Ref"', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''));
119 } elsif (/^Refer:\s*\d+\s*\d+\s*\S\s*(-?\d+(?:\.\d*)?(?:e-?\d+)?)\s*(-?\d+(?:\.\d*)?(?:e-?\d+)?)\s*(-?\d+(?:\.\d*)?(?:e-?\d+)?)\s*(-?\d+(?:\.\d*)?(?:e-?\d+)?)\s*-?\d+(?:\.\d*)?(?:e-?\d+)?\s*-?\d+(?:\.\d*)?(?:e-?\d+)?\s*(\d+)/) {
121 if (($5 & 0x2) != 0x2) {
122 problem
(0, 'reference is not rounded to grid', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''), ' flags=', $5);
124 if (!(($1 == 1) && ($2 == 0) && ($3 == 0) && ($4 == 1))) {
125 problem
(0, 'transformed reference', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''));
127 } elsif (/^DisplaySize:/) {
128 problem
(0, 'not normalized: DisplaySize');
129 } elsif (/^WinInfo:/) {
130 problem
(0, 'not normalized: WinInfo');
131 } elsif (/^HStem:/) {
132 problem
(0, 'not normalized: HStem');
133 } elsif (/^VStem:/) {
134 problem
(0, 'not normalized: VStem');
135 } elsif (/^KernsSLIF:/) {
136 problem
(0, 'not normalized: KernsSLIF');
137 } elsif (/^EndChar\s*$/) {
138 if (!defined $colorized && !$is_empty) {
139 $content_glyphs{$curchar}{'dec_enc'} = $dec_enc;
140 $content_glyphs{$curchar}{'hex_enc'} = $hex_enc;
141 @
{$content_glyphs{$curchar}{'ligature'}} = @ligature_refs;
144 if ($glyphs_loaded) {
145 if (!exists $glyphs{$curchar}) {
146 problem
(2, 'etalon-free glyph', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''));
149 $glyphs{$curchar}{'dec_enc'} = $dec_enc;
150 $glyphs{$curchar}{'hex_enc'} = $hex_enc;
154 if (defined $colorized && !$is_empty) {
155 problem
(1, 'colorized content', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : '') , ': color=', $colorized);
157 if (defined $colorized && defined $flags && ($flags =~ /W/)) {
158 problem
(1, 'colorized content', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : '') , ': color=', $colorized, ', flags=', $flags);
160 if (!$is_mono && defined $colorized && ($curwidth != 2048) && ($curwidth != 0)) {
161 problem
(1, 'colorized content', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : '') , ': color=', $colorized, ', width=', $curwidth);
163 if ($curwidth == -1) {
164 problem
(0, 'glyph w/o width', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''));
165 } elsif ($curwidth < 0) {
166 problem
(0, 'negative width', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''), ': width=', $curwidth);
167 } elsif ($is_mono && defined $flags && ($flags =~ /W/)) {
168 if ($font_width == -1) {
169 $font_width = $curwidth;
170 } elsif ($curwidth != $font_width) {
171 problem
(0, 'incorrect width', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''), ': font width=', $font_width, ', glyph width=', $curwidth);
173 } elsif (!$is_mono && is_combining
($dec_enc) && ($curwidth != 0) && !$is_empty) {
174 problem
(0, 'combining mark with non-zero width', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''), ': width=', $curwidth);
176 if (defined $colorized && $has_ligature) {
177 problem
(3, 'colorized ligature', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''), ': color=', $colorized);
179 if ($is_empty && $has_ligature) {
180 problem
(3, 'empty ligature', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''));
182 if (exists $all_glyphs{$dec_enc}) {
183 problem
(0, 'duplicate', $curchar, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''));
185 $all_glyphs{$dec_enc} = 1;
186 } elsif (/^FontName:\s*(.*?)\s*$/) {
188 $is_mono_name = ($fontname =~ /mono/i);
189 $is_mono = 1 if ($is_mono_name);
190 } elsif (/^Panose:\s*(.*?)\s*$/) {
192 $is_mono_panose = ((split(/\s+/, $panose))[3] == 9);
193 $is_mono = 1 if ($is_mono_panose);
197 if ($is_mono_name != $is_mono_panose) {
198 problem
(0, 'mixed monospace', 'font name=', $fontname, ', panose=', $panose);
200 foreach $glyph (sort { $content_glyphs{$a}{'dec_enc'} <=> $content_glyphs{$b}{'dec_enc'} } keys %content_glyphs) {
201 my $dec_enc = $content_glyphs{$glyph}{'dec_enc'};
202 my $hex_enc = $content_glyphs{$glyph}{'hex_enc'};
203 foreach $liga (@
{$content_glyphs{$glyph}{'ligature'}}) {
204 if (!exists ($content_glyphs{$liga})) {
205 problem
(3, 'ligature references colorized or missing glyph', $glyph, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''), ': ligature ref=', $liga);
209 if ($glyphs_loaded) {
210 foreach $glyph (sort { $glyphs{$a}{'dec_enc'} <=> $glyphs{$b}{'dec_enc'} } keys %glyphs) {
211 my $dec_enc = $glyphs{$glyph}{'dec_enc'};
212 my $hex_enc = $glyphs{$glyph}{'hex_enc'};
213 if (!exists $content_glyphs{$glyph}) {
214 problem
(2, 'missing glyph', $glyph, ' ', $dec_enc, ($hex_enc ?
' U+'.$hex_enc : ''));
222 print STDERR
"usage: [-l <0|1|2|3>] sfd_files+\n";
227 if ($ARGV[0] eq '-l') {
229 $max_level = shift @ARGV;
233 foreach $sfd_file (@sfd_files) {
234 process_sfd_file
($sfd_file, $max_level);
237 foreach $problem (sort keys %problems_counter) {
238 print $problems_counter{$problem}, ' problems of type "', $problem, '"', "\n";