# # Copyright (C) 2013 Matthew Skala # # This file is part of FontForge. # # FontForge is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # FontForge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with FontForge. If not, see . # # For more details see the COPYING.gplv3 file in the root directory of this # distribution. # # Based on the Tsukurimashou Project's fontlint script (same author). # # Things to check for: # check_for[i][0] is 0, 1, or 2, for don't check, warn, or fatal # check_for[i][1] is a string message # check_for[0]..[31] refer to LSB to MSB of Validate() return # check_for[32]..[63] refer to LSB to MSB of $loadState # check_for[64]..[95] refer to LSB to MSB of $privateState # check_for[96]..[whatever] are sequentially assigned other things # note: to work around a bug in FontForge (multidimensional arrays being # horribly broken), check_for[i][j] is actually stored as check_for[2*i+j] check_for=[ \ 1,"Unknown Validate() return bit 0x1", \ 2,"Open contour", \ 2,"Self-intersecting glyph", \ 2,"Wrong direction", \ 2,"Flipped reference", \ 2,"Missing points at extrema", \ 2,"Unknown glyph referenced in GSUB/GPOS/MATH", \ 2,"More points in a glyph than PostScript allows", \ 2,"Too many hints", \ 2,"Bad glyph name", \ 2,"More points in a glyph than specified in 'maxp'", \ 2,"More paths in a glyph than specified in 'maxp'", \ 2,"More points in a composite glyph than specified in 'maxp'", \ 2,"More paths in a composite glyph than specified in 'maxp'", \ 2,"Instructions longer than allowed in 'maxp'", \ 2,"More references in a glyph than specified in 'maxp'", \ 2,"References nested more deeply than specified in 'maxp'", \ 1,"The 'prep' or 'fpgm' tables are longer than specified in 'maxp'\n" \ +" (may not be an error)", \ 2,"Adjacent points too far apart in a glyph", \ 2,"Non-integral coordinates in a glyph", \ 1,"A glyph uses at least one, but not all, anchor classes in a\n" \ +" subtable (may not be an error)", \ 2,"Two glyphs have the same name", \ 2,"Two glyphs have the same Unicode code point", \ 2,"Overlapping hints in a glyph", \ 1,"Unknown Validate() return bit 0x1000000", \ 1,"Unknown Validate() return bit 0x2000000", \ 1,"Unknown Validate() return bit 0x4000000", \ 1,"Unknown Validate() return bit 0x8000000", \ 1,"Unknown Validate() return bit 0x10000000", \ 1,"Unknown Validate() return bit 0x20000000", \ 1,"Unknown Validate() return bit 0x40000000", \ 1,"Unknown Validate() return bit 0x80000000", \ 2,"Bad PostScript fontname entry in the 'name' table", \ 2,"Bad 'glyf' or 'loca' table", \ 2,"Bad 'CFF ' table", \ 2,"Bad 'hhea', 'hmtx', 'vhea' or 'vmtx' table", \ 2,"Bad 'cmap' table", \ 2,"Bad 'EBDT', 'bdat', 'EBLC' or 'bloc' (embedded bitmap) table", \ 2,"Bad Apple GX advanced typography table", \ 2,"Bad OpenType advanced typography table", \ 2,"Bad version number in OS/2 table (must be >0, and must be >1 for\n" \ +" OT-CFF fonts)", \ 2,"Bad sfnt file header", \ 1,"Unknown loadState bit 0x400", \ 1,"Unknown loadState bit 0x800", \ 1,"Unknown loadState bit 0x1000", \ 1,"Unknown loadState bit 0x2000", \ 1,"Unknown loadState bit 0x4000", \ 1,"Unknown loadState bit 0x8000", \ 1,"Unknown loadState bit 0x10000", \ 1,"Unknown loadState bit 0x20000", \ 1,"Unknown loadState bit 0x40000", \ 1,"Unknown loadState bit 0x80000", \ 1,"Unknown loadState bit 0x100000", \ 1,"Unknown loadState bit 0x200000", \ 1,"Unknown loadState bit 0x400000", \ 1,"Unknown loadState bit 0x800000", \ 1,"Unknown loadState bit 0x1000000", \ 1,"Unknown loadState bit 0x2000000", \ 1,"Unknown loadState bit 0x4000000", \ 1,"Unknown loadState bit 0x8000000", \ 1,"Unknown loadState bit 0x1000000", \ 1,"Unknown loadState bit 0x20000000", \ 1,"Unknown loadState bit 0x40000000", \ 1,"Unknown loadState bit 0x80000000", \ 2,"Odd number of elements in either the BlueValues or OtherBlues\n" \ +" entries in the PostScript Private dictionary", \ 2,"Disordered elements in either the BlueValues or OtherBlues\n" \ +" entries in the PostScript Private dictionary", \ 2,"Too many elements in either the BlueValues or OtherBlues entries\n" \ +" in the PostScript Private dictionary", \ 2,"Elements too close in either the BlueValues or OtherBlues\n" \ +" entries in the PostScript Private dictionary (must " \ +"be at least\n 2*BlueFuzz+1 apart)", \ 2,"Non-integral elements in either the BlueValues or OtherBlues\n" \ +" entries in the PostScript Private dictionary", \ 2,"Alignment zone height in either the BlueValues or OtherBlues is\n" \ +" too big for the BlueScale in the PostScript Private " \ +"dictionary", \ 1,"Unknown privateState bit 0x40", \ 1,"Unknown privateState bit 0x80", \ 2,"Odd number of elements in either the FamilyBlues or\n" \ +" FamilyOtherBlues entries in the PostScript Private " \ +"dictionary", \ 2,"Disordered elements in either the FamilyBlues or\n" \ +" FamilyOtherBlues entries in the PostScript Private " \ +"dictionary", \ 2,"Too many elements in either the FamilyBlues or FamilyOtherBlues\n" \ +" entries in the PostScript Private dictionary", \ 2,"Elements too close in either the FamilyBlues or\n" \ +" FamilyOtherBlues entries in the PostScript Private " \ +"dictionary\n (must be at least 2*BlueFuzz+1 apart)", \ 2,"Non-integral elements in either the FamilyBlues or\n" \ +" FamilyOtherBlues entries in the PostScript Private " \ +"dictionary", \ 2,"Alignment zone height in either the FamilyBlues or\n" \ +" FamilyOtherBlues is too big for the BlueScale in " \ +"the PostScript\n Private dictionary", \ 1,"Unknown privateState bit 0x4000", \ 1,"Unknown privateState bit 0x8000", \ 2,"Missing BlueValues entry in PostScript Private dictionary", \ 2,"Bad BlueFuzz entry in PostScript Private dictionary", \ 2,"Bad BlueScale entry in PostScript Private dictionary", \ 2,"Bad StdHW entry in PostScript Private dictionary", \ 2,"Bad StdVW entry in PostScript Private dictionary", \ 2,"Bad StemSnapH entry in PostScript Private dictionary", \ 2,"Bad StemSnapV entry in PostScript Private dictionary", \ 2,"StemSnapH does not contain StdHW value in PostScript Private\n" \ +" dictionary", \ 2,"StemSnapV does not contain StdVW value in PostScript Private\n" \ +" dictionary", \ 2,"Bad BlueShift entry in PostScript Private dictionary", \ 1,"Unknown privateState bit 0x4000000", \ 1,"Unknown privateState bit 0x8000000", \ 1,"Unknown privateState bit 0x1000000", \ 1,"Unknown privateState bit 0x20000000", \ 1,"Unknown privateState bit 0x40000000", \ 1,"Unknown privateState bit 0x80000000", \ 0,"Missing BlueValues entry (issue #80) when the font is quadratic\n" \ +" (overrides general setting)", \ 0,"Non-integral coordinates (issue #19) when the font is cubic\n" \ +" (overrides general setting)", \ 2,"Self-intersecting glyph (issue #2) when FontForge is able to\n" \ +" correct this (overrides general setting)", \ 1,"Flags in the OS/2 table say the font is non-free" \ ]; # display usage message usage_message= \ "\nfontlint [OPTIONS] {fontfile} ...\n" \ +" Validates the listed fonts\n\n" \ +"Options accepted:\n" \ +" -f, --fatal ISSUES treat ISSUES as fatal errors\n" \ +" -i, --ignore ISSUES ignore ISSUES\n" \ +" -h, --help display help\n" \ +" -l, --list list issues, their numbers, and disposition\n" \ +" -w, --warning ISSUES treat ISSUES as warnings\n\n" \ +"Lists of ISSUES may include comma-separated numbers and ranges, like\n" \ +" 1,2,5,8-21,24-29,48\n"; if ($argc<=1) Print(usage_message); Quit(1); endif # process command line allowing_options=1; any_fatal=0; while ($argc>1) # handle -- to terminate option processing if ($argv[1]=='--') allowing_options=0; # handle options elseif (allowing_options && (Strsub($argv[1]+' ',0,1)=='-')) arg_type=-1; # make issues into fatal errors if ((Strsub($argv[1]+' ',0,2)=='-f') \ || (Strsub($argv[1]+' ',1,3)=='-f')) arg_type=2; # explicit request for help message elseif ((Strsub($argv[1]+' ',0,2)=='-h') \ || (Strsub($argv[1]+' ',1,3)=='-h')) Print(usage_message); # make issues ignored elseif ((Strsub($argv[1]+' ',0,2)=='-i') \ || (Strsub($argv[1]+' ',1,3)=='-i')) arg_type=0; # list current configuration elseif ((Strsub($argv[1]+' ',0,2)=='-l') \ || (Strsub($argv[1]+' ',1,3)=='-l')) Print("INFO Here are all the currently-checked issues and " \ +"their status"); i=0; while (i=0) i=0; while (i=Strtol(rangevals[0]))) j=Strtol(rangevals[0]); while (j<=Strtol(rangevals[1])) check_for[j*2]=arg_type; j=j+1; endloop else endif i=i+1; endloop endif # handle filenames else Print("CHECKING "+$argv[1]); fatal_here=0; Open($argv[1],9); ls_result=$loadState; v_result=Validate(); ps_result=$privateState; i=0; ibit=1; test_result=v_result; while (i<96) if (i==32) test_result=ls_result; ibit=1; elseif (i==64) test_result=ps_result; ibit=1; endif if (test_result & ibit) # handle overrides if ((i==2) && (check_for[2*2]!=check_for[98*2])) Print("CHECKING 98 "+check_for[98*2+1]); SelectAll(); FindIntersections(); RemoveOverlap(); if (Validate()&0x4) ci=i; else ci=98; endif elseif ((i==19) && ($order==3)) ci=97; elseif ((i==80) && ($order==2)) ci=96; else ci=i; endif istr=" "+ToString(ci); istr=Strsub(istr,Strlen(istr)-3); if (check_for[2*ci]==1) Print("WARNING "+istr+" "+check_for[ci*2+1]); elseif (check_for[2*ci]==2) Print("ERROR "+istr+" "+check_for[ci*2+1]); fatal_here=1; endif endif i=i+1; ibit=ibit*2; endloop if (GetOS2Value("FSType")!=0) if (check_for[2*99]==1) Print("WARNING 99 "+check_for[99*2+1]); elseif (check_for[2*99]==2) Print("ERROR 99 "+check_for[99*2+1]); any_fatal=1; endif endif Close(); if (fatal_here) Print("FAIL "+$argv[1]); any_fatal=1; else Print("PASS "+$argv[1]); endif Print(""); endif shift; endloop Quit(any_fatal);