Class: FontProcessor::Processor
- Inherits:
-
Object
- Object
- FontProcessor::Processor
- Defined in:
- lib/fontprocessor/processor.rb
Defined Under Namespace
Modules: FakeSwfWrapper
Constant Summary collapse
- HYPHEN_MINUS =
Unicode defs for hyphen-minus, hyphen and soft-hyphen
0x2D- HYPHEN =
0x2010- SOFT_HYPHEN =
0xAD- SPACE =
Unicode defs for space and non-breaking space
0x20- NON_BREAKING_SPACE =
0xA0- PUA_FROM_0 =
PUA glyphs for font loading detection
0xe000- PUA_TO_0 =
66- PUA_FROM_1 =
0xe001- PUA_TO_1 =
69- PUA_FROM_2 =
0xe002- PUA_TO_2 =
83- PUA_FROM_3 =
0xe003- PUA_TO_3 =
98- PUA_FROM_4 =
0xe004- PUA_TO_4 =
115- PUA_FROM_5 =
0xe005- PUA_TO_5 =
119- PUA_FROM_6 =
0xe006- PUA_TO_6 =
121- PUA_PAIRS =
[ [PUA_FROM_0, PUA_TO_0], [PUA_FROM_1, PUA_TO_1], [PUA_FROM_2, PUA_TO_2], [PUA_FROM_3, PUA_TO_3], [PUA_FROM_4, PUA_TO_4], [PUA_TO_5, PUA_TO_5], [PUA_FROM_6, PUA_TO_6] ].freeze
- FAMILY_ID =
Name table record ids
1- SUBFAMILY_ID =
2- UNIQUE_ID =
3- FULLNAME_ID =
4- POSTSCRIPT_NAME_ID =
6- LICENSE_URL_ID =
14- VALID_US_WEIGHT_CLASSES =
DWrite is picky about which weight values are used, if they aren’t correct the font won’t render. msdn.microsoft.com/en-us/library/dd368082(VS.85).aspx
[100, 200, 300, 400, 500, 600, 700, 800, 900, 950]
- MissingSourceFile =
Class.new(FontProcessorError)
- OTSValidationFailure =
Class.new(StandardError)
- WOFFProcessingFailure =
Class.new(StandardError)
- UnknownFontFormat =
Class.new(ArgumentError)
Instance Method Summary collapse
-
#add_name_record(id, filename, string) ⇒ Object
Adds a name record for both Macintosh and Windows platforms to the name table of the given font.
-
#convert(source, output) ⇒ Object
Converts from one type of outlines to another (based on the file extensions as interpreted by fontforge).
-
#copy_name_record(filename, source_id, destination_id) ⇒ Object
Copies a single name table record within a font.
-
#file_metadata(formats) ⇒ Object
Public: A helper method to inspect the metadata of the primary file for this font.
-
#fix_os2_weight_and_width(filename) ⇒ Object
Ensures that both the usWeightClass and usWidthClass conform to the OTF spec.
-
#font_formats_to_generate(format_data) ⇒ Object
font_formats_to_generate - creates an arra of FontFormats.
-
#generate_char_set(charset_data, formats, lock = true) ⇒ Object
Public: Generates the series of fonts required to display this font on the web.
-
#generate_internal_char_set(charset_data, format_data, lock) ⇒ Object
Generates all of the files required to display a single character set.
-
#initialize(naming_strategy, license_url, font_base_id) ⇒ Processor
constructor
naming_strategy - The FontFileNamingStrategy to process.
- #insert_pua_glyphs(fm, filename) ⇒ Object
-
#lock(filename) ⇒ Object
Sets the OS/2 embedding bit to deny embedding rights, which prevents fontforge from modifying it.
-
#map_uni_value_to_int(original_unicode) ⇒ Object
Pubilc: Maps a Unicode value string to an integer.
-
#obfuscate_names(filename, unique_id) ⇒ Object
This is the protection method that we use for raw fonts.
-
#original_font_format ⇒ Object
Returns a FontFormat that represents the original, primary format of this font.
-
#parse_charset_to_subset(charset_data) ⇒ Object
Removes characters contained in the source font but aren’t included in the character set.
-
#preprocess(formats) ⇒ Object
Performs all of the global font modifications to the original font creating a set of source fonts.
- #preverify_format_data(formats) ⇒ Object
-
#strip(filename) ⇒ Object
Removes all name records but except ids 0 through 6.
-
#subset(charset_data, original, source, output) ⇒ Object
Removes characters contained in the source font but aren’t included in the character set.
-
#unlock(filename) ⇒ Object
Sets the OS/2 embedding bit to be emdedable, which allows fontforge to modify it and IE to render it.
- #wrap_dyna_base(charset_id, font_format) ⇒ Object
- #wrap_installable(charset_id, font_format) ⇒ Object
-
#wrap_swf(charset_id, font_format) ⇒ Object
Wraps the given file (as specified with char_set and font_format) in a SWF.
-
#wrap_woff(charset_id, font_format) ⇒ Object
Wraps the given file (as specified with char_set and font_format) in a WOFF.
-
#wrap_woff2(charset_id, font_format) ⇒ Object
Wraps the given file (as specified with char_set and font_format) in a WOFF2 (using maximum compression).
Constructor Details
#initialize(naming_strategy, license_url, font_base_id) ⇒ Processor
naming_strategy - The FontFileNamingStrategy to process. license_url - The URL to embed within processed fonts in
LICENSE_URL_ID name table record.
55 56 57 58 59 |
# File 'lib/fontprocessor/processor.rb', line 55 def initialize(naming_strategy, license_url, font_base_id) @naming_strategy = naming_strategy @license_url = license_url @font_base_id = font_base_id end |
Instance Method Details
#add_name_record(id, filename, string) ⇒ Object
Adds a name record for both Macintosh and Windows platforms to the name table of the given font.
Note: If the font record already exists, the resulting font will contain both records and it’s behavior is undefined, so it’s best to always remove the other record first.
id - The id of the name record to add. filename - The filename of the font to modify. string - The value of the new name record.
Returns nothing.
707 708 709 710 711 712 713 714 715 716 717 |
# File 'lib/fontprocessor/processor.rb', line 707 def add_name_record(id, filename, string) fm = Skytype::FontManipulator.new(File.binread(filename)) raise StandardError, "ERROR: Failed to create FontManipulator" if fm.nil? name_data = fm.get_table("name") name_table = Skytype::NameTable.new(name_data) name_table.add_or_replace_string(string, id) fm.apply_table(name_table) fm.save(filename) end |
#convert(source, output) ⇒ Object
Converts from one type of outlines to another (based on the file extensions as interpreted by fontforge). Currently any file ending in .otf will be assumed to contain PostScript outlines and any file ending with .ttf will be assumed to contain TrueType outlines.
source - The filename of the source font to modify. output - The filename of the result.
Returns nothing.
390 391 392 393 394 395 396 397 398 |
# File 'lib/fontprocessor/processor.rb', line 390 def convert(source, output) fm = Skytype::FontManipulator.new(File.binread(source)) raise StandardError, "ERROR: Failed to create FontManipulator" if fm.nil? ttf_data = fm.convert_to_ttf raise StandardError, "ERROR: Failed to create TTF data from source #{source.to_s}." if ttf_data.nil? ttf_font = Skytype::FontManipulator.new(ttf_data) raise StandardError, "ERROR: Failed to create TTF FontManipulator from TTF source data." if ttf_font.nil? ttf_font.save(output) end |
#copy_name_record(filename, source_id, destination_id) ⇒ Object
Copies a single name table record within a font.
filename - The filename of the font to modify. source_id - The id of the name record to copy. destination_id - The id of the name record to replace.
Returns nothing.
726 727 728 729 730 731 732 733 734 735 736 737 738 739 |
# File 'lib/fontprocessor/processor.rb', line 726 def copy_name_record(filename, source_id, destination_id) fm = Skytype::FontManipulator.new(File.binread(filename)) raise StandardError, "ERROR: Failed to create FontManipulator" if fm.nil? name_data = fm.get_table("name") name_table = Skytype::NameTable.new(name_data) dest_string = name_table.get_string(source_id, Skytype::NameTable::EN_LANGUAGE_ID) return if dest_string.nil? name_table.add_or_replace_string(dest_string, destination_id) fm.apply_table(name_table) fm.save(filename) end |
#file_metadata(formats) ⇒ Object
Public: A helper method to inspect the metadata of the primary file for this font.
Returns a FontFile for the unlocked source of this font.
65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/fontprocessor/processor.rb', line 65 def (formats) return @file_metadata if @file_metadata begin preprocess(formats) @file_metadata = FontProcessor::FontFile.new(@naming_strategy.unlocked(original_font_format)) rescue FontProcessorError => e raise rescue EOFError, Error => e fpe = FontProcessorError.new "#{e.class} : #{e.}" fpe.set_backtrace(e.backtrace) raise fpe end end |
#fix_os2_weight_and_width(filename) ⇒ Object
Ensures that both the usWeightClass and usWidthClass conform to the OTF spec. If the font is already valid nothing is done.
filename - The filename of the font to fix.
Returns nothing.
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 |
# File 'lib/fontprocessor/processor.rb', line 592 def fix_os2_weight_and_width(filename) fm = Skytype::FontManipulator.new(File.binread(filename)) os2_data = fm.get_table("OS/2") os2_table = Skytype::OS2Table.new(os2_data) if os2_table.get_width < 1 os2_table.set_width(1) elsif os2_table.get_width > 9 os2_table.set_width(9) end if os2_table.get_weight > 950 os2_table.set_weight(950) else index = (VALID_US_WEIGHT_CLASSES + [os2_table.get_weight]).sort.index(os2_table.get_weight) os2_table.set_weight(VALID_US_WEIGHT_CLASSES[index]) end fm.apply_table(os2_table) fm.save(filename) end |
#font_formats_to_generate(format_data) ⇒ Object
font_formats_to_generate - creates an arra of FontFormats.
format_data - A JSON blob containing the formats to process
including process_orignal, convert and a derivatives array.
Returns an Array of FontFormats to be generated from the source font.
130 131 132 133 134 135 136 137 138 139 |
# File 'lib/fontprocessor/processor.rb', line 130 def font_formats_to_generate(format_data) @font_formats_to_generate ||= begin formats = [] formats << FontFormat.new(:cff, :otf) if original_font_format.outline_format == :cff if original_font_format.outline_format == :ttf || format_data['convert'] == true formats << FontFormat.new(:ttf, :otf) end formats end end |
#generate_char_set(charset_data, formats, lock = true) ⇒ Object
Public: Generates the series of fonts required to display this font on the web.
charset_data - A JSON blob containing the complete definition for the charset
including charset_id, features and unicode list.
formats - A JSON blob containing the formats to process
including process_orignal, convert and a derivatives array.
lock - Determines whether the raw fonts should be locked or not.
Defaults to true, only really useful for testing.
Returns nothing.
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/fontprocessor/processor.rb', line 91 def generate_char_set(charset_data, formats, lock=true) begin ProcessFontJob.log(@font_base_id, charset_data['charset_id'], "Peverifying format data") preverify_format_data(formats) or raise "Invalid format data #{formats}" ProcessFontJob.log(@font_base_id, charset_data['charset_id'], "Preprocessing") preprocess(formats) ProcessFontJob.log(@font_base_id, charset_data['charset_id'], "generate_internal_char_set...") generate_internal_char_set(charset_data, formats, lock) rescue FontProcessorError => e raise rescue EOFError, Error => e fpe = FontProcessorError.new "#{e.class} : #{e.}" fpe.set_backtrace(e.backtrace) raise fpe end end |
#generate_internal_char_set(charset_data, format_data, lock) ⇒ Object
Generates all of the files required to display a single character set.
charset_data - A JSON blob containing the complete definition for the charset
including charset_id, features and unicode list.
format_data - A JSON blob containing the formats to process
including process_orignal, convert and a derivatives array.
lock - Determines whether the raw fonts should be locked or not.
Defaults to true, only really useful for testing.
Returns nothing. Raises MissingSourceFile if one of the source files require for
processing is missing.
Raises OTSValidationFailure if the subset font cannot pass OTS.
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
# File 'lib/fontprocessor/processor.rb', line 241 def generate_internal_char_set(charset_data, format_data, lock) font_formats_to_generate(format_data).each do |font_format| raise MissingSourceFile, "Missing source file '#{@naming_strategy.source(font_format)}'" unless File.exist?(@naming_strategy.source(font_format)) ProcessFontJob.log(@font_base_id, charset_data['charset_id'], "generate_internal_char_set naming strategy: #{@naming_strategy}") filename = @naming_strategy.char_set(charset_data['charset_id'], font_format) ProcessFontJob.log(@font_base_id, charset_data['charset_id'], "Subsetting...") subset(charset_data, @naming_strategy.source(font_format), @naming_strategy.source(font_format), filename) # Get a FM instance of the created subset. If it does not contain # soft-hyphen (U+00AD), but does contain hyphen (U+002D or U+2010), # then map soft-hyphen to hyphen. # Same thing is true for non-breaking space and space. # https://git.corp.adobe.com/typekit/fontprocessor/issues/2 -- this is for browsers to not display notdefs fm = Skytype::FontManipulator.new(File.binread(filename)) if fm.get_glyph_for_unicode(SOFT_HYPHEN) == 0 if fm.get_glyph_for_unicode(HYPHEN_MINUS) != 0 fm.map_unicode_to_unicode(SOFT_HYPHEN, HYPHEN_MINUS) elsif fm.get_glyph_for_unicode(HYPHEN) != 0 fm.map_unicode_to_unicode(SOFT_HYPHEN, HYPHEN) end fm.save(filename) end # always map nbsp to space so that they will render identically if fm.get_glyph_for_unicode(SPACE) != 0 fm.map_unicode_to_unicode(NON_BREAKING_SPACE, SPACE) end fm.save(filename) # validate subset font ProcessFontJob.log(@font_base_id, charset_data['charset_id'], "Sanitizing...") sanitizer = Ots::Ruby::Sanitizer.new(File.binread(filename), Ots::Ruby::Sanitizer::HARD) sanitizer.dump_file = File.join(Dir::tmpdir, "tmp_ots_dump.txt") raise OTSValidationFailure, "Failed to sanitize subset file '#{Pathname.new(@naming_strategy.source).basename}' with CharSet '#{charset_data['charset_id']}'!" unless sanitizer.valid? ProcessFontJob.log(@font_base_id, charset_data['charset_id'], "Done sanitizing...") # Copy the family name to the full name so that IE9 can handle the # files properly ProcessFontJob.log(@font_base_id, charset_data['charset_id'], "Copying name record...") copy_name_record(filename, FAMILY_ID, FULLNAME_ID) # Work around FontSupervisor deploy issue format_data['derivatives'].push "dyna_base" unless format_data['derivatives'].include?("dyna_base") format_data['derivatives'].push "woff2" unless format_data['derivatives'].include?("woff2") format_data['derivatives'].each do |derivative| case derivative when "dyna_base" wrap_dyna_base(charset_data['charset_id'], font_format) when "woff" wrap_woff(charset_data['charset_id'], font_format) when "woff2" wrap_woff2(charset_data['charset_id'], font_format) when "inst" wrap_installable(charset_data['charset_id'], font_format) when "swf" wrap_swf(charset_data['charset_id'], font_format) when "svg" ProcessFontJob.log(@font_base_id, charset_data['charset_id'], "SVG, doing nothing for this format...") "" else raise "Invalid format found in derivatives request" end end obfuscate_names(filename, Digest::MD5.hexdigest(@naming_strategy.source)) lock(filename) if lock # remove the master font if this the orignal font should not be processed if font_format.outline_format == original_font_format.outline_format && format_data['process_original'] == false File.delete(filename) end end end |
#insert_pua_glyphs(fm, filename) ⇒ Object
221 222 223 224 225 226 |
# File 'lib/fontprocessor/processor.rb', line 221 def insert_pua_glyphs(fm, filename) PUA_PAIRS.each do | pair | fm.map_unicode_to_unicode(pair[0], pair[1]) unless fm.get_glyph_for_unicode(pair[0]) != 0 end fm.save(filename) end |
#lock(filename) ⇒ Object
Sets the OS/2 embedding bit to deny embedding rights, which prevents fontforge from modifying it.
filename - The filename of the font to lock.
Returns nothing.
634 635 636 637 638 639 640 641 642 643 644 |
# File 'lib/fontprocessor/processor.rb', line 634 def lock(filename) fm = Skytype::FontManipulator.new(File.binread(filename)) raise StandardError, "ERROR: Failed to create FontManipulator" if fm.nil? os2_data = fm.get_table("OS/2") os2_table = Skytype::OS2Table.new(os2_data) os2_table.(true) fm.apply_table(os2_table) fm.save(filename) end |
#map_uni_value_to_int(original_unicode) ⇒ Object
Pubilc: Maps a Unicode value string to an integer
original_unicode - a numeric string in decimal, Hex or Unicode (U+) format
Raises InputError if argument contains characters which are not 0-9 or a-f Raises InputError if argument contains Hex “x” char in an invalid location
Returns an integer
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 |
# File 'lib/fontprocessor/processor.rb', line 761 def map_uni_value_to_int(original_unicode) # for sanity, make it all downcase, keeping the orginal for error messages unicode = original_unicode.downcase # if in Unicode format, convert to Hex if unicode.start_with? "u+" unicode.sub!("u+", "0x"); end #validate that the entire string just 0-9, a-f or x if not unicode[/[^x0123456789abcdef]/].nil? raise InputError, "ERROR: Invalid Unicode numeric value passed! unicode string = #{original_unicode}." end #validate that if there is an x, that it is the second char if unicode["x"] raise InputError, "ERROR: Invalid Unicode numeric value passed! unicode string = #{original_unicode}." unless unicode[1] == 'x' end unicode.strip! # if Hex, convert to hex, then to int if unicode[/[abcdefx]/] unicode.hex.to_i else unicode.to_i end end |
#obfuscate_names(filename, unique_id) ⇒ Object
This is the protection method that we use for raw fonts. With these settings you can’t install fonts into Windows or OS X.
filename - The filename of the font to modify. unique_id - The string to place in the unique id field
Returns nothing.
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 |
# File 'lib/fontprocessor/processor.rb', line 678 def obfuscate_names(filename, unique_id) ProcessFontJob.log(@font_base_id, "N/A", "Obfuscating #{filename}") fm = Skytype::FontManipulator.new(File.binread(filename)) raise StandardError, "ERROR: Failed to create FontManipulator" if fm.nil? name_data = fm.get_table("name") name_table = Skytype::NameTable.new(name_data) name_table.add_or_replace_string("", FAMILY_ID) name_table.add_or_replace_string("Regular", SUBFAMILY_ID) name_table.add_or_replace_string(unique_id, UNIQUE_ID) name_table.add_or_replace_string("-", FULLNAME_ID) name_table.add_or_replace_string("-", POSTSCRIPT_NAME_ID) fm.apply_table(name_table) fm.save(filename) end |
#original_font_format ⇒ Object
Returns a FontFormat that represents the original, primary format of this
font. The result is determine by inspecting the font itself.
110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/fontprocessor/processor.rb', line 110 def original_font_format @original_font_format ||= begin source_file = FontProcessor::FontFile.new(@naming_strategy.source) outline_format = case when source_file.has_postscript_outlines? then :cff when source_file.has_truetype_outlines? then :ttf else raise UnknownFontFormat, "Does not contain either TrueType or PostScript outlines" end FontFormat.new(outline_format, :otf) end end |
#parse_charset_to_subset(charset_data) ⇒ Object
Removes characters contained in the source font but aren’t included in the character set.
charset_data - A JSON blob containing the complete definition for the charset
including charset_id, features and unicode list.
original - The pristine font file from the foundry. It is required to
undo some of fontforge's 'magic'.
source - The filename of the source font to modify. output - The filename of the result.
Returns nothing.
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
# File 'lib/fontprocessor/processor.rb', line 354 def parse_charset_to_subset(charset_data) the_subset = Skytype::Subset.new uni_vals = charset_data['unicode'] or raise "No unicode set on charset!" # split on comma or newline/carriage return uni_array = uni_vals.split(/[,\n\r]/) uni_array.each do |entry| # strip any whitespace entry.strip! # if this is a range, split the entry if entry.include? ".." range_array = entry.split("..") range_start = map_uni_value_to_int(range_array[0]) range_end = map_uni_value_to_int(range_array[1]) uni_range = Range.new(range_start, range_end) uni_range.each do |val| the_subset.add_unicode(val) end else the_subset.add_unicode(map_uni_value_to_int(entry)) end end the_subset.set_features(charset_data['features']) the_subset end |
#preprocess(formats) ⇒ Object
Performs all of the global font modifications to the original font creating a set of source fonts. If the font contains only TrueType outlines it will only have a single source font, if it has PostScript outlines it will contain two sources and the TrueType outlines will be derived from the PostScript outlines.
Note: If the source file previously exists it is assumed to be correct and will not be reprocessed.
formats - A JSON blob containing the formats to process
including process_orignal, convert and a derivatives array.
Returns nothing. Raises MissingSourceFile if the original font file can not be found.
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/fontprocessor/processor.rb', line 155 def preprocess(formats) raise MissingSourceFile, "Missing original file '#{@naming_strategy.source}'" unless File.exist?(@naming_strategy.source) filename = @naming_strategy.source(original_font_format) return if File.exists? filename unlocked_filename = @naming_strategy.unlocked(original_font_format) FileUtils.cp(@naming_strategy.source, unlocked_filename) unlock(unlocked_filename) FileUtils.cp(unlocked_filename, filename) # we want to avoid using FontForge at all costs. Skytype can fix some minor problems which # will save us from failing OTS and therefore needing FontForge. Make a "subset" of # everything here - which will not actually remove any glyphs. fm = Skytype::FontManipulator.new(File.binread(filename)) subset_data = fm.generate_subset_raw(nil, "ALL") fm = Skytype::FontManipulator.new(subset_data) fm.save(filename) sanitizer = Ots::Ruby::Sanitizer.new(File.binread(filename), Ots::Ruby::Sanitizer::SOFT) sanitizer.dump_file = File.join(Dir::tmpdir, "tmp_ots_dump.txt") if not sanitizer.valid? fm = Skytype::FontManipulator.new(File.binread(filename)) orig_had_gdef = fm.has_table("GDEF") os2_data = fm.get_table("OS/2") os2_table = Skytype::OS2Table.new(os2_data) ProcessFontJob.log(@font_base_id, "N/A", "WARNING: Original font failed OTS. Re-processing with FontForge!") tmp_file_name = File.join(Dir::tmpdir, "temp_orignal.otf") FileUtils.cp(filename, tmp_file_name) FileUtils.rm(filename) parameters = { 'input_filename' => tmp_file_name, 'output_filename' => filename} python_script = File.join(File.dirname(__FILE__), "external", "fontforge", "subset.py") FontProcessor::ExternalJSONProgram.run(python_script, parameters) ProcessFontJob.log(@font_base_id, "N/A", "FontForge reprocessing complete") # check to make sure that FF didn't add a GDEF for us fm = Skytype::FontManipulator.new(File.binread(filename)) if fm.has_table("GDEF") && !orig_had_gdef ProcessFontJob.log(@font_base_id, "N/A", "Removing GDEF table added by FontForge") fm.remove_table("GDEF") fm.save(filename) end ProcessFontJob.log(@font_base_id, "N/A", "Applying os2 table to FontForge font") fm.apply_table(os2_table) fm.save(filename) end fm = Skytype::FontManipulator.new(File.binread(filename)) ProcessFontJob.log(@font_base_id, "N/A", "Inserting PUA glyphs..") insert_pua_glyphs(fm,filename) raise FontProcessorError, "Does not contain either TrueType or PostScript outlines" unless fm.valid? strip(filename) ProcessFontJob.log(@font_base_id, "N/A", "Adding name record..") add_name_record(LICENSE_URL_ID, filename, @license_url) ProcessFontJob.log(@font_base_id, "N/A", "Fixing os2 weight and width") fix_os2_weight_and_width(filename) if original_font_format.outline_format == :cff && formats['convert'] == true ProcessFontJob.log(@font_base_id, "N/A", "Converting CFF...") convert(filename, @naming_strategy.source(FontFormat.new(:ttf, :otf))) end end |
#preverify_format_data(formats) ⇒ Object
742 743 744 745 746 747 748 749 750 751 |
# File 'lib/fontprocessor/processor.rb', line 742 def preverify_format_data(formats) if formats['process_original'] == false && formats['convert'] == false && formats['derivatives'] == [] # Nothing is going to be produces, this is not OK return false elsif original_font_format.outline_format == :cff && formats['convert'] == false && formats['derivatives'] == ["svg"] # We cannot produce a SVG unless we have a TTF outline, so if no conversion and only svg, we'll produce nothing. return false end return true end |
#strip(filename) ⇒ Object
Removes all name records but except ids 0 through 6. Removes all name records with platform records other than Windows and Mac. Will retain any required strings referenced from other tables, e.g. STAT table.
Shortens all remaining fields to 5000 characters, to prevent issues on Windows platforms.
filename - The filename of the font to strip.
Returns nothing.
656 657 658 659 660 661 662 663 664 665 666 667 668 669 |
# File 'lib/fontprocessor/processor.rb', line 656 def strip(filename) fm = Skytype::FontManipulator.new(File.binread(filename)) raise StandardError, "ERROR: Failed to create FontManipulator" if fm.nil? stat_strings = fm.referenced_string_IDs name_data = fm.get_table("name") name_table = Skytype::NameTable.new(name_data) name_table.strip_strings(5000, [ Skytype::NameTable::EN_IANA_TAG, Skytype::NameTable::FR_IANA_TAG, Skytype::NameTable::JP_IANA_TAG ], stat_strings) fm.apply_table(name_table) fm.save(filename) end |
#subset(charset_data, original, source, output) ⇒ Object
Removes characters contained in the source font but aren’t included in the character set.
charset_data - A JSON blob containing the complete definition for the charset
including charset_id, features and unicode list.
original - The pristine font file from the foundry. It is required to
undo some of fontforge's 'magic'.
source - The filename of the source font to modify. output - The filename of the result.
Returns nothing.
333 334 335 336 337 338 339 340 341 |
# File 'lib/fontprocessor/processor.rb', line 333 def subset(charset_data, original, source, output) fm = Skytype::FontManipulator.new(File.binread(source)) raise StandardError, "ERROR: Failed to create FontManipulator" if fm.nil? the_subset = parse_charset_to_subset(charset_data) subset_data = fm.generate_subset(the_subset) subset_manipulator = Skytype::FontManipulator.new(subset_data) subset_manipulator.save(output) end |
#unlock(filename) ⇒ Object
Sets the OS/2 embedding bit to be emdedable, which allows fontforge to modify it and IE to render it.
filename - The filename of the font to unlock.
Returns nothing.
618 619 620 621 622 623 624 625 626 |
# File 'lib/fontprocessor/processor.rb', line 618 def unlock(filename) fm = Skytype::FontManipulator.new(File.binread(filename)) os2_data = fm.get_table("OS/2") os2_table = Skytype::OS2Table.new(os2_data) os2_table.(false) fm.apply_table(os2_table) fm.save(filename) end |
#wrap_dyna_base(charset_id, font_format) ⇒ Object
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 |
# File 'lib/fontprocessor/processor.rb', line 496 def wrap_dyna_base(charset_id, font_format) ProcessFontJob.log(@font_base_id, charset_id, "Wrapping Dynamic Base...") ProcessFontJob.log(@font_base_id, charset_id, "Naming strategy: #{@naming_strategy}") input_filename = @naming_strategy.char_set(charset_id, font_format) output_filename = @naming_strategy.char_set(charset_id, FontFormat.new(font_format.outline_format, :dyna_base)) # read the file and save it to the output dir fm = Skytype::FontManipulator.new(File.binread(input_filename)) fm.save(output_filename) #using the saved file, obfuscate the names obfuscate_names(output_filename, Digest::MD5.hexdigest(@naming_strategy.source)) # reload the font fm = Skytype::FontManipulator.new(File.binread(output_filename)) base_name_replacement = Digest::SHA256.hexdigest(@font_base_id) name_data = fm.get_table("name") name_table = Skytype::NameTable.new(name_data) name_table.add_or_replace_string(base_name_replacement, POSTSCRIPT_NAME_ID) name_table.add_or_replace_string(base_name_replacement, FAMILY_ID) name_table.add_or_replace_string(base_name_replacement + " #{name_table.get_string(SUBFAMILY_ID, Skytype::NameTable::EN_LANGUAGE_ID)}", FULLNAME_ID) fm.apply_table(name_table) # make sure that the font's CFF NameDict entry matches the PS name of the font fm.set_cff_name(base_name_replacement) os2_data = fm.get_table("OS/2") os2_table = Skytype::OS2Table.new(os2_data) os2_table.(true) fm.apply_table(os2_table) fm.save(output_filename) end |
#wrap_installable(charset_id, font_format) ⇒ Object
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 |
# File 'lib/fontprocessor/processor.rb', line 469 def wrap_installable(charset_id, font_format) ProcessFontJob.log(@font_base_id, charset_id, "Wrapping Installable...") ProcessFontJob.log(@font_base_id, charset_id, "Naming strategy: #{@naming_strategy}") input_filename = @naming_strategy.char_set(charset_id, font_format) output_filename = @naming_strategy.char_set(charset_id, FontFormat.new(font_format.outline_format, :inst)) # read the file and save it to the output dir fm = Skytype::FontManipulator.new(File.binread(input_filename)) fm.save(output_filename) #using the saved file, obfuscate the names obfuscate_names(output_filename, Digest::MD5.hexdigest(@naming_strategy.source)) # reload the font fm = Skytype::FontManipulator.new(File.binread(output_filename)) base_name_replacement = Digest::SHA256.hexdigest(@font_base_id) name_data = fm.get_table("name") name_table = Skytype::NameTable.new(name_data) name_table.add_or_replace_string(base_name_replacement, POSTSCRIPT_NAME_ID) name_table.add_or_replace_string(base_name_replacement, FAMILY_ID) name_table.add_or_replace_string(base_name_replacement + " #{name_table.get_string(SUBFAMILY_ID, Skytype::NameTable::EN_LANGUAGE_ID)}", FULLNAME_ID) fm.apply_table(name_table) # make sure that the font's CFF NameDict entry matches the PS name of the font fm.set_cff_name(base_name_replacement) fm.save(output_filename) end |
#wrap_swf(charset_id, font_format) ⇒ Object
Wraps the given file (as specified with char_set and font_format) in a SWF.
charset_id - A CharsetID which represents which characters to keep in the
processed font.
font_format - The FontFormat of the source file.
Returns nothing.
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 |
# File 'lib/fontprocessor/processor.rb', line 543 def wrap_swf(charset_id, font_format) ProcessFontJob.log(@font_base_id, charset_id, "Wrapping SWF...") ProcessFontJob.log(@font_base_id, charset_id, "Naming strategy: #{@naming_strategy}") input_filename = @naming_strategy.char_set(charset_id, font_format) output_filename = @naming_strategy.char_set(charset_id, FontFormat.new(font_format.outline_format, :swf)) font_name = "typekit_#{@font_base_id}" actionscript_filename = File.join(Dir::tmpdir, "#{font_name}.as") actionscript = <<-ACTIONSCRIPT package { import flash.display.Sprite; import flash.text.Font public class #{font_name} extends Sprite { public function #{font_name}() { Font.registerFont(fontClass); } [Embed(source='#{input_filename}', fontName='_#{font_name}', embedAsCFF='true', mimeType='application/x-font-truetype')] public static var fontClass:Class; } } ACTIONSCRIPT begin File.open(actionscript_filename, "w") do |f| f.write(actionscript) end if ENV['RACK'] == "test" FakeSwfWrapper.wrap(output_filename) else executable = File.join(Config.flex_sdk_path, "bin", "mxmlc") command = "\"#{executable}\" -static-link-runtime-shared-libraries=true -output \"#{output_filename}\" -- #{actionscript_filename}" FontProcessor::ExternalStdOutCommand.run(command, :expect_output => :return) end ensure FileUtils.rm(actionscript_filename) end end |
#wrap_woff(charset_id, font_format) ⇒ Object
Wraps the given file (as specified with char_set and font_format) in a WOFF.
charset_id - A CharsetID which represents which characters to keep in the
processed font.
font_format - The FontFormat of the source file.
Returns nothing. Raises OTSValidationFailure if the WOFF wrapped font cannot pass OTS.
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/fontprocessor/processor.rb', line 409 def wrap_woff(charset_id, font_format) ProcessFontJob.log(@font_base_id, charset_id, "Wrapping WOFF...") ProcessFontJob.log(@font_base_id, charset_id, "Naming strategy: #{@naming_strategy}") input_filename = @naming_strategy.char_set(charset_id, font_format) output_raw_filename = @naming_strategy.char_set(charset_id, FontFormat.new(font_format.outline_format, :woff_raw)) output_filename = @naming_strategy.char_set(charset_id, FontFormat.new(font_format.outline_format, :woff)) fm = Skytype::FontManipulator.new(File.binread(input_filename)) fm.save(output_raw_filename) validate_raw_path = Pathname.new(output_raw_filename) if validate_raw_path.exist? sanitizer = Ots::Ruby::Sanitizer.new(File.binread(output_raw_filename), Ots::Ruby::Sanitizer::HARD) sanitizer.dump_file = File.join(Dir::tmpdir, "tmp_ots_woff_raw_dump.txt") raise OTSValidationFailure, "Failed to sanitize pre-WOFF file '#{Pathname.new(output_raw_filename).basename}' with CharSet '#{charset_id}'!" unless sanitizer.valid? end input_data = File.binread(input_filename) begin woff_data = Wofferizer::WOFF.sfnt_to_woff(input_data, input_data.size) rescue ExternalProgramError => e raise e unless e. =~ /:\s*### WOFF warning: checksum mismatch \(corrected\)\s*\z/ end if woff_data.nil? raise WOFFProcessingFailure, "Failed to wrap WOFF file!" end File.binwrite(output_filename, woff_data) #validate WOFF wrapped font - only check this if the wrapping succeeded validation_path = Pathname.new(output_filename) if validation_path.exist? sanitizer = Ots::Ruby::Sanitizer.new(File.binread(output_filename), Ots::Ruby::Sanitizer::HARD) sanitizer.dump_file = File.join(Dir::tmpdir, "tmp_ots_woff_dump.txt") raise OTSValidationFailure, "Failed to sanitize WOFF file '#{Pathname.new(output_filename).basename}' with CharSet '#{charset_id}'!" unless sanitizer.valid? end end |
#wrap_woff2(charset_id, font_format) ⇒ Object
Wraps the given file (as specified with char_set and font_format) in a WOFF2 (using maximum compression).
charset_id - A CharsetID which represents which characters to keep in the
processed font.
font_format - The FontFormat of the source file.
Returns nothing.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
# File 'lib/fontprocessor/processor.rb', line 454 def wrap_woff2(charset_id, font_format) ProcessFontJob.log(@font_base_id, charset_id, "Wrapping WOFF2...") ProcessFontJob.log(@font_base_id, charset_id, "Naming strategy: #{@naming_strategy}") input_filename = @naming_strategy.char_set(charset_id, font_format) output_filename = @naming_strategy.char_set(charset_id, FontFormat.new(font_format.outline_format, :woff2)) input_data = File.binread(input_filename) woff2_data = Wofferizer::WOFF.sfnt_to_woff2(input_data, input_data.size, 11) if woff2_data.nil? raise WOFFProcessingFailure, "Failed to wrap WOFF2 file!" end File.binwrite(output_filename, woff2_data) end |