require 'forwardable' require 'psd/enginedata' require 'chunky_png' require 'xmp' require 'psd/blend_mode' require 'psd/channel_image' require 'psd/color' require 'psd/descriptor' require 'psd/file' require 'psd/header' require 'psd/helpers' require 'psd/image' require 'psd/layer' require 'psd/layer_info' require 'psd/layer_mask' require 'psd/lazy_execute' require 'psd/logger' require 'psd/mask' require 'psd/node' require 'psd/path_record' require 'psd/renderer' require 'psd/resources' require 'psd/slices' require 'psd/util' require 'psd/version' # A general purpose parser for Photoshop files. PSDs are broken up in to 4 logical sections: # the header, resources, the layer mask (including layers), and the preview image. We parse # each of these sections in order. class PSD include Logger include Helpers include Slices attr_reader :file, :opts alias :options :opts # Opens the named file, parses it, and makes it available for reading. Then, closes it after you're finished. # @param filename [String] the name of the file to open # @return [PSD] the {PSD} object if no block was given, otherwise the value of the block def self.open(filename, opts={}, &block) raise "Must supply a block. Otherwise, use PSD.new." unless block_given? psd = PSD.new(filename, opts) psd.parse! if 0 == block.arity psd.instance_eval(&block) else yield psd end ensure psd.close if psd end # Create and store a reference to our PSD file def initialize(file, opts={}) @file = PSD::File.new(file, 'rb') @file.seek 0 # If the file was previously used and not closed @opts = opts @header = nil @resources = nil @layer_mask = nil @parsed = false end # Close the PSD file def close file.close unless file.closed? end # There is a specific order that must be followed when parsing # the PSD. Sections can be skipped if needed. This method will # parse all sections of the PSD. def parse! header resources layer_mask image @parsed = true return true end # Has our PSD been parsed yet? def parsed? @parsed end # Get the Header, parsing it if needed. def header return @header if @header @header = Header.new(@file) @header.parse! PSD.logger.debug @header.inspect end # Get the Resources section, parsing if needed. def resources return @resources unless @resources.nil? ensure_header @resources = Resources.new(@file) @resources.parse return @resources end # Get the LayerMask section. Ensures the header and resources # have been parsed first since they are required. def layer_mask ensure_header ensure_resources @layer_mask ||= LayerMask.new(@file, @header, @opts).parse end # Get the full size flattened preview Image. def image ensure_header ensure_resources ensure_layer_mask @image ||= ( # The image is the last section in the file, so we don't have to # bother with skipping over the bytes to read more data. image = Image.new(@file, @header) LazyExecute.new(image, @file) .later(:parse) .ignore(:width, :height) ) end private def ensure_header header # Header is always required end def ensure_resources return unless @resources.nil? @resources = Resources.new(@file) @resources.skip end def ensure_layer_mask return unless @layer_mask.nil? @layer_mask = LayerMask.new(@file, @header, @opts) @layer_mask.skip end end