@open-guji/markdown-pdf 0.1.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 ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # @open-guji/markdown-pdf
2
+
3
+ 古籍 Markdown 的 **PDF 生成器**。
4
+
5
+ > **状态:预览。** 可打印 HTML 的生成已经稳定;Chromium 后端能出 PDF,
6
+ > 但竖排的断行、双行夹注的精确排布仍在调。
7
+
8
+ 规范见仓库 [`spec/`](../../spec),本包实现规范 **0.1.0**。
9
+
10
+ ## 分层
11
+
12
+ ```
13
+ 源文 ──[@open-guji/markdown-web]──> HTML 片段
14
+
15
+ gujiToPrintHtml │ + @page 规则 + 竖排样式 + 可选 Paged.js
16
+
17
+ 可打印 HTML ──[Chromium]──> PDF
18
+ ```
19
+
20
+ 第一层是**纯函数**,没有外部依赖,浏览器里也能跑,输出可以交给任何排版引擎;
21
+ 第二层才需要 playwright。
22
+
23
+ ## 安装
24
+
25
+ ```bash
26
+ npm i @open-guji/markdown-pdf
27
+
28
+ # 要真的出 PDF
29
+ npm i -D playwright && npx playwright install chromium
30
+
31
+ # 想要更好的分页控制(页眉页脚、跨页避断),可选
32
+ npm i pagedjs
33
+ ```
34
+
35
+ ## 用法
36
+
37
+ ```js
38
+ import { gujiToPdf, gujiToPrintHtml } from '@open-guji/markdown-pdf'
39
+
40
+ // 只要可打印 HTML
41
+ const html = gujiToPrintHtml(source, { pageSize: 'A5', vertical: true })
42
+
43
+ // 出 PDF
44
+ const bytes = await gujiToPdf(source, { title: '明太祖實錄', banks: true })
45
+ ```
46
+
47
+ ### 命令行
48
+
49
+ ```bash
50
+ guji-pdf 實錄.gmd # → 實錄.pdf
51
+ guji-pdf 實錄.gmd --html # → 實錄.html,不调用排版后端
52
+ guji-pdf 實錄.gmd --size A4 --banks --title 明太祖實錄
53
+ guji-pdf --check # 检查排版后端是否可用
54
+ ```
55
+
56
+ ## 选项
57
+
58
+ ```ts
59
+ interface PdfOptions {
60
+ pageSize?: 'A4' | 'A5' | 'B5' | 'Letter' | string // 默认 'A5'
61
+ pageMargin?: string // CSS margin,默认 '20mm 18mm'
62
+ fontSize?: string // 默认 '11pt'
63
+ lineHeight?: number // 默认 1.85
64
+ vertical?: boolean // 竖排,默认 true
65
+ banks?: boolean // 版框与界行,默认 false
66
+ paged?: 'auto' | 'always' | 'never' // 是否内联 Paged.js,默认 'auto'
67
+ title?: string
68
+ extraCss?: string
69
+ executablePath?: string // 自带的 Chromium 路径
70
+ browserArgs?: string[]
71
+ timeout?: number // 默认 30000
72
+ }
73
+ ```
74
+
75
+ 外加 [web 包的全部选项](../web/README.md#选项)。
76
+
77
+ ## 为什么用浏览器排版
78
+
79
+ 竖排(`writing-mode: vertical-rl`)、专名线画在字的左侧
80
+ (`text-underline-position: left`)、抬头的逻辑方向外移——
81
+ 这些正好都是 CSS 书写模式已经解决的问题,而 web 包已经把语义标好了。
82
+ 让 PDF 复用同一份 HTML 与 CSS,意味着**网页和印本天然一致**,
83
+ 不必维护两套排版规则。
84
+
85
+ 评估过的其他后端:
86
+
87
+ | 方案 | 结论 |
88
+ | --- | --- |
89
+ | **Chromium + Paged.js**(当前) | 竖排与逻辑属性支持最好;分页控制靠 Paged.js 补齐 |
90
+ | Typst | 排版质量高、无需浏览器,但竖排 CJK 支持尚不完整 |
91
+ | XeLaTeX + xeCJK | 竖排成熟,但要另写一套从 mdast 到 TeX 的映射,且与网页样式两套维护 |
92
+ | 纯 pdfkit / PDF 库 | 需要自己实现断行与避头尾,工作量最大 |
93
+
94
+ 如果将来 Typst 的竖排 CJK 成熟,会作为第二后端加入——
95
+ `gujiToPrintHtml` 之前的那一段是共用的。
96
+
97
+ ## 已知限制
98
+
99
+ * 竖排下**双行夹注**用 CSS 多列近似,注文很长时会溢出列高。
100
+ * 没装 `pagedjs` 时由 Chromium 自行分页,页码与跨页避断的控制较弱。
101
+ * 版框与界行(`banks`)是用渐变画的,行距变化时需要同步调 `--guji-print-line-height`。
102
+
103
+ ## 路线图
104
+
105
+ - [ ] 双行夹注的精确断行(在 Paged.js 的 chunker 阶段做)
106
+ - [ ] 书口、鱼尾、版心页码
107
+ - [ ] Typst 后端
108
+ - [ ] 字体子集化,减小产物体积
109
+
110
+ ## 许可证
111
+
112
+ Apache-2.0
package/dist/cli.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ /** guji-pdf —— 把古籍 Markdown 转成 PDF 或可打印 HTML。 */
3
+ export {};
4
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,iDAAiD"}
package/dist/cli.js ADDED
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ /** guji-pdf —— 把古籍 Markdown 转成 PDF 或可打印 HTML。 */
3
+ import { readFile, writeFile } from 'node:fs/promises';
4
+ import { basename, extname } from 'node:path';
5
+ import { checkBackend, gujiToPdf, gujiToPrintHtml } from './index.js';
6
+ const USAGE = `用法:guji-pdf <输入.gmd> [选项]
7
+
8
+ 选项:
9
+ -o, --out <文件> 输出路径(默认与输入同名,改扩展名)
10
+ --html 只生成可打印 HTML,不调用排版后端
11
+ --size <纸张> A4 | A5 | B5 | Letter,默认 A5
12
+ --margin <边距> CSS margin 语法,默认 "20mm 18mm"
13
+ --font-size <值> 正文字号,默认 11pt
14
+ --line-height <值> 正文行距倍数,默认 1.85
15
+ --horizontal 横排(默认竖排)
16
+ --banks 加版框与界行
17
+ --title <标题> 文档标题
18
+ --check 检查排版后端是否可用
19
+ -h, --help 显示本帮助
20
+ `;
21
+ function parseArgs(argv) {
22
+ const options = {};
23
+ let input;
24
+ let out;
25
+ let html = false;
26
+ let check = false;
27
+ for (let i = 0; i < argv.length; i++) {
28
+ const arg = argv[i];
29
+ const next = () => {
30
+ const value = argv[++i];
31
+ if (value === undefined)
32
+ throw new Error(`选项 ${arg} 缺少参数`);
33
+ return value;
34
+ };
35
+ switch (arg) {
36
+ case '-h':
37
+ case '--help':
38
+ process.stdout.write(USAGE);
39
+ process.exit(0);
40
+ break;
41
+ case '--check':
42
+ check = true;
43
+ break;
44
+ case '--html':
45
+ html = true;
46
+ break;
47
+ case '-o':
48
+ case '--out':
49
+ out = next();
50
+ break;
51
+ case '--size':
52
+ options.pageSize = next();
53
+ break;
54
+ case '--margin':
55
+ options.pageMargin = next();
56
+ break;
57
+ case '--font-size':
58
+ options.fontSize = next();
59
+ break;
60
+ case '--line-height':
61
+ options.lineHeight = Number(next());
62
+ break;
63
+ case '--horizontal':
64
+ options.vertical = false;
65
+ break;
66
+ case '--banks':
67
+ options.banks = true;
68
+ break;
69
+ case '--title':
70
+ options.title = next();
71
+ break;
72
+ default:
73
+ if (arg.startsWith('-'))
74
+ throw new Error(`未知选项 ${arg}`);
75
+ input = arg;
76
+ }
77
+ }
78
+ return { input, out, html, check, options };
79
+ }
80
+ async function main() {
81
+ const { input, out, html, check, options } = parseArgs(process.argv.slice(2));
82
+ if (check) {
83
+ const backend = await checkBackend();
84
+ process.stdout.write(`playwright: ${backend.playwright ? '可用' : '未安装'}\n` +
85
+ `pagedjs: ${backend.pagedjs ? '可用' : '未安装(将由 Chromium 自行分页)'}\n`);
86
+ return;
87
+ }
88
+ if (!input) {
89
+ process.stderr.write(USAGE);
90
+ process.exitCode = 1;
91
+ return;
92
+ }
93
+ const source = await readFile(input, 'utf8');
94
+ const stem = basename(input, extname(input));
95
+ options.title ??= stem;
96
+ if (html) {
97
+ const target = out ?? `${stem}.html`;
98
+ await writeFile(target, gujiToPrintHtml(source, options));
99
+ process.stdout.write(`${target}\n`);
100
+ return;
101
+ }
102
+ const target = out ?? `${stem}.pdf`;
103
+ await writeFile(target, await gujiToPdf(source, options));
104
+ process.stdout.write(`${target}\n`);
105
+ }
106
+ main().catch((error) => {
107
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
108
+ process.exitCode = 1;
109
+ });
110
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `@open-guji/markdown-pdf`
3
+ *
4
+ * 古籍 Markdown 的 PDF 生成器。分两层:
5
+ *
6
+ * - {@link gujiToPrintHtml} —— 纯函数,源文 → 带 `@page` 规则的可打印 HTML;
7
+ * - {@link gujiToPdf} —— 把上面的 HTML 交给 Chromium 出 PDF(需要 playwright)。
8
+ *
9
+ * 规范见仓库 `spec/` 目录,排版决策见本包 README。
10
+ */
11
+ export { gujiToPrintHtml, loadPagedPolyfill } from './print-html.js';
12
+ export { gujiToPdf, checkBackend } from './pdf.js';
13
+ export { DEFAULT_PRINT_OPTIONS, type PageSize, type PrintOptions, type PdfOptions } from './options.js';
14
+ export { printCss } from './styles.generated.js';
15
+ /** 本包实现的规范版本。 */
16
+ export declare const SPEC_VERSION = "0.1.0";
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACpE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAClD,OAAO,EAAE,qBAAqB,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AACvG,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAEhD,iBAAiB;AACjB,eAAO,MAAM,YAAY,UAAU,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `@open-guji/markdown-pdf`
3
+ *
4
+ * 古籍 Markdown 的 PDF 生成器。分两层:
5
+ *
6
+ * - {@link gujiToPrintHtml} —— 纯函数,源文 → 带 `@page` 规则的可打印 HTML;
7
+ * - {@link gujiToPdf} —— 把上面的 HTML 交给 Chromium 出 PDF(需要 playwright)。
8
+ *
9
+ * 规范见仓库 `spec/` 目录,排版决策见本包 README。
10
+ */
11
+ export { gujiToPrintHtml, loadPagedPolyfill } from './print-html.js';
12
+ export { gujiToPdf, checkBackend } from './pdf.js';
13
+ export { DEFAULT_PRINT_OPTIONS } from './options.js';
14
+ export { printCss } from './styles.generated.js';
15
+ /** 本包实现的规范版本。 */
16
+ export const SPEC_VERSION = '0.1.0';
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACpE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAClD,OAAO,EAAE,qBAAqB,EAAqD,MAAM,cAAc,CAAA;AACvG,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAEhD,iBAAiB;AACjB,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAA"}
@@ -0,0 +1,60 @@
1
+ import type { DocumentOptions } from '@open-guji/markdown-web';
2
+ /** 纸张。字符串会原样写进 `@page { size: … }`。 */
3
+ export type PageSize = 'A4' | 'A5' | 'B5' | 'Letter' | (string & {});
4
+ export interface PrintOptions extends DocumentOptions {
5
+ /**
6
+ * 纸张尺寸。
7
+ * @default 'A5'
8
+ */
9
+ pageSize?: PageSize;
10
+ /**
11
+ * 页边距,CSS `margin` 语法。
12
+ * @default '20mm 18mm'
13
+ */
14
+ pageMargin?: string;
15
+ /**
16
+ * 正文字号。
17
+ * @default '11pt'
18
+ */
19
+ fontSize?: string;
20
+ /**
21
+ * 正文行距(无单位倍数)。
22
+ * @default 1.85
23
+ */
24
+ lineHeight?: number;
25
+ /**
26
+ * 加版框与界行(线装书版式)。
27
+ * @default false
28
+ */
29
+ banks?: boolean;
30
+ /**
31
+ * 是否内联 Paged.js。
32
+ * `auto` 表示能解析到 `pagedjs` 依赖时才内联,否则交给排版引擎自己分页。
33
+ * @default 'auto'
34
+ */
35
+ paged?: 'auto' | 'always' | 'never';
36
+ }
37
+ export interface PdfOptions extends PrintOptions {
38
+ /**
39
+ * 渲染超时(毫秒)。
40
+ * @default 30000
41
+ */
42
+ timeout?: number;
43
+ /**
44
+ * 浏览器可执行文件路径。留空则用 playwright 自带的 Chromium;
45
+ * CI 里常有一份预装的浏览器,指过去可以省掉一次下载。
46
+ */
47
+ executablePath?: string;
48
+ /** 传给 Chromium 的额外命令行参数。 */
49
+ browserArgs?: string[];
50
+ }
51
+ export declare const DEFAULT_PRINT_OPTIONS: {
52
+ readonly pageSize: "A5";
53
+ readonly pageMargin: "20mm 18mm";
54
+ readonly fontSize: "11pt";
55
+ readonly lineHeight: 1.85;
56
+ readonly banks: false;
57
+ readonly paged: "auto";
58
+ readonly vertical: true;
59
+ };
60
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAE9D,uCAAuC;AACvC,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,QAAQ,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAEpE,MAAM,WAAW,YAAa,SAAQ,eAAe;IACnD;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAA;CACpC;AAED,MAAM,WAAW,UAAW,SAAQ,YAAY;IAC9C;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,4BAA4B;IAC5B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB;AAED,eAAO,MAAM,qBAAqB;;;;;;;;CAQQ,CAAA"}
@@ -0,0 +1,10 @@
1
+ export const DEFAULT_PRINT_OPTIONS = {
2
+ pageSize: 'A5',
3
+ pageMargin: '20mm 18mm',
4
+ fontSize: '11pt',
5
+ lineHeight: 1.85,
6
+ banks: false,
7
+ paged: 'auto',
8
+ vertical: true
9
+ };
10
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAsDA,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE,WAAW;IACvB,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE,KAAK;IACZ,KAAK,EAAE,MAAM;IACb,QAAQ,EAAE,IAAI;CAC0B,CAAA"}
package/dist/pdf.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Chromium 排版后端。
3
+ *
4
+ * PDF 复用网页渲染的结果,理由见 README「为什么用浏览器排版」。
5
+ * playwright 是可选依赖,只有真的要出 PDF 时才需要。
6
+ */
7
+ import { type PdfOptions } from './options.js';
8
+ /** 源文 → PDF 字节流。 */
9
+ export declare function gujiToPdf(source: string, options?: PdfOptions): Promise<Uint8Array>;
10
+ /** 当前环境是否具备出 PDF 的条件。 */
11
+ export declare function checkBackend(): Promise<{
12
+ playwright: boolean;
13
+ pagedjs: boolean;
14
+ }>;
15
+ //# sourceMappingURL=pdf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf.d.ts","sourceRoot":"","sources":["../src/pdf.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAuCrE,oBAAoB;AACpB,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAwB7F;AAED,yBAAyB;AACzB,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAC5C,UAAU,EAAE,OAAO,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;CACjB,CAAC,CASD"}
package/dist/pdf.js ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Chromium 排版后端。
3
+ *
4
+ * PDF 复用网页渲染的结果,理由见 README「为什么用浏览器排版」。
5
+ * playwright 是可选依赖,只有真的要出 PDF 时才需要。
6
+ */
7
+ import { gujiToPrintHtml, loadPagedPolyfill } from './print-html.js';
8
+ import { DEFAULT_PRINT_OPTIONS } from './options.js';
9
+ async function loadChromium() {
10
+ try {
11
+ const mod = (await import('playwright'));
12
+ return mod.chromium;
13
+ }
14
+ catch {
15
+ throw new Error('生成 PDF 需要 playwright:\n' +
16
+ ' npm i -D playwright && npx playwright install chromium\n' +
17
+ '若只需要可打印的 HTML,请改用 gujiToPrintHtml()。');
18
+ }
19
+ }
20
+ /** 等待 Paged.js 完成分页;没有 Paged.js 时立即返回。 */
21
+ const WAIT_FOR_PAGED = `new Promise((resolve) => {
22
+ if (!window.PagedPolyfill) return resolve('native')
23
+ if (window.PagedPolyfill.rendered) return resolve('paged')
24
+ window.addEventListener('pagedjs:rendered', () => resolve('paged'), { once: true })
25
+ setTimeout(() => resolve('timeout'), 20000)
26
+ })`;
27
+ /** 源文 → PDF 字节流。 */
28
+ export async function gujiToPdf(source, options = {}) {
29
+ const o = { ...DEFAULT_PRINT_OPTIONS, timeout: 30_000, ...options };
30
+ const html = gujiToPrintHtml(source, o);
31
+ const chromium = await loadChromium();
32
+ const browser = await chromium.launch({
33
+ ...(o.executablePath ? { executablePath: o.executablePath } : {}),
34
+ ...(o.browserArgs ? { args: o.browserArgs } : {})
35
+ });
36
+ try {
37
+ const page = await browser.newPage();
38
+ await page.setContent(html, { waitUntil: 'load', timeout: o.timeout });
39
+ await page.evaluate(WAIT_FOR_PAGED);
40
+ await page.emulateMedia({ media: 'print' });
41
+ // Paged.js 已经把内容切成了固定尺寸的页盒,此时再让 Chromium 分页会重复分页;
42
+ // 没有 Paged.js 时则交给 Chromium 自己按 @page 规则分页。
43
+ return await page.pdf({
44
+ printBackground: true,
45
+ preferCSSPageSize: true,
46
+ displayHeaderFooter: false
47
+ });
48
+ }
49
+ finally {
50
+ await browser.close();
51
+ }
52
+ }
53
+ /** 当前环境是否具备出 PDF 的条件。 */
54
+ export async function checkBackend() {
55
+ let playwright = false;
56
+ try {
57
+ await import('playwright');
58
+ playwright = true;
59
+ }
60
+ catch {
61
+ playwright = false;
62
+ }
63
+ return { playwright, pagedjs: loadPagedPolyfill() !== undefined };
64
+ }
65
+ //# sourceMappingURL=pdf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf.js","sourceRoot":"","sources":["../src/pdf.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACpE,OAAO,EAAE,qBAAqB,EAAmB,MAAM,cAAc,CAAA;AAkBrE,KAAK,UAAU,YAAY;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,CAA0C,CAAA;QACjF,OAAO,GAAG,CAAC,QAAQ,CAAA;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,yBAAyB;YACvB,4DAA4D;YAC5D,sCAAsC,CACzC,CAAA;IACH,CAAC;AACH,CAAC;AAED,0CAA0C;AAC1C,MAAM,cAAc,GAAG;;;;;GAKpB,CAAA;AAEH,oBAAoB;AACpB,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc,EAAE,UAAsB,EAAE;IACtE,MAAM,CAAC,GAAG,EAAE,GAAG,qBAAqB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAAA;IACnE,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IACvC,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAA;IACrC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;QACpC,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClD,CAAC,CAAA;IAEF,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;QACpC,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;QACtE,MAAM,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA;QACnC,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;QAC3C,kDAAkD;QAClD,4CAA4C;QAC5C,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC;YACpB,eAAe,EAAE,IAAI;YACrB,iBAAiB,EAAE,IAAI;YACvB,mBAAmB,EAAE,KAAK;SAC3B,CAAC,CAAA;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;IACvB,CAAC;AACH,CAAC;AAED,yBAAyB;AACzB,MAAM,CAAC,KAAK,UAAU,YAAY;IAIhC,IAAI,UAAU,GAAG,KAAK,CAAA;IACtB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,YAAY,CAAC,CAAA;QAC1B,UAAU,GAAG,IAAI,CAAA;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,UAAU,GAAG,KAAK,CAAA;IACpB,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,SAAS,EAAE,CAAA;AACnE,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 源文 → 可打印 HTML。
3
+ *
4
+ * 这一步是纯函数、无外部依赖,浏览器里也能跑;
5
+ * 真正生成 PDF 的排版后端只是把它喂给一个排版引擎。
6
+ */
7
+ import { type PrintOptions } from './options.js';
8
+ /** 解析 `pagedjs` 的 polyfill 脚本;没装则返回 undefined。 */
9
+ export declare function loadPagedPolyfill(): string | undefined;
10
+ /** 源文 → 一张带 `@page` 规则、可以直接打印的 HTML 页面。 */
11
+ export declare function gujiToPrintHtml(source: string, options?: PrintOptions): string;
12
+ //# sourceMappingURL=print-html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"print-html.d.ts","sourceRoot":"","sources":["../src/print-html.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAGvE,kDAAkD;AAClD,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,SAAS,CAQtD;AAYD,2CAA2C;AAC3C,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAiBlF"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * 源文 → 可打印 HTML。
3
+ *
4
+ * 这一步是纯函数、无外部依赖,浏览器里也能跑;
5
+ * 真正生成 PDF 的排版后端只是把它喂给一个排版引擎。
6
+ */
7
+ import { gujiToHtml, renderDocument } from '@open-guji/markdown-web';
8
+ import { createRequire } from 'node:module';
9
+ import { readFileSync } from 'node:fs';
10
+ import { DEFAULT_PRINT_OPTIONS } from './options.js';
11
+ import { printCss } from './styles.generated.js';
12
+ /** 解析 `pagedjs` 的 polyfill 脚本;没装则返回 undefined。 */
13
+ export function loadPagedPolyfill() {
14
+ try {
15
+ const require = createRequire(import.meta.url);
16
+ const entry = require.resolve('pagedjs/dist/paged.polyfill.js');
17
+ return readFileSync(entry, 'utf8');
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ }
23
+ function pageVariables(options) {
24
+ return `:root {
25
+ --guji-page-size: ${options.pageSize};
26
+ --guji-page-margin: ${options.pageMargin};
27
+ --guji-print-font-size: ${options.fontSize};
28
+ --guji-print-line-height: ${options.lineHeight};
29
+ }`;
30
+ }
31
+ /** 源文 → 一张带 `@page` 规则、可以直接打印的 HTML 页面。 */
32
+ export function gujiToPrintHtml(source, options = {}) {
33
+ const o = { ...DEFAULT_PRINT_OPTIONS, ...options };
34
+ const polyfill = o.paged === 'never' ? undefined : loadPagedPolyfill();
35
+ if (o.paged === 'always' && !polyfill) {
36
+ throw new Error("paged: 'always' 需要安装 pagedjs(npm i pagedjs),或改用 paged: 'auto'。");
37
+ }
38
+ return renderDocument(gujiToHtml(source, o), {
39
+ ...o,
40
+ className: [o.banks ? 'guji-banks' : '', o.className ?? ''].filter(Boolean).join(' '),
41
+ extraCss: [pageVariables(o), printCss, o.extraCss ?? ''].filter(Boolean).join('\n'),
42
+ extraHead: [o.extraHead ?? '', polyfill ? `<script>\n${polyfill}\n</script>` : ''].
43
+ filter(Boolean).join('\n')
44
+ });
45
+ }
46
+ //# sourceMappingURL=print-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"print-html.js","sourceRoot":"","sources":["../src/print-html.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,qBAAqB,EAAqB,MAAM,cAAc,CAAA;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAEhD,kDAAkD;AAClD,MAAM,UAAU,iBAAiB;IAC/B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAA;QAC/D,OAAO,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,OACkC;IACvD,OAAO;sBACa,OAAO,CAAC,QAAQ;wBACd,OAAO,CAAC,UAAU;4BACd,OAAO,CAAC,QAAQ;8BACd,OAAO,CAAC,UAAU;EAC9C,CAAA;AACF,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,UAAwB,EAAE;IACxE,MAAM,CAAC,GAAG,EAAE,GAAG,qBAAqB,EAAE,GAAG,OAAO,EAAE,CAAA;IAElD,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAA;IACtE,IAAI,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CACb,gEAAgE,CACjE,CAAA;IACH,CAAC;IAED,OAAO,cAAc,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE;QAC3C,GAAG,CAAC;QACJ,SAAS,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QACrF,QAAQ,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACnF,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,aAAa,QAAQ,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;YAChF,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;KAC7B,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare const printCss = "/*!\n * print.css \u2014 \u53E4\u7C4D Markdown \u7684\u5206\u9875\u6392\u7248\u6837\u5F0F\n * @open-guji/markdown-pdf \u00B7 Apache-2.0\n *\n * \u4E0E guji.css / guji-vertical.css \u53E0\u52A0\u4F7F\u7528\u3002\u8FD9\u91CC\u53EA\u7BA1\"\u9875\"\u7684\u4E8B\uFF1A\n * \u9875\u9762\u5C3A\u5BF8\u3001\u9875\u8FB9\u8DDD\u3001\u7248\u5FC3\u3001\u4E66\u53E3\u4E0E\u5206\u9875\u63A7\u5236\u3002\n */\n\n@page {\n size: var(--guji-page-size, A5);\n margin: var(--guji-page-margin, 20mm 18mm);\n\n @bottom-center {\n content: counter(page);\n font-family: var(--guji-font);\n font-size: 9pt;\n color: #666;\n }\n}\n\n@page :first {\n @bottom-center {\n content: none;\n }\n}\n\n.guji-doc {\n /* \u6253\u5370\u65F6\u62AC\u5934\u7559\u767D\u6309\u5B9E\u9645\u5B57\u53F7\u6298\u7B97\uFF0C\u4E0D\u518D\u4F9D\u8D56\u89C6\u53E3 */\n --guji-raise-unit: 1em;\n font-size: var(--guji-print-font-size, 11pt);\n line-height: var(--guji-print-line-height, 1.85);\n}\n\n/* \u6807\u9898\u4E0D\u843D\u5728\u9875\u672B */\n.guji-doc h1,\n.guji-doc h2,\n.guji-doc h3 {\n break-after: avoid-page;\n break-inside: avoid;\n}\n\n/* \u62AC\u5934\u884C\u4E0D\u88AB\u5207\u5F00 */\n.guji-tt,\n.guji-tt-block {\n break-inside: avoid;\n}\n\n/* \u5939\u6CE8\u8DDF\u968F\u6B63\u6587\uFF0C\u4E0D\u5355\u72EC\u6210\u9875 */\n.guji-jz {\n break-inside: avoid;\n}\n\n.guji-doc p {\n orphans: 2;\n widows: 2;\n}\n\n/* ------------------------------------------------------ \u7EBF\u88C5\u4E66\u7248\u5F0F */\n\n/*\n * \u53EF\u9009\uFF1A\u52A0\u8FB9\u6846\u4E0E\u754C\u884C\uFF0C\u63A5\u8FD1\u96D5\u7248\u7684\u7248\u6846\u3002\n * \u7528\u6CD5\uFF1A<article class=\"guji-doc guji-vertical guji-banks\">\n */\n.guji-banks {\n --guji-frame-color: #3a3a3a;\n --guji-frame-width: 0.6pt;\n padding: 4mm;\n border: calc(var(--guji-frame-width) * 3) double var(--guji-frame-color);\n}\n\n/* \u754C\u884C\uFF1A\u7528\u91CD\u590D\u7EBF\u6027\u6E10\u53D8\u753B\u51FA\u6BCF\u5217\u4E4B\u95F4\u7684\u7EC6\u7EBF */\n.guji-banks.guji-vertical {\n background-image: repeating-linear-gradient(\n to left,\n transparent,\n transparent calc(var(--guji-print-line-height, 1.85) * 1em - var(--guji-frame-width)),\n var(--guji-frame-color) calc(var(--guji-print-line-height, 1.85) * 1em - var(--guji-frame-width)),\n var(--guji-frame-color) calc(var(--guji-print-line-height, 1.85) * 1em)\n );\n}\n";
2
+ export declare const STYLE_FILES: readonly ["print.css"];
3
+ //# sourceMappingURL=styles.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.generated.d.ts","sourceRoot":"","sources":["../src/styles.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,QAAQ,u1EAA0qD,CAAA;AAE/rD,eAAO,MAAM,WAAW,wBAEd,CAAA"}
@@ -0,0 +1,7 @@
1
+ // 本文件由 scripts/build-styles.mjs 从 styles/*.css 生成,请勿手改。
2
+ /* eslint-disable */
3
+ export const printCss = "/*!\n * print.css — 古籍 Markdown 的分页排版样式\n * @open-guji/markdown-pdf · Apache-2.0\n *\n * 与 guji.css / guji-vertical.css 叠加使用。这里只管\"页\"的事:\n * 页面尺寸、页边距、版心、书口与分页控制。\n */\n\n@page {\n size: var(--guji-page-size, A5);\n margin: var(--guji-page-margin, 20mm 18mm);\n\n @bottom-center {\n content: counter(page);\n font-family: var(--guji-font);\n font-size: 9pt;\n color: #666;\n }\n}\n\n@page :first {\n @bottom-center {\n content: none;\n }\n}\n\n.guji-doc {\n /* 打印时抬头留白按实际字号折算,不再依赖视口 */\n --guji-raise-unit: 1em;\n font-size: var(--guji-print-font-size, 11pt);\n line-height: var(--guji-print-line-height, 1.85);\n}\n\n/* 标题不落在页末 */\n.guji-doc h1,\n.guji-doc h2,\n.guji-doc h3 {\n break-after: avoid-page;\n break-inside: avoid;\n}\n\n/* 抬头行不被切开 */\n.guji-tt,\n.guji-tt-block {\n break-inside: avoid;\n}\n\n/* 夹注跟随正文,不单独成页 */\n.guji-jz {\n break-inside: avoid;\n}\n\n.guji-doc p {\n orphans: 2;\n widows: 2;\n}\n\n/* ------------------------------------------------------ 线装书版式 */\n\n/*\n * 可选:加边框与界行,接近雕版的版框。\n * 用法:<article class=\"guji-doc guji-vertical guji-banks\">\n */\n.guji-banks {\n --guji-frame-color: #3a3a3a;\n --guji-frame-width: 0.6pt;\n padding: 4mm;\n border: calc(var(--guji-frame-width) * 3) double var(--guji-frame-color);\n}\n\n/* 界行:用重复线性渐变画出每列之间的细线 */\n.guji-banks.guji-vertical {\n background-image: repeating-linear-gradient(\n to left,\n transparent,\n transparent calc(var(--guji-print-line-height, 1.85) * 1em - var(--guji-frame-width)),\n var(--guji-frame-color) calc(var(--guji-print-line-height, 1.85) * 1em - var(--guji-frame-width)),\n var(--guji-frame-color) calc(var(--guji-print-line-height, 1.85) * 1em)\n );\n}\n";
4
+ export const STYLE_FILES = [
5
+ "print.css"
6
+ ];
7
+ //# sourceMappingURL=styles.generated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.generated.js","sourceRoot":"","sources":["../src/styles.generated.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,oBAAoB;AAEpB,MAAM,CAAC,MAAM,QAAQ,GAAG,uqDAAuqD,CAAA;AAE/rD,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,WAAW;CACH,CAAA"}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@open-guji/markdown-pdf",
3
+ "version": "0.1.0",
4
+ "description": "古籍 Markdown 的 PDF 生成器:可打印 HTML 与 Chromium 排版后端",
5
+ "keywords": [
6
+ "markdown",
7
+ "pdf",
8
+ "古籍",
9
+ "guji",
10
+ "chinese",
11
+ "classics",
12
+ "typesetting",
13
+ "pagedjs"
14
+ ],
15
+ "license": "Apache-2.0",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/open-guji/guji-markdown.git",
19
+ "directory": "packages/pdf"
20
+ },
21
+ "homepage": "https://github.com/open-guji/guji-markdown/tree/main/packages/pdf",
22
+ "bugs": "https://github.com/open-guji/guji-markdown/issues",
23
+ "type": "module",
24
+ "engines": {
25
+ "node": ">=18.18"
26
+ },
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "default": "./dist/index.js"
31
+ },
32
+ "./styles/print.css": "./styles/print.css",
33
+ "./package.json": "./package.json"
34
+ },
35
+ "bin": {
36
+ "guji-pdf": "./dist/cli.js"
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "styles",
41
+ "README.md",
42
+ "LICENSE"
43
+ ],
44
+ "dependencies": {
45
+ "@open-guji/markdown-core": "^0.1.0",
46
+ "@open-guji/markdown-web": "^0.1.0"
47
+ },
48
+ "peerDependencies": {
49
+ "pagedjs": "^0.4.3",
50
+ "playwright": "^1.49.0"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "playwright": {
54
+ "optional": true
55
+ },
56
+ "pagedjs": {
57
+ "optional": true
58
+ }
59
+ },
60
+ "devDependencies": {
61
+ "typescript": "^5.9.3",
62
+ "vitest": "^3.2.4",
63
+ "@types/node": "^22.10.0"
64
+ },
65
+ "publishConfig": {
66
+ "access": "public"
67
+ },
68
+ "scripts": {
69
+ "build:styles": "node scripts/build-styles.mjs",
70
+ "build": "npm run build:styles && tsc -p tsconfig.json",
71
+ "typecheck": "npm run build:styles && tsc -p tsconfig.json --noEmit",
72
+ "test": "npm run build:styles && vitest run"
73
+ }
74
+ }
@@ -0,0 +1,80 @@
1
+ /*!
2
+ * print.css — 古籍 Markdown 的分页排版样式
3
+ * @open-guji/markdown-pdf · Apache-2.0
4
+ *
5
+ * 与 guji.css / guji-vertical.css 叠加使用。这里只管"页"的事:
6
+ * 页面尺寸、页边距、版心、书口与分页控制。
7
+ */
8
+
9
+ @page {
10
+ size: var(--guji-page-size, A5);
11
+ margin: var(--guji-page-margin, 20mm 18mm);
12
+
13
+ @bottom-center {
14
+ content: counter(page);
15
+ font-family: var(--guji-font);
16
+ font-size: 9pt;
17
+ color: #666;
18
+ }
19
+ }
20
+
21
+ @page :first {
22
+ @bottom-center {
23
+ content: none;
24
+ }
25
+ }
26
+
27
+ .guji-doc {
28
+ /* 打印时抬头留白按实际字号折算,不再依赖视口 */
29
+ --guji-raise-unit: 1em;
30
+ font-size: var(--guji-print-font-size, 11pt);
31
+ line-height: var(--guji-print-line-height, 1.85);
32
+ }
33
+
34
+ /* 标题不落在页末 */
35
+ .guji-doc h1,
36
+ .guji-doc h2,
37
+ .guji-doc h3 {
38
+ break-after: avoid-page;
39
+ break-inside: avoid;
40
+ }
41
+
42
+ /* 抬头行不被切开 */
43
+ .guji-tt,
44
+ .guji-tt-block {
45
+ break-inside: avoid;
46
+ }
47
+
48
+ /* 夹注跟随正文,不单独成页 */
49
+ .guji-jz {
50
+ break-inside: avoid;
51
+ }
52
+
53
+ .guji-doc p {
54
+ orphans: 2;
55
+ widows: 2;
56
+ }
57
+
58
+ /* ------------------------------------------------------ 线装书版式 */
59
+
60
+ /*
61
+ * 可选:加边框与界行,接近雕版的版框。
62
+ * 用法:<article class="guji-doc guji-vertical guji-banks">
63
+ */
64
+ .guji-banks {
65
+ --guji-frame-color: #3a3a3a;
66
+ --guji-frame-width: 0.6pt;
67
+ padding: 4mm;
68
+ border: calc(var(--guji-frame-width) * 3) double var(--guji-frame-color);
69
+ }
70
+
71
+ /* 界行:用重复线性渐变画出每列之间的细线 */
72
+ .guji-banks.guji-vertical {
73
+ background-image: repeating-linear-gradient(
74
+ to left,
75
+ transparent,
76
+ transparent calc(var(--guji-print-line-height, 1.85) * 1em - var(--guji-frame-width)),
77
+ var(--guji-frame-color) calc(var(--guji-print-line-height, 1.85) * 1em - var(--guji-frame-width)),
78
+ var(--guji-frame-color) calc(var(--guji-print-line-height, 1.85) * 1em)
79
+ );
80
+ }