add woff2 support

use es6 module
This commit is contained in:
kekee000 2019-10-22 02:39:52 +08:00
parent b855db23ee
commit b2f3fe5685
27 changed files with 219 additions and 472 deletions

View File

@ -1,54 +0,0 @@
/**
* @file 单独编译css文件
* @author mengke01(kekee000@gmail.com)
*/
var path = require('path');
var fs = require('fs');
var less = require('less');
var BASE_DIR = process.cwd();
// 默认的文件列表
var DEFAULT_FILES = [
'css/main.less',
'css/preview.less'
];
main(process.argv.slice(2));
function main(fileList) {
var list = fileList.length ? fileList : DEFAULT_FILES;
list.forEach(function (filePath) {
filePath = path.resolve(BASE_DIR, filePath);
less2css(filePath);
});
}
function compileLess(code, options, onsuccess) {
less.render(code, options).then(function (output) {
onsuccess(output.css);
}, function (error) {
throw error;
});
}
function less2css(filePath) {
if (!fs.existsSync(filePath)) {
throw new Error('file not exist:' + filePath);
}
var paths = [
__dirname
];
var options = {
relativeUrls: true,
compress: true,
paths: paths,
filename: filePath
};
compileLess(fs.readFileSync(filePath, 'utf-8'), options, function (code) {
fs.writeFileSync(filePath.replace(/\.less$/, '.css'), code);
console.log('build less success:' + filePath);
});
}

View File

@ -25,8 +25,9 @@ function format(source, data) {
module.exports = function main(tpl) {
let language = String(this.resource).match(/\?en-us/) ? 'en-us' : 'zh-cn';
let i18n = {};
i18n.lang = require('./i18n.zh-cn');
i18n.lang = require('./i18n.' + language);
let fileContent = format(tpl, i18n);
return 'module.exports=' + JSON.stringify(fileContent);
};

View File

@ -9,7 +9,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/fonteditor/index',
// editor: './src/fonteditor/editor'
editor: './src/fonteditor/editor'
},
output: {
path: path.resolve(__dirname, '../dist'),
@ -22,27 +22,42 @@ module.exports = {
resolve: {
extensions: ['.js', '.jsx', '.json'],
alias: {
'css': path.resolve(__dirname, '../css'),
'common': path.resolve(__dirname, '../src/common'),
'graphics': path.resolve(__dirname, '../src/graphics'),
'math': path.resolve(__dirname, '../src/math'),
'editor': path.resolve(__dirname, '../src/editor'),
'render': path.resolve(__dirname, '../src/render'),
'fonteditor-core': path.resolve(__dirname, '../node_modules/fonteditor-core/src'),
'JSZip': path.resolve(__dirname, '../dep/jszip')
'JSZip$': path.resolve(__dirname, '../dep/jszip/jszip.min.js'),
'inflate$': path.resolve(__dirname, '../dep/pako_inflate.min.js'),
'deflate$': path.resolve(__dirname, '../dep/pako_deflate.min.js'),
'paper$': path.resolve(__dirname, '../dep/paper-full.js'),
'utpl$': path.resolve(__dirname, '../dep/utpl.min.js')
}
},
externals: {
jquery: 'window.jQuery',
$: 'window.jQuery',
utpl: 'window.utpl',
paper: 'window.paper'
$: 'window.jQuery'
},
plugins: [
new HtmlWebpackPlugin({
title: 'index',
filename: 'index.html',
template: path.resolve(__dirname, '../index.tpl'),
template: path.resolve(__dirname, '../index.tpl?zh-cn'),
chunks: ['index']
}),
new HtmlWebpackPlugin({
title: 'index',
filename: 'index-en.html',
template: path.resolve(__dirname, '../index.tpl?en-us'),
chunks: ['index']
}),
new HtmlWebpackPlugin({
title: 'editor',
filename: 'editor.html',
template: path.resolve(__dirname, '../editor.tpl'),
chunks: ['editor']
})
],
module: {
@ -56,22 +71,36 @@ module.exports = {
loader: 'tpl-loader'
},
{
test: /\.css$/,
test: /\.lesstpl$/,
use: [
'style-loader',
'css-loader'
'css-loader',
'less-loader'
]
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
{
loader: 'url-loader',
options: {
limit: 1000
}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf|wasm)$/,
use: [
'file-loader'
{
loader: 'url-loader',
options: {
limit: 1000
}
}
]
}
]

122
config/webpack.prod.js Normal file
View File

@ -0,0 +1,122 @@
/**
* @file webpack 配置
* @author mengke01(kekee000@gmail.com)
*/
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
entry: {
index: './src/fonteditor/index',
editor: './src/fonteditor/editor'
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'js/[name].js'
},
devtool: false,
devServer: {
contentBase: './'
},
resolve: {
extensions: ['.js', '.jsx', '.json'],
alias: {
'css': path.resolve(__dirname, '../css'),
'common': path.resolve(__dirname, '../src/common'),
'graphics': path.resolve(__dirname, '../src/graphics'),
'math': path.resolve(__dirname, '../src/math'),
'editor': path.resolve(__dirname, '../src/editor'),
'render': path.resolve(__dirname, '../src/render'),
'fonteditor-core': path.resolve(__dirname, '../node_modules/fonteditor-core/src'),
'JSZip$': path.resolve(__dirname, '../dep/jszip/jszip.min.js'),
'inflate$': path.resolve(__dirname, '../dep/pako_inflate.min.js'),
'deflate$': path.resolve(__dirname, '../dep/pako_deflate.min.js'),
'paper$': path.resolve(__dirname, '../dep/paper-full.js'),
'utpl$': path.resolve(__dirname, '../dep/utpl.min.js')
}
},
externals: {
jquery: 'window.jQuery',
$: 'window.jQuery'
},
plugins: [
new HtmlWebpackPlugin({
title: 'index',
filename: 'index.html',
template: path.resolve(__dirname, '../index.tpl?zh-cn'),
chunks: ['index'],
language: 'zh-ch'
}),
new HtmlWebpackPlugin({
title: 'index',
filename: 'index-en.html',
template: path.resolve(__dirname, '../index.tpl?en-us'),
chunks: ['index'],
language: 'en-us'
}),
new HtmlWebpackPlugin({
title: 'editor',
filename: 'editor.html',
template: path.resolve(__dirname, '../editor.tpl'),
chunks: ['editor']
}),
new MiniCssPlugin({
filename: 'css/[name]-[chunkhash:8].css',
chunkFilename: 'css/[id]-[chunkhash:8].css'
}),
new OptimizeCssAssetsPlugin()
],
module: {
rules: [
{
test: /\/(index|empty|editor)\.tpl$/,
loader: 'index-loader'
},
{
test: /template\/(.+?)\.tpl$/,
loader: 'tpl-loader'
},
{
test: /\.lesstpl$/,
use: [
'css-loader',
'less-loader'
]
},
{
test: /\.less$/,
use: [MiniCssPlugin.loader, 'css-loader', 'less-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 15000,
name: 'css/[name]-[hash:8].[ext]'
}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf|wasm)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 15000,
name: 'css/[name]-[hash:8].[ext]'
}
}
]
}
]
},
resolveLoader: {
modules: [path.resolve(__dirname, '../build'), 'node_modules']
}
};

View File

