让Adobe字体在IE9中使用CSS3 @font-face的方法

135
我正在构建一个小型的内部网络应用程序,尝试使用最近购买的Adobe字体,但没有成功。据我所知,在我们的情况下,这不是违反许可证的行为。我将字体的.ttf/.otf版本转换为.woff、.eot和.svg,以便针对所有主要浏览器。我使用的@font-face语法基本上是Font Spring的弹性方案。
@font-face {
    font-family: 'MyFontFamily';
    src: url('myfont-webfont.eot');
    src: url('myfont-webfont.eot?#iehack') format('eot'), 
         url('myfont-webfont.woff') format('woff'), 
         url('myfont-webfont.ttf')  format('truetype'),
         url('myfont-webfont.svg#svgFontName') format('svg');
    }

我修改了HTTP头(添加了Access-Control-Allow-Origin =“*”),以允许跨域引用。在Firefox和Chrome中它完美地工作,但在IE9中我得到:

CSS3111: @font-face encountered unknown error.  
myfont-webfont.woff
CSS3114: @font-face failed OpenType embedding permission check. Permission must be Installable. 
myfont-webfont.ttf

我注意到将字体从.ttf/.otf转换为.woff时,同时会生成一个.afm文件,但我不知道它是否重要...
有什么想法吗?
[编辑] - 我使用IIS 7.5托管我的网站(也包括字体,但是在单独的目录和子域名下用于静态内容)。

17
太棒了,这是一个精彩、聪明且措辞得体的问题,你已经完成了所有的功课。现在我们这些问题越来越少了!+1 - Pekka
确实这是一个很好的问题,但遗憾的是,它是一个重复的问题。 - Joseph Earl
1
不,这绝对不是重复的问题,因为在非Adobe字体中,我找到的解决方案完美地运行。不同之处在于,我猜测跨域字体引用不是这种情况 - 我使用.woff字体时会出现“@font-face遇到未知错误”的情况,而在其他提到的情况下则是“@font-face失败的跨域请求”。 - Piotr Szmyd
在更改嵌入选项后,我遇到了这行代码的问题:url('myfont-webfont.eot?#iehack') format('eot'), 。删除它解决了最后一个错误(未知错误)。 - Jonathan DeMarks
19个回答

3
如果您想使用PHP脚本来完成此操作,而不必运行C代码(或者您像我一样使用Mac,并且不想在Xcode中进行编译,只需要等待一年才能打开),那么这里有一个PHP函数,您可以使用它来删除字体的嵌入权限:
function convertRestrictedFont($filename) {
    $font = fopen($filename,'r+');
    if ($font === false) {
        throw new Exception('Could not open font file.');
    }

    fseek($font, 12, 0);

    while (!feof($font)) {
        $type = '';
        for ($i = 0; $i < 4; $i++) {
            $type .= fgetc($font);
            if (feof($font)) {
                fclose($font);
                throw new Exception('Could not read the table definitions of the font.');
            }
        }
        if ($type == 'OS/2') {
            // Save the location of the table definition
            // containing the checksum and pointer to the data
            $os2TableDefinition = ftell($font);
            $checksum = 0;

            for ($i = 0; $i < 4; $i++) {
                fgetc($font);
                if (feof($font)) {
                    fclose($font);
                    throw new Exception('Could not read the OS/2 table header of the font.');
                }
            }

            // Get the pointer to the OS/2 table data
            $os2TablePointer = ord(fgetc($font)) << 24;
            $os2TablePointer |= ord(fgetc($font)) << 16;
            $os2TablePointer |= ord(fgetc($font)) << 8;
            $os2TablePointer |= ord(fgetc($font));

            $length = ord(fgetc($font)) << 24;
            $length |= ord(fgetc($font)) << 16;
            $length |= ord(fgetc($font)) << 8;
            $length |= ord(fgetc($font));

            if (fseek($font, $os2TablePointer + 8, 0) !== 0) {
                fclose($font);
                throw new Exception('Could not read the embeddable type of the font.');
            }

            // Read the fsType before overriding it
            $fsType = ord(fgetc($font)) << 8;
            $fsType |= ord(fgetc($font));

            error_log('Installable Embedding: ' . ($fsType == 0));
            error_log('Reserved: ' . ($fsType & 1));
            error_log('Restricted License: ' . ($fsType & 2));
            error_log('Preview & Print: ' . ($fsType & 4));
            error_log('Editable Embedding: ' . ($fsType & 8));
            error_log('Reserved: ' . ($fsType & 16)); 
            error_log('Reserved: ' . ($fsType & 32));
            error_log('Reserved: ' . ($fsType & 64));
            error_log('Reserved: ' . ($fsType & 128));
            error_log('No subsetting: ' . ($fsType & 256));
            error_log('Bitmap embedding only: ' . ($fsType & 512));                         
            error_log('Reserved: ' . ($fsType & 1024));
            error_log('Reserved: ' . ($fsType & 2048));
            error_log('Reserved: ' . ($fsType & 4096));
            error_log('Reserved: ' . ($fsType & 8192));
            error_log('Reserved: ' . ($fsType & 16384));
            error_log('Reserved: ' . ($fsType & 32768));

            fseek($font, ftell($font) - 2);

            // Set the two bytes of fsType to 0
            fputs($font, chr(0), 1);
            fputs($font, chr(0), 1);

            // Go to the beginning of the OS/2 table data
            fseek($font, $os2TablePointer, 0);

            // Generate a new checksum based on the changed 
            for ($i = 0; $i < $length; $i++) {
                $checksum += ord(fgetc($font));
            }
            fseek($font, $os2TableDefinition, 0);
            fputs($font, chr($checksum >> 24), 1);
            fputs($font, chr(255 & ($checksum >> 16)), 1);
            fputs($font, chr(255 & ($checksum >> 8)), 1);
            fputs($font, chr(255 & $checksum), 1);

            fclose($font);

            return true;
        }
        for ($i = 0; $i < 12; $i++) {
            fgetc($font);
            if (feof($font)) {
                fclose($font);
                throw new Exception('Could not skip a table definition of the font.');
            }
        }
    }

    fclose($font);

    return false;
}

