Merge pull request #164 from vingtetun/master
Let Chrome (and others?) handle Type1 Fonts (TrueType not working yet)
This commit is contained in:
		
						commit
						637708cf1a
					
				
							
								
								
									
										234
									
								
								fonts.js
									
									
									
									
									
								
							
							
						
						
									
										234
									
								
								fonts.js
									
									
									
									
									
								
							| @ -466,6 +466,29 @@ var Font = (function () { | ||||
|     return array; | ||||
|   }; | ||||
| 
 | ||||
|   function int16(bytes) { | ||||
|     return (bytes[0] << 8) + (bytes[1] & 0xff); | ||||
|   }; | ||||
| 
 | ||||
|   function int32(bytes) { | ||||
|     return (bytes[0] << 24) + (bytes[1] << 16) + | ||||
|            (bytes[2] << 8) + (bytes[3] & 0xff); | ||||
|   }; | ||||
| 
 | ||||
|   function getMaxPower2(number) { | ||||
|     var maxPower = 0; | ||||
|     var value = number; | ||||
|     while (value >= 2) { | ||||
|       value /= 2; | ||||
|       maxPower++; | ||||
|     } | ||||
| 
 | ||||
|     value = 2; | ||||
|     for (var i = 1; i < maxPower; i++) | ||||
|       value *= 2; | ||||
|     return value; | ||||
|   }; | ||||
| 
 | ||||
|   function string16(value) { | ||||
|     return String.fromCharCode((value >> 8) & 0xff) + | ||||
|            String.fromCharCode(value & 0xff); | ||||
| @ -486,7 +509,7 @@ var Font = (function () { | ||||
|     header += string16(numTables); | ||||
| 
 | ||||
|     // searchRange (2 bytes)
 | ||||
|     var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables); | ||||
|     var tablesMaxPower2 = getMaxPower2(numTables); | ||||
|     var searchRange = tablesMaxPower2 * 16; | ||||
|     header += string16(searchRange); | ||||
| 
 | ||||
| @ -518,7 +541,7 @@ var Font = (function () { | ||||
|     // checksum
 | ||||
|     var checksum = 0; | ||||
|     for (var i = 0; i < length; i+=4) | ||||
|       checksum += FontsUtils.bytesToInteger([data[i], data[i+1], data[i+2], data[i+3]]); | ||||
|       checksum += int16([data[i], data[i+1], data[i+2], data[i+3]]); | ||||
| 
 | ||||
|     var tableEntry = tag + string32(checksum) + string32(offset) + string32(length); | ||||
|     tableEntry = stringToArray(tableEntry); | ||||
| @ -565,7 +588,7 @@ var Font = (function () { | ||||
|     var headerSize = (12 * 2 + (ranges.length * 5 * 2)); | ||||
|     var segCount = ranges.length + 1; | ||||
|     var segCount2 = segCount * 2; | ||||
|     var searchRange = FontsUtils.getMaxPower2(segCount) * 2; | ||||
|     var searchRange = getMaxPower2(segCount) * 2; | ||||
|     var searchEntry = Math.log(segCount) / Math.log(2); | ||||
|     var rangeShift = 2 * segCount - searchRange; | ||||
| 
 | ||||
| @ -712,9 +735,9 @@ var Font = (function () { | ||||
|               String.fromCharCode(tag[2]) + | ||||
|               String.fromCharCode(tag[3]); | ||||
| 
 | ||||
|         var checksum = FontsUtils.bytesToInteger(file.getBytes(4)); | ||||
|         var offset = FontsUtils.bytesToInteger(file.getBytes(4)); | ||||
|         var length = FontsUtils.bytesToInteger(file.getBytes(4)); | ||||
|         var checksum = int32(file.getBytes(4)); | ||||
|         var offset = int32(file.getBytes(4)); | ||||
|         var length = int32(file.getBytes(4)); | ||||
| 
 | ||||
|         // Read the table associated data
 | ||||
|         var previousPosition = file.pos; | ||||
| @ -735,26 +758,26 @@ var Font = (function () { | ||||
|       function readOpenTypeHeader(ttf) { | ||||
|         return { | ||||
|           version: ttf.getBytes(4), | ||||
|           numTables: FontsUtils.bytesToInteger(ttf.getBytes(2)), | ||||
|           searchRange: FontsUtils.bytesToInteger(ttf.getBytes(2)), | ||||
|           entrySelector: FontsUtils.bytesToInteger(ttf.getBytes(2)), | ||||
|           rangeShift: FontsUtils.bytesToInteger(ttf.getBytes(2)) | ||||
|           numTables: int16(ttf.getBytes(2)), | ||||
|           searchRange: int16(ttf.getBytes(2)), | ||||
|           entrySelector: int16(ttf.getBytes(2)), | ||||
|           rangeShift: int16(ttf.getBytes(2)) | ||||
|         } | ||||
|       }; | ||||
| 
 | ||||
|       function replaceCMapTable(cmap, font, properties) { | ||||
|         font.pos = (font.start ? font.start : 0) + cmap.length; | ||||
| 
 | ||||
|         var version = FontsUtils.bytesToInteger(font.getBytes(2)); | ||||
|         var numTables = FontsUtils.bytesToInteger(font.getBytes(2)); | ||||
|         var version = int16(font.getBytes(2)); | ||||
|         var numTables = int16(font.getBytes(2)); | ||||
| 
 | ||||
|         for (var i = 0; i < numTables; i++) { | ||||
|           var platformID = FontsUtils.bytesToInteger(font.getBytes(2)); | ||||
|           var encodingID = FontsUtils.bytesToInteger(font.getBytes(2)); | ||||
|           var offset = FontsUtils.bytesToInteger(font.getBytes(4)); | ||||
|           var format = FontsUtils.bytesToInteger(font.getBytes(2)); | ||||
|           var length = FontsUtils.bytesToInteger(font.getBytes(2)); | ||||
|           var language = FontsUtils.bytesToInteger(font.getBytes(2)); | ||||
|           var platformID = int16(font.getBytes(2)); | ||||
|           var encodingID = int16(font.getBytes(2)); | ||||
|           var offset = int32(font.getBytes(4)); | ||||
|           var format = int16(font.getBytes(2)); | ||||
|           var length = int16(font.getBytes(2)); | ||||
|           var language = int16(font.getBytes(2)); | ||||
| 
 | ||||
|           if ((format == 0 && numTables == 1) || | ||||
|               (format == 6 && numTables == 1 && !properties.encoding.empty)) { | ||||
| @ -776,13 +799,13 @@ var Font = (function () { | ||||
|             // table. (This looks weird, so I can have missed something), this
 | ||||
|             // works on Linux but seems to fails on Mac so let's rewrite the
 | ||||
|             // cmap table to a 3-1-4 style
 | ||||
|             var firstCode = FontsUtils.bytesToInteger(font.getBytes(2)); | ||||
|             var entryCount = FontsUtils.bytesToInteger(font.getBytes(2)); | ||||
|             var firstCode = int16(font.getBytes(2)); | ||||
|             var entryCount = int16(font.getBytes(2)); | ||||
| 
 | ||||
|             var glyphs = []; | ||||
|             var min = 0xffff, max = 0; | ||||
|             for (var j = 0; j < entryCount; j++) { | ||||
|               var charcode = FontsUtils.bytesToInteger(font.getBytes(2)); | ||||
|               var charcode = int16(font.getBytes(2)); | ||||
|               glyphs.push(charcode); | ||||
| 
 | ||||
|               if (charcode < min) | ||||
| @ -882,7 +905,7 @@ var Font = (function () { | ||||
| 
 | ||||
|         // Tables needs to be written by ascendant alphabetic order
 | ||||
|         tables.sort(function tables_sort(a, b) { | ||||
|           return a.tag > b.tag; | ||||
|           return (a.tag > b.tag) - (a.tag < b.tag); | ||||
|         }); | ||||
| 
 | ||||
|         // rewrite the tables but tweak offsets
 | ||||
| @ -921,8 +944,6 @@ var Font = (function () { | ||||
|     }, | ||||
| 
 | ||||
|     convert: function font_convert(fontName, font, properties) { | ||||
|       var otf = new Uint8Array(kMaxFontFileSize); | ||||
| 
 | ||||
|       function createNameTable(name) { | ||||
|   	    // All the strings of the name table should be an odd number of bytes
 | ||||
|         if (name.length % 2) | ||||
| @ -994,46 +1015,34 @@ var Font = (function () { | ||||
| 		    return true; | ||||
|       }; | ||||
| 
 | ||||
|       // Required Tables
 | ||||
|       var CFF = | ||||
|         font.data,   // PostScript Font Program
 | ||||
|         OS2,         // OS/2 and Windows Specific metrics
 | ||||
|         cmap,        // Character to glyphs mapping
 | ||||
|         head,        // Font header
 | ||||
|         hhea,        // Horizontal header
 | ||||
|         hmtx,        // Horizontal metrics
 | ||||
|         maxp,        // Maximum profile
 | ||||
|         name,        // Naming tables
 | ||||
|         post;        // PostScript informations
 | ||||
|       var tables = [CFF, OS2, cmap, head, hhea, hmtx, maxp, name, post]; | ||||
| 
 | ||||
|       // The offsets object holds at the same time a representation of where
 | ||||
|       // to write the table entry information about a table and another offset
 | ||||
|       // representing the offset where to draw the actual data of a particular
 | ||||
|       // table
 | ||||
|       var kRequiredTablesCount = 9; | ||||
|       var offsets = { | ||||
|         currentOffset: 0, | ||||
|         virtualOffset: tables.length * (4 * 4) | ||||
|         virtualOffset: 9 * (4 * 4) | ||||
|       }; | ||||
| 
 | ||||
|       // It there is only one font, offset table is the first bytes of the file
 | ||||
|       createOpenTypeHeader("\x4F\x54\x54\x4F", otf, offsets, tables.length); | ||||
|       var otf = new Uint8Array(kMaxFontFileSize); | ||||
|       createOpenTypeHeader("\x4F\x54\x54\x4F", otf, offsets, 9); | ||||
| 
 | ||||
|       /** CFF */ | ||||
|       createTableEntry(otf, offsets, "CFF ", CFF); | ||||
| 
 | ||||
|       /** OS/2 */ | ||||
| 	    var charstrings = font.charstrings; | ||||
| 	    properties.fixedPitch = isFixedPitch(charstrings); | ||||
|       OS2 = stringToArray(createOS2Table(properties)); | ||||
|       createTableEntry(otf, offsets, "OS/2", OS2); | ||||
|       var fields = { | ||||
|         // PostScript Font Program
 | ||||
|         "CFF ": font.data, | ||||
| 
 | ||||
|       /** CMAP */ | ||||
|       cmap = createCMapTable(charstrings.slice()); | ||||
|       createTableEntry(otf, offsets, "cmap", cmap); | ||||
|         // OS/2 and Windows Specific metrics
 | ||||
|         "OS/2": stringToArray(createOS2Table(properties)), | ||||
| 
 | ||||
|       /** HEAD */ | ||||
|       head = stringToArray( | ||||
|         // Character to glyphs mapping
 | ||||
|         "cmap": createCMapTable(charstrings.slice()), | ||||
| 
 | ||||
|         // Font header
 | ||||
|         "head": (function() { | ||||
|           return stringToArray( | ||||
|               "\x00\x01\x00\x00" + // Version number
 | ||||
|               "\x00\x00\x10\x00" + // fontRevision
 | ||||
|               "\x00\x00\x00\x00" + // checksumAdjustement
 | ||||
| @ -1050,12 +1059,12 @@ var Font = (function () { | ||||
|               "\x00\x11" + // lowestRecPPEM
 | ||||
|               "\x00\x00" + // fontDirectionHint
 | ||||
|               "\x00\x00" + // indexToLocFormat
 | ||||
|               "\x00\x00"   // glyphDataFormat
 | ||||
|       ); | ||||
|       createTableEntry(otf, offsets, "head", head); | ||||
|               "\x00\x00");  // glyphDataFormat
 | ||||
|         })(), | ||||
| 
 | ||||
|       /** HHEA */ | ||||
|       hhea = stringToArray( | ||||
|         // Horizontal header
 | ||||
|         "hhea": (function() { | ||||
|           return stringToArray( | ||||
|               "\x00\x01\x00\x00" + // Version number
 | ||||
|               string16(properties.ascent) + // Typographic Ascent
 | ||||
|               string16(properties.descent) + // Typographic Descent
 | ||||
| @ -1072,41 +1081,37 @@ var Font = (function () { | ||||
|               "\x00\x00" + // -reserved-
 | ||||
|               "\x00\x00" + // -reserved-
 | ||||
|               "\x00\x00" + // metricDataFormat
 | ||||
|                  string16(charstrings.length + 1) // Number of HMetrics
 | ||||
|       ); | ||||
|       createTableEntry(otf, offsets, "hhea", hhea); | ||||
|               string16(charstrings.length + 1)); // Number of HMetrics
 | ||||
|         })(), | ||||
| 
 | ||||
|       /** HMTX */ | ||||
|       /* For some reasons, probably related to how the backend handle fonts, | ||||
|       * Linux seems to ignore this file and prefer the data from the CFF itself | ||||
|       * while Windows use this data. So be careful if you hack on Linux and | ||||
|       * have to touch the 'hmtx' table | ||||
|       */ | ||||
|       hmtx = "\x00\x00\x00\x00"; // Fake .notdef
 | ||||
|         // Horizontal metrics
 | ||||
|         "hmtx": (function() { | ||||
|           var hmtx = "\x00\x00\x00\x00"; // Fake .notdef
 | ||||
|           for (var i = 0; i < charstrings.length; i++) { | ||||
|             hmtx += string16(charstrings[i].width) + string16(0); | ||||
|           } | ||||
|       hmtx = stringToArray(hmtx); | ||||
|       createTableEntry(otf, offsets, "hmtx", hmtx); | ||||
|           return stringToArray(hmtx); | ||||
|         })(), | ||||
| 
 | ||||
|       /** MAXP */ | ||||
|       maxp = "\x00\x00\x50\x00" + // Version number
 | ||||
|              string16(charstrings.length + 1); // Num of glyphs
 | ||||
|       maxp = stringToArray(maxp); | ||||
|       createTableEntry(otf, offsets, "maxp", maxp); | ||||
|         // Maximum profile
 | ||||
|         "maxp": (function() { | ||||
|           return stringToArray( | ||||
|               "\x00\x00\x50\x00" + // Version number
 | ||||
|              string16(charstrings.length + 1)); // Num of glyphs
 | ||||
|         })(), | ||||
| 
 | ||||
|       /** NAME */ | ||||
|       name = stringToArray(createNameTable(fontName)); | ||||
|       createTableEntry(otf, offsets, "name", name); | ||||
|         // Naming tables
 | ||||
|         "name": stringToArray(createNameTable(fontName)), | ||||
| 
 | ||||
|       /** POST */ | ||||
|       post = stringToArray(createPostTable(properties)); | ||||
|       createTableEntry(otf, offsets, "post", post); | ||||
|         // PostScript informations
 | ||||
|         "post": stringToArray(createPostTable(properties)) | ||||
|       }; | ||||
| 
 | ||||
|       // Once all the table entries header are written, dump the data!
 | ||||
|       var tables = [CFF, OS2, cmap, head, hhea, hmtx, maxp, name, post]; | ||||
|       for (var i = 0; i < tables.length; i++) { | ||||
|         var table = tables[i]; | ||||
|       for (var field in fields) | ||||
|         createTableEntry(otf, offsets, field, fields[field]); | ||||
| 
 | ||||
|       for (var field in fields) { | ||||
|         var table = fields[field]; | ||||
|         otf.set(table, offsets.currentOffset); | ||||
|         offsets.currentOffset += table.length; | ||||
|       } | ||||
| @ -1144,54 +1149,6 @@ var Font = (function () { | ||||
|   return constructor; | ||||
| })(); | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * FontsUtils is a static class dedicated to hold codes that are not related | ||||
|  * to fonts in particular and needs to be share between them. | ||||
|  */ | ||||
| var FontsUtils = { | ||||
|   _bytesArray: new Uint8Array(4), | ||||
|   integerToBytes: function fu_integerToBytes(value, bytesCount) { | ||||
|     var bytes = this._bytesArray; | ||||
| 
 | ||||
|     if (bytesCount == 1) { | ||||
|       bytes.set([value]); | ||||
|       return bytes[0]; | ||||
|     } else if (bytesCount == 2) { | ||||
|       bytes.set([value >> 8, value & 0xff]); | ||||
|       return [bytes[0], bytes[1]]; | ||||
|     } else if (bytesCount == 4) { | ||||
|       bytes.set([value >> 24, value >> 16, value >> 8, value]); | ||||
|       return [bytes[0], bytes[1], bytes[2], bytes[3]]; | ||||
|     } | ||||
| 
 | ||||
|     error("This number of bytes " + bytesCount + " is not supported"); | ||||
|     return null; | ||||
|   }, | ||||
| 
 | ||||
|   bytesToInteger: function fu_bytesToInteger(bytesArray) { | ||||
|     var value = 0; | ||||
|     for (var i = 0; i < bytesArray.length; i++) | ||||
|       value = (value << 8) + bytesArray[i]; | ||||
|     return value; | ||||
|   }, | ||||
| 
 | ||||
|   getMaxPower2: function fu_getMaxPower2(number) { | ||||
|     var maxPower = 0; | ||||
|     var value = number; | ||||
|     while (value >= 2) { | ||||
|       value /= 2; | ||||
|       maxPower++; | ||||
|     } | ||||
| 
 | ||||
|     value = 2; | ||||
|     for (var i = 1; i < maxPower; i++) | ||||
|       value *= 2; | ||||
|     return value; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Type1Parser encapsulate the needed code for parsing a Type1 font | ||||
|  * program. | ||||
| @ -1638,10 +1595,7 @@ CFF.prototype = { | ||||
|     if (count == 0) | ||||
|       return "\x00\x00\x00"; | ||||
| 
 | ||||
|     var data = ""; | ||||
|     var bytes = FontsUtils.integerToBytes(count, 2); | ||||
|     for (var i = 0; i < bytes.length; i++) | ||||
|       data += String.fromCharCode(bytes[i]); | ||||
|     var data = String.fromCharCode(count >> 8, count & 0xff); | ||||
| 
 | ||||
|     // Next byte contains the offset size use to reference object in the file
 | ||||
|     // Actually we're using 0x04 to be sure to be able to store everything
 | ||||
| @ -1651,9 +1605,8 @@ CFF.prototype = { | ||||
|     // Add another offset after this one because we need a new offset
 | ||||
|     var relativeOffset = 1; | ||||
|     for (var i = 0; i < count + 1; i++) { | ||||
|       var bytes = FontsUtils.integerToBytes(relativeOffset, 4); | ||||
|       for (var j = 0; j < bytes.length; j++) | ||||
|         data += String.fromCharCode(bytes[j]); | ||||
|       data += String.fromCharCode(relativeOffset >> 24, relativeOffset >> 16, | ||||
|                                   relativeOffset >> 8, relativeOffset & 0xff); | ||||
| 
 | ||||
|       if (objects[i]) | ||||
|         relativeOffset += objects[i].length; | ||||
| @ -1703,7 +1656,7 @@ CFF.prototype = { | ||||
|     }; | ||||
| 
 | ||||
|     charstrings.sort(function charstrings_sort(a, b) { | ||||
|       return a.unicode > b.unicode; | ||||
|       return a.unicode - b.unicode; | ||||
|     }); | ||||
|     return charstrings; | ||||
|   }, | ||||
| @ -1848,8 +1801,7 @@ CFF.prototype = { | ||||
|           if (index == -1) | ||||
|             index = 0; | ||||
| 
 | ||||
|           var bytes = FontsUtils.integerToBytes(index, 2); | ||||
|           charset += String.fromCharCode(bytes[0]) + String.fromCharCode(bytes[1]); | ||||
|           charset += String.fromCharCode(index >> 8, index & 0xff); | ||||
|         } | ||||
|         return charset; | ||||
|       })(this), | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user