Profiles / Documents & integration
PD

PDF & document automation specialist

Generate invoices and reports, merge and split dossiers, watermark, protect, fill forms and extract text.

10 recipes on this page · open in the interactive view

Recipes
  1. A4 invoice with a table, totals and page numbers
  2. Assemble an audit dossier, then split it for the reviewers
  3. Watermark and password-protect a contract
  4. Fill and flatten a PDF form
  5. KPI one-pager with a hand-drawn chart and a thumbnail
  6. Training certificate in A4 landscape with a seal
  7. Shrink a scanned dossier before archiving
  8. Stamp a PDF received in base64 from an API
  9. Monthly statements: one file, then one PDF per customer
  10. Archive compliance: check and fix the PDF metadata
executed on MentDB

A4 invoice with a table, totals and page numbers

A tool shop issues invoice F-2026-118 with three lines. The script draws the header band, the lines table, computes the VAT and the total, right-aligns the total in a box sized with the text width, adds a payment link and the page numbers, saves the file and reads its text back to check it.

Pipeline
Script
admin
#1. New A4 document with its metadata;
pdf create "rx_inv";
pdf info set "rx_inv" "title=Invoice F-2026-118;author=Acme Tools;subject=Invoice";
pdf page add "rx_inv" "A4" "portrait";
#2. Header band, customer and lines;
pdf draw rect "rx_inv" 1 0 0 595 90 "fill=#1E5AA0";
pdf text add "rx_inv" 1 40 32 "ACME TOOLS" "font=HELVETICA_BOLD;size=22;color=#FFFFFF";
pdf text add "rx_inv" 1 555 36 "INVOICE F-2026-118" "font=HELVETICA_BOLD;size=13;color=#FFFFFF;align=right";
pdf text add "rx_inv" 1 40 110 "Bill to: Garage Hoarau, 12 rue de Paris, 97400 Saint-Denis" "size=10";
json load "rx_pos" (pdf table add "rx_inv" 1 40 135 "{\"columns\":[\"Item\",{\"title\":\"Qty\",\"align\":\"right\",\"width\":50},{\"title\":\"Unit\",\"align\":\"right\",\"width\":70},{\"title\":\"Amount\",\"align\":\"right\",\"width\":80}],\"rows\":[[\"Cordless drill 18V\",\"2\",\"129.00\",\"258.00\"],[\"HSS bits set\",\"5\",\"12.90\",\"64.50\"],[\"Safety glasses\",\"10\",\"4.20\",\"42.00\"]]}" "zebra=#F2F5FA;width=515;header_bg=#1E5AA0");
#3. VAT 20 percent and total in a box sized to the text;
-> "[net]" (+ 258 64.5 42);
-> "[total]" (concat "Total incl. VAT: " (math decimal_format (* [net] 1.2) "0.00" "." " ") " EUR");
-> "[w]" (pdf text width "rx_inv" [total] "font=HELVETICA_BOLD;size=12");
-> "[y]" (+ (json select "rx_pos" "/y") 15);
pdf draw rect "rx_inv" 1 (- 549 [w]) [y] (+ [w] 12) 24 "fill=#FFF4D6;stroke=#E0A800;radius=4";
pdf text add "rx_inv" 1 549 (+ [y] 6) [total] "font=HELVETICA_BOLD;size=12;align=right";
#4. Payment link, footer line and page numbers;
pdf text add "rx_inv" 1 40 (+ [y] 50) "Pay online: https://pay.example.com/F-2026-118" "size=10;color=#1E5AA0;underline=true";
pdf link add "rx_inv" 1 40 (+ [y] 50) 260 14 "https://pay.example.com/F-2026-118";
pdf draw line "rx_inv" 1 40 800 555 800 "color=#CCCCCC;line_width=1";
pdf page_number add "rx_inv" "Page {page} / {total}" 555 810 "align=right;size=9";
pdf save "rx_inv" "tmp/rx_invoice.pdf";
pdf close "rx_inv";
#5. Check the file;
concat "pages: " (pdf page_count_file "tmp/rx_invoice.pdf") [_n_] (pdf extract_text "tmp/rx_invoice.pdf");
Result
mentdb
pages: 1
ACME TOOLS INVOICE F-2026-118
Bill to: Garage Hoarau, 12 rue de Paris, 97400 Saint-Denis
Item Qty Unit Amount
Cordless drill 18V 2 129.00 258.00
HSS bits set 5 12.90 64.50
Safety glasses 10 4.20 42.00
Total incl. VAT: 437.40 EUR
Pay online: https://pay.example.com/F-2026-118
Page 1 / 1
Go further
executed on MentDB

