• Font
  • Family
  • Foundry
  • Designer
  • Sample
  • Article
  • Help
Fontke.com>Article>Details

玩转字体内嵌

Date:2005-09-18 23:30:23| School|Browse: 467|Source: http://blog.bs2.to/EdwardLee|Author: EdwardLee
  • Follow FontKe on Wechat to get Zcode
  • Scan the Qrcode to participate in the SVIP lottery
Introduction在制作文件的时候,常常需要把字体嵌入文件中,也就是让文件带着字体跑,这样拿到文件的人才不会因为系统上没有这个字体而无法显示,或显示失真,这里我们就来探讨一下字体是否可以内嵌的问题。这里的字体我们主要以矢量字体(PostScript, TrueType, OpenType)字体为

在制作文件的时候,常常需要把字体嵌入文件中,也就是让文件带着字体跑,这样拿到文件的人才不会因为系统上没有这个字体而无法显示,或显示失真,这里我们就来探讨一下字体是否可以内嵌的问题。

这里的字体我们主要以矢量字体(PostScript, TrueType, OpenType)字体为主来探讨,因为一般会嵌入的,多数是此类字体。

首先,要视字体的书面文字授权(license)而定,授权允许嵌入的,那才可以嵌入,否则,我们是不能随意将一些字体嵌入文件中的,因为一套字体有其不可分割的完整性,把其中的一些字取出,嵌入文件当中,就破坏了字体的完整性,所以,需要有特别的授权才能做这样子的处理。

但有些字体规格里头,也可以有是否可以嵌入的旗标,例如 TrueType/openType 字体就有这样子的旗标,我们也可以从这个旗标来判断字体是否允许被嵌入(但仍然要以书面的授权为准),但像 PostScript 字体,则除非是 wrap 成 OpenType 字体,否则通常是没有这种旗标,得由书面、文字授权来判定。

这里,我们就以最普遍的 TrueType(以下简称为 TTF)字体为例来探讨。基本上 TTF 的结构分成三个部份:

TTF header(文件头): 12 bytes,它的结构如下:

  TTF header {
  Fixed  sfnt version   # 4 bytes
  USHORT numTables      # 2 bytes, table 的总数在此
  USHORT searchRange
  USHORT entrySelector
  USHORT rangeShift
}

表格目录区(table directory 区):数目不限,每个 table dir 16 bytes,它的结构如下:

  table directory {
  ULONG tag             # 4 bytes,决定是什么表格
  ULONG checkSum        # 看表格有没有破掉
  ULONG offset          # 指向这个 table dir 所要索引的 table data 起始处
  ULONG length                                         
}

表格资料区(table data 区):数目同 table dir。table data 的结构,要视哪一种 table,查 TTF spec。这里以 OS/2 table 为例(只列出我们目前要用到的):

Type    Name                Description
-------------------------------------------------------------------------
uint16  version              table version number (set to 0)
int1     xAvgCharWidth   average weighted advance width of lower case letters and space.
uint16  usWeightClass    visual weight (degree of blackness or thickness) of stroke in glyphs.
uint16  usWidthClass     relative change from the normal aspect ratio (width to height ratio) as
                                    specified by a font designer for the glyphs in the font. 
int16    fsType               characteristics and properties of this font (set undefined bits to zero).
[以下略……]

我们要判断的就是 TTF OS/2 table 里头的 fsType 旗标。这个 fsType 旗标,有多种情况,可自行参考 TTF spec 及 OpenType spec。其中第二个 bit 如果是打开的,这是最严格不能嵌入的情形。

我们就是依据 TTF/OTF spec 来移动固定的 bytes,并取得其中信息,再来输出显示,或者是继续索引下去,有些值只是指示资料在什么地方的位移,那就要移到那个位移位置来取得所需要的资料。这里就以Ruby script language来实作看看。当然,要用 Python/Perl 都行,甚至 C/C++ 都可以,但这种小 case 就用 script language 把它打发掉较省时间。

#!/usr/bin/env ruby
# vim:ts=2 sw=2 et
# ttfemb.rb : check if TTF can be embedded.
# Edward G.J. Lee

# check if this file is a TTF or OTF.
def ifTTF(file)
  f = File.new(file)
  tag = f.read(4)
  head = tag[0].to_s + tag[1].to_s + tag[2].to_s + tag[3].to_s
  if (head == '0100') || (tag == 'OTTO')
    return true
  else
    return false
  end
  f.close
end

# where the tname table data area start?
def dataOffset(file, tname)
  open(file, 'rb') do |f|
    f.seek(4, 1)
    tnum = f.read(2).unpack('n')
    f.seek(12, 0)

    0.upto(tnum[0] - 1) do |i|
      if f.read(4) == tname
        break
      end
    f.seek(12, 1)
    end

    f.seek(4, 1)
    offset = f.read(4).unpack('N')

    return offset[0]
  end
end

# check OS/2 table's fsType value.
def scanOS2(file)
  start = dataOffset(file, 'OS/2')
  f = File.new(file, 'rb')
  f.seek(start, 0)

  f.seek(8, 1)
  fsType = f.read(2).unpack('n')

  return fsType[0]
  f.close
end

if ARGV.length != 1
  puts "ttfemb.rb your.ttf"
  exit
elsif !test(?e, ARGV[0])
  puts 'File not exist!'
  exit
elsif File.extname(ARGV[0]).downcase == '.ttc'
  puts 'Not support yet!'
  exit
elsif !ifTTF(ARGV[0])
  puts 'Not a TrueType or OpenType font.'
  exit
else
  ft = scanOS2(ARGV[0])
  puts "fsType(dec) = " + ft.to_s
  print "fsType(bin) = "
  printf("%16.16b\n", ft)

  if ft == 2
    puts 'Warning: you cannot embed this TTF!'
  else
    puts "You may embed this TTF."
  end
end

存盘后,把权限设成可执行即可。这只在 GNU/Linux 及 FreeBSD 测试过,但应该其它的操作系统也可以执行,前提当然是要先安装 Ruby。但以下提供GNU/Linux 编译好的static可执行文件,这样就无需安装 Ruby 了,下载后改变权限为可执行文件即可。

我们来试试两个字体例子:

第一个是 AnglicanText.ttf:

edt1023:~/MyPro/ruby$ ./ttfemb.rb AnglicanText.ttf
fsType(dec) = 2
fsType(bin) = 0000000000000010
Warning: you cannot embed this TTF!

所以,这个字体虽然是免费的,但它是不能嵌入文件中的。

第二个例子是 cwming.ttf:

edt1023:~/MyPro/ruby$ ./ttfemb.rb cwming.ttf
fsType(dec) = 0
fsType(bin) = 0000000000000000
You may embed this TTF.

这个字体没有严格说不能内嵌,再参考它的字体授权,它是可以内嵌的 GNU GPL 字体。

最后再强调一次,字体是否允许嵌入是要先以字体的使用授权为准,以上的判断只是辅助,而且以上的判断只是在判断严格不能嵌入的情形,并没有考虑其它的情形。

0
  • Follow FontKe on Wechat to get Zcode
  • Scan the Qrcode to participate in the SVIP lottery
玩转字体内嵌 Comments
Guest Please obey the rules of this website. Unclear?
玩转字体内嵌 Latest comments
No relevant comments