mirror of
https://github.com/ocrmypdf/OCRmyPDF.git
synced 2026-07-30 23:17:52 -04:00
Writing the output PDF to stdout (ocrmypdf in.pdf -) previously relied on an honor system: no in-process code -- third-party libraries, plugins, or stray print() calls -- was supposed to write to stdout, enforced only indirectly. A single accidental write to fd 1 would silently corrupt the output PDF. Enforce this at the OS level. At CLI startup, before plugins load or any worker process/thread starts, save the real stdout via os.dup() and point fd 1 at stderr, so stray writes are diverted to stderr while only the final "produce the PDF" step writes to the preserved descriptor. Exposed as the opt-in public API function configure_stdout_protection(), mirroring configure_logging(); it is not enabled inside ocr() so in-process library users keep their own stdout. Also fix check_requested_output_file() to test the preserved real stdout for tty-ness, since after the redirect sys.stdout reports stderr's status. Fold unreleased v17.7.2 notes into v17.8.0.
43 lines
1.1 KiB
Python
43 lines
1.1 KiB
Python
# SPDX-FileCopyrightText: 2024 James R. Barlow
|
|
# SPDX-License-Identifier: MIT
|
|
"""Test plugin that deliberately writes garbage to stdout.
|
|
|
|
Used to verify that OCRmyPDF's stdout protection diverts stray writes (from
|
|
plugins or libraries) to stderr, so that a PDF written to stdout is never
|
|
corrupted. Pollutes at three points: plugin import (main process), the
|
|
``validate`` hook (main process), and the ``filter_ocr_image`` hook (worker
|
|
process/thread).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
|
|
from ocrmypdf import hookimpl
|
|
|
|
POLLUTION = b'POLLUTION'
|
|
|
|
|
|
def _pollute(where: bytes) -> None:
|
|
# Write to file descriptor 1 directly (as a careless C library might) and
|
|
# via Python's sys.stdout (as a stray print() might).
|
|
os.write(1, POLLUTION + b'-fd1-' + where + b'\n')
|
|
print(POLLUTION.decode() + '-stdout-' + where.decode())
|
|
sys.stdout.flush()
|
|
|
|
|
|
# Pollute at import time, which happens while plugins are being loaded.
|
|
_pollute(b'import')
|
|
|
|
|
|
@hookimpl
|
|
def validate(pdfinfo, options):
|
|
_pollute(b'validate')
|
|
|
|
|
|
@hookimpl
|
|
def filter_ocr_image(page, image):
|
|
_pollute(b'filter_ocr_image')
|
|
return image
|