@laisuk/opencc-fmmseg-wasm 0.2.0 → 0.2.2
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/bin/opencc.js +291 -0
- package/opencc_fmmseg_wasm_bg.wasm +0 -0
- package/package.json +18 -1
package/bin/opencc.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import process from "process";
|
|
6
|
+
|
|
7
|
+
import init, {
|
|
8
|
+
OpenccWasm,
|
|
9
|
+
convert_office_bytes
|
|
10
|
+
} from "../opencc_fmmseg_wasm.js";
|
|
11
|
+
|
|
12
|
+
const OFFICE_FORMATS = new Set([
|
|
13
|
+
"docx",
|
|
14
|
+
"xlsx",
|
|
15
|
+
"pptx",
|
|
16
|
+
"odt",
|
|
17
|
+
"ods",
|
|
18
|
+
"odp",
|
|
19
|
+
"epub"
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
let wasmInitialized = false;
|
|
23
|
+
|
|
24
|
+
async function ensureWasmInitialized() {
|
|
25
|
+
if (wasmInitialized) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const wasmPath = new URL("../opencc_fmmseg_wasm_bg.wasm", import.meta.url);
|
|
30
|
+
const wasmBytes = fs.readFileSync(wasmPath);
|
|
31
|
+
|
|
32
|
+
await init({
|
|
33
|
+
module_or_path: wasmBytes
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
wasmInitialized = true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function printHelp() {
|
|
40
|
+
console.log(`
|
|
41
|
+
opencc-fmmseg WASM CLI
|
|
42
|
+
|
|
43
|
+
Usage:
|
|
44
|
+
opencc.js convert [options]
|
|
45
|
+
opencc.js office [options]
|
|
46
|
+
|
|
47
|
+
Commands:
|
|
48
|
+
convert Convert plain text
|
|
49
|
+
office Convert Office / EPUB documents
|
|
50
|
+
|
|
51
|
+
Convert options:
|
|
52
|
+
-i, --input <file> Input text file; stdin if omitted
|
|
53
|
+
-o, --output <file> Output text file; stdout if omitted
|
|
54
|
+
-c, --config <conversion> Conversion config (default: s2t)
|
|
55
|
+
-p, --punct Enable punctuation conversion
|
|
56
|
+
--in-enc <encoding> Input encoding (default: utf8)
|
|
57
|
+
--out-enc <encoding> Output encoding (default: utf8)
|
|
58
|
+
|
|
59
|
+
Office options:
|
|
60
|
+
-i, --input <file> Input Office / EPUB file
|
|
61
|
+
-o, --output <file> Output file
|
|
62
|
+
-c, --config <conversion> Conversion config (default: s2t)
|
|
63
|
+
-p, --punct Enable punctuation conversion
|
|
64
|
+
--format <format> docx | xlsx | pptx | odt | ods | odp | epub
|
|
65
|
+
--auto-ext Append extension to output if missing
|
|
66
|
+
--keep-font Preserve font-family information (default)
|
|
67
|
+
--no-keep-font Do not preserve font-family information
|
|
68
|
+
|
|
69
|
+
General options:
|
|
70
|
+
-h, --help Show help
|
|
71
|
+
|
|
72
|
+
Examples:
|
|
73
|
+
node ./bin/opencc.js convert -i a.txt -o b.txt -c s2t
|
|
74
|
+
node ./bin/opencc.js convert -i a.txt -o b.txt -c s2tw -p
|
|
75
|
+
cat a.txt | node ./bin/opencc.js convert -c t2s
|
|
76
|
+
|
|
77
|
+
node ./bin/opencc.js office -i a.docx -o b.docx -c s2t -p
|
|
78
|
+
node ./bin/opencc.js office -i a.epub -c s2tw --auto-ext
|
|
79
|
+
`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getArg(args, shortName, longName, defaultValue = null) {
|
|
83
|
+
const candidates = [];
|
|
84
|
+
|
|
85
|
+
if (shortName) {
|
|
86
|
+
candidates.push(shortName);
|
|
87
|
+
}
|
|
88
|
+
if (longName) {
|
|
89
|
+
candidates.push(longName);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const name of candidates) {
|
|
93
|
+
const index = args.indexOf(name);
|
|
94
|
+
|
|
95
|
+
if (index !== -1 && index + 1 < args.length) {
|
|
96
|
+
return args[index + 1];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return defaultValue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function hasFlag(args, shortName, longName) {
|
|
104
|
+
return (
|
|
105
|
+
(shortName && args.includes(shortName)) ||
|
|
106
|
+
(longName && args.includes(longName))
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function readInputText(filePath, encoding) {
|
|
111
|
+
if (!filePath) {
|
|
112
|
+
return fs.readFileSync(0, encoding);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!fs.existsSync(filePath)) {
|
|
116
|
+
console.error(`Error: Input file not found: ${filePath}`);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return fs.readFileSync(filePath, encoding);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function writeOutputText(filePath, text, encoding) {
|
|
124
|
+
if (!filePath) {
|
|
125
|
+
process.stdout.write(text);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
fs.writeFileSync(filePath, text, encoding);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function inferOfficeFormat(inputFile, explicitFormat) {
|
|
133
|
+
if (explicitFormat) {
|
|
134
|
+
const normalized = explicitFormat.trim().toLowerCase();
|
|
135
|
+
|
|
136
|
+
if (!OFFICE_FORMATS.has(normalized)) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Invalid office format: ${explicitFormat}. ` +
|
|
139
|
+
`Valid formats: ${Array.from(OFFICE_FORMATS).join(", ")}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return normalized;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const ext = path.extname(inputFile).slice(1).toLowerCase();
|
|
147
|
+
|
|
148
|
+
if (!OFFICE_FORMATS.has(ext)) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`Invalid Office file extension: .${ext || "(none)"}. ` +
|
|
151
|
+
"Valid extensions: .docx | .xlsx | .pptx | .odt | .ods | .odp | .epub"
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return ext;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function makeDefaultOfficeOutput(inputFile, officeFormat, autoExt) {
|
|
159
|
+
const parsed = path.parse(inputFile);
|
|
160
|
+
const ext = autoExt && OFFICE_FORMATS.has(officeFormat)
|
|
161
|
+
? `.${officeFormat}`
|
|
162
|
+
: parsed.ext;
|
|
163
|
+
|
|
164
|
+
return path.join(
|
|
165
|
+
parsed.dir || process.cwd(),
|
|
166
|
+
`${parsed.name}_converted${ext}`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function applyAutoExt(outputFile, officeFormat, autoExt) {
|
|
171
|
+
if (!autoExt || path.extname(outputFile)) {
|
|
172
|
+
return outputFile;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!OFFICE_FORMATS.has(officeFormat)) {
|
|
176
|
+
return outputFile;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return `${outputFile}.${officeFormat}`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function runConvert(args) {
|
|
183
|
+
const input = getArg(args, "-i", "--input");
|
|
184
|
+
const output = getArg(args, "-o", "--output");
|
|
185
|
+
const config = getArg(args, "-c", "--config", "s2t");
|
|
186
|
+
const inEnc = getArg(args, null, "--in-enc", "utf8");
|
|
187
|
+
const outEnc = getArg(args, null, "--out-enc", "utf8");
|
|
188
|
+
const punct = hasFlag(args, "-p", "--punct");
|
|
189
|
+
|
|
190
|
+
await ensureWasmInitialized();
|
|
191
|
+
|
|
192
|
+
const cc = new OpenccWasm(config);
|
|
193
|
+
|
|
194
|
+
const inputText = readInputText(input, inEnc);
|
|
195
|
+
const outputText = cc.convert(inputText, punct);
|
|
196
|
+
|
|
197
|
+
writeOutputText(output, outputText, outEnc);
|
|
198
|
+
|
|
199
|
+
const inFrom = input || "<stdin>";
|
|
200
|
+
const outTo = output || "stdout";
|
|
201
|
+
|
|
202
|
+
if (process.stderr.isTTY) {
|
|
203
|
+
if (!output && outputText && !outputText.endsWith("\n")) {
|
|
204
|
+
console.error();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
console.error(`Conversion completed (${config}): ${inFrom} -> ${outTo}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function runOffice(args) {
|
|
212
|
+
const input = getArg(args, "-i", "--input");
|
|
213
|
+
let output = getArg(args, "-o", "--output");
|
|
214
|
+
const config = getArg(args, "-c", "--config", "s2t");
|
|
215
|
+
const explicitFormat = getArg(args, null, "--format");
|
|
216
|
+
const punct = hasFlag(args, "-p", "--punct");
|
|
217
|
+
const autoExt = hasFlag(args, null, "--auto-ext");
|
|
218
|
+
const keepFont = !hasFlag(args, null, "--no-keep-font");
|
|
219
|
+
|
|
220
|
+
if (!input) {
|
|
221
|
+
throw new Error("Input file is missing.");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!fs.existsSync(input) || !fs.statSync(input).isFile()) {
|
|
225
|
+
throw new Error(`Input file not found: ${input}`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const officeFormat = inferOfficeFormat(input, explicitFormat);
|
|
229
|
+
|
|
230
|
+
if (!output) {
|
|
231
|
+
output = makeDefaultOfficeOutput(input, officeFormat, autoExt);
|
|
232
|
+
console.error(`Output file not specified. Using: ${output}`);
|
|
233
|
+
} else {
|
|
234
|
+
output = applyAutoExt(output, officeFormat, autoExt);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
await ensureWasmInitialized();
|
|
238
|
+
|
|
239
|
+
const inputBytes = fs.readFileSync(input);
|
|
240
|
+
|
|
241
|
+
const outputBytes = convert_office_bytes(
|
|
242
|
+
inputBytes,
|
|
243
|
+
officeFormat,
|
|
244
|
+
config,
|
|
245
|
+
punct,
|
|
246
|
+
keepFont
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
fs.writeFileSync(output, outputBytes);
|
|
250
|
+
|
|
251
|
+
console.error(`Conversion completed (${config}, ${officeFormat}): ${input} -> ${output}`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function main() {
|
|
255
|
+
const args = process.argv.slice(2);
|
|
256
|
+
|
|
257
|
+
if (args.length === 0 || hasFlag(args, "-h", "--help")) {
|
|
258
|
+
printHelp();
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const command = args[0];
|
|
263
|
+
|
|
264
|
+
switch (command) {
|
|
265
|
+
case "convert":
|
|
266
|
+
if (hasFlag(args.slice(1), "-h", "--help")) {
|
|
267
|
+
printHelp();
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
await runConvert(args.slice(1));
|
|
271
|
+
break;
|
|
272
|
+
|
|
273
|
+
case "office":
|
|
274
|
+
if (hasFlag(args.slice(1), "-h", "--help")) {
|
|
275
|
+
printHelp();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
await runOffice(args.slice(1));
|
|
279
|
+
break;
|
|
280
|
+
|
|
281
|
+
default:
|
|
282
|
+
console.error(`Unknown command: ${command}`);
|
|
283
|
+
printHelp();
|
|
284
|
+
process.exit(1);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
main().catch(err => {
|
|
289
|
+
console.error(err && err.message ? err.message : err);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
});
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -2,13 +2,30 @@
|
|
|
2
2
|
"name": "@laisuk/opencc-fmmseg-wasm",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"description": "WebAssembly bindings for opencc-fmmseg, a high-performance OpenCC-compatible Simplified/Traditional Chinese converter.",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.2",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/laisuk/opencc-fmmseg-wasm.git"
|
|
10
10
|
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"opencc",
|
|
13
|
+
"chinese",
|
|
14
|
+
"simplified-chinese",
|
|
15
|
+
"traditional-chinese",
|
|
16
|
+
"wasm",
|
|
17
|
+
"webassembly",
|
|
18
|
+
"opencc-fmmseg",
|
|
19
|
+
"text-conversion",
|
|
20
|
+
"cjk",
|
|
21
|
+
"zh-hans",
|
|
22
|
+
"zh-hant"
|
|
23
|
+
],
|
|
24
|
+
"bin": {
|
|
25
|
+
"opencc-fmmseg": "bin/opencc.js"
|
|
26
|
+
},
|
|
11
27
|
"files": [
|
|
28
|
+
"bin/",
|
|
12
29
|
"opencc_fmmseg_wasm_bg.wasm",
|
|
13
30
|
"opencc_fmmseg_wasm.js",
|
|
14
31
|
"opencc_fmmseg_wasm.d.ts"
|