PdfCraft v0.20.0

PdfCraft is a free, open-source PDF application designed as an alternative to Adobe Acrobat. Built from scratch in Rust, it combines PDF viewing, document organization, page management, file merging, splitting, and encryption tools in a native desktop application.

Unlike cloud-based PDF services, PdfCraft is designed to process documents locally without requiring an account, telemetry, or cloud processing. It supports Windows, macOS, Linux, and FreeBSD, with a WebAssembly version that runs in a browser.

PdfCraft is particularly interesting for users who want a lightweight, privacy-focused PDF workbench without a subscription. However, it is still an early-stage project, and users who depend on advanced PDF editing, digital signatures, or OCR should consider its current limitations before adopting it.

Features

PDF Viewing and Search

PdfCraft provides a document viewer with text selection, page navigation, and search-as-you-type functionality. Its text selection follows the document's reading order, making it easier to select and copy content from complex layouts.

The renderer is designed to handle international text, vertical Japanese writing, color emoji, and transparency. The application also includes a dark theme and two-page reading mode for a more comfortable reading experience.

A command palette provides quick access to tools and commands, reducing the need to navigate through multiple menus.

Organize and Rearrange Pages

PdfCraft includes a visual page organizer that makes managing PDF documents straightforward. Users can rotate, delete, insert, and reorder pages, with undo support for reversing changes.

These tools are useful for rearranging reports, removing unwanted pages, correcting scanned documents, and assembling documents in the desired order.

The application also supports incremental editing and atomic saves, alongside autosave and crash recovery features designed to reduce the risk of losing work.

Merge, Extract, and Split PDFs

PdfCraft allows users to combine multiple PDF files into a single document, extract selected pages, and split documents into smaller files.

Its document-processing engine aims to preserve important PDF structures during these operations, including links, form fields, layers, and bookmarks.

This makes it useful for administrative work, combining reports, preparing documents for submission, and separating large files into manageable sections.

However, complex documents should be checked after processing to ensure that their formatting and interactive elements remain intact.

PDF Security and Encryption

PdfCraft supports opening encrypted PDF files using older RC4 encryption and modern AES-256 encryption.

It also respects document permissions specified by the author. This is useful when working with protected documents that restrict certain operations.

Encryption support does not mean that PdfCraft provides every security feature available in professional PDF software. Users who require certificate-based digital signatures, advanced redaction, or specialized document-security workflows should verify those capabilities separately.

Forms, Layers, and Document Structure

PdfCraft is designed to preserve PDF features beyond ordinary page content. These include form fields, layers, links, and bookmarks when combining or splitting documents.

This is important because PDF files often contain interactive elements and structural information that can be lost when processed by simpler PDF utilities.

The project emphasizes document integrity, although its developers acknowledge that its rendering quality has not yet been comprehensively compared against Adobe Acrobat.

Command-Line Tools and AI Automation

PdfCraft includes a command-line interface for inspecting documents, extracting text, rendering pages, editing PDF files, combining documents, extracting pages, and splitting files.

For example, you can inspect a PDF's structure in JSON format:

pdfcraft-cli info document.pdf

Extract text from a specific page:

pdfcraft-cli text document.pdf --page 3

Rotate pages, delete a page, and update the document title:

pdfcraft-cli edit input.pdf \
  --rotate 1,2:90 \
  --delete 5 \
  --title "Q3" \
  --out output.pdf

These capabilities make PdfCraft useful for batch processing, scripts, and automated document workflows. Its agent-oriented architecture also makes it suitable for integration with compatible AI tools.

Download PdfCraft v0.20.0 - Software Mirrors

PdfCraft v0.20.0 for Windows

pdfcraft-0.20.0-windows-x64.msi | 93.71 MB

pdfcraft-0.20.0-windows-x64-portable.zip | 115.21 MB

pdfcraft-0.20.0-windows-arm64.msi | 89.18 MB

pdfcraft-0.20.0-windows-arm64-portable.zip | 110.32 MB

PdfCraft v0.20.0 for macOS

pdfcraft-cli-0.20.0-macos-universal.zip | 86.36 MB

pdfcraft-0.20.0-macos-universal.dmg | 127.06 MB

PdfCraft v0.20.0 for Linux

pdfcraft-cli-0.20.0-linux-x86_64.tar.gz.asc | 228 B

pdfcraft-cli-0.20.0-linux-x86_64.tar.gz | 42.92 MB

pdfcraft-cli-0.20.0-linux-aarch64.tar.gz.asc | 228 B

pdfcraft-cli-0.20.0-linux-aarch64.tar.gz | 41.25 MB

pdfcraft-0.20.0-linux-x86_64.tar.gz.asc | 228 B

pdfcraft-0.20.0-linux-x86_64.tar.gz | 113.74 MB

pdfcraft-0.20.0-linux-x86_64.rpm.asc | 228 B

pdfcraft-0.20.0-linux-x86_64.rpm | 114.33 MB

pdfcraft-0.20.0-linux-x86_64.pkg.tar.zst | 88.18 MB

pdfcraft-0.20.0-linux-x86_64.flatpak.asc | 228 B

pdfcraft-0.20.0-linux-x86_64.flatpak | 84.5 MB

pdfcraft-0.20.0-linux-x86_64.deb.asc | 228 B

pdfcraft-0.20.0-linux-x86_64.deb | 114.88 MB

pdfcraft-0.20.0-linux-x86_64.AppImage.zsync.asc | 228 B

pdfcraft-0.20.0-linux-x86_64.AppImage.zsync | 189.1 kB

pdfcraft-0.20.0-linux-x86_64.AppImage.asc | 228 B

pdfcraft-0.20.0-linux-x86_64.AppImage | 107.92 MB

