Overhaul debug rendering to support possible use of Helvetica for testing

This commit is contained in:
James R. Barlow
2023-12-23 23:47:35 -08:00
parent f2714586d8
commit 9adcad84da
3 changed files with 68 additions and 25 deletions
+13 -4
View File
@@ -34,7 +34,7 @@ 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.pdfa import generate_pdfa_ps
from ocrmypdf.pdfinfo import Colorspace, Encoding, PageInfo, PdfInfo
from ocrmypdf.pluginspec import OrientationConfidence
@@ -741,12 +741,21 @@ 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 = options.pdf_renderer == 'hocrdebug'
debug_render_options = None
if debug:
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,
)
HocrTransform(
hocr_filename=hocr,
dpi=dpi.to_scalar(), # square
debug=debug_mode,
debug_render_options=debug_render_options,
).to_pdf(
out_filename=output_file,
image_filename=None,
+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 Helvetica(EncodableFont):
"""Helvetica 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 / 2.0
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.Helvetica,
Type=Name.Font,
Subtype=Name.Type1,
)
)
+24 -19
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."""
@@ -311,8 +317,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
@@ -365,7 +370,7 @@ class HocrTransform:
if font_width > 0:
text.text_transform(Matrix(1, 0, 0, -1, box.llx, 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 = (
@@ -389,7 +394,7 @@ class HocrTransform:
space_width = self._font.text_width(' ', fontsize)
if space_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."""