@u1f992/pdfdiff 0.1.0 → 0.2.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/dist/browser.js +194 -125
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +213 -161
- 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 +5 -0
- package/dist/index.js +186 -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/worker.d.ts +41 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +3173 -38
- package/dist/worker.js.map +1 -1
- package/package.json +2 -1
- package/rollup.config.js +29 -42
- package/src/browser.ts +13 -1
- package/src/cli.ts +21 -1
- package/src/diff.ts +42 -27
- package/src/image.ts +31 -9
- package/src/index.html +5 -0
- package/src/index.test.ts +97 -0
- package/src/index.ts +195 -106
- package/src/jimp.ts +1 -0
- package/src/pdf.ts +34 -2
- package/src/worker.ts +136 -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,30 @@ 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
|
+
|
|
39581
39523
|
/*
|
|
39582
39524
|
* Copyright (C) 2025 Koutaro Mukai
|
|
39583
39525
|
*
|
|
@@ -39604,9 +39546,86 @@ const defaultOptions = {
|
|
|
39604
39546
|
deletion: [0xff, 0x57, 0x24, 0xff],
|
|
39605
39547
|
modification: [0xff, 0xc1, 0x05, 0xff],
|
|
39606
39548
|
},
|
|
39549
|
+
workers: 1,
|
|
39607
39550
|
};
|
|
39551
|
+
function asSharedBytes(bytes) {
|
|
39552
|
+
const isNode = typeof globalThis.process !== "undefined" &&
|
|
39553
|
+
!!globalThis.process.versions?.node;
|
|
39554
|
+
const coiOk = globalThis.crossOriginIsolated ===
|
|
39555
|
+
true;
|
|
39556
|
+
if (typeof SharedArrayBuffer !== "undefined" && (isNode || coiOk)) {
|
|
39557
|
+
const sab = new SharedArrayBuffer(bytes.byteLength);
|
|
39558
|
+
const view = new Uint8Array(sab);
|
|
39559
|
+
view.set(bytes);
|
|
39560
|
+
return view;
|
|
39561
|
+
}
|
|
39562
|
+
return new Uint8Array(bytes);
|
|
39563
|
+
}
|
|
39564
|
+
class WorkerHandle {
|
|
39565
|
+
worker;
|
|
39566
|
+
pendingResolve = null;
|
|
39567
|
+
pendingReject = null;
|
|
39568
|
+
constructor(url) {
|
|
39569
|
+
this.worker = new Worker(url, { type: "module" });
|
|
39570
|
+
this.worker.addEventListener("message", (e) => {
|
|
39571
|
+
const resolve = this.pendingResolve;
|
|
39572
|
+
this.pendingResolve = null;
|
|
39573
|
+
this.pendingReject = null;
|
|
39574
|
+
resolve?.(e.data);
|
|
39575
|
+
});
|
|
39576
|
+
this.worker.addEventListener("error", (e) => {
|
|
39577
|
+
const reject = this.pendingReject;
|
|
39578
|
+
this.pendingResolve = null;
|
|
39579
|
+
this.pendingReject = null;
|
|
39580
|
+
reject?.(e.error ?? new Error(e.message));
|
|
39581
|
+
});
|
|
39582
|
+
}
|
|
39583
|
+
init(msg) {
|
|
39584
|
+
return new Promise((resolve, reject) => {
|
|
39585
|
+
this.pendingResolve = resolve;
|
|
39586
|
+
this.pendingReject = reject;
|
|
39587
|
+
this.worker.postMessage(msg);
|
|
39588
|
+
});
|
|
39589
|
+
}
|
|
39590
|
+
processPage(index) {
|
|
39591
|
+
return new Promise((resolve, reject) => {
|
|
39592
|
+
this.pendingResolve = resolve;
|
|
39593
|
+
this.pendingReject = reject;
|
|
39594
|
+
const msg = { type: "page", index };
|
|
39595
|
+
this.worker.postMessage(msg);
|
|
39596
|
+
});
|
|
39597
|
+
}
|
|
39598
|
+
terminate() {
|
|
39599
|
+
this.worker.terminate();
|
|
39600
|
+
}
|
|
39601
|
+
}
|
|
39602
|
+
function workerUrl() {
|
|
39603
|
+
return new URL(import.meta.url.endsWith(".ts") ? "./worker.ts" : "./worker.js", import.meta.url);
|
|
39604
|
+
}
|
|
39605
|
+
function pageResultToResult(msg) {
|
|
39606
|
+
return {
|
|
39607
|
+
a: Jimp.fromBitmap({
|
|
39608
|
+
width: msg.a.width,
|
|
39609
|
+
height: msg.a.height,
|
|
39610
|
+
data: new Uint8Array(msg.a.data),
|
|
39611
|
+
}),
|
|
39612
|
+
b: Jimp.fromBitmap({
|
|
39613
|
+
width: msg.b.width,
|
|
39614
|
+
height: msg.b.height,
|
|
39615
|
+
data: new Uint8Array(msg.b.data),
|
|
39616
|
+
}),
|
|
39617
|
+
diff: Jimp.fromBitmap({
|
|
39618
|
+
width: msg.diff.width,
|
|
39619
|
+
height: msg.diff.height,
|
|
39620
|
+
data: new Uint8Array(msg.diff.data),
|
|
39621
|
+
}),
|
|
39622
|
+
addition: msg.addition,
|
|
39623
|
+
deletion: msg.deletion,
|
|
39624
|
+
modification: msg.modification,
|
|
39625
|
+
};
|
|
39626
|
+
}
|
|
39608
39627
|
async function* visualizeDifferences(a, b, options) {
|
|
39609
|
-
const
|
|
39628
|
+
const merged = {
|
|
39610
39629
|
dpi: options?.dpi ?? defaultOptions.dpi,
|
|
39611
39630
|
alpha: options?.alpha ?? defaultOptions.alpha,
|
|
39612
39631
|
mask: options?.mask ?? defaultOptions.mask,
|
|
@@ -39616,76 +39635,92 @@ async function* visualizeDifferences(a, b, options) {
|
|
|
39616
39635
|
deletion: options?.pallet?.deletion ?? defaultOptions.pallet.deletion,
|
|
39617
39636
|
modification: options?.pallet?.modification ?? defaultOptions.pallet.modification,
|
|
39618
39637
|
},
|
|
39638
|
+
workers: options?.workers ?? defaultOptions.workers,
|
|
39619
39639
|
};
|
|
39620
|
-
const
|
|
39621
|
-
const
|
|
39622
|
-
const
|
|
39623
|
-
? PDFDocument.openDocument(
|
|
39640
|
+
const probe = PDFDocument.openDocument(a, "application/pdf");
|
|
39641
|
+
const probeB = PDFDocument.openDocument(b, "application/pdf");
|
|
39642
|
+
const probeMask = typeof merged.mask !== "undefined"
|
|
39643
|
+
? PDFDocument.openDocument(merged.mask, "application/pdf")
|
|
39624
39644
|
: 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
|
-
|
|
39645
|
+
const maxPages = Math.max(probe.countPages(), probeB.countPages(), probeMask.countPages());
|
|
39646
|
+
probe.destroy();
|
|
39647
|
+
probeB.destroy();
|
|
39648
|
+
probeMask.destroy();
|
|
39649
|
+
if (maxPages === 0)
|
|
39650
|
+
return;
|
|
39651
|
+
const aBytes = asSharedBytes(a);
|
|
39652
|
+
const bBytes = asSharedBytes(b);
|
|
39653
|
+
const maskBytes = typeof merged.mask !== "undefined" ? asSharedBytes(merged.mask) : null;
|
|
39654
|
+
const initMsg = {
|
|
39655
|
+
type: "init",
|
|
39656
|
+
aBytes,
|
|
39657
|
+
bBytes,
|
|
39658
|
+
maskBytes,
|
|
39659
|
+
dpi: merged.dpi,
|
|
39660
|
+
alpha: merged.alpha,
|
|
39661
|
+
pallet: merged.pallet,
|
|
39662
|
+
align: merged.align,
|
|
39663
|
+
};
|
|
39664
|
+
const N = Math.max(1, Math.min(merged.workers, maxPages));
|
|
39665
|
+
const url = workerUrl();
|
|
39666
|
+
const worker0 = new WorkerHandle(url);
|
|
39667
|
+
await worker0.init(initMsg);
|
|
39668
|
+
const buffered = new Map();
|
|
39669
|
+
let nextToAssign = 0;
|
|
39670
|
+
const workers = [worker0];
|
|
39671
|
+
for (let i = 1; i < N; i++) {
|
|
39672
|
+
const w = new WorkerHandle(url);
|
|
39673
|
+
await w.init(initMsg);
|
|
39674
|
+
workers.push(w);
|
|
39675
|
+
}
|
|
39676
|
+
const resolvers = new Map();
|
|
39677
|
+
let workerError = null;
|
|
39678
|
+
const loops = workers.map(async (w) => {
|
|
39679
|
+
while (nextToAssign < maxPages && workerError === null) {
|
|
39680
|
+
const idx = nextToAssign++;
|
|
39681
|
+
try {
|
|
39682
|
+
const msg = await w.processPage(idx);
|
|
39683
|
+
const result = pageResultToResult(msg);
|
|
39684
|
+
const resolve = resolvers.get(idx);
|
|
39685
|
+
if (resolve) {
|
|
39686
|
+
resolvers.delete(idx);
|
|
39687
|
+
resolve(result);
|
|
39688
|
+
}
|
|
39689
|
+
else {
|
|
39690
|
+
buffered.set(idx, result);
|
|
39691
|
+
}
|
|
39692
|
+
}
|
|
39693
|
+
catch (e) {
|
|
39694
|
+
workerError = e;
|
|
39695
|
+
for (const [, resolve] of resolvers)
|
|
39696
|
+
resolve(null);
|
|
39697
|
+
resolvers.clear();
|
|
39698
|
+
return;
|
|
39699
|
+
}
|
|
39700
|
+
}
|
|
39701
|
+
});
|
|
39702
|
+
try {
|
|
39703
|
+
for (let i = 0; i < maxPages; i++) {
|
|
39704
|
+
if (workerError !== null)
|
|
39705
|
+
throw workerError;
|
|
39706
|
+
let r;
|
|
39707
|
+
const buf = buffered.get(i);
|
|
39708
|
+
if (buf !== undefined) {
|
|
39709
|
+
buffered.delete(i);
|
|
39710
|
+
r = buf;
|
|
39711
|
+
}
|
|
39712
|
+
else {
|
|
39713
|
+
r = await new Promise((resolve) => resolvers.set(i, resolve));
|
|
39714
|
+
if (workerError !== null)
|
|
39715
|
+
throw workerError;
|
|
39716
|
+
}
|
|
39717
|
+
yield r;
|
|
39683
39718
|
}
|
|
39684
|
-
|
|
39685
|
-
|
|
39686
|
-
|
|
39687
|
-
|
|
39688
|
-
|
|
39719
|
+
await Promise.all(loops);
|
|
39720
|
+
}
|
|
39721
|
+
finally {
|
|
39722
|
+
for (const w of workers)
|
|
39723
|
+
w.terminate();
|
|
39689
39724
|
}
|
|
39690
39725
|
}
|
|
39691
39726
|
|
|
@@ -39705,7 +39740,7 @@ async function* visualizeDifferences(a, b, options) {
|
|
|
39705
39740
|
* You should have received a copy of the GNU General Public License
|
|
39706
39741
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
39707
39742
|
*/
|
|
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({
|
|
39743
|
+
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
39744
|
allowPositionals: true,
|
|
39710
39745
|
options: {
|
|
39711
39746
|
dpi: { type: "string" },
|
|
@@ -39715,6 +39750,7 @@ const { positionals, values: { dpi: dpi_, alpha: alpha_, mask: mask_, align: ali
|
|
|
39715
39750
|
"addition-color": { type: "string" },
|
|
39716
39751
|
"deletion-color": { type: "string" },
|
|
39717
39752
|
"modification-color": { type: "string" },
|
|
39753
|
+
workers: { type: "string" },
|
|
39718
39754
|
version: { type: "boolean", short: "v" },
|
|
39719
39755
|
help: { type: "boolean", short: "h" },
|
|
39720
39756
|
},
|
|
@@ -39733,8 +39769,17 @@ OPTIONS:
|
|
|
39733
39769
|
--addition-color <#HEX> default: ${formatHex(defaultOptions.pallet.addition)}
|
|
39734
39770
|
--deletion-color <#HEX> default: ${formatHex(defaultOptions.pallet.deletion)}
|
|
39735
39771
|
--modification-color <#HEX> default: ${formatHex(defaultOptions.pallet.modification)}
|
|
39772
|
+
--workers <N> default: ${defaultOptions.workers}
|
|
39736
39773
|
-v, --version
|
|
39737
39774
|
-h, --help
|
|
39775
|
+
|
|
39776
|
+
NOTES:
|
|
39777
|
+
Approximate per-worker memory:
|
|
39778
|
+
a_size_MB + b_size_MB [+ mask_size_MB] (PDF buffers in wasm)
|
|
39779
|
+
+ 300 MB (mupdf + V8 base)
|
|
39780
|
+
+ (dpi / 150)^2 * 50 MB (pixmap working set)
|
|
39781
|
+
The main process adds ~500 MB - 1 GB (varies with --workers).
|
|
39782
|
+
Choose --workers so the total stays under ~80% of available memory.
|
|
39738
39783
|
`);
|
|
39739
39784
|
process.exit(0);
|
|
39740
39785
|
}
|
|
@@ -39782,6 +39827,12 @@ if (additionColor === null ||
|
|
|
39782
39827
|
modificationColor === null) {
|
|
39783
39828
|
throw new Error("Invalid color format");
|
|
39784
39829
|
}
|
|
39830
|
+
const workers = typeof workers_ !== "undefined"
|
|
39831
|
+
? parseInt(workers_, 10)
|
|
39832
|
+
: defaultOptions.workers;
|
|
39833
|
+
if (Number.isNaN(workers) || workers < 1) {
|
|
39834
|
+
throw new Error("Invalid workers value");
|
|
39835
|
+
}
|
|
39785
39836
|
fs.mkdirSync(outDir, { recursive: true });
|
|
39786
39837
|
for await (const [i, { a, b, diff, addition, deletion, modification },] of withIndex(visualizeDifferences(pdfA, pdfB, {
|
|
39787
39838
|
dpi,
|
|
@@ -39793,6 +39844,7 @@ for await (const [i, { a, b, diff, addition, deletion, modification },] of withI
|
|
|
39793
39844
|
deletion: deletionColor,
|
|
39794
39845
|
modification: modificationColor,
|
|
39795
39846
|
},
|
|
39847
|
+
workers,
|
|
39796
39848
|
}), 1)) {
|
|
39797
39849
|
console.log(`Page ${i}, Addition: ${addition.length}, Deletion: ${deletion.length}, Modification: ${modification.length}`);
|
|
39798
39850
|
const dir = path.join(outDir, i.toString(10));
|