UNPKG

unicode-shaper

Version:

Shape unicode text so that renderers like WebGL and WebGPU can properly display the glyphs.

130 lines 4.21 kB
import { DEFAULT_OPTIONS } from './shape/internal.js'; import wasmBase64 from './uShaper.wasm.js'; /** * WASM based text shaper */ export class WasmTextShaper { instance; wasmMemory; tmpString = ''; /** Construct the WASM instance */ constructor() { const mod = new WebAssembly.Module(base64ToArrayBuffer(wasmBase64)); this.instance = new WebAssembly.Instance(mod, { env: { /** * Set a unicode array * @param ptr - pointer * @param len - length */ setUnicodeArray: (ptr, len) => { this.tmpString = ''; if (len === 0 || ptr < 0) return; const buf = this.#get(ptr, len); for (let i = 0; i < len; i++) this.tmpString += String.fromCharCode(buf[i]); }, }, }); } /** * Converts a string into a shaped string * @param str - input string * @param options - shaping options * @returns - shaped string */ shapeString(str, options = DEFAULT_OPTIONS) { const processText = this.instance.exports.processText; const free = this.instance.exports.free; if (str.length === 0) return str; const len = str.length; // NOTE: putString allocates memory, but processText will free it for us const ptr = this.#putString(str); processText(ptr, len, options); free(ptr, len); return this.tmpString; } /** * Check if a character is a "right-to-left" unicode character * @param unicode - input unicode character * @returns - True if right-to-left */ isRTL(unicode) { const isRTL = this.instance.exports.isRTL; return isRTL(unicode) === 1; } /** * Check if a character is CJK (Chinese, Japanese, or Korean) * @param unicode - input unicode character * @returns - True if CJK */ isCJK(unicode) { const isCJK = this.instance.exports.isCJK; return isCJK(unicode) === 1; } /** * Converts a string into a unicode array inside the WASM memory. Returns the pointer * @param str - input string * @returns - pointer */ #putString(str) { const len = str.length; const buf = new Uint16Array(len); const ptr = this.#allocUnicodeArray(len); for (let i = 0; i < len; i++) buf[i] = str.charCodeAt(i); const view = this.#getMemory(); view.subarray(ptr, ptr + len * 2).set(new Uint8Array(buf.buffer)); return ptr; } /** * Reads a unicode array from the WASM memory * @param ptr - pointer * @param len - length * @returns - unicode array */ #get(ptr, len) { const view = this.#getMemory(); const view16 = new Uint16Array(view.buffer, ptr, len); const copy = new Uint16Array(len); for (let i = 0; i < len; i++) copy[i] = view16[i]; return copy; } /** * Allocates a unicode array * @param size - size of array * @returns - pointer to array memory */ #allocUnicodeArray(size) { const allocUnicodeArray = this.instance.exports.allocUnicodeArray; return allocUnicodeArray(size); } /** * Returns the WASM memory. Rebuilds pointer if needed * @returns - WASM memory */ #getMemory() { const memory = this.instance.exports.memory; if (this.wasmMemory === undefined || this.wasmMemory.buffer !== memory.buffer) { this.wasmMemory = new Uint8Array(memory.buffer); } return this.wasmMemory; } } /** * polyfill to convert wasm base64 binary to ArrayBuffer * @param base64 - input base64 string * @returns - ArrayBuffer */ function base64ToArrayBuffer(base64) { const binaryString = atob(base64); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) bytes[i] = binaryString.charCodeAt(i); return bytes.buffer; } //# sourceMappingURL=textShaperWasm.js.map