@ -1,333 +0,0 @@
html,
body {
height: 100%;
}
/**
* @file 重置样式
* @author mengke01(kekee000@gmail.com)
*/
* {
margin: 0;
padding: 0;
list-style: none;
}
blockquote,
body,
button,
dd,
dl,
dt,
fieldset,
form,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
input,
legend,
li,
ol,
p,
pre,
td,
textarea,
th,
ul {
margin: 0;
padding: 0;
}
body,
button,
input,
select,
textarea {
font: 12px/1.5 tahoma, arial, sans-serif;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: 100%;
}
address,
cite,
dfn,
em,
var {
font-style: normal;
}
code,
kbd,
pre,
samp {
font-family: courier new, courier, monospace;
}
small {
font-size: 12px;
}
ol,
ul {
list-style: none;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
legend {
color: #000;
}
fieldset,
img {
border: 0;
}
button,
input,
select,
textarea {
font-size: 100%;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
@font-face {
font-family: 'fonteditor';
src: url('../../font/fonteditor.ttf') format('truetype');
}
.ico {
display: inline-block;
}
.ico:before,
.ico:after {
font-family: 'fonteditor';
font-style: normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.1px;
}
.i-leave:before {
content: '\e00c';
}
.i-edit,
.i-del,
.i-leave {
font-size: 12px;
}
.i-edit:hover,
.i-del:hover,
.i-leave:hover {
cursor: pointer;
color: #4A90E2;
}
.i-edit,
.i-del {
color: #76abe9;
}
.i-edit:before {
content: '\e021';
}
.i-del:before {
content: '\e020';
}
.i-github:before {
content: '\e01e';
}
.i-help:before {
content: '\e016';
}
.i-new:before {
content: '\e019';
}
.i-open:before {
content: '\e01A';
}
.i-add:before {
content: '\e003';
}
.i-undo:before {
content: '\e001';
}
.i-redo:before {
content: '\e002';
}
.i-down:before {
content: '\e00e';
}
.i-left:before {
content: '\e00a';
}
.i-ttf:before {
content: '\e00f';
}
.i-woff:before {
content: '\e010';
}
.i-zip:before {
content: '\e011';
}
.i-save:before {
content: '\e022';
}
.i-upshape:before {
content: '\e014';
}
.i-downshape:before {
content: '\e00b';
}
.i-reversepoints:before {
content: '\e00d';
}
.i-alignleft:before {
content: '\e006';
}
.i-aligncenter:before {
content: '\e004';
}
.i-alignright:before {
content: '\e007';
}
.i-aligntop:before {
content: '\e008';
}
.i-alignmiddle:before {
content: '\e005';
}
.i-aligndescent:before {
content: '\e009';
}
.i-alignbaseline:before {
content: '\e009';
}
.i-rotateleft:before {
content: '\e01c';
}
.i-rotateright:before {
content: '\e01d';
}
.i-flip:before {
content: '\e013';
}
.i-mirror:before {
content: '\e012';
}
.i-splitshapes:before {
content: '\e024';
}
.i-joinshapes:before {
content: '\e025';
}
.i-intersectshapes:before {
content: '\e026';
}
.i-tangencyshapes:before {
content: '\e027';
}
.i-rangemode:before {
content: '\e029';
}
.i-pointmode:before {
content: '\e028';
}
.glyf-editor {
width: 100%;
height: 100%;
font-size: 12px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
display: none;
}
.glyf-editor .marker-x,
.glyf-editor .marker-y {
position: absolute;
width: 0;
height: 0;
z-index: 60;
pointer-events: none;
}
.glyf-editor .marker-x {
width: 20px;
border-top: 1px dashed #000;
}
.glyf-editor .marker-y {
height: 20px;
border-left: 1px dashed #000;
}
.editor-contextmenu {
color: #333;
width: 150px;
border: 1px solid #999;
background: #FEFEFE;
line-height: 24px;
padding: 0 4px;
box-shadow: 1px 1px 1px #CCC;
position: absolute;
}
.editor-contextmenu li {
padding-left: 4px;
cursor: pointer;
border-bottom: 1px solid #CCC;
}
.editor-contextmenu li > ul {
display: none;
color: #333;
width: 150px;
border: 1px solid #999;
background: #FEFEFE;
line-height: 24px;
padding: 0 4px;
box-shadow: 1px 1px 1px #CCC;
position: absolute;
margin-left: 130px;
}
.editor-contextmenu > li[data-sub] {
color: #4A90E2;
}
.editor-contextmenu > li[data-sub]:after {
content: '>';
margin-right: 10px;
float: right;
font-family: 'Simsun';
}
.editor-contextmenu li[data-tag="selected"] {
display: inline-block;
display: block;
}
.editor-contextmenu li[data-tag="selected"]:before,
.editor-contextmenu li[data-tag="selected"]:after {
font-family: 'fonteditor';
font-style: normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.1px;
}
.editor-contextmenu li[data-tag="selected"]:after {
content: '\e01f';
}
.editor-contextmenu li[data-tag="selected"]:after {
float: right;
margin-right: 4px;
color: #4A90E2;
}
.editor-contextmenu li:hover {
background: #EEE;
}
.editor-contextmenu li:hover > ul {
display: block;
}
.editor-contextmenu li:last-child {
border-bottom: none;
}
.editor-panel {
width: 100%;
height: 100%;
font-size: 12px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
*{margin:0;padding:0;list-style:none}blockquote,body,button,dd,dl,dt,fieldset,form,h1,h2,h3,h4,h5,h6,hr,input,legend,li,ol,p,pre,td,textarea,th,ul{margin:0;padding:0}body,button,input,select,textarea{font:12px/1.5 tahoma,arial,sans-serif}h1,h2,h3,h4,h5,h6{font-size:100%}address,cite,dfn,em,var{font-style:normal}code,kbd,pre,samp{font-family:courier new,courier,monospace}small{font-size:12px}ol,ul{list-style:none}a{text-decoration:none}a:hover{text-decoration:underline}legend{color:#000}fieldset,img{border:0}button,input,select,textarea{font-size:100%}table{border-collapse:collapse;border-spacing:0}.main{padding:30px 100px}.main h1{font-size:36px;color:#333;text-align:left;margin-bottom:30px;border-bottom:1px solid #eee}.helps{margin-top:40px}.helps pre{padding:20px;margin:10px 0;border:solid 1px #e7e1cd;background-color:#fffdef;overflow:auto}.iconfont-list{overflow:hidden}.iconfont-list li{float:left;width:100px;height:150px;text-align:center}.iconfont-list .icon{font-size:42px;line-height:100px;margin:10px 0;color:#333;font-style:normal;-webkit-transition:font-size .25s ease-out 0s;-moz-transition:font-size .25s ease-out 0s;transition:font-size .25s ease-out 0s}.iconfont-list .icon:hover{font-size:100px}.iconfont-list .code{color:green;font-weight:bold}

View File

@ -195,3 +195,7 @@
.i-pointmode {
.i-ico('\e028');
}
.i-woff2 {
.i-ico('\e030');
}

View File

@ -15,7 +15,7 @@
float: left;
width: 180px;
height: 50px;
background: url(../img/logo.png) no-repeat;
background: url(../img/logo@1x.png) no-repeat;
background-color: #fff;
background-position: center;
background-image: url(../img/logo@1x.png);

View File

@ -1,7 +1,4 @@
/**
* @file 重置样式
* @author mengke01(kekee000@gmail.com)
*/
// 重置样式
* {
margin:0;

View File

@ -1,7 +1,3 @@
/**
* @file 页面相关工具
* @author mengke01(kekee000@gmail.com)
*/
.ellipsis() {
overflow: hidden;

View File

@ -1,33 +0,0 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>Editor</title>
<link rel="shortcut icon" href="http://www.baidu.com/favicon.ico" type="image/x-icon">
<link rel="stylesheet" type="text/css" href="./css/editor.css">
</head>
<body>
<div id="editor-panel" class="editor-panel" oncontextMenu="return false"></div>
<script src="./dep/esl.js"></script>
<script src="./dep/jquery.min.js"></script>
<script src="./dep/paper-full.js"></script>
<script src="./dep/hidpi-canvas.js"></script>
<script>
window.language = 'zh-cn';
require.config({
baseUrl: './src',
packages: [
{
name: 'fonteditor-core',
location: '../dep/fonteditor-core/src'
}
]
});
define('jquery', window.jQuery);
paper.install(window);
define('paper', window.paper);
require(['fonteditor/editor']);
</script>
</body>
</html>

17
editor.tpl Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>Editor</title>
<link rel="shortcut icon" href="http://www.baidu.com/favicon.ico" type="image/x-icon">
</head>
<body>
<div id="editor-panel" class="editor-panel" oncontextMenu="return false"></div>
<script src="./dep/jquery.min.js"></script>
<script src="./dep/hidpi-canvas.js"></script>
<script>
window.language = 'zh-cn';
</script>
</body>
</html>

View File

@ -5,7 +5,6 @@
<title>FontEditor</title>
<link rel="shortcut icon" href="http://www.baidu.com/favicon.ico" type="image/x-icon">
<link rel="stylesheet" type="text/css" href="./dep/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="./css/main.css">
</head>
<body>
@ -150,12 +149,7 @@
<script src="./dep/jquery.min.js"></script>
<script src="./dep/jqColorPicker.min.js"></script>
<script src="./dep/bootstrap/js/bootstrap.min.js"></script>
<script src="./dep/paper-full.js"></script>
<script src="./dep/hidpi-canvas.js"></script>
<script src="./dep/jszip/jszip.min.js"></script>
<script src="./dep/utpl.min.js"></script>
<script src="./dep/pako_inflate.min.js"></script>
<script src="./dep/pako_deflate.min.js"></script>
<script>
window.language = '${lang.lang}';
</script>

View File

@ -19,11 +19,18 @@
"build": "webpack --production --config ./config/webpack.prod.js"
},
"devDependencies": {
"css-loader": "^3.2.0",
"file-loader": "^4.2.0",
"fonteditor-core": "^2.0.3",
"html-webpack-plugin": "^3.2.0",
"less": "~2.0.0",
"jquery": "^3.4.1",
"less": "^2.0.0",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.8.0",
"q": "^1.2.0",
"style-loader": "^1.0.0",
"to-string-loader": "^1.1.5",
"url-loader": "^2.2.0",
"webpack": "^4.41.1",
"webpack-cli": "^3.3.9",
"webpack-dev-server": "^3.8.2"

View File

@ -12,7 +12,7 @@ import pathsUtil from 'graphics/pathsUtil';
* @param {number} angle 弧度
* @return {boolean} `false`或者`undefined`
*/
function rotate(shapes, angle) {
function rotateShapes(shapes, angle) {
if (!angle) {
return false;
}
@ -24,7 +24,7 @@ function rotate(shapes, angle) {
}
export default {
const transform = {
/**
* 旋转指定角度
@ -39,7 +39,7 @@ export default {
return false;
}
let ret = rotate(shapes, angle);
let ret = rotateShapes(shapes, angle);
if (false !== ret) {
this.fontLayer.refresh();
this.refreshSelected(shapes);
@ -55,7 +55,7 @@ export default {
*/
rotateleft(shapes) {
shapes = shapes || (this.currentGroup && this.currentGroup.shapes);
return this.rotate.call(this, shapes, -Math.PI / 2);
return transform.rotate.call(this, shapes, -Math.PI / 2);
},
/**
@ -66,7 +66,7 @@ export default {
*/
rotateright(shapes) {
shapes = shapes || (this.currentGroup && this.currentGroup.shapes);
return this.rotate.call(this, shapes, Math.PI / 2);
return transform.rotate.call(this, shapes, Math.PI / 2);
},
/**
@ -107,3 +107,5 @@ export default {
this.refreshSelected(shapes);
}
};
export default transform;

View File

@ -2,6 +2,7 @@
* @file 编辑器导出接口用于编辑字形相关的矢量图形
* @author mengke01(kekee000@gmail.com)
*/
import 'css/editor.less';
import lang from 'common/lang';
import editor from '../editor/main';

View File

@ -3,6 +3,8 @@
* @author mengke01(kekee000@gmail.com)
*/
import 'css/main.less';
import i18n from './i18n/i18n';
import program from './widget/program';
import controller from './controller/default';
@ -44,7 +46,7 @@ function loadFiles(files) {
if (program.ttfManager.get()) {
let ext = file.name.slice(file.name.lastIndexOf('.') + 1).toLowerCase();
let reg = new RegExp('\.' + ext + '$', 'i');
let files = Array.prototype.slice.call(files).filter(function (f) {
files = Array.prototype.slice.call(files).filter(function (f) {
return reg.test(f.name);
});

View File

@ -4,17 +4,11 @@
*/
import utpl from 'utpl';
import previewTpl from 'css/preview.lesstpl';
let fontExampleRender = null; // 图标示例渲染器
let fontCssRender = null; // 图标css渲染器
let tplPreviewCss = null; // 预览css样式
let symbolExampleRender = null; // symbol渲染器
$.get('./css/preview.css', function (text) {
tplPreviewCss = text;
});
export default {
/**
@ -56,6 +50,6 @@ export default {
* @return {string} html片段
*/
renderPreviewCss() {
return tplPreviewCss;
return previewTpl.toString();
}
};

View File

@ -8,6 +8,9 @@ import observable from 'common/observable';
function init(spliter) {
const fireChange = lang.throttle(function (e) {
if (!e) {
return;
}
let offset = e.pageX - spliter.x;
let deltaX = e.pageX - spliter.deltaX;
spliter.deltaX = e.pageX;

View File

@ -8,8 +8,8 @@ import resolvettf from './util/resolvettf';
import config from 'fonteditor-core/ttf/data/default';
import exportRender from '../template/export-render';
import download from './util/download';
import JSZip from 'JSZip';
const JSZip = window.JSZip;
const font = core.Font;
const ttf2icon = core.ttf2icon;

View File

@ -9,7 +9,7 @@ import i18n from '../i18n/i18n';
import font from 'fonteditor-core/ttf/font';
import loading from './loading';
import program from './program';
const inflate = window.pako.inflate;
import inflate from 'inflate';
function svg2ttf(buffer) {
let options = program.setting.get('ie').import;

View File

@ -3,7 +3,7 @@
* @author mengke01(kekee000@gmail.com)
*/
import {makeLink} from 'graphics/pathUtil';
import pathUtil from 'graphics/pathUtil';
import vector from 'graphics/vector';
const getCos = vector.getCos;
@ -27,7 +27,7 @@ function dist(p0, p1) {
*/
export default function getBreakPoints(contour, scale) {
contour = makeLink(contour);
contour = pathUtil.makeLink(contour);
let farDist = THRESHOLD_FAR_DIST * scale;
let longDist = THRESHOLD_LONG_DIST * scale;

View File

@ -4,7 +4,7 @@
*/
import douglasPeuckerReducePoints from './douglasPeuckerReducePoints';
import {makeLink} from '../../pathUtil';
import pathUtil from '../../pathUtil';
import vector from '../../vector';
@ -21,7 +21,7 @@ import vector from '../../vector';
*/
export default function reducePoints(contour, firstIndex, lastIndex, scale, threshold) {
let points = douglasPeuckerReducePoints(contour, firstIndex, lastIndex, scale, threshold);
points = makeLink(points);
points = pathUtil.makeLink(points);
let start = points[0];
let tinyDist = 3 * scale;

View File

@ -7,7 +7,7 @@ import contours2svg from 'fonteditor-core/ttf/util/contours2svg';
import path2contours from 'fonteditor-core/ttf/svg/path2contours';
import pathUtil from './pathUtil';
import reducePath from './reducePath';
const paper = window.paper;
import paper from 'paper';
const RELATION = {
intersect: 0, // 相交

View File

@ -7,7 +7,7 @@ import path2contours from 'fonteditor-core/ttf/svg/path2contours';
import contours2svg from 'fonteditor-core/ttf/util/contours2svg';
import pathUtil from './pathUtil';
import reducePath from './reducePath';
const paper = window.paper;
import paper from 'paper';
function initPaper() {
if (!paper.project) {