Add RTL output - seems to work, but debug does not

This commit is contained in:
James R. Barlow
2023-11-20 20:28:07 -08:00
parent d944254e45
commit 73f8f6aac8
2 changed files with 59 additions and 28 deletions
+22 -4
View File
@@ -7,6 +7,7 @@ import logging
import unicodedata
from contextlib import contextmanager
from dataclasses import dataclass
from enum import Enum
from importlib.resources import files as package_files
from pathlib import Path
@@ -29,6 +30,11 @@ GLYPHLESS_FONT = (package_files('ocrmypdf.data') / GLYPHLESS_FONT_NAME).read_byt
CHAR_ASPECT = 2
class TextDirection(Enum):
LTR = ...
RTL = ...
def register_glyphlessfont(pdf: Pdf):
"""Register the glyphless font.
@@ -141,7 +147,7 @@ class ContentStreamBuilder:
self._instructions.append(inst)
return self
def begin_marked_content(self, mctype: Name, mcid: int):
def begin_marked_content_proplist(self, mctype: Name, mcid: int):
"""Begin marked content sequence."""
inst = ContentStreamInstruction(
[mctype, Dictionary(MCID=mcid)], Operator("BDC")
@@ -149,6 +155,12 @@ class ContentStreamBuilder:
self._instructions.append(inst)
return self
def begin_marked_content(self, mctype: Name):
"""Begin marked content sequence."""
inst = ContentStreamInstruction([mctype], Operator("BMC"))
self._instructions.append(inst)
return self
def end_marked_content(self):
"""End marked content sequence."""
inst = ContentStreamInstruction([], Operator("EMC"))
@@ -396,10 +408,11 @@ class PikepdfCanvas:
class PikepdfText:
def __init__(self, x=0, y=0, direction=None):
def __init__(self, x=0, y=0, direction=TextDirection.LTR):
self._cs = ContentStreamBuilder()
self._cs.begin_text()
self._p0 = (x, y)
self._direction = direction
def set_font(self, font, size):
self._cs.set_text_font(Name("/f-0-0"), size)
@@ -414,8 +427,13 @@ class PikepdfText:
self._p0 = (matrix.e, matrix.f)
return self
def show(self, text):
self._cs.show_text(text)
def show(self, text: str):
if self._direction == TextDirection.LTR:
self._cs.show_text(text)
else:
self._cs.begin_marked_content(Name.ReversedChars)
self._cs.show_text(text)
self._cs.end_marked_content()
return self
def set_horiz_scale(self, scale):
+37 -24
View File
@@ -20,7 +20,7 @@ from xml.etree import ElementTree
from pikepdf import Matrix, Rectangle
from ocrmypdf.hocrtransform._canvas import PikepdfCanvas as Canvas
from ocrmypdf.hocrtransform._canvas import PikepdfText
from ocrmypdf.hocrtransform._canvas import PikepdfText, TextDirection
from ocrmypdf.hocrtransform.color import (
BLACK,
BLUE,
@@ -94,9 +94,9 @@ class HocrTransform:
render_baseline=False,
render_triangle=False,
render_line_bbox=False,
render_word_bbox=True,
render_word_bbox=False,
render_paragraph_bbox=False,
render_space_bbox=True,
render_space_bbox=False,
)
def _get_element_text(self, element: Element):
@@ -183,21 +183,23 @@ class HocrTransform:
self._debug_draw_paragraph_boxes(canvas)
found_lines = False
for line in (
element
for element in self.hocr.iterfind(self._child_xpath('span'))
if 'class' in element.attrib
and element.attrib['class']
in {'ocr_header', 'ocr_line', 'ocr_textfloat'}
):
found_lines = True
self._do_line(
canvas,
line,
"ocrx_word",
fontname,
invisible_text,
)
for par in self.hocr.iterfind(self._child_xpath('p', 'ocr_par')):
for line in (
element
for element in par.iterfind(self._child_xpath('span'))
if 'class' in element.attrib
and element.attrib['class']
in {'ocr_header', 'ocr_line', 'ocr_textfloat'}
):
found_lines = True
direction = (
TextDirection.RTL
if par.attrib.get('dir', 'ltr') == 'rtl'
else TextDirection.LTR
)
self._do_line(
canvas, line, "ocrx_word", fontname, invisible_text, direction
)
if not found_lines:
# Tesseract did not report any lines (just words)
@@ -208,6 +210,7 @@ class HocrTransform:
"ocrx_word",
fontname,
invisible_text,
TextDirection.LTR,
)
# put the image on the page, scaled to fill the page
if image_filename is not None:
@@ -230,6 +233,7 @@ class HocrTransform:
elemclass: str,
fontname: str,
invisible_text: bool,
text_direction: TextDirection,
):
"""Render the text for a given line.
@@ -265,7 +269,7 @@ class HocrTransform:
)
canvas.do.cm(line_matrix)
log.debug(line_matrix)
text = PikepdfText()
text = PikepdfText(direction=text_direction)
# Don't allow the font to break out of the bounding box. Division by
# cos_a accounts for extra clearance between the glyph's vertical axis
@@ -291,6 +295,7 @@ class HocrTransform:
fontsize,
elem,
next_elem,
text_direction,
)
canvas.do.draw_text(text)
@@ -303,6 +308,7 @@ class HocrTransform:
fontsize: float,
elem: Element,
next_elem: Element | None,
text_direction: TextDirection,
):
"""Render the text for a single word."""
if elem is None:
@@ -337,11 +343,18 @@ class HocrTransform:
# occupy the space the between the words helps the PDF viewer
# avoid combiningthewordstogether.
next_box = line_matrix.inverse().transform(hocr_next_box)
space_box = Rectangle(box.urx, box.lly, next_box.llx, next_box.ury)
self._debug_draw_space_bbox(canvas, space_box)
text.set_text_transform(Matrix(1, 0, 0, 1, space_box.llx, 0))
space_width = canvas.string_width(' ', fontname, fontsize)
space_box_width = space_box.urx - space_box.llx
if text_direction == TextDirection.LTR:
space_box = Rectangle(box.urx, box.lly, next_box.llx, next_box.ury)
self._debug_draw_space_bbox(canvas, space_box)
text.set_text_transform(Matrix(1, 0, 0, 1, space_box.llx, 0))
space_width = canvas.string_width(' ', fontname, fontsize)
space_box_width = space_box.urx - space_box.llx
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.set_text_transform(Matrix(1, 0, 0, 1, space_box.llx, 0))
space_width = canvas.string_width(' ', fontname, fontsize)
space_box_width = space_box.ury - space_box.lly
text.set_horiz_scale(100 * space_box_width / space_width)
text.show(' ')