def decode(effects): """ Reads and decodes info about layer effects. """ fp = io.BytesIO(effects) version, effects_count = read_fmt("HH", fp) effects_list = [] for idx in range(effects_count): sig = fp.read(4) if sig != b'8BIM': raise Error("Error parsing layer effect: invalid signature (%r)" % sig) effect_type = fp.read(4) if not EffectOSType.is_known(effect_type): warnings.warn("Unknown effect type (%s)" % effect_type) effect_info_length = read_fmt("I", fp)[0] effect_info = fp.read(effect_info_length) decoder = _effect_info_decoders.get(effect_type, lambda data: data) effects_list.append(LayerEffect(effect_type, decoder(effect_info))) return Effects(version, effects_count, effects_list)
def _read_layer_record(fp, encoding, version): """ Reads single layer record. """ top, left, bottom, right, num_channels = read_fmt("4i H", fp) logger.debug(' top=%d, left=%d, bottom=%d, right=%d, num_channels=%d', top, left, bottom, right, num_channels) channel_info = [] for channel_num in range(num_channels): if version == 1: info = ChannelInfo(*read_fmt("hI", fp)) elif version == 2: info = ChannelInfo(*read_fmt("hQ", fp)) channel_info.append(info) sig = fp.read(4) if sig != b'8BIM': raise Error("Error parsing layer: invalid signature (%r)" % sig) blend_mode = fp.read(4) if not BlendMode.is_known(blend_mode): warnings.warn("Unknown blend mode (%s)" % blend_mode) opacity, clipping, flags, extra_length = read_fmt("BBBxI", fp) if not Clipping.is_known(clipping): warnings.warn("Unknown clipping (%s)" % clipping) logger.debug(' extra_length=%s', extra_length) flags = LayerFlags( bool(flags & 1), not bool(flags & 2), # why "not"? bool(flags & 16) if bool(flags & 8) else None) start_pos = fp.tell() mask_data = _read_layer_mask_data(fp) blending_ranges = _read_layer_blending_ranges(fp) name = read_pascal_string(fp, encoding, 4) remaining_length = extra_length - (fp.tell() - start_pos) logger.debug(' reading layer tagged blocks...') logger.debug(' length=%d, start_pos=%d', remaining_length, fp.tell()) tagged_blocks = _read_layer_tagged_blocks(fp, remaining_length, version) remaining_length = extra_length - (fp.tell() - start_pos) if remaining_length > 0: fp.seek(remaining_length, 1) # skip the remainder logger.debug(' skipping %s bytes', remaining_length) return LayerRecord(top, left, bottom, right, num_channels, channel_info, blend_mode, opacity, clipping, flags, mask_data, blending_ranges, name, tagged_blocks)
def _read_blend_mode(fp): sig = fp.read(4) if sig != b'8BIM': raise Error("Error parsing layer effect: invalid signature (%r)" % sig) blend_mode = fp.read(4) if not BlendMode.is_known(blend_mode): warnings.warn("Unknown blend mode (%s)" % blend_mode) return blend_mode
def read(fp): """ Reads PSD file header. """ logger.debug("reading header..") signature = fp.read(4) if signature != b'8BPS': raise Error("This is not a PSD or PSB file") version = read_fmt("H", fp)[0] if not version in (1, 2): raise Error("Unsupported PSD version (%s)" % version) header = PsdHeader(version, *read_fmt("6x HIIHH", fp)) if not ColorMode.is_known(header.color_mode): warnings.warn("Unknown color mode: %s" % header.color_mode) logger.debug(header) return header
def _read_block(fp, encoding): """ Reads single image resource block. Such blocks contain non-pixel data for the images (e.g. pen tool paths). """ sig = fp.read(4) if sig not in [b'8BIM', b'MeSa']: raise Error("Invalid resource signature (%r)" % sig) resource_id = read_fmt("H", fp)[0] name = read_pascal_string(fp, encoding, 2) data_size = pad(read_fmt("I", fp)[0], 2) data = fp.read(data_size) return ImageResource(resource_id, name, data)
def _read_layer_record(fp, encoding): """ Reads single layer record. """ top, left, bottom, right, num_channels = read_fmt("4i H", fp) channel_info = [] for channel_num in range(num_channels): info = ChannelInfo(*read_fmt("hI", fp)) channel_info.append(info) sig = fp.read(4) if sig != b'8BIM': raise Error("Error parsing layer: invalid signature (%r)" % sig) blend_mode = fp.read(4).decode('ascii') if not BlendMode.is_known(blend_mode): warnings.warn("Unknown blend mode (%s)" % blend_mode) opacity, clipping, flags, extra_length = read_fmt("BBBxI", fp) flags = LayerFlags(bool(flags & 1), not bool(flags & 2)) # why not? if not Clipping.is_known(clipping): warnings.warn("Unknown clipping: %s" % clipping) start = fp.tell() mask_data = _read_layer_mask_data(fp) blending_ranges = _read_layer_blending_ranges(fp) name = read_pascal_string(fp, encoding, 4) remaining_length = extra_length - (fp.tell()-start) tagged_blocks = _read_layer_tagged_blocks(fp, remaining_length) remaining_length = extra_length - (fp.tell()-start) fp.seek(remaining_length, 1) # skip the reminder return LayerRecord( top, left, bottom, right, num_channels, channel_info, blend_mode, opacity, clipping, flags, mask_data, blending_ranges, name, tagged_blocks )