Assemble an audit dossier, then split it for the reviewers

An auditor renders three chapters from HTML and merges them with a draft page. The script removes the draft, appends an appendix page, rotates it, adds bookmarks, then splits the dossier into one file per page and extracts the findings into a separate file.

Pipeline
Script
admin
#1. Chapters rendered from HTML;
pdf from html "<html><body><h1>1. Scope</h1><p>Purchase-to-pay process, fiscal year 2026.</p></body></html>" "tmp/rx_ch1.pdf";
pdf from html "<html><body><h1>DRAFT NOTES</h1><p>To remove.</p></body></html>" "tmp/rx_draft.pdf";
pdf from html "<html><body><h1>2. Findings</h1><p>3 invoices paid twice, 1 order without PO.</p></body></html>" "tmp/rx_ch2.pdf";
pdf from html "<html><body><h1>Appendix A</h1><p>Sample of 40 invoices.</p></body></html>" "tmp/rx_appx.pdf";
#2. Merge, remove the draft page, append and rotate the appendix;
pdf merge "[\"tmp/rx_ch1.pdf\",\"tmp/rx_draft.pdf\",\"tmp/rx_ch2.pdf\"]" "tmp/rx_audit.pdf";
pdf load "rx_aud" "tmp/rx_audit.pdf";
pdf page delete "rx_aud" 2;
-> "[p]" (pdf page import "rx_aud" "tmp/rx_appx.pdf" 1);
pdf page rotate "rx_aud" [p] 90;
#3. Bookmarks for the reader outline;
pdf bookmark add "rx_aud" "1. Scope" 1;
pdf bookmark add "rx_aud" "2. Findings" 2;
pdf bookmark add "rx_aud" "Appendix A" [p];
-> "[n]" (pdf page count "rx_aud");
json load "rx_ps" (pdf page size "rx_aud" [p]);
pdf save "rx_aud" "tmp/rx_audit.pdf";
pdf close "rx_aud";
#4. One file per page, and the findings alone;
json load "rx_parts" (pdf split "tmp/rx_audit.pdf" "tmp/rx_parts");
pdf extract_pages "tmp/rx_audit.pdf" "tmp/rx_findings.pdf" 2 2;
concat "{\"pages\": " [n] ", \"appendix_rotation\": " (json select "rx_ps" "/rotation") ", \"split_files\": " (json count "rx_parts" "/") ", \"findings\": \"" (string replace (string trim (pdf extract_text "tmp/rx_findings.pdf")) [_n_] " | ") "\"}";
Result
mentdb
{"pages": 3, "appendix_rotation": 90, "split_files": 3, "findings": "2. Findings | 3 invoices paid twice, 1 order without PO."}
Go further
  • Split every N pages with pdf split <source> <directory> <pagesPerFile>
  • Use pdf extract_text <source> <from> <to> to index each chapter in a search engine
executed on MentDB

Watermark and password-protect a contract

A legal team sends a contract to a client: it must be marked CONFIDENTIAL, open only with a password, and forbid copy and modification. The script watermarks the PDF, protects it with AES-256, re-opens it with the user password to check the encryption, then removes the protection with the owner password.

