react-pdf-html-flabs-2
Version:
Html component for react-pdf with CSS support optimized
177 lines • 7.75 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertNode = exports.convertElementStyle = exports.convertStylesheet = exports.convertRule = void 0;
const node_html_parser_1 = require("node-html-parser");
const css_tree_1 = __importDefault(require("css-tree"));
const supportedStyles_1 = __importDefault(require("./supportedStyles"));
const htmlOverrides_1 = require("./htmlOverrides");
const camelize = require('camelize');
const convertRule = (rule, source = 'style') => {
const declarations = rule.children
.filter((declaration) => declaration.type === 'Declaration')
.toArray();
return declarations
.map((entry) => (Object.assign(Object.assign({}, entry), { property: camelize(entry.property) })))
.reduce((style, { property, value }) => {
let valueString = css_tree_1.default.generate(value);
if (property && value !== undefined) {
if (property == 'fontFamily') {
valueString = valueString.replace(/["']+/g, '');
if (valueString.includes(',')) {
const reduced = valueString.split(',', 2)[0];
// console.warn(
// `react-pdf doesn't support fontFamily lists like "${valueString}". Reducing to "${reduced}".`
// );
}
return style;
}
else if (!supportedStyles_1.default.includes(property)) {
if ((property === 'background' &&
/^#?[a-zA-Z0-9]+$/.test(valueString)) ||
/^rgba?\([0-9, ]+\)$/i.test(valueString) ||
/^hsla?\([0-9.%, ]+\)$/i.test(valueString)) {
property = 'backgroundColor';
}
else {
// console.warn(`${source}: Found unsupported style "${property}"`, {
// property,
// value,
// });
return style;
}
}
if (property.startsWith('border')) {
if (typeof valueString === 'string' && valueString.trim() !== '') {
const borderParts = valueString.split(' ').filter(Boolean);
const widthRegex = /^\d+(\.\d+)?(px|pt|em|rem|%)?$/i;
const styleRegex = /^(solid|dashed|dotted|double|groove|ridge|inset|outset|none|hidden)$/i;
const colorRegex = /^#|rgb|rgba|hsl|hsla|[a-zA-Z]+$/;
let width = '1px';
let style = 'solid';
let color = 'black';
let explicitNone = false;
if (borderParts.length === 3) {
borderParts.forEach((part) => {
const partLower = part.toLowerCase();
if (widthRegex.test(partLower)) {
width = part;
}
else if (styleRegex.test(partLower)) {
if (partLower === 'none' || partLower === 'hidden') {
explicitNone = true;
}
else {
style = partLower;
}
}
else if (colorRegex.test(part)) {
color = part;
}
});
valueString = `${width} ${style} ${color}`;
}
{
console.warn(`${source}: Unexpected border format "${valueString}"`);
}
if (explicitNone) {
return style; // ✅ do not apply this style at all
}
}
else {
console.warn(`${source}: Invalid border value "${valueString}"`);
return style;
}
}
if (property == 'border' && valueString == 'none')
valueString = '0px';
style[property] = valueString;
}
return style;
}, {});
};
exports.convertRule = convertRule;
const convertStylesheet = (stylesheet) => {
const response = {};
try {
const parsed = css_tree_1.default.parse(stylesheet);
const rules = parsed.children.filter((rule) => { var _a; return rule.type === 'Rule' && ((_a = rule.prelude) === null || _a === void 0 ? void 0 : _a.type) === 'SelectorList'; });
rules.forEach((rule) => {
const style = (0, exports.convertRule)(rule.block);
if (rule.prelude.type !== 'SelectorList') {
return;
}
rule.prelude.children.forEach((selector) => {
const selectorString = css_tree_1.default.generate(selector);
response[selectorString] = style;
});
});
}
catch (e) {
console.error(`Error parsing stylesheet: "${stylesheet}"`, e);
}
return response;
};
exports.convertStylesheet = convertStylesheet;
const convertElementStyle = (styleAttr, tag) => {
try {
const parsed = css_tree_1.default.parse(`${tag} { ${styleAttr} }`);
const rules = parsed.children.filter((rule) => { var _a; return rule.type === 'Rule' && ((_a = rule.prelude) === null || _a === void 0 ? void 0 : _a.type) === 'SelectorList'; });
const firstRule = rules.first();
return firstRule ? (0, exports.convertRule)(firstRule.block, tag) : undefined;
}
catch (e) {
console.error(`Error parsing style attribute "${styleAttr}" for tag: ${tag}`, e);
}
};
exports.convertElementStyle = convertElementStyle;
const convertNode = (node) => {
if (node.nodeType === node_html_parser_1.NodeType.TEXT_NODE) {
return node.rawText;
}
if (node.nodeType === node_html_parser_1.NodeType.COMMENT_NODE) {
return '';
}
if (node.nodeType !== node_html_parser_1.NodeType.ELEMENT_NODE) {
throw new Error('Not sure what this is');
}
const html = node;
const content = html.childNodes.map(exports.convertNode);
const kindCounters = {};
content.forEach((child) => {
if (typeof child !== 'string') {
child.indexOfType =
child.tag in kindCounters
? (kindCounters[child.tag] = kindCounters[child.tag] + 1)
: (kindCounters[child.tag] = 0);
}
});
let style;
if (html.attributes.style && html.attributes.style.trim()) {
style = (0, exports.convertElementStyle)(html.attributes.style, html.tagName);
}
return Object.assign(html, {
tag: (html.tagName || '').toLowerCase(),
style: style ? [style] : [],
content,
indexOfType: 0,
});
};
exports.convertNode = convertNode;
const parseHtml = (text) => {
const overideText = (0, htmlOverrides_1.replaceText)(text);
const html = (0, node_html_parser_1.parse)(overideText, { comment: false });
const stylesheets = html
.querySelectorAll('style')
.map((styleNode) => styleNode.childNodes.map((textNode) => textNode.rawText.trim()).join('\n'))
.filter((styleText) => !!styleText)
.map(exports.convertStylesheet);
return {
stylesheets,
rootElement: (0, exports.convertNode)(html),
};
};
exports.default = parseHtml;
//# sourceMappingURL=parse.js.map