Current Dev State

This commit is contained in:
Tim Lorsbach
2025-06-23 20:13:54 +02:00
parent b4f9bb277d
commit ded50edaa2
22617 changed files with 4345095 additions and 174 deletions

160
static/js/ketcher2/node_modules/svg2ttf/CHANGELOG.md generated vendored Normal file
View File

@ -0,0 +1,160 @@
4.1.0 / 2017-06-24
------------------
- Added font subfamily name support, #57.
4.0.3 / 2017-05-27
------------------
- Script tags should be arranged alpabetically, #55.
4.0.2 / 2016-08-04
------------------
- Added option to customize version string.
4.0.1 / 2016-06-03
------------------
- Fix IE ligatures by explicitly adding the latin script to the script list, #47.
4.0.0 / 2016-03-08
------------------
- Deps update (lodash -> 4.+).
- Set HHEA lineGap to 0, #37 (second attempt :).
- Cleanup, testing.
3.0.0 / 2016-02-12
------------------
- Changed defaults to workaround IE bugs.
- Set HHEA lineGap to 0, #37.
- Set OS/2 fsType to 0, #45.
2.1.1 / 2015-12-22
------------------
- Maintenance release: deps bump (svgpath with bugfixes).
2.1.0 / 2015-10-28
------------------
- Fixed smoothness at the ends of interpolated cubic beziers.
2.0.2 / 2015-08-23
------------------
- Fixed parse empty SVG glyphs without `d` attribute.
2.0.1 / 2015-07-17
------------------
- Fix: TTF creation timestamp should not depende on timezone, thanks to @nfroidure.
2.0.0 / 2015-04-25
------------------
- Added ligatures support, big thanks to @sabberworm.
- Added arcs support in SVG paths.
- Added `--ts` option to override default timestamp.
- Fixed horisontal offset (`lsb`) when glyph exceed width.
- Allow zero-width glyphs.
- Better error message on attempt to convert SVG image instead of SVG font.
1.2.0 / 2014-10-05
------------------
- Fixed usWinAscent/usWinDescent - should not go below ascent/descent.
- Upgraded ByteBuffer internal lib.
- Code cleanup.
1.1.2 / 2013-12-02
------------------
- Fixed crash on SVG with empty <metadata> (#8)
- Fixed descent when input font has descent = 0 (@nfroidure)
1.1.1 / 2013-09-26
------------------
- SVG parser moved to external package
- Speed opts
- Code refactoring/cleanup
1.1.0 / 2013-09-25
------------------
- Rewritten svg parser to improve speed
- API changed, now returns buffer as array/Uint8Array
1.0.7 / 2013-09-22
------------------
- Improved speed x2.5 times
1.0.6 / 2013-09-12
------------------
- Improved handling glyphs without codes or names
- Fixed crash on glyphs with `v`/`h` commands
- Logic cleanup
1.0.5 / 2013-08-27
------------------
- Added CLI option `-c` to set copyright string
- Fixed crash when some metrics missed in source SVG
- Minor code cleanup
1.0.4 / 2013-08-09
------------------
- Fixed importing into OSX Font Book
1.0.3 / 2013-08-02
------------------
- Fixed maxp table max points count (solved chrome problems under windozze)
1.0.2 / 2013-07-24
------------------
- Fixed htmx table size
- Fixed loca table size for long format
- Fixed glyph bounding boxes writing
1.0.1 / 2013-07-24
------------------
- Added options support
- Added `ttfinfo` utility
- Multiple fixes
1.0.0 / 2013-07-19
------------------
- First release

21
static/js/ketcher2/node_modules/svg2ttf/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
(The MIT License)
Copyright (C) 2013-2015 by Vitaly Puzrin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

70
static/js/ketcher2/node_modules/svg2ttf/README.md generated vendored Normal file
View File

@ -0,0 +1,70 @@
svg2ttf
========
[![Build Status](https://img.shields.io/travis/fontello/svg2ttf/master.svg?style=flat)](https://travis-ci.org/fontello/svg2ttf)
[![NPM version](https://img.shields.io/npm/v/svg2ttf.svg?style=flat)](https://www.npmjs.org/package/svg2ttf)
> Converts SVG fonts to TTF format. It was initially written for
[Fontello](http://fontello.com), but you can find it useful for your projects.
__For developpers:__
Internal API is similar to FontForge's one. Since primary goal
is generating iconic fonts, sources can lack some specific TTF/OTF features,
like kerning and so on. Anyway, current code is a good base for development,
because it will save you tons of hours to implement correct writing & optimizing
TTF tables.
Using from CLI
----------------
Install:
``` bash
npm install -g svg2ttf
```
Usage example:
``` bash
svg2ttf fontello.svg fontello.ttf
```
API
---
### svg2ttf(svgFontString, options) -> buf
- `svgFontString` - SVG font content
- `options`
- `copyright` - copyright string (optional)
- `ts` - Unix timestamp (in seconds) to override creation time (optional)
- `version` - font version string, can be `Version x.y` or `x.y`.
- `buf` - internal [byte buffer](https://github.com/fontello/microbuffer)
object, similar to DataView. It's `buffer` property is `Uin8Array` or `Array`
with ttf content.
Example:
``` javascript
var fs = require('fs');
var svg2ttf = require('svg2ttf');
var ttf = svg2ttf(fs.readFileSync('myfont.svg', 'utf8'), {});
fs.writeFileSync('myfont.ttf', new Buffer(ttf.buffer));
```
Authors
-------
* Sergey Batishchev - [@snb2013](https://github.com/snb2013)
* Vitaly Puzrin - [@puzrin](https://github.com/puzrin)
License
-------
[MIT](https://github.com/nodeca/svg2ttf/blob/master/LICENSE).

186
static/js/ketcher2/node_modules/svg2ttf/index.js generated vendored Normal file
View File

@ -0,0 +1,186 @@
/*
* Copyright: Vitaly Puzrin
* Author: Sergey Batishchev <snb2003@rambler.ru>
*
* Written for fontello.com project.
*/
'use strict';
var _ = require('lodash');
var SvgPath = require('svgpath');
var ucs2 = require('./lib/ucs2');
var svg = require('./lib/svg');
var sfnt = require('./lib/sfnt');
var VERSION_RE = /^(Version )?(\d+[.]\d+)$/i;
function svg2ttf(svgString, options) {
var font = new sfnt.Font();
var svgFont = svg.load(svgString);
options = options || {};
font.id = options.id || svgFont.id;
font.familyName = options.familyname || svgFont.familyName || svgFont.id;
font.copyright = options.copyright || svgFont.metadata;
font.sfntNames.push({ id: 2, value: options.subfamilyname || svgFont.subfamilyName || 'Regular' }); // subfamily name
font.sfntNames.push({ id: 4, value: options.fullname || svgFont.id }); // full name
var versionString = options.version || 'Version 1.0';
if (typeof versionString !== 'string') {
throw new Error('svg2ttf: version option should be a string');
}
if (!VERSION_RE.test(versionString)) {
throw new Error('svg2ttf: invalid option, version - "' + options.version + '"');
}
versionString = 'Version ' + versionString.match(VERSION_RE)[2];
font.sfntNames.push({ id: 5, value: versionString }); // version ID for TTF name table
font.sfntNames.push({ id: 6, value: (options.fullname || svgFont.id).replace(/[\s\(\)\[\]<>%\/]/g, '').substr(0, 62) }); // Postscript name for the font, required for OSX Font Book
if (typeof options.ts !== 'undefined') {
font.createdDate = font.modifiedDate = new Date(parseInt(options.ts, 10) * 1000);
}
// Try to fill font metrics or guess defaults
//
font.unitsPerEm = svgFont.unitsPerEm || 1000;
font.horizOriginX = svgFont.horizOriginX || 0;
font.horizOriginY = svgFont.horizOriginY || 0;
font.vertOriginX = svgFont.vertOriginX || 0;
font.vertOriginY = svgFont.vertOriginY || 0;
// need to correctly convert text values, use default (400) until compleete
//font.weightClass = svgFont.weightClass;
font.width = svgFont.width || svgFont.unitsPerEm;
font.height = svgFont.height || svgFont.unitsPerEm;
font.descent = !isNaN(svgFont.descent) ? svgFont.descent : -font.vertOriginY;
font.ascent = svgFont.ascent || (font.unitsPerEm - font.vertOriginY);
var glyphs = font.glyphs;
var codePoints = font.codePoints;
var ligatures = font.ligatures;
function addCodePoint(codePoint, glyph) {
if (codePoints[codePoint]) {
// Ignore code points already defined
return false;
}
codePoints[codePoint] = glyph;
return true;
}
// add SVG glyphs to SFNT font
_.forEach(svgFont.glyphs, function (svgGlyph) {
var glyph = new sfnt.Glyph();
glyph.name = svgGlyph.name;
glyph.d = svgGlyph.d;
glyph.height = !isNaN(svgGlyph.height) ? svgGlyph.height : font.height;
glyph.width = !isNaN(svgGlyph.width) ? svgGlyph.width : font.width;
glyphs.push(glyph);
svgGlyph.sfntGlyph = glyph;
_.forEach(svgGlyph.unicode, function (codePoint) {
addCodePoint(codePoint, glyph);
});
});
var missingGlyph;
// add missing glyph to SFNT font
// also, check missing glyph existance and single instance
if (svgFont.missingGlyph) {
missingGlyph = new sfnt.Glyph();
missingGlyph.d = svgFont.missingGlyph.d;
missingGlyph.height = !isNaN(svgFont.missingGlyph.height) ? svgFont.missingGlyph.height : font.height;
missingGlyph.width = !isNaN(svgFont.missingGlyph.width) ? svgFont.missingGlyph.width : font.width;
} else {
missingGlyph = _.find(glyphs, function (glyph) {
return glyph.name === '.notdef';
});
}
if (!missingGlyph) { // no missing glyph and .notdef glyph, we need to create missing glyph
missingGlyph = new sfnt.Glyph();
}
// Create glyphs for all characters used in ligatures
_.forEach(svgFont.ligatures, function (svgLigature) {
var ligature = {
ligature: svgLigature.ligature,
unicode: svgLigature.unicode,
glyph: svgLigature.glyph.sfntGlyph
};
_.forEach(ligature.unicode, function (charPoint) {
// We need to have a distinct glyph for each code point so we can reference it in GSUB
var glyph = new sfnt.Glyph();
var added = addCodePoint(charPoint, glyph);
if (added) {
glyph.name = ucs2.encode([ charPoint ]);
glyphs.push(glyph);
}
});
ligatures.push(ligature);
});
// Missing Glyph needs to have index 0
if (glyphs.indexOf(missingGlyph) !== -1) {
glyphs.splice(glyphs.indexOf(missingGlyph), 1);
}
glyphs.unshift(missingGlyph);
var nextID = 0;
//add IDs
_.forEach(glyphs, function (glyph) {
glyph.id = nextID;
nextID++;
});
_.forEach(glyphs, function (glyph) {
// Calculate accuracy for cubicToQuad transformation
// For glyphs with height and width smaller than 500 use relative 0.06% accuracy,
// for larger glyphs use fixed accuracy 0.3.
var glyphSize = Math.max(glyph.width, glyph.height);
var accuracy = (glyphSize > 500) ? 0.3 : glyphSize * 0.0006;
//SVG transformations
var svgPath = new SvgPath(glyph.d)
.abs()
.unshort()
.unarc()
.iterate(function (segment, index, x, y) {
return svg.cubicToQuad(segment, index, x, y, accuracy);
});
var sfntContours = svg.toSfntCoutours(svgPath);
// Add contours to SFNT font
glyph.contours = _.map(sfntContours, function (sfntContour) {
var contour = new sfnt.Contour();
contour.points = _.map(sfntContour, function (sfntPoint) {
var point = new sfnt.Point();
point.x = sfntPoint.x;
point.y = sfntPoint.y;
point.onCurve = sfntPoint.onCurve;
return point;
});
return contour;
});
});
var ttf = sfnt.toTTF(font);
return ttf;
}
module.exports = svg2ttf;

54
static/js/ketcher2/node_modules/svg2ttf/lib/math.js generated vendored Normal file
View File

@ -0,0 +1,54 @@
'use strict';
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.add = function (point) {
return new Point(this.x + point.x, this.y + point.y);
};
Point.prototype.sub = function (point) {
return new Point(this.x - point.x, this.y - point.y);
};
Point.prototype.mul = function (value) {
return new Point(this.x * value, this.y * value);
};
Point.prototype.div = function (value) {
return new Point(this.x / value, this.y / value);
};
Point.prototype.dist = function () {
return Math.sqrt(this.x * this.x + this.y * this.y);
};
Point.prototype.sqr = function () {
return this.x * this.x + this.y * this.y;
};
/*
* Check if 3 points are in line, and second in the midle.
* Used to replace quad curves with lines or join lines
*
*/
function isInLine(p1, m, p2, accuracy) {
var a = p1.sub(m).sqr();
var b = p2.sub(m).sqr();
var c = p1.sub(p2).sqr();
// control point not between anchors
if ((a > (b + c)) || (b > (a + c))) {
return false;
}
// count distance via scalar multiplication
var distance = Math.sqrt(Math.pow((p1.x - m.x) * (p2.y - m.y) - (p2.x - m.x) * (p1.y - m.y), 2) / c);
return distance < accuracy ? true : false;
}
module.exports.Point = Point;
module.exports.isInLine = isInLine;

348
static/js/ketcher2/node_modules/svg2ttf/lib/sfnt.js generated vendored Normal file
View File

@ -0,0 +1,348 @@
'use strict';
var _ = require('lodash');
function Font() {
this.ascent = 850;
this.copyright = '';
this.createdDate = new Date();
this.glyphs = [];
this.ligatures = [];
// Maping of code points to glyphs.
// Keys are actually numeric, thus should be `parseInt`ed.
this.codePoints = {};
this.isFixedPitch = 0;
this.italicAngle = 0;
this.familyClass = 0; // No Classification
this.familyName = '';
this.fsSelection = 0x40; // Characters are in the standard weight/style for the font.
// Non zero value can cause issues in IE, https://github.com/fontello/svg2ttf/issues/45
this.fsType = 0;
this.lowestRecPPEM = 8;
this.macStyle = 0;
this.modifiedDate = new Date();
this.panose = {
familyType: 2, // Latin Text
serifStyle: 0, // any
weight: 5, // book
proportion: 3, //modern
contrast: 0, //any
strokeVariation: 0, //any,
armStyle: 0, //any,
letterform: 0, //any,
midline: 0, //any,
xHeight: 0 //any,
};
this.revision = 1;
this.sfntNames = [];
this.underlineThickness = 0;
this.unitsPerEm = 1000;
this.weightClass = 400; // normal
this.width = 1000;
this.widthClass = 5; // Medium (normal)
this.ySubscriptXOffset = 0;
this.ySuperscriptXOffset = 0;
this.int_descent = -150;
//getters and setters
Object.defineProperty(this, 'descent', {
get: function () {
return this.int_descent;
},
set: function (value) {
this.int_descent = parseInt(Math.round(-Math.abs(value)), 10);
}
});
this.__defineGetter__('avgCharWidth', function () {
if (this.glyphs.length === 0) {
return 0;
}
var widths = _.map(this.glyphs, 'width');
return parseInt(widths.reduce(function (prev, cur) {
return prev + cur;
}) / widths.length, 10);
});
Object.defineProperty(this, 'ySubscriptXSize', {
get: function () {
return parseInt(!_.isUndefined(this.int_ySubscriptXSize) ? this.int_ySubscriptXSize : (this.width * 0.6347), 10);
},
set: function (value) {
this.int_ySubscriptXSize = value;
}
});
Object.defineProperty(this, 'ySubscriptYSize', {
get: function () {
return parseInt(!_.isUndefined(this.int_ySubscriptYSize) ? this.int_ySubscriptYSize : ((this.ascent - this.descent) * 0.7), 10);
},
set: function (value) {
this.int_ySubscriptYSize = value;
}
});
Object.defineProperty(this, 'ySubscriptYOffset', {
get: function () {
return parseInt(!_.isUndefined(this.int_ySubscriptYOffset) ? this.int_ySubscriptYOffset : ((this.ascent - this.descent) * 0.14), 10);
},
set: function (value) {
this.int_ySubscriptYOffset = value;
}
});
Object.defineProperty(this, 'ySuperscriptXSize', {
get: function () {
return parseInt(!_.isUndefined(this.int_ySuperscriptXSize) ? this.int_ySuperscriptXSize : (this.width * 0.6347), 10);
},
set: function (value) {
this.int_ySuperscriptXSize = value;
}
});
Object.defineProperty(this, 'ySuperscriptYSize', {
get: function () {
return parseInt(!_.isUndefined(this.int_ySuperscriptYSize) ? this.int_ySuperscriptYSize : ((this.ascent - this.descent) * 0.7), 10);
},
set: function (value) {
this.int_ySuperscriptYSize = value;
}
});
Object.defineProperty(this, 'ySuperscriptYOffset', {
get: function () {
return parseInt(!_.isUndefined(this.int_ySuperscriptYOffset) ? this.int_ySuperscriptYOffset : ((this.ascent - this.descent) * 0.48), 10);
},
set: function (value) {
this.int_ySuperscriptYOffset = value;
}
});
Object.defineProperty(this, 'yStrikeoutSize', {
get: function () {
return parseInt(!_.isUndefined(this.int_yStrikeoutSize) ? this.int_yStrikeoutSize : ((this.ascent - this.descent) * 0.049), 10);
},
set: function (value) {
this.int_yStrikeoutSize = value;
}
});
Object.defineProperty(this, 'yStrikeoutPosition', {
get: function () {
return parseInt(!_.isUndefined(this.int_yStrikeoutPosition) ? this.int_yStrikeoutPosition : ((this.ascent - this.descent) * 0.258), 10);
},
set: function (value) {
this.int_yStrikeoutPosition = value;
}
});
Object.defineProperty(this, 'minLsb', {
get: function () {
return parseInt(_.min(_.map(this.glyphs, 'xMin')), 10);
}
});
Object.defineProperty(this, 'minRsb', {
get: function () {
if (!this.glyphs.length) return parseInt(this.width, 10);
return parseInt(_.reduce(this.glyphs, function (minRsb, glyph) {
return Math.min(minRsb, glyph.width - glyph.xMax);
}, 0), 10);
}
});
Object.defineProperty(this, 'xMin', {
get: function () {
if (!this.glyphs.length) return this.width;
return _.reduce(this.glyphs, function (xMin, glyph) {
return Math.min(xMin, glyph.xMin);
}, 0);
}
});
Object.defineProperty(this, 'yMin', {
get: function () {
if (!this.glyphs.length) return this.width;
return _.reduce(this.glyphs, function (yMin, glyph) {
return Math.min(yMin, glyph.yMin);
}, 0);
}
});
Object.defineProperty(this, 'xMax', {
get: function () {
if (!this.glyphs.length) return this.width;
return _.reduce(this.glyphs, function (xMax, glyph) {
return Math.max(xMax, glyph.xMax);
}, 0);
}
});
Object.defineProperty(this, 'yMax', {
get: function () {
if (!this.glyphs.length) return this.width;
return _.reduce(this.glyphs, function (yMax, glyph) {
return Math.max(yMax, glyph.yMax);
}, 0);
}
});
Object.defineProperty(this, 'avgWidth', {
get: function () {
var len = this.glyphs.length;
if (len === 0) {
return this.width;
}
var sumWidth = _.reduce(this.glyphs, function (sumWidth, glyph) {
return sumWidth + glyph.width;
}, 0);
return Math.round(sumWidth / len);
}
});
Object.defineProperty(this, 'maxWidth', {
get: function () {
if (!this.glyphs.length) return this.width;
return _.reduce(this.glyphs, function (maxWidth, glyph) {
return Math.max(maxWidth, glyph.width);
}, 0);
}
});
Object.defineProperty(this, 'maxExtent', {
get: function () {
if (!this.glyphs.length) return this.width;
return _.reduce(this.glyphs, function (maxExtent, glyph) {
return Math.max(maxExtent, glyph.xMax /*- glyph.xMin*/);
}, 0);
}
});
// Property used for `sTypoLineGap` in OS/2 and not used for `lineGap` in HHEA, because
// non zero lineGap causes bad offset in IE, https://github.com/fontello/svg2ttf/issues/37
Object.defineProperty(this, 'lineGap', {
get: function () {
return parseInt(!_.isUndefined(this.int_lineGap) ? this.int_lineGap : ((this.ascent - this.descent) * 0.09), 10);
},
set: function (value) {
this.int_lineGap = value;
}
});
Object.defineProperty(this, 'underlinePosition', {
get: function () {
return parseInt(!_.isUndefined(this.int_underlinePosition) ? this.int_underlinePosition : ((this.ascent - this.descent) * 0.01), 10);
},
set: function (value) {
this.int_underlinePosition = value;
}
});
}
function Glyph() {
this.contours = [];
this.d = '';
this.id = '';
this.height = 0;
this.name = '';
this.width = 0;
}
Object.defineProperty(Glyph.prototype, 'xMin', {
get: function () {
var xMin = 0;
var hasPoints = false;
_.forEach(this.contours, function (contour) {
_.forEach(contour.points, function (point) {
xMin = Math.min(xMin, Math.floor(point.x));
hasPoints = true;
});
});
return hasPoints ? xMin : 0;
}
});
Object.defineProperty(Glyph.prototype, 'xMax', {
get: function () {
var xMax = 0;
var hasPoints = false;
_.forEach(this.contours, function (contour) {
_.forEach(contour.points, function (point) {
xMax = Math.max(xMax, -Math.floor(-point.x));
hasPoints = true;
});
});
return hasPoints ? xMax : this.width;
}
});
Object.defineProperty(Glyph.prototype, 'yMin', {
get: function () {
var yMin = 0;
var hasPoints = false;
_.forEach(this.contours, function (contour) {
_.forEach(contour.points, function (point) {
yMin = Math.min(yMin, Math.floor(point.y));
hasPoints = true;
});
});
return hasPoints ? yMin : 0;
}
});
Object.defineProperty(Glyph.prototype, 'yMax', {
get: function () {
var yMax = 0;
var hasPoints = false;
_.forEach(this.contours, function (contour) {
_.forEach(contour.points, function (point) {
yMax = Math.max(yMax, -Math.floor(-point.y));
hasPoints = true;
});
});
return hasPoints ? yMax : 0;
}
});
function Contour() {
this.points = [];
}
function Point() {
this.onCurve = true;
this.x = 0;
this.y = 0;
}
function SfntName() {
this.id = 0;
this.value = '';
}
module.exports.Font = Font;
module.exports.Glyph = Glyph;
module.exports.Contour = Contour;
module.exports.Point = Point;
module.exports.SfntName = SfntName;
module.exports.toTTF = require('./ttf');

43
static/js/ketcher2/node_modules/svg2ttf/lib/str.js generated vendored Normal file
View File

@ -0,0 +1,43 @@
'use strict';
function Str(str) {
if (!(this instanceof Str)) {
return new Str(str);
}
this.str = str;
this.toUTF8Bytes = function () {
var byteArray = [];
for (var i = 0; i < str.length; i++) {
if (str.charCodeAt(i) <= 0x7F) {
byteArray.push(str.charCodeAt(i));
} else {
var h = encodeURIComponent(str.charAt(i)).substr(1).split('%');
for (var j = 0; j < h.length; j++) {
byteArray.push(parseInt(h[j], 16));
}
}
}
return byteArray;
};
this.toUCS2Bytes = function () {
// Code is taken here:
// http://stackoverflow.com/questions/6226189/how-to-convert-a-string-to-bytearray
var byteArray = [];
var ch;
for (var i = 0; i < str.length; ++i) {
ch = str.charCodeAt(i); // get char
byteArray.push(ch >> 8);
byteArray.push(ch & 0xFF);
}
return byteArray;
};
}
module.exports = Str;

223
static/js/ketcher2/node_modules/svg2ttf/lib/svg.js generated vendored Normal file
View File

@ -0,0 +1,223 @@
'use strict';
var _ = require('lodash');
var cubic2quad = require('cubic2quad');
var DOMParser = require('xmldom').DOMParser;
var ucs2 = require('./ucs2');
function getGlyph(glyphElem) {
var glyph = {};
glyph.d = glyphElem.getAttribute('d').trim();
glyph.unicode = [];
if (glyphElem.getAttribute('unicode')) {
glyph.character = glyphElem.getAttribute('unicode');
var unicode = ucs2.decode(glyph.character);
// If more than one code point is involved, the glyph is a ligature glyph
if (unicode.length > 1) {
glyph.ligature = glyph.character;
glyph.ligatureCodes = unicode;
} else {
glyph.unicode.push(unicode[0]);
}
}
glyph.name = glyphElem.getAttribute('glyph-name');
if (glyphElem.getAttribute('horiz-adv-x')) {
glyph.width = parseInt(glyphElem.getAttribute('horiz-adv-x'), 10);
}
return glyph;
}
function deduplicateGlyps(glyphs, ligatures) {
// Result (the list of unique glyphs)
var result = [];
_.forEach(glyphs, function (glyph) {
// Search for glyphs with the same properties (width and d)
var canonical = _.find(result, { width: glyph.width, d: glyph.d });
if (canonical) {
// Add the code points to the unicode array.
// The fields “name” and “character” are not that important so we leave them how we first enounter them and throw the rest away
canonical.unicode = canonical.unicode.concat(glyph.unicode);
glyph.canonical = canonical;
} else {
result.push(glyph);
}
});
// Update ligatures to point to the canonical version
_.forEach(ligatures, function (ligature) {
while (_.has(ligature.glyph, 'canonical')) {
ligature.glyph = ligature.glyph.canonical;
}
});
return result;
}
function load(str) {
var attrs;
var doc = (new DOMParser()).parseFromString(str, 'application/xml');
var metadata, fontElem, fontFaceElem;
metadata = doc.getElementsByTagName('metadata')[0];
fontElem = doc.getElementsByTagName('font')[0];
if (!fontElem) {
throw new Error("Can't find <font> tag. Make sure you SVG file is font, not image.");
}
fontFaceElem = fontElem.getElementsByTagName('font-face')[0];
var familyName = fontFaceElem.getAttribute('font-family') || 'fontello';
var subfamilyName = fontFaceElem.getAttribute('font-style') || 'Regular';
var id = fontElem.getAttribute('id') || (familyName + '-' + subfamilyName).replace(/[\s\(\)\[\]<>%\/]/g, '').substr(0, 62);
var font = {
id: id,
familyName: familyName,
subfamilyName: subfamilyName,
stretch: fontFaceElem.getAttribute('font-stretch') || 'normal'
};
// Doesn't work with complex content like <strong>Copyright:></strong><em>Fontello</em>
if (metadata && metadata.textContent) {
font.metadata = metadata.textContent;
}
// Get <font> numeric attributes
attrs = {
width: 'horiz-adv-x',
//height: 'vert-adv-y',
horizOriginX: 'horiz-origin-x',
horizOriginY: 'horiz-origin-y',
vertOriginX: 'vert-origin-x',
vertOriginY: 'vert-origin-y'
};
_.forEach(attrs, function (val, key) {
if (fontElem.hasAttribute(val)) { font[key] = parseInt(fontElem.getAttribute(val), 10); }
});
// Get <font-face> numeric attributes
attrs = {
ascent: 'ascent',
descent: 'descent',
unitsPerEm: 'units-per-em'
};
_.forEach(attrs, function (val, key) {
if (fontFaceElem.hasAttribute(val)) { font[key] = parseInt(fontFaceElem.getAttribute(val), 10); }
});
if (fontFaceElem.hasAttribute('font-weight')) {
font.weightClass = fontFaceElem.getAttribute('font-weight');
}
var missingGlyphElem = fontElem.getElementsByTagName('missing-glyph')[0];
if (missingGlyphElem) {
font.missingGlyph = {};
font.missingGlyph.d = missingGlyphElem.getAttribute('d') || '';
if (missingGlyphElem.getAttribute('horiz-adv-x')) {
font.missingGlyph.width = parseInt(missingGlyphElem.getAttribute('horiz-adv-x'), 10);
}
}
var glyphs = [];
var ligatures = [];
_.forEach(fontElem.getElementsByTagName('glyph'), function (glyphElem) {
var glyph = getGlyph(glyphElem);
if (_.has(glyph, 'ligature')) {
ligatures.push({
ligature: glyph.ligature,
unicode: glyph.ligatureCodes,
glyph: glyph
});
}
glyphs.push(glyph);
});
glyphs = deduplicateGlyps(glyphs, ligatures);
font.glyphs = glyphs;
font.ligatures = ligatures;
return font;
}
function cubicToQuad(segment, index, x, y, accuracy) {
if (segment[0] === 'C') {
var quadCurves = cubic2quad(
x, y,
segment[1], segment[2],
segment[3], segment[4],
segment[5], segment[6],
accuracy
);
var res = [];
for (var i = 2; i < quadCurves.length; i += 4) {
res.push([ 'Q', quadCurves[i], quadCurves[i + 1], quadCurves[i + 2], quadCurves[i + 3] ]);
}
return res;
}
}
// Converts svg points to contours. All points must be converted
// to relative ones, smooth curves must be converted to generic ones
// before this conversion.
//
function toSfntCoutours(svgPath) {
var resContours = [];
var resContour = [];
svgPath.iterate(function (segment, index, x, y) {
//start new contour
if (index === 0 || segment[0] === 'M') {
resContour = [];
resContours.push(resContour);
}
var name = segment[0];
if (name === 'Q') {
//add control point of quad spline, it is not on curve
resContour.push({ x: segment[1], y: segment[2], onCurve: false });
}
// add on-curve point
if (name === 'H') {
// vertical line has Y coordinate only, X remains the same
resContour.push({ x: segment[1], y: y, onCurve: true });
} else if (name === 'V') {
// horizontal line has X coordinate only, Y remains the same
resContour.push({ x: x, y: segment[1], onCurve: true });
} else if (name !== 'Z') {
// for all commands (except H and V) X and Y are placed in the end of the segment
resContour.push({ x: segment[segment.length - 2], y: segment[segment.length - 1], onCurve: true });
}
});
return resContours;
}
module.exports.load = load;
module.exports.cubicToQuad = cubicToQuad;
module.exports.toSfntCoutours = toSfntCoutours;

161
static/js/ketcher2/node_modules/svg2ttf/lib/ttf.js generated vendored Normal file
View File

@ -0,0 +1,161 @@
'use strict';
var _ = require('lodash');
var ByteBuffer = require('microbuffer');
var createGSUBTable = require('./ttf/tables/gsub');
var createOS2Table = require('./ttf/tables/os2');
var createCMapTable = require('./ttf/tables/cmap');
var createGlyfTable = require('./ttf/tables/glyf');
var createHeadTable = require('./ttf/tables/head');
var createHHeadTable = require('./ttf/tables/hhea');
var createHtmxTable = require('./ttf/tables/hmtx');
var createLocaTable = require('./ttf/tables/loca');
var createMaxpTable = require('./ttf/tables/maxp');
var createNameTable = require('./ttf/tables/name');
var createPostTable = require('./ttf/tables/post');
var utils = require('./ttf/utils');
// Tables
var TABLES = [
{ innerName: 'GSUB', order: 4, create: createGSUBTable }, // GSUB
{ innerName: 'OS/2', order: 4, create: createOS2Table }, // OS/2
{ innerName: 'cmap', order: 6, create: createCMapTable }, // cmap
{ innerName: 'glyf', order: 8, create: createGlyfTable }, // glyf
{ innerName: 'head', order: 2, create: createHeadTable }, // head
{ innerName: 'hhea', order: 1, create: createHHeadTable }, // hhea
{ innerName: 'hmtx', order: 5, create: createHtmxTable }, // hmtx
{ innerName: 'loca', order: 7, create: createLocaTable }, // loca
{ innerName: 'maxp', order: 3, create: createMaxpTable }, // maxp
{ innerName: 'name', order: 9, create: createNameTable }, // name
{ innerName: 'post', order: 10, create: createPostTable } // post
];
// Various constants
var CONST = {
VERSION: 0x10000,
CHECKSUM_ADJUSTMENT: 0xB1B0AFBA
};
function ulong(t) {
t &= 0xffffffff;
if (t < 0) {
t += 0x100000000;
}
return t;
}
function calc_checksum(buf) {
var sum = 0;
var nlongs = Math.floor(buf.length / 4);
var i;
for (i = 0; i < nlongs; ++i) {
var t = buf.getUint32(i * 4);
sum = ulong(sum + t);
}
var leftBytes = buf.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++) {
leftRes = (leftRes << 8) + ((i < leftBytes) ? buf.getUint8(nlongs * 4 + i) : 0);
}
sum = ulong(sum + leftRes);
}
return sum;
}
function generateTTF(font) {
// Prepare TTF contours objects. Note, that while sfnt countours are classes,
// ttf contours are just plain arrays of points
_.forEach(font.glyphs, function (glyph) {
glyph.ttfContours = _.map(glyph.contours, function (contour) {
return contour.points;
});
});
// Process ttf contours data
_.forEach(font.glyphs, function (glyph) {
// 0.3px accuracy is ok. fo 1000x1000.
glyph.ttfContours = utils.simplify(glyph.ttfContours, 0.3);
glyph.ttfContours = utils.simplify(glyph.ttfContours, 0.3); // one pass is not enougth
// Interpolated points can be removed. 1.1px is acceptable
// measure - it will give us 1px error after coordinates rounding.
glyph.ttfContours = utils.interpolate(glyph.ttfContours, 1.1);
glyph.ttfContours = utils.roundPoints(glyph.ttfContours);
glyph.ttfContours = utils.removeClosingReturnPoints(glyph.ttfContours);
glyph.ttfContours = utils.toRelative(glyph.ttfContours);
});
// Add tables
var headerSize = 12 + 16 * TABLES.length; // TTF header plus table headers
var bufSize = headerSize;
_.forEach(TABLES, function (table) {
//store each table in its own buffer
table.buffer = table.create(font);
table.length = table.buffer.length;
table.corLength = table.length + (4 - table.length % 4) % 4; // table size should be divisible to 4
table.checkSum = calc_checksum(table.buffer);
bufSize += table.corLength;
});
//calculate offsets
var offset = headerSize;
_.forEach(_.sortBy(TABLES, 'order'), function (table) {
table.offset = offset;
offset += table.corLength;
});
//create TTF buffer
var buf = new ByteBuffer(bufSize);
//special constants
var entrySelector = Math.floor(Math.log(TABLES.length) / Math.LN2);
var searchRange = Math.pow(2, entrySelector) * 16;
var rangeShift = TABLES.length * 16 - searchRange;
// Add TTF header
buf.writeUint32(CONST.VERSION);
buf.writeUint16(TABLES.length);
buf.writeUint16(searchRange);
buf.writeUint16(entrySelector);
buf.writeUint16(rangeShift);
_.forEach(TABLES, function (table) {
buf.writeUint32(utils.identifier(table.innerName)); //inner name
buf.writeUint32(table.checkSum); //checksum
buf.writeUint32(table.offset); //offset
buf.writeUint32(table.length); //length
});
var headOffset = 0;
_.forEach(_.sortBy(TABLES, 'order'), function (table) {
if (table.innerName === 'head') { //we must store head offset to write font checksum
headOffset = buf.tell();
}
buf.writeBytes(table.buffer.buffer);
for (var i = table.length; i < table.corLength; i++) { //align table to be divisible to 4
buf.writeUint8(0);
}
});
// Write font checksum (corrected by magic value) into HEAD table
buf.setUint32(headOffset + 8, ulong(CONST.CHECKSUM_ADJUSTMENT - calc_checksum(buf)));
return buf;
}
module.exports = generateTTF;

View File

@ -0,0 +1,304 @@
'use strict';
// See documentation here: http://www.microsoft.com/typography/otspec/cmap.htm
var _ = require('lodash');
var ByteBuffer = require('microbuffer');
function getIDByUnicode(font, unicode) {
return font.codePoints[unicode] ? font.codePoints[unicode].id : 0;
}
// Calculate character segments with non-interruptable chains of unicodes
function getSegments(font, bounds) {
bounds = bounds || Number.MAX_VALUE;
var result = [];
var segment;
// prevEndCode only changes when a segment closes
_.forEach(font.codePoints, function (glyph, unicode) {
unicode = parseInt(unicode, 10);
if (unicode >= bounds) {
return false;
}
// Initialize first segment or add new segment if code "hole" is found
if (!segment || unicode !== (segment.end + 1)) {
if (segment) {
result.push(segment);
}
segment = {
start: unicode
};
}
segment.end = unicode;
});
// Need to finish the last segment
if (segment) {
result.push(segment);
}
_.forEach(result, function (segment) {
segment.length = segment.end - segment.start + 1;
});
return result;
}
// Returns an array of {unicode, glyph} sets for all valid code points up to bounds
function getCodePoints(codePoints, bounds) {
bounds = bounds || Number.MAX_VALUE;
var result = [];
_.forEach(codePoints, function (glyph, unicode) {
unicode = parseInt(unicode, 10);
// Since this is a sparse array, iterating will only yield the valid code points
if (unicode > bounds) {
return false;
}
result.push({
unicode: unicode,
glyph: glyph
});
});
return result;
}
function bufferForTable(format, length) {
var fieldWidth = format === 8 || format === 10 || format === 12 || format === 13 ? 4 : 2;
length += (0
+ fieldWidth // Format
+ fieldWidth // Length
+ fieldWidth // Language
);
var LANGUAGE = 0;
var buffer = new ByteBuffer(length);
var writer = fieldWidth === 4 ? buffer.writeUint32 : buffer.writeUint16;
// Format specifier
buffer.writeUint16(format);
if (fieldWidth === 4) {
// In case of formats 8.…, 10.…, 12.… and 13.…, this is the decimal part of the format number
// But since have not been any point releases, this can be zero in that case as well
buffer.writeUint16(0);
}
// Length
writer.call(buffer, length);
// Language code (0, only used for legacy quickdraw tables)
writer.call(buffer, LANGUAGE);
return buffer;
}
function createFormat0Table(font) {
var FORMAT = 0;
var i;
var length = 0xff + 1; //Format 0 maps only single-byte code points
var buffer = bufferForTable(FORMAT, length);
for (i = 0; i < length; i++) {
buffer.writeUint8(getIDByUnicode(font, i)); // existing char in table 0..255
}
return buffer;
}
function createFormat4Table(font) {
var FORMAT = 4;
var i;
var segments = getSegments(font, 0xFFFF);
var glyphIndexArrays = [];
_.forEach(segments, function (segment) {
var glyphIndexArray = [];
for (var unicode = segment.start; unicode <= segment.end; unicode++) {
glyphIndexArray.push(getIDByUnicode(font, unicode));
}
glyphIndexArrays.push(glyphIndexArray);
});
var segCount = segments.length + 1; // + 1 for the 0xFFFF section
var glyphIndexArrayLength = _.reduce(_.map(glyphIndexArrays, 'length'), function (result, count) { return result + count; }, 0);
var length = (0
+ 2 // segCountX2
+ 2 // searchRange
+ 2 // entrySelector
+ 2 // rangeShift
+ 2 * segCount // endCodes
+ 2 // Padding
+ 2 * segCount //startCodes
+ 2 * segCount //idDeltas
+ 2 * segCount //idRangeOffsets
+ 2 * glyphIndexArrayLength
);
var buffer = bufferForTable(FORMAT, length);
buffer.writeUint16(segCount * 2); // segCountX2
var maxExponent = Math.floor(Math.log(segCount) / Math.LN2);
var searchRange = 2 * Math.pow(2, maxExponent);
buffer.writeUint16(searchRange); // searchRange
buffer.writeUint16(maxExponent); // entrySelector
buffer.writeUint16(2 * segCount - searchRange); // rangeShift
// Array of end counts
_.forEach(segments, function (segment) {
buffer.writeUint16(segment.end);
});
buffer.writeUint16(0xFFFF); // endCountArray should be finished with 0xFFFF
buffer.writeUint16(0); // reservedPad
// Array of start counts
_.forEach(segments, function (segment) {
buffer.writeUint16(segment.start); //startCountArray
});
buffer.writeUint16(0xFFFF); // startCountArray should be finished with 0xFFFF
// Array of deltas. Leave it zero to not complicate things when using the glyph index array
for (i = 0; i < segments.length; i++) {
buffer.writeUint16(0); // delta is always zero because we use the glyph array
}
buffer.writeUint16(1); // idDeltaArray should be finished with 1
// Array of range offsets
var offset = 0;
for (i = 0; i < segments.length; i++) {
buffer.writeUint16(2 * ((segments.length - i + 1) + offset));
offset += glyphIndexArrays[i].length;
}
buffer.writeUint16(0); // rangeOffsetArray should be finished with 0
_.forEach(glyphIndexArrays, function (glyphIndexArray) {
_.forEach(glyphIndexArray, function (glyphId) {
buffer.writeUint16(glyphId);
});
});
return buffer;
}
function createFormat12Table(font) {
var FORMAT = 12;
var codePoints = getCodePoints(font.codePoints);
var length = (0
+ 4 // nGroups
+ 4 * codePoints.length // startCharCode
+ 4 * codePoints.length // endCharCode
+ 4 * codePoints.length // startGlyphCode
);
var buffer = bufferForTable(FORMAT, length);
buffer.writeUint32(codePoints.length); // nGroups
_.forEach(codePoints, function (codePoint) {
buffer.writeUint32(codePoint.unicode); // startCharCode
buffer.writeUint32(codePoint.unicode); // endCharCode
buffer.writeUint32(codePoint.glyph.id); // startGlyphCode
});
return buffer;
}
function createCMapTable(font) {
var TABLE_HEAD = (0
+ 2 // platform
+ 2 // encoding
+ 4 // offset
);
var singleByteTable = createFormat0Table(font);
var twoByteTable = createFormat4Table(font);
var fourByteTable = createFormat12Table(font);
// Subtable headers must be sorted by platformID, encodingID
var tableHeaders = [
// subtable 4, unicode
{
platformID: 0,
encodingID: 3,
table: twoByteTable
},
// subtable 12, unicode
{
platformID: 0,
encodingID: 4,
table: fourByteTable
},
// subtable 0, mac standard
{
platformID: 1,
encodingID: 0,
table: singleByteTable
},
// subtable 4, windows standard, identical to the unicode table
{
platformID: 3,
encodingID: 1,
table: twoByteTable
},
// subtable 12, windows ucs4
{
platformID: 3,
encodingID: 10,
table: fourByteTable
}
];
var tables = [
twoByteTable,
singleByteTable,
fourByteTable
];
var tableOffset = (0
+ 2 // version
+ 2 // number of subtable headers
+ tableHeaders.length * TABLE_HEAD
);
// Calculate offsets for each table
_.forEach(tables, function (table) {
table._tableOffset = tableOffset;
tableOffset += table.length;
});
var length = tableOffset;
var buffer = new ByteBuffer(length);
// Write table header.
buffer.writeUint16(0); // version
buffer.writeUint16(tableHeaders.length); // count
// Write subtable headers
_.forEach(tableHeaders, function (header) {
buffer.writeUint16(header.platformID); // platform
buffer.writeUint16(header.encodingID); // encoding
buffer.writeUint32(header.table._tableOffset); // offset
});
// Write subtables
_.forEach(tables, function (table) {
buffer.writeBytes(table.buffer);
});
return buffer;
}
module.exports = createCMapTable;

View File

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

View File

@ -0,0 +1,407 @@
'use strict';
// See documentation here: http://www.microsoft.com/typography/otspec/GSUB.htm
var _ = require('lodash');
var identifier = require('../utils.js').identifier;
var ByteBuffer = require('microbuffer');
function createScript() {
var scriptRecord = (0
+ 2 // Script DefaultLangSys Offset
+ 2 // Script[0] LangSysCount (0)
);
var langSys = (0
+ 2 // Script DefaultLangSys LookupOrder
+ 2 // Script DefaultLangSys ReqFeatureIndex
+ 2 // Script DefaultLangSys FeatureCount (0?)
+ 2 // Script Optional Feature Index[0]
);
var length = (0
+ scriptRecord
+ langSys
);
var buffer = new ByteBuffer(length);
// Script Record
// Offset to the start of langSys from the start of scriptRecord
buffer.writeUint16(scriptRecord); // DefaultLangSys
// Number of LangSys entries other than the default (none)
buffer.writeUint16(0);
// LangSys record (DefaultLangSys)
// LookupOrder
buffer.writeUint16(0);
// ReqFeatureIndex -> only one required feature: all ligatures
buffer.writeUint16(0);
// Number of FeatureIndex values for this language system (excludes the required feature)
buffer.writeUint16(1);
// FeatureIndex for the first optional feature
// Note: Adding the same feature to both the optional
// and the required features is a clear violation of the spec
// but it fixes IE not displaying the ligatures.
// See http://partners.adobe.com/public/developer/opentype/index_table_formats.html, Section “Language System Table”
// “FeatureCount: Number of FeatureIndex values for this language system-*excludes the required feature*” (emphasis added)
buffer.writeUint16(0);
return buffer;
}
function createScriptList() {
var scriptSize = (0
+ 4 // Tag
+ 2 // Offset
);
// tags should be arranged alphabetically
var scripts = [
[ 'DFLT', createScript() ],
[ 'latn', createScript() ]
];
var header = (0
+ 2 // Script count
+ scripts.length * scriptSize
);
var tableLengths = _.reduce(_.map(scripts, function (script) { return script[1].length; }), function (result, count) { return result + count; }, 0);
var length = (0
+ header
+ tableLengths
);
var buffer = new ByteBuffer(length);
// Script count
buffer.writeUint16(scripts.length);
// Write all ScriptRecords
var offset = header;
_.forEach(scripts, function (script) {
var name = script[0], table = script[1];
// Script identifier (DFLT/latn)
buffer.writeUint32(identifier(name));
// Offset to the ScriptRecord from start of the script list
buffer.writeUint16(offset);
// Increment offset by script table length
offset += table.length;
});
// Write all ScriptTables
_.forEach(scripts, function (script) {
var table = script[1];
buffer.writeBytes(table.buffer);
});
return buffer;
}
// Write one feature containing all ligatures
function createFeatureList() {
var header = (0
+ 2 // FeatureCount
+ 4 // FeatureTag[0]
+ 2 // Feature Offset[0]
);
var length = (0
+ header
+ 2 // FeatureParams[0]
+ 2 // LookupCount[0]
+ 2 // Lookup[0] LookupListIndex[0]
);
var buffer = new ByteBuffer(length);
// FeatureCount
buffer.writeUint16(1);
// FeatureTag[0]
buffer.writeUint32(identifier('liga'));
// Feature Offset[0]
buffer.writeUint16(header);
// FeatureParams[0]
buffer.writeUint16(0);
// LookupCount[0]
buffer.writeUint16(1);
// Index into lookup table. Since we only have ligatures, the index is always 0
buffer.writeUint16(0);
return buffer;
}
function createLigatureCoverage(font, ligatureGroups) {
var glyphCount = ligatureGroups.length;
var length = (0
+ 2 // CoverageFormat
+ 2 // GlyphCount
+ 2 * glyphCount // GlyphID[i]
);
var buffer = new ByteBuffer(length);
// CoverageFormat
buffer.writeUint16(1);
// Length
buffer.writeUint16(glyphCount);
_.forEach(ligatureGroups, function (group) {
buffer.writeUint16(group.startGlyph.id);
});
return buffer;
}
function createLigatureTable(font, ligature) {
var allCodePoints = font.codePoints;
var unicode = ligature.unicode;
var length = (0
+ 2 // LigGlyph
+ 2 // CompCount
+ 2 * (unicode.length - 1)
);
var buffer = new ByteBuffer(length);
// LigGlyph
var glyph = ligature.glyph;
buffer.writeUint16(glyph.id);
// CompCount
buffer.writeUint16(unicode.length);
// Compound glyphs (excluding first as its already in the coverage table)
for (var i = 1; i < unicode.length; i++) {
glyph = allCodePoints[unicode[i]];
buffer.writeUint16(glyph.id);
}
return buffer;
}
function createLigatureSet(font, codePoint, ligatures) {
var ligatureTables = [];
_.forEach(ligatures, function (ligature) {
ligatureTables.push(createLigatureTable(font, ligature));
});
var tableLengths = _.reduce(_.map(ligatureTables, 'length'), function (result, count) { return result + count; }, 0);
var offset = (0
+ 2 // LigatureCount
+ 2 * ligatures.length
);
var length = (0
+ offset
+ tableLengths
);
var buffer = new ByteBuffer(length);
// LigatureCount
buffer.writeUint16(ligatures.length);
// Ligature offsets
_.forEach(ligatureTables, function (table) {
// The offset to the current set, from SubstFormat
buffer.writeUint16(offset);
offset += table.length;
});
// Ligatures
_.forEach(ligatureTables, function (table) {
buffer.writeBytes(table.buffer);
});
return buffer;
}
function createLigatureList(font, ligatureGroups) {
var sets = [];
_.forEach(ligatureGroups, function (group) {
var set = createLigatureSet(font, group.codePoint, group.ligatures);
sets.push(set);
});
var setLengths = _.reduce(_.map(sets, 'length'), function (result, count) { return result + count; }, 0);
var coverage = createLigatureCoverage(font, ligatureGroups);
var tableOffset = (0
+ 2 // Lookup type
+ 2 // Lokup flag
+ 2 // SubTableCount
+ 2 // SubTable[0] Offset
);
var setOffset = (0
+ 2 // SubstFormat
+ 2 // Coverage offset
+ 2 // LigSetCount
+ 2 * sets.length // LigSet Offsets
);
var coverageOffset = setOffset + setLengths;
var length = (0
+ tableOffset
+ coverageOffset
+ coverage.length
);
var buffer = new ByteBuffer(length);
// Lookup type 4 ligatures
buffer.writeUint16(4);
// Lookup flag empty
buffer.writeUint16(0);
// Subtable count
buffer.writeUint16(1);
// Subtable[0] offset
buffer.writeUint16(tableOffset);
// SubstFormat
buffer.writeUint16(1);
// Coverage
buffer.writeUint16(coverageOffset);
// LigSetCount
buffer.writeUint16(sets.length);
_.forEach(sets, function (set) {
// The offset to the current set, from SubstFormat
buffer.writeUint16(setOffset);
setOffset += set.length;
});
_.forEach(sets, function (set) {
buffer.writeBytes(set.buffer);
});
buffer.writeBytes(coverage.buffer);
return buffer;
}
// Add a lookup for each ligature
function createLookupList(font) {
var ligatures = font.ligatures;
var groupedLigatures = {};
// Group ligatures by first code point
_.forEach(ligatures, function (ligature) {
var first = ligature.unicode[0];
if (!_.has(groupedLigatures, first)) {
groupedLigatures[first] = [];
}
groupedLigatures[first].push(ligature);
});
var ligatureGroups = [];
_.forEach(groupedLigatures, function (ligatures, codePoint) {
codePoint = parseInt(codePoint, 10);
// Order ligatures by length, descending
// “Ligatures with more components must be stored ahead of those with fewer components in order to be found”
// From: http://partners.adobe.com/public/developer/opentype/index_tag7.html#liga
ligatures.sort(function (ligA, ligB) {
return ligB.unicode.length - ligA.unicode.length;
});
ligatureGroups.push({
codePoint: codePoint,
ligatures: ligatures,
startGlyph: font.codePoints[codePoint]
});
});
ligatureGroups.sort(function (a, b) {
return a.startGlyph.id - b.startGlyph.id;
});
var offset = (0
+ 2 // Lookup count
+ 2 // Lookup[0] offset
);
var set = createLigatureList(font, ligatureGroups);
var length = (0
+ offset
+ set.length
);
var buffer = new ByteBuffer(length);
// Lookup count
buffer.writeUint16(1);
// Lookup[0] offset
buffer.writeUint16(offset);
// Lookup[0]
buffer.writeBytes(set.buffer);
return buffer;
}
function createGSUB(font) {
var scriptList = createScriptList();
var featureList = createFeatureList();
var lookupList = createLookupList(font);
var lists = [ scriptList, featureList, lookupList ];
var offset = (0
+ 4 // Version
+ 2 * lists.length // List offsets
);
// Calculate offsets
_.forEach(lists, function (list) {
list._listOffset = offset;
offset += list.length;
});
var length = offset;
var buffer = new ByteBuffer(length);
// Version
buffer.writeUint32(0x00010000);
// Offsets
_.forEach(lists, function (list) {
buffer.writeUint16(list._listOffset);
});
// List contents
_.forEach(lists, function (list) {
buffer.writeBytes(list.buffer);
});
return buffer;
}
module.exports = createGSUB;

View File

@ -0,0 +1,42 @@
'use strict';
// See documentation here: http://www.microsoft.com/typography/otspec/head.htm
var ByteBuffer = require('microbuffer');
function dateToUInt64(date) {
var startDate = new Date('1904-01-01T00:00:00.000Z');
return Math.floor((date - startDate) / 1000);
}
function createHeadTable(font) {
var buf = new ByteBuffer(54); // fixed table length
buf.writeInt32(0x10000); // version
buf.writeInt32(font.revision * 0x10000); // fontRevision
buf.writeUint32(0); // checkSumAdjustment
buf.writeUint32(0x5F0F3CF5); // magicNumber
// FLag meanings:
// Bit 0: Baseline for font at y=0;
// Bit 1: Left sidebearing point at x=0;
// Bit 3: Force ppem to integer values for all internal scaler math; may use fractional ppem sizes if this bit is clear;
buf.writeUint16(0x000B); // flags
buf.writeUint16(font.unitsPerEm); // unitsPerEm
buf.writeUint64(dateToUInt64(font.createdDate)); // created
buf.writeUint64(dateToUInt64(font.modifiedDate)); // modified
buf.writeInt16(font.xMin); // xMin
buf.writeInt16(font.yMin); // yMin
buf.writeInt16(font.xMax); // xMax
buf.writeInt16(font.yMax); // yMax
buf.writeUint16(font.macStyle); //macStyle
buf.writeUint16(font.lowestRecPPEM); // lowestRecPPEM
buf.writeInt16(2); // fontDirectionHint
buf.writeInt16(font.ttf_glyph_size < 0x20000 ? 0 : 1); // indexToLocFormat, 0 for short offsets, 1 for long offsets
buf.writeInt16(0); // glyphDataFormat
return buf;
}
module.exports = createHeadTable;

View File

@ -0,0 +1,31 @@
'use strict';
// See documentation here: http://www.microsoft.com/typography/otspec/hhea.htm
var ByteBuffer = require('microbuffer');
function createHHeadTable(font) {
var buf = new ByteBuffer(36); // fixed table length
buf.writeInt32(0x10000); // version
buf.writeInt16(font.ascent); // ascent
buf.writeInt16(font.descent); // descend
// Non zero lineGap causes offset in IE, https://github.com/fontello/svg2ttf/issues/37
buf.writeInt16(0); // lineGap
buf.writeUint16(font.maxWidth); // advanceWidthMax
buf.writeInt16(font.minLsb); // minLeftSideBearing
buf.writeInt16(font.minRsb); // minRightSideBearing
buf.writeInt16(font.maxExtent); // xMaxExtent
buf.writeInt16(1); // caretSlopeRise
buf.writeInt16(0); // caretSlopeRun
buf.writeUint32(0); // reserved1
buf.writeUint32(0); // reserved2
buf.writeUint16(0); // reserved3
buf.writeInt16(0); // metricDataFormat
buf.writeUint16(font.glyphs.length); // numberOfHMetrics
return buf;
}
module.exports = createHHeadTable;

View File

@ -0,0 +1,19 @@
'use strict';
// See documentation here: http://www.microsoft.com/typography/otspec/hmtx.htm
var _ = require('lodash');
var ByteBuffer = require('microbuffer');
function createHtmxTable(font) {
var buf = new ByteBuffer(font.glyphs.length * 4);
_.forEach(font.glyphs, function (glyph) {
buf.writeUint16(glyph.width); //advanceWidth
buf.writeInt16(glyph.xMin); //lsb
});
return buf;
}
module.exports = createHtmxTable;

View File

@ -0,0 +1,43 @@
'use strict';
// See documentation here: http://www.microsoft.com/typography/otspec/loca.htm
var _ = require('lodash');
var ByteBuffer = require('microbuffer');
function tableSize(font, isShortFormat) {
var result = (font.glyphs.length + 1) * (isShortFormat ? 2 : 4); // by glyph count + tail
return result;
}
function createLocaTable(font) {
var isShortFormat = font.ttf_glyph_size < 0x20000;
var buf = new ByteBuffer(tableSize(font, isShortFormat));
var location = 0;
// Array of offsets in GLYF table for each glyph
_.forEach(font.glyphs, function (glyph) {
if (isShortFormat) {
buf.writeUint16(location);
location += glyph.ttf_size / 2; // actual location must be divided to 2 in short format
} else {
buf.writeUint32(location);
location += glyph.ttf_size; //actual location is stored as is in long format
}
});
// The last glyph location is stored to get last glyph length
if (isShortFormat) {
buf.writeUint16(location);
} else {
buf.writeUint32(location);
}
return buf;
}
module.exports = createLocaTable;

View File

@ -0,0 +1,46 @@
'use strict';
// See documentation here: http://www.microsoft.com/typography/otspec/maxp.htm
var _ = require('lodash');
var ByteBuffer = require('microbuffer');
// Find max points in glyph TTF contours.
function getMaxPoints(font) {
return _.max(_.map(font.glyphs, function (glyph) {
return _.reduce(glyph.ttfContours, function (sum, ctr) { return sum + ctr.length; }, 0);
}));
}
function getMaxContours(font) {
return _.max(_.map(font.glyphs, function (glyph) {
return glyph.ttfContours.length;
}));
}
function createMaxpTable(font) {
var buf = new ByteBuffer(32);
buf.writeInt32(0x10000); // version
buf.writeUint16(font.glyphs.length); // numGlyphs
buf.writeUint16(getMaxPoints(font)); // maxPoints
buf.writeUint16(getMaxContours(font)); // maxContours
buf.writeUint16(0); // maxCompositePoints
buf.writeUint16(0); // maxCompositeContours
buf.writeUint16(2); // maxZones
buf.writeUint16(0); // maxTwilightPoints
// It is unclear how to calculate maxStorage, maxFunctionDefs and maxInstructionDefs.
// These are magic constants now, with values exceeding values from FontForge
buf.writeUint16(10); // maxStorage
buf.writeUint16(10); // maxFunctionDefs
buf.writeUint16(0); // maxInstructionDefs
buf.writeUint16(255); // maxStackElements
buf.writeUint16(0); // maxSizeOfInstructions
buf.writeUint16(0); // maxComponentElements
buf.writeUint16(0); // maxComponentDepth
return buf;
}
module.exports = createMaxpTable;

View File

@ -0,0 +1,106 @@
'use strict';
// See documentation here: http://www.microsoft.com/typography/otspec/name.htm
var _ = require('lodash');
var ByteBuffer = require('microbuffer');
var Str = require('../../str');
var TTF_NAMES = {
COPYRIGHT: 0,
FONT_FAMILY: 1,
ID: 3,
DESCRIPTION: 10,
URL_VENDOR: 11
};
function tableSize(names) {
var result = 6; // table header
_.forEach(names, function (name) {
result += 12 + name.data.length; //name header and data
});
return result;
}
function getStrings(name, id) {
var result = [];
var str = new Str(name);
result.push({ data: str.toUTF8Bytes(), id: id, platformID : 1, encodingID : 0, languageID : 0 }); //mac standard
result.push({ data: str.toUCS2Bytes(), id: id, platformID : 3, encodingID : 1, languageID : 0x409 }); //windows standard
return result;
}
// Collect font names
function getNames(font) {
var result = [];
if (font.copyright) {
result.push.apply(result, getStrings(font.copyright, TTF_NAMES.COPYRIGHT));
}
if (font.familyName) {
result.push.apply(result, getStrings(font.familyName, TTF_NAMES.FONT_FAMILY));
}
if (font.id) {
result.push.apply(result, getStrings(font.id, TTF_NAMES.ID));
}
result.push.apply(result, getStrings('Generated by svg2ttf from Fontello project.', TTF_NAMES.DESCRIPTION));
result.push.apply(result, getStrings('http://fontello.com', TTF_NAMES.URL_VENDOR));
_.forEach(font.sfntNames, function (sfntName) {
result.push.apply(result, getStrings(sfntName.value, sfntName.id));
});
result.sort(function (a, b) {
var orderFields = [ 'platformID', 'encodingID', 'languageID', 'id' ];
var i;
for (i = 0; i < orderFields.length; i++) {
if (a[orderFields[i]] !== b[orderFields[i]]) {
return a[orderFields[i]] < b[orderFields[i]] ? -1 : 1;
}
}
return 0;
});
return result;
}
function createNameTable(font) {
var names = getNames(font);
var buf = new ByteBuffer(tableSize(names));
buf.writeUint16(0); // formatSelector
buf.writeUint16(names.length); // nameRecordsCount
var offsetPosition = buf.tell();
buf.writeUint16(0); // offset, will be filled later
var nameOffset = 0;
_.forEach(names, function (name) {
buf.writeUint16(name.platformID); // platformID
buf.writeUint16(name.encodingID); // platEncID
buf.writeUint16(name.languageID); // languageID, English (USA)
buf.writeUint16(name.id); // nameID
buf.writeUint16(name.data.length); // reclength
buf.writeUint16(nameOffset); // offset
nameOffset += name.data.length;
});
var actualStringDataOffset = buf.tell();
//Array of bytes with actual string data
_.forEach(names, function (name) {
buf.writeBytes(name.data);
});
//write actual string data offset
buf.seek(offsetPosition);
buf.writeUint16(actualStringDataOffset); // offset
return buf;
}
module.exports = createNameTable;

View File

@ -0,0 +1,74 @@
'use strict';
// See documentation here: http://www.microsoft.com/typography/otspec/os2.htm
var _ = require('lodash');
var identifier = require('../utils.js').identifier;
var ByteBuffer = require('microbuffer');
//get first glyph unicode
function getFirstCharIndex(font) {
return Math.max(0, Math.min(0xffff, Math.abs(_.minBy(Object.keys(font.codePoints), function (point) {
return parseInt(point, 10);
}))));
}
//get last glyph unicode
function getLastCharIndex(font) {
return Math.max(0, Math.min(0xffff, Math.abs(_.maxBy(Object.keys(font.codePoints), function (point) {
return parseInt(point, 10);
}))));
}
function createOS2Table(font) {
var buf = new ByteBuffer(86);
buf.writeUint16(1); //version
buf.writeInt16(font.avgWidth); // xAvgCharWidth
buf.writeUint16(font.weightClass); // usWeightClass
buf.writeUint16(font.widthClass); // usWidthClass
buf.writeInt16(font.fsType); // fsType
buf.writeInt16(font.ySubscriptXSize); // ySubscriptXSize
buf.writeInt16(font.ySubscriptYSize); //ySubscriptYSize
buf.writeInt16(font.ySubscriptXOffset); // ySubscriptXOffset
buf.writeInt16(font.ySubscriptYOffset); // ySubscriptYOffset
buf.writeInt16(font.ySuperscriptXSize); // ySuperscriptXSize
buf.writeInt16(font.ySuperscriptYSize); // ySuperscriptYSize
buf.writeInt16(font.ySuperscriptXOffset); // ySuperscriptXOffset
buf.writeInt16(font.ySuperscriptYOffset); // ySuperscriptYOffset
buf.writeInt16(font.yStrikeoutSize); // yStrikeoutSize
buf.writeInt16(font.yStrikeoutPosition); // yStrikeoutPosition
buf.writeInt16(font.familyClass); // sFamilyClass
buf.writeUint8(font.panose.familyType); // panose.bFamilyType
buf.writeUint8(font.panose.serifStyle); // panose.bSerifStyle
buf.writeUint8(font.panose.weight); // panose.bWeight
buf.writeUint8(font.panose.proportion); // panose.bProportion
buf.writeUint8(font.panose.contrast); // panose.bContrast
buf.writeUint8(font.panose.strokeVariation); // panose.bStrokeVariation
buf.writeUint8(font.panose.armStyle); // panose.bArmStyle
buf.writeUint8(font.panose.letterform); // panose.bLetterform
buf.writeUint8(font.panose.midline); // panose.bMidline
buf.writeUint8(font.panose.xHeight); // panose.bXHeight
// TODO: This field is used to specify the Unicode blocks or ranges based on the 'cmap' table.
buf.writeUint32(0); // ulUnicodeRange1
buf.writeUint32(0); // ulUnicodeRange2
buf.writeUint32(0); // ulUnicodeRange3
buf.writeUint32(0); // ulUnicodeRange4
buf.writeUint32(identifier('PfEd')); // achVendID, equal to PfEd
buf.writeUint16(font.fsSelection); // fsSelection
buf.writeUint16(getFirstCharIndex(font)); // usFirstCharIndex
buf.writeUint16(getLastCharIndex(font)); // usLastCharIndex
buf.writeInt16(font.ascent); // sTypoAscender
buf.writeInt16(font.descent); // sTypoDescender
buf.writeInt16(font.lineGap); // lineGap
// Enlarge win acscent/descent to avoid clipping
buf.writeInt16(Math.max(font.yMax, font.ascent)); // usWinAscent
buf.writeInt16(-Math.min(font.yMin, font.descent)); // usWinDescent
buf.writeInt32(1); // ulCodePageRange1, Latin 1
buf.writeInt32(0); // ulCodePageRange2
return buf;
}
module.exports = createOS2Table;

View File

@ -0,0 +1,73 @@
'use strict';
// See documentation here: http://www.microsoft.com/typography/otspec/post.htm
var _ = require('lodash');
var ByteBuffer = require('microbuffer');
function tableSize(font, names) {
var result = 36; // table header
result += font.glyphs.length * 2; // name declarations
_.forEach(names, function (name) {
result += name.length;
});
return result;
}
function pascalString(str) {
var bytes = [];
var len = str ? (str.length < 256 ? str.length : 255) : 0; //length in Pascal string is limited with 255
bytes.push(len);
for (var i = 0; i < len; i++) {
var char = str.charCodeAt(i);
bytes.push(char < 128 ? char : 95); //non-ASCII characters are substituted with '_'
}
return bytes;
}
function createPostTable(font) {
var names = [];
_.forEach(font.glyphs, function (glyph) {
if (glyph.unicode !== 0) {
names.push(pascalString(glyph.name));
}
});
var buf = new ByteBuffer(tableSize(font, names));
buf.writeInt32(0x20000); // formatType, version 2.0
buf.writeInt32(font.italicAngle); // italicAngle
buf.writeInt16(font.underlinePosition); // underlinePosition
buf.writeInt16(font.underlineThickness); // underlineThickness
buf.writeUint32(font.isFixedPitch); // isFixedPitch
buf.writeUint32(0); // minMemType42
buf.writeUint32(0); // maxMemType42
buf.writeUint32(0); // minMemType1
buf.writeUint32(0); // maxMemType1
buf.writeUint16(font.glyphs.length); // numberOfGlyphs
// Array of glyph name indexes
var index = 258; // first index of custom glyph name, it is calculated as glyph name index + 258
_.forEach(font.glyphs, function (glyph) {
if (glyph.unicode === 0) {
buf.writeUint16(0);// missed element should have .notDef name in the Macintosh standard order.
} else {
buf.writeUint16(index++);
}
});
// Array of glyph name indexes
_.forEach(names, function (name) {
buf.writeBytes(name);
});
return buf;
}
module.exports = createPostTable;

View File

@ -0,0 +1,129 @@
'use strict';
var _ = require('lodash');
var math = require('../math');
// Remove points, that looks like straight line
function simplify(contours, accuracy) {
return _.map(contours, function (contour) {
var i, curr, prev, next;
var p, pPrev, pNext;
// run from the end, to simplify array elements removal
for (i = contour.length - 2; i > 1; i--) {
prev = contour[i - 1];
next = contour[i + 1];
curr = contour[i];
// skip point (both oncurve & offcurve),
// if [prev,next] is straight line
if (prev.onCurve && next.onCurve) {
p = new math.Point(curr.x, curr.y);
pPrev = new math.Point(prev.x, prev.y);
pNext = new math.Point(next.x, next.y);
if (math.isInLine(pPrev, p, pNext, accuracy)) {
contour.splice(i, 1);
}
}
}
return contour;
});
}
// Remove interpolateable oncurve points
// Those should be in the middle of nebor offcurve points
function interpolate(contours, accuracy) {
return _.map(contours, function (contour) {
var resContour = [];
_.forEach(contour, function (point, idx) {
// Never skip first and last points
if (idx === 0 || idx === (contour.length - 1)) {
resContour.push(point);
return;
}
var prev = contour[idx - 1];
var next = contour[idx + 1];
var p, pPrev, pNext;
// skip interpolateable oncurve points (if exactly between previous and next offcurves)
if (!prev.onCurve && point.onCurve && !next.onCurve) {
p = new math.Point(point.x, point.y);
pPrev = new math.Point(prev.x, prev.y);
pNext = new math.Point(next.x, next.y);
if (pPrev.add(pNext).div(2).sub(p).dist() < accuracy) {
return;
}
}
// keep the rest
resContour.push(point);
});
return resContour;
});
}
function roundPoints(contours) {
return _.map(contours, function (contour) {
return _.map(contour, function (point) {
return { x: Math.round(point.x), y: Math.round(point.y), onCurve: point.onCurve };
});
});
}
// Remove closing point if it is the same as first point of contour.
// TTF doesn't need this point when drawing contours.
function removeClosingReturnPoints(contours) {
return _.map(contours, function (contour) {
var length = contour.length;
if (length > 1 &&
contour[0].x === contour[length - 1].x &&
contour[0].y === contour[length - 1].y) {
contour.splice(length - 1);
}
return contour;
});
}
function toRelative(contours) {
var prevPoint = { x: 0, y: 0 };
var resContours = [];
var resContour;
_.forEach(contours, function (contour) {
resContour = [];
resContours.push(resContour);
_.forEach(contour, function (point) {
resContour.push({
x: point.x - prevPoint.x,
y: point.y - prevPoint.y,
onCurve: point.onCurve
});
prevPoint = point;
});
});
return resContours;
}
function identifier(string, littleEndian) {
var result = 0;
for (var i = 0; i < string.length; i++) {
result = result << 8;
var index = littleEndian ? string.length - i - 1 : i;
result += string.charCodeAt(index);
}
return result;
}
module.exports.interpolate = interpolate;
module.exports.simplify = simplify;
module.exports.roundPoints = roundPoints;
module.exports.removeClosingReturnPoints = removeClosingReturnPoints;
module.exports.toRelative = toRelative;
module.exports.identifier = identifier;

50
static/js/ketcher2/node_modules/svg2ttf/lib/ucs2.js generated vendored Normal file
View File

@ -0,0 +1,50 @@
'use strict';
var _ = require('lodash');
// Taken from the punycode library
function ucs2encode(array) {
return _.map(array, function (value) {
var output = '';
if (value > 0xFFFF) {
value -= 0x10000;
output += String.fromCharCode(value >>> 10 & 0x3FF | 0xD800);
value = 0xDC00 | value & 0x3FF;
}
output += String.fromCharCode(value);
return output;
}).join('');
}
function ucs2decode(string) {
var output = [],
counter = 0,
length = string.length,
value,
extra;
while (counter < length) {
value = string.charCodeAt(counter++);
if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
// high surrogate, and there is a next character
extra = string.charCodeAt(counter++);
if ((extra & 0xFC00) === 0xDC00) { // low surrogate
output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
} else {
// unmatched surrogate; only append this code unit, in case the next
// code unit is the high surrogate of a surrogate pair
output.push(value);
counter--;
}
} else {
output.push(value);
}
}
return output;
}
module.exports = {
encode: ucs2encode,
decode: ucs2decode
};

74
static/js/ketcher2/node_modules/svg2ttf/package.json generated vendored Normal file
View File

@ -0,0 +1,74 @@
{
"_from": "svg2ttf@^4.1.0",
"_id": "svg2ttf@4.1.0",
"_inBundle": false,
"_integrity": "sha1-ggIuVovQPBq7Zo/djRXwCJVqDhA=",
"_location": "/svg2ttf",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "svg2ttf@^4.1.0",
"name": "svg2ttf",
"escapedName": "svg2ttf",
"rawSpec": "^4.1.0",
"saveSpec": null,
"fetchSpec": "^4.1.0"
},
"_requiredBy": [
"/gulp-svg2ttf"
],
"_resolved": "https://registry.npmjs.org/svg2ttf/-/svg2ttf-4.1.0.tgz",
"_shasum": "82022e568bd03c1abb668fdd8d15f008956a0e10",
"_spec": "svg2ttf@^4.1.0",
"_where": "/home/manfred/enviPath/ketcher2/ketcher/node_modules/gulp-svg2ttf",
"author": {
"name": "Sergey Batishchev",
"email": "sergej.batishchev@gmail.com"
},
"bin": {
"svg2ttf": "./svg2ttf.js"
},
"bugs": {
"url": "https://github.com/fontello/svg2ttf/issues"
},
"bundleDependencies": false,
"dependencies": {
"argparse": "^1.0.6",
"cubic2quad": "^1.0.0",
"lodash": "^4.6.1",
"microbuffer": "^1.0.0",
"svgpath": "^2.1.5",
"xmldom": "~0.1.22"
},
"deprecated": false,
"description": "Converts SVG font to TTF font",
"devDependencies": {
"eslint": "^4.1.0",
"mocha": "^3.0.0"
},
"files": [
"index.js",
"svg2ttf.js",
"lib/"
],
"homepage": "https://github.com/fontello/svg2ttf#readme",
"keywords": [
"font",
"ttf",
"svg",
"convertor"
],
"license": "MIT",
"name": "svg2ttf",
"repository": {
"type": "git",
"url": "git+https://github.com/fontello/svg2ttf.git"
},
"scripts": {
"lint": "eslint .",
"test": "npm run lint && mocha",
"update_fixture": "./svg2ttf.js --ts 1457357570703 test/fixtures/test.svg test/fixtures/test.ttf"
},
"version": "4.1.0"
}

85
static/js/ketcher2/node_modules/svg2ttf/svg2ttf.js generated vendored Executable file
View File

@ -0,0 +1,85 @@
#!/usr/bin/env node
/*
Author: Sergey Batishchev <sergej.batishchev@gmail.com>
Written for fontello.com project.
*/
/*eslint-disable no-console*/
'use strict';
var fs = require('fs');
var ArgumentParser = require('argparse').ArgumentParser;
var svg2ttf = require('./');
var parser = new ArgumentParser({
version: require('./package.json').version,
addHelp: true,
description: 'SVG to TTF font converter'
});
parser.addArgument(
[ '-c', '--copyright' ],
{
help: 'Copyright text',
required: false
}
);
parser.addArgument(
[ '--ts' ],
{
help: 'Override font creation time (Unix time stamp)',
required: false,
type: 'int'
}
);
parser.addArgument(
[ '--vs' ],
{
help: 'Override default font version string (Version 1.0), can be "x.y" or "Version x.y"',
required: false,
type: 'string'
}
);
parser.addArgument(
[ 'infile' ],
{
nargs: 1,
help: 'Input file'
}
);
parser.addArgument(
[ 'outfile' ],
{
nargs: 1,
help: 'Output file'
}
);
var args = parser.parseArgs();
var svg;
var options = {};
try {
svg = fs.readFileSync(args.infile[0], 'utf-8');
} catch (e) {
console.error("Can't open input file (%s)", args.infile[0]);
process.exit(1);
}
if (args.copyright) options.copyright = args.copyright;
if (args.ts !== null) options.ts = args.ts;
if (args.vs) options.version = args.vs;
fs.writeFileSync(args.outfile[0], new Buffer(svg2ttf(svg, options).buffer));