psdfile.coffee | |
|---|---|
| Simulation and abstraction of a disk-based file. Provides methods to read the raw binary file data, which is stored in a variable instead of read from disk. A lot of these functions are from C, but some of them are helper functions to make things a bit easier to understand. | class PSDFile
unicodeRegex: /\\u([\d\w]{4})/gi
constructor: (@data) -> |
| Track our current position in the file data. This is analogous to the file pointer in C. | @pos = 0 |
| Get the current position in the file. This is here to parallel the C method | tell: -> @pos |
| Read one or more bytes from the file. Note that this moves the file pointer forward the number of bytes specified. | read: (bytes) -> (@data[@pos++] for i in [0...bytes]) |
| Move the file pointer to a new position. By default, this is done relative to the current file pointer position. Setting the 2nd argument to false causes the file pointer to move to the absolute location specified. | seek: (amount, rel = true) ->
if rel then @pos += amount else @pos = amount |
| Helper functions so we don't have to remember the unpack format codes. | |
| 4 bytes / 32-bit | readInt: ->
int = @readUInt()
if int >= 0x80000000 then int - 0x100000000 else int
readUInt: ->
b1 = @read(1)[0] << 24
b2 = @read(1)[0] << 16
b3 = @read(1)[0] << 8
b4 = @read(1)[0]
b1 | b2 | b3 | b4 |
| 2 bytes | readShortInt: ->
int = @readShortUInt()
if int >= 0x8000 then int - 0x10000 else int
readShortUInt: ->
b1 = @read(1)[0] << 8
b2 = @read(1)[0]
b1 | b2 |
| 4 bytes | readLongInt: -> @readf(">l")[0]
readLongUInt: -> @readf(">L")[0] |
| 8 bytes | readDouble: -> @readf(">d")[0] |
| 1 byte | readBoolean: -> @read(1)[0] isnt 0 |
| Unfortunately Javascript does not support 64-bit integers, so we have a temporary solution for now. In the future, we can parse and store the int either as an octet string, or something more useful. | readLongLong: -> @read(8)
readULongLong: -> @read(8) |
| Reads a string with the given length. Because some strings are also null-byte padded, we strip out these null bytes since they are of no use to us in Javascript. | readString: (length) ->
ret = String.fromCharCode.apply null, @read(length)
ret.replace /\u0000/g, ""
readUnicodeString: ->
len = @readInt() * 2
str = @readf(">#{len}s")[0]
str = str.replace @unicodeRegex, (match, grp) ->
String.fromCharCode parseInt(grp, 16)
str.replace /\u0000/g, "" |
| Used for reading pascal strings, which are strings that have their length prepended to the chunk of character bytes. If a length isn't found, a string with the default length will be read instead. | readLengthWithString: (defaultLen = 4) ->
length = @read(1)[0]
if length is 0
str = @readString defaultLen
else
str = @readString length
str |
| Reads a byte list | readBytesList: (size) -> @read size
readSpaceColor: ->
colorSpace = @readShortInt()
colorComponent = []
colorComponent.push @readShortInt() >> 8 for i in [0...4]
PSDColor.colorSpaceToARGB(colorSpace, colorComponent)
|
| Reads from the file given the unpack format string. Format string codes can be easily referenced from the Python docs | readf: (format) -> jspack.Unpack format, @read(jspack.CalcLength(format)) |
| Skips a block, assuming the next byte describes the size of the section. An optional description is given to explain why we are skipping this block instead of parsing it. | skipBlock: (desc = "unknown") ->
[n] = @readf('>L')
@seek(n) if n # relative
Log.debug "Skipped #{desc} with #{n} bytes"
|