Copy link to clipboard
Copied
Hello dear Photoshop fellows,
I am working on webcomics scroll documents (960 x 95000 + px), and the most tedious part of the job consists in slicing the scroll into 1280 pixels height JPGs files... For the moment, the team laboriously worked with a Photoshop Action, but it needed to be relaunched and manually numbered for every file.
Typically a task for a script, you would tell me !
I did some Python programming by the past, but none on Javascript, neither with Photoshop SDK.
After a first theorical design for the script I needed, I stumbled here upon the Chop-O-Matic script (this page ), which makes 90% of the job, which is great, but i need to tweak it, based on my needs. I'll leave my algorithm sketch at bottom of this message.
What I need to suppress from chop-o-matic :
- dialog window ;
- no need to slice across.
What I need to modify in chop-o-matic :
- instead of a fixed amount of slices, the new script should make the maths itself, like :
numberOfSlices = math.ceil(currentDocumentHeight / 1280)
The total height of main scroll varies from episode to episode, but every slice of it would always be 1280 px tall.
- the current version of chop-o-matic outputed 1279 px tall files, instead of 1280 px. ...
I guess it is due to integer approximation of current version of chop-o-matic.
- I need to change JPEG output so that it is quality 10, progressive scan with 3 passes ;
- The numbering of created files just need to be "000-999.jpg" template.
And that's it. Apart from specified above, the existing script does the job.
Kudos to Paul_Riggot, by the way. 🙂
This is my initial script design, before discovering Chop-o-Matic :
Focus on open document ;
Duplicate and save a PSD copy at the same level ;
Flatten current document (no dialog) ;
Focus on duplicated document ;
var sliceHeight = 1280
var sliceSerial = 000
var nameSerial = 000
get current document's height (in pixel) ;
//pre-slicing calculation...
var sliceSerial = Math.ceil(doc height / SliceHeight)
//start of loop
if (sliceSerial > 1) {
duplicate current document ;
focus on newly created document ;
Set canvas size to 1280 px height, with relative anchor top center ;
Save current document to JPEG, quality 10, progressive scan, 3 passes, located at ../Sliced_JPGs/, named "nameSerial.jpg" , (no dialog displayed) ;
var sliceSerial --
var nameSerial ++
close current document, NOSAVE ;
}
else {
duplicate current document ;
focus on newly created document ;
Save current document to JPEG, quality 10, progressive scan, 3 passes, located at ../Sliced_JPGs/, named "nameSerial.jpg" , (no dialog displayed) ;
close current document, NOSAVE ;
}
//end of loop
// variables reinitialization
var sliceSerial = 000
var nameSerial = 000
Close current document //the long scroll
Thanks for your attention !
Here's the script that will place the slices in a subfolder.
#target photoshop
var count = 1000
var docOrig = activeDocument;
var docPath = new Folder(docOrig.path + '/jpgs/');
if(!docPath.exists){docPath.create()};
dupDoc ();
var doc = activeDocument;
docOrig.close(SaveOptions.DONOTSAVECHANGES);
var docHeight = doc.height.value;
var sliceNum = Math.ceil(docHeight/1280);
for(var i=0;i<sliceNum;i++){
if(i<sliceNum-1){
cropDoc (i*1280, i*1280+1280);
}
else{
cropD
...
Copy link to clipboard
Copied
Try this. You have an open file, which has to have been saved. The script will make a flattened duplicate file, and close the original. It will then make slices of the image in 1280 increments, other than the last, which will be the remainder. It will save in the same folder as the original with file names starting at 000. Then it will close the duplicate file, and not save it.
#target photoshop
var count = 1000
var docOrig = activeDocument;
var docPath = docOrig.path
dupDoc ();
var doc = activeDocument;
docOrig.close(SaveOptions.DONOTSAVECHANGES);
var docHeight = doc.height.value;
var sliceNum = Math.ceil(docHeight/1280);
for(var i=0;i<sliceNum;i++){
if(i<sliceNum-1){
cropDoc (i*1280, i*1280+1280);
}
else{
cropDoc (i*1280, doc.height.value);
}
saveJpg (docPath + '/'+ count.toString().substr (1, 3))
count++
doc.activeHistoryState = doc.historyStates[0];
}
doc.close(SaveOptions.DONOTSAVECHANGES)
function dupDoc(){
var idDplc = charIDToTypeID( "Dplc" );
var desc5 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref1 = new ActionReference();
var idDcmn = charIDToTypeID( "Dcmn" );
var idOrdn = charIDToTypeID( "Ordn" );
var idFrst = charIDToTypeID( "Frst" );
ref1.putEnumerated( idDcmn, idOrdn, idFrst );
desc5.putReference( idnull, ref1 );
var idMrgd = charIDToTypeID( "Mrgd" );
desc5.putBoolean( idMrgd, true );
executeAction( idDplc, desc5, DialogModes.NO );
};
function cropDoc(topC, bottomC){
var idCrop = charIDToTypeID( "Crop" );
var desc30 = new ActionDescriptor();
var idT = charIDToTypeID( "T " );
var desc31 = new ActionDescriptor();
var idTop = charIDToTypeID( "Top " );
var idPxl = charIDToTypeID( "#Pxl" );
desc31.putUnitDouble( idTop, idPxl, topC );
var idLeft = charIDToTypeID( "Left" );
var idPxl = charIDToTypeID( "#Pxl" );
desc31.putUnitDouble( idLeft, idPxl, 0.000000 );
var idBtom = charIDToTypeID( "Btom" );
var idPxl = charIDToTypeID( "#Pxl" );
desc31.putUnitDouble( idBtom, idPxl, bottomC );
var idRght = charIDToTypeID( "Rght" );
var idPxl = charIDToTypeID( "#Pxl" );
desc31.putUnitDouble( idRght, idPxl, doc.width.value );
var idRctn = charIDToTypeID( "Rctn" );
desc30.putObject( idT, idRctn, desc31 );
var idAngl = charIDToTypeID( "Angl" );
var idAng = charIDToTypeID( "#Ang" );
desc30.putUnitDouble( idAngl, idAng, 0.000000 );
var idDlt = charIDToTypeID( "Dlt " );
desc30.putBoolean( idDlt, false );
var idcropAspectRatioModeKey = stringIDToTypeID( "cropAspectRatioModeKey" );
var idcropAspectRatioModeClass = stringIDToTypeID( "cropAspectRatioModeClass" );
var idpureAspectRatio = stringIDToTypeID( "pureAspectRatio" );
desc30.putEnumerated( idcropAspectRatioModeKey, idcropAspectRatioModeClass, idpureAspectRatio );
var idCnsP = charIDToTypeID( "CnsP" );
desc30.putBoolean( idCnsP, false );
executeAction( idCrop, desc30, DialogModes.NO );
}
function saveJpg(fName){
var idsave = charIDToTypeID( "save" );
var desc40 = new ActionDescriptor();
var idAs = charIDToTypeID( "As " );
var desc41 = new ActionDescriptor();
var idEQlt = charIDToTypeID( "EQlt" );
desc41.putInteger( idEQlt, 10 );
var idScns = charIDToTypeID( "Scns" );
desc41.putInteger( idScns, 3 );
var idMttC = charIDToTypeID( "MttC" );
var idMttC = charIDToTypeID( "MttC" );
var idNone = charIDToTypeID( "None" );
desc41.putEnumerated( idMttC, idMttC, idNone );
var idJPEG = charIDToTypeID( "JPEG" );
desc40.putObject( idAs, idJPEG, desc41 );
var idIn = charIDToTypeID( "In " );
desc40.putPath( idIn, new File( fName +'.jpg' ) );
var idDocI = charIDToTypeID( "DocI" );
desc40.putInteger( idDocI, 256 );
var idCpy = charIDToTypeID( "Cpy " );
desc40.putBoolean( idCpy, true );
var idsaveStage = stringIDToTypeID( "saveStage" );
var idsaveStageType = stringIDToTypeID( "saveStageType" );
var idsaveBegin = stringIDToTypeID( "saveBegin" );
desc40.putEnumerated( idsaveStage, idsaveStageType, idsaveBegin );
executeAction( idsave, desc40, DialogModes.NO );
}
Copy link to clipboard
Copied
Chuck, you are such a prodigious person ! 🙂
The script you provided is exactly what I needed !
Me and other teammates definitely owe you something for saving us time for creative tasks.
I've checked the script output files with previous handmade files : it meets the desired dimensions, and remain pixel-perfect with original scroll image file.
You've done an amazing work, but with my knowledge, I feel lost when it comes to make minor changes in script behaviour...
Maybe should I mention one feature I think would make everyday life easier, especially in stressing times ?
It would be to create a subfolder alongside the current PSD/PSB file, where all the generated JPGs would be saved to.
This behaviour is present in original Chop-o-Matic script, and I really appreciated it, for not 'polluting' current file organisation. Also when we it is time to drop sliced images to the FTP repository, it's faster to drop a single folder, with 100 % JPGs files in it, rather than having to manually exclude an unwanted big PSD/PSB file.
And eventually, the script would be PER-FECT ! 🙂
I wish you all the best for your benevolent support.
Copy link to clipboard
Copied
Here's the script that will place the slices in a subfolder.
#target photoshop
var count = 1000
var docOrig = activeDocument;
var docPath = new Folder(docOrig.path + '/jpgs/');
if(!docPath.exists){docPath.create()};
dupDoc ();
var doc = activeDocument;
docOrig.close(SaveOptions.DONOTSAVECHANGES);
var docHeight = doc.height.value;
var sliceNum = Math.ceil(docHeight/1280);
for(var i=0;i<sliceNum;i++){
if(i<sliceNum-1){
cropDoc (i*1280, i*1280+1280);
}
else{
cropDoc (i*1280, doc.height.value);
}
saveJpg (docPath + '/'+ count.toString().substr (1, 3))
count++
doc.activeHistoryState = doc.historyStates[0];
}
doc.close(SaveOptions.DONOTSAVECHANGES)
function dupDoc(){
var idDplc = charIDToTypeID( "Dplc" );
var desc5 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref1 = new ActionReference();
var idDcmn = charIDToTypeID( "Dcmn" );
var idOrdn = charIDToTypeID( "Ordn" );
var idFrst = charIDToTypeID( "Frst" );
ref1.putEnumerated( idDcmn, idOrdn, idFrst );
desc5.putReference( idnull, ref1 );
var idMrgd = charIDToTypeID( "Mrgd" );
desc5.putBoolean( idMrgd, true );
executeAction( idDplc, desc5, DialogModes.NO );
};
function cropDoc(topC, bottomC){
var idCrop = charIDToTypeID( "Crop" );
var desc30 = new ActionDescriptor();
var idT = charIDToTypeID( "T " );
var desc31 = new ActionDescriptor();
var idTop = charIDToTypeID( "Top " );
var idPxl = charIDToTypeID( "#Pxl" );
desc31.putUnitDouble( idTop, idPxl, topC );
var idLeft = charIDToTypeID( "Left" );
var idPxl = charIDToTypeID( "#Pxl" );
desc31.putUnitDouble( idLeft, idPxl, 0.000000 );
var idBtom = charIDToTypeID( "Btom" );
var idPxl = charIDToTypeID( "#Pxl" );
desc31.putUnitDouble( idBtom, idPxl, bottomC );
var idRght = charIDToTypeID( "Rght" );
var idPxl = charIDToTypeID( "#Pxl" );
desc31.putUnitDouble( idRght, idPxl, doc.width.value );
var idRctn = charIDToTypeID( "Rctn" );
desc30.putObject( idT, idRctn, desc31 );
var idAngl = charIDToTypeID( "Angl" );
var idAng = charIDToTypeID( "#Ang" );
desc30.putUnitDouble( idAngl, idAng, 0.000000 );
var idDlt = charIDToTypeID( "Dlt " );
desc30.putBoolean( idDlt, false );
var idcropAspectRatioModeKey = stringIDToTypeID( "cropAspectRatioModeKey" );
var idcropAspectRatioModeClass = stringIDToTypeID( "cropAspectRatioModeClass" );
var idpureAspectRatio = stringIDToTypeID( "pureAspectRatio" );
desc30.putEnumerated( idcropAspectRatioModeKey, idcropAspectRatioModeClass, idpureAspectRatio );
var idCnsP = charIDToTypeID( "CnsP" );
desc30.putBoolean( idCnsP, false );
executeAction( idCrop, desc30, DialogModes.NO );
}
function saveJpg(fName){
var idsave = charIDToTypeID( "save" );
var desc40 = new ActionDescriptor();
var idAs = charIDToTypeID( "As " );
var desc41 = new ActionDescriptor();
var idEQlt = charIDToTypeID( "EQlt" );
desc41.putInteger( idEQlt, 10 );
var idScns = charIDToTypeID( "Scns" );
desc41.putInteger( idScns, 3 );
var idMttC = charIDToTypeID( "MttC" );
var idMttC = charIDToTypeID( "MttC" );
var idNone = charIDToTypeID( "None" );
desc41.putEnumerated( idMttC, idMttC, idNone );
var idJPEG = charIDToTypeID( "JPEG" );
desc40.putObject( idAs, idJPEG, desc41 );
var idIn = charIDToTypeID( "In " );
desc40.putPath( idIn, new File( fName +'.jpg' ) );
var idDocI = charIDToTypeID( "DocI" );
desc40.putInteger( idDocI, 256 );
var idCpy = charIDToTypeID( "Cpy " );
desc40.putBoolean( idCpy, true );
var idsaveStage = stringIDToTypeID( "saveStage" );
var idsaveStageType = stringIDToTypeID( "saveStageType" );
var idsaveBegin = stringIDToTypeID( "saveBegin" );
desc40.putEnumerated( idsaveStage, idsaveStageType, idsaveBegin );
executeAction( idsave, desc40, DialogModes.NO );
}
Copy link to clipboard
Copied
Woah, Chuck, you truly are a gold person !
An immense THANK to you, dear sir !