Commit aabb6de1 authored by Ryan LeFevre's avatar Ryan LeFevre

Start on node tree

parent eb9ad819
...@@ -5,3 +5,13 @@ A general purpose PSD parser written in Coffeescript. Based off of [PSD.rb](http ...@@ -5,3 +5,13 @@ A general purpose PSD parser written in Coffeescript. Based off of [PSD.rb](http
Runs in both NodeJS and the browser (using browserify). Runs in both NodeJS and the browser (using browserify).
**Work in Progress** **Work in Progress**
## Usage
``` js
var PSD = require('psd');
var psd = PSD.fromFile("path/to/file.psd");
psd.parse();
console.log(psd.header.export());
```
\ No newline at end of file
...@@ -7,4 +7,5 @@ console.log(psd.header.export()); ...@@ -7,4 +7,5 @@ console.log(psd.header.export());
// psd.image.saveAsPng('./output.png').then(function () { // psd.image.saveAsPng('./output.png').then(function () {
// console.log("Finished!"); // console.log("Finished!");
// }); // });
console.log(psd.layers.map(function (l) { return l.export(); }));
\ No newline at end of file console.log(psd.tree().export());
\ No newline at end of file
...@@ -9,6 +9,9 @@ LayerMask = require './psd/layer_mask.coffee' ...@@ -9,6 +9,9 @@ LayerMask = require './psd/layer_mask.coffee'
Image = require './psd/image.coffee' Image = require './psd/image.coffee'
module.exports = class PSD module.exports = class PSD
@Node:
Root: require('./psd/nodes/root.coffee')
@fromFile: (file) -> new PSD fs.readFileSync(file) @fromFile: (file) -> new PSD fs.readFileSync(file)
constructor: (data) -> constructor: (data) ->
...@@ -52,4 +55,6 @@ module.exports = class PSD ...@@ -52,4 +55,6 @@ module.exports = class PSD
@image = new LazyExecute(image, @file) @image = new LazyExecute(image, @file)
.later('parse') .later('parse')
.ignore('width', 'height') .ignore('width', 'height')
.get() .get()
\ No newline at end of file
tree: -> new PSD.Node.Root(@)
\ No newline at end of file
...@@ -7,6 +7,7 @@ module.exports = class Layer extends Module ...@@ -7,6 +7,7 @@ module.exports = class Layer extends Module
@includes require('./layer/blending_ranges.coffee') @includes require('./layer/blending_ranges.coffee')
@includes require('./layer/name.coffee') @includes require('./layer/name.coffee')
@includes require('./layer/info.coffee') @includes require('./layer/info.coffee')
@includes require('./layer/helpers.coffee')
# @includes require('./layer/channel_image.coffee') # @includes require('./layer/channel_image.coffee')
constructor: (@file, @header) -> constructor: (@file, @header) ->
......
module.exports =
isFolder: ->
if @adjustments['sectionDivider']?
@adjustments['sectionDivider'].isFolder
else if @adjustments['nestedSectionDivider']?
@adjustments['nestedSectionDivider'].isFolder
else
@name is "<Layer group>"
isFolderEnd: ->
if @adjustments['sectionDivider']?
@adjustments['sectionDivider'].isHidden
else if @adjustments['nestedSectionDivider']?
@adjustments['nestedSectionDivider'].isHidden
else
@name is "</Layer group>"
\ No newline at end of file
...@@ -3,7 +3,8 @@ Util = require '../util.coffee' ...@@ -3,7 +3,8 @@ Util = require '../util.coffee'
LAYER_INFO = { LAYER_INFO = {
name: require('../layer_info/unicode_name.coffee'), name: require('../layer_info/unicode_name.coffee'),
nestedSectionDivider: require('../layer_info/nested_section_divider.coffee') nestedSectionDivider: require('../layer_info/nested_section_divider.coffee'),
sectionDivider: require('../layer_info/section_divider.coffee')
} }
module.exports = module.exports =
......
LayerInfo = require '../layer_info.coffee'
module.exports = class NestedSectionDivider extends LayerInfo
@shouldParse: (key) -> key is 'lsct'
SECTION_DIVIDER_TYPES = [
'other'
'open folder'
'closed folder'
'bounding section divider'
]
constructor: (layer, length) ->
super(layer, length)
@isFolder = false
@isHidden = false
@layerType = null
@blendMode = null
@subType = null
parse: ->
code = @file.readInt()
@layerType = SECTION_DIVIDER_TYPES[code]
switch code
when 1, 2 then @isFolder = true
when 3 then @isHidden = true
return unless @length >= 12
@file.seek 4, true
@blendMode = @file.readString(4)
return unless @length >= 16
@subType = if @file.readInt() is 0 then 'normal' else 'scene group'
\ No newline at end of file
_ = require 'lodash'
{Module} = require 'coffeescript-module'
module.exports = class Node extends Module
@includes require('./nodes/ancestry.coffee')
@PROPERTIES: ['name', 'left', 'right', 'top', 'bottom', 'height', 'width']
type: 'node'
constructor: (@layer, @parent = null) ->
@layer.node = @
@children = []
@name = @layer.name
@forceVisible = null
@coords =
top: @layer.top
bottom: @layer.bottom
left: @layer.left
right: @layer.right
@topOffset = 0
@leftOffset = 0
@createProperties()
createProperties: ->
Object.defineProperty @, 'top', get: -> @coords.top + @topOffset
Object.defineProperty @, 'right', get: -> @coords.right + @leftOffset
Object.defineProperty @, 'bottom', get: -> @coords.bottom + @topOffset
Object.defineProperty @, 'left', get: -> @coords.left + @leftOffset
Object.defineProperty @, 'width', get: -> @right - @left
Object.defineProperty @, 'height', get: -> @bottom - @top
get: (prop) -> if @[prop]? then @[prop] else @layer[prop]
visible: ->
return false if @layer.clipped and not @clippingMask().visible()
if @forceVisible? then @forceVisible else @layer.visible
hidden: -> not @visible()
isLayer: -> @type is 'layer'
isGroup: -> @type is 'group'
isRoot: -> @type is 'root'
clippingMask: ->
return null unless @layer.clipped
@clippingMaskCached or= (
maskNode = @nextSibling()
maskNode = maskNode.nextSibling() while maskNode.clipped
maskNode
)
clippedBy: -> @clippingMask()
export: ->
hash =
type: null
visible: @visible()
opacity: @layer.opacity / 255.0
blendingMode: @layer.blendingMode()
hash[prop] = @[prop] for prop in Node.PROPERTIES
hash
updateDimensions: ->
return if @isLayer()
child.updateDimensions() for child in @children
return if @isRoot()
nonEmptyChildren = @children.filter((c) -> not c.isEmpty())
@left = _.min(nonEmptyChildren.map((c) -> c.left)) or 0
@top = _.min(nonEmptyChildren.map((c) -> c.top)) or 0
@bottom = _.max(nonEmptyChildren.map((c) -> c.bottom)) or 0
@right = _.max(nonEmptyChildren.map((c) -> c.right)) or 0
\ No newline at end of file
_ = require 'lodash'
module.exports =
root: ->
return @ if isRoot()
return @parent.root()
isRoot: -> @depth() is 0
ancestors: ->
return [] if not @parent? or @parent.isRoot()
return @parent.ancestors().concat [@parent]
hasChildren: -> @children().length > 0
childless: -> not @hasChildren()
siblings: ->
return [] unless @parent?
@parent.children()
nextSibling: ->
return null unless @parent?
index = @siblings().indexOf(@)
@siblings()[index + 1]
prevSibling: ->
return null unless @parent?
index = @siblings().indexOf(@)
@siblings()[index - 1]
hasSiblings: -> @siblings().length > 1
onlyChild: -> not @hasSiblings()
descendants: -> _.flatten @children().map((n) -> n.subtree())
subtree: -> [@].concat @descendants()
depth: -> @ancestors().length + 1
path: (asArray = false) ->
path = @ancestors.map((n) -> n.name).concat([@name])
if asArray then path else path.join('/')
\ No newline at end of file
Node = require '../node.coffee'
module.exports = class Group extends Node
type: 'group'
passthruBlending: ->
@get('blendingMode') is 'passthru'
isEmpty: ->
return false unless child.isEmpty() for child in @children
\ No newline at end of file
Node = require '../node.coffee'
module.exports = class Layer extends Node
type: 'layer'
isEmpty: -> @width is 0 or @height is 0
\ No newline at end of file
_ = require 'lodash'
Node = require '../node.coffee'
Group = require './group.coffee'
Layer = require './layer.coffee'
module.exports = class Root extends Node
@layerForPsd: (psd) ->
layer = {}
layer[prop] = null for prop in Node.PROPERTIES
layer.top = 0
layer.left = 0
layer.right = psd.header.width
layer.bottom = psd.header.height
layer
type: 'root'
constructor: (@psd) ->
super Root.layerForPsd(@psd)
@buildHeirarchy()
documentDimensions: -> [
@documentWidth(),
@documentHeight()
]
depth: -> 0
opacity: -> 255
fillOpacity: -> 255
export: ->
children: @children.map((c) -> c.export())
buildHeirarchy: ->
currentGroup = @
parseStack = []
for layer in @psd.layers
if layer.isFolder()
parseStack.push currentGroup
currentGroup = new Group(layer, _.last(parseStack))
else if layer.isFolderEnd()
parent = parseStack.pop()
parent.children.push currentGroup
currentGroup = parent
else
currentGroup.children.push new Layer(layer, currentGroup)
@updateDimensions()
\ No newline at end of file
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
"jspack": "~ 0.0.3", "jspack": "~ 0.0.3",
"coffeescript-module": "~ 0.0.2", "coffeescript-module": "~ 0.0.2",
"png": "~ 3.0.3", "png": "~ 3.0.3",
"rsvp": "~ 3.0.6" "rsvp": "~ 3.0.6",
"lodash": "~ 2.4"
}, },
"devDependencies": { "devDependencies": {
"coffeeify": "~ 0.6.0" "coffeeify": "~ 0.6.0"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment