Xtool -mpng+reflate ((full)) Online
Here’s a solid, production-ready feature implementation for that adds support for MPNG (Multi-Piece PNG) + reflate (recompressing/zlib stream repair).
while True: chunk_type, data, _ = read_chunk(f) if chunk_type == CHUNK_TYPE_MPNG: # MPNG chunk structure: [index:4][compressed_data...] idx = struct.unpack('>I', data[:4])[0] compressed = data[4:] streams.append((idx, compressed)) elif chunk_type == CHUNK_TYPE_IEND: break return streams def reflate_stream(compressed_data, level=6, extract_only=False): """Reflate: decompress then recompress zlib stream""" decompressed = zlib.decompress(compressed_data) if extract_only: return decompressed # raw decompressed data recompressed = zlib.compress(decompressed, level) return recompressed xtool -mpng+reflate
def extract_mpng_streams(png_path): """Extract all zlib-compressed streams from MPNG chunks""" streams = [] with open(png_path, 'rb') as f: # Verify PNG signature if f.read(8) != PNG_SIGNATURE: raise ValueError("Not a valid PNG file") Here’s a solid
if chunk_type == CHUNK_TYPE_IEND: break def mpng_reflate_cli(args): input_png = args.input output_png = args.output or input_png.replace('.png', '_reflated.png') level = args.recompress_level or 6 extract_only = args.extract_only replace_data = args.replace data[:4])[0] compressed = data[4:] streams.append((idx
while True: chunk_type, data, crc = read_chunk(fin) if chunk_type == CHUNK_TYPE_MPNG: idx = struct.unpack('>I', data[:4])[0] if replace_map and idx in replace_map: new_compressed = replace_map[idx] else: # Find matching stream and reflate for stored_idx, comp in streams: if stored_idx == idx: new_compressed = reflate_stream(comp, recompress_level) break else: new_compressed = data[4:] # unchanged new_data = struct.pack('>I', idx) + new_compressed write_chunk(fout, CHUNK_TYPE_MPNG, new_data) else: # Copy other chunks unchanged fout.write(struct.pack('>I', len(data))) fout.write(chunk_type) fout.write(data) fout.write(struct.pack('>I', crc))
PNG_SIGNATURE = b'\x89PNG\r\n\x1a\n' CHUNK_TYPE_MPNG = b'mPNg' # Custom MPNG chunk CHUNK_TYPE_IDAT = b'IDAT' CHUNK_TYPE_IEND = b'IEND'