增加glyf查看器

This commit is contained in:
mkwiser 2014-08-29 00:13:10 +08:00
parent 2c81e350fb
commit 9973d33c63
13 changed files with 548 additions and 10 deletions

65
demo/css/glyf.css Normal file
View File

@ -0,0 +1,65 @@
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
a {
color: #03C;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.hide {
display: none;
}
body {
font-size: 13px;
}
.upload-file {
margin: 20px;
}
.i-font {
font-family: 'truetype';
display: inline-block;
margin-right: 6px;
color: green;
}
.font-list {
height: 200px;
overflow: auto;
}
.font-list li {
width: 70px;
float: left;
padding: 10px;
border: 1px solid #EEE;
list-style: none;
cursor: pointer;
}
.font-list li:hover {
background: #E0E0E0;
}
.font-list li.selected {
background: #ECECEC;
}
.svg-view {
width: 500px;
height: 500px;
background: #F0F0F0;
}
.glyf {
width: 100%;
}
.glyf .path {
fill: green;
stroke: none;
stroke-width: 10px;
}
.boundary {
fill: none;
stroke-width: 1px;
stroke: black;
}

60
demo/css/glyf.less Normal file
View File

@ -0,0 +1,60 @@
@import './reset';
body {
font-size: 13px;
}
.upload-file {
margin: 20px;
}
.i-font {
font-family: 'truetype';
display: inline-block;
margin-right: 6px;
color: green;
}
.font-list {
height: 200px;
overflow: auto;
li {
width: 70px;
float: left;
padding: 10px;
border: 1px solid #EEE;
list-style: none;
cursor: pointer;
}
li:hover {
background: #E0E0E0;
}
li.selected {
background: #ECECEC;
}
}
.svg-view {
width: 500px;
height: 500px;
background: #F0F0F0;
}
.glyf {
width: 100%;
.path {
fill: green;
stroke: none;
stroke-width: 10px;
}
}
.boundary {
fill: none;
stroke-width: 1px;
stroke: black;
}

18
demo/css/reset.less Normal file
View File

@ -0,0 +1,18 @@
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
a {
color: #03C;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.hide {
display: none;
}

35
demo/glyf.html Normal file
View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>glyf查看</title>
<script src="http://s1.bdstatic.com/r/www/cache/ecom/esl/1-8-2/esl.js"></script>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="./css/glyf.css">
<style id="font-face"></style>
</head>
<body>
<script>
require.config({
baseUrl: './js',
paths: {
editor: '../../src',
}
});
define('jquery', $);
</script>
<form>
<input id="upload-file" type="file" class="upload-file">
</form>
<ul id="font-list" class="font-list"></ul>
<div id="svg-view" class="svg-view"></div>
<script>
require(['glyf']);
</script>
</body>
</html>

127
demo/js/glyf.js Normal file
View File

@ -0,0 +1,127 @@
/**
* @file glyf.js
* @author mengke01
* @date
* @description
* glyf 查看
*/
define(
function(require) {
var ttfreader = require('editor/ttf/ttfreader');
var TTF = require('editor/ttf/ttf');
var ttf2base64 = require('editor/ttf/ttf2base64');
var ajaxBinaryFile = require('editor/common/ajaxBinaryFile');
var glyf2svg = require('editor/ttf/glyf2svg');
var setFontface = require('./setFontface');
var ttf = null;
// 设置字体
function setFont(arrayBuffer) {
var base64 = ttf2base64(arrayBuffer);
setFontface('truetype', base64, 'font-face');
}
// 查看ttf glyf
function showTTFGlyf(ttfData) {
ttf = new TTF(ttfData);
var chars = ttf.chars();
var str = '';
// 获取unicode字符
chars.forEach(function(item) {
str += '<li data-code="'+ item +'">'
+ '<span class="i-font">'+ String.fromCharCode(item) +'</span>'
+ (item > 255 ? '\\u' + Number(item).toString(16) : item)
+'</li>';
});
$('#font-list').html(str);
$('#font-list li:nth-child(4)').click();
}
function showGlyf(charcode) {
var tpl = ''
+ '<svg class="glyf" viewBox="0 0 1200 2000">'
+ ' <g transform="translate(1000, 0) scale(-1, 1) rotate(180, 500, 500)">'
+ '<polyline class="boundary" points="" />'
+ '<path class="path" d="M 0,0" />'
+ '</g>'
+ '</svg>';
var svg = $(tpl);
var glyf = ttf.getCharGlyf(charcode);
var path = glyf2svg(glyf);
if (path) {
var scale = 1000 / ttf.ttf.head.unitsPerEm;
var boundary = ''
+ glyf.xMin + ',' + glyf.yMin + ' '
+ glyf.xMin + ',' + glyf.yMax + ' '
+ glyf.xMax + ',' + glyf.yMax + ' '
+ glyf.xMax + ',' + glyf.yMin + ' '
+ glyf.xMin + ',' + glyf.yMin + ' ';
svg.find(".boundary").attr('points', boundary).attr('transform', 'scale(' + scale + ',' + scale + ')');
svg.find(".path").attr('d', path).attr('transform', 'scale(' + scale + ',' + scale + ')');
}
$('#svg-view').html(svg);
}
function onUpFileChange(e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
var binaryData = e.target.result;
setFont(binaryData);
var ttfData = new ttfreader().read(binaryData);
showTTFGlyf(ttfData);
}
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) {
setFont(binaryData);
var ttfData = new ttfreader().read(binaryData);
showTTFGlyf(ttfData);
},
onError: function() {
console.error('error read file');
}
});
$('#font-list').delegate('li', 'click', function(e) {
$('#font-list li').removeClass('selected');
$(this).addClass('selected');
showGlyf(+$(this).attr('data-code'));
});
}
};
entry.init();
return entry;
}
);

