Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
054c0773a3 | ||
|
|
89aa78b724 | ||
|
|
708113a514 | ||
|
|
95ef5410c2 | ||
|
|
868b3b4abd | ||
|
|
045bdff95a | ||
|
|
d12b27ac1d | ||
|
|
e4e00de79f | ||
|
|
a53a3937c2 | ||
|
|
343424b4d2 | ||
|
|
c5edff2c2f | ||
|
|
8c5f8b8ddd | ||
|
|
39da931a56 | ||
|
|
9fe354359b |
@@ -39,6 +39,7 @@ RUN pip3 install --no-cache-dir \
|
|||||||
-r requirements/main.txt \
|
-r requirements/main.txt \
|
||||||
-r requirements/webservice.txt \
|
-r requirements/webservice.txt \
|
||||||
-r requirements/test.txt \
|
-r requirements/test.txt \
|
||||||
|
-r requirements/watcher.txt \
|
||||||
.
|
.
|
||||||
|
|
||||||
FROM base
|
FROM base
|
||||||
@@ -69,6 +70,7 @@ COPY --from=builder /usr/local/lib/ /usr/local/lib/
|
|||||||
COPY --from=builder /usr/local/bin/ /usr/local/bin/
|
COPY --from=builder /usr/local/bin/ /usr/local/bin/
|
||||||
|
|
||||||
COPY --from=builder /app/misc/webservice.py /app/
|
COPY --from=builder /app/misc/webservice.py /app/
|
||||||
|
COPY --from=builder /app/misc/watcher.py /app/
|
||||||
|
|
||||||
# Copy minimal project files to get the test suite.
|
# Copy minimal project files to get the test suite.
|
||||||
COPY --from=builder /app/setup.cfg /app/setup.py /app/README.md /app/
|
COPY --from=builder /app/setup.cfg /app/setup.py /app/README.md /app/
|
||||||
|
|||||||
+14
-8
@@ -1,10 +1,4 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/psf/black
|
|
||||||
rev: stable
|
|
||||||
hooks:
|
|
||||||
- id: black
|
|
||||||
language_version: python3.7
|
|
||||||
exclude: ^src/ocrmypdf/lib/_leptonica.py
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v2.4.0
|
rev: v2.4.0
|
||||||
hooks:
|
hooks:
|
||||||
@@ -13,5 +7,17 @@ repos:
|
|||||||
- id: check-toml
|
- id: check-toml
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
- id: debug-statements
|
- id: debug-statements
|
||||||
- id: name-tests-test
|
- repo: https://github.com/asottile/seed-isort-config
|
||||||
args: ["--django"]
|
rev: v1.9.3
|
||||||
|
hooks:
|
||||||
|
- id: seed-isort-config
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-isort
|
||||||
|
rev: v4.3.21 # pick the isort version you'd like to use from https://github.com/pre-commit/mirrors-isort/releases
|
||||||
|
hooks:
|
||||||
|
- id: isort
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: stable
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
language_version: python3.7
|
||||||
|
exclude: ^src/ocrmypdf/lib/_leptonica.py
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ and users of Fedora 29 or later may simply
|
|||||||
dnf install ocrmypdf
|
dnf install ocrmypdf
|
||||||
```
|
```
|
||||||
|
|
||||||
and macOS users with Homebrew may simply
|
and Homebrew users (macOS, Linux, Windows Subsystem for Linux) may simply
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
brew install ocrmypdf
|
brew install ocrmypdf
|
||||||
@@ -113,18 +113,12 @@ ocrmypdf --help
|
|||||||
|
|
||||||
Our [documentation is served on Read the Docs](https://ocrmypdf.readthedocs.io/en/latest/index.html).
|
Our [documentation is served on Read the Docs](https://ocrmypdf.readthedocs.io/en/latest/index.html).
|
||||||
|
|
||||||
If you detect an issue, please:
|
Please report issues on our [GitHub issues](https://github.com/jbarlow83/OCRmyPDF/issues) page, and follow the issue template for quick response.
|
||||||
|
|
||||||
- Check whether your issue is already known
|
|
||||||
- If no problem report exists on github, please create one here: <https://github.com/jbarlow83/OCRmyPDF/issues>
|
|
||||||
- Describe your problem thoroughly
|
|
||||||
- Append the console output of the script when running the debug mode (`-v 1` option)
|
|
||||||
- If possible provide your input PDF file as well as the content of the temporary folder (using a file sharing service like Dropbox)
|
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
In addition to the required Python version (3.6+), OCRmyPDF requires external program installations of Ghostscript, Tesseract OCR, QPDF, and Leptonica. ocrmypdf is pure Python, but uses CFFI to portably generate library bindings.
|
In addition to the required Python version (3.6+), OCRmyPDF requires external program installations of Ghostscript, Tesseract OCR, QPDF, and Leptonica. ocrmypdf is pure Python, but uses CFFI to portably generate library bindings. OCRmyPDF works on pretty much everything: Linux, macOS, Windows and FreeBSD.
|
||||||
|
|
||||||
Press & Media
|
Press & Media
|
||||||
-------------
|
-------------
|
||||||
|
|||||||
+4
-18
@@ -34,27 +34,20 @@ stages:
|
|||||||
displayName: "Install system packages"
|
displayName: "Install system packages"
|
||||||
- pwsh: |
|
- pwsh: |
|
||||||
refreshenv
|
refreshenv
|
||||||
$env:path = "C:\Program Files\Tesseract-OCR;C:\Program Files\gs\gs9.50\bin;" + $env:path
|
python -m pip install --upgrade pip wheel
|
||||||
pip install --upgrade pip wheel
|
python -m pip install -r requirements/main.txt -r requirements/test.txt .
|
||||||
pip install -r requirements/main.txt -r requirements/test.txt .
|
|
||||||
tesseract --version
|
|
||||||
displayName: "Install Python packages"
|
displayName: "Install Python packages"
|
||||||
- pwsh: |
|
- pwsh: |
|
||||||
refreshenv
|
refreshenv
|
||||||
$env:path = "C:\Program Files\Tesseract-OCR;C:\Program Files\gs\gs9.50\bin;" + $env:path
|
|
||||||
$env:pathext += ';.py'
|
$env:pathext += ';.py'
|
||||||
# -n auto helps Windows
|
# -n auto helps Windows
|
||||||
pytest -n auto --junitxml=test.xml --cov=ocrmypdf --cov-report=xml
|
python -m pytest -n auto --junitxml=test.xml --cov=ocrmypdf --cov-report=xml
|
||||||
displayName: "Test"
|
displayName: "Test"
|
||||||
- task: PublishTestResults@2
|
- task: PublishTestResults@2
|
||||||
inputs:
|
inputs:
|
||||||
testResultsFiles: "test.xml"
|
testResultsFiles: "test.xml"
|
||||||
testRunTitle: "$(Agent.OS) - $(Build.DefinitionName) - Python $(python.version)"
|
testRunTitle: "$(Agent.OS) - $(Build.DefinitionName) - Python $(python.version)"
|
||||||
condition: succeededOrFailed()
|
condition: succeededOrFailed()
|
||||||
- task: PublishCodeCoverageResults@1
|
|
||||||
inputs:
|
|
||||||
codeCoverageTool: Cobertura
|
|
||||||
summaryFileLocation: "$(System.DefaultWorkingDirectory)/**/coverage.xml"
|
|
||||||
- job: "Ubuntu_1804"
|
- job: "Ubuntu_1804"
|
||||||
pool:
|
pool:
|
||||||
vmImage: "ubuntu-18.04"
|
vmImage: "ubuntu-18.04"
|
||||||
@@ -105,10 +98,6 @@ stages:
|
|||||||
testResultsFiles: "test.xml"
|
testResultsFiles: "test.xml"
|
||||||
testRunTitle: "$(Agent.OS) - $(Build.DefinitionName) - Python $(python.version)"
|
testRunTitle: "$(Agent.OS) - $(Build.DefinitionName) - Python $(python.version)"
|
||||||
condition: succeededOrFailed()
|
condition: succeededOrFailed()
|
||||||
- task: PublishCodeCoverageResults@1
|
|
||||||
inputs:
|
|
||||||
codeCoverageTool: Cobertura
|
|
||||||
summaryFileLocation: "$(System.DefaultWorkingDirectory)/**/coverage.xml"
|
|
||||||
- job: "Ubuntu_1604"
|
- job: "Ubuntu_1604"
|
||||||
pool:
|
pool:
|
||||||
vmImage: "ubuntu-16.04"
|
vmImage: "ubuntu-16.04"
|
||||||
@@ -157,10 +146,6 @@ stages:
|
|||||||
testResultsFiles: "test.xml"
|
testResultsFiles: "test.xml"
|
||||||
testRunTitle: "$(Agent.OS) - $(Build.DefinitionName) - Python $(python.version)"
|
testRunTitle: "$(Agent.OS) - $(Build.DefinitionName) - Python $(python.version)"
|
||||||
condition: succeededOrFailed()
|
condition: succeededOrFailed()
|
||||||
- task: PublishCodeCoverageResults@1
|
|
||||||
inputs:
|
|
||||||
codeCoverageTool: Cobertura
|
|
||||||
summaryFileLocation: "$(System.DefaultWorkingDirectory)/**/coverage.xml"
|
|
||||||
- job: "macOS_Mojave"
|
- job: "macOS_Mojave"
|
||||||
pool:
|
pool:
|
||||||
vmImage: "macos-10.14"
|
vmImage: "macos-10.14"
|
||||||
@@ -176,6 +161,7 @@ stages:
|
|||||||
versionSpec: "$(python.version)"
|
versionSpec: "$(python.version)"
|
||||||
- bash: |
|
- bash: |
|
||||||
brew update
|
brew update
|
||||||
|
brew unlink python@2
|
||||||
brew install \
|
brew install \
|
||||||
exempi \
|
exempi \
|
||||||
ghostscript \
|
ghostscript \
|
||||||
|
|||||||
+52
-4
@@ -198,6 +198,54 @@ and all inquiries are appreciated.
|
|||||||
Hot (watched) folders
|
Hot (watched) folders
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
|
Watched folders with Docker
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
The OCRmyPDF Docker image includes a watcher service. This service can
|
||||||
|
be launched as follows:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
docker run \
|
||||||
|
-v <path to files to convert>:/input \
|
||||||
|
-v <path to store results>:/output \
|
||||||
|
-e OCR_OUTPUT_DIRECTORY_YEAR_MONTH=1 \
|
||||||
|
-it --entrypoint python3 \
|
||||||
|
jbarlow83/ocrmypdf \
|
||||||
|
watcher.py
|
||||||
|
|
||||||
|
This service will watch for a file that matches ``/input/\*.pdf`` and will
|
||||||
|
convert it to a OCRed PDF in ``/output/``. The parameters to this image are:
|
||||||
|
|
||||||
|
.. csv-table:: watcher.py parameters for Docker
|
||||||
|
:header: "Parameter", "Description"
|
||||||
|
:widths: 50, 50
|
||||||
|
|
||||||
|
"``-v <path to files to convert>:/input``", "Files placed in this location will be OCRed"
|
||||||
|
"``-v <path to store results>:/output``", "This is where OCRed files will be stored"
|
||||||
|
"``-e OCR_OUTPUT_DIRECTORY_YEAR_MONTH=1``", "This will place files in the output in {output}/{year}/{month}/{filename}"
|
||||||
|
|
||||||
|
This service relies on polling to check for changes to the filesystem. It
|
||||||
|
may not be suitable for some environments, such as filesystems shared on a
|
||||||
|
slow network.
|
||||||
|
|
||||||
|
Watched folders with watcher.py
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
The watcher service may also be run natively.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
pip3 install -r reqs/watcher.txt
|
||||||
|
|
||||||
|
env OCR_INPUT_DIRECTORY=/mnt/input-pdfs \
|
||||||
|
OCR_OUTPUT_DIRECTORY=/mnt/output-pdfs \
|
||||||
|
OCR_OUTPUT_DIRECTORY_YEAR_MONTH=1 \
|
||||||
|
python3 watcher.py
|
||||||
|
|
||||||
|
Watched folders with CLI
|
||||||
|
------------------------
|
||||||
|
|
||||||
To set up a "hot folder" that will trigger OCR for every file inserted,
|
To set up a "hot folder" that will trigger OCR for every file inserted,
|
||||||
use a program like Python
|
use a program like Python
|
||||||
`watchdog <https://pypi.python.org/pypi/watchdog>`__ (supports all major
|
`watchdog <https://pypi.python.org/pypi/watchdog>`__ (supports all major
|
||||||
@@ -225,12 +273,12 @@ told to run ``ocrmypdf`` on any .pdf added to the current directory
|
|||||||
--command='ocrmypdf "${watch_src_path}" "out/${watch_src_path}" ' \
|
--command='ocrmypdf "${watch_src_path}" "out/${watch_src_path}" ' \
|
||||||
. # don't forget the final dot
|
. # don't forget the final dot
|
||||||
|
|
||||||
For more complex behavior you can write a Python script around to use
|
|
||||||
the watchdog API.
|
|
||||||
|
|
||||||
On file servers, you could configure watchmedo as a system service so it
|
On file servers, you could configure watchmedo as a system service so it
|
||||||
will run all the time.
|
will run all the time.
|
||||||
|
|
||||||
|
For more complex behavior you can write a Python script around to use
|
||||||
|
the watchdog API. You can refer to the watcher.py script as an example.
|
||||||
|
|
||||||
Caveats
|
Caveats
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@@ -250,7 +298,7 @@ Caveats
|
|||||||
Alternatives
|
Alternatives
|
||||||
------------
|
------------
|
||||||
|
|
||||||
- `systemd user services <https://wiki.archlinux.org/index.php/Systemd/User>`__
|
- On Linux, `systemd user services <https://wiki.archlinux.org/index.php/Systemd/User>`__
|
||||||
can be configured to automatically perform OCR on a collection of files.
|
can be configured to automatically perform OCR on a collection of files.
|
||||||
|
|
||||||
- `Watchman <https://facebook.github.io/watchman/>`__ is a more
|
- `Watchman <https://facebook.github.io/watchman/>`__ is a more
|
||||||
|
|||||||
+20
-1
@@ -15,7 +15,10 @@ Code style
|
|||||||
==========
|
==========
|
||||||
|
|
||||||
We use PEP8, ``black`` for code formatting and ``isort`` for import sorting. The
|
We use PEP8, ``black`` for code formatting and ``isort`` for import sorting. The
|
||||||
settings for programs are in ``pyproject.toml`` and ``setup.cfg``.
|
settings for these programs are in ``pyproject.toml`` and ``setup.cfg``. Pull
|
||||||
|
requests should follow the style guide. One difference we use from "black" style
|
||||||
|
is that strings shown to the user are always in double quotes (``"``) and strings
|
||||||
|
for internal uses are in single quotes (``'``).
|
||||||
|
|
||||||
Tests
|
Tests
|
||||||
=====
|
=====
|
||||||
@@ -36,3 +39,19 @@ New non-Python dependencies
|
|||||||
|
|
||||||
OCRmyPDF uses several external programs (Tesseract, Ghostscript and others) for
|
OCRmyPDF uses several external programs (Tesseract, Ghostscript and others) for
|
||||||
its functionality. In general we prefer to avoid adding new external programs.
|
its functionality. In general we prefer to avoid adding new external programs.
|
||||||
|
|
||||||
|
Style guide: Is it OCRmyPDF or ocrmypdf?
|
||||||
|
========================================
|
||||||
|
|
||||||
|
The program/project is OCRmyPDF and the name of the executable or library is ocrmypdf.
|
||||||
|
|
||||||
|
Known ports/packagers
|
||||||
|
=====================
|
||||||
|
|
||||||
|
OCRmyPDF has been ported to many platforms already. If you are interesting in
|
||||||
|
porting to a new platform, check with
|
||||||
|
`Repology <https://repology.org/projects/?search=ocrmypdf>`__ to see the status
|
||||||
|
of that platform.
|
||||||
|
|
||||||
|
Packager maintainers, please ensure that the command line completion scripts in
|
||||||
|
``misc/`` are installed.
|
||||||
|
|||||||
+38
-14
@@ -8,11 +8,20 @@ Installing OCRmyPDF
|
|||||||
|latest|
|
|latest|
|
||||||
|
|
||||||
The easiest way to install OCRmyPDF is to follow the steps for your operating
|
The easiest way to install OCRmyPDF is to follow the steps for your operating
|
||||||
system/platform, although sometimes this version may be out of date.
|
system/platform, although sometimes this version may be out of date. This
|
||||||
|
installation guide provides information allowing you to compare the current
|
||||||
|
version to the one provided by your platform.
|
||||||
|
|
||||||
If you want to use the latest version of OCRmyPDF, your best bet is to install
|
If you want to use the latest version of OCRmyPDF and all of its optional
|
||||||
the most recent version your platform provides, and then upgrade that version by
|
dependencies, the easiest way to get that is install the Homebrew package. Homebrew
|
||||||
installing the Python binary wheels.
|
is best known as a macOS package manger, but also works for
|
||||||
|
`Linux and Windows Subsystem for Linux <https://docs.brew.sh/Homebrew-on-Linux>`__.
|
||||||
|
After Homebrew is installed, simply run ``brew install ocrmypdf``.
|
||||||
|
|
||||||
|
You can also use the more detailed procedures here to manually install OCRmyPDF
|
||||||
|
from source or with the ``pip`` package manager for binary wheels. The reason
|
||||||
|
for these varied steps is that OCRmyPDF requires third-party executables that are
|
||||||
|
not part of Python.
|
||||||
|
|
||||||
.. contents:: Platform-specific steps
|
.. contents:: Platform-specific steps
|
||||||
:depth: 2
|
:depth: 2
|
||||||
@@ -55,8 +64,8 @@ Debian and Ubuntu 18.04 or newer
|
|||||||
| |ubu-1804| |ubu-1810| |ubu-1904| |ubu-1910| |
|
| |ubu-1804| |ubu-1810| |ubu-1904| |ubu-1910| |
|
||||||
+-----------------------------------------------+
|
+-----------------------------------------------+
|
||||||
|
|
||||||
Users of Debian 9 ("stretch") or later or Ubuntu 18.04 or later may
|
Users of Debian 9 ("stretch") or later or Ubuntu 18.04 or later, including users
|
||||||
simply
|
of Windows Subsystem for Linux, may simply
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
@@ -303,6 +312,19 @@ the following command.
|
|||||||
If you have any difficulties with installation, check the repository
|
If you have any difficulties with installation, check the repository
|
||||||
package page.
|
package page.
|
||||||
|
|
||||||
|
Alpine Linux
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. image:: https://repology.org/badge/version-for-repo/alpine_edge/ocrmypdf.svg
|
||||||
|
:alt: Alpine Linux
|
||||||
|
:target: https://repology.org/metapackage/ocrmypdf
|
||||||
|
|
||||||
|
To install OCRmyPDF for Alpine Linux:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
apk add ocrmypdf
|
||||||
|
|
||||||
Other Linux packages
|
Other Linux packages
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
@@ -380,7 +402,7 @@ packs. If you need other languages you can optionally install them all:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
brew install tesseract --with-all-languages # Option 2: for all language packs
|
brew install tesseract-lang # Option 2: for all language packs
|
||||||
|
|
||||||
Update the homebrew pip:
|
Update the homebrew pip:
|
||||||
|
|
||||||
@@ -427,8 +449,7 @@ Installing the Docker image
|
|||||||
===========================
|
===========================
|
||||||
|
|
||||||
For some users, installing the Docker image will be easier than
|
For some users, installing the Docker image will be easier than
|
||||||
installing all of OCRmyPDF's dependencies. For Windows, it is the only
|
installing all of OCRmyPDF's dependencies.
|
||||||
option.
|
|
||||||
|
|
||||||
See `OCRmyPDF Docker Image <docker>`__ for more information.
|
See `OCRmyPDF Docker Image <docker>`__ for more information.
|
||||||
|
|
||||||
@@ -448,7 +469,7 @@ Installing on Windows
|
|||||||
|
|
||||||
You must install the following for Windows:
|
You must install the following for Windows:
|
||||||
|
|
||||||
* Python 3.7 (64-bit recommended)
|
* Python 3.7 (64-bit)
|
||||||
* Tesseract 4.0 or later
|
* Tesseract 4.0 or later
|
||||||
* Ghostscript 9.50 or later
|
* Ghostscript 9.50 or later
|
||||||
|
|
||||||
@@ -463,11 +484,14 @@ Also consider adding:
|
|||||||
* ``choco install pngquant``
|
* ``choco install pngquant``
|
||||||
|
|
||||||
Windows 10 64-bit and 64-bit versions of applications are recommended. Earlier
|
Windows 10 64-bit and 64-bit versions of applications are recommended. Earlier
|
||||||
versions of Windows and 32-bit versions of these programs are not tested.
|
versions of Windows and 32-bit versions of these programs are not tested, and not
|
||||||
|
supported at this time.
|
||||||
|
|
||||||
Modify your ``PATH`` environment variable so that Tesseract and Ghostscript, and
|
OCRmyPDF will check for Tesseract-OCR and Ghostscript in your Program Files folder.
|
||||||
any optional executables can be found. You can enter it in the command line
|
If they are in some other location, you may need to modify the ``PATH``
|
||||||
or `follow these directions <https://www.computerhope.com/issues/ch000549.htm#dospath>`_
|
environment variable so Tesseract, Ghostscript, and other any optional executables can
|
||||||
|
be found. You can enter it in the command line or
|
||||||
|
`follow these directions <https://www.computerhope.com/issues/ch000549.htm#dospath>`_
|
||||||
to make the change persistent and system-wide.
|
to make the change persistent and system-wide.
|
||||||
|
|
||||||
You may then use pip to install ocrmypdf:
|
You may then use pip to install ocrmypdf:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Introduction
|
Introduction
|
||||||
============
|
============
|
||||||
|
|
||||||
OCRmyPDF is a Python 3 package that adds OCR layers to PDFs.
|
OCRmyPDF is a Python 3 application and library that adds OCR layers to PDFs.
|
||||||
|
|
||||||
About OCR
|
About OCR
|
||||||
=========
|
=========
|
||||||
|
|||||||
@@ -13,6 +13,19 @@ Note that it is licensed under GPLv3, so scripts that
|
|||||||
``import ocrmypdf`` and are released publicly should probably also be
|
``import ocrmypdf`` and are released publicly should probably also be
|
||||||
licensed under GPLv3.
|
licensed under GPLv3.
|
||||||
|
|
||||||
|
v9.3.0
|
||||||
|
======
|
||||||
|
|
||||||
|
- Improved native Windows support: we now check in the obvious places in
|
||||||
|
the "Program Files" folders installations of Tesseract and Ghostscript,
|
||||||
|
rather than relying on the user to edit ``PATH`` to specify their location.
|
||||||
|
The ``PATH`` environment variable can still be used to differentiate when
|
||||||
|
multiple installations are present or the programs are installed to non-
|
||||||
|
standard locations.
|
||||||
|
- Fixed an exception on parsing Ghostscript error messages.
|
||||||
|
- Added an improved example demonstrating how to set up a watched folder
|
||||||
|
for automated OCR processing (thanks to @ianalexander for the contribution).
|
||||||
|
|
||||||
v9.2.0
|
v9.2.0
|
||||||
======
|
======
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ _ocrmypdf()
|
|||||||
COMPREPLY=( $( compgen -W '{1..13}' -- "$cur" ) )
|
COMPREPLY=( $( compgen -W '{1..13}' -- "$cur" ) )
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
--sidecar|--title|--author|--subject|--keywords|--unpaper-args|--pages)
|
--sidecar|--title|--author|--subject|--keywords|--unpaper-args|--pages|--fast-web-view)
|
||||||
# argument required but no completions available
|
# argument required but no completions available
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
@@ -76,7 +76,8 @@ _ocrmypdf()
|
|||||||
--max-image-mpixels --tesseract-config --tesseract-pagesegmode
|
--max-image-mpixels --tesseract-config --tesseract-pagesegmode
|
||||||
--help --tesseract-oem --pdf-renderer --tesseract-timeout
|
--help --tesseract-oem --pdf-renderer --tesseract-timeout
|
||||||
--rotate-pages-threshold --pdfa-image-compression --user-words
|
--rotate-pages-threshold --pdfa-image-compression --user-words
|
||||||
--user-patterns --keep-temporary-files --output-type' \
|
--user-patterns --keep-temporary-files --output-type
|
||||||
|
--no-progress-bar --pages --fast-web-view' \
|
||||||
-- "$cur" ) )
|
-- "$cur" ) )
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ function __fish_ocrmypdf_verbose
|
|||||||
end
|
end
|
||||||
complete -c ocrmypdf -x -s v -l verbose -a '(__fish_ocrmypdf_verbose)' -d "set verbosity level"
|
complete -c ocrmypdf -x -s v -l verbose -a '(__fish_ocrmypdf_verbose)' -d "set verbosity level"
|
||||||
|
|
||||||
|
complete -c ocrmypdf -x -l no-progress-bar -d "disable the progress bar"
|
||||||
|
|
||||||
function __fish_ocrmypdf_pdfa_compression
|
function __fish_ocrmypdf_pdfa_compression
|
||||||
echo -e "auto\t"(_ "let Ghostscript decide how to compress images")
|
echo -e "auto\t"(_ "let Ghostscript decide how to compress images")
|
||||||
echo -e "jpeg\t"(_ "convert color and grayscale images to JPEG")
|
echo -e "jpeg\t"(_ "convert color and grayscale images to JPEG")
|
||||||
@@ -111,5 +113,6 @@ complete -c ocrmypdf -x -l rotate-pages-threshold -d "page rotation confidence"
|
|||||||
|
|
||||||
complete -c ocrmypdf -r -l user-words -d "specify location of user words file"
|
complete -c ocrmypdf -r -l user-words -d "specify location of user words file"
|
||||||
complete -c ocrmypdf -r -l user-patterns -d "specify location of user patterns file"
|
complete -c ocrmypdf -r -l user-patterns -d "specify location of user patterns file"
|
||||||
|
complete -c ocrmypdf -x -l fast-web-view -d "if file size if above this amount in MB, linearize PDF"
|
||||||
|
|
||||||
complete -c ocrmypdf -x -a "(__fish_complete_suffix .pdf)"
|
complete -c ocrmypdf -x -a "(__fish_complete_suffix .pdf)"
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# Copyright (C) 2019 Ian Alexander: https://github.com/ianalexander
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from watchdog.events import PatternMatchingEventHandler
|
||||||
|
from watchdog.observers import Observer
|
||||||
|
|
||||||
|
import ocrmypdf
|
||||||
|
|
||||||
|
INPUT_DIRECTORY = os.getenv('OCR_INPUT_DIRECTORY', '/input')
|
||||||
|
OUTPUT_DIRECTORY = os.getenv('OCR_OUTPUT_DIRECTORY', '/output')
|
||||||
|
OUTPUT_DIRECTORY_YEAR_MONTH = bool(os.getenv('OCR_OUTPUT_DIRECTORY_YEAR_MONTH', False))
|
||||||
|
PATTERNS = ['*.pdf']
|
||||||
|
|
||||||
|
|
||||||
|
def execute_ocrmypdf(file_path):
|
||||||
|
filename = Path(file_path).name
|
||||||
|
if OUTPUT_DIRECTORY_YEAR_MONTH:
|
||||||
|
today = datetime.today()
|
||||||
|
output_directory_year_month = Path(
|
||||||
|
f'{OUTPUT_DIRECTORY}/{today.year}/{today.month}'
|
||||||
|
)
|
||||||
|
if not output_directory_year_month.exists():
|
||||||
|
output_directory_year_month.mkdir(parents=True, exist_ok=True)
|
||||||
|
output_path = Path(output_directory_year_month) / filename
|
||||||
|
else:
|
||||||
|
output_path = Path(OUTPUT_DIRECTORY) / filename
|
||||||
|
print(f'New file: {file_path}.\nAttempting to OCRmyPDF to: {output_path}')
|
||||||
|
ocrmypdf.ocr(file_path, output_path)
|
||||||
|
|
||||||
|
|
||||||
|
class HandleObserverEvent(PatternMatchingEventHandler):
|
||||||
|
def on_any_event(self, event):
|
||||||
|
if event.event_type in ['created', 'modified']:
|
||||||
|
execute_ocrmypdf(event.src_path)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(
|
||||||
|
f"Starting OCRmyPDF watcher with config:\n"
|
||||||
|
f"Input Directory: {INPUT_DIRECTORY}\n"
|
||||||
|
f"Output Directory: {OUTPUT_DIRECTORY}\n"
|
||||||
|
f"Output Directory Year & Month: {OUTPUT_DIRECTORY_YEAR_MONTH}"
|
||||||
|
)
|
||||||
|
handler = HandleObserverEvent(patterns=PATTERNS)
|
||||||
|
observer = Observer()
|
||||||
|
observer.schedule(handler, INPUT_DIRECTORY, recursive=True)
|
||||||
|
observer.start()
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
time.sleep(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
observer.stop()
|
||||||
|
observer.join()
|
||||||
+2
-1
@@ -10,7 +10,8 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 88
|
line-length = 88
|
||||||
target-version = ["py36", "py37", "py38"]
|
target-version = ["py36",
|
||||||
|
"py37", "py38"]
|
||||||
skip-string-normalization = true
|
skip-string-normalization = true
|
||||||
include = '\.pyi?$'
|
include = '\.pyi?$'
|
||||||
exclude = '''
|
exclude = '''
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
watchdog >= 0.8.2, < 1.0
|
||||||
@@ -22,6 +22,8 @@ include_trailing_comma=True
|
|||||||
force_grid_wrap=0
|
force_grid_wrap=0
|
||||||
use_parentheses=True
|
use_parentheses=True
|
||||||
line_length=88
|
line_length=88
|
||||||
|
known_first_party = ocrmypdf
|
||||||
|
known_third_party = PIL,PyPDF2,_cffi_backend,cffi,flask,gs,img2pdf,pdfminer,pikepdf,pkg_resources,pytest,reportlab,setuptools,sphinx_rtd_theme,tqdm,watchdog,werkzeug
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
license_file = LICENSE
|
license_file = LICENSE
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ from .helpers import safe_symlink
|
|||||||
from .hocrtransform import HocrTransform
|
from .hocrtransform import HocrTransform
|
||||||
from .optimize import optimize
|
from .optimize import optimize
|
||||||
from .pdfa import generate_pdfa_ps
|
from .pdfa import generate_pdfa_ps
|
||||||
from .pdfinfo import Colorspace, PdfInfo, Encoding
|
from .pdfinfo import Colorspace, Encoding, PdfInfo
|
||||||
|
|
||||||
VECTOR_PAGE_DPI = 400
|
VECTOR_PAGE_DPI = 400
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import threading
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
|
|
||||||
from tqdm import tqdm
|
|
||||||
import PIL
|
import PIL
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
from ._graft import OcrGrafter
|
from ._graft import OcrGrafter
|
||||||
from ._jobcontext import PDFContext, cleanup_working_files, make_logger
|
from ._jobcontext import PDFContext, cleanup_working_files, make_logger
|
||||||
|
|||||||
@@ -58,6 +58,15 @@ log = logging.getLogger(__name__)
|
|||||||
verify_python3_env()
|
verify_python3_env()
|
||||||
|
|
||||||
|
|
||||||
|
def check_platform():
|
||||||
|
if os.name == 'nt' and sys.maxsize <= 2 ** 32: # pragma: no cover
|
||||||
|
# 32-bit interpreter on Windows
|
||||||
|
log.error(
|
||||||
|
"You are running OCRmyPDF in a 32-bit (x86) Python interpreter."
|
||||||
|
"Please use a 64-bit (x86-64) version of Python."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def check_options_languages(options):
|
def check_options_languages(options):
|
||||||
if not options.language:
|
if not options.language:
|
||||||
options.language = [DEFAULT_LANGUAGE]
|
options.language = [DEFAULT_LANGUAGE]
|
||||||
@@ -292,6 +301,7 @@ def check_options_pillow(options):
|
|||||||
|
|
||||||
|
|
||||||
def check_options(options):
|
def check_options(options):
|
||||||
|
check_platform()
|
||||||
check_options_languages(options)
|
check_options_languages(options)
|
||||||
check_options_metadata(options)
|
check_options_metadata(options)
|
||||||
check_options_output(options)
|
check_options_output(options)
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ import sys
|
|||||||
import warnings
|
import warnings
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional, Dict
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,12 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
import shutil
|
import shutil
|
||||||
|
import sys
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from subprocess import PIPE, STDOUT, CalledProcessError, run as subprocess_run
|
from functools import lru_cache
|
||||||
|
from subprocess import PIPE, STDOUT, CalledProcessError
|
||||||
|
from subprocess import run as subprocess_run
|
||||||
|
|
||||||
from ..exceptions import ExitCode, MissingDependencyError
|
from ..exceptions import ExitCode, MissingDependencyError
|
||||||
|
|
||||||
@@ -39,13 +41,42 @@ def _get_program(args, env=None):
|
|||||||
|
|
||||||
|
|
||||||
def run(args, *, env=None, **kwargs):
|
def run(args, *, env=None, **kwargs):
|
||||||
|
"""Wrapper around subprocess.run()
|
||||||
|
|
||||||
|
The main purpose of this wrapper is to allow us to substitute the main program
|
||||||
|
for a spoof in the test suite. The hidden variable _OCRMYPDF_TEST_PATH replaces
|
||||||
|
the main PATH as a location to check for programs to run.
|
||||||
|
|
||||||
|
Secondly we have to account for behavioral differences in Windows in particular.
|
||||||
|
Creating symbolic links in Windows requires administrator privileges and
|
||||||
|
may not work if for some reason we're using a FAT file system or the temporary
|
||||||
|
folder is on a different drive from the working folder. The test suite
|
||||||
|
works around this by creating shim Python scripts that perform the same function
|
||||||
|
as a symbolic link, but those shims require support on this side, to ensure
|
||||||
|
we call them with Python.
|
||||||
|
|
||||||
|
"""
|
||||||
if not env:
|
if not env:
|
||||||
env = os.environ
|
env = os.environ
|
||||||
|
|
||||||
|
# Search in spoof path if necessary
|
||||||
program = _get_program(args, env)
|
program = _get_program(args, env)
|
||||||
|
|
||||||
|
# If we are running a .py on Windows, ensure we call it with this Python
|
||||||
|
# (to support test suite shims)
|
||||||
if os.name == 'nt' and program.lower().endswith('.py'):
|
if os.name == 'nt' and program.lower().endswith('.py'):
|
||||||
args = [sys.executable, program] + args[1:]
|
args = [sys.executable, program] + args[1:]
|
||||||
else:
|
else:
|
||||||
args = [program] + args[1:]
|
args = [program] + args[1:]
|
||||||
|
|
||||||
|
if os.name == 'nt':
|
||||||
|
paths = os.pathsep.join(os.get_exec_path(env))
|
||||||
|
if not shutil.which(args[0], path=paths):
|
||||||
|
shimmed_path = shim_paths_with_program_files(env)
|
||||||
|
new_args0 = shutil.which(args[0], path=shimmed_path)
|
||||||
|
if new_args0:
|
||||||
|
args[0] = new_args0
|
||||||
|
|
||||||
log.debug(args)
|
log.debug(args)
|
||||||
if sys.version_info < (3, 7) and os.name == 'nt':
|
if sys.version_info < (3, 7) and os.name == 'nt':
|
||||||
# Can't use close_fds=True on Windows with Python 3.6 or older
|
# Can't use close_fds=True on Windows with Python 3.6 or older
|
||||||
@@ -55,7 +86,7 @@ def run(args, *, env=None, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def get_version(program, *, version_arg='--version', regex=r'(\d+(\.\d+)*)', env=None):
|
def get_version(program, *, version_arg='--version', regex=r'(\d+(\.\d+)*)', env=None):
|
||||||
"Get the version of the specified program"
|
"""Get the version of the specified program"""
|
||||||
args_prog = [program, version_arg]
|
args_prog = [program, version_arg]
|
||||||
try:
|
try:
|
||||||
proc = run(
|
proc = run(
|
||||||
@@ -91,6 +122,32 @@ def get_version(program, *, version_arg='--version', regex=r'(\d+(\.\d+)*)', env
|
|||||||
return version
|
return version
|
||||||
|
|
||||||
|
|
||||||
|
def shim_paths_with_program_files(env=None):
|
||||||
|
if not env:
|
||||||
|
env = os.environ
|
||||||
|
program_files = env.get('PROGRAMFILES', '')
|
||||||
|
if not program_files:
|
||||||
|
return env.get('PATH', '')
|
||||||
|
paths = []
|
||||||
|
try:
|
||||||
|
for dirname in os.listdir(program_files):
|
||||||
|
if dirname.lower() == 'tesseract-ocr':
|
||||||
|
paths.append(os.path.join(program_files, dirname))
|
||||||
|
elif dirname.lower() == 'gs':
|
||||||
|
try:
|
||||||
|
latest_gs = max(
|
||||||
|
os.listdir(os.path.join(program_files, dirname)),
|
||||||
|
key=lambda d: float(d[2:]),
|
||||||
|
)
|
||||||
|
except (FileNotFoundError, NotADirectoryError):
|
||||||
|
continue
|
||||||
|
paths.append(os.path.join(program_files, dirname, latest_gs, 'bin'))
|
||||||
|
except EnvironmentError:
|
||||||
|
pass
|
||||||
|
paths.extend(path for path in os.get_exec_path(env) if path not in set(paths))
|
||||||
|
return os.pathsep.join(paths)
|
||||||
|
|
||||||
|
|
||||||
missing_program = '''
|
missing_program = '''
|
||||||
The program '{program}' could not be executed or was not found on your
|
The program '{program}' could not be executed or was not found on your
|
||||||
system PATH.
|
system PATH.
|
||||||
|
|||||||
@@ -18,20 +18,20 @@
|
|||||||
"""Interface to Ghostscript executable"""
|
"""Interface to Ghostscript executable"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from os import fspath
|
from os import fspath
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import PIPE, CalledProcessError
|
|
||||||
from shutil import which
|
from shutil import which
|
||||||
|
from subprocess import PIPE, CalledProcessError
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from ..exceptions import SubprocessOutputError, MissingDependencyError
|
from ..exceptions import MissingDependencyError, SubprocessOutputError
|
||||||
from . import get_version, run
|
from . import get_version, run
|
||||||
|
|
||||||
gslog = logging.getLogger()
|
gslog = logging.getLogger()
|
||||||
@@ -329,7 +329,7 @@ def generate_pdfa(
|
|||||||
if _gs_error_reported(stderr):
|
if _gs_error_reported(stderr):
|
||||||
last_part = None
|
last_part = None
|
||||||
repcount = 0
|
repcount = 0
|
||||||
for part in p.stdout.split('****'):
|
for part in stderr.split('****'):
|
||||||
if part != last_part:
|
if part != last_part:
|
||||||
if repcount > 1:
|
if repcount > 1:
|
||||||
log.error(f"(previous error message repeated {repcount} times)")
|
log.error(f"(previous error message repeated {repcount} times)")
|
||||||
|
|||||||
@@ -17,11 +17,11 @@
|
|||||||
|
|
||||||
"""Interface to Tesseract executable"""
|
"""Interface to Tesseract executable"""
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
import logging
|
|
||||||
from os import fspath
|
from os import fspath
|
||||||
from subprocess import PIPE, STDOUT, CalledProcessError, TimeoutExpired
|
from subprocess import PIPE, STDOUT, CalledProcessError, TimeoutExpired
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ from tempfile import TemporaryDirectory
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from ..exceptions import MissingDependencyError, SubprocessOutputError
|
from ..exceptions import MissingDependencyError, SubprocessOutputError
|
||||||
from . import get_version, run as external_run
|
from . import get_version
|
||||||
|
from . import run as external_run
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=1)
|
@lru_cache(maxsize=1)
|
||||||
|
|||||||
@@ -33,8 +33,9 @@ from io import BytesIO
|
|||||||
from os import fspath
|
from os import fspath
|
||||||
from tempfile import TemporaryFile
|
from tempfile import TemporaryFile
|
||||||
|
|
||||||
from .lib._leptonica import ffi
|
|
||||||
from .exceptions import MissingDependencyError
|
from .exceptions import MissingDependencyError
|
||||||
|
from .exec import shim_paths_with_program_files
|
||||||
|
from .lib._leptonica import ffi
|
||||||
|
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
libname = 'liblept-5'
|
libname = 'liblept-5'
|
||||||
|
os.environ['PATH'] = shim_paths_with_program_files()
|
||||||
else:
|
else:
|
||||||
libname = 'lept'
|
libname = 'lept'
|
||||||
_libpath = find_library(libname)
|
_libpath = find_library(libname)
|
||||||
|
|||||||
+2
-1
@@ -21,10 +21,11 @@ import platform
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import PIPE, run
|
from subprocess import PIPE, run
|
||||||
from ocrmypdf import api, cli
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from ocrmypdf import api, cli
|
||||||
|
|
||||||
pytest_plugins = ['helpers_namespace']
|
pytest_plugins = ['helpers_namespace']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -25,14 +25,12 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from subprocess import check_call
|
from subprocess import check_call
|
||||||
|
|
||||||
|
from gs import real_ghostscript
|
||||||
|
|
||||||
"""Replicate one type of Ghostscript feature elision warning during
|
"""Replicate one type of Ghostscript feature elision warning during
|
||||||
PDF/A creation."""
|
PDF/A creation."""
|
||||||
|
|
||||||
|
|
||||||
from gs import real_ghostscript
|
|
||||||
|
|
||||||
|
|
||||||
elision_warning = """GPL Ghostscript 9.20: Setting Overprint Mode to 1
|
elision_warning = """GPL Ghostscript 9.20: Setting Overprint Mode to 1
|
||||||
not permitted in PDF/A-2, overprint mode not set"""
|
not permitted in PDF/A-2, overprint mode not set"""
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,12 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from gs import real_ghostscript
|
||||||
|
|
||||||
|
|
||||||
"""Replicate Ghostscript PDF/A conversion failure by suppressing some
|
"""Replicate Ghostscript PDF/A conversion failure by suppressing some
|
||||||
arguments"""
|
arguments"""
|
||||||
|
|
||||||
from gs import real_ghostscript
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if '--version' in sys.argv:
|
if '--version' in sys.argv:
|
||||||
|
|||||||
@@ -24,11 +24,10 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
"""Replicate Ghostscript raster failure while allowing rendering"""
|
|
||||||
|
|
||||||
|
|
||||||
from gs import real_ghostscript
|
from gs import real_ghostscript
|
||||||
|
|
||||||
|
"""Replicate Ghostscript raster failure while allowing rendering"""
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if '--version' in sys.argv:
|
if '--version' in sys.argv:
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
from gs import real_ghostscript
|
from gs import real_ghostscript
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
"""Tesseract bad utf8 spoof
|
"""Tesseract bad utf8 spoof
|
||||||
|
|
||||||
In 'hocr' mode or 'pdf' mode, return error code 1 and some non-Unicode
|
In 'hocr' mode or 'pdf' mode, return error code 1 and some non-Unicode
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
__version__ = subprocess.check_output(
|
__version__ = subprocess.check_output(
|
||||||
['tesseract', '--version'], stderr=subprocess.STDOUT
|
['tesseract', '--version'], stderr=subprocess.STDOUT
|
||||||
).decode()
|
).decode()
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import pytest
|
|||||||
|
|
||||||
import ocrmypdf
|
import ocrmypdf
|
||||||
|
|
||||||
|
|
||||||
check_ocrmypdf = pytest.helpers.check_ocrmypdf
|
check_ocrmypdf = pytest.helpers.check_ocrmypdf
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
|
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from subprocess import run, PIPE
|
from subprocess import PIPE, run
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
import logging
|
import logging
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
|
||||||
import pikepdf
|
import pikepdf
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|||||||
+1
-1
@@ -18,10 +18,10 @@
|
|||||||
import os
|
import os
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pikepdf
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import ocrmypdf
|
import ocrmypdf
|
||||||
import pikepdf
|
|
||||||
|
|
||||||
|
|
||||||
def test_no_glyphless_graft(resources, outdir):
|
def test_no_glyphless_graft(resources, outdir):
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
|
||||||
from PIL import Image
|
|
||||||
import img2pdf
|
import img2pdf
|
||||||
import pikepdf
|
import pikepdf
|
||||||
|
import pytest
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
import ocrmypdf
|
import ocrmypdf
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -16,8 +16,8 @@
|
|||||||
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
|
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
from os import fspath
|
|
||||||
import os
|
import os
|
||||||
|
from os import fspath
|
||||||
from pickle import dumps, loads
|
from pickle import dumps, loads
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
|||||||
@@ -17,22 +17,22 @@
|
|||||||
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from datetime import timezone
|
|
||||||
import logging
|
import logging
|
||||||
import mmap
|
import mmap
|
||||||
from os import fspath
|
|
||||||
import os
|
import os
|
||||||
|
from datetime import timezone
|
||||||
|
from os import fspath
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shutil import copyfile, move
|
from shutil import copyfile, move
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
import pikepdf
|
import pikepdf
|
||||||
|
import pytest
|
||||||
|
from pikepdf.models.metadata import decode_pdf_date
|
||||||
|
|
||||||
from ocrmypdf._jobcontext import PDFContext
|
from ocrmypdf._jobcontext import PDFContext
|
||||||
from ocrmypdf.exceptions import ExitCode
|
from ocrmypdf.exceptions import ExitCode
|
||||||
from ocrmypdf.pdfa import SRGB_ICC_PROFILE, file_claims_pdfa, generate_pdfa_ps
|
from ocrmypdf.pdfa import SRGB_ICC_PROFILE, file_claims_pdfa, generate_pdfa_ps
|
||||||
from pikepdf.models.metadata import decode_pdf_date
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import fitz
|
import fitz
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ import logging
|
|||||||
from os import fspath
|
from os import fspath
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pikepdf
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
import pikepdf
|
|
||||||
from ocrmypdf import optimize as opt
|
from ocrmypdf import optimize as opt
|
||||||
from ocrmypdf.exec import jbig2enc, pngquant
|
from ocrmypdf.exec import jbig2enc, pngquant
|
||||||
from ocrmypdf.exec.ghostscript import rasterize_pdf
|
from ocrmypdf.exec.ghostscript import rasterize_pdf
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import pytest
|
|||||||
|
|
||||||
import ocrmypdf
|
import ocrmypdf
|
||||||
from ocrmypdf._validation import _pages_from_ranges
|
from ocrmypdf._validation import _pages_from_ranges
|
||||||
from ocrmypdf.pdfinfo import PdfInfo
|
|
||||||
from ocrmypdf.exceptions import BadArgsError
|
from ocrmypdf.exceptions import BadArgsError
|
||||||
|
from ocrmypdf.pdfinfo import PdfInfo
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ from math import isclose
|
|||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
import img2pdf
|
import img2pdf
|
||||||
|
import pikepdf
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from reportlab.pdfgen.canvas import Canvas
|
from reportlab.pdfgen.canvas import Canvas
|
||||||
|
|
||||||
import pikepdf
|
|
||||||
from ocrmypdf import pdfinfo
|
from ocrmypdf import pdfinfo
|
||||||
from ocrmypdf.pdfinfo import Colorspace, Encoding
|
from ocrmypdf.pdfinfo import Colorspace, Encoding
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ from os import fspath
|
|||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
import img2pdf
|
import img2pdf
|
||||||
|
import pikepdf
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
import pikepdf
|
|
||||||
from ocrmypdf import leptonica
|
from ocrmypdf import leptonica
|
||||||
from ocrmypdf.exec import ghostscript, tesseract
|
from ocrmypdf.exec import ghostscript, tesseract
|
||||||
from ocrmypdf.pdfinfo import PdfInfo
|
from ocrmypdf.pdfinfo import PdfInfo
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import DEVNULL, PIPE, run, Popen, CalledProcessError
|
from subprocess import DEVNULL, PIPE, CalledProcessError, Popen, run
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ from unittest.mock import patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ocrmypdf.cli import parser
|
|
||||||
from ocrmypdf._validation import check_options
|
from ocrmypdf._validation import check_options
|
||||||
|
from ocrmypdf.cli import parser
|
||||||
from ocrmypdf.exceptions import ExitCode, MissingDependencyError
|
from ocrmypdf.exceptions import ExitCode, MissingDependencyError
|
||||||
from ocrmypdf.exec import unpaper
|
from ocrmypdf.exec import unpaper
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import pytest
|
|||||||
|
|
||||||
import ocrmypdf._validation as vd
|
import ocrmypdf._validation as vd
|
||||||
from ocrmypdf.api import create_options
|
from ocrmypdf.api import create_options
|
||||||
from ocrmypdf.exceptions import MissingDependencyError, BadArgsError
|
from ocrmypdf.exceptions import BadArgsError, MissingDependencyError
|
||||||
from ocrmypdf.pdfinfo import PdfInfo
|
from ocrmypdf.pdfinfo import PdfInfo
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user