Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
583 lines (532 sloc)
22.5 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using OTFontFile; | |
namespace OTFontFileVal | |
{ | |
/// <summary> | |
/// Summary description for val_post. | |
/// </summary> | |
public class val_post : Table_post, ITableValidate | |
{ | |
/************************ | |
* constructors | |
*/ | |
public val_post(OTTag tag, MBOBuffer buf) : base(tag, buf) | |
{ | |
} | |
/************************ | |
* public methods | |
*/ | |
public bool Validate(Validator v, OTFontVal fontOwner) | |
{ | |
bool bRet = true; | |
if (v.PerformTest(T.post_TableLength)) | |
{ | |
bool bLenOk = true; | |
if (Version.GetUint() == 0x00010000 || | |
Version.GetUint() == 0x00030000) | |
{ | |
if (GetLength() != 32) | |
{ | |
v.Error(T.post_TableLength, E.post_E_TableLenNot32, m_tag); | |
bLenOk = false; | |
bRet = false; | |
} | |
} | |
if (Version.GetUint() == 0x00020000) | |
{ | |
if (GetLength() < 34) | |
{ | |
v.Error(T.post_TableLength, E.post_E_InvalidTableLen, m_tag); | |
bLenOk = false; | |
bRet = false; | |
} | |
} | |
// TODO: Version.GetUint() === 0x00025000, GetLength() | |
if (bLenOk) | |
{ | |
v.Pass(T.post_TableLength, P.post_P_TableLength, m_tag); | |
} | |
} | |
if (v.PerformTest(T.post_Version)) | |
{ | |
uint ver = Version.GetUint(); | |
if (ver == 0x00025000) | |
{ | |
v.Warning(T.post_Version, W.post_W_Version_2_5, m_tag); | |
} | |
else if (ver == 0x00010000 || ver == 0x00020000) | |
{ | |
v.Pass(T.post_Version, P.post_P_Version, m_tag); | |
} | |
else if (ver == 0x00030000) | |
{ | |
v.Warning(T.post_Version, W.post_W_Version_3_Apple, m_tag); | |
} | |
else if (ver == 0x00040000) | |
{ | |
v.Warning(T.post_Version, W.post_W_Version_4_Apple, m_tag); | |
} | |
else | |
{ | |
v.Error(T.post_Version, E.post_E_Version, m_tag, "0x"+ver.ToString("x8")); | |
} | |
} | |
if (v.PerformTest(T.post_italicAngle)) | |
{ | |
bool bItalOk = true; | |
uint ia = italicAngle.GetUint(); | |
double dItalicAngle = italicAngle.GetDouble(); | |
if (dItalicAngle < -30.0 || dItalicAngle > 360.0 || (dItalicAngle > 0.0 && dItalicAngle < 330.0 )) | |
{ | |
v.Warning(T.post_italicAngle, W.post_W_italicAngle_unlikely, m_tag, "0x"+ia.ToString("x8")); | |
bItalOk = false; | |
} | |
Table_head headTable = (Table_head)fontOwner.GetTable("head"); | |
if (headTable != null) | |
{ | |
if ((headTable.macStyle & 0x0002) != 0) | |
{ | |
if (ia == 0) | |
{ | |
v.Error(T.post_italicAngle, E.post_E_italicAngleZero_macStyle, m_tag); | |
bItalOk = false; | |
bRet = false; | |
} | |
} | |
else | |
{ | |
if (ia != 0) | |
{ | |
v.Error(T.post_italicAngle, E.post_E_italicAngleNonzero_macStyle, m_tag); | |
bItalOk = false; | |
bRet = false; | |
} | |
} | |
} | |
else | |
{ | |
v.Error(T.post_italicAngle, E._TEST_E_TableMissing, m_tag, "head table missing, can't compare italicAngle to head.macStyle"); | |
bItalOk = false; | |
} | |
Table_hhea hheaTable = (Table_hhea)fontOwner.GetTable("hhea"); | |
if (hheaTable != null) | |
{ | |
if (ia == 0) | |
{ | |
if (hheaTable.caretSlopeRun != 0) | |
{ | |
v.Error(T.post_italicAngle, E.post_E_italicAngleZero_caretSlopeRun, m_tag); | |
bItalOk = false; | |
bRet = false; | |
} | |
} | |
else | |
{ | |
if (hheaTable.caretSlopeRun == 0) | |
{ | |
v.Error(T.post_italicAngle, E.post_E_italicAngleNonzero_caretSlopeRun, m_tag); | |
bItalOk = false; | |
bRet = false; | |
} | |
else | |
{ | |
double dActualAngle = 90.0 + italicAngle.GetDouble(); | |
double dhheaAngle = (Math.Atan2(hheaTable.caretSlopeRise, hheaTable.caretSlopeRun)) * (180.0 / Math.PI); | |
if (Math.Abs(dActualAngle-dhheaAngle) >= 1.0) | |
{ | |
string sDetails = "italicAngle = 0x" + ia.ToString("x8") + | |
" (" + italicAngle.GetDouble() + " degrees)" + | |
",caretSlope Rise:Run = " + hheaTable.caretSlopeRise + ":" + hheaTable.caretSlopeRun + | |
" (" + (dhheaAngle-90.0) + " degrees)"; | |
v.Error(T.post_italicAngle, E.post_E_italicAngleNonzero_hheaAngle, m_tag, sDetails); | |
bItalOk = false; | |
bRet = false; | |
} | |
} | |
} | |
} | |
else | |
{ | |
v.Error(T.post_italicAngle, E._TEST_E_TableMissing, m_tag, "hhea table missing, can't compare italicAngle to hhea.caretSlopeRun"); | |
bItalOk = false; | |
bRet = false; | |
} | |
if (bItalOk) | |
{ | |
v.Pass(T.post_italicAngle, P.post_P_italicAngle, m_tag); | |
} | |
} | |
if (v.PerformTest(T.post_underlinePosition)) | |
{ | |
Table_hhea hheaTable = (Table_hhea)fontOwner.GetTable("hhea"); | |
if (hheaTable != null) | |
{ | |
if (underlinePosition >= hheaTable.Descender) | |
{ | |
v.Pass(T.post_underlinePosition, P.post_P_underlinePosition, m_tag); | |
} | |
else | |
{ | |
v.Warning(T.post_underlinePosition, W.post_W_underlinePos_LT_descender, m_tag); | |
//bRet = false; | |
} | |
} | |
else | |
{ | |
v.Error(T.post_underlinePosition, E._TEST_E_TableMissing, m_tag, "hhea table missing, can't compare underlinePosition to hhea.Descender"); | |
} | |
} | |
if (v.PerformTest(T.post_underlineThickness)) | |
{ | |
Table_head headTable = (Table_head)fontOwner.GetTable("head"); | |
if (headTable != null) | |
{ | |
if (underlineThickness >= 0 && underlineThickness < headTable.unitsPerEm / 2 ) | |
{ | |
v.Pass(T.post_underlineThickness, P.post_P_underlineThickness, m_tag); | |
} | |
else | |
{ | |
v.Warning(T.post_underlineThickness, W.post_W_underlineThickness, m_tag, underlineThickness.ToString()); | |
} | |
} | |
else | |
{ | |
v.Error(T.post_underlineThickness, E._TEST_E_TableMissing, m_tag, "head table missing, can't compare underlineThickness to head.unitsPerEm"); | |
bRet = false; | |
} | |
} | |
if (v.PerformTest(T.post_isFixedPitch)) | |
{ | |
bool bFixedPitchOk = true; | |
Table_hmtx hmtxTable = (Table_hmtx)fontOwner.GetTable("hmtx"); | |
if (hmtxTable != null) | |
{ | |
uint numberOfHMetrics = 0; | |
Table_hhea hheaTable = (Table_hhea)fontOwner.GetTable("hhea"); | |
if (hheaTable != null) | |
{ | |
numberOfHMetrics = hheaTable.numberOfHMetrics; | |
} | |
else | |
{ | |
v.Error(T.post_isFixedPitch, E._TEST_E_TableMissing, m_tag, "hhea table missing, can't compare isFixedPitch to horizontal metrics"); | |
bRet = false; | |
} | |
bool bHmtxMono = hmtxTable.IsMonospace(fontOwner); | |
if (isFixedPitch == 0 && bHmtxMono) | |
{ | |
v.Error(T.post_isFixedPitch, E.post_E_isFixedPitchZero_hmtx, m_tag); | |
bFixedPitchOk = false; | |
bRet = false; | |
} | |
else if (isFixedPitch != 0 && !bHmtxMono) | |
{ | |
v.Error(T.post_isFixedPitch, E.post_E_isFixedPitchNonzero_hmtx, m_tag); | |
bFixedPitchOk = false; | |
bRet = false; | |
} | |
} | |
else | |
{ | |
v.Error(T.post_isFixedPitch, E._TEST_E_TableMissing, m_tag, "hmtx table missing, can't compare isFixedPitch to horizontal metrics"); | |
bRet = false; | |
} | |
Table_OS2 OS2Table = (Table_OS2)fontOwner.GetTable("OS/2"); | |
if (OS2Table != null) | |
{ | |
if (OS2Table.panose_byte1 == 2) // PANOSE family kind == LatinText | |
{ | |
if (isFixedPitch == 0 && OS2Table.panose_byte4 == 9) // PANOSE proportion == monospaced | |
{ | |
v.Error(T.post_isFixedPitch, E.post_E_isFixedPitchZero_OS_2, m_tag); | |
bFixedPitchOk = false; | |
bRet = false; | |
} | |
else if (isFixedPitch != 0 && OS2Table.panose_byte4 != 9) | |
{ | |
v.Error(T.post_isFixedPitch, E.post_E_isFixedPitchNonzero_OS_2, m_tag); | |
bFixedPitchOk = false; | |
bRet = false; | |
} | |
} | |
} | |
else | |
{ | |
v.Error(T.post_isFixedPitch, E._TEST_E_TableMissing, | |
m_tag, "OS/2 table is missing, can't compare isFixedPitch to OS/2 PANOSE"); | |
bRet = false; | |
} | |
if (bFixedPitchOk && hmtxTable != null && OS2Table != null) | |
{ | |
v.Pass(T.post_isFixedPitch, P.post_P_isFixedPitch, m_tag, "matches the hmtx and OS/2 tables"); | |
} | |
else if (bFixedPitchOk && hmtxTable == null && OS2Table != null) | |
{ | |
v.Pass(T.post_isFixedPitch, P.post_P_isFixedPitch, m_tag, "matches the OS/2 table"); | |
} | |
else if (bFixedPitchOk && hmtxTable != null && OS2Table == null) | |
{ | |
v.Pass(T.post_isFixedPitch, P.post_P_isFixedPitch, m_tag, "matches the hmtx table"); | |
} | |
} | |
if (v.PerformTest(T.post_v2_numberOfGlyphs)) | |
{ | |
if (Version.GetUint() == 0x00020000) | |
{ | |
Table_maxp maxpTable = (Table_maxp)fontOwner.GetTable("maxp"); | |
if (maxpTable == null) | |
{ | |
v.Error(T.post_v2_numberOfGlyphs, E._TEST_E_TableMissing, m_tag, "maxp table is missing"); | |
bRet = false; | |
} | |
else | |
{ | |
if (numberOfGlyphs == fontOwner.GetMaxpNumGlyphs()) | |
{ | |
v.Pass(T.post_v2_numberOfGlyphs, P.post_P_v2_numberOfGlyphs, m_tag); | |
} | |
else | |
{ | |
v.Error(T.post_v2_numberOfGlyphs, E.post_E_v2_numberOfGlyphs, | |
m_tag, "numberOfGlyphs = " + numberOfGlyphs + | |
", maxp.numGlyphs = " + fontOwner.GetMaxpNumGlyphs()); | |
bRet = false; | |
} | |
} | |
} | |
else | |
{ | |
v.Info(T.post_v2_numberOfGlyphs, I.post_I_v2_numberOfGlyphs_notv2, m_tag); | |
} | |
} | |
if (v.PerformTest(T.post_v2_glyphNameIndex)) | |
{ | |
if (Version.GetUint() == 0x00020000 && GetLength() >= 34) | |
{ | |
bool bIndexOk = true; | |
for (uint i=0; i<numberOfGlyphs; i++) | |
{ | |
if ((uint)FieldOffsetsVer2.glyphNameIndex + i*2 + 2 <= m_bufTable.GetLength()) | |
{ | |
ushort index = GetGlyphNameIndex((ushort)i); | |
if (index-258 >= m_nameOffsets.Length) | |
{ | |
string s = "glyphNameIndex[" + i + "] = " + | |
index + ", # names = " + m_nameOffsets.Length; | |
v.Error(T.post_v2_glyphNameIndex, E.post_E_glyphNameIndex_range, m_tag, s); | |
bIndexOk = false; | |
bRet = false; | |
} | |
} | |
else | |
{ | |
v.Warning(T.post_v2_glyphNameIndex, | |
W._TEST_W_OtherErrorsInTable, m_tag, | |
"unable to validate any more glyph indexes, index " + | |
i + " is past end of table"); | |
bIndexOk = false; | |
break; | |
} | |
} | |
if (bIndexOk) | |
{ | |
v.Pass(T.post_v2_glyphNameIndex, P.post_P_glyphNameIndex, m_tag); | |
} | |
} | |
else | |
{ | |
v.Info(T.post_v2_glyphNameIndex, I.post_I_v2_glyphNameIndex_notv2, m_tag); | |
} | |
} | |
if (v.PerformTest(T.post_v2_names)) | |
{ | |
if (Version.GetUint() == 0x00020000) | |
{ | |
PostNames pn = new PostNames(); | |
bool bNamesOk = true; | |
byte [] buf = new byte[2]; | |
for (uint iGlyph=0; iGlyph<numberOfGlyphs; iGlyph++) | |
{ | |
if ((uint)FieldOffsetsVer2.glyphNameIndex + iGlyph*2 + 2 | |
<= m_bufTable.GetLength()) | |
{ | |
uint index = GetGlyphNameIndex((ushort)iGlyph); | |
if (index >= 258 && index-258 < m_nameOffsets.Length) | |
{ | |
if (m_nameOffsets[index-258] >= GetLength()) | |
{ | |
string s = "name index = " + index; | |
v.Error(T.post_v2_names, | |
E.post_E_v2_NameOffsetInvalid, | |
m_tag, s); | |
bNamesOk = false; | |
bRet = false; | |
} | |
else | |
{ | |
uint length = | |
m_bufTable.GetByte(m_nameOffsets[index-258]); | |
if (m_nameOffsets[index-258] + length < GetLength()) | |
{ | |
bNamesOk = CheckNames( v, fontOwner, pn, iGlyph, index ); | |
} | |
else | |
{ | |
string s = "name index = " + index; | |
v.Error(T.post_v2_names, E.post_E_v2_NameLengthInvalid, m_tag, s); | |
bNamesOk = false; | |
bRet = false; | |
} | |
} | |
} | |
} | |
else | |
{ | |
v.Warning(T.post_v2_names, W._TEST_W_OtherErrorsInTable, m_tag, | |
"unable to validate any more names, index " + | |
iGlyph + " is past end of table"); | |
bNamesOk = false; | |
break; | |
} | |
} | |
if (bNamesOk) | |
{ | |
v.Pass(T.post_v2_names, P.post_P_names, m_tag); | |
} | |
} | |
else | |
{ | |
v.Info(T.post_v2_names, I.post_I_v2_names_notv2, m_tag); | |
} | |
} | |
return bRet; | |
} | |
private bool CheckNames( Validator v, | |
OTFontVal fontOwner, | |
PostNames pn, | |
uint iGlyph, | |
uint index ) | |
{ | |
bool bNamesOk = true; | |
string sName = GetNameString(index-258); | |
System.Globalization.NumberStyles hexStyle = | |
System.Globalization.NumberStyles.HexNumber; | |
if (Is_uniXXXX(sName)) | |
{ | |
char c = (char)Int16.Parse(sName.Substring(3, 4), hexStyle ); | |
if (fontOwner.FastMapUnicodeToGlyphID(c) != iGlyph) | |
{ | |
char cMapped = fontOwner.MapGlyphIDToUnicode(iGlyph,(char)0); | |
string s = "glyph = " + iGlyph + | |
", char = U+" + | |
((uint)cMapped).ToString("X4") | |
+ ", name = " + sName; | |
v.Info(T.post_v2_names, | |
I.post_I_names_uni_unexpected, | |
m_tag, s); | |
bNamesOk = false; | |
} | |
} | |
else if (Is_uXXXXX(sName)) | |
{ | |
uint c = (uint)Int32.Parse(sName.Substring(1), hexStyle ); | |
if (c < 0xffff) | |
{ | |
if (fontOwner.FastMapUnicodeToGlyphID((char)c) != iGlyph) | |
{ | |
char cMapped = fontOwner.MapGlyphIDToUnicode(iGlyph, | |
(char)0); | |
string s = "glyph = " + iGlyph + ", char = U+" + | |
((uint)cMapped).ToString("X4") + ", name = " + sName; | |
v.Info(T.post_v2_names, I.post_I_names_uni_unexpected, | |
m_tag, s); | |
bNamesOk = false; | |
} | |
} | |
else | |
{ | |
if (fontOwner.FastMapUnicode32ToGlyphID(c) != iGlyph) | |
{ | |
uint cMapped = | |
fontOwner.MapGlyphIDToUnicode32(iGlyph, 0); | |
string s = "glyph = " + iGlyph + ", char = U+" + | |
((uint)cMapped).ToString("X5") + ", name = " + sName; | |
v.Info(T.post_v2_names, I.post_I_names_uni_unexpected, | |
m_tag, s); | |
bNamesOk = false; | |
} | |
} | |
} | |
else | |
{ | |
// check the Adobe Glyph Names | |
char c = (char)fontOwner.MapGlyphIDToUnicode(iGlyph, (char)0); | |
if (c != 0xffff) | |
{ | |
string sAdobeName = pn.GetAdobeGlyphName(c); | |
if (sAdobeName == null) | |
{ | |
string s = "glyph = " + iGlyph + ", char = U+" + | |
((uint)c).ToString("X4") + ", name = " + sName; | |
v.Info(T.post_v2_names, I.post_I_names_noAdobe, m_tag,s); | |
bNamesOk = false; | |
} | |
else if (sName != sAdobeName) | |
{ | |
string s = "glyph = " + iGlyph + ", char = U+" + | |
((uint)c).ToString("X4") + ", name = " + sName; | |
v.Info(T.post_v2_names, I.post_I_names_nomatch, m_tag,s); | |
bNamesOk = false; | |
} | |
} | |
} | |
return bNamesOk; | |
} | |
private bool Is_uniXXXX(string s) | |
{ | |
bool bRet = false; | |
if (s.Length == 7) | |
{ | |
if (s.Substring(0,3) == "uni") | |
{ | |
bRet = true; | |
for (int i=3; i<7; i++) | |
{ | |
if (!IsCapitalHexDigit(s[i])) | |
{ | |
bRet = false; | |
break; | |
} | |
} | |
} | |
} | |
return bRet; | |
} | |
private bool Is_uXXXXX(string s) | |
{ | |
bool bRet = false; | |
// Adobe allows 4 to 6 digits in this format | |
if (s.Length >=5 && s.Length <=7 && s[0] == 'u') | |
{ | |
bRet = true; | |
for (int i=1; i<s.Length; i++) | |
{ | |
if (!IsCapitalHexDigit(s[i])) | |
{ | |
bRet = false; | |
break; | |
} | |
} | |
} | |
return bRet; | |
} | |
private bool IsCapitalHexDigit(char c) | |
{ | |
if ((c >='0' && c <= '9') || (c >='A' && c <= 'F')) | |
return true; | |
else | |
return false; | |
} | |
} | |
} |