Repository URL to install this package:
|
Version:
1.100.3-1748872405 ▾
|
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Merges the provided trees into a single lookup tree. When conflicting lookups
* are encountered between two trees, the one with the lower index, then the
* lower subindex is chosen.
*
* @param trees Array of trees to merge. Entries in earlier trees are favored
* over those in later trees when there is a choice.
*/
function mergeTrees(trees) {
const result = {
individual: {},
range: []
};
for (const tree of trees) {
mergeSubtree(result, tree);
}
return result;
}
exports.default = mergeTrees;
/**
* Recursively merges the data for the mergeTree into the mainTree.
*
* @param mainTree The tree where the values should be merged
* @param mergeTree The tree to be merged into the mainTree
*/
function mergeSubtree(mainTree, mergeTree) {
// Need to fix this recursively (and handle lookups)
for (const [glyphId, value] of Object.entries(mergeTree.individual)) {
// The main tree is guaranteed to have no overlaps between the
// individual and range values, so if we match an invididual, there
// must not be a range
if (mainTree.individual[glyphId]) {
mergeTreeEntry(mainTree.individual[glyphId], value);
}
else {
let matched = false;
for (const [index, { range, entry }] of mainTree.range.entries()) {
const overlap = getIndividualOverlap(Number(glyphId), range);
// Don't overlap
if (overlap.both === null) {
continue;
}
matched = true;
// If they overlap, we have to split the range and then
// merge the overlap
mainTree.individual[glyphId] = value;
mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry));
// When there's an overlap, we also have to fix up the range
// that we had already processed
mainTree.range.splice(index, 1);
for (const glyph of overlap.second) {
if (Array.isArray(glyph)) {
mainTree.range.push({
range: glyph,
entry: cloneEntry(entry)
});
}
else {
mainTree.individual[glyph] = cloneEntry(entry);
}
}
}
if (!matched) {
mainTree.individual[glyphId] = value;
}
}
}
for (const { range, entry } of mergeTree.range) {
// Ranges are more complicated, because they can overlap with
// multiple things, individual and range alike. We start by
// eliminating ranges that are already present in another range
let remainingRanges = [range];
for (let index = 0; index < mainTree.range.length; index++) {
const { range, entry: resultEntry } = mainTree.range[index];
for (const [remainingIndex, remainingRange] of remainingRanges.entries()) {
if (Array.isArray(remainingRange)) {
const overlap = getRangeOverlap(remainingRange, range);
if (overlap.both === null) {
continue;
}
mainTree.range.splice(index, 1);
index--;
const entryToMerge = cloneEntry(resultEntry);
if (Array.isArray(overlap.both)) {
mainTree.range.push({
range: overlap.both,
entry: entryToMerge
});
}
else {
mainTree.individual[overlap.both] = entryToMerge;
}
mergeTreeEntry(entryToMerge, cloneEntry(entry));
for (const second of overlap.second) {
if (Array.isArray(second)) {
mainTree.range.push({
range: second,
entry: cloneEntry(resultEntry)
});
}
else {
mainTree.individual[second] = cloneEntry(resultEntry);
}
}
remainingRanges = overlap.first;
}
else {
const overlap = getIndividualOverlap(remainingRange, range);
if (overlap.both === null) {
continue;
}
// If they overlap, we have to split the range and then
// merge the overlap
mainTree.individual[remainingRange] = cloneEntry(entry);
mergeTreeEntry(mainTree.individual[remainingRange], cloneEntry(resultEntry));
// When there's an overlap, we also have to fix up the range
// that we had already processed
mainTree.range.splice(index, 1);
index--;
for (const glyph of overlap.second) {
if (Array.isArray(glyph)) {
mainTree.range.push({
range: glyph,
entry: cloneEntry(resultEntry)
});
}
else {
mainTree.individual[glyph] = cloneEntry(resultEntry);
}
}
remainingRanges.splice(remainingIndex, 1, ...overlap.first);
break;
}
}
}
// Next, we run the same against any individual glyphs
for (const glyphId of Object.keys(mainTree.individual)) {
for (const [remainingIndex, remainingRange] of remainingRanges.entries()) {
if (Array.isArray(remainingRange)) {
const overlap = getIndividualOverlap(Number(glyphId), remainingRange);
if (overlap.both === null) {
continue;
}
// If they overlap, we have to merge the overlap
mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry));
// Update the remaining ranges
remainingRanges.splice(remainingIndex, 1, ...overlap.second);
break;
}
else {
if (Number(glyphId) === remainingRange) {
mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry));
break;
}
}
}
}
// Any remaining ranges should just be added directly
for (const remainingRange of remainingRanges) {
if (Array.isArray(remainingRange)) {
mainTree.range.push({
range: remainingRange,
entry: cloneEntry(entry)
});
}
else {
mainTree.individual[remainingRange] = cloneEntry(entry);
}
}
}
}
/**
* Recursively merges the entry forr the mergeTree into the mainTree
*
* @param mainTree The entry where the values should be merged
* @param mergeTree The entry to merge into the mainTree
*/
function mergeTreeEntry(mainTree, mergeTree) {
if (mergeTree.lookup && (!mainTree.lookup ||
mainTree.lookup.index > mergeTree.lookup.index ||
(mainTree.lookup.index === mergeTree.lookup.index && mainTree.lookup.subIndex > mergeTree.lookup.subIndex))) {
mainTree.lookup = mergeTree.lookup;
}
if (mergeTree.forward) {
if (!mainTree.forward) {
mainTree.forward = mergeTree.forward;
}
else {
mergeSubtree(mainTree.forward, mergeTree.forward);
}
}
if (mergeTree.reverse) {
if (!mainTree.reverse) {
mainTree.reverse = mergeTree.reverse;
}
else {
mergeSubtree(mainTree.reverse, mergeTree.reverse);
}
}
}
/**
* Determines the overlap (if any) between two ranges. Returns the distinct
* ranges for each range and the overlap (if any).
*
* @param first First range
* @param second Second range
*/
function getRangeOverlap(first, second) {
const result = {
first: [],
second: [],
both: null
};
// Both
if (first[0] < second[1] && second[0] < first[1]) {
const start = Math.max(first[0], second[0]);
const end = Math.min(first[1], second[1]);
result.both = rangeOrIndividual(start, end);
}
// Before
if (first[0] < second[0]) {
const start = first[0];
const end = Math.min(second[0], first[1]);
result.first.push(rangeOrIndividual(start, end));
}
else if (second[0] < first[0]) {
const start = second[0];
const end = Math.min(second[1], first[0]);
result.second.push(rangeOrIndividual(start, end));
}
// After
if (first[1] > second[1]) {
const start = Math.max(first[0], second[1]);
const end = first[1];
result.first.push(rangeOrIndividual(start, end));
}
else if (second[1] > first[1]) {
const start = Math.max(first[1], second[0]);
const end = second[1];
result.second.push(rangeOrIndividual(start, end));
}
return result;
}
/**
* Determines the overlap (if any) between the individual glyph and the range
* provided. Returns the glyphs and/or ranges that are unique to each provided
* and the overlap (if any).
*
* @param first Individual glyph
* @param second Range
*/
function getIndividualOverlap(first, second) {
// Disjoint
if (first < second[0] || first > second[1]) {
return {
first: [first],
second: [second],
both: null
};
}
const result = {
first: [],
second: [],
both: first
};
if (second[0] < first) {
result.second.push(rangeOrIndividual(second[0], first));
}
if (second[1] > first) {
result.second.push(rangeOrIndividual(first + 1, second[1]));
}
return result;
}
/**
* Returns an individual glyph if the range is of size one or a range if it is
* larger.
*
* @param start Beginning of the range (inclusive)
* @param end End of the range (exclusive)
*/
function rangeOrIndividual(start, end) {
if (end - start === 1) {
return start;
}
else {
return [start, end];
}
}
/**
* Clones an individual lookup tree entry.
*
* @param entry Lookup tree entry to clone
*/
function cloneEntry(entry) {
const result = {};
if (entry.forward) {
result.forward = cloneTree(entry.forward);
}
if (entry.reverse) {
result.reverse = cloneTree(entry.reverse);
}
if (entry.lookup) {
result.lookup = {
contextRange: entry.lookup.contextRange.slice(),
index: entry.lookup.index,
length: entry.lookup.length,
subIndex: entry.lookup.subIndex,
substitutions: entry.lookup.substitutions.slice()
};
}
return result;
}
/**
* Clones a lookup tree.
*
* @param tree Lookup tree to clone
*/
function cloneTree(tree) {
const individual = {};
for (const [glyphId, entry] of Object.entries(tree.individual)) {
individual[glyphId] = cloneEntry(entry);
}
return {
individual,
range: tree.range.map(({ range, entry }) => ({
range: range.slice(),
entry: cloneEntry(entry)
}))
};
}//# sourceMappingURL=https://main.vscode-cdn.net/sourcemaps/258e40fedc6cb8edf399a463ce3a9d32e7e1f6f3/node_modules/font-ligatures/dist/merge.js.map