Pipeline
Script
admin
#1. The contract and its watermark;
pdf from html "<html><body><h2>Service agreement 2026-044</h2><p>Between Acme Tools and Garage Hoarau. Duration 24 months, fee 1 200 EUR per month.</p></body></html>" "tmp/rx_contract.pdf";
pdf load "rx_c" "tmp/rx_contract.pdf";
pdf watermark add "rx_c" "CONFIDENTIAL" "color=#C00000;opacity=0.12;size=70;angle=45";
pdf save "rx_c" "tmp/rx_contract.pdf";
pdf close "rx_c";
#2. Protect: user password to open, no copy, no modification;
pdf protect "tmp/rx_contract.pdf" "tmp/rx_contract_sec.pdf" "Legal-Owner-26" "Client-7Q4" "print=true;copy=false;modify=false";
#3. Open it as the client would;
pdf load "rx_s" "tmp/rx_contract_sec.pdf" "Client-7Q4";
json load "rx_info" (pdf info get "rx_s");
-> "[loaded]" (concat "{\"exist\": " (pdf exist "rx_s") ", \"pdfs\": " (string regexp_replace (pdf show) "\s" "") "}");
pdf close "rx_s";
#4. The owner removes the protection, then shares a read-only copy without password;
pdf unprotect "tmp/rx_contract_sec.pdf" "tmp/rx_contract_open.pdf" "Legal-Owner-26";
pdf protect "tmp/rx_contract_open.pdf" "tmp/rx_contract_ro.pdf" "Legal-Owner-26" "";
concat "{\"encrypted\": " (json select "rx_info" "/encrypted") ", \"pages\": " (json select "rx_info" "/pages") ", \"loaded\": " [loaded] ", \"title_after_unprotect\": \"" (string regexp_extract (pdf extract_text "tmp/rx_contract_open.pdf" 1 1) "(Service agreement [0-9-]+)" 1) "\", \"read_only_pages\": " (pdf page_count_file "tmp/rx_contract_ro.pdf") "}";
Result
mentdb
{"encrypted": true, "pages": 1, "loaded": {"exist": 1, "pdfs": ["rx_s"]}, "title_after_unprotect": "Service agreement 2026-044", "read_only_pages": 1}
Go further
  • Allow only printing and form filling with pdf protect ... "print=true;fill_in=true;copy=false;modify=false"
  • Generate a strong password per client with string generate_random_str and send it by another channel
executed on MentDB

Fill and flatten a PDF form

A supplier registration arrives as a fillable PDF (AcroForm). For the demo, the script first writes a tiny two-field form; then it lists the fields, fills them, flattens the form so it cannot be edited, saves it, and returns it as base64 ready for an API.

Pipeline
Script
admin
#1. A minimal fillable PDF with two text fields, for the demo;
file create "tmp/rx_form.pdf" (concat "%PDF-1.4" [_n_] "1 0 obj <</Type/Catalog/Pages 2 0 R/AcroForm <</Fields[4 0 R 6 0 R]/DA(/Helv 0 Tf 0 g)/DR<</Font<</Helv 5 0 R>>>>>>>> endobj" [_n_] "2 0 obj <</Type/Pages/Kids[3 0 R]/Count 1>> endobj" [_n_] "3 0 obj <</Type/Page/Parent 2 0 R/MediaBox[0 0 595 842]/Annots[4 0 R 6 0 R]>> endobj" [_n_] "4 0 obj <</Type/Annot/Subtype/Widget/FT/Tx/T(company)/Rect[100 700 400 720]/P 3 0 R/DA(/Helv 12 Tf 0 g)>> endobj" [_n_] "6 0 obj <</Type/Annot/Subtype/Widget/FT/Tx/T(siret)/Rect[100 660 400 680]/P 3 0 R/DA(/Helv 12 Tf 0 g)>> endobj" [_n_] "5 0 obj <</Type/Font/Subtype/Type1/BaseFont/Helvetica>> endobj" [_n_] "trailer <</Root 1 0 R>>" [_n_] "%%EOF");
#2. Load it and list the fields;
pdf load "rx_form" "tmp/rx_form.pdf";
json load "rx_fields" (pdf form fields "rx_form");
#3. Fill, flatten and save;
-> "[filled]" (pdf form fill "rx_form" "{\"company\":\"Garage Hoarau SARL\",\"siret\":\"812 345 678 00021\"}");
pdf form flatten "rx_form";
pdf save "rx_form" "tmp/rx_form_done.pdf";
#4. Base64 for an API, reloaded to check it;
-> "[b64]" (pdf to_base64 "rx_form");
pdf load_base64 "rx_copy" [b64];
-> "[copy_fields]" (json load "rx_f2" (pdf form fields "rx_copy"));
pdf close_all;
concat "{\"fields\": [\"" (json select "rx_fields" "/[0]/name") "\", \"" (json select "rx_fields" "/[1]/name") "\"], \"type\": \"" (json select "rx_fields" "/[0]/type") "\", \"filled\": " [filled] ", \"fields_after_flatten\": " (json count "rx_f2" "/") ", \"base64_starts\": \"" (string left [b64] 8) "\", \"text\": \"" (string replace (string trim (pdf extract_text "tmp/rx_form_done.pdf")) [_n_] " | ") "\"}";
Result
mentdb
{"fields": ["company", "siret"], "type": "TextField", "filled": 2, "fields_after_flatten": 0, "base64_starts": "JVBERi0x", "text": "Garage Hoarau SARL | 812 345 678 00021"}
Go further
executed on MentDB

