@u1f992/pdfdiff 0.0.1 → 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/.github/workflows/gh-pages.yml +60 -0
- package/.github/workflows/publish.yml +34 -0
- package/.vscode/extensions.json +3 -0
- package/.vscode/settings.json +20 -0
- package/LICENSE +674 -0
- package/README.md +24 -45
- package/dist/browser.d.ts +2 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +3621 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +39804 -0
- package/dist/cli.js.map +1 -0
- package/dist/diff.d.ts +15 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/image.d.ts +13 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.html +67 -0
- package/dist/index.js +3493 -0
- package/dist/index.js.map +1 -0
- package/dist/iterable.d.ts +2 -0
- package/dist/iterable.d.ts.map +1 -0
- package/dist/iterable.test.d.ts +2 -0
- package/dist/iterable.test.d.ts.map +1 -0
- package/dist/jimp.d.ts +6 -0
- package/dist/jimp.d.ts.map +1 -0
- package/dist/mupdf-wasm.wasm +0 -0
- package/dist/pdf.d.ts +377 -0
- package/dist/pdf.d.ts.map +1 -0
- package/dist/rgba-color.d.ts +4 -0
- package/dist/rgba-color.d.ts.map +1 -0
- package/dist/rgba-color.test.d.ts +2 -0
- package/dist/rgba-color.test.d.ts.map +1 -0
- package/dist/style.css +19 -0
- package/dist/worker.d.ts +2 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +380 -0
- package/dist/worker.js.map +1 -0
- package/package.json +44 -7
- package/prettier.config.js +3 -0
- package/prototyping/README.md +1 -0
- package/prototyping/flat-map-concurrency.js +218 -0
- package/prototyping/worker.js +10 -0
- package/rollup.config.js +121 -0
- package/src/browser.ts +184 -0
- package/src/cli.ts +175 -0
- package/src/diff.ts +70 -0
- package/src/image.ts +128 -0
- package/src/index.html +67 -0
- package/src/index.ts +186 -0
- package/src/iterable.test.ts +40 -0
- package/src/iterable.ts +24 -0
- package/src/jimp.ts +14 -0
- package/src/pdf.ts +42 -0
- package/src/rgba-color.test.ts +43 -0
- package/src/rgba-color.ts +63 -0
- package/src/style.css +19 -0
- package/src/worker.ts +62 -0
- package/test/a.pdf +0 -0
- package/test/b.pdf +0 -0
- package/test/base.xcf +0 -0
- package/test/expected.png +0 -0
- package/test/mask.pdf +0 -0
- package/tsconfig.json +50 -0
package/package.json
CHANGED
|
@@ -1,10 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@u1f992/pdfdiff",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Visualize and quantify differences between two PDF files.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"pdfdiff": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node --test",
|
|
11
|
+
"test:cli": "node src/cli.js test/a.pdf test/b.pdf out --mask test/mask.pdf --dpi 300 && echo \"expected: Page 1, Addition: 7500, Deletion: 7500, Modification: 7500\"",
|
|
12
|
+
"build": "rollup -c",
|
|
13
|
+
"serve": "npm run build && http-server dist"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/u1f992/pdfdiff.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [],
|
|
20
|
+
"author": "Koutaro Mukai",
|
|
21
|
+
"license": "GPL-3.0",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/u1f992/pdfdiff/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/u1f992/pdfdiff",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@rollup/plugin-alias": "^5.1.1",
|
|
29
|
+
"@rollup/plugin-commonjs": "^28.0.3",
|
|
30
|
+
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
31
|
+
"@rollup/plugin-typescript": "^12.1.4",
|
|
32
|
+
"@types/node": "^22.18.7",
|
|
33
|
+
"http-server": "^14.1.1",
|
|
34
|
+
"nodehog": "^0.1.2",
|
|
35
|
+
"prettier": "^3.5.3",
|
|
36
|
+
"rollup": "^4.42.0",
|
|
37
|
+
"rollup-plugin-copy": "^3.5.0",
|
|
38
|
+
"tslib": "^2.8.1",
|
|
39
|
+
"typescript": "^5.9.2"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"ix": "^7.0.0",
|
|
43
|
+
"jimp": "^1.6.0",
|
|
44
|
+
"mupdf": "^1.26.2",
|
|
45
|
+
"web-worker": "^1.5.0"
|
|
46
|
+
}
|
|
10
47
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
試作用のコード片
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { range } from "ix/asynciterable";
|
|
4
|
+
import { flatMap } from "ix/asynciterable/operators";
|
|
5
|
+
import Worker from "web-worker";
|
|
6
|
+
import NodeHog from "nodehog";
|
|
7
|
+
|
|
8
|
+
const dateStr = () => new Date().toISOString();
|
|
9
|
+
|
|
10
|
+
console.log({ hardwareConcurrency: navigator.hardwareConcurrency });
|
|
11
|
+
|
|
12
|
+
for await (const pow2 of range(0, 10).pipe(
|
|
13
|
+
flatMap(async (i) => {
|
|
14
|
+
console.log(`${dateStr()} start:${i}`);
|
|
15
|
+
// 処理を他CPUに分散する例
|
|
16
|
+
return await /** @type {Promise<number>} */ (
|
|
17
|
+
new Promise((resolve) => {
|
|
18
|
+
const url = new URL("./worker.js", import.meta.url);
|
|
19
|
+
const worker = new Worker(url, { type: "module" });
|
|
20
|
+
worker.addEventListener("message", (e) => {
|
|
21
|
+
resolve(e.data);
|
|
22
|
+
worker.terminate();
|
|
23
|
+
});
|
|
24
|
+
worker.postMessage(i);
|
|
25
|
+
})
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// 処理が分散されない例
|
|
29
|
+
// await new NodeHog("cpu", 5_000, 1, 1).start();
|
|
30
|
+
// return i * i;
|
|
31
|
+
}, navigator.hardwareConcurrency),
|
|
32
|
+
)) {
|
|
33
|
+
console.log(`${dateStr()} result:${pow2}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/*
|
|
37
|
+
他CPUに分散された実行結果例:
|
|
38
|
+
|
|
39
|
+
{ hardwareConcurrency: 8 }
|
|
40
|
+
2025-06-07T07:44:28.633Z start:0
|
|
41
|
+
2025-06-07T07:44:28.636Z start:1
|
|
42
|
+
2025-06-07T07:44:28.636Z start:2
|
|
43
|
+
2025-06-07T07:44:28.638Z start:3
|
|
44
|
+
2025-06-07T07:44:28.638Z start:4
|
|
45
|
+
2025-06-07T07:44:28.639Z start:5
|
|
46
|
+
2025-06-07T07:44:28.640Z start:6
|
|
47
|
+
2025-06-07T07:44:28.640Z start:7
|
|
48
|
+
|
|
49
|
+
===========================================
|
|
50
|
+
> Starting new NodeHog [ pypq8 ].
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
===========================================
|
|
54
|
+
> Starting new NodeHog [ hbqos ].
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
===========================================
|
|
58
|
+
> Starting new NodeHog [ kmn4i ].
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
===========================================
|
|
62
|
+
> Starting new NodeHog [ q5b5w ].
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
===========================================
|
|
66
|
+
> Starting new NodeHog [ c3j87 ].
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
===========================================
|
|
70
|
+
> Starting new NodeHog [ 7zrgj ].
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
===========================================
|
|
74
|
+
> Starting new NodeHog [ r99a1 ].
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
===========================================
|
|
78
|
+
> Starting new NodeHog [ oh88p ].
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
[ pypq8 ] --> Stressing CPU...
|
|
82
|
+
|
|
83
|
+
[ pypq8 ] ----> 1 second of stress period complete.
|
|
84
|
+
[ pypq8 ] ----> 2 seconds of stress period complete.
|
|
85
|
+
[ pypq8 ] ----> 3 seconds of stress period complete.
|
|
86
|
+
[ pypq8 ] ----> 4 seconds of stress period complete.
|
|
87
|
+
[ pypq8 ] ----> 5 seconds of stress period complete.
|
|
88
|
+
|
|
89
|
+
[ pypq8 ] --> Relieving...
|
|
90
|
+
|
|
91
|
+
2025-06-07T07:44:33.703Z result:0
|
|
92
|
+
2025-06-07T07:44:33.703Z start:8
|
|
93
|
+
|
|
94
|
+
[ hbqos ] --> Stressing CPU...
|
|
95
|
+
|
|
96
|
+
[ hbqos ] ----> 1 second of stress period complete.
|
|
97
|
+
[ hbqos ] ----> 2 seconds of stress period complete.
|
|
98
|
+
[ hbqos ] ----> 3 seconds of stress period complete.
|
|
99
|
+
[ hbqos ] ----> 4 seconds of stress period complete.
|
|
100
|
+
[ hbqos ] ----> 5 seconds of stress period complete.
|
|
101
|
+
|
|
102
|
+
[ hbqos ] --> Relieving...
|
|
103
|
+
|
|
104
|
+
2025-06-07T07:44:33.705Z result:1
|
|
105
|
+
2025-06-07T07:44:33.706Z start:9
|
|
106
|
+
|
|
107
|
+
[ q5b5w ] --> Stressing CPU...
|
|
108
|
+
|
|
109
|
+
[ q5b5w ] ----> 1 second of stress period complete.
|
|
110
|
+
[ q5b5w ] ----> 2 seconds of stress period complete.
|
|
111
|
+
[ q5b5w ] ----> 3 seconds of stress period complete.
|
|
112
|
+
[ q5b5w ] ----> 4 seconds of stress period complete.
|
|
113
|
+
[ q5b5w ] ----> 5 seconds of stress period complete.
|
|
114
|
+
|
|
115
|
+
[ q5b5w ] --> Relieving...
|
|
116
|
+
|
|
117
|
+
2025-06-07T07:44:33.707Z result:9
|
|
118
|
+
|
|
119
|
+
[ kmn4i ] --> Stressing CPU...
|
|
120
|
+
|
|
121
|
+
[ kmn4i ] ----> 1 second of stress period complete.
|
|
122
|
+
[ kmn4i ] ----> 2 seconds of stress period complete.
|
|
123
|
+
[ kmn4i ] ----> 3 seconds of stress period complete.
|
|
124
|
+
[ kmn4i ] ----> 4 seconds of stress period complete.
|
|
125
|
+
[ kmn4i ] ----> 5 seconds of stress period complete.
|
|
126
|
+
|
|
127
|
+
[ kmn4i ] --> Relieving...
|
|
128
|
+
|
|
129
|
+
2025-06-07T07:44:33.708Z result:16
|
|
130
|
+
|
|
131
|
+
[ r99a1 ] --> Stressing CPU...
|
|
132
|
+
|
|
133
|
+
[ r99a1 ] ----> 1 second of stress period complete.
|
|
134
|
+
[ r99a1 ] ----> 2 seconds of stress period complete.
|
|
135
|
+
[ r99a1 ] ----> 3 seconds of stress period complete.
|
|
136
|
+
[ r99a1 ] ----> 4 seconds of stress period complete.
|
|
137
|
+
[ r99a1 ] ----> 5 seconds of stress period complete.
|
|
138
|
+
|
|
139
|
+
[ r99a1 ] --> Relieving...
|
|
140
|
+
|
|
141
|
+
2025-06-07T07:44:33.711Z result:4
|
|
142
|
+
|
|
143
|
+
[ c3j87 ] --> Stressing CPU...
|
|
144
|
+
|
|
145
|
+
[ c3j87 ] ----> 1 second of stress period complete.
|
|
146
|
+
[ c3j87 ] ----> 2 seconds of stress period complete.
|
|
147
|
+
[ c3j87 ] ----> 3 seconds of stress period complete.
|
|
148
|
+
[ c3j87 ] ----> 4 seconds of stress period complete.
|
|
149
|
+
[ c3j87 ] ----> 5 seconds of stress period complete.
|
|
150
|
+
|
|
151
|
+
[ c3j87 ] --> Relieving...
|
|
152
|
+
|
|
153
|
+
2025-06-07T07:44:33.711Z result:36
|
|
154
|
+
|
|
155
|
+
[ 7zrgj ] --> Stressing CPU...
|
|
156
|
+
|
|
157
|
+
[ 7zrgj ] ----> 1 second of stress period complete.
|
|
158
|
+
[ 7zrgj ] ----> 2 seconds of stress period complete.
|
|
159
|
+
[ 7zrgj ] ----> 3 seconds of stress period complete.
|
|
160
|
+
[ 7zrgj ] ----> 4 seconds of stress period complete.
|
|
161
|
+
[ 7zrgj ] ----> 5 seconds of stress period complete.
|
|
162
|
+
|
|
163
|
+
[ 7zrgj ] --> Relieving...
|
|
164
|
+
|
|
165
|
+
2025-06-07T07:44:33.712Z result:49
|
|
166
|
+
|
|
167
|
+
[ oh88p ] --> Stressing CPU...
|
|
168
|
+
|
|
169
|
+
[ oh88p ] ----> 1 second of stress period complete.
|
|
170
|
+
[ oh88p ] ----> 2 seconds of stress period complete.
|
|
171
|
+
[ oh88p ] ----> 3 seconds of stress period complete.
|
|
172
|
+
[ oh88p ] ----> 4 seconds of stress period complete.
|
|
173
|
+
[ oh88p ] ----> 5 seconds of stress period complete.
|
|
174
|
+
|
|
175
|
+
[ oh88p ] --> Relieving...
|
|
176
|
+
|
|
177
|
+
2025-06-07T07:44:33.721Z result:25
|
|
178
|
+
|
|
179
|
+
===========================================
|
|
180
|
+
> Starting new NodeHog [ p21ap ].
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
===========================================
|
|
184
|
+
> Starting new NodeHog [ 3yywg ].
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
[ p21ap ] --> Stressing CPU...
|
|
188
|
+
|
|
189
|
+
[ p21ap ] ----> 1 second of stress period complete.
|
|
190
|
+
[ p21ap ] ----> 2 seconds of stress period complete.
|
|
191
|
+
[ p21ap ] ----> 3 seconds of stress period complete.
|
|
192
|
+
[ p21ap ] ----> 4 seconds of stress period complete.
|
|
193
|
+
[ p21ap ] ----> 5 seconds of stress period complete.
|
|
194
|
+
|
|
195
|
+
[ p21ap ] --> Relieving...
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
> Killing NodeHog [ p21ap ].
|
|
199
|
+
-------------------------------------------
|
|
200
|
+
|
|
201
|
+
2025-06-07T07:44:38.755Z result:64
|
|
202
|
+
|
|
203
|
+
[ 3yywg ] --> Stressing CPU...
|
|
204
|
+
|
|
205
|
+
[ 3yywg ] ----> 1 second of stress period complete.
|
|
206
|
+
[ 3yywg ] ----> 2 seconds of stress period complete.
|
|
207
|
+
[ 3yywg ] ----> 3 seconds of stress period complete.
|
|
208
|
+
[ 3yywg ] ----> 4 seconds of stress period complete.
|
|
209
|
+
[ 3yywg ] ----> 5 seconds of stress period complete.
|
|
210
|
+
|
|
211
|
+
[ 3yywg ] --> Relieving...
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
> Killing NodeHog [ 3yywg ].
|
|
215
|
+
-------------------------------------------
|
|
216
|
+
|
|
217
|
+
2025-06-07T07:44:38.757Z result:81
|
|
218
|
+
*/
|
package/rollup.config.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import alias from "@rollup/plugin-alias";
|
|
2
|
+
import commonjs from "@rollup/plugin-commonjs";
|
|
3
|
+
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
|
4
|
+
import typescript from "@rollup/plugin-typescript";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { defineConfig } from "rollup";
|
|
7
|
+
import copy from "rollup-plugin-copy";
|
|
8
|
+
|
|
9
|
+
const plugins = [
|
|
10
|
+
nodeResolve(),
|
|
11
|
+
commonjs(),
|
|
12
|
+
copy({
|
|
13
|
+
targets: [
|
|
14
|
+
{
|
|
15
|
+
src: "node_modules/mupdf/dist/mupdf-wasm.wasm",
|
|
16
|
+
dest: "dist",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
src: "src/index.html",
|
|
20
|
+
dest: "dist",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
src: "src/style.css",
|
|
24
|
+
dest: "dist",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
}),
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const rollupConfig = defineConfig([
|
|
31
|
+
{
|
|
32
|
+
input: "src/index.ts",
|
|
33
|
+
output: {
|
|
34
|
+
file: "dist/index.js",
|
|
35
|
+
sourcemap: true,
|
|
36
|
+
},
|
|
37
|
+
plugins: [
|
|
38
|
+
alias({
|
|
39
|
+
entries: [
|
|
40
|
+
{
|
|
41
|
+
find: "jimp",
|
|
42
|
+
replacement: path.resolve(
|
|
43
|
+
"node_modules/jimp/dist/browser/index.js",
|
|
44
|
+
),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
find: "web-worker",
|
|
48
|
+
replacement: path.resolve(
|
|
49
|
+
"node_modules/web-worker/dist/browser/index.cjs",
|
|
50
|
+
),
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
}),
|
|
54
|
+
typescript({ tsconfig: "./tsconfig.json" }),
|
|
55
|
+
...plugins,
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
input: "src/worker.ts",
|
|
60
|
+
output: {
|
|
61
|
+
file: "dist/worker.js",
|
|
62
|
+
sourcemap: true,
|
|
63
|
+
},
|
|
64
|
+
plugins: [
|
|
65
|
+
alias({
|
|
66
|
+
entries: [
|
|
67
|
+
{
|
|
68
|
+
find: "jimp",
|
|
69
|
+
replacement: path.resolve(
|
|
70
|
+
"node_modules/jimp/dist/browser/index.js",
|
|
71
|
+
),
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
}),
|
|
75
|
+
typescript({ tsconfig: "./tsconfig.json" }),
|
|
76
|
+
...plugins,
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
input: "src/cli.ts",
|
|
81
|
+
output: {
|
|
82
|
+
file: "dist/cli.js",
|
|
83
|
+
sourcemap: true,
|
|
84
|
+
},
|
|
85
|
+
external: ["web-worker"],
|
|
86
|
+
plugins: [
|
|
87
|
+
typescript({ tsconfig: "./tsconfig.json" }),
|
|
88
|
+
nodeResolve(),
|
|
89
|
+
commonjs(),
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
input: "src/browser.ts",
|
|
94
|
+
output: {
|
|
95
|
+
file: "dist/browser.js",
|
|
96
|
+
sourcemap: true,
|
|
97
|
+
},
|
|
98
|
+
plugins: [
|
|
99
|
+
alias({
|
|
100
|
+
entries: [
|
|
101
|
+
{
|
|
102
|
+
find: "jimp",
|
|
103
|
+
replacement: path.resolve(
|
|
104
|
+
"node_modules/jimp/dist/browser/index.js",
|
|
105
|
+
),
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
find: "web-worker",
|
|
109
|
+
replacement: path.resolve(
|
|
110
|
+
"node_modules/web-worker/dist/browser/index.cjs",
|
|
111
|
+
),
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
}),
|
|
115
|
+
typescript({ tsconfig: "./tsconfig.json" }),
|
|
116
|
+
...plugins,
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
export default rollupConfig;
|
package/src/browser.ts
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
|
|
3
|
+
import * as pdfdiff from "./index.js";
|
|
4
|
+
|
|
5
|
+
async function readFileAsUint8Array(file: File): Promise<Uint8Array> {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const reader = new FileReader();
|
|
8
|
+
reader.onload = () => {
|
|
9
|
+
resolve(new Uint8Array(reader.result as ArrayBuffer));
|
|
10
|
+
};
|
|
11
|
+
reader.onerror = () => reject(reader.error);
|
|
12
|
+
reader.readAsArrayBuffer(file);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
document
|
|
17
|
+
.getElementById("pdf-diff-form")
|
|
18
|
+
?.addEventListener("submit", async (event) => {
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
|
|
21
|
+
const resultsContainer = document.getElementById("results");
|
|
22
|
+
if (resultsContainer) resultsContainer.innerHTML = "";
|
|
23
|
+
|
|
24
|
+
const errorElement = document.getElementById("error-message");
|
|
25
|
+
if (errorElement) errorElement.textContent = "";
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const pdfAFile = (
|
|
29
|
+
document.getElementById("pdf-a") as HTMLInputElement | null
|
|
30
|
+
)?.files?.[0];
|
|
31
|
+
const pdfBFile = (
|
|
32
|
+
document.getElementById("pdf-b") as HTMLInputElement | null
|
|
33
|
+
)?.files?.[0];
|
|
34
|
+
if (typeof pdfAFile === "undefined" || typeof pdfBFile === "undefined") {
|
|
35
|
+
throw new Error();
|
|
36
|
+
}
|
|
37
|
+
const pdfA = await readFileAsUint8Array(pdfAFile);
|
|
38
|
+
const pdfB = await readFileAsUint8Array(pdfBFile);
|
|
39
|
+
|
|
40
|
+
const pdfMaskFile = (
|
|
41
|
+
document.getElementById("pdf-mask") as HTMLInputElement | null
|
|
42
|
+
)?.files?.[0];
|
|
43
|
+
const pdfMask = pdfMaskFile
|
|
44
|
+
? await readFileAsUint8Array(pdfMaskFile)
|
|
45
|
+
: undefined;
|
|
46
|
+
|
|
47
|
+
const dpi = ((
|
|
48
|
+
val = (document.getElementById("dpi") as HTMLInputElement | null)
|
|
49
|
+
?.value,
|
|
50
|
+
) => (typeof val !== "undefined" ? parseInt(val, 10) : undefined))();
|
|
51
|
+
const alpha = (
|
|
52
|
+
document.getElementById("alpha") as HTMLInputElement | null
|
|
53
|
+
)?.checked;
|
|
54
|
+
|
|
55
|
+
const align = (
|
|
56
|
+
document.getElementById("align") as HTMLInputElement | null
|
|
57
|
+
)?.value;
|
|
58
|
+
if (
|
|
59
|
+
typeof align !== "undefined" &&
|
|
60
|
+
!pdfdiff.isValidAlignStrategy(align)
|
|
61
|
+
) {
|
|
62
|
+
throw new Error();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const additionColorHex = (
|
|
66
|
+
document.getElementById("addition-color") as HTMLInputElement | null
|
|
67
|
+
)?.value;
|
|
68
|
+
const deletionColorHex = (
|
|
69
|
+
document.getElementById("deletion-color") as HTMLInputElement | null
|
|
70
|
+
)?.value;
|
|
71
|
+
const modificationColorHex = (
|
|
72
|
+
document.getElementById("modification-color") as HTMLInputElement | null
|
|
73
|
+
)?.value;
|
|
74
|
+
const additionColor = additionColorHex
|
|
75
|
+
? pdfdiff.parseHex(additionColorHex)
|
|
76
|
+
: undefined;
|
|
77
|
+
const deletionColor = deletionColorHex
|
|
78
|
+
? pdfdiff.parseHex(deletionColorHex)
|
|
79
|
+
: undefined;
|
|
80
|
+
const modificationColor = modificationColorHex
|
|
81
|
+
? pdfdiff.parseHex(modificationColorHex)
|
|
82
|
+
: undefined;
|
|
83
|
+
if (
|
|
84
|
+
additionColor === null ||
|
|
85
|
+
deletionColor === null ||
|
|
86
|
+
modificationColor === null
|
|
87
|
+
) {
|
|
88
|
+
throw new Error();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const options: Parameters<typeof pdfdiff.visualizeDifferences>[2] = {};
|
|
92
|
+
if (dpi !== undefined) options.dpi = dpi;
|
|
93
|
+
if (alpha !== undefined) options.alpha = alpha;
|
|
94
|
+
if (pdfMask !== undefined) options.mask = pdfMask;
|
|
95
|
+
if (align !== undefined) options.align = align;
|
|
96
|
+
if (additionColor || deletionColor || modificationColor) {
|
|
97
|
+
options.pallet = {};
|
|
98
|
+
if (additionColor) options.pallet.addition = additionColor;
|
|
99
|
+
if (deletionColor) options.pallet.deletion = deletionColor;
|
|
100
|
+
if (modificationColor) options.pallet.modification = modificationColor;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for await (const [
|
|
104
|
+
i,
|
|
105
|
+
{ a, b, diff, addition, deletion, modification },
|
|
106
|
+
] of pdfdiff.withIndex(
|
|
107
|
+
pdfdiff.visualizeDifferences(pdfA, pdfB, options),
|
|
108
|
+
1,
|
|
109
|
+
)) {
|
|
110
|
+
const pageResult = document.createElement("details");
|
|
111
|
+
pageResult.className = "diff-details";
|
|
112
|
+
pageResult.open =
|
|
113
|
+
addition.length + deletion.length + modification.length > 0;
|
|
114
|
+
|
|
115
|
+
const summary = document.createElement("summary");
|
|
116
|
+
|
|
117
|
+
const summaryInline = document.createElement("div");
|
|
118
|
+
summaryInline.className = "summary-content";
|
|
119
|
+
|
|
120
|
+
const pageTitle = document.createElement("h3");
|
|
121
|
+
pageTitle.textContent = `Page ${i}`;
|
|
122
|
+
|
|
123
|
+
const pageSummary = document.createElement("div");
|
|
124
|
+
pageSummary.textContent = `Addition: ${addition.length}, Deletion: ${deletion.length}, Modification: ${modification.length}`;
|
|
125
|
+
|
|
126
|
+
summaryInline.appendChild(pageTitle);
|
|
127
|
+
summaryInline.appendChild(pageSummary);
|
|
128
|
+
summary.appendChild(summaryInline);
|
|
129
|
+
pageResult.appendChild(summary);
|
|
130
|
+
|
|
131
|
+
const imagesTable = document.createElement("table");
|
|
132
|
+
imagesTable.className = "diff-table";
|
|
133
|
+
|
|
134
|
+
const headerRow = document.createElement("tr");
|
|
135
|
+
|
|
136
|
+
const headerA = document.createElement("th");
|
|
137
|
+
headerA.textContent = "A";
|
|
138
|
+
headerRow.appendChild(headerA);
|
|
139
|
+
|
|
140
|
+
const headerB = document.createElement("th");
|
|
141
|
+
headerB.textContent = "B";
|
|
142
|
+
headerRow.appendChild(headerB);
|
|
143
|
+
|
|
144
|
+
const headerDiff = document.createElement("th");
|
|
145
|
+
headerDiff.textContent = "Diff";
|
|
146
|
+
headerRow.appendChild(headerDiff);
|
|
147
|
+
|
|
148
|
+
imagesTable.appendChild(headerRow);
|
|
149
|
+
|
|
150
|
+
const imagesRow = document.createElement("tr");
|
|
151
|
+
|
|
152
|
+
const cellA = document.createElement("td");
|
|
153
|
+
const imageA = document.createElement("img");
|
|
154
|
+
imageA.src = await a.getBase64("image/png");
|
|
155
|
+
imageA.className = "checkerboard-bg";
|
|
156
|
+
cellA.appendChild(imageA);
|
|
157
|
+
imagesRow.appendChild(cellA);
|
|
158
|
+
|
|
159
|
+
const cellB = document.createElement("td");
|
|
160
|
+
const imageB = document.createElement("img");
|
|
161
|
+
imageB.src = await b.getBase64("image/png");
|
|
162
|
+
imageB.className = "checkerboard-bg";
|
|
163
|
+
cellB.appendChild(imageB);
|
|
164
|
+
imagesRow.appendChild(cellB);
|
|
165
|
+
|
|
166
|
+
const cellDiff = document.createElement("td");
|
|
167
|
+
const imageDiff = document.createElement("img");
|
|
168
|
+
imageDiff.src = await diff.getBase64("image/png");
|
|
169
|
+
imageDiff.className = "checkerboard-bg";
|
|
170
|
+
cellDiff.appendChild(imageDiff);
|
|
171
|
+
imagesRow.appendChild(cellDiff);
|
|
172
|
+
|
|
173
|
+
imagesTable.appendChild(imagesRow);
|
|
174
|
+
|
|
175
|
+
pageResult.appendChild(imagesTable);
|
|
176
|
+
resultsContainer?.appendChild(pageResult);
|
|
177
|
+
}
|
|
178
|
+
} catch (e) {
|
|
179
|
+
console.error(e);
|
|
180
|
+
if (errorElement) {
|
|
181
|
+
errorElement.textContent = `Error: ${(e as Error).message}`;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
});
|