Skip to content
Merged
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
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,10 @@ See [`fixtures/sample`](fixtures/sample) for working examples.

Conversion decodes semicolon-terminated named references used by the bundled
fixtures, including common currency (`£`, `¢`, `¥`) and mathematical
(`∉`, `≤`, `≥`) references. The complete HTML Latin-1 set is
supported, so `½`, `×`, and `÷` become `½`, `×`, and `÷`.
(`∉`, `≤`, `≥`) references. It also supports the HTML 4 Greek-letter
set, common mathematical operators and relations, and directional arrows (for
example, `α`, `∑`, `≠`, and `→`). The complete HTML Latin-1
set is supported, so `½`, `×`, and `÷` become `½`, `×`, and `÷`.
Named references are case-sensitive: defined aliases such as `Á` decode,
while undefined spellings such as `Á` remain unchanged. Unknown named references remain
unchanged so fixture comparisons do not silently discard source text.
Expand Down Expand Up @@ -113,9 +115,10 @@ hexadecimal forms may omit the trailing semicolon, null, surrogate, and
out-of-range values become `�`, and the HTML C1 replacement table is applied.
Common named references, including HTML Latin-1 names such as `ç` and
`é`, `½`, `×`, and `÷` and symbols such as `™`,
`€`, and `“`, are decoded only
when the trailing semicolon is present and the name's case exactly matches a
defined reference. Unknown or differently cased named references are preserved.
`€`, and `“`, are supported alongside Greek letters, mathematical
operators and relations, and arrows. Named references are decoded only when
the trailing semicolon is present and the name's case exactly matches a defined
reference. Unknown or differently cased named references are preserved.

## Safety boundaries

Expand Down
16 changes: 15 additions & 1 deletion src/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,21 @@ const ENTITIES = new Map([
['agrave', 'à'], ['aacute', 'á'], ['acirc', 'â'], ['atilde', 'ã'], ['auml', 'ä'], ['aring', 'å'], ['aelig', 'æ'], ['ccedil', 'ç'],
['egrave', 'è'], ['eacute', 'é'], ['ecirc', 'ê'], ['euml', 'ë'], ['igrave', 'ì'], ['iacute', 'í'], ['icirc', 'î'], ['iuml', 'ï'],
['eth', 'ð'], ['ntilde', 'ñ'], ['ograve', 'ò'], ['oacute', 'ó'], ['ocirc', 'ô'], ['otilde', 'õ'], ['ouml', 'ö'], ['oslash', 'ø'],
['ugrave', 'ù'], ['uacute', 'ú'], ['ucirc', 'û'], ['uuml', 'ü'], ['yacute', 'ý'], ['thorn', 'þ'], ['yuml', 'ÿ']
['ugrave', 'ù'], ['uacute', 'ú'], ['ucirc', 'û'], ['uuml', 'ü'], ['yacute', 'ý'], ['thorn', 'þ'], ['yuml', 'ÿ'],
['Alpha', 'Α'], ['Beta', 'Β'], ['Gamma', 'Γ'], ['Delta', 'Δ'], ['Epsilon', 'Ε'], ['Zeta', 'Ζ'], ['Eta', 'Η'], ['Theta', 'Θ'],
['Iota', 'Ι'], ['Kappa', 'Κ'], ['Lambda', 'Λ'], ['Mu', 'Μ'], ['Nu', 'Ν'], ['Xi', 'Ξ'], ['Omicron', 'Ο'], ['Pi', 'Π'],
['Rho', 'Ρ'], ['Sigma', 'Σ'], ['Tau', 'Τ'], ['Upsilon', 'Υ'], ['Phi', 'Φ'], ['Chi', 'Χ'], ['Psi', 'Ψ'], ['Omega', 'Ω'],
['alpha', 'α'], ['beta', 'β'], ['gamma', 'γ'], ['delta', 'δ'], ['epsilon', 'ε'], ['zeta', 'ζ'], ['eta', 'η'], ['theta', 'θ'],
['iota', 'ι'], ['kappa', 'κ'], ['lambda', 'λ'], ['mu', 'μ'], ['nu', 'ν'], ['xi', 'ξ'], ['omicron', 'ο'], ['pi', 'π'],
['rho', 'ρ'], ['sigmaf', 'ς'], ['sigma', 'σ'], ['tau', 'τ'], ['upsilon', 'υ'], ['phi', 'φ'], ['chi', 'χ'], ['psi', 'ψ'],
['omega', 'ω'], ['thetasym', 'ϑ'], ['upsih', 'ϒ'], ['piv', 'ϖ'],
['forall', '∀'], ['part', '∂'], ['exist', '∃'], ['empty', '∅'], ['nabla', '∇'], ['isin', '∈'], ['ni', '∋'], ['prod', '∏'],
['sum', '∑'], ['minus', '−'], ['lowast', '∗'], ['radic', '√'], ['prop', '∝'], ['infin', '∞'], ['ang', '∠'], ['and', '∧'],
['or', '∨'], ['cap', '∩'], ['cup', '∪'], ['int', '∫'], ['there4', '∴'], ['sim', '∼'], ['cong', '≅'], ['asymp', '≈'],
['ne', '≠'], ['equiv', '≡'], ['sub', '⊂'], ['sup', '⊃'], ['nsub', '⊄'], ['sube', '⊆'], ['supe', '⊇'], ['oplus', '⊕'],
['otimes', '⊗'], ['perp', '⊥'], ['sdot', '⋅'],
['larr', '←'], ['uarr', '↑'], ['rarr', '→'], ['darr', '↓'], ['harr', '↔'], ['crarr', '↵'],
['lArr', '⇐'], ['uArr', '⇑'], ['rArr', '⇒'], ['dArr', '⇓'], ['hArr', '⇔']
]);

const NUMERIC_REFERENCE_REPLACEMENTS = new Map([
Expand Down
18 changes: 18 additions & 0 deletions test/html.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,24 @@ test('decodeEntities handles common currency and math references', () => {
assert.equal(decodeEntities('£ ¢ ¥ ∉ ≤ ≥'), '£ ¢ ¥ ∉ ≤ ≥');
});

test('decodeEntities handles common Greek, mathematical, and arrow references', () => {
const cases = [
['Greek letters', 'Α Ω α β ω', 'Α Ω α β ω'],
['mathematical operators', '∀ ∂ ∏ ∑ √ ∫', '∀ ∂ ∏ ∑ √ ∫'],
['mathematical relations', '∈ ∉ ≠ ≡ ⊆ ⊥', '∈ ∉ ≠ ≡ ⊆ ⊥'],
['directional arrows', '← ↑ → ↓ ↔ ⇒', '← ↑ → ↓ ↔ ⇒']
];

for (const [name, input, expected] of cases) assert.equal(decodeEntities(input), expected, name);
});

test('convertHtmlToText exposes common named-reference symbols', () => {
assert.equal(
convertHtmlToText('<p>&alpha; + &beta; &ne; &sum; &rarr; &Omega;</p>').text,
'α + β ≠ ∑ → Ω'
);
});

test('decodeEntities preserves undefined reference spellings case-sensitively', () => {
assert.equal(decodeEntities('&Aacute; &aacute; &AACUTE; &POUND; &NotIn; &madeup;'), 'Á á &AACUTE; &POUND; &NotIn; &madeup;');
});
Expand Down