Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21cacad93b | ||
|
|
3589f4e7d1 | ||
|
|
1cdc2591e5 | ||
|
|
e05f9575a8 | ||
|
|
10c703e119 | ||
|
|
0ac15dd0b2 | ||
|
|
808b24d59f | ||
|
|
c082526dea | ||
|
|
33cdabaf65 | ||
|
|
94f8e36601 | ||
|
|
865002c7be | ||
|
|
5d0cc0a092 | ||
|
|
6c427f82ea | ||
|
|
e7a44ba87a | ||
|
|
c311768452 | ||
|
|
f53fedee63 | ||
|
|
87838127b0 | ||
|
|
4db4df5c72 | ||
|
|
11125c5367 | ||
|
|
e648411067 | ||
|
|
11365575d7 | ||
|
|
845cb5c40c | ||
|
|
b699e158be | ||
|
|
603da52026 | ||
|
|
8d0765a5e0 | ||
|
|
1ca327e13b | ||
|
|
f504fd1875 | ||
|
|
cf7c20ca16 | ||
|
|
b00fe3dc5d | ||
|
|
e6aa3a4299 | ||
|
|
24f1b57288 | ||
|
|
43302d7e12 | ||
|
|
fed0226761 | ||
|
|
27e22b4f07 |
+1
-1
@@ -1,7 +1,7 @@
|
||||
# OCRmyPDF
|
||||
#
|
||||
|
||||
FROM debian:bookworm-slim as base
|
||||
FROM ubuntu:22.04 as base
|
||||
|
||||
ENV LANG=C.UTF-8
|
||||
ENV TZ=UTC
|
||||
|
||||
@@ -22,7 +22,7 @@ Run with verbosity or higher `-v1` to see more detailed logging. This informatio
|
||||
**Example file**
|
||||
If your issue is a problem that affects only certain files, and we will require an input file (PDF or image) that demonstrates your issue.
|
||||
|
||||
Please provide an input file with no personal or confidential information. At your option you may [GPG-encrypt the file](https://github.com/jbarlow83/OCRmyPDF/wiki) for OCRmyPDF's author only.
|
||||
Please provide an input file with no personal or confidential information. At your option you may [GPG-encrypt the file](https://github.com/ocrmypdf/OCRmyPDF/wiki) for OCRmyPDF's author only.
|
||||
|
||||
Links to files hosted elsewhere are perfectly acceptable. You could also look in ``tests/resources`` and see if any of those files reproduce your issue.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ A clear and concise description of any alternative solutions or features you've
|
||||
**Example file**
|
||||
If your issue concerns how OCRmyPDF processes certain files, and please provide an example file that helps illustrate how OCRmyPDF's output could be improve.
|
||||
|
||||
Please provide an input file with no personal or confidential information. At your option you may [GPG-encrypt the file](https://github.com/jbarlow83/OCRmyPDF/wiki) for OCRmyPDF's author only.
|
||||
Please provide an input file with no personal or confidential information. At your option you may [GPG-encrypt the file](https://github.com/ocrmypdf/OCRmyPDF/wiki) for OCRmyPDF's author only.
|
||||
|
||||
Links to files hosted elsewhere are perfectly acceptable. You could also look in ``tests/resources`` and see if any of those files reproduce your issue.
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
- os: ubuntu-latest
|
||||
python: "3.9"
|
||||
- os: ubuntu-latest
|
||||
python: "pypy-3.7"
|
||||
python: "pypy-3.8"
|
||||
- os: ubuntu-latest
|
||||
python: "3.9"
|
||||
tesseract5: true
|
||||
@@ -196,7 +196,7 @@ jobs:
|
||||
- name: Install system packages
|
||||
run: |
|
||||
choco install --yes --no-progress --pre tesseract
|
||||
choco install --yes --no-progress --ignore-checksums ghostscript pngquant
|
||||
choco install --yes --no-progress --ignore-checksums ghostscript
|
||||
|
||||
- name: Install Python packages
|
||||
run: |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0
|
||||
rev: v4.2.0
|
||||
hooks:
|
||||
- id: check-case-conflict
|
||||
- id: check-merge-conflict
|
||||
@@ -22,12 +22,12 @@ repos:
|
||||
hooks:
|
||||
- id: setup-cfg-fmt
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.31.1
|
||||
rev: v2.32.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: ["--py37-plus"]
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.942
|
||||
rev: v0.950
|
||||
hooks:
|
||||
- id: mypy
|
||||
additional_dependencies:
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<img src="docs/images/logo.svg" width="240" alt="OCRmyPDF">
|
||||
|
||||
[](https://github.com/jbarlow83/OCRmyPDF/actions/workflows/build.yml) [![PyPI version][pypi]](https://pypi.org/project/ocrmypdf/) ![Homebrew version][homebrew] ![ReadTheDocs][docs] ![Python versions][pyversions]
|
||||
[](https://github.com/ocrmypdf/OCRmyPDF/actions/workflows/build.yml) [![PyPI version][pypi]](https://pypi.org/project/ocrmypdf/) ![Homebrew version][homebrew] ![ReadTheDocs][docs] ![Python versions][pyversions]
|
||||
|
||||
[azure]: https://dev.azure.com/jim0585/ocrmypdf/_apis/build/status/jbarlow83.OCRmyPDF?branchName=master
|
||||
[travis]: https://travis-ci.org/jbarlow83/OCRmyPDF.svg?branch=master "Travis build status"
|
||||
[pypi]: https://img.shields.io/pypi/v/ocrmypdf.svg "PyPI version"
|
||||
[homebrew]: https://img.shields.io/homebrew/v/ocrmypdf.svg "Homebrew version"
|
||||
[docs]: https://readthedocs.org/projects/ocrmypdf/badge/?version=latest "RTD"
|
||||
@@ -106,7 +104,7 @@ ocrmypdf --help
|
||||
|
||||
Our [documentation is served on Read the Docs](https://ocrmypdf.readthedocs.io/en/latest/index.html).
|
||||
|
||||
Please report issues on our [GitHub issues](https://github.com/jbarlow83/OCRmyPDF/issues) page, and follow the issue template for quick response.
|
||||
Please report issues on our [GitHub issues](https://github.com/ocrmypdf/OCRmyPDF/issues) page, and follow the issue template for quick response.
|
||||
|
||||
## Requirements
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: OCRmyPDF
|
||||
Upstream-Contact: James R. Barlow <barlow.jim@gmail.com>
|
||||
Source: https://github.com/jbarlow83/OCRmyPDF
|
||||
Source: https://github.com/ocrmypdf/OCRmyPDF
|
||||
|
||||
Files: *
|
||||
Copyright:
|
||||
|
||||
+5
-5
@@ -67,11 +67,11 @@ without modifying the PDF. This is to ensure that PDFs that were
|
||||
previously OCRed or were "born digital" rather than scanned are not
|
||||
processed.
|
||||
|
||||
If ``--skip-text`` is issued, then no OCR will be performed on pages
|
||||
that already have text. The page will be copied to the output. This may
|
||||
be useful for documents that contain both "born digital" and scanned
|
||||
content, or to use OCRmyPDF to normalize and convert to PDF/A regardless
|
||||
of their contents.
|
||||
If ``--skip-text`` is issued, then no image processing or OCR will be
|
||||
performed on pages that already have text. The page will be copied to
|
||||
the output. This may be useful for documents that contain both "born
|
||||
digital" and scanned content, or to use OCRmyPDF to normalize and
|
||||
convert to PDF/A regardless of their contents.
|
||||
|
||||
If ``--redo-ocr`` is issued, then a detailed text analysis is performed.
|
||||
Text is categorized as either visible or invisible. Invisible text (OCR)
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ extensions = [
|
||||
# Extension settings
|
||||
intersphinx_mapping = {'https://docs.python.org/': None}
|
||||
napoleon_use_rtype = False
|
||||
issues_github_path = "jbarlow83/OCRmyPDF"
|
||||
issues_github_path = "ocrmypdf/OCRmyPDF"
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
+8
-7
@@ -243,10 +243,11 @@ You can also optimize all images without performing any OCR:
|
||||
|
||||
ocrmypdf --tesseract-timeout=0 --optimize 3 --skip-text input.pdf output.pdf
|
||||
|
||||
Perform OCR only certain pages
|
||||
------------------------------
|
||||
Process only certain pages
|
||||
--------------------------
|
||||
|
||||
You can ask OCRmyPDF to only apply OCR to certain pages.
|
||||
You can ask OCRmyPDF to only apply `image processing <#image-processing>`__
|
||||
and OCR to certain pages.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -260,10 +261,10 @@ overlap pages. OCRmyPDF does not currently account for document page numbers,
|
||||
such as an introduction section of a book that uses Roman numerals. It simply
|
||||
counts the number of virtual pieces of paper since the start.
|
||||
|
||||
Regardless of the argument to ``--pages``, OCRmyPDF will optimize all pages in
|
||||
the file and convert it to PDF/A, unless you disable those options. In this
|
||||
example, we want to OCR only the title and otherwise change the PDF as little
|
||||
as possible:
|
||||
Regardless of the argument to ``--pages``, OCRmyPDF will optimize all pages/images
|
||||
in the file and convert it to PDF/A, unless you disable those options. Both of these
|
||||
steps are "whole file" operations. In this example, we want to OCR only the title
|
||||
and otherwise change the PDF as little as possible:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
||||
@@ -375,7 +375,7 @@ languages you can optionally install them all:
|
||||
.. note::
|
||||
|
||||
Users who previously installed OCRmyPDF from the private tap should
|
||||
switch to the mainline version (``brew untap jbarlow83/ocrmypdf``)
|
||||
switch to the mainline version (``brew untap ocrmypdf/OCRmyPDF``)
|
||||
and install from there.
|
||||
|
||||
Manual installation on macOS
|
||||
@@ -696,7 +696,7 @@ environment:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip3 install git+https://github.com/jbarlow83/OCRmyPDF.git
|
||||
pip3 install git+https://github.com/ocrmypdf/OCRmyPDF.git
|
||||
|
||||
Or, to install in `development
|
||||
mode <https://pythonhosted.org/setuptools/setuptools.html#development-mode>`__,
|
||||
@@ -704,14 +704,14 @@ allowing customization of OCRmyPDF, use the ``-e`` flag:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip3 install -e git+https://github.com/jbarlow83/OCRmyPDF.git
|
||||
pip3 install -e git+https://github.com/ocrmypdf/OCRmyPDF.git
|
||||
|
||||
You may find it easiest to install in a virtual environment, rather than
|
||||
system-wide:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone -b master https://github.com/jbarlow83/OCRmyPDF.git
|
||||
git clone -b master https://github.com/ocrmypdf/OCRmyPDF.git
|
||||
python3 -m venv
|
||||
source venv/bin/activate
|
||||
cd OCRmyPDF
|
||||
@@ -738,7 +738,7 @@ To install all of the development and test requirements:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone -b master https://github.com/jbarlow83/OCRmyPDF.git
|
||||
git clone -b master https://github.com/ocrmypdf/OCRmyPDF.git
|
||||
python3 -m venv
|
||||
source venv/bin/activate
|
||||
cd OCRmyPDF
|
||||
|
||||
@@ -32,6 +32,9 @@ For all other Linux, you must build a JBIG2 encoder from source:
|
||||
|
||||
.. _jbig2-lossy:
|
||||
|
||||
Dependencies include libtoolize and libleptonica, which on Ubuntu systems
|
||||
are packaged as libtool and libleptonica-dev.
|
||||
|
||||
Lossy mode JBIG2
|
||||
================
|
||||
|
||||
|
||||
@@ -54,6 +54,33 @@ to what languages it should search for. Multiple languages can be
|
||||
requested using either ``-l eng+fra`` (English and French) or
|
||||
``-l eng -l fra``.
|
||||
|
||||
Gentoo users
|
||||
============
|
||||
|
||||
On Gentoo the package ``app-text/tessdata_fast``, which ``app-text/tesseract`` depends on, handles Tesseract languages.
|
||||
It accepts USE flags to select what languages should be installed, these can be set in ``/etc/portage/package.use``.
|
||||
Alternatively one can globally set the `L10N use extension <https://wiki.gentoo.org/wiki/Localization/Guide#L10N>`__ in ``/etc/portage/make.conf``.
|
||||
This enables these languages for all packages (e.g. including aspell).
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Display a list of all Tesseract language packs
|
||||
equery uses app-text/tessdata_fast
|
||||
|
||||
# Add English and German language support for Tesseract only
|
||||
echo 'app-text/tessdata_fast l10n_de l10n_en' >> /etc/portage/package.use
|
||||
|
||||
# Add global English and German language support (the `l10n_` from equery has to be omited)
|
||||
echo L10N="de en" >> /etc/portage/make.conf
|
||||
|
||||
# update system to reflect changed USE flags
|
||||
emerge --update --deep --newuse @world
|
||||
|
||||
You can then pass the ``-l LANG`` argument to OCRmyPDF to give a hint as
|
||||
to what languages it should search for. Multiple languages can be
|
||||
requested using either ``-l eng+fra`` (English and French) or
|
||||
``-l eng -l fra``.
|
||||
|
||||
macOS users
|
||||
===========
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Maintainer notes
|
||||
================
|
||||
|
||||
This is for those who package OCRmyPDF for downstream use. (Thank you
|
||||
This is for those who package OCRmyPDF for downstream use. (Thank you
|
||||
for your hard work.)
|
||||
|
||||
Known ports/packagers
|
||||
@@ -25,7 +25,7 @@ Non-Python dependencies
|
||||
|
||||
Note that we have non-Python dependencies. In particular, OCRmyPDF requires
|
||||
Ghostscript and Tesseract OCR to be installed and needs to be able to locate their
|
||||
binaries on the system PATH. On Windows, OCRmyPDF will also check the registry
|
||||
binaries on the system PATH. On Windows, OCRmyPDF will also check the registry
|
||||
for their locations.
|
||||
|
||||
Tesseract OCR relies on SIMD for performance and only has proper support for this
|
||||
@@ -38,13 +38,13 @@ OCRmyPDF uses setuptools-scm for versioning, which derives the version from
|
||||
Git as a single source of truth. This may be unsuitable for some distributions, e.g.
|
||||
to indicate that your distribution modifies OCRmyPDF in some way.
|
||||
|
||||
You can patch the ``__version__`` variable in ``src/ocrmypdf/_version.py`` if
|
||||
You can patch the ``__version__`` variable in ``src/ocrmypdf/_version.py`` if
|
||||
necessary.
|
||||
|
||||
OCRmyPDF uses setuptools-scm-git-archive to ensure that tarballs downloaded from
|
||||
GitHub contain version information. Unfortunately, these tarballs are not always
|
||||
deterministic. See this
|
||||
`issue <https://github.com/jbarlow83/OCRmyPDF/issues/841#issuecomment-936562696>`_.
|
||||
deterministic. See this
|
||||
`issue <https://github.com/ocrmypdf/OCRmyPDF/issues/841#issuecomment-936562696>`_.
|
||||
|
||||
jbig2enc
|
||||
--------
|
||||
|
||||
@@ -18,6 +18,38 @@ tagged yet.
|
||||
|
||||
.. |OCRmyPDF PyPI| image:: https://img.shields.io/pypi/v/ocrmypdf.svg
|
||||
|
||||
v13.4.7
|
||||
=======
|
||||
|
||||
- Fixed PermissionError when cleaning up temporary files in rare cases. :issue:`974`
|
||||
- Fixed PermissionError when calling ``os.nice`` on platforms that lack it. :issue:`973`
|
||||
- Suppressed some warnings from libxmp during tests.
|
||||
|
||||
v13.4.6
|
||||
=======
|
||||
|
||||
- Convert error on corrupt ICC profiles into a warning. Thanks to @oscherler.
|
||||
|
||||
v13.4.5
|
||||
=======
|
||||
|
||||
- Remove upper bound on pdfminer.six version.
|
||||
- Documentation.
|
||||
|
||||
v13.4.4
|
||||
=======
|
||||
|
||||
- Updated pdfminer.six version.
|
||||
- Docker image changed to Ubuntu 22.04 now that it is released and provides the
|
||||
dependencies we need. This seems more consistent than our recent change to
|
||||
Debian.
|
||||
|
||||
v13.4.3
|
||||
=======
|
||||
|
||||
- Fix error on pytest.skip() with older versions of pytest.
|
||||
- Documentation updates.
|
||||
|
||||
v13.4.2
|
||||
=======
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ name = ocrmypdf
|
||||
description = OCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to be searched
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
url = https://github.com/jbarlow83/OCRmyPDF
|
||||
url = https://github.com/ocrmypdf/OCRmyPDF
|
||||
author = James R. Barlow
|
||||
author_email = james@purplerock.ca
|
||||
license = MPL-2.0
|
||||
@@ -39,8 +39,8 @@ keywords =
|
||||
scanning
|
||||
project_urls =
|
||||
Documentation = https://ocrmypdf.readthedocs.io/
|
||||
Source = https://github.com/jbarlow83/ocrmypdf
|
||||
Tracker = https://github.com/jbarlow83/ocrmypdf/issues
|
||||
Source = https://github.com/ocrmypdf/OCRmyPDF
|
||||
Tracker = https://github.com/ocrmypdf/OCRmyPDF/issues
|
||||
|
||||
[options]
|
||||
packages = find:
|
||||
@@ -49,7 +49,7 @@ install_requires =
|
||||
coloredlogs>=14.0 # strictly optional
|
||||
img2pdf>=0.3.0,<0.5 # pure Python
|
||||
packaging>=20
|
||||
pdfminer.six!=20200720,>=20191110,<=20220319
|
||||
pdfminer.six!=20200720,>=20191110
|
||||
pikepdf!=5.0.0,>=4.0.0
|
||||
pluggy>=0.13.0,<2
|
||||
reportlab>=3.5.66
|
||||
|
||||
@@ -10,6 +10,7 @@ import logging
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
from contextlib import suppress
|
||||
from multiprocessing import set_start_method
|
||||
|
||||
from ocrmypdf import __version__
|
||||
@@ -34,7 +35,7 @@ def sigbus(*args):
|
||||
def run(args=None):
|
||||
_parser, options, plugin_manager = get_parser_options_plugins(args=args)
|
||||
|
||||
if hasattr(os, 'nice'):
|
||||
with suppress(AttributeError, PermissionError):
|
||||
os.nice(5)
|
||||
|
||||
verbosity = options.verbose
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
import logging
|
||||
import os
|
||||
import shlex
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
from decimal import Decimal
|
||||
from pathlib import Path
|
||||
from subprocess import PIPE, STDOUT
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Iterator, List, Optional, Tuple, Union
|
||||
|
||||
from PIL import Image
|
||||
@@ -25,6 +25,23 @@ from PIL import Image
|
||||
from ocrmypdf.exceptions import MissingDependencyError, SubprocessOutputError
|
||||
from ocrmypdf.subprocess import get_version, run
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
from tempfile import TemporaryDirectory
|
||||
else:
|
||||
from tempfile import TemporaryDirectory as _TemporaryDirectory
|
||||
|
||||
# Consume the ignore_cleanup_errors kwarg in Python 3.9 and older, without acting
|
||||
# on this keyword. Users who need this issue full resolved should upgrade to Python
|
||||
# 3.10.
|
||||
# See: https://github.com/python/cpython/pull/24793
|
||||
|
||||
class TemporaryDirectory(_TemporaryDirectory):
|
||||
def __init__(self, ignore_cleanup_errors=False, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
del _TemporaryDirectory
|
||||
|
||||
|
||||
UNPAPER_IMAGE_PIXEL_LIMIT = 256 * 1024 * 1024
|
||||
|
||||
DecFloat = Union[Decimal, float]
|
||||
@@ -82,7 +99,7 @@ def _setup_unpaper_io(input_file: Path) -> Iterator[Tuple[Path, Path, Path]]:
|
||||
raise UnpaperImageTooLargeError(w=im.width, h=im.height)
|
||||
im, im_modified, suffix = _convert_image(im)
|
||||
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
with TemporaryDirectory(ignore_cleanup_errors=True) as tmpdir:
|
||||
tmppath = Path(tmpdir)
|
||||
if im_modified or input_file.suffix != '.pnm':
|
||||
input_pnm = tmppath / 'input.pnm'
|
||||
|
||||
+1
-1
@@ -295,7 +295,7 @@ def ocr( # pylint: disable=unused-argument
|
||||
text already, and settings did not tell us to proceed.
|
||||
ocrmypdf.InputFileError: Any other problem with the input file.
|
||||
ocrmypdf.SubprocessOutputError: Any error related to executing a subprocess.
|
||||
ocrmypdf.EncryptedPdfERror: If the input PDF is encrypted (password protected).
|
||||
ocrmypdf.EncryptedPdfError: If the input PDF is encrypted (password protected).
|
||||
OCRmyPDF does not remove passwords.
|
||||
ocrmypdf.TesseractConfigError: If Tesseract reported its configuration was not
|
||||
valid.
|
||||
|
||||
@@ -37,6 +37,7 @@ from pikepdf import (
|
||||
PdfImage,
|
||||
PdfInlineImage,
|
||||
PdfMatrix,
|
||||
UnsupportedImageTypeError,
|
||||
parse_content_stream,
|
||||
)
|
||||
|
||||
@@ -350,13 +351,20 @@ class ImageInfo:
|
||||
|
||||
if self._color == Colorspace.icc:
|
||||
# Check the ICC profile to determine actual colorspace
|
||||
pim_icc = pim.icc
|
||||
if pim_icc.profile.xcolor_space == 'GRAY':
|
||||
self._comp = 1
|
||||
elif pim_icc.profile.xcolor_space == 'CMYK':
|
||||
self._comp = 4
|
||||
else:
|
||||
self._comp = 3
|
||||
try:
|
||||
pim_icc = pim.icc
|
||||
if pim_icc.profile.xcolor_space == 'GRAY':
|
||||
self._comp = 1
|
||||
elif pim_icc.profile.xcolor_space == 'CMYK':
|
||||
self._comp = 4
|
||||
else:
|
||||
self._comp = 3
|
||||
except UnsupportedImageTypeError as ex:
|
||||
self._comp = None
|
||||
logger.warning(
|
||||
f"An image with a corrupt or unreadable ICC profile was found. "
|
||||
f"The output PDF may not match the input PDF visually: {ex}. {self}"
|
||||
)
|
||||
else:
|
||||
if isinstance(self._color, Colorspace):
|
||||
self._comp = FRIENDLY_COMP.get(self._color)
|
||||
@@ -409,15 +417,10 @@ class ImageInfo:
|
||||
return _get_dpi(self._shorthand, (self._width, self._height))
|
||||
|
||||
def __repr__(self):
|
||||
class_locals = {
|
||||
attr: getattr(self, attr, None)
|
||||
for attr in dir(self)
|
||||
if not attr.startswith('_')
|
||||
}
|
||||
return (
|
||||
"<ImageInfo '{name}' {type_} {width}x{height} {color} "
|
||||
"{comp} {bpc} {enc} {dpi}>"
|
||||
).format(**class_locals)
|
||||
f"<ImageInfo '{self.name}' {self.type_} {self.width}x{self.height} "
|
||||
f"{self.color} {self.comp} {self.bpc} {self.enc} {self.dpi}>"
|
||||
)
|
||||
|
||||
|
||||
def _find_inline_images(contentsinfo: ContentsInfo) -> Iterator[ImageInfo]:
|
||||
|
||||
@@ -50,11 +50,11 @@ import logging
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import threading
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
from subprocess import PIPE, CalledProcessError, CompletedProcess
|
||||
from unittest.mock import patch
|
||||
import threading
|
||||
|
||||
from ocrmypdf import hookimpl
|
||||
from ocrmypdf.builtin_plugins.tesseract_ocr import TesseractOcrEngine
|
||||
@@ -177,28 +177,40 @@ def cached_run(options, run_args, **run_kwargs):
|
||||
|
||||
|
||||
class CacheOcrEngine(TesseractOcrEngine):
|
||||
# Concurrent threads (with --use-threads) might try to use different parts
|
||||
# of the OcrEngine, so we need a lock to protect the state of patched
|
||||
# module whenever it's patched. Should refactor ocrmypdf._exec.tesseract so that
|
||||
# it does not to be patched at all for testing.
|
||||
lock = threading.Lock()
|
||||
|
||||
@staticmethod
|
||||
def get_orientation(input_file, options):
|
||||
with CacheOcrEngine.lock, patch('ocrmypdf._exec.tesseract.run', new=partial(cached_run, options)):
|
||||
return TesseractOcrEngine.get_orientation(input_file, options)
|
||||
with CacheOcrEngine.lock, patch(
|
||||
'ocrmypdf._exec.tesseract.run', new=partial(cached_run, options)
|
||||
):
|
||||
return TesseractOcrEngine.get_orientation(input_file, options)
|
||||
|
||||
@staticmethod
|
||||
def get_deskew(input_file, options) -> float:
|
||||
with CacheOcrEngine.lock, patch('ocrmypdf._exec.tesseract.run', new=partial(cached_run, options)):
|
||||
with CacheOcrEngine.lock, patch(
|
||||
'ocrmypdf._exec.tesseract.run', new=partial(cached_run, options)
|
||||
):
|
||||
return TesseractOcrEngine.get_deskew(input_file, options)
|
||||
|
||||
@staticmethod
|
||||
def generate_hocr(input_file, output_hocr, output_text, options):
|
||||
with CacheOcrEngine.lock, patch('ocrmypdf._exec.tesseract.run', new=partial(cached_run, options)):
|
||||
with CacheOcrEngine.lock, patch(
|
||||
'ocrmypdf._exec.tesseract.run', new=partial(cached_run, options)
|
||||
):
|
||||
TesseractOcrEngine.generate_hocr(
|
||||
input_file, output_hocr, output_text, options
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def generate_pdf(input_file, output_pdf, output_text, options):
|
||||
with CacheOcrEngine.lock, patch('ocrmypdf._exec.tesseract.run', new=partial(cached_run, options)):
|
||||
with CacheOcrEngine.lock, patch(
|
||||
'ocrmypdf._exec.tesseract.run', new=partial(cached_run, options)
|
||||
):
|
||||
TesseractOcrEngine.generate_pdf(
|
||||
input_file, output_pdf, output_text, options
|
||||
)
|
||||
|
||||
+50
-11
@@ -465,12 +465,18 @@ def test_overlay(resources, outpdf):
|
||||
)
|
||||
|
||||
|
||||
def test_destination_not_writable(resources, outdir):
|
||||
if os.name != 'nt' and (os.getuid() == 0 or os.geteuid() == 0):
|
||||
pytest.xfail(reason="root can write to anything")
|
||||
@pytest.fixture
|
||||
def protected_file(outdir):
|
||||
protected_file = outdir / 'protected.pdf'
|
||||
protected_file.touch()
|
||||
protected_file.chmod(0o400) # Read-only
|
||||
yield protected_file
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.name == 'nt' or os.geteuid() == 0, reason="root can write to anything"
|
||||
)
|
||||
def test_destination_not_writable(resources, protected_file):
|
||||
p = run_ocrmypdf(
|
||||
resources / 'jbig2.pdf',
|
||||
protected_file,
|
||||
@@ -480,7 +486,8 @@ def test_destination_not_writable(resources, outdir):
|
||||
assert p.returncode == ExitCode.file_access_error, "Expected error"
|
||||
|
||||
|
||||
def test_tesseract_config_valid(resources, outdir):
|
||||
@pytest.fixture
|
||||
def valid_tess_config(outdir):
|
||||
cfg_file = outdir / 'test.cfg'
|
||||
with cfg_file.open('w') as f:
|
||||
f.write(
|
||||
@@ -490,20 +497,22 @@ language_model_penalty_non_dict_word 0
|
||||
language_model_penalty_non_freq_dict_word 0
|
||||
'''
|
||||
)
|
||||
yield cfg_file
|
||||
|
||||
|
||||
def test_tesseract_config_valid(resources, valid_tess_config, outpdf):
|
||||
check_ocrmypdf(
|
||||
resources / '3small.pdf',
|
||||
outdir / 'out.pdf',
|
||||
outpdf,
|
||||
'--tesseract-config',
|
||||
cfg_file,
|
||||
valid_tess_config,
|
||||
'--pages',
|
||||
'1',
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.slow # This test sometimes times out in CI
|
||||
@pytest.mark.parametrize('renderer', RENDERERS)
|
||||
def test_tesseract_config_invalid(renderer, resources, outdir):
|
||||
@pytest.fixture
|
||||
def invalid_tess_config(outdir):
|
||||
cfg_file = outdir / 'test.cfg'
|
||||
with cfg_file.open('w') as f:
|
||||
f.write(
|
||||
@@ -511,14 +520,19 @@ def test_tesseract_config_invalid(renderer, resources, outdir):
|
||||
THIS FILE IS INVALID
|
||||
'''
|
||||
)
|
||||
yield cfg_file
|
||||
|
||||
|
||||
@pytest.mark.slow # This test sometimes times out in CI
|
||||
@pytest.mark.parametrize('renderer', RENDERERS)
|
||||
def test_tesseract_config_invalid(renderer, resources, invalid_tess_config, outpdf):
|
||||
p = run_ocrmypdf(
|
||||
resources / 'ccitt.pdf',
|
||||
outdir / 'out.pdf',
|
||||
outpdf,
|
||||
'--pdf-renderer',
|
||||
renderer,
|
||||
'--tesseract-config',
|
||||
cfg_file,
|
||||
invalid_tess_config,
|
||||
)
|
||||
assert (
|
||||
"parameter not found" in p.stderr.lower()
|
||||
@@ -801,6 +815,9 @@ def test_text_curves(resources, outpdf):
|
||||
info = PdfInfo(outpdf)
|
||||
assert len(info.pages[0].images) == 0, "added images to the vector PDF"
|
||||
|
||||
|
||||
def test_text_curves_force(resources, outpdf):
|
||||
with patch('ocrmypdf._pipeline.VECTOR_PAGE_DPI', 100):
|
||||
check_ocrmypdf(
|
||||
resources / 'vector.pdf',
|
||||
outpdf,
|
||||
@@ -922,3 +939,25 @@ def test_outputtype_none(resources, outtxt):
|
||||
'tests/plugins/tesseract_noop.py',
|
||||
)
|
||||
assert p.returncode == ExitCode.ok
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def graph_bad_icc(resources, outdir):
|
||||
synth_input_file = outdir / 'graph-bad-icc.pdf'
|
||||
with pikepdf.open(resources / 'graph.pdf') as pdf:
|
||||
icc = pdf.make_stream(
|
||||
b'invalid icc profile', N=3, Alternate=pikepdf.Name.DeviceRGB
|
||||
)
|
||||
pdf.pages[0].Resources.XObject['/Im0'].ColorSpace = pikepdf.Array(
|
||||
[pikepdf.Name.ICCBased, icc]
|
||||
)
|
||||
pdf.save(synth_input_file)
|
||||
yield synth_input_file
|
||||
|
||||
|
||||
def test_corrupt_icc(graph_bad_icc, outpdf, caplog):
|
||||
result = run_ocrmypdf_api(graph_bad_icc, outpdf)
|
||||
assert result == ExitCode.ok
|
||||
assert any(
|
||||
'corrupt or unreadable ICC profile' in rec.message for rec in caplog.records
|
||||
)
|
||||
|
||||
+19
-9
@@ -6,10 +6,10 @@
|
||||
|
||||
|
||||
import datetime
|
||||
import warnings
|
||||
from datetime import timezone
|
||||
from os import fspath
|
||||
from shutil import copyfile
|
||||
from unittest.mock import patch
|
||||
|
||||
import pikepdf
|
||||
import pytest
|
||||
@@ -173,6 +173,19 @@ def test_creation_date_preserved(output_type, resources, infile, outpdf):
|
||||
assert seconds_between_dates(date_after, datetime.datetime.now(timezone.utc)) < 1000
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def libxmp_file_to_dict():
|
||||
try:
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
from libxmp.utils import (
|
||||
file_to_dict, # pylint: disable=import-outside-toplevel
|
||||
)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pytest.skip("libxmp not available or libexempi3 not installed")
|
||||
return file_to_dict
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'test_file,output_type',
|
||||
[
|
||||
@@ -182,15 +195,12 @@ def test_creation_date_preserved(output_type, resources, infile, outpdf):
|
||||
('3small.pdf', 'pdfa'),
|
||||
],
|
||||
)
|
||||
def test_xml_metadata_preserved(test_file, output_type, resources, outpdf):
|
||||
def test_xml_metadata_preserved(
|
||||
libxmp_file_to_dict, test_file, output_type, resources, outpdf
|
||||
):
|
||||
input_file = resources / test_file
|
||||
|
||||
try:
|
||||
from libxmp.utils import file_to_dict # pylint: disable=import-outside-toplevel
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pytest.skip(reason="libxmp not available or libexempi3 not installed")
|
||||
|
||||
before = file_to_dict(str(input_file))
|
||||
before = libxmp_file_to_dict(str(input_file))
|
||||
|
||||
check_ocrmypdf(
|
||||
input_file,
|
||||
@@ -202,7 +212,7 @@ def test_xml_metadata_preserved(test_file, output_type, resources, outpdf):
|
||||
'tests/plugins/tesseract_noop.py',
|
||||
)
|
||||
|
||||
after = file_to_dict(str(outpdf))
|
||||
after = libxmp_file_to_dict(str(outpdf))
|
||||
|
||||
equal_properties = [
|
||||
'dc:contributor',
|
||||
|
||||
+16
-8
@@ -4,23 +4,31 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import os
|
||||
|
||||
import pikepdf
|
||||
import pytest
|
||||
|
||||
from ocrmypdf.exceptions import MissingDependencyError
|
||||
|
||||
from .conftest import check_ocrmypdf
|
||||
|
||||
|
||||
@pytest.mark.parametrize('optimize', (0, 3))
|
||||
@pytest.mark.parametrize('pdfa_level', (1, 2, 3))
|
||||
def test_pdfa(resources, outpdf, optimize, pdfa_level):
|
||||
check_ocrmypdf(
|
||||
resources / 'francais.pdf',
|
||||
outpdf,
|
||||
'--plugin',
|
||||
'tests/plugins/tesseract_noop.py',
|
||||
f'--output-type=pdfa-{pdfa_level}',
|
||||
f'--optimize={optimize}',
|
||||
)
|
||||
try:
|
||||
check_ocrmypdf(
|
||||
resources / 'francais.pdf',
|
||||
outpdf,
|
||||
'--plugin',
|
||||
'tests/plugins/tesseract_noop.py',
|
||||
f'--output-type=pdfa-{pdfa_level}',
|
||||
f'--optimize={optimize}',
|
||||
)
|
||||
except MissingDependencyError as e:
|
||||
if 'pngquant' in str(e) and optimize in (2, 3) and os.name == 'nt':
|
||||
pytest.xfail("pngquant currently not available on Windows")
|
||||
if pdfa_level in (2, 3):
|
||||
# PDF/A-2 allows ObjStm
|
||||
assert b'/ObjStm' in outpdf.read_bytes()
|
||||
|
||||
@@ -181,7 +181,7 @@ def test_stack_abuse():
|
||||
pdfinfo.info._interpret_contents(stream)
|
||||
|
||||
stream = pikepdf.Stream(p, b'q ' * 135)
|
||||
with pytest.warns():
|
||||
with pytest.warns(UserWarning):
|
||||
with pytest.raises(RuntimeError):
|
||||
pdfinfo.info._interpret_contents(stream)
|
||||
|
||||
|
||||
+2
-2
@@ -35,7 +35,7 @@ def test_stdin(ocrmypdf_exec, resources, outpdf):
|
||||
|
||||
def test_stdout(ocrmypdf_exec, resources, outpdf):
|
||||
if 'COV_CORE_DATAFILE' in os.environ:
|
||||
pytest.skip(reason="Coverage uses stdout")
|
||||
pytest.skip("Coverage uses stdout")
|
||||
|
||||
input_file = str(resources / 'francais.pdf')
|
||||
output_file = str(outpdf)
|
||||
@@ -72,7 +72,7 @@ def test_bad_locale(monkeypatch):
|
||||
)
|
||||
def test_dev_null(resources):
|
||||
if 'COV_CORE_DATAFILE' in os.environ:
|
||||
pytest.skip(reason="Coverage uses stdout")
|
||||
pytest.skip("Coverage uses stdout")
|
||||
|
||||
p = run_ocrmypdf(
|
||||
resources / 'trivial.pdf',
|
||||
|
||||
Reference in New Issue
Block a user