Skip to content

Commit

Permalink
Properly set CFF encoding for subset font
Browse files Browse the repository at this point in the history
CFF top dict could skip entries that have default values. We have to
specifically set encoding in subset fonts because it's very unlikely
we'll be using the default encoding.
  • Loading branch information
pointlessone committed Feb 14, 2024
1 parent 0a508e3 commit ce11cb7
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 1 deletion.
3 changes: 3 additions & 0 deletions lib/ttfunk/table/cff.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ def tag
# TTFunk::Subset::Unicode, TTFunk::Subset::Unicode8Bit]
# @return [TTFunk::EncodedString]
def encode(subset)
# Make sure TopDict has an entry for encoding so it could be properly replaced
top_index[0][TopDict::OPERATORS[:encoding]] = 0

EncodedString.new do |result|
result.concat(
header.encode,
Expand Down
11 changes: 10 additions & 1 deletion lib/ttfunk/table/cff/dict.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ def [](operator)
@dict[operator]
end

# Add dict entry.
#
# @param operator [Integer] Entry operator. Must be in range 0..255. Wide operators must be in range 1200..1455.
# @param operands [Array<Integer, TTFunk::SciForm>]
def []=(operator, *operands)
@dict[operator] = Array(*operands)
end

# Iterate over dict entries.
#
# @yieldparam key [Integer]
Expand All @@ -59,7 +67,8 @@ def each(&block)
#
# @return [String]
def encode
map do |(operator, operands)|
sort_by(&:first)
.map do |(operator, operands)|
operands.map { |operand| encode_operand(operand) }.join +
encode_operator(operator)
end.join
Expand Down
30 changes: 30 additions & 0 deletions spec/ttfunk/table/cff/dict_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,34 @@
described_class::TooManyOperandsError
)
end

it 'allows addition of entries' do
dict = described_class.new(TestFile.new(StringIO.new('')), 0, 0)

dict[1] = 42
dict[1201] = [43, 44]

expect(dict.encode).to eq("\xB5\x01\xb6\xb7\x0c\x01".b)
end

it 'allows replacement of entries' do
dict = described_class.new(TestFile.new(StringIO.new("\xB5\x01\xb6\xb7\x0c\x01".b)), 0, 6)

dict[1] = 0

expect(dict.encode).to eq("\x8b\x01\xb6\xb7\x0c\x01".b)
end

it 'uses a stable encoding order' do
dict1 = described_class.new(TestFile.new(StringIO.new('')), 0, 0)
dict2 = described_class.new(TestFile.new(StringIO.new('')), 0, 0)

dict1[1] = 1
dict1[2] = 2

dict2[2] = 2
dict2[1] = 1

expect(dict1.encode).to eq(dict2.encode)
end
end

0 comments on commit ce11cb7

Please sign in to comment.