Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ Options is an optional _{GlyphRenderOptions}_ object containing:
* `language`: language system used to determine which features to apply (default: `"dflt"`)
* `kerning`: if true takes kerning information into account (default: `true`)
* `features`: an object with [OpenType feature tags](https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags) as keys, and a boolean value to enable each feature.
Currently only ligature features `"liga"` and `"rlig"` are supported (default: `true`).
Currently only ligature features `"liga"`, `"rlig"` and `"calt"` are supported (default: `true`).
* `hinting`: if true uses TrueType font hinting if available (default: `false`).
* `colorFormat`: the format colors are converted to for rendering (default: `"hexa"`). Can be `"rgb"`/`"rgba"` for `rgb()`/`rgba()` output, `"hex"`/`"hexa"` for 6/8 digit hex colors, or `"hsl"`/`"hsla"` for `hsl()`/`hsla()` output. `"bgra"` outputs an object with r, g, b, a keys (r/g/b from 0-255, a from 0-1). `"raw"` outputs an integer as used in the CPAL table.
* `fill`: font color, the color used to render each glyph (default: `"black"`)
Expand Down
4 changes: 3 additions & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ <h1><a href="./">opentype.js</a></h1>
<label><input type="checkbox" name="drawMetrics">Draw Metrics</label>
<label><input type="checkbox" name="kerning" checked="checked">Kerning</label>
<label><input type="checkbox" name="ligatures" checked="checked">Ligatures</label>
<label><input type="checkbox" name="contextualAlternates" checked="checked">Contextual Alternates</label>
<label class="disabled"><input name="hinting" type="checkbox" disabled="true">Hinting</label>

<div id="variation-options"></div>
Expand Down Expand Up @@ -145,7 +146,8 @@ <h1>Free Software</h1>
hinting: form.hinting.checked,
features: {
liga: form.ligatures.checked,
rlig: form.ligatures.checked
rlig: form.ligatures.checked,
calt: form.contextualAlternates.checked
}
};
window.fontOptions = Object.assign({}, window.font.defaultRenderOptions, window.fontOptions, options);
Expand Down
18 changes: 17 additions & 1 deletion src/bidi.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ccmpReplacementCheck from './features/ccmp/contextCheck/ccmpReplacement.m
import ccmpReplacement from './features/ccmp/ccmpReplacementLigatures.mjs';
import latinWordCheck from './features/latn/contextCheck/latinWord.mjs';
import latinLigature from './features/latn/latinLigatures.mjs';
import latinContextualAlternates from './features/latn/latinContextualAlternates.mjs';
import thaiWordCheck from './features/thai/contextCheck/thaiWord.mjs';
import thaiGlyphComposition from './features/thai/thaiGlyphComposition.mjs';
import thaiLigatures from './features/thai/thaiLigatures.mjs';
Expand Down Expand Up @@ -202,6 +203,16 @@ function applyLatinLigatures() {
}
}

function applyLatinContextualAlternates() {
if (!this.hasFeatureEnabled('latn', 'calt')) return;
checkGlyphIndexStatus.call(this);
const ranges = this.tokenizer.getContextRanges('latinWord');
for (let i = 0; i < ranges.length; i++) {
const range = ranges[i];
latinContextualAlternates.call(this, range);
}
}

function applyUnicodeVariationSequences() {
const ranges = this.tokenizer.getContextRanges('unicodeVariationSequence');
for(let i = 0; i < ranges.length; i++) {
Expand Down Expand Up @@ -248,6 +259,7 @@ Bidi.prototype.applyFeaturesToContexts = function () {
}
if (this.checkContextReady('latinWord')) {
applyLatinLigatures.call(this);
applyLatinContextualAlternates.call(this);
}
if (this.checkContextReady('arabicSentence')) {
reverseArabicSentences.call(this);
Expand Down Expand Up @@ -303,7 +315,11 @@ Bidi.prototype.getTextGlyphs = function (text) {
const token = this.tokenizer.tokens[i];
if (token.state.deleted) continue;
const index = token.activeState.value;
indexes.push(Array.isArray(index) ? index[0] : index);
if (Array.isArray(index)) {
for (const g of index) indexes.push(g);
} else {
indexes.push(index);
}
}
return indexes;
};
Expand Down
19 changes: 17 additions & 2 deletions src/features/applySubstitution.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ function singleSubstitutionFormat2(action, tokens, index) {
tokens[index].setState(action.tag, action.substitution);
}

/**
* Apply multiple substitution format 1 (one glyph → sequence of glyphs).
* The full sequence is stored on the token; getTextGlyphs expands it into
* separate glyph indices, and latinContextualAlternates uses only the first
* element for context matching in subsequent lookups.
*/
function multipleSubstitutionFormat1(action, tokens, index) {
tokens[index].setState(action.tag, action.substitution);
}

/**
* Apply chaining context substitution format 3
* @param {Array} substitutions substitutions
Expand All @@ -32,8 +42,9 @@ function chainingSubstitutionFormat3(action, tokens, index) {
const token = tokens[index + i];
if (Array.isArray(subst)) {
if (subst.length){
// TODO: replace one glyph with multiple glyphs
token.setState(action.tag, subst[0]);
// Store the full multi-glyph sequence on the token so all
// fragment glyphs reach the output stream via getTextGlyphs.
token.setState(action.tag, subst);
} else {
token.setState('deleted', true);
}
Expand Down Expand Up @@ -65,9 +76,13 @@ function ligatureSubstitutionFormat1(action, tokens, index) {
const SUBSTITUTIONS = {
11: singleSubstitutionFormat1,
12: singleSubstitutionFormat2,
21: multipleSubstitutionFormat1,
61: chainingSubstitutionFormat3,
62: chainingSubstitutionFormat3,
63: chainingSubstitutionFormat3,
41: ligatureSubstitutionFormat1,
51: chainingSubstitutionFormat3,
52: chainingSubstitutionFormat3,
53: chainingSubstitutionFormat3
};

Expand Down
Loading