diff --git a/font/baiduHealth.fcp b/font/baiduHealth.fcp
new file mode 100644
index 0000000..405954c
Binary files /dev/null and b/font/baiduHealth.fcp differ
diff --git a/font/baiduHealth.ttf b/font/baiduHealth.ttf
new file mode 100644
index 0000000..126b2d0
Binary files /dev/null and b/font/baiduHealth.ttf differ
diff --git a/font/baiduHealth.woff b/font/baiduHealth.woff
new file mode 100644
index 0000000..ac427e5
Binary files /dev/null and b/font/baiduHealth.woff differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..73312dc
--- /dev/null
+++ b/index.html
@@ -0,0 +1,24 @@
+
+
+
+
+ font编辑器
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/common/ajaxBinaryFile.js b/src/common/ajaxBinaryFile.js
new file mode 100644
index 0000000..d933bbb
--- /dev/null
+++ b/src/common/ajaxBinaryFile.js
@@ -0,0 +1,64 @@
+/**
+ * @file ajaxBinaryFile.js
+ * @author mengke01
+ * @date
+ * @description
+ * ajax获取二进制数据
+ */
+
+
+define(
+ function(require) {
+
+ /**
+ * ajax获取二进制数据
+ *
+ * @param {Object} options 参数选项
+ * @param {string=} options.method method
+ * @param {Function=} options.onSuccess 成功回调
+ * @param {Function=} options.onError 失败回调
+ * @param {Object=} options.params 参数集合
+ */
+ function ajaxBinaryFile(options) {
+ var xhr = new XMLHttpRequest();
+
+ xhr.onreadystatechange = function(){
+ if (xhr.readyState=== 4 && xhr.status === 200){
+ if(options.onSuccess) {
+ var buffer = xhr.responseBlob || xhr.response;
+ options.onSuccess(buffer);
+ }
+ }
+ else if(xhr.status > 200){
+ if(options.onError) {
+ options.onError(xhr, xhr.status);
+ }
+ }
+ };
+
+ var method = (options.method || 'GET').toUpperCase();
+ var params = null;
+
+ if (options.params) {
+
+ var str = [];
+ Object.keys(options.params).forEach(function(key) {
+ str.push(key + '=' + encodeURIComponent(options.params[key]));
+ });
+ str = str.join('&');
+ if(method == 'GET') {
+ options.url += (options.url.indexOf('?') == -1 ? '?' : '&') + str;
+ }
+ else {
+ params = str;
+ }
+ }
+
+ xhr.open(method, options.url, true);
+ xhr.responseType = "arraybuffer";
+ xhr.send(null);
+ }
+
+ return ajaxBinaryFile;
+ }
+);
diff --git a/src/common/lang.js b/src/common/lang.js
new file mode 100644
index 0000000..ec008e1
--- /dev/null
+++ b/src/common/lang.js
@@ -0,0 +1,126 @@
+/**
+ * @file lang.js
+ * @author mengke01
+ * @date
+ * @description
+ * 语言相关函数
+ */
+
+
+define(
+ function(require) {
+
+ /**
+ * 为函数提前绑定前置参数(柯里化)
+ *
+ * @see http://en.wikipedia.org/wiki/Currying
+ * @param {Function} fn 要绑定的函数
+ * @param {...*=} args 函数执行时附加到执行时函数前面的参数
+ * @return {Function}
+ */
+ function curry( fn ) {
+ var xargs = [].slice.call( arguments, 1 );
+ return function () {
+ var args = xargs.concat( [].slice.call( arguments ) );
+ return fn.apply( this, args );
+ };
+ }
+
+
+ /**
+ * 方法静态化
+ *
+ * 反绑定、延迟绑定
+ * @inner
+ * @param {Function} method 待静态化的方法
+ *
+ * @return {Function} 静态化包装后方法
+ */
+ function generic(method) {
+ return function () {
+ return Function.call.apply(method, arguments);
+ };
+ }
+
+
+ /**
+ * 为函数绑定this与前置参数
+ *
+ * @param {Function} fn 需要操作的函数
+ * @param {Object} thisArg 需要绑定的this
+ * @param {...*=} args 函数执行时附加的前置绑定参数
+ * @return {Function}
+ */
+ function bind(fn, thisArg) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ return function () {
+ return fn.apply(
+ thisArg,
+ // 绑定参数先于扩展参数
+ // see http://es5.github.io/#x15.3.4.5.1
+ args.concat(Array.prototype.slice.call(arguments))
+ );
+ };
+ }
+
+ /**
+ * 为类型构造器建立继承关系
+ *
+ * @param {Function} subClass 子类构造器
+ * @param {Function} superClass 父类构造器
+ * @return {Function}
+ */
+ function inherits( subClass, superClass ) {
+ var Empty = function () {};
+ Empty.prototype = superClass.prototype;
+ var selfPrototype = subClass.prototype;
+ var proto = subClass.prototype = new Empty();
+
+ for ( var key in selfPrototype ) {
+ proto[ key ] = selfPrototype[ key ];
+ }
+ subClass.prototype.constructor = subClass;
+
+ return subClass;
+ }
+
+ /**
+ * 对象属性拷贝
+ *
+ * @param {Object} target 目标对象
+ * @param {...Object} source 源对象
+ * @return {Object}
+ */
+ function extend( target, source ) {
+ for ( var i = 1, len = arguments.length; i < len; i++ ) {
+ source = arguments[ i ];
+
+ if ( !source ) {
+ continue;
+ }
+
+ for ( var key in source ) {
+ if ( source.hasOwnProperty( key ) ) {
+ target[ key ] = source[ key ];
+ }
+ }
+
+ }
+
+ return target;
+ }
+
+
+
+
+ var exports = {
+ extend: extend,
+ bind: bind,
+ inherits: inherits,
+ curry: curry,
+ uncurry: generic
+ };
+
+ return exports;
+ }
+);
diff --git a/src/common/pad.js b/src/common/pad.js
new file mode 100644
index 0000000..f51675d
--- /dev/null
+++ b/src/common/pad.js
@@ -0,0 +1,30 @@
+/**
+ * @file pad.js
+ * @author mengke01
+ * @date
+ * @description
+ * 使用指定字符填充
+ */
+
+
+define(
+ function(require) {
+
+ /**
+ * 使用指定字符填充字符串,默认`0`
+ *
+ * @param {string} str 字符串
+ * @param {number} size 填充到的大小
+ * @param {string=} ch 填充字符
+ * @return {string} 字符串
+ */
+ function pad(str, size, ch) {
+ if(str.length > size) {
+ return str.slice(str.length - size);
+ }
+ return new Array(size - str.length + 1).join(ch || '0') + str;
+ }
+
+ return pad;
+ }
+);
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 0000000..e6e7eac
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,56 @@
+/**
+ * @file main.js
+ * @author mengke01
+ * @date
+ * @description
+ * 主函数入口
+ */
+
+
+define(
+ function(require) {
+ var ttfreader = require('ttf/ttfreader');
+ var ajaxBinaryFile = require('common/ajaxBinaryFile');
+
+ function onUpFileChange(e) {
+ var file = e.target.files[0];
+ var reader = new FileReader();
+ reader.onload = function(e) {
+ var ttf = new ttfreader().read(e.target.result);
+ }
+
+ reader.onerror = function(e) {
+ console.error(e);
+ };
+
+ reader.readAsArrayBuffer(file);
+ }
+
+ var entry = {
+
+ /**
+ * 初始化
+ */
+ init: function() {
+ var upFile = document.getElementById('upload-file');
+ upFile.addEventListener('change', onUpFileChange);
+
+ ajaxBinaryFile({
+ url: 'font/baiduHealth.ttf',
+ onSuccess: function(binaryData) {
+ var ttf = new ttfreader().read(binaryData);
+ },
+ onError: function() {
+ console.error('error read file');
+ }
+ });
+
+
+ }
+ };
+
+ entry.init();
+
+ return entry;
+ }
+);
diff --git a/src/ttf/enum/componentFlag.js b/src/ttf/enum/componentFlag.js
new file mode 100644
index 0000000..bbccd73
--- /dev/null
+++ b/src/ttf/enum/componentFlag.js
@@ -0,0 +1,33 @@
+/**
+ * @file componentFlag.js
+ * @author mengke01
+ * @date
+ * @description
+ *
+ * 复合图元标记位
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html
+ */
+
+
+define(
+ function(require) {
+
+ var componentFlag= {
+ ARG_1_AND_2_ARE_WORDS: 0x01,
+ ARGS_ARE_XY_VALUES: 0x02,
+ ROUND_XY_TO_GRID: 0x04,
+ WE_HAVE_A_SCALE: 0x08,
+ RESERVED: 0x10,
+ MORE_COMPONENTS: 0x20,
+ WE_HAVE_AN_X_AND_Y_SCALE: 0x40,
+ WE_HAVE_A_TWO_BY_TWO: 0x80,
+ WE_HAVE_INSTRUCTIONS: 0x100,
+ USE_MY_METRICS: 0x200,
+ OVERLAP_COMPOUND: 0x400,
+ SCALED_COMPONENT_OFFSET: 0x800,
+ UNSCALED_COMPONENT_OFFSET: 0x1000
+ };
+
+ return componentFlag;
+ }
+);
diff --git a/src/ttf/enum/glyFlag.js b/src/ttf/enum/glyFlag.js
new file mode 100644
index 0000000..240639a
--- /dev/null
+++ b/src/ttf/enum/glyFlag.js
@@ -0,0 +1,35 @@
+/**
+ * @file glyFlag.js
+ * @author mengke01
+ * @date
+ * @description
+ *
+ * 轮廓标记位
+ *
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html
+ */
+
+
+define(
+ function(require) {
+
+ var glyFlag = {
+
+ G_ONCURVE: 0x01, // on curve ,off curve
+ G_REPEAT: 0x08, //next byte is flag repeat count
+ G_XMASK: 0x12,
+ G_XADDBYTE: 0x12, //X is positive byte
+ G_XSUBBYTE: 0x12, //X is negative byte
+ G_XSAME: 0x10, //X is same
+ G_XADDINT: 0x00, //X is signed word
+
+ G_YMASK: 0x24,
+ G_YADDBYTE: 0x24, //Y is positive byte
+ G_YSUBBYTE: 0x04, //Y is negative byte
+ G_YSAME: 0x20, //Y is same
+ G_YADDINT: 0x00, //Y is signed word
+ };
+
+ return glyFlag;
+ }
+);
diff --git a/src/ttf/enum/platform.js b/src/ttf/enum/platform.js
new file mode 100644
index 0000000..8c5d6cb
--- /dev/null
+++ b/src/ttf/enum/platform.js
@@ -0,0 +1,22 @@
+/**
+ * @file platform.js
+ * @author mengke01
+ * @date
+ * @description
+ * 字体所属平台
+ */
+
+
+define(
+ function(require) {
+
+ var platform = {
+ Unicode: 0,
+ Macintosh: 1,
+ reserved: 2,
+ Microsoft: 3
+ };
+
+ return platform;
+ }
+);
diff --git a/src/ttf/enum/platformSpec.js b/src/ttf/enum/platformSpec.js
new file mode 100644
index 0000000..544de59
--- /dev/null
+++ b/src/ttf/enum/platformSpec.js
@@ -0,0 +1,25 @@
+/**
+ * @file encodingIdentifiers.js
+ * @author mengke01
+ * @date
+ * @description
+ * Unicode Platform-specific Encoding Identifiers
+ */
+
+
+define(
+ function(require) {
+
+ var spec = {
+ 'Default': 0,
+ 'Version1.1': 1,
+ 'ISO10646': 2,
+ 'UnicodeBMP': 3,
+ 'UnicodenonBMP': 4,
+ 'UnicodeVariationSequences': 5,
+ 'FullUnicodecoverage': 6
+ };
+
+ return spec;
+ }
+);
diff --git a/src/ttf/reader.js b/src/ttf/reader.js
new file mode 100644
index 0000000..6c5323c
--- /dev/null
+++ b/src/ttf/reader.js
@@ -0,0 +1,208 @@
+/**
+ * @file reader.js
+ * @author mengke01
+ * @date
+ * @description
+ * ttf读取器
+ */
+
+define(
+ function(require) {
+
+ var extend = require('common/lang').extend;
+ var curry = require('common/lang').curry;
+
+ // 检查数组支持情况
+ if(typeof ArrayBuffer === 'undefined' || typeof DataView === 'undefined') {
+ throw 'not support ArrayBuffer and DataView';
+ }
+
+ // 数据类型
+ var dataType = {
+ 'Int8': 1,
+ 'Int16': 2,
+ 'Int32': 4,
+ 'Uint8': 1,
+ 'Uint16': 2,
+ 'Uint32': 4,
+ 'Float32': 4,
+ 'Float64': 8
+ };
+
+
+ var proto = {};
+
+ /**
+ * 读取指定的数据类型
+ *
+ * @param {number} size 大小
+ * @param {number=} offset 位移
+ * @param {boolean=} littleEndian 是否小尾
+ * @return {number} 返回值
+ */
+ function read(type, offset, littleEndian) {
+ var size = dataType[type];
+ // 使用当前位移
+ if(undefined == offset) {
+ offset = this.offset;
+ }
+
+ // 使用小尾
+ if(undefined == littleEndian) {
+ littleEndian = this.littleEndian;
+ }
+ this.offset = offset + size;
+
+ return this.view['get' + type](offset, littleEndian);
+ }
+
+ // 直接支持的数据类型
+ Object.keys(dataType).forEach(function(type) {
+ proto['read' + type] = curry(read, type);
+ });
+
+
+ /**
+ * 读取器
+ *
+ * @constructor
+ * @param {Array.} buffer 缓冲数组
+ * @param {number} offset 起始偏移
+ * @param {number} length 数组长度
+ * @param {boolean} bigEndian 是否大尾
+ */
+ function Reader(buffer, offset, length, littleEndian) {
+
+ var bufferLength = buffer.byteLength || buffer.length;
+
+ this.offset = offset || 0;
+ this.length = length || (bufferLength - this.offset);
+ this.littleEndian = littleEndian || false;
+
+ this.view = new DataView(buffer, this.offset, this.length);
+ }
+
+ Reader.prototype = {
+ read: read,
+
+ /**
+ * 读取一个string
+ *
+ * @param {number} offset 偏移
+ * @param {number} length 长度
+ * @return {string} 字符串
+ */
+ readString: function(offset, length) {
+
+ if(arguments.length == 1) {
+ length = arguments[0];
+ offset = this.offset;
+ }
+
+ if(length < 0 || offset + length > this.length) {
+ throw 'length out of range:' + offset + ',' + length;
+ }
+
+ var value = '';
+ for (var i = 0; i < length; ++i) {
+ var c = this.readUint8(offset + i);
+ value += String.fromCharCode(c > 127 ? 65533 : c);
+ }
+
+ this.offset = offset + length;
+
+ return value;
+ },
+
+ /**
+ * 读取一个字符
+ *
+ * @param {number} offset 偏移
+ * @return {string} 字符串
+ */
+ readChar: function(offset) {
+ return this.readString(offset, 1);
+ },
+
+ /**
+ * 读取fixed类型
+ *
+ * @param {number} offset 偏移
+ * @return {number} float
+ */
+ readFixed: function(offset) {
+ if(undefined == offset) {
+ offset = this.offset;
+ }
+ var val = this.readInt32(offset, false) / 65536.0;
+ return Math.ceil(val * 100000) / 100000;
+ },
+
+ /**
+ * 读取长日期
+ *
+ * @param {number} offset 偏移
+ * @return {Date} Date对象
+ */
+ readLongDateTime: function(offset) {
+ if(undefined == offset) {
+ offset = this.offset;
+ }
+ var delta = -2080198800000;// (new Date(1904, 1, 1)).getTime();
+ var date = new Date();
+ date.setTime(this.readUint32(offset + 4, false));
+ return date;
+ },
+
+ /**
+ * 跳转到指定偏移
+ *
+ * @param {number} offset 偏移
+ * @return {Object} this
+ */
+ seek: function (offset) {
+ if (undefined == offset) {
+ this.offset = 0;
+ }
+
+ if (offset < 0 || offset > this.length) {
+ throw 'offset out of range:' + offset;
+ }
+
+ this.offset = offset;
+
+ return this;
+ },
+
+ /**
+ * 获取指定的字节数组
+ *
+ * @return {Array} 字节数组
+ */
+ readBytes: function(offset, length) {
+
+ if(arguments.length == 1) {
+ length = arguments[0];
+ offset = this.offset;
+ }
+
+ if(length < 0 || offset + length > this.length) {
+ throw 'length out of range:' + offset + ',' + length;
+ }
+
+ var buffer = [];
+ for (var i = 0; i < length; ++i) {
+ buffer.push(this.view.getUint8(offset + i));
+ }
+
+ this.offset = offset + length;
+ return buffer;
+ }
+
+ };
+
+ extend(Reader.prototype, proto);
+
+ return Reader;
+ }
+);
diff --git a/src/ttf/table/cmap.js b/src/ttf/table/cmap.js
new file mode 100644
index 0000000..ac380a7
--- /dev/null
+++ b/src/ttf/table/cmap.js
@@ -0,0 +1,168 @@
+/**
+ * @file cmap.js
+ * @author mengke01
+ * @date
+ * @description
+ * cmap 表
+ */
+
+define(
+ function(require) {
+ var table = require('./table');
+ var struct = require('./struct');
+
+
+ /**
+ * 读取子表
+ * Each 'cmap' subtable is in one of nine currently available
+ * formats. These are format 0, format 2, format 4,
+ * format 6, format 8.0, format 10.0, format 12.0,
+ * format 13.0, and format 14 described in the next section.
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html
+ */
+ function readSubTable(reader, ttf, subTable, cmapOffset) {
+ var startOffset = cmapOffset + subTable.offset;
+ subTable.format = reader.readUint16(startOffset);
+
+ var i;
+
+ // 双字节编码,非紧凑排列
+ if(subTable.format == 4) {
+ var format4 = subTable;
+ // 跳过format字段
+ format4.length = reader.readUint16();
+ format4.language = reader.readUint16();
+ format4.segCountX2 = reader.readUint16();
+ format4.searchRange = reader.readUint16();
+ format4.entrySelector = reader.readUint16();
+ format4.rangeShift = reader.readUint16();
+
+ var segCount = format4.segCountX2 / 2;
+
+ // end code
+ var endCode = [];
+ for(i = 0; i < segCount; ++i) {
+ endCode.push(reader.readUint16());
+ }
+ format4.endCode = endCode;
+
+ format4.reservedPad = reader.readUint16();
+
+ // start code
+ var startCode = [];
+ for(i = 0; i < segCount; ++i) {
+ startCode.push(reader.readUint16());
+ }
+ format4.startCode = startCode;
+
+ // idDelta
+ var idDelta = [];
+ for(i = 0; i < segCount; ++i) {
+ idDelta.push(reader.readUint16());
+ }
+ format4.idDelta = idDelta;
+
+ // idRangeOffset
+ var idRangeOffset = [];
+ for(i = 0; i < segCount; ++i) {
+ idRangeOffset.push(reader.readUint16());
+ }
+ format4.idRangeOffset = idRangeOffset;
+
+ // 总长度 - glyphIdArray起始偏移/2
+ var glyphCount = (format4.length - (reader.offset - startOffset)) / 2;
+
+ // glyphIdArray
+ var glyphIdArray = [];
+ for(i = 0; i < glyphCount; ++i) {
+ glyphIdArray.push(reader.readUint16());
+ }
+
+ format4.glyphIdArray = glyphIdArray;
+
+ }
+ // The firstCode and entryCount values in the subtable specify
+ // the useful subrange within the range of possible character codes.
+ // The range begins with firstCode and has a length equal to entryCount.
+ else if(subTable.format == 6) {
+ var format6 = subTable;
+
+ format6.length = reader.readUint16();
+ format6.language = reader.readUint16();
+ format6.firstCode = reader.readUint16();
+ format6.entryCount = reader.readUint16();
+
+ var glyphIndexArray = [];
+ var entryCount = format6.entryCount;
+ // 读取字符分组
+ for (i = 0; i < entryCount; ++i){
+ glyphIndexArray.push(reader.readUint16());
+ }
+ format6.glyphIdArray = glyphIndexArray;
+
+ }
+ // defines segments for sparse representation in 4-byte character space
+ else if(subTable.format == 12) {
+ var format12 = subTable;
+
+ format12.reserved = reader.readUint16();
+ format12.length = reader.readUint32();
+ format12.language = reader.readUint32();
+ format12.nGroups = reader.readUint32();
+
+ var groups = [];
+ var nGroups = format12.nGroups;
+ // 读取字符分组
+ for (i = 0; i < nGroups; ++i){
+ var group = {};
+ group.startCharCode = reader.readUint32();
+ group.endCharCode = reader.readUint32();
+ group.startGlyphID = reader.readUint32();
+ groups.push(groups);
+ }
+
+ }
+ }
+
+
+
+ var cmap = table.create(
+ 'cmap',
+ [],
+ {
+ read: function(reader, ttf) {
+ var tcmap = {};
+ var cmapOffset = this.offset;
+
+ reader.seek(cmapOffset);
+
+ tcmap.version = reader.readUint16(); // 编码方式
+ var numberSubtables = tcmap.numberSubtables = reader.readUint16(); // 表个数
+
+
+ var subTables = tcmap.tables = []; // 名字表
+ var offset = reader.offset;
+
+ // 使用offset读取,以便于查找
+ for(var i = 0, l = numberSubtables; i < l; i++) {
+ var subTable = {};
+ subTable.platformID = reader.readUint16(offset);
+ subTable.encodingID = reader.readUint16(offset + 2);
+ subTable.offset = reader.readUint32(offset + 4);
+
+ readSubTable(reader, ttf, subTable, cmapOffset);
+ subTables.push(subTable);
+
+ offset += 8;
+ }
+
+ tcmap.tables = subTables;
+
+ return tcmap;
+ }
+ }
+ );
+
+ return cmap;
+ }
+);
\ No newline at end of file
diff --git a/src/ttf/table/gasp.js b/src/ttf/table/gasp.js
new file mode 100644
index 0000000..165cb6d
--- /dev/null
+++ b/src/ttf/table/gasp.js
@@ -0,0 +1,47 @@
+/**
+ * @file gasp.js
+ * @author mengke01
+ * @date
+ * @description
+ * gasp表
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gasp.html
+ */
+
+define(
+ function(require) {
+ var table = require('./table');
+ var struct = require('./struct');
+
+ var gasp = table.create(
+ 'gasp',
+ [],
+ {
+ /**
+ * 解析gasp表
+ */
+ read: function(reader, ttf) {
+ var offset = this.offset;
+ var gasp = {};
+
+ reader.seek(offset);
+
+ gasp.version = reader.readUint16();
+ gasp.numRanges = reader.readUint16();
+
+ var GASPRangeTbl = [];
+ for (var i = 0; i < gasp.numRanges; ++i) {
+ var GASPRange = {};
+ GASPRange.rangeMaxPPEM = reader.readUint16();
+ GASPRange.rangeGaspBehavior = reader.readUint16();
+ GASPRangeTbl.push(GASPRange);
+ };
+ gasp.GASPRangeTbl = GASPRangeTbl;
+
+ return gasp;
+ }
+ }
+ );
+
+ return gasp;
+ }
+);
\ No newline at end of file
diff --git a/src/ttf/table/glyf.js b/src/ttf/table/glyf.js
new file mode 100644
index 0000000..991fd11
--- /dev/null
+++ b/src/ttf/table/glyf.js
@@ -0,0 +1,56 @@
+/**
+ * @file glyf.js
+ * @author mengke01
+ * @date
+ * @description
+ * glyf表
+ */
+
+define(
+ function(require) {
+ var table = require('./table');
+ var struct = require('./struct');
+ var ttfglyf = require('./ttfglyf');
+
+ var glyf = table.create(
+ 'glyf',
+ [],
+ {
+ /**
+ * 解析glyfl表
+ */
+ read: function(reader, ttf) {
+ var glyfOffset = this.offset;
+ var loca = ttf.loca;
+ var numGlyphs = ttf.maxp.numGlyphs;
+ var glyf = [];
+ var glyfDataList = {};
+ var glyfPath = new ttfglyf();
+
+ reader.seek(glyfOffset);
+
+ // 解析字体轮廓
+ for ( var i = 0, l = numGlyphs; i < l; i++) {
+ var offset = glyfOffset + loca[i];
+
+ //保存offset下的重复图形
+ if (undefined == glyfDataList[offset]) {
+ // 空路径
+ if(i + 1 < l && loca[i] === loca[i + 1]) {
+ glyfDataList[offset] = ttfglyf.empty();
+ }
+ else {
+ glyfPath.offset = offset;
+ glyfDataList[offset] = glyfPath.read(reader, ttf);
+ }
+ }
+ glyf[i] = glyfDataList[offset];
+ }
+ return glyf;
+ }
+ }
+ );
+
+ return glyf;
+ }
+);
\ No newline at end of file
diff --git a/src/ttf/table/head.js b/src/ttf/table/head.js
new file mode 100644
index 0000000..479a172
--- /dev/null
+++ b/src/ttf/table/head.js
@@ -0,0 +1,39 @@
+/**
+ * @file head.js
+ * @author mengke01
+ * @date
+ * @description
+ * head表
+ */
+
+
+define(
+ function(require) {
+ var table = require('./table');
+ var struct = require('./struct');
+ var head = table.create(
+ 'head',
+ [
+ ['version', struct.Fixed],
+ ['fontRevision', struct.Fixed],
+ ['checkSumAdjustment', struct.Uint32],
+ ['magickNumber', struct.Uint32],
+ ['flags', struct.Uint16],
+ ['unitsPerEm', struct.Uint16],
+ ['created', struct.LongDateTime],
+ ['modified', struct.LongDateTime],
+ ['xMin', struct.Int16],
+ ['yMin', struct.Int16],
+ ['xMax', struct.Int16],
+ ['yMax', struct.Int16],
+ ['macStyle', struct.Uint16],
+ ['lowestRecPPEM', struct.Uint16],
+ ['fontDirectionHint', struct.Int16],
+ ['indexToLocFormat', struct.Int16],
+ ['glyphDataFormat', struct.Int16]
+ ]
+ );
+
+ return head;
+ }
+);
\ No newline at end of file
diff --git a/src/ttf/table/hhea.js b/src/ttf/table/hhea.js
new file mode 100644
index 0000000..c1eb415
--- /dev/null
+++ b/src/ttf/table/hhea.js
@@ -0,0 +1,49 @@
+/**
+ * @file hhea.js
+ * @author mengke01
+ * @date
+ * @description
+ * hhea 表
+ *
+ * The 'hhea' table contains information needed to
+ * layout fonts whose characters are written
+ * horizontally, that is, either left
+ * to right or right to left.
+ * This table contains information that is general
+ * to the font as a whole. Information which
+ * pertains to specific glyphs is given in the
+ * 'hmtx' table defined below.
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6hhea.html
+ */
+
+
+define(
+ function(require) {
+ var table = require('./table');
+ var struct = require('./struct');
+ var hhea = table.create(
+ 'hhea',
+ [
+ ['version', struct.Fixed],
+ ['ascent', struct.Int16],
+ ['descent', struct.Int16],
+ ['lineGap', struct.Int16],
+ ['advanceWidthMax', struct.Uint16],
+ ['minLeftSideBearing', struct.Int16],
+ ['minRightSideBearing', struct.Int16],
+ ['xMaxExtent', struct.Int16],
+ ['caretSlopeRise', struct.Int16],
+ ['caretSlopeRun', struct.Int16],
+ ['caretOffset', struct.Int16],
+ ['reserved0', struct.Int16],
+ ['reserved1', struct.Int16],
+ ['reserved2', struct.Int16],
+ ['reserved3', struct.Int16],
+ ['metricDataFormat', struct.Int16],
+ ['numOfLongHorMetrics', struct.Uint16]
+ ]
+ );
+
+ return hhea;
+ }
+);
diff --git a/src/ttf/table/hmtx.js b/src/ttf/table/hmtx.js
new file mode 100644
index 0000000..d26ae66
--- /dev/null
+++ b/src/ttf/table/hmtx.js
@@ -0,0 +1,58 @@
+/**
+ * @file hmtx.js
+ * @author mengke01
+ * @date
+ * @description
+ * hmtx 表
+ *
+ * The 'hmtx' table contains metric information
+ * for the horizontal layout each of the glyphs in the font
+ *
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6hmtx.html
+ */
+
+
+define(
+ function(require) {
+ var table = require('./table');
+ var struct = require('./struct');
+ var hmtx = table.create(
+ 'hmtx',
+ [
+ ],
+ {
+ read: function(reader, ttf) {
+ var offset = this.offset;
+ reader.seek(offset);
+
+ var numOfLongHorMetrics = ttf.hhea.numOfLongHorMetrics;
+ var hMetrics = [];
+
+ for (var i = 0; i < numOfLongHorMetrics; ++i) {
+ var hMetric = {};
+ hMetric.advanceWidth = reader.readUint16();
+ hMetric.leftSideBearing = reader.readInt16();
+ hMetrics.push(hMetric);
+ }
+
+ // 最后一个宽度
+ var advanceWidth = hMetrics[numOfLongHorMetrics - 1].advanceWidth;
+ var numOfLast = ttf.maxp.numGlyphs - numOfLongHorMetrics;
+
+ // 获取后续的hmetrics
+ for (var i = 0; i < numOfLast; ++i) {
+ var hMetric = {};
+ hMetric.advanceWidth = advanceWidth;
+ hMetric.leftSideBearing = reader.readInt16();
+ hMetrics.push(hMetric);
+ }
+
+ return hMetrics;
+
+ }
+ }
+ );
+
+ return hmtx;
+ }
+);
diff --git a/src/ttf/table/loca.js b/src/ttf/table/loca.js
new file mode 100644
index 0000000..4e75eaa
--- /dev/null
+++ b/src/ttf/table/loca.js
@@ -0,0 +1,44 @@
+/**
+ * @file loca.js
+ * @author mengke01
+ * @date
+ * @description
+ * loca表
+ */
+
+define(
+ function(require) {
+ var table = require('./table');
+ var struct = require('./struct');
+ var loca = table.create(
+ 'loca',
+ [],
+ {
+ /**
+ * 解析local表
+ */
+ read: function(reader, ttf) {
+ var offset = this.offset;
+ var indexToLocFormat = ttf.head.indexToLocFormat;
+ // indexToLocFormat有2字节和4字节的区别
+ var type = struct.names[(indexToLocFormat === 0) ? struct.Uint16 : struct.Uint32];
+ var size = (indexToLocFormat === 0) ? 2 : 4; //字节大小
+ var sizeRatio = (indexToLocFormat === 0) ? 2 : 1; //真实地址偏移
+ var wordOffset = [];
+
+ reader.seek(offset);
+
+ var numGlyphs = ttf.maxp.numGlyphs;
+ for (var i = 0; i < numGlyphs; ++i) {
+ wordOffset.push(reader.read(type, offset, false) * sizeRatio);
+ offset += size;
+ }
+
+ return wordOffset;
+ }
+ }
+ );
+
+ return loca;
+ }
+);
\ No newline at end of file
diff --git a/src/ttf/table/maxp.js b/src/ttf/table/maxp.js
new file mode 100644
index 0000000..6e92fd8
--- /dev/null
+++ b/src/ttf/table/maxp.js
@@ -0,0 +1,36 @@
+/**
+ * @file maxp.js
+ * @author mengke01
+ * @date
+ * @description
+ * maxp 表
+ */
+
+define(
+ function(require) {
+ var table = require('./table');
+ var struct = require('./struct');
+ var maxp = table.create(
+ 'maxp',
+ [
+ ['version', struct.Fixed],
+ ['numGlyphs', struct.Uint16],
+ ['maxPoints', struct.Uint16],
+ ['maxCompositePoints', struct.Uint16],
+ ['maxCompositeContours', struct.Uint16],
+ ['maxZones', struct.Uint16],
+ ['maxTwilightPoints', struct.Uint16],
+ ['maxStorage', struct.Uint16],
+ ['maxFunctionDefs', struct.Uint16],
+ ['maxInstructionDefs', struct.Uint16],
+ ['maxStackElements', struct.Uint16],
+ ['maxSizeOfInstructions', struct.Uint16],
+ ['macStyle', struct.Uint16],
+ ['maxComponentElements', struct.Uint16],
+ ['maxComponentDepth', struct.Int16]
+ ]
+ );
+
+ return maxp;
+ }
+);
\ No newline at end of file
diff --git a/src/ttf/table/name.js b/src/ttf/table/name.js
new file mode 100644
index 0000000..6ff3448
--- /dev/null
+++ b/src/ttf/table/name.js
@@ -0,0 +1,56 @@
+/**
+ * @file name.js
+ * @author mengke01
+ * @date
+ * @description
+ * name表
+ */
+define(
+ function(require) {
+ var table = require('./table');
+ var struct = require('./struct');
+ var name = table.create(
+ 'name',
+ [], {
+
+ read: function(reader, ttf) {
+ var offset = this.offset;
+ reader.seek(offset);
+
+ var nameTbl = {};
+ nameTbl.format = reader.readUint16();
+ nameTbl.count = reader.readUint16();
+ nameTbl.stringOffset = reader.readUint16();
+
+ var nameRecordTbl = [];
+ var count = nameTbl.count;
+ for (var i = 0; i < count; ++i) {
+ var nameRecord = {};
+ nameRecord.platformID = reader.readUint16();
+ nameRecord.platformSpecificID = reader.readUint16();
+ nameRecord.languageID = reader.readUint16();
+ nameRecord.nameID = reader.readUint16();
+ nameRecord.length = reader.readUint16();
+ nameRecord.offset = reader.readUint16();
+ nameRecordTbl.push(nameRecord);
+ };
+
+
+ //nameTbl.name = reader.readString(reader.offset, nameTbl.stringOffset);
+
+ offset = offset + nameTbl.stringOffset;
+ // 读取字符名字
+ for (var i = 0; i < count; ++i) {
+ var nameRecord = nameRecordTbl[i];
+ nameRecord.name = reader.readString(offset + nameRecord.offset, nameRecord.length);
+ }
+
+ nameTbl.nameRecord = nameRecordTbl;
+ return nameTbl;
+ }
+ }
+ );
+
+ return name;
+ }
+);
\ No newline at end of file
diff --git a/src/ttf/table/post.js b/src/ttf/table/post.js
new file mode 100644
index 0000000..5392ac2
--- /dev/null
+++ b/src/ttf/table/post.js
@@ -0,0 +1,87 @@
+/**
+ * @file post.js
+ * @author mengke01
+ * @date
+ * @description
+ *
+ * post 表
+ *
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6post.html
+ */
+
+define(
+ function(require) {
+ var table = require('./table');
+ var struct = require('./struct');
+ var posthead = table.create(
+ 'posthead',
+ [
+ ['format', struct.Fixed],
+ ['italicAngle', struct.Fixed],
+ ['postoints', struct.Uint16],
+ ['underlinePosition', struct.Int16],
+ ['underlineThickness', struct.Int16],
+ ['isFixedPitch', struct.Uint32],
+ ['minMemType42', struct.Uint32],
+ ['maxMemType42', struct.Uint32],
+ ['minMemType1', struct.Uint32],
+ ['maxMemType1', struct.Uint32]
+ ]
+ );
+
+ /**
+ * 读取poststring
+ *
+ * @param {Array.} byteArray byte数组
+ * @return {Array.} 读取后的字符串数组
+ */
+ function readPascalString(byteArray) {
+ var strArray = [];
+ var i = 0;
+ var l = byteArray.length;
+ while(i < l) {
+ var strLength = byteArray[i];
+ var str = '';
+ while(strLength-- >= 0 && i < l) {
+ str += String.fromCharCode(byteArray[++i]);
+ }
+ strArray.push(str);
+ }
+ return strArray;
+ }
+
+ var post = table.create(
+ 'post',
+ [
+ ],
+ {
+ read: function(reader, ttf) {
+ // 读取表头
+ var tbl = new posthead(this.offset).read(reader, ttf);
+ var offset = reader.offset;
+
+ // format2
+ if(tbl.format == 2) {
+ var numberOfGlyphs = ttf.maxp.numGlyphs;
+
+ var glyphNameIndex = [];
+ for(var i = 0; i < numberOfGlyphs; ++i) {
+ glyphNameIndex.push(reader.readUint16());
+ }
+
+ tbl.glyphNameIndex = glyphNameIndex;
+
+ var pascalStringOffset = reader.offset;
+ var pascalStringLength = ttf.tables.post.length - (pascalStringOffset - this.offset);
+ var pascalStringBytes = reader.readBytes(reader.offset, pascalStringLength);
+ tbl.names = readPascalString(pascalStringBytes);
+ }
+
+ return tbl;
+ }
+ }
+ );
+
+ return post;
+ }
+);
\ No newline at end of file
diff --git a/src/ttf/table/struct.js b/src/ttf/table/struct.js
new file mode 100644
index 0000000..4265fe7
--- /dev/null
+++ b/src/ttf/table/struct.js
@@ -0,0 +1,43 @@
+/**
+ * @file struct.js
+ * @author mengke01
+ * @date
+ * @description
+ * 基本数据结构
+ */
+
+
+define(
+ function(require) {
+
+ var struct = {
+ Int8: 1,
+ Uint8: 2,
+ Int16: 3,
+ Uint16: 4,
+ Int32: 5,
+ Uint32: 6,
+ Fixed: 7, // 32-bit signed fixed-point number (16.16)
+ FUnit: 8, // Smallest measurable distance in the em space
+ // 16-bit signed integer (SHORT) that describes a quantity in FUnits.
+ //FWord: 9,
+ // Unsigned 16-bit integer (USHORT) that describes a quantity in FUnits
+ //UFWord: 10,
+ // 16-bit signed fixed number with the low 14 bits of fraction
+ F2Dot14: 11,
+ // The long internal format of a date in seconds since 12:00 midnight,
+ // January 1, 1904. It is represented as a signed 64-bit integer.
+ LongDateTime: 12
+ };
+
+ var names = {};
+
+ for(var key in struct) {
+ names[struct[key]] = key;
+ }
+
+ struct.names = names;
+
+ return struct;
+ }
+);
diff --git a/src/ttf/table/support.js b/src/ttf/table/support.js
new file mode 100644
index 0000000..ba6e8c0
--- /dev/null
+++ b/src/ttf/table/support.js
@@ -0,0 +1,28 @@
+/**
+ * @file support.js
+ * @author mengke01
+ * @date
+ * @description
+ * 列举支持的表
+ */
+
+
+define(
+ function(require) {
+
+ var support = {
+ 'head': require('./head'),
+ 'maxp': require('./maxp'),
+ 'loca': require('./loca'),
+ 'glyf': require('./glyf'),
+ 'cmap': require('./cmap'),
+ 'name': require('./name'),
+ 'gasp': require('./gasp'),
+ 'hhea': require('./hhea'),
+ 'hmtx': require('./hhea'),
+ 'post': require('./post')
+ };
+
+ return support;
+ }
+);
diff --git a/src/ttf/table/table.js b/src/ttf/table/table.js
new file mode 100644
index 0000000..f4a2d0d
--- /dev/null
+++ b/src/ttf/table/table.js
@@ -0,0 +1,104 @@
+/**
+ * @file table.js
+ * @author mengke01
+ * @date
+ * @description
+ * ttf表操作类
+ */
+
+
+define(
+ function(require) {
+ var struct = require('./struct');
+ var extend = require('common/lang').extend;
+
+
+ /**
+ * 读取一个表结构
+ *
+ * @param {Object} reader reader对象
+ * @param {Object} reader 已解析的ttf对象
+ * @return {Object} 当前对象
+ */
+ function read(reader, ttf) {
+ var offset = this.offset;
+
+ if(undefined !== offset) {
+ reader.seek(offset);
+ }
+
+ var me = this;
+
+ this.struct.forEach(function(item){
+ var name = item[0];
+ var type = item[1];
+
+ switch (type) {
+ case struct.Int8:
+ case struct.Uint8:
+ case struct.Int16:
+ case struct.Uint16:
+ case struct.Int32:
+ case struct.Uint32:
+ var typeName = struct.names[type];
+ me[name] = reader.read(typeName);
+ break;
+ case struct.Fixed:
+ me[name] = reader.readFixed();
+ break;
+ case struct.LongDateTime:
+ me[name] = reader.readLongDateTime();
+ break;
+ default:
+ throw 'unknown type:' + name + ':' + type;
+ }
+ });
+
+ return this.valueOf();
+ }
+
+
+ /**
+ * 获取对象的值
+ *
+ * @return {*} 当前对象的值
+ */
+ function valueOf() {
+ var val = {};
+ var me = this;
+ this.struct.forEach(function(item){
+ val[item[0]] = me[item[0]];
+ });
+
+ return val;
+ }
+
+ var exports = {
+
+ /**
+ * 创建一个表结构
+ *
+ * @param {string} name 表名
+ * @param {Object} struct 表结构
+ * @param {Object} prototype 原型
+ * @return {Function} 表构造函数
+ */
+ create: function(name, struct, prototype) {
+ function Table(offset) {
+ this._name = name;
+ this.struct = struct;
+ this.offset = offset;
+ }
+
+ Table.prototype.read = read;
+ Table.prototype.valueOf = valueOf;
+
+ extend(Table.prototype, prototype);
+
+ return Table;
+ }
+ };
+
+ return exports;
+ }
+);
diff --git a/src/ttf/table/ttfglyf.js b/src/ttf/table/ttfglyf.js
new file mode 100644
index 0000000..730f162
--- /dev/null
+++ b/src/ttf/table/ttfglyf.js
@@ -0,0 +1,195 @@
+/**
+ * @file ttfglyf.js
+ * @author mengke01
+ * @date
+ * @description
+ *
+ * ttf的glyf轮廓,用来解析单个路径
+ *
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html
+ */
+
+
+define(
+ function(require) {
+
+ var table = require('./table');
+
+ function readSimpleGlyf(reader, ttf, offset, val) {
+
+ reader.seek(offset);
+
+ // 轮廓个数
+ var contours = val.endPtsOfContours[
+ val.endPtsOfContours.length - 1
+ ] + 1;
+
+ val.contours = contours;
+
+ // 获取flag标志
+ var i = 0;
+ while (i < contours) {
+ var flag = reader.readUint8();
+ val.flags.push(flag);
+ i++;
+
+ // 标志位3表示重复flag
+ // If set, the next byte specifies the number of additional
+ // times this set of flags is to be repeated. In this way,
+ // the number of flags listed can be smaller than the
+ // number of points in a character
+ //
+ if (flag & 8 && i < contours) {
+ // 重复个数
+ var repeat = reader.readUint8();
+ for ( var j = 0; j < repeat; j++) {
+ val.flags.push(flag);
+ i++;
+ }
+ }
+ }
+
+ // xCoordinates
+ val.xCoorinateOffset = reader.offset;
+ var prevX = 0;
+ for (var i = 0, l = val.flags.length; i < l; ++i) {
+ var x = 0;
+ var flag = val.flags[i];
+
+ //标志位1
+ // If set, the corresponding y-coordinate is 1 byte long, not 2
+ if (flag & 2) {
+ x = reader.readUint8();
+ offset += 1;
+
+ //标志位5, 是否负值
+ x = (flag & 16) ? x : -1 * x;
+ }
+
+ //标志位5,空值
+ // This flag has two meanings, depending on how the x-Short Vector flag is set. If x-Short Vector is set, this
+ // bit describes the sign of the value, with 1 equalling
+ // positive and 0 negative. If the x-Short Vector bit is
+ // not set and this bit is set, then the current x-coordinate is the same as the previous x-coordinate.
+ // If the x-Short Vector bit is not set and this bit is also
+ // not set, the current x-coordinate is a signed 16-bit
+ // delta vector
+ else if (flag & 16) {
+ x = 0;
+ }
+
+ else {
+ x = reader.readInt16();
+ }
+
+ prevX += x;
+ val.xCoordinates[i] = prevX;
+ val.coordinates[i] = {
+ x : prevX,
+ y : 0,
+ isOnCurve : Boolean(flag & 1)
+ };
+ }
+
+ // yCoordinates
+ val.yCoorinateOffset = reader.offset;
+ var prevY = 0;
+ for ( var i = 0, l = val.flags.length; i < l; i++) {
+ var y = 0;
+ var flag = val.flags[i];
+
+ if (flag & 4) {
+ y = reader.readUint8();
+ y = (flag & 32) ? y : -1 * y;
+ }
+
+ else if (flag & 32) {
+ y = 0;
+ }
+
+ else {
+ y = reader.readInt16();
+ }
+
+ prevY += y;
+ val.yCoordinates[i] = prevY;
+ if (val.coordinates[i]) {
+ val.coordinates[i].y = prevY;
+ }
+ }
+ }
+
+ var ttfglyf = table.create(
+ 'ttfglyf',
+ [],
+ {
+ /**
+ * 解析ttfglyfl表
+ */
+ read: function(reader, ttf) {
+ var offset = this.offset;
+ var val = ttfglyf.empty();
+
+ reader.seek(offset);
+
+ // 边界值
+ val.numberOfContours = reader.readInt16();
+ val.xMin = reader.readInt16();
+ val.yMin = reader.readInt16();
+ val.xMax = reader.readInt16();
+ val.yMax = reader.readInt16();
+
+ // endPtsOfConturs
+ var endPtsOfContours = [];
+ if (val.numberOfContours >= 0) {
+ for ( var i = 0; i < val.numberOfContours; i++) {
+ endPtsOfContours.push(reader.readUint16());
+ }
+ val.endPtsOfContours = endPtsOfContours;
+ }
+
+ // instructions
+ var length = reader.readUint16();
+ var instructions = [];
+ for ( var i = 0; i < length; ++i) {
+ instructions.push(reader.readUint8());
+ }
+
+ val.instructions = instructions;
+
+ // 读取简单字形
+ if (val.numberOfContours >= 0) {
+ readSimpleGlyf.call(
+ this,
+ reader,
+ ttf,
+ reader.offset,
+ val
+ );
+ }
+ else {
+ // 读取复杂字形
+ throw 'not support Compound glyf';
+ }
+
+ return val;
+ }
+ }
+ );
+
+ // 空路径
+ ttfglyf.empty = function() {
+ var val = {};
+ val.flags = [];
+ val.contours = 0;
+ val.coordinates = [];
+ val.xCoorinateOffset = 0; // x偏移
+ val.xCoordinates = []; //x 坐标集合
+ val.yCoorinateOffset = 0; // y偏移
+ val.yCoordinates = []; // y坐标集合
+ return val;
+ };
+
+ return ttfglyf;
+ }
+);
diff --git a/src/ttf/ttf.js b/src/ttf/ttf.js
new file mode 100644
index 0000000..890236c
--- /dev/null
+++ b/src/ttf/ttf.js
@@ -0,0 +1,35 @@
+/**
+ * @file ttf.js
+ * @author mengke01
+ * @date
+ * @description
+ *
+ * ttf 信息读取函数
+ */
+
+
+define(
+ function(require) {
+
+ /**
+ * ttf读取函数
+ *
+ * @constructor
+ * @param {Object} ttf ttf文件结构
+ */
+ function TTF(ttf) {
+ this.ttf = ttf;
+ }
+
+ /**
+ * 获取所有的字符信息
+ *
+ * @return {Object} 字符信息
+ */
+ TTF.prototype.chars = function() {
+
+ };
+
+ return ttf;
+ }
+);
diff --git a/src/ttf/ttfreader.js b/src/ttf/ttfreader.js
new file mode 100644
index 0000000..b412d3e
--- /dev/null
+++ b/src/ttf/ttfreader.js
@@ -0,0 +1,104 @@
+/**
+ * @file ttfreader.js
+ * @author mengke01
+ * @date
+ * @description
+ * ttf定义
+ *
+ * thanks to:
+ * ynakajima/ttf.js
+ * https://github.com/ynakajima/ttf.js
+ */
+
+
+define(
+ function(require) {
+
+ var supportTables = require('./table/support');
+ var reader = require('./reader');
+
+
+ /**
+ * 读取ttf表偏移量
+ *
+ */
+ function readTableOffset() {
+ var tables = {};
+ var numTables = this.ttf.numTables;
+ var offset = this.reader.offset;
+ var reader = this.reader;
+ for (var i = offset, l = numTables * 16; i < l; i += 16) {
+
+ var name = reader.readString(i, 4);
+ var checkSum = reader.readUint32(i + 4);
+ var tblOffset = reader.readUint32(i + 8);
+ var length = reader.readUint32(i + 12);
+
+ tables[name] = {
+ name : name,
+ checkSum : checkSum,
+ offset : tblOffset,
+ length : length,
+ };
+ }
+
+ return tables;
+ }
+
+
+ /**
+ * 初始化
+ */
+ function init() {
+ var reader = this.reader;
+ var ttf = this.ttf;
+
+ // version
+ ttf.version = reader.readFixed(0);
+
+ // num tables
+ ttf.numTables = reader.readUint16();
+
+ // searchRenge
+ ttf.searchRenge = reader.readUint16();
+
+ // entrySelector
+ ttf.entrySelector = reader.readUint16();
+
+ // rengeShift
+ ttf.rengeShift = reader.readUint16();
+
+ ttf.tables = readTableOffset.call(this);
+
+ // 读取支持的表数据
+ Object.keys(supportTables).forEach(function(tableName) {
+ console.log(tableName);
+ var offset = ttf.tables[tableName].offset;
+ ttf[tableName] = new supportTables[tableName](offset).read(reader, ttf);
+ });
+ }
+
+ /**
+ * TTF的构造函数
+ *
+ * @constructor
+ */
+ function TTFReader() {
+ }
+
+ /**
+ * 获取解析后的ttf文档
+ *
+ * @return {Object} ttf文档
+ */
+ TTFReader.prototype.read = function(buffer) {
+ this.reader = new reader(buffer, 0, buffer.byteLength, false);
+ this.ttf = {};
+ init.call(this);
+ console.log(this.ttf);
+ return this.ttf;
+ };
+
+ return TTFReader;
+ }
+);