feat: expose fill-color ink classification on ImageInfo (#1688)
This commit is contained in:
@@ -36,6 +36,7 @@ from ocrmypdf.pdfinfo._types import (
|
||||
UNIT_SQUARE,
|
||||
Colorspace,
|
||||
Encoding,
|
||||
Ink,
|
||||
)
|
||||
|
||||
logger = logging.getLogger()
|
||||
@@ -61,10 +62,12 @@ class ImageInfo:
|
||||
pdfimage: Object | None = None,
|
||||
inline: PdfInlineImage | None = None,
|
||||
shorthand=None,
|
||||
fill_ink: Ink | None = None,
|
||||
):
|
||||
"""Initialize an ImageInfo."""
|
||||
self._name = str(name)
|
||||
self._shorthand = shorthand
|
||||
self._fill_ink = fill_ink
|
||||
|
||||
pim: PdfInlineImage | PdfImage
|
||||
|
||||
@@ -175,6 +178,17 @@ class ImageInfo:
|
||||
"""Type of image, either 'image' or 'stencil'."""
|
||||
return self._type
|
||||
|
||||
@property
|
||||
def ink(self) -> Ink | None:
|
||||
"""Fill-color classification for stencil masks, else None.
|
||||
|
||||
A stencil (image mask) is painted with the current fill color; this
|
||||
reports whether that color is mono/gray/color so the rasterizer can
|
||||
choose a device that does not discard the distinction. Non-stencil
|
||||
images return None.
|
||||
"""
|
||||
return self._fill_ink if self._type == 'stencil' else None
|
||||
|
||||
@property
|
||||
def width(self) -> int:
|
||||
"""Width of the image in pixels."""
|
||||
@@ -249,7 +263,10 @@ def _find_inline_images(contentsinfo: ContentsInfo) -> Iterator[ImageInfo]:
|
||||
"""Find inline images in the contentstream."""
|
||||
for n, inline in enumerate(contentsinfo.inline_images):
|
||||
yield ImageInfo(
|
||||
name=f'inline-{n:02d}', shorthand=inline.shorthand, inline=inline.iimage
|
||||
name=f'inline-{n:02d}',
|
||||
shorthand=inline.shorthand,
|
||||
inline=inline.iimage,
|
||||
fill_ink=inline.fill_ink,
|
||||
)
|
||||
|
||||
|
||||
@@ -300,7 +317,12 @@ def _find_regular_images(
|
||||
# these from our DPI calculation for the page.
|
||||
continue
|
||||
|
||||
yield ImageInfo(name=draw.name, pdfimage=pdfimage, shorthand=draw.shorthand)
|
||||
yield ImageInfo(
|
||||
name=draw.name,
|
||||
pdfimage=pdfimage,
|
||||
shorthand=draw.shorthand,
|
||||
fill_ink=draw.fill_ink,
|
||||
)
|
||||
|
||||
|
||||
def _find_form_xobject_images(pdf: Pdf, container: Object, contentsinfo: ContentsInfo):
|
||||
|
||||
@@ -371,3 +371,64 @@ def test_fill_ink_tolerates_malformed_color_operands(body):
|
||||
)
|
||||
def test_ink_from_components(space, comps, expected):
|
||||
assert _ink_from_components(space, comps) is Ink[expected]
|
||||
|
||||
|
||||
def _make_image_mask_pdf(path, content_fill: bytes):
|
||||
"""Build a 1-page PDF with one 8x8 image mask painted with content_fill.
|
||||
|
||||
content_fill is the color operator sequence emitted before drawing the
|
||||
mask, e.g. b"0.263 0.263 0.263 rg".
|
||||
"""
|
||||
pdf = pikepdf.Pdf.new()
|
||||
pdf.add_blank_page(page_size=(72, 72))
|
||||
# 8x8 1-bpc mask, each row padded to a byte (1 byte per row).
|
||||
mask_bytes = bytes([0x7E] * 8)
|
||||
mask = pikepdf.Stream(pdf, mask_bytes)
|
||||
mask.Type = pikepdf.Name.XObject
|
||||
mask.Subtype = pikepdf.Name.Image
|
||||
mask.Width = 8
|
||||
mask.Height = 8
|
||||
mask.ImageMask = True
|
||||
mask.BitsPerComponent = 1
|
||||
name = pdf.pages[0].add_resource(mask, pikepdf.Name.XObject)
|
||||
pdf.pages[0].Contents = pikepdf.Stream(
|
||||
pdf, b"q 72 0 0 72 0 0 cm %s %s Do Q" % (content_fill, bytes(name))
|
||||
)
|
||||
pdf.save(path)
|
||||
return path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mask_gray_pdf(outdir):
|
||||
return _make_image_mask_pdf(outdir / 'mask_gray.pdf', b"0.263 0.263 0.263 rg")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mask_rgb_pdf(outdir):
|
||||
return _make_image_mask_pdf(outdir / 'mask_rgb.pdf', b"0.8 0.2 0.2 rg")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mask_black_pdf(outdir):
|
||||
return _make_image_mask_pdf(outdir / 'mask_black.pdf', b"0 g")
|
||||
|
||||
|
||||
def test_imageinfo_ink_gray(mask_gray_pdf):
|
||||
image = pdfinfo.PdfInfo(mask_gray_pdf)[0].images[0]
|
||||
assert image.type_ == 'stencil'
|
||||
assert image.ink is Ink.gray
|
||||
|
||||
|
||||
def test_imageinfo_ink_color(mask_rgb_pdf):
|
||||
image = pdfinfo.PdfInfo(mask_rgb_pdf)[0].images[0]
|
||||
assert image.ink is Ink.color
|
||||
|
||||
|
||||
def test_imageinfo_ink_black(mask_black_pdf):
|
||||
image = pdfinfo.PdfInfo(mask_black_pdf)[0].images[0]
|
||||
assert image.ink is Ink.mono
|
||||
|
||||
|
||||
def test_imageinfo_ink_none_for_regular_image(eight_by_eight_regular_image):
|
||||
image = pdfinfo.PdfInfo(eight_by_eight_regular_image)[0].images[0]
|
||||
assert image.ink is None
|
||||
|
||||
Reference in New Issue
Block a user