ttfunk 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +2 -0
- data/README.rdoc +39 -0
- data/data/fonts/DejaVuSans.ttf +0 -0
- data/data/fonts/comicsans.ttf +0 -0
- data/examples/metrics.rb +46 -0
- data/lib/ttfunk/directory.rb +17 -0
- data/lib/ttfunk/encoding/mac_roman.rb +88 -0
- data/lib/ttfunk/encoding/windows_1252.rb +69 -0
- data/lib/ttfunk/reader.rb +44 -0
- data/lib/ttfunk/resource_file.rb +78 -0
- data/lib/ttfunk/subset/base.rb +141 -0
- data/lib/ttfunk/subset/mac_roman.rb +50 -0
- data/lib/ttfunk/subset/unicode.rb +48 -0
- data/lib/ttfunk/subset/unicode_8bit.rb +63 -0
- data/lib/ttfunk/subset/windows_1252.rb +55 -0
- data/lib/ttfunk/subset.rb +18 -0
- data/lib/ttfunk/subset_collection.rb +72 -0
- data/lib/ttfunk/table/cmap/format00.rb +54 -0
- data/lib/ttfunk/table/cmap/format04.rb +126 -0
- data/lib/ttfunk/table/cmap/subtable.rb +79 -0
- data/lib/ttfunk/table/cmap.rb +34 -0
- data/lib/ttfunk/table/glyf/compound.rb +81 -0
- data/lib/ttfunk/table/glyf/simple.rb +37 -0
- data/lib/ttfunk/table/glyf.rb +64 -0
- data/lib/ttfunk/table/head.rb +44 -0
- data/lib/ttfunk/table/hhea.rb +41 -0
- data/lib/ttfunk/table/hmtx.rb +47 -0
- data/lib/ttfunk/table/kern/format0.rb +62 -0
- data/lib/ttfunk/table/kern.rb +79 -0
- data/lib/ttfunk/table/loca.rb +43 -0
- data/lib/ttfunk/table/maxp.rb +40 -0
- data/lib/ttfunk/table/name.rb +125 -0
- data/lib/ttfunk/table/os2.rb +78 -0
- data/lib/ttfunk/table/post/format10.rb +43 -0
- data/lib/ttfunk/table/post/format20.rb +35 -0
- data/lib/ttfunk/table/post/format25.rb +23 -0
- data/lib/ttfunk/table/post/format30.rb +17 -0
- data/lib/ttfunk/table/post/format40.rb +17 -0
- data/lib/ttfunk/table/post.rb +91 -0
- data/lib/ttfunk/table/simple.rb +14 -0
- data/lib/ttfunk/table.rb +46 -0
- data/lib/ttfunk.rb +102 -0
- metadata +121 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'ttfunk/table'
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class Table
|
5
|
+
class Head < TTFunk::Table
|
6
|
+
attr_reader :version
|
7
|
+
attr_reader :font_revision
|
8
|
+
attr_reader :checksum_adjustment
|
9
|
+
attr_reader :magic_number
|
10
|
+
attr_reader :flags
|
11
|
+
attr_reader :units_per_em
|
12
|
+
attr_reader :created
|
13
|
+
attr_reader :modified
|
14
|
+
attr_reader :x_min
|
15
|
+
attr_reader :y_min
|
16
|
+
attr_reader :x_max
|
17
|
+
attr_reader :y_max
|
18
|
+
attr_reader :mac_style
|
19
|
+
attr_reader :lowest_rec_ppem
|
20
|
+
attr_reader :font_direction_hint
|
21
|
+
attr_reader :index_to_loc_format
|
22
|
+
attr_reader :glyph_data_format
|
23
|
+
|
24
|
+
def self.encode(head, loca)
|
25
|
+
table = head.raw
|
26
|
+
table[8,4] = "\0\0\0\0" # set checksum adjustment to 0 initially
|
27
|
+
table[-4,2] = [loca[:type]].pack("n") # set index_to_loc_format
|
28
|
+
return table
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def parse!
|
34
|
+
@version, @font_revision, @check_sum_adjustment, @magic_number,
|
35
|
+
@flags, @units_per_em, @created, @modified = read(36, "N4n2q2")
|
36
|
+
|
37
|
+
@x_min, @y_min, @x_max, @y_max = read_signed(4)
|
38
|
+
|
39
|
+
@mac_style, @lowest_rec_ppem, @font_direction_hint,
|
40
|
+
@index_to_loc_format, @glyph_data_format = read(10, "n*")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'ttfunk/table'
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class Table
|
5
|
+
class Hhea < Table
|
6
|
+
attr_reader :version
|
7
|
+
attr_reader :ascent
|
8
|
+
attr_reader :descent
|
9
|
+
attr_reader :line_gap
|
10
|
+
attr_reader :advance_width_max
|
11
|
+
attr_reader :min_left_side_bearing
|
12
|
+
attr_reader :min_right_side_bearing
|
13
|
+
attr_reader :x_max_extent
|
14
|
+
attr_reader :carot_slope_rise
|
15
|
+
attr_reader :carot_slope_run
|
16
|
+
attr_reader :metric_data_format
|
17
|
+
attr_reader :number_of_metrics
|
18
|
+
|
19
|
+
def self.encode(hhea, hmtx)
|
20
|
+
raw = hhea.raw
|
21
|
+
raw[-2,2] = [hmtx[:number_of_metrics]].pack("n")
|
22
|
+
return raw
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def parse!
|
28
|
+
@version = read(4, "N").first
|
29
|
+
@ascent, @descent, @line_gap = read_signed(3)
|
30
|
+
@advance_width_max = read(2, "n").first
|
31
|
+
|
32
|
+
@min_left_side_bearing, @min_right_side_bearing, @x_max_extent,
|
33
|
+
@carot_slope_rise, @carot_slope_run, @caret_offset,
|
34
|
+
reserved, reserved, reserved, reserved,
|
35
|
+
@metric_data_format = read_signed(11)
|
36
|
+
|
37
|
+
@number_of_metrics = read(2, "n").first
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'ttfunk/table'
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class Table
|
5
|
+
class Hmtx < Table
|
6
|
+
attr_reader :metrics
|
7
|
+
attr_reader :left_side_bearings
|
8
|
+
attr_reader :widths
|
9
|
+
|
10
|
+
def self.encode(hmtx, mapping)
|
11
|
+
metrics = mapping.keys.sort.map do |new_id|
|
12
|
+
metric = hmtx.for(mapping[new_id])
|
13
|
+
[metric.advance_width, metric.left_side_bearing]
|
14
|
+
end
|
15
|
+
|
16
|
+
{ :number_of_metrics => metrics.length,
|
17
|
+
:table => metrics.flatten.pack("n*") }
|
18
|
+
end
|
19
|
+
|
20
|
+
HorizontalMetric = Struct.new(:advance_width, :left_side_bearing)
|
21
|
+
|
22
|
+
def for(glyph_id)
|
23
|
+
@metrics[glyph_id] ||
|
24
|
+
HorizontalMetric.new(@metrics.last.advance_width,
|
25
|
+
@left_side_bearings[glyph_id - @metrics.length])
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def parse!
|
31
|
+
@metrics = []
|
32
|
+
|
33
|
+
file.horizontal_header.number_of_metrics.times do
|
34
|
+
advance = read(2, "n").first
|
35
|
+
lsb = read_signed(1).first
|
36
|
+
@metrics.push HorizontalMetric.new(advance, lsb)
|
37
|
+
end
|
38
|
+
|
39
|
+
lsb_count = file.maximum_profile.num_glyphs - file.horizontal_header.number_of_metrics
|
40
|
+
@left_side_bearings = read_signed(lsb_count)
|
41
|
+
|
42
|
+
@widths = @metrics.map { |metric| metric.advance_width }
|
43
|
+
@widths += [@widths.last] * @left_side_bearings.length
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'ttfunk/reader'
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class Table
|
5
|
+
class Kern
|
6
|
+
class Format0
|
7
|
+
include Reader
|
8
|
+
|
9
|
+
attr_reader :attributes
|
10
|
+
attr_reader :pairs
|
11
|
+
|
12
|
+
def initialize(attributes={})
|
13
|
+
@attributes = attributes
|
14
|
+
|
15
|
+
num_pairs, search_range, entry_selector, range_shift, *pairs =
|
16
|
+
attributes.delete(:data).unpack("n*")
|
17
|
+
|
18
|
+
@pairs = {}
|
19
|
+
num_pairs.times do |i|
|
20
|
+
break if i*3+2 > pairs.length # sanity check, in case there's a bad length somewhere
|
21
|
+
left = pairs[i*3]
|
22
|
+
right = pairs[i*3+1]
|
23
|
+
value = to_signed(pairs[i*3+2])
|
24
|
+
@pairs[[left, right]] = value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def vertical?
|
29
|
+
@attributes[:vertical]
|
30
|
+
end
|
31
|
+
|
32
|
+
def horizontal?
|
33
|
+
!vertical?
|
34
|
+
end
|
35
|
+
|
36
|
+
def cross_stream?
|
37
|
+
@attributes[:cross]
|
38
|
+
end
|
39
|
+
|
40
|
+
def recode(mapping)
|
41
|
+
subset = []
|
42
|
+
pairs.each do |(left, right), value|
|
43
|
+
if mapping[left] && mapping[right]
|
44
|
+
subset << [mapping[left], mapping[right], value]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
return nil if subset.empty?
|
49
|
+
|
50
|
+
num_pairs = subset.length
|
51
|
+
search_range = 2 * 2 ** (Math.log(num_pairs) / Math.log(2)).to_i
|
52
|
+
entry_selector = (Math.log(search_range / 2) / Math.log(2)).to_i
|
53
|
+
range_shift = (2 * num_pairs) - search_range
|
54
|
+
|
55
|
+
[attributes[:version], num_pairs * 6 + 14, attributes[:coverage],
|
56
|
+
num_pairs, search_range, entry_selector, range_shift, subset].
|
57
|
+
flatten.pack("n*")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'ttfunk/table'
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class Table
|
5
|
+
class Kern < Table
|
6
|
+
attr_reader :version
|
7
|
+
attr_reader :tables
|
8
|
+
|
9
|
+
def self.encode(kerning, mapping)
|
10
|
+
return nil unless kerning.exists? && kerning.tables.any?
|
11
|
+
tables = kerning.tables.map { |table| table.recode(mapping) }.compact
|
12
|
+
return nil if tables.empty?
|
13
|
+
|
14
|
+
[0, tables.length, tables.join].pack("nnA*")
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def parse!
|
20
|
+
@version, num_tables = read(4, "n*")
|
21
|
+
@tables = []
|
22
|
+
|
23
|
+
if @version == 1 # Mac OS X fonts
|
24
|
+
@version = (@version << 16) + num_tables
|
25
|
+
num_tables = read(4, "N").first
|
26
|
+
parse_version_1_tables(num_tables)
|
27
|
+
else
|
28
|
+
parse_version_0_tables(num_tables)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_version_0_tables(num_tables)
|
33
|
+
# It looks like some MS fonts report their kerning subtable lengths
|
34
|
+
# wrong. In one case, the length was reported to be some 19366, and yet
|
35
|
+
# the table also claimed to hold 14148 pairs (each pair consisting of 6 bytes).
|
36
|
+
# You do the math!
|
37
|
+
#
|
38
|
+
# We're going to assume that the microsoft fonts hold only a single kerning
|
39
|
+
# subtable, which occupies the entire length of the kerning table. Worst
|
40
|
+
# case, we lose any other subtables that the font contains, but it's better
|
41
|
+
# than reading a truncated kerning table.
|
42
|
+
#
|
43
|
+
# And what's more, it appears to work. So.
|
44
|
+
version, length, coverage = read(6, "n*")
|
45
|
+
format = coverage >> 8
|
46
|
+
|
47
|
+
add_table format, :version => version, :length => length,
|
48
|
+
:coverage => coverage, :data => raw[10..-1],
|
49
|
+
:vertical => (coverage & 0x1 == 0),
|
50
|
+
:minimum => (coverage & 0x2 != 0),
|
51
|
+
:cross => (coverage & 0x4 != 0),
|
52
|
+
:override => (coverage & 0x8 != 0)
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse_version_1_tables(num_tables)
|
56
|
+
num_tables.times do
|
57
|
+
length, coverage, tuple_index = read(8, "Nnn")
|
58
|
+
format = coverage & 0x0FF
|
59
|
+
|
60
|
+
add_table format, :length => length, :coverage => coverage,
|
61
|
+
:tuple_index => tuple_index, :data => io.read(length-8),
|
62
|
+
:vertical => (coverage & 0x8000 != 0),
|
63
|
+
:cross => (coverage & 0x4000 != 0),
|
64
|
+
:variation => (coverage & 0x2000 != 0)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_table(format, attributes={})
|
69
|
+
if format == 0
|
70
|
+
@tables << Kern::Format0.new(attributes)
|
71
|
+
else
|
72
|
+
# silently ignore unsupported kerning tables
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
require 'ttfunk/table/kern/format0'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'ttfunk/table'
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class Table
|
5
|
+
class Loca < Table
|
6
|
+
attr_reader :offsets
|
7
|
+
|
8
|
+
# Accepts an array of offsets, with each index corresponding to the
|
9
|
+
# glyph id with that index.
|
10
|
+
#
|
11
|
+
# Returns a hash containing:
|
12
|
+
#
|
13
|
+
# * :table - the string representing the table's contents
|
14
|
+
# * :type - the type of offset (to be encoded in the 'head' table)
|
15
|
+
def self.encode(offsets)
|
16
|
+
if offsets.any? { |ofs| ofs > 0xFFFF }
|
17
|
+
{ :type => 1, :table => offsets.pack("N*") }
|
18
|
+
else
|
19
|
+
{ :type => 0, :table => offsets.map { |o| o/2 }.pack("n*") }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def index_of(glyph_id)
|
24
|
+
@offsets[glyph_id]
|
25
|
+
end
|
26
|
+
|
27
|
+
def size_of(glyph_id)
|
28
|
+
@offsets[glyph_id+1] - @offsets[glyph_id]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def parse!
|
34
|
+
type = file.header.index_to_loc_format == 0 ? "n" : "N"
|
35
|
+
@offsets = read(length, "#{type}*")
|
36
|
+
|
37
|
+
if file.header.index_to_loc_format == 0
|
38
|
+
@offsets.map! { |v| v * 2 }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'ttfunk/table'
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class Table
|
5
|
+
class Maxp < Table
|
6
|
+
attr_reader :version
|
7
|
+
attr_reader :num_glyphs
|
8
|
+
attr_reader :max_points
|
9
|
+
attr_reader :max_contours
|
10
|
+
attr_reader :max_component_points
|
11
|
+
attr_reader :max_component_contours
|
12
|
+
attr_reader :max_zones
|
13
|
+
attr_reader :max_twilight_points
|
14
|
+
attr_reader :max_storage
|
15
|
+
attr_reader :max_function_defs
|
16
|
+
attr_reader :max_instruction_defs
|
17
|
+
attr_reader :max_stack_elements
|
18
|
+
attr_reader :max_size_of_instructions
|
19
|
+
attr_reader :max_component_elements
|
20
|
+
attr_reader :max_component_depth
|
21
|
+
|
22
|
+
def self.encode(maxp, mapping)
|
23
|
+
num_glyphs = mapping.length
|
24
|
+
raw = maxp.raw
|
25
|
+
raw[4,2] = [num_glyphs].pack("n")
|
26
|
+
return raw
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def parse!
|
32
|
+
@version, @num_glyphs, @max_points, @max_contours, @max_component_points,
|
33
|
+
@max_component_contours, @max_zones, @max_twilight_points, @max_storage,
|
34
|
+
@max_function_defs, @max_instruction_defs, @max_stack_elements,
|
35
|
+
@max_size_of_instructions, @max_component_elements, @max_component_depth =
|
36
|
+
read(length, "Nn*")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'ttfunk/table'
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class Table
|
5
|
+
class Name < Table
|
6
|
+
class String < ::String
|
7
|
+
attr_reader :platform_id
|
8
|
+
attr_reader :encoding_id
|
9
|
+
attr_reader :language_id
|
10
|
+
|
11
|
+
def initialize(text, platform_id, encoding_id, language_id)
|
12
|
+
super(text)
|
13
|
+
@platform_id = platform_id
|
14
|
+
@encoding_id = encoding_id
|
15
|
+
@language_id = language_id
|
16
|
+
end
|
17
|
+
|
18
|
+
def strip_extended
|
19
|
+
stripped = gsub(/[\x00-\x19\x80-\xff]/n, "")
|
20
|
+
stripped = "[not-postscript]" if stripped.empty?
|
21
|
+
return stripped
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :strings
|
26
|
+
|
27
|
+
attr_reader :copyright
|
28
|
+
attr_reader :font_family
|
29
|
+
attr_reader :font_subfamily
|
30
|
+
attr_reader :unique_subfamily
|
31
|
+
attr_reader :font_name
|
32
|
+
attr_reader :version
|
33
|
+
attr_reader :trademark
|
34
|
+
attr_reader :manufacturer
|
35
|
+
attr_reader :designer
|
36
|
+
attr_reader :description
|
37
|
+
attr_reader :vendor_url
|
38
|
+
attr_reader :designer_url
|
39
|
+
attr_reader :license
|
40
|
+
attr_reader :license_url
|
41
|
+
attr_reader :preferred_family
|
42
|
+
attr_reader :preferred_subfamily
|
43
|
+
attr_reader :compatible_full
|
44
|
+
attr_reader :sample_text
|
45
|
+
|
46
|
+
@@subset_tag = "AAAAAA"
|
47
|
+
|
48
|
+
def self.encode(names)
|
49
|
+
tag = @@subset_tag.dup
|
50
|
+
@@subset_tag.succ!
|
51
|
+
|
52
|
+
postscript_name = Name::String.new("#{tag}+#{names.postscript_name}", 1, 0, 0)
|
53
|
+
|
54
|
+
strings = names.strings.dup
|
55
|
+
strings[6] = [postscript_name]
|
56
|
+
str_count = strings.inject(0) { |sum, (id, list)| sum + list.length }
|
57
|
+
|
58
|
+
table = [0, str_count, 6 + 12 * str_count].pack("n*")
|
59
|
+
strtable = ""
|
60
|
+
|
61
|
+
strings.each do |id, list|
|
62
|
+
list.each do |string|
|
63
|
+
table << [string.platform_id, string.encoding_id, string.language_id, id, string.length, strtable.length].pack("n*")
|
64
|
+
strtable << string
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
table << strtable
|
69
|
+
end
|
70
|
+
|
71
|
+
def postscript_name
|
72
|
+
return @postscript_name if @postscript_name
|
73
|
+
font_family.first || "unnamed"
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def parse!
|
79
|
+
format, count, string_offset = read(6, "n*")
|
80
|
+
|
81
|
+
entries = []
|
82
|
+
count.times do
|
83
|
+
platform, encoding, language, id, length, start_offset = read(12, "n*")
|
84
|
+
entries << {
|
85
|
+
:platform_id => platform,
|
86
|
+
:encoding_id => encoding,
|
87
|
+
:language_id => language,
|
88
|
+
:name_id => id,
|
89
|
+
:length => length,
|
90
|
+
:offset => offset + string_offset + start_offset
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
@strings = Hash.new { |h,k| h[k] = [] }
|
95
|
+
|
96
|
+
count.times do |i|
|
97
|
+
io.pos = entries[i][:offset]
|
98
|
+
text = io.read(entries[i][:length])
|
99
|
+
@strings[entries[i][:name_id]] << Name::String.new(text,
|
100
|
+
entries[i][:platform_id], entries[i][:encoding_id], entries[i][:language_id])
|
101
|
+
end
|
102
|
+
|
103
|
+
@copyright = @strings[0]
|
104
|
+
@font_family = @strings[1]
|
105
|
+
@font_subfamily = @strings[2]
|
106
|
+
@unique_subfamily = @strings[3]
|
107
|
+
@font_name = @strings[4]
|
108
|
+
@version = @strings[5]
|
109
|
+
@postscript_name = @strings[6].first.strip_extended # should only be ONE postscript name
|
110
|
+
@trademark = @strings[7]
|
111
|
+
@manufacturer = @strings[8]
|
112
|
+
@designer = @strings[9]
|
113
|
+
@description = @strings[10]
|
114
|
+
@vendor_url = @strings[11]
|
115
|
+
@designer_url = @strings[12]
|
116
|
+
@license = @strings[13]
|
117
|
+
@license_url = @strings[14]
|
118
|
+
@preferred_family = @strings[15]
|
119
|
+
@preferred_subfamily = @strings[17]
|
120
|
+
@compatible_full = @strings[18]
|
121
|
+
@sample_text = @strings[19]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'ttfunk/table'
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class Table
|
5
|
+
class OS2 < Table
|
6
|
+
attr_reader :version
|
7
|
+
|
8
|
+
attr_reader :ave_char_width
|
9
|
+
attr_reader :weight_class
|
10
|
+
attr_reader :width_class
|
11
|
+
attr_reader :type
|
12
|
+
attr_reader :y_subscript_x_size
|
13
|
+
attr_reader :y_subscript_y_size
|
14
|
+
attr_reader :y_subscript_x_offset
|
15
|
+
attr_reader :y_subscript_y_offset
|
16
|
+
attr_reader :y_superscript_x_size
|
17
|
+
attr_reader :y_superscript_y_size
|
18
|
+
attr_reader :y_superscript_x_offset
|
19
|
+
attr_reader :y_superscript_y_offset
|
20
|
+
attr_reader :y_strikeout_size
|
21
|
+
attr_reader :y_strikeout_position
|
22
|
+
attr_reader :family_class
|
23
|
+
attr_reader :panose
|
24
|
+
attr_reader :char_range
|
25
|
+
attr_reader :vendor_id
|
26
|
+
attr_reader :selection
|
27
|
+
attr_reader :first_char_index
|
28
|
+
attr_reader :last_char_index
|
29
|
+
|
30
|
+
attr_reader :ascent
|
31
|
+
attr_reader :descent
|
32
|
+
attr_reader :line_gap
|
33
|
+
attr_reader :win_ascent
|
34
|
+
attr_reader :win_descent
|
35
|
+
attr_reader :code_page_range
|
36
|
+
|
37
|
+
attr_reader :x_height
|
38
|
+
attr_reader :cap_height
|
39
|
+
attr_reader :default_char
|
40
|
+
attr_reader :break_char
|
41
|
+
attr_reader :max_context
|
42
|
+
|
43
|
+
def tag
|
44
|
+
"OS/2"
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def parse!
|
50
|
+
@version = read(2, "n").first
|
51
|
+
|
52
|
+
@ave_char_width = read_signed(1)
|
53
|
+
@weight_class, @width_class = read(4, "nn")
|
54
|
+
@type, @y_subscript_x_size, @y_subscript_y_size, @y_subscript_x_offset,
|
55
|
+
@y_subscript_y_offset, @y_superscript_x_size, @y_superscript_y_size,
|
56
|
+
@y_superscript_x_offset, @y_superscript_y_offset, @y_strikeout_size,
|
57
|
+
@y_strikeout_position, @family_class = read_signed(12)
|
58
|
+
@panose = io.read(10)
|
59
|
+
|
60
|
+
@char_range = io.read(16)
|
61
|
+
@vendor_id = io.read(4)
|
62
|
+
|
63
|
+
@selection, @first_char_index, @last_char_index = read(6, "n*")
|
64
|
+
|
65
|
+
if @version > 0
|
66
|
+
@ascent, @descent, @line_gap = read_signed(3)
|
67
|
+
@win_ascent, @win_descent = read(4, "nn")
|
68
|
+
@code_page_range = io.read(8)
|
69
|
+
|
70
|
+
if @version > 1
|
71
|
+
@x_height, @cap_height = read_signed(2)
|
72
|
+
@default_char, @break_char, @max_context = read(6, "nnn")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module TTFunk
|
2
|
+
class Table
|
3
|
+
class Post
|
4
|
+
module Format10
|
5
|
+
POSTSCRIPT_GLYPHS = %w(
|
6
|
+
.notdef .null nonmarkingreturn space exclam quotedbl numbersign dollar percent
|
7
|
+
ampersand quotesingle parenleft parenright asterisk plus comma hyphen period slash
|
8
|
+
zero one two three four five six seven eight nine colon semicolon less equal greater
|
9
|
+
question at A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
|
10
|
+
bracketleft backslash bracketright asciicircum underscore grave
|
11
|
+
a b c d e f g h i j k l m n o p q r s t u v w x y z
|
12
|
+
braceleft bar braceright asciitilde Adieresis Aring Ccedilla Eacute Ntilde Odieresis
|
13
|
+
Udieresis aacute agrave acircumflex adieresis atilde aring ccedilla eacute egrave
|
14
|
+
ecircumflex edieresis iacute igrave icircumflex idieresis ntilde oacute ograve
|
15
|
+
ocircumflex odieresis otilde uacute ugrave ucircumflex udieresis dagger degree cent
|
16
|
+
sterling section bullet paragraph germandbls registered copyright trademark acute
|
17
|
+
dieresis notequal AE Oslash infinity plusminus lessequal greaterequal yen mu
|
18
|
+
partialdiff summation product pi integral ordfeminine ordmasculine Omega ae oslash
|
19
|
+
questiondown exclamdown logicalnot radical florin approxequal Delta guillemotleft
|
20
|
+
guillemotright ellipsis nonbreakingspace Agrave Atilde Otilde OE oe endash emdash
|
21
|
+
quotedblleft quotedblright quoteleft quoteright divide lozenge ydieresis Ydieresis
|
22
|
+
fraction currency guilsinglleft guilsinglright fi fl daggerdbl periodcentered
|
23
|
+
quotesinglbase quotedblbase perthousand Acircumflex Ecircumflex Aacute Edieresis
|
24
|
+
Egrave Iacute Icircumflex Idieresis Igrave Oacute Ocircumflex apple Ograve Uacute
|
25
|
+
Ucircumflex Ugrave dotlessi circumflex tilde macron breve dotaccent ring cedilla
|
26
|
+
hungarumlaut ogonek caron Lslash lslash Scaron scaron Zcaron zcaron brokenbar Eth
|
27
|
+
eth Yacute yacute Thorn thorn minus multiply onesuperior twosuperior threesuperior
|
28
|
+
onehalf onequarter threequarters franc Gbreve gbreve Idotaccent Scedilla scedilla
|
29
|
+
Cacute cacute Ccaron ccaron dcroat)
|
30
|
+
|
31
|
+
def glyph_for(code)
|
32
|
+
POSTSCRIPT_GLYPHS[code] || ".notdef"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def parse_format!
|
38
|
+
# do nothing. Format 1 is easy-sauce.
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'ttfunk/table/post/format10'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module TTFunk
|
5
|
+
class Table
|
6
|
+
class Post
|
7
|
+
module Format20
|
8
|
+
include Format10
|
9
|
+
|
10
|
+
def glyph_for(code)
|
11
|
+
index = @glyph_name_index[code]
|
12
|
+
if index <= 257
|
13
|
+
POSTSCRIPT_GLYPHS[index]
|
14
|
+
else
|
15
|
+
@names[index - 258] || ".notdef"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def parse_format!
|
22
|
+
number_of_glyphs = read(2, 'n').first
|
23
|
+
@glyph_name_index = read(number_of_glyphs*2, 'n*')
|
24
|
+
@names = []
|
25
|
+
|
26
|
+
strings = StringIO.new(io.read(offset + length - io.pos))
|
27
|
+
while !strings.eof?
|
28
|
+
length = strings.read(1).unpack("C").first
|
29
|
+
@names << strings.read(length)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'ttfunk/table/post/format10'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module TTFunk
|
5
|
+
class Table
|
6
|
+
class Post
|
7
|
+
module Format25
|
8
|
+
include Format10
|
9
|
+
|
10
|
+
def glyph_for(code)
|
11
|
+
POSTSCRIPT_GLYPHS[code + @offsets[code]] || ".notdef"
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def parse_format!
|
17
|
+
number_of_glyphs = read(2, 'n').first
|
18
|
+
@offsets = read(@number_of_glyphs, "c*")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|