KPI one-pager with a hand-drawn chart and a thumbnail

A sales manager wants a one-page A5 report: a paragraph of comment, a line chart of monthly orders drawn with polygons and circles, and the logo. The script maps the values to page coordinates, draws the chart, saves the PDF, renders a PNG thumbnail and tests the image compression.

Pipeline
Script
admin
#1. A5 landscape page, a TrueType font and the comment;
pdf create "rx_kpi";
pdf page add "rx_kpi" "A5" "landscape";
pdf font load "rx_kpi" "mono" "MentDB_Weak_Editor_GPLv3/tools/JetBrainsMono-Regular.ttf";
json load "rx_size" (pdf page size "rx_kpi" 1);
json load "rx_fonts" (pdf font show "rx_kpi");
pdf image add "rx_kpi" 1 "images/info.png" 30 25 0 24;
pdf text add "rx_kpi" 1 62 30 "Orders per month - H1 2026" "font=HELVETICA_BOLD;size=16";
json load "rx_end" (pdf text paragraph "rx_kpi" 1 30 65 530 "Orders grew by 62 percent between January and May, then slowed down in June after the price change." "font=mono;size=10;align=justify");
#2. Map the values to page coordinates, y grows downwards;
json load "rx_v" "[42, 55, 49, 63, 68, 61]";
-> "[pts]" "";
-> "[i]" 0;
json parse_array "rx_v" "/" "[v]" {
	-> "[x]" (+ 60 (* [i] 90));
	-> "[y]" (- 380 (* [v] 3.5));
	-> "[pts]" (concat [pts] " " [x] "," [y]);
	pdf draw circle "rx_kpi" 1 [x] [y] 4 "fill=#1E5AA0";
	pdf text add "rx_kpi" 1 [x] (- [y] 18) [v] "size=9;align=center";
	-> "[i]" (+ [i] 1);
};
#3. Axis and curve;
pdf draw line "rx_kpi" 1 50 380 530 380 "color=#999999;line_width=1";
pdf draw polygon "rx_kpi" 1 (string trim [pts]) "stroke=#1E5AA0;line_width=2;close=false";
pdf save "rx_kpi" "tmp/rx_kpi.pdf";
pdf close "rx_kpi";
#4. Thumbnail and compression test;
pdf to_image "tmp/rx_kpi.pdf" 1 50 "tmp/rx_kpi.png";
pdf compress_image "tmp/rx_kpi.pdf" "tmp/rx_kpi_small.pdf" 72;
concat "{\"page_width\": " (math round (json select "rx_size" "/width") 0) ", \"page_height\": " (math round (json select "rx_size" "/height") 0) ", \"points\": \"" (string trim [pts]) "\", \"after_paragraph_y\": " (json select "rx_end" "/y") ", \"fonts\": " (json count "rx_fonts" "/") ", \"thumbnail_png\": " (file exist "tmp/rx_kpi.png") ", \"compressed_is_image_only\": \"" (string trim (pdf extract_text "tmp/rx_kpi_small.pdf")) "\"}";
Result
mentdb
{"page_width": 595, "page_height": 420, "points": "60,233 150,187.5 240,208.5 330,159.5 420,142 510,166.5", "after_paragraph_y": 90.0, "fonts": 15, "thumbnail_png": 1, "compressed_is_image_only": ""}
Go further
executed on MentDB

