Hacking Layers to Files to support PNG

While presenting a seminar on "Photoshop & Flash: Optimizing Pixels and Workflow" at the Macromedia Max conference, Mike Ninness mentioned that he wished Photoshop's 'Layers To Files' script could save to PNG. A friend in the audience sent me an email, so I whipped something together that night.

The full script is available here: Export Layers To Files_PNG.jsx

How to hack an existing script

I started with the 'Layers To Files' script that ships with Photoshop CS2.

First, I built out all the UI I needed. If you do a search for the following comment, you can see all the code that was needed:

//PNGFileTypes

I created a some funtions for PNG using the Scripting Listener:

case png24Index:
saveFile(docRef, fileNameBody, exportInfo, dlgMain.pnlFileType.pnlOptions.grpPNG24Options.png24Inter.value, dlgMain.pnlFileType.pnlOptions.grpPNG24Options.png24Trans.value);
function saveFile( docRef, fileNameBody, exportInfo, interlacedValue, transparencyValue) {
var id6 = charIDToTypeID( "Expr" );
var desc3 = new ActionDescriptor();
var id7 = charIDToTypeID( "Usng" );
var desc4 = new ActionDescriptor();
var id8 = charIDToTypeID( "Op " );
var id9 = charIDToTypeID( "SWOp" );
var id10 = charIDToTypeID( "OpSa" );
desc4.putEnumerated( id8, id9, id10 );
var id11 = charIDToTypeID( "Fmt " );
var id12 = charIDToTypeID( "IRFm" );
var id13 = charIDToTypeID( "PN24" );
desc4.putEnumerated( id11, id12, id13 );
var id14 = charIDToTypeID( "Intr" );
desc4.putBoolean( id14, interlacedValue );
var id15 = charIDToTypeID( "Trns" );
desc4.putBoolean( id15, transparencyValue );
var id16 = charIDToTypeID( "Mtt " );
desc4.putBoolean( id16, true );
var id17 = charIDToTypeID( "MttR" );
desc4.putInteger( id17, 255 );
var id18 = charIDToTypeID( "MttG" );
desc4.putInteger( id18, 255 );
var id19 = charIDToTypeID( "MttB" );
desc4.putInteger( id19, 255 );
var id20 = charIDToTypeID( "SHTM" );
desc4.putBoolean( id20, false );
var id21 = charIDToTypeID( "SImg" );
desc4.putBoolean( id21, true );
var id22 = charIDToTypeID( "SSSO" );
desc4.putBoolean( id22, false );
var id23 = charIDToTypeID( "SSLt" );
var list1 = new ActionList();
desc4.putList( id23, list1 );
var id24 = charIDToTypeID( "DIDr" );
desc4.putBoolean( id24, false );
var id25 = charIDToTypeID( "In " );
desc4.putPath( id25, new File( exportInfo.destination + "/" + fileNameBody + ".png") );
var id26 = stringIDToTypeID( "SaveForWeb" );
desc3.putObject( id7, id26, desc4 );
executeAction( id6, desc3, DialogModes.NO );
}

I tweaked a couple of conditionals to preserve the transparency:

// PNGFileOptions
if ((psdIndex == exportInfo.fileType)||(png24Index == exportInfo.fileType)||(png8Index == exportInfo.fileType)) { // PSD: Keep transparency
removeAllInvisible(duppedDocumentTmp);

//PNGFileOptions
if ((png24Index == exportInfo.fileType)||(png8Index == exportInfo.fileType)) { // PNGFileOptions
if ((dlgMain.pnlFileType.pnlOptions.grpPNG8Options.png8Trm.value == true)&&(png8Index == exportInfo.fileType)) { //transparancy checked?

if (activeDocument.activeLayer.isBackgroundLayer == false) { //is it anything but a background layer?

app.activeDocument.trim(TrimType.TRANSPARENT);

}

}
if ((dlgMain.pnlFileType.pnlOptions.grpPNG24Options.png24Trm.value == true)&&(png24Index == exportInfo.fileType)) { //transparancy checked?

if (activeDocument.activeLayer.isBackgroundLayer == false) { //is it anything but a background layer?

app.activeDocument.trim(TrimType.TRANSPARENT);

}

}
}

} else { // just flatten
duppedDocumentTmp.flatten();
}

Note, I also added a step that trims each layer so you don't have an excess of transparent areas:

app.activeDocument.trim(TrimType.TRANSPARENT);