Compare commits

...
22 Commits
Author SHA1 Message Date
James R. Barlow 035ebea72f Change Windows build to use older Ghostscript 2024-01-07 01:56:38 -08:00
James R. Barlow a499956462 v16.0.4 release notes 2024-01-07 01:39:06 -08:00
James R. Barlow 74d2a156c4 Update cache 2024-01-07 01:35:05 -08:00
James R. Barlow f87fc7b12d gs: check for text-corrupting versions 2024-01-07 01:33:49 -08:00
James R. Barlow 602f5632cb hocr: fix unbound local 2024-01-07 01:33:36 -08:00
James R. Barlow 9fbbcf7599 Merge branch 'feature/hocrdebug' 2024-01-07 01:22:54 -08:00
James R. Barlow 9498f01f59 hocr: Pass text direction for standalone words 2024-01-06 16:21:21 -08:00
James R. Barlow 2c59aca5a1 Fix render_mode ON_TOP when previous content stream does not restore CTM 2024-01-06 16:20:55 -08:00
James R. Barlow 51301d69c9 Change default graft render mode back to underneath
It was mistakenly changed to on top at some point
2024-01-06 16:20:30 -08:00
James R. Barlow 7e608fd1df Use Courier as debug font 2024-01-06 14:24:25 -08:00
James R. Barlow ecc79315df Adjust issue template files description 2024-01-04 00:27:06 -08:00
James R. Barlow 14365d10b8 Skip testing oom killer on Python 3.12
Need to investigate further if there's a safe way to do this test.
2024-01-02 16:28:22 -08:00
James R. Barlow 5e5320020f v16.0.3 release notes 2024-01-02 02:49:54 -08:00
James R. Barlow 103c3e0cd6 Drop Ghostscript CVE warning and adjust version down to 9.54
Most distros have probably now backported the CVE fix. Dropped version to 9.54 so RHEL 9 can use recent ocrmypdf.
2024-01-01 13:02:57 -08:00
Anthony NabilandGitHub 7a1c89edd9 fixed a spelling mistake (#1222) 2023-12-30 15:53:22 -08:00
James R. Barlow a5ff3d2f42 Update instructions about custom language packages
Closes #1218
2023-12-27 16:46:10 -08:00
James R. Barlow b71d16dd96 Draw RTL text with reversed matrix instead of reversing characters 2023-12-24 01:34:03 -08:00
James R. Barlow fd593eb5e9 Reversing character order for RTL helps output 2023-12-24 01:24:44 -08:00
James R. Barlow a0b98abb94 Improve and externalize debug rendering settings 2023-12-24 00:10:09 -08:00
James R. Barlow 18353e1e94 Also fix space rendering 2023-12-24 00:09:47 -08:00
James R. Barlow 9adcad84da Overhaul debug rendering to support possible use of Helvetica for testing 2023-12-23 23:47:35 -08:00
James R. Barlow f2714586d8 Fix upside down glyphless font 2023-12-23 23:44:37 -08:00
56 changed files with 397 additions and 63 deletions
@@ -21,7 +21,7 @@ body:
id: reproduce
attributes:
label: Steps to reproduce
description: Please include steps to reproduce
description: Please include steps to reproduce.
value: |
1. Run ocrmypdf -v1 ...arguments... input.pdf output.pdf
2. Open output.pdf
@@ -31,9 +31,8 @@ body:
id: files
attributes:
label: Files
description: Please attach the input and output files, or any screenshots that may be helpful.
placeholder: |
Drag and drop files here.
description: |
Please attach the input and output files, or any screenshots that may be helpful.
If you cannot provide a test file, we probably won't be able to help with the issue.
PDF is a complex file format, and there may be technical details in the PDF that are
@@ -42,10 +41,11 @@ body:
We understand files may contain personal or sensitive information. Here are some options:
- Try reproducing the issue with a file from the test suite. (See tests/resources)
- Try to create another file in the same way as your private file.
- Encrypt the file to OCRmyPDF's private GPG key.
- Encrypt the file to OCRmyPDF's private GPG key, and then zip the GPG file.
- Use ``qpdf --json yourfile.pdf`` to produce a JSON representation of your file that
omits personal information.
placeholder: |
Drag and drop files here.
- type: dropdown
id: packaging-system
attributes:
+1 -1
View File
@@ -189,7 +189,7 @@ jobs:
- name: Install system packages
run: |
choco install --yes --no-progress --pre tesseract
choco install --yes --no-progress --ignore-checksums ghostscript
choco install --yes --no-progress --ignore-checksums ghostscript --version 9.56.1
- name: Install Python packages
run: |
+2 -2
View File
@@ -404,7 +404,7 @@ the following when running in an Administrator command prompt):
* ``choco install --pre tesseract``
* ``choco install pngquant`` (optional)
Either set of commands will install the required software. At the mmoment there is no
Either set of commands will install the required software. At the moment there is no
single command to install Windows.
You may then use ``pip`` to install ocrmypdf. (This can performed by a user or
@@ -681,4 +681,4 @@ 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.
ARM NAS or Raspberry Pi processing small documents, it should work.
+31 -13
View File
@@ -18,16 +18,26 @@ Tesseract's documentation also lists the three-letter code for your language.
Some are anglicized, e.g. Spanish is ``spa`` rather than ``esp``, while others
are not, e.g. German is ``deu`` and French is ``fra``.
Language packs (strictly speaking, Tesseract "traineddata" files) generally correspond
to the language in question, but different language packs are used in certain
situations. For German, the "Fraktur" language pack can assist with reading older
materials in the Fraktur typeface family (``deu_frak``). Some communities have changed
their script from Cyrillic to Latin; the Cyrillic version of Uzbek is available
as ``uzb_cyrl`` and the Latin version is ``uzb``.
After you have installed a language pack, you can use it with ``ocrmypdf -l <language>``,
for example ``ocrmypdf -l spa``. For multilingual documents, you can specify
all languages to be expected, e.g. ``ocrmypdf -l eng+fra`` for English and French.
English is assumed by default unless other language(s) are specified.
For Linux users, you can often find packages that provide language
packs:
packs.
Debian and Ubuntu users
=======================
Platform install steps
======================
Debian and Ubuntu (apt)
-----------------------
.. code-block:: bash
@@ -42,8 +52,8 @@ to what languages it should search for. Multiple languages can be
requested using either ``-l eng+fra`` (English and French) or
``-l eng -l fra``.
Fedora users
============
Fedora
------
.. code-block:: bash
@@ -58,8 +68,8 @@ to what languages it should search for. Multiple languages can be
requested using either ``-l eng+fra`` (English and French) or
``-l eng -l fra``.
Gentoo users
============
Gentoo
------
On Gentoo the package ``app-text/tessdata_fast``, which ``app-text/tesseract`` depends on, handles Tesseract languages.
It accepts USE flags to select what languages should be installed, these can be set in ``/etc/portage/package.use``.
@@ -85,23 +95,31 @@ to what languages it should search for. Multiple languages can be
requested using either ``-l eng+fra`` (English and French) or
``-l eng -l fra``.
macOS users
===========
macOS
-----
You can install additional language packs by
:ref:`installing Tesseract using Homebrew with all language packs <macos-all-languages>`.
Docker users
============
Docker
------
Users of the OCRmyPDF Docker image should install language packs into a
derived Docker image as
:ref:`described in that section <docker-lang-packs>`.
Windows users
=============
Windows
-------
The Tesseract installer provided by Chocolatey currently includes only English language.
To install other languages, download the respective language pack (``.traineddata`` file)
from https://github.com/tesseract-ocr/tessdata/ and place it in
``C:\\Program Files\\Tesseract-OCR\\tessdata`` (or wherever Tesseract OCR is installed).
Custom language packs
=====================
If you have fine-tuned or trained Tesseract and generated custom trained data, you can
copy your ``customlang.traineddata`` file into your Tesseract "tessdata" folder, and
then use the ``-l customlang`` argument to tell OCRmyPDF to pass that language on to
Tesseract.
+17
View File
@@ -30,6 +30,23 @@ OCRmyPDF typically supports the three most recent Python versions.
.. |OCRmyPDF PyPI| image:: https://img.shields.io/pypi/v/ocrmypdf.svg
v16.0.4
=======
- Fixed some issues for left-to-right text with the new hOCR renderer. It is still
not default yet but will be made so soon. Right-to-left text is still in progress.
- Added an error to prevent use of several versions of Ghostscript that seem
corrupt existing text in input PDFs. Newly generated OCR is not affected.
For best results, use Ghostscript 10.02.1 or newer, which contains the fix
for the issue.
v16.0.3
=======
- Changed minimum required Ghostscript to 9.54, to support users of RHEL 9 and its
derivatives, since that is the latest version available there.
- Removed warning message about CVE-2023-43115, on the assumption that most
distributions have backported the patch by now.
v16.0.2
=======
+10 -2
View File
@@ -292,6 +292,7 @@ class OcrGrafter:
# finally move the lower left corner to match the mediabox. All transforms
# must be premultiplied so they are applied in reverse order here.
ctm = corner @ untranslate @ scale @ rotate @ translate
log.debug("Grafting with ctm %r", ctm)
base_resources = _ensure_dictionary(base_page.obj, Name.Resources)
base_xobjs = _ensure_dictionary(base_resources, Name.XObject)
@@ -311,9 +312,16 @@ class OcrGrafter:
if strip_old_text:
strip_invisible_text(self.pdf_base, base_page)
base_page.contents_coalesce()
if self.render_mode == RenderMode.ON_TOP:
# Add q/Q to ensure content we append is drawn correctly
# Strictly speaking this needs to trace the whole q/Q stack in case
# stack is not balanced.
original = base_page.Contents.read_bytes()
base_page.Contents.write(b'q\n' + original + b'\nQ\n')
base_page.contents_add(
new_text_layer, prepend=self.render_mode == RenderMode.ON_TOP
new_text_layer, prepend=self.render_mode == RenderMode.UNDERNEATH
)
base_page.contents_coalesce()
_update_resources(obj=base_page.obj, font=font, font_key=font_key)
+17 -6
View File
@@ -34,7 +34,8 @@ from ocrmypdf.exceptions import (
UnsupportedImageFormatError,
)
from ocrmypdf.helpers import IMG2PDF_KWARGS, Resolution, safe_symlink
from ocrmypdf.hocrtransform import HocrTransform
from ocrmypdf.hocrtransform import DebugRenderOptions, HocrTransform
from ocrmypdf.hocrtransform._font import Courier
from ocrmypdf.pdfa import generate_pdfa_ps
from ocrmypdf.pdfinfo import Colorspace, Encoding, PageInfo, PdfInfo
from ocrmypdf.pluginspec import OrientationConfidence
@@ -741,15 +742,25 @@ def render_hocr_page(hocr: Path, page_context: PageContext) -> Path:
return output_file
dpi = get_page_square_dpi(page_context, calculate_image_dpi(page_context))
debug_mode = options.pdf_renderer == 'hocrdebug'
debug_kwargs = {}
if options.pdf_renderer == 'hocrdebug':
debug_kwargs = dict(
debug_render_options=DebugRenderOptions(
render_baseline=True,
render_triangle=True,
render_line_bbox=False,
render_word_bbox=True,
render_paragraph_bbox=False,
render_space_bbox=False,
),
font=Courier(),
)
HocrTransform(
hocr_filename=hocr,
dpi=dpi.to_scalar(), # square
debug=debug_mode,
hocr_filename=hocr, dpi=dpi.to_scalar(), **debug_kwargs # square
).to_pdf(
out_filename=output_file,
image_filename=None,
invisible_text=True if not debug_kwargs else False,
)
return output_file
+12 -9
View File
@@ -17,7 +17,7 @@ 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()
BLACKLISTED_GS_VERSIONS: frozenset[Version] = frozenset()
@hookimpl
@@ -54,7 +54,7 @@ def check_options(options):
program='gs',
package='ghostscript',
version_checker=ghostscript.version,
need_version='9.55', # Ubuntu 22.04's version
need_version='9.54', # RHEL 9's version; Ubuntu 22.04 has 9.55
)
gs_version = ghostscript.version()
if gs_version in BLACKLISTED_GS_VERSIONS:
@@ -62,13 +62,16 @@ def check_options(options):
f"Ghostscript {gs_version} contains serious regressions and is not "
"supported. Please upgrade to a newer version."
)
if gs_version < Version('10.02.0'):
log.warning(
f"The installed version of Ghostscript {gs_version}, contains a remote "
"code execution security vulnerability. Please upgrade to a newer "
"version. For details see CVE-2023-43115. The issue is not known to "
"affect OCRmyPDF or processing PDFs with Ghostscript, but upgrading "
"Ghostscript is recommended."
if Version('10.0.0') <= gs_version < Version('10.02.1') and (
options.skip_text or options.redo_ocr
):
raise MissingDependencyError(
f"Ghostscript 10.0.0 through 10.02.0 (your version: {gs_version}) "
"contain serious regressions that corrupt PDFs with existing text, "
"such as those processed using --skip-text or --redo-ocr. "
"Please upgrade to a "
"newer version, or use --output-type pdf to avoid Ghostscript, or "
"use --force-ocr to discard existing text."
)
if options.output_type == 'pdfa':
+31 -2
View File
@@ -18,7 +18,12 @@ from pikepdf.canvas import Font
log = logging.getLogger(__name__)
class GlyphlessFont(Font):
class EncodableFont(Font):
def text_encode(self, text: str) -> bytes:
raise NotImplementedError()
class GlyphlessFont(EncodableFont):
CID_TO_GID_DATA = zlib.compress(b"\x00\x01" * 65536)
GLYPHLESS_FONT_NAME = 'pdf.ttf'
GLYPHLESS_FONT = (package_files('ocrmypdf.data') / GLYPHLESS_FONT_NAME).read_bytes()
@@ -27,11 +32,14 @@ class GlyphlessFont(Font):
def __init__(self):
pass
def text_width(self, text: str, fontsize: float) -> int:
def text_width(self, text: str, fontsize: float) -> float:
"""Estimate the width of a text string when rendered with the given font."""
# NFKC: split ligatures, combine diacritics
return len(unicodedata.normalize("NFKC", text)) * (fontsize / self.CHAR_ASPECT)
def text_encode(self, text: str) -> bytes:
return text.encode('utf-16be')
def register(self, pdf: Pdf):
"""Register the glyphless font.
@@ -110,3 +118,24 @@ class GlyphlessFont(Font):
font_descriptor.FontFile2 = pdf.make_stream(self.GLYPHLESS_FONT)
cid_font_type2.FontDescriptor = font_descriptor
return basefont
class Courier(EncodableFont):
"""Courier font."""
def text_width(self, text: str, fontsize: float) -> float:
"""Estimate the width of a text string when rendered with the given font."""
return len(text) * fontsize
def text_encode(self, text: str) -> bytes:
return text.encode('pdfdoc', errors='ignore')
def register(self, pdf: Pdf) -> Dictionary:
"""Register the font."""
return pdf.make_indirect(
Dictionary(
BaseFont=Name.Courier,
Type=Name.Font,
Subtype=Name.Type1,
)
)
+38 -22
View File
@@ -27,11 +27,11 @@ from pikepdf.canvas import (
MAGENTA,
RED,
Canvas,
Font,
Text,
TextDirection,
)
from ocrmypdf.hocrtransform._font import EncodableFont as Font
from ocrmypdf.hocrtransform._font import GlyphlessFont
log = logging.getLogger(__name__)
@@ -45,12 +45,12 @@ Element = ElementTree.Element
class DebugRenderOptions:
"""A class for managing rendering options."""
render_paragraph_bbox: bool
render_baseline: bool
render_triangle: bool
render_line_bbox: bool
render_word_bbox: bool
render_space_bbox: bool
render_paragraph_bbox: bool = False
render_baseline: bool = False
render_triangle: bool = False
render_line_bbox: bool = False
render_word_bbox: bool = False
render_space_bbox: bool = False
class HocrTransformError(Exception):
@@ -81,8 +81,22 @@ class HocrTransform:
debug: bool = False,
fontname: Name = Name("/f-0-0"),
font: Font = GlyphlessFont(),
debug_render_options: DebugRenderOptions | None = None,
):
"""Initialize the HocrTransform object."""
if debug:
log.warning("Use debug_render_options instead", DeprecationWarning)
self.render_options = DebugRenderOptions(
render_baseline=debug,
render_triangle=debug,
render_line_bbox=False,
render_word_bbox=debug,
render_paragraph_bbox=False,
render_space_bbox=False,
)
else:
self.render_options = debug_render_options or DebugRenderOptions()
self.dpi = dpi
self.hocr = ElementTree.parse(os.fspath(hocr_filename))
self._fontname = fontname
@@ -103,14 +117,6 @@ class HocrTransform:
self.height = (coords.ury - coords.lly) / (self.dpi / INCH)
# Stop after first div that has page coordinates
break
self.render_options = DebugRenderOptions(
render_baseline=debug,
render_triangle=debug,
render_line_bbox=False,
render_word_bbox=debug,
render_paragraph_bbox=False,
render_space_bbox=False,
)
def _get_element_text(self, element: Element):
"""Return the textual content of the element and its children."""
@@ -215,12 +221,13 @@ class HocrTransform:
if not found_lines:
# Tesseract did not report any lines (just words)
root = self.hocr.find(self._child_xpath('div', 'ocr_page'))
direction = self._get_text_direction(root)
self._do_line(
canvas,
root,
"ocrx_word",
invisible_text,
TextDirection.LTR,
direction,
True,
)
# put the image on the page, scaled to fill the page
@@ -311,8 +318,7 @@ class HocrTransform:
line_box_height = abs(line_box.height) / cos(angle)
fontsize = line_box_height + intercept
text.font(self._fontname, fontsize)
if invisible_text or True:
text.render_mode(3) # Invisible (indicates OCR text)
text.render_mode(3 if invisible_text else 0)
self._debug_draw_baseline(
canvas, line_matrix.inverse().transform(line_box), 0
@@ -362,10 +368,15 @@ class HocrTransform:
self._debug_draw_word_bbox(canvas, box)
# If this word is 0 units wide, our best bet seems to be to suppress this text
if text_direction == TextDirection.RTL:
log.info("RTL: %s", elemtxt)
if font_width > 0:
text.text_transform(Matrix(1, 0, 0, 1, box.llx, 0))
if text_direction == TextDirection.LTR:
text.text_transform(Matrix(1, 0, 0, -1, box.llx, 0))
elif text_direction == TextDirection.RTL:
text.text_transform(Matrix(-1, 0, 0, -1, box.llx + box.width, 0))
text.horiz_scale(100 * box.width / font_width)
text.show(elemtxt.encode('utf-16be'))
text.show(self._font.text_encode(elemtxt))
# Get coordinates of the next word (if there is one)
hocr_next_box = (
@@ -385,11 +396,16 @@ class HocrTransform:
elif text_direction == TextDirection.RTL:
space_box = Rectangle(next_box.urx, box.lly, box.llx, next_box.ury)
self._debug_draw_space_bbox(canvas, space_box)
text.text_transform(Matrix(1, 0, 0, 1, space_box.llx, 0))
space_width = self._font.text_width(' ', fontsize)
if space_width > 0:
if text_direction == TextDirection.LTR:
text.text_transform(Matrix(1, 0, 0, -1, space_box.llx, 0))
elif text_direction == TextDirection.RTL:
text.text_transform(
Matrix(-1, 0, 0, -1, space_box.llx + space_box.width, 0)
)
text.horiz_scale(100 * space_box.width / space_width)
text.show(' '.encode('utf-16be'))
text.show(self._font.text_encode(' '))
def _debug_draw_paragraph_boxes(self, canvas: Canvas, color=CYAN):
"""Draw boxes around paragraphs in the document."""
@@ -0,0 +1,24 @@
i a la Waterman
4 ons linzen
3 liter water
3 uien
bloem, boter
2 kopjes melk
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
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.
@@ -0,0 +1,15 @@
Tarnose
Bokale oa
Lehuntze
Mugerre
Milafranga Komunikabideak
BAIONA zeiteninsiie —
7 Trenbideak -----
t\ Basusarri — spmeans:20141004 ae: . _ ~
@@ -0,0 +1 @@
Covfefe is a perfectly cromulent word.
@@ -0,0 +1,24 @@
Linzensoep a la Waterman
4 ons linzen
3 liter water
3 uien
bloem, boter
2 kopjes melk
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
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.
@@ -0,0 +1,12 @@
Portez ce vieux whisky au juge
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
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 qualit& de son
ceuvre.
@@ -0,0 +1,22 @@
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]
re Percentage [note 3]
+12
View File
@@ -71,3 +71,15 @@
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.6", "argv_slug": "__-l__eng__000001_ocr.png__000001_ocr_hocr__hocr__txt", "sourcefile": "resources/poster.pdf", "args": ["-l", "eng", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_hocr", "hocr", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.6", "argv_slug": "__-l__eng__000001_ocr.png__000001_ocr_tess__pdf__txt", "sourcefile": "resources/multipage.pdf", "args": ["-l", "eng", "-c", "textonly_pdf=1", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_tess", "pdf", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.6", "argv_slug": "__-l__eng__000001_ocr.png__000001_ocr_hocr__hocr__txt", "sourcefile": "resources/poster.pdf", "args": ["-l", "eng", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_hocr", "hocr", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.7", "argv_slug": "__-l__deu__000001_ocr.png__000001_ocr_tess__pdf__txt", "sourcefile": "resources/francais.pdf", "args": ["-l", "deu", "-c", "textonly_pdf=1", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_tess", "pdf", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.7", "argv_slug": "__-l__eng__--oem__1__000001_ocr.png__000001_ocr_tess__pdf__txt", "sourcefile": "resources/trivial.pdf", "args": ["-l", "eng", "--oem", "1", "-c", "textonly_pdf=1", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_tess", "pdf", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.7", "argv_slug": "__-l__eng__000001_ocr.png__000001_ocr_tess__pdf__txt", "sourcefile": "resources/trivial.pdf", "args": ["-l", "eng", "-c", "textonly_pdf=1", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_tess", "pdf", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.7", "argv_slug": "__-l__eng__000001_ocr.png__000001_ocr_tess__pdf__txt", "sourcefile": "resources/2400dpi.pdf", "args": ["-l", "eng", "-c", "textonly_pdf=1", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_tess", "pdf", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.7", "argv_slug": "__-l__eng__thresholding_method=1__000001_ocr.png__000001_ocr_tess__pdf__txt", "sourcefile": "resources/trivial.pdf", "args": ["-l", "eng", "-c", "textonly_pdf=1", "-c", "thresholding_method=1", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_tess", "pdf", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.7", "argv_slug": "__-l__eng__thresholding_method=2__000001_ocr.png__000001_ocr_tess__pdf__txt", "sourcefile": "resources/trivial.pdf", "args": ["-l", "eng", "-c", "textonly_pdf=1", "-c", "thresholding_method=2", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_tess", "pdf", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.7", "argv_slug": "__-l__eng__000001_ocr.png__000001_ocr_tess__pdf__txt", "sourcefile": "resources/graph_ocred.pdf", "args": ["-l", "eng", "-c", "textonly_pdf=1", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_tess", "pdf", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.7", "argv_slug": "__-l__eng__000002_ocr.png__000002_ocr_tess__pdf__txt", "sourcefile": "resources/3small.pdf", "args": ["-l", "eng", "-c", "textonly_pdf=1", "$TMPDIR/000002_ocr.png", "$TMPDIR/000002_ocr_tess", "pdf", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.7", "argv_slug": "__-l__eng__000001_ocr.png__000001_ocr_tess__pdf__txt", "sourcefile": "resources/3small.pdf", "args": ["-l", "eng", "-c", "textonly_pdf=1", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_tess", "pdf", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.7", "argv_slug": "__-l__eng__000003_ocr.png__000003_ocr_tess__pdf__txt", "sourcefile": "resources/3small.pdf", "args": ["-l", "eng", "-c", "textonly_pdf=1", "$TMPDIR/000003_ocr.png", "$TMPDIR/000003_ocr_tess", "pdf", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.7", "argv_slug": "__-l__eng__000001_ocr.png__000001_ocr_tess__pdf__txt", "sourcefile": "resources/poster.pdf", "args": ["-l", "eng", "-c", "textonly_pdf=1", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_tess", "pdf", "txt"]}
{"tesseract_version": "5.3.2", "system": "Linux", "python": "3.11.7", "argv_slug": "__-l__eng__000001_ocr.png__000001_ocr_tess__pdf__txt", "sourcefile": "resources/poster.pdf", "args": ["-l", "eng", "-c", "textonly_pdf=1", "$TMPDIR/000001_ocr.png", "$TMPDIR/000001_ocr_tess", "pdf", "txt"]}
@@ -0,0 +1,118 @@
The LinnSequencer
32 Track MIDI Sequence Recorder
The LinnSequencer is a state-of-the-art composition and performance tool for the professional musician. It is
extremely powerful, yet amazingly simple to learn and use. Its many remarkable features include:
© Operation is similar to multi-track tape recorder with PLAY, STOP, RECORD, FAST
FORWARD, REWIND, and LOCATE controls.
e Each of the 100 sequences contains 32 simultaneous, polyphonic tracks. Each track may
be assigned to one of 16 MIDI channels. Simultaneously plays up to 16 polyphonic
synthesizers!
© Ultra-fast 3!” disk drive stores complex songs in seconds and holds over 110,000 notes
per disk!
¢ One or all tracks may be TRANSPOSED at the touch of a key.
e Exclusive real-time ERASE function makes editing FAST.
e Exclusive REPEAT function automatically repeats any held notes at a pre-selected
rhythmic value.
¢ TIMING CORRECTION works during playback and operates without chopping notes.
¢ Optional SMPTE time code synchronization.
¢ Optional remote control.
Recording a Sequence
To record a sequence, simply press RECORD and PLAY,
then play your MIDI keyboard in time to the Sequencers
click track. When the sequence loops back around to bar 1,
you ll hear what you played—only all timing errors will be
corrected! (Timing correction may be adjusted or defeated).
Any additional notes played will be added into the track
—existing notes are not erased while recording!
FAST FORWARD, REWIND, and LOCATE controls
may be used at any time to quickly access any location in
your sequence for spot-recording. To overdub a new part,
select a different track and start recording—while you
record, the first track will play in perfect sync (unless you
MUTE it, or SOLO another track). In this way, up to 32
tracks may be overdubbed! All MIDI effects are recorded
including pitch bend, modulation, velocity, aftertouch,
sustain pedal, and program changes!
Editing
To erase a wrong note, simply hold ERASE and press
the note to be erased just before it plays in the sequence—
when played back, it will be gone. Notes may also be
added, erased, or changed using the SINGLE STEP func-
tion. To overdub notes at specific points within a sequence,
Additional Features
simply use LOCATE, FAST FORWARD, or REWIND to
find the desired bar number, then start recording.
The INSERT/COPY function allows you to move bars
from one location to another—in the same sequence or a
different one. For example, you might insert a copy of the
first verse between the second chorus and the bridge.
DELETE BARS operates the same way to remove
unwanted sections.
Creating a Song
One way to create a song is to record each track all the
way through (up to 999 bars). Another way is to record
each basic section (verse, chorus, etc.) in individual
sequences, then use the CREATE SONG function to “chain”
them together. CREATE SONG will then automatically
copy all the parts into a new sequence. If desired, you can
even set the last few bars to repeat infinitely, for a fadeout.
Composition Without Compromise
The technology you use should never be so complex that
it interferes with the creative process. Thats precisely why
the LinnSequencer is designed to let you compose, record
and edit while devoting your undivided attention to your
music. See your Linn dealer today for a demonstration!
¢ Simple, easy to learn operation—the 32 character LCD display clearly guides you through all operations. If needed, the
HELP button displays additional explanations.
® Non-destructive recording—existing notes are not erased while recording.
© Two FOOTSWITCH INPUTS may be assigned to remotely control many of the commonly used functions, including
ERASE, REPEAT, PLAY/STOP, or LOCATE.
¢ Two TRIGGER OUTPUTS may be programmed to output pulses at any selected note value.
@ Will sync to standard LinnDrum or Linn 9000 sync tone.
© Utilizes ultra high-speed, 8 MHz 80186 16 bit computer internally for FAST operation.
¢ TEMPO may be specified in BEATS-PER-MINUTE or FRAMES-PER-BEAT at 24, 25, or 30 frames per second,
(even drop frame!)
¢ TEMPO may be entered numerically, adjustable in tenths of a Beat-Per-Minute increments, or by tapping quarter notes
on the TAP TEMPO button.
¢ TEMPO CHANGES may be programmed into a sequence, with smooth transitions if desired.
e Any TIME SIGNATURE may be used, and may be changed within a song.
linn
Linn Electronics, Inc.
18720 Oxnard Street, Tarzana, CA 91356
(818) 708-8131 TELEX #298949 LINN UR
+4
View File
@@ -4,6 +4,7 @@
from __future__ import annotations
import os
import platform
import pytest
@@ -13,6 +14,9 @@ from .conftest import run_ocrmypdf_api
@pytest.mark.skipif(os.name == 'nt', reason="Windows doesn't have SIGKILL")
@pytest.mark.skipif(
platform.python_version_tuple() >= ('3', '12'), reason="can deadlock due to fork"
)
def test_simulate_oom_killer(multipage, no_outpdf):
exitcode = run_ocrmypdf_api(
multipage,