Copy link to clipboard
Copied
Looking for someone who has any idea how to debug the following script by Mike Hale. Any ideas or even method of were to start would be great, this script is a little beyond my knowledge base....
The Script follows through and saves a ACV file, but it flags the following warning when you try and load the curve. Presumable something has changed in the syntax of the curve preset since CS4 when this script was written.
I opened an . acv of the same curve saved both via photoshop and via the script see below the difference in binary via Synalze pro, i dont really understand how to interpret this data though...
 
// Works with RGB or CMYK images.
// Does not work at all with greyscale, or indexColor
// Does not work correctly with Lab
File.prototype.writeByte = function(b) {
this.write(String.fromCharCode(b));
};
File.prototype.writeShort = function(ET,s) {
var self = this;
if(ET == "BE"){
self.writeByte(s & 0xFF);
self.writeByte((s >> 8) & 0xFF);
}else{
self.writeByte((s >> 8) & 0xFF);
self.writeByte(s & 0xFF);
}
return self;
};
function convertBCD( num ){
var res = new Array;
if(num > 16 ){
res.unshift(1);
num = num - 16;
}else{
res.unshift(0);
}
if(num > 8 ){
res.unshift(1);
num = num - 8;
}else{
res.unshift(0);
}
if(num > 4 ){
res.unshift(1);
num = num - 4;
}else{
res.unshift(0);
}
if(num > 2 ){
res.unshift(1);
num = num - 2;
}else{
res.unshift(0);
}
if(num == 1 ){
res.unshift(1);
}else{
res.unshift(0);
}
return res;
};
function getCurve( numberOfPoints ){
this.getPoints = function(){
this.tempArray = new Array;
this.tempArray.push( rawDesc.charCodeAt( pointer ) );
pointer = pointer + 2;// set pointer to next point
this.tempArray.push( rawDesc.charCodeAt( pointer ) );
return this.tempArray;
}
this.pointsArray = new Array;
for( var i = 0; i < numberOfPoints; i++ ){
pointer = pointer + 2;// next point
this.pointsArray.push( this.getPoints() );
}
pointer = pointer + 2;// next curve
return this.pointsArray;
};
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID( 'Lyr ' ), charIDToTypeID( 'Ordn' ), charIDToTypeID( 'Trgt' ) );
var rawDesc = executeActionGet( ref ).getList( stringIDToTypeID( 'adjustment' ) ).getObjectValue( 0 ).getData( stringIDToTypeID( 'legacyContentData' ) );
var pointer = 2;// used to read rawData similar to reading a file
var flag = rawDesc.charCodeAt( pointer );// char at offset 2 always == 1 so use to validate data
if( flag != 1 ) forceError;// unknown problem with rawData
pointer = 6;// update pointer to BCD byte
var bcd = rawDesc.charCodeAt( pointer );
if( bcd < 0 || bcd > 31 ) forceError;// check for valid value
if( bcd == 0 ) forceError;// an empty adjustment - no curves to process
var bcdArray = convertBCD( bcd );
var numberOfCurves = bcdArray.toString().match(/(1)/g).length;
var curvesArray = new Array;
pointer = 8;// set pointer to start of curve data
for(var i = 0; i < numberOfCurves; i++ ){
var numberOfPoints = rawDesc.charCodeAt( pointer );
curvesArray.push( getCurve( numberOfPoints ) );
}
// now need to map rawData curves in curvesArray to known channel curves
var acvArray = new Array;
if( bcdArray[0] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
if( bcdArray[1] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
if( bcdArray[2] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
if( bcdArray[3] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
if( bcdArray[4] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
var myACV = new File('~/desktop/myCurve.acv');
myACV.open('w');
myACV.writeShort( 'LE','4' );// acv header
myACV.writeShort( 'LE','5' );// acv header
for( var i = 0; i< 5; i++ ) {
var numberOfPoints = acvArray.length;
myACV.writeShort( 'LE', numberOfPoints.toString() );
for( var p = 0; p < numberOfPoints; p++ ){
myACV.writeShort( 'LE', acvArray
[0].toString() );
myACV.writeShort( 'LE', acvArray
[1].toString() );
}
}
myACV.close();
I thought there might be lack of myACV.encoding = 'binary', but that doesn't help as well. You may try with 'destructive' Curves you're going to call by script and set to some values manually. Then use fromStream() method on actionDescriptor and write the file to the disk, which you're going to compare with .acv you make from Adjustement Layer to find differences. There's also toStream() method you can read how to use on this forum.
Copy link to clipboard
Copied
Thought id try an alternative method via Action Manager code from script listener, but this doesnt even save a file for some reason
var idset = stringIDToTypeID( "set" );
var desc236 = new ActionDescriptor();
var idnull = stringIDToTypeID( "null" );
var ref12 = new ActionReference();
var idadjustmentLayer = stringIDToTypeID( "adjustmentLayer" );
var idordinal = stringIDToTypeID( "ordinal" );
var idtargetEnum = stringIDToTypeID( "targetEnum" );
ref12.putEnumerated( idadjustmentLayer, idordinal, idtargetEnum );
desc236.putReference( idnull, ref12 );
var idto = stringIDToTypeID( "to" );
var desc237 = new ActionDescriptor();
var idpresetKind = stringIDToTypeID( "presetKind" );
var idpresetKindType = stringIDToTypeID( "presetKindType" );
var idpresetKindUserDefined = stringIDToTypeID( "presetKindUserDefined" );
desc237.putEnumerated( idpresetKind, idpresetKindType, idpresetKindUserDefined );
var idusing = stringIDToTypeID( "using" );
desc237.putPath( idusing, new File( "~/Desktop/new.acv" ) );
var idcurves = stringIDToTypeID( "curves" );
desc236.putObject( idto, idcurves, desc237 );
executeAction( idset, desc236, DialogModes.NO );
Copy link to clipboard
Copied
I have just realised all the action manager code does is sets the curve not saves it.... will keep going with Mike Hales script...
Copy link to clipboard
Copied
For sure, the last part of the script is missing some arrays reference.
Try with :
for( var i = 0; i< 5; i++ ) {
var numberOfPoints = acvArray[i].length;
myACV.writeShort( 'LE', numberOfPoints.toString() );
for( var p = 0; p < numberOfPoints; p++ ){
myACV.writeShort( 'LE', acvArray[i][p][0].toString() );
myACV.writeShort( 'LE', acvArray[i][p][1].toString() );
}
}
Copy link to clipboard
Copied
Interesting, that now produces a readable acv file but with different data from the original curve adjustment...
Copy link to clipboard
Copied
It seems to be wrongly corrected.
Copy link to clipboard
Copied
What do you means by different data ? Maybe Format encoding ?
I tested the code withPsCS6, the preset loads correctly and curves points coordinates are identicals to what the were at the time of the acv creation.
Copy link to clipboard
Copied
In CS6 the original code of Michael_L_Hale doesn't write .acv I could load with no problem.
Copy link to clipboard
Copied
I'm testing in PS2021... maybe file format changed, see my most recent post for more detail
Copy link to clipboard
Copied
I have correct the script and it now produces a file with the correct structure but its not writing the curve points correctly...
File.prototype.writeByte = function(b) {
this.write(String.fromCharCode(b));
};
File.prototype.writeShort = function(ET,s) {
var self = this;
if(ET == "BE"){
self.writeByte(s & 0xFF);
self.writeByte((s >> 8) & 0xFF);
}else{
self.writeByte((s >> 8) & 0xFF);
self.writeByte(s & 0xFF);
}
return self;
};
convertBCD = function( num ){
if(num == 13){
return [1,1,0,1];// special case
}
var res = new Array;
if(num > 8 ){
res.unshift(1);
num = num - 8;
}else{
res.unshift(0);
}
if(num > 4 ){
res.unshift(1);
num = num - 4;
}else{
res.unshift(0);
}
if(num > 2 ){
res.unshift(1);
num = num - 2;
}else{
res.unshift(0);
}
if(num == 1 ){
res.unshift(1);
}else{
res.unshift(0);
}
return res;
};
getCurve = function( numberOfPoints ){
this.getPoints = function(){
this.tempArray = new Array;
this.tempArray.push( rawDesc.charCodeAt( pointer ) );
pointer = pointer + 2;// set pointer to next point
this.tempArray.push( rawDesc.charCodeAt( pointer ) );
return this.tempArray;
}
this.pointsArray = new Array;
for( var i = 0; i < numberOfPoints; i++ ){
pointer = pointer + 2;// next point
this.pointsArray.push( this.getPoints() );
}
pointer = pointer + 2;// next curve
return this.pointsArray;
};
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID( 'Lyr ' ), charIDToTypeID( 'Ordn' ), charIDToTypeID( 'Trgt' ) );
var rawDesc = executeActionGet( ref ).getList( stringIDToTypeID( 'adjustment' ) ).getObjectValue( 0 ).getData( stringIDToTypeID( 'legacyContentData' ) );
var pointer = 2;// used to read rawData similar to reading a file
var flag = rawDesc.charCodeAt( pointer );// char at offset 2 always == 1 so use to validate data
if( flag != 1 ) forceError;// unknown problem with rawData
pointer = 6;// update pointer to BCD byte
var bcd = rawDesc.charCodeAt( pointer );
if( bcd < 0 || bcd > 15 ) forceError;// check for valid value
if( bcd == 0 ) forceError;// an empty adjustment - no curves to process
var bcdArray = convertBCD( bcd );
var numberOfCurves = bcdArray.toString().match(/(1)/g).length;
var curvesArray = new Array;
pointer = 8;// set pointer to start of curve data
for(var i = 0; i < numberOfCurves; i++ ){
var numberOfPoints = rawDesc.charCodeAt( pointer );
curvesArray.push( getCurve( numberOfPoints ) );
}
// now need to map rawData curves in curvesArray to known channel curves
// for example if bcd == 7 then there is a RGB, RED, and GREEN curve
// if bcd == 8 then there is only a BLUE curve. etc
var acvArray = new Array;
if( bcdArray[0] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
if( bcdArray[1] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
if( bcdArray[2] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
if( bcdArray[3] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
acvArray.push( [ [0,0],[255,255] ] );
var myACV = new File('~/Desktop/myCurve1.acv');
myACV.open('w');
myACV.writeShort( 'LE','4' );// acv header
myACV.writeShort( 'LE','5' );// acv header
for( var i = 0; i< 5; i++ ) {
var numberOfPoints = acvArray[i].length;
myACV.writeShort( 'LE', numberOfPoints.toString() );
//myACV.writeByte( 0 );
for( var p = 0; p < numberOfPoints; p++ ){
myACV.writeShort( 'LE', acvArray[p][0].toString() );
myACV.writeShort( 'LE', acvArray[p][1].toString() );
}
}
myACV.close();
'm'
The front end of the script is causing issues. Can anyone tell me why the if(ET== "BE") statement as "BE" is not used anywhere....
also what is the signifcance of 0xFF ...
File.prototype.writeByte = function(b) {
this.write(String.fromCharCode(b));
};
File.prototype.writeShort = function(ET,s) {
var self = this;
if(ET == "BE"){
self.writeByte(s & 0xFF);
self.writeByte((s >> 8) & 0xFF);
}else{
self.writeByte((s >> 8) & 0xFF);
self.writeByte(s & 0xFF);
}
return self;
};
I have analyzed a file created from the script and one using photoshop using the adobe syntax which can be found here http://fileformats.archiveteam.org/wiki/Photoshop_Curve
as you can see the structure is now correct, with it showing the correct number of curves and correct number of points for each curve... However the data which is perfectly captured in the acvArray is not being written correctly to the file...
Copy link to clipboard
Copied
Below line of original code has no [i]:
var numberOfPoints = acvArray[i].length;
Copy link to clipboard
Copied
Yes I changed that otherwise it was outputting then number of points per curve as not the actual number of points.
Copy link to clipboard
Copied
So there's bug in original code?
Copy link to clipboard
Copied
Sorry, it won't help you, but could you tell what is name of the software you have used for decoding the data of the acv file ?
Copy link to clipboard
Copied
It's this https://www.synalysis.net and i found the syntax grammar file on Wikipedia see my previous post
Copy link to clipboard
Copied
I found a post on PS Scripts in which @DBarranca posted speaking about how curves with 5 curves... Perhaps @DBarranca can shed some light on this conundrum. From my debugging, it seems the data is being passed correctly to the writing part of the script. individual values are passed, but it seems like the way they are being written is not correct, and that is as far as i have got to ...
In theory the data should be pushed to the writing part of the script as follows, as individual input ouput numbers for each point on each curve sequentially, however this somehow mucks up the structure in the script and saves the wrong number of points.
var myACV = new File('~/Desktop/curveScriptMH.acv');
myACV.open('w');
myACV.writeShort( 'LE','4' );// acv header WORKS
myACV.writeShort( 'LE','5' );// acv header WORKS
for( var i = 0; i< 5; i++ ) {
var numberOfPoints = acvArray[i].length;
myACV.writeShort( 'LE', numberOfPoints.toString() ); //WORKS
//myACV.writeByte( 0 );
debugger
for( var p = 0; p < numberOfPoints; p++ ){
myACV.writeShort( 'LE', acvArray[i][p][0].toString() );
myACV.writeShort( 'LE', acvArray[i][p][1].toString() );
}
}
When i change the script to pass the values as pairs not individual values it maintains a perfect structure with the correct number of points.
var myACV = new File('~/Desktop/curveScript.acv');
myACV.open('w');
myACV.writeShort( 'LE','4' );// acv header
myACV.writeShort( 'LE','5' );// acv header
for( var i = 0; i< 5; i++ ) {
var numberOfPoints = acvArray[i].length;
myACV.writeShort( 'LE', numberOfPoints.toString() );
//myACV.writeByte( 0 );
debugger
for( var p =0 ; p < numberOfPoints; p++ ){
myACV.writeShort( 'LE', acvArray[i][p].toString() );
myACV.writeShort( 'LE', acvArray[i][p].toString() );
}
In both cases the acv headers are perfectly written and in both cases none of the point values are written correctly to the file....
I am still sure that there is some sort of bug on this part of the code.... (I just dont understand how those functions work)
File.prototype.writeByte = function(b) {
this.write(String.fromCharCode(b));
};
File.prototype.writeShort = function(ET,s) {
var self = this;
if(ET == "BE"){
self.writeByte(s & 0xFF);
self.writeByte((s >> 8) & 0xFF);
}else{
self.writeByte((s >> 8) & 0xFF);
self.writeByte(s & 0xFF);
}
return self;
};
Copy link to clipboard
Copied
I thought there might be lack of myACV.encoding = 'binary', but that doesn't help as well. You may try with 'destructive' Curves you're going to call by script and set to some values manually. Then use fromStream() method on actionDescriptor and write the file to the disk, which you're going to compare with .acv you make from Adjustement Layer to find differences. There's also toStream() method you can read how to use on this forum.
Copy link to clipboard
Copied
The following code works perfectly with the addition of the myACV.encoding = 'BINARY'
File.prototype.writeByte = function(b) {
this.write(String.fromCharCode(b));
};
File.prototype.writeShort = function(ET,s) {
var self = this;
if(ET == "BE"){
self.writeByte(s & 0xFF);
self.writeByte((s >> 8) & 0xFF);
}else{
self.writeByte((s >> 8) & 0xFF);
self.writeByte(s & 0xFF);
}
return self;
};
convertBCD = function( num ){
if(num == 13){
return [1,1,0,1];// special case
}
var res = new Array;
if(num > 8 ){
res.unshift(1);
num = num - 8;
}else{
res.unshift(0);
}
if(num > 4 ){
res.unshift(1);
num = num - 4;
}else{
res.unshift(0);
}
if(num > 2 ){
res.unshift(1);
num = num - 2;
}else{
res.unshift(0);
}
if(num == 1 ){
res.unshift(1);
}else{
res.unshift(0);
}
return res;
};
getCurve = function( numberOfPoints ){
this.getPoints = function(){
this.tempArray = new Array;
this.tempArray.push( rawDesc.charCodeAt( pointer ) );
pointer = pointer + 2;// set pointer to next point
this.tempArray.push( rawDesc.charCodeAt( pointer ) );
return this.tempArray;
}
this.pointsArray = new Array;
for( var i = 0; i < numberOfPoints; i++ ){
pointer = pointer + 2;// next point
this.pointsArray.push( this.getPoints() );
}
pointer = pointer + 2;// next curve
return this.pointsArray;
};
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID( 'Lyr ' ), charIDToTypeID( 'Ordn' ), charIDToTypeID( 'Trgt' ) );
var rawDesc = executeActionGet( ref ).getList( stringIDToTypeID( 'adjustment' ) ).getObjectValue( 0 ).getData( stringIDToTypeID( 'legacyContentData' ) );
var pointer = 2;// used to read rawData similar to reading a file
var flag = rawDesc.charCodeAt( pointer );// char at offset 2 always == 1 so use to validate data
if( flag != 1 ) forceError;// unknown problem with rawData
pointer = 6;// update pointer to BCD byte
var bcd = rawDesc.charCodeAt( pointer );
if( bcd < 0 || bcd > 15 ) forceError;// check for valid value
if( bcd == 0 ) forceError;// an empty adjustment - no curves to process
var bcdArray = convertBCD( bcd );
var numberOfCurves = bcdArray.toString().match(/(1)/g).length;
var curvesArray = new Array;
pointer = 8;// set pointer to start of curve data
for(var i = 0; i < numberOfCurves; i++ ){
var numberOfPoints = rawDesc.charCodeAt( pointer );
curvesArray.push( getCurve( numberOfPoints ) );
}
// now need to map rawData curves in curvesArray to known channel curves
// for example if bcd == 7 then there is a RGB, RED, and GREEN curve
// if bcd == 8 then there is only a BLUE curve. etc
var acvArray = new Array;
if( bcdArray[0] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
if( bcdArray[1] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
if( bcdArray[2] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
if( bcdArray[3] == 1 ) {
acvArray.push( curvesArray.shift() );
} else {
acvArray.push( [ [0,0],[255,255] ] );
}
acvArray.push( [ [0,0],[255,255] ] );
var myACV = new File('~/Desktop/curveScriptMH.acv');
myACV.encoding = 'BINARY'
myACV.open('w');
myACV.writeShort( 'LE','4' );// acv header WORKS
myACV.writeShort( 'LE','5' );// acv header WORKS
for( var i = 0; i< 5; i++ ) {
var numberOfPoints = acvArray[i].length;
myACV.writeShort( 'LE', numberOfPoints.toString() ); //WORKS
//myACV.writeByte( 0 );
//debugger
for( var p = 0; p < numberOfPoints; p++ ){
myACV.writeShort( 'LE', acvArray[i][p][0].toString() );
myACV.writeShort( 'LE', acvArray[i][p][1].toString() );
}
}
myACV.close();
Copy link to clipboard
Copied
That's right it works! Loll, I tried binary encoding with original code from other forum thread and that failed. So I have no idea why non author code works but not the one he created 😕
Copy link to clipboard
Copied
Thank you, i was trying everything to get it to work. I'm working on a Mac so i think thats why it needed that line. Also on random googling found the codes original author (Mike Hale) mentioning this.... https://community.adobe.com/t5/photoshop-ecosystem-discussions/possible-to-script-palette-menus-save...
Copy link to clipboard
Copied
I found 3 threads on this forum with this code. Then I stepped back to original place where the code was shared, so I thought that was better to link you to the source. For some reason it was better to provide later code 😕
Anyway it seems the ghost of Michael_L_Hale (R.I.P) talks by me (about binary encoding) 😄
Copy link to clipboard
Copied
Must have been! Thanks again
Copy link to clipboard
Copied
Since I am moderator on PS SCRIPTS forum I could edit linked post. I found in edit mode that code looks differently than displayed. It appears some forum transition erased reserved emoticons characters which fortunately were correctly stored in the core of the post. There wasn't binary encoding but lacking 3 [i] characters, which I now reposted the code with.
Copy link to clipboard
Copied
Copy link to clipboard
Copied
thanks for that,
for some reason it fails on the first line, how should that be corrected...
Code: Select allFile.prototype.writeByte = function(b) {
this.write(String.fromCharCode(b));
};