可安装 OpenType 字体的 Windows 要求是啥?
Posted
技术标签:
【中文标题】可安装 OpenType 字体的 Windows 要求是啥?【英文标题】:What are the windows requirements for an OpenType font to be installable?可安装 OpenType 字体的 Windows 要求是什么? 【发布时间】:2014-03-15 03:24:28 【问题描述】:利基编程问题:我正在通过字节制作来开发 OpenType 字体(而不是使用 Fontlab 或 FontForge 等设计工具),现在有一个自定义 OpenType-CFF 字体,它实现了基本拉丁语 unicode 块的子集(特别是它实现了 .notdef 和波浪号)。
CFF 块通过tx -3
验证(http://www.adobe.com/devnet/opentype/afdko.html),整个字体通过TTX
(https://github.com/behdad/fonttools)和微软的“字体验证器”(http://www.microsoft.com/typography/FontValidator.mspx ) 不会报告字体中的任何错误。将其作为自定义 webfont 加载(在支持 otf 的浏览器中作为纯 otf,在不支持 otf 的浏览器中包装为 WOFF)正确设置实现字形的样式。
然而,即使有这么明显的正确性,windows 字体预览器仍报告它不是一个有效的字体文件,window 不会让我安装它。我一直在寻找有关 Windows 需要什么字体才能安装字体的信息,但互联网似乎充满了如何以用户身份使用字体,而不是作为开发人员需要满足哪些要求。
我不太确定包含此问题的代码的最佳方法是什么,因为它不是很传统的编程;生成器代码是用 javascript 编写的,但该代码工作得非常好,并为所有意图和目的生成了一个(保存一个)正确的字体。
如果您知道如何使用十六进制编辑器,那么以下是字体的十六进制字符串:
4F 54 54 4F 00 09 00 80 00 03 00 10 43 46 46 20 03 00 B4 92 00 00 02 A4 00 00 00 B3 4F 53 2F 32
30 F6 24 D4 00 00 01 00 00 00 00 60 63 6D 61 70 00 0D 00 B7 00 00 02 50 00 00 00 32 68 65 61 64
61 E4 43 91 00 00 00 9C 00 00 00 36 68 68 65 61 06 96 01 52 00 00 00 D4 00 00 00 24 68 6D 74 78
02 A8 00 00 00 00 03 58 00 00 00 08 6D 61 78 70 00 02 50 00 00 00 00 F8 00 00 00 06 6E 61 6D 65
C6 CC FF EC 00 00 01 60 00 00 00 F0 70 6F 73 74 00 03 00 01 00 00 02 84 00 00 00 20 00 01 00 00
00 01 00 00 1A EA FF 64 5F 0F 3C F5 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 14 FF EC 02 BC 02 A8 00 00 00 08 00 02 00 00 00 00 00 00 00 01 00 00 03 EC FE A8 00 00 02 A8
00 00 00 00 02 A8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 50 00 00 02 00 00
00 03 00 00 01 90 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 20 3D 29 20 00 40
00 7E 00 7E 02 A8 FF EC 01 44 03 EC 01 58 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 7E 00 00
00 00 00 08 00 66 00 01 00 20 00 00 00 01 00 0B 00 00 00 01 00 20 00 00 00 02 00 07 00 21 00 01
00 20 00 00 00 04 00 11 00 36 00 01 00 20 00 00 00 05 00 0B 00 69 00 03 00 01 04 09 00 01 00 16
00 0B 00 03 00 01 04 09 00 02 00 0E 00 28 00 03 00 01 04 09 00 04 00 22 00 47 00 03 00 01 04 09
00 05 00 16 00 74 43 75 73 74 6F 6D 20 46 6F 6E 74 00 43 00 75 00 73 00 74 00 6F 00 6D 00 20 00
46 00 6F 00 6E 00 74 52 65 67 75 6C 61 72 00 52 00 65 00 67 00 75 00 6C 00 61 00 72 43 75 73 74
6F 6D 20 47 6C 79 70 68 20 46 6F 6E 74 00 43 00 75 00 73 00 74 00 6F 00 6D 00 20 00 47 00 6C 00
79 00 70 00 68 00 20 00 46 00 6F 00 6E 00 74 56 65 72 73 69 6F 6E 20 31 2E 30 00 56 00 65 00 72
00 73 00 69 00 6F 00 6E 00 20 00 31 00 2E 00 30 00 00 00 01 00 03 00 01 00 00 00 0C 00 04 00 26
00 00 00 04 00 04 00 01 00 00 00 7E FF FF 00 00 00 7E FF FF FF 83 00 01 00 00 00 00 00 00 00 00
00 01 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 01 00 04 01 00 01 01 01 0B 63 75 73 74 6F 6D 66 6F 6E 74 00 01 01 01 23 F8 1B 00 F8
1C 02 F8 1D 03 F8 19 04 8C 0D 9F 77 F9 50 F9 3C 05 F7 05 0F F7 08 10 F7 0B 11 9B F7 37 12 00 04
01 01 0C 1D 28 2E 56 65 72 73 69 6F 6E 20 31 2E 30 43 75 73 74 6F 6D 20 47 6C 79 70 68 20 46 6F
6E 74 43 75 73 74 6F 6D 20 46 6F 6E 74 63 75 73 74 6F 6D 00 00 00 01 8A 00 01 01 00 02 01 01 02
27 0E 9F 77 15 8B F9 50 05 F9 3C 8B 05 8B FD 50 05 FD 3C 8B 05 F7 2A F7 2A 15 8B F8 24 05 F8 10
8B 05 8B FC 24 05 0E 8B 8B 06 8B 8B 08 95 0A 95 0B F9 50 14 F9 50 14 00 00 00 00 00 02 A8 00 00
(自定义字体可以做得非常小=)
但是,如果需要实际文件和/或 TTX 输出,可以在 https://github.com/Pomax/CFF-glyphlet-fonts/tree/gh-pages/binaries/test 找到它们,包括 TTX xml(直接链接:https://github.com/Pomax/CFF-glyphlet-fonts/blob/gh-pages/binaries/with%20GSUB/customfont.ttx),这是为了实现自包含问题是:
<?xml version="1.0" encoding="utf-8"?>
<ttFont sfntVersion="OTTO" ttLibVersion="2.4">
<GlyphOrder>
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
<GlyphID id="0" name=".notdef"/>
<GlyphID id="1" name="custom"/>
</GlyphOrder>
<head>
<!-- Most of this table will be recalculated by the compiler -->
<tableVersion value="1.0"/>
<fontRevision value="1.0"/>
<checkSumAdjustment value="0x1aeaff64"/>
<magicNumber value="0x5f0f3cf5"/>
<flags value="00000000 00000000"/>
<unitsPerEm value="1024"/>
<created value="Thu Jan 01 00:00:00 1970"/>
<modified value="Thu Jan 01 00:00:00 1970"/>
<xMin value="20"/>
<yMin value="-20"/>
<xMax value="700"/>
<yMax value="680"/>
<macStyle value="00000000 00000000"/>
<lowestRecPPEM value="8"/>
<fontDirectionHint value="2"/>
<indexToLocFormat value="0"/>
<glyphDataFormat value="0"/>
</head>
<hhea>
<tableVersion value="1.0"/>
<ascent value="1004"/>
<descent value="-344"/>
<lineGap value="0"/>
<advanceWidthMax value="680"/>
<minLeftSideBearing value="0"/>
<minRightSideBearing value="0"/>
<xMaxExtent value="680"/>
<caretSlopeRise value="0"/>
<caretSlopeRun value="0"/>
<caretOffset value="0"/>
<reserved0 value="0"/>
<reserved1 value="0"/>
<reserved2 value="0"/>
<reserved3 value="0"/>
<metricDataFormat value="0"/>
<numberOfHMetrics value="2"/>
</hhea>
<maxp>
<tableVersion value="0x5000"/>
<numGlyphs value="2"/>
</maxp>
<OS_2>
<version value="3"/>
<xAvgCharWidth value="0"/>
<usWeightClass value="400"/>
<usWidthClass value="1"/>
<fsType value="00000000 00000000"/>
<ySubscriptXSize value="0"/>
<ySubscriptYSize value="0"/>
<ySubscriptXOffset value="0"/>
<ySubscriptYOffset value="0"/>
<ySuperscriptXSize value="0"/>
<ySuperscriptYSize value="0"/>
<ySuperscriptXOffset value="0"/>
<ySuperscriptYOffset value="0"/>
<yStrikeoutSize value="0"/>
<yStrikeoutPosition value="0"/>
<sFamilyClass value="0"/>
<panose>
<bFamilyType value="0"/>
<bSerifStyle value="0"/>
<bWeight value="0"/>
<bProportion value="0"/>
<bContrast value="0"/>
<bStrokeVariation value="0"/>
<bArmStyle value="0"/>
<bLetterForm value="0"/>
<bMidline value="0"/>
<bXHeight value="0"/>
</panose>
<ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
<ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
<achVendID value=" =) "/>
<fsSelection value="00000000 01000000"/>
<fsFirstCharIndex value="126"/>
<fsLastCharIndex value="126"/>
<sTypoAscender value="680"/>
<sTypoDescender value="-20"/>
<sTypoLineGap value="324"/>
<usWinAscent value="1004"/>
<usWinDescent value="344"/>
<ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
<sxHeight value="0"/>
<sCapHeight value="0"/>
<usDefaultChar value="0"/>
<usBreakChar value="126"/>
<usMaxContex value="0"/>
</OS_2>
<name>
<namerecord nameID="1" platformID="1" platEncID="32" langID="0x0">
Custom Font
</namerecord>
<namerecord nameID="2" platformID="1" platEncID="32" langID="0x0">
Regular
</namerecord>
<namerecord nameID="4" platformID="1" platEncID="32" langID="0x0">
Custom Glyph Font
</namerecord>
<namerecord nameID="5" platformID="1" platEncID="32" langID="0x0">
Version 1.0
</namerecord>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
Custom Font
</namerecord>
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
Regular
</namerecord>
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
Custom Glyph Font
</namerecord>
<namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
Version 1.0
</namerecord>
</name>
<cmap>
<tableVersion version="0"/>
<cmap_format_4 platformID="3" platEncID="1" language="0">
<map code="0x7e" name="custom"/><!-- TILDE -->
</cmap_format_4>
</cmap>
<post>
<formatType value="3.0"/>
<italicAngle value="0.0"/>
<underlinePosition value="0"/>
<underlineThickness value="0"/>
<isFixedPitch value="1"/>
<minMemType42 value="0"/>
<maxMemType42 value="0"/>
<minMemType1 value="0"/>
<maxMemType1 value="0"/>
</post>
<CFF>
<CFFFont name="customfont">
<version value="Version 1.0"/>
<FullName value="Custom Glyph Font"/>
<FamilyName value="Custom Font"/>
<Weight value="Roman"/>
<isFixedPitch value="0"/>
<ItalicAngle value="0"/>
<UnderlineThickness value="50"/>
<PaintType value="0"/>
<CharstringType value="2"/>
<FontMatrix value="0.001 0 0 0.001 0 0"/>
<UniqueID value="1"/>
<FontBBox value="20 -20 700 680"/>
<StrokeWidth value="0"/>
<!-- charset is dumped separately as the 'GlyphOrder' element -->
<Encoding>
<map code="0x1" name="custom"/>
</Encoding>
<Private>
<BlueValues value="0 0"/>
<FamilyBlues value="0 0"/>
<BlueScale value="0.039625"/>
<BlueShift value="7"/>
<BlueFuzz value="1"/>
<StdHW value="10"/>
<StdVW value="10"/>
<ForceBold value="0"/>
<LanguageGroup value="0"/>
<ExpansionFactor value="0.06"/>
<initialRandomSeed value="0"/>
<defaultWidthX value="700"/>
<nominalWidthX value="0"/>
</Private>
<CharStrings>
<CharString name=".notdef">
endchar
</CharString>
<CharString name="custom">
20 -20 rmoveto
0 700 rlineto
680 0 rlineto
0 -700 rlineto
-680 0 rlineto
150 150 rmoveto
0 400 rlineto
380 0 rlineto
0 -400 rlineto
endchar
</CharString>
</CharStrings>
</CFFFont>
<GlobalSubrs>
<!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
</GlobalSubrs>
</CFF>
<hmtx>
<mtx name=".notdef" lsb="0"/>
<mtx name="custom" lsb="0"/>
</hmtx>
</ttFont>
如果有任何其他工具可用于确定字体是否“完成”到可以安装的程度,或者如果有人知道说明字体在安装之前需要满足哪些标准的文档,该信息非常受欢迎。当然,如果有人碰巧知道我的字体代码仍然缺少什么(作为字节码或 TTX XML),或者他们看到奇怪/错误的位,我也对您的 cmets/answers 非常感兴趣。
【问题讨论】:
当我看到标题时,我确信这会离题,但你说服我不是。 好问题。您可能还想反转内部轮廓的方向。 好观察!虽然 TTF 和 CFF 的剪切方向规则不同,但事实证明 android 只是假设 TTF 规则并显示出一个实心矩形;反转使其正确显示切口 =) 事实证明,问题在于name
表中缺少两条记录。如果 NameID 3 和 6 的记录丢失,则没有骰子。 Windows 不会预览它,更不用说安装它了。奇怪的是,NameID 4 和 5 的记录——尽管代表了完整的字体名称和版本——与安装目的无关。
【参考方案1】:
正如 Typophile 论坛上的某个人指出的那样,就 Windows 而言,“名称”表是不完整的。虽然 Microsoft Font Validator 不会标记任何问题,但字体必须列出以下名称表条目才能安装:
1: the regular font name
2: the font subfamily ("Regular", etc)
3: any (seriously, ANY) string to act as a unique font identifier
6: the postscript font name (a subset of true ASCII. Not ANSI)
我定义的字体使用 NameID 1、2、4 和 5;虽然 4 和 5 实际上是“验证”所必需的,但它们与预览/安装完全无关,而 3 和 6 则至关重要。谁知道(说真的,谁知道......如果你知道了,我想在聊天或推特上与你交谈或其他东西来挑选你的大脑=)
(请注意,要使字体可在 OSX 上安装,仍需要 4 和 5)
2017 年编辑:Microsoft 的字体验证器于 2015 年开源,由于 MS(出于法律原因)无法合并重要的社区贡献,因此设置了一个由社区维护的分支,并且与 https://github.com/HinTak/Font-Validator 上的 OpenType 规范中的更改保持同步——如果字体验证器是您的工具集的一部分,您绝对想开始使用这个 fork而是。
【讨论】:
我这几天一直在扯掉我留下的头发,将我手工生成的 TTX 与许多工作字体的 TTX 进行比较,慢慢消除差异并使 MS 验证器报告几乎完全绿色,所有这些都是为了徒劳无功。 Validator 显然在这里缺少测试。 公平地说:现在存在的 MS 验证器有点糟糕。值得庆幸的是,它最近已经开源(什么?!?!是的,它已经开源了)并且有一组直接的 PR 可以使其在 github.com/Microsoft/Font-Validator/pull/1 上适用于现代 OpenType 很高兴知道。希望这项工作将产生更好的工具,并更清楚地了解实际需求。 也值得订阅 OpenType 邮件列表,这是一个问“这到底是哪里出了问题??”的好地方作为工程师(而不是字体设计师)进行字体开发时。【参考方案2】:首先,我不知道AFDKO以外的要求。
我确实安装了DTL OTMaster Light,它报告created
和modified
的值超出范围(customfont.otf)。它们位于 Windows 所需的 head
表中。
[NOTE] Open log for OTM Light 1.000 on za feb 15 20:20:16 2014.
[NOTE] Extern to intern conversion of 'maxp' table successfully done.
[NOTE] Extern to intern conversion of 'CFF ' table successfully done.
[NOTE] Extern to intern conversion of 'hhea' table successfully done.
[NOTE] Extern to intern conversion of 'hmtx' table successfully done.
[NOTE] Extern to intern conversion of 'cmap' table successfully done.
[NOTE] Extern to intern conversion of 'OS2' table successfully done.
[NOTE] Extern to intern conversion of 'head' table successfully done.
[NOTE] Extern to intern conversion of 'name' table successfully done.
[NOTE] Extern to intern conversion of 'post' table successfully done.
[ERROR] Node "/OTF/head/Struct/created" value 0 out of range (2082844800 - 4294967294).
[ERROR] Node "/OTF/head/Struct/modified" value 0 out of range (2082844800 - 4294967294).
【讨论】:
那将是 DTL FontMaster Light 中的一个错误;创建和修改的日期以 1904 年 1 月 1 日以来的秒数为单位(请参阅microsoft.com/typography/otspec/head.htm),合法范围为 0 到适合 64 位的秒数(不少!)。这是一个不寻常的值,因为当时没有具有 opentype 的计算机,但充其量应该导致警告而不是错误。 (TTX 会将 Unix 纪元之前的值解释为纪元以来的秒数,以解决这个问题,因为一些字体铸造厂实际上错误地将创建/修改日期用作自纪元值) 啊,好吧。仅供参考:DTL OTM 将值调整为1970-jan-01 01:00:00
。
TTX xml 也有“Thu Jan 01 00:00:00 1970”。而不是 0 或 1904 年 1 月 1 日!
是的,我已经指出了这一点。 TTX 将简单地将 unix 纪元之前的任何日期重写为纪元以来的以秒为单位的日期。这在技术上是错误的,但由于几家大型代工厂在其字体中使用了错误的时间戳,这是必要的。
这在现代 Windows 上应该不是问题,但足够公平:我 cygwin 将文件修改为 777,而 Windows 的行为没有改变以上是关于可安装 OpenType 字体的 Windows 要求是啥?的主要内容,如果未能解决你的问题,请参考以下文章
Microsoft Windows OpenType Font (OTF)驱动程序无效数组远程代码执行漏洞(MS10-091)
无法让 Ghostscript 使用 OpenType 字体