Class: Fontisan::Stitcher

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/stitcher.rb,
lib/fontisan/stitcher/source.rb,
lib/fontisan/stitcher/selector.rb,
lib/fontisan/stitcher/glyph_limit.rb,
lib/fontisan/stitcher/deduplicator.rb,
lib/fontisan/stitcher/glyph_copier.rb,
lib/fontisan/stitcher/selector/gid.rb,
lib/fontisan/stitcher/selector/range.rb,
lib/fontisan/stitcher/cbdt_propagator.rb,
lib/fontisan/stitcher/format_metadata.rb,
lib/fontisan/stitcher/glyph_signature.rb,
lib/fontisan/stitcher/collection_result.rb,
lib/fontisan/stitcher/partition_strategy.rb,
lib/fontisan/stitcher/selector/codepoints.rb,
lib/fontisan/stitcher/partition_strategy/base.rb,
lib/fontisan/stitcher/partition_strategy/by_block.rb,
lib/fontisan/stitcher/partition_strategy/by_plane.rb,
lib/fontisan/stitcher/partition_strategy/blueprint.rb,
lib/fontisan/stitcher/partition_strategy/by_script.rb,
lib/fontisan/stitcher/partition_strategy/partition.rb

Overview

Multi-source font stitcher with explicit subfont declaration.

Every set of codepoints is explicitly assigned to a named subfont via the required into: keyword. The user controls the collection structure upfront — there are no defaults and no after-the-fact splitting.

Single-font output: write_to requires a subfont: name. Collection output: write_collection writes all declared subfonts.

Examples:

Single font

stitcher = Fontisan::Stitcher.new
stitcher.add_source(:latin, Fontisan::Ufo::Font.open("latin.ufo"))
stitcher.include_range(0x41..0x5A, from: :latin, into: :main)
stitcher.write_to("out.ttf", format: :ttf, subfont: :main)

Collection

stitcher = Fontisan::Stitcher.new
stitcher.add_source(:noto_sans, noto_sans)
stitcher.add_source(:noto_cjk, noto_cjk)
stitcher.include_range(0x41..0x5A, from: :noto_sans, into: :latin)
stitcher.include_range(0x4E00..0x9FFF, from: :noto_cjk, into: :cjk)
stitcher.write_collection("out.otc", format: :otf2)

Defined Under Namespace

Modules: GlyphLimit, GlyphSignature, PartitionStrategy, Selector Classes: CbdtPropagator, CollectionResult, Deduplicator, FormatMetadata, GlyphCopier, Source, SubfontStats

Constant Summary collapse

DEFAULT_DEDUPLICATE =
true

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(deduplicate: DEFAULT_DEDUPLICATE) ⇒ Stitcher

Returns a new instance of Stitcher.



50
51
52
53
54
55
# File 'lib/fontisan/stitcher.rb', line 50

def initialize(deduplicate: DEFAULT_DEDUPLICATE)
  @sources = {}
  @subfonts = Hash.new { |h, k| h[k] = [] }
  @info = nil
  @deduplicate = deduplicate
end

Instance Attribute Details

#infoObject (readonly)

Returns the value of attribute info.



48
49
50
# File 'lib/fontisan/stitcher.rb', line 48

def info
  @info
end

#sourcesObject (readonly)

Returns the value of attribute sources.



48
49
50
# File 'lib/fontisan/stitcher.rb', line 48

def sources
  @sources
end

#subfontsObject (readonly)

Returns the value of attribute subfonts.



48
49
50
# File 'lib/fontisan/stitcher.rb', line 48

def subfonts
  @subfonts
end

Instance Method Details

#add_source(label, font, remap: nil) ⇒ Object



57
58
59
# File 'lib/fontisan/stitcher.rb', line 57

def add_source(label, font, remap: nil)
  @sources[label.to_sym] = Source.new(font, remap: remap)
end

#build_target_font(subfont:) ⇒ Object



92
93
94
# File 'lib/fontisan/stitcher.rb', line 92

def build_target_font(subfont:)
  build_target_for(subfont)
end

#include_codepoints(codepoints, from:, into:) ⇒ Object



65
66
67
# File 'lib/fontisan/stitcher.rb', line 65

def include_codepoints(codepoints, from:, into:)
  Selector::Codepoints.new(codepoints).apply(source(from), @subfonts[into])
end

#include_codepoints_map(cp_map, into:) ⇒ Object



69
70
71
72
73
74
# File 'lib/fontisan/stitcher.rb', line 69

def include_codepoints_map(cp_map, into:)
  cp_map.to_h
    .group_by { |_cp, label| label }
    .transform_values { |pairs| pairs.map(&:first).sort }
    .each { |label, cps| include_codepoints(cps, from: label, into: into) }
end

#include_gid(donor_gid, from:, into:) ⇒ Object



76
77
78
# File 'lib/fontisan/stitcher.rb', line 76

def include_gid(donor_gid, from:, into:)
  Selector::Gid.new(donor_gid).apply(source(from), @subfonts[into])
end

#include_notdef(from:, into:) ⇒ Object



80
81
82
# File 'lib/fontisan/stitcher.rb', line 80

def include_notdef(from:, into:)
  include_gid(0, from: from, into: into)
end

#include_range(range, from:, into:) ⇒ Object



61
62
63
# File 'lib/fontisan/stitcher.rb', line 61

def include_range(range, from:, into:)
  Selector::Range.new(range).apply(source(from), @subfonts[into])
end

#set_info(info_hash) ⇒ Object



84
85
86
# File 'lib/fontisan/stitcher.rb', line 84

def set_info(info_hash)
  @info = Ufo::Info.new(info_hash)
end

#subfont_namesObject



88
89
90
# File 'lib/fontisan/stitcher.rb', line 88

def subfont_names
  @subfonts.keys
end

#write_collection(path, format:) ⇒ Object

Raises:

  • (ArgumentError)


107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/fontisan/stitcher.rb', line 107

def write_collection(path, format:)
  raise ArgumentError, "no subfonts declared" if @subfonts.empty?

  compiled = @subfonts.keys.map do |name|
    compile_subfont_with_stats(name, format: format)
  end
  fonts = compiled.map(&:font)

  collection_format = FormatMetadata.resolve(format).collection_format
  Collection::Builder.new(fonts, format: collection_format,
                                 optimize: true).build_to_file(path)

  CollectionResult.new(
    path: path,
    bytes: File.size(path),
    subfonts: compiled.map(&:stats),
  )
end

#write_to(path, format:, subfont:) ⇒ Object



96
97
98
99
100
101
102
103
104
105
# File 'lib/fontisan/stitcher.rb', line 96

def write_to(path, format:, subfont:)
  target = build_target_for(subfont)
  GlyphLimit.check!(target.glyphs.size, format: format)

   = FormatMetadata.resolve(format)
  .compiler_class.new(target).compile(output_path: path)

  cbdt_propagator.propagate_tables_into(cbdt_propagator.cbdt_source, path)
  path
end