#!/usr/bin/env python3 """Convert an XWD dump (from xwd) into a PNG, without external image libraries. Usage: xwd -root -silent | python3 xwd2png.py out.png """ import struct import sys import zlib def read_header(data): fields = struct.unpack(">25I", data[:100]) names = [ "header_size", "file_version", "pixmap_format", "pixmap_depth", "pixmap_width", "pixmap_height", "xoffset", "byte_order", "bitmap_unit", "bitmap_bit_order", "bitmap_pad", "bits_per_pixel", "bytes_per_line", "visual_class", "red_mask", "green_mask", "blue_mask", "bits_per_rgb", "colormap_entries", "ncolors", "window_width", "window_height", "window_x", "window_y", "window_bdrwidth", ] return dict(zip(names, fields)) def write_png(path, width, height, rgb_rows): raw = b"".join(b"\x00" + row for row in rgb_rows) def chunk(tag, payload): return (struct.pack(">I", len(payload)) + tag + payload + struct.pack(">I", zlib.crc32(tag + payload) & 0xFFFFFFFF)) header = struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0) png = (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", header) + chunk(b"IDAT", zlib.compress(raw, 6)) + chunk(b"IEND", b"")) with open(path, "wb") as handle: handle.write(png) def main(): out_path = sys.argv[1] if len(sys.argv) > 1 else "out.png" data = sys.stdin.buffer.read() head = read_header(data) width = head["pixmap_width"] height = head["pixmap_height"] bpp = head["bits_per_pixel"] stride = head["bytes_per_line"] offset = head["header_size"] + head["ncolors"] * 12 pixels = data[offset:] if bpp == 32: rows = [] for y in range(height): line = pixels[y * stride:y * stride + width * 4] out = bytearray(width * 3) for x in range(width): src = x * 4 dst = x * 3 out[dst] = line[src + 2] out[dst + 1] = line[src + 1] out[dst + 2] = line[src] rows.append(bytes(out)) elif bpp == 24: rows = [] for y in range(height): line = pixels[y * stride:y * stride + width * 3] out = bytearray(width * 3) for x in range(width): src = x * 3 dst = x * 3 out[dst] = line[src + 2] out[dst + 1] = line[src + 1] out[dst + 2] = line[src] rows.append(bytes(out)) else: sys.stderr.write("unsupported bits_per_pixel=%s\n" % bpp) return 2 write_png(out_path, width, height, rows) print("%s %dx%d" % (out_path, width, height)) return 0 if __name__ == "__main__": sys.exit(main())