@publiwrite/html-to-epub
Version:
A library to generate EPUB from HTML. Inspired by epub-gen.
87 lines (86 loc) • 2.94 kB
JavaScript
import { visit } from 'unist-util-visit';
import { v4 as uuid } from 'uuid';
import mime from 'mime';
import { unified } from "unified";
import rehypeParse from "rehype-parse";
import rehypeStringify from "rehype-stringify";
const loadHtml = (content, plugins) => unified()
.use(rehypeParse, { fragment: true })
.use(plugins)
// Voids: [] is required for epub generation, and causes little/no harm for non-epub usage
.use(rehypeStringify, { allowDangerousHtml: true, voids: [] })
.processSync(content)
.toString();
const validateElements = (node, options, index) => {
const attrs = node.properties;
if (["img", "br", "hr"].includes(node.tagName)) {
if (node.tagName === "img") {
node.properties.alt = node.properties.alt || "image-placeholder";
}
}
for (const k of Object.keys(attrs)) {
if (options.allowedAttributes.includes(k)) {
if (k === "type") {
if (attrs[k] !== "script") {
delete node.properties[k];
}
}
}
else {
delete node.properties[k];
}
}
if (options.version === 2) {
if (!options.allowedXhtml11Tags.includes(node.tagName)) {
if (options.verbose) {
console.log("Warning (content[" + index + "]):", node.tagName, "tag isn't allowed on EPUB 2/XHTML 1.1 DTD.");
}
node.tagName = "div";
}
}
};
const processImgTags = (node, options, dir) => {
if (!["img", "input"].includes(node.tagName)) {
return;
}
const url = node.properties.src;
if (url === undefined || url === null) {
return;
}
let extension, id;
const image = options.images.find((element) => element.url === url);
if (image) {
id = image.id;
extension = image.extension;
}
else {
id = uuid();
const mediaType = mime.getType(url.replace(/\?.*/, ""));
if (mediaType === null) {
if (options.verbose) {
console.error("[Image Error]", `The image can't be processed : ${url}`);
}
return;
}
extension = mime.getExtension(mediaType);
if (extension === null) {
if (options.verbose) {
console.error("[Image Error]", `The image can't be processed : ${url}`);
}
return;
}
options.images.push({ id, url, dir, mediaType, extension });
}
node.properties.src = `images/${id}.${extension}`;
};
export const processHtmlContent = (content, options, dir) => {
const html = loadHtml(content, [
() => (tree) => {
visit(tree, "element", (node, index) => validateElements(node, options, index));
},
() => (tree) => {
visit(tree, "element", (node) => processImgTags(node, options, dir));
},
]);
return html;
};