ttfunk 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
![Maintained: yes](https://img.shields.io/badge/maintained-yes-brightgreen.svg)
|
4
4
|
![CI status](https://github.com/prawnpdf/ttfunk/workflows/CI/badge.svg)
|
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
|