From 45bfe6ee715c90ee64ffbc90dd048ccbf606d97d Mon Sep 17 00:00:00 2001 From: John Factotum <50942278+johnfactotum@users.noreply.github.com> Date: Tue, 21 Mar 2023 12:03:39 +0800 Subject: [PATCH] Fix whitespace normalization - Normalize ASCII whitespace only - Collapse sequence of *one* or more consecutive whitespace characters --- epub.js | 10 +++++++--- fb2.js | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/epub.js b/epub.js index 7df3613..65cda41 100644 --- a/epub.js +++ b/epub.js @@ -26,8 +26,12 @@ const MIME = { // convert to camel case const camel = x => x.toLowerCase().replace(/[-:](.)/g, (_, g) => g.toUpperCase()) -// remove leading, trailing, and excess internal whitespace -const whitespacePreLine = str => str ? str.trim().replace(/\s{2,}/g, ' ') : '' +// strip and collapse ASCII whitespace +// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace +const normalizeWhitespace = str => str ? str + .replace(/[\t\n\f\r ]+/g, ' ') + .replace(/^[\t\n\f\r ]+/, '') + .replace(/[\t\n\f\r ]+$/, '') : '' const filterAttribute = (attr, value, isList) => isList ? el => el.getAttribute(attr)?.split(/\s/)?.includes(value) @@ -38,7 +42,7 @@ const filterAttribute = (attr, value, isList) => isList const getAttributes = (...xs) => el => el ? Object.fromEntries(xs.map(x => [camel(x), el.getAttribute(x)])) : null -const getElementText = el => whitespacePreLine(el?.textContent) +const getElementText = el => normalizeWhitespace(el?.textContent) const childGetter = (doc, ns) => { // ignore the namespace if it doesn't appear in document at all diff --git a/fb2.js b/fb2.js index f99e8c2..b85277b 100644 --- a/fb2.js +++ b/fb2.js @@ -1,5 +1,8 @@ -const trim = str => str?.trim()?.replace(/\s{2,}/g, ' ') -const getElementText = el => trim(el?.textContent) +const normalizeWhitespace = str => str ? str + .replace(/[\t\n\f\r ]+/g, ' ') + .replace(/^[\t\n\f\r ]+/, '') + .replace(/[\t\n\f\r ]+$/, '') : '' +const getElementText = el => normalizeWhitespace(el?.textContent) const NS = { XLINK: 'http://www.w3.org/1999/xlink', @@ -287,7 +290,8 @@ export const makeFB2 = async blob => { const str = template(el.outerHTML) const blob = new Blob([str], { type: MIME.XHTML }) const url = URL.createObjectURL(blob) - const title = trim(el.querySelector('.title, .subtitle, p')?.textContent + const title = normalizeWhitespace( + el.querySelector('.title, .subtitle, p')?.textContent ?? (el.classList.contains('title') ? el.textContent : '')) return { ids, title, titles, load: () => url,