ttfunk 1.5.1 → 1.6.0
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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +60 -0
- data/README.md +2 -1
- data/lib/ttfunk.rb +45 -0
- data/lib/ttfunk/aggregate.rb +15 -0
- data/lib/ttfunk/bin_utils.rb +47 -0
- data/lib/ttfunk/bit_field.rb +31 -0
- data/lib/ttfunk/collection.rb +3 -1
- data/lib/ttfunk/directory.rb +6 -0
- data/lib/ttfunk/encoded_string.rb +97 -0
- data/lib/ttfunk/max.rb +25 -0
- data/lib/ttfunk/min.rb +25 -0
- data/lib/ttfunk/one_based_array.rb +36 -0
- data/lib/ttfunk/otf_encoder.rb +61 -0
- data/lib/ttfunk/placeholder.rb +13 -0
- data/lib/ttfunk/reader.rb +34 -32
- data/lib/ttfunk/resource_file.rb +7 -5
- data/lib/ttfunk/sci_form.rb +29 -0
- data/lib/ttfunk/sub_table.rb +38 -0
- data/lib/ttfunk/subset.rb +2 -0
- data/lib/ttfunk/subset/base.rb +61 -120
- data/lib/ttfunk/subset/code_page.rb +89 -0
- data/lib/ttfunk/subset/mac_roman.rb +5 -42
- data/lib/ttfunk/subset/unicode.rb +12 -6
- data/lib/ttfunk/subset/unicode_8bit.rb +14 -12
- data/lib/ttfunk/subset/windows_1252.rb +5 -47
- data/lib/ttfunk/subset_collection.rb +4 -0
- data/lib/ttfunk/sum.rb +20 -0
- data/lib/ttfunk/table.rb +4 -0
- data/lib/ttfunk/table/cff.rb +69 -0
- data/lib/ttfunk/table/cff/charset.rb +212 -0
- data/lib/ttfunk/table/cff/charsets.rb +14 -0
- data/lib/ttfunk/table/cff/charsets/expert.rb +189 -0
- data/lib/ttfunk/table/cff/charsets/expert_subset.rb +119 -0
- data/lib/ttfunk/table/cff/charsets/iso_adobe.rb +241 -0
- data/lib/ttfunk/table/cff/charsets/standard_strings.rb +404 -0
- data/lib/ttfunk/table/cff/charstring.rb +487 -0
- data/lib/ttfunk/table/cff/charstrings_index.rb +39 -0
- data/lib/ttfunk/table/cff/dict.rb +266 -0
- data/lib/ttfunk/table/cff/encoding.rb +220 -0
- data/lib/ttfunk/table/cff/encodings.rb +12 -0
- data/lib/ttfunk/table/cff/encodings/expert.rb +206 -0
- data/lib/ttfunk/table/cff/encodings/standard.rb +181 -0
- data/lib/ttfunk/table/cff/fd_selector.rb +150 -0
- data/lib/ttfunk/table/cff/font_dict.rb +79 -0
- data/lib/ttfunk/table/cff/font_index.rb +29 -0
- data/lib/ttfunk/table/cff/header.rb +33 -0
- data/lib/ttfunk/table/cff/index.rb +125 -0
- data/lib/ttfunk/table/cff/one_based_index.rb +31 -0
- data/lib/ttfunk/table/cff/path.rb +66 -0
- data/lib/ttfunk/table/cff/private_dict.rb +84 -0
- data/lib/ttfunk/table/cff/subr_index.rb +19 -0
- data/lib/ttfunk/table/cff/top_dict.rb +230 -0
- data/lib/ttfunk/table/cff/top_index.rb +16 -0
- data/lib/ttfunk/table/cmap.rb +4 -4
- data/lib/ttfunk/table/cmap/format00.rb +1 -2
- data/lib/ttfunk/table/cmap/format04.rb +11 -3
- data/lib/ttfunk/table/cmap/format06.rb +2 -0
- data/lib/ttfunk/table/cmap/format10.rb +2 -0
- data/lib/ttfunk/table/cmap/format12.rb +2 -0
- data/lib/ttfunk/table/cmap/subtable.rb +12 -8
- data/lib/ttfunk/table/dsig.rb +50 -0
- data/lib/ttfunk/table/glyf.rb +11 -9
- data/lib/ttfunk/table/glyf/compound.rb +14 -7
- data/lib/ttfunk/table/glyf/path_based.rb +47 -0
- data/lib/ttfunk/table/glyf/simple.rb +21 -15
- data/lib/ttfunk/table/head.rb +43 -5
- data/lib/ttfunk/table/hhea.rb +47 -4
- data/lib/ttfunk/table/hmtx.rb +11 -4
- data/lib/ttfunk/table/kern.rb +3 -0
- data/lib/ttfunk/table/kern/format0.rb +3 -0
- data/lib/ttfunk/table/loca.rb +2 -0
- data/lib/ttfunk/table/maxp.rb +144 -10
- data/lib/ttfunk/table/name.rb +75 -37
- data/lib/ttfunk/table/os2.rb +327 -4
- data/lib/ttfunk/table/post.rb +8 -1
- data/lib/ttfunk/table/post/format10.rb +2 -0
- data/lib/ttfunk/table/post/format20.rb +5 -1
- data/lib/ttfunk/table/post/format30.rb +2 -0
- data/lib/ttfunk/table/post/format40.rb +2 -0
- data/lib/ttfunk/table/sbix.rb +2 -0
- data/lib/ttfunk/table/simple.rb +2 -0
- data/lib/ttfunk/table/vorg.rb +54 -0
- data/lib/ttfunk/ttf_encoder.rb +220 -0
- metadata +88 -20
- metadata.gz.sig +0 -0
- data/lib/ttfunk/encoding/mac_roman.rb +0 -100
- data/lib/ttfunk/encoding/windows_1252.rb +0 -76
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1036e45ae6026254c29d0d105dfab5e55864994d6b97bc88dbb0db9cd645f761
|
4
|
+
data.tar.gz: c8a8e0cd09c56f408949fb86ed2b6b19a062d3cf5697c2bc451fa363efb61238
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42012ccb4f26b39bcf82ff620a5d42417e1cfa1f678c5e398e1536c87abbe354e7caea988a0b5eb7141497e148ac8f338c3917b5a985ef5b64e153b3c2730cdc
|
7
|
+
data.tar.gz: bfb13cd6a01acfd9bbdff89a18b8261605d9acc235a229b2f75692653fe2a5761f524c53b699a587e49997685fd2bf3212536157645c41a51b77d2feedf25c52
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,66 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
### Added
|
11
|
+
|
12
|
+
* OpenType fonts support
|
13
|
+
|
14
|
+
* Added support for CFF-flavored fonts (also known as CID-keyed or OpenType fonts)
|
15
|
+
* Added support for the VORG and DSIG tables
|
16
|
+
* Improved charset encoding support
|
17
|
+
* Improved font metrics calculations in the head, maxp, hhea, hmtx, and os/2 tables
|
18
|
+
* Subsetted fonts verified with Font-Validator, fontlint, and Mac OS's Font Book
|
19
|
+
|
20
|
+
Cameron Dutro
|
21
|
+
|
22
|
+
* Ruby 2.6 support
|
23
|
+
|
24
|
+
Alexander Mankuta
|
25
|
+
|
26
|
+
* JRuby 9.2 support
|
27
|
+
|
28
|
+
Alexander Mankuta
|
29
|
+
|
30
|
+
### Removed
|
31
|
+
|
32
|
+
* Dropped Ruby 2.1 & 2.2 support
|
33
|
+
|
34
|
+
Alexander Mankuta
|
35
|
+
|
36
|
+
* Removed JRuby 9.1 support
|
37
|
+
|
38
|
+
Alexander Mankuta
|
39
|
+
|
40
|
+
### Fixed
|
41
|
+
|
42
|
+
* Sort name table entries when generating subset font
|
43
|
+
|
44
|
+
Matjaz Gregoric
|
45
|
+
|
46
|
+
* Map the 0xFFFF char code to glyph 0 in cmap format 4
|
47
|
+
|
48
|
+
Matjaz Gregoric
|
49
|
+
|
50
|
+
* Order tables by tag when generating font subset
|
51
|
+
|
52
|
+
Matjaz Gregoric
|
53
|
+
|
54
|
+
* Fix typo in TTFunk::Subset::Unicode#includes?
|
55
|
+
|
56
|
+
Matjaz Gregoric
|
57
|
+
|
58
|
+
* Fixe calculation of search_range for font subsets
|
59
|
+
|
60
|
+
Matjaz Gregoric
|
61
|
+
|
62
|
+
* Fixed instance variable @offset and @length not initialized
|
63
|
+
|
64
|
+
Katsuya HIDAKA
|
65
|
+
|
66
|
+
* Code style fixes
|
67
|
+
|
68
|
+
Katsuya HIDAKA, Matjaz Gregoric, Alexander Mankuta
|
69
|
+
|
10
70
|
## [1.5.1]
|
11
71
|
|
12
72
|
### Fixed
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# TTFunk
|
2
2
|
|
3
|
-

|
4
|
+

|
4
5
|
|
5
6
|
TTFunk is a TrueType font parser written in pure ruby.
|
6
7
|
|
data/lib/ttfunk.rb
CHANGED
@@ -1,9 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'stringio'
|
2
4
|
require 'pathname'
|
3
5
|
|
6
|
+
require_relative 'ttfunk/aggregate'
|
4
7
|
require_relative 'ttfunk/directory'
|
5
8
|
require_relative 'ttfunk/resource_file'
|
6
9
|
require_relative 'ttfunk/collection'
|
10
|
+
require_relative 'ttfunk/ttf_encoder'
|
11
|
+
require_relative 'ttfunk/encoded_string'
|
12
|
+
require_relative 'ttfunk/placeholder'
|
13
|
+
require_relative 'ttfunk/otf_encoder'
|
14
|
+
require_relative 'ttfunk/sci_form'
|
15
|
+
require_relative 'ttfunk/bit_field'
|
16
|
+
require_relative 'ttfunk/bin_utils'
|
17
|
+
require_relative 'ttfunk/sub_table'
|
18
|
+
require_relative 'ttfunk/min'
|
19
|
+
require_relative 'ttfunk/max'
|
20
|
+
require_relative 'ttfunk/sum'
|
21
|
+
require_relative 'ttfunk/one_based_array'
|
7
22
|
|
8
23
|
module TTFunk
|
9
24
|
class File
|
@@ -37,6 +52,7 @@ module TTFunk
|
|
37
52
|
# String or Pathname
|
38
53
|
io_or_path = Pathname.new(io_or_path)
|
39
54
|
raise ArgumentError, "#{io_or_path} not found" unless io_or_path.file?
|
55
|
+
|
40
56
|
io = io_or_path.open('rb')
|
41
57
|
io
|
42
58
|
end
|
@@ -116,10 +132,38 @@ module TTFunk
|
|
116
132
|
def sbix
|
117
133
|
@sbix ||= TTFunk::Table::Sbix.new(self)
|
118
134
|
end
|
135
|
+
|
136
|
+
def cff
|
137
|
+
@cff ||= TTFunk::Table::Cff.new(self)
|
138
|
+
end
|
139
|
+
|
140
|
+
def vertical_origins
|
141
|
+
@vertical_origins ||=
|
142
|
+
if directory.tables.include?(TTFunk::Table::Vorg::TAG)
|
143
|
+
TTFunk::Table::Vorg.new(self)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def digital_signature
|
148
|
+
@digital_signature ||=
|
149
|
+
if directory.tables.include?(TTFunk::Table::Dsig::TAG)
|
150
|
+
TTFunk::Table::Dsig.new(self)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def find_glyph(glyph_id)
|
155
|
+
if cff.exists?
|
156
|
+
cff.top_index[0].charstrings_index[glyph_id].glyph
|
157
|
+
else
|
158
|
+
glyph_outlines.for(glyph_id)
|
159
|
+
end
|
160
|
+
end
|
119
161
|
end
|
120
162
|
end
|
121
163
|
|
164
|
+
require_relative 'ttfunk/table/cff'
|
122
165
|
require_relative 'ttfunk/table/cmap'
|
166
|
+
require_relative 'ttfunk/table/dsig'
|
123
167
|
require_relative 'ttfunk/table/glyf'
|
124
168
|
require_relative 'ttfunk/table/head'
|
125
169
|
require_relative 'ttfunk/table/hhea'
|
@@ -131,3 +175,4 @@ require_relative 'ttfunk/table/name'
|
|
131
175
|
require_relative 'ttfunk/table/os2'
|
132
176
|
require_relative 'ttfunk/table/post'
|
133
177
|
require_relative 'ttfunk/table/sbix'
|
178
|
+
require_relative 'ttfunk/table/vorg'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
module BinUtils
|
5
|
+
# assumes big-endian
|
6
|
+
def stitch_int(arr, bit_width:)
|
7
|
+
value = 0
|
8
|
+
|
9
|
+
arr.each_with_index do |element, index|
|
10
|
+
value |= element << bit_width * index
|
11
|
+
end
|
12
|
+
|
13
|
+
value
|
14
|
+
end
|
15
|
+
|
16
|
+
# assumes big-endian
|
17
|
+
def slice_int(value, bit_width:, slice_count:)
|
18
|
+
mask = 2**bit_width - 1
|
19
|
+
|
20
|
+
Array.new(slice_count) do |i|
|
21
|
+
(value >> bit_width * i) & mask
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def twos_comp_to_int(num, bit_width:)
|
26
|
+
if num >> (bit_width - 1) == 1
|
27
|
+
# we want all ones
|
28
|
+
mask = (2**bit_width) - 1
|
29
|
+
|
30
|
+
# find 2's complement, i.e. flip bits (xor with mask) and add 1
|
31
|
+
-((num ^ mask) + 1)
|
32
|
+
else
|
33
|
+
num
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# turns a (sorted) sequence of values into a series of two-element arrays
|
38
|
+
# where the first element is the start and the second is the length
|
39
|
+
def rangify(values)
|
40
|
+
values
|
41
|
+
.slice_when { |a, b| b - a > 1 }
|
42
|
+
.map { |span| [span.first, span.length - 1] }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
BinUtils.extend(BinUtils)
|
47
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class BitField
|
5
|
+
attr_reader :value
|
6
|
+
|
7
|
+
def initialize(value = 0)
|
8
|
+
@value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def on(pos)
|
12
|
+
@value |= 2**pos
|
13
|
+
end
|
14
|
+
|
15
|
+
def on?(pos)
|
16
|
+
value & 2**pos > 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def off(pos)
|
20
|
+
@value &= 2**Math.log2(value).ceil - 2**pos - 1
|
21
|
+
end
|
22
|
+
|
23
|
+
def off?(pos)
|
24
|
+
!on?(pos)
|
25
|
+
end
|
26
|
+
|
27
|
+
def dup
|
28
|
+
self.class.new(value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/ttfunk/collection.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module TTFunk
|
2
4
|
class Collection
|
3
5
|
include Enumerable
|
@@ -13,7 +15,7 @@ module TTFunk
|
|
13
15
|
raise ArgumentError, 'not a TTC file' unless tag == 'ttcf'
|
14
16
|
|
15
17
|
_major, _minor = io.read(4).unpack('n*')
|
16
|
-
count = io.read(4).
|
18
|
+
count = io.read(4).unpack1('N')
|
17
19
|
@offsets = io.read(count * 4).unpack('N*')
|
18
20
|
|
19
21
|
io.rewind
|
data/lib/ttfunk/directory.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module TTFunk
|
2
4
|
class Directory
|
3
5
|
attr_reader :tables
|
@@ -5,6 +7,10 @@ module TTFunk
|
|
5
7
|
|
6
8
|
def initialize(io, offset = 0)
|
7
9
|
io.seek(offset)
|
10
|
+
|
11
|
+
# https://www.microsoft.com/typography/otspec/otff.htm#offsetTable
|
12
|
+
# We're ignoring searchRange, entrySelector, and rangeShift here, but
|
13
|
+
# skipping past them to get to the table information.
|
8
14
|
@scaler_type, table_count = io.read(12).unpack('Nn')
|
9
15
|
|
10
16
|
@tables = {}
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module TTFunk
|
6
|
+
class UnresolvedPlaceholderError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
class DuplicatePlaceholderError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class EncodedString
|
13
|
+
def initialize
|
14
|
+
yield self if block_given?
|
15
|
+
end
|
16
|
+
|
17
|
+
def <<(obj)
|
18
|
+
case obj
|
19
|
+
when String
|
20
|
+
io << obj
|
21
|
+
when Placeholder
|
22
|
+
add_placeholder(obj)
|
23
|
+
io << "\0" * obj.length
|
24
|
+
when self.class
|
25
|
+
# adjust placeholders to be relative to the entire encoded string
|
26
|
+
obj.placeholders.each_pair do |_, placeholder|
|
27
|
+
add_placeholder(placeholder.dup, placeholder.position + io.length)
|
28
|
+
end
|
29
|
+
|
30
|
+
self << obj.unresolved_string
|
31
|
+
end
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def align!(width = 4)
|
37
|
+
if length % width > 0
|
38
|
+
self << "\0" * (width - length % width)
|
39
|
+
end
|
40
|
+
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def length
|
45
|
+
io.length
|
46
|
+
end
|
47
|
+
|
48
|
+
def string
|
49
|
+
unless placeholders.empty?
|
50
|
+
raise UnresolvedPlaceholderError, 'string contains '\
|
51
|
+
"#{placeholders.size} unresolved placeholder(s)"
|
52
|
+
end
|
53
|
+
|
54
|
+
io.string
|
55
|
+
end
|
56
|
+
|
57
|
+
def bytes
|
58
|
+
string.bytes
|
59
|
+
end
|
60
|
+
|
61
|
+
def unresolved_string
|
62
|
+
io.string
|
63
|
+
end
|
64
|
+
|
65
|
+
def resolve_placeholder(name, value)
|
66
|
+
last_pos = io.pos
|
67
|
+
|
68
|
+
if (placeholder = placeholders[name])
|
69
|
+
io.seek(placeholder.position)
|
70
|
+
io.write(value[0..placeholder.length])
|
71
|
+
placeholders.delete(name)
|
72
|
+
end
|
73
|
+
ensure
|
74
|
+
io.seek(last_pos)
|
75
|
+
end
|
76
|
+
|
77
|
+
def placeholders
|
78
|
+
@placeholders ||= {}
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def add_placeholder(new_placeholder, pos = io.pos)
|
84
|
+
if placeholders.include?(new_placeholder.name)
|
85
|
+
raise DuplicatePlaceholderError,
|
86
|
+
"placeholder #{new_placeholder.name} already exists"
|
87
|
+
end
|
88
|
+
|
89
|
+
new_placeholder.position = pos
|
90
|
+
placeholders[new_placeholder.name] = new_placeholder
|
91
|
+
end
|
92
|
+
|
93
|
+
def io
|
94
|
+
@io ||= StringIO.new(''.b).binmode
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/ttfunk/max.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class Max < Aggregate
|
5
|
+
attr_reader :value
|
6
|
+
|
7
|
+
def initialize(init_value = nil)
|
8
|
+
@value = init_value
|
9
|
+
end
|
10
|
+
|
11
|
+
def <<(new_value)
|
12
|
+
new_value = coerce(new_value)
|
13
|
+
|
14
|
+
if value.nil? || new_value > value
|
15
|
+
@value = new_value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def value_or(default)
|
20
|
+
return default if value.nil?
|
21
|
+
|
22
|
+
value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/ttfunk/min.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class Min < Aggregate
|
5
|
+
attr_reader :value
|
6
|
+
|
7
|
+
def initialize(init_value = nil)
|
8
|
+
@value = init_value
|
9
|
+
end
|
10
|
+
|
11
|
+
def <<(new_value)
|
12
|
+
new_value = coerce(new_value)
|
13
|
+
|
14
|
+
if value.nil? || new_value < value
|
15
|
+
@value = new_value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def value_or(default)
|
20
|
+
return default if value.nil?
|
21
|
+
|
22
|
+
value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|