mash.pdf · Files & Storage
mash.pdf reads the text out of a PDF, blacks out sensitive content for good, splits a long PDF into several, and builds new PDFs. Use text when you need to look at what a document says. Use redact before you share something. Use the split methods for a scanned batch where each document starts with a recognisable header or footer.
How it works
Section titled “How it works”text reads the text layer. It returns the text with a form feed character (\f) after each page. A PDF that is only a picture of text, such as a scan, has no text layer, so text comes back empty. Captur’s OCR (mash.ai.ocr) is the way in for those.
redact removes content, it doesn’t cover it. Redacted text is deleted from the page and can’t be copied out afterwards. Give it phrases, areas or both. Phrases match exactly and ignore case, against the text layer only. An area is a box on a page, burnt out whatever lies underneath, including the pixels of a scanned image. It returns a dictionary:
| Key | Meaning |
|---|---|
file | the redacted copy, held in temporary storage |
redacted | how many places were blanked |
found | the phrases that matched |
missing | the phrases that didn’t |
scanned_pages | pages with no text layer, which phrases can’t search |
redact writes nothing to a share. You save file yourself with a folder’s write, so the run’s permissions apply.
Areas count from the page’s top-left. An area is {page, rect: [x0, y0, x1, y1]}. Pages start at 1. The units are points (1/72 inch) unless you say unit: "mm" or give a dpi for pixel positions.
Splitting uses a custom field. splitOnHeader and splitOnFooter cut a PDF where a field’s text zone changes (or, with discard, drop the pages that don’t match). They give a list of parts, each with its own file.
Common tasks
Section titled “Common tasks”Count a PDF’s pages and search it:
set pdf to mash.files.get("Contracts", "Inbox/agreement.pdf")set text to mash.pdf.text(pdf)set pages to mash.str(text).split("\f")printline "{{count(pages) - 1}} pages"if mash.str(text).containsAny(["termination", "Termination"]) printline "Mentions termination"endRedact names and save a copy:
set pdf to mash.files.get("Contracts", "Inbox/agreement.pdf")set result to mash.pdf.redact(pdf, ["Jane Smith", "07700 900123"])if count(result["scanned_pages"]) is more than 0 fail("Some pages are scans and can't be searched. A person needs to look.")endset out to mash.files.get("Contracts", "Redacted")out.write(result["file"].name, result["file"])printline "Blanked {{result['redacted']}} places"Black out a box on page 1, such as a signature:
set result to mash.pdf.redact(pdf, [], [ {page: 1, rect: [50, 700, 250, 760]}])Watch out for
Section titled “Watch out for”- A scan isn’t searched. Check
scanned_pagesand decide what to do. A copy that looks redacted but wasn’t is worse than no copy. - Phrases of one character are ignored, and a call takes at most 200 phrases and 500 areas.
redactalways returns a copy, even when nothing matched, so a script that shares “the redacted version” never falls back to the original by mistake. Checkredactedto see whether anything was blanked.- The page count is one less than the pieces.
textends each page with a form feed, so splitting on it leaves an empty piece at the end. - Only PDFs, and not huge ones. A file that isn’t a PDF, or is over the size limit, is an error.
See also
Section titled “See also”Files and folders, and mash.files for the same split calls.
Properties
Section titled “Properties”| Name | Type | Description |
|---|---|---|
tmpManager |
Methods
Section titled “Methods”builder
Section titled “builder”pdf.builder(orientation?: mixed = "P", unit?: mixed = "mm", size?: mixed = "A4")
Create a new PDF builder (mPDF)
redact
Section titled “redact”pdf.redact(file: File, phrases: array, areas?: array = [])
Redact phrases and/or areas from a PDF: removed from the page, not covered. areas: [{page, rect: [x0, y0, x1, y1], unit?: “pt”|“mm”, dpi?}], from the page’s top-left. Returns { file, redacted, found, missing, scanned_pages }; write file where you want it.
Same engine as Grace’s redaction (PdfRedactor: mupdf text search, then mutool run redact.js). Matching is exact and case-insensitive against the PDF’s TEXT LAYER, so a scanned page cannot be searched here: its page numbers come back in scanned_pages and the script must decide (refuse, or route to a person) rather than treat it as clean.
An area is burned whatever lies under it: text, vector drawing, and the PIXELS of any image it overlaps (mupdf rewrites the image with that region blanked, so extracting the image afterwards does not recover it). That is the way to scrub a scanned page, a photo or a signature. Coordinates are on the page as displayed (rotation applied), measured from its top-left corner: PDF points by default (1/72 in), unit: "mm", or dpi: 300 for pixel positions on a page rendered at that resolution, which is what OCR word boxes are.
Nothing is written to storage. The redacted copy is a TmpFile and the script saves it through the ordinary write path, which carries the run’s permissions; this method only reads a file the script already holds.
splitOnFooter
Section titled “splitOnFooter”pdf.splitOnFooter(file: File, zone: mixed, onChange?: mixed = false, discard?: mixed = false) → array
Split a PDF on footer zone matches.
splitOnHeader
Section titled “splitOnHeader”pdf.splitOnHeader(file: File, zone: mixed, onChange?: mixed = false, discard?: mixed = false) → array
Split a PDF on header zone matches.
pdf.text(file: File) → string
The PDF’s text layer, pages separated by a form feed (“\f”). The same text pdf.redact() searches, so a phrase copied out of it will match.