@u1f992/pdfdiff 0.1.0 → 0.2.1
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/dist/browser.js +218 -125
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +235 -170
- package/dist/cli.js.map +1 -1
- package/dist/coi-serviceworker.min.js +2 -0
- package/dist/diff.d.ts +3 -3
- package/dist/diff.d.ts.map +1 -1
- package/dist/image.d.ts +1 -1
- package/dist/image.d.ts.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.html +6 -1
- package/dist/index.js +207 -125
- package/dist/index.js.map +1 -1
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/jimp.d.ts +1 -1
- package/dist/jimp.d.ts.map +1 -1
- package/dist/pdf.d.ts +2 -374
- package/dist/pdf.d.ts.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/worker.d.ts +48 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +3184 -38
- package/dist/worker.js.map +1 -1
- package/package.json +4 -2
- package/rollup.config.js +29 -42
- package/scripts/version.ts +35 -0
- package/src/browser.ts +17 -1
- package/src/cli.ts +23 -11
- package/src/diff.ts +42 -27
- package/src/image.ts +31 -9
- package/src/index.html +6 -1
- package/src/index.test.ts +97 -0
- package/src/index.ts +210 -106
- package/src/jimp.ts +1 -0
- package/src/pdf.ts +34 -2
- package/src/worker.ts +157 -39
package/dist/cli.js
CHANGED
|
@@ -18,7 +18,6 @@ var HeaderTypes;
|
|
|
18
18
|
HeaderTypes[HeaderTypes["BITMAP_V4_HEADER"] = 108] = "BITMAP_V4_HEADER";
|
|
19
19
|
HeaderTypes[HeaderTypes["BITMAP_V5_HEADER"] = 124] = "BITMAP_V5_HEADER";
|
|
20
20
|
})(HeaderTypes || (HeaderTypes = {}));
|
|
21
|
-
var HeaderTypes$1 = HeaderTypes;
|
|
22
21
|
|
|
23
22
|
// We have these:
|
|
24
23
|
//
|
|
@@ -148,7 +147,7 @@ class BmpDecoder {
|
|
|
148
147
|
this.offset = this.readUInt32LE();
|
|
149
148
|
// End of BITMAP_FILE_HEADER
|
|
150
149
|
this.headerSize = this.readUInt32LE();
|
|
151
|
-
if (!(this.headerSize in HeaderTypes
|
|
150
|
+
if (!(this.headerSize in HeaderTypes)) {
|
|
152
151
|
throw new Error(`Unsupported BMP header size ${this.headerSize}`);
|
|
153
152
|
}
|
|
154
153
|
this.width = this.readUInt32LE();
|
|
@@ -180,7 +179,7 @@ class BmpDecoder {
|
|
|
180
179
|
this.maskBlue = 0x001f;
|
|
181
180
|
}
|
|
182
181
|
// End of BITMAP_INFO_HEADER
|
|
183
|
-
if (this.headerSize > HeaderTypes
|
|
182
|
+
if (this.headerSize > HeaderTypes.BITMAP_INFO_HEADER ||
|
|
184
183
|
this.compression === BmpCompression.BI_BIT_FIELDS ||
|
|
185
184
|
this.compression === BmpCompression.BI_ALPHA_BIT_FIELDS) {
|
|
186
185
|
this.maskRed = this.readUInt32LE();
|
|
@@ -188,18 +187,18 @@ class BmpDecoder {
|
|
|
188
187
|
this.maskBlue = this.readUInt32LE();
|
|
189
188
|
}
|
|
190
189
|
// End of BITMAP_V2_INFO_HEADER
|
|
191
|
-
if (this.headerSize > HeaderTypes
|
|
190
|
+
if (this.headerSize > HeaderTypes.BITMAP_V2_INFO_HEADER ||
|
|
192
191
|
this.compression === BmpCompression.BI_ALPHA_BIT_FIELDS) {
|
|
193
192
|
this.maskAlpha = this.readUInt32LE();
|
|
194
193
|
}
|
|
195
194
|
// End of BITMAP_V3_INFO_HEADER
|
|
196
|
-
if (this.headerSize > HeaderTypes
|
|
195
|
+
if (this.headerSize > HeaderTypes.BITMAP_V3_INFO_HEADER) {
|
|
197
196
|
this.pos +=
|
|
198
|
-
HeaderTypes
|
|
197
|
+
HeaderTypes.BITMAP_V4_HEADER - HeaderTypes.BITMAP_V3_INFO_HEADER;
|
|
199
198
|
}
|
|
200
199
|
// End of BITMAP_V4_HEADER
|
|
201
|
-
if (this.headerSize > HeaderTypes
|
|
202
|
-
this.pos += HeaderTypes
|
|
200
|
+
if (this.headerSize > HeaderTypes.BITMAP_V4_HEADER) {
|
|
201
|
+
this.pos += HeaderTypes.BITMAP_V5_HEADER - HeaderTypes.BITMAP_V4_HEADER;
|
|
203
202
|
}
|
|
204
203
|
// End of BITMAP_V5_HEADER
|
|
205
204
|
if (this.bitPP <= 8 || this.colors > 0) {
|
|
@@ -515,7 +514,7 @@ class BmpEncoder {
|
|
|
515
514
|
this.buffer = imgData.data;
|
|
516
515
|
this.width = imgData.width;
|
|
517
516
|
this.height = imgData.height;
|
|
518
|
-
this.headerSize = HeaderTypes
|
|
517
|
+
this.headerSize = HeaderTypes.BITMAP_INFO_HEADER;
|
|
519
518
|
// Header
|
|
520
519
|
this.flag = 'BM';
|
|
521
520
|
this.bitPP = imgData.bitPP || 24;
|
|
@@ -2032,31 +2031,6 @@ function intToRGBA$1(i) {
|
|
|
2032
2031
|
Math.pow(256, 0));
|
|
2033
2032
|
return rgba;
|
|
2034
2033
|
}
|
|
2035
|
-
/**
|
|
2036
|
-
* A static helper method that converts RGBA values to a single integer value
|
|
2037
|
-
* @param r the red value (0-255)
|
|
2038
|
-
* @param g the green value (0-255)
|
|
2039
|
-
* @param b the blue value (0-255)
|
|
2040
|
-
* @param a the alpha value (0-255)
|
|
2041
|
-
* @example
|
|
2042
|
-
* ```ts
|
|
2043
|
-
* import { rgbaToInt } from "@jimp/utils";
|
|
2044
|
-
*
|
|
2045
|
-
* rgbaToInt(255, 0, 0, 255); // 0xFF0000FF
|
|
2046
|
-
* ```
|
|
2047
|
-
*/
|
|
2048
|
-
function rgbaToInt(r, g, b, a) {
|
|
2049
|
-
let i = r & 0xff;
|
|
2050
|
-
i <<= 8;
|
|
2051
|
-
i |= g & 0xff;
|
|
2052
|
-
i <<= 8;
|
|
2053
|
-
i |= b & 0xff;
|
|
2054
|
-
i <<= 8;
|
|
2055
|
-
i |= a & 0xff;
|
|
2056
|
-
// Ensure sign is correct
|
|
2057
|
-
i >>>= 0;
|
|
2058
|
-
return i;
|
|
2059
|
-
}
|
|
2060
2034
|
/**
|
|
2061
2035
|
* Compute color difference
|
|
2062
2036
|
* 0 means no difference, 1 means maximum difference.
|
|
@@ -36374,13 +36348,13 @@ const defaultPlugins = [
|
|
|
36374
36348
|
];
|
|
36375
36349
|
const defaultFormats = [bmp, msBmp, gif, jpeg$1, png, tiff];
|
|
36376
36350
|
/** Convenience object for getting the MIME types of the default formats */
|
|
36377
|
-
|
|
36351
|
+
({
|
|
36378
36352
|
bmp: bmp().mime,
|
|
36379
36353
|
gif: gif().mime,
|
|
36380
36354
|
jpeg: jpeg$1().mime,
|
|
36381
36355
|
png: png().mime,
|
|
36382
36356
|
tiff: tiff().mime,
|
|
36383
|
-
};
|
|
36357
|
+
});
|
|
36384
36358
|
// TODO: This doesn't document the constructor of the class
|
|
36385
36359
|
/**
|
|
36386
36360
|
* @class
|
|
@@ -39449,13 +39423,6 @@ globalThis.$libmupdf_device = {
|
|
|
39449
39423
|
* You should have received a copy of the GNU General Public License
|
|
39450
39424
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
39451
39425
|
*/
|
|
39452
|
-
function createEmptyImage(width, height) {
|
|
39453
|
-
return new Jimp({
|
|
39454
|
-
width,
|
|
39455
|
-
height,
|
|
39456
|
-
color: rgbaToInt(0, 0, 0, 0),
|
|
39457
|
-
});
|
|
39458
|
-
}
|
|
39459
39426
|
const alignStrategyValues = new Set([
|
|
39460
39427
|
"resize",
|
|
39461
39428
|
"top-left",
|
|
@@ -39470,55 +39437,6 @@ const alignStrategyValues = new Set([
|
|
|
39470
39437
|
]);
|
|
39471
39438
|
const isValidAlignStrategy = (str) => alignStrategyValues.has(str);
|
|
39472
39439
|
|
|
39473
|
-
/*
|
|
39474
|
-
* Copyright (C) 2025 Koutaro Mukai
|
|
39475
|
-
*
|
|
39476
|
-
* This program is free software: you can redistribute it and/or modify
|
|
39477
|
-
* it under the terms of the GNU General Public License as published by
|
|
39478
|
-
* the Free Software Foundation, either version 3 of the License, or
|
|
39479
|
-
* (at your option) any later version.
|
|
39480
|
-
*
|
|
39481
|
-
* This program is distributed in the hope that it will be useful,
|
|
39482
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
39483
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
39484
|
-
* GNU General Public License for more details.
|
|
39485
|
-
*
|
|
39486
|
-
* You should have received a copy of the GNU General Public License
|
|
39487
|
-
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
39488
|
-
*/
|
|
39489
|
-
async function* withIndex(iter, start = 0) {
|
|
39490
|
-
let index = start;
|
|
39491
|
-
for await (const item of iter) {
|
|
39492
|
-
yield [index, item];
|
|
39493
|
-
index++;
|
|
39494
|
-
}
|
|
39495
|
-
}
|
|
39496
|
-
|
|
39497
|
-
/*
|
|
39498
|
-
* Copyright (C) 2025 Koutaro Mukai
|
|
39499
|
-
*
|
|
39500
|
-
* This program is free software: you can redistribute it and/or modify
|
|
39501
|
-
* it under the terms of the GNU General Public License as published by
|
|
39502
|
-
* the Free Software Foundation, either version 3 of the License, or
|
|
39503
|
-
* (at your option) any later version.
|
|
39504
|
-
*
|
|
39505
|
-
* This program is distributed in the hope that it will be useful,
|
|
39506
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
39507
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
39508
|
-
* GNU General Public License for more details.
|
|
39509
|
-
*
|
|
39510
|
-
* You should have received a copy of the GNU General Public License
|
|
39511
|
-
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
39512
|
-
*/
|
|
39513
|
-
async function pageToImage(page, dpi, alpha) {
|
|
39514
|
-
const zoom = dpi / 72;
|
|
39515
|
-
const pixmap = page.toPixmap([zoom, 0, 0, zoom, 0, 0], ColorSpace.DeviceRGB, alpha);
|
|
39516
|
-
const ret = await Jimp.fromBuffer(new Uint8Array(pixmap.asPNG()).buffer);
|
|
39517
|
-
pixmap.destroy();
|
|
39518
|
-
page.destroy();
|
|
39519
|
-
return ret;
|
|
39520
|
-
}
|
|
39521
|
-
|
|
39522
39440
|
/*
|
|
39523
39441
|
* Copyright (C) 2025 Koutaro Mukai
|
|
39524
39442
|
*
|
|
@@ -39578,6 +39496,32 @@ const formatHex = ([r, g, b, a]) => "#" +
|
|
|
39578
39496
|
})
|
|
39579
39497
|
.join("");
|
|
39580
39498
|
|
|
39499
|
+
/*
|
|
39500
|
+
* Copyright (C) 2025 Koutaro Mukai
|
|
39501
|
+
*
|
|
39502
|
+
* This program is free software: you can redistribute it and/or modify
|
|
39503
|
+
* it under the terms of the GNU General Public License as published by
|
|
39504
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
39505
|
+
* (at your option) any later version.
|
|
39506
|
+
*
|
|
39507
|
+
* This program is distributed in the hope that it will be useful,
|
|
39508
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
39509
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
39510
|
+
* GNU General Public License for more details.
|
|
39511
|
+
*
|
|
39512
|
+
* You should have received a copy of the GNU General Public License
|
|
39513
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
39514
|
+
*/
|
|
39515
|
+
async function* withIndex(iter, start = 0) {
|
|
39516
|
+
let index = start;
|
|
39517
|
+
for await (const item of iter) {
|
|
39518
|
+
yield [index, item];
|
|
39519
|
+
index++;
|
|
39520
|
+
}
|
|
39521
|
+
}
|
|
39522
|
+
|
|
39523
|
+
const VERSION = "0.2.1";
|
|
39524
|
+
|
|
39581
39525
|
/*
|
|
39582
39526
|
* Copyright (C) 2025 Koutaro Mukai
|
|
39583
39527
|
*
|
|
@@ -39604,9 +39548,105 @@ const defaultOptions = {
|
|
|
39604
39548
|
deletion: [0xff, 0x57, 0x24, 0xff],
|
|
39605
39549
|
modification: [0xff, 0xc1, 0x05, 0xff],
|
|
39606
39550
|
},
|
|
39551
|
+
workers: 1,
|
|
39607
39552
|
};
|
|
39553
|
+
function asSharedBytes(bytes) {
|
|
39554
|
+
const isNode = typeof globalThis.process !== "undefined" &&
|
|
39555
|
+
!!globalThis.process.versions?.node;
|
|
39556
|
+
const coiOk = globalThis.crossOriginIsolated ===
|
|
39557
|
+
true;
|
|
39558
|
+
if (typeof SharedArrayBuffer !== "undefined" && (isNode || coiOk)) {
|
|
39559
|
+
const sab = new SharedArrayBuffer(bytes.byteLength);
|
|
39560
|
+
const view = new Uint8Array(sab);
|
|
39561
|
+
view.set(bytes);
|
|
39562
|
+
return view;
|
|
39563
|
+
}
|
|
39564
|
+
return new Uint8Array(bytes);
|
|
39565
|
+
}
|
|
39566
|
+
class WorkerHandle {
|
|
39567
|
+
worker;
|
|
39568
|
+
loaded;
|
|
39569
|
+
pendingResolve = null;
|
|
39570
|
+
pendingReject = null;
|
|
39571
|
+
constructor(url) {
|
|
39572
|
+
this.worker = new Worker(url, { type: "module" });
|
|
39573
|
+
this.loaded = new Promise((resolveLoaded, rejectLoaded) => {
|
|
39574
|
+
const onMessage = (e) => {
|
|
39575
|
+
const data = e.data;
|
|
39576
|
+
if (data.type === "loaded") {
|
|
39577
|
+
resolveLoaded();
|
|
39578
|
+
return;
|
|
39579
|
+
}
|
|
39580
|
+
const resolve = this.pendingResolve;
|
|
39581
|
+
const reject = this.pendingReject;
|
|
39582
|
+
this.pendingResolve = null;
|
|
39583
|
+
this.pendingReject = null;
|
|
39584
|
+
if (data.type === "error") {
|
|
39585
|
+
reject?.(new Error(`worker: ${data.message}`));
|
|
39586
|
+
}
|
|
39587
|
+
else {
|
|
39588
|
+
resolve?.(data);
|
|
39589
|
+
}
|
|
39590
|
+
};
|
|
39591
|
+
this.worker.addEventListener("message", onMessage);
|
|
39592
|
+
this.worker.addEventListener("error", (e) => {
|
|
39593
|
+
const err = e.error ?? new Error(e.message);
|
|
39594
|
+
rejectLoaded(err);
|
|
39595
|
+
const reject = this.pendingReject;
|
|
39596
|
+
this.pendingResolve = null;
|
|
39597
|
+
this.pendingReject = null;
|
|
39598
|
+
reject?.(err);
|
|
39599
|
+
});
|
|
39600
|
+
});
|
|
39601
|
+
}
|
|
39602
|
+
async init(msg) {
|
|
39603
|
+
await this.loaded;
|
|
39604
|
+
return new Promise((resolve, reject) => {
|
|
39605
|
+
this.pendingResolve = resolve;
|
|
39606
|
+
this.pendingReject = reject;
|
|
39607
|
+
this.worker.postMessage(msg);
|
|
39608
|
+
});
|
|
39609
|
+
}
|
|
39610
|
+
processPage(index) {
|
|
39611
|
+
return new Promise((resolve, reject) => {
|
|
39612
|
+
this.pendingResolve = resolve;
|
|
39613
|
+
this.pendingReject = reject;
|
|
39614
|
+
const msg = { type: "page", index };
|
|
39615
|
+
this.worker.postMessage(msg);
|
|
39616
|
+
});
|
|
39617
|
+
}
|
|
39618
|
+
terminate() {
|
|
39619
|
+
this.worker.terminate();
|
|
39620
|
+
}
|
|
39621
|
+
}
|
|
39622
|
+
function workerUrl() {
|
|
39623
|
+
const file = import.meta.url.endsWith(".ts") ? "./worker.ts" : "./worker.js";
|
|
39624
|
+
return new URL(`${file}?v=${encodeURIComponent(VERSION)}`, import.meta.url);
|
|
39625
|
+
}
|
|
39626
|
+
function pageResultToResult(msg) {
|
|
39627
|
+
return {
|
|
39628
|
+
a: Jimp.fromBitmap({
|
|
39629
|
+
width: msg.a.width,
|
|
39630
|
+
height: msg.a.height,
|
|
39631
|
+
data: new Uint8Array(msg.a.data),
|
|
39632
|
+
}),
|
|
39633
|
+
b: Jimp.fromBitmap({
|
|
39634
|
+
width: msg.b.width,
|
|
39635
|
+
height: msg.b.height,
|
|
39636
|
+
data: new Uint8Array(msg.b.data),
|
|
39637
|
+
}),
|
|
39638
|
+
diff: Jimp.fromBitmap({
|
|
39639
|
+
width: msg.diff.width,
|
|
39640
|
+
height: msg.diff.height,
|
|
39641
|
+
data: new Uint8Array(msg.diff.data),
|
|
39642
|
+
}),
|
|
39643
|
+
addition: msg.addition,
|
|
39644
|
+
deletion: msg.deletion,
|
|
39645
|
+
modification: msg.modification,
|
|
39646
|
+
};
|
|
39647
|
+
}
|
|
39608
39648
|
async function* visualizeDifferences(a, b, options) {
|
|
39609
|
-
const
|
|
39649
|
+
const merged = {
|
|
39610
39650
|
dpi: options?.dpi ?? defaultOptions.dpi,
|
|
39611
39651
|
alpha: options?.alpha ?? defaultOptions.alpha,
|
|
39612
39652
|
mask: options?.mask ?? defaultOptions.mask,
|
|
@@ -39616,76 +39656,92 @@ async function* visualizeDifferences(a, b, options) {
|
|
|
39616
39656
|
deletion: options?.pallet?.deletion ?? defaultOptions.pallet.deletion,
|
|
39617
39657
|
modification: options?.pallet?.modification ?? defaultOptions.pallet.modification,
|
|
39618
39658
|
},
|
|
39659
|
+
workers: options?.workers ?? defaultOptions.workers,
|
|
39619
39660
|
};
|
|
39620
|
-
const
|
|
39621
|
-
const
|
|
39622
|
-
const
|
|
39623
|
-
? PDFDocument.openDocument(
|
|
39661
|
+
const probe = PDFDocument.openDocument(a, "application/pdf");
|
|
39662
|
+
const probeB = PDFDocument.openDocument(b, "application/pdf");
|
|
39663
|
+
const probeMask = typeof merged.mask !== "undefined"
|
|
39664
|
+
? PDFDocument.openDocument(merged.mask, "application/pdf")
|
|
39624
39665
|
: new PDFDocument();
|
|
39625
|
-
const maxPages = Math.max(
|
|
39626
|
-
|
|
39627
|
-
|
|
39628
|
-
|
|
39629
|
-
|
|
39630
|
-
|
|
39631
|
-
|
|
39632
|
-
|
|
39633
|
-
|
|
39634
|
-
|
|
39635
|
-
|
|
39636
|
-
|
|
39637
|
-
|
|
39638
|
-
|
|
39639
|
-
|
|
39640
|
-
|
|
39641
|
-
|
|
39642
|
-
|
|
39643
|
-
|
|
39644
|
-
|
|
39645
|
-
|
|
39646
|
-
|
|
39647
|
-
|
|
39648
|
-
|
|
39649
|
-
|
|
39650
|
-
|
|
39651
|
-
|
|
39652
|
-
|
|
39653
|
-
|
|
39654
|
-
|
|
39655
|
-
|
|
39656
|
-
|
|
39657
|
-
|
|
39658
|
-
|
|
39659
|
-
|
|
39660
|
-
|
|
39661
|
-
|
|
39662
|
-
|
|
39663
|
-
|
|
39664
|
-
|
|
39665
|
-
|
|
39666
|
-
|
|
39667
|
-
|
|
39668
|
-
|
|
39669
|
-
|
|
39670
|
-
|
|
39671
|
-
|
|
39672
|
-
|
|
39673
|
-
|
|
39674
|
-
|
|
39675
|
-
|
|
39676
|
-
|
|
39677
|
-
|
|
39678
|
-
|
|
39679
|
-
|
|
39680
|
-
|
|
39681
|
-
|
|
39682
|
-
|
|
39666
|
+
const maxPages = Math.max(probe.countPages(), probeB.countPages(), probeMask.countPages());
|
|
39667
|
+
probe.destroy();
|
|
39668
|
+
probeB.destroy();
|
|
39669
|
+
probeMask.destroy();
|
|
39670
|
+
if (maxPages === 0)
|
|
39671
|
+
return;
|
|
39672
|
+
const aBytes = asSharedBytes(a);
|
|
39673
|
+
const bBytes = asSharedBytes(b);
|
|
39674
|
+
const maskBytes = typeof merged.mask !== "undefined" ? asSharedBytes(merged.mask) : null;
|
|
39675
|
+
const initMsg = {
|
|
39676
|
+
type: "init",
|
|
39677
|
+
aBytes,
|
|
39678
|
+
bBytes,
|
|
39679
|
+
maskBytes,
|
|
39680
|
+
dpi: merged.dpi,
|
|
39681
|
+
alpha: merged.alpha,
|
|
39682
|
+
pallet: merged.pallet,
|
|
39683
|
+
align: merged.align,
|
|
39684
|
+
};
|
|
39685
|
+
const N = Math.max(1, Math.min(merged.workers, maxPages));
|
|
39686
|
+
const url = workerUrl();
|
|
39687
|
+
const worker0 = new WorkerHandle(url);
|
|
39688
|
+
await worker0.init(initMsg);
|
|
39689
|
+
const buffered = new Map();
|
|
39690
|
+
let nextToAssign = 0;
|
|
39691
|
+
const workers = [worker0];
|
|
39692
|
+
for (let i = 1; i < N; i++) {
|
|
39693
|
+
const w = new WorkerHandle(url);
|
|
39694
|
+
await w.init(initMsg);
|
|
39695
|
+
workers.push(w);
|
|
39696
|
+
}
|
|
39697
|
+
const resolvers = new Map();
|
|
39698
|
+
let workerError = null;
|
|
39699
|
+
const loops = workers.map(async (w) => {
|
|
39700
|
+
while (nextToAssign < maxPages && workerError === null) {
|
|
39701
|
+
const idx = nextToAssign++;
|
|
39702
|
+
try {
|
|
39703
|
+
const msg = await w.processPage(idx);
|
|
39704
|
+
const result = pageResultToResult(msg);
|
|
39705
|
+
const resolve = resolvers.get(idx);
|
|
39706
|
+
if (resolve) {
|
|
39707
|
+
resolvers.delete(idx);
|
|
39708
|
+
resolve(result);
|
|
39709
|
+
}
|
|
39710
|
+
else {
|
|
39711
|
+
buffered.set(idx, result);
|
|
39712
|
+
}
|
|
39713
|
+
}
|
|
39714
|
+
catch (e) {
|
|
39715
|
+
workerError = e;
|
|
39716
|
+
for (const [, resolve] of resolvers)
|
|
39717
|
+
resolve(null);
|
|
39718
|
+
resolvers.clear();
|
|
39719
|
+
return;
|
|
39720
|
+
}
|
|
39721
|
+
}
|
|
39722
|
+
});
|
|
39723
|
+
try {
|
|
39724
|
+
for (let i = 0; i < maxPages; i++) {
|
|
39725
|
+
if (workerError !== null)
|
|
39726
|
+
throw workerError;
|
|
39727
|
+
let r;
|
|
39728
|
+
const buf = buffered.get(i);
|
|
39729
|
+
if (buf !== undefined) {
|
|
39730
|
+
buffered.delete(i);
|
|
39731
|
+
r = buf;
|
|
39732
|
+
}
|
|
39733
|
+
else {
|
|
39734
|
+
r = await new Promise((resolve) => resolvers.set(i, resolve));
|
|
39735
|
+
if (workerError !== null)
|
|
39736
|
+
throw workerError;
|
|
39737
|
+
}
|
|
39738
|
+
yield r;
|
|
39683
39739
|
}
|
|
39684
|
-
|
|
39685
|
-
|
|
39686
|
-
|
|
39687
|
-
|
|
39688
|
-
|
|
39740
|
+
await Promise.all(loops);
|
|
39741
|
+
}
|
|
39742
|
+
finally {
|
|
39743
|
+
for (const w of workers)
|
|
39744
|
+
w.terminate();
|
|
39689
39745
|
}
|
|
39690
39746
|
}
|
|
39691
39747
|
|
|
@@ -39705,7 +39761,7 @@ async function* visualizeDifferences(a, b, options) {
|
|
|
39705
39761
|
* You should have received a copy of the GNU General Public License
|
|
39706
39762
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
39707
39763
|
*/
|
|
39708
|
-
const { positionals, values: { dpi: dpi_, alpha: alpha_, mask: mask_, align: align_, "addition-color": additionColorHex, "deletion-color": deletionColorHex, "modification-color": modificationColorHex, version, help, }, } = util$2.parseArgs({
|
|
39764
|
+
const { positionals, values: { dpi: dpi_, alpha: alpha_, mask: mask_, align: align_, "addition-color": additionColorHex, "deletion-color": deletionColorHex, "modification-color": modificationColorHex, workers: workers_, version, help, }, } = util$2.parseArgs({
|
|
39709
39765
|
allowPositionals: true,
|
|
39710
39766
|
options: {
|
|
39711
39767
|
dpi: { type: "string" },
|
|
@@ -39715,6 +39771,7 @@ const { positionals, values: { dpi: dpi_, alpha: alpha_, mask: mask_, align: ali
|
|
|
39715
39771
|
"addition-color": { type: "string" },
|
|
39716
39772
|
"deletion-color": { type: "string" },
|
|
39717
39773
|
"modification-color": { type: "string" },
|
|
39774
|
+
workers: { type: "string" },
|
|
39718
39775
|
version: { type: "boolean", short: "v" },
|
|
39719
39776
|
help: { type: "boolean", short: "h" },
|
|
39720
39777
|
},
|
|
@@ -39733,21 +39790,22 @@ OPTIONS:
|
|
|
39733
39790
|
--addition-color <#HEX> default: ${formatHex(defaultOptions.pallet.addition)}
|
|
39734
39791
|
--deletion-color <#HEX> default: ${formatHex(defaultOptions.pallet.deletion)}
|
|
39735
39792
|
--modification-color <#HEX> default: ${formatHex(defaultOptions.pallet.modification)}
|
|
39793
|
+
--workers <N> default: ${defaultOptions.workers}
|
|
39736
39794
|
-v, --version
|
|
39737
39795
|
-h, --help
|
|
39796
|
+
|
|
39797
|
+
NOTES:
|
|
39798
|
+
Approximate per-worker memory:
|
|
39799
|
+
a_size_MB + b_size_MB [+ mask_size_MB] (PDF buffers in wasm)
|
|
39800
|
+
+ 300 MB (mupdf + V8 base)
|
|
39801
|
+
+ (dpi / 150)^2 * 50 MB (pixmap working set)
|
|
39802
|
+
The main process adds ~500 MB - 1 GB (varies with --workers).
|
|
39803
|
+
Choose --workers so the total stays under ~80% of available memory.
|
|
39738
39804
|
`);
|
|
39739
39805
|
process.exit(0);
|
|
39740
39806
|
}
|
|
39741
39807
|
if (version) {
|
|
39742
|
-
|
|
39743
|
-
const versionStr = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), {
|
|
39744
|
-
encoding: "utf-8",
|
|
39745
|
-
})).version;
|
|
39746
|
-
console.log(versionStr);
|
|
39747
|
-
}
|
|
39748
|
-
catch {
|
|
39749
|
-
console.log("unknown");
|
|
39750
|
-
}
|
|
39808
|
+
console.log(VERSION);
|
|
39751
39809
|
process.exit(0);
|
|
39752
39810
|
}
|
|
39753
39811
|
if (positionals.length !== 3) {
|
|
@@ -39782,6 +39840,12 @@ if (additionColor === null ||
|
|
|
39782
39840
|
modificationColor === null) {
|
|
39783
39841
|
throw new Error("Invalid color format");
|
|
39784
39842
|
}
|
|
39843
|
+
const workers = typeof workers_ !== "undefined"
|
|
39844
|
+
? parseInt(workers_, 10)
|
|
39845
|
+
: defaultOptions.workers;
|
|
39846
|
+
if (Number.isNaN(workers) || workers < 1) {
|
|
39847
|
+
throw new Error("Invalid workers value");
|
|
39848
|
+
}
|
|
39785
39849
|
fs.mkdirSync(outDir, { recursive: true });
|
|
39786
39850
|
for await (const [i, { a, b, diff, addition, deletion, modification },] of withIndex(visualizeDifferences(pdfA, pdfB, {
|
|
39787
39851
|
dpi,
|
|
@@ -39793,6 +39857,7 @@ for await (const [i, { a, b, diff, addition, deletion, modification },] of withI
|
|
|
39793
39857
|
deletion: deletionColor,
|
|
39794
39858
|
modification: modificationColor,
|
|
39795
39859
|
},
|
|
39860
|
+
workers,
|
|
39796
39861
|
}), 1)) {
|
|
39797
39862
|
console.log(`Page ${i}, Addition: ${addition.length}, Deletion: ${deletion.length}, Modification: ${modification.length}`);
|
|
39798
39863
|
const dir = path.join(outDir, i.toString(10));
|