You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							195 lines
						
					
					
						
							5.1 KiB
						
					
					
				
			
		
		
	
	
							195 lines
						
					
					
						
							5.1 KiB
						
					
					
				| 'use strict'; | |
| 
 | |
| // See documentation here: http://www.microsoft.com/typography/otspec/glyf.htm | |
|  | |
| var _ = require('lodash'); | |
| var ByteBuffer = require('microbuffer'); | |
| 
 | |
| function getFlags(glyph) { | |
|   var result = []; | |
| 
 | |
|   _.forEach(glyph.ttfContours, function (contour) { | |
|     _.forEach(contour, function (point) { | |
|       var flag = point.onCurve ? 1 : 0; | |
| 
 | |
|       if (point.x === 0) { | |
|         flag += 16; | |
|       } else { | |
|         if (-0xFF <= point.x && point.x <= 0xFF) { | |
|           flag += 2; // the corresponding x-coordinate is 1 byte long | |
|         } | |
|         if (point.x > 0 && point.x <= 0xFF) { | |
|           flag += 16; // If x-Short Vector is set, this bit describes the sign of the value, with 1 equalling positive and 0 negative | |
|         } | |
|       } | |
|       if (point.y === 0) { | |
|         flag += 32; | |
|       } else { | |
|         if (-0xFF <= point.y && point.y <= 0xFF) { | |
|           flag += 4; // the corresponding y-coordinate is 1 byte long | |
|         } | |
|         if (point.y > 0 && point.y <= 0xFF) { | |
|           flag += 32; // If y-Short Vector is set, this bit describes the sign of the value, with 1 equalling positive and 0 negative. | |
|         } | |
|       } | |
|       result.push(flag); | |
|     }); | |
|   }); | |
|   return result; | |
| } | |
| 
 | |
| //repeating flags can be packed | |
| function compactFlags(flags) { | |
|   var result = []; | |
|   var prevFlag = -1; | |
|   var firstRepeat = false; | |
| 
 | |
|   _.forEach(flags, function (flag) { | |
|     if (prevFlag === flag) { | |
|       if (firstRepeat) { | |
|         result[result.length - 1] += 8; //current flag repeats previous one, need to set 3rd bit of previous flag and set 1 to the current one | |
|         result.push(1); | |
|         firstRepeat = false; | |
|       } else { | |
|         result[result.length - 1]++; //when flag is repeating second or more times, we need to increase the last flag value | |
|       } | |
|     } else { | |
|       firstRepeat = true; | |
|       prevFlag = flag; | |
|       result.push(flag); | |
|     } | |
|   }); | |
|   return result; | |
| } | |
| 
 | |
| function getCoords(glyph, coordName) { | |
|   var result = []; | |
| 
 | |
|   _.forEach(glyph.ttfContours, function (contour) { | |
|     result.push.apply(result, _.map(contour, coordName)); | |
|   }); | |
|   return result; | |
| } | |
| 
 | |
| function compactCoords(coords) { | |
|   return _.filter(coords, function (coord) { | |
|     return coord !== 0; | |
|   }); | |
| } | |
| 
 | |
| //calculates length of glyph data in GLYF table | |
| function glyphDataSize(glyph) { | |
|   // Ignore glyphs without outlines. These will get a length of zero in the “loca” table | |
|   if (!glyph.contours.length) { | |
|     return 0; | |
|   } | |
| 
 | |
|   var result = 12; //glyph fixed properties | |
|  | |
|   result += glyph.contours.length * 2; //add contours | |
|  | |
|   _.forEach(glyph.ttf_x, function (x) { | |
|     //add 1 or 2 bytes for each coordinate depending of its size | |
|     result += ((-0xFF <= x && x <= 0xFF)) ? 1 : 2; | |
|   }); | |
| 
 | |
|   _.forEach(glyph.ttf_y, function (y) { | |
|     //add 1 or 2 bytes for each coordinate depending of its size | |
|     result += ((-0xFF <= y && y <= 0xFF)) ? 1 : 2; | |
|   }); | |
| 
 | |
|   // Add flags length to glyph size. | |
|   result += glyph.ttf_flags.length; | |
| 
 | |
|   if (result % 4 !== 0) { // glyph size must be divisible by 4. | |
|     result += 4 - result % 4; | |
|   } | |
|   return result; | |
| } | |
| 
 | |
