Compare commits
84
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
072db75fa3 | ||
|
|
8519b3f625 | ||
|
|
dd7c4f3eaa | ||
|
|
c8e6f20f8d | ||
|
|
10530a8698 | ||
|
|
207866abf5 | ||
|
|
3829af16fb | ||
|
|
24db31b4c5 | ||
|
|
8132a4ae10 | ||
|
|
d5128c5cf5 | ||
|
|
270e31fa67 | ||
|
|
85e31d0a19 | ||
|
|
ea36aedb5f | ||
|
|
bd4d44e182 | ||
|
|
8fcf358934 | ||
|
|
47b0f28564 | ||
|
|
7018e2b247 | ||
|
|
8d12ecb798 | ||
|
|
0ab29ec0ba | ||
|
|
179714770a | ||
|
|
f04f45545c | ||
|
|
a3a083c125 | ||
|
|
d855f63985 | ||
|
|
d4863cbf0f | ||
|
|
7b8f081fbf | ||
|
|
7d33039bcd | ||
|
|
fde886baf4 | ||
|
|
146da79c00 | ||
|
|
2fc3b0d973 | ||
|
|
5667424530 | ||
|
|
8add531ffd | ||
|
|
0388c23ae7 | ||
|
|
9b77daae7c | ||
|
|
3e1b3ec98d | ||
|
|
0f0ca6f517 | ||
|
|
c93349c350 | ||
|
|
0c287929c2 | ||
|
|
23a37fc35c | ||
|
|
162a47f98e | ||
|
|
0239f69912 | ||
|
|
2ad8961d0b | ||
|
|
eec8a2b574 | ||
|
|
6c78076bea | ||
|
|
2637e84691 | ||
|
|
e8c82ee4b6 | ||
|
|
de2bb5ce8c | ||
|
|
ec1c377532 | ||
|
|
173428e81a | ||
|
|
67ed29dcea | ||
|
|
3454c050ed | ||
|
|
5ee99b26e7 | ||
|
|
ac3aa67d8a | ||
|
|
1768a1eda9 | ||
|
|
5902fe45c1 | ||
|
|
78981641f0 | ||
|
|
c77ae4b34c | ||
|
|
b2cbbf0099 | ||
|
|
6b6c34af01 | ||
|
|
be12f7a728 | ||
|
|
e3c813fc67 | ||
|
|
35a1eaf62a | ||
|
|
d393d18c13 | ||
|
|
54e622ad10 | ||
|
|
330352aeed | ||
|
|
ac2fc49208 | ||
|
|
4bee7355e9 | ||
|
|
86f2b1f9a7 | ||
|
|
3002409e49 | ||
|
|
0cf6828c20 | ||
|
|
331c829b6e | ||
|
|
06a5e0c3f6 | ||
|
|
811f23381a | ||
|
|
a371655052 | ||
|
|
a6ce35b13a | ||
|
|
45added738 | ||
|
|
6e20439c91 | ||
|
|
72e056436c | ||
|
|
e02ba19097 | ||
|
|
d3b858f994 | ||
|
|
19045c4f21 | ||
|
|
f4d89fe6cc | ||
|
|
ab85c0f5a9 | ||
|
|
32693b683d | ||
|
|
b5dc276ba1 |
+44
-31
@@ -22,23 +22,15 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
python: "3.8"
|
||||
- os: ubuntu-20.04
|
||||
- os: ubuntu-22.04
|
||||
python: "3.9"
|
||||
- os: ubuntu-20.04
|
||||
- os: ubuntu-22.04
|
||||
python: "3.10"
|
||||
- os: ubuntu-latest
|
||||
python: "3.9"
|
||||
- os: ubuntu-latest
|
||||
python: "3.10"
|
||||
- os: ubuntu-latest
|
||||
- os: ubuntu-22.04
|
||||
python: "3.11"
|
||||
# - os: ubuntu-latest
|
||||
# python: "pypy3.8"
|
||||
#- os: ubuntu-latest
|
||||
# python: "pypy3.9"
|
||||
- os: ubuntu-latest
|
||||
- os: ubuntu-22.04
|
||||
python: "3.9"
|
||||
tesseract5: true
|
||||
|
||||
@@ -47,7 +39,7 @@ jobs:
|
||||
PYTHON: ${{ matrix.python }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: "0" # 0=all, needed for setuptools-scm to resolve version tags
|
||||
|
||||
@@ -68,6 +60,7 @@ jobs:
|
||||
curl \
|
||||
ghostscript \
|
||||
img2pdf \
|
||||
libexempi8 \
|
||||
libffi-dev \
|
||||
libsm6 libxext6 libxrender-dev \
|
||||
pngquant \
|
||||
@@ -79,12 +72,6 @@ jobs:
|
||||
unpaper \
|
||||
zlib1g
|
||||
|
||||
- name: Install Ubuntu 20.04 packages
|
||||
if: matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
libexempi8
|
||||
|
||||
- name: Install Ubuntu packages for PyPy
|
||||
if: startsWith(matrix.python, 'pypy')
|
||||
run: |
|
||||
@@ -129,7 +116,7 @@ jobs:
|
||||
PYTHON: ${{ matrix.python }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: "0" # 0=all, needed for setuptools-scm to resolve version tags
|
||||
|
||||
@@ -185,7 +172,7 @@ jobs:
|
||||
PYTHON: ${{ matrix.python }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: "0" # 0=all, needed for setuptools-scm to resolve version tags
|
||||
|
||||
@@ -218,14 +205,14 @@ jobs:
|
||||
name: Build sdist and wheels
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: "0" # 0=all, needed for setuptools-scm to resolve version tags
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: "3.7"
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Make wheels and sdist
|
||||
run: |
|
||||
@@ -242,6 +229,9 @@ jobs:
|
||||
name: Deploy artifacts to PyPI
|
||||
needs: [wheel_sdist_linux, test_linux, test_macos, test_windows]
|
||||
runs-on: ubuntu-latest
|
||||
environment: release
|
||||
permissions:
|
||||
id-token: write # mandatory for PyPI publishing
|
||||
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
@@ -249,11 +239,34 @@ jobs:
|
||||
name: artifact
|
||||
path: dist
|
||||
|
||||
- uses: pypa/gh-action-pypi-publish@release/v1
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
|
||||
create_release:
|
||||
name: Create GitHub release
|
||||
needs: [wheel_sdist_linux, test_linux, test_macos, test_windows]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
|
||||
permissions:
|
||||
# Required to create a release
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.TOKEN_PYPI }}
|
||||
# repository_url: https://test.pypi.org/legacy/
|
||||
name: artifact
|
||||
path: dist
|
||||
|
||||
- name: Create Release
|
||||
id: create-release
|
||||
uses: shogo82148/actions-create-release@v1
|
||||
|
||||
- name: Upload Assets
|
||||
uses: shogo82148/actions-upload-release-asset@v1
|
||||
with:
|
||||
upload_url: ${{ steps.create-release.outputs.upload_url }}
|
||||
asset_path: |
|
||||
./dist/*.whl
|
||||
./dist/*.tar.gz
|
||||
|
||||
docker:
|
||||
name: Build Docker images
|
||||
@@ -274,22 +287,22 @@ jobs:
|
||||
- name: Set image name
|
||||
run: echo "DOCKER_IMAGE_NAME=ocrmypdf" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: "0" # 0=all, needed for setuptools-scm to resolve version tags
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: jbarlow83
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Print image tag
|
||||
run: echo "Building image ${DOCKER_REPOSITORY}/${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG}"
|
||||
|
||||
@@ -27,6 +27,7 @@ venv*/
|
||||
*.traineddata
|
||||
/private
|
||||
/coverage.xml
|
||||
/issuepdf
|
||||
|
||||
# Package building
|
||||
*.egg-info/
|
||||
|
||||
+5
-1
@@ -16,8 +16,12 @@ formats:
|
||||
- pdf
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.9"
|
||||
|
||||
python:
|
||||
version: "3.8"
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
|
||||
@@ -70,7 +70,7 @@ Linux, Windows, macOS and FreeBSD are supported. Docker images are also availabl
|
||||
| Windows Subsystem for Linux | ``apt install ocrmypdf`` |
|
||||
| Fedora | ``dnf install ocrmypdf`` |
|
||||
| macOS (Homebrew) | ``brew install ocrmypdf`` |
|
||||
| macOS (nix) | ``nix-env -i ocrmypdf`` |
|
||||
| macOS (nix) | ``nix-env -i ocrmypdf`` |
|
||||
| LinuxBrew | ``brew install ocrmypdf`` |
|
||||
| FreeBSD | ``pkg install py-ocrmypdf`` |
|
||||
| Conda | ``conda install ocrmypdf`` |
|
||||
|
||||
+48
-9
@@ -47,8 +47,8 @@ and clean up the margins of both.
|
||||
|
||||
Some ``unpaper`` features cause multiple input or output files to be
|
||||
consumed or produced. OCRmyPDF requires ``unpaper`` to consume one
|
||||
file and produce one file. An deviation from that condition will
|
||||
result in errors.
|
||||
file and produce one file; errors will result if this assumption is not
|
||||
met.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -82,14 +82,17 @@ is stripped out. Then an image of each page is created with visible text
|
||||
masked out. The page image is sent for OCR, and any additional text is
|
||||
inserted as OCR. If a file contains a mix of text and bitmap images that
|
||||
contain text, OCRmyPDF will locate the additional text in images without
|
||||
disrupting the existing text.
|
||||
disrupting the existing text. Some PDF OCR solutions render text as
|
||||
technically printable or visible in some way, perhaps by drawing it and
|
||||
then painting over it. OCRmyPDF cannot distinguish this type of OCR
|
||||
text from real text, so it will not be "redone".
|
||||
|
||||
If ``--force-ocr`` is issued, then all pages will be rasterized to
|
||||
images, discarding any hidden OCR text, and rasterizing any printable
|
||||
text. This is useful for redoing OCR, for fixing OCR text with a damaged
|
||||
character map (text is selectable but not searchable), and destroying
|
||||
redacted information. Any forms and vector graphics will be rasterized
|
||||
as well.
|
||||
images, discarding any hidden OCR text, rasterizing any printable
|
||||
text, and flattening form fields or interactive objects into their visual
|
||||
representation. This is useful for redoing OCR, for fixing OCR text
|
||||
with a damaged character map (text is selectable but not searchable),
|
||||
and destroying redacted information.
|
||||
|
||||
Time and image size limits
|
||||
--------------------------
|
||||
@@ -111,6 +114,41 @@ exceed a certain number of megapixels with ``--skip-big``. (A 300 DPI,
|
||||
# Allow 300 seconds for OCR; skip any page larger than 50 megapixels
|
||||
ocrmypdf --tesseract-timeout 300 --skip-big 50 bigfile.pdf output.pdf
|
||||
|
||||
OCR for huge images
|
||||
-------------------
|
||||
|
||||
Separate from these settings, Tesseract has internal limits on the size
|
||||
of images it will process. If you issue
|
||||
``--tesseract-downsample-large-images``, OCRmyPDF will downsample images
|
||||
to fit Tesseract limits. (The limits are usually entered only for scanned
|
||||
images of oversized media, such as large maps or blueprints exceeding
|
||||
110 cm or 43 inches in either dimension, and at high DPI.)
|
||||
|
||||
``--tesseract-downsample-above`` adjusts the threshold at which images
|
||||
will be downsampled. By default, only images that exceed any of Tesseract's
|
||||
internal limits are downsampled.
|
||||
|
||||
You will also need to set ``--tesseract-timeout`` high enough to allow
|
||||
for processing.
|
||||
|
||||
Only the image sent for OCR is downsampled. The original image is
|
||||
preserved.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Allow 600 seconds for OCR on huge images
|
||||
ocrmypdf --tesseract-timeout 600 \
|
||||
--tesseract-downsample-large-images \
|
||||
bigfile.pdf output.pdf
|
||||
|
||||
# Downsample images above 5000 pixels on the longest dimension to
|
||||
# 5000 pixels
|
||||
ocrmypdf --tesseract-timeout 120 \
|
||||
--tesseract-downsample-large-images \
|
||||
--tesseract-downsample-above 5000 \
|
||||
bigfile.pdf output_downsampled_ocr.pdf
|
||||
|
||||
|
||||
Overriding default tesseract
|
||||
----------------------------
|
||||
|
||||
@@ -154,7 +192,8 @@ In addition to tesseract, OCRmyPDF uses the following external binaries:
|
||||
- ``jbig2``
|
||||
|
||||
In each case OCRmyPDF will search the ``PATH`` environment variable to
|
||||
locate the binaries.
|
||||
locate the binaries. By modifying the ``PATH`` environment variable, you
|
||||
can override the binaries that OCRmyPDF uses.
|
||||
|
||||
Changing tesseract configuration variables
|
||||
------------------------------------------
|
||||
|
||||
+12
-13
@@ -46,16 +46,6 @@ place, and printing each filename in between runs:
|
||||
|
||||
find . -printf '%p\n' -name '*.pdf' -exec ocrmypdf '{}' '{}' \;
|
||||
|
||||
Alternatively, with a Docker container and streaming the file through
|
||||
standard input and output:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
find . -name '*.pdf' -print0 | xargs -0 | while read pdf; do
|
||||
pdfout=$(mktemp)
|
||||
docker run --rm -i jbarlow83/ocrmypdf - - <$pdf >$pdfout && cp $pdfout $pdf
|
||||
done
|
||||
|
||||
This only runs one ``ocrmypdf`` process at a time. This variation uses
|
||||
``find`` to create a directory list and ``parallel`` to parallelize runs
|
||||
of ``ocrmypdf``, again updating files in place.
|
||||
@@ -70,6 +60,15 @@ In a Windows batch file, use
|
||||
|
||||
for /r %%f in (*.pdf) do ocrmypdf %%f %%f
|
||||
|
||||
With a Docker container, you will need to stream through standard input and output:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
find . -name '*.pdf' -print0 | xargs -0 | while read pdf; do
|
||||
pdfout=$(mktemp)
|
||||
docker run --rm -i jbarlow83/ocrmypdf - - <$pdf >$pdfout && cp $pdfout $pdf
|
||||
done
|
||||
|
||||
Sample script
|
||||
-------------
|
||||
|
||||
@@ -88,9 +87,9 @@ package <https://www.synology.com/en-global/dsm/packages/Docker>`__ is
|
||||
installed. Attached is a script to address particular quirks of using
|
||||
OCRmyPDF on one of these devices.
|
||||
|
||||
This is only possible for x86-based Synology products. Some Synology
|
||||
products use ARM or Power processors and do not support Docker. Further
|
||||
adjustments might be needed to deal with the Synology's relatively
|
||||
At the time this script was written, it only worked for x86-based Synology
|
||||
products. It is not known if it will work on ARM-based Synology products.
|
||||
Further adjustments might be needed to deal with the Synology's relatively
|
||||
limited CPU and RAM.
|
||||
|
||||
.. literalinclude:: ../misc/synology.py
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ master_doc = 'index'
|
||||
# General information about the project.
|
||||
project = 'ocrmypdf'
|
||||
copyright = (
|
||||
'2022, James R. Barlow. Licensed under Creative Commons Attribution-ShareAlike 4.0.'
|
||||
'2023, James R. Barlow. Licensed under Creative Commons Attribution-ShareAlike 4.0.'
|
||||
)
|
||||
author = 'James R. Barlow'
|
||||
|
||||
|
||||
+25
-8
@@ -29,12 +29,17 @@ Tests
|
||||
|
||||
New features should come with tests that confirm their correctness.
|
||||
|
||||
New Python dependencies
|
||||
=======================
|
||||
New dependencies
|
||||
================
|
||||
|
||||
If you are proposing a change that will require a new Python dependency, we
|
||||
If you are proposing a change that will require a new dependency, we
|
||||
prefer dependencies that are already packaged by Debian or Red Hat. This makes
|
||||
life much easier for our downstream package maintainers.
|
||||
life much easier for our downstream package maintainers. A package that is only
|
||||
available on PyPI or GitHub, and not more widely packaged, may not be accepted.
|
||||
|
||||
We are unlikely to accept a dependency on CUDA or other GPU-based libraries,
|
||||
because these are still difficult to package and install on many systems.
|
||||
We recommend implementing these changes as plugins.
|
||||
|
||||
Python dependencies must also be license-compatible. GPLv3 or AGPLv3 are likely
|
||||
incompatible with the project's license, but LGPLv3 is compatible.
|
||||
@@ -43,7 +48,19 @@ New non-Python dependencies
|
||||
===========================
|
||||
|
||||
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,
|
||||
and if we are to add external programs, we prefer those that are already
|
||||
packaged by Debian or Red Hat.
|
||||
|
||||
Plugins
|
||||
=======
|
||||
|
||||
Some new features may be a good fit for a plugin. Plugins are a way to add
|
||||
features to OCRmyPDF without adding them to the core program. Plugins are
|
||||
installed separately from OCRmyPDF. They are written in Python and can be
|
||||
installed from PyPI. See the `plugin documentation <https://ocrmypdf.readthedocs.io/en/latest/plugins.html>`_.
|
||||
|
||||
We are happy to link users to your plugin from the documentation.
|
||||
|
||||
Style guide: Is it OCRmyPDF or ocrmypdf?
|
||||
========================================
|
||||
@@ -53,8 +70,8 @@ The program/project is OCRmyPDF and the name of the executable or library is ocr
|
||||
Copyright and license
|
||||
=====================
|
||||
|
||||
For contributions over 10 lines of code, please include your name to list of
|
||||
For contributions over 10 lines of code, please add your name to list of
|
||||
copyright holders for that file. The core program is licensed under MPL-2.0,
|
||||
test files and documentation under CC-BY-SA 4.0, and miscellaneous files under
|
||||
MIT. Please contribute code only that you wrote and you have the permission to
|
||||
contribute or license to us.
|
||||
MIT, with a few minor exceptions. Please contribute only content that you own
|
||||
or have the right to contribute under these licenses.
|
||||
|
||||
+26
-4
@@ -231,13 +231,20 @@ Don't actually OCR my PDF
|
||||
=========================
|
||||
|
||||
If you set ``--tesseract-timeout 0`` OCRmyPDF will apply its image
|
||||
processing without performing OCR, if all you want to is to apply image
|
||||
processing or PDF/A conversion.
|
||||
processing without performing OCR (by causing OCR to time out). This works
|
||||
if all you want to is to apply image processing or PDF/A conversion.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
ocrmypdf --tesseract-timeout=0 --remove-background input.pdf output.pdf
|
||||
|
||||
.. versionchanged:: v14.1.0
|
||||
|
||||
Prior to this version, ``--tesseract-timeout 0`` would prevent other
|
||||
uses of Tesseract, such as deskewing, from working. This is no longer
|
||||
the case. Use ``--tesseract-non-ocr-timeout`` to control the timeout
|
||||
for non-OCR operations, if needed.
|
||||
|
||||
Optimize images without performing OCR
|
||||
--------------------------------------
|
||||
|
||||
@@ -261,9 +268,10 @@ 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,
|
||||
overlapping 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.
|
||||
counts the number of virtual pieces of paper since the start. If your list of
|
||||
pages is out of numerical order, OCRmyPDF will sort it for you.
|
||||
|
||||
Regardless of the argument to ``--pages``, OCRmyPDF will optimize all pages/images
|
||||
in the file and convert it to PDF/A, unless you disable those options. Both of these
|
||||
@@ -372,3 +380,17 @@ Some users may consider enabling lossy JBIG2. See: :ref:`jbig2-lossy`.
|
||||
|
||||
Image processing and PDF/A conversion can also introduce lossy transformations
|
||||
to your PDF images, even when ``--optimize 1`` is in use.
|
||||
|
||||
|
||||
Digitally signed PDFs
|
||||
=====================
|
||||
|
||||
OCRmyPDF cannot preserve digital signatures in PDFs and also add to OCR to them.
|
||||
By default, it will refuse to modify a signed PDF regardless of other settings. You can
|
||||
override this behavior with ``--invalidate-digital-signatures``; as the name suggests,
|
||||
any digital signatures will be invalidated.
|
||||
|
||||
OCRmyPDF cannot open documents that are encrypted with a digital certificate.
|
||||
|
||||
Versions of OCRmyPDF prior to 14.4.0 would invalidate existing digital signatures
|
||||
without warning.
|
||||
@@ -0,0 +1,32 @@
|
||||
.. SPDX-FileCopyrightText: 2023 James R. Barlow
|
||||
.. SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
|
||||
============
|
||||
Design notes
|
||||
============
|
||||
|
||||
Why doesn't OCRmyPDF use PyTesseract?
|
||||
=====================================
|
||||
|
||||
PyTesseract is a Python wrapper around the Tesseract OCR engine. When OCRmyPDF was
|
||||
first written, PyTesseract used ABI bindings to call the Tesseract library. This
|
||||
was not a good fit for OCRmyPDF because ABI bindings can be fragile.
|
||||
|
||||
PyTesseract has since evolved calling the Tesseract executable, abandoning the ABI
|
||||
approach and using the CLI instead, just like OCRmyPDF does. If it were written from
|
||||
scratch today, OCRmyPDF might use PyTesseract.
|
||||
|
||||
PyTesseract has more features don't particularly need PDF output, but less features
|
||||
than OCRmyPDF's API for creating PDFs.
|
||||
|
||||
What is ``executor()``?
|
||||
=======================
|
||||
|
||||
OCRmyPDF uses a custom concurrent executor which can support either threads or
|
||||
processes with the same interface. This is useful because OCRmyPDF can use
|
||||
either threads or processes to parallelize work, whichever is more appropriate
|
||||
for the task at hand.
|
||||
|
||||
The interface is currently private and subject to change. In particular, if
|
||||
experiments with asyncio and anyio are successful, the interface will change.
|
||||
|
||||
+2
-1
@@ -12,7 +12,7 @@ files, allowing them to be searched.
|
||||
|
||||
PDF is the best format for storing and exchanging scanned documents.
|
||||
Unfortunately, PDFs can be difficult to modify. OCRmyPDF makes it easy to apply
|
||||
image processing and OCR to existing PDFs.
|
||||
image processing and OCR (recognized, searchable text) to existing PDFs.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
@@ -44,6 +44,7 @@ image processing and OCR to existing PDFs.
|
||||
api
|
||||
plugins
|
||||
apiref
|
||||
design_notes
|
||||
contributing
|
||||
maintainers
|
||||
|
||||
|
||||
+67
-74
@@ -72,8 +72,7 @@ Debian and Ubuntu 20.04 or newer
|
||||
| |ubu-2004| |ubu-2204| |
|
||||
+-----------------------------------------------+
|
||||
|
||||
Users of Debian 9 ("stretch") or later, or Ubuntu 18.04 or later, including users
|
||||
of Windows Subsystem for Linux, may simply
|
||||
Users of Debian or Ubuntu may simply
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -100,11 +99,11 @@ For full details on version availability for your platform, check the
|
||||
Fedora
|
||||
------
|
||||
|
||||
.. |fedora-35| image:: https://repology.org/badge/version-for-repo/fedora_35/ocrmypdf.svg
|
||||
:alt: Fedora 35
|
||||
.. |fedora-37| image:: https://repology.org/badge/version-for-repo/fedora_37/ocrmypdf.svg
|
||||
:alt: Fedora 37
|
||||
|
||||
.. |fedora-36| image:: https://repology.org/badge/version-for-repo/fedora_36/ocrmypdf.svg
|
||||
:alt: Fedora 36
|
||||
.. |fedora-38| image:: https://repology.org/badge/version-for-repo/fedora_38/ocrmypdf.svg
|
||||
:alt: Fedora 38
|
||||
|
||||
.. |fedora-rawhide| image:: https://repology.org/badge/version-for-repo/fedora_rawhide/ocrmypdf.svg
|
||||
:alt: Fedore Rawhide
|
||||
@@ -114,10 +113,10 @@ Fedora
|
||||
+-----------------------------------------------+
|
||||
| |latest| |
|
||||
+-----------------------------------------------+
|
||||
| |fedora-35| |fedora-36| |fedora-rawhide| |
|
||||
| |fedora-37| |fedora-38| |fedora-rawhide| |
|
||||
+-----------------------------------------------+
|
||||
|
||||
Users of Fedora 29 or later may simply
|
||||
Users of Fedora may simply
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -156,7 +155,7 @@ user, follow these steps:
|
||||
|
||||
If you get the message ``WARNING: The script ocrmypdf is installed in
|
||||
'/home/$USER/.local/bin' which is not on PATH.``, you may need to re-login
|
||||
or open a new shell, or manually add this to your user's PATH.
|
||||
or open a new shell, or manually adjust your PATH.
|
||||
|
||||
To add JBIG2 encoding, see :ref:`jbig2`.
|
||||
|
||||
@@ -275,6 +274,21 @@ To install OCRmyPDF for Alpine Linux:
|
||||
|
||||
apk add ocrmypdf
|
||||
|
||||
Gentoo Linux
|
||||
------------
|
||||
|
||||
.. image:: https://repology.org/badge/version-for-repo/gentoo_ovl_guru/ocrmypdf.svg
|
||||
:alt: Gentoo Linux
|
||||
:target: https://repology.org/metapackage/ocrmypdf
|
||||
|
||||
To install OCRmyPDF on Gentoo Linux, use the following commands:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
eselect repository enable guru
|
||||
emaint sync --repo guru
|
||||
emerge --ask app-text/OCRmyPDF
|
||||
|
||||
Other Linux packages
|
||||
--------------------
|
||||
|
||||
@@ -344,18 +358,12 @@ Update the homebrew pip:
|
||||
|
||||
pip install --upgrade pip
|
||||
|
||||
You can then install OCRmyPDF from PyPI, for the current user:
|
||||
You can then install OCRmyPDF from PyPI for the current user:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install --user ocrmypdf
|
||||
|
||||
or system-wide:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install ocrmypdf
|
||||
|
||||
The command line program should now be available:
|
||||
|
||||
.. code-block:: bash
|
||||
@@ -374,29 +382,33 @@ Native Windows
|
||||
|
||||
You must install the following for Windows:
|
||||
|
||||
* Python 3.8 (64-bit) or later
|
||||
* Tesseract 4.1.1 (64-bit) or later
|
||||
* Ghostscript 9.50 (64-bit) or later
|
||||
* Python 64-bit
|
||||
* Tesseract 64-bit
|
||||
* Ghostscript 64-bit
|
||||
|
||||
Using the `Chocolatey <https://chocolatey.org/>`_ package manager, install the
|
||||
following when running in an Administrator command prompt:
|
||||
Using the `winget <https://docs.microsoft.com/en-us/windows/package-manager/winget/>`_
|
||||
package manager:
|
||||
|
||||
* ``winget install -e --id Python.Python.3.11``
|
||||
* ``winget install -e --id UB-Mannheim.TesseractOCR``
|
||||
* ``winget install -e --id ArtifexSoftware.GhostScript``
|
||||
|
||||
|
||||
(Or alternately, using the `Chocolatey <https://chocolatey.org/>`_ package manager, install
|
||||
the following when running in an Administrator command prompt):
|
||||
|
||||
* ``choco install python3``
|
||||
* ``choco install --pre tesseract``
|
||||
* ``choco install ghostscript``
|
||||
* ``choco install pngquant`` (optional)
|
||||
|
||||
The commands above will install Python 3.x (latest version), Tesseract, Ghostscript
|
||||
and pngquant. Chocolatey may also need to install the Windows Visual C++ Runtime
|
||||
DLLs or other Windows patches, and may require a reboot.
|
||||
Either set of commands will install the required software. At the mmoment there is no
|
||||
single command to install Windows.
|
||||
|
||||
You may then use ``pip`` to install ocrmypdf. (This can performed by a user or
|
||||
Administrator.):
|
||||
|
||||
* ``pip install ocrmypdf``
|
||||
|
||||
Chocolatey automatically selects appropriate versions of these applications. Please make sure
|
||||
you are installing the 64-bit versions.
|
||||
* ``python3 -m pip install ocrmypdf``
|
||||
|
||||
OCRmyPDF will check the Windows Registry and standard locations in your Program Files
|
||||
for third party software it needs (specifically, Tesseract and Ghostscript). To
|
||||
@@ -440,7 +452,7 @@ Cygwin64
|
||||
|
||||
First install the the following prerequisite Cygwin packages using ``setup-x86_64.exe``::
|
||||
|
||||
python38 (or later)
|
||||
python39 (or later)
|
||||
python3?-devel
|
||||
python3?-pip
|
||||
python3?-lxml
|
||||
@@ -449,7 +461,7 @@ First install the the following prerequisite Cygwin packages using ``setup-x86_6
|
||||
(where 3? means match the version of python3 you installed)
|
||||
|
||||
gcc-g++
|
||||
ghostscript (<=9.50 or >=9.52-2 see note below)
|
||||
ghostscript
|
||||
libexempi3
|
||||
libexempi-devel
|
||||
libffi6
|
||||
@@ -460,13 +472,6 @@ First install the the following prerequisite Cygwin packages using ``setup-x86_6
|
||||
tesseract-ocr
|
||||
tesseract-ocr-devel
|
||||
|
||||
.. note::
|
||||
|
||||
The Cygwin package for Ghostscript in versions 9.52 and
|
||||
9.52-1 contained a bug that caused an exception to occur when
|
||||
ocrmypdf invoked gs. Make sure you have either 9.50 (or earlier)
|
||||
or 9.52-2 (or later).
|
||||
|
||||
Then open a Cygwin terminal (i.e. ``mintty``), run the following commands. Note
|
||||
that if you are using the version of ``pip`` that was installed with the Cygwin
|
||||
Python package, the command name will be ``pip3``. If you have since updated
|
||||
@@ -518,21 +523,6 @@ the latest version. However, PyPI and ``pip`` cannot address the fact
|
||||
that ``ocrmypdf`` depends on certain non-Python system libraries and
|
||||
programs being installed.
|
||||
|
||||
.. warning::
|
||||
|
||||
Debian and Ubuntu users: unfortunately, Debian and Ubuntu customize
|
||||
Python in non-standard ways, and the nature of these customizations
|
||||
varies from release to release. This can make for a frustrating
|
||||
user experience. The instructions below work on almost all platforms that
|
||||
have Python installed, except for Debian and Ubuntu, where you may need
|
||||
to take additional steps. For best results on Debian and Ubuntu, use the
|
||||
``apt`` packages; or if these are too old, run
|
||||
``apt install python3-pip python3-venv``, create a virtual environment,
|
||||
and install OCRmyPDF in that environment.
|
||||
|
||||
`See here for more information on Debian-Python issues
|
||||
<https://gist.github.com/tiran/2dec9e03c6f901814f6d1e8dad09528e>`__.
|
||||
|
||||
For best results, first install `your platform's
|
||||
version <https://repology.org/metapackage/ocrmypdf/versions>`__ of
|
||||
``ocrmypdf``, using the instructions elsewhere in this document. Then
|
||||
@@ -551,21 +541,6 @@ try:
|
||||
You should then be able to run ``ocrmypdf --version`` and see that the
|
||||
latest version was located.
|
||||
|
||||
Since ``pip install --user`` does not work correctly on some platforms,
|
||||
notably Ubuntu 16.04 and older, and the Homebrew version of Python,
|
||||
instead use this for a system wide installation:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install ocrmypdf
|
||||
|
||||
.. note::
|
||||
|
||||
AArch64 (ARM64) users: this process will be difficult because most
|
||||
Python packages are not available as binary wheels for your platform.
|
||||
You're probably better off using a platform install on Debian, Ubuntu,
|
||||
or Fedora.
|
||||
|
||||
Requirements for pip and HEAD install
|
||||
-------------------------------------
|
||||
|
||||
@@ -575,13 +550,16 @@ manager. ``pip`` cannot provide them.
|
||||
|
||||
The following versions are required:
|
||||
|
||||
- Python 3.8 or newer
|
||||
- Ghostscript 9.50 or newer
|
||||
- Python 3.9 or newer
|
||||
- Ghostscript 9.55 or newer
|
||||
- Tesseract 4.1.1 or newer
|
||||
- jbig2enc 0.29 or newer
|
||||
- pngquant 2.5 or newer
|
||||
- unpaper 6.1
|
||||
|
||||
We recommend 64-bit versions of all software. (32-bit versions are not
|
||||
supported, although they may still work.)
|
||||
|
||||
jbig2enc, pngquant, and unpaper are optional. If missing certain
|
||||
features are disabled. OCRmyPDF will discover them as soon as they are
|
||||
available.
|
||||
@@ -608,7 +586,7 @@ unfortunately, the ``pip install`` command cannot satisfy all of them.
|
||||
Installing HEAD revision from sources
|
||||
=====================================
|
||||
|
||||
If you have ``git`` and Python 3.8 or newer installed, you can install
|
||||
If you have ``git`` and Python 3.9 or newer installed, you can install
|
||||
from source. When the ``pip`` installer runs, it will alert you if
|
||||
dependencies are missing.
|
||||
|
||||
@@ -638,8 +616,8 @@ system-wide:
|
||||
.. code-block:: bash
|
||||
|
||||
git clone -b main https://github.com/ocrmypdf/OCRmyPDF.git
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
cd OCRmyPDF
|
||||
pip install .
|
||||
|
||||
@@ -665,8 +643,8 @@ To install all of the development and test requirements:
|
||||
.. code-block:: bash
|
||||
|
||||
git clone -b main https://github.com/ocrmypdf/OCRmyPDF.git
|
||||
python -m venv
|
||||
source venv/bin/activate
|
||||
python -m .venv
|
||||
source .venv/bin/activate
|
||||
cd OCRmyPDF
|
||||
pip install -e .[test]
|
||||
|
||||
@@ -687,3 +665,18 @@ To manually install the ``bash`` completion, copy
|
||||
To manually install the ``fish`` completion, copy
|
||||
``misc/completion/ocrmypdf.fish`` to
|
||||
``~/.config/fish/completions/ocrmypdf.fish``.
|
||||
|
||||
Note on 32-bit support
|
||||
======================
|
||||
|
||||
Many Python libraries no longer 32-bit binary wheels for Linux. This
|
||||
includes many of the libraries that OCRmyPDF depends on, such as
|
||||
Pillow. The easiest way to express this to end users is to say we don't
|
||||
support 32-bit Linux.
|
||||
|
||||
However, if your Linux distribution still supports 32-bit binaries, you
|
||||
can still install and use OCRmyPDF. A warning message will appear.
|
||||
In practice, OCRmyPDF may need more than 32-bit memory space to run when
|
||||
large documents are processed, so there are practical limitations to what
|
||||
users can accomplish with it. Still, for the common use case of an 32-bit
|
||||
ARM NAS or Raspberry Pi processing small documents, it should work.
|
||||
@@ -45,11 +45,6 @@ to indicate that your distribution modifies OCRmyPDF in some way.
|
||||
You can patch the ``__version__`` variable in ``src/ocrmypdf/_version.py`` if
|
||||
necessary.
|
||||
|
||||
OCRmyPDF uses setuptools-scm-git-archive to ensure that tarballs downloaded from
|
||||
GitHub contain version information. Unfortunately, these tarballs are not always
|
||||
deterministic. See this
|
||||
`issue <https://github.com/ocrmypdf/OCRmyPDF/issues/841#issuecomment-936562696>`_.
|
||||
|
||||
jbig2enc
|
||||
--------
|
||||
|
||||
@@ -61,4 +56,12 @@ improve OCRmyPDF's compression.
|
||||
Command line completions
|
||||
------------------------
|
||||
|
||||
Please ensure that command line completions are installed.
|
||||
Please ensure that command line completions are installed, as described in the
|
||||
installation documentation.
|
||||
|
||||
32-bit Linux support
|
||||
--------------------
|
||||
|
||||
If you maintain a Linux distribution that supports 32-bit x86 or ARM, OCRmyPDF
|
||||
should continue to work as long as all of its dependencies continue to be
|
||||
available in 32-bit form. Please note we do not test on 32-bit platforms.
|
||||
+55
-14
@@ -54,31 +54,72 @@ into the existing PDF or it may essentially reconstruct ("re-fry") a
|
||||
visually identical PDF that may be quite different at the binary level.
|
||||
That said, OCRmyPDF is not a tool designed for sanitizing PDFs.
|
||||
|
||||
Password protection, digital signatures and certification
|
||||
=========================================================
|
||||
Password protected PDFs
|
||||
=======================
|
||||
|
||||
Password protected PDFs usually have two passwords, and owner and user
|
||||
password. When the user password is set to empty, PDF readers will open
|
||||
the file automatically and marked it as "(SECURED)". While not as
|
||||
reliable as a digital signature, this indicates that whoever set the
|
||||
password approved of the file at that time. When the user password is
|
||||
set, the document cannot be viewed without the password.
|
||||
the file automatically and mark it as "(SECURED)". Password security can
|
||||
also request certain restrictions on the PDF, but anyone can remove these
|
||||
restrictions if they have either the owner *or* user password. Passwords
|
||||
mainly present a barrier for casual users.
|
||||
|
||||
Either way, OCRmyPDF does not remove passwords from PDFs and exits with
|
||||
an error on encountering them.
|
||||
OCRmyPDF cannot remove passwords from PDFs. If you want to remove a
|
||||
password from a PDF, you must use other software, such as ``qpdf``.
|
||||
|
||||
``qpdf`` can remove passwords. If the owner and user password are set, a
|
||||
If the owner and user password are set, a
|
||||
password is required for ``qpdf``. If only the owner password is set, then the
|
||||
password can be stripped, even if one does not have the owner password.
|
||||
password can be stripped, even if one does not have the owner password. To
|
||||
remove the password from a using QPDF, use:
|
||||
|
||||
After OCR is applied, password protection is not permitted on PDF/A
|
||||
documents but the file can be converted to regular PDF.
|
||||
.. code-block:: bash
|
||||
|
||||
qpdf --decrypt --password='abc123' input.pdf no_password.pdf
|
||||
|
||||
Then you can run OCRmyPDF on the file.
|
||||
|
||||
In its default mode, OCRmyPDF generates PDF/A. Passwords may not be set on PDF/A
|
||||
documents. If you want to set a password on the output PDF, you must
|
||||
specify ``--output-type pdf``.
|
||||
|
||||
Signature images
|
||||
================
|
||||
|
||||
Many programs exist which are capable of inserting an image of someone's
|
||||
signature. On its own, this offers no security guarantees. It is trivial
|
||||
to remove the signature image and apply it to other files. This practice
|
||||
offers no real security.
|
||||
|
||||
Digital signatures
|
||||
==================
|
||||
|
||||
Important documents can be digitally signed and certified to attest to
|
||||
their authorship. OCRmyPDF cannot do this. Open source tools such as
|
||||
pdfbox (Java) have this capability as does Adobe Acrobat.
|
||||
their authorship, approval or execution of a legal agreement. OCRmyPDF
|
||||
will detect signed PDFs and will not modify them, unless the
|
||||
``--invalidate-digital-signatures`` option is used, which will
|
||||
invalidate any signatures. (The signature may still be present in the PDF
|
||||
if opened, but PDF readers will not validate it.)
|
||||
|
||||
A digital signature adds a cryptographic hash of the document to the
|
||||
document, so tamper protection is provided. That also precludes OCRmyPDF
|
||||
from modifying the document and preserving the signature.
|
||||
|
||||
Digital signatures are not the same as a signature image. A digital
|
||||
signature is a cryptographic hash of the document that is encrypted with
|
||||
the author's private key. The signature is decrypted with the author's
|
||||
public key. The public key is usually distributed by a certificate
|
||||
authority. The signature is then verified by the PDF reader. If the
|
||||
document is modified, the signature will be invalidated.
|
||||
|
||||
Certificate-encrypted PDFs
|
||||
==========================
|
||||
|
||||
PDFs can be encrypted with a certificate. This is a more secure form of
|
||||
encryption than a password. The certificate is usually issued by a
|
||||
certificate authority. A certificate is used to encrypt the document using
|
||||
the public key for the benefit of a specific recipient who possesses
|
||||
the private key.
|
||||
|
||||
OCRmyPDF cannot open certificate-encrypted PDFs. If you have the
|
||||
certificate, you can use other PDF software, such as Acrobat, to
|
||||
decrypt the PDF.
|
||||
+48
-1
@@ -28,6 +28,53 @@ tagged yet.
|
||||
|
||||
.. |OCRmyPDF PyPI| image:: https://img.shields.io/pypi/v/ocrmypdf.svg
|
||||
|
||||
v15.0.1
|
||||
=======
|
||||
|
||||
- Wheels Python tag changed to py39.
|
||||
- Marked as a expected fail a test that fails on recent Ghostscript versions.
|
||||
- Clarified documentation and release notes around the extent of 32-bit support.
|
||||
- Updated installation documentation to changes in v15.
|
||||
|
||||
v15.0.0
|
||||
=======
|
||||
|
||||
- Dropped support for Python 3.8.
|
||||
- Dropped support many older dependencies - see ``pyproject.toml`` for details.
|
||||
Generally speaking, Ubuntu 22.04 is our baseline system.
|
||||
- Dropped support for 32-bit Linux wheels. You must use a 64-bit operating system,
|
||||
and 64-bit versions of Python, Tesseract and Ghostscript to use OCRmyPDF. Many of
|
||||
our dependencies are dropping 32-bit builds (e.g. Pillow), and we are following
|
||||
suit. (Maintainers may still build 32-bit versions from source.)
|
||||
- Changed to trusted release for PyPI publishing.
|
||||
- pikepdf memory mapping is enabled again for improved performance, now an issue
|
||||
with pikepdf has been fixed.
|
||||
- ``ocrmypdf.helpers.calculate_downsample`` previously had two variants, one
|
||||
that took a ``PIL.Image`` and one that took a ``tuple[int, int]``. The latter
|
||||
was removed.
|
||||
- The snap version of ocrmypdf is now based on Ubuntu core22.
|
||||
- We now account situations where a small portion of an image on a page reports a
|
||||
high DPI (resolution). Previously, the entire page would be rasterized at the
|
||||
highest resolution, which caused performance problems. Now, the page is rasterized
|
||||
at a resolution based on the average DPI of the page, weighted by the area that
|
||||
each feature occupies. Typically, small areas of high resolution in PDFs are
|
||||
errors or quirks from the repeated use of assets and high resolution is not
|
||||
beneficial. :issue:`1010,1104,1004,1079,1010`
|
||||
- Ghostscript color conversion strategy is now configurable. :issue:`1143`
|
||||
|
||||
v14.4.0
|
||||
=======
|
||||
|
||||
- Digitally signed PDFs are now detected. If the PDF is signed, OCRmyPDF will
|
||||
refuse to modify it. Previously, only encrypted PDFs were detected, not
|
||||
those that were signed but not encrypted. :issue:`1040`
|
||||
- In addition, ``--invalidate-digital-signatures`` can be used to override the
|
||||
above behavior and modify the PDF anyway. :issue:`1040`
|
||||
- tqdm progress bars replaced with "rich" progress bars. The rich library is
|
||||
a new dependency. Certain APIs that used tqdm are now deprecated and will
|
||||
be removed in the next major release.
|
||||
- Improved integration with GitHub Releases. Thanks to @stumpylog.
|
||||
|
||||
v14.3.0
|
||||
=======
|
||||
|
||||
@@ -54,7 +101,7 @@ v14.2.1
|
||||
v14.2.0
|
||||
=======
|
||||
|
||||
- Added `--tesseract-downsample-above` to downsample larger images even when
|
||||
- Added ``--tesseract-downsample-above`` to downsample larger images even when
|
||||
they do not exceed Tesseract's internal limits. This can be used to speed
|
||||
up OCR, possibly sacrificing accuracy.
|
||||
- Fixed resampling AttributeError on older Pillow. :issue:`1096`
|
||||
|
||||
@@ -6,52 +6,56 @@ set -o errexit
|
||||
|
||||
__ocrmypdf_arguments()
|
||||
{
|
||||
local arguments="--help (show help message)
|
||||
--language (language(s) of the file to be OCRed)
|
||||
--image-dpi (assume this DPI if input image DPI is unknown)
|
||||
--output-type (select PDF output options)
|
||||
--sidecar (write OCR to text file)
|
||||
--version (print program version and exit)
|
||||
--jobs (how many worker processes to use)
|
||||
--quiet (suppress INFO messages)
|
||||
--verbose (set verbosity level)
|
||||
--title (set metadata)
|
||||
--author (set metadata)
|
||||
--subject (set metadata)
|
||||
--keywords (set metadata)
|
||||
--rotate-pages (rotate pages to correct orientation)
|
||||
--remove-background (attempt to remove background from pages)
|
||||
--deskew (fix small horizontal alignment skew)
|
||||
--clean (clean document images before OCR)
|
||||
--clean-final (clean document images and keep result)
|
||||
--unpaper-args (a quoted string of arguments to pass to unpaper)
|
||||
--oversample (oversample images to this DPI)
|
||||
--remove-vectors (don\'t send vector objects to OCR)
|
||||
--threshold (threshold images before OCR)
|
||||
--force-ocr (OCR documents that already have printable text)
|
||||
--skip-text (skip OCR on any pages that already contain text)
|
||||
--redo-ocr (redo OCR on any pages that seem to have OCR already)
|
||||
--skip-big (skip OCR on pages larger than this many MPixels)
|
||||
--optimize (select optimization level)
|
||||
--jpeg-quality (JPEG quality [0..100])
|
||||
--png-quality (PNG quality [0..100])
|
||||
--jbig2-lossy (enable lossy JBIG2 (see docs))
|
||||
--pages (apply OCR to only the specified pages)
|
||||
--max-image-mpixels (image decompression bomb threshold)
|
||||
--pdf-renderer (select PDF renderer options)
|
||||
--rotate-pages-threshold (page rotation confidence)
|
||||
--pdfa-image-compression (set PDF/A image compression options)
|
||||
--fast-web-view (if file size if above this amount in MB linearize PDF)
|
||||
--plugin (name of plugin to import)
|
||||
--keep-temporary-files (keep temporary files (debug)
|
||||
--tesseract-config (set custom tesseract config file)
|
||||
--tesseract-pagesegmode (set tesseract --psm)
|
||||
--tesseract-oem (set tesseract --oem)
|
||||
--tesseract-thresholding (set tesseract image thresholding)
|
||||
--tesseract-timeout (maximum number of seconds to wait for OCR)
|
||||
--user-words (specify location of user words file)
|
||||
--user-patterns (specify location of user patterns file)
|
||||
--no-progress-bar (disable the progress bar)
|
||||
local arguments="\
|
||||
--help (show help message)
|
||||
--language (language(s) of the file to be OCRed)
|
||||
--image-dpi (assume this DPI if input image DPI is unknown)
|
||||
--output-type (select PDF output options)
|
||||
--sidecar (write OCR to text file)
|
||||
--version (print program version and exit)
|
||||
--jobs (how many worker processes to use)
|
||||
--quiet (suppress INFO messages)
|
||||
--verbose (set verbosity level)
|
||||
--title (set metadata)
|
||||
--author (set metadata)
|
||||
--subject (set metadata)
|
||||
--keywords (set metadata)
|
||||
--rotate-pages (rotate pages to correct orientation)
|
||||
--remove-background (attempt to remove background from pages)
|
||||
--deskew (fix small horizontal alignment skew)
|
||||
--clean (clean document images before OCR)
|
||||
--clean-final (clean document images and keep result)
|
||||
--unpaper-args (a quoted string of arguments to pass to unpaper)
|
||||
--oversample (oversample images to this DPI)
|
||||
--remove-vectors (don\'t send vector objects to OCR)
|
||||
--threshold (threshold images before OCR)
|
||||
--force-ocr (OCR documents that already have printable text)
|
||||
--skip-text (skip OCR on any pages that already contain text)
|
||||
--redo-ocr (redo OCR on any pages that seem to have OCR already)
|
||||
--invalidate-digital-signatures (remove digital signatures from PDF)
|
||||
--skip-big (skip OCR on pages larger than this many MPixels)
|
||||
--optimize (select optimization level)
|
||||
--jpeg-quality (JPEG quality [0..100])
|
||||
--png-quality (PNG quality [0..100])
|
||||
--jbig2-lossy (enable lossy JBIG2 (see docs))
|
||||
--jbig2-threshold (set JBIG2 threshold (see docs))
|
||||
--pages (apply OCR to only the specified pages)
|
||||
--max-image-mpixels (image decompression bomb threshold)
|
||||
--pdf-renderer (select PDF renderer options)
|
||||
--rotate-pages-threshold (page rotation confidence)
|
||||
--pdfa-image-compression (set PDF/A image compression options)
|
||||
--fast-web-view (if file size if above this amount in MB linearize PDF)
|
||||
--plugin (name of plugin to import)
|
||||
--keep-temporary-files (keep temporary files (debug)
|
||||
--tesseract-config (set custom tesseract config file)
|
||||
--tesseract-pagesegmode (set tesseract --psm)
|
||||
--tesseract-oem (set tesseract --oem)
|
||||
--tesseract-thresholding (set tesseract image thresholding)
|
||||
--tesseract-timeout (maximum number of seconds to wait for OCR)
|
||||
--user-words (specify location of user words file)
|
||||
--user-patterns (specify location of user patterns file)
|
||||
--no-progress-bar (disable the progress bar)
|
||||
--color-conversion-strategy (select color conversion strategy)
|
||||
"
|
||||
|
||||
COMPREPLY=( $( compgen -W "$arguments" -- "$cur") )
|
||||
@@ -191,6 +195,20 @@ sauvola (use Sauvola thresholding)"
|
||||
fi
|
||||
}
|
||||
|
||||
__ocrmypdf_color-conversion-strategy()
|
||||
{
|
||||
local choices="LeaveColorUnchanged (default)
|
||||
CMYK (convert to CMYK)
|
||||
Gray (convert to grayscale)
|
||||
RGB (convert to RGB)
|
||||
UseDeviceIndependentColor (convert with device independent color)"
|
||||
|
||||
COMPREPLY=( $( compgen -W "$choices" -- "$cur") )
|
||||
# Remove description if only one completion exists
|
||||
if [[ ${#COMPREPLY[*]} -eq 1 ]]; then
|
||||
COMPREPLY=( ${COMPREPLY[0]%% *} )
|
||||
fi
|
||||
}
|
||||
|
||||
__ocrmypdf_check_previous()
|
||||
{
|
||||
@@ -250,6 +268,10 @@ __ocrmypdf_check_previous()
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
--color-conversion-strategy)
|
||||
__ocrmypdf_color-conversion-strategy
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
return 1
|
||||
|
||||
@@ -14,8 +14,9 @@ complete -c ocrmypdf -s i -l clean-final -d "clean document images and keep resu
|
||||
complete -c ocrmypdf -l remove-vectors -d "don't send vector objects to OCR"
|
||||
|
||||
complete -c ocrmypdf -s f -l force-ocr -d "OCR documents that already have printable text"
|
||||
complete -c ocrmypdf -s s -l skip-ocr -d "skip OCR on pages that text, otherwise try OCR"
|
||||
complete -c ocrmypdf -s s -l skip-text -d "skip OCR on any pages that already contain text"
|
||||
complete -c ocrmypdf -l redo-ocr -d "redo OCR on any pages that seem to have OCR already"
|
||||
complete -c ocrmypdf -l invalidate-digital-signatures -d "invalidate digital signatures and allow OCR to proceed"
|
||||
|
||||
complete -c ocrmypdf -s k -l keep-temporary-files -d "keep temporary files (debug)"
|
||||
|
||||
@@ -83,6 +84,7 @@ complete -c ocrmypdf -x -l skip-big -d "skip OCR on pages larger than this many
|
||||
complete -c ocrmypdf -x -l jpeg-quality -d "JPEG quality [0..100]"
|
||||
complete -c ocrmypdf -x -l png-quality -d "PNG quality [0..100]"
|
||||
complete -c ocrmypdf -x -l jbig2-lossy -d "enable lossy JBIG2 (see docs)"
|
||||
complete -c ocrmypdf -x -l jbig2-threshold -d "JBIG2 compression threshold (see docs)"
|
||||
complete -c ocrmypdf -x -l max-image-mpixels -d "image decompression bomb threshold"
|
||||
complete -c ocrmypdf -x -l pages -d "apply OCR to only the specified pages"
|
||||
complete -c ocrmypdf -x -l tesseract-config -d "set custom tesseract config file"
|
||||
@@ -128,4 +130,27 @@ 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 -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; __fish_complete_suffix .PDF; __fish_complete_suffix .jpg; __fish_complete_suffix .png)"
|
||||
function __fish_ocrmypdf_color_conversion_strategy
|
||||
echo -e "LeaveColorUnchanged\t"(_ "do not convert color spaces (default)")
|
||||
echo -e "CMYK\t"(_ "convert all color spaces to CMYK")
|
||||
echo -e "Gray\t"(_ "convert all color spaces to grayscale")
|
||||
echo -e "RGB\t"(_ "convert all color spaces to RGB")
|
||||
echo -e "UseDeviceIndependentColor\t"(_ "convert all color spaces to ICC-based color spaces")
|
||||
end
|
||||
|
||||
complete -c ocrmypdf -x -l color-conversion-strategy -a '(__fish_ocrmypdf_color_conversion_strategy)' -d "set color conversion strategy"
|
||||
|
||||
function __fish_ocrmypdf_input_file_given
|
||||
set -l tokens (commandline -opc)
|
||||
for token in $tokens
|
||||
if string match -q -r '^-' -- $token
|
||||
continue
|
||||
end
|
||||
if test -f "$token"
|
||||
return 0
|
||||
end
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
complete -c ocrmypdf -x -n 'not __fish_ocrmypdf_input_file_given' -a "(__fish_complete_suffix .pdf)" -d "input file"
|
||||
|
||||
+29
-41
@@ -1,11 +1,7 @@
|
||||
# SPDX-FileCopyrightText: 2022 James R. Barlow
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
[build-system]
|
||||
requires = [
|
||||
"setuptools >= 61",
|
||||
"setuptools_scm[toml] >= 7.0.5",
|
||||
"wheel"
|
||||
]
|
||||
requires = ["setuptools >= 61", "setuptools_scm[toml] >= 7.0.5", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
@@ -13,23 +9,21 @@ name = "ocrmypdf"
|
||||
dynamic = ["version"]
|
||||
description = "OCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to be searched"
|
||||
readme = "README.md"
|
||||
license = {text = "MPL-2.0"}
|
||||
requires-python = ">=3.8"
|
||||
license = { text = "MPL-2.0" }
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"Pillow>=8.2.0",
|
||||
"coloredlogs>=14.0",
|
||||
"Pillow>=9.0.1",
|
||||
"deprecation>=2.1.0",
|
||||
"img2pdf>=0.3.0", # pure Python
|
||||
"img2pdf>=0.4.4",
|
||||
"packaging>=20",
|
||||
"pdfminer.six>=20201018",
|
||||
"pikepdf>=5.0.1",
|
||||
"pdfminer.six>=20220319",
|
||||
"pikepdf>=8",
|
||||
"pluggy>=0.13.0",
|
||||
"reportlab>=3.5.66",
|
||||
"tqdm>=4",
|
||||
"importlib-resources>=5;python_version<'3.9'", # until Python 3.9
|
||||
"reportlab>=3.6.8",
|
||||
"rich>=13",
|
||||
"typing-extensions>=4;python_version<'3.10'",
|
||||
]
|
||||
authors = [{name = "James R. Barlow", email="james@purplerock.ca"}]
|
||||
authors = [{ name = "James R. Barlow", email = "james@purplerock.ca" }]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: Console",
|
||||
@@ -47,13 +41,7 @@ classifiers = [
|
||||
"Topic :: Text Processing :: Indexing",
|
||||
"Topic :: Text Processing :: Linguistic",
|
||||
]
|
||||
keywords = [
|
||||
"PDF",
|
||||
"OCR",
|
||||
"optical character recognition",
|
||||
"PDF/A",
|
||||
"scanning",
|
||||
]
|
||||
keywords = ["PDF", "OCR", "optical character recognition", "PDF/A", "scanning"]
|
||||
|
||||
[project.urls]
|
||||
Documentation = "https://ocrmypdf.readthedocs.io/"
|
||||
@@ -64,17 +52,17 @@ Tracker = "https://github.com/ocrmypdf/OCRmyPDF/issues"
|
||||
docs = ["sphinx", "sphinx-issues", "sphinx-rtd-theme"]
|
||||
extended_test = ["PyMuPDF==1.19.1"]
|
||||
test = [
|
||||
"coverage[toml]>=5",
|
||||
"hypothesis>=6.0.0",
|
||||
"pytest>=6.0.0",
|
||||
"pytest-cov>=2.11.1",
|
||||
"pytest-xdist>=2.2.0",
|
||||
"python-xmp-toolkit==2.0.1", # also requires apt-get install libexempi3
|
||||
"coverage[toml]>=6.2",
|
||||
"hypothesis>=6.36.0",
|
||||
"pytest>=6.2.5",
|
||||
"pytest-cov>=3.0.0",
|
||||
"pytest-xdist>=2.5.0",
|
||||
"python-xmp-toolkit==2.0.1", # also requires apt-get install libexempi3
|
||||
"types-Pillow",
|
||||
"types-humanfriendly",
|
||||
]
|
||||
watcher = ["watchdog>=1.0.2"]
|
||||
webservice = ["Flask>=1"]
|
||||
webservice = ["Flask>=2.0.1"]
|
||||
|
||||
[project.scripts]
|
||||
ocrmypdf = "ocrmypdf.__main__:run"
|
||||
@@ -89,11 +77,11 @@ namespaces = false
|
||||
[tool.setuptools_scm]
|
||||
|
||||
[tool.distutils.bdist_wheel]
|
||||
python-tag = "py38"
|
||||
python-tag = "py39"
|
||||
|
||||
[tool.black]
|
||||
line-length = 88
|
||||
target-version = ["py38", "py39", "py310", "py311"]
|
||||
target-version = ["py39", "py310", "py311"]
|
||||
skip-string-normalization = true
|
||||
include = '\.pyi?$'
|
||||
exclude = '''
|
||||
@@ -134,7 +122,7 @@ exclude_lines = [
|
||||
"if 0:",
|
||||
"if False:",
|
||||
"if __name__ == .__main__.:",
|
||||
"if TYPE_CHECKING:"
|
||||
"if TYPE_CHECKING:",
|
||||
]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
@@ -156,20 +144,20 @@ module = [
|
||||
'pdfminer.*',
|
||||
'reportlab.*',
|
||||
'fitz',
|
||||
'libxmp.utils'
|
||||
'libxmp.utils',
|
||||
]
|
||||
ignore_missing_imports = true
|
||||
|
||||
[tool.ruff]
|
||||
select = [
|
||||
"D", # pydocstyle
|
||||
"E", # pycodestyle
|
||||
"W", # pycodestyle
|
||||
"F", # pyflakes
|
||||
"D", # pydocstyle
|
||||
"E", # pycodestyle
|
||||
"W", # pycodestyle
|
||||
"F", # pyflakes
|
||||
"I001", # isort
|
||||
"UP", # pyupgrade
|
||||
"UP", # pyupgrade
|
||||
]
|
||||
target-version = "py38"
|
||||
target-version = "py39"
|
||||
|
||||
[tool.ruff.isort]
|
||||
known-first-party = ["ocrmypdf"]
|
||||
@@ -182,4 +170,4 @@ convention = "google"
|
||||
"docs/conf.py" = ["D100", "D101", "D105"]
|
||||
"tests/*.py" = ["D100", "D101", "D102", "D103", "D105"]
|
||||
"misc/*.py" = ["D103", "D101", "D102"]
|
||||
"src/ocrmypdf/builtin_plugins/*.py" = ["D103", "D102", "D105"]
|
||||
"src/ocrmypdf/builtin_plugins/*.py" = ["D103", "D102", "D105"]
|
||||
|
||||
+5
-5
@@ -5,9 +5,9 @@
|
||||
|
||||
name: ocrmypdf
|
||||
title: OCRmyPDF
|
||||
base: core20
|
||||
base: core22
|
||||
version: git
|
||||
summary: OCRmyPDF adds optical character recognition (OCR) to PDFs
|
||||
summary: OCRmyPDF adds a searchable text layer to scanned PDF files
|
||||
description: OCRmyPDF packaged for snap
|
||||
grade: stable
|
||||
confinement: strict
|
||||
@@ -18,8 +18,8 @@ architectures: [amd64]
|
||||
|
||||
environment:
|
||||
TESSDATA_PREFIX: $SNAP/usr/share/tesseract-ocr/4.00/tessdata
|
||||
GS_LIB: $SNAP/usr/share/ghostscript/9.50/Resource/Init
|
||||
GS_FONTPATH: $SNAP/usr/share/ghostscript/9.50/Resource/Font
|
||||
GS_LIB: $SNAP/usr/share/ghostscript/9.55/Resource/Init
|
||||
GS_FONTPATH: $SNAP/usr/share/ghostscript/9.55/Resource/Font
|
||||
LD_LIBRARY_PATH: $SNAP/usr/lib/x86_64-linux-gnu
|
||||
|
||||
apps:
|
||||
@@ -49,7 +49,7 @@ parts:
|
||||
jbig2enc:
|
||||
plugin: autotools
|
||||
source: https://github.com/agl/jbig2enc.git
|
||||
source-tag: '0.29'
|
||||
source-tag: "0.29"
|
||||
build-packages:
|
||||
- libleptonica-dev
|
||||
|
||||
|
||||
@@ -28,24 +28,50 @@ except AttributeError:
|
||||
# Pillow 9 shim
|
||||
Transpose = Image # type: ignore
|
||||
|
||||
|
||||
COLOR_CONVERSION_STRATEGIES = frozenset(
|
||||
[
|
||||
'CMYK',
|
||||
'Gray',
|
||||
'LeaveColorUnchanged',
|
||||
'RGB',
|
||||
'UseDeviceIndependentColor',
|
||||
]
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Most reliable way to get the bitness of Python interpreter, according to Python docs
|
||||
_IS_64BIT = sys.maxsize > 2**32
|
||||
|
||||
_GSWIN = None
|
||||
if os.name == 'nt':
|
||||
if _IS_64BIT:
|
||||
_GSWIN = 'gswin64c'
|
||||
else:
|
||||
_GSWIN = 'gswin32c'
|
||||
class DuplicateFilter(logging.Filter):
|
||||
"""Filter out duplicate log messages."""
|
||||
|
||||
GS = _GSWIN if _GSWIN else 'gs'
|
||||
del _GSWIN
|
||||
def __init__(self, logger: logging.Logger):
|
||||
self.last: logging.LogRecord | None = None
|
||||
self.count = 0
|
||||
self.logger = logger
|
||||
|
||||
def filter(self, record):
|
||||
if self.last and record.msg == self.last.msg:
|
||||
self.count += 1
|
||||
return False
|
||||
else:
|
||||
if self.count >= 1:
|
||||
rep_msg = f"(previous message repeated {self.count} times)"
|
||||
self.count = 0 # Avoid infinite recursion
|
||||
self.logger.log(self.last.levelno, rep_msg)
|
||||
self.last = record
|
||||
return True
|
||||
|
||||
|
||||
def version():
|
||||
return get_version(GS)
|
||||
log.addFilter(DuplicateFilter(log))
|
||||
|
||||
|
||||
# Ghostscript executable - gswin32c is not supported
|
||||
GS = 'gswin64c' if os.name == 'nt' else 'gs'
|
||||
|
||||
|
||||
def version() -> Version:
|
||||
return Version(get_version(GS))
|
||||
|
||||
|
||||
def _gs_error_reported(stream) -> bool:
|
||||
@@ -160,6 +186,7 @@ def generate_pdfa(
|
||||
output_file: os.PathLike,
|
||||
*,
|
||||
compression: str,
|
||||
color_conversion_strategy: str,
|
||||
pdf_version: str = '1.5',
|
||||
pdfa_part: str = '2',
|
||||
progressbar_class=None,
|
||||
@@ -189,8 +216,7 @@ def generate_pdfa(
|
||||
"-dAutoFilterGrayImages=true",
|
||||
]
|
||||
|
||||
strategy = 'LeaveColorUnchanged'
|
||||
gs_version = Version(version())
|
||||
gs_version = version()
|
||||
if gs_version == Version('9.56.0'):
|
||||
# 9.56.0 breaks our OCR, should be fixed in 9.56.1
|
||||
# https://bugs.ghostscript.com/show_bug.cgi?id=705187
|
||||
@@ -209,16 +235,16 @@ def generate_pdfa(
|
||||
"-dBATCH",
|
||||
"-dNOPAUSE",
|
||||
"-dSAFER",
|
||||
"-dCompatibilityLevel=" + str(pdf_version),
|
||||
f"-dCompatibilityLevel={str(pdf_version)}",
|
||||
"-sDEVICE=pdfwrite",
|
||||
"-dAutoRotatePages=/None",
|
||||
"-sColorConversionStrategy=" + strategy,
|
||||
f"-sColorConversionStrategy={color_conversion_strategy}",
|
||||
]
|
||||
+ (['-dPDFSTOPONERROR'] if stop_on_error else [])
|
||||
+ compression_args
|
||||
+ [
|
||||
"-dJPEGQ=95",
|
||||
"-dPDFA=" + pdfa_part,
|
||||
f"-dPDFA={pdfa_part}",
|
||||
"-dPDFACompatibilityPolicy=1",
|
||||
"-o",
|
||||
"-",
|
||||
@@ -249,14 +275,9 @@ def generate_pdfa(
|
||||
# If there is an error we log the whole stderr, except for filtering
|
||||
# duplicates.
|
||||
if _gs_error_reported(stderr):
|
||||
last_part = None
|
||||
repcount = 0
|
||||
# Ghostscript outputs the pattern **** Error: .... frequently.
|
||||
# Occasionally the error message is spammed many times. We filter
|
||||
# out duplicates of this message using the filter above. We use
|
||||
# the **** pattern to split the stderr into parts.
|
||||
for part in stderr.split('****'):
|
||||
if part != last_part:
|
||||
if repcount > 1:
|
||||
log.error(f"(previous error message repeated {repcount} times)")
|
||||
repcount = 0
|
||||
log.error(part)
|
||||
else:
|
||||
repcount += 1
|
||||
last_part = part
|
||||
log.error(part)
|
||||
|
||||
@@ -7,12 +7,14 @@ from __future__ import annotations
|
||||
|
||||
from subprocess import PIPE
|
||||
|
||||
from packaging.version import Version
|
||||
|
||||
from ocrmypdf.exceptions import MissingDependencyError
|
||||
from ocrmypdf.subprocess import get_version, run
|
||||
|
||||
|
||||
def version():
|
||||
return get_version('jbig2', regex=r'jbig2enc (\d+(\.\d+)*).*')
|
||||
def version() -> Version:
|
||||
return Version(get_version('jbig2', regex=r'jbig2enc (\d+(\.\d+)*).*'))
|
||||
|
||||
|
||||
def available():
|
||||
@@ -23,15 +25,17 @@ def available():
|
||||
return True
|
||||
|
||||
|
||||
def convert_group(*, cwd, infiles, out_prefix):
|
||||
def convert_group(*, cwd, infiles, out_prefix, threshold):
|
||||
args = [
|
||||
'jbig2',
|
||||
'-b',
|
||||
out_prefix,
|
||||
'-s', # symbol mode (lossy)
|
||||
'--symbol-mode', # symbol mode (lossy)
|
||||
'-t',
|
||||
str(threshold), # threshold
|
||||
# '-r', # refinement mode (lossless symbol mode, currently disabled in
|
||||
# jbig2)
|
||||
'-p',
|
||||
'--pdf',
|
||||
]
|
||||
args.extend(infiles)
|
||||
proc = run(args, cwd=cwd, stdout=PIPE, stderr=PIPE)
|
||||
@@ -40,11 +44,13 @@ def convert_group(*, cwd, infiles, out_prefix):
|
||||
|
||||
|
||||
def convert_group_mp(args):
|
||||
return convert_group(cwd=args[0], infiles=args[1], out_prefix=args[2])
|
||||
return convert_group(
|
||||
cwd=args[0], infiles=args[1], out_prefix=args[2], threshold=args[3]
|
||||
)
|
||||
|
||||
|
||||
def convert_single(*, cwd, infile, outfile):
|
||||
args = ['jbig2', '-p', infile]
|
||||
def convert_single(*, cwd, infile, outfile, threshold):
|
||||
args = ['jbig2', '--pdf', '-t', str(threshold), infile]
|
||||
with open(outfile, 'wb') as fstdout:
|
||||
proc = run(args, cwd=cwd, stdout=fstdout, stderr=PIPE)
|
||||
proc.check_returncode()
|
||||
@@ -52,4 +58,6 @@ def convert_single(*, cwd, infile, outfile):
|
||||
|
||||
|
||||
def convert_single_mp(args):
|
||||
return convert_single(cwd=args[0], infile=args[1], outfile=args[2])
|
||||
return convert_single(
|
||||
cwd=args[0], infile=args[1], outfile=args[2], threshold=args[3]
|
||||
)
|
||||
|
||||
@@ -10,14 +10,15 @@ from io import BytesIO
|
||||
from pathlib import Path
|
||||
from subprocess import PIPE
|
||||
|
||||
from packaging.version import Version
|
||||
from PIL import Image
|
||||
|
||||
from ocrmypdf.exceptions import MissingDependencyError
|
||||
from ocrmypdf.subprocess import get_version, run
|
||||
|
||||
|
||||
def version():
|
||||
return get_version('pngquant', regex=r'(\d+(\.\d+)*).*')
|
||||
def version() -> Version:
|
||||
return Version(get_version('pngquant', regex=r'(\d+(\.\d+)*).*'))
|
||||
|
||||
|
||||
def available():
|
||||
|
||||
@@ -113,13 +113,13 @@ class TesseractVersion(Version):
|
||||
)
|
||||
|
||||
|
||||
def version() -> str:
|
||||
return get_version('tesseract', regex=r'tesseract\s(.+)')
|
||||
def version() -> Version:
|
||||
return TesseractVersion(get_version('tesseract', regex=r'tesseract\s(.+)'))
|
||||
|
||||
|
||||
def has_thresholding() -> bool:
|
||||
"""Does Tesseract have -c thresholding method capability?"""
|
||||
return version() >= '5.0'
|
||||
return version() >= Version('5.0')
|
||||
|
||||
|
||||
def get_languages() -> set[str]:
|
||||
|
||||
@@ -15,6 +15,7 @@ from pathlib import Path
|
||||
from subprocess import PIPE, STDOUT
|
||||
from typing import Iterator, Union
|
||||
|
||||
from packaging.version import Version
|
||||
from PIL import Image
|
||||
|
||||
from ocrmypdf.exceptions import MissingDependencyError, SubprocessOutputError
|
||||
@@ -67,8 +68,8 @@ class UnpaperImageTooLargeError(Exception):
|
||||
super().__init__(self.message)
|
||||
|
||||
|
||||
def version() -> str:
|
||||
return get_version('unpaper')
|
||||
def version() -> Version:
|
||||
return Version(get_version('unpaper'))
|
||||
|
||||
|
||||
SUPPORTED_MODES = {'1', 'L', 'RGB'}
|
||||
|
||||
+65
-16
@@ -6,9 +6,18 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from contextlib import suppress
|
||||
|
||||
from tqdm import tqdm
|
||||
from rich.console import Console
|
||||
from rich.logging import RichHandler
|
||||
from rich.progress import (
|
||||
BarColumn,
|
||||
MofNCompleteColumn,
|
||||
Progress,
|
||||
TaskProgressColumn,
|
||||
TextColumn,
|
||||
TimeRemainingColumn,
|
||||
)
|
||||
from rich.table import Column
|
||||
|
||||
|
||||
class PageNumberFilter(logging.Filter):
|
||||
@@ -23,21 +32,61 @@ class PageNumberFilter(logging.Filter):
|
||||
return True
|
||||
|
||||
|
||||
class TqdmConsole:
|
||||
"""Wrapper to log messages in a way that is compatible with tqdm progress bar.
|
||||
class RichLoggingHandler(RichHandler):
|
||||
def __init__(self, console: Console, **kwargs):
|
||||
super().__init__(
|
||||
console=console, show_level=False, show_time=False, markup=True, **kwargs
|
||||
)
|
||||
|
||||
This routes log messages through tqdm so that it can print them above the
|
||||
progress bar, and then refresh the progress bar, rather than overwriting
|
||||
it which looks messy.
|
||||
"""
|
||||
|
||||
def __init__(self, file):
|
||||
self.file = file
|
||||
class RichTqdmProgressAdapter:
|
||||
"""Adapt tqdm API to rich progress bar."""
|
||||
|
||||
def write(self, msg):
|
||||
# When no progress bar is active, tqdm.write() routes to print()
|
||||
tqdm.write(msg.rstrip(), end='\n', file=self.file)
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
console: Console,
|
||||
desc: str,
|
||||
total: float | None = None,
|
||||
unit: str | None = None,
|
||||
unit_scale: float | None = 1.0,
|
||||
disable: bool = False,
|
||||
**kwargs,
|
||||
):
|
||||
self.progress = Progress(
|
||||
TextColumn(
|
||||
"[progress.description]{task.description}",
|
||||
table_column=Column(min_width=20),
|
||||
),
|
||||
BarColumn(),
|
||||
TaskProgressColumn(),
|
||||
MofNCompleteColumn(),
|
||||
TimeRemainingColumn(),
|
||||
console=console,
|
||||
auto_refresh=True,
|
||||
redirect_stderr=True,
|
||||
redirect_stdout=False,
|
||||
disable=disable,
|
||||
**kwargs,
|
||||
)
|
||||
self.unit_scale = unit_scale
|
||||
self.progress_bar = self.progress.add_task(
|
||||
desc,
|
||||
total=total * self.unit_scale
|
||||
if total is not None and self.unit_scale is not None
|
||||
else None,
|
||||
unit=unit,
|
||||
)
|
||||
|
||||
def flush(self):
|
||||
with suppress(AttributeError):
|
||||
self.file.flush()
|
||||
def __enter__(self):
|
||||
self.progress.start()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.progress.refresh()
|
||||
self.progress.stop()
|
||||
return False
|
||||
|
||||
def update(self, value=None):
|
||||
advance = self.unit_scale if value is None else value
|
||||
self.progress.update(self.progress_bar, advance=advance)
|
||||
|
||||
+141
-63
@@ -14,7 +14,7 @@ from contextlib import suppress
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from shutil import copyfileobj
|
||||
from typing import Any, BinaryIO, Iterable, Sequence, cast
|
||||
from typing import Any, BinaryIO, Iterable, Iterator, Sequence, cast
|
||||
|
||||
import img2pdf
|
||||
import pikepdf
|
||||
@@ -27,6 +27,7 @@ from ocrmypdf._jobcontext import PageContext, PdfContext
|
||||
from ocrmypdf._version import PROGRAM_NAME
|
||||
from ocrmypdf._version import __version__ as VERSION
|
||||
from ocrmypdf.exceptions import (
|
||||
DigitalSignatureError,
|
||||
DpiError,
|
||||
EncryptedPdfError,
|
||||
InputFileError,
|
||||
@@ -80,34 +81,32 @@ def triage_image_file(input_file: Path, output_file: Path, options) -> None:
|
||||
if im.info['dpi'] <= (96, 96) and not options.image_dpi:
|
||||
log.info("Image size: (%d, %d)", *im.size)
|
||||
log.info("Image resolution: (%d, %d)", *im.info['dpi'])
|
||||
log.error(
|
||||
raise DpiError(
|
||||
"Input file is an image, but the resolution (DPI) is "
|
||||
"not credible. Estimate the resolution at which the "
|
||||
"image was scanned and specify it using --image-dpi."
|
||||
)
|
||||
raise DpiError()
|
||||
elif not options.image_dpi:
|
||||
log.info("Image size: (%d, %d)", *im.size)
|
||||
log.error(
|
||||
raise DpiError(
|
||||
"Input file is an image, but has no resolution (DPI) "
|
||||
"in its metadata. Estimate the resolution at which "
|
||||
"image was scanned and specify it using --image-dpi."
|
||||
)
|
||||
raise DpiError()
|
||||
|
||||
if im.mode in ('RGBA', 'LA'):
|
||||
log.error(
|
||||
raise UnsupportedImageFormatError(
|
||||
"The input image has an alpha channel. Remove the alpha "
|
||||
"channel first."
|
||||
)
|
||||
raise UnsupportedImageFormatError()
|
||||
|
||||
if 'iccprofile' not in im.info:
|
||||
if im.mode == 'RGB':
|
||||
log.info("Input image has no ICC profile, assuming sRGB")
|
||||
elif im.mode == 'CMYK':
|
||||
log.error("Input CMYK image has no ICC profile, not usable")
|
||||
raise UnsupportedImageFormatError()
|
||||
raise UnsupportedImageFormatError(
|
||||
"Input CMYK image has no ICC profile, not usable"
|
||||
)
|
||||
|
||||
try:
|
||||
log.info("Image seems valid. Try converting to PDF...")
|
||||
@@ -125,7 +124,6 @@ def triage_image_file(input_file: Path, output_file: Path, options) -> None:
|
||||
)
|
||||
log.info("Successfully converted to PDF, processing...")
|
||||
except img2pdf.ImageOpenError as e:
|
||||
log.error(e)
|
||||
raise UnsupportedImageFormatError() from e
|
||||
|
||||
|
||||
@@ -195,18 +193,21 @@ def validate_pdfinfo_options(context: PdfContext) -> None:
|
||||
options = context.options
|
||||
|
||||
if pdfinfo.needs_rendering:
|
||||
log.error(
|
||||
raise InputFileError(
|
||||
"This PDF contains dynamic XFA forms created by Adobe LiveCycle "
|
||||
"Designer and can only be read by Adobe Acrobat or Adobe Reader."
|
||||
)
|
||||
raise InputFileError()
|
||||
if pdfinfo.has_signature:
|
||||
if options.invalidate_digital_signatures:
|
||||
log.warning("All digital signatures will be invalidated")
|
||||
else:
|
||||
raise DigitalSignatureError()
|
||||
if pdfinfo.has_acroform:
|
||||
if options.redo_ocr:
|
||||
log.error(
|
||||
raise InputFileError(
|
||||
"This PDF has a user fillable form. --redo-ocr is not "
|
||||
"currently possible on such files."
|
||||
)
|
||||
raise InputFileError()
|
||||
else:
|
||||
log.warning(
|
||||
"This PDF has a fillable form. "
|
||||
@@ -227,28 +228,19 @@ def _vector_page_dpi(pageinfo: PageInfo) -> int:
|
||||
return VECTOR_PAGE_DPI if pageinfo.has_vector or pageinfo.has_text else 0
|
||||
|
||||
|
||||
def get_page_dpi(pageinfo: PageInfo, options) -> Resolution:
|
||||
"""Get the DPI when nonsquare DPI is tolerable."""
|
||||
xres = max(
|
||||
pageinfo.dpi.x or VECTOR_PAGE_DPI,
|
||||
options.oversample or 0.0,
|
||||
_vector_page_dpi(pageinfo),
|
||||
)
|
||||
yres = max(
|
||||
pageinfo.dpi.y or VECTOR_PAGE_DPI,
|
||||
options.oversample or 0,
|
||||
_vector_page_dpi(pageinfo),
|
||||
)
|
||||
return Resolution(float(xres), float(yres))
|
||||
|
||||
|
||||
def get_page_square_dpi(pageinfo: PageInfo, options) -> Resolution:
|
||||
def get_page_square_dpi(
|
||||
page_context: PageContext, image_dpi: Resolution | None = None
|
||||
) -> Resolution:
|
||||
"""Get the DPI when we require xres == yres, scaled to physical units.
|
||||
|
||||
Page DPI includes UserUnit scaling.
|
||||
"""
|
||||
xres = pageinfo.dpi.x or 0.0
|
||||
yres = pageinfo.dpi.y or 0.0
|
||||
pageinfo = page_context.pageinfo
|
||||
options = page_context.options
|
||||
if not image_dpi:
|
||||
image_dpi = pageinfo.dpi
|
||||
xres = image_dpi.x or 0.0
|
||||
yres = image_dpi.y or 0.0
|
||||
userunit = float(pageinfo.userunit) or 1.0
|
||||
units = float(
|
||||
max(
|
||||
@@ -261,17 +253,23 @@ def get_page_square_dpi(pageinfo: PageInfo, options) -> Resolution:
|
||||
return Resolution(units, units)
|
||||
|
||||
|
||||
def get_canvas_square_dpi(pageinfo: PageInfo, options) -> Resolution:
|
||||
def get_canvas_square_dpi(
|
||||
page_context: PageContext, image_dpi: Resolution | None = None
|
||||
) -> Resolution:
|
||||
"""Get the DPI when we require xres == yres, in Postscript units.
|
||||
|
||||
Canvas DPI is independent of PDF UserUnit scaling, which is
|
||||
used to describe situations where the PDF user space is not 1:1 with
|
||||
the physical units of the page.
|
||||
"""
|
||||
pageinfo = page_context.pageinfo
|
||||
options = page_context.options
|
||||
if not image_dpi:
|
||||
image_dpi = pageinfo.dpi
|
||||
units = float(
|
||||
max(
|
||||
(pageinfo.dpi.x) or VECTOR_PAGE_DPI,
|
||||
(pageinfo.dpi.y) or VECTOR_PAGE_DPI,
|
||||
image_dpi.x or VECTOR_PAGE_DPI,
|
||||
image_dpi.y or VECTOR_PAGE_DPI,
|
||||
_vector_page_dpi(pageinfo),
|
||||
options.oversample or 0.0,
|
||||
)
|
||||
@@ -358,8 +356,10 @@ def is_ocr_required(page_context: PageContext) -> bool:
|
||||
def rasterize_preview(input_file: Path, page_context: PageContext) -> Path:
|
||||
"""Generate a lower quality preview image."""
|
||||
output_file = page_context.get_path('rasterize_preview.jpg')
|
||||
canvas_dpi = get_canvas_square_dpi(page_context.pageinfo, page_context.options)
|
||||
page_dpi = get_page_square_dpi(page_context.pageinfo, page_context.options)
|
||||
canvas_dpi = Resolution(300.0, 300.0).take_min(
|
||||
[get_canvas_square_dpi(page_context)]
|
||||
)
|
||||
page_dpi = Resolution(300.0, 300.0).take_min([get_page_square_dpi(page_context)])
|
||||
page_context.plugin_manager.hook.rasterize_pdf_page(
|
||||
input_file=input_file,
|
||||
output_file=output_file,
|
||||
@@ -433,6 +433,37 @@ def get_orientation_correction(preview: Path, page_context: PageContext) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def calculate_image_dpi(page_context: PageContext) -> Resolution:
|
||||
pageinfo = page_context.pageinfo
|
||||
dpi_profile = pageinfo.page_dpi_profile()
|
||||
if dpi_profile and dpi_profile.average_to_max_dpi_ratio < 0.8:
|
||||
image_dpi = Resolution(dpi_profile.weighted_dpi, dpi_profile.weighted_dpi)
|
||||
else:
|
||||
image_dpi = pageinfo.dpi
|
||||
return image_dpi
|
||||
|
||||
|
||||
def calculate_raster_dpi(page_context: PageContext):
|
||||
"""Calculate the DPI for rasterization."""
|
||||
# Produce the page image with square resolution or else deskew and OCR
|
||||
# will not work properly.
|
||||
image_dpi = calculate_image_dpi(page_context)
|
||||
dpi_profile = page_context.pageinfo.page_dpi_profile()
|
||||
canvas_dpi = get_canvas_square_dpi(page_context, image_dpi)
|
||||
page_dpi = get_page_square_dpi(page_context, image_dpi)
|
||||
if dpi_profile and dpi_profile.average_to_max_dpi_ratio < 0.8:
|
||||
log.warning(
|
||||
"Weight average image DPI is %0.1f, max DPI is %0.1f. "
|
||||
"The discrepancy may indicate a high detail region on this page, "
|
||||
"but could also indicate a problem with the input PDF file. "
|
||||
"Page image will be rendered at %0.1f DPI.",
|
||||
dpi_profile.weighted_dpi,
|
||||
dpi_profile.max_dpi,
|
||||
canvas_dpi.to_scalar(),
|
||||
)
|
||||
return canvas_dpi, page_dpi
|
||||
|
||||
|
||||
def rasterize(
|
||||
input_file: Path,
|
||||
page_context: PageContext,
|
||||
@@ -484,10 +515,7 @@ def rasterize(
|
||||
|
||||
log.debug(f"Rasterize with {device}, rotation {correction}")
|
||||
|
||||
# Produce the page image with square resolution or else deskew and OCR
|
||||
# will not work properly.
|
||||
canvas_dpi = get_canvas_square_dpi(pageinfo, page_context.options)
|
||||
page_dpi = get_page_square_dpi(pageinfo, page_context.options)
|
||||
canvas_dpi, page_dpi = calculate_raster_dpi(page_context)
|
||||
|
||||
page_context.plugin_manager.hook.rasterize_pdf_page(
|
||||
input_file=input_file,
|
||||
@@ -524,7 +552,7 @@ def preprocess_deskew(input_file: Path, page_context: PageContext) -> Path:
|
||||
Path: The path to the deskewed image file.
|
||||
"""
|
||||
output_file = page_context.get_path('pp_deskew.png')
|
||||
dpi = get_page_square_dpi(page_context.pageinfo, page_context.options)
|
||||
dpi = get_page_square_dpi(page_context, calculate_image_dpi(page_context))
|
||||
|
||||
ocr_engine = page_context.plugin_manager.hook.get_ocr_engine()
|
||||
deskew_angle_degrees = ocr_engine.get_deskew(input_file, page_context.options)
|
||||
@@ -544,11 +572,11 @@ def preprocess_deskew(input_file: Path, page_context: PageContext) -> Path:
|
||||
|
||||
def preprocess_clean(input_file: Path, page_context: PageContext) -> Path:
|
||||
output_file = page_context.get_path('pp_clean.png')
|
||||
dpi = get_page_square_dpi(page_context.pageinfo, page_context.options)
|
||||
dpi = get_page_square_dpi(page_context, calculate_image_dpi(page_context))
|
||||
return unpaper.clean(
|
||||
input_file,
|
||||
output_file,
|
||||
dpi=dpi.x,
|
||||
dpi=dpi.to_scalar(),
|
||||
unpaper_args=page_context.options.unpaper_args,
|
||||
)
|
||||
|
||||
@@ -645,7 +673,7 @@ def create_visible_page_jpg(image: Path, page_context: PageContext) -> Path:
|
||||
dpi = Resolution(*im.info['dpi'])
|
||||
else:
|
||||
# Fallback to page-implied DPI
|
||||
dpi = get_page_square_dpi(page_context.pageinfo, page_context.options)
|
||||
dpi = get_page_square_dpi(page_context, calculate_image_dpi(page_context))
|
||||
|
||||
# Pillow requires integer DPI
|
||||
im.save(output_file, format='JPEG', dpi=dpi.to_int())
|
||||
@@ -688,7 +716,7 @@ def create_pdf_page_from_image(
|
||||
def render_hocr_page(hocr: Path, page_context: PageContext) -> Path:
|
||||
options = page_context.options
|
||||
output_file = page_context.get_path('ocr_hocr.pdf')
|
||||
dpi = get_page_square_dpi(page_context.pageinfo, options)
|
||||
dpi = get_page_square_dpi(page_context, calculate_image_dpi(page_context))
|
||||
debug_mode = options.pdf_renderer == 'hocrdebug'
|
||||
|
||||
hocrtransform = HocrTransform(hocr_filename=hocr, dpi=dpi.x) # square
|
||||
@@ -751,6 +779,14 @@ def get_docinfo(base_pdf: pikepdf.Pdf, context: PdfContext) -> dict[str, str]:
|
||||
|
||||
|
||||
def generate_postscript_stub(context: PdfContext) -> Path:
|
||||
"""Generates a PostScript file stub for the given PDF context.
|
||||
|
||||
Args:
|
||||
context: The PDF context to generate the PostScript file stub for.
|
||||
|
||||
Returns:
|
||||
Path: The path to the generated PostScript file stub.
|
||||
"""
|
||||
output_file = context.get_path('pdfa.ps')
|
||||
generate_pdfa_ps(output_file)
|
||||
return output_file
|
||||
@@ -767,21 +803,8 @@ def convert_to_pdfa(input_pdf: Path, input_ps_stub: Path, context: PdfContext) -
|
||||
# NULs in DocumentInfo seem to be common since older Acrobats included them.
|
||||
# pikepdf can deal with this, but we make the world a better place by
|
||||
# stamping them out as soon as possible.
|
||||
modified = False
|
||||
with pikepdf.open(input_pdf) as pdf_file:
|
||||
try:
|
||||
len(pdf_file.docinfo)
|
||||
except TypeError:
|
||||
log.error(
|
||||
"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 _repair_docinfo_nuls(pdf_file):
|
||||
pdf_file.save(fix_docinfo_file)
|
||||
else:
|
||||
safe_symlink(input_pdf, fix_docinfo_file)
|
||||
@@ -791,7 +814,7 @@ def convert_to_pdfa(input_pdf: Path, input_ps_stub: Path, context: PdfContext) -
|
||||
pdf_pages=[fix_docinfo_file],
|
||||
pdfmark=input_ps_stub,
|
||||
output_file=output_file,
|
||||
compression=options.pdfa_image_compression,
|
||||
context=context,
|
||||
pdfa_part=options.output_type[-1], # is pdfa-1, pdfa-2, or pdfa-3
|
||||
progressbar_class=(
|
||||
context.plugin_manager.hook.get_progressbar_class()
|
||||
@@ -804,6 +827,25 @@ def convert_to_pdfa(input_pdf: Path, input_ps_stub: Path, context: PdfContext) -
|
||||
return output_file
|
||||
|
||||
|
||||
def _repair_docinfo_nuls(pdf):
|
||||
"""If the DocumentInfo block contains NUL characters, remove them.
|
||||
|
||||
If the DocumentInfo block is malformed, log an error and continue.
|
||||
"""
|
||||
modified = False
|
||||
try:
|
||||
if not isinstance(pdf.docinfo, pikepdf.Dictionary):
|
||||
raise TypeError("DocumentInfo is not a dictionary")
|
||||
for k, v in pdf.docinfo.items():
|
||||
if isinstance(v, str) and b'\x00' in bytes(v):
|
||||
pdf.docinfo[k] = bytes(v).replace(b'\x00', b'')
|
||||
modified = True
|
||||
except TypeError:
|
||||
# TypeError can also be raised if dictionary items are unexpected types
|
||||
log.error("File contains a malformed DocumentInfo block - continuing anyway.")
|
||||
return modified
|
||||
|
||||
|
||||
def should_linearize(working_file: Path, context: PdfContext) -> bool:
|
||||
filesize = os.stat(working_file).st_size
|
||||
if filesize > (context.options.fast_web_view * 1_000_000):
|
||||
@@ -853,7 +895,9 @@ def metadata_fixup(working_file: Path, context: PdfContext) -> Path:
|
||||
with pikepdf.open(context.origin) as original, pikepdf.open(working_file) as pdf:
|
||||
docinfo = get_docinfo(original, context)
|
||||
with pdf.open_metadata() as meta_pdf:
|
||||
meta_pdf.load_from_docinfo(docinfo, delete_missing=False, raise_failure=False)
|
||||
meta_pdf.load_from_docinfo(
|
||||
docinfo, delete_missing=False, raise_failure=False
|
||||
)
|
||||
# If xmp:CreateDate is missing, set it to the modify date to
|
||||
# ensure consistency with Ghostscript.
|
||||
if 'xmp:CreateDate' not in meta_pdf:
|
||||
@@ -907,6 +951,17 @@ def metadata_fixup(working_file: Path, context: PdfContext) -> Path:
|
||||
def _file_size_ratio(
|
||||
input_file: Path, output_file: Path
|
||||
) -> tuple[float | None, float | None]:
|
||||
"""Calculate ratio of input to output file sizes and percentage savings.
|
||||
|
||||
Args:
|
||||
input_file (Path): The path to the input file.
|
||||
output_file (Path): The path to the output file.
|
||||
|
||||
Returns:
|
||||
tuple[float | None, float | None]: A tuple containing the file size
|
||||
ratio and the percentage savings achieved by the output file size
|
||||
compared to the input file size.
|
||||
"""
|
||||
input_size = input_file.stat().st_size
|
||||
output_size = output_file.stat().st_size
|
||||
if output_size == 0:
|
||||
@@ -937,7 +992,20 @@ def optimize_pdf(
|
||||
return output_pdf, messages
|
||||
|
||||
|
||||
def enumerate_compress_ranges(iterable):
|
||||
def enumerate_compress_ranges(
|
||||
iterable: Iterable,
|
||||
) -> Iterator[tuple[tuple[int, int], Any]]:
|
||||
"""Enumerate the ranges of non-empty elements in an iterable.
|
||||
|
||||
Compresses consecutive ranges of length 1 into single elements.
|
||||
|
||||
Args:
|
||||
iterable: An iterable of elements to enumerate.
|
||||
|
||||
Yields:
|
||||
A tuple containing a range of indices and the corresponding element.
|
||||
If the element is None, the range represents a skipped range of indices.
|
||||
"""
|
||||
skipped_from, index = None, None
|
||||
for index, txt_file in enumerate(iterable):
|
||||
index += 1
|
||||
@@ -981,6 +1049,16 @@ def merge_sidecars(txt_files: Iterable[Path | None], context: PdfContext) -> Pat
|
||||
def copy_final(
|
||||
input_file: Path, output_file: str | Path | BinaryIO, _context: PdfContext
|
||||
) -> None:
|
||||
"""Copy the final temporary file to the output destination.
|
||||
|
||||
Args:
|
||||
input_file (Path): The input file to copy.
|
||||
output_file (str | Path | BinaryIO): The output file to copy to.
|
||||
_context (PdfContext): The PDF context.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
log.debug('%s -> %s', input_file, output_file)
|
||||
with input_file.open('rb') as input_stream:
|
||||
if output_file == '-':
|
||||
|
||||
@@ -100,7 +100,9 @@ class OcrmypdfPluginManager(pluggy.PluginManager):
|
||||
self.register(module)
|
||||
|
||||
|
||||
def get_plugin_manager(plugins: list[str | Path], builtins=True):
|
||||
def get_plugin_manager(
|
||||
plugins: list[str | Path], builtins=True
|
||||
) -> OcrmypdfPluginManager:
|
||||
return OcrmypdfPluginManager(
|
||||
project_name='ocrmypdf',
|
||||
plugins=plugins,
|
||||
|
||||
@@ -43,10 +43,11 @@ log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def check_platform() -> None:
|
||||
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."
|
||||
if sys.maxsize <= 2**32: # pragma: no cover
|
||||
log.warning(
|
||||
"You are running OCRmyPDF in a 32-bit (x86) Python interpreter. "
|
||||
"This is not supported. 32-bit does not have enough address space "
|
||||
"to process large files. "
|
||||
"Please use a 64-bit (x86-64) version of Python."
|
||||
)
|
||||
|
||||
@@ -204,16 +205,6 @@ def check_options_ocr_behavior(options: Namespace) -> None:
|
||||
options.pages = _pages_from_ranges(options.pages)
|
||||
|
||||
|
||||
def check_options_advanced(options: Namespace) -> None:
|
||||
if options.pdfa_image_compression != 'auto' and not options.output_type.startswith(
|
||||
'pdfa'
|
||||
):
|
||||
log.warning(
|
||||
"--pdfa-image-compression argument only applies when "
|
||||
"--output-type is one of 'pdfa', 'pdfa-1', or 'pdfa-2'"
|
||||
)
|
||||
|
||||
|
||||
def check_options_metadata(options: Namespace) -> None:
|
||||
docinfo = [options.title, options.author, options.keywords, options.subject]
|
||||
for s in (m for m in docinfo if m):
|
||||
@@ -240,7 +231,6 @@ def _check_plugin_invariant_options(options: Namespace) -> None:
|
||||
check_options_sidecar(options)
|
||||
check_options_preprocessing(options)
|
||||
check_options_ocr_behavior(options)
|
||||
check_options_advanced(options)
|
||||
check_options_pillow(options)
|
||||
|
||||
|
||||
|
||||
+19
-14
@@ -9,16 +9,16 @@ import logging
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
from argparse import Namespace
|
||||
from enum import IntEnum
|
||||
from io import IOBase
|
||||
from pathlib import Path
|
||||
from typing import AnyStr, BinaryIO, Iterable, Union
|
||||
from warnings import warn
|
||||
|
||||
import coloredlogs
|
||||
from humanfriendly.terminal import enable_ansi_support
|
||||
import pluggy
|
||||
|
||||
from ocrmypdf._logging import PageNumberFilter, TqdmConsole
|
||||
from ocrmypdf._logging import PageNumberFilter
|
||||
from ocrmypdf._plugin_manager import get_plugin_manager
|
||||
from ocrmypdf._sync import run_pipeline
|
||||
from ocrmypdf._validation import check_options
|
||||
@@ -46,7 +46,7 @@ def configure_logging(
|
||||
*,
|
||||
progress_bar_friendly: bool = True,
|
||||
manage_root_logger: bool = False,
|
||||
plugin_manager=None,
|
||||
plugin_manager: pluggy.PluginManager | None = None,
|
||||
):
|
||||
"""Set up logging.
|
||||
|
||||
@@ -112,14 +112,7 @@ def configure_logging(
|
||||
else:
|
||||
fmt = '%(pageno)s%(message)s'
|
||||
|
||||
use_colors = progress_bar_friendly
|
||||
formatter = None
|
||||
if use_colors:
|
||||
use_colors = enable_ansi_support()
|
||||
if use_colors:
|
||||
use_colors = coloredlogs.terminal_supports_colors()
|
||||
if use_colors:
|
||||
formatter = coloredlogs.ColoredFormatter(fmt=fmt)
|
||||
|
||||
if not formatter:
|
||||
formatter = logging.Formatter(fmt=fmt)
|
||||
@@ -141,8 +134,21 @@ def configure_logging(
|
||||
|
||||
def create_options(
|
||||
*, input_file: PathOrIO, output_file: PathOrIO, parser: ArgumentParser, **kwargs
|
||||
):
|
||||
"""Construct an options object from the input/output files and keyword arguments."""
|
||||
) -> Namespace:
|
||||
"""Construct an options object from the input/output files and keyword arguments.
|
||||
|
||||
Args:
|
||||
input_file: Input file path or file object.
|
||||
output_file: Output file path or file object.
|
||||
parser: ArgumentParser object.
|
||||
**kwargs: Keyword arguments.
|
||||
|
||||
Returns:
|
||||
argparse.Namespace: A Namespace object containing the parsed arguments.
|
||||
|
||||
Raises:
|
||||
TypeError: If the type of a keyword argument is not supported.
|
||||
"""
|
||||
cmdline = []
|
||||
deferred = []
|
||||
|
||||
@@ -349,7 +355,6 @@ def ocr( # noqa: ruff: disable=D417
|
||||
|
||||
__all__ = [
|
||||
'PageNumberFilter',
|
||||
'TqdmConsole',
|
||||
'Verbosity',
|
||||
'check_options',
|
||||
'configure_logging',
|
||||
|
||||
@@ -16,10 +16,10 @@ from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_compl
|
||||
from contextlib import suppress
|
||||
from typing import Callable, Iterable, Type, Union
|
||||
|
||||
from tqdm import tqdm
|
||||
from rich.console import Console as RichConsole
|
||||
|
||||
from ocrmypdf import Executor, hookimpl
|
||||
from ocrmypdf._logging import TqdmConsole
|
||||
from ocrmypdf._logging import RichLoggingHandler, RichTqdmProgressAdapter
|
||||
from ocrmypdf.exceptions import InputFileError
|
||||
from ocrmypdf.helpers import remove_all_log_handlers
|
||||
|
||||
@@ -68,7 +68,7 @@ def process_init(q: Queue, user_init: UserInit, loglevel) -> None:
|
||||
# Windows and Cygwin do not have pthread_sigmask or SIGBUS
|
||||
signal.signal(signal.SIGBUS, process_sigbus)
|
||||
|
||||
# Remove any log handlers that belong to the parent process
|
||||
# Remove any log handlers inherited from the parent process
|
||||
root = logging.getLogger()
|
||||
remove_all_log_handlers(root)
|
||||
|
||||
@@ -95,15 +95,6 @@ def thread_init(q: Queue, user_init: UserInit, loglevel) -> None:
|
||||
class StandardExecutor(Executor):
|
||||
"""Standard OCRmyPDF concurrent task executor."""
|
||||
|
||||
def _cancel_futures_kwargs(self):
|
||||
"""Shim older Pythons that do not have Executor.shutdown(...cancel_futures=).
|
||||
|
||||
Remove this code when support for Python 3.8 is dropped.
|
||||
"""
|
||||
if sys.version_info[:2] < (3, 9):
|
||||
return {}
|
||||
return dict(cancel_futures=True)
|
||||
|
||||
def _execute(
|
||||
self,
|
||||
*,
|
||||
@@ -142,7 +133,7 @@ class StandardExecutor(Executor):
|
||||
task_finished(result, pbar)
|
||||
except KeyboardInterrupt:
|
||||
# Terminate pool so we exit instantly
|
||||
executor.shutdown(wait=False, **self._cancel_futures_kwargs())
|
||||
executor.shutdown(wait=False, cancel_futures=True)
|
||||
raise
|
||||
except Exception:
|
||||
if not os.environ.get("PYTEST_CURRENT_TEST", ""):
|
||||
@@ -151,7 +142,7 @@ class StandardExecutor(Executor):
|
||||
# results will be discard. But if the condition above is True,
|
||||
# then we are running in pytest, and we want everything to exit
|
||||
# as cleanly as possible so that we get good error messages.
|
||||
executor.shutdown(wait=False, **self._cancel_futures_kwargs())
|
||||
executor.shutdown(wait=False, cancel_futures=True)
|
||||
raise
|
||||
finally:
|
||||
# Terminate log listener
|
||||
@@ -168,13 +159,20 @@ def get_executor(progressbar_class):
|
||||
return StandardExecutor(pbar_class=progressbar_class)
|
||||
|
||||
|
||||
RICH_CONSOLE = RichConsole(stderr=True)
|
||||
|
||||
|
||||
@hookimpl
|
||||
def get_progressbar_class():
|
||||
"""Return the default progress bar class."""
|
||||
return tqdm
|
||||
|
||||
def partial_RichTqdmProgressAdapter(*args, **kwargs):
|
||||
return RichTqdmProgressAdapter(*args, **kwargs, console=RICH_CONSOLE)
|
||||
|
||||
return partial_RichTqdmProgressAdapter
|
||||
|
||||
|
||||
@hookimpl
|
||||
def get_logging_console():
|
||||
"""Return the default logging console handler."""
|
||||
return logging.StreamHandler(stream=TqdmConsole(sys.stderr))
|
||||
return RichLoggingHandler(console=RICH_CONSOLE)
|
||||
|
||||
@@ -13,6 +13,37 @@ from ocrmypdf.subprocess import check_external_program
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Currently all blacklisted versions are lower than 9.55, so none need to
|
||||
# be added here. If a future version is blacklisted, add it here.
|
||||
BLACKLISTED_GS_VERSIONS: frozenset[str] = frozenset()
|
||||
|
||||
|
||||
@hookimpl
|
||||
def add_options(parser):
|
||||
gs = parser.add_argument_group("Ghostscript", "Advanced control of Ghostscript")
|
||||
gs.add_argument(
|
||||
'--color-conversion-strategy',
|
||||
action='store',
|
||||
type=str,
|
||||
metavar='STRATEGY',
|
||||
choices=ghostscript.COLOR_CONVERSION_STRATEGIES,
|
||||
default='LeaveColorUnchanged',
|
||||
help="Set Ghostscript color conversion strategy",
|
||||
)
|
||||
gs.add_argument(
|
||||
'--pdfa-image-compression',
|
||||
choices=['auto', 'jpeg', 'lossless'],
|
||||
default='auto',
|
||||
help="Specify how to compress images in the output PDF/A. 'auto' lets "
|
||||
"OCRmyPDF decide. 'jpeg' changes all grayscale and color images to "
|
||||
"JPEG compression. 'lossless' uses PNG-style lossless compression "
|
||||
"for all images. Monochrome images are always compressed using a "
|
||||
"lossless codec. Compression settings "
|
||||
"are applied to all pages, including those for which OCR was "
|
||||
"skipped. Not supported for --output-type=pdf ; that setting "
|
||||
"preserves the original compression of all images.",
|
||||
)
|
||||
|
||||
|
||||
@hookimpl
|
||||
def check_options(options):
|
||||
@@ -21,10 +52,10 @@ def check_options(options):
|
||||
program='gs',
|
||||
package='ghostscript',
|
||||
version_checker=ghostscript.version,
|
||||
need_version='9.50', # Ubuntu 20.04's version
|
||||
need_version='9.55', # Ubuntu 22.04's version
|
||||
)
|
||||
gs_version = ghostscript.version()
|
||||
if gs_version in ('9.51',):
|
||||
if gs_version in BLACKLISTED_GS_VERSIONS:
|
||||
raise MissingDependencyError(
|
||||
f"Ghostscript {gs_version} contains serious regressions and is not "
|
||||
"supported. Please upgrade to a newer version, or downgrade to the "
|
||||
@@ -33,6 +64,17 @@ def check_options(options):
|
||||
|
||||
if options.output_type == 'pdfa':
|
||||
options.output_type = 'pdfa-2'
|
||||
if options.color_conversion_strategy not in ghostscript.COLOR_CONVERSION_STRATEGIES:
|
||||
raise ValueError(
|
||||
f"Invalid color conversion strategy: {options.color_conversion_strategy}"
|
||||
)
|
||||
if options.pdfa_image_compression != 'auto' and not options.output_type.startswith(
|
||||
'pdfa'
|
||||
):
|
||||
log.warning(
|
||||
"--pdfa-image-compression argument only applies when "
|
||||
"--output-type is one of 'pdfa', 'pdfa-1', or 'pdfa-2'"
|
||||
)
|
||||
|
||||
|
||||
@hookimpl
|
||||
@@ -67,7 +109,7 @@ def generate_pdfa(
|
||||
pdf_pages,
|
||||
pdfmark,
|
||||
output_file,
|
||||
compression,
|
||||
context,
|
||||
pdf_version,
|
||||
pdfa_part,
|
||||
progressbar_class,
|
||||
@@ -77,7 +119,8 @@ def generate_pdfa(
|
||||
ghostscript.generate_pdfa(
|
||||
pdf_pages=[*pdf_pages, pdfmark],
|
||||
output_file=output_file,
|
||||
compression=compression,
|
||||
compression=context.options.pdfa_image_compression,
|
||||
color_conversion_strategy=context.options.color_conversion_strategy,
|
||||
pdf_version=pdf_version,
|
||||
pdfa_part=pdfa_part,
|
||||
progressbar_class=progressbar_class,
|
||||
|
||||
@@ -86,6 +86,16 @@ def add_options(parser):
|
||||
# Adjust number of pages to consider at once for JBIG2 compression
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
optimizing.add_argument(
|
||||
'--jbig2-threshold',
|
||||
type=numeric(float, 0.4, 0.9),
|
||||
default=0.85,
|
||||
metavar='T',
|
||||
help=(
|
||||
"Adjust JBIG2 symbol code classification threshold "
|
||||
"(default 0.85), range 0.4 to 0.9."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@hookimpl
|
||||
@@ -95,7 +105,7 @@ def check_options(options):
|
||||
program='pngquant',
|
||||
package='pngquant',
|
||||
version_checker=pngquant.version,
|
||||
need_version='2.0.1',
|
||||
need_version='2.12.2',
|
||||
required_for='--optimize {2,3}',
|
||||
)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ def add_options(parser):
|
||||
action='append',
|
||||
metavar='CFG',
|
||||
default=[],
|
||||
help="Additional Tesseract configuration files -- see documentation",
|
||||
help="Additional Tesseract configuration files -- see documentation.",
|
||||
)
|
||||
tess.add_argument(
|
||||
'--tesseract-pagesegmode',
|
||||
@@ -38,7 +38,7 @@ def add_options(parser):
|
||||
type=int,
|
||||
metavar='PSM',
|
||||
choices=range(0, 14),
|
||||
help="Set Tesseract page segmentation mode (see tesseract --help)",
|
||||
help="Set Tesseract page segmentation mode (see tesseract --help).",
|
||||
)
|
||||
tess.add_argument(
|
||||
'--tesseract-oem',
|
||||
@@ -75,7 +75,10 @@ def add_options(parser):
|
||||
metavar='SECONDS',
|
||||
help=(
|
||||
"Give up on OCR after the timeout, but copy the preprocessed page "
|
||||
"into the final output."
|
||||
"into the final output. This timeout is only used when using Tesseract "
|
||||
"for OCR. When Tesseract is used for other operations such as "
|
||||
"deskewing and orientation, the timeout is controlled by "
|
||||
"--tesseract-non-ocr-timeout."
|
||||
),
|
||||
)
|
||||
tess.add_argument(
|
||||
@@ -137,7 +140,7 @@ def check_options(options):
|
||||
program='tesseract',
|
||||
package={'linux': 'tesseract-ocr'},
|
||||
version_checker=tesseract.version,
|
||||
need_version='4.1.1', # Ubuntu 20.04 version
|
||||
need_version='4.1.1', # Ubuntu 22.04 version (also 20.04)
|
||||
version_parser=tesseract.TesseractVersion,
|
||||
)
|
||||
|
||||
@@ -175,6 +178,15 @@ def validate(pdfinfo, options):
|
||||
tess_threads = int(os.environ['OMP_THREAD_LIMIT'])
|
||||
log.debug("Using Tesseract OpenMP thread limit %d", tess_threads)
|
||||
|
||||
if (
|
||||
options.tesseract_downsample_above != 32767
|
||||
and not options.tesseract_downsample_large_images
|
||||
):
|
||||
log.warning(
|
||||
"The --tesseract-downsample-above argument will have no effect unless "
|
||||
"--tesseract-downsample-large-images is also given."
|
||||
)
|
||||
|
||||
|
||||
@hookimpl
|
||||
def filter_ocr_image(page: PageContext, image: Image.Image) -> Image.Image:
|
||||
@@ -200,7 +212,7 @@ class TesseractOcrEngine(OcrEngine):
|
||||
|
||||
@staticmethod
|
||||
def version():
|
||||
return tesseract.version()
|
||||
return str(tesseract.version())
|
||||
|
||||
@staticmethod
|
||||
def creator_tag(options):
|
||||
|
||||
+15
-15
@@ -15,7 +15,11 @@ T = TypeVar('T', int, float)
|
||||
|
||||
|
||||
def numeric(basetype: Callable[[Any], T], min_: T | None = None, max_: T | None = None):
|
||||
"""Validator for numeric params."""
|
||||
"""Validator for numeric command line parameters.
|
||||
|
||||
Stipulates that the value must be of type basetype (typically int or float), and
|
||||
optionally, within the range [min_, max_].
|
||||
"""
|
||||
min_ = basetype(min_) if min_ is not None else None
|
||||
max_ = basetype(max_) if max_ is not None else None
|
||||
|
||||
@@ -177,7 +181,9 @@ Online documentation is located at:
|
||||
'--image-dpi',
|
||||
metavar='DPI',
|
||||
type=int,
|
||||
help="For input image instead of PDF, use this DPI instead of file's.",
|
||||
help="When the input file is an image, not a PDF, use this DPI instead "
|
||||
"of the DPI claimed by the input file. If the input does not claim a "
|
||||
"sensible DPI, this option will be required.",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--output-type',
|
||||
@@ -359,6 +365,13 @@ Online documentation is located at:
|
||||
help="Skip OCR on pages larger than the specified amount of megapixels, "
|
||||
"but include skipped pages in final output",
|
||||
)
|
||||
ocrsettings.add_argument(
|
||||
'--invalidate-digital-signatures',
|
||||
action='store_true',
|
||||
help="Normally, OCRmyPDF will refuse to OCR a PDF that has a digital "
|
||||
"signature. This option allows OCR to proceed, but the digital signature "
|
||||
"will be invalidated.",
|
||||
)
|
||||
|
||||
advanced = parser.add_argument_group(
|
||||
"Advanced", "Advanced options to control OCRmyPDF"
|
||||
@@ -395,19 +408,6 @@ Online documentation is located at:
|
||||
help="Only rotate pages when confidence is above this value (arbitrary "
|
||||
"units reported by tesseract)",
|
||||
)
|
||||
advanced.add_argument(
|
||||
'--pdfa-image-compression',
|
||||
choices=['auto', 'jpeg', 'lossless'],
|
||||
default='auto',
|
||||
help="Specify how to compress images in the output PDF/A. 'auto' lets "
|
||||
"OCRmyPDF decide. 'jpeg' changes all grayscale and color images to "
|
||||
"JPEG compression. 'lossless' uses PNG-style lossless compression "
|
||||
"for all images. Monochrome images are always compressed using a "
|
||||
"lossless codec. Compression settings "
|
||||
"are applied to all pages, including those for which OCR was "
|
||||
"skipped. Not supported for --output-type=pdf ; that setting "
|
||||
"preserves the original compression of all images.",
|
||||
)
|
||||
advanced.add_argument(
|
||||
'--fast-web-view',
|
||||
type=numeric(float, 0),
|
||||
|
||||
@@ -108,6 +108,18 @@ class EncryptedPdfError(ExitCodeException):
|
||||
)
|
||||
|
||||
|
||||
class DigitalSignatureError(ExitCodeException):
|
||||
"""PDF has a digital signature."""
|
||||
|
||||
exit_code = ExitCode.input_file
|
||||
message = dedent(
|
||||
"""\
|
||||
Input PDF has a digital signature. OCR would alter the document,
|
||||
invalidating the signature.
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
class TesseractConfigError(ExitCodeException):
|
||||
"""Tesseract config can't be parsed."""
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ worker communicates only with the main process.
|
||||
|
||||
This is not without drawbacks. If the tasks are not "even" in size, which cannot
|
||||
be guaranteed, some workers may end up with too much work while others are idle.
|
||||
It is less efficient than the standard implementation, so not th edefault.
|
||||
It is less efficient than the standard implementation, so not the default.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
+56
-39
@@ -12,28 +12,30 @@ import shutil
|
||||
import warnings
|
||||
from collections.abc import Iterable
|
||||
from contextlib import suppress
|
||||
from decimal import Decimal
|
||||
from io import StringIO
|
||||
from math import isclose, isfinite
|
||||
from pathlib import Path
|
||||
from typing import Any, Generic, Sequence, SupportsFloat, SupportsRound, TypeVar
|
||||
from statistics import harmonic_mean
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Generic,
|
||||
Sequence,
|
||||
SupportsFloat,
|
||||
SupportsRound,
|
||||
TypeVar,
|
||||
)
|
||||
|
||||
import img2pdf
|
||||
import pikepdf
|
||||
from packaging.version import Version
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
if Version(img2pdf.__version__) < Version('0.4.0'):
|
||||
IMG2PDF_KWARGS = dict(without_pdfw=True)
|
||||
elif Version(img2pdf.__version__) < Version('0.4.3'):
|
||||
IMG2PDF_KWARGS = dict(engine=img2pdf.Engine.pikepdf)
|
||||
else:
|
||||
IMG2PDF_KWARGS = dict(
|
||||
engine=img2pdf.Engine.pikepdf, rotation=img2pdf.Rotation.ifvalid
|
||||
)
|
||||
IMG2PDF_KWARGS = dict(engine=img2pdf.Engine.pikepdf, rotation=img2pdf.Rotation.ifvalid)
|
||||
|
||||
|
||||
T = TypeVar('T', bound=SupportsRound[Any])
|
||||
T = TypeVar('T', float, int, Decimal)
|
||||
|
||||
|
||||
class Resolution(Generic[T]):
|
||||
@@ -77,21 +79,40 @@ class Resolution(Generic[T]):
|
||||
@property
|
||||
def is_finite(self) -> bool:
|
||||
"""True if both x and y are finite numbers."""
|
||||
if isinstance(self.x, SupportsFloat) and isinstance(self.y, SupportsFloat):
|
||||
return isfinite(self.x) and isfinite(self.y)
|
||||
return True
|
||||
return isfinite(self.x) and isfinite(self.y)
|
||||
|
||||
def to_scalar(self) -> float:
|
||||
"""Return the harmonic mean of x and y as a 1D approximation.
|
||||
|
||||
In most cases, Resolution is 2D, but typically it is "square" (x == y) and
|
||||
can be approximated as a single number. When not square, the harmonic mean
|
||||
is used to approximate the 2D resolution as a single number.
|
||||
"""
|
||||
return harmonic_mean([float(self.x), float(self.y)])
|
||||
|
||||
def _take_minmax(
|
||||
self, vals: Iterable[Any], yvals: Iterable[Any] | None, cmp: Callable
|
||||
) -> Resolution:
|
||||
"""Return a new Resolution object with the maximum resolution of inputs."""
|
||||
if yvals is not None:
|
||||
return Resolution(cmp(self.x, *vals), cmp(self.y, *yvals))
|
||||
cmp_x, cmp_y = self.x, self.y
|
||||
for x, y in vals:
|
||||
cmp_x = cmp(x, cmp_x)
|
||||
cmp_y = cmp(y, cmp_y)
|
||||
return Resolution(cmp_x, cmp_y)
|
||||
|
||||
def take_max(
|
||||
self, vals: Iterable[Any], yvals: Iterable[Any] | None = None
|
||||
) -> Resolution:
|
||||
"""Return a new Resolution object with the maximum resolution of inputs."""
|
||||
if yvals is not None:
|
||||
return Resolution(max(self.x, *vals), max(self.y, *yvals))
|
||||
max_x, max_y = self.x, self.y
|
||||
for x, y in vals:
|
||||
max_x = max(x, max_x)
|
||||
max_y = max(y, max_y)
|
||||
return Resolution(max_x, max_y)
|
||||
return self._take_minmax(vals, yvals, max)
|
||||
|
||||
def take_min(
|
||||
self, vals: Iterable[Any], yvals: Iterable[Any] | None = None
|
||||
) -> Resolution:
|
||||
"""Return a new Resolution object with the minimum resolution of inputs."""
|
||||
return self._take_minmax(vals, yvals, min)
|
||||
|
||||
def flip_axis(self) -> Resolution[T]:
|
||||
"""Return a new Resolution object with x and y swapped."""
|
||||
@@ -103,11 +124,11 @@ class Resolution(Generic[T]):
|
||||
|
||||
def __str__(self):
|
||||
"""Return a string representation of the resolution."""
|
||||
return f"{self.x:f}x{self.y:f}"
|
||||
return f"{self.x:f}×{self.y:f}"
|
||||
|
||||
def __repr__(self): # pragma: no cover
|
||||
"""Return a repr() of the resolution."""
|
||||
return f"Resolution({self.x}x{self.y} dpi)"
|
||||
return f"Resolution({self.x!r}, {self.y!r})"
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Return True if the resolution is equal to another resolution."""
|
||||
@@ -122,7 +143,7 @@ class NeverRaise(Exception):
|
||||
"""An exception that is never raised."""
|
||||
|
||||
|
||||
def safe_symlink(input_file: os.PathLike, soft_link_name: os.PathLike):
|
||||
def safe_symlink(input_file: os.PathLike, soft_link_name: os.PathLike) -> None:
|
||||
"""Create a symbolic link at ``soft_link_name``, which references ``input_file``.
|
||||
|
||||
Think of this as copying ``input_file`` to ``soft_link_name`` with less overhead.
|
||||
@@ -137,7 +158,7 @@ def safe_symlink(input_file: os.PathLike, soft_link_name: os.PathLike):
|
||||
# Guard against soft linking to oneself
|
||||
if input_file == soft_link_name:
|
||||
log.warning(
|
||||
"No symbolic link created. You are using the original data directory "
|
||||
"No symbolic link created. You are using the original data directory "
|
||||
"as the working directory."
|
||||
)
|
||||
return
|
||||
@@ -163,7 +184,7 @@ def safe_symlink(input_file: os.PathLike, soft_link_name: os.PathLike):
|
||||
os.symlink(os.path.abspath(input_file), soft_link_name)
|
||||
|
||||
|
||||
def samefile(file1: os.PathLike, file2: os.PathLike):
|
||||
def samefile(file1: os.PathLike, file2: os.PathLike) -> bool:
|
||||
"""Return True if two files are the same file.
|
||||
|
||||
Attempts to account for different relative paths to the same file.
|
||||
@@ -282,12 +303,12 @@ def check_pdf(input_file: Path) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def clamp(n, smallest, largest): # mypy doesn't understand types for this
|
||||
def clamp(n: T, smallest: T, largest: T) -> T:
|
||||
"""Clamps the value of ``n`` to between ``smallest`` and ``largest``."""
|
||||
return max(smallest, min(n, largest))
|
||||
|
||||
|
||||
def remove_all_log_handlers(logger):
|
||||
def remove_all_log_handlers(logger: logging.Logger) -> None:
|
||||
"""Remove all log handlers, usually used in a child process.
|
||||
|
||||
The child process inherits the log handlers from the parent process when
|
||||
@@ -300,15 +321,11 @@ def remove_all_log_handlers(logger):
|
||||
handler.close() # To ensure handlers with opened resources are released
|
||||
|
||||
|
||||
def pikepdf_enable_mmap():
|
||||
def pikepdf_enable_mmap() -> None:
|
||||
"""Enable pikepdf mmap."""
|
||||
# try:
|
||||
# if pikepdf._qpdf.set_access_default_mmap(True):
|
||||
# log.debug("pikepdf mmap enabled")
|
||||
# except AttributeError:
|
||||
# log.debug("pikepdf mmap not available")
|
||||
# We found a race condition probably related to pybind issue #2252 that can
|
||||
# cause a crash. For now, disable pikepdf mmap to be on the safe side.
|
||||
# Fix is not in pybind11 2.6.0
|
||||
# log.debug("pikepdf mmap disabled")
|
||||
return
|
||||
try:
|
||||
if pikepdf._core.set_access_default_mmap(True):
|
||||
log.debug("pikepdf mmap enabled")
|
||||
except AttributeError:
|
||||
log.debug("pikepdf mmap not available")
|
||||
log.debug("pikepdf mmap disabled")
|
||||
|
||||
@@ -19,6 +19,7 @@ from xml.etree import ElementTree
|
||||
|
||||
with warnings.catch_warnings():
|
||||
# reportlab uses deprecated load_module
|
||||
# shim can be removed when we require reportlab >= 3.7
|
||||
warnings.filterwarnings(
|
||||
'ignore', category=DeprecationWarning, message=r".*load_module.*"
|
||||
)
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from functools import singledispatch
|
||||
from math import floor, sqrt
|
||||
from typing import Optional, Tuple
|
||||
|
||||
@@ -43,8 +42,7 @@ def bytes_per_pixel(mode: str) -> int:
|
||||
return 4
|
||||
|
||||
|
||||
@singledispatch
|
||||
def calculate_downsample(
|
||||
def _calculate_downsample(
|
||||
image_size: Tuple[int, int],
|
||||
bytes_per_pixel: int,
|
||||
*,
|
||||
@@ -105,10 +103,8 @@ def calculate_downsample(
|
||||
return size
|
||||
|
||||
|
||||
@calculate_downsample.register
|
||||
def _(
|
||||
def calculate_downsample(
|
||||
image: Image.Image,
|
||||
arg: None = None,
|
||||
*,
|
||||
max_size: Optional[Tuple[int, int]] = None,
|
||||
max_pixels: Optional[int] = None,
|
||||
@@ -126,7 +122,7 @@ def _(
|
||||
max_bytes: The maximum number of bytes in the image. RGB is counted as 4
|
||||
bytes; all other modes are counted as 1 byte.
|
||||
"""
|
||||
return calculate_downsample(
|
||||
return _calculate_downsample(
|
||||
image.size,
|
||||
bytes_per_pixel(image.mode),
|
||||
max_size=max_size,
|
||||
|
||||
+49
-46
@@ -68,10 +68,10 @@ def jpg_name(root: Path, xref: Xref) -> Path:
|
||||
|
||||
|
||||
def extract_image_filter(
|
||||
pike: Pdf, root: Path, image: Stream, xref: Xref
|
||||
pdf: Pdf, root: Path, image: Stream, xref: Xref
|
||||
) -> tuple[PdfImage, tuple[Name, Object]] | None:
|
||||
"""Determine if an image is extractable."""
|
||||
del pike # unused args
|
||||
del pdf # unused args
|
||||
del root
|
||||
|
||||
if image.Subtype != Name.Image:
|
||||
@@ -126,12 +126,12 @@ def extract_image_filter(
|
||||
|
||||
|
||||
def extract_image_jbig2(
|
||||
*, pike: Pdf, root: Path, image: Stream, xref: Xref, options
|
||||
*, pdf: Pdf, root: Path, image: Stream, xref: Xref, options
|
||||
) -> XrefExt | None:
|
||||
"""Extract an image, saving it as a JBIG2 file."""
|
||||
del options # unused arg
|
||||
|
||||
result = extract_image_filter(pike, root, image, xref)
|
||||
result = extract_image_filter(pdf, root, image, xref)
|
||||
if result is None:
|
||||
return None
|
||||
pim, filtdp = result
|
||||
@@ -168,10 +168,10 @@ def extract_image_jbig2(
|
||||
|
||||
|
||||
def extract_image_generic(
|
||||
*, pike: Pdf, root: Path, image: Stream, xref: Xref, options
|
||||
*, pdf: Pdf, root: Path, image: Stream, xref: Xref, options
|
||||
) -> XrefExt | None:
|
||||
"""Generic image extraction."""
|
||||
result = extract_image_filter(pike, root, image, xref)
|
||||
result = extract_image_filter(pdf, root, image, xref)
|
||||
if result is None:
|
||||
return None
|
||||
pim, filtdp = result
|
||||
@@ -239,7 +239,7 @@ def _find_image_xrefs_container(
|
||||
pageno_for_xref: dict[Xref, int],
|
||||
depth: int = 0,
|
||||
):
|
||||
"""Find all image XRefs in a page or Form XObject and add to the include/exclude sets."""
|
||||
"""Find all image XRefs or Form XObject and add to the include/exclude sets."""
|
||||
if depth > 10:
|
||||
log.warning("Recursion depth exceeded in _find_image_xrefs_page")
|
||||
return
|
||||
@@ -290,7 +290,7 @@ def _find_image_xrefs(pdf: Pdf):
|
||||
|
||||
|
||||
def extract_images(
|
||||
pike: Pdf,
|
||||
pdf: Pdf,
|
||||
root: Path,
|
||||
options,
|
||||
extract_fn: Callable[..., XrefExt | None],
|
||||
@@ -310,12 +310,12 @@ def extract_images(
|
||||
extension. extract_fn must also extract the file it finds interesting.
|
||||
"""
|
||||
errors = 0
|
||||
working_xrefs, pageno_for_xref = _find_image_xrefs(pike)
|
||||
working_xrefs, pageno_for_xref = _find_image_xrefs(pdf)
|
||||
for xref in working_xrefs:
|
||||
image = pike.get_object((xref, 0))
|
||||
image = pdf.get_object((xref, 0))
|
||||
try:
|
||||
result = extract_fn(
|
||||
pike=pike, root=root, image=image, xref=xref, options=options
|
||||
pdf=pdf, root=root, image=image, xref=xref, options=options
|
||||
)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
log.exception(
|
||||
@@ -329,12 +329,12 @@ def extract_images(
|
||||
|
||||
|
||||
def extract_images_generic(
|
||||
pike: Pdf, root: Path, options
|
||||
pdf: Pdf, root: Path, options
|
||||
) -> tuple[list[Xref], list[Xref]]:
|
||||
"""Extract any >=2bpp image we think we can improve."""
|
||||
jpegs = []
|
||||
pngs = []
|
||||
for _, xref_ext in extract_images(pike, root, options, extract_image_generic):
|
||||
for _, xref_ext in extract_images(pdf, root, options, extract_image_generic):
|
||||
log.debug('%s', xref_ext)
|
||||
if xref_ext.ext == '.png':
|
||||
pngs.append(xref_ext.xref)
|
||||
@@ -344,10 +344,10 @@ def extract_images_generic(
|
||||
return jpegs, pngs
|
||||
|
||||
|
||||
def extract_images_jbig2(pike: Pdf, root: Path, options) -> dict[int, list[XrefExt]]:
|
||||
def extract_images_jbig2(pdf: Pdf, root: Path, options) -> dict[int, list[XrefExt]]:
|
||||
"""Extract any bitonal image that we think we can improve as JBIG2."""
|
||||
jbig2_groups = defaultdict(list)
|
||||
for pageno, xref_ext in extract_images(pike, root, options, extract_image_jbig2):
|
||||
for pageno, xref_ext in extract_images(pdf, root, options, extract_image_jbig2):
|
||||
group = pageno // options.jbig2_page_group_size
|
||||
jbig2_groups[group].append(xref_ext)
|
||||
|
||||
@@ -367,6 +367,7 @@ def _produce_jbig2_images(
|
||||
fspath(root), # =cwd
|
||||
(img_name(root, xref, ext) for xref, ext in xref_exts), # =infiles
|
||||
prefix, # =out_prefix
|
||||
options.jbig2_threshold,
|
||||
)
|
||||
|
||||
def jbig2_single_args(root, groups: dict[int, list[XrefExt]]):
|
||||
@@ -379,6 +380,7 @@ def _produce_jbig2_images(
|
||||
fspath(root),
|
||||
img_name(root, xref, ext),
|
||||
root / f'{prefix}.{n:04d}',
|
||||
options.jbig2_threshold,
|
||||
)
|
||||
|
||||
if options.jbig2_page_group_size > 1:
|
||||
@@ -403,7 +405,7 @@ def _produce_jbig2_images(
|
||||
|
||||
|
||||
def convert_to_jbig2(
|
||||
pike: Pdf,
|
||||
pdf: Pdf,
|
||||
jbig2_groups: dict[int, list[XrefExt]],
|
||||
root: Path,
|
||||
options,
|
||||
@@ -430,7 +432,7 @@ def convert_to_jbig2(
|
||||
jbig2_symfile = root / (prefix + '.sym')
|
||||
if jbig2_symfile.exists():
|
||||
jbig2_globals_data = jbig2_symfile.read_bytes()
|
||||
jbig2_globals = Stream(pike, jbig2_globals_data)
|
||||
jbig2_globals = Stream(pdf, jbig2_globals_data)
|
||||
jbig2_globals_dict = Dictionary(JBIG2Globals=jbig2_globals)
|
||||
elif options.jbig2_page_group_size == 1:
|
||||
jbig2_globals_dict = None
|
||||
@@ -441,7 +443,7 @@ def convert_to_jbig2(
|
||||
xref, _ = xref_ext
|
||||
jbig2_im_file = root / (prefix + f'.{n:04d}')
|
||||
jbig2_im_data = jbig2_im_file.read_bytes()
|
||||
im_obj = pike.get_object(xref, 0)
|
||||
im_obj = pdf.get_object(xref, 0)
|
||||
im_obj.write(
|
||||
jbig2_im_data, filter=Name.JBIG2Decode, decode_parms=jbig2_globals_dict
|
||||
)
|
||||
@@ -461,7 +463,7 @@ def _optimize_jpeg(args: tuple[Xref, Path, Path, int]) -> tuple[Xref, Path | Non
|
||||
|
||||
|
||||
def transcode_jpegs(
|
||||
pike: Pdf, jpegs: Sequence[Xref], root: Path, options, executor: Executor
|
||||
pdf: Pdf, jpegs: Sequence[Xref], root: Path, options, executor: Executor
|
||||
) -> None:
|
||||
"""Optimize JPEGs according to optimization settings."""
|
||||
|
||||
@@ -475,7 +477,7 @@ def transcode_jpegs(
|
||||
xref, opt_jpg = result
|
||||
if opt_jpg:
|
||||
compdata = opt_jpg.read_bytes() # JPEG can inserted into PDF as is
|
||||
im_obj = pike.get_object(xref, 0)
|
||||
im_obj = pdf.get_object(xref, 0)
|
||||
im_obj.write(compdata, filter=Name.DCTDecode)
|
||||
pbar.update()
|
||||
|
||||
@@ -495,9 +497,9 @@ def transcode_jpegs(
|
||||
|
||||
|
||||
def _find_deflatable_jpeg(
|
||||
*, pike: Pdf, root: Path, image: Stream, xref: Xref, options
|
||||
*, pdf: Pdf, root: Path, image: Stream, xref: Xref, options
|
||||
) -> XrefExt | None:
|
||||
result = extract_image_filter(pike, root, image, xref)
|
||||
result = extract_image_filter(pdf, root, image, xref)
|
||||
if result is None:
|
||||
return None
|
||||
_pim, filtdp = result
|
||||
@@ -509,9 +511,9 @@ def _find_deflatable_jpeg(
|
||||
|
||||
|
||||
def _deflate_jpeg(args: tuple[Pdf, threading.Lock, Xref, int]) -> tuple[Xref, bytes]:
|
||||
pike, lock, xref, complevel = args
|
||||
pdf, lock, xref, complevel = args
|
||||
with lock:
|
||||
xobj = pike.get_object(xref, 0)
|
||||
xobj = pdf.get_object(xref, 0)
|
||||
try:
|
||||
data = xobj.read_raw_bytes()
|
||||
except PdfError:
|
||||
@@ -522,7 +524,7 @@ def _deflate_jpeg(args: tuple[Pdf, threading.Lock, Xref, int]) -> tuple[Xref, by
|
||||
return xref, compdata
|
||||
|
||||
|
||||
def deflate_jpegs(pike: Pdf, root: Path, options, executor: Executor) -> None:
|
||||
def deflate_jpegs(pdf: Pdf, root: Path, options, executor: Executor) -> None:
|
||||
"""Apply FlateDecode to JPEGs.
|
||||
|
||||
This is a lossless compression method that is supported by all PDF viewers,
|
||||
@@ -530,7 +532,7 @@ def deflate_jpegs(pike: Pdf, root: Path, options, executor: Executor) -> None:
|
||||
images.
|
||||
"""
|
||||
jpegs = []
|
||||
for _pageno, xref_ext in extract_images(pike, root, options, _find_deflatable_jpeg):
|
||||
for _pageno, xref_ext in extract_images(pdf, root, options, _find_deflatable_jpeg):
|
||||
xref = xref_ext.xref
|
||||
log.debug(f'xref {xref}: marking this JPEG as deflatable')
|
||||
jpegs.append(xref)
|
||||
@@ -542,13 +544,13 @@ def deflate_jpegs(pike: Pdf, root: Path, options, executor: Executor) -> None:
|
||||
|
||||
def deflate_args() -> Iterator:
|
||||
for xref in jpegs:
|
||||
yield pike, lock, xref, complevel
|
||||
yield pdf, lock, xref, complevel
|
||||
|
||||
def finish(result, pbar):
|
||||
xref, compdata = result
|
||||
if len(compdata) > 0:
|
||||
with lock:
|
||||
xobj = pike.get_object(xref, 0)
|
||||
xobj = pdf.get_object(xref, 0)
|
||||
xobj.write(compdata, filter=[Name.FlateDecode, Name.DCTDecode])
|
||||
pbar.update()
|
||||
|
||||
@@ -567,16 +569,16 @@ def deflate_jpegs(pike: Pdf, root: Path, options, executor: Executor) -> None:
|
||||
)
|
||||
|
||||
|
||||
def _transcode_png(pike: Pdf, filename: Path, xref: Xref) -> bool:
|
||||
def _transcode_png(pdf: Pdf, filename: Path, xref: Xref) -> bool:
|
||||
output = filename.with_suffix('.png.pdf')
|
||||
with output.open('wb') as f:
|
||||
img2pdf.convert(fspath(filename), outputstream=f, **IMG2PDF_KWARGS)
|
||||
|
||||
with Pdf.open(output) as pdf_image:
|
||||
foreign_image = next(iter(pdf_image.pages[0].images.values()))
|
||||
local_image = pike.copy_foreign(foreign_image)
|
||||
local_image = pdf.copy_foreign(foreign_image)
|
||||
|
||||
im_obj = pike.get_object(xref, 0)
|
||||
im_obj = pdf.get_object(xref, 0)
|
||||
im_obj.write(
|
||||
local_image.read_raw_bytes(),
|
||||
filter=local_image.Filter,
|
||||
@@ -609,7 +611,7 @@ def _transcode_png(pike: Pdf, filename: Path, xref: Xref) -> bool:
|
||||
|
||||
|
||||
def transcode_pngs(
|
||||
pike: Pdf,
|
||||
pdf: Pdf,
|
||||
images: Sequence[Xref],
|
||||
image_name_fn: Callable[[Path, Xref], Path],
|
||||
root: Path,
|
||||
@@ -650,7 +652,7 @@ def transcode_pngs(
|
||||
|
||||
for xref in modified:
|
||||
filename = png_name(root, xref)
|
||||
_transcode_png(pike, filename, xref)
|
||||
_transcode_png(pdf, filename, xref)
|
||||
|
||||
|
||||
DEFAULT_EXECUTOR = SerialExecutor()
|
||||
@@ -676,24 +678,24 @@ def optimize(
|
||||
if options.jbig2_page_group_size == 0:
|
||||
options.jbig2_page_group_size = 10 if options.jbig2_lossy else 1
|
||||
|
||||
with Pdf.open(input_file) as pike:
|
||||
with Pdf.open(input_file) as pdf:
|
||||
root = output_file.parent / 'images'
|
||||
root.mkdir(exist_ok=True)
|
||||
|
||||
jpegs, pngs = extract_images_generic(pike, root, options)
|
||||
transcode_jpegs(pike, jpegs, root, options, executor)
|
||||
deflate_jpegs(pike, root, options, executor)
|
||||
jpegs, pngs = extract_images_generic(pdf, root, options)
|
||||
transcode_jpegs(pdf, jpegs, root, options, executor)
|
||||
deflate_jpegs(pdf, root, options, executor)
|
||||
# if options.optimize >= 2:
|
||||
# Try pngifying the jpegs
|
||||
# transcode_pngs(pike, jpegs, jpg_name, root, options)
|
||||
transcode_pngs(pike, pngs, png_name, root, options, executor)
|
||||
# transcode_pngs(pdf, jpegs, jpg_name, root, options)
|
||||
transcode_pngs(pdf, pngs, png_name, root, options, executor)
|
||||
|
||||
jbig2_groups = extract_images_jbig2(pike, root, options)
|
||||
convert_to_jbig2(pike, jbig2_groups, root, options, executor)
|
||||
jbig2_groups = extract_images_jbig2(pdf, root, options)
|
||||
convert_to_jbig2(pdf, jbig2_groups, root, options, executor)
|
||||
|
||||
target_file = output_file.with_suffix('.opt.pdf')
|
||||
pike.remove_unreferenced_resources()
|
||||
pike.save(target_file, **save_settings)
|
||||
pdf.remove_unreferenced_resources()
|
||||
pdf.save(target_file, **save_settings)
|
||||
|
||||
input_size = input_file.stat().st_size
|
||||
output_size = target_file.stat().st_size
|
||||
@@ -710,9 +712,9 @@ def optimize(
|
||||
"optimizations will not be used"
|
||||
)
|
||||
# We still need to save the file
|
||||
with Pdf.open(input_file) as pike:
|
||||
pike.remove_unreferenced_resources()
|
||||
pike.save(output_file, **save_settings)
|
||||
with Pdf.open(input_file) as pdf:
|
||||
pdf.remove_unreferenced_resources()
|
||||
pdf.save(output_file, **save_settings)
|
||||
else:
|
||||
safe_symlink(target_file, output_file)
|
||||
|
||||
@@ -737,6 +739,7 @@ def main(infile, outfile, level, jobs=1):
|
||||
self.png_quality = png_quality
|
||||
self.jbig2_page_group_size = 0
|
||||
self.jbig2_lossy = jb2lossy
|
||||
self.jbig2_threshold = 0.85
|
||||
self.quiet = True
|
||||
self.progress_bar = False
|
||||
|
||||
|
||||
@@ -6,14 +6,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
from importlib.resources import files as package_files
|
||||
from pathlib import Path
|
||||
from typing import Iterator
|
||||
|
||||
try:
|
||||
from importlib.resources import files as package_files
|
||||
except ImportError:
|
||||
from importlib_resources import files as package_files # type: ignore
|
||||
|
||||
import pikepdf
|
||||
|
||||
SRGB_ICC_PROFILE_NAME = 'sRGB.icc'
|
||||
|
||||
+119
-33
@@ -9,6 +9,8 @@ from __future__ import annotations
|
||||
import atexit
|
||||
import logging
|
||||
import re
|
||||
import statistics
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from contextlib import ExitStack
|
||||
from decimal import Decimal
|
||||
@@ -21,7 +23,9 @@ from typing import Container, Iterable, Iterator, Mapping, NamedTuple, Sequence,
|
||||
from warnings import warn
|
||||
|
||||
from pikepdf import (
|
||||
Name,
|
||||
Object,
|
||||
Page,
|
||||
Pdf,
|
||||
PdfImage,
|
||||
PdfInlineImage,
|
||||
@@ -418,12 +422,12 @@ class ImageInfo:
|
||||
return self._type
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
def width(self) -> int:
|
||||
"""Width of the image in pixels."""
|
||||
return self._width
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
def height(self) -> int:
|
||||
"""Height of the image in pixels."""
|
||||
return self._height
|
||||
|
||||
@@ -456,17 +460,24 @@ class ImageInfo:
|
||||
return self.dpi.is_finite and self.width >= 0 and self.height >= 0
|
||||
|
||||
@property
|
||||
def dpi(self):
|
||||
def dpi(self) -> Resolution:
|
||||
"""Dots per inch of the image.
|
||||
|
||||
Calculated based on where and how the image is drawn in the PDF.
|
||||
"""
|
||||
return _get_dpi(self._shorthand, (self._width, self._height))
|
||||
|
||||
@property
|
||||
def printed_area(self) -> float:
|
||||
"""Physical area of the image in square inches."""
|
||||
if not self.renderable:
|
||||
return 0.0
|
||||
return float(self.width * self.dpi.x * self.height * self.dpi.y)
|
||||
|
||||
def __repr__(self):
|
||||
"""Return a string representation of the image."""
|
||||
return (
|
||||
f"<ImageInfo '{self.name}' {self.type_} {self.width}x{self.height} "
|
||||
f"<ImageInfo '{self.name}' {self.type_} {self.width}×{self.height} "
|
||||
f"{self.color} {self.comp} {self.bpc} {self.enc} {self.dpi}>"
|
||||
)
|
||||
|
||||
@@ -491,15 +502,15 @@ def _image_xobjects(container) -> Iterator[tuple[Object, str]]:
|
||||
since the object does not know its own name.
|
||||
|
||||
"""
|
||||
if '/Resources' not in container:
|
||||
if Name.Resources not in container:
|
||||
return
|
||||
resources = container['/Resources']
|
||||
if '/XObject' not in resources:
|
||||
resources = container[Name.Resources]
|
||||
if Name.XObject not in resources:
|
||||
return
|
||||
for key, candidate in resources['/XObject'].items():
|
||||
if candidate is None or '/Subtype' not in candidate:
|
||||
for key, candidate in resources[Name.XObject].items():
|
||||
if candidate is None or Name.Subtype not in candidate:
|
||||
continue
|
||||
if candidate['/Subtype'] == '/Image':
|
||||
if candidate[Name.Subtype] == Name.Image:
|
||||
pdfimage = candidate
|
||||
yield (pdfimage, key)
|
||||
|
||||
@@ -535,15 +546,15 @@ def _find_form_xobject_images(pdf: Pdf, container: Object, contentsinfo: Content
|
||||
The container may be a page, or a parent Form XObject.
|
||||
|
||||
"""
|
||||
if '/Resources' not in container:
|
||||
if Name.Resources not in container:
|
||||
return
|
||||
resources = container['/Resources']
|
||||
if '/XObject' not in resources:
|
||||
resources = container[Name.Resources]
|
||||
if Name.XObject not in resources:
|
||||
return
|
||||
xobjs = resources['/XObject'].as_dict()
|
||||
xobjs = resources[Name.XObject].as_dict()
|
||||
for xobj in xobjs:
|
||||
candidate = xobjs[xobj]
|
||||
if candidate is None or candidate['/Subtype'] != '/Form':
|
||||
if candidate is None or candidate[Name.Subtype] != Name.Form:
|
||||
continue
|
||||
|
||||
form_xobject = candidate
|
||||
@@ -581,16 +592,19 @@ def _process_content_streams(
|
||||
downsampling.
|
||||
|
||||
"""
|
||||
if container.get('/Type') == '/Page' and '/Contents' in container:
|
||||
if container.get(Name.Type) == Name.Page and Name.Contents in container:
|
||||
initial_shorthand = shorthand or UNIT_SQUARE
|
||||
elif container.get('/Type') == '/XObject' and container['/Subtype'] == '/Form':
|
||||
elif (
|
||||
container.get(Name.Type) == Name.XObject
|
||||
and container[Name.Subtype] == Name.Form
|
||||
):
|
||||
# Set the CTM to the state it was when the "Do" operator was
|
||||
# encountered that is drawing this instance of the Form XObject
|
||||
ctm = PdfMatrix(shorthand) if shorthand else PdfMatrix.identity()
|
||||
|
||||
# A Form XObject may provide its own matrix to map form space into
|
||||
# user space. Get this if one exists
|
||||
form_shorthand = container.get('/Matrix', PdfMatrix.identity())
|
||||
form_shorthand = container.get(Name.Matrix, PdfMatrix.identity())
|
||||
form_matrix = PdfMatrix(form_shorthand)
|
||||
|
||||
# Concatenate form matrix with CTM to ensure CTM is correct for
|
||||
@@ -742,12 +756,38 @@ def _pdf_pageinfo_concurrent(
|
||||
return pages
|
||||
|
||||
|
||||
class PageResolutionProfile(NamedTuple):
|
||||
"""Information about the resolutions of a page."""
|
||||
|
||||
weighted_dpi: float
|
||||
"""The weighted average DPI of the page, weighted by the area of each image."""
|
||||
|
||||
max_dpi: float
|
||||
"""The maximum DPI of an image on the page."""
|
||||
|
||||
average_to_max_dpi_ratio: float
|
||||
"""The average DPI of the page divided by the maximum DPI of the page.
|
||||
|
||||
This indicates the intensity of the resolution variation on the page.
|
||||
|
||||
If the average is 1.0 or close to 1.0, has all of its content at a uniform
|
||||
resolution. If the average is much lower than 1.0, some content is at a
|
||||
higher resolution than the rest of the page.
|
||||
"""
|
||||
|
||||
area_ratio: float
|
||||
"""The maximum-DPI area of the page divided by the total drawn area.
|
||||
|
||||
This indicates the prevalence of high-resolution content on the page.
|
||||
"""
|
||||
|
||||
|
||||
class PageInfo:
|
||||
"""Information about type of contents on each page in a PDF."""
|
||||
|
||||
_has_text: bool | None
|
||||
_has_vector: bool | None
|
||||
_images: list[ImageInfo]
|
||||
_images: list[ImageInfo] = []
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -771,7 +811,7 @@ class PageInfo:
|
||||
check_pages: Container[int],
|
||||
detailed_analysis: bool,
|
||||
):
|
||||
page = pdf.pages[pageno]
|
||||
page: Page = pdf.pages[pageno]
|
||||
mediabox = [Decimal(d) for d in page.MediaBox.as_list()]
|
||||
width_pt = mediabox[2] - mediabox[0]
|
||||
height_pt = mediabox[3] - mediabox[1]
|
||||
@@ -779,7 +819,7 @@ class PageInfo:
|
||||
check_this_page = pageno in check_pages
|
||||
|
||||
if check_this_page and detailed_analysis:
|
||||
pscript5_mode = str(pdf.docinfo.get('/Creator')).startswith('PScript5')
|
||||
pscript5_mode = str(pdf.docinfo.get(Name.Creator)).startswith('PScript5')
|
||||
miner = get_page_analysis(infile, pageno, pscript5_mode)
|
||||
self._textboxes = list(simplify_textboxes(miner, get_text_boxes))
|
||||
bboxes = (box.bbox for box in self._textboxes)
|
||||
@@ -789,17 +829,13 @@ class PageInfo:
|
||||
self._textboxes = []
|
||||
self._has_text = None # i.e. "no information"
|
||||
|
||||
userunit = page.get('/UserUnit', Decimal(1.0))
|
||||
userunit = page.get(Name.UserUnit, Decimal(1.0))
|
||||
if not isinstance(userunit, Decimal):
|
||||
userunit = Decimal(userunit)
|
||||
self._userunit = userunit
|
||||
self._width_inches = width_pt * userunit / Decimal(72.0)
|
||||
self._height_inches = height_pt * userunit / Decimal(72.0)
|
||||
|
||||
try:
|
||||
self._rotate = int(page['/Rotate'])
|
||||
except KeyError:
|
||||
self._rotate = 0
|
||||
self._rotate = int(getattr(page.obj, 'Rotate', 0))
|
||||
|
||||
userunit_shorthand = (userunit, 0, 0, userunit, 0, 0)
|
||||
|
||||
@@ -938,6 +974,47 @@ class PageInfo:
|
||||
else:
|
||||
return '1.5'
|
||||
|
||||
def page_dpi_profile(self) -> PageResolutionProfile | None:
|
||||
"""Return information about the DPIs of the page.
|
||||
|
||||
This is useful to detect pages with a small proportion of high-resolution
|
||||
content that is forcing us to use a high DPI for the whole page. The ratio
|
||||
is weighted by the area of each image. If images overlap, the overlapped
|
||||
area counts.
|
||||
|
||||
Vector graphics and text are ignored.
|
||||
|
||||
Returns None if there is no meaningful DPI for the page.
|
||||
"""
|
||||
image_dpis = [
|
||||
image.dpi.to_scalar() for image in self._images if image.renderable
|
||||
]
|
||||
image_areas = [image.printed_area for image in self._images if image.renderable]
|
||||
total_drawn_area = sum(image_areas)
|
||||
if total_drawn_area == 0:
|
||||
return None
|
||||
|
||||
weights = [area / total_drawn_area for area in image_areas]
|
||||
# Calculate harmonic mean of DPIs weighted by area
|
||||
if sys.version_info >= (3, 10):
|
||||
weighted_dpi = statistics.harmonic_mean(image_dpis, weights)
|
||||
else:
|
||||
weighted_dpi = sum(weights) / sum(
|
||||
weight / dpi for weight, dpi in zip(weights, image_dpis)
|
||||
)
|
||||
max_dpi = max(image_dpis)
|
||||
dpi_average_max_ratio = weighted_dpi / max_dpi
|
||||
|
||||
arg_max_dpi = image_dpis.index(max_dpi)
|
||||
max_area_ratio = image_areas[arg_max_dpi] / total_drawn_area
|
||||
|
||||
return PageResolutionProfile(
|
||||
weighted_dpi,
|
||||
max_dpi,
|
||||
dpi_average_max_ratio,
|
||||
max_area_ratio,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
"""Return string representation."""
|
||||
return (
|
||||
@@ -953,6 +1030,10 @@ DEFAULT_EXECUTOR = SerialExecutor()
|
||||
class PdfInfo:
|
||||
"""Get summary information about a PDF."""
|
||||
|
||||
_has_acroform: bool = False
|
||||
_has_signature: bool = False
|
||||
_needs_rendering: bool = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
infile,
|
||||
@@ -980,13 +1061,13 @@ class PdfInfo:
|
||||
check_pages=check_pages,
|
||||
detailed_analysis=detailed_analysis,
|
||||
)
|
||||
self._needs_rendering = pdf.Root.get('/NeedsRendering', False)
|
||||
self._has_acroform = False
|
||||
if '/AcroForm' in pdf.Root:
|
||||
if len(pdf.Root.AcroForm.get('/Fields', [])) > 0:
|
||||
self._needs_rendering = pdf.Root.get(Name.NeedsRendering, False)
|
||||
if Name.AcroForm in pdf.Root:
|
||||
if len(pdf.Root.AcroForm.get(Name.Fields, [])) > 0:
|
||||
self._has_acroform = True
|
||||
elif '/XFA' in pdf.Root.AcroForm:
|
||||
elif Name.XFA in pdf.Root.AcroForm:
|
||||
self._has_acroform = True
|
||||
self._has_signature = bool(pdf.Root.AcroForm.get(Name.SigFlags, 0) & 1)
|
||||
|
||||
@property
|
||||
def pages(self) -> Sequence[PageInfo | None]:
|
||||
@@ -1006,9 +1087,14 @@ class PdfInfo:
|
||||
|
||||
@property
|
||||
def has_acroform(self) -> bool:
|
||||
"""Return True if any page has an AcroForm."""
|
||||
"""Return True if the document catalog has an AcroForm."""
|
||||
return self._has_acroform
|
||||
|
||||
@property
|
||||
def has_signature(self) -> bool:
|
||||
"""Return True if the document annotations has a digital signature."""
|
||||
return self._has_signature
|
||||
|
||||
@property
|
||||
def filename(self) -> str | Path:
|
||||
"""Return filename of PDF."""
|
||||
|
||||
@@ -351,7 +351,6 @@ def filter_pdf_page(page: PageContext, image_filename: Path, output_pdf: Path) -
|
||||
This hook will be called from child processes. Modifying global state
|
||||
will not affect the main process or other child processes.
|
||||
|
||||
|
||||
Note:
|
||||
This is a :ref:`firstresult hook<firstresult>`.
|
||||
"""
|
||||
@@ -466,7 +465,7 @@ def generate_pdfa(
|
||||
pdf_pages: list[Path],
|
||||
pdfmark: Path,
|
||||
output_file: Path,
|
||||
compression: str,
|
||||
context: PdfContext,
|
||||
pdf_version: str,
|
||||
pdfa_part: str,
|
||||
progressbar_class,
|
||||
@@ -484,11 +483,7 @@ def generate_pdfa(
|
||||
pdfmark: A PostScript file intended for Ghostscript with details on
|
||||
how to perform the PDF/A conversion.
|
||||
output_file: The name of the desired output file.
|
||||
compression: One of ``'jpeg'``, ``'lossless'``, ``''``. For ``'jpeg'``,
|
||||
the PDF/A generator should convert all images to JPEG encoding where
|
||||
possible. For lossless, all images should be converted to FlateEncode
|
||||
(lossless PNG). If an empty string, the PDF generator should make its
|
||||
own decisions about how to encode images.
|
||||
context: The current context.
|
||||
pdf_version: The minimum PDF version that the output file should be.
|
||||
At its own discretion, the PDF/A generator may raise the version,
|
||||
but should not lower it.
|
||||
@@ -514,6 +509,11 @@ def generate_pdfa(
|
||||
Note:
|
||||
This is a :ref:`firstresult hook<firstresult>`.
|
||||
|
||||
Note:
|
||||
Before version 15.0.0, the ``context`` was not provided and ``compression``
|
||||
was provided instead. Plugins should now read the context object to determine
|
||||
if compression is requested.
|
||||
|
||||
See Also:
|
||||
https://github.com/tqdm/tqdm
|
||||
"""
|
||||
|
||||
@@ -292,21 +292,12 @@ def _error_old_version(
|
||||
_error_trailer(**locals())
|
||||
|
||||
|
||||
def _remove_leading_v(s: str) -> str:
|
||||
if sys.version_info >= (3, 9):
|
||||
return s.removeprefix('v')
|
||||
|
||||
if s.startswith('v'):
|
||||
return s[1:]
|
||||
return s
|
||||
|
||||
|
||||
def check_external_program(
|
||||
*,
|
||||
program: str,
|
||||
package: str,
|
||||
version_checker: Callable[[], str],
|
||||
need_version: str,
|
||||
version_checker: Callable[[], Version],
|
||||
need_version: str | Version,
|
||||
required_for: str | None = None,
|
||||
recommended: bool = False,
|
||||
version_parser: type[Version] = Version,
|
||||
@@ -326,6 +317,8 @@ def check_external_program(
|
||||
version_parser: A class that should be used to parse and compare version
|
||||
numbers. Used when version numbers do not follow standard conventions.
|
||||
"""
|
||||
if not isinstance(need_version, Version):
|
||||
need_version = version_parser(need_version)
|
||||
try:
|
||||
found_version = version_checker()
|
||||
except (CalledProcessError, FileNotFoundError) as e:
|
||||
@@ -339,11 +332,10 @@ def check_external_program(
|
||||
raise
|
||||
return
|
||||
|
||||
found_version = _remove_leading_v(found_version)
|
||||
need_version = _remove_leading_v(need_version)
|
||||
|
||||
if found_version and version_parser(found_version) < version_parser(need_version):
|
||||
_error_old_version(program, package, need_version, found_version, required_for)
|
||||
if found_version and found_version < need_version:
|
||||
_error_old_version(
|
||||
program, package, str(need_version), str(found_version), required_for
|
||||
)
|
||||
if not recommended:
|
||||
raise MissingDependencyError(program)
|
||||
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
+1
-4
@@ -1,7 +1,5 @@
|
||||
i a la Waterman
|
||||
|
||||
|
||||
|
||||
4 ons linzen
|
||||
|
||||
3 liter water
|
||||
@@ -17,11 +15,10 @@ laurier, kruidnagel, kerrie, zout
|
||||
De linzgen wassen en in-l liter kokend wa-
|
||||
ter 1 dag laten weken, 2 liter water bij
|
||||
de linzen voegen, zonder het water waarin
|
||||
ze geweekt zijn af te gieten., De helft van
|
||||
ze geweekt zijn af te gieten, De helft van
|
||||
de uien bakken met laurier en Kruidnagel.
|
||||
Alle uien, kerrie en zgout bij de linzen
|
||||
voegen, Alles aan de kook brengen, Van de
|
||||
bloem met boter en melk een papje maken en
|
||||
verder afmaken met de soep, Als de linzen
|
||||
gfgaar Zijn is de soep klaar.
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
-15
@@ -1,25 +1,11 @@
|
||||
Tarnose
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bokale oa
|
||||
|
||||
|
||||
|
||||
Lehuntze
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Mugerre
|
||||
|
||||
|
||||
|
||||
|
||||
Milafranga Komunikabideak
|
||||
|
||||
BAIONA zeiteninsiie —
|
||||
@@ -27,4 +13,3 @@ BAIONA zeiteninsiie —
|
||||
7 Trenbideak -----
|
||||
|
||||
t\ Basusarri — spmeans:20141004 ae: . _ ~
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
-1
@@ -1,2 +1 @@
|
||||
Covfefe is a perfectly cromulent word.
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
+1
-4
@@ -1,7 +1,5 @@
|
||||
Linzensoep a la Waterman
|
||||
|
||||
|
||||
|
||||
4 ons linzen
|
||||
|
||||
3 liter water
|
||||
@@ -17,11 +15,10 @@ laurier, kruidnagel, kerrie, zout
|
||||
De linzgen wassen en in-l liter kokend wa-
|
||||
ter 1 dag laten weken, 2 liter water bij
|
||||
de linzen voegen, zonder het water waarin
|
||||
ze geweekt zijn af te gieten., De helft van
|
||||
ze geweekt zijn af te gieten, De helft van
|
||||
de uien bakken met laurier en Kruidnagel.
|
||||
Alle uien, kerrie en zgout bij de linzen
|
||||
voegen, Alles aan de kook brengen, Van de
|
||||
bloem met boter en melk een papje maken en
|
||||
verder afmaken met de soep, Als de linzen
|
||||
gfgaar Zijn is de soep klaar.
|
||||
|
||||
+11
-17
@@ -5,27 +5,21 @@
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
||||
<meta name='ocr-system' content='tesseract 5.0.0-beta-20210916-12-g19cc9' />
|
||||
<meta name='ocr-system' content='tesseract 5.3.2' />
|
||||
<meta name='ocr-capabilities' content='ocr_page ocr_carea ocr_par ocr_line ocrx_word ocrp_wconf'/>
|
||||
</head>
|
||||
<body>
|
||||
<div class='ocr_page' id='page_1' title='image "/tmp/ocrmypdf.io.gzrr1v_b/000001_ocr.png"; bbox 0 0 1000 800; ppageno 0'>
|
||||
<div class='ocr_carea' id='block_1_1' title="bbox 296 96 704 504">
|
||||
<p class='ocr_par' id='par_1_1' lang='eng' title="bbox 296 96 704 504">
|
||||
<span class='ocr_line' id='line_1_1' title="bbox 296 96 704 504; baseline 0 296; x_size 169.33333; x_descenders 42.333332; x_ascenders 42.333336">
|
||||
<span class='ocrx_word' id='word_1_1' title='bbox 296 96 704 504; x_wconf 95'> </span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class='ocr_page' id='page_1' title='image "/tmp/ocrmypdf.io.9_hn68nz/000001_ocr.png"; bbox 0 0 1000 800; ppageno 0; scan_res 200 200'>
|
||||
<div class='ocr_photo' id='block_1_1' title="bbox 296 96 704 504"></div>
|
||||
<div class='ocr_carea' id='block_1_2' title="bbox 150 592 841 622">
|
||||
<p class='ocr_par' id='par_1_2' lang='eng' title="bbox 150 592 841 622">
|
||||
<span class='ocr_line' id='line_1_2' title="bbox 150 592 841 622; baseline 0 -6; x_size 30; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_2' title='bbox 150 592 230 616; x_wconf 95'>This</span>
|
||||
<span class='ocrx_word' id='word_1_3' title='bbox 260 592 384 616; x_wconf 95'>should</span>
|
||||
<span class='ocrx_word' id='word_1_4' title='bbox 413 592 449 616; x_wconf 95'>be</span>
|
||||
<span class='ocrx_word' id='word_1_5' title='bbox 479 600 493 616; x_wconf 94'>a</span>
|
||||
<span class='ocrx_word' id='word_1_6' title='bbox 523 592 668 622; x_wconf 94'>perfect</span>
|
||||
<span class='ocrx_word' id='word_1_7' title='bbox 698 592 841 616; x_wconf 61'>circle:</span>
|
||||
<p class='ocr_par' id='par_1_1' lang='eng' title="bbox 150 592 841 622">
|
||||
<span class='ocr_line' id='line_1_1' title="bbox 150 592 841 622; baseline 0 -5; x_size 30; x_descenders 5; x_ascenders 7">
|
||||
<span class='ocrx_word' id='word_1_1' title='bbox 150 592 230 617; x_wconf 96'>This</span>
|
||||
<span class='ocrx_word' id='word_1_2' title='bbox 260 592 384 617; x_wconf 96'>should</span>
|
||||
<span class='ocrx_word' id='word_1_3' title='bbox 413 592 449 617; x_wconf 95'>be</span>
|
||||
<span class='ocrx_word' id='word_1_4' title='bbox 479 599 493 617; x_wconf 95'>a</span>
|
||||
<span class='ocrx_word' id='word_1_5' title='bbox 523 592 668 622; x_wconf 95'>perfect</span>
|
||||
<span class='ocrx_word' id='word_1_6' title='bbox 698 592 841 617; x_wconf 25'>circle.</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
+1
-4
@@ -1,4 +1 @@
|
||||
|
||||
|
||||
This should be a perfect circle:
|
||||
|
||||
This should be a perfect circle.
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
+1
-4
@@ -1,4 +1 @@
|
||||
|
||||
|
||||
This should be a perfect circle:
|
||||
|
||||
This should be a perfect circle.
|
||||
|
||||
+31
-31
@@ -5,11 +5,11 @@
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
||||
<meta name='ocr-system' content='tesseract 5.0.0-beta-20210916-12-g19cc9' />
|
||||
<meta name='ocr-system' content='tesseract 5.3.2' />
|
||||
<meta name='ocr-capabilities' content='ocr_page ocr_carea ocr_par ocr_line ocrx_word ocrp_wconf'/>
|
||||
</head>
|
||||
<body>
|
||||
<div class='ocr_page' id='page_1' title='image "/tmp/ocrmypdf.io.8je4vgpg/000001_ocr.png"; bbox 0 0 2550 3300; ppageno 0'>
|
||||
<div class='ocr_page' id='page_1' title='image "/tmp/ocrmypdf.io.q0nk4qy2/000001_ocr.png"; bbox 0 0 2550 3300; ppageno 0; scan_res 300 300'>
|
||||
<div class='ocr_carea' id='block_1_1' title="bbox 582 131 1968 303">
|
||||
<p class='ocr_par' id='par_1_1' lang='eng' title="bbox 582 131 1968 303">
|
||||
<span class='ocr_header' id='line_1_1' title="bbox 882 131 1657 217; baseline 0.001 -17; x_size 85; x_descenders 16; x_ascenders 19">
|
||||
@@ -136,9 +136,9 @@
|
||||
<div class='ocr_carea' id='block_1_5' title="bbox 349 748 2117 793">
|
||||
<p class='ocr_par' id='par_1_7' lang='eng' title="bbox 349 748 2117 793">
|
||||
<span class='ocr_header' id='line_1_10' title="bbox 349 748 2117 793; baseline 0 -9; x_size 45; x_descenders 9; x_ascenders 11">
|
||||
<span class='ocrx_word' id='word_1_84' title='bbox 349 755 367 774; x_wconf 42'>©</span>
|
||||
<span class='ocrx_word' id='word_1_85' title='bbox 383 748 573 784; x_wconf 90'>Ultra-fast</span>
|
||||
<span class='ocrx_word' id='word_1_86' title='bbox 616 749 677 784; x_wconf 9'>32”</span>
|
||||
<span class='ocrx_word' id='word_1_84' title='bbox 349 755 367 774; x_wconf 50'>©</span>
|
||||
<span class='ocrx_word' id='word_1_85' title='bbox 383 748 573 784; x_wconf 91'>Ultra-fast</span>
|
||||
<span class='ocrx_word' id='word_1_86' title='bbox 616 749 677 784; x_wconf 10'>32”</span>
|
||||
<span class='ocrx_word' id='word_1_87' title='bbox 694 748 775 784; x_wconf 96'>disk</span>
|
||||
<span class='ocrx_word' id='word_1_88' title='bbox 790 748 887 784; x_wconf 96'>drive</span>
|
||||
<span class='ocrx_word' id='word_1_89' title='bbox 901 754 1012 785; x_wconf 96'>stores</span>
|
||||
@@ -150,7 +150,7 @@
|
||||
<span class='ocrx_word' id='word_1_95' title='bbox 1638 748 1746 784; x_wconf 96'>holds</span>
|
||||
<span class='ocrx_word' id='word_1_96' title='bbox 1761 759 1844 784; x_wconf 96'>over</span>
|
||||
<span class='ocrx_word' id='word_1_97' title='bbox 1859 749 2000 791; x_wconf 96'>110,000</span>
|
||||
<span class='ocrx_word' id='word_1_98' title='bbox 2013 753 2117 784; x_wconf 97'>notes</span>
|
||||
<span class='ocrx_word' id='word_1_98' title='bbox 2013 753 2117 784; x_wconf 96'>notes</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -165,13 +165,13 @@
|
||||
<div class='ocr_carea' id='block_1_7' title="bbox 349 855 2030 1016">
|
||||
<p class='ocr_par' id='par_1_9' lang='eng' title="bbox 349 855 2030 1016">
|
||||
<span class='ocr_header' id='line_1_12' title="bbox 350 855 1638 900; baseline 0.001 -9; x_size 45; x_descenders 9; x_ascenders 11">
|
||||
<span class='ocrx_word' id='word_1_101' title='bbox 350 863 367 881; x_wconf 51'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_102' title='bbox 383 856 464 891; x_wconf 95'>One</span>
|
||||
<span class='ocrx_word' id='word_1_101' title='bbox 350 863 367 881; x_wconf 46'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_102' title='bbox 383 856 464 891; x_wconf 96'>One</span>
|
||||
<span class='ocrx_word' id='word_1_103' title='bbox 478 866 520 891; x_wconf 95'>or</span>
|
||||
<span class='ocrx_word' id='word_1_104' title='bbox 534 855 580 891; x_wconf 96'>all</span>
|
||||
<span class='ocrx_word' id='word_1_105' title='bbox 594 856 712 892; x_wconf 95'>tracks</span>
|
||||
<span class='ocrx_word' id='word_1_106' title='bbox 726 867 811 900; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_107' title='bbox 823 856 869 892; x_wconf 85'>be</span>
|
||||
<span class='ocrx_word' id='word_1_107' title='bbox 823 856 869 892; x_wconf 84'>be</span>
|
||||
<span class='ocrx_word' id='word_1_108' title='bbox 882 856 1212 892; x_wconf 96'>TRANSPOSED</span>
|
||||
<span class='ocrx_word' id='word_1_109' title='bbox 1227 861 1264 892; x_wconf 96'>at</span>
|
||||
<span class='ocrx_word' id='word_1_110' title='bbox 1277 856 1338 892; x_wconf 96'>the</span>
|
||||
@@ -256,8 +256,8 @@
|
||||
<div class='ocr_carea' id='block_1_12' title="bbox 346 1288 1239 1491">
|
||||
<p class='ocr_par' id='par_1_14' lang='eng' title="bbox 346 1288 749 1329">
|
||||
<span class='ocr_line' id='line_1_19' title="bbox 346 1288 749 1329; baseline 0.002 -9; x_size 42; x_descenders 9; x_ascenders 10">
|
||||
<span class='ocrx_word' id='word_1_158' title='bbox 346 1288 535 1329; x_wconf 96'>Recording</span>
|
||||
<span class='ocrx_word' id='word_1_159' title='bbox 547 1298 567 1321; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_158' title='bbox 346 1288 535 1329; x_wconf 95'>Recording</span>
|
||||
<span class='ocrx_word' id='word_1_159' title='bbox 547 1298 567 1321; x_wconf 95'>a</span>
|
||||
<span class='ocrx_word' id='word_1_160' title='bbox 579 1288 749 1328; x_wconf 96'>Sequence</span>
|
||||
</span>
|
||||
</p>
|
||||
@@ -278,8 +278,8 @@
|
||||
<span class='ocrx_word' id='word_1_170' title='bbox 346 1379 411 1406; x_wconf 96'>then</span>
|
||||
<span class='ocrx_word' id='word_1_171' title='bbox 422 1378 483 1412; x_wconf 96'>play</span>
|
||||
<span class='ocrx_word' id='word_1_172' title='bbox 493 1387 562 1412; x_wconf 96'>your</span>
|
||||
<span class='ocrx_word' id='word_1_173' title='bbox 572 1379 659 1405; x_wconf 95'>MIDI</span>
|
||||
<span class='ocrx_word' id='word_1_174' title='bbox 671 1379 810 1412; x_wconf 95'>keyboard</span>
|
||||
<span class='ocrx_word' id='word_1_173' title='bbox 572 1379 659 1405; x_wconf 96'>MIDI</span>
|
||||
<span class='ocrx_word' id='word_1_174' title='bbox 671 1379 810 1412; x_wconf 96'>keyboard</span>
|
||||
<span class='ocrx_word' id='word_1_175' title='bbox 821 1379 848 1406; x_wconf 96'>in</span>
|
||||
<span class='ocrx_word' id='word_1_176' title='bbox 858 1379 923 1406; x_wconf 96'>time</span>
|
||||
<span class='ocrx_word' id='word_1_177' title='bbox 934 1384 963 1406; x_wconf 96'>to</span>
|
||||
@@ -288,7 +288,7 @@
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_22' title="bbox 347 1417 1232 1452; baseline 0.002 -8; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_180' title='bbox 347 1417 414 1445; x_wconf 96'>click</span>
|
||||
<span class='ocrx_word' id='word_1_181' title='bbox 425 1418 506 1445; x_wconf 91'>track.</span>
|
||||
<span class='ocrx_word' id='word_1_181' title='bbox 425 1418 506 1445; x_wconf 92'>track.</span>
|
||||
<span class='ocrx_word' id='word_1_182' title='bbox 519 1418 609 1445; x_wconf 96'>When</span>
|
||||
<span class='ocrx_word' id='word_1_183' title='bbox 620 1419 665 1445; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_184' title='bbox 676 1426 810 1452; x_wconf 96'>sequence</span>
|
||||
@@ -297,7 +297,7 @@
|
||||
<span class='ocrx_word' id='word_1_187' title='bbox 995 1419 1101 1446; x_wconf 96'>around</span>
|
||||
<span class='ocrx_word' id='word_1_188' title='bbox 1112 1423 1141 1446; x_wconf 96'>to</span>
|
||||
<span class='ocrx_word' id='word_1_189' title='bbox 1152 1419 1201 1446; x_wconf 96'>bar</span>
|
||||
<span class='ocrx_word' id='word_1_190' title='bbox 1213 1419 1232 1450; x_wconf 88'>1,</span>
|
||||
<span class='ocrx_word' id='word_1_190' title='bbox 1213 1419 1232 1450; x_wconf 85'>1,</span>
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_23' title="bbox 346 1457 1223 1491; baseline 0.001 -7; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_191' title='bbox 346 1457 430 1490; x_wconf 16'>you’</span>
|
||||
@@ -620,7 +620,7 @@
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_50' title="bbox 1295 1565 1577 1593; baseline 0.004 -1; x_size 34.748871; x_descenders 6.7488689; x_ascenders 9">
|
||||
<span class='ocrx_word' id='word_1_423' title='bbox 1295 1565 1441 1592; x_wconf 96'>unwanted</span>
|
||||
<span class='ocrx_word' id='word_1_424' title='bbox 1452 1565 1577 1593; x_wconf 94'>sections,</span>
|
||||
<span class='ocrx_word' id='word_1_424' title='bbox 1452 1565 1577 1593; x_wconf 93'>sections,</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -668,8 +668,8 @@
|
||||
<span class='ocrx_word' id='word_1_454' title='bbox 1458 1768 1562 1796; x_wconf 96'>section</span>
|
||||
<span class='ocrx_word' id='word_1_455' title='bbox 1574 1769 1666 1802; x_wconf 96'>(verse,</span>
|
||||
<span class='ocrx_word' id='word_1_456' title='bbox 1679 1769 1788 1801; x_wconf 96'>chorus,</span>
|
||||
<span class='ocrx_word' id='word_1_457' title='bbox 1800 1769 1865 1802; x_wconf 95'>etc.)</span>
|
||||
<span class='ocrx_word' id='word_1_458' title='bbox 1876 1768 1904 1795; x_wconf 95'>in</span>
|
||||
<span class='ocrx_word' id='word_1_457' title='bbox 1800 1769 1865 1802; x_wconf 96'>etc.)</span>
|
||||
<span class='ocrx_word' id='word_1_458' title='bbox 1876 1768 1904 1795; x_wconf 96'>in</span>
|
||||
<span class='ocrx_word' id='word_1_459' title='bbox 1914 1768 2066 1796; x_wconf 96'>individual</span>
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_55' title="bbox 1296 1808 2215 1841; baseline 0 -6; x_size 32; x_descenders 5; x_ascenders 8">
|
||||
@@ -697,7 +697,7 @@
|
||||
<span class='ocrx_word' id='word_1_477' title='bbox 1377 1887 1412 1914; x_wconf 96'>all</span>
|
||||
<span class='ocrx_word' id='word_1_478' title='bbox 1422 1887 1468 1914; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_479' title='bbox 1478 1891 1552 1920; x_wconf 96'>parts</span>
|
||||
<span class='ocrx_word' id='word_1_480' title='bbox 1563 1887 1621 1914; x_wconf 96'>into</span>
|
||||
<span class='ocrx_word' id='word_1_480' title='bbox 1563 1887 1621 1914; x_wconf 95'>into</span>
|
||||
<span class='ocrx_word' id='word_1_481' title='bbox 1632 1895 1649 1914; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_482' title='bbox 1659 1895 1718 1914; x_wconf 96'>new</span>
|
||||
<span class='ocrx_word' id='word_1_483' title='bbox 1729 1895 1870 1920; x_wconf 96'>sequence.</span>
|
||||
@@ -829,9 +829,9 @@
|
||||
<div class='ocr_carea' id='block_1_23' title="bbox 347 2427 2145 2507">
|
||||
<p class='ocr_par' id='par_1_31' lang='eng' title="bbox 347 2427 2145 2507">
|
||||
<span class='ocr_header' id='line_1_67' title="bbox 347 2427 1468 2461; baseline 0.002 -8; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_573' title='bbox 347 2432 361 2446; x_wconf 77'>*</span>
|
||||
<span class='ocrx_word' id='word_1_573' title='bbox 347 2432 361 2446; x_wconf 75'>*</span>
|
||||
<span class='ocrx_word' id='word_1_574' title='bbox 373 2427 612 2454; x_wconf 91'>Non-destructive</span>
|
||||
<span class='ocrx_word' id='word_1_575' title='bbox 622 2427 914 2461; x_wconf 84'>recording—existing</span>
|
||||
<span class='ocrx_word' id='word_1_575' title='bbox 622 2427 914 2461; x_wconf 87'>recording—existing</span>
|
||||
<span class='ocrx_word' id='word_1_576' title='bbox 924 2432 1002 2455; x_wconf 96'>notes</span>
|
||||
<span class='ocrx_word' id='word_1_577' title='bbox 1013 2436 1057 2455; x_wconf 96'>are</span>
|
||||
<span class='ocrx_word' id='word_1_578' title='bbox 1068 2432 1116 2455; x_wconf 96'>not</span>
|
||||
@@ -912,12 +912,12 @@
|
||||
<div class='ocr_carea' id='block_1_27' title="bbox 347 2648 2100 2727">
|
||||
<p class='ocr_par' id='par_1_35' lang='eng' title="bbox 347 2648 2100 2727">
|
||||
<span class='ocr_header' id='line_1_72' title="bbox 347 2648 1664 2682; baseline 0.001 -7; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_630' title='bbox 347 2654 360 2667; x_wconf 47'>®</span>
|
||||
<span class='ocrx_word' id='word_1_631' title='bbox 372 2648 483 2675; x_wconf 96'>Utilizes</span>
|
||||
<span class='ocrx_word' id='word_1_630' title='bbox 347 2654 360 2667; x_wconf 45'>®</span>
|
||||
<span class='ocrx_word' id='word_1_631' title='bbox 372 2648 483 2675; x_wconf 94'>Utilizes</span>
|
||||
<span class='ocrx_word' id='word_1_632' title='bbox 493 2648 564 2680; x_wconf 96'>ultra</span>
|
||||
<span class='ocrx_word' id='word_1_633' title='bbox 573 2648 744 2682; x_wconf 96'>high-speed,</span>
|
||||
<span class='ocrx_word' id='word_1_634' title='bbox 766 2649 772 2676; x_wconf 95'>8</span>
|
||||
<span class='ocrx_word' id='word_1_635' title='bbox 783 2649 862 2675; x_wconf 94'>MHz</span>
|
||||
<span class='ocrx_word' id='word_1_635' title='bbox 783 2649 862 2675; x_wconf 95'>MHz</span>
|
||||
<span class='ocrx_word' id='word_1_636' title='bbox 873 2649 954 2676; x_wconf 95'>80186</span>
|
||||
<span class='ocrx_word' id='word_1_637' title='bbox 965 2649 994 2676; x_wconf 96'>16</span>
|
||||
<span class='ocrx_word' id='word_1_638' title='bbox 1004 2648 1043 2676; x_wconf 96'>bit</span>
|
||||
@@ -928,12 +928,12 @@
|
||||
<span class='ocrx_word' id='word_1_643' title='bbox 1512 2648 1664 2682; x_wconf 96'>operation.</span>
|
||||
</span>
|
||||
<span class='ocr_header' id='line_1_73' title="bbox 347 2694 2100 2727; baseline 0.001 -7; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_644' title='bbox 347 2699 361 2713; x_wconf 52'>*</span>
|
||||
<span class='ocrx_word' id='word_1_644' title='bbox 347 2699 361 2713; x_wconf 44'>*</span>
|
||||
<span class='ocrx_word' id='word_1_645' title='bbox 372 2694 504 2721; x_wconf 96'>TEMPO</span>
|
||||
<span class='ocrx_word' id='word_1_646' title='bbox 515 2702 578 2727; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_647' title='bbox 589 2694 623 2721; x_wconf 96'>be</span>
|
||||
<span class='ocrx_word' id='word_1_647' title='bbox 589 2694 623 2721; x_wconf 95'>be</span>
|
||||
<span class='ocrx_word' id='word_1_648' title='bbox 633 2694 764 2727; x_wconf 95'>specified</span>
|
||||
<span class='ocrx_word' id='word_1_649' title='bbox 774 2694 802 2721; x_wconf 92'>in</span>
|
||||
<span class='ocrx_word' id='word_1_649' title='bbox 774 2694 802 2721; x_wconf 93'>in</span>
|
||||
<span class='ocrx_word' id='word_1_650' title='bbox 814 2695 1172 2722; x_wconf 91'>BEATS-PER-MINUTE</span>
|
||||
<span class='ocrx_word' id='word_1_651' title='bbox 1183 2703 1215 2722; x_wconf 93'>or</span>
|
||||
<span class='ocrx_word' id='word_1_652' title='bbox 1225 2695 1567 2722; x_wconf 92'>FRAMES-PER-BEAT</span>
|
||||
@@ -995,7 +995,7 @@
|
||||
<div class='ocr_carea' id='block_1_31' title="bbox 347 2861 1792 2940">
|
||||
<p class='ocr_par' id='par_1_39' lang='eng' title="bbox 347 2861 1792 2940">
|
||||
<span class='ocr_header' id='line_1_77' title="bbox 347 2861 1792 2895; baseline 0.001 -8; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_687' title='bbox 347 2866 360 2880; x_wconf 62'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_687' title='bbox 347 2866 360 2880; x_wconf 59'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_688' title='bbox 372 2861 504 2887; x_wconf 96'>TEMPO</span>
|
||||
<span class='ocrx_word' id='word_1_689' title='bbox 515 2861 696 2888; x_wconf 96'>CHANGES</span>
|
||||
<span class='ocrx_word' id='word_1_690' title='bbox 707 2869 771 2894; x_wconf 96'>may</span>
|
||||
@@ -1011,7 +1011,7 @@
|
||||
<span class='ocrx_word' id='word_1_700' title='bbox 1679 2861 1792 2888; x_wconf 84'>desired.</span>
|
||||
</span>
|
||||
<span class='ocr_header' id='line_1_78' title="bbox 347 2906 1507 2940; baseline 0.002 -8; x_size 33; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_701' title='bbox 347 2911 360 2925; x_wconf 76'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_701' title='bbox 347 2911 360 2925; x_wconf 79'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_702' title='bbox 371 2906 434 2938; x_wconf 96'>Any</span>
|
||||
<span class='ocrx_word' id='word_1_703' title='bbox 444 2906 539 2932; x_wconf 96'>TIME</span>
|
||||
<span class='ocrx_word' id='word_1_704' title='bbox 550 2906 763 2933; x_wconf 96'>SIGNATURE</span>
|
||||
@@ -1022,8 +1022,8 @@
|
||||
<span class='ocrx_word' id='word_1_709' title='bbox 1046 2915 1109 2940; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_710' title='bbox 1120 2907 1154 2934; x_wconf 96'>be</span>
|
||||
<span class='ocrx_word' id='word_1_711' title='bbox 1164 2907 1288 2940; x_wconf 96'>changed</span>
|
||||
<span class='ocrx_word' id='word_1_712' title='bbox 1299 2907 1393 2934; x_wconf 96'>within</span>
|
||||
<span class='ocrx_word' id='word_1_713' title='bbox 1404 2915 1420 2934; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_712' title='bbox 1299 2907 1393 2934; x_wconf 95'>within</span>
|
||||
<span class='ocrx_word' id='word_1_713' title='bbox 1404 2915 1420 2934; x_wconf 95'>a</span>
|
||||
<span class='ocrx_word' id='word_1_714' title='bbox 1431 2915 1507 2940; x_wconf 96'>song.</span>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
-1
@@ -120,4 +120,3 @@ Linn Electronics, Inc.
|
||||
|
||||
18720 Oxnard Street, Tarzana, CA 91356
|
||||
(818) 708-8131 TELEX #298949 LINN UR
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
-1
@@ -120,4 +120,3 @@ Linn Electronics, Inc.
|
||||
|
||||
18720 Oxnard Street, Tarzana, CA 91356
|
||||
(818) 708-8131 TELEX #298949 LINN UR
|
||||
|
||||
+31
-31
@@ -5,11 +5,11 @@
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
||||
<meta name='ocr-system' content='tesseract 5.0.0-beta-20210916-12-g19cc9' />
|
||||
<meta name='ocr-system' content='tesseract 5.3.2' />
|
||||
<meta name='ocr-capabilities' content='ocr_page ocr_carea ocr_par ocr_line ocrx_word ocrp_wconf'/>
|
||||
</head>
|
||||
<body>
|
||||
<div class='ocr_page' id='page_1' title='image "/tmp/ocrmypdf.io.8je4vgpg/000002_ocr.png"; bbox 0 0 2550 3300; ppageno 0'>
|
||||
<div class='ocr_page' id='page_1' title='image "/tmp/ocrmypdf.io.q0nk4qy2/000002_ocr.png"; bbox 0 0 2550 3300; ppageno 0; scan_res 300 300'>
|
||||
<div class='ocr_carea' id='block_1_1' title="bbox 582 131 1968 303">
|
||||
<p class='ocr_par' id='par_1_1' lang='eng' title="bbox 582 131 1968 303">
|
||||
<span class='ocr_header' id='line_1_1' title="bbox 882 131 1657 217; baseline 0.001 -17; x_size 85; x_descenders 16; x_ascenders 19">
|
||||
@@ -136,9 +136,9 @@
|
||||
<div class='ocr_carea' id='block_1_5' title="bbox 349 748 2117 793">
|
||||
<p class='ocr_par' id='par_1_7' lang='eng' title="bbox 349 748 2117 793">
|
||||
<span class='ocr_header' id='line_1_10' title="bbox 349 748 2117 793; baseline 0 -9; x_size 45; x_descenders 9; x_ascenders 11">
|
||||
<span class='ocrx_word' id='word_1_84' title='bbox 349 755 367 774; x_wconf 42'>©</span>
|
||||
<span class='ocrx_word' id='word_1_85' title='bbox 383 748 573 784; x_wconf 90'>Ultra-fast</span>
|
||||
<span class='ocrx_word' id='word_1_86' title='bbox 616 749 677 784; x_wconf 9'>32”</span>
|
||||
<span class='ocrx_word' id='word_1_84' title='bbox 349 755 367 774; x_wconf 50'>©</span>
|
||||
<span class='ocrx_word' id='word_1_85' title='bbox 383 748 573 784; x_wconf 91'>Ultra-fast</span>
|
||||
<span class='ocrx_word' id='word_1_86' title='bbox 616 749 677 784; x_wconf 10'>32”</span>
|
||||
<span class='ocrx_word' id='word_1_87' title='bbox 694 748 775 784; x_wconf 96'>disk</span>
|
||||
<span class='ocrx_word' id='word_1_88' title='bbox 790 748 887 784; x_wconf 96'>drive</span>
|
||||
<span class='ocrx_word' id='word_1_89' title='bbox 901 754 1012 785; x_wconf 96'>stores</span>
|
||||
@@ -150,7 +150,7 @@
|
||||
<span class='ocrx_word' id='word_1_95' title='bbox 1638 748 1746 784; x_wconf 96'>holds</span>
|
||||
<span class='ocrx_word' id='word_1_96' title='bbox 1761 759 1844 784; x_wconf 96'>over</span>
|
||||
<span class='ocrx_word' id='word_1_97' title='bbox 1859 749 2000 791; x_wconf 96'>110,000</span>
|
||||
<span class='ocrx_word' id='word_1_98' title='bbox 2013 753 2117 784; x_wconf 97'>notes</span>
|
||||
<span class='ocrx_word' id='word_1_98' title='bbox 2013 753 2117 784; x_wconf 96'>notes</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -165,13 +165,13 @@
|
||||
<div class='ocr_carea' id='block_1_7' title="bbox 349 855 2030 1016">
|
||||
<p class='ocr_par' id='par_1_9' lang='eng' title="bbox 349 855 2030 1016">
|
||||
<span class='ocr_header' id='line_1_12' title="bbox 350 855 1638 900; baseline 0.001 -9; x_size 45; x_descenders 9; x_ascenders 11">
|
||||
<span class='ocrx_word' id='word_1_101' title='bbox 350 863 367 881; x_wconf 51'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_102' title='bbox 383 856 464 891; x_wconf 95'>One</span>
|
||||
<span class='ocrx_word' id='word_1_101' title='bbox 350 863 367 881; x_wconf 46'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_102' title='bbox 383 856 464 891; x_wconf 96'>One</span>
|
||||
<span class='ocrx_word' id='word_1_103' title='bbox 478 866 520 891; x_wconf 95'>or</span>
|
||||
<span class='ocrx_word' id='word_1_104' title='bbox 534 855 580 891; x_wconf 96'>all</span>
|
||||
<span class='ocrx_word' id='word_1_105' title='bbox 594 856 712 892; x_wconf 95'>tracks</span>
|
||||
<span class='ocrx_word' id='word_1_106' title='bbox 726 867 811 900; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_107' title='bbox 823 856 869 892; x_wconf 85'>be</span>
|
||||
<span class='ocrx_word' id='word_1_107' title='bbox 823 856 869 892; x_wconf 84'>be</span>
|
||||
<span class='ocrx_word' id='word_1_108' title='bbox 882 856 1212 892; x_wconf 96'>TRANSPOSED</span>
|
||||
<span class='ocrx_word' id='word_1_109' title='bbox 1227 861 1264 892; x_wconf 96'>at</span>
|
||||
<span class='ocrx_word' id='word_1_110' title='bbox 1277 856 1338 892; x_wconf 96'>the</span>
|
||||
@@ -256,8 +256,8 @@
|
||||
<div class='ocr_carea' id='block_1_12' title="bbox 346 1288 1239 1491">
|
||||
<p class='ocr_par' id='par_1_14' lang='eng' title="bbox 346 1288 749 1329">
|
||||
<span class='ocr_line' id='line_1_19' title="bbox 346 1288 749 1329; baseline 0.002 -9; x_size 42; x_descenders 9; x_ascenders 10">
|
||||
<span class='ocrx_word' id='word_1_158' title='bbox 346 1288 535 1329; x_wconf 96'>Recording</span>
|
||||
<span class='ocrx_word' id='word_1_159' title='bbox 547 1298 567 1321; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_158' title='bbox 346 1288 535 1329; x_wconf 95'>Recording</span>
|
||||
<span class='ocrx_word' id='word_1_159' title='bbox 547 1298 567 1321; x_wconf 95'>a</span>
|
||||
<span class='ocrx_word' id='word_1_160' title='bbox 579 1288 749 1328; x_wconf 96'>Sequence</span>
|
||||
</span>
|
||||
</p>
|
||||
@@ -278,8 +278,8 @@
|
||||
<span class='ocrx_word' id='word_1_170' title='bbox 346 1379 411 1406; x_wconf 96'>then</span>
|
||||
<span class='ocrx_word' id='word_1_171' title='bbox 422 1378 483 1412; x_wconf 96'>play</span>
|
||||
<span class='ocrx_word' id='word_1_172' title='bbox 493 1387 562 1412; x_wconf 96'>your</span>
|
||||
<span class='ocrx_word' id='word_1_173' title='bbox 572 1379 659 1405; x_wconf 95'>MIDI</span>
|
||||
<span class='ocrx_word' id='word_1_174' title='bbox 671 1379 810 1412; x_wconf 95'>keyboard</span>
|
||||
<span class='ocrx_word' id='word_1_173' title='bbox 572 1379 659 1405; x_wconf 96'>MIDI</span>
|
||||
<span class='ocrx_word' id='word_1_174' title='bbox 671 1379 810 1412; x_wconf 96'>keyboard</span>
|
||||
<span class='ocrx_word' id='word_1_175' title='bbox 821 1379 848 1406; x_wconf 96'>in</span>
|
||||
<span class='ocrx_word' id='word_1_176' title='bbox 858 1379 923 1406; x_wconf 96'>time</span>
|
||||
<span class='ocrx_word' id='word_1_177' title='bbox 934 1384 963 1406; x_wconf 96'>to</span>
|
||||
@@ -288,7 +288,7 @@
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_22' title="bbox 347 1417 1232 1452; baseline 0.002 -8; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_180' title='bbox 347 1417 414 1445; x_wconf 96'>click</span>
|
||||
<span class='ocrx_word' id='word_1_181' title='bbox 425 1418 506 1445; x_wconf 91'>track.</span>
|
||||
<span class='ocrx_word' id='word_1_181' title='bbox 425 1418 506 1445; x_wconf 92'>track.</span>
|
||||
<span class='ocrx_word' id='word_1_182' title='bbox 519 1418 609 1445; x_wconf 96'>When</span>
|
||||
<span class='ocrx_word' id='word_1_183' title='bbox 620 1419 665 1445; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_184' title='bbox 676 1426 810 1452; x_wconf 96'>sequence</span>
|
||||
@@ -297,7 +297,7 @@
|
||||
<span class='ocrx_word' id='word_1_187' title='bbox 995 1419 1101 1446; x_wconf 96'>around</span>
|
||||
<span class='ocrx_word' id='word_1_188' title='bbox 1112 1423 1141 1446; x_wconf 96'>to</span>
|
||||
<span class='ocrx_word' id='word_1_189' title='bbox 1152 1419 1201 1446; x_wconf 96'>bar</span>
|
||||
<span class='ocrx_word' id='word_1_190' title='bbox 1213 1419 1232 1450; x_wconf 88'>1,</span>
|
||||
<span class='ocrx_word' id='word_1_190' title='bbox 1213 1419 1232 1450; x_wconf 85'>1,</span>
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_23' title="bbox 346 1457 1223 1491; baseline 0.001 -7; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_191' title='bbox 346 1457 430 1490; x_wconf 16'>you’</span>
|
||||
@@ -620,7 +620,7 @@
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_50' title="bbox 1295 1565 1577 1593; baseline 0.004 -1; x_size 34.748871; x_descenders 6.7488689; x_ascenders 9">
|
||||
<span class='ocrx_word' id='word_1_423' title='bbox 1295 1565 1441 1592; x_wconf 96'>unwanted</span>
|
||||
<span class='ocrx_word' id='word_1_424' title='bbox 1452 1565 1577 1593; x_wconf 94'>sections,</span>
|
||||
<span class='ocrx_word' id='word_1_424' title='bbox 1452 1565 1577 1593; x_wconf 93'>sections,</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -668,8 +668,8 @@
|
||||
<span class='ocrx_word' id='word_1_454' title='bbox 1458 1768 1562 1796; x_wconf 96'>section</span>
|
||||
<span class='ocrx_word' id='word_1_455' title='bbox 1574 1769 1666 1802; x_wconf 96'>(verse,</span>
|
||||
<span class='ocrx_word' id='word_1_456' title='bbox 1679 1769 1788 1801; x_wconf 96'>chorus,</span>
|
||||
<span class='ocrx_word' id='word_1_457' title='bbox 1800 1769 1865 1802; x_wconf 95'>etc.)</span>
|
||||
<span class='ocrx_word' id='word_1_458' title='bbox 1876 1768 1904 1795; x_wconf 95'>in</span>
|
||||
<span class='ocrx_word' id='word_1_457' title='bbox 1800 1769 1865 1802; x_wconf 96'>etc.)</span>
|
||||
<span class='ocrx_word' id='word_1_458' title='bbox 1876 1768 1904 1795; x_wconf 96'>in</span>
|
||||
<span class='ocrx_word' id='word_1_459' title='bbox 1914 1768 2066 1796; x_wconf 96'>individual</span>
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_55' title="bbox 1296 1808 2215 1841; baseline 0 -6; x_size 32; x_descenders 5; x_ascenders 8">
|
||||
@@ -697,7 +697,7 @@
|
||||
<span class='ocrx_word' id='word_1_477' title='bbox 1377 1887 1412 1914; x_wconf 96'>all</span>
|
||||
<span class='ocrx_word' id='word_1_478' title='bbox 1422 1887 1468 1914; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_479' title='bbox 1478 1891 1552 1920; x_wconf 96'>parts</span>
|
||||
<span class='ocrx_word' id='word_1_480' title='bbox 1563 1887 1621 1914; x_wconf 96'>into</span>
|
||||
<span class='ocrx_word' id='word_1_480' title='bbox 1563 1887 1621 1914; x_wconf 95'>into</span>
|
||||
<span class='ocrx_word' id='word_1_481' title='bbox 1632 1895 1649 1914; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_482' title='bbox 1659 1895 1718 1914; x_wconf 96'>new</span>
|
||||
<span class='ocrx_word' id='word_1_483' title='bbox 1729 1895 1870 1920; x_wconf 96'>sequence.</span>
|
||||
@@ -829,9 +829,9 @@
|
||||
<div class='ocr_carea' id='block_1_23' title="bbox 347 2427 2145 2507">
|
||||
<p class='ocr_par' id='par_1_31' lang='eng' title="bbox 347 2427 2145 2507">
|
||||
<span class='ocr_header' id='line_1_67' title="bbox 347 2427 1468 2461; baseline 0.002 -8; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_573' title='bbox 347 2432 361 2446; x_wconf 77'>*</span>
|
||||
<span class='ocrx_word' id='word_1_573' title='bbox 347 2432 361 2446; x_wconf 75'>*</span>
|
||||
<span class='ocrx_word' id='word_1_574' title='bbox 373 2427 612 2454; x_wconf 91'>Non-destructive</span>
|
||||
<span class='ocrx_word' id='word_1_575' title='bbox 622 2427 914 2461; x_wconf 84'>recording—existing</span>
|
||||
<span class='ocrx_word' id='word_1_575' title='bbox 622 2427 914 2461; x_wconf 87'>recording—existing</span>
|
||||
<span class='ocrx_word' id='word_1_576' title='bbox 924 2432 1002 2455; x_wconf 96'>notes</span>
|
||||
<span class='ocrx_word' id='word_1_577' title='bbox 1013 2436 1057 2455; x_wconf 96'>are</span>
|
||||
<span class='ocrx_word' id='word_1_578' title='bbox 1068 2432 1116 2455; x_wconf 96'>not</span>
|
||||
@@ -912,12 +912,12 @@
|
||||
<div class='ocr_carea' id='block_1_27' title="bbox 347 2648 2100 2727">
|
||||
<p class='ocr_par' id='par_1_35' lang='eng' title="bbox 347 2648 2100 2727">
|
||||
<span class='ocr_header' id='line_1_72' title="bbox 347 2648 1664 2682; baseline 0.001 -7; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_630' title='bbox 347 2654 360 2667; x_wconf 47'>®</span>
|
||||
<span class='ocrx_word' id='word_1_631' title='bbox 372 2648 483 2675; x_wconf 96'>Utilizes</span>
|
||||
<span class='ocrx_word' id='word_1_630' title='bbox 347 2654 360 2667; x_wconf 45'>®</span>
|
||||
<span class='ocrx_word' id='word_1_631' title='bbox 372 2648 483 2675; x_wconf 94'>Utilizes</span>
|
||||
<span class='ocrx_word' id='word_1_632' title='bbox 493 2648 564 2680; x_wconf 96'>ultra</span>
|
||||
<span class='ocrx_word' id='word_1_633' title='bbox 573 2648 744 2682; x_wconf 96'>high-speed,</span>
|
||||
<span class='ocrx_word' id='word_1_634' title='bbox 766 2649 772 2676; x_wconf 95'>8</span>
|
||||
<span class='ocrx_word' id='word_1_635' title='bbox 783 2649 862 2675; x_wconf 94'>MHz</span>
|
||||
<span class='ocrx_word' id='word_1_635' title='bbox 783 2649 862 2675; x_wconf 95'>MHz</span>
|
||||
<span class='ocrx_word' id='word_1_636' title='bbox 873 2649 954 2676; x_wconf 95'>80186</span>
|
||||
<span class='ocrx_word' id='word_1_637' title='bbox 965 2649 994 2676; x_wconf 96'>16</span>
|
||||
<span class='ocrx_word' id='word_1_638' title='bbox 1004 2648 1043 2676; x_wconf 96'>bit</span>
|
||||
@@ -928,12 +928,12 @@
|
||||
<span class='ocrx_word' id='word_1_643' title='bbox 1512 2648 1664 2682; x_wconf 96'>operation.</span>
|
||||
</span>
|
||||
<span class='ocr_header' id='line_1_73' title="bbox 347 2694 2100 2727; baseline 0.001 -7; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_644' title='bbox 347 2699 361 2713; x_wconf 52'>*</span>
|
||||
<span class='ocrx_word' id='word_1_644' title='bbox 347 2699 361 2713; x_wconf 44'>*</span>
|
||||
<span class='ocrx_word' id='word_1_645' title='bbox 372 2694 504 2721; x_wconf 96'>TEMPO</span>
|
||||
<span class='ocrx_word' id='word_1_646' title='bbox 515 2702 578 2727; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_647' title='bbox 589 2694 623 2721; x_wconf 96'>be</span>
|
||||
<span class='ocrx_word' id='word_1_647' title='bbox 589 2694 623 2721; x_wconf 95'>be</span>
|
||||
<span class='ocrx_word' id='word_1_648' title='bbox 633 2694 764 2727; x_wconf 95'>specified</span>
|
||||
<span class='ocrx_word' id='word_1_649' title='bbox 774 2694 802 2721; x_wconf 92'>in</span>
|
||||
<span class='ocrx_word' id='word_1_649' title='bbox 774 2694 802 2721; x_wconf 93'>in</span>
|
||||
<span class='ocrx_word' id='word_1_650' title='bbox 814 2695 1172 2722; x_wconf 91'>BEATS-PER-MINUTE</span>
|
||||
<span class='ocrx_word' id='word_1_651' title='bbox 1183 2703 1215 2722; x_wconf 93'>or</span>
|
||||
<span class='ocrx_word' id='word_1_652' title='bbox 1225 2695 1567 2722; x_wconf 92'>FRAMES-PER-BEAT</span>
|
||||
@@ -995,7 +995,7 @@
|
||||
<div class='ocr_carea' id='block_1_31' title="bbox 347 2861 1792 2940">
|
||||
<p class='ocr_par' id='par_1_39' lang='eng' title="bbox 347 2861 1792 2940">
|
||||
<span class='ocr_header' id='line_1_77' title="bbox 347 2861 1792 2895; baseline 0.001 -8; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_687' title='bbox 347 2866 360 2880; x_wconf 62'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_687' title='bbox 347 2866 360 2880; x_wconf 59'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_688' title='bbox 372 2861 504 2887; x_wconf 96'>TEMPO</span>
|
||||
<span class='ocrx_word' id='word_1_689' title='bbox 515 2861 696 2888; x_wconf 96'>CHANGES</span>
|
||||
<span class='ocrx_word' id='word_1_690' title='bbox 707 2869 771 2894; x_wconf 96'>may</span>
|
||||
@@ -1011,7 +1011,7 @@
|
||||
<span class='ocrx_word' id='word_1_700' title='bbox 1679 2861 1792 2888; x_wconf 84'>desired.</span>
|
||||
</span>
|
||||
<span class='ocr_header' id='line_1_78' title="bbox 347 2906 1507 2940; baseline 0.002 -8; x_size 33; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_701' title='bbox 347 2911 360 2925; x_wconf 76'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_701' title='bbox 347 2911 360 2925; x_wconf 79'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_702' title='bbox 371 2906 434 2938; x_wconf 96'>Any</span>
|
||||
<span class='ocrx_word' id='word_1_703' title='bbox 444 2906 539 2932; x_wconf 96'>TIME</span>
|
||||
<span class='ocrx_word' id='word_1_704' title='bbox 550 2906 763 2933; x_wconf 96'>SIGNATURE</span>
|
||||
@@ -1022,8 +1022,8 @@
|
||||
<span class='ocrx_word' id='word_1_709' title='bbox 1046 2915 1109 2940; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_710' title='bbox 1120 2907 1154 2934; x_wconf 96'>be</span>
|
||||
<span class='ocrx_word' id='word_1_711' title='bbox 1164 2907 1288 2940; x_wconf 96'>changed</span>
|
||||
<span class='ocrx_word' id='word_1_712' title='bbox 1299 2907 1393 2934; x_wconf 96'>within</span>
|
||||
<span class='ocrx_word' id='word_1_713' title='bbox 1404 2915 1420 2934; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_712' title='bbox 1299 2907 1393 2934; x_wconf 95'>within</span>
|
||||
<span class='ocrx_word' id='word_1_713' title='bbox 1404 2915 1420 2934; x_wconf 95'>a</span>
|
||||
<span class='ocrx_word' id='word_1_714' title='bbox 1431 2915 1507 2940; x_wconf 96'>song.</span>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
-1
@@ -120,4 +120,3 @@ Linn Electronics, Inc.
|
||||
|
||||
18720 Oxnard Street, Tarzana, CA 91356
|
||||
(818) 708-8131 TELEX #298949 LINN UR
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
-1
@@ -120,4 +120,3 @@ Linn Electronics, Inc.
|
||||
|
||||
18720 Oxnard Street, Tarzana, CA 91356
|
||||
(818) 708-8131 TELEX #298949 LINN UR
|
||||
|
||||
+31
-31
@@ -5,11 +5,11 @@
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
||||
<meta name='ocr-system' content='tesseract 5.0.0-beta-20210916-12-g19cc9' />
|
||||
<meta name='ocr-system' content='tesseract 5.3.2' />
|
||||
<meta name='ocr-capabilities' content='ocr_page ocr_carea ocr_par ocr_line ocrx_word ocrp_wconf'/>
|
||||
</head>
|
||||
<body>
|
||||
<div class='ocr_page' id='page_1' title='image "/tmp/ocrmypdf.io.8je4vgpg/000003_ocr.png"; bbox 0 0 2550 3300; ppageno 0'>
|
||||
<div class='ocr_page' id='page_1' title='image "/tmp/ocrmypdf.io.q0nk4qy2/000003_ocr.png"; bbox 0 0 2550 3300; ppageno 0; scan_res 300 300'>
|
||||
<div class='ocr_carea' id='block_1_1' title="bbox 582 131 1968 303">
|
||||
<p class='ocr_par' id='par_1_1' lang='eng' title="bbox 582 131 1968 303">
|
||||
<span class='ocr_header' id='line_1_1' title="bbox 882 131 1657 217; baseline 0.001 -17; x_size 85; x_descenders 16; x_ascenders 19">
|
||||
@@ -136,9 +136,9 @@
|
||||
<div class='ocr_carea' id='block_1_5' title="bbox 349 748 2117 793">
|
||||
<p class='ocr_par' id='par_1_7' lang='eng' title="bbox 349 748 2117 793">
|
||||
<span class='ocr_header' id='line_1_10' title="bbox 349 748 2117 793; baseline 0 -9; x_size 45; x_descenders 9; x_ascenders 11">
|
||||
<span class='ocrx_word' id='word_1_84' title='bbox 349 755 367 774; x_wconf 42'>©</span>
|
||||
<span class='ocrx_word' id='word_1_85' title='bbox 383 748 573 784; x_wconf 90'>Ultra-fast</span>
|
||||
<span class='ocrx_word' id='word_1_86' title='bbox 616 749 677 784; x_wconf 9'>32”</span>
|
||||
<span class='ocrx_word' id='word_1_84' title='bbox 349 755 367 774; x_wconf 50'>©</span>
|
||||
<span class='ocrx_word' id='word_1_85' title='bbox 383 748 573 784; x_wconf 91'>Ultra-fast</span>
|
||||
<span class='ocrx_word' id='word_1_86' title='bbox 616 749 677 784; x_wconf 10'>32”</span>
|
||||
<span class='ocrx_word' id='word_1_87' title='bbox 694 748 775 784; x_wconf 96'>disk</span>
|
||||
<span class='ocrx_word' id='word_1_88' title='bbox 790 748 887 784; x_wconf 96'>drive</span>
|
||||
<span class='ocrx_word' id='word_1_89' title='bbox 901 754 1012 785; x_wconf 96'>stores</span>
|
||||
@@ -150,7 +150,7 @@
|
||||
<span class='ocrx_word' id='word_1_95' title='bbox 1638 748 1746 784; x_wconf 96'>holds</span>
|
||||
<span class='ocrx_word' id='word_1_96' title='bbox 1761 759 1844 784; x_wconf 96'>over</span>
|
||||
<span class='ocrx_word' id='word_1_97' title='bbox 1859 749 2000 791; x_wconf 96'>110,000</span>
|
||||
<span class='ocrx_word' id='word_1_98' title='bbox 2013 753 2117 784; x_wconf 97'>notes</span>
|
||||
<span class='ocrx_word' id='word_1_98' title='bbox 2013 753 2117 784; x_wconf 96'>notes</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -165,13 +165,13 @@
|
||||
<div class='ocr_carea' id='block_1_7' title="bbox 349 855 2030 1016">
|
||||
<p class='ocr_par' id='par_1_9' lang='eng' title="bbox 349 855 2030 1016">
|
||||
<span class='ocr_header' id='line_1_12' title="bbox 350 855 1638 900; baseline 0.001 -9; x_size 45; x_descenders 9; x_ascenders 11">
|
||||
<span class='ocrx_word' id='word_1_101' title='bbox 350 863 367 881; x_wconf 51'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_102' title='bbox 383 856 464 891; x_wconf 95'>One</span>
|
||||
<span class='ocrx_word' id='word_1_101' title='bbox 350 863 367 881; x_wconf 46'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_102' title='bbox 383 856 464 891; x_wconf 96'>One</span>
|
||||
<span class='ocrx_word' id='word_1_103' title='bbox 478 866 520 891; x_wconf 95'>or</span>
|
||||
<span class='ocrx_word' id='word_1_104' title='bbox 534 855 580 891; x_wconf 96'>all</span>
|
||||
<span class='ocrx_word' id='word_1_105' title='bbox 594 856 712 892; x_wconf 95'>tracks</span>
|
||||
<span class='ocrx_word' id='word_1_106' title='bbox 726 867 811 900; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_107' title='bbox 823 856 869 892; x_wconf 85'>be</span>
|
||||
<span class='ocrx_word' id='word_1_107' title='bbox 823 856 869 892; x_wconf 84'>be</span>
|
||||
<span class='ocrx_word' id='word_1_108' title='bbox 882 856 1212 892; x_wconf 96'>TRANSPOSED</span>
|
||||
<span class='ocrx_word' id='word_1_109' title='bbox 1227 861 1264 892; x_wconf 96'>at</span>
|
||||
<span class='ocrx_word' id='word_1_110' title='bbox 1277 856 1338 892; x_wconf 96'>the</span>
|
||||
@@ -256,8 +256,8 @@
|
||||
<div class='ocr_carea' id='block_1_12' title="bbox 346 1288 1239 1491">
|
||||
<p class='ocr_par' id='par_1_14' lang='eng' title="bbox 346 1288 749 1329">
|
||||
<span class='ocr_line' id='line_1_19' title="bbox 346 1288 749 1329; baseline 0.002 -9; x_size 42; x_descenders 9; x_ascenders 10">
|
||||
<span class='ocrx_word' id='word_1_158' title='bbox 346 1288 535 1329; x_wconf 96'>Recording</span>
|
||||
<span class='ocrx_word' id='word_1_159' title='bbox 547 1298 567 1321; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_158' title='bbox 346 1288 535 1329; x_wconf 95'>Recording</span>
|
||||
<span class='ocrx_word' id='word_1_159' title='bbox 547 1298 567 1321; x_wconf 95'>a</span>
|
||||
<span class='ocrx_word' id='word_1_160' title='bbox 579 1288 749 1328; x_wconf 96'>Sequence</span>
|
||||
</span>
|
||||
</p>
|
||||
@@ -278,8 +278,8 @@
|
||||
<span class='ocrx_word' id='word_1_170' title='bbox 346 1379 411 1406; x_wconf 96'>then</span>
|
||||
<span class='ocrx_word' id='word_1_171' title='bbox 422 1378 483 1412; x_wconf 96'>play</span>
|
||||
<span class='ocrx_word' id='word_1_172' title='bbox 493 1387 562 1412; x_wconf 96'>your</span>
|
||||
<span class='ocrx_word' id='word_1_173' title='bbox 572 1379 659 1405; x_wconf 95'>MIDI</span>
|
||||
<span class='ocrx_word' id='word_1_174' title='bbox 671 1379 810 1412; x_wconf 95'>keyboard</span>
|
||||
<span class='ocrx_word' id='word_1_173' title='bbox 572 1379 659 1405; x_wconf 96'>MIDI</span>
|
||||
<span class='ocrx_word' id='word_1_174' title='bbox 671 1379 810 1412; x_wconf 96'>keyboard</span>
|
||||
<span class='ocrx_word' id='word_1_175' title='bbox 821 1379 848 1406; x_wconf 96'>in</span>
|
||||
<span class='ocrx_word' id='word_1_176' title='bbox 858 1379 923 1406; x_wconf 96'>time</span>
|
||||
<span class='ocrx_word' id='word_1_177' title='bbox 934 1384 963 1406; x_wconf 96'>to</span>
|
||||
@@ -288,7 +288,7 @@
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_22' title="bbox 347 1417 1232 1452; baseline 0.002 -8; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_180' title='bbox 347 1417 414 1445; x_wconf 96'>click</span>
|
||||
<span class='ocrx_word' id='word_1_181' title='bbox 425 1418 506 1445; x_wconf 91'>track.</span>
|
||||
<span class='ocrx_word' id='word_1_181' title='bbox 425 1418 506 1445; x_wconf 92'>track.</span>
|
||||
<span class='ocrx_word' id='word_1_182' title='bbox 519 1418 609 1445; x_wconf 96'>When</span>
|
||||
<span class='ocrx_word' id='word_1_183' title='bbox 620 1419 665 1445; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_184' title='bbox 676 1426 810 1452; x_wconf 96'>sequence</span>
|
||||
@@ -297,7 +297,7 @@
|
||||
<span class='ocrx_word' id='word_1_187' title='bbox 995 1419 1101 1446; x_wconf 96'>around</span>
|
||||
<span class='ocrx_word' id='word_1_188' title='bbox 1112 1423 1141 1446; x_wconf 96'>to</span>
|
||||
<span class='ocrx_word' id='word_1_189' title='bbox 1152 1419 1201 1446; x_wconf 96'>bar</span>
|
||||
<span class='ocrx_word' id='word_1_190' title='bbox 1213 1419 1232 1450; x_wconf 88'>1,</span>
|
||||
<span class='ocrx_word' id='word_1_190' title='bbox 1213 1419 1232 1450; x_wconf 85'>1,</span>
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_23' title="bbox 346 1457 1223 1491; baseline 0.001 -7; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_191' title='bbox 346 1457 430 1490; x_wconf 16'>you’</span>
|
||||
@@ -620,7 +620,7 @@
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_50' title="bbox 1295 1565 1577 1593; baseline 0.004 -1; x_size 34.748871; x_descenders 6.7488689; x_ascenders 9">
|
||||
<span class='ocrx_word' id='word_1_423' title='bbox 1295 1565 1441 1592; x_wconf 96'>unwanted</span>
|
||||
<span class='ocrx_word' id='word_1_424' title='bbox 1452 1565 1577 1593; x_wconf 94'>sections,</span>
|
||||
<span class='ocrx_word' id='word_1_424' title='bbox 1452 1565 1577 1593; x_wconf 93'>sections,</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -668,8 +668,8 @@
|
||||
<span class='ocrx_word' id='word_1_454' title='bbox 1458 1768 1562 1796; x_wconf 96'>section</span>
|
||||
<span class='ocrx_word' id='word_1_455' title='bbox 1574 1769 1666 1802; x_wconf 96'>(verse,</span>
|
||||
<span class='ocrx_word' id='word_1_456' title='bbox 1679 1769 1788 1801; x_wconf 96'>chorus,</span>
|
||||
<span class='ocrx_word' id='word_1_457' title='bbox 1800 1769 1865 1802; x_wconf 95'>etc.)</span>
|
||||
<span class='ocrx_word' id='word_1_458' title='bbox 1876 1768 1904 1795; x_wconf 95'>in</span>
|
||||
<span class='ocrx_word' id='word_1_457' title='bbox 1800 1769 1865 1802; x_wconf 96'>etc.)</span>
|
||||
<span class='ocrx_word' id='word_1_458' title='bbox 1876 1768 1904 1795; x_wconf 96'>in</span>
|
||||
<span class='ocrx_word' id='word_1_459' title='bbox 1914 1768 2066 1796; x_wconf 96'>individual</span>
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_55' title="bbox 1296 1808 2215 1841; baseline 0 -6; x_size 32; x_descenders 5; x_ascenders 8">
|
||||
@@ -697,7 +697,7 @@
|
||||
<span class='ocrx_word' id='word_1_477' title='bbox 1377 1887 1412 1914; x_wconf 96'>all</span>
|
||||
<span class='ocrx_word' id='word_1_478' title='bbox 1422 1887 1468 1914; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_479' title='bbox 1478 1891 1552 1920; x_wconf 96'>parts</span>
|
||||
<span class='ocrx_word' id='word_1_480' title='bbox 1563 1887 1621 1914; x_wconf 96'>into</span>
|
||||
<span class='ocrx_word' id='word_1_480' title='bbox 1563 1887 1621 1914; x_wconf 95'>into</span>
|
||||
<span class='ocrx_word' id='word_1_481' title='bbox 1632 1895 1649 1914; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_482' title='bbox 1659 1895 1718 1914; x_wconf 96'>new</span>
|
||||
<span class='ocrx_word' id='word_1_483' title='bbox 1729 1895 1870 1920; x_wconf 96'>sequence.</span>
|
||||
@@ -829,9 +829,9 @@
|
||||
<div class='ocr_carea' id='block_1_23' title="bbox 347 2427 2145 2507">
|
||||
<p class='ocr_par' id='par_1_31' lang='eng' title="bbox 347 2427 2145 2507">
|
||||
<span class='ocr_header' id='line_1_67' title="bbox 347 2427 1468 2461; baseline 0.002 -8; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_573' title='bbox 347 2432 361 2446; x_wconf 77'>*</span>
|
||||
<span class='ocrx_word' id='word_1_573' title='bbox 347 2432 361 2446; x_wconf 75'>*</span>
|
||||
<span class='ocrx_word' id='word_1_574' title='bbox 373 2427 612 2454; x_wconf 91'>Non-destructive</span>
|
||||
<span class='ocrx_word' id='word_1_575' title='bbox 622 2427 914 2461; x_wconf 84'>recording—existing</span>
|
||||
<span class='ocrx_word' id='word_1_575' title='bbox 622 2427 914 2461; x_wconf 87'>recording—existing</span>
|
||||
<span class='ocrx_word' id='word_1_576' title='bbox 924 2432 1002 2455; x_wconf 96'>notes</span>
|
||||
<span class='ocrx_word' id='word_1_577' title='bbox 1013 2436 1057 2455; x_wconf 96'>are</span>
|
||||
<span class='ocrx_word' id='word_1_578' title='bbox 1068 2432 1116 2455; x_wconf 96'>not</span>
|
||||
@@ -912,12 +912,12 @@
|
||||
<div class='ocr_carea' id='block_1_27' title="bbox 347 2648 2100 2727">
|
||||
<p class='ocr_par' id='par_1_35' lang='eng' title="bbox 347 2648 2100 2727">
|
||||
<span class='ocr_header' id='line_1_72' title="bbox 347 2648 1664 2682; baseline 0.001 -7; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_630' title='bbox 347 2654 360 2667; x_wconf 47'>®</span>
|
||||
<span class='ocrx_word' id='word_1_631' title='bbox 372 2648 483 2675; x_wconf 96'>Utilizes</span>
|
||||
<span class='ocrx_word' id='word_1_630' title='bbox 347 2654 360 2667; x_wconf 45'>®</span>
|
||||
<span class='ocrx_word' id='word_1_631' title='bbox 372 2648 483 2675; x_wconf 94'>Utilizes</span>
|
||||
<span class='ocrx_word' id='word_1_632' title='bbox 493 2648 564 2680; x_wconf 96'>ultra</span>
|
||||
<span class='ocrx_word' id='word_1_633' title='bbox 573 2648 744 2682; x_wconf 96'>high-speed,</span>
|
||||
<span class='ocrx_word' id='word_1_634' title='bbox 766 2649 772 2676; x_wconf 95'>8</span>
|
||||
<span class='ocrx_word' id='word_1_635' title='bbox 783 2649 862 2675; x_wconf 94'>MHz</span>
|
||||
<span class='ocrx_word' id='word_1_635' title='bbox 783 2649 862 2675; x_wconf 95'>MHz</span>
|
||||
<span class='ocrx_word' id='word_1_636' title='bbox 873 2649 954 2676; x_wconf 95'>80186</span>
|
||||
<span class='ocrx_word' id='word_1_637' title='bbox 965 2649 994 2676; x_wconf 96'>16</span>
|
||||
<span class='ocrx_word' id='word_1_638' title='bbox 1004 2648 1043 2676; x_wconf 96'>bit</span>
|
||||
@@ -928,12 +928,12 @@
|
||||
<span class='ocrx_word' id='word_1_643' title='bbox 1512 2648 1664 2682; x_wconf 96'>operation.</span>
|
||||
</span>
|
||||
<span class='ocr_header' id='line_1_73' title="bbox 347 2694 2100 2727; baseline 0.001 -7; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_644' title='bbox 347 2699 361 2713; x_wconf 52'>*</span>
|
||||
<span class='ocrx_word' id='word_1_644' title='bbox 347 2699 361 2713; x_wconf 44'>*</span>
|
||||
<span class='ocrx_word' id='word_1_645' title='bbox 372 2694 504 2721; x_wconf 96'>TEMPO</span>
|
||||
<span class='ocrx_word' id='word_1_646' title='bbox 515 2702 578 2727; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_647' title='bbox 589 2694 623 2721; x_wconf 96'>be</span>
|
||||
<span class='ocrx_word' id='word_1_647' title='bbox 589 2694 623 2721; x_wconf 95'>be</span>
|
||||
<span class='ocrx_word' id='word_1_648' title='bbox 633 2694 764 2727; x_wconf 95'>specified</span>
|
||||
<span class='ocrx_word' id='word_1_649' title='bbox 774 2694 802 2721; x_wconf 92'>in</span>
|
||||
<span class='ocrx_word' id='word_1_649' title='bbox 774 2694 802 2721; x_wconf 93'>in</span>
|
||||
<span class='ocrx_word' id='word_1_650' title='bbox 814 2695 1172 2722; x_wconf 91'>BEATS-PER-MINUTE</span>
|
||||
<span class='ocrx_word' id='word_1_651' title='bbox 1183 2703 1215 2722; x_wconf 93'>or</span>
|
||||
<span class='ocrx_word' id='word_1_652' title='bbox 1225 2695 1567 2722; x_wconf 92'>FRAMES-PER-BEAT</span>
|
||||
@@ -995,7 +995,7 @@
|
||||
<div class='ocr_carea' id='block_1_31' title="bbox 347 2861 1792 2940">
|
||||
<p class='ocr_par' id='par_1_39' lang='eng' title="bbox 347 2861 1792 2940">
|
||||
<span class='ocr_header' id='line_1_77' title="bbox 347 2861 1792 2895; baseline 0.001 -8; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_687' title='bbox 347 2866 360 2880; x_wconf 62'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_687' title='bbox 347 2866 360 2880; x_wconf 59'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_688' title='bbox 372 2861 504 2887; x_wconf 96'>TEMPO</span>
|
||||
<span class='ocrx_word' id='word_1_689' title='bbox 515 2861 696 2888; x_wconf 96'>CHANGES</span>
|
||||
<span class='ocrx_word' id='word_1_690' title='bbox 707 2869 771 2894; x_wconf 96'>may</span>
|
||||
@@ -1011,7 +1011,7 @@
|
||||
<span class='ocrx_word' id='word_1_700' title='bbox 1679 2861 1792 2888; x_wconf 84'>desired.</span>
|
||||
</span>
|
||||
<span class='ocr_header' id='line_1_78' title="bbox 347 2906 1507 2940; baseline 0.002 -8; x_size 33; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_701' title='bbox 347 2911 360 2925; x_wconf 76'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_701' title='bbox 347 2911 360 2925; x_wconf 79'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_702' title='bbox 371 2906 434 2938; x_wconf 96'>Any</span>
|
||||
<span class='ocrx_word' id='word_1_703' title='bbox 444 2906 539 2932; x_wconf 96'>TIME</span>
|
||||
<span class='ocrx_word' id='word_1_704' title='bbox 550 2906 763 2933; x_wconf 96'>SIGNATURE</span>
|
||||
@@ -1022,8 +1022,8 @@
|
||||
<span class='ocrx_word' id='word_1_709' title='bbox 1046 2915 1109 2940; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_710' title='bbox 1120 2907 1154 2934; x_wconf 96'>be</span>
|
||||
<span class='ocrx_word' id='word_1_711' title='bbox 1164 2907 1288 2940; x_wconf 96'>changed</span>
|
||||
<span class='ocrx_word' id='word_1_712' title='bbox 1299 2907 1393 2934; x_wconf 96'>within</span>
|
||||
<span class='ocrx_word' id='word_1_713' title='bbox 1404 2915 1420 2934; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_712' title='bbox 1299 2907 1393 2934; x_wconf 95'>within</span>
|
||||
<span class='ocrx_word' id='word_1_713' title='bbox 1404 2915 1420 2934; x_wconf 95'>a</span>
|
||||
<span class='ocrx_word' id='word_1_714' title='bbox 1431 2915 1507 2940; x_wconf 96'>song.</span>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
-1
@@ -120,4 +120,3 @@ Linn Electronics, Inc.
|
||||
|
||||
18720 Oxnard Street, Tarzana, CA 91356
|
||||
(818) 708-8131 TELEX #298949 LINN UR
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
-1
@@ -120,4 +120,3 @@ Linn Electronics, Inc.
|
||||
|
||||
18720 Oxnard Street, Tarzana, CA 91356
|
||||
(818) 708-8131 TELEX #298949 LINN UR
|
||||
|
||||
+31
-31
@@ -5,11 +5,11 @@
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
||||
<meta name='ocr-system' content='tesseract 5.0.0-beta-20210916-12-g19cc9' />
|
||||
<meta name='ocr-system' content='tesseract 5.3.2' />
|
||||
<meta name='ocr-capabilities' content='ocr_page ocr_carea ocr_par ocr_line ocrx_word ocrp_wconf'/>
|
||||
</head>
|
||||
<body>
|
||||
<div class='ocr_page' id='page_1' title='image "/tmp/ocrmypdf.io.8je4vgpg/000004_ocr.png"; bbox 0 0 2550 3300; ppageno 0'>
|
||||
<div class='ocr_page' id='page_1' title='image "/tmp/ocrmypdf.io.q0nk4qy2/000004_ocr.png"; bbox 0 0 2550 3300; ppageno 0; scan_res 300 300'>
|
||||
<div class='ocr_carea' id='block_1_1' title="bbox 582 131 1968 303">
|
||||
<p class='ocr_par' id='par_1_1' lang='eng' title="bbox 582 131 1968 303">
|
||||
<span class='ocr_header' id='line_1_1' title="bbox 882 131 1657 217; baseline 0.001 -17; x_size 85; x_descenders 16; x_ascenders 19">
|
||||
@@ -136,9 +136,9 @@
|
||||
<div class='ocr_carea' id='block_1_5' title="bbox 349 748 2117 793">
|
||||
<p class='ocr_par' id='par_1_7' lang='eng' title="bbox 349 748 2117 793">
|
||||
<span class='ocr_header' id='line_1_10' title="bbox 349 748 2117 793; baseline 0 -9; x_size 45; x_descenders 9; x_ascenders 11">
|
||||
<span class='ocrx_word' id='word_1_84' title='bbox 349 755 367 774; x_wconf 42'>©</span>
|
||||
<span class='ocrx_word' id='word_1_85' title='bbox 383 748 573 784; x_wconf 90'>Ultra-fast</span>
|
||||
<span class='ocrx_word' id='word_1_86' title='bbox 616 749 677 784; x_wconf 9'>32”</span>
|
||||
<span class='ocrx_word' id='word_1_84' title='bbox 349 755 367 774; x_wconf 50'>©</span>
|
||||
<span class='ocrx_word' id='word_1_85' title='bbox 383 748 573 784; x_wconf 91'>Ultra-fast</span>
|
||||
<span class='ocrx_word' id='word_1_86' title='bbox 616 749 677 784; x_wconf 10'>32”</span>
|
||||
<span class='ocrx_word' id='word_1_87' title='bbox 694 748 775 784; x_wconf 96'>disk</span>
|
||||
<span class='ocrx_word' id='word_1_88' title='bbox 790 748 887 784; x_wconf 96'>drive</span>
|
||||
<span class='ocrx_word' id='word_1_89' title='bbox 901 754 1012 785; x_wconf 96'>stores</span>
|
||||
@@ -150,7 +150,7 @@
|
||||
<span class='ocrx_word' id='word_1_95' title='bbox 1638 748 1746 784; x_wconf 96'>holds</span>
|
||||
<span class='ocrx_word' id='word_1_96' title='bbox 1761 759 1844 784; x_wconf 96'>over</span>
|
||||
<span class='ocrx_word' id='word_1_97' title='bbox 1859 749 2000 791; x_wconf 96'>110,000</span>
|
||||
<span class='ocrx_word' id='word_1_98' title='bbox 2013 753 2117 784; x_wconf 97'>notes</span>
|
||||
<span class='ocrx_word' id='word_1_98' title='bbox 2013 753 2117 784; x_wconf 96'>notes</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -165,13 +165,13 @@
|
||||
<div class='ocr_carea' id='block_1_7' title="bbox 349 855 2030 1016">
|
||||
<p class='ocr_par' id='par_1_9' lang='eng' title="bbox 349 855 2030 1016">
|
||||
<span class='ocr_header' id='line_1_12' title="bbox 350 855 1638 900; baseline 0.001 -9; x_size 45; x_descenders 9; x_ascenders 11">
|
||||
<span class='ocrx_word' id='word_1_101' title='bbox 350 863 367 881; x_wconf 51'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_102' title='bbox 383 856 464 891; x_wconf 95'>One</span>
|
||||
<span class='ocrx_word' id='word_1_101' title='bbox 350 863 367 881; x_wconf 46'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_102' title='bbox 383 856 464 891; x_wconf 96'>One</span>
|
||||
<span class='ocrx_word' id='word_1_103' title='bbox 478 866 520 891; x_wconf 95'>or</span>
|
||||
<span class='ocrx_word' id='word_1_104' title='bbox 534 855 580 891; x_wconf 96'>all</span>
|
||||
<span class='ocrx_word' id='word_1_105' title='bbox 594 856 712 892; x_wconf 95'>tracks</span>
|
||||
<span class='ocrx_word' id='word_1_106' title='bbox 726 867 811 900; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_107' title='bbox 823 856 869 892; x_wconf 85'>be</span>
|
||||
<span class='ocrx_word' id='word_1_107' title='bbox 823 856 869 892; x_wconf 84'>be</span>
|
||||
<span class='ocrx_word' id='word_1_108' title='bbox 882 856 1212 892; x_wconf 96'>TRANSPOSED</span>
|
||||
<span class='ocrx_word' id='word_1_109' title='bbox 1227 861 1264 892; x_wconf 96'>at</span>
|
||||
<span class='ocrx_word' id='word_1_110' title='bbox 1277 856 1338 892; x_wconf 96'>the</span>
|
||||
@@ -256,8 +256,8 @@
|
||||
<div class='ocr_carea' id='block_1_12' title="bbox 346 1288 1239 1491">
|
||||
<p class='ocr_par' id='par_1_14' lang='eng' title="bbox 346 1288 749 1329">
|
||||
<span class='ocr_line' id='line_1_19' title="bbox 346 1288 749 1329; baseline 0.002 -9; x_size 42; x_descenders 9; x_ascenders 10">
|
||||
<span class='ocrx_word' id='word_1_158' title='bbox 346 1288 535 1329; x_wconf 96'>Recording</span>
|
||||
<span class='ocrx_word' id='word_1_159' title='bbox 547 1298 567 1321; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_158' title='bbox 346 1288 535 1329; x_wconf 95'>Recording</span>
|
||||
<span class='ocrx_word' id='word_1_159' title='bbox 547 1298 567 1321; x_wconf 95'>a</span>
|
||||
<span class='ocrx_word' id='word_1_160' title='bbox 579 1288 749 1328; x_wconf 96'>Sequence</span>
|
||||
</span>
|
||||
</p>
|
||||
@@ -278,8 +278,8 @@
|
||||
<span class='ocrx_word' id='word_1_170' title='bbox 346 1379 411 1406; x_wconf 96'>then</span>
|
||||
<span class='ocrx_word' id='word_1_171' title='bbox 422 1378 483 1412; x_wconf 96'>play</span>
|
||||
<span class='ocrx_word' id='word_1_172' title='bbox 493 1387 562 1412; x_wconf 96'>your</span>
|
||||
<span class='ocrx_word' id='word_1_173' title='bbox 572 1379 659 1405; x_wconf 95'>MIDI</span>
|
||||
<span class='ocrx_word' id='word_1_174' title='bbox 671 1379 810 1412; x_wconf 95'>keyboard</span>
|
||||
<span class='ocrx_word' id='word_1_173' title='bbox 572 1379 659 1405; x_wconf 96'>MIDI</span>
|
||||
<span class='ocrx_word' id='word_1_174' title='bbox 671 1379 810 1412; x_wconf 96'>keyboard</span>
|
||||
<span class='ocrx_word' id='word_1_175' title='bbox 821 1379 848 1406; x_wconf 96'>in</span>
|
||||
<span class='ocrx_word' id='word_1_176' title='bbox 858 1379 923 1406; x_wconf 96'>time</span>
|
||||
<span class='ocrx_word' id='word_1_177' title='bbox 934 1384 963 1406; x_wconf 96'>to</span>
|
||||
@@ -288,7 +288,7 @@
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_22' title="bbox 347 1417 1232 1452; baseline 0.002 -8; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_180' title='bbox 347 1417 414 1445; x_wconf 96'>click</span>
|
||||
<span class='ocrx_word' id='word_1_181' title='bbox 425 1418 506 1445; x_wconf 91'>track.</span>
|
||||
<span class='ocrx_word' id='word_1_181' title='bbox 425 1418 506 1445; x_wconf 92'>track.</span>
|
||||
<span class='ocrx_word' id='word_1_182' title='bbox 519 1418 609 1445; x_wconf 96'>When</span>
|
||||
<span class='ocrx_word' id='word_1_183' title='bbox 620 1419 665 1445; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_184' title='bbox 676 1426 810 1452; x_wconf 96'>sequence</span>
|
||||
@@ -297,7 +297,7 @@
|
||||
<span class='ocrx_word' id='word_1_187' title='bbox 995 1419 1101 1446; x_wconf 96'>around</span>
|
||||
<span class='ocrx_word' id='word_1_188' title='bbox 1112 1423 1141 1446; x_wconf 96'>to</span>
|
||||
<span class='ocrx_word' id='word_1_189' title='bbox 1152 1419 1201 1446; x_wconf 96'>bar</span>
|
||||
<span class='ocrx_word' id='word_1_190' title='bbox 1213 1419 1232 1450; x_wconf 88'>1,</span>
|
||||
<span class='ocrx_word' id='word_1_190' title='bbox 1213 1419 1232 1450; x_wconf 85'>1,</span>
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_23' title="bbox 346 1457 1223 1491; baseline 0.001 -7; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_191' title='bbox 346 1457 430 1490; x_wconf 16'>you’</span>
|
||||
@@ -620,7 +620,7 @@
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_50' title="bbox 1295 1565 1577 1593; baseline 0.004 -1; x_size 34.748871; x_descenders 6.7488689; x_ascenders 9">
|
||||
<span class='ocrx_word' id='word_1_423' title='bbox 1295 1565 1441 1592; x_wconf 96'>unwanted</span>
|
||||
<span class='ocrx_word' id='word_1_424' title='bbox 1452 1565 1577 1593; x_wconf 94'>sections,</span>
|
||||
<span class='ocrx_word' id='word_1_424' title='bbox 1452 1565 1577 1593; x_wconf 93'>sections,</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -668,8 +668,8 @@
|
||||
<span class='ocrx_word' id='word_1_454' title='bbox 1458 1768 1562 1796; x_wconf 96'>section</span>
|
||||
<span class='ocrx_word' id='word_1_455' title='bbox 1574 1769 1666 1802; x_wconf 96'>(verse,</span>
|
||||
<span class='ocrx_word' id='word_1_456' title='bbox 1679 1769 1788 1801; x_wconf 96'>chorus,</span>
|
||||
<span class='ocrx_word' id='word_1_457' title='bbox 1800 1769 1865 1802; x_wconf 95'>etc.)</span>
|
||||
<span class='ocrx_word' id='word_1_458' title='bbox 1876 1768 1904 1795; x_wconf 95'>in</span>
|
||||
<span class='ocrx_word' id='word_1_457' title='bbox 1800 1769 1865 1802; x_wconf 96'>etc.)</span>
|
||||
<span class='ocrx_word' id='word_1_458' title='bbox 1876 1768 1904 1795; x_wconf 96'>in</span>
|
||||
<span class='ocrx_word' id='word_1_459' title='bbox 1914 1768 2066 1796; x_wconf 96'>individual</span>
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_55' title="bbox 1296 1808 2215 1841; baseline 0 -6; x_size 32; x_descenders 5; x_ascenders 8">
|
||||
@@ -697,7 +697,7 @@
|
||||
<span class='ocrx_word' id='word_1_477' title='bbox 1377 1887 1412 1914; x_wconf 96'>all</span>
|
||||
<span class='ocrx_word' id='word_1_478' title='bbox 1422 1887 1468 1914; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_479' title='bbox 1478 1891 1552 1920; x_wconf 96'>parts</span>
|
||||
<span class='ocrx_word' id='word_1_480' title='bbox 1563 1887 1621 1914; x_wconf 96'>into</span>
|
||||
<span class='ocrx_word' id='word_1_480' title='bbox 1563 1887 1621 1914; x_wconf 95'>into</span>
|
||||
<span class='ocrx_word' id='word_1_481' title='bbox 1632 1895 1649 1914; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_482' title='bbox 1659 1895 1718 1914; x_wconf 96'>new</span>
|
||||
<span class='ocrx_word' id='word_1_483' title='bbox 1729 1895 1870 1920; x_wconf 96'>sequence.</span>
|
||||
@@ -829,9 +829,9 @@
|
||||
<div class='ocr_carea' id='block_1_23' title="bbox 347 2427 2145 2507">
|
||||
<p class='ocr_par' id='par_1_31' lang='eng' title="bbox 347 2427 2145 2507">
|
||||
<span class='ocr_header' id='line_1_67' title="bbox 347 2427 1468 2461; baseline 0.002 -8; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_573' title='bbox 347 2432 361 2446; x_wconf 77'>*</span>
|
||||
<span class='ocrx_word' id='word_1_573' title='bbox 347 2432 361 2446; x_wconf 75'>*</span>
|
||||
<span class='ocrx_word' id='word_1_574' title='bbox 373 2427 612 2454; x_wconf 91'>Non-destructive</span>
|
||||
<span class='ocrx_word' id='word_1_575' title='bbox 622 2427 914 2461; x_wconf 84'>recording—existing</span>
|
||||
<span class='ocrx_word' id='word_1_575' title='bbox 622 2427 914 2461; x_wconf 87'>recording—existing</span>
|
||||
<span class='ocrx_word' id='word_1_576' title='bbox 924 2432 1002 2455; x_wconf 96'>notes</span>
|
||||
<span class='ocrx_word' id='word_1_577' title='bbox 1013 2436 1057 2455; x_wconf 96'>are</span>
|
||||
<span class='ocrx_word' id='word_1_578' title='bbox 1068 2432 1116 2455; x_wconf 96'>not</span>
|
||||
@@ -912,12 +912,12 @@
|
||||
<div class='ocr_carea' id='block_1_27' title="bbox 347 2648 2100 2727">
|
||||
<p class='ocr_par' id='par_1_35' lang='eng' title="bbox 347 2648 2100 2727">
|
||||
<span class='ocr_header' id='line_1_72' title="bbox 347 2648 1664 2682; baseline 0.001 -7; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_630' title='bbox 347 2654 360 2667; x_wconf 47'>®</span>
|
||||
<span class='ocrx_word' id='word_1_631' title='bbox 372 2648 483 2675; x_wconf 96'>Utilizes</span>
|
||||
<span class='ocrx_word' id='word_1_630' title='bbox 347 2654 360 2667; x_wconf 45'>®</span>
|
||||
<span class='ocrx_word' id='word_1_631' title='bbox 372 2648 483 2675; x_wconf 94'>Utilizes</span>
|
||||
<span class='ocrx_word' id='word_1_632' title='bbox 493 2648 564 2680; x_wconf 96'>ultra</span>
|
||||
<span class='ocrx_word' id='word_1_633' title='bbox 573 2648 744 2682; x_wconf 96'>high-speed,</span>
|
||||
<span class='ocrx_word' id='word_1_634' title='bbox 766 2649 772 2676; x_wconf 95'>8</span>
|
||||
<span class='ocrx_word' id='word_1_635' title='bbox 783 2649 862 2675; x_wconf 94'>MHz</span>
|
||||
<span class='ocrx_word' id='word_1_635' title='bbox 783 2649 862 2675; x_wconf 95'>MHz</span>
|
||||
<span class='ocrx_word' id='word_1_636' title='bbox 873 2649 954 2676; x_wconf 95'>80186</span>
|
||||
<span class='ocrx_word' id='word_1_637' title='bbox 965 2649 994 2676; x_wconf 96'>16</span>
|
||||
<span class='ocrx_word' id='word_1_638' title='bbox 1004 2648 1043 2676; x_wconf 96'>bit</span>
|
||||
@@ -928,12 +928,12 @@
|
||||
<span class='ocrx_word' id='word_1_643' title='bbox 1512 2648 1664 2682; x_wconf 96'>operation.</span>
|
||||
</span>
|
||||
<span class='ocr_header' id='line_1_73' title="bbox 347 2694 2100 2727; baseline 0.001 -7; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_644' title='bbox 347 2699 361 2713; x_wconf 52'>*</span>
|
||||
<span class='ocrx_word' id='word_1_644' title='bbox 347 2699 361 2713; x_wconf 44'>*</span>
|
||||
<span class='ocrx_word' id='word_1_645' title='bbox 372 2694 504 2721; x_wconf 96'>TEMPO</span>
|
||||
<span class='ocrx_word' id='word_1_646' title='bbox 515 2702 578 2727; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_647' title='bbox 589 2694 623 2721; x_wconf 96'>be</span>
|
||||
<span class='ocrx_word' id='word_1_647' title='bbox 589 2694 623 2721; x_wconf 95'>be</span>
|
||||
<span class='ocrx_word' id='word_1_648' title='bbox 633 2694 764 2727; x_wconf 95'>specified</span>
|
||||
<span class='ocrx_word' id='word_1_649' title='bbox 774 2694 802 2721; x_wconf 92'>in</span>
|
||||
<span class='ocrx_word' id='word_1_649' title='bbox 774 2694 802 2721; x_wconf 93'>in</span>
|
||||
<span class='ocrx_word' id='word_1_650' title='bbox 814 2695 1172 2722; x_wconf 91'>BEATS-PER-MINUTE</span>
|
||||
<span class='ocrx_word' id='word_1_651' title='bbox 1183 2703 1215 2722; x_wconf 93'>or</span>
|
||||
<span class='ocrx_word' id='word_1_652' title='bbox 1225 2695 1567 2722; x_wconf 92'>FRAMES-PER-BEAT</span>
|
||||
@@ -995,7 +995,7 @@
|
||||
<div class='ocr_carea' id='block_1_31' title="bbox 347 2861 1792 2940">
|
||||
<p class='ocr_par' id='par_1_39' lang='eng' title="bbox 347 2861 1792 2940">
|
||||
<span class='ocr_header' id='line_1_77' title="bbox 347 2861 1792 2895; baseline 0.001 -8; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_687' title='bbox 347 2866 360 2880; x_wconf 62'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_687' title='bbox 347 2866 360 2880; x_wconf 59'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_688' title='bbox 372 2861 504 2887; x_wconf 96'>TEMPO</span>
|
||||
<span class='ocrx_word' id='word_1_689' title='bbox 515 2861 696 2888; x_wconf 96'>CHANGES</span>
|
||||
<span class='ocrx_word' id='word_1_690' title='bbox 707 2869 771 2894; x_wconf 96'>may</span>
|
||||
@@ -1011,7 +1011,7 @@
|
||||
<span class='ocrx_word' id='word_1_700' title='bbox 1679 2861 1792 2888; x_wconf 84'>desired.</span>
|
||||
</span>
|
||||
<span class='ocr_header' id='line_1_78' title="bbox 347 2906 1507 2940; baseline 0.002 -8; x_size 33; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_701' title='bbox 347 2911 360 2925; x_wconf 76'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_701' title='bbox 347 2911 360 2925; x_wconf 79'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_702' title='bbox 371 2906 434 2938; x_wconf 96'>Any</span>
|
||||
<span class='ocrx_word' id='word_1_703' title='bbox 444 2906 539 2932; x_wconf 96'>TIME</span>
|
||||
<span class='ocrx_word' id='word_1_704' title='bbox 550 2906 763 2933; x_wconf 96'>SIGNATURE</span>
|
||||
@@ -1022,8 +1022,8 @@
|
||||
<span class='ocrx_word' id='word_1_709' title='bbox 1046 2915 1109 2940; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_710' title='bbox 1120 2907 1154 2934; x_wconf 96'>be</span>
|
||||
<span class='ocrx_word' id='word_1_711' title='bbox 1164 2907 1288 2940; x_wconf 96'>changed</span>
|
||||
<span class='ocrx_word' id='word_1_712' title='bbox 1299 2907 1393 2934; x_wconf 96'>within</span>
|
||||
<span class='ocrx_word' id='word_1_713' title='bbox 1404 2915 1420 2934; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_712' title='bbox 1299 2907 1393 2934; x_wconf 95'>within</span>
|
||||
<span class='ocrx_word' id='word_1_713' title='bbox 1404 2915 1420 2934; x_wconf 95'>a</span>
|
||||
<span class='ocrx_word' id='word_1_714' title='bbox 1431 2915 1507 2940; x_wconf 96'>song.</span>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
-1
@@ -120,4 +120,3 @@ Linn Electronics, Inc.
|
||||
|
||||
18720 Oxnard Street, Tarzana, CA 91356
|
||||
(818) 708-8131 TELEX #298949 LINN UR
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
-1
@@ -120,4 +120,3 @@ Linn Electronics, Inc.
|
||||
|
||||
18720 Oxnard Street, Tarzana, CA 91356
|
||||
(818) 708-8131 TELEX #298949 LINN UR
|
||||
|
||||
+59
-59
@@ -5,11 +5,11 @@
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
||||
<meta name='ocr-system' content='tesseract 5.0.0-beta-20210916-12-g19cc9' />
|
||||
<meta name='ocr-system' content='tesseract 5.3.2' />
|
||||
<meta name='ocr-capabilities' content='ocr_page ocr_carea ocr_par ocr_line ocrx_word ocrp_wconf'/>
|
||||
</head>
|
||||
<body>
|
||||
<div class='ocr_page' id='page_1' title='image "/tmp/ocrmypdf.io.rvbrparf/000001_ocr.png"; bbox 0 0 2550 3300; ppageno 0'>
|
||||
<div class='ocr_page' id='page_1' title='image "/tmp/ocrmypdf.io.zopxh6db/000001_ocr.png"; bbox 0 0 2550 3300; ppageno 0; scan_res 300 300'>
|
||||
<div class='ocr_carea' id='block_1_1' title="bbox 582 131 1968 303">
|
||||
<p class='ocr_par' id='par_1_1' lang='eng' title="bbox 582 131 1968 303">
|
||||
<span class='ocr_header' id='line_1_1' title="bbox 882 131 1657 217; baseline 0.001 -17; x_size 85; x_descenders 16; x_ascenders 19">
|
||||
@@ -29,7 +29,7 @@
|
||||
<p class='ocr_par' id='par_1_2' lang='eng' title="bbox 347 380 2188 423">
|
||||
<span class='ocr_header' id='line_1_3' title="bbox 347 380 2188 423; baseline -0.001 -12; x_size 38; x_descenders 8; x_ascenders 10">
|
||||
<span class='ocrx_word' id='word_1_8' title='bbox 347 380 412 410; x_wconf 93'>The</span>
|
||||
<span class='ocrx_word' id='word_1_9' title='bbox 424 380 676 417; x_wconf 91'>LinnSequencer</span>
|
||||
<span class='ocrx_word' id='word_1_9' title='bbox 424 380 676 417; x_wconf 90'>LinnSequencer</span>
|
||||
<span class='ocrx_word' id='word_1_10' title='bbox 688 380 712 411; x_wconf 96'>is</span>
|
||||
<span class='ocrx_word' id='word_1_11' title='bbox 724 390 743 411; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_12' title='bbox 754 381 1005 423; x_wconf 96'>state-of-the-art</span>
|
||||
@@ -38,8 +38,8 @@
|
||||
<span class='ocrx_word' id='word_1_15' title='bbox 1311 380 1525 418; x_wconf 96'>performance</span>
|
||||
<span class='ocrx_word' id='word_1_16' title='bbox 1536 380 1602 411; x_wconf 96'>tool</span>
|
||||
<span class='ocrx_word' id='word_1_17' title='bbox 1615 380 1663 411; x_wconf 96'>for</span>
|
||||
<span class='ocrx_word' id='word_1_18' title='bbox 1674 381 1725 410; x_wconf 97'>the</span>
|
||||
<span class='ocrx_word' id='word_1_19' title='bbox 1737 380 1940 417; x_wconf 96'>professional</span>
|
||||
<span class='ocrx_word' id='word_1_18' title='bbox 1674 381 1725 410; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_19' title='bbox 1737 380 1940 417; x_wconf 95'>professional</span>
|
||||
<span class='ocrx_word' id='word_1_20' title='bbox 1952 380 2112 411; x_wconf 96'>musician.</span>
|
||||
<span class='ocrx_word' id='word_1_21' title='bbox 2127 381 2152 410; x_wconf 96'>It</span>
|
||||
<span class='ocrx_word' id='word_1_22' title='bbox 2164 380 2188 410; x_wconf 96'>is</span>
|
||||
@@ -136,9 +136,9 @@
|
||||
<div class='ocr_carea' id='block_1_5' title="bbox 349 748 2117 793">
|
||||
<p class='ocr_par' id='par_1_7' lang='eng' title="bbox 349 748 2117 793">
|
||||
<span class='ocr_header' id='line_1_10' title="bbox 349 748 2117 793; baseline 0 -9; x_size 45; x_descenders 9; x_ascenders 11">
|
||||
<span class='ocrx_word' id='word_1_84' title='bbox 349 755 367 774; x_wconf 42'>©</span>
|
||||
<span class='ocrx_word' id='word_1_85' title='bbox 383 748 573 784; x_wconf 90'>Ultra-fast</span>
|
||||
<span class='ocrx_word' id='word_1_86' title='bbox 616 749 677 784; x_wconf 9'>32”</span>
|
||||
<span class='ocrx_word' id='word_1_84' title='bbox 349 755 367 774; x_wconf 50'>©</span>
|
||||
<span class='ocrx_word' id='word_1_85' title='bbox 383 748 573 784; x_wconf 91'>Ultra-fast</span>
|
||||
<span class='ocrx_word' id='word_1_86' title='bbox 616 749 677 784; x_wconf 10'>32”</span>
|
||||
<span class='ocrx_word' id='word_1_87' title='bbox 694 748 775 784; x_wconf 96'>disk</span>
|
||||
<span class='ocrx_word' id='word_1_88' title='bbox 790 748 887 784; x_wconf 96'>drive</span>
|
||||
<span class='ocrx_word' id='word_1_89' title='bbox 901 754 1012 785; x_wconf 96'>stores</span>
|
||||
@@ -150,7 +150,7 @@
|
||||
<span class='ocrx_word' id='word_1_95' title='bbox 1638 748 1746 784; x_wconf 96'>holds</span>
|
||||
<span class='ocrx_word' id='word_1_96' title='bbox 1761 759 1844 784; x_wconf 96'>over</span>
|
||||
<span class='ocrx_word' id='word_1_97' title='bbox 1859 749 2000 791; x_wconf 96'>110,000</span>
|
||||
<span class='ocrx_word' id='word_1_98' title='bbox 2013 753 2117 784; x_wconf 97'>notes</span>
|
||||
<span class='ocrx_word' id='word_1_98' title='bbox 2013 753 2117 784; x_wconf 96'>notes</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -165,13 +165,13 @@
|
||||
<div class='ocr_carea' id='block_1_7' title="bbox 349 855 2030 1016">
|
||||
<p class='ocr_par' id='par_1_9' lang='eng' title="bbox 349 855 2030 1016">
|
||||
<span class='ocr_header' id='line_1_12' title="bbox 350 855 1638 900; baseline 0.001 -9; x_size 45; x_descenders 9; x_ascenders 11">
|
||||
<span class='ocrx_word' id='word_1_101' title='bbox 350 863 367 881; x_wconf 51'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_102' title='bbox 383 856 464 891; x_wconf 95'>One</span>
|
||||
<span class='ocrx_word' id='word_1_101' title='bbox 350 863 367 881; x_wconf 46'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_102' title='bbox 383 856 464 891; x_wconf 96'>One</span>
|
||||
<span class='ocrx_word' id='word_1_103' title='bbox 478 866 520 891; x_wconf 95'>or</span>
|
||||
<span class='ocrx_word' id='word_1_104' title='bbox 534 855 580 891; x_wconf 96'>all</span>
|
||||
<span class='ocrx_word' id='word_1_105' title='bbox 594 856 712 892; x_wconf 95'>tracks</span>
|
||||
<span class='ocrx_word' id='word_1_106' title='bbox 726 867 811 900; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_107' title='bbox 823 856 869 892; x_wconf 85'>be</span>
|
||||
<span class='ocrx_word' id='word_1_107' title='bbox 823 856 869 892; x_wconf 84'>be</span>
|
||||
<span class='ocrx_word' id='word_1_108' title='bbox 882 856 1212 892; x_wconf 96'>TRANSPOSED</span>
|
||||
<span class='ocrx_word' id='word_1_109' title='bbox 1227 861 1264 892; x_wconf 96'>at</span>
|
||||
<span class='ocrx_word' id='word_1_110' title='bbox 1277 856 1338 892; x_wconf 96'>the</span>
|
||||
@@ -209,7 +209,7 @@
|
||||
<div class='ocr_carea' id='block_1_8' title="bbox 382 1021 689 1065">
|
||||
<p class='ocr_par' id='par_1_10' lang='eng' title="bbox 382 1021 689 1065">
|
||||
<span class='ocr_line' id='line_1_15' title="bbox 382 1021 689 1065; baseline 0.003 -8; x_size 45; x_descenders 8; x_ascenders 12">
|
||||
<span class='ocrx_word' id='word_1_135' title='bbox 382 1021 564 1065; x_wconf 96'>rhythmic</span>
|
||||
<span class='ocrx_word' id='word_1_135' title='bbox 382 1021 564 1065; x_wconf 95'>rhythmic</span>
|
||||
<span class='ocrx_word' id='word_1_136' title='bbox 577 1021 689 1058; x_wconf 96'>value.</span>
|
||||
</span>
|
||||
</p>
|
||||
@@ -249,15 +249,15 @@
|
||||
<span class='ocrx_word' id='word_1_154' title='bbox 349 1203 367 1222; x_wconf 73'>©</span>
|
||||
<span class='ocrx_word' id='word_1_155' title='bbox 382 1195 560 1240; x_wconf 96'>Optional</span>
|
||||
<span class='ocrx_word' id='word_1_156' title='bbox 573 1201 709 1233; x_wconf 96'>remote</span>
|
||||
<span class='ocrx_word' id='word_1_157' title='bbox 723 1196 874 1233; x_wconf 96'>control.</span>
|
||||
<span class='ocrx_word' id='word_1_157' title='bbox 723 1196 874 1233; x_wconf 95'>control.</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class='ocr_carea' id='block_1_12' title="bbox 346 1288 1239 1491">
|
||||
<p class='ocr_par' id='par_1_14' lang='eng' title="bbox 346 1288 749 1329">
|
||||
<span class='ocr_line' id='line_1_19' title="bbox 346 1288 749 1329; baseline 0.002 -9; x_size 42; x_descenders 9; x_ascenders 10">
|
||||
<span class='ocrx_word' id='word_1_158' title='bbox 346 1288 535 1329; x_wconf 96'>Recording</span>
|
||||
<span class='ocrx_word' id='word_1_159' title='bbox 547 1298 567 1321; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_158' title='bbox 346 1288 535 1329; x_wconf 95'>Recording</span>
|
||||
<span class='ocrx_word' id='word_1_159' title='bbox 547 1298 567 1321; x_wconf 95'>a</span>
|
||||
<span class='ocrx_word' id='word_1_160' title='bbox 579 1288 749 1328; x_wconf 96'>Sequence</span>
|
||||
</span>
|
||||
</p>
|
||||
@@ -278,8 +278,8 @@
|
||||
<span class='ocrx_word' id='word_1_170' title='bbox 346 1379 411 1406; x_wconf 96'>then</span>
|
||||
<span class='ocrx_word' id='word_1_171' title='bbox 422 1378 483 1412; x_wconf 96'>play</span>
|
||||
<span class='ocrx_word' id='word_1_172' title='bbox 493 1387 562 1412; x_wconf 96'>your</span>
|
||||
<span class='ocrx_word' id='word_1_173' title='bbox 572 1379 659 1405; x_wconf 95'>MIDI</span>
|
||||
<span class='ocrx_word' id='word_1_174' title='bbox 671 1379 810 1412; x_wconf 95'>keyboard</span>
|
||||
<span class='ocrx_word' id='word_1_173' title='bbox 572 1379 659 1405; x_wconf 96'>MIDI</span>
|
||||
<span class='ocrx_word' id='word_1_174' title='bbox 671 1379 810 1412; x_wconf 96'>keyboard</span>
|
||||
<span class='ocrx_word' id='word_1_175' title='bbox 821 1379 848 1406; x_wconf 96'>in</span>
|
||||
<span class='ocrx_word' id='word_1_176' title='bbox 858 1379 923 1406; x_wconf 96'>time</span>
|
||||
<span class='ocrx_word' id='word_1_177' title='bbox 934 1384 963 1406; x_wconf 96'>to</span>
|
||||
@@ -288,7 +288,7 @@
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_22' title="bbox 347 1417 1232 1452; baseline 0.002 -8; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_180' title='bbox 347 1417 414 1445; x_wconf 96'>click</span>
|
||||
<span class='ocrx_word' id='word_1_181' title='bbox 425 1418 506 1445; x_wconf 91'>track.</span>
|
||||
<span class='ocrx_word' id='word_1_181' title='bbox 425 1418 506 1445; x_wconf 92'>track.</span>
|
||||
<span class='ocrx_word' id='word_1_182' title='bbox 519 1418 609 1445; x_wconf 96'>When</span>
|
||||
<span class='ocrx_word' id='word_1_183' title='bbox 620 1419 665 1445; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_184' title='bbox 676 1426 810 1452; x_wconf 96'>sequence</span>
|
||||
@@ -297,15 +297,15 @@
|
||||
<span class='ocrx_word' id='word_1_187' title='bbox 995 1419 1101 1446; x_wconf 96'>around</span>
|
||||
<span class='ocrx_word' id='word_1_188' title='bbox 1112 1423 1141 1446; x_wconf 96'>to</span>
|
||||
<span class='ocrx_word' id='word_1_189' title='bbox 1152 1419 1201 1446; x_wconf 96'>bar</span>
|
||||
<span class='ocrx_word' id='word_1_190' title='bbox 1213 1419 1232 1450; x_wconf 88'>1,</span>
|
||||
<span class='ocrx_word' id='word_1_190' title='bbox 1213 1419 1232 1450; x_wconf 85'>1,</span>
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_23' title="bbox 346 1457 1223 1491; baseline 0.001 -7; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_191' title='bbox 346 1457 430 1490; x_wconf 12'>you’</span>
|
||||
<span class='ocrx_word' id='word_1_192' title='bbox 410 1453 432 1496; x_wconf 12'>ll</span>
|
||||
<span class='ocrx_word' id='word_1_193' title='bbox 441 1457 506 1485; x_wconf 95'>hear</span>
|
||||
<span class='ocrx_word' id='word_1_191' title='bbox 346 1457 430 1490; x_wconf 16'>you’</span>
|
||||
<span class='ocrx_word' id='word_1_192' title='bbox 410 1453 436 1496; x_wconf 16'>ll</span>
|
||||
<span class='ocrx_word' id='word_1_193' title='bbox 441 1457 506 1485; x_wconf 96'>hear</span>
|
||||
<span class='ocrx_word' id='word_1_194' title='bbox 517 1458 590 1485; x_wconf 96'>what</span>
|
||||
<span class='ocrx_word' id='word_1_195' title='bbox 600 1466 654 1491; x_wconf 93'>you</span>
|
||||
<span class='ocrx_word' id='word_1_196' title='bbox 666 1458 865 1491; x_wconf 90'>played—only</span>
|
||||
<span class='ocrx_word' id='word_1_196' title='bbox 666 1458 865 1491; x_wconf 91'>played—only</span>
|
||||
<span class='ocrx_word' id='word_1_197' title='bbox 876 1458 910 1485; x_wconf 96'>all</span>
|
||||
<span class='ocrx_word' id='word_1_198' title='bbox 921 1458 1018 1491; x_wconf 96'>timing</span>
|
||||
<span class='ocrx_word' id='word_1_199' title='bbox 1029 1466 1115 1485; x_wconf 96'>errors</span>
|
||||
@@ -356,12 +356,12 @@
|
||||
|
||||
<p class='ocr_par' id='par_1_18' lang='eng' title="bbox 346 1616 1205 1965">
|
||||
<span class='ocr_line' id='line_1_27' title="bbox 384 1616 1199 1648; baseline 0.001 -6; x_size 32; x_descenders 5; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_228' title='bbox 384 1616 471 1642; x_wconf 96'>FAST</span>
|
||||
<span class='ocrx_word' id='word_1_228' title='bbox 384 1616 471 1642; x_wconf 95'>FAST</span>
|
||||
<span class='ocrx_word' id='word_1_229' title='bbox 481 1616 671 1648; x_wconf 96'>FORWARD,</span>
|
||||
<span class='ocrx_word' id='word_1_230' title='bbox 684 1617 844 1648; x_wconf 95'>REWIND,</span>
|
||||
<span class='ocrx_word' id='word_1_231' title='bbox 857 1616 912 1643; x_wconf 95'>and</span>
|
||||
<span class='ocrx_word' id='word_1_232' title='bbox 924 1616 1068 1643; x_wconf 96'>LOCATE</span>
|
||||
<span class='ocrx_word' id='word_1_233' title='bbox 1079 1616 1199 1643; x_wconf 96'>controls</span>
|
||||
<span class='ocrx_word' id='word_1_233' title='bbox 1079 1616 1199 1643; x_wconf 95'>controls</span>
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_28' title="bbox 346 1655 1202 1689; baseline 0 -7; x_size 34; x_descenders 6; x_ascenders 9">
|
||||
<span class='ocrx_word' id='word_1_234' title='bbox 346 1663 409 1688; x_wconf 87'>may</span>
|
||||
@@ -401,12 +401,12 @@
|
||||
<span class='ocr_line' id='line_1_31' title="bbox 346 1773 1203 1808; baseline 0.001 -8; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_263' title='bbox 346 1774 448 1806; x_wconf 96'>record,</span>
|
||||
<span class='ocrx_word' id='word_1_264' title='bbox 460 1774 506 1801; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_265' title='bbox 517 1773 577 1801; x_wconf 96'>first</span>
|
||||
<span class='ocrx_word' id='word_1_266' title='bbox 587 1774 662 1801; x_wconf 96'>track</span>
|
||||
<span class='ocrx_word' id='word_1_265' title='bbox 510 1769 577 1812; x_wconf 95'>first</span>
|
||||
<span class='ocrx_word' id='word_1_266' title='bbox 589 1774 658 1801; x_wconf 95'>track</span>
|
||||
<span class='ocrx_word' id='word_1_267' title='bbox 673 1774 726 1801; x_wconf 96'>will</span>
|
||||
<span class='ocrx_word' id='word_1_268' title='bbox 736 1774 799 1807; x_wconf 96'>play</span>
|
||||
<span class='ocrx_word' id='word_1_269' title='bbox 809 1774 836 1801; x_wconf 96'>in</span>
|
||||
<span class='ocrx_word' id='word_1_270' title='bbox 847 1774 949 1808; x_wconf 97'>perfect</span>
|
||||
<span class='ocrx_word' id='word_1_270' title='bbox 847 1774 949 1808; x_wconf 96'>perfect</span>
|
||||
<span class='ocrx_word' id='word_1_271' title='bbox 961 1782 1026 1808; x_wconf 96'>sync</span>
|
||||
<span class='ocrx_word' id='word_1_272' title='bbox 1037 1774 1137 1808; x_wconf 96'>(unless</span>
|
||||
<span class='ocrx_word' id='word_1_273' title='bbox 1148 1782 1203 1807; x_wconf 96'>you</span>
|
||||
@@ -416,7 +416,7 @@
|
||||
<span class='ocrx_word' id='word_1_275' title='bbox 464 1813 492 1845; x_wconf 95'>it,</span>
|
||||
<span class='ocrx_word' id='word_1_276' title='bbox 505 1821 537 1840; x_wconf 95'>or</span>
|
||||
<span class='ocrx_word' id='word_1_277' title='bbox 547 1813 642 1840; x_wconf 95'>SOLO</span>
|
||||
<span class='ocrx_word' id='word_1_278' title='bbox 653 1814 769 1841; x_wconf 96'>another</span>
|
||||
<span class='ocrx_word' id='word_1_278' title='bbox 653 1814 769 1841; x_wconf 95'>another</span>
|
||||
<span class='ocrx_word' id='word_1_279' title='bbox 779 1814 875 1847; x_wconf 94'>track).</span>
|
||||
<span class='ocrx_word' id='word_1_280' title='bbox 889 1814 920 1840; x_wconf 96'>In</span>
|
||||
<span class='ocrx_word' id='word_1_281' title='bbox 930 1813 984 1841; x_wconf 96'>this</span>
|
||||
@@ -523,8 +523,8 @@
|
||||
<span class='ocrx_word' id='word_1_353' title='bbox 691 2212 718 2235; x_wconf 96'>at</span>
|
||||
<span class='ocrx_word' id='word_1_354' title='bbox 729 2208 841 2242; x_wconf 96'>specific</span>
|
||||
<span class='ocrx_word' id='word_1_355' title='bbox 851 2209 943 2242; x_wconf 97'>points</span>
|
||||
<span class='ocrx_word' id='word_1_356' title='bbox 955 2208 1049 2236; x_wconf 96'>within</span>
|
||||
<span class='ocrx_word' id='word_1_357' title='bbox 1060 2217 1076 2236; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_356' title='bbox 955 2208 1049 2236; x_wconf 97'>within</span>
|
||||
<span class='ocrx_word' id='word_1_357' title='bbox 1060 2217 1076 2236; x_wconf 97'>a</span>
|
||||
<span class='ocrx_word' id='word_1_358' title='bbox 1086 2216 1228 2242; x_wconf 96'>sequence,</span>
|
||||
</span>
|
||||
</p>
|
||||
@@ -620,7 +620,7 @@
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_50' title="bbox 1295 1565 1577 1593; baseline 0.004 -1; x_size 34.748871; x_descenders 6.7488689; x_ascenders 9">
|
||||
<span class='ocrx_word' id='word_1_423' title='bbox 1295 1565 1441 1592; x_wconf 96'>unwanted</span>
|
||||
<span class='ocrx_word' id='word_1_424' title='bbox 1452 1565 1577 1593; x_wconf 94'>sections,</span>
|
||||
<span class='ocrx_word' id='word_1_424' title='bbox 1452 1565 1577 1593; x_wconf 93'>sections,</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -668,8 +668,8 @@
|
||||
<span class='ocrx_word' id='word_1_454' title='bbox 1458 1768 1562 1796; x_wconf 96'>section</span>
|
||||
<span class='ocrx_word' id='word_1_455' title='bbox 1574 1769 1666 1802; x_wconf 96'>(verse,</span>
|
||||
<span class='ocrx_word' id='word_1_456' title='bbox 1679 1769 1788 1801; x_wconf 96'>chorus,</span>
|
||||
<span class='ocrx_word' id='word_1_457' title='bbox 1800 1769 1865 1802; x_wconf 95'>etc.)</span>
|
||||
<span class='ocrx_word' id='word_1_458' title='bbox 1876 1768 1904 1795; x_wconf 95'>in</span>
|
||||
<span class='ocrx_word' id='word_1_457' title='bbox 1800 1769 1865 1802; x_wconf 96'>etc.)</span>
|
||||
<span class='ocrx_word' id='word_1_458' title='bbox 1876 1768 1904 1795; x_wconf 96'>in</span>
|
||||
<span class='ocrx_word' id='word_1_459' title='bbox 1914 1768 2066 1796; x_wconf 96'>individual</span>
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_55' title="bbox 1296 1808 2215 1841; baseline 0 -6; x_size 32; x_descenders 5; x_ascenders 8">
|
||||
@@ -677,11 +677,11 @@
|
||||
<span class='ocrx_word' id='word_1_461' title='bbox 1463 1808 1528 1835; x_wconf 96'>then</span>
|
||||
<span class='ocrx_word' id='word_1_462' title='bbox 1538 1816 1587 1835; x_wconf 96'>use</span>
|
||||
<span class='ocrx_word' id='word_1_463' title='bbox 1597 1808 1643 1835; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_464' title='bbox 1653 1809 1799 1835; x_wconf 96'>CREATE</span>
|
||||
<span class='ocrx_word' id='word_1_464' title='bbox 1653 1809 1799 1835; x_wconf 95'>CREATE</span>
|
||||
<span class='ocrx_word' id='word_1_465' title='bbox 1810 1808 1911 1835; x_wconf 96'>SONG</span>
|
||||
<span class='ocrx_word' id='word_1_466' title='bbox 1923 1808 2050 1836; x_wconf 96'>function</span>
|
||||
<span class='ocrx_word' id='word_1_467' title='bbox 2060 1812 2089 1835; x_wconf 95'>to</span>
|
||||
<span class='ocrx_word' id='word_1_468' title='bbox 2103 1808 2215 1836; x_wconf 92'>“chain”</span>
|
||||
<span class='ocrx_word' id='word_1_467' title='bbox 2060 1812 2089 1835; x_wconf 96'>to</span>
|
||||
<span class='ocrx_word' id='word_1_468' title='bbox 2103 1808 2215 1836; x_wconf 93'>“chain”</span>
|
||||
</span>
|
||||
<span class='ocr_line' id='line_1_56' title="bbox 1295 1847 2135 1881; baseline 0.001 -7; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_469' title='bbox 1295 1848 1370 1874; x_wconf 96'>them</span>
|
||||
@@ -697,7 +697,7 @@
|
||||
<span class='ocrx_word' id='word_1_477' title='bbox 1377 1887 1412 1914; x_wconf 96'>all</span>
|
||||
<span class='ocrx_word' id='word_1_478' title='bbox 1422 1887 1468 1914; x_wconf 96'>the</span>
|
||||
<span class='ocrx_word' id='word_1_479' title='bbox 1478 1891 1552 1920; x_wconf 96'>parts</span>
|
||||
<span class='ocrx_word' id='word_1_480' title='bbox 1563 1887 1621 1914; x_wconf 96'>into</span>
|
||||
<span class='ocrx_word' id='word_1_480' title='bbox 1563 1887 1621 1914; x_wconf 95'>into</span>
|
||||
<span class='ocrx_word' id='word_1_481' title='bbox 1632 1895 1649 1914; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_482' title='bbox 1659 1895 1718 1914; x_wconf 96'>new</span>
|
||||
<span class='ocrx_word' id='word_1_483' title='bbox 1729 1895 1870 1920; x_wconf 96'>sequence.</span>
|
||||
@@ -829,9 +829,9 @@
|
||||
<div class='ocr_carea' id='block_1_23' title="bbox 347 2427 2145 2507">
|
||||
<p class='ocr_par' id='par_1_31' lang='eng' title="bbox 347 2427 2145 2507">
|
||||
<span class='ocr_header' id='line_1_67' title="bbox 347 2427 1468 2461; baseline 0.002 -8; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_573' title='bbox 347 2432 361 2446; x_wconf 77'>*</span>
|
||||
<span class='ocrx_word' id='word_1_573' title='bbox 347 2432 361 2446; x_wconf 75'>*</span>
|
||||
<span class='ocrx_word' id='word_1_574' title='bbox 373 2427 612 2454; x_wconf 91'>Non-destructive</span>
|
||||
<span class='ocrx_word' id='word_1_575' title='bbox 622 2427 914 2461; x_wconf 84'>recording—existing</span>
|
||||
<span class='ocrx_word' id='word_1_575' title='bbox 622 2427 914 2461; x_wconf 87'>recording—existing</span>
|
||||
<span class='ocrx_word' id='word_1_576' title='bbox 924 2432 1002 2455; x_wconf 96'>notes</span>
|
||||
<span class='ocrx_word' id='word_1_577' title='bbox 1013 2436 1057 2455; x_wconf 96'>are</span>
|
||||
<span class='ocrx_word' id='word_1_578' title='bbox 1068 2432 1116 2455; x_wconf 96'>not</span>
|
||||
@@ -863,7 +863,7 @@
|
||||
<div class='ocr_carea' id='block_1_24' title="bbox 372 2510 1090 2543">
|
||||
<p class='ocr_par' id='par_1_32' lang='eng' title="bbox 372 2510 1090 2543">
|
||||
<span class='ocr_line' id='line_1_69' title="bbox 372 2510 1090 2543; baseline 0.001 -6; x_size 35.625; x_descenders 8.90625; x_ascenders 8.90625">
|
||||
<span class='ocrx_word' id='word_1_599' title='bbox 372 2510 500 2542; x_wconf 95'>ERASE,</span>
|
||||
<span class='ocrx_word' id='word_1_599' title='bbox 372 2510 500 2542; x_wconf 96'>ERASE,</span>
|
||||
<span class='ocrx_word' id='word_1_600' title='bbox 513 2511 660 2542; x_wconf 93'>REPEAT,</span>
|
||||
<span class='ocrx_word' id='word_1_601' title='bbox 673 2511 883 2543; x_wconf 91'>PLAY/STOP,</span>
|
||||
<span class='ocrx_word' id='word_1_602' title='bbox 896 2519 927 2538; x_wconf 96'>or</span>
|
||||
@@ -874,10 +874,10 @@
|
||||
<div class='ocr_carea' id='block_1_25' title="bbox 347 2556 1768 2590">
|
||||
<p class='ocr_par' id='par_1_33' lang='eng' title="bbox 347 2556 1768 2590">
|
||||
<span class='ocr_header' id='line_1_70' title="bbox 347 2556 1768 2590; baseline 0.001 -8; x_size 34; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_604' title='bbox 347 2561 361 2575; x_wconf 86'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_605' title='bbox 372 2556 433 2583; x_wconf 80'>Two</span>
|
||||
<span class='ocrx_word' id='word_1_604' title='bbox 347 2561 361 2575; x_wconf 87'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_605' title='bbox 372 2556 433 2583; x_wconf 79'>Two</span>
|
||||
<span class='ocrx_word' id='word_1_606' title='bbox 443 2556 612 2583; x_wconf 95'>TRIGGER</span>
|
||||
<span class='ocrx_word' id='word_1_607' title='bbox 623 2556 797 2584; x_wconf 95'>OUTPUTS</span>
|
||||
<span class='ocrx_word' id='word_1_607' title='bbox 623 2556 797 2584; x_wconf 96'>OUTPUTS</span>
|
||||
<span class='ocrx_word' id='word_1_608' title='bbox 808 2565 871 2590; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_609' title='bbox 881 2557 915 2584; x_wconf 96'>be</span>
|
||||
<span class='ocrx_word' id='word_1_610' title='bbox 925 2557 1119 2590; x_wconf 96'>programmed</span>
|
||||
@@ -911,10 +911,10 @@
|
||||
</div>
|
||||
<div class='ocr_carea' id='block_1_27' title="bbox 347 2648 2100 2727">
|
||||
<p class='ocr_par' id='par_1_35' lang='eng' title="bbox 347 2648 2100 2727">
|
||||
<span class='ocr_header' id='line_1_72' title="bbox 347 2648 1664 2682; baseline 0 -7; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_630' title='bbox 347 2654 360 2667; x_wconf 42'>®</span>
|
||||
<span class='ocrx_word' id='word_1_631' title='bbox 372 2648 483 2675; x_wconf 95'>Utilizes</span>
|
||||
<span class='ocrx_word' id='word_1_632' title='bbox 493 2648 564 2677; x_wconf 96'>ultra</span>
|
||||
<span class='ocr_header' id='line_1_72' title="bbox 347 2648 1664 2682; baseline 0.001 -7; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_630' title='bbox 347 2654 360 2667; x_wconf 45'>®</span>
|
||||
<span class='ocrx_word' id='word_1_631' title='bbox 372 2648 483 2675; x_wconf 94'>Utilizes</span>
|
||||
<span class='ocrx_word' id='word_1_632' title='bbox 493 2648 564 2680; x_wconf 96'>ultra</span>
|
||||
<span class='ocrx_word' id='word_1_633' title='bbox 573 2648 744 2682; x_wconf 96'>high-speed,</span>
|
||||
<span class='ocrx_word' id='word_1_634' title='bbox 766 2649 772 2676; x_wconf 95'>8</span>
|
||||
<span class='ocrx_word' id='word_1_635' title='bbox 783 2649 862 2675; x_wconf 95'>MHz</span>
|
||||
@@ -928,12 +928,12 @@
|
||||
<span class='ocrx_word' id='word_1_643' title='bbox 1512 2648 1664 2682; x_wconf 96'>operation.</span>
|
||||
</span>
|
||||
<span class='ocr_header' id='line_1_73' title="bbox 347 2694 2100 2727; baseline 0.001 -7; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_644' title='bbox 347 2699 361 2713; x_wconf 52'>*</span>
|
||||
<span class='ocrx_word' id='word_1_644' title='bbox 347 2699 361 2713; x_wconf 44'>*</span>
|
||||
<span class='ocrx_word' id='word_1_645' title='bbox 372 2694 504 2721; x_wconf 96'>TEMPO</span>
|
||||
<span class='ocrx_word' id='word_1_646' title='bbox 515 2702 578 2727; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_647' title='bbox 589 2694 623 2721; x_wconf 96'>be</span>
|
||||
<span class='ocrx_word' id='word_1_647' title='bbox 589 2694 623 2721; x_wconf 95'>be</span>
|
||||
<span class='ocrx_word' id='word_1_648' title='bbox 633 2694 764 2727; x_wconf 95'>specified</span>
|
||||
<span class='ocrx_word' id='word_1_649' title='bbox 774 2694 802 2721; x_wconf 92'>in</span>
|
||||
<span class='ocrx_word' id='word_1_649' title='bbox 774 2694 802 2721; x_wconf 93'>in</span>
|
||||
<span class='ocrx_word' id='word_1_650' title='bbox 814 2695 1172 2722; x_wconf 91'>BEATS-PER-MINUTE</span>
|
||||
<span class='ocrx_word' id='word_1_651' title='bbox 1183 2703 1215 2722; x_wconf 93'>or</span>
|
||||
<span class='ocrx_word' id='word_1_652' title='bbox 1225 2695 1567 2722; x_wconf 92'>FRAMES-PER-BEAT</span>
|
||||
@@ -960,7 +960,7 @@
|
||||
<div class='ocr_carea' id='block_1_29' title="bbox 347 2777 2174 2811">
|
||||
<p class='ocr_par' id='par_1_37' lang='eng' title="bbox 347 2777 2174 2811">
|
||||
<span class='ocr_header' id='line_1_75' title="bbox 347 2777 2174 2811; baseline 0.001 -8; x_size 33; x_descenders 5; x_ascenders 9">
|
||||
<span class='ocrx_word' id='word_1_664' title='bbox 347 2782 360 2796; x_wconf 74'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_664' title='bbox 347 2782 360 2796; x_wconf 78'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_665' title='bbox 372 2777 504 2804; x_wconf 95'>TEMPO</span>
|
||||
<span class='ocrx_word' id='word_1_666' title='bbox 515 2785 578 2810; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_667' title='bbox 588 2777 622 2804; x_wconf 95'>be</span>
|
||||
@@ -968,10 +968,10 @@
|
||||
<span class='ocrx_word' id='word_1_669' title='bbox 751 2777 934 2811; x_wconf 96'>numerically,</span>
|
||||
<span class='ocrx_word' id='word_1_670' title='bbox 946 2777 1101 2811; x_wconf 96'>adjustable</span>
|
||||
<span class='ocrx_word' id='word_1_671' title='bbox 1111 2777 1139 2804; x_wconf 95'>in</span>
|
||||
<span class='ocrx_word' id='word_1_672' title='bbox 1149 2778 1239 2805; x_wconf 96'>tenths</span>
|
||||
<span class='ocrx_word' id='word_1_672' title='bbox 1149 2778 1239 2805; x_wconf 95'>tenths</span>
|
||||
<span class='ocrx_word' id='word_1_673' title='bbox 1250 2778 1282 2805; x_wconf 96'>of</span>
|
||||
<span class='ocrx_word' id='word_1_674' title='bbox 1290 2786 1307 2805; x_wconf 93'>a</span>
|
||||
<span class='ocrx_word' id='word_1_675' title='bbox 1317 2777 1567 2805; x_wconf 91'>Beat-Per-Minute</span>
|
||||
<span class='ocrx_word' id='word_1_675' title='bbox 1317 2777 1567 2805; x_wconf 92'>Beat-Per-Minute</span>
|
||||
<span class='ocrx_word' id='word_1_676' title='bbox 1577 2777 1748 2809; x_wconf 96'>increments,</span>
|
||||
<span class='ocrx_word' id='word_1_677' title='bbox 1760 2785 1792 2804; x_wconf 96'>or</span>
|
||||
<span class='ocrx_word' id='word_1_678' title='bbox 1803 2777 1839 2810; x_wconf 96'>by</span>
|
||||
@@ -995,7 +995,7 @@
|
||||
<div class='ocr_carea' id='block_1_31' title="bbox 347 2861 1792 2940">
|
||||
<p class='ocr_par' id='par_1_39' lang='eng' title="bbox 347 2861 1792 2940">
|
||||
<span class='ocr_header' id='line_1_77' title="bbox 347 2861 1792 2895; baseline 0.001 -8; x_size 33; x_descenders 6; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_687' title='bbox 347 2866 360 2880; x_wconf 62'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_687' title='bbox 347 2866 360 2880; x_wconf 59'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_688' title='bbox 372 2861 504 2887; x_wconf 96'>TEMPO</span>
|
||||
<span class='ocrx_word' id='word_1_689' title='bbox 515 2861 696 2888; x_wconf 96'>CHANGES</span>
|
||||
<span class='ocrx_word' id='word_1_690' title='bbox 707 2869 771 2894; x_wconf 96'>may</span>
|
||||
@@ -1011,7 +1011,7 @@
|
||||
<span class='ocrx_word' id='word_1_700' title='bbox 1679 2861 1792 2888; x_wconf 84'>desired.</span>
|
||||
</span>
|
||||
<span class='ocr_header' id='line_1_78' title="bbox 347 2906 1507 2940; baseline 0.002 -8; x_size 33; x_descenders 7; x_ascenders 8">
|
||||
<span class='ocrx_word' id='word_1_701' title='bbox 347 2911 360 2925; x_wconf 76'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_701' title='bbox 347 2911 360 2925; x_wconf 79'>¢</span>
|
||||
<span class='ocrx_word' id='word_1_702' title='bbox 371 2906 434 2938; x_wconf 96'>Any</span>
|
||||
<span class='ocrx_word' id='word_1_703' title='bbox 444 2906 539 2932; x_wconf 96'>TIME</span>
|
||||
<span class='ocrx_word' id='word_1_704' title='bbox 550 2906 763 2933; x_wconf 96'>SIGNATURE</span>
|
||||
@@ -1022,8 +1022,8 @@
|
||||
<span class='ocrx_word' id='word_1_709' title='bbox 1046 2915 1109 2940; x_wconf 96'>may</span>
|
||||
<span class='ocrx_word' id='word_1_710' title='bbox 1120 2907 1154 2934; x_wconf 96'>be</span>
|
||||
<span class='ocrx_word' id='word_1_711' title='bbox 1164 2907 1288 2940; x_wconf 96'>changed</span>
|
||||
<span class='ocrx_word' id='word_1_712' title='bbox 1299 2907 1393 2934; x_wconf 96'>within</span>
|
||||
<span class='ocrx_word' id='word_1_713' title='bbox 1404 2915 1420 2934; x_wconf 96'>a</span>
|
||||
<span class='ocrx_word' id='word_1_712' title='bbox 1299 2907 1393 2934; x_wconf 95'>within</span>
|
||||
<span class='ocrx_word' id='word_1_713' title='bbox 1404 2915 1420 2934; x_wconf 95'>a</span>
|
||||
<span class='ocrx_word' id='word_1_714' title='bbox 1431 2915 1507 2940; x_wconf 96'>song.</span>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
-1
@@ -120,4 +120,3 @@ Linn Electronics, Inc.
|
||||
|
||||
18720 Oxnard Street, Tarzana, CA 91356
|
||||
(818) 708-8131 TELEX #298949 LINN UR
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
-1
@@ -120,4 +120,3 @@ Linn Electronics, Inc.
|
||||
|
||||
18720 Oxnard Street, Tarzana, CA 91356
|
||||
(818) 708-8131 TELEX #298949 LINN UR
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v4.1.1 with Leptonica
|
||||
|
||||
+2
-3
@@ -3,11 +3,10 @@ blond qui fume sur son Ile
|
||||
interieure, a cöte de l'alcöve
|
||||
ovoide, oU les büches se
|
||||
consument dans l'ätre, ce qui
|
||||
lui permet de penser & la
|
||||
ui permet de penser & la
|
||||
caenogenese de |'etre dont il
|
||||
est question dans la cause
|
||||
ambigu& entendue a MoY, dans
|
||||
un capharnaüm qui, pense-t-il,
|
||||
diminue ca et la la qualite de son
|
||||
diminue ca et la la qualit& de son
|
||||
ceuvre.
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
Tesseract Open Source OCR Engine v5.0.0-beta-20210916-12-g19cc9 with Leptonica
|
||||
|
||||
+19
-38
@@ -1,41 +1,22 @@
|
||||
Replacement of "creationism" with "intelligent design"
|
||||
600
|
||||
500 .
|
||||
400 _ EB fp ys
|
||||
300 —f— EN / ~/ Y
|
||||
Tg ANA a3
|
||||
100 - nee -eemiae
|
||||
0-+—-* a ee ee ee eee
|
||||
S @ s > oO © ve S ) @ + & & “A 5 oo
|
||||
Se ° Ps as ge eS € x ro NS Po e? & s AS
|
||||
a a © FF SF HY HK SK BM ee sO
|
||||
e < Na ‘ : > cy xs eS @ Ww a) Oo
|
||||
ee S&S FF SF HK S e © 4
|
||||
~ & e
|
||||
3 x
|
||||
|
||||
|
||||
—¢—Support
|
||||
|
||||
|
||||
—H— No vote[note 1]
|
||||
ir Oppose
|
||||
—=—Net[note 2]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
120
|
||||
100 -
|
||||
Cc 80
|
||||
=
|
||||
©
|
||||
a 60 —@— "Creation" and "creationist"
|
||||
5 —@— "Intelligent design"
|
||||
and "design proponent"
|
||||
S «4
|
||||
20 -
|
||||
0 e- T T T ! r ®
|
||||
3) S) AN AN AN 9) oN
|
||||
no oP no no se ”
|
||||
\ Xi XR XR i XR es
|
||||
Rey oe Ss ~~ al .& se
|
||||
3 e oe as? ae Ne) os
|
||||
© % eo oe e
|
||||
3° S} s s se se
|
||||
oS 3° Q g g Qe?
|
||||
|
||||
re Percentage [note 3]
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
Orientation: 0
|
||||
WritingDirection: 0
|
||||
TextlineOrder: 2
|
||||
Deskew angle: 0.0097
|
||||
Deskew angle: 0.0000
|
||||
|
||||
+984
-413
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user