-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathredact.html
More file actions
322 lines (303 loc) · 16.3 KB
/
Copy pathredact.html
File metadata and controls
322 lines (303 loc) · 16.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#0d0f0c" />
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no" />
<link rel="preload" href="vendor/fonts/ibmplexmono-400.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="vendor/fonts/fraunces-normal-300.woff2" as="font" type="font/woff2" crossorigin />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self' https://api.counterapi.dev https://formsubmit.co; object-src 'none'; base-uri 'self'; form-action 'self' https://formsubmit.co;" />
<meta name="referrer" content="strict-origin-when-cross-origin" />
<title>Redact part of a PDF page without uploading it — ClientPDF</title>
<meta name="description" content="Draw a box over sensitive text and ClientPDF removes the underlying text, then blacks out the area — entirely in your browser. Free, fast, private." />
<link rel="canonical" href="https://abyworkings-coder.github.io/clientpdf/redact.html" />
<meta property="og:type" content="website" />
<meta property="og:title" content="Redact part of a PDF page without uploading it — ClientPDF" />
<meta property="og:description" content="Draw a box over sensitive text and ClientPDF removes the underlying text from the file's content stream, then draws a black box over the area. Nothing is uploaded, ever." />
<meta property="og:url" content="https://abyworkings-coder.github.io/clientpdf/redact.html" />
<meta property="og:site_name" content="ClientPDF" />
<meta property="og:image" content="https://abyworkings-coder.github.io/clientpdf/og-image.jpg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Redact part of a PDF page without uploading it — ClientPDF" />
<meta name="twitter:description" content="Draw a box over sensitive text — the text runs are removed from the file, not just covered." />
<meta name="twitter:image" content="https://abyworkings-coder.github.io/clientpdf/og-image.jpg" />
<link rel="icon" href="./logo.svg" type="image/svg+xml" />
<link rel="icon" href="./favicon-32.png" sizes="32x32" type="image/png" />
<link rel="icon" href="./favicon-16.png" sizes="16x16" type="image/png" />
<link rel="apple-touch-icon" href="./apple-touch-icon.png" />
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "ClientPDF Redact",
"url": "https://abyworkings-coder.github.io/clientpdf/redact.html",
"applicationCategory": "UtilitiesApplication",
"operatingSystem": "Any (runs in browser)",
"description": "Remove text runs that overlap a rectangle you draw on a PDF page, then black out the area, entirely in your browser. Nothing is uploaded, ever.",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://abyworkings-coder.github.io/clientpdf/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Tools",
"item": "https://abyworkings-coder.github.io/clientpdf/tools.html"
},
{
"@type": "ListItem",
"position": 3,
"name": "Redact",
"item": "https://abyworkings-coder.github.io/clientpdf/redact.html"
}
]
}
</script>
<script type="application/ld+json">{
"@context": "https://schema.org",
"@type": "HowTo",
"name": "How to redact part of a PDF page online without uploading it",
"description": "Remove sensitive text from a PDF's content stream and black out the area, entirely in your browser.",
"datePublished": "2026-08-02",
"dateModified": "2026-08-04",
"step": [
{
"@type": "HowToStep",
"position": 1,
"name": "Drop one PDF",
"text": "Drag and drop a single PDF file onto the page, or click to browse."
},
{
"@type": "HowToStep",
"position": 2,
"name": "Note the page size shown",
"text": "ClientPDF shows each page's width and height in points, measured from the bottom-left corner, so you know the coordinate space you're drawing in."
},
{
"@type": "HowToStep",
"position": 3,
"name": "Enter a page number and x, y, width, height in points",
"text": "Click Add area to queue a rectangle covering the text you want gone. Add as many rectangles as you need, across any pages."
},
{
"@type": "HowToStep",
"position": 4,
"name": "Click Redact",
"text": "ClientPDF tokenizes each affected page's content stream, drops every Tj/TJ text-show operator whose estimated bounding box overlaps a rectangle, and draws a solid black box over the same area."
},
{
"@type": "HowToStep",
"position": 5,
"name": "Download the redacted PDF",
"text": "The file is rewritten and downloaded straight from this tab, with the matched text removed and the area blacked out."
}
]
}</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Does this actually remove the text, or just draw a black box over it?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Both. Any text drawn with the Tj, TJ, ', or \" operators whose estimated position overlaps your rectangle is deleted from the page's content stream — the bytes no longer exist in the saved file. A black rectangle is then drawn over the same area so non-text content, like scanned images or vector art, is also visually covered, even though it isn't removed."
}
},
{
"@type": "Question",
"name": "Will this catch rotated or non-horizontal text?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Not reliably. The bounding-box estimate used to decide whether a text run overlaps your rectangle assumes a roughly horizontal text matrix. Axis-aligned text on a normal page is handled correctly; text rotated by a non-trivial angle may be under- or over-matched. If precision matters, zoom in and check the result before relying on it."
}
},
{
"@type": "Question",
"name": "Does this remove images or vector graphics under the box?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. Only text drawn via Tj/TJ text-show operators is removed from the file. Embedded images, scanned pages, and vector graphics under your rectangle are covered by the black box but not deleted — they're still present in the underlying file structure. If the sensitive content is a photo or a scan rather than selectable text, this tool visually hides it but does not scrub it."
}
},
{
"@type": "Question",
"name": "Is this safe to use for legally sensitive redaction?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Treat it as a strong first pass, not a certified redaction tool. It correctly removes matched text runs and covers the area, but the bounding-box estimate is conservative rather than pixel-exact, embedded images aren't scrubbed, and metadata or hidden layers elsewhere in the file are untouched. For court filings, FOIA responses, or anything with real legal exposure, verify the output by opening it in a text-extraction tool afterward and confirm nothing sensitive remains selectable."
}
}
]
}
</script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main-content">Skip to main content</a>
<noscript><div class="noscript-banner">ClientPDF requires JavaScript — all files are processed locally in your browser and never uploaded. Please enable JavaScript to use these tools.</div></noscript>
<div class="grain"></div>
<header class="topbar">
<div class="wrap topbar-inner">
<div class="brand">
<span class="brand-mark">◆</span>
<span class="brand-name">Client<em>PDF</em></span>
</div>
<nav class="toolnav" aria-label="Tools">
<a href="./index.html">Merge</a>
<a href="./split.html">Split</a>
<a href="./delete-pages.html">Delete Pages</a>
<a href="./duplicate-pages.html">Duplicate Pages</a>
<a href="./insert-pages.html">Insert Blank Pages</a>
<a href="./rotate.html">Rotate</a>
<a href="./reorder.html">Reorder</a>
<a href="./compress.html">Compress</a>
<a href="./page-numbers.html">Page Numbers</a>
<a href="./images-to-pdf.html">Images to PDF</a>
<a href="./extract-images.html">Extract Images</a>
<a href="./pdf-to-jpg.html">PDF to JPG</a>
<a href="./watermark.html">Watermark</a>
<a href="./metadata.html">Metadata</a>
<a href="./crop.html">Crop Pages</a>
<a href="./resize.html">Resize Pages</a>
<a href="./grayscale.html">Grayscale PDF</a>
<a href="./border.html">Page Borders</a>
<a href="./image-watermark.html">Image Watermark</a>
<a href="./flatten.html">Flatten Form Fields</a>
<a href="./fill-form.html">Fill Form Fields</a>
<a href="./bookmarks.html">Add Bookmarks</a>
<a href="./split-by-bookmarks.html">Split by Bookmarks</a>
<a href="./pages-per-sheet.html">Pages per Sheet</a>
<a href="./redact.html" aria-current="page">Redact</a>
<a href="./tools.html">All Tools</a>
<a href="./guide.html">Guide</a>
</nav>
<div class="topbar-status">
<span class="dot" aria-hidden="true"></span>
<span>running locally in this tab — nothing uploaded</span>
</div>
</div>
</header>
<main class="wrap" id="main-content" tabindex="-1">
<section class="hero">
<h1>Open DevTools.<br />Black out a name, a number, a paragraph.<br />Watch nothing get sent anywhere.</h1>
<p class="lede">
Draw a rectangle in page coordinates and ClientPDF removes the text
runs underneath it from the file's own content stream, then draws a
black box over the area — entirely inside this tab. Zero network
requests after the page loads, same rule as every other tool here.
Check the Network tab yourself.
</p>
</section>
<section class="tool" id="tool">
<div class="dropzone" id="dropzone" tabindex="0" role="button" aria-label="Add a PDF file">
<input type="file" id="fileInput" accept="application/pdf" hidden />
<div class="dropzone-inner">
<span class="dropzone-icon">⇣</span>
<p><strong>Drop one PDF here</strong> or click to browse</p>
<p class="dropzone-hint">One file at a time · stays on this device</p>
</div>
</div>
<div class="split-panel" id="redactPanel" hidden>
<div class="split-meta" id="redactMeta"></div>
<div class="split-range" id="addRow">
<label for="pageInput">Page</label>
<input type="text" id="pageInput" value="1" inputmode="numeric" class="field-narrow" />
<label for="xInput">x (pt)</label>
<input type="text" id="xInput" placeholder="e.g. 72" inputmode="decimal" />
<label for="yInput">y (pt)</label>
<input type="text" id="yInput" placeholder="e.g. 700" inputmode="decimal" />
<label for="wInput">width (pt)</label>
<input type="text" id="wInput" placeholder="e.g. 200" inputmode="decimal" />
<label for="hInput">height (pt)</label>
<input type="text" id="hInput" placeholder="e.g. 20" inputmode="decimal" />
<button class="btn btn-ghost" id="addRectBtn" type="button">Add area</button>
</div>
<p class="dropzone-hint" id="pageSizeHint"></p>
<p class="dropzone-hint">
Honest note: only text drawn with the Tj/TJ text-show operators is
removed from the file — a solid black box is drawn over every area
regardless, but images, scans, and vector graphics under the box are
only visually covered, not deleted. Bounding-box matching assumes
roughly horizontal, non-rotated text; rotated text may be under- or
over-matched. Coordinates are in PDF points, origin at each page's
bottom-left corner — use the page size shown above once a page
number is entered.
</p>
</div>
<ol class="file-list" id="rectList" aria-label="Areas queued for redaction"></ol>
<ol class="file-list" id="fileList" aria-label="Loaded file"></ol>
<div class="actions" id="actions" hidden>
<button class="btn btn-ghost" id="clearBtn" type="button">Clear</button>
<button class="btn btn-primary" id="redactBtn" type="button" disabled>
<span id="redactBtnLabel">Redact</span>
</button>
</div>
<div class="result" id="result" role="status" aria-live="polite" hidden></div>
</section>
<section class="proof">
<h2 class="sr-only">Why trust this tool</h2>
<div class="proof-live" id="proofLive">
<span class="proof-live-dot" aria-hidden="true"></span>
<span id="proofLiveText">0 network requests since page load · verified live, not our word for it</span>
</div>
<div class="proof-grid">
<div class="proof-item">
<h3>No accounts</h3>
<p>No signup, no email, no watermark. Use it and close the tab.</p>
</div>
<div class="proof-item">
<h3>No limits (for now)</h3>
<p>Queue as many redaction areas, across as many pages, as you need. No artificial caps.</p>
</div>
</div>
</section>
<section class="pro" id="pro">
<div class="pro-card">
<div class="pro-copy">
<h2>Cropping margins or deleting whole pages too? Both live too.</h2>
<p><a href="./crop.html">Trim a page's visible margins</a> or <a href="./delete-pages.html">remove entire pages</a> — same rule: everything stays local. Leave your email and we'll let you know about a Pro batch mode.</p>
</div>
<form class="pro-form" id="proForm">
<input type="email" id="proEmail" placeholder="you@company.com" required aria-label="Email address" autocomplete="email" />
<button class="btn btn-primary" type="submit">Notify me</button>
</form>
<p class="pro-note" id="proNote" role="status" aria-live="polite" hidden>Thanks — saved locally for now, we'll be in touch once signups are live.</p>
</div>
</section>
<section class="about">
<h2>Removing the text, not just hiding it</h2>
<p>Redact tokenizes each affected page's own content stream — a real parser that tracks the text matrix, current transformation matrix, font size, and line leading operator by operator — and drops every Tj, TJ, ', and " text-show operator whose estimated bounding box overlaps a rectangle you drew. Matched text is deleted from the stream before the file is saved; it isn't merely covered. A solid black rectangle is then drawn over the same area for every match, so non-text content — images, scans, vector art — is also visually covered even though it isn't removed from the file.</p>
<p>Worth knowing before you rely on this for anything sensitive: the bounding-box estimate is a deliberate overestimate based on average character width for the active font size, tuned to remove slightly too much text rather than leave a partial word behind — it assumes roughly horizontal, non-rotated text, so rotated or heavily skewed text may not line up with your rectangle. And only Tj/TJ-drawn text is actually deleted; an image of a document (a scan) has no such text to find, so the black box is the only protection you get there. If you just need to remove entire pages rather than part of one, <a href="./delete-pages.html">Delete Pages</a> is the more direct tool, and if you only need to hide a margin without touching content, <a href="./crop.html">Crop</a> does that — though as that page notes, cropping doesn't delete anything either.</p>
</section>
</main>
<footer class="footer">
<div class="wrap footer-inner">
<span>ClientPDF — built by <a href="https://github.com/abyworkings-coder" target="_blank" rel="noopener">Auto Company</a></span>
<span>No servers were harmed in the making of this tool.</span>
<span class="footer-legal"><a href="./privacy.html">Privacy</a> · <a href="./terms.html">Terms</a></span>
</div>
</footer>
<script src="./counter.js"></script>
<script type="module" src="redact.js"></script>
</body>
</html>