#!/usr/bin/fontforge -lang=ff
#
# 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);