| function tableSize(font) { | |
|   var result = 0; | |
| 
 | |
|   _.forEach(font.glyphs, function (glyph) { | |
|     glyph.ttf_size = glyphDataSize(glyph); | |
|     result += glyph.ttf_size; | |
|   }); | |
|   font.ttf_glyph_size = result; //sum of all glyph lengths | |
|   return result; | |
| } | |
| 
 | |
| function createGlyfTable(font) { | |
| 
 | |
|   _.forEach(font.glyphs, function (glyph) { | |
|     glyph.ttf_flags = getFlags(glyph); | |
|     glyph.ttf_flags = compactFlags(glyph.ttf_flags); | |
|     glyph.ttf_x = getCoords(glyph, 'x'); | |
|     glyph.ttf_x = compactCoords(glyph.ttf_x); | |
|     glyph.ttf_y = getCoords(glyph, 'y'); | |
|     glyph.ttf_y = compactCoords(glyph.ttf_y); | |
|   }); | |
| 
 | |
|   var buf = new ByteBuffer(tableSize(font)); | |
| 
 | |
|   _.forEach(font.glyphs, function (glyph) { | |
| 
 | |
|     // Ignore glyphs without outlines. These will get a length of zero in the “loca” table | |
|     if (!glyph.contours.length) { | |
|       return; | |
|     } | |
| 
 | |
|     var offset = buf.tell(); | |
| 
 | |
|     buf.writeInt16(glyph.contours.length); // numberOfContours | |
|     buf.writeInt16(glyph.xMin); // xMin | |
|     buf.writeInt16(glyph.yMin); // yMin | |
|     buf.writeInt16(glyph.xMax); // xMax | |
|     buf.writeInt16(glyph.yMax); // yMax | |
|  | |
|     // Array of end points | |
|     var endPtsOfContours = -1; | |
| 
 | |
|     var ttfContours = glyph.ttfContours; | |
| 
 | |
|     _.forEach(ttfContours, function (contour) { | |
|       endPtsOfContours += contour.length; | |
|       buf.writeInt16(endPtsOfContours); | |
|     }); | |
| 
 | |
|     buf.writeInt16(0); // instructionLength, is not used here | |
|  | |
|     // Array of flags | |
|     _.forEach(glyph.ttf_flags, function (flag) { | |
|       buf.writeInt8(flag); | |
|     }); | |
| 
 | |
|     // Array of X relative coordinates | |
|     _.forEach(glyph.ttf_x, function (x) { | |
|       if (-0xFF <= x && x <= 0xFF) { | |
|         buf.writeUint8(Math.abs(x)); | |
|       } else { | |
|         buf.writeInt16(x); | |
|       } | |
|     }); | |
| 
 | |
|     // Array of Y relative coordinates | |
|     _.forEach(glyph.ttf_y, function (y) { | |
|       if (-0xFF <= y && y <= 0xFF) { | |
|         buf.writeUint8(Math.abs(y)); | |
|       } else { | |
|         buf.writeInt16(y); | |
|       } | |
|     }); | |
| 
 | |
|     var tail = (buf.tell() - offset) % 4; | |
| 
 | |
|     if (tail !== 0) { // glyph size must be divisible by 4. | |
|       for (; tail < 4; tail++) { | |
|         buf.writeUint8(0); | |
|       } | |
|     } | |
|   }); | |
|   return buf; | |
| } | |
| 
 | |
| module.exports = createGlyfTable;
 | |
| 
 |