* fix reference problems in Sans Bold Oblique that problems.pl now detects
[dejavu.git] / dejavu-fonts / problems.pl
blobbdb901e96ef9446949147669d71d03ce33d7c7c2
1 #!/usr/bin/perl -w
3 # $Id$
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):
10 # level 0:
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
16 # duplicate glyphs
17 # combining marks with non-zero width in non-monospaced fonts
18 # missing point numbers in splines
19 # level 1 (default):
20 # colorized glyphs with content
21 # level 2:
22 # different set of mapped content glyphs (first SFD file specified on command line is taken as an etalon)
23 # level 3:
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 )
31 %glyphs = ();
32 $glyphs_loaded = 0;
33 %problems_counter = ();
35 sub process_sfd_file($$) {
36 local ($sfd_file, $max_level) = @_;
38 sub problem($@) {
39 my ($level, $problem, @args) = @_;
41 if ($level <= $max_level) {
42 print $sfd_file, ': [', $level, '] ', $problem, ': ', @args, "\n";
43 $problems_counter{'['.$level.'] '.$problem}++;
47 sub is_combining($) {
48 my ($dec_enc) = @_;
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));
56 my $curchar = '';
57 my $hex_enc = '';
58 my $dec_enc = 0;
59 my $colorized;
60 my $flags;
61 my ($fontname, $panose, $is_mono_name, $is_mono_panose) = ('', '', 0, 0);
62 my $is_mono = 0;
63 my $font_width = -1;
64 my $curwidth = 0;
65 my $has_ligature = 0;
66 my $is_empty = 1;
67 my %content_glyphs = ();
68 my @ligature_refs = ();
69 my %all_glyphs = ();
70 my $prev_enc = -1;
71 my $in_spline_set = 0;
72 open (SFD, $sfd_file) || die "Unable to open $sfd_file : $!\n";
73 while (<SFD>) {
74 if (/^StartChar:\s*(\S+)\s*$/) {
75 $curchar = $1;
76 $hex_enc = '';
77 $dec_enc = 0;
78 $curwidth = -1;
79 undef $colorized;
80 undef $flags;
81 $has_ligature = 0;
82 @ligature_refs = ();
83 $is_empty = 1;
84 $in_spline_set = 0;
85 } elsif (/^Colour:\s*(\S+)\s*/) {
86 $colorized = $1;
87 } elsif (/^Flags:\s*(\S+)\s*/) {
88 $flags = $1;
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*$/) {
93 $dec_enc = $1;
94 if ($2 > -1) {
95 $hex_enc = sprintf ('%04x', $2);
97 if ($dec_enc != $3) {
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*/) {
105 $curwidth = $1;
106 } elsif (/^Ligature:\s*\S*\s*\S*\s*\S*\s*(.*?)\s*$/) {
107 @ligature_refs = split(/\s+/, $1);
108 $has_ligature = 1;
109 } elsif (/^Fore\s*$/) {
110 $is_empty = 0;
111 $in_spline_set = 1;
112 } elsif (/^EndSplineSet\s*$/) {
113 $in_spline_set = 0;
114 } elsif ($in_spline_set && !/.+,.+,.+$/) {
115 problem (0, 'point numbers missing', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''));
116 } elsif (/^Ref:/) {
117 $is_empty = 0;
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+)/) {
120 $is_empty = 0;
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;
142 # only mapped glyphs
143 if ($hex_enc) {
144 if ($glyphs_loaded) {
145 if (!exists $glyphs{$curchar}) {
146 problem (2, 'etalon-free glyph', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''));
148 } else {
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*$/) {
187 $fontname = $1;
188 $is_mono_name = ($fontname =~ /mono/i);
189 $is_mono = 1 if ($is_mono_name);
190 } elsif (/^Panose:\s*(.*?)\s*$/) {
191 $panose = $1;
192 $is_mono_panose = ((split(/\s+/, $panose))[3] == 9);
193 $is_mono = 1 if ($is_mono_panose);
196 close (SFD);
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 : ''));
218 $glyphs_loaded = 1;
221 if (!@ARGV) {
222 print STDERR "usage: [-l <0|1|2|3>] sfd_files+\n";
223 exit 1;
226 $max_level = 1;
227 if ($ARGV[0] eq '-l') {
228 shift @ARGV;
229 $max_level = shift @ARGV;
231 @sfd_files = @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";