31
demo/js/setFontface.js Normal file
View File

@ -0,0 +1,31 @@
/**
* @file setFontface.js
* @author mengke01
* @date
* @description
* 设置fontface
*/
define(
function(require) {
/**
* 设置fontface的ttf字体
* @param {name} name 字体名
* @param {string} ttfBase64 base64字体
* @param {string} styleId domId
*/
function setFontface(name, ttfBase64, styleId) {
var str = ''
+ '@font-face {'
+ 'font-family:\'' + name + '\';'
+ 'src:url(data:font/ttf;charset=utf-8;base64,'
+ ttfBase64
+ ') format(\'truetype\');'
+ '}';
document.getElementById(styleId).innerHTML = str;
}
return setFontface;
}
);

BIN
font/arial.ttf Normal file

Binary file not shown.

158
src/ttf/glyf2svg.js Normal file
View File

@ -0,0 +1,158 @@
/**
* @file glyf2svg.js
* @author mengke01
* @date
* @description
* glyf转换svg
*
* thanks to
* ynakajima/ttf.js
* https://github.com/ynakajima/ttf.js
*/
define(
function(require) {
/**
* glyf转换svg
*
* @param {Object} glyf 解析后的glyf结构
* @return {string} svg文本
*/
function glyf2svg(glyf) {
var pathArray = [];
var startPts = 0; // 起始点
var currentPts = 0; // 结束点
if(!glyf) {
return null;
}
// 处理glyf轮廓
for ( var i = 0, l = glyf.endPtsOfContours.length; i < l; i++) {
try {
// 处理glyf坐标
for ( var endPts = glyf.endPtsOfContours[i]; currentPts < endPts + 1; currentPts++) {
var path = "";
var currentPoint = glyf.coordinates[currentPts];
var prevPoint = (currentPts === startPts)
? glyf.coordinates[endPts]
: glyf.coordinates[currentPts - 1];
var nextPoint = (currentPts === endPts)
? glyf.coordinates[startPts]
: glyf.coordinates[currentPts + 1];
if (currentPoint == undefined) {
continue;
}
// 处理起始点
if (currentPts === startPts) {
if (currentPoint.isOnCurve) {
path += "M"
+ currentPoint.x
+ ","
+ currentPoint.y
+ " ";
}
// 起始点不在曲线上
else {
var midPoint = {
x : (prevPoint.x + currentPoint.x) / 2,
y : (prevPoint.y + currentPoint.y) / 2
};
path += "M"
+ midPoint.x
+ ","
+ midPoint.y
+ " Q"
+ currentPoint.x
+ ","
+ currentPoint.y
+ " ";
}
}
else {
// 直线
if (
currentPoint != undefined
&& currentPoint.isOnCurve
&& prevPoint != undefined
&& prevPoint.isOnCurve
) {
path += " L";
}
// 当前点不在曲线上
else if (
!currentPoint.isOnCurve
&& prevPoint != undefined
&& !prevPoint.isOnCurve
) {
var midPoint = {
x : (prevPoint.x + currentPoint.x) / 2,
y : (prevPoint.y + currentPoint.y) / 2
};
path += midPoint.x
+ ","
+ midPoint.y
+ " ";
}
// 当前坐标不在曲线上
else if (!currentPoint.isOnCurve) {
path += " Q";
}
// 当前坐标
path += currentPoint.x + "," + currentPoint.y + " ";
}
pathArray.push(path);
}
// 当前点不在曲线上
if (
!currentPoint.isOnCurve
&& glyf.coordinates[startPts] != undefined
) {
// 轮廓起始点在曲线上
if (glyf.coordinates[startPts].isOnCurve) {
pathArray.push(
glyf.coordinates[startPts].x
+ ","
+ glyf.coordinates[startPts].y
+ " "
);
}
else {
var midPoint = {
x : (currentPoint.x + glyf.coordinates[startPts].x) / 2,
y : (currentPoint.y + glyf.coordinates[startPts].y) / 2
};
pathArray.push(midPoint.x + "," + midPoint.y + " ");
}
}
// 结束轮廓
pathArray.push(" Z ");
// 处理下一个轮廓
startPts = glyf.endPtsOfContours[i] + 1;
} catch (e) {
throw e;
}
}
return pathArray.join(" ");
}
return glyf2svg;
}
);

