markdown-paper 2.2.2 → 2.2.4

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 CHANGED
@@ -84,9 +84,9 @@ mdp example.md --outputDOCX
84
84
  | `--outputDOCX` | 输出 `DOCX` 文件, 默认不输出<br>**须先通过 `python` 安装依赖 `pdf2docx`**<br>使用时推荐开启 `--hideFooter` 参数 |
85
85
 
86
86
  # 模板说明
87
- `/theme/theme.ts` 中的 `Theme` 抽象类定义了模板的样式, 按照类似于 `aps` 文件夹的结构可自定义模板; 模板可以提供自定义功能
87
+ `/theme/theme.ts` 中的 `MarkdownnPaperTheme` 接口定义了模板的样式, 按照类似于 `aps` 文件夹的结构可自定义模板; 模板可以提供自定义功能
88
88
 
89
- 模板制作完成后, 在 `/lib/main.ts` 中导入并添加到 `class Options -> constructor -> case '--theme':` 中, 并在下方添加使用文档即可
89
+ 模板制作完成后, 在 `/lib/main.ts` 中导入并添加到 `class MarkdownPaperOptions -> constructor -> case '--theme':` 中, 并在下方添加使用文档即可
90
90
 
91
91
  推荐所有主题的文档和编写格式都尽量与 `aps` 主题保持一致
92
92
 
package/lib/main.ts CHANGED
@@ -3,8 +3,8 @@ import puppeteer from 'puppeteer'
3
3
  import fs from 'node:fs/promises'
4
4
  import path from 'node:path'
5
5
  import { readFileSync } from 'node:fs'
6
- import { APS } from '../theme/aps/aps'
7
- import { MarkdownPaperTheme } from '../theme/theme'
6
+ import { APS, baseAPS } from '../theme/aps/aps'
7
+ import type { MarkdownPaperTheme } from '../theme/theme'
8
8
  import type { PDFOptions } from 'puppeteer'
9
9
 
10
10
  /** 应用参数 */
@@ -40,7 +40,7 @@ class MarkdownPaperOptions {
40
40
  this.out = ''
41
41
  this.outputHTML = false
42
42
  this.outputDOCX = false
43
- this.theme = new APS(args, cwd)
43
+ this.theme = new APS(args)
44
44
  // 解析路径参数
45
45
  if (args.length === 0) throw SyntaxError()
46
46
  else args[0] = `--src=${args[0]}`
@@ -90,32 +90,14 @@ class MarkdownPaperOptions {
90
90
  /**
91
91
  * 渲染 markdown
92
92
  * @param options 参数
93
- * @param rawMarkdown 如果传入此参数, 则不再读取文件
94
93
  */
95
94
  async function renderMarkdown(
96
95
  options: MarkdownPaperOptions,
97
- rawMarkdown?: string,
98
96
  ): Promise<void> {
99
97
  // 读取 markdown 文件
100
- const raw = rawMarkdown ?? await fs.readFile(options.src, { encoding: 'utf-8' })
101
- // 预处理 markdown
102
- const md = await options.theme.preParseMarkdown(raw)
103
- // 转换 markdown 为 html
104
- let html = `
105
- <!DOCTYPE html>
106
- <html lang="zh-CN">
107
- <head>
108
- <meta charset="UTF-8">
109
- <title>${path.basename(options.src).replace('.md', '')}</title>
110
- <style>${options.theme.css}</style>
111
- </head>
112
- <body>
113
- ${await marked(md)}
114
- </body>
115
- </html>
116
- `
117
- // 预处理 html
118
- html = await options.theme.preParseHTML(html)
98
+ const raw = await fs.readFile(options.src, { encoding: 'utf-8' })
99
+ // 生成 html 文件
100
+ const html = await mdToHtml(raw, options.theme, path.basename(options.src).replace('.md', ''))
119
101
  // 保存 html 文件
120
102
  options.outputHTML && await fs.writeFile(options.out.replace('.pdf', '.html'), html)
121
103
  // 保存 pdf 文件
@@ -132,7 +114,8 @@ async function renderMarkdown(
132
114
  }
133
115
  }),
134
116
  options.out,
135
- options.theme.pdfOptions
117
+ options.theme.pdfOptions,
118
+ options.theme.script
136
119
  )
137
120
  // 保存 docx 文件
