# OCRmyPDF v17.11.0 - Product: OCRmyPDF (https://whatsnew.fyi/product/ocrmypdf) - Vendor: ocrmypdf - Date: 2026-08-28 - Version: v17.11.0 - Original notes: https://github.com/ocrmypdf/OCRmyPDF/releases/tag/v17.11.0 - Permalink: https://whatsnew.fyi/product/ocrmypdf/releases/v17.11.0 What's New is an index, not a publisher: every entry below links to the vendor's own release notes, which are the authoritative source. Entries are labelled where they are hand-curated sample data, pre-releases, or drawn from a secondary source such as a developer blog. Reuse: the summaries, labels and curation here are © What's New. Quote freely with attribution and a link back; wholesale republication of the corpus is not permitted — terms: https://whatsnew.fyi/terms. The vendors' own release notes remain their publishers'. --- - **added** — Add `--max-ocr-image-mpixels` option to downsample images sent to OCR when a page exceeds the given size, bounding memory consumption while preserving visible page appearance - **added** — Add `--output-structure` option to `watcher.py` with FLAT, YEAR_MONTH, and HIERARCHY layout choices for output files - **added** — Add `--on-conflict` option to `watcher.py` to control behavior when destination file already exists, with SUFFIX, SKIP, and OVERWRITE choices - **changed** — Change default conflict policy in `watcher.py` from silently overwriting to using SUFFIX mode, so existing output files are no longer overwritten by default - **changed** — Apply output structure and conflict policy settings equally to both `OCR_OUTPUT_DIRECTORY` and archive directory used by `OCR_ON_SUCCESS_ARCHIVE` - **changed** — Sanitize output and archive filenames and directory components for filesystems more restrictive than the input side, replacing illegal Windows/SMB characters and reserved DOS device names - **deprecated** — Deprecate `OCR_OUTPUT_DIRECTORY_YEAR_MONTH` in favor of `OCR_OUTPUT_STRUCTURE=YEAR_MONTH`, which takes precedence if both are set - **changed** — Avoid decoding and re-encoding OCR images when nothing needs to change them, linking the rasterized page directly instead on pages with no pre-existing text to mask and no filtering plugin - **changed** — Defer mask building until a text area actually needs blanking, eliminating forced image decode steps - **changed** — Reduce peak memory consumption on files with large images by approximately one third at default settings through rasterization improvements and earlier heap memory return to operating system - **changed** — Allow multiple OCR jobs to run concurrently in a single Python process using a readers-writer lock for plugin infrastructure instead of a process-wide lock - **changed** — Install a plugin set once per interpreter and reuse it across calls instead of reinstalling on every `ocrmypdf.ocr()` call - **changed** — Substantially speed up image optimization on documents with large images by repackaging `/FlateDecode` images with PNG predictor directly as PNG instead of decoding and re-encoding - **changed** — Raise minimum Pillow version to 12 to support image decoding detection improvements - **removed** — Remove full-page buffer allocation during rasterization that previously corrected PDFium rounding errors by a pixel or two **Enhancements** - New `--max-ocr-image-mpixels` downsamples the image sent to OCR when a page exceeds the given size, which bounds the largest consumer of memory. The visible page is never downsampled, so output appearance is unaffected in every mode; what it trades is OCR accuracy on very high resolution scans. A 34 megapixel page that peaks at 492 MB peaks at 325 MB under `--max-ocr-image-mpixels 8`, and recognizes the same text. See the "Memory" section of the performance documentation for how to size a memory limit. - `watcher.py` (the `watcher` extra) gained a configurable output layout and conflict policy. These are watcher-only changes; they do not affect the `ocrmypdf` library API. - New `OCR_OUTPUT_STRUCTURE` setting (`--output-structure`): `FLAT` (default, all outputs directly in the destination directory), `YEAR_MONTH` (`{destination}/{year}/{month}/{filename}`, same layout as the old `OCR_OUTPUT_DIRECTORY_YEAR_MONTH=1`), or `HIERARCHY`, which mirrors the input directory tree under the destination, e.g. `input/a/b/c.pdf` → `output/a/b/c.pdf`. - New `OCR_ON_CONFLICT` setting (`--on-conflict`) controls what happens when the intended destination file already exists: `SUFFIX` (default) writes `name (1).pdf`, `name (2).pdf`, ... in the OS style; `SKIP` logs and leaves the file unprocessed; `OVERWRITE` is the old behavior. **Behavior change: the default is now `SUFFIX`, so existing output files are no longer silently overwritten.** - Both settings now apply equally to `OCR_OUTPUT_DIRECTORY` and to the archive directory used by `OCR_ON_SUCCESS_ARCHIVE`; previously the archive directory was always flat and silently overwrote on a name collision. - Output and archive filenames and directory components are sanitized for filesystems more restrictive than the input side (e.g. an SMB share): characters illegal on Windows/SMB (`<>:"/\|?*` and control characters) are replaced with `_`, trailing dots/spaces are stripped, and reserved DOS device names (`CON`, `PRN`, `AUX`, `NUL`, `COM1`-`9`, `LPT1`-`9`) are prefixed with `_`. - `OCR_OUTPUT_DIRECTORY_YEAR_MONTH` is now deprecated in favor of `OCR_OUTPUT_STRUCTURE=YEAR_MONTH`. It is still honored and logs a deprecation warning; if both are set, `OCR_OUTPUT_STRUCTURE` wins. - See the "Watched folders with watcher.py" section of the batch processing documentation for the full description, including a note for SMB users about client-side directory/file-info caching delays. **Performance** - The image sent to OCR is no longer decoded and re-encoded when nothing needs to change it. On a page with no pre-existing text to mask and no filtering plugin -- the ordinary case for a scanned document -- the rasterized page already *is* the OCR image, so it is linked rather than rewritten. Producing it was costing 3.3 seconds and a full-size buffer on a 34 megapixel page, out of about 15 seconds for the whole file. Building the mask is now deferred until a text area actually needs blanking, since that step is what forced the decode. The minimum Pillow version is raised to 12, because deciding whether anything decoded the image reads an attribute whose shape settled in Pillow 11 (`pi-heif` already required Pillow 11.1, so the effective floor barely moves). - Reduced peak memory on files with large images by about a third at default settings. On a 34 megapixel page the process tree peaked at 752 MB and now peaks at 492 MB. Two changes account for it: rasterizing a page no longer allocates a third full-page buffer to correct PDFium's rounding of the rendered size by a pixel or two, and freed heap memory is now returned to the operating system before the OCR engine runs, instead of counting against our resident set for as long as the engine is working. Rasterization is also faster, since correcting _[Truncated at 4000 characters — full notes: https://github.com/ocrmypdf/OCRmyPDF/releases/tag/v17.11.0]_