template-replacement 3.0.8
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/.workflow/publish_to_npmjs.yml +26 -0
- package/core/base.ts +54 -0
- package/core/general.ts +13 -0
- package/core/sign.ts +13 -0
- package/db/index.ts +5 -0
- package/db/indexedDBCache.ts +129 -0
- package/dispatcher/general.ts +6 -0
- package/dispatcher/sign.ts +6 -0
- package/dispatcher/workerGeneral.ts +7 -0
- package/dispatcher/workerSign.ts +7 -0
- package/dist/assets/template_replacement_core_wasm_bg.wasm +0 -0
- package/dist/assets/template_replacement_sign_core_wasm_bg.wasm +0 -0
- package/dist/base-CC-6cmrq.js +189 -0
- package/dist/general.d.ts +1 -0
- package/dist/index-BkwrGCka.js +61 -0
- package/dist/main/general.js +4543 -0
- package/dist/main/sign.js +4566 -0
- package/dist/replace/general.js +424 -0
- package/dist/replace/sign.js +428 -0
- package/dist/sign.d.ts +1 -0
- package/download/index.ts +22 -0
- package/download/stream.ts +38 -0
- package/helper/index.ts +162 -0
- package/index.ts +21 -0
- package/office/zip.ts +116 -0
- package/package.json +33 -0
- package/replace/base.ts +124 -0
- package/replace/general.ts +14 -0
- package/replace/image.ts +116 -0
- package/replace/interface.ts +29 -0
- package/replace/paramsData.ts +117 -0
- package/replace/sign.ts +31 -0
- package/task/urlDownloadTask.ts +67 -0
- package/temp/index.ts +157 -0
- package/temp/interface.ts +18 -0
- package/tsconfig.json +108 -0
- package/vite.config.ts +36 -0
- package/worker/child/agency.ts +78 -0
- package/worker/child/base.ts +89 -0
- package/worker/child/general.ts +5 -0
- package/worker/child/sign.ts +9 -0
- package/worker/index.ts +65 -0
- package/worker/interface.ts +11 -0
- package/worker/main/general.ts +8 -0
- package/worker/main/index.ts +176 -0
- package/worker/main/sign.ts +8 -0
- package/worker/type.ts +24 -0
package/dist/sign.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import streamClass from "./stream"
|
|
2
|
+
|
|
3
|
+
function download(fileName: string, blob: Blob): void {
|
|
4
|
+
const href = window.URL.createObjectURL(blob)
|
|
5
|
+
const a = document.createElement('a')
|
|
6
|
+
a.href = href
|
|
7
|
+
a.target = 'target'
|
|
8
|
+
a.download = fileName
|
|
9
|
+
document.body.appendChild(a)
|
|
10
|
+
a.click();
|
|
11
|
+
document.body.removeChild(a)
|
|
12
|
+
window.URL.revokeObjectURL(href)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default (fileName: string, blob: Blob): void => {
|
|
16
|
+
download(fileName, blob)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//流式下载
|
|
20
|
+
export function stream(fileName: string, size?: number): streamClass {
|
|
21
|
+
return new streamClass(fileName, size)
|
|
22
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import streamSaver from "streamsaver"
|
|
2
|
+
|
|
3
|
+
export function setMitm(mitm: string) {
|
|
4
|
+
streamSaver.mitm = mitm
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export default class Stream {
|
|
8
|
+
tasks: Promise<any>[] = []
|
|
9
|
+
writer: WritableStreamDefaultWriter<any>
|
|
10
|
+
fileStream: WritableStream
|
|
11
|
+
|
|
12
|
+
constructor(fileName: string, size?: number) {
|
|
13
|
+
this.fileStream = streamSaver.createWriteStream(fileName, {
|
|
14
|
+
size
|
|
15
|
+
})
|
|
16
|
+
this.writer = this.fileStream.getWriter()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async write(chunk: any): Promise<void> {
|
|
20
|
+
const a = this.writer.write(chunk)
|
|
21
|
+
this.tasks.push(a)
|
|
22
|
+
const res = await a
|
|
23
|
+
return res
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async close(): Promise<void> {
|
|
27
|
+
if (this.tasks.length) {
|
|
28
|
+
await Promise.all(this.tasks)
|
|
29
|
+
}
|
|
30
|
+
const res = await this.writer.close()
|
|
31
|
+
this.writer.releaseLock()
|
|
32
|
+
return res
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async abort(reason?: any): Promise<void> {
|
|
36
|
+
return await this.fileStream.abort(reason)
|
|
37
|
+
}
|
|
38
|
+
}
|
package/helper/index.ts
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid'
|
|
2
|
+
import urlDownloadTask from '../task/urlDownloadTask'
|
|
3
|
+
import { fileTypeFromBuffer } from 'file-type'
|
|
4
|
+
|
|
5
|
+
export function urlSuffix(url: string): string {
|
|
6
|
+
url = url.split('?')[0]
|
|
7
|
+
if (url.lastIndexOf('.') === -1) {
|
|
8
|
+
return ''
|
|
9
|
+
}
|
|
10
|
+
return url.substring(url.lastIndexOf('.') + 1)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getFileNameFromUrl(url: string): string {
|
|
14
|
+
url = url.split('?')[0]
|
|
15
|
+
const pathParts = url.split('/')
|
|
16
|
+
return pathParts[pathParts.length - 1]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const enum fileTypes {
|
|
20
|
+
word = 'word',
|
|
21
|
+
excel = 'excel',
|
|
22
|
+
unknown = 'unknown',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const officeMIMETypes: Record<string, fileTypes> = {
|
|
26
|
+
//docx
|
|
27
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': fileTypes.word,
|
|
28
|
+
//dotx
|
|
29
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.template': fileTypes.word,
|
|
30
|
+
//xlsx
|
|
31
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': fileTypes.excel,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const officeSuffixTypes: Record<string, fileTypes> = {
|
|
35
|
+
'docx': fileTypes.word,
|
|
36
|
+
'dotx': fileTypes.word,
|
|
37
|
+
'xlsx': fileTypes.excel,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function fileType(file: File): fileTypes {
|
|
41
|
+
return officeMIMETypes[file.type] ?? fileTypes.unknown
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function fileTypeByName(name: string): fileTypes {
|
|
45
|
+
return officeSuffixTypes[urlSuffix(name)] ?? fileTypes.unknown
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function fileTypeByBuffer(buffer: Uint8Array|ArrayBuffer|Blob): Promise<fileTypes> {
|
|
49
|
+
if (buffer instanceof Blob) {
|
|
50
|
+
buffer = await buffer.arrayBuffer()
|
|
51
|
+
}
|
|
52
|
+
const type = await fileTypeFromBuffer(buffer)
|
|
53
|
+
if (type && officeMIMETypes[type.mime]) {
|
|
54
|
+
return officeMIMETypes[type.mime]
|
|
55
|
+
}
|
|
56
|
+
return fileTypes.unknown
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function generateId(): string {
|
|
60
|
+
return nanoid()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type fileArrayBufferData = {
|
|
64
|
+
name: string
|
|
65
|
+
buffer: ArrayBuffer
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function filesReaderArrayBuffer(files: File[]): Promise<fileArrayBufferData[]> {
|
|
69
|
+
const awaits = []
|
|
70
|
+
files.forEach
|
|
71
|
+
for (const file of files) {
|
|
72
|
+
awaits.push(new Promise(async (resolve, reject) => {
|
|
73
|
+
try {
|
|
74
|
+
resolve({
|
|
75
|
+
name: file.name,
|
|
76
|
+
buffer: await file.arrayBuffer()
|
|
77
|
+
} as fileArrayBufferData)
|
|
78
|
+
} catch (error) {
|
|
79
|
+
reject(error)
|
|
80
|
+
}
|
|
81
|
+
}))
|
|
82
|
+
}
|
|
83
|
+
return (await Promise.all(awaits)) as fileArrayBufferData[]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
export type fileBase64Data = {
|
|
88
|
+
name: string
|
|
89
|
+
base64: string
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function filesReaderBase64(files: File[]): Promise<fileBase64Data[]> {
|
|
93
|
+
const awaits = []
|
|
94
|
+
for (const file of files) {
|
|
95
|
+
awaits.push(new Promise((resolve, reject) => {
|
|
96
|
+
const fileReader = new FileReader()
|
|
97
|
+
fileReader.onload = function(e) {
|
|
98
|
+
let base64 = e.target?.result as string
|
|
99
|
+
if (!base64) {
|
|
100
|
+
resolve({
|
|
101
|
+
name: file.name,
|
|
102
|
+
base64: ''
|
|
103
|
+
})
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
const index = base64.indexOf(",")
|
|
107
|
+
if (index != -1) {
|
|
108
|
+
base64 = base64.slice(index + 1)
|
|
109
|
+
}
|
|
110
|
+
resolve({
|
|
111
|
+
name: file.name,
|
|
112
|
+
base64: base64
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
fileReader.onerror = function (e) {
|
|
116
|
+
reject(e)
|
|
117
|
+
}
|
|
118
|
+
fileReader.readAsDataURL(file)
|
|
119
|
+
}))
|
|
120
|
+
}
|
|
121
|
+
return await Promise.all(awaits) as fileBase64Data[]
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function base64ToBlob(base64: string): Blob {
|
|
125
|
+
const arr = base64.split(',')
|
|
126
|
+
let mime
|
|
127
|
+
if (arr.length > 1) {
|
|
128
|
+
const m: RegExpMatchArray | null = arr[0].match(/:(.*?);/)
|
|
129
|
+
if (m?.length) {
|
|
130
|
+
mime = m[1]
|
|
131
|
+
base64 = arr[1]
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (!mime) {
|
|
135
|
+
mime = 'application/octet-stream'
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const byteCharacters = atob(base64)
|
|
139
|
+
const byteArray = new Uint8Array(byteCharacters.length)
|
|
140
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
141
|
+
byteArray[i] = byteCharacters.charCodeAt(i)
|
|
142
|
+
}
|
|
143
|
+
return new Blob([byteArray], { type: mime })
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
//urls提取为文件二进制数据
|
|
147
|
+
export async function urlsToFileBlobs(urls: string[], onDownloadProgress?: (progressEvent: any) => void): Promise<(Blob|undefined)[]> {
|
|
148
|
+
const task = new urlDownloadTask(urls)
|
|
149
|
+
if(onDownloadProgress) {
|
|
150
|
+
task.onDownloadProgress(onDownloadProgress)
|
|
151
|
+
}
|
|
152
|
+
return await task.start()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function splitArrayIntoChunks<T>(array: T[], chunkSize: number): T[][] {
|
|
156
|
+
const result: T[][] = []
|
|
157
|
+
for (let i = 0; i < array.length; i += chunkSize) {
|
|
158
|
+
const chunk = array.slice(i, i + chunkSize)
|
|
159
|
+
result.push(chunk)
|
|
160
|
+
}
|
|
161
|
+
return result
|
|
162
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import general from './dispatcher/general'
|
|
2
|
+
import sign from './dispatcher/sign'
|
|
3
|
+
import workerGeneral from './dispatcher/workerGeneral'
|
|
4
|
+
import workerSign from './dispatcher/workerSign'
|
|
5
|
+
import ReplaceInterface from './replace/interface'
|
|
6
|
+
|
|
7
|
+
export function General(): ReplaceInterface {
|
|
8
|
+
return general()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function Sign(): ReplaceInterface {
|
|
12
|
+
return sign()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function WorkerGeneral(concurrency?: number): ReplaceInterface {
|
|
16
|
+
return workerGeneral(concurrency)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function WorkerSign(concurrency?: number): ReplaceInterface {
|
|
20
|
+
return workerSign(concurrency)
|
|
21
|
+
}
|
package/office/zip.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { stream } from '../download'
|
|
2
|
+
import { FlateError, unzip, Unzipped, zip, strToU8, AsyncZipOptions, Zip as fflateZip, ZipDeflate } from 'fflate'
|
|
3
|
+
|
|
4
|
+
type InputType = string|Uint8Array|ArrayBuffer|Blob
|
|
5
|
+
|
|
6
|
+
export default class Zip {
|
|
7
|
+
name: string = ''
|
|
8
|
+
fileBlob?: Blob
|
|
9
|
+
_unzipData?: Unzipped
|
|
10
|
+
_lastUpdateTime: number = 0;
|
|
11
|
+
|
|
12
|
+
constructor(file?: Blob) {
|
|
13
|
+
if (!file) {
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
this.name = (file as File)?.name ?? ''
|
|
17
|
+
this.fileBlob = file
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getFileBlob(): Blob|undefined {
|
|
21
|
+
return this.fileBlob
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async fileZip(): Promise<Unzipped> {
|
|
25
|
+
if (!this._unzipData) {
|
|
26
|
+
try {
|
|
27
|
+
const blob = this.getFileBlob()
|
|
28
|
+
if (blob) {
|
|
29
|
+
const arrayBuffer = await blob.arrayBuffer()
|
|
30
|
+
this._unzipData = await new Promise(async (resolve, reject) => {
|
|
31
|
+
unzip(new Uint8Array(arrayBuffer), (err: FlateError | null, data: Unzipped) => {
|
|
32
|
+
if (err) {
|
|
33
|
+
return reject(err)
|
|
34
|
+
}
|
|
35
|
+
resolve(data)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
} catch (e) {
|
|
40
|
+
console.warn(e)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!this._unzipData) {
|
|
44
|
+
this._unzipData = {}
|
|
45
|
+
}
|
|
46
|
+
return this._unzipData
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async setZipData(path: string, data: InputType): Promise<void> {
|
|
50
|
+
const fileZip = await this.fileZip()
|
|
51
|
+
switch (true) {
|
|
52
|
+
case data instanceof Blob:
|
|
53
|
+
data = new Uint8Array(await data.arrayBuffer())
|
|
54
|
+
break
|
|
55
|
+
case data instanceof Uint8Array:
|
|
56
|
+
break
|
|
57
|
+
case data instanceof ArrayBuffer:
|
|
58
|
+
data = new Uint8Array(data)
|
|
59
|
+
break
|
|
60
|
+
case data instanceof String:
|
|
61
|
+
data = strToU8(data as string)
|
|
62
|
+
break
|
|
63
|
+
default:
|
|
64
|
+
throw new Error('Invalid data type')
|
|
65
|
+
}
|
|
66
|
+
fileZip[path] = data as Uint8Array
|
|
67
|
+
this._lastUpdateTime = (new Date()).getTime()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async generate(options: AsyncZipOptions): Promise<Blob|undefined> {
|
|
71
|
+
const data = await this.fileZip()
|
|
72
|
+
if (!Object.keys(data).length) {
|
|
73
|
+
return undefined
|
|
74
|
+
}
|
|
75
|
+
return await new Promise(async (resolve, reject) => {
|
|
76
|
+
zip(data, options, (err: FlateError | null, data: Uint8Array) => {
|
|
77
|
+
if (err) {
|
|
78
|
+
return reject(err)
|
|
79
|
+
}
|
|
80
|
+
resolve(new Blob([data]))
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async download(fileName: string): Promise<void> {
|
|
86
|
+
const data = await this.fileZip()
|
|
87
|
+
if (!Object.keys(data).length) {
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (fileName == undefined) {
|
|
92
|
+
fileName = this.name
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const downloadStream = stream(fileName)
|
|
96
|
+
const zip = new fflateZip((err, dat, final) => {
|
|
97
|
+
if (err) {
|
|
98
|
+
downloadStream.abort(err)
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
downloadStream.write(dat)
|
|
102
|
+
if (final) {
|
|
103
|
+
downloadStream.close()
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
for (const key in data) {
|
|
108
|
+
const deflate = new ZipDeflate(key, {
|
|
109
|
+
level: 9
|
|
110
|
+
});
|
|
111
|
+
zip.add(deflate)
|
|
112
|
+
deflate.push(data[key], true);
|
|
113
|
+
}
|
|
114
|
+
zip.end()
|
|
115
|
+
}
|
|
116
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "template-replacement",
|
|
3
|
+
"description": "模板文件替换",
|
|
4
|
+
"version": "3.0.8",
|
|
5
|
+
"author": "fushiliang <994301536@qq.com>",
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "index.ts",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "npx vite build --config vite.config.ts"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@types/crypto-js": "^4.2.2",
|
|
14
|
+
"@types/streamsaver": "^2.0.5",
|
|
15
|
+
"axios": "^1.7.9",
|
|
16
|
+
"fflate": "^0.8.2",
|
|
17
|
+
"file-type": "^19.6.0",
|
|
18
|
+
"nanoid": "^4.0.2",
|
|
19
|
+
"streamsaver": "^2.0.6",
|
|
20
|
+
"template-replacement-core-wasm": "^1.0.0",
|
|
21
|
+
"template-replacement-sign-core-wasm": "^1.0.1",
|
|
22
|
+
"vite-plugin-wasm-pack": "^0.1.12"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/js-md5": "^0.7.2",
|
|
26
|
+
"typescript": "^5.7.3",
|
|
27
|
+
"vite": "^6.1.0",
|
|
28
|
+
"vite-plugin-dts": "^4.5.0"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"template-replacement"
|
|
32
|
+
]
|
|
33
|
+
}
|
package/replace/base.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import Interface, { media } from './interface';
|
|
2
|
+
import { Interface as CoreInterface } from '../core/base';
|
|
3
|
+
import paramsData from './paramsData';
|
|
4
|
+
import Temp from '../temp';
|
|
5
|
+
import { fileTypes } from '../helper';
|
|
6
|
+
|
|
7
|
+
export default class Base implements Interface{
|
|
8
|
+
#files: Temp[] = []
|
|
9
|
+
#core: CoreInterface
|
|
10
|
+
|
|
11
|
+
constructor(core: CoreInterface) {
|
|
12
|
+
this.#core = core
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
addTempFile(tempFile: Temp){
|
|
16
|
+
this.#files.push(tempFile)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
clear(): void {
|
|
20
|
+
this.#files.length = 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async extractVariables(files: Temp[] | undefined): Promise<Record<string, string[]>> {
|
|
24
|
+
if (!files) {
|
|
25
|
+
files = this.#files
|
|
26
|
+
}
|
|
27
|
+
const data: Record<string, string[]> = {}
|
|
28
|
+
const tasks = []
|
|
29
|
+
for (const file of files) {
|
|
30
|
+
tasks.push(new Promise<void>(async (resolve, reject) => {
|
|
31
|
+
const buffer = await file.getBuffer()
|
|
32
|
+
if (buffer && (await file.type()) !== fileTypes.unknown) {
|
|
33
|
+
data[file.name] = await this.#core.extract_one_file_variable_names(buffer)
|
|
34
|
+
}
|
|
35
|
+
resolve()
|
|
36
|
+
}))
|
|
37
|
+
}
|
|
38
|
+
await Promise.all(tasks)
|
|
39
|
+
return data
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async extractMedias(files: Temp[] | undefined): Promise<Record<string, media[]>> {
|
|
43
|
+
if (!files) {
|
|
44
|
+
files = this.#files
|
|
45
|
+
}
|
|
46
|
+
const data: Record<string, media[]> = {}
|
|
47
|
+
const tasks = []
|
|
48
|
+
for (const file of files) {
|
|
49
|
+
tasks.push(new Promise<void>(async (resolve, reject) => {
|
|
50
|
+
const buffer = await file.getBuffer()
|
|
51
|
+
if (buffer && (await file.type()) !== fileTypes.unknown) {
|
|
52
|
+
let medias = await this.#core.extract_one_file_medias(buffer)
|
|
53
|
+
data[file.name] = []
|
|
54
|
+
if (medias && Array.isArray(medias)) {
|
|
55
|
+
medias.forEach(m => {
|
|
56
|
+
if (m.id && m.data) {
|
|
57
|
+
data[file.name].push({
|
|
58
|
+
id: m.id,
|
|
59
|
+
data: new Uint8Array(m.data)
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
resolve()
|
|
66
|
+
}))
|
|
67
|
+
}
|
|
68
|
+
await Promise.all(tasks)
|
|
69
|
+
return data
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async handle(paramsData: paramsData, files: Uint8Array[]): Promise<Uint8Array[]> {
|
|
73
|
+
return []
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async sign(data: any): Promise<string> {
|
|
77
|
+
return ""
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async execute(params: paramsData, files: Temp[] | undefined): Promise<Record<string, Uint8Array>> {
|
|
81
|
+
if (!files) {
|
|
82
|
+
files = this.#files
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const fileData: { names: string[], uint8Arrays: Uint8Array[] } = {
|
|
86
|
+
names: [],
|
|
87
|
+
uint8Arrays: [],
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const tasks = []
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
tasks.push(new Promise<Uint8Array|undefined>(async (resolve, reject) => {
|
|
93
|
+
resolve(file.getBuffer())
|
|
94
|
+
}))
|
|
95
|
+
}
|
|
96
|
+
await Promise.all(tasks)
|
|
97
|
+
|
|
98
|
+
for (const file of files) {
|
|
99
|
+
if (file.uint8Array) {
|
|
100
|
+
fileData.names.push(file.name)
|
|
101
|
+
fileData.uint8Arrays.push(file.uint8Array)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if(!fileData.uint8Arrays.length) {
|
|
106
|
+
return {}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const res = await this.handle(params, fileData.uint8Arrays)
|
|
110
|
+
const finishStatusCount = {
|
|
111
|
+
success: 0,
|
|
112
|
+
total: fileData.uint8Arrays.length
|
|
113
|
+
} //完成状态统计
|
|
114
|
+
const resData: Record<string, Uint8Array> = {}
|
|
115
|
+
|
|
116
|
+
res.forEach((file, i) => {
|
|
117
|
+
if (file.length) {
|
|
118
|
+
resData[fileData.names[i]] = file
|
|
119
|
+
finishStatusCount.success++
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
return resData
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Base from './base';
|
|
2
|
+
import core, { replace_batch } from '../core/general'
|
|
3
|
+
import paramsData from './paramsData';
|
|
4
|
+
|
|
5
|
+
export default class General extends Base {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(core())
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async handle(paramsData: paramsData, files: Uint8Array[]): Promise<Uint8Array[]> {
|
|
11
|
+
const [params, mediaBuffers] = await paramsData.toReplaceParams()
|
|
12
|
+
return replace_batch(params, mediaBuffers, files)
|
|
13
|
+
}
|
|
14
|
+
}
|
package/replace/image.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// import { add_media } from "template-replacement-core-wasm"
|
|
2
|
+
|
|
3
|
+
export function pxToEMU(px: number): number {
|
|
4
|
+
return px * (914400 / 96)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function cmToEMU(cm: number): number {
|
|
8
|
+
return cm * 914400
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export enum textWrapTypes {
|
|
12
|
+
embed = "Embed",//嵌入型
|
|
13
|
+
belowText = "BelowText",//嵌于文字下方
|
|
14
|
+
aboveText = "AboveText",//嵌于文字上方
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getFileExtension(filename: string): string {
|
|
18
|
+
const ext = filename.split('.').pop()
|
|
19
|
+
if (ext === undefined) {
|
|
20
|
+
return ''
|
|
21
|
+
}
|
|
22
|
+
return '.' + ext
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// async function generateId(file: Blob): Promise<string> {
|
|
26
|
+
// const buffer = await file.arrayBuffer()
|
|
27
|
+
// return await add_media(new Uint8Array(buffer))
|
|
28
|
+
// }
|
|
29
|
+
|
|
30
|
+
export type extent = {
|
|
31
|
+
cy: number,
|
|
32
|
+
cx: number,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//图片替换
|
|
36
|
+
export default class image {
|
|
37
|
+
file: Blob
|
|
38
|
+
relationship = 'image'
|
|
39
|
+
id?: string
|
|
40
|
+
|
|
41
|
+
wpExtent?: extent //图片宽高
|
|
42
|
+
textWrap = textWrapTypes.embed //文字环绕
|
|
43
|
+
|
|
44
|
+
awaitInitQueue?: Function[] = []
|
|
45
|
+
|
|
46
|
+
constructor(file: Blob) {
|
|
47
|
+
if (file instanceof Blob) {
|
|
48
|
+
this.file = file
|
|
49
|
+
this.init()
|
|
50
|
+
} else {
|
|
51
|
+
throw new Error("不支持的数据类型");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async init(): Promise<void> {
|
|
56
|
+
this.awaitInitQueue = []
|
|
57
|
+
await this.getExtent()
|
|
58
|
+
for (const resolve of this.awaitInitQueue) {
|
|
59
|
+
resolve()
|
|
60
|
+
}
|
|
61
|
+
delete this.awaitInitQueue
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// async generateId(): Promise<string> {
|
|
65
|
+
// this.id = await generateId(this.file)
|
|
66
|
+
// return this.id
|
|
67
|
+
// }
|
|
68
|
+
|
|
69
|
+
async awaitInit(): Promise<void> {
|
|
70
|
+
if (this.awaitInitQueue) {
|
|
71
|
+
await new Promise(resolve => {
|
|
72
|
+
this.awaitInitQueue?.push(resolve)
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async getExtent(): Promise<extent> {
|
|
78
|
+
if (!this.wpExtent) {
|
|
79
|
+
const bitmap = await createImageBitmap(this.file)
|
|
80
|
+
!this.wpExtent && this.setPxExtent(bitmap.width, bitmap.height)
|
|
81
|
+
bitmap.close()
|
|
82
|
+
}
|
|
83
|
+
return this.wpExtent as extent
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
//设置图片范围(像素)
|
|
87
|
+
setPxExtent(width: number, height: number): void {
|
|
88
|
+
this.wpExtent = {
|
|
89
|
+
cy: pxToEMU(height),
|
|
90
|
+
cx: pxToEMU(width),
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
//设置图片范围(厘米)
|
|
95
|
+
setCmExtent(width: number, height: number): void {
|
|
96
|
+
this.wpExtent = {
|
|
97
|
+
cy: cmToEMU(height),
|
|
98
|
+
cx: cmToEMU(width),
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async outJson(): Promise<Record<string, any>> {
|
|
103
|
+
await this.awaitInit()
|
|
104
|
+
const data: Record<string, any> = {}
|
|
105
|
+
for (const key in this) {
|
|
106
|
+
data[key] = this[key]
|
|
107
|
+
}
|
|
108
|
+
return data
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
setPropertys(data: Record<string, any>) {
|
|
112
|
+
for (const key in data) {
|
|
113
|
+
this[key as keyof image] = data[key]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import paramsData from "./paramsData"
|
|
2
|
+
import Temp from "../temp"
|
|
3
|
+
|
|
4
|
+
export type media = {
|
|
5
|
+
id: string,
|
|
6
|
+
data: Uint8Array
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default interface ReplaceInterface {
|
|
10
|
+
|
|
11
|
+
//添加模板文件
|
|
12
|
+
addTempFile(tempFile: Temp): void
|
|
13
|
+
|
|
14
|
+
//清空文件和链接
|
|
15
|
+
clear(): void
|
|
16
|
+
|
|
17
|
+
//提取变量
|
|
18
|
+
extractVariables(files: Temp[] | undefined): Promise<Record<string, string[]>>
|
|
19
|
+
|
|
20
|
+
//提取媒体
|
|
21
|
+
extractMedias(files: Temp[] | undefined): Promise<Record<string, media[]>>
|
|
22
|
+
|
|
23
|
+
//签名方法
|
|
24
|
+
sign(data: any): Promise<string>
|
|
25
|
+
|
|
26
|
+
//执行替换任务
|
|
27
|
+
execute(params: paramsData, files: Temp[] | undefined): Promise<Record<string, Uint8Array>>
|
|
28
|
+
|
|
29
|
+
}
|