Class: Fontisan::Collection::TableDeduplicator

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/collection/table_deduplicator.rb

Overview

TableDeduplicator deduplicates identical tables across fonts

Single responsibility: Group identical tables and create a canonical mapping for shared table references. Ensures that each unique table content is stored only once in the collection.

Examples:

Deduplicate tables

deduplicator = TableDeduplicator.new([font1, font2, font3])
sharing_map = deduplicator.build_sharing_map
canonical_tables = deduplicator.canonical_tables

Constant Summary collapse

VARIATION_SHAREABLE_TABLES =

Tables that can be shared in variable font collections if identical

%w[fvar avar STAT HVAR VVAR MVAR].freeze
VARIATION_FONT_SPECIFIC_TABLES =

Tables that must remain font-specific in variable fonts

%w[gvar CFF2].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(fonts) ⇒ TableDeduplicator

Initialize deduplicator with fonts

Parameters:

Raises:

  • (ArgumentError)

    if fonts array is empty or invalid



36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/fontisan/collection/table_deduplicator.rb', line 36

def initialize(fonts)
  if fonts.nil? || fonts.empty?
    raise ArgumentError,
          "fonts cannot be nil or empty"
  end
  raise ArgumentError, "fonts must be an array" unless fonts.is_a?(Array)

  @fonts = fonts
  @canonical_tables = {}
  @sharing_map = {}
  @checksum_to_canonical = {}
end

Instance Attribute Details

#canonical_tablesHash<String, Hash> (readonly)

Canonical tables (unique table data)

Returns:

  • (Hash<String, Hash>)

    Map of table tag to canonical versions



26
27
28
# File 'lib/fontisan/collection/table_deduplicator.rb', line 26

def canonical_tables
  @canonical_tables
end

#sharing_mapHash<Integer, Hash<String, Hash>> (readonly)

Sharing map (font -> table -> canonical reference)

Returns:

  • (Hash<Integer, Hash<String, Hash>>)

    Sharing map



30
31
32
# File 'lib/fontisan/collection/table_deduplicator.rb', line 30

def sharing_map
  @sharing_map
end

Instance Method Details

#build_sharing_mapHash<Integer, Hash<String, Hash>>

Build sharing map for all fonts

Creates a map structure that indicates which canonical table each font should reference for each table tag. This enables efficient table sharing in the final collection.

Returns:

  • (Hash<Integer, Hash<String, Hash>>)

    Sharing map with structure: {

    font_index => {
      table_tag => {
        canonical_id: unique_id,
        checksum: sha256_checksum,
        data: table_data,
        shared: true/false,
        shared_with: [font_indices]
      }
    }
    

    }



67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/fontisan/collection/table_deduplicator.rb', line 67

def build_sharing_map
  # First pass: collect all unique tables
  collect_canonical_tables

  # Handle variable font table deduplication
  deduplicate_variation_tables if has_variable_fonts?

  # Second pass: build sharing map for each font
  build_font_sharing_references

  @sharing_map
end

#canonical_table_data(tag, canonical_id) ⇒ String?

Get canonical table data for a specific table

Parameters:

  • tag (String)

    Table tag

  • canonical_id (String)

    Canonical table identifier

Returns:

  • (String, nil)

    Binary table data



85
86
87
# File 'lib/fontisan/collection/table_deduplicator.rb', line 85

def canonical_table_data(tag, canonical_id)
  @canonical_tables.dig(tag, canonical_id, :data)
end

#canonical_tables_for_tag(tag) ⇒ Hash<String, Hash>?

Get all canonical tables for a specific tag

Parameters:

  • tag (String)

    Table tag

Returns:

  • (Hash<String, Hash>, nil)

    Map of canonical_id to table info



93
94
95
# File 'lib/fontisan/collection/table_deduplicator.rb', line 93

def canonical_tables_for_tag(tag)
  @canonical_tables[tag]
end

#statisticsHash

Get sharing statistics

Returns:

  • (Hash)

    Statistics about table sharing



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/fontisan/collection/table_deduplicator.rb', line 100

def statistics
  total_tables = 0
  shared_tables = 0
  unique_tables = 0

  @sharing_map.each_value do |tables|
    tables.each_value do |info|
      total_tables += 1
      if info[:shared]
        shared_tables += 1
      else
        unique_tables += 1
      end
    end
  end

  {
    total_tables: total_tables,
    shared_tables: shared_tables,
    unique_tables: unique_tables,
    sharing_percentage: total_tables.positive? ? (shared_tables.to_f / total_tables * 100).round(2) : 0.0,
    canonical_count: @canonical_tables.values.sum(&:size),
  }
end