在运行此代码之前,请务必备份您的字体文件,如果出现损坏问题请不要责怪我。

可以在这里找到C语言的原始源代码。


这个可行,现在应该是第一答案。可惜它还有很长的路要走才能超过旧答案。 - Goose
非常感谢@Goose!我最初是为我的工作编写了这个代码,但是代码被丢弃并替代了,所以它在Stack Overflow中得以保留。提供用于Web应用程序问题的C代码肯定不是理想的选择。 - NobleUplift
@Goose 我更喜欢 C 代码。总是这样。所以这只是一种口味问题,这就是为什么这个答案等同于答案的原因。顺便说一下,你也可以使用 CGI 在你的网站上实现 C 代码。 - 71GA

3
问题可能与您的服务器配置有关 - 它可能没有为字体文件发送正确的标头。请参考IE9阻止跨域网络字体下载中提供的答案。
EricLaw建议将以下内容添加到您的Apache配置中。
<FilesMatch "\.(ttf|otf|eot|woff)$">
    <IfModule mod_headers.c>
        Header set Access-Control-Allow-Origin "http://mydomain.com"
    </IfModule>
</FilesMatch>

但这并不是同一个情况。我已经阅读了那篇文章并尝试了您提供的解决方案。问题特别出现在Adobe字体上。我尝试使用来自Font Squirrel的字体工具包,在所有浏览器中都可以完美使用(包括IE9)。但是当我尝试以相同的方式使用Adobe字体(转换为适当的格式)时,IE9会报错…… - Piotr Szmyd
还有一件事我忘了说(我会编辑我的问题)- 我正在IIS 7.5下运行我的网站。 - Piotr Szmyd
它们是第一类字体吗? - Joseph Earl
这些都是单文件的.ttf(TrueType)字体。但是当通过onlinefontconverter.com转换为.woff格式时,不知何故会得到一个.afm(Adobe字体度量)文件。我不知道该怎么办? - Piotr Szmyd

2
对于所有在使用 ttfpatch 时遇到错误 "tableversion must be 0, 1 or and is hex:003" 的人,我已经为 64 位编译了嵌入代码。我没有更改任何内容,只是添加了必要的库并进行了编译。请自行承担使用风险。
使用方法:ConsoleApplication1 font.ttf
下载链接:http://www.mediafire.com/download/8x1px8aqq18lcx8/ConsoleApplication1.exe

0
这对我有效:
@font-face {
  font-family: FontName;
  src: url('@{path-fonts}/FontName.eot?akitpd');
  src: url('@{path-fonts}/FontName.eot?akitpd#iefix') format('embedded-opentype'),
    url('@{path-fonts}/FontName.ttf?akitpd') format('truetype'),
    url('@{path-fonts}/FontName.woff?akitpd') format('woff'),
    url('@{path-fonts}/FontName.svg?akitpd#salvage') format('svg');
}

