jspdf-md-renderer 1.2.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/LICENSE +21 -0
- package/README.md +161 -0
- package/dist/enums/mdTokenType.d.ts +18 -0
- package/dist/enums/mdTokenType.js +19 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/parser/MdTextParser.d.ts +8 -0
- package/dist/parser/MdTextParser.js +91 -0
- package/dist/renderer/MdTextRender.d.ts +10 -0
- package/dist/renderer/MdTextRender.js +57 -0
- package/dist/renderer/components/heading.d.ts +8 -0
- package/dist/renderer/components/heading.js +18 -0
- package/dist/renderer/components/index.d.ts +5 -0
- package/dist/renderer/components/index.js +5 -0
- package/dist/renderer/components/list.d.ts +5 -0
- package/dist/renderer/components/list.js +12 -0
- package/dist/renderer/components/listItem.d.ts +5 -0
- package/dist/renderer/components/listItem.js +32 -0
- package/dist/renderer/components/paragraph.d.ts +8 -0
- package/dist/renderer/components/paragraph.js +44 -0
- package/dist/renderer/components/rawItem.d.ts +5 -0
- package/dist/renderer/components/rawItem.js +16 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/parsedElement.d.ts +20 -0
- package/dist/types/parsedElement.js +1 -0
- package/dist/types/renderOption.d.ts +37 -0
- package/dist/types/renderOption.js +1 -0
- package/dist/types/wordInfo.d.ts +4 -0
- package/dist/types/wordInfo.js +1 -0
- package/dist/utils/doc-helpers.d.ts +3 -0
- package/dist/utils/doc-helpers.js +3 -0
- package/dist/utils/handlePageBreak.d.ts +6 -0
- package/dist/utils/handlePageBreak.js +11 -0
- package/dist/utils/justifyText.d.ts +15 -0
- package/dist/utils/justifyText.js +53 -0
- package/package.json +63 -0
- package/src/enums/mdTokenType.ts +18 -0
- package/src/index.ts +4 -0
- package/src/parser/MdTextParser.ts +98 -0
- package/src/renderer/MdTextRender.ts +110 -0
- package/src/renderer/components/heading.ts +30 -0
- package/src/renderer/components/index.ts +5 -0
- package/src/renderer/components/list.ts +28 -0
- package/src/renderer/components/listItem.ts +62 -0
- package/src/renderer/components/paragraph.ts +79 -0
- package/src/renderer/components/rawItem.ts +35 -0
- package/src/types/index.ts +2 -0
- package/src/types/parsedElement.ts +15 -0
- package/src/types/renderOption.ts +38 -0
- package/src/types/wordInfo.ts +4 -0
- package/src/utils/doc-helpers.ts +6 -0
- package/src/utils/handlePageBreak.ts +13 -0
- package/src/utils/justifyText.ts +103 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Jeel Gajera
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# jsPDF Markdown Renderer
|
|
2
|
+
|
|
3
|
+
A jsPDF utility to render Markdown directly into formatted PDFs with custom designs.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Usage](#usage)
|
|
9
|
+
- [API](#api)
|
|
10
|
+
- [Examples](#examples)
|
|
11
|
+
- [Contributing](#contributing)
|
|
12
|
+
- [License](#license)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
To install the library, you can use npm:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install jspdf-md-renderer
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Basic Example
|
|
25
|
+
|
|
26
|
+
Here is a basic example of how to use the library to generate a PDF from Markdown content:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { jsPDF } from 'jspdf';
|
|
30
|
+
import { MdTextRender } from 'jspdf-md-renderer';
|
|
31
|
+
|
|
32
|
+
const mdString = `
|
|
33
|
+
# Main Title
|
|
34
|
+
|
|
35
|
+
This is a brief introduction paragraph. It sets the tone for the document and introduces the main topic in a concise manner.
|
|
36
|
+
|
|
37
|
+
## Section 1: Overview
|
|
38
|
+
|
|
39
|
+
Here is a medium-length paragraph that goes into more detail about the first section. It explains the context, provides background information, and sets up the discussion for the subsections.
|
|
40
|
+
|
|
41
|
+
## Section 2: Lists and Examples
|
|
42
|
+
|
|
43
|
+
This section showcases how to create simple and nested lists.
|
|
44
|
+
|
|
45
|
+
### Simple List
|
|
46
|
+
|
|
47
|
+
- Item 1
|
|
48
|
+
- Item 2
|
|
49
|
+
- Item 3
|
|
50
|
+
|
|
51
|
+
### Nested List
|
|
52
|
+
|
|
53
|
+
1. First Level 1
|
|
54
|
+
- First Level 2
|
|
55
|
+
- First Level 3
|
|
56
|
+
2. Second Level 1
|
|
57
|
+
- Second Level 2
|
|
58
|
+
- Another Second Level 2
|
|
59
|
+
- Nested deeper
|
|
60
|
+
|
|
61
|
+
### Mixed List Example
|
|
62
|
+
|
|
63
|
+
- Topic 1
|
|
64
|
+
1. Subtopic 1.1
|
|
65
|
+
2. Subtopic 1.2
|
|
66
|
+
- Topic 2
|
|
67
|
+
- Subtopic 2.1
|
|
68
|
+
- Subtopic 2.2
|
|
69
|
+
1. Nested Subtopic 2.2.1
|
|
70
|
+
2. Nested Subtopic 2.2.2
|
|
71
|
+
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
const generatePDF = async () => {
|
|
75
|
+
const doc = new jsPDF({
|
|
76
|
+
unit: 'mm',
|
|
77
|
+
format: 'a4',
|
|
78
|
+
orientation: 'portrait',
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const options = {
|
|
82
|
+
cursor: { x: 10, y: 10 },
|
|
83
|
+
page: {
|
|
84
|
+
format: 'a4',
|
|
85
|
+
unit: 'mm',
|
|
86
|
+
orientation: 'portrait',
|
|
87
|
+
maxContentWidth: 190,
|
|
88
|
+
maxContentHeight: 277,
|
|
89
|
+
lineSpace: 1.5,
|
|
90
|
+
defaultLineHeightFactor: 1.2,
|
|
91
|
+
defaultFontSize: 12,
|
|
92
|
+
defaultTitleFontSize: 14,
|
|
93
|
+
topmargin: 10,
|
|
94
|
+
xpading: 10,
|
|
95
|
+
xmargin: 10,
|
|
96
|
+
indent: 10,
|
|
97
|
+
},
|
|
98
|
+
font: {
|
|
99
|
+
bold: { name: 'helvetica', style: 'bold' },
|
|
100
|
+
regular: { name: 'helvetica', style: 'normal' },
|
|
101
|
+
light: { name: 'helvetica', style: 'light' },
|
|
102
|
+
},
|
|
103
|
+
endCursorYHandler: (y) => {
|
|
104
|
+
console.log('End cursor Y position:', y);
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
await MdTextRender(doc, mdString, options);
|
|
109
|
+
doc.save('example.pdf');
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
generatePDF();
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## API
|
|
116
|
+
|
|
117
|
+
### `MdTextRender`
|
|
118
|
+
|
|
119
|
+
Renders parsed markdown text into a jsPDF document.
|
|
120
|
+
|
|
121
|
+
#### Parameters
|
|
122
|
+
|
|
123
|
+
- `doc`: The jsPDF document instance.
|
|
124
|
+
- `text`: The markdown content to render.
|
|
125
|
+
- `options`: The render options (fonts, page margins, etc.).
|
|
126
|
+
|
|
127
|
+
### `MdTextParser`
|
|
128
|
+
|
|
129
|
+
Parses markdown into tokens and converts to a custom parsed structure.
|
|
130
|
+
|
|
131
|
+
#### Parameters
|
|
132
|
+
|
|
133
|
+
- `text`: The markdown content to parse.
|
|
134
|
+
|
|
135
|
+
#### Returns
|
|
136
|
+
|
|
137
|
+
- `Promise<ParsedElement[]>`: Parsed markdown elements.
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
## Supported Markdown Elements
|
|
141
|
+
|
|
142
|
+
The following Markdown elements are currently supported by `jspdf-md-renderer`:
|
|
143
|
+
|
|
144
|
+
- **Headings**: `#`, `##`, `###`, etc.
|
|
145
|
+
- **Paragraphs**
|
|
146
|
+
- **Lists**:
|
|
147
|
+
- Unordered lists: `-`, `*`, `+`
|
|
148
|
+
- Ordered lists: `1.`, `2.`, `3.`, etc.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
## Examples
|
|
152
|
+
|
|
153
|
+
You can find more examples in the [examples](examples/test-pdf-gen) directory.
|
|
154
|
+
|
|
155
|
+
## Contributing
|
|
156
|
+
|
|
157
|
+
Contributions are welcome! Please read the [contributing guidelines](CONTRIBUTING.md) first.
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare enum MdTokenType {
|
|
2
|
+
Heading = "heading",
|
|
3
|
+
Paragraph = "paragraph",
|
|
4
|
+
List = "list",
|
|
5
|
+
ListItem = "list_item",
|
|
6
|
+
Blockquote = "blockquote",
|
|
7
|
+
Code = "code",
|
|
8
|
+
Table = "table",
|
|
9
|
+
Html = "html",
|
|
10
|
+
Hr = "hr",
|
|
11
|
+
Image = "image",
|
|
12
|
+
Link = "link",
|
|
13
|
+
Strong = "strong",
|
|
14
|
+
Em = "em",
|
|
15
|
+
TableHeader = "table_header",
|
|
16
|
+
TableCell = "table_cell",
|
|
17
|
+
Raw = "raw"
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export var MdTokenType;
|
|
2
|
+
(function (MdTokenType) {
|
|
3
|
+
MdTokenType["Heading"] = "heading";
|
|
4
|
+
MdTokenType["Paragraph"] = "paragraph";
|
|
5
|
+
MdTokenType["List"] = "list";
|
|
6
|
+
MdTokenType["ListItem"] = "list_item";
|
|
7
|
+
MdTokenType["Blockquote"] = "blockquote";
|
|
8
|
+
MdTokenType["Code"] = "code";
|
|
9
|
+
MdTokenType["Table"] = "table";
|
|
10
|
+
MdTokenType["Html"] = "html";
|
|
11
|
+
MdTokenType["Hr"] = "hr";
|
|
12
|
+
MdTokenType["Image"] = "image";
|
|
13
|
+
MdTokenType["Link"] = "link";
|
|
14
|
+
MdTokenType["Strong"] = "strong";
|
|
15
|
+
MdTokenType["Em"] = "em";
|
|
16
|
+
MdTokenType["TableHeader"] = "table_header";
|
|
17
|
+
MdTokenType["TableCell"] = "table_cell";
|
|
18
|
+
MdTokenType["Raw"] = "raw";
|
|
19
|
+
})(MdTokenType || (MdTokenType = {}));
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ParsedElement } from '../types/parsedElement';
|
|
2
|
+
/**
|
|
3
|
+
* Parses markdown into tokens and converts to a custom parsed structure.
|
|
4
|
+
*
|
|
5
|
+
* @param text - The markdown content to parse.
|
|
6
|
+
* @returns Parsed markdown elements.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MdTextParser: (text: string) => Promise<ParsedElement[]>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { marked } from 'marked';
|
|
3
|
+
import { MdTokenType } from '../enums/mdTokenType';
|
|
4
|
+
/**
|
|
5
|
+
* Parses markdown into tokens and converts to a custom parsed structure.
|
|
6
|
+
*
|
|
7
|
+
* @param text - The markdown content to parse.
|
|
8
|
+
* @returns Parsed markdown elements.
|
|
9
|
+
*/
|
|
10
|
+
export const MdTextParser = async (text) => {
|
|
11
|
+
const tokens = await marked.lexer(text, { async: true });
|
|
12
|
+
return convertTokens(tokens);
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Convert the markdown tokens to ParsedElements.
|
|
16
|
+
*
|
|
17
|
+
* @param tokens - The list of markdown tokens.
|
|
18
|
+
* @returns Parsed elements in a custom structure.
|
|
19
|
+
*/
|
|
20
|
+
const convertTokens = (tokens) => {
|
|
21
|
+
const parsedElements = [];
|
|
22
|
+
tokens.forEach((token) => {
|
|
23
|
+
const handler = tokenHandlers[token.type];
|
|
24
|
+
if (handler) {
|
|
25
|
+
parsedElements.push(handler(token));
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
parsedElements.push({ type: MdTokenType.Raw, content: token.raw });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return parsedElements.map((element) => element.type === MdTokenType.Raw && element.content === '\n\n'
|
|
32
|
+
? { ...element, content: element.content.replace('\n\n', '\n') }
|
|
33
|
+
: element);
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Map each token type to its handler function.
|
|
37
|
+
*/
|
|
38
|
+
const tokenHandlers = {
|
|
39
|
+
[MdTokenType.Heading]: (token) => ({
|
|
40
|
+
type: MdTokenType.Heading,
|
|
41
|
+
depth: token.depth,
|
|
42
|
+
content: token.text,
|
|
43
|
+
}),
|
|
44
|
+
[MdTokenType.Paragraph]: (token) => ({
|
|
45
|
+
type: MdTokenType.Paragraph,
|
|
46
|
+
content: token.text,
|
|
47
|
+
}),
|
|
48
|
+
[MdTokenType.List]: (token) => ({
|
|
49
|
+
type: MdTokenType.List,
|
|
50
|
+
items: token.items ? convertTokens(token.items) : [],
|
|
51
|
+
}),
|
|
52
|
+
[MdTokenType.ListItem]: (token) => ({
|
|
53
|
+
type: MdTokenType.ListItem,
|
|
54
|
+
content: token.text,
|
|
55
|
+
items: token.tokens ? convertTokens(token.tokens) : [],
|
|
56
|
+
}),
|
|
57
|
+
[MdTokenType.Code]: (token) => ({
|
|
58
|
+
type: MdTokenType.Code,
|
|
59
|
+
lang: token.lang,
|
|
60
|
+
code: token.text,
|
|
61
|
+
}),
|
|
62
|
+
[MdTokenType.Table]: (token) => ({
|
|
63
|
+
type: MdTokenType.Table,
|
|
64
|
+
header: token.header.map((header) => ({
|
|
65
|
+
type: MdTokenType.TableHeader,
|
|
66
|
+
content: header,
|
|
67
|
+
})),
|
|
68
|
+
rows: token.rows.map((row) => row.map((cell) => ({
|
|
69
|
+
type: MdTokenType.TableCell,
|
|
70
|
+
content: cell,
|
|
71
|
+
}))),
|
|
72
|
+
}),
|
|
73
|
+
[MdTokenType.Image]: (token) => ({
|
|
74
|
+
type: MdTokenType.Image,
|
|
75
|
+
src: token.href,
|
|
76
|
+
alt: token.text,
|
|
77
|
+
}),
|
|
78
|
+
[MdTokenType.Link]: (token) => ({
|
|
79
|
+
type: MdTokenType.Link,
|
|
80
|
+
href: token.href,
|
|
81
|
+
text: token.text,
|
|
82
|
+
}),
|
|
83
|
+
[MdTokenType.Strong]: (token) => ({
|
|
84
|
+
type: MdTokenType.Strong,
|
|
85
|
+
content: token.text,
|
|
86
|
+
}),
|
|
87
|
+
[MdTokenType.Em]: (token) => ({
|
|
88
|
+
type: MdTokenType.Em,
|
|
89
|
+
content: token.text,
|
|
90
|
+
}),
|
|
91
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import jsPDF from 'jspdf';
|
|
2
|
+
import { RenderOption } from '../types/renderOption';
|
|
3
|
+
/**
|
|
4
|
+
* Renders parsed markdown text into jsPDF document.
|
|
5
|
+
*
|
|
6
|
+
* @param doc - The jsPDF document.
|
|
7
|
+
* @param text - The markdown content to render.
|
|
8
|
+
* @param options - The render options (fonts, page margins, etc.).
|
|
9
|
+
*/
|
|
10
|
+
export declare const MdTextRender: (doc: jsPDF, text: string, options: RenderOption) => Promise<void>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { MdTokenType } from '../enums/mdTokenType';
|
|
2
|
+
import { MdTextParser } from '../parser/MdTextParser';
|
|
3
|
+
import { HandlePageBreaks } from '../utils/handlePageBreak';
|
|
4
|
+
import { renderHeading, renderList, renderListItem, renderParagraph, renderRawItem, } from './components';
|
|
5
|
+
import { getCharHight } from '../utils/doc-helpers';
|
|
6
|
+
/**
|
|
7
|
+
* Renders parsed markdown text into jsPDF document.
|
|
8
|
+
*
|
|
9
|
+
* @param doc - The jsPDF document.
|
|
10
|
+
* @param text - The markdown content to render.
|
|
11
|
+
* @param options - The render options (fonts, page margins, etc.).
|
|
12
|
+
*/
|
|
13
|
+
export const MdTextRender = async (doc, text, options) => {
|
|
14
|
+
const parsedElements = await MdTextParser(text);
|
|
15
|
+
console.log(parsedElements);
|
|
16
|
+
console.log(doc);
|
|
17
|
+
let y = options.cursor.y;
|
|
18
|
+
const x = options.cursor.x;
|
|
19
|
+
const renderElement = (element, indentLevel = 0, hasRawBullet = false) => {
|
|
20
|
+
const indent = indentLevel * options.page.indent;
|
|
21
|
+
if (y +
|
|
22
|
+
doc.splitTextToSize(element.content ?? '', options.page.maxContentWidth - indent).length *
|
|
23
|
+
getCharHight(doc, options) >=
|
|
24
|
+
options.page.maxContentHeight) {
|
|
25
|
+
HandlePageBreaks(doc, options);
|
|
26
|
+
y = options.page.topmargin;
|
|
27
|
+
}
|
|
28
|
+
switch (element.type) {
|
|
29
|
+
case MdTokenType.Heading:
|
|
30
|
+
y = renderHeading(doc, element, x, y, indent, options);
|
|
31
|
+
break;
|
|
32
|
+
case MdTokenType.Paragraph:
|
|
33
|
+
y = renderParagraph(doc, element, x, y, indent, options);
|
|
34
|
+
break;
|
|
35
|
+
case MdTokenType.List:
|
|
36
|
+
y = renderList(doc, element, y, indentLevel, options, renderElement);
|
|
37
|
+
break;
|
|
38
|
+
case MdTokenType.ListItem:
|
|
39
|
+
y = renderListItem(doc, element, x, y, indentLevel, options, renderElement);
|
|
40
|
+
break;
|
|
41
|
+
case MdTokenType.Raw:
|
|
42
|
+
y = renderRawItem(doc, element, x, y, indentLevel, hasRawBullet, options);
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
console.warn(`Warning: Unsupported element type encountered: ${element.type}.
|
|
46
|
+
If you believe this element type should be supported, please create an issue at:
|
|
47
|
+
https://github.com/JeelGajera/jspdf-md-renderer/issues
|
|
48
|
+
with details of the element and expected behavior. Thank you for helping improve this library!`);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
return y;
|
|
52
|
+
};
|
|
53
|
+
for (const item of parsedElements) {
|
|
54
|
+
renderElement(item);
|
|
55
|
+
}
|
|
56
|
+
options.endCursorYHandler(y);
|
|
57
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import jsPDF from 'jspdf';
|
|
2
|
+
import { ParsedElement } from '../../types/parsedElement';
|
|
3
|
+
import { RenderOption } from '../../types/renderOption';
|
|
4
|
+
/**
|
|
5
|
+
* Renders heading elements.
|
|
6
|
+
*/
|
|
7
|
+
declare const renderHeading: (doc: jsPDF, element: ParsedElement, x: number, y: number, indent: number, options: RenderOption) => number;
|
|
8
|
+
export default renderHeading;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getCharHight } from '../../utils/doc-helpers';
|
|
2
|
+
/**
|
|
3
|
+
* Renders heading elements.
|
|
4
|
+
*/
|
|
5
|
+
const renderHeading = (doc, element, x, y, indent, options) => {
|
|
6
|
+
const size = 6 - (element?.depth ?? 0) > 0 ? 6 - (element?.depth ?? 0) : 0;
|
|
7
|
+
// doc.setFont(options.font.regular.name, options.font.regular.style);
|
|
8
|
+
doc.setFontSize(options.page.defaultFontSize + size);
|
|
9
|
+
doc.text(element?.content ?? '', x + indent, y, {
|
|
10
|
+
align: 'left',
|
|
11
|
+
maxWidth: options.page.maxContentWidth - indent,
|
|
12
|
+
});
|
|
13
|
+
y += 1.5 * getCharHight(doc, options);
|
|
14
|
+
// doc.setFont(options.font.light.name, options.font.light.style);
|
|
15
|
+
doc.setFontSize(options.page.defaultFontSize);
|
|
16
|
+
return y;
|
|
17
|
+
};
|
|
18
|
+
export default renderHeading;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as renderHeading } from './heading';
|
|
2
|
+
export { default as renderParagraph } from './paragraph';
|
|
3
|
+
export { default as renderList } from './list';
|
|
4
|
+
export { default as renderListItem } from './listItem';
|
|
5
|
+
export { default as renderRawItem } from './rawItem';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as renderHeading } from './heading';
|
|
2
|
+
export { default as renderParagraph } from './paragraph';
|
|
3
|
+
export { default as renderList } from './list';
|
|
4
|
+
export { default as renderListItem } from './listItem';
|
|
5
|
+
export { default as renderRawItem } from './rawItem';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import jsPDF from 'jspdf';
|
|
2
|
+
import { ParsedElement } from '../../types/parsedElement';
|
|
3
|
+
import { RenderOption } from '../../types/renderOption';
|
|
4
|
+
declare const renderList: (doc: jsPDF, element: ParsedElement, y: number, indentLevel: number, options: RenderOption, parentElementRenderer: (element: ParsedElement, indentLevel: number, hasRawBullet?: boolean) => number) => number;
|
|
5
|
+
export default renderList;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getCharHight } from '../../utils/doc-helpers';
|
|
2
|
+
const renderList = (doc, element, y, indentLevel, options, parentElementRenderer) => {
|
|
3
|
+
doc.setFontSize(options.page.defaultFontSize);
|
|
4
|
+
// doc.setFont(options.font.light.name, options.font.light.style);
|
|
5
|
+
for (const point of element?.items ?? []) {
|
|
6
|
+
y =
|
|
7
|
+
parentElementRenderer(point, indentLevel + 1, true) +
|
|
8
|
+
getCharHight(doc, options) * 0.2; // Recursively render nested list items
|
|
9
|
+
}
|
|
10
|
+
return y;
|
|
11
|
+
};
|
|
12
|
+
export default renderList;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import jsPDF from 'jspdf';
|
|
2
|
+
import { ParsedElement } from '../../types/parsedElement';
|
|
3
|
+
import { RenderOption } from '../../types/renderOption';
|
|
4
|
+
declare const renderListItem: (doc: jsPDF, element: ParsedElement, x: number, y: number, indentLevel: number, options: RenderOption, parentElementRenderer: (element: ParsedElement, indentLevel: number, hasRawBullet?: boolean) => number) => number;
|
|
5
|
+
export default renderListItem;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { justifyText } from '../../utils/justifyText';
|
|
2
|
+
import { HandlePageBreaks } from '../../utils/handlePageBreak';
|
|
3
|
+
import { getCharHight } from '../../utils/doc-helpers';
|
|
4
|
+
const renderListItem = (doc, element, x, y, indentLevel, options, parentElementRenderer) => {
|
|
5
|
+
const indent = indentLevel * options.page.indent;
|
|
6
|
+
if (y +
|
|
7
|
+
doc.splitTextToSize(element.content ?? '', options.page.maxContentWidth - indent).length *
|
|
8
|
+
getCharHight(doc, options) -
|
|
9
|
+
2 * getCharHight(doc, options) >=
|
|
10
|
+
options.page.maxContentHeight) {
|
|
11
|
+
HandlePageBreaks(doc, options);
|
|
12
|
+
y = options.page.topmargin;
|
|
13
|
+
}
|
|
14
|
+
if (!element.items && element.content) {
|
|
15
|
+
const lineHeight = doc.getTextWidth(element.content) >
|
|
16
|
+
options.page.maxContentWidth - indent
|
|
17
|
+
? options.page.defaultLineHeightFactor
|
|
18
|
+
: options.page.defaultLineHeightFactor + 0.4;
|
|
19
|
+
y =
|
|
20
|
+
justifyText(doc, '\u2022 ' + element.content, x + indent, y, options.page.maxContentWidth - indent, lineHeight) + getCharHight(doc, options);
|
|
21
|
+
}
|
|
22
|
+
// Recursively render nested items if they exist
|
|
23
|
+
if (element.items && element.items.length > 0) {
|
|
24
|
+
for (const subItem of element.items) {
|
|
25
|
+
y =
|
|
26
|
+
parentElementRenderer(subItem, indentLevel + 1, true) +
|
|
27
|
+
getCharHight(doc, options) * 0.2;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return y;
|
|
31
|
+
};
|
|
32
|
+
export default renderListItem;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import jsPDF from 'jspdf';
|
|
2
|
+
import { ParsedElement } from '../../types/parsedElement';
|
|
3
|
+
import { RenderOption } from '../../types/renderOption';
|
|
4
|
+
/**
|
|
5
|
+
* Renders paragraph elements.
|
|
6
|
+
*/
|
|
7
|
+
declare const renderParagraph: (doc: jsPDF, element: ParsedElement, x: number, y: number, indent: number, options: RenderOption) => number;
|
|
8
|
+
export default renderParagraph;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { justifyText } from '../../utils/justifyText';
|
|
2
|
+
import { HandlePageBreaks } from '../../utils/handlePageBreak';
|
|
3
|
+
import { getCharHight } from '../../utils/doc-helpers';
|
|
4
|
+
/**
|
|
5
|
+
* Renders paragraph elements.
|
|
6
|
+
*/
|
|
7
|
+
const renderParagraph = (doc, element, x, y, indent, options) => {
|
|
8
|
+
doc.setFontSize(options.page.defaultFontSize);
|
|
9
|
+
// doc.setFont(options.font.light.name, options.font.light.style);
|
|
10
|
+
let content = element.content;
|
|
11
|
+
const lineHeight = doc.getTextDimensions('A').h * options.page.defaultLineHeightFactor;
|
|
12
|
+
if (y +
|
|
13
|
+
doc.splitTextToSize(content ?? '', options.page.maxContentWidth - indent).length *
|
|
14
|
+
lineHeight -
|
|
15
|
+
3 * lineHeight >=
|
|
16
|
+
options.page.maxContentHeight) {
|
|
17
|
+
// ADD Possible text to Page bottom
|
|
18
|
+
const contentLeft = doc.splitTextToSize(content ?? '', options.page.maxContentWidth - indent);
|
|
19
|
+
const possibleContentLines = [];
|
|
20
|
+
const possibleContentY = y;
|
|
21
|
+
for (let j = 0; j < contentLeft.length; j++) {
|
|
22
|
+
if (y - 2 * lineHeight < options.page.maxContentHeight) {
|
|
23
|
+
possibleContentLines.push(contentLeft[j]);
|
|
24
|
+
y += options.page.lineSpace;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// set left content to move next page
|
|
28
|
+
if (j <= contentLeft.length - 1) {
|
|
29
|
+
content = contentLeft.slice(j).join('');
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (possibleContentLines.length > 0) {
|
|
35
|
+
y = justifyText(doc, possibleContentLines.join(' '), x + indent, possibleContentY, options.page.maxContentWidth - indent, options.page.defaultLineHeightFactor);
|
|
36
|
+
}
|
|
37
|
+
HandlePageBreaks(doc, options);
|
|
38
|
+
y = options.page.topmargin;
|
|
39
|
+
}
|
|
40
|
+
y =
|
|
41
|
+
justifyText(doc, content ?? '', x + indent, y, options.page.maxContentWidth - indent, options.page.defaultLineHeightFactor) + getCharHight(doc, options);
|
|
42
|
+
return y;
|
|
43
|
+
};
|
|
44
|
+
export default renderParagraph;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import jsPDF from 'jspdf';
|
|
2
|
+
import { ParsedElement } from '../../types/parsedElement';
|
|
3
|
+
import { RenderOption } from '../../types/renderOption';
|
|
4
|
+
declare const renderRawItem: (doc: jsPDF, element: ParsedElement, x: number, y: number, indentLevel: number, hasRawBullet: boolean, options: RenderOption) => number;
|
|
5
|
+
export default renderRawItem;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { HandlePageBreaks } from '../../utils/handlePageBreak';
|
|
2
|
+
import { getCharHight } from '../../utils/doc-helpers';
|
|
3
|
+
const renderRawItem = (doc, element, x, y, indentLevel, hasRawBullet, options) => {
|
|
4
|
+
const indent = indentLevel * options.page.indent;
|
|
5
|
+
const bullet = hasRawBullet ? '\u2022 ' : ''; // unicode for bullet point
|
|
6
|
+
const lines = doc.splitTextToSize(bullet + element.content, options.page.maxContentWidth - indent);
|
|
7
|
+
if (y + lines.length * getCharHight(doc, options) >=
|
|
8
|
+
options.page.maxContentHeight) {
|
|
9
|
+
HandlePageBreaks(doc, options);
|
|
10
|
+
y = options.page.topmargin;
|
|
11
|
+
}
|
|
12
|
+
doc.text(lines, x + indent, y);
|
|
13
|
+
y += lines.length * getCharHight(doc, options);
|
|
14
|
+
return y;
|
|
15
|
+
};
|
|
16
|
+
export default renderRawItem;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type ParsedElement = {
|
|
2
|
+
type: string;
|
|
3
|
+
content?: string;
|
|
4
|
+
depth?: number;
|
|
5
|
+
items?: ParsedElement[];
|
|
6
|
+
lang?: string;
|
|
7
|
+
code?: string;
|
|
8
|
+
src?: string;
|
|
9
|
+
alt?: string;
|
|
10
|
+
href?: string;
|
|
11
|
+
text?: string;
|
|
12
|
+
header?: {
|
|
13
|
+
type?: string;
|
|
14
|
+
content?: any;
|
|
15
|
+
};
|
|
16
|
+
rows?: {
|
|
17
|
+
type?: string;
|
|
18
|
+
content?: any;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsPDFOptions } from 'jspdf';
|
|
2
|
+
export type RenderOption = {
|
|
3
|
+
cursor: {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
};
|
|
7
|
+
page: {
|
|
8
|
+
format?: string | number[];
|
|
9
|
+
unit?: jsPDFOptions['unit'];
|
|
10
|
+
orientation?: jsPDFOptions['orientation'];
|
|
11
|
+
maxContentWidth: number;
|
|
12
|
+
maxContentHeight: number;
|
|
13
|
+
lineSpace: number;
|
|
14
|
+
defaultLineHeightFactor: number;
|
|
15
|
+
defaultFontSize: number;
|
|
16
|
+
defaultTitleFontSize: number;
|
|
17
|
+
topmargin: number;
|
|
18
|
+
xpading: number;
|
|
19
|
+
xmargin: number;
|
|
20
|
+
indent: number;
|
|
21
|
+
};
|
|
22
|
+
font: {
|
|
23
|
+
bold: FontItem;
|
|
24
|
+
regular: FontItem;
|
|
25
|
+
light: FontItem;
|
|
26
|
+
};
|
|
27
|
+
content?: {
|
|
28
|
+
textAlignment: 'left' | 'right' | 'center' | 'justify';
|
|
29
|
+
};
|
|
30
|
+
pageBreakHandler?: () => void;
|
|
31
|
+
endCursorYHandler: (y: number) => void;
|
|
32
|
+
};
|
|
33
|
+
type FontItem = {
|
|
34
|
+
name: string;
|
|
35
|
+
style: string;
|
|
36
|
+
};
|
|
37
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|