Skip to content

Commit 6ba4640

Browse files
AntikodeAntikode
authored andcommitted
fix: redact.js ignored non-zero MediaBox origin, misplacing redaction rects and missing text on such PDFs
addRectBtn's page-size hint tells users coordinates are relative to the page's visible bottom-left corner (origin bottom-left), but redactPdf() passed the raw x/y straight into page.drawRectangle() and redactContentStream() with no adjustment for the page's actual MediaBox origin. For any PDF whose MediaBox doesn't start at (0,0) -- a real, if less common, pattern from scanned/ print-production PDFs, the same precedent that motivated the crop.js/ border.js/watermark.js MediaBox-origin fixes -- the black box landed in the wrong absolute position and the underlying text was not matched/removed, while the tool still reported success. Fixed by capturing each page's box.x/box.y at load time and offsetting the rect before both the text-removal pass and the visual rectangle draw, so the two stay consistent and match what the size hint promises. Reproduced and verified against the real vendored pdf-lib: a text run invisible to the un-offset rect (0 removed) is correctly matched once the offset is applied (1 removed); the common zero-origin case is unaffected.
1 parent 98eb3dd commit 6ba4640

1 file changed

Lines changed: 15 additions & 3 deletions

File tree

‎redact.js‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,14 @@ async function loadFile(file) {
124124
const bytes = await file.arrayBuffer();
125125
const doc = await PDFDocument.load(bytes, { ignoreEncryption: true });
126126
const pageSizes = doc.getPages().map((p) => {
127-
const { width, height } = p.getSize();
128-
return { w: width, h: height, rotation: ((p.getRotation().angle % 360) + 360) % 360 };
127+
const box = p.getMediaBox();
128+
return {
129+
w: box.width,
130+
h: box.height,
131+
x: box.x,
132+
y: box.y,
133+
rotation: ((p.getRotation().angle % 360) + 360) % 360,
134+
};
129135
});
130136
loaded = { file, pageCount: doc.getPageCount(), pageSizes };
131137
renderFile();
@@ -222,8 +228,14 @@ async function redactPdf() {
222228
const byPage = new Map();
223229
for (const r of rects) {
224230
const idx = r.page - 1;
231+
// User-entered x/y are relative to the page's visible bottom-left corner (as shown
232+
// in the page-size hint), but pdf-lib's drawing/content-stream coordinates are in
233+
// absolute PDF user space. For a PDF whose MediaBox has a non-zero origin (common in
234+
// scanned or print-production files), these differ — offset by the box's own x/y so
235+
// the redacted rectangle (and the text it removes) lands where the user intended.
236+
const { x: boxX, y: boxY } = loaded.pageSizes[idx];
225237
if (!byPage.has(idx)) byPage.set(idx, []);
226-
byPage.get(idx).push([r.x, r.y, r.x + r.w, r.y + r.h]);
238+
byPage.get(idx).push([r.x + boxX, r.y + boxY, r.x + r.w + boxX, r.y + r.h + boxY]);
227239
}
228240

229241
let totalRemoved = 0;

0 commit comments

Comments
 (0)