diff --git a/src/ocrmypdf/_options.py b/src/ocrmypdf/_options.py index e686ab92..275483df 100644 --- a/src/ocrmypdf/_options.py +++ b/src/ocrmypdf/_options.py @@ -188,6 +188,9 @@ class OcrOptions(BaseModel): """Compatibility alias for jpg_quality.""" self.jpg_quality = value + # Output behavior + no_overwrite: bool = False + # Advanced options max_image_mpixels: float = 250.0 pdf_renderer: str = 'auto' diff --git a/src/ocrmypdf/_validation.py b/src/ocrmypdf/_validation.py index 06927daa..a662ee69 100644 --- a/src/ocrmypdf/_validation.py +++ b/src/ocrmypdf/_validation.py @@ -217,6 +217,17 @@ def check_requested_output_file(options: OcrOptions) -> None: f"Output file location ({options.output_file}) is not a writable file." ) + if ( + options.no_overwrite + and not hasattr(options.output_file, 'writable') + and options.output_file != '-' + and Path(str(options.output_file)).exists() + ): + raise OutputFileAccessError( + f"Output file already exists: {options.output_file}\n" + "To overwrite it, omit the --no-overwrite / -n option." + ) + def report_output_file_size( options: OcrOptions, diff --git a/src/ocrmypdf/api.py b/src/ocrmypdf/api.py index 605b8b1c..1bd30995 100644 --- a/src/ocrmypdf/api.py +++ b/src/ocrmypdf/api.py @@ -440,6 +440,7 @@ def ocr( continue_on_soft_render_error: bool | None = None, invalidate_digital_signatures: bool | None = None, tagged_pdf_mode: str | None = None, + no_overwrite: bool | None = None, plugins: Iterable[Path | str] | None = None, plugin_manager: OcrmypdfPluginManager | None = None, keep_temporary_files: bool | None = None, @@ -502,6 +503,7 @@ def ocr( # noqa: D417 continue_on_soft_render_error: bool | None = None, invalidate_digital_signatures: bool | None = None, tagged_pdf_mode: str | None = None, + no_overwrite: bool | None = None, plugins: Iterable[Path | str] | None = None, plugin_manager: OcrmypdfPluginManager | None = None, keep_temporary_files: bool | None = None, diff --git a/src/ocrmypdf/cli.py b/src/ocrmypdf/cli.py index e590dbac..a4bbf2ff 100644 --- a/src/ocrmypdf/cli.py +++ b/src/ocrmypdf/cli.py @@ -137,8 +137,9 @@ Online documentation is located at: 'output_file', metavar="output_pdf", help="Output searchable PDF file (or '-' to write to standard output). " - "Existing files will be overwritten. If same as input file, the " - "input file will be updated only if processing is successful.", + "Existing files will be overwritten (use --no-overwrite to prevent this). " + "If same as input file, the input file will be updated only if " + "processing is successful.", ) parser.add_argument( '-l', @@ -190,6 +191,15 @@ Online documentation is located at: "may not both use stdout at the same time.", ) + parser.add_argument( + '-n', + '--no-overwrite', + action='store_true', + default=False, + help="If the output file already exists, exit with an error instead of " + "overwriting it.", + ) + parser.add_argument( '--version', action='version', diff --git a/uv.lock b/uv.lock index 82dc47ce..0a993f95 100644 --- a/uv.lock +++ b/uv.lock @@ -1283,7 +1283,7 @@ wheels = [ [[package]] name = "ocrmypdf" -version = "17.2.0" +version = "17.3.0" source = { editable = "." } dependencies = [ { name = "deprecation" },