protected bool CheckForRecommendedTables(Validator v) { bool bRet = true; bool bMissing = false; if (!IsPostScript()) { if (GetDirectoryEntry("gasp") == null) { v.Warning(T.T_NULL, W._FONT_W_MissingRecommendedTable, null, "gasp"); bMissing = true; } Table_hmtx hmtxTable = (Table_hmtx)GetTable("hmtx"); if (hmtxTable != null) { if (!hmtxTable.IsMonospace(this) && !ContainsSymbolsOnly()) { if (GetDirectoryEntry("kern") == null) { v.Warning(T.T_NULL, W._FONT_W_MissingRecommendedTable, null, "kern"); bMissing = true; } if (GetDirectoryEntry("hdmx") == null) { v.Warning(T.T_NULL, W._FONT_W_MissingRecommendedTable, null, "hdmx"); bMissing = true; } } } if (GetDirectoryEntry("VDMX") == null) { v.Warning(T.T_NULL, W._FONT_W_MissingRecommendedTable, null, "VDMX"); bMissing = true; } } if (GetDirectoryEntry("DSIG") == null && !GetFile().IsCollection()) { v.Warning(T.T_NULL, W._FONT_W_MissingRecommendedTable, null, "DSIG"); bMissing = true; } if (!bMissing) { v.Pass(P._FONT_P_MissingRecommendedTable, null); } return(bRet); }
protected bool CheckForNoUnnecessaryTables(Validator v) { bool bRet = true; bool bFoundUnnecessary = false; Table_hmtx hmtxTable = (Table_hmtx)GetTable("hmtx"); if (hmtxTable != null) { if (hmtxTable.IsMonospace(this)) { if (GetDirectoryEntry("hdmx") != null) { v.Warning(T.T_NULL, W._FONT_W_UnnecessaryTable, null, "hdmx table not needed for monospaced font"); bFoundUnnecessary = true; } if (GetDirectoryEntry("LTSH") != null) { v.Warning(T.T_NULL, W._FONT_W_UnnecessaryTable, null, "LTSH table not needed for monospaced font"); bFoundUnnecessary = true; } if (GetDirectoryEntry("kern") != null) { v.Warning(T.T_NULL, W._FONT_W_UnnecessaryTable, null, "kern table not needed for monospaced font"); bFoundUnnecessary = true; } } if (GetDirectoryEntry("CFF ") == null && GetDirectoryEntry("VORG") != null) { v.Warning(T.T_NULL, W._FONT_W_UnnecessaryTable, null, "VORG table not needed, it may optionally be present for fonts with Postscript outlines"); bFoundUnnecessary = true; } if (GetDirectoryEntry("PCLT") != null) { v.Warning(T.T_NULL, W._FONT_W_UnnecessaryTable, null, "PCLT table not needed, Microsoft no longer recommends including this table"); bFoundUnnecessary = true; } } if (GetDirectoryEntry("CFF ") != null) { string [] UnnecessaryTables = { "glyf", "fpgm", "cvt ", "loca", "prep" }; for (int i = 0; i < UnnecessaryTables.Length; i++) { if (GetDirectoryEntry(UnnecessaryTables[i]) != null) { v.Warning(T.T_NULL, W._FONT_W_UnnecessaryTable, null, UnnecessaryTables[i] + " not needed since the font contains a 'CFF ' table"); bFoundUnnecessary = true; } } } if (GetDirectoryEntry("glyf") != null) { if (GetDirectoryEntry("CFF ") != null) { v.Warning(T.T_NULL, W._FONT_W_UnnecessaryTable, null, "CFF not needed since the font contains a 'CFF ' table"); bFoundUnnecessary = true; } } if (ContainsLatinOnly()) { if (GetDirectoryEntry("vhea") != null) { v.Warning(T.T_NULL, W._FONT_W_UnnecessaryTable, null, "vhea not needed since the font only contains Latin characters"); bFoundUnnecessary = true; } if (GetDirectoryEntry("vmtx") != null) { v.Warning(T.T_NULL, W._FONT_W_UnnecessaryTable, null, "vmtx not needed since the font only contains Latin characters"); bFoundUnnecessary = true; } } if (!bFoundUnnecessary) { v.Pass(P._FONT_P_UnnecessaryTable, null); } return(bRet); }
/************************ * public methods */ public bool Validate(Validator v, OTFontVal fontOwner) { bool bRet = true; if (v.PerformTest(T.hhea_version)) { if (TableVersionNumber.GetUint() == 0x00010000) { v.Pass(T.hhea_version, P.hhea_P_version, m_tag); } else { v.Error(T.hhea_version, E.hhea_E_version, m_tag, "0x" + TableVersionNumber.GetUint().ToString("x8")); bRet = false; } } if (v.PerformTest(T.hhea_AscenderPositive)) { if (Ascender <= 0) { string s = "Ascender = " + Ascender; v.Error(T.hhea_AscenderPositive, E.hhea_E_AscenderPositive, m_tag, s); bRet = false; } else { v.Pass(T.hhea_AscenderPositive, P.hhea_P_AscenderPositive, m_tag); } } if (v.PerformTest(T.hhea_DescenderNegative)) { if (Descender >= 0) { string s = "Descender = " + Descender; v.Error(T.hhea_DescenderNegative, E.hhea_E_DescenderNegative, m_tag, s); bRet = false; } else { v.Pass(T.hhea_DescenderNegative, P.hhea_P_DescenderNegative, m_tag); } } Table_head headTable = (Table_head)fontOwner.GetTable("head"); if (headTable != null) { if (v.PerformTest(T.hhea_Ascender_yMax)) { if (Ascender > headTable.yMax) { string s = "Ascender = " + Ascender + ", head.yMax = " + headTable.yMax; v.Info(T.hhea_Ascender_yMax, I.hhea_I_Ascender_yMax, m_tag, s); // bRet = false; } else { v.Pass(T.hhea_Ascender_yMax, P.hhea_P_Ascender_yMax, m_tag); } } if (v.PerformTest(T.hhea_Descender_yMin)) { if (Descender < headTable.yMin) { string s = "Descender = " + Descender + ", head.yMin = " + headTable.yMin; v.Info(T.hhea_Descender_yMin, I.hhea_I_Descender_yMin, m_tag, s); // bRet = false; } else { v.Pass(T.hhea_Descender_yMin, P.hhea_P_Descender_yMin, m_tag); } } } else { v.Error(T.hhea_Ascender_yMax, E._TEST_E_TableMissing, m_tag, "head"); bRet = false; } if (v.PerformTest(T.hhea_LineGapPositive)) { if (LineGap < 0) { string s = "LineGap = " + LineGap; v.Warning(T.hhea_LineGapPositive, W.hhea_W_LineGapPositive, m_tag, s); //bRet = false; } else { v.Pass(T.hhea_LineGapPositive, P.hhea_P_LineGapPositive, m_tag); } } Table_OS2 OS2Table = (Table_OS2)fontOwner.GetTable("OS/2"); if (OS2Table != null) { if (v.PerformTest(T.hhea_Ascender_usWinAscent)) { if (OS2Table.usWinAscent != Ascender) { string s = "hhea.Ascender = " + Ascender + ", OS/2.usWinAscent = " + OS2Table.usWinAscent; v.Warning(T.hhea_Ascender_usWinAscent, W.hhea_W_Ascender_usWinAscent, m_tag, s); } else { v.Pass(T.hhea_Ascender_usWinAscent, P.hhea_P_Ascender_usWinAscent, m_tag); } } if (v.PerformTest(T.hhea_Descender_usWinDescent)) { if (OS2Table.usWinDescent != (Descender * -1)) { string s = "hhea.Descender = " + Descender + ", OS/2.usWinDescent = " + OS2Table.usWinDescent; v.Warning(T.hhea_Descender_usWinDescent, W.hhea_W_Descender_usWinDescent, m_tag, s); } else { v.Pass(T.hhea_Descender_usWinDescent, P.hhea_P_Descender_usWinDescent, m_tag); } } // Microsoft is recommending that Ascender, Descender and LineGap be in line with OS2.winAscent, OS2.winDescent // and formulated value for LineGap to make sure font displays the same on Apple as it does no Windows. if (v.PerformTest(T.hhea_LineGap_minGap)) { int sMinGap = (OS2Table.usWinAscent + OS2Table.usWinDescent) - (Ascender - Descender); if (LineGap < sMinGap) { string s = "LineGap = " + LineGap + ", recommended = " + sMinGap; v.Warning(T.hhea_LineGap_minGap, W.hhea_W_LineGap_minGap, m_tag, s); //bRet = false; } else { v.Pass(T.hhea_LineGap_minGap, P.hhea_P_LineGap_minGap, m_tag); } } else { v.Error(T.hhea_LineGap_minGap, E._TEST_E_TableMissing, m_tag, "OS/2"); } } else { v.Error(T.hhea_Ascender_usWinAscent, E._TEST_E_TableMissing, m_tag, "OS/2"); v.Error(T.hhea_Descender_usWinDescent, E._TEST_E_TableMissing, m_tag, "OS/2"); v.Error(T.hhea_LineGap_minGap, E._TEST_E_TableMissing, m_tag, "OS/2"); bRet = false; } if (v.PerformTest(T.hhea_MinMax)) { if (fontOwner.ContainsTrueTypeOutlines()) { Table_hmtx hmtxTable = (Table_hmtx)fontOwner.GetTable("hmtx"); Table_glyf glyfTable = (Table_glyf)fontOwner.GetTable("glyf"); Table_maxp maxpTable = (Table_maxp)fontOwner.GetTable("maxp"); if (hmtxTable == null) { v.Error(T.hhea_MinMax, E._TEST_E_TableMissing, m_tag, "hmtx"); bRet = false; } else if (glyfTable == null) { v.Error(T.hhea_MinMax, E._TEST_E_TableMissing, m_tag, "glyf"); bRet = false; } else if (maxpTable == null) { v.Error(T.hhea_MinMax, E._TEST_E_TableMissing, m_tag, "maxp"); bRet = false; } else { ushort numGlyphs = fontOwner.GetMaxpNumGlyphs(); ushort awMax = 0; short minLSB = 32767; short minRSB = 32767; short xmaxEx = -32768; Table_hmtx.longHorMetric hm = null; for (uint iGlyph = 0; iGlyph < numGlyphs; iGlyph++) { hm = hmtxTable.GetOrMakeHMetric(iGlyph, fontOwner); if (hm == null) { break; } if (awMax < hm.advanceWidth) { awMax = hm.advanceWidth; // We want to give the user feedback to know what glyph id // has the bad width assigned if (awMax > advanceWidthMax) { string s = "glyph ID = " + iGlyph + ", advance width = " + awMax; v.Error(T.hhea_MinMax, E.hhea_E_advanceWidthMax, m_tag, s); bRet = false; } } Table_glyf.header gh = glyfTable.GetGlyphHeader(iGlyph, fontOwner); // calculate for all non-zero contours...this includes composites if (gh != null && gh.numberOfContours != 0) { if (minLSB > hm.lsb) { minLSB = hm.lsb; } short rsb = (short)(hm.advanceWidth - hm.lsb - (gh.xMax - gh.xMin)); if (minRSB > rsb) { minRSB = rsb; } short extent = (short)(hm.lsb + (gh.xMax - gh.xMin)); if (xmaxEx < extent) { xmaxEx = extent; } } } if (hm != null) { if (advanceWidthMax == awMax) { v.Pass(T.hhea_MinMax, P.hhea_P_advanceWidthMax, m_tag); } else { string s = "actual = " + advanceWidthMax + ", calc = " + awMax; v.Error(T.hhea_MinMax, E.hhea_E_advanceWidthMax, m_tag, s); bRet = false; } if (minLeftSideBearing == minLSB) { v.Pass(T.hhea_MinMax, P.hhea_P_minLeftSideBearing, m_tag); } else { string s = "actual = " + minLeftSideBearing + ", calc = " + minLSB; v.Error(T.hhea_MinMax, E.hhea_E_minLeftSideBearing, m_tag, s); bRet = false; } if (minRightSideBearing == minRSB) { v.Pass(T.hhea_MinMax, P.hhea_P_minRightSideBearing, m_tag); } else { string s = "actual = " + minRightSideBearing + ", calc = " + minRSB; v.Error(T.hhea_MinMax, E.hhea_E_minRightSideBearing, m_tag, s); bRet = false; } if (xMaxExtent == xmaxEx) { v.Pass(T.hhea_MinMax, P.hhea_P_xMaxExtent, m_tag); } else { string s = "actual = " + xMaxExtent + ", calc = " + xmaxEx; v.Error(T.hhea_MinMax, E.hhea_E_xMaxExtent, m_tag, s); bRet = false; } } else { v.Warning(T.hhea_MinMax, W.hhea_W_hmtx_invalid, m_tag, "unable to parse hmtx table"); } } } else { v.Info(T.hhea_MinMax, I._TEST_I_NotForCFF, m_tag, "test = hhea_MinMax"); } } if (v.PerformTest(T.hhea_reserved)) { if (reserved1 != 0) { v.Error(T.hhea_reserved, E.hhea_E_reserved1, m_tag, reserved1.ToString()); bRet = false; } else if (reserved2 != 0) { v.Error(T.hhea_reserved, E.hhea_E_reserved2, m_tag, reserved2.ToString()); bRet = false; } else if (reserved3 != 0) { v.Error(T.hhea_reserved, E.hhea_E_reserved3, m_tag, reserved3.ToString()); bRet = false; } else if (reserved4 != 0) { v.Error(T.hhea_reserved, E.hhea_E_reserved4, m_tag, reserved4.ToString()); bRet = false; } else { v.Pass(T.hhea_reserved, P.hhea_P_reserved, m_tag); } } if (v.PerformTest(T.hhea_metricDataFormat)) { if (metricDataFormat == 0) { v.Pass(T.hhea_metricDataFormat, P.hhea_P_metricDataFormat, m_tag); } else { v.Error(T.hhea_metricDataFormat, E.hhea_E_metricDataFormat, m_tag, metricDataFormat.ToString()); bRet = false; } } if (v.PerformTest(T.hhea_numberOfHMetrics)) { Table_hmtx hmtxTable = (Table_hmtx)fontOwner.GetTable("hmtx"); Table_maxp maxpTable = (Table_maxp)fontOwner.GetTable("maxp"); if (hmtxTable == null) { v.Error(T.hhea_numberOfHMetrics, E._TEST_E_TableMissing, m_tag, "hmtx"); bRet = false; } else if (maxpTable == null) { v.Error(T.hhea_numberOfHMetrics, E._TEST_E_TableMissing, m_tag, "maxp"); bRet = false; } else { ushort numGlyphs = fontOwner.GetMaxpNumGlyphs(); if (numberOfHMetrics * 4 + (numGlyphs - numberOfHMetrics) * 2 == hmtxTable.GetLength()) { v.Pass(T.hhea_numberOfHMetrics, P.hhea_P_numberOfHMetrics, m_tag); } else { v.Error(T.hhea_numberOfHMetrics, E.hhea_E_numberOfHMetrics, m_tag); bRet = false; } } } if (v.PerformTest(T.hhea_caretSlope)) { bool bSlopeOk = true; Table_post postTable = (Table_post)fontOwner.GetTable("post"); if (postTable != null) { uint ia = postTable.italicAngle.GetUint(); double dItalicAngle = postTable.italicAngle.GetDouble(); if (ia == 0) { if (caretSlopeRun != 0) { v.Error(T.hhea_caretSlope, E.hhea_E_caretSlopeRunNonZero_italicAngle, m_tag); bSlopeOk = false; bRet = false; } } else { if (caretSlopeRun == 0) { v.Error(T.hhea_caretSlope, E.hhea_E_caretSlopeRunZero_italicAngle, m_tag); bSlopeOk = false; bRet = false; } else { double dActualAngle = 90.0 + postTable.italicAngle.GetDouble(); double dhheaAngle = (Math.Atan2(caretSlopeRise, caretSlopeRun)) * (180.0 / Math.PI); if (Math.Abs(dActualAngle - dhheaAngle) >= 1.0) { string sDetails = "caretSlope Rise:Run = " + caretSlopeRise + ":" + caretSlopeRun + " (" + (dhheaAngle - 90.0) + " degrees)" + ", post.italicAngle = 0x" + ia.ToString("x8") + " (" + postTable.italicAngle.GetDouble() + " degrees)"; v.Error(T.hhea_caretSlope, E.hhea_E_caretSlopeAngle_italicAngle, m_tag, sDetails); bSlopeOk = false; bRet = false; } } } } else { v.Error(T.hhea_caretSlope, E._TEST_E_TableMissing, m_tag, "post table missing, can't compare caret slope to italicAngle"); bSlopeOk = false; bRet = false; } if (bSlopeOk) { v.Pass(T.hhea_caretSlope, P.hhea_P_caretSlopeAngle_italicAngle, m_tag); } } return(bRet); }
/************************ * 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; } } 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); }
/************************ * public methods */ public bool Validate(Validator v, OTFontVal fontOwner) { bool bRet = true; if (v.PerformTest(T.kern_TableVersion)) { if (version == 0) { v.Pass(T.kern_TableVersion, P.kern_P_TableVersion, m_tag); } else { v.Error(T.kern_TableVersion, E.kern_E_TableVersion, m_tag, "version = " + version.ToString() + ", unrecognized version #, no further tests can be performed"); return(false); } } if (v.PerformTest(T.kern_NumSubtables)) { if (nTables != 0) { v.Pass(T.kern_NumSubtables, P.kern_P_NumSubTables, m_tag, nTables.ToString()); } else { v.Error(T.kern_NumSubtables, E.kern_E_NumSubTables, m_tag); bRet = false; } } if (v.PerformTest(T.kern_SubtableFormats)) { bool bFormatsOk = true; for (uint i = 0; i < nTables; i++) { SubTableHeader sth = GetSubTableHeader(i); if (sth != null) { if (sth.GetFormat() != 0 && sth.GetFormat() != 2) { v.Error(T.kern_SubtableFormats, E.kern_E_SubtableFormats, m_tag, "subtable #" + i + ", format " + sth.GetFormat()); bFormatsOk = false; bRet = false; } } else { v.Error(T.kern_SubtableFormats, E.kern_E_SubTableExtendsPastEOT, m_tag, "subtable #" + i); bFormatsOk = false; bRet = false; break; } } if (bFormatsOk) { v.Pass(T.kern_SubtableFormats, P.kern_P_SubtableFormats, m_tag); } } if (!bRet) { v.Warning(T.kern_SubtableFormats, W._TEST_W_OtherErrorsInTable, m_tag, "kern table appears to be corrupt. No further tests will be performed."); return(bRet); } if (v.PerformTest(T.kern_SubtableLength)) { bool bLengthsOk = true; for (uint i = 0; i < nTables; i++) { SubTable st = this.GetSubTable(i); if (st.length != st.CalculatedLength()) { v.Error(T.kern_SubtableLength, E.kern_E_SubtableLength, m_tag, "subtable #" + i + ", length = " + st.length + ", calculated length = " + st.CalculatedLength()); bLengthsOk = false; bRet = false; } } if (bLengthsOk) { v.Pass(T.kern_SubtableLength, P.kern_P_SubtableLengths, m_tag); } } if (v.PerformTest(T.kern_CoverageReservedBits)) { bool bReservedOk = true; for (uint i = 0; i < nTables; i++) { SubTable st = this.GetSubTable(i); if ((st.coverage & 0xf0) != 0) { bReservedOk = false; v.Error(T.kern_CoverageReservedBits, E.kern_E_ReservedCoverageBits, m_tag, "subtable #" + i); bRet = false; break; } } if (bReservedOk) { v.Pass(T.kern_CoverageReservedBits, P.kern_P_ReservedCoverageBits, m_tag); } } if (v.PerformTest(T.kern_Format0_SearchFields)) { bool bBinaryFieldsOk = true; for (uint i = 0; i < nTables; i++) { SubTable st = this.GetSubTable(i); if (st.version == 0) { SubTableFormat0 stf0 = (SubTableFormat0)st; ushort nPairs = stf0.nPairs; ushort sizeofEntry = 6; if (nPairs != 0) { ushort CalculatedSearchRange = (ushort)(util.MaxPower2LE(nPairs) * sizeofEntry); ushort CalculatedEntrySelector = util.Log2(util.MaxPower2LE(nPairs)); ushort CalculatedRangeShift = (ushort)((nPairs - util.MaxPower2LE(nPairs)) * sizeofEntry); if (stf0.searchRange != CalculatedSearchRange) { string s = "subtable #" + i + ", calc = " + CalculatedSearchRange + ", actual = " + stf0.searchRange; v.Error(T.kern_Format0_SearchFields, E.kern_E_Format0_searchRange, m_tag, s); bBinaryFieldsOk = false; bRet = false; } if (stf0.entrySelector != CalculatedEntrySelector) { string s = "subtable #" + i + ", calc = " + CalculatedEntrySelector + ", actual = " + stf0.entrySelector; v.Error(T.kern_Format0_SearchFields, E.kern_E_Format0_entrySelector, m_tag, s); bBinaryFieldsOk = false; bRet = false; } if (stf0.rangeShift != CalculatedRangeShift) { string s = "subtable #" + i + ", calc = " + CalculatedRangeShift + ", actual = " + stf0.rangeShift; v.Error(T.kern_Format0_SearchFields, E.kern_E_Format0_rangeShift, m_tag, s); bBinaryFieldsOk = false; bRet = false; } } else { // cannot validate fields since they are undefined when nPairs is zero v.Warning(T.kern_Format0_SearchFields, W.kern_W_Format0_SearchFields, m_tag, "subtable #" + i + ", nPairs = " + stf0.nPairs); bBinaryFieldsOk = false; } } } if (bBinaryFieldsOk) { v.Pass(T.kern_Format0_SearchFields, P.kern_P_Format0_SearchFields, m_tag); } } if (v.PerformTest(T.kern_Format0_GlyphIDs)) { for (uint i = 0; i < nTables; i++) { SubTable st = this.GetSubTable(i); if (st.version == 0) { bool bGlyphIDsOk = true; SubTableFormat0 stf0 = (SubTableFormat0)st; for (int iPair = 0; iPair < stf0.nPairs; iPair++) { ushort left = 0, right = 0; short kernvalue = 0; stf0.GetKerningPairAndValue(iPair, ref left, ref right, ref kernvalue); ushort numGlyphs = fontOwner.GetMaxpNumGlyphs(); if (left >= numGlyphs) { v.Error(T.kern_Format0_GlyphIDs, E.kern_E_Format0_GlyphIDs, m_tag, "kern pair[" + iPair + "]: left = " + left); bGlyphIDsOk = false; bRet = false; } if (right >= numGlyphs) { v.Error(T.kern_Format0_GlyphIDs, E.kern_E_Format0_GlyphIDs, m_tag, "kern pair[" + iPair + "]: right = " + right); bGlyphIDsOk = false; bRet = false; } } if (bGlyphIDsOk) { v.Pass(T.kern_Format0_GlyphIDs, P.kern_P_Format0_GlyphIDs, m_tag); } } } } if (v.PerformTest(T.kern_Format0_Values)) { for (uint i = 0; i < nTables; i++) { SubTable st = this.GetSubTable(i); if (st.version == 0) { SubTableFormat0 stf0 = (SubTableFormat0)st; Table_hmtx hmtxTable = (Table_hmtx)fontOwner.GetTable("hmtx"); if (hmtxTable != null) { bool bValuesOk = true; for (int iPair = 0; iPair < stf0.nPairs; iPair++) { ushort left = 0, right = 0; short kernvalue = 0; stf0.GetKerningPairAndValue(iPair, ref left, ref right, ref kernvalue); short absKernValue = Math.Abs(kernvalue); Table_hmtx.longHorMetric lhmLeft = null; Table_hmtx.longHorMetric lhmRight = null; try { lhmLeft = hmtxTable.GetOrMakeHMetric(left, fontOwner); lhmRight = hmtxTable.GetOrMakeHMetric(right, fontOwner); } catch (Exception e) { v.ApplicationError(T.kern_Format0_Values, E._Table_E_Exception, m_tag, "GetOrMakeHMetric:" + e.Message); bValuesOk = false; bRet = false; break; } if (lhmLeft == null || lhmRight == null) { v.ApplicationError(T.kern_Format0_Values, E._Table_E_Exception, m_tag, "hmtx Left or Right null"); bValuesOk = false; bRet = false; break; } if (absKernValue > lhmLeft.advanceWidth && absKernValue > lhmRight.advanceWidth) { v.Error(T.kern_Format0_Values, E.kern_E_Format0_Values, m_tag, "kern pair[" + iPair + "]: left id = " + left + ", right id = " + right + ", value = " + kernvalue); bValuesOk = false; bRet = false; } } if (bValuesOk) { v.Pass(T.kern_Format0_Values, P.kern_P_Format0_Values, m_tag); } } } } } if (v.PerformTest("kern_Format0_IDsInCmap")) { for (uint i = 0; i < nTables; i++) { SubTable st = this.GetSubTable(i); if (st.version == 0) { SubTableFormat0 stf0 = (SubTableFormat0)st; Table_cmap cmapTable = (Table_cmap)fontOwner.GetTable("cmap"); if (cmapTable != null) { Table_cmap.Subtable cmapSubtable = cmapTable.GetSubtable(3, 10); if (cmapSubtable == null) { cmapSubtable = cmapTable.GetSubtable(3, 1); } if (cmapSubtable == null) { Table_cmap.EncodingTableEntry ete = cmapTable.GetEncodingTableEntry(0); cmapSubtable = cmapTable.GetSubtable(ete); } if (cmapSubtable != null) { uint [] map = cmapSubtable.GetMap(); bool bAllIDsInCmap = true; for (int iPair = 0; iPair < stf0.nPairs; iPair++) { ushort left = 0, right = 0; short kernvalue = 0; stf0.GetKerningPairAndValue(iPair, ref left, ref right, ref kernvalue); bool bFoundLeft = false; bool bFoundRight = false; for (int j = 0; j < map.Length; j++) { if (left == map[j]) { bFoundLeft = true; break; } } for (int j = 0; j < map.Length; j++) { if (right == map[j]) { bFoundRight = true; break; } } if (bFoundLeft == false) { v.Error(T.kern_Format0_IDsInCmap, E.kern_E_Format0_GlyphIdInCmap, m_tag, "kern pair[" + iPair + "]: left id = " + left); bAllIDsInCmap = false; bRet = false; } if (bFoundRight == false) { v.Error(T.kern_Format0_IDsInCmap, E.kern_E_Format0_GlyphIdInCmap, m_tag, "kern pair[" + iPair + "]: right id = " + right); bAllIDsInCmap = false; bRet = false; } } if (bAllIDsInCmap) { v.Pass(T.kern_Format0_IDsInCmap, P.kern_P_Format0_GlyphIdInCmap, m_tag); } } } } } } return(bRet); }
/************************ * public methods */ public bool Validate(Validator v, OTFontVal fontOwner) { bool bRet = true; if (v.PerformTest(T.PCLT_TableLength)) { if (GetLength() == 54) { v.Pass(T.PCLT_TableLength, P.PCLT_P_TableLength, m_tag); } else { v.Error(T.PCLT_TableLength, E.PCLT_E_TableLength, m_tag, GetLength().ToString()); bRet = false; } } if (v.PerformTest(T.PCLT_Version)) { if (Version.GetUint() == 0x00010000) { v.Pass(T.PCLT_Version, P.PCLT_P_Version, m_tag); } else { v.Error(T.PCLT_Version, E.PCLT_E_Version, m_tag, "0x" + Version.GetUint().ToString("x8")); bRet = false; } } if (v.PerformTest(T.PCLT_Pitch)) { Table_hmtx hmtxTable = (Table_hmtx)fontOwner.GetTable("hmtx"); Table_maxp maxpTable = (Table_maxp)fontOwner.GetTable("maxp"); if (hmtxTable == null) { v.Error(T.PCLT_Pitch, E._TEST_E_TableMissing, m_tag, "hmtx"); bRet = false; } else if (maxpTable == null) { v.Error(T.PCLT_Pitch, E._TEST_E_TableMissing, m_tag, "maxp"); bRet = false; } else { uint iSpaceGlyph = fontOwner.FastMapUnicodeToGlyphID(' '); if (iSpaceGlyph < fontOwner.GetMaxpNumGlyphs()) { Table_hmtx.longHorMetric hmSpace = hmtxTable.GetOrMakeHMetric(iSpaceGlyph, fontOwner); if (hmSpace != null) { if (Pitch == hmSpace.advanceWidth) { v.Pass(T.PCLT_Pitch, P.PCLT_P_Pitch, m_tag); } else { string s = "actual = " + Pitch + ", expected = " + hmSpace.advanceWidth; v.Error(T.PCLT_Pitch, E.PCLT_E_Pitch, m_tag, s); bRet = false; } } } else { // JJF Figure out what to do v.Warning(T.PCLT_Pitch, W._TEST_W_ErrorInAnotherTable, m_tag, "can't validate Pitch field, error getting the space glyph"); bRet = false; } } } if (v.PerformTest(T.PCLT_Style)) { ushort Posture = (ushort)(Style & 0x0003); ushort Width = (ushort)((Style >> 2) & 0x0007); ushort Structure = (ushort)((Style >> 5) & 0x001f); ushort Reserved = (ushort)(Style >> 10); bool bBitsOk = true; if (Posture == 3) { v.Error(T.PCLT_Style, E.PCLT_E_Style_Posture, m_tag, "0x" + Style.ToString("x4")); bBitsOk = false; bRet = false; } if (Width == 5) { v.Error(T.PCLT_Style, E.PCLT_E_Style_Width, m_tag, "0x" + Style.ToString("x4")); bBitsOk = false; bRet = false; } if (Structure > 17) { v.Error(T.PCLT_Style, E.PCLT_E_Style_Structure, m_tag, "0x" + Style.ToString("x4")); bBitsOk = false; bRet = false; } if (Reserved != 0) { v.Error(T.PCLT_Style, E.PCLT_E_Style_Reserved, m_tag, "0x" + Style.ToString("x4")); bBitsOk = false; bRet = false; } if (bBitsOk) { v.Pass(T.PCLT_Style, P.PCLT_P_Style, m_tag); } } if (v.PerformTest(T.PCLT_StrokeWeight)) { if (StrokeWeight >= -7 && StrokeWeight <= 7) { v.Pass(T.PCLT_StrokeWeight, P.PCLT_P_StrokeWeight, m_tag, StrokeWeight.ToString()); } else { v.Error(T.PCLT_StrokeWeight, E.PCLT_E_StrokeWeight, m_tag, StrokeWeight.ToString()); bRet = false; } } if (v.PerformTest(T.PCLT_WidthType)) { if (WidthType >= -5 && WidthType <= 5) { v.Pass(T.PCLT_WidthType, P.PCLT_P_WidthType, m_tag, WidthType.ToString()); } else { v.Error(T.PCLT_WidthType, E.PCLT_E_WidthType, m_tag, WidthType.ToString()); bRet = false; } } if (v.PerformTest(T.PCLT_SerifStyle)) { uint bot6 = (uint)SerifStyle & 0x3f; uint top2 = (uint)SerifStyle >> 6; bool bBitsOk = true; if (bot6 > 12) { v.Error(T.PCLT_SerifStyle, E.PCLT_E_Bottom6, m_tag, "0x" + SerifStyle.ToString("x2")); bBitsOk = false; bRet = false; } if (top2 == 0 || top2 == 3) { v.Error(T.PCLT_SerifStyle, E.PCLT_E_Top2, m_tag); bBitsOk = false; bRet = false; } if (bBitsOk) { v.Pass(T.PCLT_SerifStyle, P.PCLT_P_SerifStyle, m_tag); } } if (v.PerformTest(T.PCLT_Reserved)) { if (Reserved == 0) { v.Pass(T.PCLT_Reserved, P.PCLT_P_Reserved, m_tag); } else { v.Error(T.PCLT_Reserved, E.PCLT_E_Reserved, m_tag, Reserved.ToString()); bRet = false; } } return(bRet); }