diff --git a/demo/js/iconfont.json b/demo/js/iconfont.json new file mode 100644 index 0000000..4e8cb64 --- /dev/null +++ b/demo/js/iconfont.json @@ -0,0 +1 @@ +{"version":1,"numTables":15,"searchRenge":128,"entrySelector":3,"rengeShift":112,"tables":{"FFTM":{"name":"FFTM","checkSum":1826967136,"offset":252,"length":28},"OS/2":{"name":"OS/2","checkSum":1462805046,"offset":280,"length":96},"cmap":{"name":"cmap","checkSum":877345668,"offset":376,"length":338},"cvt ":{"name":"cvt ","checkSum":213778120,"offset":2760,"length":36},"fpgm":{"name":"fpgm","checkSum":821534357,"offset":2796,"length":2454},"gasp":{"name":"gasp","checkSum":16,"offset":2752,"length":8},"glyf":{"name":"glyf","checkSum":3220684404,"offset":716,"length":1238},"head":{"name":"head","checkSum":63303459,"offset":1956,"length":54},"hhea":{"name":"hhea","checkSum":126419824,"offset":2012,"length":36},"hmtx":{"name":"hmtx","checkSum":184615113,"offset":2048,"length":24},"loca":{"name":"loca","checkSum":60949657,"offset":2072,"length":16},"maxp":{"name":"maxp","checkSum":19466795,"offset":2088,"length":32},"name":{"name":"name","checkSum":8902151,"offset":2120,"length":558},"post":{"name":"post","checkSum":2967987990,"offset":2680,"length":72},"prep":{"name":"prep","checkSum":2780413542,"offset":5252,"length":149}},"head":{"version":1,"fontRevision":1,"checkSumAdjustment":3974893314,"magickNumber":1594834165,"flags":11,"unitsPerEm":1024,"created":"2014-12-03T12:25:44.000Z","modified":"2014-12-03T12:25:44.000Z","xMin":0,"yMin":-150,"xMax":1023,"yMax":812,"macStyle":0,"lowestRecPPEM":8,"fontDirectionHint":2,"indexToLocFormat":0,"glyphDataFormat":0},"maxp":{"version":1,"numGlyphs":7,"maxPoints":95,"maxContours":5,"maxCompositePoints":0,"maxCompositeContours":0,"maxZones":2,"maxTwilightPoints":38,"maxStorage":52,"maxFunctionDefs":108,"maxInstructionDefs":0,"maxStackElements":138,"maxSizeOfInstructions":2454,"maxComponentElements":0,"maxComponentDepth":0},"loca":[0,80,80,80,712,956,1068],"glyf":[{"contours":[[{"x":34,"y":0,"onCurve":true},{"x":34,"y":682,"onCurve":true},{"x":306,"y":682,"onCurve":true},{"x":306,"y":0,"onCurve":true}],[{"x":68,"y":34,"onCurve":true},{"x":272,"y":34,"onCurve":true},{"x":272,"y":648,"onCurve":true},{"x":68,"y":648,"onCurve":true}]],"xMin":34,"yMin":0,"xMax":306,"yMax":682,"instructions":[64,38,0,0,0,3,2,0,3,87,0,2,1,1,2,75,0,2,2,1,79,4,1,1,2,1,67,0,0,7,6,5,4,0,3,0,3,17,5,15,43],"unicode":[65535],"advanceWidth":374,"leftSideBearing":34,"name":".notdef"},{"contours":[],"unicode":[0,8,29],"advanceWidth":0,"leftSideBearing":0,"name":".null"},{"contours":[],"unicode":[9,13],"advanceWidth":341,"leftSideBearing":0,"name":"nonmarkingreturn"},{"contours":[[{"x":281,"y":543,"onCurve":true},{"x":254,"y":542},{"x":228,"y":542,"onCurve":true},{"x":145,"y":542,"onCurve":true},{"x":127,"y":542},{"x":90,"y":530},{"x":62,"y":505},{"x":44,"y":466},{"x":44,"y":440,"onCurve":true},{"x":44,"y":364,"onCurve":true},{"x":956,"y":364,"onCurve":true},{"x":956,"y":405,"onCurve":true},{"x":956,"y":421},{"x":955,"y":449},{"x":955,"y":453,"onCurve":true},{"x":955,"y":466},{"x":945,"y":498},{"x":921,"y":525},{"x":882,"y":543},{"x":852,"y":543,"onCurve":true},{"x":719,"y":543,"onCurve":true},{"x":719,"y":446,"onCurve":true},{"x":281,"y":446,"onCurve":true}],[{"x":955,"y":310,"onCurve":true},{"x":955,"y":258,"onCurve":true},{"x":955,"y":235},{"x":956,"y":177},{"x":956,"y":119},{"x":935,"y":82},{"x":904,"y":59},{"x":869,"y":50},{"x":855,"y":50,"onCurve":true},{"x":841,"y":49},{"x":810,"y":50},{"x":796,"y":50,"onCurve":true},{"x":764,"y":50,"onCurve":true},{"x":719,"y":178,"onCurve":true},{"x":280,"y":178,"onCurve":true},{"x":236,"y":50,"onCurve":true},{"x":207,"y":50,"onCurve":true},{"x":173,"y":50,"onCurve":true},{"x":153,"y":50},{"x":128,"y":51,"onCurve":true},{"x":103,"y":51},{"x":71,"y":70},{"x":52,"y":97},{"x":44,"y":129},{"x":44,"y":143,"onCurve":true},{"x":44,"y":310,"onCurve":true}],[{"x":163,"y":247,"onCurve":true},{"x":151,"y":247},{"x":133,"y":230},{"x":133,"y":204},{"x":151,"y":187},{"x":163,"y":187,"onCurve":true},{"x":176,"y":187},{"x":194,"y":204},{"x":194,"y":230},{"x":176,"y":247}],[{"x":316,"y":123,"onCurve":true},{"x":308,"y":97},{"x":302,"y":75,"onCurve":true},{"x":297,"y":56},{"x":286,"y":20},{"x":282,"y":6},{"x":280,"y":-10},{"x":284,"y":-23},{"x":299,"y":-31},{"x":313,"y":-31,"onCurve":true},{"x":350,"y":-31,"onCurve":true},{"x":417,"y":-31,"onCurve":true},{"x":498,"y":-31,"onCurve":true},{"x":578,"y":-31,"onCurve":true},{"x":642,"y":-31,"onCurve":true},{"x":678,"y":-31,"onCurve":true},{"x":701,"y":-31},{"x":723,"y":-7},{"x":714,"y":19,"onCurve":true},{"x":709,"y":32},{"x":700,"y":67},{"x":695,"y":84,"onCurve":true},{"x":690,"y":103},{"x":684,"y":123,"onCurve":true}],[{"x":336,"y":498,"onCurve":true},{"x":336,"y":726,"onCurve":true},{"x":336,"y":737},{"x":341,"y":761},{"x":356,"y":780},{"x":382,"y":792},{"x":403,"y":792,"onCurve":true},{"x":591,"y":792,"onCurve":true},{"x":622,"y":792},{"x":663,"y":763},{"x":663,"y":725,"onCurve":true},{"x":663,"y":498,"onCurve":true}]],"xMin":44,"yMin":-31,"xMax":956,"yMax":792,"instructions":[75,176,19,80,88,64,74,2,1,0,13,14,13,0,14,102,0,3,14,1,14,3,94,0,1,8,8,1,92,16,1,9,8,10,6,9,94,17,1,12,6,4,6,12,94,0,11,4,11,105,15,1,8,0,6,12,8,6,88,0,10,7,5,2,4,11,10,4,89,18,1,14,14,13,81,0,13,13,10,14,66,27,75,176,23,80,88,64,75,2,1,0,13,14,13,0,14,102,0,3,14,1,14,3,94,0,1,8,8,1,92,16,1,9,8,10,8,9,10,102,17,1,12,6,4,6,12,94,0,11,4,11,105,15,1,8,0,6,12,8,6,88,0,10,7,5,2,4,11,10,4,89,18,1,14,14,13,81,0,13,13,10,14,66,27,75,176,24,80,88,64,76,2,1,0,13,14,13,0,14,102,0,3,14,1,14,3,94,0,1,8,8,1,92,16,1,9,8,10,8,9,10,102,17,1,12,6,4,6,12,4,102,0,11,4,11,105,15,1,8,0,6,12,8,6,88,0,10,7,5,2,4,11,10,4,89,18,1,14,14,13,81,0,13,13,10,14,66,27,64,78,2,1,0,13,14,13,0,14,102,0,3,14,1,14,3,1,102,0,1,8,14,1,8,100,16,1,9,8,10,8,9,10,102,17,1,12,6,4,6,12,4,102,0,11,4,11,105,15,1,8,0,6,12,8,6,88,0,10,7,5,2,4,11,10,4,89,18,1,14,14,13,81,0,13,13,10,14,66,89,89,89,64,40,83,83,59,59,50,49,23,23,83,94,83,94,91,88,59,82,59,82,75,67,55,53,49,58,50,58,23,48,23,48,81,17,49,24,17,40,21,64,19,22,43],"unicode":[120],"advanceWidth":1001,"leftSideBearing":44,"name":"x"},{"contours":[[{"x":781,"y":196,"onCurve":true},{"x":799,"y":115},{"x":812,"y":50,"onCurve":true},{"x":818,"y":23},{"x":828,"y":-32},{"x":837,"y":-77},{"x":843,"y":-109},{"x":844,"y":-113,"onCurve":true},{"x":847,"y":-133},{"x":823,"y":-147},{"x":808,"y":-147,"onCurve":true},{"x":803,"y":-147},{"x":783,"y":-140},{"x":778,"y":-138,"onCurve":true},{"x":512,"y":17,"onCurve":true},{"x":440,"y":-25},{"x":380,"y":-59,"onCurve":true},{"x":355,"y":-73},{"x":305,"y":-102},{"x":264,"y":-126},{"x":235,"y":-143},{"x":229,"y":-145,"onCurve":true},{"x":219,"y":-150},{"x":198,"y":-147},{"x":180,"y":-134},{"x":172,"y":-117},{"x":174,"y":-108,"onCurve":true},{"x":175,"y":-104},{"x":184,"y":-72},{"x":194,"y":-28},{"x":207,"y":25},{"x":214,"y":52,"onCurve":true},{"x":229,"y":115},{"x":249,"y":195,"onCurve":true},{"x":189,"y":247},{"x":141,"y":288,"onCurve":true},{"x":120,"y":305},{"x":81,"y":339},{"x":49,"y":366},{"x":28,"y":384},{"x":27,"y":385,"onCurve":true},{"x":15,"y":396},{"x":-2,"y":423},{"x":2,"y":452},{"x":20,"y":468},{"x":32,"y":470,"onCurve":true},{"x":347,"y":499,"onCurve":true},{"x":464,"y":772,"onCurve":true},{"x":470,"y":789},{"x":493,"y":812},{"x":512,"y":812,"onCurve":true},{"x":522,"y":812},{"x":537,"y":803},{"x":548,"y":791},{"x":555,"y":778},{"x":557,"y":774,"onCurve":true},{"x":671,"y":501,"onCurve":true},{"x":986,"y":471,"onCurve":true},{"x":1007,"y":466},{"x":1023,"y":452},{"x":1023,"y":437,"onCurve":true},{"x":1023,"y":422},{"x":1006,"y":401},{"x":992,"y":386,"onCurve":true}]],"xMin":0,"yMin":-149,"xMax":1023,"yMax":812,"instructions":[183,33,14,0,3,0,1,1,64,75,176,26,80,88,64,11,0,0,1,0,105,0,1,1,10,1,66,27,64,9,0,1,0,1,104,0,0,0,95,89,180,51,49,41,2,15,43],"unicode":[13362],"advanceWidth":1024,"leftSideBearing":0,"name":"uni3432"},{"contours":[[{"x":77,"y":288,"onCurve":true},{"x":77,"y":458,"onCurve":true},{"x":356,"y":642,"onCurve":true},{"x":426,"y":508,"onCurve":true},{"x":601,"y":645,"onCurve":true},{"x":673,"y":510,"onCurve":true},{"x":849,"y":642,"onCurve":true},{"x":944,"y":501,"onCurve":true},{"x":941,"y":278,"onCurve":true},{"x":807,"y":433,"onCurve":true},{"x":623,"y":299,"onCurve":true},{"x":554,"y":436,"onCurve":true},{"x":370,"y":307,"onCurve":true},{"x":292,"y":439,"onCurve":true}],[{"x":77,"y":-95,"onCurve":true},{"x":77,"y":74,"onCurve":true},{"x":356,"y":258,"onCurve":true},{"x":426,"y":124,"onCurve":true},{"x":601,"y":261,"onCurve":true},{"x":673,"y":126,"onCurve":true},{"x":849,"y":258,"onCurve":true},{"x":944,"y":118,"onCurve":true},{"x":941,"y":-106,"onCurve":true},{"x":807,"y":50,"onCurve":true},{"x":623,"y":-85,"onCurve":true},{"x":554,"y":52,"onCurve":true},{"x":370,"y":-77,"onCurve":true},{"x":292,"y":56,"onCurve":true}]],"xMin":77,"yMin":-106,"xMax":944,"yMax":645,"instructions":[181,22,18,8,4,2,38,43],"unicode":[13367],"advanceWidth":1024,"leftSideBearing":77,"name":"uni3437"},{"contours":[[{"x":564,"y":339,"onCurve":true},{"x":574,"y":454},{"x":663,"y":642},{"x":731,"y":706,"onCurve":true},{"x":899,"y":706,"onCurve":true},{"x":830,"y":642},{"x":753,"y":454},{"x":744,"y":339,"onCurve":true},{"x":879,"y":339,"onCurve":true},{"x":879,"y":187,"onCurve":true},{"x":748,"y":187,"onCurve":true},{"x":759,"y":82},{"x":838,"y":-91},{"x":902,"y":-150,"onCurve":true},{"x":735,"y":-150,"onCurve":true},{"x":671,"y":-91},{"x":582,"y":82},{"x":567,"y":187,"onCurve":true},{"x":458,"y":187,"onCurve":true},{"x":443,"y":82},{"x":355,"y":-91},{"x":290,"y":-150,"onCurve":true},{"x":123,"y":-150,"onCurve":true},{"x":187,"y":-91},{"x":266,"y":82},{"x":278,"y":187,"onCurve":true},{"x":135,"y":187,"onCurve":true},{"x":135,"y":339,"onCurve":true},{"x":282,"y":339,"onCurve":true},{"x":273,"y":454},{"x":196,"y":642},{"x":127,"y":706,"onCurve":true},{"x":294,"y":706,"onCurve":true},{"x":362,"y":642},{"x":452,"y":454},{"x":461,"y":339,"onCurve":true}]],"xMin":123,"yMin":-150,"xMax":902,"yMax":706,"instructions":[64,53,8,1,0,1,0,104,5,1,3,2,3,105,10,9,7,3,1,2,2,1,75,10,9,7,3,1,1,2,80,6,4,2,2,1,2,68,0,0,0,35,0,35,19,17,19,19,19,19,17,19,19,11,23,43],"unicode":[13368],"advanceWidth":1024,"leftSideBearing":123,"name":"uni3438"}],"cmap":{"0":1,"8":1,"9":2,"13":2,"29":1,"120":3,"13362":4,"13367":5,"13368":6,"65535":0},"name":{"fontFamily":"\u0000i\u0000c\u0000o\u0000n\u0000f\u0000o\u0000n\u0000t","fontSubFamily":"\u0000M\u0000e\u0000d\u0000i\u0000u\u0000m","uniqueSubFamily":"\u0000F\u0000o\u0000n\u0000t\u0000F\u0000o\u0000r\u0000g\u0000e\u0000 \u00002\u0000.\u00000\u0000 \u0000:\u0000 \u0000i\u0000c\u0000o\u0000n\u0000f\u0000o\u0000n\u0000t\u0000 \u0000:\u0000 \u00003\u0000-\u00001\u00000\u0000-\u00002\u00000\u00001\u00004","fullName":"\u0000i\u0000c\u0000o\u0000n\u0000f\u0000o\u0000n\u0000t","version":"\u0000V\u0000e\u0000r\u0000s\u0000i\u0000o\u0000n\u0000 \u00001\u0000.\u00000\u0000 \u0000;\u0000 \u0000t\u0000t\u0000f\u0000a\u0000u\u0000t\u0000o\u0000h\u0000i\u0000n\u0000t\u0000 \u0000(\u0000v\u00000\u0000.\u00009\u00004\u0000)\u0000 \u0000-\u0000l\u0000 \u00008\u0000 \u0000-\u0000r\u0000 \u00005\u00000\u0000 \u0000-\u0000G\u0000 \u00002\u00000\u00000\u0000 \u0000-\u0000x\u0000 \u00001\u00004\u0000 \u0000-\u0000w\u0000 \u0000\"\u0000G\u0000\"\u0000 \u0000-\u0000f\u0000 \u0000-\u0000s","postScriptName":"\u0000i\u0000c\u0000o\u0000n\u0000f\u0000o\u0000n\u0000t"},"gasp":{"version":1,"numRanges":1,"GASPRangeTbl":[{"rangeMaxPPEM":65535,"rangeGaspBehavior":15}]},"hhea":{"version":1,"ascent":812,"descent":-150,"lineGap":92,"advanceWidthMax":1024,"minLeftSideBearing":0,"minRightSideBearing":0,"xMaxExtent":1023,"caretSlopeRise":1,"caretSlopeRun":0,"caretOffset":0,"reserved0":0,"reserved1":0,"reserved2":0,"reserved3":0,"metricDataFormat":0,"numOfLongHorMetrics":5},"hmtx":[{"advanceWidth":374,"leftSideBearing":34},{"advanceWidth":0,"leftSideBearing":0},{"advanceWidth":341,"leftSideBearing":0},{"advanceWidth":1001,"leftSideBearing":44},{"advanceWidth":1024,"leftSideBearing":0},{"advanceWidth":1024,"leftSideBearing":77},{"advanceWidth":1024,"leftSideBearing":123}],"post":{"italicAngle":0,"postoints":65411,"underlinePosition":50,"underlineThickness":0,"isFixedPitch":0,"minMemType42":0,"maxMemType42":0,"minMemType1":0,"maxMemType1":7,"format":2,"glyphNameIndex":[0,1,2,91,258,259,260],"names":["uni3432","uni3437","uni3438"]},"OS/2":{"version":4,"xAvgCharWidth":1018,"usWeightClass":500,"usWidthClass":5,"fsType":0,"ySubscriptXSize":665,"ySubscriptYSize":716,"ySubscriptXOffset":0,"ySubscriptYOffset":143,"ySuperscriptXSize":665,"ySuperscriptYSize":716,"ySuperscriptXOffset":0,"ySuperscriptYOffset":491,"yStrikeoutSize":51,"yStrikeoutPosition":265,"sFamilyClass":0,"bFamilyType":2,"bSerifStyle":0,"bWeight":6,"bProportion":3,"bContrast":0,"bStrokeVariation":0,"bArmStyle":0,"bLetterform":0,"bMidline":0,"bXHeight":0,"ulUnicodeRange1":1,"ulUnicodeRange2":134217728,"ulUnicodeRange3":0,"ulUnicodeRange4":0,"achVendID":"PfEd","fsSelection":192,"usFirstCharIndex":120,"usLastCharIndex":13368,"sTypoAscender":812,"sTypoDescender":-212,"sTypoLineGap":92,"usWinAscent":812,"usWinDescent":150,"ulCodePageRange1":1,"ulCodePageRange2":0,"sxHeight":792,"sCapHeight":0,"usDefaultChar":0,"usBreakChar":32,"usMaxContext":1}} \ No newline at end of file diff --git a/demo/js/ttfparse.js b/demo/js/ttfparse.js index 086c7d4..0952014 100644 --- a/demo/js/ttfparse.js +++ b/demo/js/ttfparse.js @@ -42,7 +42,7 @@ define( upFile.addEventListener('change', onUpFileChange); ajaxBinaryFile({ - url: '../font/baiduHealth.ttf', + url: '../font/iconfont.ttf', onSuccess: function(binaryData) { var ttfReader = new ttfreader(); ttfReader.read(binaryData); diff --git a/demo/js/ttfwriter.js b/demo/js/ttfwriter.js index 6d8e302..184c68c 100644 --- a/demo/js/ttfwriter.js +++ b/demo/js/ttfwriter.js @@ -18,13 +18,14 @@ define( * 初始化 */ init: function () { - $.getJSON('./js/baiduHealth.json', function(ttf) { + $.getJSON('./js/iconfont.json', function(ttf) { var reader = new TTFReader(); var writer = new TTFWriter(); var buffer = writer.write(ttf); - var ttfData = reader.read(buffer); + reader.read(buffer); + var ttfData = reader.resolve(); console.log(ttfData); var base64str = 'data:font/ttf;charset=utf-8;base64,' + ttf2base64(buffer); diff --git a/font/icomoon.ttf b/font/icomoon.ttf new file mode 100644 index 0000000..980c8ef Binary files /dev/null and b/font/icomoon.ttf differ diff --git a/font/icomoon.woff b/font/icomoon.woff new file mode 100644 index 0000000..6a9efcf Binary files /dev/null and b/font/icomoon.woff differ diff --git a/font/iconfont.eot b/font/iconfont.eot new file mode 100644 index 0000000..1663361 Binary files /dev/null and b/font/iconfont.eot differ diff --git a/font/iconfont.ttf b/font/iconfont.ttf index e178ff5..47dade9 100644 Binary files a/font/iconfont.ttf and b/font/iconfont.ttf differ diff --git a/font/iconfont.woff b/font/iconfont.woff new file mode 100644 index 0000000..d6ff79e Binary files /dev/null and b/font/iconfont.woff differ diff --git a/src/ttf/table/cmap.js b/src/ttf/table/cmap.js index 527befb..13b852a 100644 --- a/src/ttf/table/cmap.js +++ b/src/ttf/table/cmap.js @@ -28,15 +28,15 @@ define( var i; // 0~256 紧凑排列 if (subTable.format == 0) { - var format4 = subTable; + var format0 = subTable; // 跳过format字段 - format4.length = reader.readUint16(); - format4.language = reader.readUint16(); + format0.length = reader.readUint16(); + format0.language = reader.readUint16(); var glyphIdArray = []; - for (var i = 0, l = format4.length - 6; i < l; i++) { + for (var i = 0, l = format0.length - 6; i < l; i++) { glyphIdArray.push(reader.readUint8()); } - format4.glyphIdArray = glyphIdArray; + format0.glyphIdArray = glyphIdArray; } // 双字节编码,非紧凑排列 else if(subTable.format == 4) { @@ -136,12 +136,12 @@ define( // 读取字符分组 for (i = 0; i < nGroups; ++i){ var group = {}; - group.startCharCode = reader.readUint32(); - group.endCharCode = reader.readUint32(); - group.startGlyphID = reader.readUint32(); - groups.push(groups); + group.start = reader.readUint32(); + group.end = reader.readUint32(); + group.startId = reader.readUint32(); + groups.push(group); } - + format12.groups = groups; } } diff --git a/src/ttf/table/cmapwriter.js b/src/ttf/table/cmapwriter.js index 3b839c4..4e93d17 100644 --- a/src/ttf/table/cmapwriter.js +++ b/src/ttf/table/cmapwriter.js @@ -258,17 +258,22 @@ define( ttf.glyf.forEach(function(glyph, index) { if (lang.isArray(glyph.unicode)) { glyph.unicode.forEach(function(unicode) { - unicodes.push({ - unicode: unicode, - id: index - }); + if (unicode !== 0xFFFF) { + unicodes.push({ + unicode: unicode, + id: index + }); + } + }); } else if (typeof glyph.unicode == 'number') { - unicodes.push({ - unicode: glyph.unicode, - id: index - }); + if (glyph.unicode !== 0xFFFF) { + unicodes.push({ + unicode: glyph.unicode, + id: index + }); + } } }); @@ -277,18 +282,23 @@ define( }); ttf.support.cmap.unicodes = unicodes; - ttf.support.cmap.format4Segments = getSegments(unicodes, 0xFFFF); + + var unicodes2Bytes = unicodes.filter(function(a) { + return a.unicode > 29; + }); + + ttf.support.cmap.format4Segments = getSegments(unicodes2Bytes, 0xFFFF); ttf.support.cmap.format4Size = 24 + ttf.support.cmap.format4Segments.length * 8; // We need subtable 12 only if found unicodes with > 2 bytes. - var hasGLyphsOver2Bytes = unicodes.some(function(glyph) { + var hasGLyphsOver2Bytes = unicodes2Bytes.some(function(glyph) { return glyph.unicode > 0xFFFF; }); if (hasGLyphsOver2Bytes) { ttf.support.cmap.hasGLyphsOver2Bytes = hasGLyphsOver2Bytes; - ttf.support.cmap.format12Segments = getSegments(unicodes); + ttf.support.cmap.format12Segments = getSegments(unicodes2Bytes); ttf.support.cmap.format12Size = 16 + ttf.support.cmap.format12Segments.length * 12 } diff --git a/src/ttf/table/directory.js b/src/ttf/table/directory.js index 0ecb92c..0a2933a 100644 --- a/src/ttf/table/directory.js +++ b/src/ttf/table/directory.js @@ -23,7 +23,7 @@ define( for (var i = offset, l = numTables * 16; i < l; i += 16) { var name = reader.readString(i, 4); - + //console.log(name); tables[name] = { name : name, checkSum : reader.readUint32(i + 4), diff --git a/src/ttf/table/glyfcontour.js b/src/ttf/table/glyfcontour.js index f8890c0..0570858 100644 --- a/src/ttf/table/glyfcontour.js +++ b/src/ttf/table/glyfcontour.js @@ -128,9 +128,9 @@ define( val.contours = contours; // FIXME for test - val.flags = flags; - val.xCoordinates = xCoordinates; - val.yCoordinates = yCoordinates; + //val.flags = flags; + //val.xCoordinates = xCoordinates; + //val.yCoordinates = yCoordinates; } } diff --git a/src/ttf/table/glyfwriter.js b/src/ttf/table/glyfwriter.js index 5a2a90e..9a4b005 100644 --- a/src/ttf/table/glyfwriter.js +++ b/src/ttf/table/glyfwriter.js @@ -20,6 +20,7 @@ define( * @return {number} 大小 */ function sizeof(glyf, glyfSupport) { + if (!glyf.contours.length) { return 0; } @@ -27,7 +28,7 @@ define( //fixed header + instructions + endPtsOfContours var result = 10 + 2 - + (glyf.instructions ? glyf.instructions.length : 0) + //+ (glyf.instructions ? glyf.instructions.length : 0) + glyf.contours.length * 2 + glyfSupport.flags.length; @@ -58,27 +59,29 @@ define( // a, b, c, d, e // xy values or points - if(t.e >= -0xFF && t.e <= 0xFF && t.f >= 0xFF && t.f <= 0xFF) { - size += 2; - } - else { + if(t.e < 0 || t.e > 0x7F || t.f < 0 || t.f > 0x7F) { size += 4; } - - // scale - if (t.a != 1 || t.d != 1) { - size += t.a == t.d ? 2 : 4; + else { + size += 2; } // 01 , 10 if (t.b || t.c) { - size += 4; + size += 8; } + else { + // scale + if (t.a != 1 || t.d != 1) { + size += t.a == t.d ? 2 : 4; + } + } + }); - if (glyf.instructions) { - size += 2 + glyf.instructions.length; - } + // if (glyf.instructions) { + // size += 2 + glyf.instructions.length; + // } return size; } @@ -226,6 +229,8 @@ define( ttf.glyf.forEach(function(glyf, index) { + var writerOffset = writer.offset; + if (!glyf.compound && 0 == glyf.contours.length) { return; } @@ -253,7 +258,7 @@ define( var g = glyf.glyfs[i]; // instructions - flags += glyf.instructions ? componentFlag.WE_HAVE_INSTRUCTIONS : 0; + //flags += glyf.instructions ? componentFlag.WE_HAVE_INSTRUCTIONS : 0; // use my metrics flags += g.useMyMetrics ? componentFlag.USE_MY_METRICS : 0; // overlap compound @@ -269,7 +274,7 @@ define( // xy values or points // int 8 放不下,则用int16放 - if(e < -0xFF || e > 0xFF || f < -0xFF || f > 0xFF) { + if(e < 0 || e > 0x7F || f < 0 || f > 0x7F) { flags += componentFlag.ARG_1_AND_2_ARE_WORDS; } @@ -294,8 +299,8 @@ define( } else { - writer.writeInt8(e); - writer.writeInt8(f); + writer.writeUint8(e); + writer.writeUint8(f); } if (componentFlag.WE_HAVE_A_SCALE & flags) { @@ -313,16 +318,17 @@ define( } } - if (glyf.instructions) { - var instructions = glyf.instructions; - writer.writeUint16(instructions.length); - for (var i = 0, l = instructions.length; i < l; i++) { - writer.writeUint8(instructions[i] & 0xFF); - } - } + // if (glyf.instructions) { + // var instructions = glyf.instructions; + // writer.writeUint16(instructions.length); + // for (var i = 0, l = instructions.length; i < l; i++) { + // writer.writeUint8(instructions[i] & 0xFF); + // } + // } } else { + var endPtsOfContours = -1; glyf.contours.forEach(function(contour) { endPtsOfContours += contour.length; @@ -330,16 +336,16 @@ define( }); // not support instruction - if (glyf.instructions) { - var instructions = glyf.instructions; - writer.writeUint16(instructions.length); - for (var i = 0, l = instructions.length; i < l; i++) { - writer.writeUint8(instructions[i] & 0xFF); - } - } - else { + // if (glyf.instructions) { + // var instructions = glyf.instructions; + // writer.writeUint16(instructions.length); + // for (var i = 0, l = instructions.length; i < l; i++) { + // writer.writeUint8(instructions[i] & 0xFF); + // } + // } + // else { writer.writeUint16(0); - } + //} // 获取暂存中的flags var flags = ttf.support.glyf[index].flags; @@ -370,6 +376,8 @@ define( // 4字节对齐 var glyfSize = ttf.support.glyf[index].glyfSize; + //console.log(glyfSize, writer.offset - writerOffset); + // if (glyfSize % 4) { for (var i = 0, l = 4 - glyfSize % 4; i < l; i++) { writer.writeUint8(0); @@ -386,13 +394,10 @@ define( ttf.glyf.forEach(function(glyf) { var glyfSupport = {}; var glyfSupport = glyf.compound ? glyfSupport : getFlags(glyf, glyfSupport); - var contoursSize = glyf.compound ? sizeofCompound(glyf) : sizeof(glyf, glyfSupport); - var size = contoursSize; + var glyfSize = glyf.compound ? sizeofCompound(glyf) : sizeof(glyf, glyfSupport); + var size = glyfSize; - // 记录实际size, 用于4字节对齐 - var glyfSize = size; - - // glyph size must be divisible by 4. + // 4字节对齐 if (size % 4) { size += 4 - size % 4; } @@ -411,6 +416,12 @@ define( var xMin = 16384, yMin = 16384, xMax = -16384, yMax = -16384; var advanceWidthMax = -1, minLeftSideBearing = 16384, minRightSideBearing = 16384, xMaxExtent = -16384; ttf.glyf.forEach(function(glyf) { + + // 如果没有轮廓 + if (!glyf.compound && 0 == glyf.contours.length) { + return; + } + advanceWidthMax = Math.max(advanceWidthMax, glyf.advanceWidth); minLeftSideBearing = Math.min(minLeftSideBearing, glyf.leftSideBearing); minRightSideBearing = Math.min(minRightSideBearing, glyf.advanceWidth - glyf.xMax); diff --git a/src/ttf/table/loca.js b/src/ttf/table/loca.js index b70af7f..2316392 100644 --- a/src/ttf/table/loca.js +++ b/src/ttf/table/loca.js @@ -54,11 +54,19 @@ define( offset += glyfSupport[i].size * sizeRatio; } + // write extra + if (indexToLocFormat) { + writer.writeUint32(offset); + } + else { + writer.writeUint16(offset); + } + return writer; }, size: function(ttf) { - var numGlyphs = ttf.glyf.length; - return ttf.head.indexToLocFormat ? numGlyphs * 4 : numGlyphs * 2; + var locaCount = ttf.glyf.length + 1; + return ttf.head.indexToLocFormat ? locaCount * 4 : locaCount * 2; } } ); diff --git a/src/ttf/table/maxp.js b/src/ttf/table/maxp.js index d5e2b70..de9b639 100644 --- a/src/ttf/table/maxp.js +++ b/src/ttf/table/maxp.js @@ -36,21 +36,42 @@ define( }, size: function(ttf) { var maxPoints = 0, maxContours = 0, maxCompositePoints = 0, - maxCompositeContours = 0; + maxCompositeContours = 0, maxSizeOfInstructions = 0; ttf.glyf.forEach(function(glyf) { // 复合图元 if (glyf.compound) { - maxCompositePoints++; - maxCompositeContours += glyf.glyfs.length; + + var compositeContours = 0; + var compositePoints = 0; + glyf.glyfs.forEach(function(g) { + var cglyf = ttf.glyf[g.glyphIndex]; + compositeContours += cglyf.contours ? cglyf.contours.length : 0; + + if (cglyf.contours && cglyf.contours.length) { + cglyf.contours.forEach(function(contour) { + compositePoints += contour.length; + }); + } + + }); + + maxCompositePoints = Math.max(maxCompositePoints, compositePoints); + maxCompositeContours = Math.max(maxCompositeContours, compositeContours); + } // 简单图元 else { - maxContours += glyf.contours.length; + maxContours = Math.max(maxContours, glyf.contours.length); + + var points = 0, twilightPoints = 0; glyf.contours.forEach(function(contour) { - maxPoints += contour.length; + points += contour.length; }); - } + maxPoints = Math.max(maxPoints, points); + } + + maxSizeOfInstructions += glyf.instructions ? glyf.instructions.length : 0; }); ttf.support.maxp = { @@ -61,14 +82,14 @@ define( maxCompositePoints: maxCompositePoints, maxCompositeContours: maxCompositeContours, maxZones: 2, - maxTwilightPoints: 0, + maxTwilightPoints: 255, // It is unclear how to calculate maxStorage, maxFunctionDefs and maxInstructionDefs. // These are magic constants now, with values exceeding values from FontForge // see svg2ttf on github maxStorage: 10, maxFunctionDefs: 10, maxStackElements: 255, - maxSizeOfInstructions: 0, + maxSizeOfInstructions: maxSizeOfInstructions, maxComponentElements: 0, maxComponentDepth: 0 }; diff --git a/src/ttf/table/name.js b/src/ttf/table/name.js index 5a4bcfb..6e64b1b 100644 --- a/src/ttf/table/name.js +++ b/src/ttf/table/name.js @@ -8,7 +8,7 @@ define( function(require) { var table = require('./table'); - var nameId = require('../enum/nameId'); + var nameIdTbl = require('../enum/nameId'); var string = require('../util/string'); var name = table.create( @@ -31,7 +31,7 @@ define( nameRecord.platformID = reader.readUint16(); nameRecord.platformSpecificID = reader.readUint16(); nameRecord.languageID = reader.readUint16(); - nameRecord.nameID = reader.readUint16(); + nameRecord.nameId = reader.readUint16(); nameRecord.length = reader.readUint16(); nameRecord.offset = reader.readUint16(); nameRecordTbl.push(nameRecord); @@ -54,8 +54,8 @@ define( var nameRecord = nameRecordTbl[i]; if (nameRecord.platformID == platformID && nameRecord.platformSpecificID == platformSpecificID - && nameId[nameRecord.nameID]) { - names[nameId[nameRecord.nameID]] = decodeURIComponent(nameRecord.name); + && nameIdTbl[nameRecord.nameId]) { + names[nameIdTbl[nameRecord.nameId]] = decodeURIComponent(nameRecord.name); } } @@ -75,7 +75,7 @@ define( writer.writeUint16(nameRecord.platformID); writer.writeUint16(nameRecord.platformSpecificID); writer.writeUint16(nameRecord.languageID); - writer.writeUint16(nameRecord.nameID); + writer.writeUint16(nameRecord.nameId); writer.writeUint16(nameRecord.name.length); writer.writeUint16(offset); // offset offset += nameRecord.name.length; @@ -83,7 +83,7 @@ define( // write name tbl strings nameRecordTbl.forEach(function(nameRecord) { - writer.writeString(nameRecord.name); + writer.writeBytes(nameRecord.name); }); return writer; @@ -98,31 +98,47 @@ define( // 中文编码字符将被转化成url encode var size = 6; Object.keys(names).forEach(function(name) { - var id = nameId.names[name]; - var nameStr = encodeURIComponent(names[name] || '').replace(/%00/g, ''); + var id = nameIdTbl.names[name]; + + var utf8Bytes = string.toUTF8Bytes(names[name]); + var usc2Bytes = string.toUCS2Bytes(names[name]); + if (undefined !== id) { // mac nameRecordTbl.push({ - nameID: id, + nameId: id, platformID: 1, platformSpecificID: 0, languageID: 0, - name: nameStr + name: utf8Bytes }); // windows nameRecordTbl.push({ - nameID: id, + nameId: id, platformID: 3, platformSpecificID: 1, languageID: 0, // - name: nameStr + name: usc2Bytes }); // 子表大小 - size += 12 * 2 + nameStr.length * 2; + size += 12 * 2 + utf8Bytes.length + usc2Bytes.length; } }); + + var namingOrder = ['platformID', 'platformSpecificID', 'languageID', 'nameId']; + nameRecordTbl = nameRecordTbl.sort(function(a, b) { + var l = 0; + namingOrder.some(function(name) { + var o = a[name] - b[name]; + if (o) { + l = o; + return true; + } + }); + return l; + }); // 保存预处理信息 ttf.support.name = nameRecordTbl; diff --git a/src/ttf/table/post.js b/src/ttf/table/post.js index dae4d91..b0f69f4 100644 --- a/src/ttf/table/post.js +++ b/src/ttf/table/post.js @@ -19,7 +19,6 @@ define( var Posthead = table.create( 'posthead', [ - ['format', struct.Fixed], ['italicAngle', struct.Fixed], ['postoints', struct.Uint16], ['underlinePosition', struct.Int16], @@ -38,11 +37,16 @@ define( ], { read: function(reader, ttf) { - // 读取表头 - var tbl = new Posthead(this.offset).read(reader, ttf); + var tbl = null; + var format = reader.readFixed(this.offset); // format2 - if(tbl.format == 2) { + if(format == 2) { + + // 读取表头 + tbl = new Posthead(reader.offset).read(reader, ttf); + tbl.format = format; + var numberOfGlyphs = ttf.maxp.numGlyphs; var glyphNameIndex = []; @@ -57,6 +61,11 @@ define( var pascalStringBytes = reader.readBytes(reader.offset, pascalStringLength); tbl.names = string.readPascalString(pascalStringBytes); } + else { + tbl = { + format: format + }; + } return tbl; }, @@ -99,15 +108,25 @@ define( // 这里需要注意,"" 有可能是"\3" length不为0,但是是空字符串 if (glyf.name && glyf.name.charCodeAt(0) > 31) { + + if (glyf.name == '.notdef') { + nameIndexs.push(0); + } + else if (glyf.name == '.null') { + nameIndexs.push(1); + } + else if (glyf.name == 'nonmarkingreturn') { + nameIndexs.push(2); + } // 这里需要注意 // unicode如果小于258,并且glyph名字与默认的名字相等, // 则不需要放入name tbl - if (glyf.unicode - && glyf.unicode[0] < 258 - && postName[glyf.unicode[0]] == glyf.name + else if (glyf.unicode + && glyf.unicode[0] - 29 < 258 + && postName[glyf.unicode[0] - 29] == glyf.name ) { - nameIndexs.push(glyf.unicode[0]); + nameIndexs.push(glyf.unicode[0] - 29); } else { nameIndexs.push(258 + nameIndex++); @@ -119,8 +138,8 @@ define( } // 如果代码点有名字,则按默认的名字 - else if (glyf.unicode && glyf.unicode[0] < 258) { - nameIndexs.push(glyf.unicode[0]); + else if (glyf.unicode && glyf.unicode[0] - 29 < 258) { + nameIndexs.push(glyf.unicode[0] - 29); } // 否则命名为 .notdef else { diff --git a/src/ttf/ttfwriter.js b/src/ttf/ttfwriter.js index b72e69d..adc7a90 100644 --- a/src/ttf/ttfwriter.js +++ b/src/ttf/ttfwriter.js @@ -88,8 +88,8 @@ define( name: tableName, checkSum: 0, offset: offset, - length: size, - tableSize: tableSize + length: tableSize, + size: size }); ttfSize += size; @@ -112,16 +112,18 @@ define( var tableStart = writer.offset; !new supportTables[table.name]().write(writer, ttf); + + console.log(writer.offset - tableStart - table.length); - if (table.tableSize % 4) { + if (table.length % 4) { // 对齐字节 - for (var i = 0, l = 4 - table.tableSize % 4; i < l; i++) { + for (var i = 0, l = 4 - table.length % 4; i < l; i++) { writer.writeUint8(0); } } // 计算校验和 - table.checkSum = checkSum(writer.getBuffer(), tableStart, table.tableSize); + table.checkSum = checkSum(writer.getBuffer(), tableStart, table.size); }); @@ -137,6 +139,8 @@ define( var ttfCheckSum = (0xB1B0AFBA - checkSum(writer.getBuffer()) + 0x100000000) % 0x100000000; writer.writeUint32(ttfCheckSum, ttfHeadOffset + 8); + delete ttf.support; + return writer.getBuffer(); } diff --git a/src/ttf/util/checkSum.js b/src/ttf/util/checkSum.js index 2f3865e..74ae064 100644 --- a/src/ttf/util/checkSum.js +++ b/src/ttf/util/checkSum.js @@ -45,7 +45,41 @@ define( return sum % 0x100000000; } + function ulong(t) { + /*jshint bitwise:false*/ + t &= 0xffffffff; + if (t < 0) { + t += 0x100000000; + } + return t; + } - return checkSum; + function calc_checksum(buffer, offset, length) { + + var offset = offset || 0; + var length = length || buffer.byteLength; + var view = new DataView(buffer, offset, length); + + var sum = 0; + var nlongs = Math.floor(length / 4); + + for (var i = 0; i < nlongs; ++i) { + var t = view.getUint32(i * 4, false); + sum = ulong(sum + t); + } + + var leftBytes = length - nlongs * 4; //extra 1..3 bytes found, because table is not aligned. Need to include them in checksum too. + if (leftBytes > 0) { + var leftRes = 0; + for (i = 0; i < 4; i++) { + /*jshint bitwise:false*/ + leftRes = (leftRes << 8) + ((i < leftBytes) ? view.getUint8(nlongs * 4 + i, false) : 0); + } + sum = ulong(sum + leftRes); + } + return sum; + } + + return calc_checksum; } ); diff --git a/src/ttf/util/readWindowsAllCodes.js b/src/ttf/util/readWindowsAllCodes.js index 0d11c14..97fb6c1 100644 --- a/src/ttf/util/readWindowsAllCodes.js +++ b/src/ttf/util/readWindowsAllCodes.js @@ -18,55 +18,96 @@ define( * @see https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html */ function readWindowsAllCodes(tables) { + + var codes = {}; + + // 读取windows unicode 编码段 - var usc2Arr = tables.filter(function(item) { - return item.platformID == 3 && item.encodingID == 1 && item.format == 4; + var format0 = tables.filter(function(item) { + return item.format == 0; }); - if(usc2Arr.length) { - // 只取第一个format - var format4 = usc2Arr[0]; - var segCount = format4.segCountX2 / 2; - - var codes = {}; - - // graphIdArray 和idRangeOffset的偏移量 - var graphIdArrayIndexOffset = (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2; - - for (var i = 0; i < segCount; ++i) { - // 读取单个字符 - for( - var start = format4.startCode[i], end = format4.endCode[i]; - start <= end; - ++start - ) { - // range offset = 0 - if(format4.idRangeOffset[i] === 0) { - codes[start] = (start + format4.idDelta[i]) % 0x10000; - } - // rely on to glyphIndexArray - else { - var index = i + format4.idRangeOffset[i] / 2 - + (start - format4.startCode[i]) - - graphIdArrayIndexOffset; - - var graphId = format4.glyphIdArray[index]; - if(graphId !== 0) { - codes[start] = (graphId + format4.idDelta[i]) % 0x10000; - } - else { - codes[start] = 0; - } - - } + if (format0.length) { + format0 = format0[0]; + for (var i = 0, l = format0.glyphIdArray.length; i < l ; i++) { + if (format0.glyphIdArray[i]) { + codes[i] = format0.glyphIdArray[i]; } } - - return codes; - } - else { - return {}; } + + + // 读取windows unicode 编码段 + var format12 = tables.filter(function(item) { + return item.platformID == 3 && item.encodingID == 10 && item.format == 12; + }); + + // 读取format12表 + if (format12.length) { + format12 = format12[0]; + for (var i = 0, l = format12.nGroups; i < l ; i++) { + var group = format12.groups[i]; + var start = group.start, end = group.end, startId = group.startId; + for (;start <= end;) { + codes[start++] = startId++; + } + } + } + // 读取format4表 + else { + + // 读取windows unicode 编码段 + var format4 = tables.filter(function(item) { + return item.platformID == 3 && item.encodingID == 1 && item.format == 4; + }); + + if(format4.length) { + // 只取第一个format + format4 = format4[0]; + var segCount = format4.segCountX2 / 2; + + + + // graphIdArray 和idRangeOffset的偏移量 + var graphIdArrayIndexOffset = (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2; + + for (var i = 0; i < segCount; ++i) { + // 读取单个字符 + for( + var start = format4.startCode[i], end = format4.endCode[i]; + start <= end; + ++start + ) { + // range offset = 0 + if(format4.idRangeOffset[i] === 0) { + codes[start] = (start + format4.idDelta[i]) % 0x10000; + } + // rely on to glyphIndexArray + else { + var index = i + format4.idRangeOffset[i] / 2 + + (start - format4.startCode[i]) + - graphIdArrayIndexOffset; + + var graphId = format4.glyphIdArray[index]; + if(graphId !== 0) { + codes[start] = (graphId + format4.idDelta[i]) % 0x10000; + } + else { + codes[start] = 0; + } + + } + } + } + + } + + } + + // 去除 0xFFFF + delete codes[65535]; + + return codes; } return readWindowsAllCodes;