def getc(): c = infile.read(1) if not c: if not allow_eof or EOF[0]: raise png.Error('premature End of file') else: # small hack to simulate trailing whitespace at the end of file EOF[0] = True # but only once return ' ' return c
def deblock(f): """ Decompress a single block from a compressed Plan 9 image file. Each block starts with 2 decimal strings of 12 bytes each. Yields a sequence of (row, data) pairs where row is the total number of rows processed according to the file format and data is the decompressed data for a set of rows. """ row = int(f.read(12)) size = int(f.read(12)) if not (0 <= size <= 6000): raise 'block has invalid size; not a Plan 9 image file?' # Since each block is at most 6000 bytes we may as well read it all in # one go. d = f.read(size) i = 0 o = [] while i < size: x = ord(d[i:i + 1]) # hack to avoid interpreting bytes item as int i += 1 if x & 0x80: x = (x & 0x7f) + 1 lit = d[i:i + x] i += x o.extend(lit) continue # x's high-order bit is 0 l = (x >> 2) + 3 # Offset is made from bottom 2 bits of x and all 8 bits of next # byte. http://plan9.bell-labs.com/magic/man2html/6/image doesn't # say whether x's 2 bits are most significant or least significant. # But it is clear from inspecting a random file, # http://plan9.bell-labs.com/sources/plan9/sys/games/lib/sokoban/images/cargo.bit # that x's 2 bit are most significant. offset = (x & 3) << 8 offset |= ord(d[i:i + 1]) i += 1 # Note: complement operator neatly maps (0 to 1023) to (-1 to # -1024). Adding len(o) gives a (non-negative) offset into o from # which to start indexing. offset = ~offset + len(o) if offset < 0: raise png.Error('byte offset indexes off the begininning' 'of the output buffer; not a Plan 9 image file?') for j in range(l): o.append(o[offset + j]) try: res = ''.join(o) except: res = bytearray(o) return row, res
def read_pam_header(infile): """ Read (the rest of a) PAM header. `infile` should be positioned immediately after the initial 'P7' line (at the beginning of the second line). Returns are as for `read_pnm_header`. """ # Unlike PBM, PGM, and PPM, we can read the header a line at a time. header = dict() while True: l = infile.readline().strip() if l == png.strtobytes('ENDHDR'): break if not l: raise EOFError('PAM ended prematurely') if l[0] == png.strtobytes('#'): continue l = l.split(None, 1) if l[0] not in header: header[l[0]] = l[1] else: header[l[0]] += png.strtobytes(' ') + l[1] required = ['WIDTH', 'HEIGHT', 'DEPTH', 'MAXVAL'] required = [png.strtobytes(x) for x in required] WIDTH, HEIGHT, DEPTH, MAXVAL = required present = [x for x in required if x in header] if len(present) != len(required): raise png.Error('PAM file must specify ' 'WIDTH, HEIGHT, DEPTH, and MAXVAL') width = int(header[WIDTH]) height = int(header[HEIGHT]) depth = int(header[DEPTH]) maxval = int(header[MAXVAL]) if (width <= 0 or height <= 0 or depth <= 0 or maxval <= 0): raise png.Error( 'WIDTH, HEIGHT, DEPTH, MAXVAL must all be positive integers') return 'P7', width, height, depth, maxval
def read_int_tokens(infile, n, allow_eof=False): """ Read ASCII integers separated with whitespaces as list of length `n` Skip comments started with '#' to the end of line If comment starts right after digit and newline starts with digit these digits form single number. """ result = [] EOF = [False] # Hack to allow modification in nested function # We may consume less or more than one line, so characters read one by one def getc(): c = infile.read(1) if not c: if not allow_eof or EOF[0]: raise png.Error('premature End of file') else: # small hack to simulate trailing whitespace at the end of file EOF[0] = True # but only once return ' ' return c token = bytes() while True: c = getc() if c.isspace(): if token: # post-token whitespace, save token result.append(int(token)) if len(result) == n: # we get here on last whitespace break # and clean for new token token = bytes() # Skip whitespace that precedes a token or between tokens. elif c == png.strtobytes('#'): # Skip comments to the end of line. infile.readline() # If there is no whitespaces after conventional newline # continue reading token elif c.isdigit(): token += c else: raise png.Error('unexpected character %s found ' % c) return result