Training certificate in A4 landscape with a seal

Produce the completion certificate of a safety training: an A4 landscape page with a double frame, a round seal and a justified paragraph, then check the page geometry and the metadata before sending it.

Pipeline
Script
admin
#1. A4 landscape page with a double frame;
pdf create "rx_p4i_cert";
pdf info set "rx_p4i_cert" "title=Certificate SST-2026-0457;author=Training Center Est;subject=Workplace first aid;keywords=SST,training,certificate";
-> "[p]" (pdf page add "rx_p4i_cert" "A4" "landscape");
pdf draw rect "rx_p4i_cert" [p] 20 20 802 555 "stroke=#1E5AA0;line_width=3";
pdf draw rect "rx_p4i_cert" [p] 30 30 782 535 "stroke=#C9A227;line_width=1;dash=4 2";
#2. Title, trainee and a justified paragraph;
pdf text add "rx_p4i_cert" [p] 421 80 "CERTIFICATE OF COMPLETION" "font=TIMES_BOLD;size=30;color=#1E5AA0;align=center";
pdf text add "rx_p4i_cert" [p] 421 150 "Marie Lauret" "font=TIMES_BOLD_ITALIC;size=26;align=center";
json load "rx_p4i_pos" (pdf text paragraph "rx_p4i_cert" [p] 120 210 600 "has successfully completed the Workplace First Aid training, 14 hours, from 13 to 14 October 2026, and passed the practical assessment on cardiac arrest, bleeding and choking. This certificate is valid for 24 months." "font=TIMES_ROMAN;size=14;align=justify;line_height=1.4");
#3. Round gold seal at the bottom right, signature line at the left;
pdf draw circle "rx_p4i_cert" [p] 700 470 55 "fill=#C9A227;opacity=0.85";
pdf draw circle "rx_p4i_cert" [p] 700 470 45 "stroke=#FFFFFF;line_width=2";
pdf text add "rx_p4i_cert" [p] 700 462 "SST 2026" "font=HELVETICA_BOLD;size=13;color=#FFFFFF;align=center";
pdf draw line "rx_p4i_cert" [p] 120 500 340 500 "color=#333333;line_width=1";
pdf text add "rx_p4i_cert" [p] 120 508 "J. Hoarau, lead trainer" "font=HELVETICA_OBLIQUE;size=10";
#4. Checks before saving;
json load "rx_p4i_size" (pdf page size "rx_p4i_cert" [p]);
-> "[fonts]" (string regexp_count (pdf font show "rx_p4i_cert") "TIMES");
pdf save "rx_p4i_cert" "tmp/rx_p4i_certificate.pdf";
pdf close "rx_p4i_cert";
pdf load "rx_p4i_chk" "tmp/rx_p4i_certificate.pdf";
json load "rx_p4i_info" (pdf info get "rx_p4i_chk");
pdf close "rx_p4i_chk";
concat "Page " (math round (json select "rx_p4i_size" "/width") 0) " x " (math round (json select "rx_p4i_size" "/height") 0) " pt, landscape: " (> (json select "rx_p4i_size" "/width") (json select "rx_p4i_size" "/height")) [_n_] "Paragraph ends at y = " (json select "rx_p4i_pos" "/y") " on page " (json select "rx_p4i_pos" "/page") ", Times fonts available: " [fonts] [_n_] "Title: " (json select "rx_p4i_info" "/title") ", author: " (json select "rx_p4i_info" "/author") ", pages: " (json select "rx_p4i_info" "/pages");
Result
mentdb
Page 842 x 595 pt, landscape: 1
Paragraph ends at y = 268.8 on page 1, Times fonts available: 4
Title: Certificate SST-2026-0457, author: Training Center Est, pages: 1
Go further
  • Load a TrueType font with pdf font load to print accented or non Latin names.
  • Add a verification URL with pdf link add under the seal.
  • Generate one certificate per trainee from a SQL query with sql parse.
executed on MentDB

Shrink a scanned dossier before archiving

