patent-xml-generator 1.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/LICENSE +21 -0
- package/README.md +167 -0
- package/dist/generator.d.ts +46 -0
- package/dist/generator.js +246 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/types.d.ts +95 -0
- package/dist/types.js +2 -0
- package/dist/xml-utils.d.ts +31 -0
- package/dist/xml-utils.js +95 -0
- package/dtd/cn-application-body.dtd +1778 -0
- package/package.json +36 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CNIPA.AI
|
|
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,167 @@
|
|
|
1
|
+
# Patent XML Generator
|
|
2
|
+
|
|
3
|
+
将专利申请内容一键转为符合 CNIPA(国家知识产权局)DTD 标准的 XML 文件,可直接用于专利电子申请提交。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- 支持发明专利五书(权利要求书、说明书、说明书附图、说明书摘要、摘要附图)转换
|
|
8
|
+
- 自动段落编号(`[0001]`、`[0002]`...)
|
|
9
|
+
- 自动权利要求编号,识别独立权利要求与从属权利要求
|
|
10
|
+
- 输出符合 `cn-application-body` DTD 标准的 XML
|
|
11
|
+
- 支持上标 `<sup>`、下标 `<sub>`、加粗 `<b>`、斜体 `<i>` 等行内格式
|
|
12
|
+
- 支持 MathML 数学公式嵌入
|
|
13
|
+
- 提供 XML 校验功能
|
|
14
|
+
- TypeScript 编写,类型安全
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install patent-xml-generator
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 快速开始
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { PatentXMLGenerator } from 'patent-xml-generator';
|
|
26
|
+
|
|
27
|
+
const generator = new PatentXMLGenerator();
|
|
28
|
+
|
|
29
|
+
const xml = generator.generate({
|
|
30
|
+
title: '一种制冷压缩机排气过热度控制方法和系统',
|
|
31
|
+
claims: [
|
|
32
|
+
{
|
|
33
|
+
text: '一种制冷压缩机排气过热度控制方法,其特征在于,包括以下步骤:步骤S1:实时采集制冷系统运行参数;步骤S2:计算当前排气过热度;步骤S3:采用模糊自适应PID控制算法计算频率修正量。',
|
|
34
|
+
type: 'independent'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
text: '根据权利要求1所述的方法,其特征在于,所述步骤S1中的运行参数包括压缩机排气温度、排气压力、吸气温度、吸气压力和环境温度。',
|
|
38
|
+
type: 'dependent',
|
|
39
|
+
dependsOn: 1
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
description: {
|
|
43
|
+
technicalField: [
|
|
44
|
+
'本发明涉及制冷空调技术领域,具体涉及一种制冷压缩机排气过热度控制方法和系统。'
|
|
45
|
+
],
|
|
46
|
+
backgroundArt: [
|
|
47
|
+
'在制冷空调系统中,压缩机排气温度是衡量系统运行状态的关键参数。',
|
|
48
|
+
'现有的排气过热度控制方法主要包括基于电子膨胀阀的开度调节、简单的频率限制法等方案。'
|
|
49
|
+
],
|
|
50
|
+
disclosure: [
|
|
51
|
+
'为解决上述技术问题,本发明提供一种制冷压缩机排气过热度控制方法。'
|
|
52
|
+
],
|
|
53
|
+
drawingsDescription: [
|
|
54
|
+
'图1为本发明控制方法的流程示意图;',
|
|
55
|
+
'图2为模糊自适应PID控制器的结构框图。'
|
|
56
|
+
],
|
|
57
|
+
embodiments: [
|
|
58
|
+
'下面结合附图和具体实施例对本发明作进一步详细描述。'
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
abstract: '本发明公开了一种制冷压缩机排气过热度控制方法和系统,通过模糊自适应PID控制算法动态调整压缩机运行频率,实现排气过热度的精确控制。'
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
console.log(xml);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 输出示例
|
|
68
|
+
|
|
69
|
+
```xml
|
|
70
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
71
|
+
<!DOCTYPE cn-application-body SYSTEM "cn-application-body.dtd">
|
|
72
|
+
<cn-application-body lang="zh" country="CN" dtd-version="1.0">
|
|
73
|
+
<cn-claims>
|
|
74
|
+
<claim num="1" claim-type="independent">
|
|
75
|
+
<claim-text>一种制冷压缩机排气过热度控制方法,其特征在于...</claim-text>
|
|
76
|
+
</claim>
|
|
77
|
+
<claim num="2" claim-type="dependent">
|
|
78
|
+
<claim-text>根据权利要求1所述的方法,其特征在于...</claim-text>
|
|
79
|
+
</claim>
|
|
80
|
+
</cn-claims>
|
|
81
|
+
<description>
|
|
82
|
+
<invention-title lang="zh">一种制冷压缩机排气过热度控制方法和系统</invention-title>
|
|
83
|
+
<technical-field>
|
|
84
|
+
<heading level="1">技术领域</heading>
|
|
85
|
+
<p num="0001" Italic="0">本发明涉及制冷空调技术领域...</p>
|
|
86
|
+
</technical-field>
|
|
87
|
+
<background-art>
|
|
88
|
+
<heading level="1">背景技术</heading>
|
|
89
|
+
<p num="0002" Italic="0">在制冷空调系统中...</p>
|
|
90
|
+
</background-art>
|
|
91
|
+
...
|
|
92
|
+
</description>
|
|
93
|
+
<cn-abstract>
|
|
94
|
+
<p num="0001" Italic="0">本发明公开了一种...</p>
|
|
95
|
+
</cn-abstract>
|
|
96
|
+
</cn-application-body>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## API
|
|
100
|
+
|
|
101
|
+
### `PatentXMLGenerator`
|
|
102
|
+
|
|
103
|
+
#### `constructor(options?)`
|
|
104
|
+
|
|
105
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
106
|
+
|------|------|--------|------|
|
|
107
|
+
| `lang` | `string` | `'zh'` | 语言代码 |
|
|
108
|
+
| `country` | `string` | `'CN'` | 国家代码 |
|
|
109
|
+
| `dtdVersion` | `string` | `'1.0'` | DTD 版本 |
|
|
110
|
+
| `indent` | `boolean` | `true` | 是否格式化输出 |
|
|
111
|
+
|
|
112
|
+
#### `generate(patent: PatentInput): string`
|
|
113
|
+
|
|
114
|
+
生成完整的专利申请 XML 字符串。
|
|
115
|
+
|
|
116
|
+
#### `generateClaims(claims: ClaimInput[]): string`
|
|
117
|
+
|
|
118
|
+
仅生成权利要求书 XML 片段。
|
|
119
|
+
|
|
120
|
+
#### `generateDescription(title: string, desc: DescriptionInput): string`
|
|
121
|
+
|
|
122
|
+
仅生成说明书 XML 片段。
|
|
123
|
+
|
|
124
|
+
#### `generateAbstract(text: string): string`
|
|
125
|
+
|
|
126
|
+
仅生成摘要 XML 片段。
|
|
127
|
+
|
|
128
|
+
#### `validate(xml: string): ValidationResult`
|
|
129
|
+
|
|
130
|
+
校验 XML 是否符合 DTD 规范。
|
|
131
|
+
|
|
132
|
+
### 类型定义
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
interface PatentInput {
|
|
136
|
+
title: string;
|
|
137
|
+
claims: ClaimInput[];
|
|
138
|
+
description: DescriptionInput;
|
|
139
|
+
abstract: string;
|
|
140
|
+
drawings?: DrawingInput[];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
interface ClaimInput {
|
|
144
|
+
text: string;
|
|
145
|
+
type: 'independent' | 'dependent';
|
|
146
|
+
dependsOn?: number;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
interface DescriptionInput {
|
|
150
|
+
technicalField: string[];
|
|
151
|
+
backgroundArt: string[];
|
|
152
|
+
disclosure: string[];
|
|
153
|
+
drawingsDescription?: string[];
|
|
154
|
+
embodiments: string[];
|
|
155
|
+
sequenceList?: string[];
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 关于
|
|
160
|
+
|
|
161
|
+
本工具是 [CNIPA.AI](https://cnipa.ai) 专利智能服务平台的开源组件。
|
|
162
|
+
|
|
163
|
+
CNIPA.AI 是一个 AI 原生的知识产权智能服务平台,基于全球公开专利数据,提供专利导航、企业画像、侵权检测、价值评估、趋势预测、AI 辅助撰写六大服务。
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { PatentInput, ClaimInput, DescriptionInput, DrawingInput, GeneratorOptions, ValidationResult } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* 专利申请 XML 生成器
|
|
4
|
+
*
|
|
5
|
+
* 生成符合 CNIPA cn-application-body DTD 标准的 XML 文件
|
|
6
|
+
*/
|
|
7
|
+
export declare class PatentXMLGenerator {
|
|
8
|
+
private lang;
|
|
9
|
+
private country;
|
|
10
|
+
private dtdVersion;
|
|
11
|
+
private useIndent;
|
|
12
|
+
private dtdPath;
|
|
13
|
+
constructor(options?: GeneratorOptions);
|
|
14
|
+
/**
|
|
15
|
+
* 生成完整的专利申请 XML
|
|
16
|
+
*/
|
|
17
|
+
generate(patent: PatentInput): string;
|
|
18
|
+
/**
|
|
19
|
+
* 生成权利要求书 XML 片段
|
|
20
|
+
*/
|
|
21
|
+
generateClaims(claims: ClaimInput[]): string;
|
|
22
|
+
/**
|
|
23
|
+
* 生成说明书 XML 片段
|
|
24
|
+
*/
|
|
25
|
+
generateDescription(title: string, desc: DescriptionInput): string;
|
|
26
|
+
/**
|
|
27
|
+
* 生成附图 XML 片段
|
|
28
|
+
*/
|
|
29
|
+
generateDrawings(drawings: DrawingInput[]): string;
|
|
30
|
+
/**
|
|
31
|
+
* 生成摘要 XML 片段
|
|
32
|
+
*/
|
|
33
|
+
generateAbstract(text: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* 校验 XML 是否符合 DTD 基本规范
|
|
36
|
+
*/
|
|
37
|
+
validate(xml: string): ValidationResult;
|
|
38
|
+
/**
|
|
39
|
+
* 处理权利要求文本,为从属引用添加 claim-ref 标记
|
|
40
|
+
*/
|
|
41
|
+
private processClaimText;
|
|
42
|
+
/**
|
|
43
|
+
* 添加说明书的一个章节
|
|
44
|
+
*/
|
|
45
|
+
private addDescriptionSection;
|
|
46
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PatentXMLGenerator = void 0;
|
|
4
|
+
const xml_utils_1 = require("./xml-utils");
|
|
5
|
+
/**
|
|
6
|
+
* 专利申请 XML 生成器
|
|
7
|
+
*
|
|
8
|
+
* 生成符合 CNIPA cn-application-body DTD 标准的 XML 文件
|
|
9
|
+
*/
|
|
10
|
+
class PatentXMLGenerator {
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.lang = options?.lang ?? 'zh';
|
|
13
|
+
this.country = options?.country ?? 'CN';
|
|
14
|
+
this.dtdVersion = options?.dtdVersion ?? '1.0';
|
|
15
|
+
this.useIndent = options?.indent ?? true;
|
|
16
|
+
this.dtdPath = options?.dtdPath ?? 'cn-application-body.dtd';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 生成完整的专利申请 XML
|
|
20
|
+
*/
|
|
21
|
+
generate(patent) {
|
|
22
|
+
const n = (0, xml_utils_1.newline)(this.useIndent);
|
|
23
|
+
const parts = [];
|
|
24
|
+
// XML 声明
|
|
25
|
+
parts.push(`<?xml version="1.0" encoding="UTF-8"?>`);
|
|
26
|
+
parts.push(`<!DOCTYPE cn-application-body SYSTEM "${this.dtdPath}">`);
|
|
27
|
+
// 根元素
|
|
28
|
+
parts.push(`<cn-application-body lang="${this.lang}" country="${this.country}" dtd-version="${this.dtdVersion}">`);
|
|
29
|
+
// 权利要求书
|
|
30
|
+
parts.push(this.generateClaims(patent.claims));
|
|
31
|
+
// 说明书
|
|
32
|
+
parts.push(this.generateDescription(patent.title, patent.description));
|
|
33
|
+
// 附图(如果有)
|
|
34
|
+
if (patent.drawings && patent.drawings.length > 0) {
|
|
35
|
+
parts.push(this.generateDrawings(patent.drawings));
|
|
36
|
+
}
|
|
37
|
+
// 摘要
|
|
38
|
+
parts.push(this.generateAbstract(patent.abstract));
|
|
39
|
+
// 关闭根元素
|
|
40
|
+
parts.push(`</cn-application-body>`);
|
|
41
|
+
return parts.join(n);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 生成权利要求书 XML 片段
|
|
45
|
+
*/
|
|
46
|
+
generateClaims(claims) {
|
|
47
|
+
const n = (0, xml_utils_1.newline)(this.useIndent);
|
|
48
|
+
const i1 = (0, xml_utils_1.indent)(1, this.useIndent);
|
|
49
|
+
const i2 = (0, xml_utils_1.indent)(2, this.useIndent);
|
|
50
|
+
const i3 = (0, xml_utils_1.indent)(3, this.useIndent);
|
|
51
|
+
const lines = [];
|
|
52
|
+
lines.push(`${i1}<cn-claims>`);
|
|
53
|
+
claims.forEach((claim, index) => {
|
|
54
|
+
const num = index + 1;
|
|
55
|
+
const claimType = claim.type === 'independent' ? 'independent' : 'dependent';
|
|
56
|
+
lines.push(`${i2}<claim num="${num}" claim-type="${claimType}">`);
|
|
57
|
+
lines.push(`${i3}<claim-text>${this.processClaimText(claim.text, claim.dependsOn)}</claim-text>`);
|
|
58
|
+
lines.push(`${i2}</claim>`);
|
|
59
|
+
});
|
|
60
|
+
lines.push(`${i1}</cn-claims>`);
|
|
61
|
+
return lines.join(n);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 生成说明书 XML 片段
|
|
65
|
+
*/
|
|
66
|
+
generateDescription(title, desc) {
|
|
67
|
+
const n = (0, xml_utils_1.newline)(this.useIndent);
|
|
68
|
+
const i1 = (0, xml_utils_1.indent)(1, this.useIndent);
|
|
69
|
+
const i2 = (0, xml_utils_1.indent)(2, this.useIndent);
|
|
70
|
+
const lines = [];
|
|
71
|
+
let paragraphCounter = 1;
|
|
72
|
+
lines.push(`${i1}<description>`);
|
|
73
|
+
// 发明名称
|
|
74
|
+
lines.push(`${i2}<invention-title lang="${this.lang}">${(0, xml_utils_1.escapeXml)(title)}</invention-title>`);
|
|
75
|
+
// 技术领域
|
|
76
|
+
paragraphCounter = this.addDescriptionSection(lines, 'technical-field', '技术领域', desc.technicalField, paragraphCounter);
|
|
77
|
+
// 背景技术
|
|
78
|
+
paragraphCounter = this.addDescriptionSection(lines, 'background-art', '背景技术', desc.backgroundArt, paragraphCounter);
|
|
79
|
+
// 发明内容
|
|
80
|
+
paragraphCounter = this.addDescriptionSection(lines, 'disclosure', '发明内容', desc.disclosure, paragraphCounter);
|
|
81
|
+
// 附图说明
|
|
82
|
+
if (desc.drawingsDescription && desc.drawingsDescription.length > 0) {
|
|
83
|
+
paragraphCounter = this.addDescriptionSection(lines, 'description-of-drawings', '附图说明', desc.drawingsDescription, paragraphCounter);
|
|
84
|
+
}
|
|
85
|
+
// 具体实施方式
|
|
86
|
+
paragraphCounter = this.addDescriptionSection(lines, 'mode-for-invention', '具体实施方式', desc.embodiments, paragraphCounter);
|
|
87
|
+
// 序列表(可选)
|
|
88
|
+
if (desc.sequenceList && desc.sequenceList.length > 0) {
|
|
89
|
+
paragraphCounter = this.addDescriptionSection(lines, 'sequence-list-text', '序列表', desc.sequenceList, paragraphCounter);
|
|
90
|
+
}
|
|
91
|
+
lines.push(`${i1}</description>`);
|
|
92
|
+
return lines.join(n);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 生成附图 XML 片段
|
|
96
|
+
*/
|
|
97
|
+
generateDrawings(drawings) {
|
|
98
|
+
const n = (0, xml_utils_1.newline)(this.useIndent);
|
|
99
|
+
const i1 = (0, xml_utils_1.indent)(1, this.useIndent);
|
|
100
|
+
const i2 = (0, xml_utils_1.indent)(2, this.useIndent);
|
|
101
|
+
const i3 = (0, xml_utils_1.indent)(3, this.useIndent);
|
|
102
|
+
const lines = [];
|
|
103
|
+
lines.push(`${i1}<cn-drawings>`);
|
|
104
|
+
drawings.forEach((drawing) => {
|
|
105
|
+
const widthAttr = drawing.width ? ` wi="${drawing.width}"` : '';
|
|
106
|
+
const heightAttr = drawing.height ? ` he="${drawing.height}"` : '';
|
|
107
|
+
lines.push(`${i2}<figure num="${(0, xml_utils_1.escapeXml)(drawing.figureNumber)}">`);
|
|
108
|
+
lines.push(`${i3}<img file="${(0, xml_utils_1.escapeXml)(drawing.filePath)}" img-format="${drawing.format}" img-content="drawing"${widthAttr}${heightAttr}/>`);
|
|
109
|
+
lines.push(`${i2}</figure>`);
|
|
110
|
+
});
|
|
111
|
+
lines.push(`${i1}</cn-drawings>`);
|
|
112
|
+
return lines.join(n);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 生成摘要 XML 片段
|
|
116
|
+
*/
|
|
117
|
+
generateAbstract(text) {
|
|
118
|
+
const n = (0, xml_utils_1.newline)(this.useIndent);
|
|
119
|
+
const i1 = (0, xml_utils_1.indent)(1, this.useIndent);
|
|
120
|
+
const i2 = (0, xml_utils_1.indent)(2, this.useIndent);
|
|
121
|
+
const lines = [];
|
|
122
|
+
lines.push(`${i1}<cn-abstract>`);
|
|
123
|
+
lines.push(`${i2}<p num="0001" Italic="0">${(0, xml_utils_1.escapeXmlPreservingInlineTags)(text)}</p>`);
|
|
124
|
+
lines.push(`${i1}</cn-abstract>`);
|
|
125
|
+
return lines.join(n);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* 校验 XML 是否符合 DTD 基本规范
|
|
129
|
+
*/
|
|
130
|
+
validate(xml) {
|
|
131
|
+
const errors = [];
|
|
132
|
+
const warnings = [];
|
|
133
|
+
// 检查 XML 声明
|
|
134
|
+
if (!xml.startsWith('<?xml')) {
|
|
135
|
+
errors.push({
|
|
136
|
+
type: 'invalid_structure',
|
|
137
|
+
message: '缺少 XML 声明(<?xml version="1.0" encoding="UTF-8"?>)'
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// 检查根元素必需属性
|
|
141
|
+
if (!/<cn-application-body[^>]*lang="/.test(xml)) {
|
|
142
|
+
errors.push({
|
|
143
|
+
type: 'missing_required',
|
|
144
|
+
message: 'cn-application-body 缺少必需属性 lang'
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (!/<cn-application-body[^>]*country="/.test(xml)) {
|
|
148
|
+
errors.push({
|
|
149
|
+
type: 'missing_required',
|
|
150
|
+
message: 'cn-application-body 缺少必需属性 country'
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// 检查 claim 的 num 属性
|
|
154
|
+
const claimMatches = xml.matchAll(/<claim(\s[^>]*)?>/g);
|
|
155
|
+
for (const match of claimMatches) {
|
|
156
|
+
if (!match[1] || !/num="/.test(match[1])) {
|
|
157
|
+
errors.push({
|
|
158
|
+
type: 'missing_required',
|
|
159
|
+
message: `claim 元素缺少必需属性 num`
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// 检查 p 的 num 和 Italic 属性
|
|
164
|
+
const pMatches = xml.matchAll(/<p(\s[^>]*)?>/g);
|
|
165
|
+
for (const match of pMatches) {
|
|
166
|
+
const attrs = match[1] || '';
|
|
167
|
+
if (!/num="/.test(attrs)) {
|
|
168
|
+
errors.push({
|
|
169
|
+
type: 'missing_required',
|
|
170
|
+
message: `p 元素缺少必需属性 num`
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (!/Italic="/.test(attrs)) {
|
|
174
|
+
errors.push({
|
|
175
|
+
type: 'missing_required',
|
|
176
|
+
message: `p 元素缺少必需属性 Italic`
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// 检查权利要求编号连续性
|
|
181
|
+
const claimNums = [...xml.matchAll(/<claim[^>]*num="(\d+)"/g)].map(m => parseInt(m[1]));
|
|
182
|
+
for (let i = 0; i < claimNums.length; i++) {
|
|
183
|
+
if (claimNums[i] !== i + 1) {
|
|
184
|
+
errors.push({
|
|
185
|
+
type: 'invalid_attribute',
|
|
186
|
+
message: `权利要求编号不连续:期望 ${i + 1},实际 ${claimNums[i]}`
|
|
187
|
+
});
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// 检查段落编号连续性(说明书内)
|
|
192
|
+
const descMatch = xml.match(/<description>([\s\S]*?)<\/description>/);
|
|
193
|
+
if (descMatch) {
|
|
194
|
+
const pNums = [...descMatch[1].matchAll(/<p[^>]*num="(\d+)"/g)].map(m => parseInt(m[1]));
|
|
195
|
+
for (let i = 0; i < pNums.length; i++) {
|
|
196
|
+
if (pNums[i] !== i + 1) {
|
|
197
|
+
warnings.push(`说明书段落编号不连续:第${i + 1}个段落编号为 ${pNums[i]}`);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// 检查 GB18030 字符集
|
|
203
|
+
const textContent = xml.replace(/<[^>]*>/g, '');
|
|
204
|
+
const gb18030Check = (0, xml_utils_1.checkGB18030)(textContent);
|
|
205
|
+
if (!gb18030Check.valid) {
|
|
206
|
+
warnings.push(`发现 ${gb18030Check.invalidChars.length} 个超出 GB18030 字符集的字符:${gb18030Check.invalidChars.join(', ')},提交时可能需要转为图片`);
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
valid: errors.length === 0,
|
|
210
|
+
errors,
|
|
211
|
+
warnings
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// ===== 私有方法 =====
|
|
215
|
+
/**
|
|
216
|
+
* 处理权利要求文本,为从属引用添加 claim-ref 标记
|
|
217
|
+
*/
|
|
218
|
+
processClaimText(text, dependsOn) {
|
|
219
|
+
let processed = (0, xml_utils_1.escapeXmlPreservingInlineTags)(text);
|
|
220
|
+
// 自动识别 "根据权利要求X" 并添加 claim-ref
|
|
221
|
+
if (dependsOn) {
|
|
222
|
+
const refPattern = new RegExp(`(根据权利要求)(${dependsOn})(所述)`, 'g');
|
|
223
|
+
processed = processed.replace(refPattern, `$1<claim-ref idref="CLM-${String(dependsOn).padStart(4, '0')}">$2</claim-ref>$3`);
|
|
224
|
+
}
|
|
225
|
+
return processed;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* 添加说明书的一个章节
|
|
229
|
+
*/
|
|
230
|
+
addDescriptionSection(lines, tagName, headingText, paragraphs, startNum) {
|
|
231
|
+
const n = (0, xml_utils_1.newline)(this.useIndent);
|
|
232
|
+
const i2 = (0, xml_utils_1.indent)(2, this.useIndent);
|
|
233
|
+
const i3 = (0, xml_utils_1.indent)(3, this.useIndent);
|
|
234
|
+
let num = startNum;
|
|
235
|
+
lines.push(`${i2}<${tagName}>`);
|
|
236
|
+
lines.push(`${i3}<heading level="1">${headingText}</heading>`);
|
|
237
|
+
for (const para of paragraphs) {
|
|
238
|
+
const formattedNum = (0, xml_utils_1.formatParagraphNum)(num);
|
|
239
|
+
lines.push(`${i3}<p num="${formattedNum}" Italic="0">${(0, xml_utils_1.escapeXmlPreservingInlineTags)(para)}</p>`);
|
|
240
|
+
num++;
|
|
241
|
+
}
|
|
242
|
+
lines.push(`${i2}</${tagName}>`);
|
|
243
|
+
return num;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
exports.PatentXMLGenerator = PatentXMLGenerator;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PatentXMLGenerator = void 0;
|
|
4
|
+
var generator_1 = require("./generator");
|
|
5
|
+
Object.defineProperty(exports, "PatentXMLGenerator", { enumerable: true, get: function () { return generator_1.PatentXMLGenerator; } });
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 专利申请输入数据结构
|
|
3
|
+
*/
|
|
4
|
+
export interface PatentInput {
|
|
5
|
+
/** 发明名称 */
|
|
6
|
+
title: string;
|
|
7
|
+
/** 权利要求列表 */
|
|
8
|
+
claims: ClaimInput[];
|
|
9
|
+
/** 说明书各部分 */
|
|
10
|
+
description: DescriptionInput;
|
|
11
|
+
/** 摘要文本 */
|
|
12
|
+
abstract: string;
|
|
13
|
+
/** 附图信息(可选) */
|
|
14
|
+
drawings?: DrawingInput[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 权利要求
|
|
18
|
+
*/
|
|
19
|
+
export interface ClaimInput {
|
|
20
|
+
/** 权利要求正文 */
|
|
21
|
+
text: string;
|
|
22
|
+
/** 权利要求类型 */
|
|
23
|
+
type: 'independent' | 'dependent';
|
|
24
|
+
/** 从属权利要求引用的权项号 */
|
|
25
|
+
dependsOn?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 说明书各部分
|
|
29
|
+
*/
|
|
30
|
+
export interface DescriptionInput {
|
|
31
|
+
/** 技术领域(每个元素为一个段落) */
|
|
32
|
+
technicalField: string[];
|
|
33
|
+
/** 背景技术 */
|
|
34
|
+
backgroundArt: string[];
|
|
35
|
+
/** 发明内容 */
|
|
36
|
+
disclosure: string[];
|
|
37
|
+
/** 附图说明 */
|
|
38
|
+
drawingsDescription?: string[];
|
|
39
|
+
/** 具体实施方式 */
|
|
40
|
+
embodiments: string[];
|
|
41
|
+
/** 序列表(可选,生物领域专利) */
|
|
42
|
+
sequenceList?: string[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 附图
|
|
46
|
+
*/
|
|
47
|
+
export interface DrawingInput {
|
|
48
|
+
/** 图号,如 "1"、"2a" */
|
|
49
|
+
figureNumber: string;
|
|
50
|
+
/** 图片文件路径 */
|
|
51
|
+
filePath: string;
|
|
52
|
+
/** 图片格式 */
|
|
53
|
+
format: 'tif' | 'jpg' | 'png';
|
|
54
|
+
/** 图片宽度(像素) */
|
|
55
|
+
width?: number;
|
|
56
|
+
/** 图片高度(像素) */
|
|
57
|
+
height?: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* XML生成器选项
|
|
61
|
+
*/
|
|
62
|
+
export interface GeneratorOptions {
|
|
63
|
+
/** 语言代码,默认 'zh' */
|
|
64
|
+
lang?: string;
|
|
65
|
+
/** 国家代码,默认 'CN' */
|
|
66
|
+
country?: string;
|
|
67
|
+
/** DTD 版本,默认 '1.0' */
|
|
68
|
+
dtdVersion?: string;
|
|
69
|
+
/** 是否格式化输出(缩进),默认 true */
|
|
70
|
+
indent?: boolean;
|
|
71
|
+
/** DTD 文件路径,默认 'cn-application-body.dtd' */
|
|
72
|
+
dtdPath?: string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* XML校验结果
|
|
76
|
+
*/
|
|
77
|
+
export interface ValidationResult {
|
|
78
|
+
/** 是否通过校验 */
|
|
79
|
+
valid: boolean;
|
|
80
|
+
/** 错误列表 */
|
|
81
|
+
errors: ValidationError[];
|
|
82
|
+
/** 警告列表 */
|
|
83
|
+
warnings: string[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 校验错误
|
|
87
|
+
*/
|
|
88
|
+
export interface ValidationError {
|
|
89
|
+
/** 错误类型 */
|
|
90
|
+
type: 'missing_required' | 'invalid_attribute' | 'invalid_structure' | 'encoding';
|
|
91
|
+
/** 错误描述 */
|
|
92
|
+
message: string;
|
|
93
|
+
/** 出错位置(行号) */
|
|
94
|
+
line?: number;
|
|
95
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XML 工具函数
|
|
3
|
+
*/
|
|
4
|
+
/** XML 特殊字符转义 */
|
|
5
|
+
export declare function escapeXml(text: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* 智能转义:保留合法的行内XML标记,转义其余内容
|
|
8
|
+
* 支持 <sup>、<sub>、<b>、<i>、<u>、<br/> 等 DTD 允许的行内标记
|
|
9
|
+
*/
|
|
10
|
+
export declare function escapeXmlPreservingInlineTags(text: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* 生成缩进字符串
|
|
13
|
+
*/
|
|
14
|
+
export declare function indent(level: number, useIndent: boolean): string;
|
|
15
|
+
/**
|
|
16
|
+
* 生成换行符
|
|
17
|
+
*/
|
|
18
|
+
export declare function newline(useIndent: boolean): string;
|
|
19
|
+
/**
|
|
20
|
+
* 段落编号格式化:将数字转为4位补零字符串
|
|
21
|
+
* 1 -> "0001", 12 -> "0012"
|
|
22
|
+
*/
|
|
23
|
+
export declare function formatParagraphNum(num: number): string;
|
|
24
|
+
/**
|
|
25
|
+
* 检测文本是否为GB18030字符集范围内
|
|
26
|
+
* 超出范围的字符需要转为图片
|
|
27
|
+
*/
|
|
28
|
+
export declare function checkGB18030(text: string): {
|
|
29
|
+
valid: boolean;
|
|
30
|
+
invalidChars: string[];
|
|
31
|
+
};
|