Class: Fontist::FontFile
- Inherits:
-
Object
- Object
- Fontist::FontFile
- Defined in:
- lib/fontist/font_file.rb
Class Method Summary collapse
-
.check_extension_warning(path, is_collection) ⇒ Object
rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity.
-
.detect_font_format(path) ⇒ Object
rubocop:disable Metrics/MethodLength.
-
.extract_font_info_from_path(path) ⇒ Object
rubocop:disable Metrics/AbcSize, Metrics/MethodLength.
-
.extract_names_from_font(font) ⇒ Object
rubocop:disable Metrics/MethodLength.
-
.from_content(content) ⇒ Object
rubocop:disable Metrics/MethodLength.
- .from_path(path) ⇒ Object
-
.load_indexability_validator ⇒ Object
rubocop:enable Metrics/MethodLength.
-
.raise_font_file_error(exception) ⇒ Object
rubocop:enable Metrics/MethodLength.
-
.validate_and_load_single_font(path) ⇒ Object
rubocop:disable Metrics/MethodLength.
Instance Method Summary collapse
- #family ⇒ Object
- #full_name ⇒ Object
-
#initialize(font_info, tempfile = nil) ⇒ FontFile
constructor
A new instance of FontFile.
- #preferred_family_name ⇒ Object
- #preferred_subfamily_name ⇒ Object
- #subfamily ⇒ Object
Constructor Details
#initialize(font_info, tempfile = nil) ⇒ FontFile
Returns a new instance of FontFile.
200 201 202 203 204 |
# File 'lib/fontist/font_file.rb', line 200 def initialize(font_info, tempfile = nil) @info = font_info # Keep tempfile alive to prevent GC issues on Windows @tempfile = tempfile end |
Class Method Details
.check_extension_warning(path, is_collection) ⇒ Object
rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/fontist/font_file.rb', line 101 def check_extension_warning(path, is_collection) expected_ext = File.extname(path).downcase.sub(/^\./, "") # Determine actual format based on content actual_format = if is_collection # Use ttc as generic collection extension "ttc" else detect_font_format(path) end # Map common collection extensions to "ttc" collection_extensions = %w[ttc otc dfont] is_expected_collection = collection_extensions.include?(expected_ext) # Check for mismatch if is_collection && !is_expected_collection Fontist.ui.warn( # rubocop:disable Layout/LineLength "WARNING: File '#{File.basename(path)}' has extension '.#{expected_ext}' " \ "but appears to be a font collection (.ttc/.otc/.dfont). " \ "The file will be indexed, but consider renaming for clarity.", # rubocop:enable Layout/LineLength ) elsif !is_collection && is_expected_collection # File has collection extension but is actually a single font Fontist.ui.warn( # rubocop:disable Layout/LineLength "WARNING: File '#{File.basename(path)}' has collection extension '.#{expected_ext}' " \ "but appears to be a single font (.#{actual_format}). " \ "The file will be indexed, but consider renaming for clarity.", # rubocop:enable Layout/LineLength ) elsif !is_collection && expected_ext != actual_format # Single font with wrong format extension Fontist.ui.warn( # rubocop:disable Layout/LineLength "WARNING: File '#{File.basename(path)}' has extension '.#{expected_ext}' " \ "but appears to be a #{actual_format.upcase} font. " \ "The file will be indexed, but consider renaming for clarity.", # rubocop:enable Layout/LineLength ) end rescue StandardError => e # Don't fail indexing just because we can't detect the format # rubocop:disable Layout/LineLength Fontist.ui.debug("Could not detect file format for warning: #{e.}") # rubocop:enable Layout/LineLength end |
.detect_font_format(path) ⇒ Object
rubocop:disable Metrics/MethodLength
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/fontist/font_file.rb', line 153 def detect_font_format(path) # Open file and check SFNT version to determine actual format File.open(path, "rb") do |io| signature = io.read(4) io.rewind case signature when "\x00\x01\x00\x00", "true" "ttf" when "OTTO" "otf" when "wOFF" "woff" when "wOF2" "woff2" else "unknown" end end rescue StandardError "unknown" end |
.extract_font_info_from_path(path) ⇒ Object
rubocop:disable Metrics/AbcSize, Metrics/MethodLength
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/fontist/font_file.rb', line 33 def extract_font_info_from_path(path) # rubocop:disable Layout/LineLength # First, detect if the file is actually a collection (regardless of extension) # rubocop:enable Layout/LineLength # This handles cases where .ttf files are actually .ttc collections is_collection = Fontisan::FontLoader.collection?(path) # Check for extension mismatch and issue warning check_extension_warning(path, is_collection) # For collections, we need different handling if is_collection # Load and validate the first font in the collection for indexability font = Fontisan::FontLoader.load(path, font_index: 0, mode: :metadata, lazy: true) # Validate the font using indexability profile validator = load_indexability_validator validation_report = validator.validate(font) unless validation_report.valid? = validation_report.errors.map do |e| "#{e.category}: #{e.}" end.join("; ") # rubocop:disable Layout/LineLength raise Errors::FontFileError, "Font from collection failed indexability validation: #{}" # rubocop:enable Layout/LineLength end else # Single font - validate and load font = validate_and_load_single_font(path) end names = extract_names_from_font(font) font.close names rescue StandardError => e raise_font_file_error(e) end |
.extract_names_from_font(font) ⇒ Object
rubocop:disable Metrics/MethodLength
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/fontist/font_file.rb', line 177 def extract_names_from_font(font) # Access name table directly name_table = font.table(Fontisan::Constants::NAME_TAG) return {} unless name_table # Extract all needed name strings using Fontisan's API { full_name: name_table.english_name(Fontisan::Tables::Name::FULL_NAME), family_name: name_table.english_name(Fontisan::Tables::Name::FAMILY), subfamily_name: name_table.english_name(Fontisan::Tables::Name::SUBFAMILY), preferred_family: name_table.english_name(Fontisan::Tables::Name::PREFERRED_FAMILY), preferred_subfamily: name_table.english_name(Fontisan::Tables::Name::PREFERRED_SUBFAMILY), postscript_name: name_table.english_name(Fontisan::Tables::Name::POSTSCRIPT_NAME), } end |
.from_content(content) ⇒ Object
rubocop:disable Metrics/MethodLength
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/fontist/font_file.rb', line 14 def from_content(content) tmpfile = Tempfile.new(["font", ".ttf"]) tmpfile.binmode tmpfile.write(content) tmpfile.flush tmpfile.close font_info = extract_font_info_from_path(tmpfile.path) # Keep tempfile alive to prevent GC issues on Windows # rubocop:disable Layout/LineLength # Return both the font object and tempfile so caller can keep it referenced # rubocop:enable Layout/LineLength new(font_info, tmpfile) rescue StandardError => e raise_font_file_error(e) end |
.from_path(path) ⇒ Object
8 9 10 11 |
# File 'lib/fontist/font_file.rb', line 8 def from_path(path) font_info = extract_font_info_from_path(path) new(font_info) end |
.load_indexability_validator ⇒ Object
rubocop:enable Metrics/MethodLength
96 97 98 |
# File 'lib/fontist/font_file.rb', line 96 def load_indexability_validator Fontisan::Validators::ProfileLoader.load(:indexability) end |
.raise_font_file_error(exception) ⇒ Object
rubocop:enable Metrics/MethodLength
194 195 196 197 |
# File 'lib/fontist/font_file.rb', line 194 def raise_font_file_error(exception) raise Errors::FontFileError, "Font file could not be parsed: #{exception.inspect}." end |
.validate_and_load_single_font(path) ⇒ Object
rubocop:disable Metrics/MethodLength
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/fontist/font_file.rb', line 76 def validate_and_load_single_font(path) # Load font first (we need to validate it, not the file path) font = Fontisan::FontLoader.load(path, mode: :metadata, lazy: true) # Validate the font using indexability profile validator = load_indexability_validator validation_report = validator.validate(font) unless validation_report.valid? = validation_report.errors.map do |e| "#{e.category}: #{e.}" end.join("; ") raise Errors::FontFileError, "Font file failed indexability validation: #{}" end font end |
Instance Method Details
#family ⇒ Object
210 211 212 |
# File 'lib/fontist/font_file.rb', line 210 def family @info[:family_name] end |
#full_name ⇒ Object
206 207 208 |
# File 'lib/fontist/font_file.rb', line 206 def full_name @info[:full_name] end |
#preferred_family_name ⇒ Object
218 219 220 |
# File 'lib/fontist/font_file.rb', line 218 def preferred_family_name @info[:preferred_family] end |
#preferred_subfamily_name ⇒ Object
222 223 224 |
# File 'lib/fontist/font_file.rb', line 222 def preferred_subfamily_name @info[:preferred_subfamily] end |
#subfamily ⇒ Object
214 215 216 |
# File 'lib/fontist/font_file.rb', line 214 def subfamily @info[:subfamily_name] end |