Skip to content

Instantly share code, notes, and snippets.

@vladdancer
Created June 11, 2019 10:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save vladdancer/832aec8790995a5676dae0839993807a to your computer and use it in GitHub Desktop.
Save vladdancer/832aec8790995a5676dae0839993807a to your computer and use it in GitHub Desktop.
#photoshop #raw-data #parser #mapper #art-layers #adjustment-layers #curves #kaitai-struct #deserializer Example of Curves Art Layer parser based on KaitaiStruct declarative parser
kaitai-struct-compiler -t javascript curves.format_spec.ksy
meta:
id: ps_curves
title: PS Curves Art Layer settings parser
application: Adobe Photoshop
endian: be
doc: |
Binary parser of PS action descriptor code,
getted via legacyContentData() method.
doc-ref: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1056330
seq:
- id: magic_1
contents: [0,0]
- id: magic_2
contents: [1]
- id: unknowns_data1
type: u2
- id: unknowns_data2
type: u1
- id: channel_state
type: bitmap_u1
- id: raw_curves
type: curve
repeat: expr
repeat-expr: channel_state.active
- id: extra_data_magic
contents: [0x43, 0x72, 0x76, 0x20]
- id: extra_data_ver
contents: [0,4]
- id: unknown_data3
type: u2
- id: extra_data_items_count
type: u2
- id: extra_data_items
type: extra_data_item
repeat: expr
repeat-expr: extra_data_items_count
types:
bitmap_u1:
seq:
- id: b8
type: b1
- id: b7
type: b1
- id: b6
type: b1
- id: b5
type: b1
- id: b4
type: b1
- id: b3
type: b1
- id: b2
type: b1
- id: b1
type: b1
instances:
active:
value: >
b1.to_i +
b2.to_i +
b3.to_i +
b4.to_i +
b5.to_i +
b6.to_i +
b7.to_i +
b8.to_i
curve:
seq:
- id: points_count
type: u2
- id: points
type: curve_point
repeat: expr
repeat-expr: points_count
curve_point:
seq:
- id: input
type: u2
- id: output
type: u2
extra_data_item:
seq:
- id: channel_index
type: u2
- id: item
type: curve
// somewhere in html
//<script type="text/javascript">
// window.require = window.require || window.cep_node.require;
// window.process = window.process || window.cep_node.process;
//</script>
import PsCurves from "./PsCurves";
const rawData = window.require('fs').readFileSync('PATH_FILE_W_DATA_GEN_BY_AM_METHOD_legacyContentData')
const KaitaiStream = window.require('kaitai-struct/KaitaiStream');
const dataStream = new KaitaiStream(
!isTypeOf(rawData, 'Uint8Array')
? convertOldBufferToArrayBuffer(rawData)
: rawData
);
const parsedData = new PsCurves(dataStream);
if (!parsedData) {
return false;
}
const mapper = new PsCurvesMapper(parsedData);
const data = mapper.map();
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['kaitai-struct/KaitaiStream'], factory);
} else if (typeof module === 'object' && module.exports) {
module.exports = factory(require('kaitai-struct/KaitaiStream'));
} else {
root.PsCurves = factory(root.KaitaiStream);
}
}(this, function (KaitaiStream) {
/**
* Binary parser of PS action descriptor code,
* getted via legacyContentData() method.
* @see {@link https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1056330|Source}
*/
var PsCurves = (function() {
function PsCurves(_io, _parent, _root) {
this._io = _io;
this._parent = _parent;
this._root = _root || this;
this._read();
}
PsCurves.prototype._read = function() {
this.magic1 = this._io.ensureFixedContents([0, 0]);
this.magic2 = this._io.ensureFixedContents([1]);
this.unknownsData1 = this._io.readU2be();
this.unknownsData2 = this._io.readU1();
this.channelState = new BitmapU1(this._io, this, this._root);
this.rawCurves = new Array(this.channelState.active);
for (var i = 0; i < this.channelState.active; i++) {
this.rawCurves[i] = new Curve(this._io, this, this._root);
}
this.extraDataMagic = this._io.ensureFixedContents([67, 114, 118, 32]);
this.extraDataVer = this._io.ensureFixedContents([0, 4]);
this.unknownData3 = this._io.readU2be();
this.extraDataItemsCount = this._io.readU2be();
this.extraDataItems = new Array(this.extraDataItemsCount);
for (var i = 0; i < this.extraDataItemsCount; i++) {
this.extraDataItems[i] = new ExtraDataItem(this._io, this, this._root);
}
}
var BitmapU1 = PsCurves.BitmapU1 = (function() {
function BitmapU1(_io, _parent, _root) {
this._io = _io;
this._parent = _parent;
this._root = _root || this;
this._read();
}
BitmapU1.prototype._read = function() {
this.b8 = this._io.readBitsInt(1) != 0;
this.b7 = this._io.readBitsInt(1) != 0;
this.b6 = this._io.readBitsInt(1) != 0;
this.b5 = this._io.readBitsInt(1) != 0;
this.b4 = this._io.readBitsInt(1) != 0;
this.b3 = this._io.readBitsInt(1) != 0;
this.b2 = this._io.readBitsInt(1) != 0;
this.b1 = this._io.readBitsInt(1) != 0;
}
Object.defineProperty(BitmapU1.prototype, 'active', {
get: function() {
if (this._m_active !== undefined)
return this._m_active;
this._m_active = ((((((((this.b1 | 0) + (this.b2 | 0)) + (this.b3 | 0)) + (this.b4 | 0)) + (this.b5 | 0)) + (this.b6 | 0)) + (this.b7 | 0)) + (this.b8 | 0));
return this._m_active;
}
});
return BitmapU1;
})();
var Curve = PsCurves.Curve = (function() {
function Curve(_io, _parent, _root) {
this._io = _io;
this._parent = _parent;
this._root = _root || this;
this._read();
}
Curve.prototype._read = function() {
this.pointsCount = this._io.readU2be();
this.points = new Array(this.pointsCount);
for (var i = 0; i < this.pointsCount; i++) {
this.points[i] = new CurvePoint(this._io, this, this._root);
}
}
return Curve;
})();
var CurvePoint = PsCurves.CurvePoint = (function() {
function CurvePoint(_io, _parent, _root) {
this._io = _io;
this._parent = _parent;
this._root = _root || this;
this._read();
}
CurvePoint.prototype._read = function() {
this.input = this._io.readU2be();
this.output = this._io.readU2be();
}
return CurvePoint;
})();
var ExtraDataItem = PsCurves.ExtraDataItem = (function() {
function ExtraDataItem(_io, _parent, _root) {
this._io = _io;
this._parent = _parent;
this._root = _root || this;
this._read();
}
ExtraDataItem.prototype._read = function() {
this.channelIndex = this._io.readU2be();
this.item = new Curve(this._io, this, this._root);
}
return ExtraDataItem;
})();
return PsCurves;
})();
return PsCurves;
}));
import PsCurves from './PsCurves';
import __zipObject from 'lodash.zipobject';
import __isEmpty from 'lodash.isempty';
interface LayerInfo {
docChannelOneName: string;
id: number,
name: string,
opacity: number,
blendMore: string,
fillOpacity: string,
kind: string,
docColorMode: string,
jsonData?: object
filename: string | boolean
sizeKb: number | boolean
}
interface AdnvancedLayerParser {
setLayerInfo(info:LayerInfo):void;
}
class PsCurvesMapper implements AdnvancedLayerParser {
private readonly obj: PsCurves;
private layerInfo: LayerInfo;
constructor(obj) {
this.obj = obj;
}
map() {
this.obj.colorMode = this.layerInfo.docColorMode.replace('DocumentMode.', '');
let channelColorNames = [];
if (this.obj.colorMode === 'RGB') {
channelColorNames = ['composite', 'red', 'green', 'blue'];
}
else if (this.obj.colorMode === 'CMYK') {
channelColorNames = ['composite', 'cyan', 'magenta', 'yellow', 'keyColor'];
}
else if (this.obj.colorMode === 'LAB') {
channelColorNames = ['lightness', 'a', 'b'];
}
else if (this.obj.colorMode === 'GRAYSCALE') {
channelColorNames = ['black'];
}
else {
throw Error(`Unsupported colormode: ${this.obj.colorMode} in Curves Art Layer type.`)
}
this.obj.curves = [];
this.obj.extraDataItems.forEach((curve, index) => {
if (!__isEmpty(curve.item)) {
curve.name = channelColorNames[curve.channelIndex];
curve.points = curve.item.points;
this.obj.curves.push(curve);
}
});
return this.obj;
}
setLayerInfo(info): void {
this.layerInfo = info;
}
}
export default PsCurvesMapper;
0000 0100 0000 1f00 0300 0000 0000 d400
a700 ff00 ff00 0500 0000 0000 6b00 4300
6300 4a00 b100 8f00 ff00 ff00 0300 0000
0000 8c00 6600 ff00 ff00 0400 0000 0000
6800 4500 7d00 4c00 ff00 ff00 0400 0000
0000 4300 3000 e100 c200 ff00 ff43 7276
2000 0400 0000 0500 0000 0300 0000 0000
d400 a700 ff00 ff00 0100 0500 0000 0000
6b00 4300 6300 4a00 b100 8f00 ff00 ff00
0200 0300 0000 0000 8c00 6600 ff00 ff00
0300 0400 0000 0000 6800 4500 7d00 4c00
ff00 ff00 0400 0400 0000 0000 4300 3000
e100 c200 ff00 ff00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment