ttfunk 1.7.0 → 1.8.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +74 -0
- data/README.md +17 -15
- data/lib/ttfunk/aggregate.rb +5 -0
- data/lib/ttfunk/bin_utils.rb +27 -8
- data/lib/ttfunk/bit_field.rb +25 -2
- data/lib/ttfunk/collection.rb +27 -3
- data/lib/ttfunk/directory.rb +7 -1
- data/lib/ttfunk/encoded_string.rb +58 -4
- data/lib/ttfunk/max.rb +14 -0
- data/lib/ttfunk/min.rb +14 -0
- data/lib/ttfunk/one_based_array.rb +20 -0
- data/lib/ttfunk/otf_encoder.rb +5 -14
- data/lib/ttfunk/placeholder.rb +15 -1
- data/lib/ttfunk/reader.rb +6 -4
- data/lib/ttfunk/resource_file.rb +29 -5
- data/lib/ttfunk/sci_form.rb +20 -3
- data/lib/ttfunk/sub_table.rb +29 -4
- data/lib/ttfunk/subset/base.rb +48 -0
- data/lib/ttfunk/subset/code_page.rb +49 -2
- data/lib/ttfunk/subset/mac_roman.rb +2 -0
- data/lib/ttfunk/subset/unicode.rb +32 -0
- data/lib/ttfunk/subset/unicode_8bit.rb +32 -0
- data/lib/ttfunk/subset/windows_1252.rb +2 -0
- data/lib/ttfunk/subset.rb +8 -0
- data/lib/ttfunk/subset_collection.rb +39 -14
- data/lib/ttfunk/sum.rb +13 -0
- data/lib/ttfunk/table/cff/charset.rb +96 -18
- data/lib/ttfunk/table/cff/charsets/expert.rb +3 -2
- data/lib/ttfunk/table/cff/charsets/expert_subset.rb +3 -2
- data/lib/ttfunk/table/cff/charsets/iso_adobe.rb +3 -2
- data/lib/ttfunk/table/cff/charsets/standard_strings.rb +3 -2
- data/lib/ttfunk/table/cff/charsets.rb +1 -0
- data/lib/ttfunk/table/cff/charstring.rb +33 -12
- data/lib/ttfunk/table/cff/charstrings_index.rb +17 -11
- data/lib/ttfunk/table/cff/dict.rb +53 -23
- data/lib/ttfunk/table/cff/encoding.rb +82 -24
- data/lib/ttfunk/table/cff/encodings/expert.rb +3 -2
- data/lib/ttfunk/table/cff/encodings/standard.rb +3 -2
- data/lib/ttfunk/table/cff/encodings.rb +1 -0
- data/lib/ttfunk/table/cff/fd_selector.rb +61 -21
- data/lib/ttfunk/table/cff/font_dict.rb +30 -18
- data/lib/ttfunk/table/cff/font_index.rb +22 -10
- data/lib/ttfunk/table/cff/header.rb +16 -3
- data/lib/ttfunk/table/cff/index.rb +97 -65
- data/lib/ttfunk/table/cff/one_based_index.rb +11 -1
- data/lib/ttfunk/table/cff/path.rb +43 -4
- data/lib/ttfunk/table/cff/private_dict.rb +31 -11
- data/lib/ttfunk/table/cff/subr_index.rb +7 -2
- data/lib/ttfunk/table/cff/top_dict.rb +82 -59
- data/lib/ttfunk/table/cff/top_index.rb +10 -6
- data/lib/ttfunk/table/cff.rb +41 -21
- data/lib/ttfunk/table/cmap/format00.rb +27 -6
- data/lib/ttfunk/table/cmap/format04.rb +34 -14
- data/lib/ttfunk/table/cmap/format06.rb +28 -1
- data/lib/ttfunk/table/cmap/format10.rb +29 -2
- data/lib/ttfunk/table/cmap/format12.rb +29 -2
- data/lib/ttfunk/table/cmap/subtable.rb +50 -6
- data/lib/ttfunk/table/cmap.rb +21 -0
- data/lib/ttfunk/table/dsig.rb +47 -6
- data/lib/ttfunk/table/glyf/compound.rb +73 -6
- data/lib/ttfunk/table/glyf/path_based.rb +40 -3
- data/lib/ttfunk/table/glyf/simple.rb +50 -5
- data/lib/ttfunk/table/glyf.rb +15 -7
- data/lib/ttfunk/table/head.rb +84 -6
- data/lib/ttfunk/table/hhea.rb +71 -10
- data/lib/ttfunk/table/hmtx.rb +32 -5
- data/lib/ttfunk/table/kern/format0.rb +25 -7
- data/lib/ttfunk/table/kern.rb +16 -4
- data/lib/ttfunk/table/loca.rb +21 -8
- data/lib/ttfunk/table/maxp.rb +195 -10
- data/lib/ttfunk/table/name.rb +126 -9
- data/lib/ttfunk/table/os2.rb +150 -26
- data/lib/ttfunk/table/post/format10.rb +7 -0
- data/lib/ttfunk/table/post/format20.rb +9 -0
- data/lib/ttfunk/table/post/format30.rb +6 -0
- data/lib/ttfunk/table/post/format40.rb +5 -0
- data/lib/ttfunk/table/post.rb +63 -7
- data/lib/ttfunk/table/sbix.rb +50 -14
- data/lib/ttfunk/table/simple.rb +5 -0
- data/lib/ttfunk/table/vorg.rb +31 -3
- data/lib/ttfunk/table.rb +20 -1
- data/lib/ttfunk/ttf_encoder.rb +39 -41
- data/lib/ttfunk.rb +154 -1
- data.tar.gz.sig +0 -0
- metadata +50 -28
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb1a969a808f290fe7ac4dda94e2fff26dbfc69ba183b6e409062c18b28cfc7b
|
4
|
+
data.tar.gz: 89534c924740d79858f0fe8a9e8bd1830ad7fa89473d170365870eb15d9345c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b5282d51944572615cb694dc810e55e45961cb9a122fa31481542fd2417e5465bae2585b2e06867221b93fb2f2cbf885bca9b9bd7fb34bd1ea21cde560f83e9b
|
7
|
+
data.tar.gz: ae54beef0f1f1cbc9b7e02929d73a1da5264239611802f57c2d9f5ce7cba7db4ca9cee25847a2d07ed2cccfdcce6cfd04e6e53cffc694fe94996a8a581336cea
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,80 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
### Fixed
|
11
|
+
|
12
|
+
* Corrupted CFF index data
|
13
|
+
|
14
|
+
there was a subtle bug in cff index implementation that resulted in
|
15
|
+
a data corruption. in certain circumstances some items didn't get
|
16
|
+
properly encoded. this happened when items were not previously accessed.
|
17
|
+
|
18
|
+
this resulted, for instance, in missing glyphs. but only sometimes
|
19
|
+
because indexes might've still contain data that shouldn't've been
|
20
|
+
there. in combination with incorrect encoding (see further) this
|
21
|
+
resulted in some glyphs still being rendered, sometimes even correctly.
|
22
|
+
|
23
|
+
along with the fix a rather large api change landed. this resulted in
|
24
|
+
quite a big diff.
|
25
|
+
|
26
|
+
Alexander Mankuta
|
27
|
+
|
28
|
+
* Incorrect CFF encoding in subsets
|
29
|
+
|
30
|
+
TTFunk used to reuse encoding from the original font. This mapping was
|
31
|
+
incorrect for subset fonts which used not just a subset of glyphs but
|
32
|
+
also a different encoding.
|
33
|
+
|
34
|
+
A separate issue was that some fonts have empty CFF encoding. This
|
35
|
+
incorrect mapping resulted in encoding that mapped all codes to glyph 0.
|
36
|
+
|
37
|
+
This had impact on Prawn in particular. PDF spec explicitly says that
|
38
|
+
CFF encoding is not to be used in OpenType fonts. `cmap` table should
|
39
|
+
directly index charstrings in the CFF table. Despite this PDF renderers
|
40
|
+
still use CFF encoding to retrieve glyphs. So TTFunk has to discard the
|
41
|
+
original CFF encoding and supply its own.
|
42
|
+
|
43
|
+
Alexander Mankuta
|
44
|
+
|
45
|
+
* `maxp` table
|
46
|
+
|
47
|
+
The table is now correctly parsed and encoded for both TrueType and CFF-based
|
48
|
+
OpenType fonts.
|
49
|
+
|
50
|
+
Cameron Dutro, Alexander Mankuta
|
51
|
+
|
52
|
+
* Files are closed sooner
|
53
|
+
|
54
|
+
Files were garbage collected but could stay open for longer than necessary.
|
55
|
+
|
56
|
+
Jon Burgess
|
57
|
+
|
58
|
+
* Long date time in the `head` table
|
59
|
+
|
60
|
+
The `created` and `modified` fields we parsed and encoded with incorrect
|
61
|
+
endiannes. Additionally helper methods were added to convert these fields to
|
62
|
+
and from Ruby `Time`.
|
63
|
+
|
64
|
+
Jens Kutilek, Peter Goldstein
|
65
|
+
|
66
|
+
* Removed execution permissions on non-executable files
|
67
|
+
|
68
|
+
Keenan Brock
|
69
|
+
|
70
|
+
### Changes
|
71
|
+
|
72
|
+
* Minimum Ruby is 2.7
|
73
|
+
|
74
|
+
Alexander Mankuta
|
75
|
+
|
76
|
+
* Performance improvement in subsets construction
|
77
|
+
|
78
|
+
Thomas Leitner
|
79
|
+
|
80
|
+
* CI improvememnts
|
81
|
+
|
82
|
+
Peter Goldstein
|
83
|
+
|
10
84
|
## 1.7.0
|
11
85
|
|
12
86
|
### Changes
|
data/README.md
CHANGED
@@ -3,24 +3,29 @@
|
|
3
3
|

|
4
4
|

|
5
5
|
|
6
|
-
TTFunk is a TrueType font
|
6
|
+
TTFunk is a TrueType and OpenType font library written in pure ruby. It supports
|
7
|
+
both parsing and encoding of fonts. Also provides limited font subsetting.
|
7
8
|
|
8
9
|
## Installation
|
9
10
|
|
10
11
|
The recommended installation method is via Rubygems.
|
11
12
|
|
12
|
-
|
13
|
+
```shell
|
14
|
+
gem install ttfunk
|
15
|
+
```
|
13
16
|
|
14
17
|
## Usage
|
15
18
|
|
16
19
|
Basic usage:
|
17
20
|
|
18
|
-
|
21
|
+
```ruby
|
22
|
+
require 'ttfunk'
|
19
23
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
+
file = TTFunk::File.open("some/path/myfont.ttf")
|
25
|
+
puts "name : #{file.name.font_name.join(', ')}"
|
26
|
+
puts "ascent : #{file.ascent}"
|
27
|
+
puts "descent : #{file.descent}"
|
28
|
+
```
|
24
29
|
|
25
30
|
For more detailed examples, explore the examples directory.
|
26
31
|
|
@@ -32,15 +37,12 @@ Matz's terms for Ruby, GPLv2, or GPLv3. See LICENSE for details.
|
|
32
37
|
|
33
38
|
This project is maintained by the same folks who run the Prawn PDF project.
|
34
39
|
|
35
|
-
|
36
|
-
|
40
|
+
Here's the [full list](https://github.com/prawnpdf/ttfunk/contributors) of
|
41
|
+
Github users who have at least one patch accepted to TTFunk.
|
37
42
|
|
38
|
-
|
39
|
-
|
40
|
-
## Mailing List
|
43
|
+
## Community support
|
41
44
|
|
42
45
|
TTFunk is maintained as a dependency of Prawn, the ruby PDF generation library.
|
43
46
|
|
44
|
-
Any questions or feedback should be sent to the Prawn
|
45
|
-
|
46
|
-
https://groups.google.com/group/prawn-ruby
|
47
|
+
Any questions or feedback should be sent to the [Prawn
|
48
|
+
Diccussions](https://github.com/orgs/prawnpdf/discussions) group.
|
data/lib/ttfunk/aggregate.rb
CHANGED
data/lib/ttfunk/bin_utils.rb
CHANGED
@@ -1,27 +1,43 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module TTFunk
|
3
|
+
module TTFunk # rubocop: disable Style/Documentation # false positive
|
4
|
+
# Bit crunching utility methods.
|
4
5
|
module BinUtils
|
5
|
-
#
|
6
|
+
# Turn a bunch of small integers into one big integer. Assumes big-endian.
|
7
|
+
#
|
8
|
+
# @param arr [Array<Integer>]
|
9
|
+
# @param bit_width [Integer] bit width of the elements
|
10
|
+
# @return [Integer]
|
6
11
|
def stitch_int(arr, bit_width:)
|
7
12
|
value = 0
|
8
13
|
|
9
14
|
arr.each_with_index do |element, index|
|
10
|
-
value |= element << bit_width * index
|
15
|
+
value |= element << (bit_width * index)
|
11
16
|
end
|
12
17
|
|
13
18
|
value
|
14
19
|
end
|
15
20
|
|
16
|
-
#
|
21
|
+
# Slice a big integer into a bunch of small integers. Assumes big-endian.
|
22
|
+
#
|
23
|
+
# @param value [Integer]
|
24
|
+
# @param bit_width [Integer] bit width of the elements
|
25
|
+
# @param slice_count [Integer] number of elements to slice into. This is
|
26
|
+
# needed for cases where top bits are zero.
|
27
|
+
# @return [Array<Integer>]
|
17
28
|
def slice_int(value, bit_width:, slice_count:)
|
18
|
-
mask = 2**bit_width - 1
|
29
|
+
mask = (2**bit_width) - 1
|
19
30
|
|
20
31
|
Array.new(slice_count) do |i|
|
21
|
-
(value >> bit_width * i) & mask
|
32
|
+
(value >> (bit_width * i)) & mask
|
22
33
|
end
|
23
34
|
end
|
24
35
|
|
36
|
+
# Two's compliment to an integer.
|
37
|
+
#
|
38
|
+
# @param num [Integer]
|
39
|
+
# @param bit_width [Integer] number width
|
40
|
+
# @return [Integer]
|
25
41
|
def twos_comp_to_int(num, bit_width:)
|
26
42
|
if num >> (bit_width - 1) == 1
|
27
43
|
# we want all ones
|
@@ -34,8 +50,11 @@ module TTFunk
|
|
34
50
|
end
|
35
51
|
end
|
36
52
|
|
37
|
-
#
|
38
|
-
# where the first element is the start and the second is the length
|
53
|
+
# Turns a (sorted) sequence of values into a series of two-element arrays
|
54
|
+
# where the first element is the start and the second is the length.
|
55
|
+
#
|
56
|
+
# @param values [Array<Integer>]
|
57
|
+
# @return [Array<Array(Integer, Integer)>]
|
39
58
|
def rangify(values)
|
40
59
|
values
|
41
60
|
.slice_when { |a, b| b - a > 1 }
|
data/lib/ttfunk/bit_field.rb
CHANGED
@@ -1,29 +1,52 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TTFunk
|
4
|
+
# Bitfield represents a series of bits that can individually be toggled.
|
4
5
|
class BitField
|
6
|
+
# Serialized value.
|
7
|
+
# @return [Integer]
|
5
8
|
attr_reader :value
|
6
9
|
|
10
|
+
# @param value [Integer] initial value
|
7
11
|
def initialize(value = 0)
|
8
12
|
@value = value
|
9
13
|
end
|
10
14
|
|
15
|
+
# Set bit on.
|
16
|
+
#
|
17
|
+
# @param pos [Integer] bit position
|
18
|
+
# @return [void]
|
11
19
|
def on(pos)
|
12
20
|
@value |= 2**pos
|
13
21
|
end
|
14
22
|
|
23
|
+
# If bit on?
|
24
|
+
#
|
25
|
+
# @param pos [Integer]
|
26
|
+
# @return [Boolean]
|
15
27
|
def on?(pos)
|
16
|
-
(value & 2**pos).positive?
|
28
|
+
(value & (2**pos)).positive?
|
17
29
|
end
|
18
30
|
|
31
|
+
# Set bit off.
|
32
|
+
#
|
33
|
+
# @param pos [Integer]
|
34
|
+
# @return [void]
|
19
35
|
def off(pos)
|
20
|
-
@value &= 2**Math.log2(value).ceil - 2**pos - 1
|
36
|
+
@value &= (2**Math.log2(value).ceil) - (2**pos) - 1
|
21
37
|
end
|
22
38
|
|
39
|
+
# Is bit off?
|
40
|
+
#
|
41
|
+
# @param pos [Integer]
|
42
|
+
# @return [Boolean]
|
23
43
|
def off?(pos)
|
24
44
|
!on?(pos)
|
25
45
|
end
|
26
46
|
|
47
|
+
# Get a duplicate of this bit field.
|
48
|
+
#
|
49
|
+
# @return [BitField]
|
27
50
|
def dup
|
28
51
|
self.class.new(value)
|
29
52
|
end
|
data/lib/ttfunk/collection.rb
CHANGED
@@ -1,21 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TTFunk
|
4
|
+
# TrueType font collection. Usually a file with `.ttc` extension.
|
4
5
|
class Collection
|
5
6
|
include Enumerable
|
6
7
|
|
8
|
+
# Load a TrueType collection.
|
9
|
+
#
|
10
|
+
# @overload open(io)
|
11
|
+
# @param io [IO] IO to read the collection from.
|
12
|
+
# @yieldparam collection [TTFunk::Collection]
|
13
|
+
# @return [any] whatever the block returns
|
14
|
+
# @overload open(file_path)
|
15
|
+
# @param file_path [String, Pathname] Path to the font collection file.
|
16
|
+
# @yieldparam collection [TTFunk::Collection]
|
17
|
+
# @return [any] whatever the block returns
|
7
18
|
def self.open(path)
|
8
19
|
if path.respond_to?(:read)
|
9
|
-
result = yield
|
20
|
+
result = yield(new(path))
|
10
21
|
path.rewind
|
11
22
|
result
|
12
23
|
else
|
13
24
|
::File.open(path, 'rb') do |io|
|
14
|
-
yield
|
25
|
+
yield(new(io))
|
15
26
|
end
|
16
27
|
end
|
17
28
|
end
|
18
29
|
|
30
|
+
# @param io [IO(#read & #rewind)]
|
31
|
+
# @raise [ArgumentError] if `io` doesn't start with a ttc tag
|
19
32
|
def initialize(io)
|
20
33
|
tag = io.read(4)
|
21
34
|
raise ArgumentError, 'not a TTC file' unless tag == 'ttcf'
|
@@ -29,17 +42,28 @@ module TTFunk
|
|
29
42
|
@cache = []
|
30
43
|
end
|
31
44
|
|
45
|
+
# Number of fonts in this collection.
|
46
|
+
#
|
47
|
+
# @return [Integer]
|
32
48
|
def count
|
33
49
|
@offsets.length
|
34
50
|
end
|
35
51
|
|
52
|
+
# Iterate over fonts in the collection.
|
53
|
+
#
|
54
|
+
# @yieldparam font [TTFunk::File]
|
55
|
+
# @return [self]
|
36
56
|
def each
|
37
57
|
count.times do |index|
|
38
|
-
yield
|
58
|
+
yield(self[index])
|
39
59
|
end
|
40
60
|
self
|
41
61
|
end
|
42
62
|
|
63
|
+
# Get font by index.
|
64
|
+
#
|
65
|
+
# @param index [Integer]
|
66
|
+
# @return [TTFunk::File]
|
43
67
|
def [](index)
|
44
68
|
@cache[index] ||= TTFunk::File.new(@contents, @offsets[index])
|
45
69
|
end
|
data/lib/ttfunk/directory.rb
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TTFunk
|
4
|
+
# SFNT table directory.
|
4
5
|
class Directory
|
6
|
+
# Table descriptors
|
7
|
+
# @return [Hash{String => Hash}]
|
5
8
|
attr_reader :tables
|
9
|
+
|
10
|
+
# Scaler type
|
11
|
+
# @return [Integer]
|
6
12
|
attr_reader :scaler_type
|
7
13
|
|
8
14
|
def initialize(io, offset = 0)
|
@@ -20,7 +26,7 @@ module TTFunk
|
|
20
26
|
tag: tag,
|
21
27
|
checksum: checksum,
|
22
28
|
offset: offset,
|
23
|
-
length: length
|
29
|
+
length: length,
|
24
30
|
}
|
25
31
|
end
|
26
32
|
end
|
@@ -1,50 +1,88 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'stringio'
|
4
|
+
require_relative 'placeholder'
|
4
5
|
|
5
6
|
module TTFunk
|
7
|
+
# Risen when the final encoded string was requested but there were some
|
8
|
+
# unresolved placeholders in it.
|
6
9
|
class UnresolvedPlaceholderError < StandardError
|
7
10
|
end
|
8
11
|
|
12
|
+
# Risen when a placeholder is added to an Encoded String but it already
|
13
|
+
# contains a placeholder with the same name.
|
9
14
|
class DuplicatePlaceholderError < StandardError
|
10
15
|
end
|
11
16
|
|
17
|
+
# Encoded string takes care of placeholders in binary strings. Placeholders
|
18
|
+
# are used when bytes need to be placed in the stream before their value is
|
19
|
+
# known.
|
20
|
+
#
|
21
|
+
# @api private
|
12
22
|
class EncodedString
|
23
|
+
# @yieldparam [self]
|
13
24
|
def initialize
|
14
|
-
yield
|
25
|
+
yield(self) if block_given?
|
15
26
|
end
|
16
27
|
|
28
|
+
# Append to string.
|
29
|
+
#
|
30
|
+
# @param obj [String, Placeholder, EncodedString]
|
31
|
+
# @return [self]
|
17
32
|
def <<(obj)
|
18
33
|
case obj
|
19
34
|
when String
|
20
35
|
io << obj
|
21
36
|
when Placeholder
|
22
37
|
add_placeholder(obj)
|
23
|
-
io << "\0" * obj.length
|
38
|
+
io << ("\0" * obj.length)
|
24
39
|
when self.class
|
25
40
|
# adjust placeholders to be relative to the entire encoded string
|
26
41
|
obj.placeholders.each_pair do |_, placeholder|
|
27
42
|
add_placeholder(placeholder.dup, placeholder.position + io.length)
|
28
43
|
end
|
29
44
|
|
30
|
-
|
45
|
+
io << obj.unresolved_string
|
31
46
|
end
|
32
47
|
|
33
48
|
self
|
34
49
|
end
|
35
50
|
|
51
|
+
# Append multiple objects.
|
52
|
+
#
|
53
|
+
# @param objs [Array<String, Placeholder, EncodedString>]
|
54
|
+
# @return [self]
|
55
|
+
def concat(*objs)
|
56
|
+
objs.each do |obj|
|
57
|
+
self << obj
|
58
|
+
end
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Append padding to align string to the specified word width.
|
63
|
+
#
|
64
|
+
# @param width [Integer]
|
65
|
+
# @return [self]
|
36
66
|
def align!(width = 4)
|
37
67
|
if (length % width).positive?
|
38
|
-
self << "\0" * (width - length % width)
|
68
|
+
self << ("\0" * (width - (length % width)))
|
39
69
|
end
|
40
70
|
|
41
71
|
self
|
42
72
|
end
|
43
73
|
|
74
|
+
# Length of this string.
|
75
|
+
#
|
76
|
+
# @return [Integer]
|
44
77
|
def length
|
45
78
|
io.length
|
46
79
|
end
|
47
80
|
|
81
|
+
# Raw string.
|
82
|
+
#
|
83
|
+
# @return [String]
|
84
|
+
# @raise [UnresolvedPlaceholderError] if there are any unresolved
|
85
|
+
# placeholders left.
|
48
86
|
def string
|
49
87
|
unless placeholders.empty?
|
50
88
|
raise UnresolvedPlaceholderError,
|
@@ -54,14 +92,27 @@ module TTFunk
|
|
54
92
|
io.string
|
55
93
|
end
|
56
94
|
|
95
|
+
# Raw bytes.
|
96
|
+
#
|
97
|
+
# @return [Array<Integer>]
|
98
|
+
# @raise [UnresolvedPlaceholderError] if there are any unresolved
|
99
|
+
# placeholders left.
|
57
100
|
def bytes
|
58
101
|
string.bytes
|
59
102
|
end
|
60
103
|
|
104
|
+
# Unresolved raw string.
|
105
|
+
#
|
106
|
+
# @return [String]
|
61
107
|
def unresolved_string
|
62
108
|
io.string
|
63
109
|
end
|
64
110
|
|
111
|
+
# Resolve placeholder.
|
112
|
+
#
|
113
|
+
# @param name [Symbol]
|
114
|
+
# @param value [String]
|
115
|
+
# @return [void]
|
65
116
|
def resolve_placeholder(name, value)
|
66
117
|
last_pos = io.pos
|
67
118
|
|
@@ -74,6 +125,9 @@ module TTFunk
|
|
74
125
|
io.seek(last_pos)
|
75
126
|
end
|
76
127
|
|
128
|
+
# Plaholders
|
129
|
+
#
|
130
|
+
# @return [Hash{Symbol => Plaholder}]
|
77
131
|
def placeholders
|
78
132
|
@placeholders ||= {}
|
79
133
|
end
|
data/lib/ttfunk/max.rb
CHANGED
@@ -1,14 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TTFunk
|
4
|
+
# Maximum aggregate. Its value can only become greater.
|
4
5
|
class Max < Aggregate
|
6
|
+
# Value
|
7
|
+
#
|
8
|
+
# @return [Comparable, nil]
|
5
9
|
attr_reader :value
|
6
10
|
|
11
|
+
# @param init_value [Comparable] initial value
|
7
12
|
def initialize(init_value = nil)
|
8
13
|
super()
|
9
14
|
@value = init_value
|
10
15
|
end
|
11
16
|
|
17
|
+
# Push a value. It will become the new value if it's greater than the
|
18
|
+
# current value (or if there was no value).
|
19
|
+
#
|
20
|
+
# @param new_value [Comparable]
|
21
|
+
# @return [void]
|
12
22
|
def <<(new_value)
|
13
23
|
new_value = coerce(new_value)
|
14
24
|
|
@@ -17,6 +27,10 @@ module TTFunk
|
|
17
27
|
end
|
18
28
|
end
|
19
29
|
|
30
|
+
# Get the stored value or default.
|
31
|
+
#
|
32
|
+
# @param default [any]
|
33
|
+
# @return [any]
|
20
34
|
def value_or(default)
|
21
35
|
return default if value.nil?
|
22
36
|
|
data/lib/ttfunk/min.rb
CHANGED
@@ -1,14 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TTFunk
|
4
|
+
# Minimum aggregate. Its value can only become lower.
|
4
5
|
class Min < Aggregate
|
6
|
+
# Value
|
7
|
+
#
|
8
|
+
# @return [Comparable, nil]
|
5
9
|
attr_reader :value
|
6
10
|
|
11
|
+
# @param init_value [Comparable] initial value
|
7
12
|
def initialize(init_value = nil)
|
8
13
|
super()
|
9
14
|
@value = init_value
|
10
15
|
end
|
11
16
|
|
17
|
+
# Push a value. It will become the new value if it's lower than the current
|
18
|
+
# value (or if there was no value).
|
19
|
+
#
|
20
|
+
# @param new_value [Comparable]
|
21
|
+
# @return [void]
|
12
22
|
def <<(new_value)
|
13
23
|
new_value = coerce(new_value)
|
14
24
|
|
@@ -17,6 +27,10 @@ module TTFunk
|
|
17
27
|
end
|
18
28
|
end
|
19
29
|
|
30
|
+
# Get the stored value or default.
|
31
|
+
#
|
32
|
+
# @param default [any]
|
33
|
+
# @return [any]
|
20
34
|
def value_or(default)
|
21
35
|
return default if value.nil?
|
22
36
|
|
@@ -1,13 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TTFunk
|
4
|
+
# Array with indexing starting at 1.
|
4
5
|
class OneBasedArray
|
5
6
|
include Enumerable
|
6
7
|
|
8
|
+
# @overload initialize(size)
|
9
|
+
# @param size [Integer] number of entries in this array
|
10
|
+
# @overload initialize(entries)
|
11
|
+
# @param entries [Array] an array to take entries from
|
7
12
|
def initialize(size = 0)
|
8
13
|
@entries = Array.new(size)
|
9
14
|
end
|
10
15
|
|
16
|
+
# Get element by index.
|
17
|
+
#
|
18
|
+
# @param idx [Integer]
|
19
|
+
# @return [any, nil]
|
20
|
+
# @raise IndexError if index is 0
|
11
21
|
def [](idx)
|
12
22
|
if idx.zero?
|
13
23
|
raise IndexError,
|
@@ -17,14 +27,24 @@ module TTFunk
|
|
17
27
|
entries[idx - 1]
|
18
28
|
end
|
19
29
|
|
30
|
+
# Number of elements in this array.
|
31
|
+
#
|
32
|
+
# @return [Integer]
|
20
33
|
def size
|
21
34
|
entries.size
|
22
35
|
end
|
23
36
|
|
37
|
+
# Convert to native array.
|
38
|
+
#
|
39
|
+
# @return [Array]
|
24
40
|
def to_ary
|
25
41
|
entries
|
26
42
|
end
|
27
43
|
|
44
|
+
# Iterate over elements.
|
45
|
+
#
|
46
|
+
# @yieldparam element [any]
|
47
|
+
# @return [void]
|
28
48
|
def each(&block)
|
29
49
|
entries.each(&block)
|
30
50
|
end
|
data/lib/ttfunk/otf_encoder.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TTFunk
|
4
|
+
# Encodes a CFF-based OpenType font subset to its binary representation.
|
4
5
|
class OTFEncoder < TTFEncoder
|
5
|
-
|
6
|
-
|
7
|
-
].freeze
|
6
|
+
# Optimal table order according to OpenType specification.
|
7
|
+
OPTIMAL_TABLE_ORDER = ['head', 'hhea', 'maxp', 'OS/2', 'name', 'cmap', 'post', 'CFF '].freeze
|
8
8
|
|
9
9
|
private
|
10
10
|
|
@@ -27,7 +27,7 @@ module TTFunk
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def cff_table
|
30
|
-
@cff_table ||= original.cff.encode(
|
30
|
+
@cff_table ||= original.cff.encode(subset)
|
31
31
|
end
|
32
32
|
|
33
33
|
def vorg_table
|
@@ -38,7 +38,7 @@ module TTFunk
|
|
38
38
|
@tables ||= super.merge(
|
39
39
|
'BASE' => base_table,
|
40
40
|
'VORG' => vorg_table,
|
41
|
-
'CFF ' => cff_table
|
41
|
+
'CFF ' => cff_table,
|
42
42
|
).compact
|
43
43
|
end
|
44
44
|
|
@@ -48,14 +48,5 @@ module TTFunk
|
|
48
48
|
(tables.keys - ['DSIG'] - OPTIMAL_TABLE_ORDER) +
|
49
49
|
['DSIG']
|
50
50
|
end
|
51
|
-
|
52
|
-
def collect_glyphs(glyph_ids)
|
53
|
-
# CFF top indexes are supposed to contain only one font, although they're
|
54
|
-
# capable of supporting many (no idea why this is true, maybe for CFF
|
55
|
-
# v2??). Anyway it's cool to do top_index[0], don't worry about it.
|
56
|
-
glyph_ids.each_with_object({}) do |id, h|
|
57
|
-
h[id] = original.cff.top_index[0].charstrings_index[id]
|
58
|
-
end
|
59
|
-
end
|
60
51
|
end
|
61
52
|
end
|