0

我发现eot文件应该放在ttf之外。如果它在ttf下面,虽然字体显示正确,但IE9仍会抛出错误。

建议:

@font-face {
  font-family: 'Font-Name';
  src: url('../fonts/Font-Name.eot?#iefix') format('embedded-opentype');
  src: url('../fonts/Font-Name.ttf')  format('truetype');
}

不建议:

@font-face {
  font-family: 'Font-Name';
  src: url('../fonts/Font-Name.ttf')  format('truetype');
  src: url('../fonts/Font-Name.eot?#iefix') format('embedded-opentype');
  }

0

我最近遇到了使用.eot和.otf字体时,在加载时会在控制台中产生CSS3114和CSS3111错误的问题。对我有效的解决方案是仅使用.woff和.woff2格式,并附带一个.ttf格式的备用方案。在大多数浏览器中,.woff格式将优先于.ttf格式使用,并且似乎不会触发字体嵌入权限问题(css3114)和字体命名格式不正确问题(css3111)。我在这篇关于CSS3111和CSS3114问题的非常有用的文章中找到了我的解决方案,还阅读了这篇关于使用@font-face的文章

注意:此解决方案不需要重新编译、转换或编辑任何字体文件。这是一种仅使用CSS的解决方案。我测试过的字体确实生成了.eot、.otf、.woff、.woff2和.svg版本,可能是从原始的.ttf源文件生成的,当我尝试使用它时确实出现了3114错误,但.woff和.woff2文件似乎免疫了这个问题。

以下是我在使用@font-face时成功的方法:

@font-face {
  font-family: "Your Font Name";
  font-weight: normal;
  src: url('your-font-name.woff2') format('woff2'),
       url('your-font-name.woff') format('woff'),
       url('your-font-name.ttf')  format('truetype');
}

这就是在IE中触发@font-face错误的原因:

@font-face {
  font-family: 'Your Font Name';
  src: url('your-font-name.eot');
  src: url('your-font-name.eot?#iefix') format('embedded-opentype'),
       url('your-font-name.woff2') format('woff2'),
       url('your-font-name.woff') format('woff'),
       url('your-font-name.ttf')  format('truetype'),
       url('your-font-name.svg#svgFontName') format('svg');
}

0

0

你可以通过以下代码解决它

@font-face {
  font-family: 'Font-Name';
  src: url('../fonts/Font-Name.ttf');
  src: url('../fonts/Font-Name.eot?#iefix') format('embedded-opentype');
}

不,这样行不通。我的情况严格来说是关于那些设计上不允许嵌入的字体(但其许可证允许嵌入)。所以问题不在于我如何嵌入它。使用一个明确禁止网络嵌入的TTF字体,你就会理解我的问题了。 - Piotr Szmyd

0
如果你想使用Python脚本来代替运行C/PHP代码来完成这个任务,那么这里有一个Python3函数可以用来移除字体的嵌入权限:
def convert_restricted_font(filename):
with open(filename, 'rb+') as font:

    font.read(12)
    while True:
        _type = font.read(4)
        if not _type:
            raise Exception('Could not read the table definitions of the font.')
        try:
            _type = _type.decode()
        except UnicodeDecodeError:
            pass
        except Exception as err:
            pass
        if _type != 'OS/2':
            continue
        loc = font.tell()
        font.read(4)
        os2_table_pointer = int.from_bytes(font.read(4), byteorder='big')
        length = int.from_bytes(font.read(4), byteorder='big')
        font.seek(os2_table_pointer + 8)

        fs_type = int.from_bytes(font.read(2), byteorder='big')
        print(f'Installable Embedding: {fs_type == 0}')
        print(f'Restricted License: {fs_type & 2}')
        print(f'Preview & Print: {fs_type & 4}')
        print(f'Editable Embedding: {fs_type & 8}')

        print(f'No subsetting: {fs_type & 256}')
        print(f'Bitmap embedding only: {fs_type & 512}')

        font.seek(font.tell()-2)
        installable_embedding = 0 # True
        font.write(installable_embedding.to_bytes(2, 'big'))
        font.seek(os2_table_pointer)
        checksum = 0
        for i in range(length):
            checksum += ord(font.read(1))
        font.seek(loc)
        font.write(checksum.to_bytes(4, 'big'))
        break


if __name__ == '__main__':
    convert_restricted_font("19700-webfont.ttf")

它可以工作,但我最终通过使用https解决了IE中加载字体的问题,像这样this

感谢NobleUplift

C语言的原始源代码可以在这里找到。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接