A three-page dossier made of scanned images is too heavy for the archive. Rebuild it at a lower resolution with pdf compress_image and report the size gain, keeping the page count.

Pipeline
Script
admin
#1. Simulate the scan: one full-page picture per page;
pdf create "rx_p4i_scan";
for (-> "[i]" 1) (<= [i] 3) (++ "[i]") {
	-> "[p]" (pdf page add "rx_p4i_scan" "A4" "portrait");
	pdf image add "rx_p4i_scan" [p] "images/bg1500x500.jpeg" 0 0 595 0;
	pdf text add "rx_p4i_scan" [p] 40 300 (concat "Scanned page " [i] " / 3") "size=16";
};
pdf save "rx_p4i_scan" "tmp/rx_p4i_scan.pdf";
pdf close "rx_p4i_scan";
#2. Rebuild the dossier at 60 dpi;
pdf compress_image "tmp/rx_p4i_scan.pdf" "tmp/rx_p4i_scan_60dpi.pdf" 60;
#3. Compare;
-> "[before]" (file size "tmp/rx_p4i_scan.pdf");
-> "[after]" (file size "tmp/rx_p4i_scan_60dpi.pdf");
-> "[gain]" (math round (* 100 (- 1 (/ [after] [before]))) 1);
concat "Pages: " (pdf page_count_file "tmp/rx_p4i_scan.pdf") " -> " (pdf page_count_file "tmp/rx_p4i_scan_60dpi.pdf") [_n_] "Size: " (math round (/ [before] 1024) 1) " KB -> " (math round (/ [after] 1024) 1) " KB" [_n_] "Gain: " [gain] " %";
Result
mentdb
Pages: 3 -> 3
Size: 189.9 KB -> 87.1 KB
Gain: 54.2 %
Go further
  • Run it on every file of an archive folder with file dir_list_regex.
  • Keep the original only if the gain is under 20 % with an if on [gain].
  • Note: the output is made of images, run pdf extract_text before compressing if you need the text.
executed on MentDB

Stamp a PDF received in base64 from an API

A supplier portal sends invoices as base64 strings in JSON. Load the document from base64, stamp it RECEIVED with the date and a reference, and return it in base64 for the ERP, with a short control report.

Pipeline
Script
admin
#1. Simulate the portal payload: a PDF in base64 inside a JSON;
pdf from html "<html><body><h2>Invoice SUP-88412</h2><p>Supplier: Ocean Pack Ltd</p><p>Total: 4 812.60 EUR</p></body></html>" "tmp/rx_p4i_sup.pdf";
pdf load "rx_p4i_src" "tmp/rx_p4i_sup.pdf";
json load "rx_p4i_msg" "{}";
json iobject "rx_p4i_msg" / "invoice" "SUP-88412" STR;
json iobject "rx_p4i_msg" / "file" (concat "data:application/pdf;base64," (pdf to_base64 "rx_p4i_src")) STR;
pdf close "rx_p4i_src";
#2. Load the document from the base64 field;
pdf load_base64 "rx_p4i_inv" (json select "rx_p4i_msg" "/file");
#3. Stamp: rotated red text and a reception reference;
-> "[ref]" (concat "REC-" (string replace (date sysdate) "-" "") "-0042");
pdf draw rect "rx_p4i_inv" 1 380 40 180 56 "stroke=#C00000;line_width=2;radius=6";
pdf text add "rx_p4i_inv" 1 470 48 "RECEIVED" "font=HELVETICA_BOLD;size=18;color=#C00000;align=center";
pdf text add "rx_p4i_inv" 1 470 72 [ref] "font=COURIER;size=10;color=#C00000;align=center";
#4. Back to base64 for the ERP, write it once to check it;
-> "[out]" (pdf to_base64 "rx_p4i_inv");
pdf close "rx_p4i_inv";
file b64_write [out] "tmp/rx_p4i_sup_stamped.pdf";
-> "[text]" (pdf extract_text "tmp/rx_p4i_sup_stamped.pdf");
concat "Invoice " (json select "rx_p4i_msg" "/invoice") ": base64 in " (string length (json select "rx_p4i_msg" "/file")) " chars, out " (string length [out]) " chars" [_n_] "Stamp found: " (string contains [text] "RECEIVED") ", reference found: " (string contains [text] [ref]) ", total line: " (string regexp_extract [text] "(Total: [0-9 .]+ EUR)" 1);
Result
mentdb
Invoice SUP-88412: base64 in 1708 chars, out 3312 chars
Stamp found: 1, reference found: 1, total line: Total: 4 812.60 EUR
Go further
executed on MentDB