pdfcraft-0.20.0-linux-riscv64.tar.gz.asc | 228 B

pdfcraft-0.20.0-linux-riscv64.tar.gz | 111.2 MB

pdfcraft-0.20.0-linux-riscv64.rpm.asc | 228 B

pdfcraft-0.20.0-linux-riscv64.rpm | 111.79 MB

pdfcraft-0.20.0-linux-riscv64.deb.asc | 228 B

pdfcraft-0.20.0-linux-riscv64.deb | 112.15 MB

pdfcraft-0.20.0-linux-aarch64.tar.gz.asc | 228 B

pdfcraft-0.20.0-linux-aarch64.tar.gz | 109.67 MB

pdfcraft-0.20.0-linux-aarch64.rpm.asc | 228 B

pdfcraft-0.20.0-linux-aarch64.rpm | 111.07 MB

pdfcraft-0.20.0-linux-aarch64.pkg.tar.zst | 85.5 MB

pdfcraft-0.20.0-linux-aarch64.flatpak.asc | 228 B

pdfcraft-0.20.0-linux-aarch64.flatpak | 80.37 MB

pdfcraft-0.20.0-linux-aarch64.deb.asc | 228 B

pdfcraft-0.20.0-linux-aarch64.deb | 111.78 MB

pdfcraft-0.20.0-linux-aarch64.AppImage.zsync.asc | 228 B

pdfcraft-0.20.0-linux-aarch64.AppImage.zsync | 182.4 kB

pdfcraft-0.20.0-linux-aarch64.AppImage.asc | 228 B

pdfcraft-0.20.0-linux-aarch64.AppImage | 104.09 MB

PdfCraft v0.20.0 for BSD

pdfcraft-0.20.0-freebsd-x86_64.tar.gz | 113.13 MB

pdfcraft-0.20.0-freebsd-aarch64.tar.gz | 108.28 MB

Others Download related to PdfCraft v0.20.0

release-signing-key.asc | 803 B

pdfcraft-web-0.20.0.zip | 32.2 MB

PdfCraft v0.20.0 Source Code

PdfCraft v0.20.0 Source code (zip)

PdfCraft v0.20.0 Source code (tar.gz)

PdfCraft v0.20.0 Release Notes:

What's Changed

  • perf(cos): an object that can't be read is searched for once, not on every read by @KenM76 in #5802
  • M3: copy keeps formatting as HTML (Copy with formatting) by @KenM76 in #6197
  • Add text: gray or CMYK colour, painted with g or k (#6440) by @ZIDAN44 in #6471
  • pdfcraft-web: window.pdfcraft, the page command API from craft-web-api, opt-in with ?control by @helenkwok in #6497
  • feat(cos): list the objects that could not be read in doc_info by @KenM76 in #5816
  • refactor(ui): tell Edit text, the text layer and links whether a press is theirs (router PR 4b) by @alehel in #6243
  • Add dry-run preview to doc_batch without writing outputs by @eliets2 in #6486
  • Release: refresh contributor credits for each release (#6487) by @Luis-0508 in #6488
  • M9: signed appearances show their real validation state (#79) by @chiranjeevi-max in #2808
  • Keep ruled tables when exporting a PDF to Word by @Silvertree2010 in #5154
  • M2.6: never rewrite a content stream from the part before its damage by @Piohrun in #6289
  • fix(filters): inflate with zlib-rs everywhere, and charge the image allowance for what Flate reads by @Piohrun in #5825
  • contributors: set names for vangel76 by @vangel76 in #6067
  • Add Crop pages to the document quick toolbar (#5330) by @vangel76 in #5564
  • Select the bookmark title when creating or renaming a bookmark (#5529) by @vangel76 in #5562
  • perf(cos): write hex strings, integers and references without the formatter by @vangel76 in #4878
  • UI: allow a half-screen window, and fold the mode bar when narrow (#1913) by @vangel76 in #1988
  • Optimize: optional target_bytes for doc_optimize (#1889) by @vangel76 in #1983
  • perf(cos): speed up byte searches during cross-reference reconstruction by @vangel76 in #4874
  • perf(cos): write_full keeps the objects it parsed while numbering by @vangel76 in #4876
  • Redact: mark text by caller-supplied regex (#1886) by @vangel76 in #1977
  • Signing a form that sets NeedAppearances no longer makes Acrobat ignore the signature by @vangel76 in #6208
  • M7: Ungroup draws Form artwork in place, so its text can be edited (#2964, #3005) by @mtlcl410 in #3067
  • fix(edit): restore page rules after the content stream API change by @FredApps in #6619
  • Fix the build: page_rules joins content streams with text::joined by @bflatastic in #6621
  • Word tables get outer borders only where the page rules them by @bflatastic in #6626
  • M10: Correct recognized text keeps the window's tabs and toolbar and shows the page (ocr.correct-recognized-text) by @mtlcl410 in #6490
  • M10: adopt file-backed Windows printing with approved dependencies and the crash fix by @songzhihui in #6514
  • Word and HTML exports name the document's language (#5498, merged with main) by @bflatastic in #6631
  • Word and HTML exports name the document's language by @oracola91 in #5498
  • An edited OCR phrase is drawn at one size (#5454, with the requested test) by @bflatastic in #6633
  • fix(edit): an edited OCR phrase is drawn at one size (refs #3313) by @TiagoCK in #5454
  • OCR in the web build (#1641, merged with main, two fixes) by @bflatastic in #6634
  • M10: OCR in the browser, in Web Workers with SIMD (ocr.ocr-web) by @bmh129 in #1641
  • House rules: copy the licence allowlist with Unicode-3.0 from craft-contrib by @bflatastic in #6635
  • M7: edit grouped Thai text with one click, on Ungroup's form expansion (#6249 + review fixes) by @bflatastic in #6627
  • M7: preserve source fonts when editing grouped Thai text by @xullul in #6249
  • redact: treat unusable /QuadPoints as absent in every reader by @chamber074 in #6525
  • M7: break Thai rows between dictionary words (#3492, merged with main) by @bflatastic in #6637
  • M7: break Thai rows between dictionary words by @rutthawitc in #3492
  • Edit a PDF: select all, copy, cut and paste page objects within and between documents (#5989) by @bflatastic in #6639
  • Draw Thai in Fill & Sign, text boxes, form fields, headers, footers and watermarks (#2354) by @bflatastic in #6632
  • Exports keep a wrapped paragraph and a bulleted list as paragraphs (#5404, merged with main) by @bflatastic in #6643
  • Exports keep a wrapped paragraph and a bulleted list as paragraphs by @oracola91 in #5404
  • Fill & Sign test: check Escape by edits, not by comparing saves by @bflatastic in #6646
  • Combine files: a drop is one undo step, in order, and names what it can't take by @bflatastic in #6645
  • M5: a comment whose colour is written [r g b null] is drawn, as CMYK with the null read as 0 (view.render-annotation-appearances) by @mtlcl410 in #6503
  • fix(render): TIFF predictor 2 is undone a byte row at a time at 8 and 16 bits (hayro-syntax patch 42) by @mtlcl410 in #6505
  • fix(render): a form XObject that draws itself is drawn once (hayro-interpret patch 89) by @mtlcl410 in #6515
  • M2: info lists the repairs made while opening, as Document Properties does (misc.cli-inspect) by @mtlcl410 in #6518
  • M2: a wide stroke of a path with y curves is drawn, not dropped (view.render-pages) by @mtlcl410 in #6526
  • fix(engine): map URL text line rects with view_rect_to_user and filter non-finite coordinates by @jonathanwxh-cell in #6536
  • fix(js): decode ', numeric, and HTML named character references in FormCalc Decode by @jonathanwxh-cell in #6538
  • refactor(cos): consolidate duplicate civil_from_days onto pdfcraft_cos::civil_from_days by @jonathanwxh-cell in #6540
  • M11: PDF/A verify checks that predefined XMP properties have their value types (optimize.pdfa-validate) by @mtlcl410 in #6220
  • M5: Replace Text keeps its replacement on the group's primary, the caret (comment.replace-text) by @mtlcl410 in #5689
  • M3: pages shorter than the window sit in its middle (#5708) by @hasannadeem in #5759
  • M3: a protected file named before others at launch keeps its password prompt (#5756) by @mtlcl410 in #5762
  • Keep the font size when making text box properties default by @Silvertree2010 in #5784
  • M12: an XFA page area keeps at most 128 content areas, so a subform split across them lays out within its budget (#5843) by @mtlcl410 in #5844
  • fix(create): image export checks a JPEG's Extended XMP chunks before decoding it by @dylanmtaylor in #5860
  • M14: localization parity counts every catalog with the i18n report (#5805) by @hasannadeem in #5894
  • fix(measure): charge snapping per filter stage and bound listing work by @dylanmtaylor in #5920
  • fix(redact): prune the resources of Forms a surviving Type 3 glyph draws by @dylanmtaylor in #5924
  • fix(edit): stop rescanning what a loop already holds in wrapping, grouping and flattening by @dylanmtaylor in #5963
  • fix(print): index page labels once and cap how many pages a range selects by @dylanmtaylor in #5967
  • fix(engine): coalesce script resetForm requests and bound console output per edit by @dylanmtaylor in #5975
  • Harden Bates sequence validation by @Harrison-Booth in #6003
  • fix(sign): read each existing DSS stream once and within a decoded-size allowance by @dylanmtaylor in #6040
  • fix(a11y): heading titles decode their source strings within a shared allowance by @dylanmtaylor in #6049
  • Expose widget rectangles to field scripts by @Harrison-Booth in #6093
  • fix(xfa): read and validate the first alternative in typed_date by @jonathanwxh-cell in #6120
  • fix(js): decode FormCalc Unicode string escapes and preserve chained unary not/! by @jonathanwxh-cell in #6122
  • fix(ui): two-page continuous view prefetches whole rows ahead by @dotCooCoo in #6126
  • Scroll: a touch zoom keeps zooming after its keys are let go by @dotCooCoo in #6130
  • fix(export): skip empty Markdown runs before merging and emphasis lookahead by @jonathanwxh-cell in #6134
  • fix(sign): merge CMS unsignedAttrs into one SET and validate SignerIdentifier by @jonathanwxh-cell in #6137
  • fix(fonts): a subset that gives the space no width can't encode one (#6131) by @fatihkunak in #6158
  • M12: floating fields show the values scripts give them, and the sheet number and count (#6160) by @mtlcl410 in #6166
  • M1: correct the COS object reference API example by @MMexicann in #6183
  • Windows: shortcuts take pdfcraft.exe's icon, so a taskbar pin keeps it by @art804 in #6184
  • M1.10: richer synthetic seeds for the mutation fuzzer by @rp6186030 in #6186
  • M5: document implemented annotation builders and rich text by @MMexicann in #6189
  • M1: define the Info object in the testkit README example by @MMexicann in #6191
  • M8: use AuditFinding in the audit README example by @MMexicann in #6204
  • fix(canvas): release closed panels' thumbnails while a visible page renders by @quincy-jc in #6227
  • fix(canvas): find a page's pictures off the window thread by @quincy-jc in #6228
  • M9: a signed string respelled in hexadecimal is no change after signing (sign.changes-after-signing) by @mtlcl410 in #6502
  • Ignore dictionary key order when checking signed pages for changes by @Silvertree2010 in #5962
  • M10: a layer the document marks screen-only stays off the printed sheet (print.print-ready-pdf) by @mtlcl410 in #6387
  • M1: a V4 RC4 file with a 40- or 48-bit key reads and saves (#6397) by @mtlcl410 in #6398
  • M12: headless orbit navigation for 3D models by @KenM76 in #6406
  • M7: a click away from a picture being cropped applies the crop (edit.image-crop) by @mtlcl410 in #6428
  • M3: End shows the end of the document, the last page down to its bottom (view.page-navigation) by @mtlcl410 in #6435
  • M6: a widget whose /Parent names a missing object is a field of its own (form.field-list) by @mtlcl410 in #6445
  • M6: a field whose /Kids loop back or name a root field keeps its other kids (form.field-list) by @mtlcl410 in #6446
  • M5: a font a page lists among its annotations is not a comment (comment.list-view) by @mtlcl410 in #6447
  • Signature validation takes the certification from the catalog by @oracola91 in #6453
  • Apply Redactions replaces the recovery snapshot at once by @oracola91 in #6454
  • Automation follows a path toward its root before looking anything up by @oracola91 in #6455
  • Links, actions and JavaScripts takes the scripts out of bookmarks by @oracola91 in #6456
  • Private data takes OPI links and alternate images by @oracola91 in #6457
  • Page copies bring only the form fields on the pages copied by @oracola91 in #6461
  • feat(reduce): ask where to save before compressing by @quincy-jc in #6229
  • perf(optimize): work out pictures on threads and skip needless page reads by @quincy-jc in #6230
  • M6: Shift keeps a field's move horizontal or vertical in Prepare a form, as in Acrobat (form.prepare-form-mode) by @mtlcl410 in #6260
  • fix(render): a JBIG2 scan whose data starts with a JBIG2 file header draws instead of a white page (hayro-jbig2 patch 17) by @mtlcl410 in #6279
  • fix(edit): break the row on every Unicode row break by @MatrixMagician in #6295
  • M10: applying recognized words refreshes only the page content they add (#6310) by @mtlcl410 in #6316
  • M6: a multi-select list's picks reach its XFA data as one value element each (form.fill-choice) by @mtlcl410 in #6328
  • M10: comments and fields without an appearance print as the page shows them (print.comments-and-forms) by @mtlcl410 in #6367
  • fix(ui-egui): stop held middle-button auto-scroll on release (#3795) by @jonathanwxh-cell in #6544
  • M5: the hotbar settings button shows one tooltip (#6548) by @mtlcl410 in #6550
  • M10: Pages per sheet takes a custom grid of columns by rows (#6551) by @mtlcl410 in #6559
  • M6: a product of empty fields is 0, not 1 (form.af-simple-calculate) by @mtlcl410 in #6567
  • M7: Edit text lines set in a Type 3 font are as high as their glyphs (edit.text-edit) by @mtlcl410 in #6571
  • Deduplicate pages in ocr_job and use a set for page lookup by @eliets2 in #6586
  • M3: Find matches words divided by soft hyphens, zero width characters or a Chinese or Japanese line end (view.find) by @mtlcl410 in #6595
  • pdfcraft-web: say how to wait for window.pdfcraft's functions by @helenkwok in #6604
  • M6: a calculate script that sets its own field through the field object keeps that value (form.custom-scripts) by @mtlcl410 in #6616
  • Fix main's build after the latest merges by @bflatastic in #6660
  • Land render and syntax patches (#6355, #6501, #6506, #6510, #6534, #6561, #6290, #6312, #6348, #6532, #6438) by @bflatastic in #6664
  • M1: a document that encrypts only its embedded files opens without a password (core.attachment-only-encryption) by @mtlcl410 in #6312
  • fix(render): a ToUnicode CMap reads its mappings past a token it can't parse (hayro-cmap patch 3) by @mtlcl410 in #6348
  • fix(render): a page whose MediaBox or CropBox encloses no area shows its content (hayro-syntax patch 38) by @mtlcl410 in #6355
  • M2: a page past the rasterizer's coverage renders what fitted instead of failing (view.render-error-isolation) by @mtlcl410 in #6438
  • fix(render): an image whose PNG predictor was declared but never applied draws its samples (hayro-syntax patch 40) by @mtlcl410 in #6501
  • fix(render): a fax image with more /Rows than its /Height is decoded to its height (hayro-syntax patch 41) by @mtlcl410 in #6506
  • M1: the renderer reads a cut-off last update's objects, as the document model does (core.repair-broken-files) by @mtlcl410 in #6510
  • M2: a CMap's bfchar mappings read codes at their codespace length (view.render-cid-fonts) by @mtlcl410 in #6532
  • M2: a page without a /MediaBox is US Letter (view.render-pages) by @mtlcl410 in #6534
  • M1: a file whose last update has no /Prev opens with all its pages and a repair note (core.repair-broken-files) by @mtlcl410 in #6561
  • M3.9: the control screenshot test makes its renderer before the 5 s deadline by @austin62jake in #5252
  • Forms: highlight every selected widget in Prepare a form by @KnottyDyes in #5253
  • docs: a portable copy keeps its single-instance socket in %LOCALAPPDATA% by @austin62jake in #5260
  • M4: Initial View magnification offers 25% and 100% (#5254) by @Ethunxxx in #5265
  • Track XMP attribute quotes only inside tags by @TheGalitube in #5271
  • M7: links that go to a page open at a chosen zoom (edit.action-goto) by @Ethunxxx in #5277
  • Reload: a file changed under an open dialog waits for it to close (#3917) by @mmmaarian03-crypto in #5278
  • fix(organize): remove uninherited crop boxes and guard disjoint or overflowing boxes by @jonathanwxh-cell in #5282
  • M3: text_find can stop at its limit without reading on to the end (#5328) by @Lcoding15 in #5335
  • fix(forms): resolve field and widget references in ResetForm and Hide actions by @jonathanwxh-cell in #5364
  • M4: bookmarks run their actions as links do (organize.bookmark-actions) by @Ethunxxx in #5410
  • fix(optimize): downsampling never enlarges; a huge target_ppi aborted on a 20 TB allocation by @jayzed80 in #5425
  • fix(compare): visual compare no longer overflows on declared sizes its buffers cannot hold by @jayzed80 in #5458
  • fix(ui): show creation and modification dates in Document Properties (#5534) by @NathanGlasby in #5536
  • tests(ui): the Search panel snapshot no longer depends on which page's text arrives first by @guillehr2 in #5560
  • Reject signs in XMP numeric character references by @Silvertree2010 in #4755
  • macOS: declare the UI languages so the open and save panels are translated by @Atalantis in #4785
  • fix(render): treat combined fill-and-stroke operators as a knockout group (#3229) by @jonathanwxh-cell in #4788
  • edit: a variation selector after existing text draws the base glyph instead of failing the edit by @midasmatterhorn in #4899
  • Keep the outline intact when move_bookmark gets an invalid target by @Silvertree2010 in #4901
  • Reject '+' sign in print page ranges by @Silvertree2010 in #4931
  • fix(optimize): size objects in compressed object streams by their compressed share by @jonathanwxh-cell in #4951
  • M12: an XFA field with a long label beside it is as tall as its label, not a page (form.xfa-dynamic) by @mtlcl410 in #5109
  • fix(forms): name tree nodes sharing one long /Kids array no longer exhaust memory by @lowtide-lab in #5121
  • Dialogs: a dialog no longer keeps a taller one's height (#5118) by @mmmaarian03-crypto in #5139
  • Roll human_size over to MB when KB rounds to 1024.0 by @Silvertree2010 in #5186
  • Edit background, watermark and header colours as sRGB by @Silvertree2010 in #5218
  • Publish action wizard outputs without replacing existing files (#1805) by @MarekSpodBiedry in #1874
  • Check the remembered path of a pathless doc_save against the root (#1791) by @MarekSpodBiedry in #1879
  • Refuse a legacy encryption key length above 128 bits instead of panicking (#1769) by @MarekSpodBiedry in #1882
  • Use PingFang from the reserved font directory on macOS 26 (#1716) by @MarekSpodBiedry in #1893
  • M14: the Windows installer lets the user choose the install folder (#1139) by @mtlcl410 in #2480
  • fix(content): an inline image ends at its own EI, not a later one by @luigiPinna in #2483
  • fix(edit): preserve annotation optional-content membership in marked content (#3213) by @Ozt-y in #3275
  • Attachments: a saved attachment keeps the PDF's Mark-of-the-Web on Windows (#3990) by @mmmaarian03-crypto in #4091
  • UI: a panic in a frame no longer closes the app and its tabs (#3914) by @mmmaarian03-crypto in #4201
  • Skip the Bates ACL test where the deny entry doesn't bind by @bflatastic in #6670
  • Land render and font patches (#6292, #6287, #6314, #6353, #6359, #6368, #6409, #6491, #6545, #6516, #6521, #6528, #6533, #6529, #6542, #6543, #6569, #6572) by @bflatastic in #6663
  • fix(render): a small image with a soft mask far larger than it draws in seconds, its mask averaged down once (hayro-interpret patch 82) by @mtlcl410 in #6287
  • fix(render): a page of stencil masks painted with a tiling pattern draws in seconds instead of running out of memory (hayro patch 30) by @mtlcl410 in #6292
  • fix(render): a TrueType font marked with an out-of-range loca format draws its letters instead of boxes (hayro-interpret patch 83) by @mtlcl410 in #6314
  • fix(render): an embedded CJK font without a usable ToUnicode reads its text through its collection's UCS2 CMap (hayro-interpret patch 84) by @mtlcl410 in #6353
  • fix(render): a Type 3 glyph shown again isn't decoded again (hayro-interpret patch 87) by @mtlcl410 in #6409
  • fix(render): a non-embedded Times New Roman draws the glyphs its codes name (hayro-interpret patch 88) by @mtlcl410 in #6491
  • fix(render): a picture that doesn't decode is decoded once, not at every draw (hayro-interpret patch 91) by @mtlcl410 in #6516
  • fix(render): glyph names read as text only in the glyph list's uni and u forms (hayro-interpret patch 92) by @mtlcl410 in #6521
  • fix(render): a CID-keyed CFF font with an emptied font dict still draws its other glyphs (hayro-interpret patch 93) by @mtlcl410 in #6528
  • fix(render): a non-embedded SymbolMT draws Symbol's glyphs, whatever encoding it declares (hayro-interpret patch 98) by @mtlcl410 in #6529
  • M2: a font without a /Subtype is drawn by its /BaseFont (view.render-pages) by @mtlcl410 in #6533
  • fix(render): a patch mesh painted through a path its own size draws every patch (hayro-interpret patch 99) by @mtlcl410 in #6542
  • fix(render): a symbolic TrueType font in the Macintosh glyph order reads its codes as Mac Roman (hayro-interpret patch 101) by @mtlcl410 in #6569
  • M3: Find matches words on a vertical line (view.find) by @mtlcl410 in #6572
  • fix(render): a ToUnicode written with CID ranges gives its text (hayro-interpret patch 85) by @mtlcl410 in #6359
  • Revert #5536: it breaks the Document Properties tests on main by @bflatastic in #6671
  • Land PDF/A verify and convert checks (#6232, #6254, #6172, #6202, #6203, #6206, #6207, #6214, #6215, #6219) by @bflatastic in #6662
  • M11: PDF/A verify asks only the fonts something draws with to be embedded (optimize.pdfa-validate) by @mtlcl410 in #6172
  • M11: PDF/A verify reports integers, reals, strings, names, CIDs and page boundaries past the implementation limits (optimize.pdfa-validate) by @mtlcl410 in #6202
  • M11: PDF/A verify reports Separation spaces of one name that differ and DeviceN spot colours missing from /Colorants (optimize.pdfa-validate) by @mtlcl410 in #6203
  • M11: PDF/A verify checks the file header and Save as PDF/A rewrites it (optimize.pdfa-validate) by @mtlcl410 in #6206
  • M11: PDF/A verify reports transfer functions, halftones and rendering intents a graphics state may not use (optimize.pdfa-validate) by @mtlcl410 in #6207
  • M11: PDF/A verify checks the file identifier and Save as PDF/A adds one (optimize.pdfa-validate) by @mtlcl410 in #6214
  • M11: PDF/A verify checks that TIFF, EXIF and aux XMP properties have their value types (optimize.pdfa-validate) by @mtlcl410 in #6232
  • M11: PDF/A verify checks that XMP extension schema descriptions are complete (optimize.pdfa-validate) by @mtlcl410 in #6254
  • M11: PDF/A verify reports pages with transparency but no blending colour space, and blend modes ISO 32000-1 doesn't define (optimize.pdfa-validate) by @mtlcl410 in #6219
  • Land redaction, organize, forms, annotation, text, measure, XFA, XFDF and edit fixes (#6458, #6459, #6460, #6463, #6474, #5969, #5761, #5852, #6231, #6366, #6504, #6570, #6610, #6597, #6620, #6650) by @bflatastic in #6666
  • M3: a snapshot keeps its resolution when zoomed out (#5716) by @hasannadeem in #5761
  • M5: a text markup, line, polyline, polygon or ink comment whose points reach past its Rect is drawn through them (view.render-annotation-appearances) by @mtlcl410 in #6366
  • Removed layers leave every optional-content configuration by @oracola91 in #6459
  • Hidden layers includes layers a View usage rule turns off by @oracola91 in #6460
  • Combine and Insert bring only the bookmarks and attachments of the pages chosen by @oracola91 in #6463
  • Type 3 pruning counts a page's appearances toward one operator total by @oracola91 in #6474
  • M6: a named kid is named up its own /Parent chain, and a radio option of any same-named field is chosen by the name (form.field-list) by @mtlcl410 in #6570
  • M6: a field whose normal appearance gives no stream is drawn from its value, /MK and /BS (view.render-annotation-appearances) by @mtlcl410 in #6610
  • fix(edit): editing an OCR phrase redraws only the words it changes (refs #3313) by @TiagoCK in #6650
  • Redaction keeps Type 3 glyphs that annotations, patterns and soft masks show by @oracola91 in #5197
  • fix(redact): read inherited forms once for all names in resource cleanup by @dylanmtaylor in #5969
  • Remove Hidden Information unlinks images nothing draws by @oracola91 in #6458
  • Updates: take craft-update from craft-libs main at 1b34eba41 by @dotCooCoo in #6674
  • fix(redact): Remove Hidden Information reads only what the chosen categories need (part of #5873) by @ljkx in #6021
  • fix(render): a stream ending in endstrea is not read into a later object's stream (hayro-syntax patch 36) by @mtlcl410 in #6669
  • fix(preflight): resolve indirect spot families, null dict entries, and numeric equality by @jonathanwxh-cell in #6679
  • fix(preflight): walk soft-mask groups, indirect subtypes, and type 5 halftones by @jonathanwxh-cell in #6680
  • M4: custom document properties are kept in XMP as pdfx: properties (organize.custom-properties) by @Ethunxxx in #6686
  • Updates: a ready update starts its helper when the app exits by @dotCooCoo in #6688
  • Print production: Fix hairlines (dialog, edit, tool) by @regendorf-ux in #1906
  • M3: whole-word Find matches inside Chinese, Japanese and Thai text (view.find-options) by @mtlcl410 in #2075
  • M2: a page whose content was cut at the renderer's limits says so (#623) by @mtlcl410 in #2113
  • Print: custom paper sizes, and the paper remembered for each printer (page setup) by @regendorf-ux in #2381
  • M14: "Combine files in PdfCraft" on File Explorer's PDF menu (#585) by @mtlcl410 in #2503
  • feat(ui): split view: two panes, one document or two by @regendorf-ux in #2518
  • M3: copied text keeps one line per baseline, reading order for turned lines, letters for ligatures (#4467) by @mtlcl410 in #4468
  • M3: web and email addresses written as page text open like links (#4207) by @mtlcl410 in #4472
  • M6: radio buttons sharing an on state are exclusive unless RadiosInUnison (form.fill-checkbox-radio) by @mtlcl410 in #4489
  • M12: an XFA date field keeps what its patterns don't read as typed, and reads its own patterns first by @EricKaden in #4511
  • edit: preserve gap-drawn spaces and indents in subset fonts via /ActualText (#4229) by @jonathanwxh-cell in #4514
  • fix(automation): XFA clicks and form edits return only the script output they produced by @dotCooCoo in #4559
  • automation: compare canonical root components exactly and test roots-only escape confinement (#4003) by @jonathanwxh-cell in #4569
  • export: order multi-column and turned page items in reading order (#3657) by @jonathanwxh-cell in #4580
  • M12: a page break splits an empty XFA text field that grows with its text (form.xfa-dynamic) by @mtlcl410 in #5525
  • fix(a11y): resolve indirect structure kids, parent tree keys, and table attributes by @jonathanwxh-cell in #5611
  • fix(organize): preserve numbered names, indirect resource dicts, and comment threads by @jonathanwxh-cell in #5612
  • fix(forms): harden tab order, AF script parsing, and null/inherited tree entries by @jonathanwxh-cell in #5614
  • M5: a reply is not drawn on the page or flattened into it (comment.reply) by @mtlcl410 in #5632
  • fix(sign): read OCSP responseStatus as ENUMERATED; drop redundant RSA leading zeros by @dylanmtaylor in #6005
  • fix(create): refuse PNG headers the file cannot back; no truncating JP2 box length by @dylanmtaylor in #6007
  • fix(engine): unknown permissions on an encrypted document refuse copying and printing by @dylanmtaylor in #6011
  • fix(xfa): remember every picture hash written in a pass by @dylanmtaylor in #6035
  • fix(xfdf): FDF comment export excludes source pages by identity, not by /Type by @dylanmtaylor in #6036
  • fix(engine): summarizing comments requires copying permission by @dylanmtaylor in #6039
  • fix(forms): comb detection ignores repeated boxes by @dylanmtaylor in #6077
  • fix(xfa): existing-field traversal shares a field's name among its queued children by @dylanmtaylor in #6080
  • fix(xfa): strip reads a shared indirect Kids array once by @dylanmtaylor in #6081
  • fix(print): a printed page's form shares the page's Resources when it adds no appearance by @dylanmtaylor in #6089
  • fix(a11y): auto-tagging bounds the content it plans across all pages by @dylanmtaylor in #6090
  • fix(sign): chain building hashes a certificate once for all candidate issuers by @dylanmtaylor in #6096
  • fix(render): the named-destination index spends key bytes from one allowance by @dylanmtaylor in #6097
  • export: the Office zip writer outgrows the classic records with ZIP64 by @midasmatterhorn in #2554
  • fix(organize): preserve page labels across extract, split, and combine (#2670) by @jonathanwxh-cell in #2799
  • M7: replacing a line keeps the source font for the words it can show (#2378) by @mtlcl410 in #2955
  • Search: centre the active match in the viewport (#3530) by @MarekSpodBiedry in #3547
  • M2: the content crate's parser is checked against hayro's, operator by operator (#3107, #3108, #3111, #3177) by @mtlcl410 in #3563
  • Split edit-text paragraphs at headings and blank lines by @Silvertree2010 in #3642
  • M14: the portable copy can register as a PDF handler for the current user (#3758) by @mtlcl410 in #3790
  • M14: the --control file is owner-only on Windows too, and the CLI checks it (#3922) by @mtlcl410 in #3956
  • M5: review statuses are hidden, chain per reviewer and show per reviewer, by /IRT reference (#4122) by @mtlcl410 in #4597
  • fix(edit): catch paragraph edges and frames without aiming for the hairline by @meethere in #4624
  • packaging: Flatpak may talk to the accessibility bus (#4612) by @Lcoding15 in #4641
  • fix(app): set Windows taskbar and Alt+Tab big icon (ICON_BIG) (#3818) by @jonathanwxh-cell in #4681
  • Tests: highlighting an area off the text waits up to 30 s for the page text by @dotCooCoo in #4683
  • fix(optimize): merge side-by-side and grid-tiled image fragments by @jonathanwxh-cell in #4818
  • M7: the Preferences language test opens the app menu on macOS by @EricKaden in #4882
  • Tests: a retained PDF version is the later of header and catalog (#3227 follow-up) by @Harrison-Booth in #4933
  • Test preservation of PDF 2.0 structure metadata by @Harrison-Booth in #5073
  • M12: XFA date pictures read a two-digit year with the century split of 30 by @EricKaden in #5074
  • M12: an XFA breakBefore or breakAfter without a targetType starts no new page (form.xfa-dynamic) by @mtlcl410 in #5145
  • fix(measure): resolve indirect scale objects and validate closed area edges by @jonathanwxh-cell in #5270
  • fix(geom): normalize quad ordering for perspective foreshortening and scale by @jonathanwxh-cell in #5273
  • M3: a pinned folder on Home collapses to its heading, and stays collapsed (#5295) by @Lcoding15 in #5341
  • fix(optimize): validate named destination targets and ignore orphaned refs by @jonathanwxh-cell in #5362
  • fix(ui): selecting in an unchanged paragraph lands on the words the page shows (refs #5185) by @TiagoCK in #5390
  • Create PDF from text takes Cyrillic and Greek, and reports no longer escape them by @oracola91 in #5403
  • Text from a justified line keeps one space between words by @oracola91 in #5496
  • Show form tab-order numbers by @Harrison-Booth in #5509
  • M5: cloudy squares, circles and text boxes are drawn (comment.cloud) by @mtlcl410 in #4195
  • Accessibility: turn a layout table into Div and P tags in one call (#4105) by @pat26-dotcom in #4250
  • M10: (n) names the page at position n, whatever its label, in page ranges and the page box (print.page-range) by @mtlcl410 in #4282
  • M4: a recovered document's first Save asks where instead of writing the sidecar's path (#3923) by @Lcoding15 in #4327
  • M11: PDF Optimizer can discard form submission, import and reset actions (optimize.discard-form-actions) by @dotCooCoo in #4416
  • fix(content): an inline image only the renderer reads counts as ambiguous by @dylanmtaylor in #5874
  • fix(model,oracle): read a page box only when it has four entries; skip empty images by @dylanmtaylor in #5941
  • fix(redact): read text-state operands from the end, as the renderer does by @dylanmtaylor in #5951
  • fix(automation): sign_trust deduplicates certificates with a set by @dylanmtaylor in #5965
  • fix(print,a11y): read summary scans one operator at a time and borrow form resources by @dylanmtaylor in #5968
  • fix(engine): open linked documents without blocking and check the handle is a regular file by @dylanmtaylor in #5982
  • fix(redact): read the page tree once per sanitize pass, not per page by @dylanmtaylor in #5983
  • fix(xtask): render-oracle writes page pixels into a private temporary folder by @dylanmtaylor in #5988
  • fix(cli): render needs the copying permission, as text does by @dylanmtaylor in #5990
  • fix(cli): cap input reads, budget check's edit pass and escape its stderr records by @dylanmtaylor in #5997
  • fix(xfa): one decoding allowance for all XFA packets by @dylanmtaylor in #6000
  • Fix main's build and tests after the latest merges by @bflatastic in #6706

New Contributors

  • @Luis-0508 made their first contribution in #6488
  • @xullul made their first contribution in #6249
  • @chamber074 made their first contribution in #6525
  • @rutthawitc made their first contribution in #3492
  • @austin62jake made their first contribution in #5252
  • @Ethunxxx made their first contribution in #5265
  • @TheGalitube made their first contribution in #5271
  • @jayzed80 made their first contribution in #5425
  • @NathanGlasby made their first contribution in #5536
  • @luigiPinna made their first contribution in #2483
Full Changelog: v0.19.0...v0.20.0

Performance and Compatibility

PdfCraft is written in Rust, with its PDF parsing, rendering, and editing logic organized into reusable libraries. The graphical interface is built on top of this underlying engine.

The project emphasizes faithful document rendering, reliable file operations, and local processing. Its developers report zero crashes across a test corpus of 983 PDF files, although this result should not be interpreted as a guarantee of compatibility with every PDF document.

Supported platforms include Windows, macOS, Linux, FreeBSD, and modern web browsers through WebAssembly. Available packages include Windows installers and portable archives, a universal macOS disk image, Linux AppImage packages, Debian packages, RPM packages, and other distribution formats.

PdfCraft is still in early development. Its roadmap identifies renderer improvements, stronger compatibility testing, editing existing PDF content, digital signatures, OCR, Office-related workflows, preflight tools, and XFA support as areas for further development.

Consequently, it is best viewed as a promising PDF workbench rather than a complete replacement for every Adobe Acrobat workflow.

System Requirements

  • Operating systems: Windows, macOS, Linux, and FreeBSD.

  • Browser support: WebAssembly build for modern browsers.

  • Windows architectures: x86, x64, and ARM64.

  • Linux architectures: x86-64 and ARM64.

  • macOS: Universal build for Intel and Apple silicon.

  • Build requirements: Rust 1.90 or newer, along with the project's documented development dependencies.

The project does not establish a comprehensive minimum RAM or processor specification in the information reviewed. Basic PDF viewing should be suitable for a broad range of modern computers, but performance will depend on document complexity and system resources.

Pros and Cons

Pros

  • Free and open-source under permissive licenses.

  • Native applications for major desktop operating systems.

  • Local document processing without mandatory accounts or cloud uploads.

  • PDF viewing with text search and selection.

  • Page rotation, deletion, insertion, and reordering.

  • PDF merging, extraction, and splitting.

  • Support for encrypted PDFs and document permissions.

  • Designed to preserve links, form fields, layers, and bookmarks.

  • Autosave, crash recovery, and undo support.

  • Command-line tools for scripting and batch processing.

  • WebAssembly version for browser-based use.

Cons

  • Still in early development, with some features planned rather than implemented.

  • PDF rendering quality has not yet been comprehensively benchmarked against Adobe Acrobat.

  • Advanced editing of existing PDF content remains a development priority.

  • Digital signatures, OCR, and specialized document workflows are not yet comprehensive.

  • Compatibility with complex forms and unusual PDF documents requires further testing.

  • Professional publishing and document-preflight capabilities are limited.

How to Install

The easiest way to install PdfCraft is to visit its official website and download the package for your operating system.

Windows users can choose an installer or portable archive. macOS users can download the universal disk image, while Linux users can select AppImage, DEB, RPM, or another available package.

To build PdfCraft from source, install Rust 1.90 or newer and run:

git clone https://github.com/storytold/pdfcraft.git
cd pdfcraft
cargo run --release -p pdfcraft

For command-line PDF processing, build or install the CLI package provided by the project. Consult the official documentation for supported commands, build dependencies, and platform-specific installation instructions.

Final Verdict

PdfCraft is a promising open-source PDF editor for users who want to read, organize, combine, split, and secure documents without relying on a cloud service or paid subscription.

Its strongest advantages are local processing, cross-platform availability, careful handling of PDF structure, and a command-line interface that supports automation. These features make it appealing to developers, privacy-conscious users, and anyone who regularly manages PDF documents.

The main limitation is maturity. PdfCraft does not yet cover every advanced capability offered by established PDF applications, particularly for editing existing content, digital signatures, OCR, and specialized document workflows.

For everyday PDF viewing and organization, PdfCraft is worth trying. Users who need a complete professional PDF solution should evaluate its current feature set against their specific requirements before replacing their existing software.

PdfCraft v0.20.0
Free
Software Informations:
Developer:

Operating System:
Windows / macOS / Linux / BSD
Date Added:
2026-11-10T07:07:16.440Z
Categories:

Post a Comment/Report Broken Link: