A general purpose Photoshop file parser written in Ruby. It allows you to work with a Photoshop document in a manageable tree structure and find out important data such as:
- Document structure
- Document size
- Layer/folder size + positioning
- Layer/folder names
- Layer/folder visibility and opacity
- Font data (via psd-enginedata)
- Text area contents
- Font names, sizes, and colors
- Color mode and bit-depth
- Vector mask data
- Flattened image data
- Layer comps
PSD.rb is tested against:
- MRI 1.9.3 & 2.0.0
- JRuby (1.9.3 mode)
- Rubinius (1.9.3 mode)
If you use MRI Ruby and are interested in significantly speeding up PSD.rb with native code, check out psd_native.
Add this line to your application's Gemfile:
gem 'psd'
And then execute:
$ bundle
Or install it yourself as:
$ gem install psd
The full source code documentation is available, but here are some common ways to use and access the PSD data:
require 'psd'
psd = PSD.new('/path/to/file.psd')
psd.parse!
Or, if you prefer the File.open way of doing things, you can do that too.
require 'psd'
PSD.open('path/to/file.psd') do |psd|
p psd.tree.to_hash
end
As you can see, open
calls parse!
for you, so that you can get down to business right away.
If you happen to prefer things DSL-style, the open
method will also let you operate on the PSD object directly. Again, the call to parse!
is handled for you.
require 'psd'
PSD.open('path/to/file.psd') do
p tree.to_hash
end
To access the document as a tree structure, use psd.tree
to get the root node. From there, you can traverse the tree using any of these methods:
root
: get the root node from anywhere in the treechildren
: get all immediate children of the nodeancestors
: get all ancestors in the path of this node (excluding the root)siblings
: get all sibling tree nodes including the current one (e.g. all layers in a folder)descendants
: get all descendant nodes not including the current onesubtree
: same as descendants but starts with the current nodedepth
: calculate the depth of the current node
For any of the traversal methods, you can also retrieve folder or layer nodes only by appending _layers
or _groups
to the method. For example:
psd.tree.descendant_layers
If you know the path to a group or layer within the tree, you can search by that path. Note that this always returns an Array because layer/group names do not have to be unique.
psd.tree.children_at_path("Version A/Matte")
You can also filter nodes based on a layer comp. To generate a new tree consisting only of the layers that are enabled in a certain layer comp:
# Get information about all the available layer comps
puts psd.layer_comps
# Can filter by name or by ID (obtained from above)
tree = psd.tree.filter_by_comp('Version A')
puts tree.children.map(&:name)
This returns a new node tree and does not alter the original so you won't lose any data.
To get data such as the name or dimensions of a layer:
psd.tree.descendant_layers.first.name
psd.tree.descendant_layers.first.width
PSD files also store various pieces of information in "layer info" blocks. Which blocks a layer has varies from layer-to-layer, but to access them you can do:
psd.tree.descendant_layers.first.text[:font]
# Returns
{:name=>"HelveticaNeue-Light",
:sizes=>[33.0],
:colors=>[[255, 19, 120, 98]],
:css=>
"font-family: \"HelveticaNeue-Light\", \"AdobeInvisFont\", \"MyriadPro-Regular\";\nfont-size: 33.0pt;\ncolor: rgba(19, 120, 98, 255);"}
When working with the tree structure, you can recursively export any node to a Hash.
pp psd.tree.to_hash
Which produces something like:
{:children=>
[{:type=>:group,
:visible=>false,
:opacity=>1.0,
:blending_mode=>"normal",
:name=>"Version D",
:left=>0,
:right=>900,
:top=>0,
:bottom=>600,
:height=>900,
:width=>600,
:children=>
[{:type=>:layer,
:visible=>true,
:opacity=>1.0,
:blending_mode=>"normal",
:name=>"Make a change and save.",
:left=>275,
:right=>636,
:top=>435,
:bottom=>466,
:height=>31,
:width=>361,
:text=>
{:value=>"Make a change and save.",
:font=>
{:name=>"HelveticaNeue-Light",
:sizes=>[33.0],
:colors=>[[255, 19, 120, 98]],
:css=>
"font-family: \"HelveticaNeue-Light\", \"AdobeInvisFont\", \"MyriadPro-Regular\";\nfont-size: 33.0pt;\ncolor: rgba(19, 120, 98, 255);"},
:left=>0,
:top=>0,
:right=>0,
:bottom=>0,
:transform=>
{:xx=>1.0, :xy=>0.0, :yx=>0.0, :yy=>1.0, :tx=>456.0, :ty=>459.0}},
:ref_x=>264.0,
:ref_y=>-3.0}]
}],
:document=>{:width=>900, :height=>600}}
You can also export the PSD to a flattened image. Please note that, at this time, not all image modes + depths are supported.
png = psd.image.to_png # reference to PNG data
psd.image.save_as_png 'path/to/output.png' # writes PNG to disk
If you run into any problems parsing a PSD, you can enable debug logging via the PSD_DEBUG
environment variable. For example:
PSD_DEBUG=STDOUT bundle exec examples/parse.rb
You can also give a path to a file instead. If you need to enable debugging programatically:
PSD.debug = true
This is currently an experimental feature. It works "well enough" but is not perfect yet.
You can build previews of any subset or version of the PSD document. This is useful for generating previews of layer comps or exporting individual layer groups as images.
NOTE: You MUST initialize with the parse_layer_images option set to true, as shown below.
psd = PSD.new(file, parse_layer_images: true)
# Save a layer comp
psd.tree.filter_by_comp("Version A").save_as_png('./Version A.png')
# Generate PNG of individual layer group
psd.tree.children_at_path("Group 1").first.to_png
There are a few features that are currently missing from PSD.rb.
- More image modes + depths for image exporting
- A few layer info blocks