unicode-shaper
Version:
Shape unicode text so that renderers like WebGL and WebGPU can properly display the glyphs.
129 lines • 4.62 kB
JavaScript
// https://www.unicode.org/charts/PDF/U1A00.pdf
// https://r12a.github.io/scripts/bugi/bug.html
import { WHITESPACE } from './index.js';
import { buildClusters, buildDefinitions, commonGetSorted } from './shared.js';
/**
* Check if a character is a "buginese" unicode character
* @param c - input unicode character
* @returns - True if buginese
*/
export function isBuginese(c) {
return c >= 0x1a00 && c <= 0x1a1f;
}
/** The type of a character in a Buginese run */
var MType;
(function (MType) {
MType[MType["C"] = 0] = "C";
MType[MType["GB"] = 1] = "GB";
MType[MType["J"] = 2] = "J";
MType[MType["O"] = 3] = "O";
MType[MType["R"] = 4] = "R";
MType[MType["S"] = 5] = "S";
MType[MType["VAbv"] = 6] = "VAbv";
MType[MType["VBlw"] = 7] = "VBlw";
MType[MType["VPre"] = 8] = "VPre";
MType[MType["VPst"] = 9] = "VPst";
MType[MType["VS"] = 10] = "VS";
MType[MType["WJ"] = 11] = "WJ";
MType[MType["NJ"] = 12] = "NJ";
MType[MType["WS"] = 13] = "WS";
})(MType || (MType = {}));
/**
* Find the type of a buginese character
* NOTE: isSpecialSequence is for K, if true, '103A, 1039' come after c
* @param c - input unicode character
* @returns - The type of the character
*/
function toMType(c) {
// match c {
// Consonants (1A00-1A16)
if (c >= 0x1a00 && c <= 0x1a16)
return MType.C;
// Generic base characters (00A0, 00D7, 2012–2015, 2022, 25CC, 25FB–25FE)
if (c === 0x00a0 ||
c === 0x00d7 ||
(c >= 0x2012 && c <= 0x2015) ||
c === 0x2022 ||
c === 0x25cc ||
(c >= 0x25fb && c <= 0x25fe))
return MType.GB;
// Joiners (200C, 200D)
if (c === 0x200d || c === 0x034f)
return MType.J;
// Reserved characters from the Buginese block (1A1C, 1A1D)
if (c === 0x1a1c || c === 0x1a1d)
return MType.R;
// Symbols (1A1E, 1A1F, A9CF)
if (c === 0x1a1e || c === 0x1a1f || c === 0xa9cf)
return MType.S;
// Above base dependent vowel (1A17, 1A1B)
if (c === 0x1a17 || c === 0x1a1b)
return MType.VAbv;
// Below base dependent vowel (1A18)
if (c === 0x1a18)
return MType.VBlw;
// Pre base dependent vowel (1A19)
if (c === 0x1a19)
return MType.VPre;
// Post base dependent vowel (1A1A)
if (c === 0x1a1a)
return MType.VPst;
// Variation selectors (FE00–FE0F)
if (c >= 0xfe00 && c <= 0xfe0f)
return MType.VS;
// Word joiner (2060)
if (c === 0x2060)
return MType.WJ;
// Non-joiner (200C) [Zero Width Non-Joiner]
if (c === 0x200c)
return MType.NJ;
if (WHITESPACE.includes(c))
return MType.WS;
return MType.O;
}
/**
* Once the Buginese shaping engine has analyzed the run into
* clusters as described above, it performs any required reordering.
* Pre-base vowels (VPre) are reordered to the start of the syllable
* cluster. A sequence of multiple pre-base vowels is permitted.
* Such sequences are moved as a block to the beginning of the cluster.
* In the following example, the run of code points represents a
* single cluster.
* @param cluster - cluster of definitions
* @returns - Returns the cluster sorted by dominant type
*/
function getSorted(cluster) {
return commonGetSorted(cluster, MType);
}
/**
* Shape/Reordering characters
* The shaping engine inserts a placeholder glyph (U+25CC) wherever
* combining marks occur without a valid base. The character U+25CC
* belongs to the class of generic bases (GB). Well-formed Buginese
* character clusters are defined as follows:
*
* Cases:
* 1) Simple non-compounding cluster: < S | Rsv | WS | O | J | WJ >
* 2) Clusters: < C | GB > [VS] (VPre)* (VAbv)* (VBlv)* (VPst)* [J]
*
* Ex. ᨔᨗᨔᨗᨊᨗᨊ
* @param input - array of unicode characters to be shaped in place if the input contains buginese
*/
export function shapeBuginese(input) {
const res = [];
// Step 1: Convert input to clusters
const defs = buildDefinitions(input, toMType);
// Step 2: Split clusters by WS (white space)
const clustersSets = buildClusters(defs, (mType) => mType === MType.WS || mType === MType.NJ);
// Step 3: Reorder the clusters and add them to result
clustersSets.forEach((c) => {
res.push(...getSorted(c));
// append whitespace of cluster if it exists
if (c.whitespace !== undefined)
res.push(c.whitespace);
});
// now map the result to the original input
for (let i = 0; i < input.length; i++)
input[i] = res[i];
}
//# sourceMappingURL=buginese.js.map