Monthly statements: one file, then one PDF per customer

Print all the monthly statements of account in one PDF (one page per customer, overdue amounts flagged in red), then cut it into one file per customer named by the account number, ready to be mailed.

Pipeline
Script
admin
#1. The accounts: number, name, invoiced, overdue;
json load "rx_p4i_acc" "[[\"C-1001\",\"Garage Hoarau\",1240.50,0],[\"C-1002\",\"Boulangerie Payet\",385.20,385.20],[\"C-1003\",\"Pharmacie du Port\",2910.00,910.00]]";
pdf create "rx_p4i_st";
json parse_array "rx_p4i_acc" "/" "[a]" {
	json load "rx_p4i_a" [a];
	-> "[p]" (pdf page add "rx_p4i_st" "A4" "portrait");
	pdf text add "rx_p4i_st" [p] 40 40 (concat "Statement of account, October 2026 - " (json select "rx_p4i_a" "/[0]")) "font=HELVETICA_BOLD;size=14";
	pdf text add "rx_p4i_st" [p] 40 70 (json select "rx_p4i_a" "/[1]") "size=12";
	pdf text add "rx_p4i_st" [p] 40 100 (concat "Invoiced: " (math decimal_format (json select "rx_p4i_a" "/[2]") "0.00" "." " ") " EUR") "size=11";
	#2. Red badge when an amount is overdue;
	if (> (json select "rx_p4i_a" "/[3]") 0) {
		pdf draw rect "rx_p4i_st" [p] 380 92 175 26 "fill=#C00000;radius=4";
		pdf text add "rx_p4i_st" [p] 467 98 (concat "OVERDUE " (math decimal_format (json select "rx_p4i_a" "/[3]") "0.00" "." " ")) "font=HELVETICA_BOLD;size=11;color=#FFFFFF;align=center";
	};
};
pdf page_number add "rx_p4i_st" "Page {page} / {total}" 555 815 "align=right;size=9";
pdf save "rx_p4i_st" "tmp/rx_p4i_statements.pdf";
pdf close "rx_p4i_st";
#3. One file per customer, page i = customer i;
file mkdir "tmp/rx_p4i_st_out";
-> "[i]" 0;
json parse_array "rx_p4i_acc" "/" "[a]" {
	json load "rx_p4i_a" [a];
	++ "[i]";
	pdf extract_pages "tmp/rx_p4i_statements.pdf" (concat "tmp/rx_p4i_st_out/" (json select "rx_p4i_a" "/[0]") ".pdf") [i] [i];
};
#4. Check the folder and one file;
concat "Pages in the batch: " (pdf page_count_file "tmp/rx_p4i_statements.pdf") [_n_] "Files: " (string regexp_replace (file dir_list_regex "tmp/rx_p4i_st_out" "C-.*\.pdf" true false) "\s+" "") [_n_] "C-1003.pdf says: " (string replace (string trim (pdf extract_text "tmp/rx_p4i_st_out/C-1003.pdf")) [_n_] " | ");
Result
mentdb
Pages in the batch: 3
Files: ["C-1001.pdf","C-1003.pdf","C-1002.pdf"]
C-1003.pdf says: Statement of account, October 2026 - C-1003 | Pharmacie du Port | Invoiced: 2910.00 EUR OVERDUE 910.00 | Page 3 / 3
Go further
  • Mail each file to its customer with mail send and the account in a SQL table.
  • Use pdf split with pagesPerFile when each customer has a fixed number of pages.
  • Add the payment terms with pdf text paragraph on each page.
executed on MentDB

Archive compliance: check and fix the PDF metadata

