@v1hz/md2docx 2.4.0 → 2.5.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 +26 -5
- package/config/config.schema.json +2 -2
- package/config/style-config.json +23 -2
- package/config/style-config.schema.json +55 -0
- package/dist/index.js +101 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -216,8 +216,8 @@ CLI 不支持覆盖单个配置项,所有配置都通过 JSON 文件管理。
|
|
|
216
216
|
| `renderMermaid.theme` | beautiful-mermaid 主题 | `"tokyo-night-light"` |
|
|
217
217
|
| `renderMermaid.density` | PNG 输出 DPI,最小值 72 | `200` |
|
|
218
218
|
| `imageSize.enabled` | 等比缩小超过尺寸限制的图片 | `true` |
|
|
219
|
-
| `imageSize.maxWidthCm` | DOCX 图片最大宽度(厘米) | `
|
|
220
|
-
| `imageSize.maxHeightCm` | DOCX 图片最大高度(厘米) | `
|
|
219
|
+
| `imageSize.maxWidthCm` | DOCX 图片最大宽度(厘米) | `12` |
|
|
220
|
+
| `imageSize.maxHeightCm` | DOCX 图片最大高度(厘米) | `12` |
|
|
221
221
|
| `removeThematicBreaks.enabled` | 移除 `---`、`***`、`___` 等分隔符行 | `true` |
|
|
222
222
|
|
|
223
223
|
图片尺寸限制在 Pandoc 生成 DOCX 前通过 Lua filter 应用。程序读取图片像素尺寸和 DPI,仅缩小超限图片,不会放大小图;宽度和高度使用同一缩放比例。Markdown 中已经显式设置 `width` 或 `height` 的图片视为用户覆盖,不应用全局限制。单张图片无法读取尺寸时会输出警告并继续转换。
|
|
@@ -234,11 +234,32 @@ CLI 不支持覆盖单个配置项,所有配置都通过 JSON 文件管理。
|
|
|
234
234
|
"schemaVersion": 1,
|
|
235
235
|
"options": {
|
|
236
236
|
"body": {
|
|
237
|
-
"firstLineIndent": false
|
|
237
|
+
"firstLineIndent": false,
|
|
238
|
+
"lineSpacing": "onePointFive"
|
|
238
239
|
},
|
|
239
240
|
"headings": {
|
|
240
241
|
"1": {
|
|
241
|
-
"startOnNewPage": false
|
|
242
|
+
"startOnNewPage": false,
|
|
243
|
+
"alignment": "left",
|
|
244
|
+
"bold": true
|
|
245
|
+
},
|
|
246
|
+
"2": {
|
|
247
|
+
"bold": true
|
|
248
|
+
},
|
|
249
|
+
"3": {
|
|
250
|
+
"bold": true
|
|
251
|
+
},
|
|
252
|
+
"4": {
|
|
253
|
+
"bold": true,
|
|
254
|
+
"italic": false
|
|
255
|
+
},
|
|
256
|
+
"5": {
|
|
257
|
+
"bold": true,
|
|
258
|
+
"italic": false
|
|
259
|
+
},
|
|
260
|
+
"6": {
|
|
261
|
+
"bold": true,
|
|
262
|
+
"italic": false
|
|
242
263
|
}
|
|
243
264
|
},
|
|
244
265
|
"inlineCode": {
|
|
@@ -257,7 +278,7 @@ CLI 不支持覆盖单个配置项,所有配置都通过 JSON 文件管理。
|
|
|
257
278
|
md2docx -f report.md --style-config style-config.json
|
|
258
279
|
```
|
|
259
280
|
|
|
260
|
-
字段缺失表示继承底层样式;`true` 表示显式启用完整效果;`false`
|
|
281
|
+
字段缺失表示继承底层样式;`true` 表示显式启用完整效果;`false` 表示显式关闭。正文行距为浮点数倍数,例如 `1.5` 表示 1.5 倍行距;一级标题对齐只接受 `left`、`center`。当前还开放一至六级标题粗体,以及四至六级标题斜体。完整设计见 [`docs/style-config-design.md`](docs/style-config-design.md)。
|
|
261
282
|
|
|
262
283
|
转换时按以下规则选择输入:
|
|
263
284
|
|
|
@@ -131,13 +131,13 @@
|
|
|
131
131
|
"description": "图片最大宽度(厘米)",
|
|
132
132
|
"type": "number",
|
|
133
133
|
"exclusiveMinimum": 0,
|
|
134
|
-
"default":
|
|
134
|
+
"default": 12
|
|
135
135
|
},
|
|
136
136
|
"maxHeightCm": {
|
|
137
137
|
"description": "图片最大高度(厘米)",
|
|
138
138
|
"type": "number",
|
|
139
139
|
"exclusiveMinimum": 0,
|
|
140
|
-
"default":
|
|
140
|
+
"default": 12
|
|
141
141
|
}
|
|
142
142
|
},
|
|
143
143
|
"required": ["enabled", "maxWidthCm", "maxHeightCm"]
|
package/config/style-config.json
CHANGED
|
@@ -3,11 +3,32 @@
|
|
|
3
3
|
"schemaVersion": 1,
|
|
4
4
|
"options": {
|
|
5
5
|
"body": {
|
|
6
|
-
"firstLineIndent": true
|
|
6
|
+
"firstLineIndent": true,
|
|
7
|
+
"lineSpacing": 1.5
|
|
7
8
|
},
|
|
8
9
|
"headings": {
|
|
9
10
|
"1": {
|
|
10
|
-
"startOnNewPage": false
|
|
11
|
+
"startOnNewPage": false,
|
|
12
|
+
"alignment": "left",
|
|
13
|
+
"bold": true
|
|
14
|
+
},
|
|
15
|
+
"2": {
|
|
16
|
+
"bold": true
|
|
17
|
+
},
|
|
18
|
+
"3": {
|
|
19
|
+
"bold": true
|
|
20
|
+
},
|
|
21
|
+
"4": {
|
|
22
|
+
"bold": true,
|
|
23
|
+
"italic": false
|
|
24
|
+
},
|
|
25
|
+
"5": {
|
|
26
|
+
"bold": true,
|
|
27
|
+
"italic": false
|
|
28
|
+
},
|
|
29
|
+
"6": {
|
|
30
|
+
"bold": true,
|
|
31
|
+
"italic": false
|
|
11
32
|
}
|
|
12
33
|
},
|
|
13
34
|
"inlineCode": {
|
|
@@ -23,6 +23,11 @@
|
|
|
23
23
|
"firstLineIndent": {
|
|
24
24
|
"type": "boolean",
|
|
25
25
|
"description": "正文是否使用预设的首行缩进"
|
|
26
|
+
},
|
|
27
|
+
"lineSpacing": {
|
|
28
|
+
"type": "number",
|
|
29
|
+
"exclusiveMinimum": 0,
|
|
30
|
+
"description": "正文行距倍数,如 1.5 表示 1.5 倍行距"
|
|
26
31
|
}
|
|
27
32
|
}
|
|
28
33
|
},
|
|
@@ -37,8 +42,32 @@
|
|
|
37
42
|
"startOnNewPage": {
|
|
38
43
|
"type": "boolean",
|
|
39
44
|
"description": "一级标题是否另起一页"
|
|
45
|
+
},
|
|
46
|
+
"alignment": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"enum": ["left", "center"],
|
|
49
|
+
"description": "一级标题左对齐或居中"
|
|
50
|
+
},
|
|
51
|
+
"bold": {
|
|
52
|
+
"type": "boolean",
|
|
53
|
+
"description": "一级标题是否加粗"
|
|
40
54
|
}
|
|
41
55
|
}
|
|
56
|
+
},
|
|
57
|
+
"2": {
|
|
58
|
+
"$ref": "#/$defs/headingBold"
|
|
59
|
+
},
|
|
60
|
+
"3": {
|
|
61
|
+
"$ref": "#/$defs/headingBold"
|
|
62
|
+
},
|
|
63
|
+
"4": {
|
|
64
|
+
"$ref": "#/$defs/headingBoldItalic"
|
|
65
|
+
},
|
|
66
|
+
"5": {
|
|
67
|
+
"$ref": "#/$defs/headingBoldItalic"
|
|
68
|
+
},
|
|
69
|
+
"6": {
|
|
70
|
+
"$ref": "#/$defs/headingBoldItalic"
|
|
42
71
|
}
|
|
43
72
|
}
|
|
44
73
|
},
|
|
@@ -64,5 +93,31 @@
|
|
|
64
93
|
}
|
|
65
94
|
}
|
|
66
95
|
}
|
|
96
|
+
},
|
|
97
|
+
"$defs": {
|
|
98
|
+
"headingBold": {
|
|
99
|
+
"type": "object",
|
|
100
|
+
"additionalProperties": false,
|
|
101
|
+
"properties": {
|
|
102
|
+
"bold": {
|
|
103
|
+
"type": "boolean",
|
|
104
|
+
"description": "标题是否加粗"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"headingBoldItalic": {
|
|
109
|
+
"type": "object",
|
|
110
|
+
"additionalProperties": false,
|
|
111
|
+
"properties": {
|
|
112
|
+
"bold": {
|
|
113
|
+
"type": "boolean",
|
|
114
|
+
"description": "标题是否加粗"
|
|
115
|
+
},
|
|
116
|
+
"italic": {
|
|
117
|
+
"type": "boolean",
|
|
118
|
+
"description": "标题是否斜体"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
67
122
|
}
|
|
68
123
|
}
|
package/dist/index.js
CHANGED
|
@@ -128401,7 +128401,7 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
|
128401
128401
|
// package.json
|
|
128402
128402
|
var package_default = {
|
|
128403
128403
|
name: "@v1hz/md2docx",
|
|
128404
|
-
version: "2.
|
|
128404
|
+
version: "2.5.0",
|
|
128405
128405
|
description: "基于 pandoc 的 Markdown 转 Word 工具,自动格式化 Markdown,支持自定义样式,AI 友好,用户友好。",
|
|
128406
128406
|
keywords: [
|
|
128407
128407
|
"converter",
|
|
@@ -147199,11 +147199,32 @@ var style_config_default = `{
|
|
|
147199
147199
|
"schemaVersion": 1,
|
|
147200
147200
|
"options": {
|
|
147201
147201
|
"body": {
|
|
147202
|
-
"firstLineIndent": true
|
|
147202
|
+
"firstLineIndent": true,
|
|
147203
|
+
"lineSpacing": 1.5
|
|
147203
147204
|
},
|
|
147204
147205
|
"headings": {
|
|
147205
147206
|
"1": {
|
|
147206
|
-
"startOnNewPage": false
|
|
147207
|
+
"startOnNewPage": false,
|
|
147208
|
+
"alignment": "left",
|
|
147209
|
+
"bold": true
|
|
147210
|
+
},
|
|
147211
|
+
"2": {
|
|
147212
|
+
"bold": true
|
|
147213
|
+
},
|
|
147214
|
+
"3": {
|
|
147215
|
+
"bold": true
|
|
147216
|
+
},
|
|
147217
|
+
"4": {
|
|
147218
|
+
"bold": true,
|
|
147219
|
+
"italic": false
|
|
147220
|
+
},
|
|
147221
|
+
"5": {
|
|
147222
|
+
"bold": true,
|
|
147223
|
+
"italic": false
|
|
147224
|
+
},
|
|
147225
|
+
"6": {
|
|
147226
|
+
"bold": true,
|
|
147227
|
+
"italic": false
|
|
147207
147228
|
}
|
|
147208
147229
|
},
|
|
147209
147230
|
"inlineCode": {
|
|
@@ -147265,21 +147286,25 @@ function validateOptions(value2, path3) {
|
|
|
147265
147286
|
rejectUnknown(options, ["body", "headings", "inlineCode", "codeBlock"], path3, "options");
|
|
147266
147287
|
if (options.body !== undefined) {
|
|
147267
147288
|
const body = expectRecord2(options.body, path3, "options.body");
|
|
147268
|
-
rejectUnknown(body, ["firstLineIndent"], path3, "options.body");
|
|
147289
|
+
rejectUnknown(body, ["firstLineIndent", "lineSpacing"], path3, "options.body");
|
|
147269
147290
|
if (body.firstLineIndent !== undefined) {
|
|
147270
147291
|
expectBoolean2(body.firstLineIndent, path3, "options.body.firstLineIndent");
|
|
147271
147292
|
}
|
|
147293
|
+
if (body.lineSpacing !== undefined) {
|
|
147294
|
+
if (typeof body.lineSpacing !== "number" || !Number.isFinite(body.lineSpacing) || body.lineSpacing <= 0) {
|
|
147295
|
+
invalidStyleConfig(path3, "options.body.lineSpacing", "必须是大于 0 的有限数字");
|
|
147296
|
+
}
|
|
147297
|
+
}
|
|
147272
147298
|
}
|
|
147273
147299
|
if (options.headings !== undefined) {
|
|
147274
147300
|
const headings = expectRecord2(options.headings, path3, "options.headings");
|
|
147275
|
-
rejectUnknown(headings, ["1"], path3, "options.headings");
|
|
147276
|
-
|
|
147277
|
-
|
|
147278
|
-
|
|
147279
|
-
|
|
147280
|
-
|
|
147281
|
-
|
|
147282
|
-
}
|
|
147301
|
+
rejectUnknown(headings, ["1", "2", "3", "4", "5", "6"], path3, "options.headings");
|
|
147302
|
+
validateHeading(headings, "1", ["startOnNewPage", "alignment", "bold"], path3);
|
|
147303
|
+
validateHeading(headings, "2", ["bold"], path3);
|
|
147304
|
+
validateHeading(headings, "3", ["bold"], path3);
|
|
147305
|
+
validateHeading(headings, "4", ["bold", "italic"], path3);
|
|
147306
|
+
validateHeading(headings, "5", ["bold", "italic"], path3);
|
|
147307
|
+
validateHeading(headings, "6", ["bold", "italic"], path3);
|
|
147283
147308
|
}
|
|
147284
147309
|
if (options.inlineCode !== undefined) {
|
|
147285
147310
|
const inlineCode2 = expectRecord2(options.inlineCode, path3, "options.inlineCode");
|
|
@@ -147296,6 +147321,23 @@ function validateOptions(value2, path3) {
|
|
|
147296
147321
|
}
|
|
147297
147322
|
}
|
|
147298
147323
|
}
|
|
147324
|
+
function validateHeading(headings, level, allowed, path3) {
|
|
147325
|
+
if (headings[level] === undefined)
|
|
147326
|
+
return;
|
|
147327
|
+
const field = `options.headings["${level}"]`;
|
|
147328
|
+
const heading2 = expectRecord2(headings[level], path3, field);
|
|
147329
|
+
rejectUnknown(heading2, allowed, path3, field);
|
|
147330
|
+
if (heading2.startOnNewPage !== undefined) {
|
|
147331
|
+
expectBoolean2(heading2.startOnNewPage, path3, `${field}.startOnNewPage`);
|
|
147332
|
+
}
|
|
147333
|
+
if (heading2.alignment !== undefined) {
|
|
147334
|
+
expectEnum(heading2.alignment, ["left", "center"], path3, `${field}.alignment`);
|
|
147335
|
+
}
|
|
147336
|
+
if (heading2.bold !== undefined)
|
|
147337
|
+
expectBoolean2(heading2.bold, path3, `${field}.bold`);
|
|
147338
|
+
if (heading2.italic !== undefined)
|
|
147339
|
+
expectBoolean2(heading2.italic, path3, `${field}.italic`);
|
|
147340
|
+
}
|
|
147299
147341
|
function rejectUnknown(value2, allowed, path3, field) {
|
|
147300
147342
|
const unknown2 = Object.keys(value2).find((key) => !allowed.includes(key));
|
|
147301
147343
|
if (unknown2 !== undefined)
|
|
@@ -147317,6 +147359,11 @@ function expectString2(value2, path3, field) {
|
|
|
147317
147359
|
if (typeof value2 !== "string")
|
|
147318
147360
|
invalidStyleConfig(path3, field, "必须是字符串");
|
|
147319
147361
|
}
|
|
147362
|
+
function expectEnum(value2, allowed, path3, field) {
|
|
147363
|
+
if (typeof value2 !== "string" || !allowed.includes(value2)) {
|
|
147364
|
+
invalidStyleConfig(path3, field, `必须是 ${allowed.join("、")} 之一`);
|
|
147365
|
+
}
|
|
147366
|
+
}
|
|
147320
147367
|
function invalidStyleConfig(path3, field, reason) {
|
|
147321
147368
|
throw new Error(`样式配置无效:${path3}
|
|
147322
147369
|
位置:${field}
|
|
@@ -147324,6 +147371,7 @@ function invalidStyleConfig(path3, field, reason) {
|
|
|
147324
147371
|
}
|
|
147325
147372
|
|
|
147326
147373
|
// src/style/compiler.ts
|
|
147374
|
+
var HEADING_LEVELS = ["1", "2", "3", "4", "5", "6"];
|
|
147327
147375
|
function resolveEffectiveStyles(sources, defaults) {
|
|
147328
147376
|
const rawPath = sources.styleRawPath ?? defaults?.styleRawPath ?? materializeDefaultStyleRaw();
|
|
147329
147377
|
const rawStyle = loadStyleRaw(rawPath);
|
|
@@ -147365,9 +147413,22 @@ function compileStyleConfig(rawStyle, config) {
|
|
|
147365
147413
|
const firstLineIndent = options.body?.firstLineIndent;
|
|
147366
147414
|
if (firstLineIndent !== undefined)
|
|
147367
147415
|
applyBodyFirstLineIndent(styles, firstLineIndent);
|
|
147416
|
+
const lineSpacing = options.body?.lineSpacing;
|
|
147417
|
+
if (lineSpacing !== undefined)
|
|
147418
|
+
applyBodyLineSpacing(styles, lineSpacing);
|
|
147368
147419
|
const startOnNewPage = options.headings?.["1"]?.startOnNewPage;
|
|
147369
147420
|
if (startOnNewPage !== undefined)
|
|
147370
147421
|
applyHeading1PageBreak(styles, startOnNewPage);
|
|
147422
|
+
const heading1Alignment = options.headings?.["1"]?.alignment;
|
|
147423
|
+
if (heading1Alignment !== undefined)
|
|
147424
|
+
applyHeading1Alignment(styles, heading1Alignment);
|
|
147425
|
+
for (const level of HEADING_LEVELS) {
|
|
147426
|
+
const heading2 = options.headings?.[level];
|
|
147427
|
+
if (heading2?.bold !== undefined)
|
|
147428
|
+
applyHeadingBold(styles, level, heading2.bold);
|
|
147429
|
+
if (heading2?.italic !== undefined)
|
|
147430
|
+
applyHeadingItalic(styles, level, heading2.italic);
|
|
147431
|
+
}
|
|
147371
147432
|
const inlineCodeBackground = options.inlineCode?.background;
|
|
147372
147433
|
if (inlineCodeBackground !== undefined) {
|
|
147373
147434
|
applyInlineCodeBackground(styles, inlineCodeBackground);
|
|
@@ -147377,6 +147438,18 @@ function compileStyleConfig(rawStyle, config) {
|
|
|
147377
147438
|
applyCodeBlockBorder(styles, codeBlockBorder);
|
|
147378
147439
|
return styles;
|
|
147379
147440
|
}
|
|
147441
|
+
function applyBodyLineSpacing(styles, multiplier) {
|
|
147442
|
+
const line = Math.round(multiplier * 240);
|
|
147443
|
+
for (const name2 of ["First Paragraph", "Body Text"]) {
|
|
147444
|
+
const style = findStyle(styles, "paragraphStyles", name2);
|
|
147445
|
+
const paragraph2 = ensureObject(style, "paragraph");
|
|
147446
|
+
paragraph2.spacing = {
|
|
147447
|
+
...optionalObject(paragraph2.spacing),
|
|
147448
|
+
line,
|
|
147449
|
+
lineRule: "auto"
|
|
147450
|
+
};
|
|
147451
|
+
}
|
|
147452
|
+
}
|
|
147380
147453
|
function applyBodyFirstLineIndent(styles, enabled) {
|
|
147381
147454
|
const firstParagraph = findStyle(styles, "paragraphStyles", "First Paragraph");
|
|
147382
147455
|
const bodyText = findStyle(styles, "paragraphStyles", "Body Text");
|
|
@@ -147393,6 +147466,22 @@ function applyHeading1PageBreak(styles, enabled) {
|
|
|
147393
147466
|
const heading1 = findStyle(styles, "paragraphStyles", "heading 1");
|
|
147394
147467
|
ensureObject(heading1, "paragraph").pageBreakBefore = enabled;
|
|
147395
147468
|
}
|
|
147469
|
+
function applyHeading1Alignment(styles, alignment) {
|
|
147470
|
+
const heading1 = findStyle(styles, "paragraphStyles", "heading 1");
|
|
147471
|
+
ensureObject(heading1, "paragraph").alignment = alignment;
|
|
147472
|
+
}
|
|
147473
|
+
function applyHeadingBold(styles, level, enabled) {
|
|
147474
|
+
const heading2 = findStyle(styles, "paragraphStyles", `heading ${level}`);
|
|
147475
|
+
const run = ensureObject(heading2, "run");
|
|
147476
|
+
run.bold = enabled;
|
|
147477
|
+
run.boldComplexScript = enabled;
|
|
147478
|
+
}
|
|
147479
|
+
function applyHeadingItalic(styles, level, enabled) {
|
|
147480
|
+
const heading2 = findStyle(styles, "paragraphStyles", `heading ${level}`);
|
|
147481
|
+
const run = ensureObject(heading2, "run");
|
|
147482
|
+
run.italics = enabled;
|
|
147483
|
+
run.italicsComplexScript = enabled;
|
|
147484
|
+
}
|
|
147396
147485
|
function applyInlineCodeBackground(styles, enabled) {
|
|
147397
147486
|
const inlineCode2 = findStyle(styles, "characterStyles", "Inline Code");
|
|
147398
147487
|
ensureObject(inlineCode2, "run").shading = {
|