react-pdf-html-flabs-2 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +243 -0
- package/dist/Html.d.ts +13 -0
- package/dist/Html.js +12 -0
- package/dist/Html.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/ordered.type.d.ts +3 -0
- package/dist/ordered.type.js +34 -0
- package/dist/ordered.type.js.map +1 -0
- package/dist/parse.d.ts +23 -0
- package/dist/parse.js +131 -0
- package/dist/parse.js.map +1 -0
- package/dist/parse.test.d.ts +1 -0
- package/dist/parse.test.js.map +1 -0
- package/dist/render.d.ts +45 -0
- package/dist/render.js +241 -0
- package/dist/render.js.map +1 -0
- package/dist/render.test.d.ts +1 -0
- package/dist/render.test.js.map +1 -0
- package/dist/renderers.d.ts +9 -0
- package/dist/renderers.js +144 -0
- package/dist/renderers.js.map +1 -0
- package/dist/styles.d.ts +10 -0
- package/dist/styles.js +165 -0
- package/dist/styles.js.map +1 -0
- package/dist/supportedStyles.d.ts +2 -0
- package/dist/supportedStyles.js +102 -0
- package/dist/supportedStyles.js.map +1 -0
- package/dist/tags.d.ts +2 -0
- package/dist/tags.js +60 -0
- package/dist/tags.js.map +1 -0
- package/package.json +59 -0
- package/tsconfig.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# react-pdf-html
|
|
2
|
+
|
|
3
|
+
`<Html>` component for [react-pdf](https://github.com/diegomura/react-pdf/)
|
|
4
|
+
|
|
5
|
+
- Support for CSS via `<style>` tags and `style` attributes (limited to `Style` properties supported by `react-pdf`)
|
|
6
|
+
- [Browser CSS defaults](https://www.w3schools.com/cssref/css_default_values.asp) with option for [style reset](https://meyerweb.com/eric/tools/css/reset/)
|
|
7
|
+
- Basic `<table>`(attempted using flex layouts) `<ul>` and `<ol>` support
|
|
8
|
+
- Ability to provide custom renderers for any tag
|
|
9
|
+
|
|
10
|
+
## How it Works
|
|
11
|
+
|
|
12
|
+
1. Parses the HTML string into a JSON tree of nodes using [node-html-parser](https://github.com/taoqf/node-html-parser)
|
|
13
|
+
2. Parses any `<style>` tags in the document and `style` attributes using [css-tree](https://github.com/csstree/csstree)
|
|
14
|
+
3. Renders all nodes using the appropriate `react-pdf` components, applying cascading styles for each node as an array passed to the `style` prop:
|
|
15
|
+
- block/container nodes using `<View>`
|
|
16
|
+
- inline/text nodes using `<Text>`, with appropriate nesting and collapsing of whitepace
|
|
17
|
+
- `<img>` nodes using `<Image>`
|
|
18
|
+
- `<a>` nodes using `<Link>`
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm i react-pdf-html
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import Html from 'react-pdf-html';
|
|
30
|
+
|
|
31
|
+
const html = `<html>
|
|
32
|
+
<body>
|
|
33
|
+
<style>
|
|
34
|
+
.my-heading4 {
|
|
35
|
+
background: darkgreen;
|
|
36
|
+
color: white;
|
|
37
|
+
}
|
|
38
|
+
pre {
|
|
39
|
+
background-color: #eee;
|
|
40
|
+
padding: 10px;
|
|
41
|
+
}
|
|
42
|
+
</style>
|
|
43
|
+
<h1>Heading 1</h1>
|
|
44
|
+
<h2 style="background-color: pink">Heading 2</h2>
|
|
45
|
+
<h3>Heading 3</h3>
|
|
46
|
+
<h4 class="my-heading4">Heading 4</h4>
|
|
47
|
+
<p>
|
|
48
|
+
Paragraph with <strong>bold</strong>, <i>italic</i>, <u>underline</u>,
|
|
49
|
+
<s>strikethrough</s>,
|
|
50
|
+
<strong><u><s><i>and all of the above</i></s></u></strong>
|
|
51
|
+
</p>
|
|
52
|
+
<p>
|
|
53
|
+
Paragraph with image <img src="${myFile}" /> and
|
|
54
|
+
<a href="http://google.com">link</a>
|
|
55
|
+
</p>
|
|
56
|
+
<hr />
|
|
57
|
+
<ul>
|
|
58
|
+
<li>Unordered item</li>
|
|
59
|
+
<li>Unordered item</li>
|
|
60
|
+
</ul>
|
|
61
|
+
<ol>
|
|
62
|
+
<li>Ordered item</li>
|
|
63
|
+
<li>Ordered item</li>
|
|
64
|
+
</ol>
|
|
65
|
+
<br /><br /><br /><br /><br />
|
|
66
|
+
Text outside of any tags
|
|
67
|
+
<table>
|
|
68
|
+
<thead>
|
|
69
|
+
<tr>
|
|
70
|
+
<th>Column 1</th>
|
|
71
|
+
<th>Column 2</th>
|
|
72
|
+
<th>Column 3</th>
|
|
73
|
+
</tr>
|
|
74
|
+
</thead>
|
|
75
|
+
<tbody>
|
|
76
|
+
<tr>
|
|
77
|
+
<td>Foo</td>
|
|
78
|
+
<td>Bar</td>
|
|
79
|
+
<td>Foobar</td>
|
|
80
|
+
</tr>
|
|
81
|
+
<tr>
|
|
82
|
+
<td colspan="2">Foo</td>
|
|
83
|
+
<td>Bar</td>
|
|
84
|
+
</tr>
|
|
85
|
+
<tr>
|
|
86
|
+
<td>Some longer thing</td>
|
|
87
|
+
<td>Even more content than before!</td>
|
|
88
|
+
<td>Even more content than before!</td>
|
|
89
|
+
</tr>
|
|
90
|
+
</tbody>
|
|
91
|
+
</table>
|
|
92
|
+
<div style="width: 200px; height: 200px; background: pink"></div>
|
|
93
|
+
<pre>
|
|
94
|
+
function myCode() {
|
|
95
|
+
const foo = 'bar';
|
|
96
|
+
}
|
|
97
|
+
</pre>
|
|
98
|
+
</body>
|
|
99
|
+
</html>
|
|
100
|
+
`;
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<Document>
|
|
104
|
+
<Page>
|
|
105
|
+
<Html>{html}</Html>
|
|
106
|
+
</Page>
|
|
107
|
+
</Document>
|
|
108
|
+
);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Rendering React Components
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
import ReactDOMServer from 'react-dom/server';
|
|
115
|
+
|
|
116
|
+
const element = (
|
|
117
|
+
<html>
|
|
118
|
+
<body>
|
|
119
|
+
<style>
|
|
120
|
+
{`
|
|
121
|
+
.heading4 {
|
|
122
|
+
background: darkgreen;
|
|
123
|
+
color: white;
|
|
124
|
+
}
|
|
125
|
+
pre {
|
|
126
|
+
background-color: #eee;
|
|
127
|
+
padding: 10px;
|
|
128
|
+
}`}
|
|
129
|
+
</style>
|
|
130
|
+
<h1>Heading 1</h1>
|
|
131
|
+
<h2 style={{ backgroundColor: 'pink' }}>Heading 2</h2>
|
|
132
|
+
...
|
|
133
|
+
</body>
|
|
134
|
+
</html>
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const html = ReactDOMServer.renderToStaticMarkup(element);
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<Document>
|
|
141
|
+
<Page>
|
|
142
|
+
<Html>{html}</Html>
|
|
143
|
+
</Page>
|
|
144
|
+
</Document>
|
|
145
|
+
);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Props
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
type HtmlProps = {
|
|
152
|
+
children: string; // the HTML
|
|
153
|
+
collapse?: boolean; // Default: true. Collapse whitespace. If false, render newlines as breaks
|
|
154
|
+
renderers?: HtmlRenderers; // Mapping of { tagName: HtmlRenderer }
|
|
155
|
+
style?: Style | Style[]; // Html root View style
|
|
156
|
+
stylesheet?: HtmlStyles | HtmlStyles[]; // Mapping of { selector: Style }
|
|
157
|
+
resetStyles?: false; // If true, style/CSS reset
|
|
158
|
+
};
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Overriding Element Styles
|
|
162
|
+
|
|
163
|
+
### Provide a Stylesheet
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
const stylesheet = {
|
|
167
|
+
// clear margins for all <p> tags
|
|
168
|
+
p: {
|
|
169
|
+
margin: 0,
|
|
170
|
+
},
|
|
171
|
+
// add pink background color to elements with class="special"
|
|
172
|
+
['.special']: {
|
|
173
|
+
backgroundColor: 'pink',
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<Document>
|
|
179
|
+
<Page>
|
|
180
|
+
<Html stylesheet={stylesheet}>{html}</Html>
|
|
181
|
+
</Page>
|
|
182
|
+
</Document>
|
|
183
|
+
);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Inline Styles
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
const html = `<div style="width: 200px; height: 200px; background-color: pink">Foobar</div>`;
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<Document>
|
|
193
|
+
<Page>
|
|
194
|
+
<Html>{html}</Html>
|
|
195
|
+
</Page>
|
|
196
|
+
</Document>
|
|
197
|
+
);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Resetting Styles
|
|
201
|
+
|
|
202
|
+
Reset browser default styles (see [CSS reset](https://meyerweb.com/eric/tools/css/reset/))
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
return (
|
|
206
|
+
<Document>
|
|
207
|
+
<Page>
|
|
208
|
+
<Html resetStyles>{html}</Html>
|
|
209
|
+
</Page>
|
|
210
|
+
</Document>
|
|
211
|
+
);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Font Sizes
|
|
215
|
+
|
|
216
|
+
The default styesheet roughly matches browser defaults, using a rough emulation of ems:
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
const em = (em: number, relativeSize: number = fontSize) => em * relativeSize;
|
|
220
|
+
|
|
221
|
+
StyleSheet.create({
|
|
222
|
+
h1: {
|
|
223
|
+
fontSize: em(2),
|
|
224
|
+
marginVertical: em(0.67, em(2)),
|
|
225
|
+
fontWeight: 'bold',
|
|
226
|
+
},
|
|
227
|
+
...
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
By default, the basis for the font size ems is based on the `fontSize` from `props.style`:
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
return (
|
|
235
|
+
<Document>
|
|
236
|
+
<Page>
|
|
237
|
+
<Html style={{ fontSize: 10 }}>{html}</Html>
|
|
238
|
+
</Page>
|
|
239
|
+
</Document>
|
|
240
|
+
);
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
If this is not defined, it falls back to a default of `18`
|
package/dist/Html.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { HtmlRenderers } from './render';
|
|
3
|
+
import { HtmlStyle, HtmlStyles } from './styles';
|
|
4
|
+
export type HtmlProps = {
|
|
5
|
+
collapse?: boolean;
|
|
6
|
+
renderers?: HtmlRenderers;
|
|
7
|
+
style?: HtmlStyle | (HtmlStyle | undefined)[];
|
|
8
|
+
stylesheet?: HtmlStyles | HtmlStyles[];
|
|
9
|
+
resetStyles?: boolean;
|
|
10
|
+
children: string;
|
|
11
|
+
};
|
|
12
|
+
declare const Html: React.FC<HtmlProps>;
|
|
13
|
+
export default Html;
|
package/dist/Html.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = __importDefault(require("react"));
|
|
7
|
+
const render_1 = __importDefault(require("./render"));
|
|
8
|
+
const Html = (props) => {
|
|
9
|
+
return react_1.default.createElement(react_1.default.Fragment, null, (0, render_1.default)(props.children, props));
|
|
10
|
+
};
|
|
11
|
+
exports.default = Html;
|
|
12
|
+
//# sourceMappingURL=Html.js.map
|
package/dist/Html.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Html.js","sourceRoot":"","sources":["../src/Html.tsx"],"names":[],"mappings":";;;;;AAAA,kDAA0B;AAC1B,sDAAqD;AAYrD,MAAM,IAAI,GAAwB,CAAC,KAAK,EAAE,EAAE;IAC1C,OAAO,8DAAG,IAAA,gBAAU,EAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAI,CAAC;AAClD,CAAC,CAAC;AAEF,kBAAe,IAAI,CAAC","sourcesContent":["import React from 'react';\nimport renderHtml, { HtmlRenderers } from './render';\nimport { HtmlStyle, HtmlStyles } from './styles';\n\nexport type HtmlProps = {\n collapse?: boolean;\n renderers?: HtmlRenderers;\n style?: HtmlStyle | (HtmlStyle | undefined)[];\n stylesheet?: HtmlStyles | HtmlStyles[];\n resetStyles?: boolean;\n children: string;\n};\n\nconst Html: React.FC<HtmlProps> = (props) => {\n return <>{renderHtml(props.children, props)}</>;\n};\n\nexport default Html;\n"]}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.renderHtml = exports.Html = exports.default = void 0;
|
|
7
|
+
var Html_1 = require("./Html");
|
|
8
|
+
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(Html_1).default; } });
|
|
9
|
+
Object.defineProperty(exports, "Html", { enumerable: true, get: function () { return __importDefault(Html_1).default; } });
|
|
10
|
+
var render_1 = require("./render");
|
|
11
|
+
Object.defineProperty(exports, "renderHtml", { enumerable: true, get: function () { return __importDefault(render_1).default; } });
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,+BAAkD;AAAzC,gHAAA,OAAO,OAAA;AAAE,6GAAA,OAAO,OAAQ;AACjC,mCAAiD;AAAxC,qHAAA,OAAO,OAAc","sourcesContent":["export { default, default as Html } from './Html';\nexport { default as renderHtml } from './render';\nexport { HtmlStyle, HtmlStyles } from './styles';\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.upperAlpha = exports.lowerAlpha = exports.orderedAlpha = void 0;
|
|
4
|
+
exports.orderedAlpha = [
|
|
5
|
+
'a',
|
|
6
|
+
'b',
|
|
7
|
+
'c',
|
|
8
|
+
'd',
|
|
9
|
+
'e',
|
|
10
|
+
'f',
|
|
11
|
+
'g',
|
|
12
|
+
'h',
|
|
13
|
+
'i',
|
|
14
|
+
'j',
|
|
15
|
+
'k',
|
|
16
|
+
'l',
|
|
17
|
+
'n',
|
|
18
|
+
'm',
|
|
19
|
+
'o',
|
|
20
|
+
'p',
|
|
21
|
+
'q',
|
|
22
|
+
'r',
|
|
23
|
+
's',
|
|
24
|
+
't',
|
|
25
|
+
'u',
|
|
26
|
+
'v',
|
|
27
|
+
'w',
|
|
28
|
+
'x',
|
|
29
|
+
'y',
|
|
30
|
+
'z',
|
|
31
|
+
];
|
|
32
|
+
exports.lowerAlpha = ['lower-alpha', 'lower-latin'];
|
|
33
|
+
exports.upperAlpha = ['upper-alpha', 'upper-latin'];
|
|
34
|
+
//# sourceMappingURL=ordered.type.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ordered.type.js","sourceRoot":"","sources":["../src/ordered.type.ts"],"names":[],"mappings":";;;AAAa,QAAA,YAAY,GAAG;IAC1B,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;CACJ,CAAC;AAEW,QAAA,UAAU,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAC5C,QAAA,UAAU,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC","sourcesContent":["export const orderedAlpha = [\n 'a',\n 'b',\n 'c',\n 'd',\n 'e',\n 'f',\n 'g',\n 'h',\n 'i',\n 'j',\n 'k',\n 'l',\n 'n',\n 'm',\n 'o',\n 'p',\n 'q',\n 'r',\n 's',\n 't',\n 'u',\n 'v',\n 'w',\n 'x',\n 'y',\n 'z',\n];\n\nexport const lowerAlpha = ['lower-alpha', 'lower-latin'];\nexport const upperAlpha = ['upper-alpha', 'upper-latin'];\n"]}
|
package/dist/parse.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { HTMLElement, Node as HTMLNode } from 'node-html-parser';
|
|
2
|
+
import { Tag } from './tags';
|
|
3
|
+
import { Block } from 'css-tree';
|
|
4
|
+
import { HtmlStyle, HtmlStyles } from './styles';
|
|
5
|
+
export type HtmlContent = (HtmlElement | string)[];
|
|
6
|
+
export type HtmlElement = HTMLElement & {
|
|
7
|
+
tag: Tag | 'string';
|
|
8
|
+
parentNode: HtmlElement;
|
|
9
|
+
style: HtmlStyle[];
|
|
10
|
+
content: HtmlContent;
|
|
11
|
+
indexOfType: number;
|
|
12
|
+
querySelectorAll: (selector: string) => HtmlElement[];
|
|
13
|
+
querySelector: (selector: string) => HtmlElement;
|
|
14
|
+
};
|
|
15
|
+
export declare const convertRule: (rule: Block, source?: string) => HtmlStyle;
|
|
16
|
+
export declare const convertStylesheet: (stylesheet: string) => HtmlStyles;
|
|
17
|
+
export declare const convertElementStyle: (styleAttr: string, tag: string) => HtmlStyle | undefined;
|
|
18
|
+
export declare const convertNode: (node: HTMLNode) => HtmlElement | string;
|
|
19
|
+
declare const parseHtml: (text: string) => {
|
|
20
|
+
stylesheets: HtmlStyles[];
|
|
21
|
+
rootElement: HtmlElement;
|
|
22
|
+
};
|
|
23
|
+
export default parseHtml;
|
package/dist/parse.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.convertNode = exports.convertElementStyle = exports.convertStylesheet = exports.convertRule = void 0;
|
|
7
|
+
const node_html_parser_1 = require("node-html-parser");
|
|
8
|
+
const css_tree_1 = __importDefault(require("css-tree"));
|
|
9
|
+
const supportedStyles_1 = __importDefault(require("./supportedStyles"));
|
|
10
|
+
const camelize = require('camelize');
|
|
11
|
+
const convertRule = (rule, source = 'style') => {
|
|
12
|
+
const declarations = rule.children
|
|
13
|
+
.filter((declaration) => declaration.type === 'Declaration')
|
|
14
|
+
.toArray();
|
|
15
|
+
return declarations
|
|
16
|
+
.map((entry) => (Object.assign(Object.assign({}, entry), { property: camelize(entry.property) })))
|
|
17
|
+
.reduce((style, { property, value }) => {
|
|
18
|
+
let valueString = css_tree_1.default.generate(value);
|
|
19
|
+
if (property && value) {
|
|
20
|
+
if (property === 'fontFamily') {
|
|
21
|
+
valueString = valueString.replace(/["']+/g, '');
|
|
22
|
+
if (valueString.includes(',')) {
|
|
23
|
+
const reduced = valueString.split(',', 2)[0];
|
|
24
|
+
console.warn(`react-pdf doesn't support fontFamily lists like "${valueString}". Reducing to "${reduced}".`);
|
|
25
|
+
return style;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else if (!supportedStyles_1.default.includes(property)) {
|
|
29
|
+
if ((property === 'background' &&
|
|
30
|
+
/^#?[a-zA-Z0-9]+$/.test(valueString)) ||
|
|
31
|
+
/^rgba?\([0-9, ]+\)$/i.test(valueString) ||
|
|
32
|
+
/^hsla?\([0-9.%, ]+\)$/i.test(valueString)) {
|
|
33
|
+
property = 'backgroundColor';
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.warn(`${source}: Found unsupported style "${property}"`, {
|
|
37
|
+
property,
|
|
38
|
+
value,
|
|
39
|
+
});
|
|
40
|
+
return style;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (property == 'border' && valueString == 'none')
|
|
44
|
+
valueString = '0';
|
|
45
|
+
style[property] = valueString;
|
|
46
|
+
}
|
|
47
|
+
return style;
|
|
48
|
+
}, {});
|
|
49
|
+
};
|
|
50
|
+
exports.convertRule = convertRule;
|
|
51
|
+
const convertStylesheet = (stylesheet) => {
|
|
52
|
+
const response = {};
|
|
53
|
+
try {
|
|
54
|
+
const parsed = css_tree_1.default.parse(stylesheet);
|
|
55
|
+
const rules = parsed.children.filter((rule) => { var _a; return rule.type === 'Rule' && ((_a = rule.prelude) === null || _a === void 0 ? void 0 : _a.type) === 'SelectorList'; });
|
|
56
|
+
rules.forEach((rule) => {
|
|
57
|
+
const style = (0, exports.convertRule)(rule.block);
|
|
58
|
+
if (rule.prelude.type !== 'SelectorList') {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
rule.prelude.children.forEach((selector) => {
|
|
62
|
+
const selectorString = css_tree_1.default.generate(selector);
|
|
63
|
+
response[selectorString] = style;
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
console.error(`Error parsing stylesheet: "${stylesheet}"`, e);
|
|
69
|
+
}
|
|
70
|
+
return response;
|
|
71
|
+
};
|
|
72
|
+
exports.convertStylesheet = convertStylesheet;
|
|
73
|
+
const convertElementStyle = (styleAttr, tag) => {
|
|
74
|
+
try {
|
|
75
|
+
const parsed = css_tree_1.default.parse(`${tag} { ${styleAttr} }`);
|
|
76
|
+
const rules = parsed.children.filter((rule) => { var _a; return rule.type === 'Rule' && ((_a = rule.prelude) === null || _a === void 0 ? void 0 : _a.type) === 'SelectorList'; });
|
|
77
|
+
const firstRule = rules.first();
|
|
78
|
+
return firstRule ? (0, exports.convertRule)(firstRule.block, tag) : undefined;
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
console.error(`Error parsing style attribute "${styleAttr}" for tag: ${tag}`, e);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
exports.convertElementStyle = convertElementStyle;
|
|
85
|
+
const convertNode = (node) => {
|
|
86
|
+
if (node.nodeType === node_html_parser_1.NodeType.TEXT_NODE) {
|
|
87
|
+
return node.rawText;
|
|
88
|
+
}
|
|
89
|
+
if (node.nodeType === node_html_parser_1.NodeType.COMMENT_NODE) {
|
|
90
|
+
return '';
|
|
91
|
+
}
|
|
92
|
+
if (node.nodeType !== node_html_parser_1.NodeType.ELEMENT_NODE) {
|
|
93
|
+
throw new Error('Not sure what this is');
|
|
94
|
+
}
|
|
95
|
+
const html = node;
|
|
96
|
+
const content = html.childNodes.map(exports.convertNode);
|
|
97
|
+
const kindCounters = {};
|
|
98
|
+
content.forEach((child) => {
|
|
99
|
+
if (typeof child !== 'string') {
|
|
100
|
+
child.indexOfType =
|
|
101
|
+
child.tag in kindCounters
|
|
102
|
+
? (kindCounters[child.tag] = kindCounters[child.tag] + 1)
|
|
103
|
+
: (kindCounters[child.tag] = 0);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
let style;
|
|
107
|
+
if (html.attributes.style && html.attributes.style.trim()) {
|
|
108
|
+
style = (0, exports.convertElementStyle)(html.attributes.style, html.tagName);
|
|
109
|
+
}
|
|
110
|
+
return Object.assign(html, {
|
|
111
|
+
tag: (html.tagName || '').toLowerCase(),
|
|
112
|
+
style: style ? [style] : [],
|
|
113
|
+
content,
|
|
114
|
+
indexOfType: 0,
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
exports.convertNode = convertNode;
|
|
118
|
+
const parseHtml = (text) => {
|
|
119
|
+
const html = (0, node_html_parser_1.parse)(text, { comment: false });
|
|
120
|
+
const stylesheets = html
|
|
121
|
+
.querySelectorAll('style')
|
|
122
|
+
.map((styleNode) => styleNode.childNodes.map((textNode) => textNode.rawText.trim()).join('\n'))
|
|
123
|
+
.filter((styleText) => !!styleText)
|
|
124
|
+
.map(exports.convertStylesheet);
|
|
125
|
+
return {
|
|
126
|
+
stylesheets,
|
|
127
|
+
rootElement: (0, exports.convertNode)(html),
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
exports.default = parseHtml;
|
|
131
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n HTMLElement,\n Node as HTMLNode,\n NodeType,\n parse,\n TextNode,\n} from 'node-html-parser';\nimport { Tag } from './tags';\nimport cssTree, { Block, Declaration, List, Rule, StyleSheet } from 'css-tree';\nimport supportedStyles from './supportedStyles';\nimport { HtmlStyle, HtmlStyles } from './styles';\nconst camelize = require('camelize');\n\nexport type HtmlContent = (HtmlElement | string)[];\n\nexport type HtmlElement = HTMLElement & {\n tag: Tag | 'string';\n parentNode: HtmlElement;\n style: HtmlStyle[];\n content: HtmlContent;\n indexOfType: number;\n querySelectorAll: (selector: string) => HtmlElement[];\n querySelector: (selector: string) => HtmlElement;\n};\n\nexport const convertRule = (\n rule: Block,\n source: string = 'style'\n): HtmlStyle => {\n const declarations = rule.children\n .filter((declaration) => declaration.type === 'Declaration')\n .toArray() as Declaration[];\n\n return declarations\n .map((entry) => ({\n ...entry,\n property: camelize(entry.property as string),\n }))\n .reduce((style, { property, value }: Declaration) => {\n let valueString = cssTree.generate(value);\n if (property && value) {\n if (property === 'fontFamily') {\n valueString = valueString.replace(/[\"']+/g, '');\n if (valueString.includes(',')) {\n const reduced = valueString.split(',', 2)[0];\n console.warn(\n `react-pdf doesn't support fontFamily lists like \"${valueString}\". Reducing to \"${reduced}\".`\n );\n return style;\n }\n } else if (!supportedStyles.includes(property)) {\n if (\n (property === 'background' &&\n /^#?[a-zA-Z0-9]+$/.test(valueString)) ||\n /^rgba?\\([0-9, ]+\\)$/i.test(valueString) ||\n /^hsla?\\([0-9.%, ]+\\)$/i.test(valueString)\n ) {\n property = 'backgroundColor';\n } else {\n console.warn(`${source}: Found unsupported style \"${property}\"`, {\n property,\n value,\n });\n return style;\n }\n }\n\n if (property == 'border' && valueString == 'none') valueString = '0';\n style[property as keyof HtmlStyle] = valueString;\n }\n return style;\n }, {} as HtmlStyle);\n};\n\nexport const convertStylesheet = (stylesheet: string): HtmlStyles => {\n const response = {} as HtmlStyles;\n try {\n const parsed = cssTree.parse(stylesheet) as StyleSheet;\n const rules = parsed.children.filter(\n (rule) => rule.type === 'Rule' && rule.prelude?.type === 'SelectorList'\n ) as List<Rule>;\n rules.forEach((rule) => {\n const style = convertRule(rule.block);\n if (rule.prelude.type !== 'SelectorList') {\n return;\n }\n rule.prelude.children.forEach((selector) => {\n const selectorString = cssTree.generate(selector);\n response[selectorString] = style;\n });\n });\n } catch (e) {\n console.error(`Error parsing stylesheet: \"${stylesheet}\"`, e);\n }\n return response;\n};\n\nexport const convertElementStyle = (\n styleAttr: string,\n tag: string\n): HtmlStyle | undefined => {\n try {\n const parsed = cssTree.parse(`${tag} { ${styleAttr} }`) as StyleSheet;\n const rules = parsed.children.filter(\n (rule) => rule.type === 'Rule' && rule.prelude?.type === 'SelectorList'\n ) as List<Rule>;\n const firstRule = rules.first();\n return firstRule ? convertRule(firstRule.block, tag) : undefined;\n } catch (e) {\n console.error(\n `Error parsing style attribute \"${styleAttr}\" for tag: ${tag}`,\n e\n );\n }\n};\n\nexport const convertNode = (node: HTMLNode): HtmlElement | string => {\n if (node.nodeType === NodeType.TEXT_NODE) {\n return (node as TextNode).rawText;\n }\n if (node.nodeType === NodeType.COMMENT_NODE) {\n return '';\n }\n if (node.nodeType !== NodeType.ELEMENT_NODE) {\n throw new Error('Not sure what this is');\n }\n const html = node as HTMLElement;\n const content = html.childNodes.map(convertNode);\n const kindCounters: Record<string, number> = {};\n content.forEach((child) => {\n if (typeof child !== 'string') {\n child.indexOfType =\n child.tag in kindCounters\n ? (kindCounters[child.tag] = kindCounters[child.tag] + 1)\n : (kindCounters[child.tag] = 0);\n }\n });\n\n let style: HtmlStyle | undefined;\n if (html.attributes.style && html.attributes.style.trim()) {\n style = convertElementStyle(html.attributes.style, html.tagName);\n }\n\n return Object.assign(html, {\n tag: (html.tagName || '').toLowerCase() as Tag | string,\n style: style ? [style] : [],\n content,\n indexOfType: 0,\n }) as HtmlElement;\n};\n\nconst parseHtml = (\n text: string\n): { stylesheets: HtmlStyles[]; rootElement: HtmlElement } => {\n const html = parse(text, { comment: false });\n const stylesheets = html\n .querySelectorAll('style')\n .map((styleNode) =>\n styleNode.childNodes.map((textNode) => textNode.rawText.trim()).join('\\n')\n )\n .filter((styleText) => !!styleText)\n .map(convertStylesheet);\n return {\n stylesheets,\n rootElement: convertNode(html) as HtmlElement,\n };\n};\n\nexport default parseHtml;\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.test.js","sourceRoot":"","sources":["../src/parse.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import parseHtml, {\n convertElementStyle,\n convertStylesheet,\n HtmlElement,\n} from './parse';\n\ndescribe('parse', () => {\n describe('convertStylesheet', () => {\n it('Should convert CSS into HtmlStyles', () => {\n const content = `.my-heading4, #foobar, div > li {\n background: darkgreen;\n color: white;\n }\n div {\n span {\n fontWeight: bold;\n }\n }\n pre {\n background-color: #eee;\n padding: 10px;\n }`;\n\n const result = convertStylesheet(content);\n const expected = {\n '.my-heading4': {\n backgroundColor: 'darkgreen',\n color: 'white',\n },\n '#foobar': {\n backgroundColor: 'darkgreen',\n color: 'white',\n },\n div: {\n // TODO: support nested styles\n },\n 'div>li': {\n backgroundColor: 'darkgreen',\n color: 'white',\n },\n pre: {\n backgroundColor: '#eee',\n padding: '10px',\n },\n };\n\n expect(result).toEqual(expected);\n });\n\n it('Should handle empty', () => {\n const content = ``;\n\n const result = convertStylesheet(content);\n const expected = {};\n\n expect(result).toEqual(expected);\n });\n });\n\n describe('convertElementStyle', () => {\n it('Should convert element CSS into HtmlStyle', () => {\n const content = `background: darkgreen;color: white;bogus: nope`;\n\n const result = convertElementStyle(content, 'div');\n const expected = {\n backgroundColor: 'darkgreen',\n bogus: 'nope',\n color: 'white',\n };\n\n expect(result).toEqual(expected);\n });\n\n it('Should handle empty', () => {\n const content = ``;\n\n const result = convertElementStyle(content, 'div');\n const expected = {};\n\n expect(result).toEqual(expected);\n });\n });\n\n describe('parseHtml', () => {\n it('Should convert HTML into a JSON tree', () => {\n const content = `\nWelcome to your <b>doom!</b>:\n<p>\n <ul>\n <li>First item</li>\n <li>Second item: <a href=\"http://google.com\">google.com</a></li>\n </ul>\n</p>\n `;\n\n const result = parseHtml(content);\n const root = result.rootElement;\n expect(root.content[0]).toEqual('\\nWelcome to your ');\n expect((root.content[1] as HtmlElement).tag).toEqual('b');\n expect((root.content[1] as HtmlElement).content).toEqual(['doom!']);\n expect(root.content[2]).toEqual(':\\n');\n\n const paragraph = root.content[3] as HtmlElement;\n expect(paragraph.tag).toEqual('p');\n expect(paragraph.content[0]).toEqual('\\n ');\n\n const list = paragraph.content[1] as HtmlElement;\n expect(list.tag).toEqual('ul');\n\n const listItem1 = list.content[1] as HtmlElement;\n expect(listItem1.tag).toBe('li');\n expect(listItem1.content).toEqual(['First item']);\n expect(listItem1.indexOfType).toEqual(0);\n\n const listItem2 = list.content[3] as HtmlElement;\n expect(listItem2.tag).toBe('li');\n expect(listItem2.content[0]).toEqual('Second item: ');\n expect(listItem2.indexOfType).toEqual(1);\n\n const link = listItem2.content[1] as HtmlElement;\n expect(link.tag).toBe('a');\n expect(link.attributes.href).toBe('http://google.com');\n expect(link.content).toEqual(['google.com']);\n });\n });\n});\n"]}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { ReactElement } from 'react';
|
|
2
|
+
import renderers from './renderers';
|
|
3
|
+
import { HtmlContent, HtmlElement } from './parse';
|
|
4
|
+
import { HtmlStyle, HtmlStyles } from './styles';
|
|
5
|
+
import { Style } from '@react-pdf/types';
|
|
6
|
+
import { Tag } from './tags';
|
|
7
|
+
export type HtmlRenderer = React.FC<React.PropsWithChildren<{
|
|
8
|
+
element: HtmlElement;
|
|
9
|
+
style: Style[];
|
|
10
|
+
stylesheets: HtmlStyles[];
|
|
11
|
+
}>>;
|
|
12
|
+
export type HtmlRenderers = Record<Tag | string, HtmlRenderer>;
|
|
13
|
+
export type HtmlRenderOptions = {
|
|
14
|
+
collapse: boolean;
|
|
15
|
+
renderers: HtmlRenderers;
|
|
16
|
+
stylesheets: HtmlStyles[];
|
|
17
|
+
resetStyles: boolean;
|
|
18
|
+
};
|
|
19
|
+
type ContentBucket = {
|
|
20
|
+
hasBlock: boolean;
|
|
21
|
+
content: HtmlContent;
|
|
22
|
+
};
|
|
23
|
+
export declare const isBlockStyle: (style: HtmlStyle) => boolean;
|
|
24
|
+
export declare const hasBlockContent: (element: HtmlElement | string) => boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Groups all block and non-block elements into buckets so that all non-block elements can be rendered in a parent Text element
|
|
27
|
+
* @param elements Elements to place in buckets of block and non-block content
|
|
28
|
+
* @param collapse
|
|
29
|
+
* @param parentTag
|
|
30
|
+
*/
|
|
31
|
+
export declare const bucketElements: (elements: HtmlContent, collapse: boolean, parentTag?: Tag | string) => ContentBucket[];
|
|
32
|
+
type RenderedContent = ReactElement | ReactElement[] | string | string[];
|
|
33
|
+
export declare const renderElement: (element: HtmlElement | string, stylesheets: HtmlStyles[], renderers: HtmlRenderers, children?: any, index?: number) => RenderedContent;
|
|
34
|
+
export declare const collapseWhitespace: (string: any) => string;
|
|
35
|
+
export declare const renderBucketElement: (element: HtmlElement | string, options: HtmlRenderOptions, index: number) => RenderedContent;
|
|
36
|
+
export declare const renderElements: (elements: HtmlContent, options: HtmlRenderOptions, parent?: HtmlElement) => RenderedContent | RenderedContent[];
|
|
37
|
+
export declare const applyStylesheets: (stylesheets: HtmlStyles[], rootElement: HtmlElement) => void;
|
|
38
|
+
declare const renderHtml: (text: string, options?: {
|
|
39
|
+
collapse?: boolean;
|
|
40
|
+
renderers?: HtmlRenderers;
|
|
41
|
+
style?: Style | (Style | undefined)[];
|
|
42
|
+
stylesheet?: HtmlStyles | HtmlStyles[];
|
|
43
|
+
resetStyles?: boolean;
|
|
44
|
+
}) => ReactElement;
|
|
45
|
+
export default renderHtml;
|