optimize: refactor tricky /Filter and /DecodeParms handling

This commit is contained in:
James R. Barlow
2018-04-18 15:30:21 -07:00
parent 3869996758
commit f5d308a156
+50 -23
View File
@@ -34,6 +34,44 @@ PAGE_GROUP_SIZE = 10
SIMPLE_COLORSPACES = ('/DeviceRGB', '/DeviceGray', '/CalRGB', '/CalGray')
def filter_decodeparms(obj):
"""
PDF has a lot of optional data structures concerning /Filter and
/DecodeParms. /Filter can be absent or a name or an array, /DecodeParms
can be absent or a dictionary (if /Filter is a name) or an array (if
/Filter is an array). When both are arrays the lengths match.
Normalize this into:
[(/FilterName, {/DecodeParmName: Value, ...}), ...]
The order of /Filter matters as indicates the encoding/decoding sequence.
"""
normalized = []
filters = []
filt = obj.get('/Filter', pikepdf.Array([]))
if filt.type_code == pikepdf.ObjectType.array:
filters.extend(filt)
elif filt.type_code == pikepdf.ObjectType.name:
filters.append(filt)
decodeparms = obj.get('/DecodeParms', pikepdf.Array([]))
if decodeparms.type_code == pikepdf.ObjectType.dictionary:
decodeparms = pikepdf.Array([decodeparms])
for n, dp in enumerate(decodeparms):
filt_parm = (filters[n], dp)
normalized.append(filt_parm)
if len(normalized) == 0:
for filt in filters:
filt_parm = (filt, {})
normalized.append(filt_parm)
if len(normalized) == 0:
return None
return normalized
def generate_ccitt_header(data, w, h, decode_parms):
# https://stackoverflow.com/questions/2641770/
# https://www.itu.int/itudoc/itu-t/com16/tiff-fx/docs/tiff6.pdf
@@ -82,20 +120,17 @@ def extract_image(doc, pike, root, log, image, xref, jbig2s,
cs = image.get('/ColorSpace', '')
w = int(image.Width)
h = int(image.Height)
if filt.type_code == pikepdf.ObjectType.array:
if len(filt) > 1:
log.debug("Skipping multiply filtered, xref {}".format(xref))
return False # Not supported: multiple filters
elif len(filt) == 1:
filt = filt[0]
else:
filt = ''
if bpc == 1 and filt != '/JBIG2Decode':
decode_parms = image.get('/DecodeParms')
filtdps = filter_decodeparms(image)
if filtdps and len(filtdps) > 1:
log.debug("Skipping multiply filtered, xref {}".format(xref))
return False
filtdp = filtdps[0]
if bpc == 1 and filtdp[0] != '/JBIG2Decode':
if filt == '/CCITTFaxDecode':
data = image.read_raw_bytes()
try:
header = generate_ccitt_header(data, w, h, decode_parms)
header = generate_ccitt_header(data, w, h, filtdp[1])
except ValueError as e:
log.info(e)
return False
@@ -108,20 +143,12 @@ def extract_image(doc, pike, root, log, image, xref, jbig2s,
else:
return False
jbig2s.append(xref)
elif filt == '/JPXDecode':
elif filtdp[0] == '/JPXDecode':
return False
elif filt == '/DCTDecode' and cs in SIMPLE_COLORSPACES:
elif filtdp[0] == '/DCTDecode' and cs in SIMPLE_COLORSPACES:
raw_jpeg = pike._get_object_id(xref, 0)
dp = raw_jpeg.get('/DecodeParms', None)
color_transform = None
try:
color_transform = dp[0].get('/ColorTransform', 1)
except ValueError:
try:
color_transform = dp.get('/ColorTransform', 1)
except ValueError:
pass
if color_transform is not None and color_transform != 1:
color_transform = filtdp[1].get('/ColorTransform', 1)
if color_transform != 1:
return False # Don't mess with JPEGs other than YUV
raw_jpeg_data = raw_jpeg.read_raw_bytes()
(root / '{:08d}.jpg'.format(xref)).write_bytes(raw_jpeg_data)