markdown-paper 2.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 +83 -0
- package/bin/mdp.ts +37 -0
- package/bun.lockb +0 -0
- package/lib/docx.ts +18 -0
- package/lib/main.ts +177 -0
- package/license +674 -0
- package/package.json +28 -0
- package/theme/aps/aps.css +134 -0
- package/theme/aps/aps.md +41 -0
- package/theme/aps/aps.ts +122 -0
- package/theme/bnu/bnu.css +0 -0
- package/theme/bnu/bnu.md +0 -0
- package/theme/bnu/bnu.ts +0 -0
- package/theme/theme.ts +45 -0
- package/tsconfig.json +27 -0
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "markdown-paper",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "2.0.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "LeafYeeXYZ",
|
|
7
|
+
"email": "xiaoyezi@leafyee.xyz"
|
|
8
|
+
},
|
|
9
|
+
"license": "GPL-3.0-only",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"mdp": "bun ./bin/mdp.ts",
|
|
12
|
+
"pub": "npm publish",
|
|
13
|
+
"try": "bun mdp ../../GoogleDrive/Curriculum/论文写作/论文.md --out=./demo/论文 --outputHTML"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"mdp": "./bin/mdp.ts"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/bun": "latest"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"typescript": "^5.0.0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"marked": "^12.0.2",
|
|
26
|
+
"puppeteer-core": "^22.9.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
* {
|
|
2
|
+
font-family: 'Times New Roman', '宋体'; /* 所有数字和英文字体都用 Times New Roman */
|
|
3
|
+
line-height: 1.55em; /* 1.5倍行距 */
|
|
4
|
+
margin: 0;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
h1 { /* 中文题目: 二号黑体 */
|
|
8
|
+
font-size: 29px;
|
|
9
|
+
font-weight: normal;
|
|
10
|
+
font-family: '黑体';
|
|
11
|
+
text-align: center;
|
|
12
|
+
margin-bottom: 9px;
|
|
13
|
+
}
|
|
14
|
+
.author { /* 作者姓名: 四号仿宋 */
|
|
15
|
+
font-size: 18px;
|
|
16
|
+
font-family: '仿宋';
|
|
17
|
+
text-align: center;
|
|
18
|
+
margin-bottom: 3px;
|
|
19
|
+
}
|
|
20
|
+
.school { /* 作者单位: 小五宋体 */
|
|
21
|
+
font-size: 12px;
|
|
22
|
+
text-align: center;
|
|
23
|
+
margin-bottom: 38px;
|
|
24
|
+
}
|
|
25
|
+
.abstract { /* 摘要和关键词: 五号宋体 */
|
|
26
|
+
font-size: 14px;
|
|
27
|
+
text-align: justify;
|
|
28
|
+
padding: 0 28px;
|
|
29
|
+
&::before {
|
|
30
|
+
content: '摘 要';
|
|
31
|
+
font-family: '黑体';
|
|
32
|
+
display: inline-block;
|
|
33
|
+
margin-right: 14px;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
.keywords {
|
|
37
|
+
font-size: 14px;
|
|
38
|
+
margin-bottom: 31px;
|
|
39
|
+
padding: 0 28px;
|
|
40
|
+
&::before {
|
|
41
|
+
content: '关键词';
|
|
42
|
+
font-family: '黑体';
|
|
43
|
+
display: inline-block;
|
|
44
|
+
margin-right: 14px;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
h2 { /* 一级标题: 四号宋体 */
|
|
49
|
+
font-size: 18px;
|
|
50
|
+
font-weight: normal;
|
|
51
|
+
margin: 7px 0;
|
|
52
|
+
}
|
|
53
|
+
h3 { /* 二级标题: 五号黑体 */
|
|
54
|
+
font-size: 14px;
|
|
55
|
+
font-weight: normal;
|
|
56
|
+
font-family: '黑体';
|
|
57
|
+
margin: 5px 0;
|
|
58
|
+
}
|
|
59
|
+
h4 { /* 三级标题: 五号黑体 */
|
|
60
|
+
font-size: 14px;
|
|
61
|
+
font-weight: normal;
|
|
62
|
+
font-family: '黑体';
|
|
63
|
+
margin: 3px 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
p { /* 正文: 五号宋体 */
|
|
67
|
+
font-size: 14px;
|
|
68
|
+
text-indent: 24px;
|
|
69
|
+
text-align: justify;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
h5 { /* "参考文献": 五号黑体 */
|
|
73
|
+
font-size: 14px;
|
|
74
|
+
font-weight: normal;
|
|
75
|
+
font-family: '黑体';
|
|
76
|
+
text-align: center;
|
|
77
|
+
margin-bottom: 7px;
|
|
78
|
+
margin-top: 20px;
|
|
79
|
+
}
|
|
80
|
+
ul, ol { /* 参考文献的项目: 小五号宋体 */
|
|
81
|
+
list-style-type: none;
|
|
82
|
+
padding: 0;
|
|
83
|
+
& > li {
|
|
84
|
+
font-size: 12px;
|
|
85
|
+
margin: 6px 0;
|
|
86
|
+
text-align: justify;
|
|
87
|
+
text-indent: -24px;
|
|
88
|
+
padding-left: 24px;
|
|
89
|
+
}
|
|
90
|
+
& a {
|
|
91
|
+
text-decoration: none;
|
|
92
|
+
color: black;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
img {
|
|
97
|
+
display: block;
|
|
98
|
+
max-width: 100%;
|
|
99
|
+
margin: 0 auto;
|
|
100
|
+
margin-top: 10px;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
blockquote, blockquote > p { /* 图片和表格的标题: 小五号宋体 */
|
|
104
|
+
font-size: 12px;
|
|
105
|
+
font-weight: normal;
|
|
106
|
+
text-align: center;
|
|
107
|
+
margin: 0;
|
|
108
|
+
}
|
|
109
|
+
blockquote > p { margin: 6px 0; }
|
|
110
|
+
table { /* 表格: 小五号宋体 */
|
|
111
|
+
font-size: 12px;
|
|
112
|
+
position: relative;
|
|
113
|
+
border-top: 1px solid black;
|
|
114
|
+
border-bottom: 1px solid black;
|
|
115
|
+
width: 100%;
|
|
116
|
+
max-width: 100%;
|
|
117
|
+
margin: 0 auto;
|
|
118
|
+
margin-bottom: 10px;
|
|
119
|
+
& th, & td {
|
|
120
|
+
font-weight: normal;
|
|
121
|
+
}
|
|
122
|
+
& thead::after { /* 用来做三线表中间的横线 */
|
|
123
|
+
content: '';
|
|
124
|
+
display: block;
|
|
125
|
+
position: absolute;
|
|
126
|
+
border-top: 1px solid #00000060;
|
|
127
|
+
width: 100%;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
b, strong { /* 加粗按黑体处理 */
|
|
132
|
+
font-family: '黑体';
|
|
133
|
+
font-weight: normal;
|
|
134
|
+
}
|
package/theme/aps/aps.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# APS 模板
|
|
2
|
+
`Acta Psychologica Sinica`, 心理学报
|
|
3
|
+
|
|
4
|
+
## 额外命令行参数
|
|
5
|
+
| 参数 | 说明 |
|
|
6
|
+
| :---: | :---: |
|
|
7
|
+
| `--showTitle` | 在页眉显示文件名, 默认不显示 |
|
|
8
|
+
| `--hideFooter` | 隐藏页码, 默认显示 |
|
|
9
|
+
| `--zhPunctuation` | 将正文中的英文标点符号替换为中文标点符号, 默认不替换<br>仅替换 `PDF` 和 `DOCX` 文件 |
|
|
10
|
+
| `--enPunctuation` | 将正文中的中文标点符号替换为英文标点符号, 默认不替换<br>仅替换 `PDF` 和 `DOCX` 文件 |
|
|
11
|
+
|
|
12
|
+
## 编写格式
|
|
13
|
+
```markdown
|
|
14
|
+
# 中文标题
|
|
15
|
+
#author# 作者信息
|
|
16
|
+
#school# 单位信息
|
|
17
|
+
#abstract# 摘要内容
|
|
18
|
+
#keywords# 关键词内容
|
|
19
|
+
|
|
20
|
+
## 1 一级标题
|
|
21
|
+
### 1.1 二级标题
|
|
22
|
+
#### 1.1.1 三级标题
|
|
23
|
+
正文
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
> 图片标题
|
|
28
|
+
|
|
29
|
+
> 表格标题
|
|
30
|
+
|
|
31
|
+
| 表头1 | 表头2 |
|
|
32
|
+
| :---: | :---: |
|
|
33
|
+
| 内容1 | 内容2 |
|
|
34
|
+
|
|
35
|
+
##### 参考文献
|
|
36
|
+
- 文献1
|
|
37
|
+
- 文献2
|
|
38
|
+
- 文献3
|
|
39
|
+
|
|
40
|
+
##### 附录
|
|
41
|
+
```
|
package/theme/aps/aps.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Theme } from '../theme'
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import { resolve } from 'node:path'
|
|
4
|
+
import type { PDFOptions } from 'puppeteer-core'
|
|
5
|
+
|
|
6
|
+
export class APS extends Theme {
|
|
7
|
+
|
|
8
|
+
css: string
|
|
9
|
+
preParseMarkdown: (md: string) => string
|
|
10
|
+
preParseHTML: (html: string) => string
|
|
11
|
+
script: () => void
|
|
12
|
+
pdfOptions: PDFOptions
|
|
13
|
+
|
|
14
|
+
constructor(args: string[], cwd: string) {
|
|
15
|
+
|
|
16
|
+
super(args, cwd)
|
|
17
|
+
|
|
18
|
+
// 默认自定义参数
|
|
19
|
+
let showTitle: boolean = false
|
|
20
|
+
let hideFooter: boolean = false
|
|
21
|
+
let zhPunctuation: boolean = false
|
|
22
|
+
let enPunctuation: boolean = false
|
|
23
|
+
// 解析参数
|
|
24
|
+
args.forEach(arg => {
|
|
25
|
+
switch (arg.split('=')[0]) {
|
|
26
|
+
case '--showTitle': showTitle = true; break
|
|
27
|
+
case '--hideFooter': hideFooter = true; break
|
|
28
|
+
case '--zhPunctuation': zhPunctuation = true; break
|
|
29
|
+
case '--enPunctuation': enPunctuation = true; break
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
// css
|
|
34
|
+
this.css = readFileSync(resolve(import.meta.dir, 'aps.css'), 'utf-8')
|
|
35
|
+
|
|
36
|
+
// preParseMarkdown
|
|
37
|
+
this.preParseMarkdown = (md: string): string => {
|
|
38
|
+
// 作者
|
|
39
|
+
md = md.replace(/#author# (.*)/mg, '<div class="author">$1</div>')
|
|
40
|
+
// 单位
|
|
41
|
+
md = md.replace(/#school# (.*)/mg, '<div class="school">$1</div>')
|
|
42
|
+
// 关键词
|
|
43
|
+
md = md.replace(/#keywords# (.*)/mg, '<div class="keywords">$1</div>')
|
|
44
|
+
// 摘要
|
|
45
|
+
md = md.replace(/#abstract# (.*)/mg, '<div class="abstract">$1</div>')
|
|
46
|
+
// 返回处理后的字符串
|
|
47
|
+
return md
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// preParseHTML
|
|
51
|
+
// 把包裹图片的 p 标签去掉
|
|
52
|
+
this.preParseHTML = (html: string): string => {
|
|
53
|
+
html = html.replace(/<p><img (.*?)><\/p>/g, '<img $1>')
|
|
54
|
+
return html
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// script
|
|
58
|
+
// 替换标点
|
|
59
|
+
if (zhPunctuation && !enPunctuation) {
|
|
60
|
+
this.script = () => {
|
|
61
|
+
const nodes = document.querySelectorAll('body > p, .abstract, .keywords, .author, .school')
|
|
62
|
+
nodes.forEach(node => {
|
|
63
|
+
let text = node.textContent ?? ''
|
|
64
|
+
// 替换中英文逗号和句号
|
|
65
|
+
text = text.replace(/, /g, ',').replace(/\. /g, '。').replace(/\.$/, '。')
|
|
66
|
+
// 替换中英文冒号
|
|
67
|
+
text = text.replace(/: /g, ':')
|
|
68
|
+
// 替换中英文分号
|
|
69
|
+
text = text.replace(/; /g, ';')
|
|
70
|
+
// 替换中英文感叹号
|
|
71
|
+
text = text.replace(/! /g, '!').replace(/!$/, '!')
|
|
72
|
+
// 替换中英文问号
|
|
73
|
+
text = text.replace(/\? /g, '?').replace(/\?$/, '?')
|
|
74
|
+
// 替换中英文括号
|
|
75
|
+
text = text.replace(/ \(/g, '(').replace(/\) /g, ')')
|
|
76
|
+
// 恢复et al.,
|
|
77
|
+
text = text.replace(/et al\.,/g, 'et al., ').replace(/et al。/g, 'et al. ')
|
|
78
|
+
// 设置新文本
|
|
79
|
+
node.textContent = text
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
} else if (!zhPunctuation && enPunctuation) {
|
|
83
|
+
this.script = () => {
|
|
84
|
+
const nodes = document.querySelectorAll('body > p, .abstract, .keywords, .author, .school')
|
|
85
|
+
nodes.forEach(node => {
|
|
86
|
+
let text = node.textContent ?? ''
|
|
87
|
+
// 替换中英文逗号和句号
|
|
88
|
+
text = text.replace(/,/g, ', ').replace(/。 /g, '. ')
|
|
89
|
+
// 替换中英文冒号
|
|
90
|
+
text = text.replace(/:/g, ': ')
|
|
91
|
+
// 替换中英文分号
|
|
92
|
+
text = text.replace(/;/g, '; ')
|
|
93
|
+
// 替换中英文感叹号
|
|
94
|
+
text = text.replace(/!/g, '! ')
|
|
95
|
+
// 替换中英文问号
|
|
96
|
+
text = text.replace(/?/g, '? ')
|
|
97
|
+
// 替换中英文括号
|
|
98
|
+
text = text.replace(/(/g, ' (').replace(/)/g, ') ')
|
|
99
|
+
// 设置新文本
|
|
100
|
+
node.textContent = text
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
this.script = () => {}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// pdfOptions
|
|
108
|
+
this.pdfOptions = {
|
|
109
|
+
format: 'A4',
|
|
110
|
+
margin: {
|
|
111
|
+
top: '2cm',
|
|
112
|
+
right: '2.5cm',
|
|
113
|
+
bottom: '2cm',
|
|
114
|
+
left: '2.5cm'
|
|
115
|
+
},
|
|
116
|
+
displayHeaderFooter: showTitle || !hideFooter,
|
|
117
|
+
headerTemplate: showTitle ? `<div style="font-size: 9px; font-family: '宋体'; color: #333; padding: 5px; margin-left: 0.6cm;"> <span class="title"></span> </div>` : `<div></div>`,
|
|
118
|
+
footerTemplate: hideFooter ? `<div></div>` : `<div style="font-size: 9px; font-family: '宋体'; color: #333; padding: 5px; margin: 0 auto;">第 <span class="pageNumber"></span> 页 / 共 <span class="totalPages"></span> 页</div>`,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
}
|
|
File without changes
|
package/theme/bnu/bnu.md
ADDED
|
File without changes
|
package/theme/bnu/bnu.ts
ADDED
|
File without changes
|
package/theme/theme.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { PDFOptions } from 'puppeteer-core'
|
|
2
|
+
|
|
3
|
+
export abstract class Theme {
|
|
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
|
|
16
|
+
/**
|
|
17
|
+
* css 样式
|
|
18
|
+
* 不含 \<style>\</style>
|
|
19
|
+
*/
|
|
20
|
+
abstract css: string
|
|
21
|
+
/**
|
|
22
|
+
* 预处理 markdown 字符串
|
|
23
|
+
* 用于转换自定义标签等
|
|
24
|
+
* @param md markdown 字符串
|
|
25
|
+
* @returns 转换后的 markdown 字符串
|
|
26
|
+
*/
|
|
27
|
+
abstract preParseMarkdown(md: string): string
|
|
28
|
+
/**
|
|
29
|
+
* 预处理 html 字符串
|
|
30
|
+
* 将在保存 html 文件前调用
|
|
31
|
+
* @param html html 字符串
|
|
32
|
+
* @returns 转换后的 html 字符串
|
|
33
|
+
*/
|
|
34
|
+
abstract preParseHTML(html: string): string
|
|
35
|
+
/**
|
|
36
|
+
* 在网页中要执行的函数
|
|
37
|
+
* 将在保存 html 文件后调用
|
|
38
|
+
*/
|
|
39
|
+
abstract script(): void
|
|
40
|
+
/**
|
|
41
|
+
* PDF 参数
|
|
42
|
+
* 无需设置路径
|
|
43
|
+
*/
|
|
44
|
+
abstract pdfOptions: PDFOptions
|
|
45
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Enable latest features
|
|
4
|
+
"lib": ["ESNext", "DOM"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
|
|
11
|
+
// Bundler mode
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
// Best practices
|
|
18
|
+
"strict": true,
|
|
19
|
+
"skipLibCheck": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
|
|
22
|
+
// Some stricter flags (disabled by default)
|
|
23
|
+
"noUnusedLocals": true,
|
|
24
|
+
"noUnusedParameters": true,
|
|
25
|
+
"noPropertyAccessFromIndexSignature": true
|
|
26
|
+
}
|
|
27
|
+
}
|