harfbuzzjs
Advanced tools
Comparing version 0.3.6 to 0.4.0
173
hbjs.js
@@ -1,13 +0,20 @@ | ||
function hbjs(instance) { | ||
function hbjs(Module) { | ||
'use strict'; | ||
var exports = instance.exports; | ||
var heapu8 = new Uint8Array(exports.memory.buffer); | ||
var heapu32 = new Uint32Array(exports.memory.buffer); | ||
var heapi32 = new Int32Array(exports.memory.buffer); | ||
var heapf32 = new Float32Array(exports.memory.buffer); | ||
var exports = Module.wasmExports; | ||
var heapu8 = Module.HEAPU8; | ||
var heapu32 = Module.HEAPU32; | ||
var heapi32 = Module.HEAP32; | ||
var heapf32 = Module.HEAPF32; | ||
var utf8Decoder = new TextDecoder("utf8"); | ||
let addFunction = Module.addFunction; | ||
var freeFuncPtr = addFunction(function (ptr) { exports.free(ptr); }, 'vi'); | ||
var HB_MEMORY_MODE_WRITABLE = 2; | ||
var HB_SET_VALUE_INVALID = -1; | ||
var HB_BUFFER_CONTENT_TYPE_GLYPHS = 2; | ||
var DONT_STOP = 0; | ||
var GSUB_PHASE = 1; | ||
var GPOS_PHASE = 2; | ||
@@ -23,2 +30,5 @@ function hb_tag(s) { | ||
var HB_BUFFER_SERIALIZE_FORMAT_JSON = hb_tag('JSON'); | ||
var HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES = 4; | ||
function _hb_untag(tag) { | ||
@@ -50,3 +60,3 @@ return [ | ||
heapu8.set(new Uint8Array(blob), blobPtr); | ||
var ptr = exports.hb_blob_create(blobPtr, blob.byteLength, HB_MEMORY_MODE_WRITABLE, blobPtr, exports.free_ptr()); | ||
var ptr = exports.hb_blob_create(blobPtr, blob.byteLength, HB_MEMORY_MODE_WRITABLE, blobPtr, freeFuncPtr); | ||
return { | ||
@@ -160,4 +170,3 @@ ptr: ptr, | ||
var pathBufferSize = 65536; // should be enough for most glyphs | ||
var pathBuffer = exports.malloc(pathBufferSize); // permanently allocated | ||
var pathBuffer = ""; | ||
@@ -173,2 +182,3 @@ var nameBufferSize = 256; // should be enough for most glyphs | ||
var ptr = exports.hb_font_create(face.ptr); | ||
var drawFuncsPtr = null; | ||
@@ -180,4 +190,35 @@ /** | ||
function glyphToPath(glyphId) { | ||
var svgLength = exports.hbjs_glyph_svg(ptr, glyphId, pathBuffer, pathBufferSize); | ||
return svgLength > 0 ? utf8Decoder.decode(heapu8.subarray(pathBuffer, pathBuffer + svgLength)) : ""; | ||
if (!drawFuncsPtr) { | ||
var moveTo = function (dfuncs, draw_data, draw_state, to_x, to_y, user_data) { | ||
pathBuffer += `M${to_x},${to_y}`; | ||
} | ||
var lineTo = function (dfuncs, draw_data, draw_state, to_x, to_y, user_data) { | ||
pathBuffer += `L${to_x},${to_y}`; | ||
} | ||
var cubicTo = function (dfuncs, draw_data, draw_state, c1_x, c1_y, c2_x, c2_y, to_x, to_y, user_data) { | ||
pathBuffer += `C${c1_x},${c1_y} ${c2_x},${c2_y} ${to_x},${to_y}`; | ||
} | ||
var quadTo = function (dfuncs, draw_data, draw_state, c_x, c_y, to_x, to_y, user_data) { | ||
pathBuffer += `Q${c_x},${c_y} ${to_x},${to_y}`; | ||
} | ||
var closePath = function (dfuncs, draw_data, draw_state, user_data) { | ||
pathBuffer += 'Z'; | ||
} | ||
var moveToPtr = addFunction(moveTo, 'viiiffi'); | ||
var lineToPtr = addFunction(lineTo, 'viiiffi'); | ||
var cubicToPtr = addFunction(cubicTo, 'viiiffffffi'); | ||
var quadToPtr = addFunction(quadTo, 'viiiffffi'); | ||
var closePathPtr = addFunction(closePath, 'viiii'); | ||
drawFuncsPtr = exports.hb_draw_funcs_create(); | ||
exports.hb_draw_funcs_set_move_to_func(drawFuncsPtr, moveToPtr, 0, 0); | ||
exports.hb_draw_funcs_set_line_to_func(drawFuncsPtr, lineToPtr, 0, 0); | ||
exports.hb_draw_funcs_set_cubic_to_func(drawFuncsPtr, cubicToPtr, 0, 0); | ||
exports.hb_draw_funcs_set_quadratic_to_func(drawFuncsPtr, quadToPtr, 0, 0); | ||
exports.hb_draw_funcs_set_close_path_func(drawFuncsPtr, closePathPtr, 0, 0); | ||
} | ||
pathBuffer = ""; | ||
exports.hb_font_draw_glyph(ptr, glyphId, drawFuncsPtr, 0); | ||
return pathBuffer; | ||
} | ||
@@ -267,3 +308,3 @@ | ||
const ptr = exports.malloc(text.length * 2); | ||
const words = new Uint16Array(exports.memory.buffer, ptr, text.length); | ||
const words = new Uint16Array(Module.wasmMemory.buffer, ptr, text.length); | ||
for (let i = 0; i < words.length; ++i) words[i] = text.charCodeAt(i); | ||
@@ -410,6 +451,21 @@ return { | ||
* prepared. | ||
* @param {object} features: (Currently unused). | ||
* @param {object} features: A string of comma-separated OpenType features to apply. | ||
*/ | ||
function shape(font, buffer, features) { | ||
exports.hb_shape(font.ptr, buffer.ptr, 0, 0); | ||
var featuresPtr = 0; | ||
var featuresLen = 0; | ||
if (features) { | ||
features = features.split(","); | ||
featuresPtr = exports.malloc(16 * features.length); | ||
features.forEach(function (feature, i) { | ||
var str = createAsciiString(feature); | ||
if (exports.hb_feature_from_string(str.ptr, -1, featuresPtr + featuresLen * 16)) | ||
featuresLen++; | ||
str.free(); | ||
}); | ||
} | ||
exports.hb_shape(font.ptr, buffer.ptr, featuresPtr, featuresLen); | ||
if (featuresPtr) | ||
exports.free(featuresPtr); | ||
} | ||
@@ -428,3 +484,3 @@ | ||
* prepared. | ||
* @param {object} features: A dictionary of OpenType features to apply. | ||
* @param {object} features: A string of comma-separated OpenType features to apply. | ||
* @param {number} stop_at: A lookup ID at which to terminate shaping. | ||
@@ -435,14 +491,73 @@ * @param {number} stop_phase: Either 0 (don't terminate shaping), 1 (`stop_at` | ||
*/ | ||
function shapeWithTrace(font, buffer, features, stop_at, stop_phase) { | ||
var trace = []; | ||
var currentPhase = DONT_STOP; | ||
var stopping = false; | ||
var failure = false; | ||
function shapeWithTrace(font, buffer, features, stop_at, stop_phase) { | ||
var bufLen = 1024 * 1024; | ||
var traceBuffer = exports.malloc(bufLen); | ||
var featurestr = createAsciiString(features); | ||
var traceLen = exports.hbjs_shape_with_trace(font.ptr, buffer.ptr, featurestr.ptr, stop_at, stop_phase, traceBuffer, bufLen); | ||
featurestr.free(); | ||
var trace = utf8Decoder.decode(heapu8.subarray(traceBuffer, traceBuffer + traceLen - 1)); | ||
exports.free(traceBuffer); | ||
return JSON.parse(trace); | ||
var traceBufLen = 1024 * 1024; | ||
var traceBufPtr = exports.malloc(traceBufLen); | ||
var traceFunc = function (bufferPtr, fontPtr, messagePtr, user_data) { | ||
var message = utf8Decoder.decode(heapu8.subarray(messagePtr, heapu8.indexOf(0, messagePtr))); | ||
if (message.startsWith("start table GSUB")) | ||
currentPhase = GSUB_PHASE; | ||
else if (message.startsWith("start table GPOS")) | ||
currentPhase = GPOS_PHASE; | ||
if (currentPhase != stop_phase) | ||
stopping = false; | ||
if (failure) | ||
return 1; | ||
if (stop_phase != DONT_STOP && currentPhase == stop_phase && message.startsWith("end lookup " + stop_at)) | ||
stopping = true; | ||
if (stopping) | ||
return 0; | ||
exports.hb_buffer_serialize_glyphs( | ||
bufferPtr, | ||
0, exports.hb_buffer_get_length(bufferPtr), | ||
traceBufPtr, traceBufLen, 0, | ||
fontPtr, | ||
HB_BUFFER_SERIALIZE_FORMAT_JSON, | ||
HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES); | ||
trace.push({ | ||
m: message, | ||
t: JSON.parse(utf8Decoder.decode(heapu8.subarray(traceBufPtr, heapu8.indexOf(0, traceBufPtr)))), | ||
glyphs: exports.hb_buffer_get_content_type(bufferPtr) == HB_BUFFER_CONTENT_TYPE_GLYPHS, | ||
}); | ||
return 1; | ||
} | ||
var traceFuncPtr = addFunction(traceFunc, 'iiiii'); | ||
exports.hb_buffer_set_message_func(buffer.ptr, traceFuncPtr, 0, 0); | ||
shape(font, buffer, features, 0); | ||
exports.free(traceBufPtr); | ||
return trace; | ||
} | ||
function version() { | ||
var versionPtr = exports.malloc(12); | ||
exports.hb_version(versionPtr, versionPtr + 4, versionPtr + 8); | ||
var version = { | ||
major: heapu32[versionPtr / 4], | ||
minor: heapu32[(versionPtr + 4) / 4], | ||
micro: heapu32[(versionPtr + 8) / 4], | ||
}; | ||
exports.free(versionPtr); | ||
return version; | ||
} | ||
function version_string() { | ||
var versionPtr = exports.hb_version_string(); | ||
var version = utf8Decoder.decode(heapu8.subarray(versionPtr, heapu8.indexOf(0, versionPtr))); | ||
return version; | ||
} | ||
return { | ||
@@ -454,7 +569,11 @@ createBlob: createBlob, | ||
shape: shape, | ||
shapeWithTrace: shapeWithTrace | ||
shapeWithTrace: shapeWithTrace, | ||
version: version, | ||
version_string: version_string, | ||
}; | ||
}; | ||
} | ||
// Should be replaced with something more reliable | ||
try { module.exports = hbjs; } catch(e) {} | ||
try { | ||
module.exports = hbjs; | ||
} catch (e) {} |
14
index.js
@@ -1,12 +0,8 @@ | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var hbjs = require('./hbjs.js') | ||
var hbjs = require('./hbjs.js'); | ||
var hb = require('./hb.js'); | ||
module.exports = new Promise(function (resolve, reject) { | ||
fs.readFile(path.resolve(__dirname, 'hb.wasm'), function (err, data) { | ||
if (err) { reject(err); return; } | ||
WebAssembly.instantiate(data).then(function (result) { | ||
resolve(hbjs(result.instance)); | ||
}, reject); | ||
}); | ||
hb().then((instance) => { | ||
resolve(hbjs(instance)); | ||
}, reject); | ||
}); |
{ | ||
"name": "harfbuzzjs", | ||
"version": "0.3.6", | ||
"version": "0.4.0", | ||
"description": "Minimal version of HarfBuzz for JavaScript use", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -25,2 +25,4 @@ const fs = require('fs'); | ||
expect(codepoints).not.to.include('ا'.codePointAt(0)); | ||
this.face.destroy(); | ||
this.blob.destroy(); | ||
}); | ||
@@ -83,2 +85,31 @@ | ||
}); | ||
it('glyphToPath converts quadratic glyph to path', function () { | ||
this.blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSans-Regular.ttf'))); | ||
this.face = hb.createFace(this.blob); | ||
this.font = hb.createFont(this.face); | ||
const expected21 = 'M520,0L48,0L48,73L235,262Q289,316 326,358Q363,400 382,440.5Q401,481 401,529Q401,\ | ||
588 366,618.5Q331,649 275,649Q223,649 183.5,631Q144,613 103,581L56,640Q98,675 152.5,699.5Q207,724 275,\ | ||
724Q375,724 433,673.5Q491,623 491,534Q491,478 468,429Q445,380 404,332.5Q363,285 308,231L159,84L159,80L520,80L520,0Z'; | ||
expect(this.font.glyphToPath(21)).to.equal(expected21); | ||
const expected22 = 'M493,547Q493,475 453,432.5Q413,390 345,376L345,372Q431,362 473,318Q515,274 515,203Q515,\ | ||
141 486,92.5Q457,44 396.5,17Q336,-10 241,-10Q185,-10 137,-1.5Q89,7 45,29L45,111Q90,89 142,76.5Q194,64 242,64Q338,\ | ||
64 380.5,101.5Q423,139 423,205Q423,272 370.5,301.5Q318,331 223,331L154,331L154,406L224,406Q312,406 357.5,443Q403,\ | ||
480 403,541Q403,593 368,621.5Q333,650 273,650Q215,650 174,633Q133,616 93,590L49,650Q87,680 143.5,702Q200,724 272,\ | ||
724Q384,724 438.5,674Q493,624 493,547Z'; | ||
expect(this.font.glyphToPath(22)).to.equal(expected22); | ||
}); | ||
it('glyphToPath converts cubic glyph to path', function () { | ||
this.blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSans-Regular.otf'))); | ||
this.face = hb.createFace(this.blob); | ||
this.font = hb.createFont(this.face); | ||
const expected21 = 'M520,0L520,80L159,80L159,84L308,231C418,338 491,422 491,534C491,652 408,724 275,724C184,724 112,\ | ||
687 56,640L103,581C158,624 205,649 275,649C350,649 401,607 401,529C401,432 342,370 235,262L48,73L48,0L520,0Z'; | ||
expect(this.font.glyphToPath(21)).to.equal(expected21); | ||
const expected22 = 'M493,547C493,649 421,724 272,724C176,724 100,690 49,650L93,590C146,625 196,650 273,650C353,\ | ||
650 403,610 403,541C403,460 341,406 224,406L154,406L154,331L223,331C349,331 423,294 423,205C423,117 370,64 242,64C178,\ | ||
64 105,81 45,111L45,29C104,0 166,-10 241,-10C430,-10 515,78 515,203C515,297 459,358 345,372L345,376C435,394 493,451 493,547Z'; | ||
expect(this.font.glyphToPath(22)).to.equal(expected22); | ||
}); | ||
}); | ||
@@ -166,3 +197,3 @@ | ||
this.buffer.guessSegmentProperties(); | ||
const result = hb.shapeWithTrace(this.font, this.buffer, 0, 0) | ||
const result = hb.shapeWithTrace(this.font, this.buffer, "", 0, 0) | ||
expect(result).to.have.lengthOf(42); | ||
@@ -188,2 +219,49 @@ expect(result[0]).to.deep.equal({ | ||
}); | ||
it('shape with tracing and features', function () { | ||
this.blob = hb.createBlob(fs.readFileSync(path.join(__dirname, 'fonts/noto/NotoSans-Regular.ttf'))); | ||
this.face = hb.createFace(this.blob); | ||
this.font = hb.createFont(this.face); | ||
this.buffer = hb.createBuffer(); | ||
this.buffer.addText('fi AV'); | ||
this.buffer.guessSegmentProperties(); | ||
const result = hb.shapeWithTrace(this.font, this.buffer, "-liga,-kern", 0, 0) | ||
expect(result).to.have.lengthOf(29); | ||
expect(result[0]).to.deep.equal({ | ||
"m": "start table GSUB script tag 'latn'", | ||
"glyphs": true, | ||
"t": [ | ||
{cl: 0, g: 73}, | ||
{cl: 1, g: 76}, | ||
{cl: 2, g: 3}, | ||
{cl: 3, g: 36}, | ||
{cl: 4, g: 57}, | ||
], | ||
}); | ||
expect(result[28]).to.deep.equal({ | ||
"m": "end table GPOS script tag 'latn'", | ||
"glyphs": true, | ||
"t": [ | ||
{cl: 0, g: 73, ax: 344, ay: 0, dx: 0, dy: 0}, | ||
{cl: 1, g: 76, ax: 258, ay: 0, dx: 0, dy: 0}, | ||
{cl: 2, g: 3, ax: 260, ay: 0, dx: 0, dy: 0}, | ||
{cl: 3, g: 36, ax: 639, ay: 0, dx: 0, dy: 0}, | ||
{cl: 4, g: 57, ax: 600, ay: 0, dx: 0, dy: 0}, | ||
], | ||
}); | ||
}); | ||
}); | ||
describe('misc', function () { | ||
it('get version', function () { | ||
const version = hb.version(); | ||
expect(version).to.have.property('major').that.is.a('number'); | ||
expect(version).to.have.property('minor').that.is.a('number'); | ||
expect(version).to.have.property('micro').that.is.a('number'); | ||
expect(version.major).to.be.at.least(10); | ||
}); | ||
it('get version string', function () { | ||
const version_string = hb.version_string(); | ||
expect(version_string).to.match(/^\d+\.\d+\.\d+$/); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
2632144
28
1296
5
3