jp2: rudimentary support for raw jpeg2000 without jp2 boxes

This commit is contained in:
Johannes Schauer Marin Rodrigues 2023-08-08 07:40:38 +02:00
parent 2f736d7891
commit 09064e8e70
Signed by untrusted user: josch
GPG key ID: F2CBA5C78FBD83E1
2 changed files with 42 additions and 12 deletions

View file

@ -37,7 +37,7 @@ if hasattr(GifImagePlugin, "LoadingStrategy"):
# TiffImagePlugin.DEBUG = True # TiffImagePlugin.DEBUG = True
from PIL.ExifTags import TAGS from PIL.ExifTags import TAGS
from datetime import datetime, timezone from datetime import datetime, timezone
from jp2 import parsejp2 import jp2
from enum import Enum from enum import Enum
from io import BytesIO from io import BytesIO
import logging import logging
@ -1301,7 +1301,7 @@ def get_imgmetadata(
if imgformat == ImageFormat.JPEG2000 and rawdata is not None and imgdata is None: if imgformat == ImageFormat.JPEG2000 and rawdata is not None and imgdata is None:
# this codepath gets called if the PIL installation is not able to # this codepath gets called if the PIL installation is not able to
# handle JPEG2000 files # handle JPEG2000 files
imgwidthpx, imgheightpx, ics, hdpi, vdpi, channels, bpp = parsejp2(rawdata) imgwidthpx, imgheightpx, ics, hdpi, vdpi, channels, bpp = jp2.parse(rawdata)
if hdpi is None: if hdpi is None:
hdpi = default_dpi hdpi = default_dpi
@ -1843,7 +1843,7 @@ def read_images(
cleanup() cleanup()
depth = 8 depth = 8
if imgformat == ImageFormat.JPEG2000: if imgformat == ImageFormat.JPEG2000:
_, _, _, _, _, _, depth = parsejp2(rawdata) *_, depth = jp2.parse(rawdata)
return [ return [
( (
color, color,
@ -2241,7 +2241,7 @@ def read_images(
r, g, b, a = newimg.convert(mode="RGBA").split() r, g, b, a = newimg.convert(mode="RGBA").split()
newimg = Image.merge("RGB", (r, g, b)) newimg = Image.merge("RGB", (r, g, b))
smaskidat, _, _ = to_png_data(a) smaskidat, *_ = to_png_data(a)
logger.warning( logger.warning(
"Image contains an alpha channel. Computing a separate " "Image contains an alpha channel. Computing a separate "
"soft mask (/SMask) image to store transparency in PDF." "soft mask (/SMask) image to store transparency in PDF."

View file

@ -101,7 +101,9 @@ def parsejp2(data):
while byteStart < noBytes and boxLengthValue != 0: while byteStart < noBytes and boxLengthValue != 0:
boxLengthValue, boxType, byteEnd, boxContents = getBox(data, byteStart, noBytes) boxLengthValue, boxType, byteEnd, boxContents = getBox(data, byteStart, noBytes)
if boxType == b"jp2h": if boxType == b"jp2h":
width, height, colorspace, hdpi, vdpi, channels, bpp = parse_jp2h(boxContents) width, height, colorspace, hdpi, vdpi, channels, bpp = parse_jp2h(
boxContents
)
break break
byteStart = byteEnd byteStart = byteEnd
if not width: if not width:
@ -114,10 +116,38 @@ def parsejp2(data):
return (width, height, colorspace, hdpi, vdpi, channels, bpp) return (width, height, colorspace, hdpi, vdpi, channels, bpp)
def parsej2k(data):
lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, _, _, _, _, csiz = struct.unpack(
">HHIIIIIIIIH", data[4:42]
)
ssiz = [None] * csiz
xrsiz = [None] * csiz
yrsiz = [None] * csiz
for i in range(csiz):
ssiz[i], xrsiz[i], yrsiz[i] = struct.unpack(
"BBB", data[42 + 3 * i : 42 + 3 * (i + 1)]
)
assert ssiz == [7, 7, 7]
return xsiz - xosiz, ysiz - yosiz, None, None, None, csiz, 8
def parse(data):
if data[:4] == b"\xff\x4f\xff\x51":
return parsej2k(data)
else:
return parsejp2(data)
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
width, height, colorspace = parsejp2(open(sys.argv[1]).read()) width, height, colorspace, hdpi, vdpi, channels, bpp = parse(
sys.stdout.write("width = %d" % width) open(sys.argv[1], "rb").read()
sys.stdout.write("height = %d" % height) )
sys.stdout.write("colorspace = %s" % colorspace) print("width = %d" % width)
print("height = %d" % height)
print("colorspace = %s" % colorspace)
print("hdpi = %s" % hdpi)
print("vdpi = %s" % vdpi)
print("channels = %s" % channels)
print("bpp = %s" % bpp)