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

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



30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/fontisan/collection/table_deduplicator.rb', line 30

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



20
21
22
# File 'lib/fontisan/collection/table_deduplicator.rb', line 20

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



24
25
26
# File 'lib/fontisan/collection/table_deduplicator.rb', line 24

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]
      }
    }
    

    }



61
62
63
64
65
66
67
68
69
# File 'lib/fontisan/collection/table_deduplicator.rb', line 61

def build_sharing_map
  # First pass: collect all unique tables
  collect_canonical_tables

  # 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



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

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



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

def canonical_tables_for_tag(tag)
  @canonical_tables[tag]
end

#statisticsHash

Get sharing statistics

Returns:

  • (Hash)

    Statistics about table sharing



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/fontisan/collection/table_deduplicator.rb', line 91

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