Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
@@ -0,0 +1,277 @@
# frozen_string_literal: true
module TTFunk
class Table
class Cff < TTFunk::Table
# CFF Encoding.
class Encoding < TTFunk::SubTable
include Enumerable
# Predefined Standard Encoding ID.
STANDARD_ENCODING_ID = 0
# Predefined Expert Encoding ID.
EXPERT_ENCODING_ID = 1
# Default encoding ID.
DEFAULT_ENCODING_ID = STANDARD_ENCODING_ID
class << self
# Get predefined encoding by ID.
#
# @param encoding_id [Integer]
# @return [TTFunk::OneBasedArray<Integer>]
def codes_for_encoding_id(encoding_id)
case encoding_id
when STANDARD_ENCODING_ID
Encodings::STANDARD
when EXPERT_ENCODING_ID
Encodings::EXPERT
end
end
end
# Top dict.
# @return [TTFunk::Table::Cff::TopDict]
attr_reader :top_dict
# Encodign format.
# @return [Integer]
attr_reader :format
# Number of encoded items.
# @return [Integer]
attr_reader :items_count
# Offset or encoding ID.
# @return [Integer]
attr_reader :offset_or_id
# @overload initialize(top_dict, file, offset = nil, length = nil)
# @param top_dict [TTFunk::Table:Cff::TopDict]
# @param file [TTFunk::File]
# @param offset [Integer]
# @param length [Integer]
# @overload initialize(top_dict, file, charset_id)
# @param top_dict [TTFunk::Table:Cff::TopDict]
# @param file [TTFunk::File]
# @param encoding_id [Integer] 0, 1, or 2
def initialize(top_dict, file, offset_or_id = nil, length = nil)
@top_dict = top_dict
@offset_or_id = offset_or_id || DEFAULT_ENCODING_ID
if offset
super(file, offset, length)
@supplemental = format >> 7 == 1
else
@items_count = self.class.codes_for_encoding_id(offset_or_id).size
@supplemental = false
end
end
# Iterate over character codes.
#
# @overload each()
# @yieldparam code [Integer]
# @return [void]
# @overload each()
# @return [Enumerator]
def each
return to_enum(__method__) unless block_given?
# +1 adjusts for the implicit .notdef glyph
(items_count + 1).times { |i| yield(self[i]) }
end
# Get character code for glyph index.
#
# @param glyph_id [Integer]
# @return [Integer, nil]
def [](glyph_id)
return 0 if glyph_id.zero?
return code_for(glyph_id) if offset
self.class.codes_for_encoding_id(offset_or_id)[glyph_id]
end
# Encoding offset in the file.
#
# @return [Integer, nil]
def offset
# Numbers from 0..1 mean encoding IDs instead of offsets. IDs are
# pre-defined, generic encodings that define the characters present
# in the font.
#
# In the case of an offset, add the CFF table's offset since the
# charset offset is relative to the start of the CFF table. Otherwise
# return nil (no offset).
if offset_or_id > 1
offset_or_id + top_dict.cff_offset
end
end
# Encode encoding.
#
# @param charmap [Hash{Integer => Hash}] keys are the charac codes,
# values are hashes:
# * `:old` (<tt>Integer</tt>) - glyph ID in the original font.
# * `:new` (<tt>Integer</tt>) - glyph ID in the subset font.
# @return [String]
def encode(charmap)
# Any subset encoding is all but guaranteed to be different from the
# standard encoding so we don't even attempt to see if it matches. We
# assume it's different and just encode it anew.
return encode_supplemental(charmap) if supplemental?
codes =
charmap
.reject { |_code, mapping| mapping[:new].zero? }
.sort_by { |_code, mapping| mapping[:new] }
.map { |(code, _m)| code }
ranges = TTFunk::BinUtils.rangify(codes)
# calculate whether storing the charset as a series of ranges is
# more efficient (i.e. takes up less space) vs storing it as an
# array of SID values
total_range_size = (2 * ranges.size) +
(element_width(:range_format) * ranges.size)
total_array_size = codes.size * element_width(:array_format)
if total_array_size <= total_range_size
([format_int(:array_format), codes.size] + codes).pack('C*')
else
element_fmt = element_format(:range_format)
result = [format_int(:range_format), ranges.size].pack('CC')
ranges.each { |range| result << range.pack(element_fmt) }
result
end
end
# Is this a supplemental encoding?
#
# @return [Boolean]
def supplemental?
# high-order bit set to 1 indicates supplemental encoding
@supplemental
end
private
def encode_supplemental(charmap)
new_entries =
charmap
.reject { |_code, mapping| mapping[:new].zero? }
.transform_values { |mapping| mapping[:new] }
result = [format_int(:supplemental), new_entries.size].pack('CC')
fmt = element_format(:supplemental)
new_entries.each do |code, new_gid|
result << [code, new_gid].pack(fmt)
end
result
end
def code_for(glyph_id)
return 0 if glyph_id.zero?
# rather than validating the glyph as part of one of the predefined
# encodings, just pass it through
return glyph_id unless offset
case format_sym
when :array_format, :supplemental
@entries[glyph_id]
when :range_format
remaining = glyph_id
@entries.each do |range|
if range.size >= remaining
return (range.first + remaining) - 1
end
remaining -= range.size
end
0
end
end
def parse!
@format, entry_count = read(2, 'C*')
@length = entry_count * element_width
case format_sym
when :array_format
@items_count = entry_count
@entries = OneBasedArray.new(read(length, 'C*'))
when :range_format
@entries = []
@items_count = 0
entry_count.times do
code, num_left = read(element_width, element_format)
@entries << (code..(code + num_left))
@items_count += num_left + 1
end
when :supplemental
@entries = {}
@items_count = entry_count
entry_count.times do
code, glyph = read(element_width, element_format)
@entries[code] = glyph
end
end
end
def element_format(fmt = format_sym)
{
array_format: 'C',
range_format: 'CC',
supplemental: 'Cn',
}[fmt]
end
# @TODO: handle supplemental encoding (necessary?)
def element_width(fmt = format_sym)
case fmt
when :array_format then 1
when :range_format then 2
when :supplemental then 3
else
raise Error, "'#{fmt}' is an unsupported encoding format"
end
end
def format_sym
return :supplemental if supplemental?
case @format
when 0 then :array_format
when 1 then :range_format
else
raise Error, "unsupported charset format '#{fmt}'"
end
end
def format_int(sym = format_sym)
case sym
when :array_format then 0
when :range_format then 1
when :supplemental then 129
else
raise Error, "unsupported charset format '#{sym}'"
end
end
end
end
end
end