/************************ * 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.head_TableLength)) { if (m_bufTable.GetLength() == 54) { v.Pass(T.head_TableLength, P.head_P_TableLength, m_tag); } else { v.Error(T.head_TableLength, E.head_E_TableLength, m_tag, m_bufTable.GetLength().ToString()); bRet = false; } } if (v.PerformTest(T.head_TableVersion)) { if (TableVersionNumber.GetUint() == 0x00010000) { v.Pass(T.head_TableVersion, P.head_P_TableVersion, m_tag); } else { v.Error(T.head_TableVersion, E.head_E_TableVersion, m_tag, "0x" + TableVersionNumber.GetUint().ToString("x8")); bRet = false; } } if (v.PerformTest(T.head_fontRevision)) { string sVersion = fontOwner.GetFontVersion(); if (sVersion != null) { if (sVersion.Length >= 11 && sVersion.StartsWith("Version ") && Char.IsDigit(sVersion, 8)) { string sVersionNum = sVersion.Substring(8); bool bFoundDecPt = false; int nLastDigitPos = 0; for (int i = 0; i < sVersionNum.Length; i++) { if (Char.IsDigit(sVersionNum, i)) { nLastDigitPos = i; } else if (sVersionNum[i] == '.') { if (!bFoundDecPt) { bFoundDecPt = true; } else { break; } } else { break; } } double fVersion = Double.Parse(sVersionNum.Substring(0, nLastDigitPos + 1)); double fRevision = fontRevision.GetDouble(); if (Math.Round(fVersion, 3) == Math.Round(fRevision, 3)) { v.Pass(T.head_fontRevision, P.head_P_fontRevision, m_tag, fRevision.ToString("f3")); } else { string s = "revision: " + fRevision.ToString("f3") + ", version: " + sVersionNum; v.Warning(T.head_fontRevision, W.head_W_fontRevision, m_tag, s); } } } } if (v.PerformTest(T.head_ChecksumAdjustment)) { if (checkSumAdjustment == 0xb1b0afba - fontOwner.CalcChecksum()) { v.Pass(T.head_ChecksumAdjustment, P.head_P_FontChecksum, m_tag, "0x" + checkSumAdjustment.ToString("x8")); } else { v.Error(T.head_ChecksumAdjustment, E.head_E_FontChecksum, m_tag, "0x" + checkSumAdjustment.ToString("x8")); bRet = false; } } if (v.PerformTest(T.head_MagicNumber)) { if (magicNumber == 0x5f0f3cf5) { v.Pass(T.head_MagicNumber, P.head_P_MagicNumber, m_tag); } else { v.Error(T.head_MagicNumber, E.head_E_MagicNumber, m_tag, "0x" + magicNumber.ToString("x8")); bRet = false; } } if (v.PerformTest(T.head_FlagTests)) { ushort val = flags; // bit 0 indicates baseline for font at y=0 // bit 1 indicates left sidebearing point at x=0 // bit 2 indicates instructions may depend on point size // bit 3 indicates forcing ppem to integer values instead of fractional values // bit 4 indicates non-linear scaling - if set allows presence of LTSH and/or hdmx tables Table_hdmx hdmxTable = (Table_hdmx)fontOwner.GetTable("hdmx"); Table_LTSH LTSHTable = (Table_LTSH)fontOwner.GetTable("LTSH"); if ((val & 0x0010) == 0) { if (hdmxTable == null) { v.Pass(T.head_FlagTests, P.head_P_flags_bit4_0_hdmx, m_tag); } else { v.Error(T.head_FlagTests, E.head_E_flags_bit4_0_hdmx, m_tag); } if (LTSHTable == null) { v.Pass(T.head_FlagTests, P.head_P_flags_bit4_0_LTSH, m_tag); } else { v.Error(T.head_FlagTests, E.head_E_flags_bit4_0_LTSH, m_tag); } } else { if (hdmxTable != null) { v.Pass(T.head_FlagTests, P.head_P_flags_bit4_1_hdmx, m_tag); } else { v.Warning(T.head_FlagTests, W.head_W_flags_bit4_1_hdmx, m_tag); } if (LTSHTable != null) { v.Pass(T.head_FlagTests, P.head_P_flags_bit4_1_LTSH, m_tag); } else { v.Warning(T.head_FlagTests, W.head_W_flags_bit4_1_LTSH, m_tag); } } // bits 5 - 10 are used by apple and ignored by windows // bit 11 indicates font data is 'lossless' as a result of having been compressed/decompressed // bit 12 indicates a converted font // bit 13 indicates optimized for cleartype // bit 14 reserved if ((val & 0x4000) == 0) { v.Pass(T.head_FlagTests, P.head_P_flags_bit14, m_tag); } else { v.Error(T.head_FlagTests, E.head_E_flags_bit14, m_tag, val.ToString()); bRet = false; } // bit 15 reserved if ((val & 0x8000) == 0) { v.Pass(T.head_FlagTests, P.head_P_flags_bit15, m_tag); } else { v.Error(T.head_FlagTests, E.head_E_flags_bit15, m_tag, val.ToString()); bRet = false; } } if (v.PerformTest(T.head_UnitsPerEmValues)) { ushort val = unitsPerEm; bool bInRange = false; if (val < 16) // opentype spec says min value is 16 { v.Error(T.head_UnitsPerEmValues, E.head_E_unitsPerEm_LT16, m_tag, val.ToString()); bRet = false; } else if (val < 64) // apple spec says min value is 64 { v.Warning(T.head_UnitsPerEmValues, W.head_W_unitsPerEM_LT64, m_tag, val.ToString()); } else if (val > 16384) { v.Error(T.head_UnitsPerEmValues, E.head_E_unitsPerEM_GT16384, m_tag, val.ToString()); bRet = false; } else { bInRange = true; } // apple spec says unitsPerEm must be power of two // but don't check if it's a CFF font if (!fontOwner.ContainsPostScriptOutlines()) { bool bPowerOfTwo = false; for (int i = 0; i < 16; i++) { ushort nPow2 = (ushort)(1 << i); if (val == nPow2) { bPowerOfTwo = true; break; } } if (bPowerOfTwo) { if (bInRange) { v.Pass(T.head_UnitsPerEmValues, P.head_P_unitsPerEm, m_tag, val.ToString()); } } else { v.Warning(T.head_UnitsPerEmValues, W.head_W_unitsPerEm_Pow2, m_tag, val.ToString()); } } } if (v.PerformTest(T.head_Dates)) { DateTime dtBeforeTrueType = new DateTime(1985, 1, 1); if ((created >> 32) == 0) { DateTime dtCreated = this.GetCreatedDateTime(); string sDetails = "created = " + created + " (" + dtCreated.ToString("f", null) + ")"; if (created == 0) { v.Warning(T.head_Dates, W.head_W_created_0, m_tag); } else if (dtCreated < dtBeforeTrueType || dtCreated > DateTime.Now) { v.Warning(T.head_Dates, W.head_W_created_unlikely, m_tag, sDetails); } else { v.Pass(T.head_Dates, P.head_P_created_0, m_tag, sDetails); } } else { string sDetails = "created = 0x" + created.ToString("x16"); v.Error(T.head_Dates, E.head_E_created_invalid, m_tag, sDetails); bRet = false; } if ((modified >> 32) == 0) { DateTime dtModified = this.GetModifiedDateTime(); string sDetails = "modified = " + modified + " (" + dtModified.ToString("f", null) + ")"; if (modified == 0) { v.Warning(T.head_Dates, W.head_W_modified_0, m_tag); } else if (dtModified < dtBeforeTrueType || dtModified > DateTime.Now) { v.Warning(T.head_Dates, W.head_W_modified_unlikely, m_tag, sDetails); } else { v.Pass(T.head_Dates, P.head_P_modified_0, m_tag, sDetails); } } else { string sDetails = "modified = 0x" + modified.ToString("x16"); v.Error(T.head_Dates, E.head_E_modified_invalid, m_tag, sDetails); bRet = false; } } if (v.PerformTest(T.head_MinMaxValues)) { if (fontOwner.ContainsTrueTypeOutlines()) { if (xMin > xMax) { v.Error(T.head_MinMaxValues, E.head_E_xMin_GT_xMax, m_tag); bRet = false; } if (yMin > yMax) { v.Error(T.head_MinMaxValues, E.head_E_yMin_GT_yMax, m_tag); bRet = false; } short xMinExpected = 32767; short xMaxExpected = -32768; short yMinExpected = 32767; short yMaxExpected = -32768; Table_glyf glyfTable = (Table_glyf)fontOwner.GetTable("glyf"); if (glyfTable != null) { Table_maxp maxp = (Table_maxp)fontOwner.GetTable("maxp"); if (maxp != null) { for (uint i = 0; i < fontOwner.GetMaxpNumGlyphs(); i++) { Table_glyf.header h = glyfTable.GetGlyphHeader(i, fontOwner); if (h != null) { if (xMinExpected > h.xMin) { xMinExpected = h.xMin; } if (xMaxExpected < h.xMax) { xMaxExpected = h.xMax; } if (yMinExpected > h.yMin) { yMinExpected = h.yMin; } if (yMaxExpected < h.yMax) { yMaxExpected = h.yMax; } } } if (xMin == xMinExpected) { String s = "xMin = " + xMin; v.Pass(T.head_MinMaxValues, P.head_P_xMin_glyf, m_tag, s); } else { string s = "actual: " + xMin + ", expected: " + xMinExpected; v.Error(T.head_MinMaxValues, E.head_E_xMin_glyf, m_tag, s); bRet = false; } if (yMin == yMinExpected) { String s = "yMin = " + yMin; v.Pass(T.head_MinMaxValues, P.head_P_yMin_glyf, m_tag, s); } else { string s = "actual: " + yMin + ", expected: " + yMinExpected; v.Error(T.head_MinMaxValues, E.head_E_yMin_glyf, m_tag, s); bRet = false; } if (xMax == xMaxExpected) { String s = "xMax = " + xMax; v.Pass(T.head_MinMaxValues, P.head_P_xMax_glyf, m_tag, s); } else { string s = "actual: " + xMax + ", expected: " + xMaxExpected; v.Error(T.head_MinMaxValues, E.head_E_xMax_glyf, m_tag, s); bRet = false; } if (yMax == yMaxExpected) { String s = "yMax = " + yMax; v.Pass(T.head_MinMaxValues, P.head_P_yMax_glyf, m_tag, s); } else { string s = "actual: " + yMax + ", expected: " + yMaxExpected; v.Error(T.head_MinMaxValues, E.head_E_yMax_glyf, m_tag, s); bRet = false; } } else { v.Error(T.head_MinMaxValues, E._TEST_E_TableMissing, m_tag, "maxp"); } } else { v.Error(T.head_MinMaxValues, E._TEST_E_TableMissing, m_tag, "glyf"); } } else { v.Info(T.head_MinMaxValues, I._TEST_I_NotForCFF, m_tag, "test = head_MinMaxValues"); } } if (v.PerformTest(T.head_MacStyleBits)) { bool bMacBold = ((macStyle & 0x0001) != 0); bool bMacItal = ((macStyle & 0x0002) != 0); // subfamily (style) string Table_name nameTable = (Table_name)fontOwner.GetTable("name"); string sStyle = null; string sStyleLower = null; if (nameTable != null) { sStyle = nameTable.GetStyleString(); if (sStyle != null) { sStyleLower = sStyle.ToLower(); } } if (sStyleLower != null) { if (bMacBold && sStyleLower.IndexOf("bold") == -1) { v.Error(T.head_MacStyleBits, E.head_E_macStyleBold_subfamily, m_tag, "macStyle bold bit is set, but subfamily is " + sStyle); bRet = false; } else if (!bMacBold && sStyleLower.IndexOf("bold") != -1) { v.Error(T.head_MacStyleBits, E.head_E_macStyleBold_subfamily, m_tag, "macStyle bold bit is clear, but subfamily is " + sStyle); bRet = false; } else { v.Pass(T.head_MacStyleBits, P.head_P_macStyleBold_subfamily, m_tag); } if (bMacItal && sStyleLower.IndexOf("italic") == -1 && sStyleLower.IndexOf("oblique") == -1) { v.Error(T.head_MacStyleBits, E.head_E_macStyleItal_subfamily, m_tag, "macStyle italic bit is set, but subfamily is " + sStyle); bRet = false; } else if (!bMacItal && (sStyleLower.IndexOf("italic") != -1 || sStyleLower.IndexOf("oblique") != -1)) { v.Error(T.head_MacStyleBits, E.head_E_macStyleItal_subfamily, m_tag, "macStyle italic bit is clear, but subfamily is " + sStyle); bRet = false; } else { v.Pass(T.head_MacStyleBits, P.head_P_macStyleItal_subfamily, m_tag); } } Table_OS2 OS2Table = (Table_OS2)fontOwner.GetTable("OS/2"); if (OS2Table != null) { // fsSelection bool bOS2Bold = ((OS2Table.fsSelection & 0x0020) != 0); bool bOS2Ital = ((OS2Table.fsSelection & 0x0001) != 0); if (bMacBold == bOS2Bold) { v.Pass(T.head_MacStyleBits, P.head_P_macStyleBold_OS2, m_tag); } else if (bMacBold) { v.Error(T.head_MacStyleBits, E.head_E_macStyleBold1_OS2, m_tag); bRet = false; } else { v.Error(T.head_MacStyleBits, E.head_E_macStyleBold0_OS2, m_tag); bRet = false; } if (bMacItal == bOS2Ital) { v.Pass(T.head_MacStyleBits, P.head_P_macStyleItal_OS2, m_tag); } else if (bMacItal) { v.Error(T.head_MacStyleBits, E.head_E_macStyleItal1_OS2, m_tag); bRet = false; } else { v.Error(T.head_MacStyleBits, E.head_E_macStyleItal0_OS2, m_tag); bRet = false; } } Table_post postTable = (Table_post)fontOwner.GetTable("post"); if (postTable != null) { bool bPostItal = (postTable.italicAngle.GetUint() != 0); if (bMacItal == bPostItal) { v.Pass(T.head_MacStyleBits, P.head_P_macStyleItal_post, m_tag); } else if (bMacItal) { v.Error(T.head_MacStyleBits, E.head_E_macStyleItal1_post, m_tag); bRet = false; } else { v.Error(T.head_MacStyleBits, E.head_E_macStyleItal0_post, m_tag); bRet = false; } } } if (v.PerformTest(T.head_LowestRecSize)) { if (lowestRecPPEM == 0) { v.Error(T.head_LowestRecSize, E.head_E_lowestRecPPEM_zero, m_tag); bRet = false; } else if (lowestRecPPEM >= 1 && lowestRecPPEM <= 6) { v.Warning(T.head_LowestRecSize, W.head_W_lowestRecPPEM_small, m_tag, "lowestRecPPEM = " + lowestRecPPEM.ToString()); } else if (lowestRecPPEM >= 36) { v.Warning(T.head_LowestRecSize, W.head_W_lowestRecPPEM_large, m_tag, "lowestRecPPEM = " + lowestRecPPEM.ToString()); } else { v.Pass(T.head_LowestRecSize, P.head_P_lowestRecPPEM, m_tag); } } if (v.PerformTest(T.head_FontDirectionHint)) { if (fontDirectionHint >= -2 && fontDirectionHint <= 2) { v.Pass(T.head_FontDirectionHint, P.head_P_fontDirectionHint, m_tag, fontDirectionHint.ToString()); } else { v.Error(T.head_FontDirectionHint, E.head_E_fontDirectionHint, m_tag, fontDirectionHint.ToString()); bRet = false; } } if (v.PerformTest(T.head_IndexToLocFormat)) { if (fontOwner.ContainsPostScriptOutlines()) { v.Pass(T.head_IndexToLocFormat, P.head_P_indexToLocFormat_ignore, m_tag, "indexToLocFormat = " + indexToLocFormat); } else { if (indexToLocFormat == 0 || indexToLocFormat == 1) { v.Pass(T.head_IndexToLocFormat, P.head_P_indexToLocFormat_range, m_tag, indexToLocFormat.ToString()); } else { v.Error(T.head_IndexToLocFormat, E.head_E_indexToLocFormat_range, m_tag, indexToLocFormat.ToString()); bRet = false; } if (indexToLocFormat == 0 || indexToLocFormat == 1) { Table_loca locaTable = (Table_loca)fontOwner.GetTable("loca"); if (locaTable != null) { Table_maxp maxpTable = (Table_maxp)fontOwner.GetTable("maxp"); if (maxpTable != null) { uint locaTableElementSize = 0; if (indexToLocFormat == 0) { locaTableElementSize = 2; } else if (indexToLocFormat == 1) { locaTableElementSize = 4; } uint CalcLocaLength = (uint)(maxpTable.NumGlyphs + 1) * locaTableElementSize; if (CalcLocaLength == locaTable.GetLength()) { v.Pass(T.head_IndexToLocFormat, P.head_P_indexToLocFormat_match, m_tag, indexToLocFormat.ToString()); } else { v.Error(T.head_IndexToLocFormat, E.head_E_indexToLocFormat_match, m_tag, indexToLocFormat.ToString()); bRet = false; } } else { v.Error(T.head_IndexToLocFormat, E._TEST_E_TableMissing, m_tag, "maxp"); } } else { v.Error(T.head_IndexToLocFormat, E._TEST_E_TableMissing, m_tag, "loca"); } } } } if (v.PerformTest(T.head_GlyphDataFormat)) { if (glyphDataFormat == 0) { v.Pass(T.head_GlyphDataFormat, P.head_P_glyphDataFormat, m_tag); } else { v.Error(T.head_GlyphDataFormat, E.head_E_glyphDataFormat, m_tag); bRet = false; } } return(bRet); }
/************************ * public methods */ public bool Validate(Validator v, OTFontVal fontOwner) { bool bRet = true; if (v.PerformTest(T.name_FormatSelector)) { if (FormatSelector == 0) { v.Pass(T.name_FormatSelector, P.name_P_FormatSelector, m_tag); } else { v.Error(T.name_FormatSelector, E.name_E_FormatSelector, m_tag, FormatSelector.ToString()); bRet = false; } } if (v.PerformTest(T.name_StringsWithinTable)) { bool bStringsWithinTable = true; uint tableLength = GetLength(); for (uint i = 0; i < NumberNameRecords; i++) { NameRecord nr = GetNameRecord(i); if (nr != null) { if (nr.StringOffset + nr.StringLength > tableLength) { v.Error(T.name_StringsWithinTable, E.name_E_StringsWithinTable, m_tag, "string# " + i); bStringsWithinTable = false; bRet = false; } } else { v.Warning(T.name_StringsWithinTable, W._TEST_W_OtherErrorsInTable, m_tag); bStringsWithinTable = false; break; } } if (bStringsWithinTable) { v.Pass(T.name_StringsWithinTable, P.name_P_StringsWithinTable, m_tag); } } if (v.PerformTest(T.name_NameRecordsSorted)) { bool bSortedOrder = true; if (NumberNameRecords > 1) { NameRecord CurrNR = GetNameRecord(0); NameRecord NextNR = null; for (uint i = 0; i < NumberNameRecords - 1; i++) { NextNR = GetNameRecord(i + 1); if (CurrNR == null || NextNR == null) { bSortedOrder = false; break; } if (CurrNR.PlatformID > NextNR.PlatformID) { bSortedOrder = false; break; } else if (CurrNR.PlatformID == NextNR.PlatformID) { if (CurrNR.EncodingID > NextNR.EncodingID) { bSortedOrder = false; break; } else if (CurrNR.EncodingID == NextNR.EncodingID) { if (CurrNR.LanguageID > NextNR.LanguageID) { bSortedOrder = false; break; } else if (CurrNR.LanguageID == NextNR.LanguageID) { if (CurrNR.NameID > NextNR.NameID) { bSortedOrder = false; break; } } } } CurrNR = NextNR; } } if (bSortedOrder) { v.Pass(T.name_NameRecordsSorted, P.name_P_NameRecordsSorted, m_tag); } else { v.Error(T.name_NameRecordsSorted, E.name_E_NameRecordsSorted, m_tag); bRet = false; } } if (v.PerformTest(T.name_ReservedNameIDs)) { bool bReservedOk = true; for (uint i = 0; i < NumberNameRecords; i++) { NameRecord nr = GetNameRecord(i); if (nr != null) { if (nr.NameID >= 26 && nr.NameID <= 255) { string s = "platID = " + nr.PlatformID + ", encID = " + nr.EncodingID + ", langID = " + nr.LanguageID + ", nameID = " + nr.NameID; v.Error(T.name_ReservedNameIDs, E.name_E_ReservedNameID, m_tag, s); bReservedOk = false; break; } } else { v.Warning(T.name_ReservedNameIDs, W._TEST_W_OtherErrorsInTable, m_tag); bReservedOk = false; break; } } if (bReservedOk) { v.Pass(T.name_ReservedNameIDs, P.name_P_ReservedNameID, m_tag); } } if (v.PerformTest(T.name_BothPlatforms)) { bool bMac = false; bool bMS = false; for (uint i = 0; i < NumberNameRecords; i++) { NameRecord nr = GetNameRecord(i); if (nr != null) { if (nr.PlatformID == 1) { bMac = true; } else if (nr.PlatformID == 3) { bMS = true; } } } if (bMac && bMS) { v.Pass(T.name_BothPlatforms, P.name_P_BothPlatforms, m_tag); } else if (!bMac) { v.Error(T.name_BothPlatforms, E.name_E_NoMacPlatform, m_tag); bRet = false; } else if (!bMS) { v.Error(T.name_BothPlatforms, E.name_E_NoMSPlatform, m_tag); bRet = false; } } if (v.PerformTest(T.name_VersionString)) { bool bFound = false; string sVersion = ""; for (uint i = 0; i < NumberNameRecords; i++) { NameRecord nr = GetNameRecord(i); if (nr != null) { if (nr.PlatformID == 3 && // ms (nr.EncodingID == 1 /* unicode */ || nr.EncodingID == 0 /*symbol*/) && nr.NameID == 5) // version string { bFound = true; bool bVerStringValid = false; byte [] buf = GetEncodedString(nr); string s = ""; if (buf != null) { sVersion = ""; for (int j = 0; j < buf.Length / 2; j++) { char c = (char)(ushort)(buf[j * 2] << 8 | buf[j * 2 + 1]); sVersion += c; } if (sVersion.Length >= 11 && sVersion.StartsWith("Version ") && Char.IsDigit(sVersion, 8)) { int j = 9; // advance past the digits in the major number while (j < sVersion.Length) { if (Char.IsDigit(sVersion, j)) { j++; } else { break; } } // if major number is followed by a period if (sVersion[j] == '.') { // advance past the period j++; // check for a digit if (Char.IsDigit(sVersion, j)) { bVerStringValid = true; } } } } s = "platID = " + nr.PlatformID + ", encID = " + nr.EncodingID + ", langID = " + nr.LanguageID + ", \"" + sVersion + "\""; if (bVerStringValid) { v.Pass(T.name_VersionString, P.name_P_VersionStringFormat, m_tag, s); } else { v.Error(T.name_VersionString, E.name_E_VersionStringFormat, m_tag, s); bRet = false; } // compare to mac version string if present string sMacVer = GetString(1, 0, 0xffff, 5); if (sMacVer != null) { if (sVersion.CompareTo(sMacVer) != 0) { v.Warning(T.name_VersionString, W.name_W_VersionMismatch_MS_MAC, m_tag); } } // compare to 3,10 version string if present string s310Ver = GetString(3, 10, nr.LanguageID, 5); if (s310Ver != null) { if (sVersion.CompareTo(s310Ver) != 0) { string s310 = "platID = 3, encID = 10, langID = " + nr.LanguageID + ", \"" + s310Ver + "\""; v.Warning(T.name_VersionString, W.name_W_VersionMismatch_3_1_3_10, m_tag, s + " / " + s310); } } } } } if (!bFound) { v.Error(T.name_VersionString, E.name_E_VersionStringNotFound, m_tag); bRet = false; } } if (v.PerformTest(T.name_PlatformSpecificEncoding)) { bool bIDsOk = true; for (uint i = 0; i < NumberNameRecords; i++) { NameRecord nr = GetNameRecord(i); if (nr != null) { if (nr.PlatformID == 0) // unicode { if (nr.EncodingID > 7) { string s = "platID = " + nr.PlatformID + ", encID = " + nr.EncodingID + ", langID = " + nr.LanguageID + ", nameID = " + nr.NameID; v.Error(T.name_PlatformSpecificEncoding, E.name_E_PlatformSpecificEncoding, m_tag, s); bIDsOk = false; bRet = false; } } else if (nr.PlatformID == 1) // mac { if (nr.EncodingID > 32) { string s = "platID = " + nr.PlatformID + ", encID = " + nr.EncodingID + ", langID = " + nr.LanguageID + ", nameID = " + nr.NameID; v.Error(T.name_PlatformSpecificEncoding, E.name_E_PlatformSpecificEncoding, m_tag, s); bIDsOk = false; bRet = false; } } else if (nr.PlatformID == 2) // iso { if (nr.EncodingID > 2) { string s = "platID = " + nr.PlatformID + ", encID = " + nr.EncodingID + ", langID = " + nr.LanguageID + ", nameID = " + nr.NameID; v.Error(T.name_PlatformSpecificEncoding, E.name_E_PlatformSpecificEncoding, m_tag, s); bIDsOk = false; bRet = false; } } else if (nr.PlatformID == 3) // MS { if (nr.EncodingID > 10) { string s = "platID = " + nr.PlatformID + ", encID = " + nr.EncodingID + ", langID = " + nr.LanguageID + ", nameID = " + nr.NameID; v.Error(T.name_PlatformSpecificEncoding, E.name_E_PlatformSpecificEncoding, m_tag, s); bIDsOk = false; bRet = false; } } /* * else if (nr.PlatformID == 4) // Custom * { * } */ } else { v.Warning(T.name_PlatformSpecificEncoding, W._TEST_W_OtherErrorsInTable, m_tag); bIDsOk = false; break; } } if (bIDsOk) { v.Pass(T.name_PlatformSpecificEncoding, P.name_P_PlatformSpecificEncoding, m_tag); } } if (v.PerformTest(T.name_MSLanguageIDs)) { bool bFound = false; bool bIDsOk = true; ushort [] MSLangIDs = // taken from Q224804 { 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040D, 0x040e, 0x040F, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041D, 0x041E, 0x041f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0429, 0x042a, 0x042b, 0x042c, 0x042D, 0x042e, 0x042f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043b, 0x043d, 0x043e, 0x043f, 0x0441, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0x0457, 0x0459, 0x0461, 0x0801, 0x0804, 0x0807, 0x0809, 0x080a, 0x080c, 0x0810, 0x0812, 0x0813, 0x0814, 0x0816, 0x0818, 0x0819, 0x081a, 0x081d, 0x0820, 0x0827, 0x082c, 0x083e, 0x0843, 0x0860, 0x0861, 0x0C01, 0x0C04, 0x0c07, 0x0c09, 0x0c0a, 0x0c0c, 0x0c1a, 0x1001, 0x1004, 0x1007, 0x1009, 0x100a, 0x100c, 0x1401, 0x1404, 0x1407, 0x1409, 0x140a, 0x140c, 0x1801, 0x1809, 0x180a, 0x180c, 0x1C01, 0x1c09, 0x1c0a, 0x2001, 0x2009, 0x200a, 0x2401, 0x2409, 0x240a, 0x2801, 0x2809, 0x280a, 0x2C01, 0x2c09, 0x2c0a, 0x3001, 0x3009, 0x300a, 0x3401, 0x3409, 0x340a, 0x3801, 0x380a, 0x3C01, 0x3c0a, 0x4001, 0x400a, 0x440a, 0x480a, 0x4c0a, 0x500a }; for (uint i = 0; i < NumberNameRecords; i++) { NameRecord nr = GetNameRecord(i); if (nr != null) { if (nr.PlatformID == 3 && nr.EncodingID == 1) { bFound = true; bool bValidID = false; for (uint j = 0; j < MSLangIDs.Length; j++) { if (nr.LanguageID == MSLangIDs[j]) { bValidID = true; break; } } if (!bValidID) { string s = "platID = " + nr.PlatformID + ", encID = " + nr.EncodingID + ", langID = 0x" + nr.LanguageID.ToString("x4") + ", nameID = " + nr.NameID; v.Error(T.name_MSLanguageIDs, E.name_E_MSLanguageID, m_tag, s); bIDsOk = false; bRet = false; } } } } if (bFound && bIDsOk) { v.Pass(T.name_MSLanguageIDs, P.name_P_MSLanguageID, m_tag); } } if (v.PerformTest(T.name_unicode_length)) { bool bLengthsOk = true; for (uint i = 0; i < NumberNameRecords; i++) { NameRecord nr = GetNameRecord(i); if (nr != null) { if (nr.PlatformID == 0 || nr.PlatformID == 2 || // unicode or iso platform (nr.PlatformID == 3 && nr.EncodingID == 1)) // microsoft platform, unicode encoding { if ((nr.StringLength & 1) == 1) { string s = "platID = " + nr.PlatformID + ", encID = " + nr.EncodingID + ", langID = " + nr.LanguageID + ", nameID = " + nr.NameID + ", length = " + nr.StringLength; v.Error(T.name_unicode_length, E.name_E_UnicodeLength, m_tag, s); bLengthsOk = false; bRet = false; } } } else { v.Warning(T.name_unicode_length, W._TEST_W_OtherErrorsInTable, m_tag); bLengthsOk = false; break; } } if (bLengthsOk) { v.Pass(T.name_unicode_length, P.name_P_UnicodeLength, m_tag); } } if (v.PerformTest(T.name_Postscript)) { bool bPostscriptOk = true; string sPostscriptMac = GetString(1, 0, 0, 6); if (sPostscriptMac != null) { if (sPostscriptMac.Length > 63) { v.Error(T.name_Postscript, E.name_E_Postscript_length, m_tag, "name string (1, 0, 0, 6) is " + sPostscriptMac.Length + " characters long"); bRet = false; bPostscriptOk = false; } for (int i = 0; i < sPostscriptMac.Length; i++) { char c = sPostscriptMac[i]; if (c < 33 || c > 126 || c == '[' || c == ']' || c == '(' || c == ')' || c == '{' || c == '}' || c == '<' || c == '>' || c == '/' || c == '%') { v.Error(T.name_Postscript, E.name_E_Postscript_chars, m_tag, "name string (1, 0, 0, 6) contains an illegal character at index " + i); bRet = false; bPostscriptOk = false; } } } ushort nEncoding = 1; if (fontOwner.ContainsMsSymbolEncodedCmap()) { nEncoding = 0; } string sPostscriptMS = GetString(3, nEncoding, 0x409, 6); // ms if (sPostscriptMS != null) { if (sPostscriptMS.Length > 63) { v.Error(T.name_Postscript, E.name_E_Postscript_length, m_tag, "name string (3, " + nEncoding + ", 0x409, 6) is " + sPostscriptMS.Length + " characters long"); bRet = false; bPostscriptOk = false; } for (int i = 0; i < sPostscriptMS.Length; i++) { char c = sPostscriptMS[i]; if (c < 33 || c > 126 || c == '[' || c == ']' || c == '(' || c == ')' || c == '{' || c == '}' || c == '<' || c == '>' || c == '/' || c == '%') { v.Error(T.name_Postscript, E.name_E_Postscript_chars, m_tag, "name string (3, " + nEncoding + ", 0x409, 6) contains an illegal character at index " + i); bRet = false; bPostscriptOk = false; } } } if (sPostscriptMac == null && sPostscriptMS != null) { v.Error(T.name_Postscript, E.name_E_Postscript_missing, m_tag, "Mac Postscript string is missing, but MS Postscript string is present"); bRet = false; bPostscriptOk = false; } else if (sPostscriptMac != null && sPostscriptMS == null) { v.Error(T.name_Postscript, E.name_E_Postscript_missing, m_tag, "MS Postscript string is missing, but Mac Postscript string is present"); bRet = false; bPostscriptOk = false; } if (sPostscriptMac != null && sPostscriptMS != null) { if (sPostscriptMac != sPostscriptMS) { v.Error(T.name_Postscript, E.name_E_Postscript_unequal, m_tag, "mac postscript = " + sPostscriptMac + ", MS postscript = " + sPostscriptMS); bRet = false; bPostscriptOk = false; } } if (sPostscriptMac != null && sPostscriptMS != null && bPostscriptOk) { v.Pass(T.name_Postscript, P.name_P_Postscript, m_tag); } } if (v.PerformTest(T.name_Subfamily)) { string sStyle = GetStyleString(); if (sStyle != null) { Table_OS2 OS2Table = (Table_OS2)fontOwner.GetTable("OS/2"); if (OS2Table != null) { bool bStyleOk = true; string sStyleDetails = ""; string s = sStyle.ToLower(); bool bItalic = ((OS2Table.fsSelection & 0x01) != 0); bool bBold = ((OS2Table.fsSelection & 0x20) != 0); if (bItalic) { if (s.IndexOf("italic") == -1 && s.IndexOf("oblique") == -1) { bStyleOk = false; sStyleDetails = "OS/2.fsSelection italic bit is set, but subfamily string = '" + sStyle + "'"; } } else { if (s.IndexOf("italic") != -1 || s.IndexOf("oblique") != -1) { bStyleOk = false; sStyleDetails = "OS/2.fsSelection italic bit is clear, but subfamily string = '" + sStyle + "'"; } } if (bBold) { if (s.IndexOf("bold") == -1) { bStyleOk = false; sStyleDetails = "OS/2.fsSelection bold bit is set, but subfamily string = '" + sStyle + "'"; } } else { if (s.IndexOf("bold") != -1) { bStyleOk = false; sStyleDetails = "OS/2.fsSelection bold bit is clear, but subfamily string = '" + sStyle + "'"; } } if (bStyleOk) { v.Pass(T.name_Subfamily, P.name_P_subfamily, m_tag); } else { v.Warning(T.name_Subfamily, W.name_W_subfamily_style, m_tag, sStyleDetails); } } } } if (v.PerformTest(T.name_NoFormat14)) { bool bStringOK = true; for (uint i = 0; i < NumberNameRecords; i++) { NameRecord nr = GetNameRecord(i); if (nr != null && nr.NameID == 19 && nr.PlatformID == 0 && nr.EncodingID == 5) { string sDetails = "name string(" + nr.PlatformID + ", " + nr.EncodingID + ", 0x" + nr.LanguageID.ToString("x4") + ", " + nr.NameID + ", offset=0x" + nr.StringOffset.ToString("x4") + ")"; v.Error(T.name_NoFormat14, E.name_E_NoFormat14, m_tag, sDetails); bRet = false; bStringOK = false; } } if (bStringOK) { string sDetails = "PlatformID=0, EncodingID=5 is for " + "Variation Sequences (Format 14)"; v.Pass(T.name_NoFormat14, P.name_P_NoFormat14, m_tag, sDetails); } } if (v.PerformTest(T.name_SampleString)) { Table_cmap cmapTable = (Table_cmap)fontOwner.GetTable("cmap"); if (cmapTable != null) { for (uint i = 0; i < NumberNameRecords; i++) { NameRecord nr = GetNameRecord(i); if (nr != null) { if (nr.NameID == 19) { if (nr.PlatformID == 0 && nr.EncodingID == 5) { // Unicode platform encoding ID 5 can be // used for encodings in the 'cmap' table // but not for strings in the 'name' table. // It has already been flagged as an error, // so we will just skip it here. break; } Table_cmap.Subtable CmapSubTable = cmapTable.GetSubtable(nr.PlatformID, nr.EncodingID); if (CmapSubTable != null) { bool bStringOk = true; byte[] strbuf = GetEncodedString(nr); for (uint j = 0; j < strbuf.Length;) { if (CmapSubTable.MapCharToGlyph(strbuf, j, true) == 0) { string sDetails = "name string(" + nr.PlatformID + ", " + nr.EncodingID + ", 0x" + nr.LanguageID.ToString("x4") + ", " + nr.NameID + "), character at index " + j + " is not mapped"; v.Error(T.name_SampleString, E.name_E_sample, m_tag, sDetails); bStringOk = false; bRet = false; break; } j += CmapSubTable.BytesInChar(strbuf, j); } if (bStringOk) { string sDetails = "name string(" + nr.PlatformID + ", " + nr.EncodingID + ", 0x" + nr.LanguageID.ToString("x4") + ", " + nr.NameID + ")"; v.Pass(T.name_SampleString, P.name_P_sample, m_tag, sDetails); } } } } } } } if (v.PerformTest(T.name_NameID1to6)) { for (uint id = 1; id <= 6; id++) { bool bFound = false; for (uint i = 0; i < NumberNameRecords; i++) { NameRecord nr = GetNameRecord(i); if (nr != null) { if (nr.NameID == id) { bFound = true; break; } } } if (!bFound) { v.Warning(T.name_NameID1to6, W.name_W_ID_1to6_Required_For_Common_OSes, m_tag, "Missing Name ID " + id); } } } if (v.PerformTest(T.name_PreferredFamily)) { bool bFound = false; for (uint i = 0; i < NumberNameRecords; i++) { NameRecord nr = GetNameRecord(i); if (nr != null) { if (nr.NameID == 16) { string sPrefFam = this.GetString(nr.PlatformID, nr.EncodingID, nr.LanguageID, 16); string s = "platID = " + nr.PlatformID + ", encID = " + nr.EncodingID + ", langID = " + nr.LanguageID + ", nameID = " + nr.NameID + ", \"" + sPrefFam + "\""; v.Info(T.name_PreferredFamily, I.name_I_Preferred_family_present, m_tag, s); bFound = true; } } } if (!bFound) { v.Info(T.name_PreferredFamily, I.name_I_Preferred_family_not_present, m_tag); } } if (v.PerformTest(T.name_PreferredSubfamily)) { bool bFound = false; for (uint i = 0; i < NumberNameRecords; i++) { NameRecord nr = GetNameRecord(i); if (nr != null) { if (nr.NameID == 17) { string sPrefSubfam = this.GetString(nr.PlatformID, nr.EncodingID, nr.LanguageID, 17); string s = "platID = " + nr.PlatformID + ", encID = " + nr.EncodingID + ", langID = " + nr.LanguageID + ", nameID = " + nr.NameID + ", \"" + sPrefSubfam + "\""; v.Info(T.name_PreferredSubfamily, I.name_I_Preferred_subfamily_present, m_tag, s); bFound = true; } } } if (!bFound) { v.Info(T.name_PreferredSubfamily, I.name_I_Preferred_subfamily_not_present, m_tag); } } if (v.PerformTest(T.name_CopyrightConsistent)) { bool bCopyrightOk = true; // get mac roman English Copyright string if present string sMac = GetString(1, 0, 0, 0); // get windows 3,0 English Copyright string if present string sWin3_0 = GetString(3, 0, 1033, 0); // get windows 3,1 English Copyright string if present string sWin3_1 = GetString(3, 1, 1033, 0); // get windows 3,10 English Copyright string if present string sWin3_10 = GetString(3, 10, 1033, 0); // compare strings if (sMac != null) { if (sWin3_0 != null) { if (sWin3_0.CompareTo(sMac) != 0) { string sDetails = "(1,0,0,0)='" + sMac + "', (3,0,1033,0)='" + sWin3_0 + "'"; v.Warning(T.name_CopyrightConsistent, W.name_W_CopyrightInconsistent, m_tag, sDetails); bCopyrightOk = false; } } if (sWin3_1 != null) { if (sWin3_1.CompareTo(sMac) != 0) { string sDetails = "(1,0,0,0)='" + sMac + "', (3,1,1033,0)='" + sWin3_1 + "'"; v.Warning(T.name_CopyrightConsistent, W.name_W_CopyrightInconsistent, m_tag, sDetails); bCopyrightOk = false; } } if (sWin3_10 != null) { if (sWin3_10.CompareTo(sMac) != 0) { string sDetails = "(1,0,0,0)='" + sMac + "', (3,10,1033,0)='" + sWin3_10 + "'"; v.Warning(T.name_CopyrightConsistent, W.name_W_CopyrightInconsistent, m_tag, sDetails); bCopyrightOk = false; } } } if (sWin3_0 != null) { if (sWin3_1 != null) { if (sWin3_1.CompareTo(sWin3_0) != 0) { string sDetails = "(3,0,1033,0)='" + sWin3_0 + "', (3,1,1033,0)='" + sWin3_1 + "'"; v.Warning(T.name_CopyrightConsistent, W.name_W_CopyrightInconsistent, m_tag, sDetails); bCopyrightOk = false; } } if (sWin3_10 != null) { if (sWin3_10.CompareTo(sWin3_0) != 0) { string sDetails = "(3,0,1033,0)='" + sWin3_0 + "', (3,10,1033,0)='" + sWin3_10 + "'"; v.Warning(T.name_CopyrightConsistent, W.name_W_CopyrightInconsistent, m_tag, sDetails); bCopyrightOk = false; } } } if (sWin3_1 != null) { if (sWin3_10 != null) { if (sWin3_10.CompareTo(sWin3_1) != 0) { string sDetails = "(3,1,1033,0)='" + sWin3_1 + "', (3,10,1033,0)='" + sWin3_10 + "'"; v.Warning(T.name_CopyrightConsistent, W.name_W_CopyrightInconsistent, m_tag, sDetails); bCopyrightOk = false; } } } if (bCopyrightOk) { v.Pass(T.name_CopyrightConsistent, P.name_P_CopyrightConsistent, m_tag); } else { //bRet = false; } } if (v.PerformTest(T.name_TrademarkConsistent)) { bool bTrademarkOk = true; // get mac roman English Trademark string if present string sMac = GetString(1, 0, 0, 7); // get windows 3,0 English Trademark string if present string sWin3_0 = GetString(3, 0, 1033, 7); // get windows 3,1 English Trademark string if present string sWin3_1 = GetString(3, 1, 1033, 7); // get windows 3,10 English Trademark string if present string sWin3_10 = GetString(3, 10, 1033, 7); // compare strings if (sMac != null) { if (sWin3_0 != null) { if (sWin3_0.CompareTo(sMac) != 0) { string sDetails = "(1,0,0,7)='" + sMac + "', (3,0,1033,7)='" + sWin3_0 + "'"; v.Warning(T.name_TrademarkConsistent, W.name_W_TrademarkInconsistent, m_tag, sDetails); bTrademarkOk = false; } } if (sWin3_1 != null) { if (sWin3_1.CompareTo(sMac) != 0) { string sDetails = "(1,0,0,7)='" + sMac + "', (3,1,1033,7)='" + sWin3_1 + "'"; v.Warning(T.name_TrademarkConsistent, W.name_W_TrademarkInconsistent, m_tag, sDetails); bTrademarkOk = false; } } if (sWin3_10 != null) { if (sWin3_10.CompareTo(sMac) != 0) { string sDetails = "(1,0,0,7)='" + sMac + "', (3,10,1033,7)='" + sWin3_10 + "'"; v.Warning(T.name_TrademarkConsistent, W.name_W_TrademarkInconsistent, m_tag, sDetails); bTrademarkOk = false; } } } if (sWin3_0 != null) { if (sWin3_1 != null) { if (sWin3_1.CompareTo(sWin3_0) != 0) { string sDetails = "(3,0,1033,7)='" + sWin3_0 + "', (3,1,1033,7)='" + sWin3_1 + "'"; v.Warning(T.name_TrademarkConsistent, W.name_W_TrademarkInconsistent, m_tag, sDetails); bTrademarkOk = false; } } if (sWin3_10 != null) { if (sWin3_10.CompareTo(sWin3_0) != 0) { string sDetails = "(3,0,1033,7)='" + sWin3_0 + "', (3,10,1033,7)='" + sWin3_10 + "'"; v.Warning(T.name_TrademarkConsistent, W.name_W_TrademarkInconsistent, m_tag, sDetails); bTrademarkOk = false; } } } if (sWin3_1 != null) { if (sWin3_10 != null) { if (sWin3_10.CompareTo(sWin3_1) != 0) { string sDetails = "(3,1,1033,7)='" + sWin3_1 + "', (3,10,1033,7)='" + sWin3_10 + "'"; v.Warning(T.name_TrademarkConsistent, W.name_W_TrademarkInconsistent, m_tag, sDetails); bTrademarkOk = false; } } } if (bTrademarkOk) { v.Pass(T.name_TrademarkConsistent, P.name_P_TrademarkConsistent, m_tag); } else { //bRet = false; } } if (v.PerformTest(T.name_DescriptionConsistent)) { bool bDescriptionOk = true; // get mac roman English Description string if present string sMac = GetString(1, 0, 0, 10); // get windows 3,0 English Description string if present string sWin3_0 = GetString(3, 0, 1033, 10); // get windows 3,1 English Description string if present string sWin3_1 = GetString(3, 1, 1033, 10); // get windows 3,10 English Description string if present string sWin3_10 = GetString(3, 10, 1033, 10); // compare strings if (sMac != null) { if (sWin3_0 != null) { if (sWin3_0.CompareTo(sMac) != 0) { string sDetails = "(1,0,0,10)='" + sMac + "', (3,0,1033,10)='" + sWin3_0 + "'"; v.Warning(T.name_DescriptionConsistent, W.name_W_DescriptionInconsistent, m_tag, sDetails); bDescriptionOk = false; } } if (sWin3_1 != null) { if (sWin3_1.CompareTo(sMac) != 0) { string sDetails = "(1,0,0,10)='" + sMac + "', (3,1,1033,10)='" + sWin3_1 + "'"; v.Warning(T.name_DescriptionConsistent, W.name_W_DescriptionInconsistent, m_tag, sDetails); bDescriptionOk = false; } } if (sWin3_10 != null) { if (sWin3_10.CompareTo(sMac) != 0) { string sDetails = "(1,0,0,10)='" + sMac + "', (3,10,1033,10)='" + sWin3_10 + "'"; v.Warning(T.name_DescriptionConsistent, W.name_W_DescriptionInconsistent, m_tag, sDetails); bDescriptionOk = false; } } } if (sWin3_0 != null) { if (sWin3_1 != null) { if (sWin3_1.CompareTo(sWin3_0) != 0) { string sDetails = "(3,0,1033,10)='" + sWin3_0 + "', (3,1,1033,10)='" + sWin3_1 + "'"; v.Warning(T.name_DescriptionConsistent, W.name_W_DescriptionInconsistent, m_tag, sDetails); bDescriptionOk = false; } } if (sWin3_10 != null) { if (sWin3_10.CompareTo(sWin3_0) != 0) { string sDetails = "(3,0,1033,10)='" + sWin3_0 + "', (3,10,1033,10)='" + sWin3_10 + "'"; v.Warning(T.name_DescriptionConsistent, W.name_W_DescriptionInconsistent, m_tag, sDetails); bDescriptionOk = false; } } } if (sWin3_1 != null) { if (sWin3_10 != null) { if (sWin3_10.CompareTo(sWin3_1) != 0) { string sDetails = "(3,1,1033,10)='" + sWin3_1 + "', (3,10,1033,10)='" + sWin3_10 + "'"; v.Warning(T.name_DescriptionConsistent, W.name_W_DescriptionInconsistent, m_tag, sDetails); bDescriptionOk = false; } } } if (bDescriptionOk) { v.Pass(T.name_DescriptionConsistent, P.name_P_DescriptionConsistent, m_tag); } else { //bRet = false; } } 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); }