View File

@ -4,13 +4,17 @@
* @date
* @description
* ttf读取器
*
* thanks to
* ynakajima/ttf.js
* https://github.com/ynakajima/ttf.js
*/
define(
function(require) {
var extend = require('common/lang').extend;
var curry = require('common/lang').curry;
var extend = require('../common/lang').extend;
var curry = require('../common/lang').curry;
// 检查数组支持情况
if(typeof ArrayBuffer === 'undefined' || typeof DataView === 'undefined') {

View File

@ -10,7 +10,7 @@
define(
function(require) {
var struct = require('./struct');
var extend = require('common/lang').extend;
var extend = require('../../common/lang').extend;
/**

View File

@ -24,8 +24,6 @@ define(
val.endPtsOfContours.length - 1
] + 1;
val.contours = contours;
// 获取flag标志
var i = 0;
while (i < contours) {
@ -181,6 +179,7 @@ define(
ttfglyf.empty = function() {
var val = {};
val.flags = [];
val.endPtsOfContours = [];
val.contours = 0;
val.coordinates = [];
val.xCoorinateOffset = 0; // x偏移

View File

@ -87,18 +87,28 @@ define(
* @return {Object} 字符信息
*/
TTF.prototype.chars = function() {
return this.ttf.chars;
return Object.keys(this.ttf.chars);
};
/**
* 获取字符的glyf信息
*
* @return {Object} 字符信息
* @return {?number} 返回glyf索引号
*/
TTF.prototype.getCharGlyfIndex = function(c) {
var charCode = typeof c == 'number' ? c : c.charCodeAt(0);
var glyfIndex = this.ttf.chars[charCode] || 0;
return glyfIndex;
};
/**
* 获取字符的glyf信息
*
* @return {?Object} 返回glyf对象
*/
TTF.prototype.getCharGlyf = function(c) {
var charCode = String.fromCharCode(c);
var glyfIndex = this.ttf.chars[charCode];
return glyfIndex == undefined ? null : this.ttf.glyf[glyfIndex];
var glyfIndex = this.getCharGlyfIndex(c);
return this.ttf.glyf[glyfIndex];
};
return TTF;

31
src/ttf/ttf2base64.js Normal file
View File

@ -0,0 +1,31 @@
/**
* @file ttf2base64.js
* @author mengke01
* @date
* @description
* ttf 二进制转base64编码
*/
define(
function(require) {
/**
* ttf 二进制转base64编码
*
* @param {Array} arrayBuffer ArrayBuffer对象
* @return {string} base64编码
*/
function ttf2base64(arrayBuffer) {
var length = arrayBuffer.byteLength || arrayBuffer.length;
var view = new DataView(arrayBuffer, 0, length);
var str = '';
for(var i = 0; i < length; i++) {
str += String.fromCharCode(view.getUint8(i, false));
}
return btoa(str);
}
return ttf2base64;
}
);