138
121
  options.outputDOCX && await pdfToDocx(options.out)
@@ -143,11 +126,14 @@ async function renderMarkdown(
143
126
  * @param html html 字符串, 图片为 base64
144
127
  * @param dist pdf 文件绝对路径
145
128
  * @param options pdf 参数, 无需设置路径
129
+ * @param script 在网页中要执行的函数
146
130
  */
147
- async function htmlToPdf(html: string, dist: string, options: PDFOptions): Promise<void> {
131
+ async function htmlToPdf(html: string, dist: string, options: PDFOptions, script: () => void): Promise<void> {
148
132
  const browser = await puppeteer.launch()
149
133
  const page = await browser.newPage()
150
134
  await page.setContent(html)
135
+ // 执行脚本
136
+ await page.evaluate(script)
151
137
  await page.pdf({ path: dist, ...options })
152
138
  await browser.close()
153
139
  }
@@ -180,14 +166,48 @@ function pdfToDocx(pdfPath: string): Promise<void> {
180
166
  })
181
167
  }
182
168
 
183
- const themes = {
184
- APS,
169
+ /**
170
+ * 把 markdown 转换为 html
171
+ * @param md markdown 字符串
172
+ * @param theme 论文模板
173
+ * @param pageTitle 页面标题
174
+ * @returns html 字符串
175
+ */
176
+ async function mdToHtml(
177
+ md: string,
178
+ theme: MarkdownPaperTheme,
179
+ pageTitle: string = 'MarkdownPaper'
180
+ ): Promise<string> {
181
+ // 预处理 markdown
182
+ let html = await theme.preParseMarkdown(md)
183
+ // 转换 markdown 为 html
184
+ html = `
185
+ <!DOCTYPE html>
186
+ <html lang="zh-CN">
187
+ <head>
188
+ <meta charset="UTF-8">
189
+ <title>${pageTitle}</title>
190
+ <style>${theme.css}</style>
191
+ </head>
192
+ <body>
193
+ ${await marked(html)}
194
+ </body>
195
+ </html>
196
+ `
197
+ // 预处理 html
198
+ html = await theme.preParseHTML(html)
199
+ return html
185
200
  }
201
+
202
+
186
203
  export {
187
- MarkdownPaperTheme,
188
204
  MarkdownPaperOptions,
189
205
  renderMarkdown,
190
206
  htmlToPdf,
191
207
  pdfToDocx,
192
- themes
208
+ mdToHtml,
209
+ baseAPS
210
+ }
211
+ export type {
212
+ MarkdownPaperTheme
193
213
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "markdown-paper",
3
3
  "type": "module",
4
- "version": "2.2.2",
4
+ "version": "2.2.4",
5
5
  "author": {
6
6
  "name": "LeafYeeXYZ",
7
7
  "email": "xiaoyezi@leafyee.xyz"
package/theme/aps/aps.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { MarkdownPaperTheme } from '../theme'
2
- import { readFileSync } from 'node:fs'
3
- import { resolve } from 'node:path'
4
- import type { PDFOptions } from 'puppeteer-core'
1
+ import type { MarkdownPaperTheme } from '../theme'
2
+ import type { PDFOptions } from 'puppeteer'
3
+ import fs from 'node:fs'
4
+ import path from 'node:path'
5
5
 
6
- export class APS extends MarkdownPaperTheme {
6
+ export class APS implements MarkdownPaperTheme {
7
7
 
8
8
  css: string
9
9
  preParseMarkdown: (md: string) => Promise<string>
@@ -11,10 +11,10 @@ export class APS extends MarkdownPaperTheme {
11
11
  script: () => void
12
12
  pdfOptions: PDFOptions
13
13
 
14
- constructor(args: string[], cwd: string) {
15
-
16
- super(args, cwd)
17
-
14
+ constructor(
15
+ args: string[] = [],
16
+ ) {
17
+
18
18
  // 默认自定义参数
19
19
  let showTitle: boolean = false
20
20
  let hideFooter: boolean = false
@@ -31,7 +31,7 @@ export class APS extends MarkdownPaperTheme {
31
31
  })
32
32
 
33
33
  // css
34
- this.css = readFileSync(resolve(import.meta.dir, 'aps.css'), 'utf-8')
34
+ this.css = fs.readFileSync(path.resolve(import.meta.dirname, 'aps.css'), 'utf-8')
35
35
 
36
36
  // preParseMarkdown
37
37
  this.preParseMarkdown = async (md: string): Promise<string> => {
@@ -119,4 +119,166 @@ export class APS extends MarkdownPaperTheme {
119
119
  footerTemplate: hideFooter ? `<div></div>` : `<div style="font-size: 9px; font-family: 'SimSun'; color: #333; padding: 5px; margin: 0 auto;">第 <span class="pageNumber"></span> 页 / 共 <span class="totalPages"></span> 页</div>`,
120
120
  }
121
121
  }
122
+ }
123
+
124
+ export const baseAPS: MarkdownPaperTheme = {
125
+ css: `
126
+ * {
127
+ font-family: 'Times', 'Times New Roman', '宋体', 'SimSun', '华文宋体', 'STSong'; /* 所有数字和英文字体都用 Times New Roman */
128
+ line-height: 1.55em; /* 1.5倍行距 */
129
+ margin: 0;
130
+ }
131
+
132
+ h1 { /* 中文题目: 二号黑体 */
133
+ font-size: 29px;
134
+ font-weight: normal;
135
+ font-family: '黑体', 'SimHei', '华文黑体', 'STHeiti';
136
+ text-align: center;
137
+ margin-bottom: 9px;
138
+ }
139
+ .author { /* 作者姓名: 四号仿宋 */
140
+ font-size: 18px;
141
+ font-weight: normal;
142
+ font-family: '仿宋', 'Fangsong', '华文仿宋', 'STFangsong';
143
+ text-align: center;
144
+ margin-bottom: 3px;
145
+ }
146
+ .school { /* 作者单位: 小五宋体 */
147
+ font-size: 12px;
148
+ text-align: center;
149
+ margin-bottom: 38px;
150
+ }
151
+ .abstract { /* 摘要和关键词: 五号宋体 */
152
+ font-size: 14px;
153
+ text-align: justify;
154
+ padding: 0 28px;
155
+ &::before {
156
+ content: '摘 要';
157
+ font-weight: normal;
158
+ font-family: '黑体', 'SimHei', '华文黑体', 'STHeiti';
159
+ display: inline-block;
160
+ margin-right: 14px;
161
+ }
162
+ }
163
+ .keywords {
164
+ font-size: 14px;
165
+ margin-bottom: 31px;
166
+ padding: 0 28px;
167
+ &::before {
168
+ content: '关键词';
169
+ font-weight: normal;
170
+ font-family: '黑体', 'SimHei', '华文黑体', 'STHeiti';
171
+ display: inline-block;
172
+ margin-right: 14px;
173
+ }
174
+ }
175
+
176
+ h2 { /* 一级标题: 四号宋体 */
177
+ font-size: 18px;
178
+ font-weight: normal;
179
+ margin: 7px 0;
180
+ }
181
+ h3 { /* 二级标题: 五号黑体 */
182
+ font-size: 14px;
183
+ font-weight: normal;
184
+ font-family: '黑体', 'SimHei', '华文黑体', 'STHeiti';
185
+ margin: 5px 0;
186
+ }
187
+ h4 { /* 三级标题: 五号黑体 */
188
+ font-size: 14px;
189
+ font-weight: normal;
190
+ font-family: '黑体', 'SimHei', '华文黑体', 'STHeiti';
191
+ margin: 3px 0;
192
+ }
193
+
194
+ p { /* 正文: 五号宋体 */
195
+ font-size: 14px;
196
+ text-indent: 24px;
197
+ text-align: justify;
198
+ }
199
+
200
+ h5 { /* "参考文献": 五号黑体 */
201
+ font-size: 14px;
202
+ font-weight: normal;
203
+ font-family: '黑体', 'SimHei', '华文黑体', 'STHeiti';
204
+ text-align: center;
205
+ margin-bottom: 7px;
206
+ margin-top: 20px;
207
+ }
208
+ ul, ol { /* 参考文献的项目: 小五号宋体 */
209
+ list-style-type: none;
210
+ padding: 0;
211
+ & > li {
212
+ font-size: 12px;
213
+ margin: 6px 0;
214
+ text-align: justify;
215
+ text-indent: -24px;
216
+ padding-left: 24px;
217
+ }
218
+ & a {
219
+ text-decoration: none;
220
+ color: black;
221
+ }
222
+ }
223
+
224
+ img {
225
+ display: block;
226
+ max-width: 100%;
227
+ margin: 0 auto;
228
+ margin-top: 10px;
229
+ }
230
+
231
+ blockquote, blockquote > p { /* 图片和表格的标题: 小五号宋体 */
232
+ font-size: 12px;
233
+ font-weight: normal;
234
+ text-align: center;
235
+ margin: 0;
236
+ }
237
+ blockquote > p { margin: 6px 0; }
238
+ table { /* 表格: 小五号宋体 */
239
+ font-size: 12px;
240
+ position: relative;
241
+ border-top: 1px solid black;
242
+ border-bottom: 1px solid black;
243
+ width: 100%;
244
+ max-width: 100%;
245
+ margin: 0 auto;
246
+ margin-bottom: 10px;
247
+ & th, & td {
248
+ font-weight: normal;
249
+ }
250
+ & thead::after { /* 用来做三线表中间的横线 */
251
+ content: '';
252
+ display: block;
253
+ position: absolute;
254
+ border-top: 1px solid #00000060;
255
+ width: 100%;
256
+ }
257
+ }
258
+
259
+ b, strong { /* 加粗按黑体处理 */
260
+ font-weight: normal;
261
+ font-family: '黑体', 'SimHei', '华文黑体', 'STHeiti';
262
+ }
263
+ `,
264
+ preParseMarkdown: async (md: string): Promise<string> => {
265
+ // 作者
266
+ md = md.replace(/#author# (.*)/mg, '<div class="author">$1</div>')
267
+ // 单位
268
+ md = md.replace(/#school# (.*)/mg, '<div class="school">$1</div>')
269
+ // 关键词
270
+ md = md.replace(/#keywords# (.*)/mg, '<div class="keywords">$1</div>')
271
+ // 摘要
272
+ md = md.replace(/#abstract# (.*)/mg, '<div class="abstract">$1</div>')
273
+ // 返回处理后的字符串
274
+ return md
275
+ },
276
+ preParseHTML: async (html: string): Promise<string> => {
277
+ // 把包裹图片的 p 标签去掉
278
+ html = html.replace(/<p><img (.*?)><\/p>/g, '<img $1>')
279
+ // 返回处理后的字符串
280
+ return html
281
+ },
282
+ script: () => {},
283
+ pdfOptions: {}
122
284
  }
package/theme/theme.ts CHANGED
@@ -1,45 +1,33 @@
1
1
  import type { PDFOptions } from 'puppeteer-core'
2
2
 
3
- export abstract class MarkdownPaperTheme {
4
- /**
5
- * @param args 命令行参数
6
- * @param cwd 当前工作目录
7
- */
8
- constructor(args?: string[], cwd?: string) {
9
- this.args = args ?? []
10
- this.cwd = cwd ?? process.cwd()
11
- }
12
- /** 命令行参数 */
13
- args: string[]
14
- /** 当前工作目录 */
15
- cwd: string
3
+ export interface MarkdownPaperTheme {
16
4
  /**
17
5
  * css 样式
18
6
  * 不含 \<style>\</style>
19
7
  */
20
- abstract css: string
8
+ css: string
21
9
  /**
22
10
  * 预处理 markdown 字符串
23
11
  * 用于转换自定义标签等
24
12
  * @param md markdown 字符串
25
13
  * @returns 转换后的 markdown 字符串
26
14
  */
27
- abstract preParseMarkdown(md: string): Promise<string>
15
+ preParseMarkdown(md: string): Promise<string>
28
16
  /**
29
17
  * 预处理 html 字符串
30
18
  * 将在保存 html 文件前调用
31
19
  * @param html html 字符串
32
20
  * @returns 转换后的 html 字符串
33
21
  */
34
- abstract preParseHTML(html: string): Promise<string>
22
+ preParseHTML(html: string): Promise<string>
35
23
  /**
36
24
  * 在网页中要执行的函数
37
25
  * 将在保存 html 文件后调用
38
26
  */
39
- abstract script(): void
27
+ script(): void
40
28
  /**
41
29
  * PDF 参数
42
30
  * 无需设置路径
43
31
  */
44
- abstract pdfOptions: PDFOptions
32
+ pdfOptions: PDFOptions
45
33
  }