Before an electronic archive deposit, every PDF must carry a title, an author and a subject. Inspect a batch with pdf info get, fill what is missing from the file name and produce a compliance report.

Pipeline
Script
admin
#1. A batch of documents, one already tagged;
pdf from html "<html><body><h3>Purchase order PO-7781</h3></body></html>" "tmp/rx_p4i_arc_PO-7781.pdf";
pdf from html "<html><body><h3>Delivery note DN-5520</h3></body></html>" "tmp/rx_p4i_arc_DN-5520.pdf";
pdf from html "<html><body><h3>Invoice INV-3301</h3></body></html>" "tmp/rx_p4i_arc_INV-3301.pdf";
pdf load "rx_p4i_d" "tmp/rx_p4i_arc_INV-3301.pdf";
pdf info set "rx_p4i_d" "title=INV-3301;author=Accounting;subject=Invoice";
pdf save "rx_p4i_d" "tmp/rx_p4i_arc_INV-3301.pdf";
pdf close "rx_p4i_d";
#2. Inspect each file, fix what is missing;
json load "rx_p4i_rep" "{}";
json load "rx_p4i_files" (file dir_list_regex "tmp" "rx_p4i_arc_.*\.pdf" true false);
json parse_array "rx_p4i_files" "/" "[f]" {
	-> "[path]" (concat "tmp/" [f]);
	-> "[docid]" (string regexp_extract [f] "rx_p4i_arc_(.*)\.pdf" 1);
	pdf load "rx_p4i_d" [path];
	json load "rx_p4i_i" (pdf info get "rx_p4i_d");
	-> "[missing]" 0;
	if (is null or empty (json select "rx_p4i_i" "/title")) {++ "[missing]";};
	if (is null or empty (json select "rx_p4i_i" "/author")) {++ "[missing]";};
	if (> [missing] 0) {
		pdf info set "rx_p4i_d" (concat "title=" [docid] ";author=Archive robot;subject=" (string regexp_extract [docid] "^([A-Z]+)-" 1));
		pdf save "rx_p4i_d" [path];
		json iobject "rx_p4i_rep" / [docid] (concat "fixed, " [missing] " field(s) were missing") STR;
	} {
		json iobject "rx_p4i_rep" / [docid] "compliant" STR;
	};
	pdf close "rx_p4i_d";
};
#3. Check one fixed file;
pdf load "rx_p4i_d" "tmp/rx_p4i_arc_DN-5520.pdf";
json load "rx_p4i_i" (pdf info get "rx_p4i_d");
json iobject "rx_p4i_rep" / "DN-5520 after fix" (concat (json select "rx_p4i_i" "/title") " / " (json select "rx_p4i_i" "/author") " / " (json select "rx_p4i_i" "/subject")) STR;
pdf close "rx_p4i_d";
json doc "rx_p4i_rep";
Result
mentdb
{
  "PO-7781": "fixed, 2 field(s) were missing",
  "INV-3301": "compliant",
  "DN-5520": "fixed, 2 field(s) were missing",
  "DN-5520 after fix": "DN-5520 / Archive robot / DN"
}
Go further
  • Add the PDF version with json select on /version and reject the files under 1.4.
  • Move the compliant files to the deposit folder with file copy_file.
  • Log each fix with log write for the audit trail.

Also in Documents & integration

ER
Excel reporting analyst
Build xlsx and xls workbooks with formulas and formats, evaluate them on the server and read them back.
10 recipes
XH
XML, HTML & Word integrator
Parse and transform XML with XPath, scrape HTML with selectors, convert to JSON and fill Word templates.
10 recipes
FA
Files & archives manager
Write and read files with streams, rotate logs, archive in zip, tar.gz or bz2, convert encodings and images.
10 recipes
FT
File transfer & remote systems engineer
Exchange files over SFTP, FTP, FTPS and Windows shares, run remote commands over SSH, call other MentDB servers and watch folders.
10 recipes
AD
API & database integrator
Call REST and SOAP services, take payments, send and read mail, and move data between SQL and MongoDB.
10 recipes
IE
Integrator & ETL developer
Expose APIs, import partner files, send reports, and reshape JSON and CSV in memory.
10 recipes