Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2deee4920 | ||
|
|
1efa79cce2 | ||
|
|
b3b61c152c | ||
|
|
e429c3d729 | ||
|
|
8308b20096 | ||
|
|
8b41f60b6e | ||
|
|
d56f749017 | ||
|
|
9f31774aa9 | ||
|
|
7d55f6e01f | ||
|
|
c3bd2f296d | ||
|
|
e40c60d4d8 | ||
|
|
3960232ae0 | ||
|
|
5fbb3fc6ac | ||
|
|
0b1db8fccd | ||
|
|
0417610f9b | ||
|
|
43a23e3695 | ||
|
|
c4ca572afd | ||
|
|
e04e4565a9 | ||
|
|
2391fb0be0 | ||
|
|
c16f79d51b | ||
|
|
e3e888efde | ||
|
|
84f7e34ace | ||
|
|
32e2175891 | ||
|
|
975abfde9a | ||
|
|
d929ed76c2 | ||
|
|
4a27124eab |
-159
@@ -1,159 +0,0 @@
|
|||||||
branches:
|
|
||||||
except:
|
|
||||||
- azure
|
|
||||||
|
|
||||||
cache:
|
|
||||||
pip: true
|
|
||||||
directories:
|
|
||||||
- $HOME/Library/Caches/Homebrew
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: linux
|
|
||||||
dist: trusty
|
|
||||||
sudo: required
|
|
||||||
language: python
|
|
||||||
python: "3.6"
|
|
||||||
env:
|
|
||||||
- DIST=trusty
|
|
||||||
- MINIMAL=true
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
update: true
|
|
||||||
sources:
|
|
||||||
- sourceline: "ppa:alex-p/tesseract-ocr"
|
|
||||||
- sourceline: "ppa:vshn/ghostscript"
|
|
||||||
packages:
|
|
||||||
- ghostscript
|
|
||||||
- libffi-dev
|
|
||||||
- tesseract-ocr
|
|
||||||
- tesseract-ocr-deu
|
|
||||||
- tesseract-ocr-eng
|
|
||||||
- tesseract-ocr-fra
|
|
||||||
before_install: |
|
|
||||||
pip3 install --upgrade pip
|
|
||||||
pip3 install --upgrade wheel
|
|
||||||
- os: linux
|
|
||||||
dist: trusty
|
|
||||||
sudo: required
|
|
||||||
language: python
|
|
||||||
python: "3.6"
|
|
||||||
env:
|
|
||||||
- DIST=trusty
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
update: true
|
|
||||||
sources:
|
|
||||||
- sourceline: "ppa:alex-p/tesseract-ocr"
|
|
||||||
- sourceline: "ppa:heyarje/libav-11"
|
|
||||||
- sourceline: "ppa:vshn/ghostscript"
|
|
||||||
packages:
|
|
||||||
- ghostscript
|
|
||||||
- libavcodec56
|
|
||||||
- libavformat56
|
|
||||||
- libavutil54
|
|
||||||
- libffi-dev
|
|
||||||
- tesseract-ocr
|
|
||||||
- tesseract-ocr-deu
|
|
||||||
- tesseract-ocr-eng
|
|
||||||
- tesseract-ocr-fra
|
|
||||||
- libexempi3 # --- optional extras from here ---
|
|
||||||
- pngquant
|
|
||||||
- poppler-utils
|
|
||||||
before_install: |
|
|
||||||
mkdir -p bin packages
|
|
||||||
pip3 install --upgrade pip
|
|
||||||
pip3 install --upgrade wheel
|
|
||||||
- os: linux
|
|
||||||
dist: xenial
|
|
||||||
sudo: required
|
|
||||||
language: python
|
|
||||||
python: "3.7"
|
|
||||||
env:
|
|
||||||
- DIST=xenial
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
update: true
|
|
||||||
sources:
|
|
||||||
- sourceline: "ppa:alex-p/tesseract-ocr"
|
|
||||||
packages:
|
|
||||||
- ghostscript
|
|
||||||
- libexempi3
|
|
||||||
- libffi-dev
|
|
||||||
- pngquant
|
|
||||||
- poppler-utils
|
|
||||||
- tesseract-ocr
|
|
||||||
- tesseract-ocr-deu
|
|
||||||
- tesseract-ocr-eng
|
|
||||||
- tesseract-ocr-fra
|
|
||||||
- unpaper
|
|
||||||
- os: linux
|
|
||||||
dist: bionic
|
|
||||||
sudo: required
|
|
||||||
language: python
|
|
||||||
python: "3.8"
|
|
||||||
env:
|
|
||||||
- DIST=bionic
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
update: true
|
|
||||||
sources:
|
|
||||||
- sourceline: "ppa:alex-p/tesseract-ocr"
|
|
||||||
packages:
|
|
||||||
- ghostscript
|
|
||||||
- libexempi3
|
|
||||||
- libffi-dev
|
|
||||||
- pngquant
|
|
||||||
- poppler-utils
|
|
||||||
- tesseract-ocr
|
|
||||||
- tesseract-ocr-deu
|
|
||||||
- tesseract-ocr-eng
|
|
||||||
- tesseract-ocr-fra
|
|
||||||
- unpaper
|
|
||||||
- os: osx
|
|
||||||
language: generic
|
|
||||||
addons:
|
|
||||||
homebrew:
|
|
||||||
update: true
|
|
||||||
packages:
|
|
||||||
- exempi
|
|
||||||
- ghostscript
|
|
||||||
- jbig2enc
|
|
||||||
- leptonica
|
|
||||||
- openjpeg
|
|
||||||
- pngquant
|
|
||||||
- python
|
|
||||||
- qpdf
|
|
||||||
- tesseract
|
|
||||||
- unpaper
|
|
||||||
before_install: |
|
|
||||||
pip3 install --upgrade pip
|
|
||||||
pip3 install wheel
|
|
||||||
|
|
||||||
before_cache:
|
|
||||||
- rm -f $HOME/.cache/pip/log/debug.log
|
|
||||||
|
|
||||||
install:
|
|
||||||
- mkdir -p bin
|
|
||||||
- export PATH=$PWD/bin:$PATH
|
|
||||||
- pip3 install -r requirements/main.txt -r requirements/test.txt .
|
|
||||||
|
|
||||||
script:
|
|
||||||
- tesseract --version
|
|
||||||
- pytest -n auto
|
|
||||||
# deploy:
|
|
||||||
# # release for main pypi
|
|
||||||
# # 3.7 is considered the build leader and does the deploy, otherwise there is
|
|
||||||
# # a race and all versions will try to deploy
|
|
||||||
# # OTOH if we ever need separate binary wheels then each version needs its
|
|
||||||
# # own deploy
|
|
||||||
# - provider: pypi
|
|
||||||
# user: ocrmypdf-travis
|
|
||||||
# password:
|
|
||||||
# secure: "DTFOmmNL6olA0+yXvp4u9jXZlZeqrJsJ0526jzqf4a3gZ6jnGTq5UI6WzRsslSyoMMfXKtHQebqHM6ogSgCZinyZ3ufHJo8fn9brxbEc2gsiWkbj5o3bGwdWMT1vNNE7XW0VCpw87rZ1EEwjl4FJHFudMlPR1yfU5+uq0k0PACo="
|
|
||||||
# distributions: "sdist bdist_wheel"
|
|
||||||
# on:
|
|
||||||
# branch: master
|
|
||||||
# tags: true
|
|
||||||
# condition: $TRAVIS_PYTHON_VERSION == "3.7" && $TRAVIS_OS_NAME == "linux"
|
|
||||||
# skip_upload_docs: true
|
|
||||||
@@ -130,6 +130,7 @@ Press & Media
|
|||||||
- [Converting a scanned document into a compressed searchable PDF with redactions](https://medium.com/@treyharris/converting-a-scanned-document-into-a-compressed-searchable-pdf-with-redactions-63f61c34fe4c)
|
- [Converting a scanned document into a compressed searchable PDF with redactions](https://medium.com/@treyharris/converting-a-scanned-document-into-a-compressed-searchable-pdf-with-redactions-63f61c34fe4c)
|
||||||
- [c't 1-2014, page 59](https://heise.de/-2279695): Detailed presentation of OCRmyPDF v1.0 in the leading German IT magazine c't
|
- [c't 1-2014, page 59](https://heise.de/-2279695): Detailed presentation of OCRmyPDF v1.0 in the leading German IT magazine c't
|
||||||
- [heise Open Source, 09/2014: Texterkennung mit OCRmyPDF](https://heise.de/-2356670)
|
- [heise Open Source, 09/2014: Texterkennung mit OCRmyPDF](https://heise.de/-2356670)
|
||||||
|
- [heise Durchsuchbare PDF-Dokumente mit OCRmyPDF erstellen](https://www.heise.de/ratgeber/Durchsuchbare-PDF-Dokumente-mit-OCRmyPDF-erstellen-4607592.html)
|
||||||
|
|
||||||
Business enquiries
|
Business enquiries
|
||||||
------------------
|
------------------
|
||||||
|
|||||||
Vendored
-6
@@ -95,12 +95,6 @@ Files: tests/resources/vector.pdf
|
|||||||
Copyright: (C) 2018 Catscratch
|
Copyright: (C) 2018 Catscratch
|
||||||
License: Expat
|
License: Expat
|
||||||
|
|
||||||
Files: test/resources/enron*.pdf
|
|
||||||
Copyright: EnronData.org
|
|
||||||
License: CC-BY-3.0
|
|
||||||
See: https://enrondata.readthedocs.io/en/latest/data/edo-enron-email-pst-dataset/
|
|
||||||
Comment: Unprocessed.
|
|
||||||
|
|
||||||
Files: src/ocrmypdf/data/sRGB.icc
|
Files: src/ocrmypdf/data/sRGB.icc
|
||||||
Copyright: Kai-Uwe Behrmann <www.behrmann.name>
|
Copyright: Kai-Uwe Behrmann <www.behrmann.name>
|
||||||
Marti Maria <www.littlecms.com>
|
Marti Maria <www.littlecms.com>
|
||||||
|
|||||||
+15
-108
@@ -69,53 +69,8 @@ Sample script
|
|||||||
This user contributed script also provides an example of batch
|
This user contributed script also provides an example of batch
|
||||||
processing.
|
processing.
|
||||||
|
|
||||||
.. code-block:: python
|
.. literalinclude:: ../misc/batch.py
|
||||||
|
:caption: misc/batch.py
|
||||||
#!/usr/bin/env python3
|
|
||||||
# Walk through directory tree, replacing all files with OCR'd version
|
|
||||||
# Original version by DeliciousPickle@github; modified
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import ocrmypdf
|
|
||||||
|
|
||||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
print(script_dir + '/ocr-tree.py: Start')
|
|
||||||
|
|
||||||
if len(sys.argv) > 1:
|
|
||||||
start_dir = sys.argv[1]
|
|
||||||
else:
|
|
||||||
start_dir = '.'
|
|
||||||
|
|
||||||
if len(sys.argv) > 2:
|
|
||||||
log_file = sys.argv[2]
|
|
||||||
else:
|
|
||||||
log_file = script_dir + '/ocr-tree.log'
|
|
||||||
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO, format='%(asctime)s %(message)s',
|
|
||||||
filename=log_file, filemode='w')
|
|
||||||
|
|
||||||
ocrmypdf.configure_logging(ocrmypdf.Verbosity.default)
|
|
||||||
|
|
||||||
for dir_name, subdirs, file_list in os.walk(start_dir):
|
|
||||||
logging.info('\n')
|
|
||||||
logging.info(dir_name + '\n')
|
|
||||||
os.chdir(dir_name)
|
|
||||||
for filename in file_list:
|
|
||||||
file_ext = os.path.splitext(filename)[1]
|
|
||||||
if file_ext == '.pdf':
|
|
||||||
full_path = dir_name + '/' + filename
|
|
||||||
print(full_path)
|
|
||||||
result = ocrmypdf.ocr(filename, filename, deskew=True)
|
|
||||||
if result == ocrmypdf.ExitCode.already_done_ocr:
|
|
||||||
print("Skipped document because it already contained text")
|
|
||||||
elif result == ocrmypdf.ExitCode.ok:
|
|
||||||
print("OCR complete")
|
|
||||||
logging.info(result)
|
|
||||||
|
|
||||||
Synology DiskStations
|
Synology DiskStations
|
||||||
---------------------
|
---------------------
|
||||||
@@ -131,62 +86,8 @@ products use ARM or Power processors and do not support Docker. Further
|
|||||||
adjustments might be needed to deal with the Synology's relatively
|
adjustments might be needed to deal with the Synology's relatively
|
||||||
limited CPU and RAM.
|
limited CPU and RAM.
|
||||||
|
|
||||||
.. code-block:: python
|
.. literalinclude:: ../misc/synology.py
|
||||||
|
:caption: misc/synology.py - Sample script for Synology DiskStations
|
||||||
#!/bin/env python3
|
|
||||||
# Contributed by github.com/Enantiomerie
|
|
||||||
|
|
||||||
# script needs 2 arguments
|
|
||||||
# 1. source dir with *.pdf - default is location of script
|
|
||||||
# 2. move dir where *.pdf and *_OCR.pdf are moved to
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import shutil
|
|
||||||
|
|
||||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
timestamp = time.strftime("%Y-%m-%d-%H%M_")
|
|
||||||
log_file = script_dir + '/' + timestamp + 'ocrmypdf.log'
|
|
||||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s', filename=log_file, filemode='w')
|
|
||||||
|
|
||||||
if len(sys.argv) > 1:
|
|
||||||
start_dir = sys.argv[1]
|
|
||||||
else:
|
|
||||||
start_dir = '.'
|
|
||||||
|
|
||||||
for dir_name, subdirs, file_list in os.walk(start_dir):
|
|
||||||
logging.info('\n')
|
|
||||||
logging.info(dir_name + '\n')
|
|
||||||
os.chdir(dir_name)
|
|
||||||
for filename in file_list:
|
|
||||||
file_ext = os.path.splitext(filename)[1]
|
|
||||||
if file_ext == '.pdf':
|
|
||||||
full_path = dir_name + '/' + filename
|
|
||||||
file_noext = os.path.splitext(filename)[0]
|
|
||||||
timestamp_OCR = time.strftime("%Y-%m-%d-%H%M_OCR_")
|
|
||||||
filename_OCR = timestamp_OCR + file_noext + '.pdf'
|
|
||||||
docker_mount = dir_name + ':/home/docker'
|
|
||||||
# create string for pdf processing
|
|
||||||
# diskstation needs a user:group docker:docker. find uid:gid of your diskstation docker:docker with id docker.
|
|
||||||
# use this uid:gid in -u flag
|
|
||||||
# rw rights for docker:docker at source dir are also necessary
|
|
||||||
# the script is processed as root user via chron
|
|
||||||
cmd = ['docker', 'run', '--rm', '-v', docker_mount, '-u=1030:65538', 'jbarlow83/ocrmypdf', , '--deskew' , filename, filename_OCR]
|
|
||||||
logging.info(cmd)
|
|
||||||
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
result = proc.stdout.read()
|
|
||||||
logging.info(result)
|
|
||||||
full_path_OCR = dir_name + '/' + filename_OCR
|
|
||||||
os.chmod(full_path_OCR, 0o666)
|
|
||||||
os.chmod(full_path, 0o666)
|
|
||||||
full_path_OCR_archive = sys.argv[2]
|
|
||||||
full_path_archive = sys.argv[2] + '/no_ocr'
|
|
||||||
shutil.move(full_path_OCR,full_path_OCR_archive)
|
|
||||||
shutil.move(full_path, full_path_archive)
|
|
||||||
logging.info('Finished.\n')
|
|
||||||
|
|
||||||
Huge batch jobs
|
Huge batch jobs
|
||||||
---------------
|
---------------
|
||||||
@@ -235,14 +136,21 @@ 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
|
may not be suitable for some environments, such as filesystems shared on a
|
||||||
slow network.
|
slow network.
|
||||||
|
|
||||||
|
A configuration manager such as Docker Compose could be used to ensure that the
|
||||||
|
service is always available.
|
||||||
|
|
||||||
|
.. literalinclude:: ../misc/docker-compose.example.yml
|
||||||
|
:language: yaml
|
||||||
|
:caption: misc/docker-compose.example.yml
|
||||||
|
|
||||||
Watched folders with watcher.py
|
Watched folders with watcher.py
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
The watcher service may also be run natively.
|
The watcher service may also be run natively, without Docker:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
pip3 install -r reqs/watcher.txt
|
pip3 install -r requirements/watcher.txt
|
||||||
|
|
||||||
env OCR_INPUT_DIRECTORY=/mnt/input-pdfs \
|
env OCR_INPUT_DIRECTORY=/mnt/input-pdfs \
|
||||||
OCR_OUTPUT_DIRECTORY=/mnt/output-pdfs \
|
OCR_OUTPUT_DIRECTORY=/mnt/output-pdfs \
|
||||||
@@ -319,8 +227,7 @@ of Automator, the ``PATH`` may be set differently your Terminal's
|
|||||||
``PATH``; you may need to explicitly set the PATH to include
|
``PATH``; you may need to explicitly set the PATH to include
|
||||||
``ocrmypdf``. The following example may serve as a starting point:
|
``ocrmypdf``. The following example may serve as a starting point:
|
||||||
|
|
||||||
|Example macOS Automator script|
|
.. figure:: images/macos-workflow.png
|
||||||
|
:alt: Example macOS Automator workflow
|
||||||
|
|
||||||
You may customize the command sent to ocrmypdf.
|
You may customize the command sent to ocrmypdf.
|
||||||
|
|
||||||
.. |Example macOS Automator script| image:: images/macos-workflow.png
|
|
||||||
|
|||||||
@@ -237,6 +237,32 @@ You can also optimize all images without performing any OCR:
|
|||||||
|
|
||||||
ocrmypdf --tesseract-timeout=0 --optimize 3 --skip-text input.pdf output.pdf
|
ocrmypdf --tesseract-timeout=0 --optimize 3 --skip-text input.pdf output.pdf
|
||||||
|
|
||||||
|
Perform OCR only certain pages
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
You can ask OCRmyPDF to only apply OCR to certain pages.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
ocrmypdf --pages 2,3,13-17 input.pdf output.pdf
|
||||||
|
|
||||||
|
Hyphens denote a range of pages and commas separate page numbers. If you prefer
|
||||||
|
to use spaces, quote all of the page numbers: ``--pages '2, 3, 5, 7'``.
|
||||||
|
|
||||||
|
OCRmyPDF will warn if your list of page numbers contains duplicates or
|
||||||
|
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:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
ocrmypdf --pages 1 --output-type pdf --optimize 0 input.pdf output.pdf
|
||||||
|
|
||||||
Redo existing OCR
|
Redo existing OCR
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
|||||||
+19
-17
@@ -56,35 +56,34 @@ See the Docker documentation for
|
|||||||
Using the Docker image on the command line
|
Using the Docker image on the command line
|
||||||
==========================================
|
==========================================
|
||||||
|
|
||||||
**Unlike typical Docker containers**, in this mode we are using the
|
**Unlike typical Docker containers**, in this section the OCRmyPDF Docker
|
||||||
OCRmyPDF Docker container is intended to be emphemeral – it runs for one
|
container is emphemeral – it runs for one OCR job and terminates, just like a
|
||||||
OCR job and then terminates, just like a command line program. We are
|
command line program. We are using Docker to deliver an application (as opposed
|
||||||
using Docker as a way of delivering an application, not a server.
|
to the more conventional case, where a Docker container runs as a server).
|
||||||
|
|
||||||
To start a Docker container (instance of the image):
|
To start a Docker container (instance of the image):
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker tag jbarlow83/ocrmypdf ocrmypdf
|
docker tag jbarlow83/ocrmypdf ocrmypdf
|
||||||
docker run --rm -i ocrmypdf (... all other arguments here...)
|
docker run --rm -i ocrmypdf (... all other arguments here...) - -
|
||||||
|
|
||||||
For convenience, create a shell alias to hide the Docker command. It is
|
For convenience, create a shell alias to hide the Docker command. It is
|
||||||
easier to send the input file to file stdin and read the output from
|
easier to send the input file as stdin and read the output from
|
||||||
stdout – this avoids the occasionally messy permission issues with
|
stdout – **this avoids the messy permission issues with Docker entirely**.
|
||||||
Docker entirely.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
alias ocrmypdf='docker run --rm -i ocrmypdf'
|
alias docker_ocrmypdf='docker run --rm -i ocrmypdf'
|
||||||
ocrmypdf --version # runs docker version
|
docker_ocrmypdf --version # runs docker version
|
||||||
ocrmypdf <input.pdf >output.pdf
|
docker_ocrmypdf - - <input.pdf >output.pdf
|
||||||
|
|
||||||
Or in the wonderful `fish shell <https://fishshell.com/>`__:
|
Or in the wonderful `fish shell <https://fishshell.com/>`__:
|
||||||
|
|
||||||
.. code-block:: fish
|
.. code-block:: fish
|
||||||
|
|
||||||
alias ocrmypdf 'docker run --rm ocrmypdf'
|
alias docker_ocrmypdf 'docker run --rm ocrmypdf'
|
||||||
funcsave ocrmypdf
|
funcsave docker_ocrmypdf
|
||||||
|
|
||||||
Alternately, you could mount the local current working directory as a
|
Alternately, you could mount the local current working directory as a
|
||||||
Docker volume:
|
Docker volume:
|
||||||
@@ -93,15 +92,18 @@ Docker volume:
|
|||||||
|
|
||||||
docker run --rm -v $(pwd):/data ocrmypdf /data/input.pdf /data/output.pdf
|
docker run --rm -v $(pwd):/data ocrmypdf /data/input.pdf /data/output.pdf
|
||||||
|
|
||||||
|
(However, when done this way, ``output.pdf`` may be owned by the root
|
||||||
|
user.)
|
||||||
|
|
||||||
.. _docker-lang-packs:
|
.. _docker-lang-packs:
|
||||||
|
|
||||||
Adding languages to the Docker image
|
Adding languages to the Docker image
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
By default the Docker image includes English, German and Simplified
|
By default the Docker image includes English, German, Simplified Chinese,
|
||||||
Chinese, the most popular languages for OCRmyPDF users based on
|
French, Portuguese and Spanish, the most popular languages for OCRmyPDF
|
||||||
feedback. You may add other languages by creating a new Dockerfile based
|
users based on feedback. You may add other languages by creating a new
|
||||||
on the public one:
|
Dockerfile based on the public one:
|
||||||
|
|
||||||
.. code-block:: dockerfile
|
.. code-block:: dockerfile
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
OCRmyPDF documentation
|
OCRmyPDF documentation
|
||||||
======================
|
======================
|
||||||
|
|
||||||
OCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to
|
OCRmyPDF adds an optical charcter recognition (OCR) text layer to scanned PDF
|
||||||
be searched.
|
files, allowing them to be searched.
|
||||||
|
|
||||||
PDF is the best format for storing and exchanging scanned documents.
|
PDF is the best format for storing and exchanging scanned documents.
|
||||||
Unfortunately, PDFs can be difficult to modify. OCRmyPDF makes it easy to apply
|
Unfortunately, PDFs can be difficult to modify. OCRmyPDF makes it easy to apply
|
||||||
|
|||||||
+79
-14
@@ -110,7 +110,7 @@ Fedora 29 or newer
|
|||||||
| |fedora-29| |fedora-30| |fedora-rawhide| |
|
| |fedora-29| |fedora-30| |fedora-rawhide| |
|
||||||
+-----------------------------------------------+
|
+-----------------------------------------------+
|
||||||
|
|
||||||
Users of Fedora 29 later may simply
|
Users of Fedora 29 or later may simply
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
@@ -136,8 +136,9 @@ from sources <#installing-head-revision-from-sources>`__.
|
|||||||
Installing the latest version on Ubuntu 18.04 LTS
|
Installing the latest version on Ubuntu 18.04 LTS
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
Ubuntu 18.04 includes ocrmypdf 6.1.2. To install a more recent version,
|
Ubuntu 18.04 includes ocrmypdf 6.1.2 - you can install that with ``apt``, but
|
||||||
first install the system version to get most of the dependencies:
|
it is quite old now. To install a more recent version, first install several
|
||||||
|
system dependencies:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
@@ -216,7 +217,8 @@ of ``pip`` at ``/usr/local/bin/pip``.
|
|||||||
**Install OCRmyPDF**
|
**Install OCRmyPDF**
|
||||||
|
|
||||||
OCRmyPDF requires the locale to be set for UTF-8. **On some minimal
|
OCRmyPDF requires the locale to be set for UTF-8. **On some minimal
|
||||||
Ubuntu installations systems**, it may be necessary to set the locale.
|
Ubuntu installations**, such as the Ubuntu 16.04 Docker images it may be
|
||||||
|
necessary to set the locale.
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
@@ -296,23 +298,86 @@ compiled by hand.
|
|||||||
|
|
||||||
To add JBIG2 encoding, see :ref:`jbig2`.
|
To add JBIG2 encoding, see :ref:`jbig2`.
|
||||||
|
|
||||||
ArchLinux (AUR)
|
Arch Linux (AUR)
|
||||||
---------------
|
----------------
|
||||||
|
|
||||||
.. image:: https://repology.org/badge/version-for-repo/aur/ocrmypdf.svg
|
.. image:: https://repology.org/badge/version-for-repo/aur/ocrmypdf.svg
|
||||||
:alt: ArchLinux
|
:alt: ArchLinux
|
||||||
:target: https://repology.org/metapackage/ocrmypdf
|
:target: https://repology.org/metapackage/ocrmypdf
|
||||||
|
|
||||||
There is an `ArchLinux User Repository package for
|
There is an `Arch User Repository (AUR) package for OCRmyPDF
|
||||||
ocrmypdf <https://aur.archlinux.org/packages/ocrmypdf/>`__. You can use
|
<https://aur.archlinux.org/packages/ocrmypdf/>`__.
|
||||||
the following command.
|
|
||||||
|
Installing AUR packages as root is not allowed, so you must first `setup a
|
||||||
|
non-root user
|
||||||
|
<https://wiki.archlinux.org/index.php/Users_and_groups#User_management>`__ and
|
||||||
|
`configure sudo <https://wiki.archlinux.org/index.php/Sudo#Configuration>`__.
|
||||||
|
The standard Docker image, ``archlinux/base:latest``, does **not** have a
|
||||||
|
non-root user configured, so users of that image must follow these guides. If
|
||||||
|
you are using a VM image, such as `the official Vagrant image
|
||||||
|
<https://app.vagrantup.com/archlinux/boxes/archlinux>`__, this work may already
|
||||||
|
be completed for you.
|
||||||
|
|
||||||
|
Next you should install the `base-devel package group
|
||||||
|
<https://www.archlinux.org/groups/x86_64/base-devel/>`__. This includes the
|
||||||
|
standard tooling needed to build packages, such as a compiler and binary tools.
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
yaourt -S ocrmypdf
|
sudo pacman -S base-devel
|
||||||
|
|
||||||
If you have any difficulties with installation, check the repository
|
The OCRmyPDF package depends on `the python-pdfminer.six AUR package
|
||||||
package page.
|
<https://aur.archlinux.org/packages/python-pdfminer.six/>`__. Dependencies on
|
||||||
|
AUR packages are not automatically resolved, so this package must be manually
|
||||||
|
installed first.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
curl -O https://aur.archlinux.org/cgit/aur.git/snapshot/python-pdfminer.six.tar.gz
|
||||||
|
tar xvzf python-pdfminer.six.tar.gz
|
||||||
|
cd python-pdfminer.six
|
||||||
|
makepkg -sri
|
||||||
|
|
||||||
|
With that complete you can then repeat the same series of steps for the
|
||||||
|
OCRmyPDF package.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
curl -O https://aur.archlinux.org/cgit/aur.git/snapshot/ocrmypdf.tar.gz
|
||||||
|
tar xvzf ocrmypdf.tar.gz
|
||||||
|
cd ocrmypdf
|
||||||
|
makepkg -sri
|
||||||
|
|
||||||
|
At this point you will have a working install of OCRmyPDF, but the Tesseract
|
||||||
|
install won’t include any OCR language data. You can install `the
|
||||||
|
tesseract-data package group
|
||||||
|
<https://www.archlinux.org/groups/any/tesseract-data/>`__ to add all supported
|
||||||
|
languages, or use that package listing to identify the appropriate package for
|
||||||
|
your desired language.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
sudo pacman -S tesseract-data-eng
|
||||||
|
|
||||||
|
As an alternative to this manual procedure, consider using an `AUR helper
|
||||||
|
<https://wiki.archlinux.org/index.php/AUR_helpers>`__. Such a tool will
|
||||||
|
automatically fetch, build and install the AUR package, resolve dependencies
|
||||||
|
(including dependencies on AUR packages), and ease the upgrade procedure.
|
||||||
|
|
||||||
|
If you have any difficulties with installation, check the repository package
|
||||||
|
page.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The OCRmyPDF AUR package currently omits the JBIG2 encoder. OCRmyPDF works
|
||||||
|
fine without it but will produce larger output files. The encoder is
|
||||||
|
available from `the jbig2enc-git AUR package
|
||||||
|
<https://aur.archlinux.org/packages/jbig2enc-git/>`__ and may be installed
|
||||||
|
using the same series of steps as for the installation of the pdfminer.six
|
||||||
|
and OCRmyPDF AUR packages. Alternatively, it may be built manually from
|
||||||
|
source following the instructions in `Installing the JBIG2 encoder
|
||||||
|
<jbig2>`__. If JBIG2 is installed, OCRmyPDF 7.0.0 and later will
|
||||||
|
automatically detect it.
|
||||||
|
|
||||||
Alpine Linux
|
Alpine Linux
|
||||||
------------
|
------------
|
||||||
@@ -437,12 +502,12 @@ Installing on FreeBSD
|
|||||||
:alt: FreeBSD
|
:alt: FreeBSD
|
||||||
:target: https://repology.org/project/python:ocrmypdf/versions
|
:target: https://repology.org/project/python:ocrmypdf/versions
|
||||||
|
|
||||||
FreeBSD 11.2, 11.3, 12.0-RELEASE and 13.0-CURRENT are supported. Other
|
FreeBSD 11.3, 12.0, 12.1-RELEASE and 13.0-CURRENT are supported. Other
|
||||||
versions likely work but have not been tested.
|
versions likely work but have not been tested.
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
pkg install py36-ocrmypdf
|
pkg install py37-ocrmypdf
|
||||||
|
|
||||||
To install a more recent version, you could attempt to first install the system
|
To install a more recent version, you could attempt to first install the system
|
||||||
version with ``pkg``, then use ``pip install --user ocrmypdf``.
|
version with ``pkg``, then use ``pip install --user ocrmypdf``.
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ About OCR
|
|||||||
`Optical character
|
`Optical character
|
||||||
recognition <https://en.wikipedia.org/wiki/Optical_character_recognition>`__
|
recognition <https://en.wikipedia.org/wiki/Optical_character_recognition>`__
|
||||||
is technology that converts images of typed or handwritten text, such as
|
is technology that converts images of typed or handwritten text, such as
|
||||||
in a scanned document, to computer text that can be searched and copied.
|
in a scanned document, to computer text that can be selected, searched and copied.
|
||||||
|
|
||||||
OCRmyPDF uses
|
OCRmyPDF uses
|
||||||
`Tesseract <https://github.com/tesseract-ocr/tesseract>`__, the best
|
`Tesseract <https://github.com/tesseract-ocr/tesseract>`__, the best
|
||||||
|
|||||||
+11
-2
@@ -4,11 +4,20 @@
|
|||||||
Installing additional language packs
|
Installing additional language packs
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
OCRmyPDF uses Tesseract for OCR, and relies on its language packs for
|
OCRmyPDF uses Tesseract for OCR, and relies on its language packs for all languages.
|
||||||
languages other than English.
|
On most platforms, English is installed with Tesseract by default, but not always.
|
||||||
|
|
||||||
Tesseract supports `most
|
Tesseract supports `most
|
||||||
languages <https://github.com/tesseract-ocr/tesseract/blob/master/doc/tesseract.1.asc#languages>`__.
|
languages <https://github.com/tesseract-ocr/tesseract/blob/master/doc/tesseract.1.asc#languages>`__.
|
||||||
|
Languages are identified by standardized three-letter codes (called ISO 639-2 Alpha-3).
|
||||||
|
Tesseract's documentation also lists the three-letter code for your language.
|
||||||
|
Some are anglicized, e.g. Spanish is ``spa`` rather than ``esp``, while others
|
||||||
|
are not, e.g. German is ``deu``.
|
||||||
|
|
||||||
|
After you have installed a language pack, you can use it ``ocrmypdf -l <language>``,
|
||||||
|
for example ``ocrmypdf -l spa``. For multilingual documents, you can specify
|
||||||
|
all languages to be expected, e.g. ``ocrmypdf -l eng+fra`` for English and French.
|
||||||
|
English is assumed by default unless other language(s) are specified.
|
||||||
|
|
||||||
For Linux users, you can often find packages that provide language
|
For Linux users, you can often find packages that provide language
|
||||||
packs:
|
packs:
|
||||||
|
|||||||
@@ -13,6 +13,30 @@ 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.6.1
|
||||||
|
======
|
||||||
|
|
||||||
|
- Documentation improvements - thanks to many users for their contributions!
|
||||||
|
|
||||||
|
- Fixed installation instructions for ArchLinux (@pigmonkey)
|
||||||
|
- Updated installation instructions for FreeBSD and other OSes (@knobix)
|
||||||
|
- Added instructions for using Docker Compose with watchdog (@ianalexander,
|
||||||
|
@deisi)
|
||||||
|
- Other miscellany (@mb720, @toy, @caiofacchinato)
|
||||||
|
- Some scripts provided in the documentation have been migrated out so that
|
||||||
|
they can be copied out as whole files, and to ensure syntax checking
|
||||||
|
is maintained.
|
||||||
|
|
||||||
|
- Fixed an error that caused bash completions to fail on macOS. (#502, #504;
|
||||||
|
@AlexanderWillner)
|
||||||
|
- Fixed a rare case where OCRmyPDF threw an exception while processing a PDF
|
||||||
|
with the wrong object type in its ``/Trailer /Info``. The error is now logged
|
||||||
|
and incorrect object is ignored. (#497)
|
||||||
|
- Removed potentially non-free file ``enron1.pdf`` and simplified the test that
|
||||||
|
used it.
|
||||||
|
- Removed potentially non-free file ``misc/media/logo.afdesign``.
|
||||||
|
|
||||||
v9.6.0
|
v9.6.0
|
||||||
======
|
======
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Original version by DeliciousPickle@github; modified
|
||||||
|
|
||||||
|
# This script must be edited to meet your needs.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import ocrmypdf
|
||||||
|
|
||||||
|
# pylint: disable=logging-format-interpolation
|
||||||
|
# pylint: disable=logging-not-lazy
|
||||||
|
|
||||||
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
print(script_dir + '/batch.py: Start')
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
start_dir = sys.argv[1]
|
||||||
|
else:
|
||||||
|
start_dir = '.'
|
||||||
|
|
||||||
|
if len(sys.argv) > 2:
|
||||||
|
log_file = sys.argv[2]
|
||||||
|
else:
|
||||||
|
log_file = script_dir + '/ocr-tree.log'
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s %(message)s',
|
||||||
|
filename=log_file,
|
||||||
|
filemode='w',
|
||||||
|
)
|
||||||
|
|
||||||
|
ocrmypdf.configure_logging(ocrmypdf.Verbosity.default)
|
||||||
|
|
||||||
|
for dir_name, subdirs, file_list in os.walk(start_dir):
|
||||||
|
logging.info(dir_name + '\n')
|
||||||
|
os.chdir(dir_name)
|
||||||
|
for filename in file_list:
|
||||||
|
file_ext = os.path.splitext(filename)[1]
|
||||||
|
if file_ext == '.pdf':
|
||||||
|
full_path = dir_name + '/' + filename
|
||||||
|
print(full_path)
|
||||||
|
result = ocrmypdf.ocr(filename, filename, deskew=True)
|
||||||
|
if result == ocrmypdf.ExitCode.already_done_ocr:
|
||||||
|
print("Skipped document because it already contained text")
|
||||||
|
elif result == ocrmypdf.ExitCode.ok:
|
||||||
|
print("OCR complete")
|
||||||
|
logging.info(result)
|
||||||
@@ -5,7 +5,33 @@ set -o errexit
|
|||||||
_ocrmypdf()
|
_ocrmypdf()
|
||||||
{
|
{
|
||||||
local cur prev cword words split
|
local cur prev cword words split
|
||||||
_init_completion -s || return
|
|
||||||
|
# Homebrew on Macs have version 1.3 of bash-completion which doesn't include - see #502
|
||||||
|
if declare -F _init_completions >/dev/null 2>&1; then
|
||||||
|
_init_completion -s || return
|
||||||
|
else
|
||||||
|
COMPREPLY=()
|
||||||
|
_get_comp_words_by_ref cur prev words cword
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $cur == -* ]]; then
|
||||||
|
COMPREPLY=( $( compgen -W '--language --image-dpi --output-type
|
||||||
|
--sidecar --version --jobs --quiet --verbose --title --author
|
||||||
|
--subject --keywords --rotate-pages --remove-background --deskew
|
||||||
|
--clean --clean-final --unpaper-args --oversample --remove-vectors
|
||||||
|
--threshold --force-ocr --skip-text --redo-ocr
|
||||||
|
--skip-big --jpeg-quality --png-quality --jbig2-lossy
|
||||||
|
--max-image-mpixels --tesseract-config --tesseract-pagesegmode
|
||||||
|
--help --tesseract-oem --pdf-renderer --tesseract-timeout
|
||||||
|
--rotate-pages-threshold --pdfa-image-compression --user-words
|
||||||
|
--user-patterns --keep-temporary-files --output-type
|
||||||
|
--no-progress-bar --pages --fast-web-view' \
|
||||||
|
-- "$cur" ) )
|
||||||
|
return
|
||||||
|
else
|
||||||
|
_filedir
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
case $prev in
|
case $prev in
|
||||||
--version|-h|--help)
|
--version|-h|--help)
|
||||||
@@ -65,25 +91,6 @@ _ocrmypdf()
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
$split && return
|
$split && return
|
||||||
|
|
||||||
if [[ $cur == -* ]]; then
|
|
||||||
COMPREPLY=( $( compgen -W '--language --image-dpi --output-type
|
|
||||||
--sidecar --version --jobs --quiet --verbose --title --author
|
|
||||||
--subject --keywords --rotate-pages --remove-background --deskew
|
|
||||||
--clean --clean-final --unpaper-args --oversample --remove-vectors
|
|
||||||
--threshold --force-ocr --skip-text --redo-ocr
|
|
||||||
--skip-big --jpeg-quality --png-quality --jbig2-lossy
|
|
||||||
--max-image-mpixels --tesseract-config --tesseract-pagesegmode
|
|
||||||
--help --tesseract-oem --pdf-renderer --tesseract-timeout
|
|
||||||
--rotate-pages-threshold --pdfa-image-compression --user-words
|
|
||||||
--user-patterns --keep-temporary-files --output-type
|
|
||||||
--no-progress-bar --pages --fast-web-view' \
|
|
||||||
-- "$cur" ) )
|
|
||||||
return
|
|
||||||
else
|
|
||||||
_filedir
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
} &&
|
} &&
|
||||||
complete -F _ocrmypdf ocrmypdf
|
complete -F _ocrmypdf ocrmypdf
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
version: "3.3"
|
||||||
|
services:
|
||||||
|
ocrmypdf:
|
||||||
|
restart: always
|
||||||
|
container_name: ocrmypdf
|
||||||
|
image: jbarlow83/ocrmypdf
|
||||||
|
volumes:
|
||||||
|
- "/media/scan:/input"
|
||||||
|
- "/mnt/scan:/output"
|
||||||
|
environment:
|
||||||
|
- OCR_OUTPUT_DIRECTORY_YEAR_MONT=0
|
||||||
|
user: "<SET TO YOUR USER ID>:<SET TO YOUR GROUP ID>"
|
||||||
|
entrypoint: python3
|
||||||
|
command: watcher.py
|
||||||
Binary file not shown.
@@ -0,0 +1,72 @@
|
|||||||
|
#!/bin/env python3
|
||||||
|
# Contributed by github.com/Enantiomerie
|
||||||
|
|
||||||
|
# This script must be edited to meet your needs.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
# pylint: disable=logging-format-interpolation
|
||||||
|
# pylint: disable=logging-not-lazy
|
||||||
|
|
||||||
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
timestamp = time.strftime("%Y-%m-%d-%H%M_")
|
||||||
|
log_file = script_dir + '/' + timestamp + 'ocrmypdf.log'
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s %(message)s',
|
||||||
|
filename=log_file,
|
||||||
|
filemode='w',
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
start_dir = sys.argv[1]
|
||||||
|
else:
|
||||||
|
start_dir = '.'
|
||||||
|
|
||||||
|
for dir_name, subdirs, file_list in os.walk(start_dir):
|
||||||
|
logging.info(dir_name)
|
||||||
|
os.chdir(dir_name)
|
||||||
|
for filename in file_list:
|
||||||
|
file_stem, file_ext = os.path.splitext(filename)
|
||||||
|
if file_ext != '.pdf':
|
||||||
|
continue
|
||||||
|
full_path = os.path.join(dir_name, filename)
|
||||||
|
timestamp_ocr = time.strftime("%Y-%m-%d-%H%M_OCR_")
|
||||||
|
filename_ocr = timestamp_ocr + file_stem + '.pdf'
|
||||||
|
# create string for pdf processing
|
||||||
|
# the script is processed as root user via chron
|
||||||
|
cmd = [
|
||||||
|
'docker',
|
||||||
|
'run',
|
||||||
|
'--rm',
|
||||||
|
'-i',
|
||||||
|
'jbarlow83/ocrmypdf',
|
||||||
|
'--deskew',
|
||||||
|
'-',
|
||||||
|
'-',
|
||||||
|
]
|
||||||
|
logging.info(cmd)
|
||||||
|
full_path_ocr = os.path.join(dir_name, filename_ocr)
|
||||||
|
with open(filename, 'rb') as input_file, open(
|
||||||
|
full_path_ocr, 'wb'
|
||||||
|
) as output_file:
|
||||||
|
proc = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
stdin=input_file,
|
||||||
|
stdout=output_file,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
logging.info(proc.stderr.read())
|
||||||
|
os.chmod(full_path_ocr, 0o664)
|
||||||
|
os.chmod(full_path, 0o664)
|
||||||
|
full_path_ocr_archive = sys.argv[2]
|
||||||
|
full_path_archive = sys.argv[2] + '/no_ocr'
|
||||||
|
shutil.move(full_path_ocr, full_path_ocr_archive)
|
||||||
|
shutil.move(full_path, full_path_archive)
|
||||||
|
logging.info('Finished.\n')
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
# Copyright (C) 2019 Ian Alexander: https://github.com/ianalexander
|
# Copyright (C) 2019 Ian Alexander: https://github.com/ianalexander
|
||||||
|
# Copyright (C) 2020 James R Barlow: https://github.com/jbarlow83
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -694,11 +694,18 @@ def convert_to_pdfa(input_pdf, input_ps_stub, context):
|
|||||||
# stamping them out as soon as possible.
|
# stamping them out as soon as possible.
|
||||||
modified = False
|
modified = False
|
||||||
with pikepdf.open(input_pdf) as pdf_file:
|
with pikepdf.open(input_pdf) as pdf_file:
|
||||||
if pdf_file.docinfo:
|
try:
|
||||||
for k, v in pdf_file.docinfo.items():
|
len(pdf_file.docinfo)
|
||||||
if b'\x00' in bytes(v):
|
except TypeError:
|
||||||
pdf_file.docinfo[k] = bytes(v).replace(b'\x00', b'')
|
context.log.error(
|
||||||
modified = True
|
"File contains a malformed DocumentInfo block - continuing anyway"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if pdf_file.docinfo:
|
||||||
|
for k, v in pdf_file.docinfo.items():
|
||||||
|
if b'\x00' in bytes(v):
|
||||||
|
pdf_file.docinfo[k] = bytes(v).replace(b'\x00', b'')
|
||||||
|
modified = True
|
||||||
if modified:
|
if modified:
|
||||||
pdf_file.save(fix_docinfo_file)
|
pdf_file.save(fix_docinfo_file)
|
||||||
else:
|
else:
|
||||||
|
|||||||
Binary file not shown.
+38
-8
@@ -31,8 +31,11 @@ import pytest
|
|||||||
from pikepdf.models.metadata import decode_pdf_date
|
from pikepdf.models.metadata import decode_pdf_date
|
||||||
|
|
||||||
from ocrmypdf._jobcontext import PDFContext
|
from ocrmypdf._jobcontext import PDFContext
|
||||||
|
from ocrmypdf._pipeline import convert_to_pdfa
|
||||||
|
from ocrmypdf.cli import parser
|
||||||
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 ocrmypdf.pdfinfo import PdfInfo
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import fitz
|
import fitz
|
||||||
@@ -313,19 +316,21 @@ def test_metadata_fixup_warning(resources, outdir, caplog):
|
|||||||
|
|
||||||
|
|
||||||
def test_prevent_gs_invalid_xml(resources, outdir):
|
def test_prevent_gs_invalid_xml(resources, outdir):
|
||||||
from ocrmypdf.__main__ import parser
|
|
||||||
from ocrmypdf._pipeline import convert_to_pdfa
|
|
||||||
from ocrmypdf.pdfa import generate_pdfa_ps
|
|
||||||
from ocrmypdf.pdfinfo import PdfInfo
|
|
||||||
|
|
||||||
generate_pdfa_ps(outdir / 'pdfa.ps')
|
generate_pdfa_ps(outdir / 'pdfa.ps')
|
||||||
copyfile(resources / 'enron1.pdf', outdir / 'layers.rendered.pdf')
|
copyfile(resources / 'trivial.pdf', outdir / 'layers.rendered.pdf')
|
||||||
|
|
||||||
|
# Inject a string with a trailing nul character into the DocumentInfo
|
||||||
|
# dictionary of this PDF, as often occurs in practice.
|
||||||
|
with pikepdf.open(outdir / 'layers.rendered.pdf') as pike:
|
||||||
|
pike.Root.DocumentInfo = pikepdf.Dictionary(
|
||||||
|
Title=b'String with trailing nul\x00'
|
||||||
|
)
|
||||||
|
|
||||||
options = parser.parse_args(
|
options = parser.parse_args(
|
||||||
args=['-j', '1', '--output-type', 'pdfa-2', 'a.pdf', 'b.pdf']
|
args=['-j', '1', '--output-type', 'pdfa-2', 'a.pdf', 'b.pdf']
|
||||||
)
|
)
|
||||||
pdfinfo = PdfInfo(resources / 'enron1.pdf')
|
pdfinfo = PdfInfo(outdir / 'layers.rendered.pdf')
|
||||||
context = PDFContext(options, outdir, resources / 'enron1.pdf', pdfinfo)
|
context = PDFContext(options, outdir, outdir / 'layers.rendered.pdf', pdfinfo)
|
||||||
|
|
||||||
convert_to_pdfa(
|
convert_to_pdfa(
|
||||||
str(outdir / 'layers.rendered.pdf'), str(outdir / 'pdfa.ps'), context
|
str(outdir / 'layers.rendered.pdf'), str(outdir / 'pdfa.ps'), context
|
||||||
@@ -339,5 +344,30 @@ def test_prevent_gs_invalid_xml(resources, outdir):
|
|||||||
xmp_start = mm.find(XMP_MAGIC)
|
xmp_start = mm.find(XMP_MAGIC)
|
||||||
xmp_end = mm.rfind(b'<?xpacket end', xmp_start)
|
xmp_end = mm.rfind(b'<?xpacket end', xmp_start)
|
||||||
assert 0 < xmp_start < xmp_end
|
assert 0 < xmp_start < xmp_end
|
||||||
|
# Ensure we did not carry the nul forward.
|
||||||
assert mm.find(b'�', xmp_start, xmp_end) == -1, "found escaped nul"
|
assert mm.find(b'�', xmp_start, xmp_end) == -1, "found escaped nul"
|
||||||
assert mm.find(b'\x00', xmp_start, xmp_end) == -1
|
assert mm.find(b'\x00', xmp_start, xmp_end) == -1
|
||||||
|
|
||||||
|
|
||||||
|
def test_malformed_docinfo(caplog, resources, outdir):
|
||||||
|
generate_pdfa_ps(outdir / 'pdfa.ps')
|
||||||
|
# copyfile(resources / 'trivial.pdf', outdir / 'layers.rendered.pdf')
|
||||||
|
|
||||||
|
with pikepdf.open(resources / 'trivial.pdf') as pike:
|
||||||
|
pike.trailer.Info = pikepdf.Stream(pike, b"<xml></xml>")
|
||||||
|
pike.save(outdir / 'layers.rendered.pdf', fix_metadata_version=False)
|
||||||
|
|
||||||
|
options = parser.parse_args(
|
||||||
|
args=['-j', '1', '--output-type', 'pdfa-2', 'a.pdf', 'b.pdf']
|
||||||
|
)
|
||||||
|
pdfinfo = PdfInfo(outdir / 'layers.rendered.pdf')
|
||||||
|
context = PDFContext(options, outdir, outdir / 'layers.rendered.pdf', pdfinfo)
|
||||||
|
|
||||||
|
convert_to_pdfa(
|
||||||
|
str(outdir / 'layers.rendered.pdf'), str(outdir / 'pdfa.ps'), context
|
||||||
|
)
|
||||||
|
|
||||||
|
print(caplog.records)
|
||||||
|
assert any(
|
||||||
|
'malformed DocumentInfo block' in record.message for record in caplog.records
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user