У меня есть много .psd
файлов, каждый из которых содержит 2+ слоя, которые мне нужно экспортировать как слои. Для одного psd
файлаD я могу использовать:
File > Scripts > Export Layers To Files ...
Однако, если я использую этот скрипт (в дроплете), мне нужно жестко закодировать папку назначения и префикс имени файла. Это нехорошо, так как многие .psd
файлы содержат слои с одинаковыми именами, поэтому они будут перезаписывать друг друга, и мне нужно, чтобы каждый файл имел префикс .psd
имени. Таким образом, слой, вызываемый text_1
из .psd
файла с именем doc_1
, будет называться doc_1_text_1
, а слой, вызываемый text_1
из .psd
файла с именем doc_2
, будет называться doc_2_text_1
или что-то подобное.
Есть ли способ сделать это? Я рад жестко закодировать папку назначения, но мне нужно, чтобы сценарий использовал любое имя .psd
файла для префикса.
Я собрал jsx-скрипт, чтобы сделать это на основе сценария «Экспорт слоев в файлы». Он поддерживает только экспорт png24 и не имеет диалога. Предполагается, что вы хотите обрезать слой, а путь к папке, в которую вы хотите экспортировать, жестко запрограммирован в самом скрипте. Чтобы установить папку, измените значение exportPath
на абсолютный путь.
https://dl.dropboxusercontent.com/u/316978/Permanent/export_layers_to_pngs.jsx
// enable double clicking from the Macintosh Finder or the Windows Explorer
#target photoshop
//=================================================================
// Globals
//=================================================================
var exportPath = "/Users/pedr/Documents/Work/Clients/Pathways/Learning_Hub/Source/Comics/export";
exportPath = exportPath + '/layers';
// UI strings to be localized
var strTitle = localize("$$$/JavaScripts/X2L/Title=X2L");
var strButtonRun = localize("$$$/JavaScripts/X2L/Run=Run");
var strButtonCancel = localize("$$$/JavaScripts/X2L/Cancel=Cancel");
var strHelpText = localize("$$$/JavaScripts/X2L/Help=Please specify the format and location for saving each layer as a file.");
var strLabelDestination = localize("$$$/JavaScripts/X2L/Destination=Destination:");
var strButtonBrowse = localize("$$$/JavaScripts/X2L/Browse=&Browse...");
var strLabelFileNamePrefix = localize("$$$/JavaScripts/X2L/FileNamePrefix=File Name Prefix:");
var strCheckboxVisibleOnly = localize("$$$/JavaScripts/X2L/VisibleOnly=&Visible Layers Only");
var strLabelFileType = localize("$$$/JavaScripts/X2L/FileType=File Type:");
var strCheckboxIncludeICCProfile = localize("$$$/JavaScripts/X2L/IncludeICC=&Include ICC Profile");
var strJPEGOptions = localize("$$$/JavaScripts/X2L/JPEGOptions=JPEG Options:");
var strLabelQuality = localize("$$$/JavaScripts/X2L/Quality=Quality:");
var strCheckboxMaximizeCompatibility = localize("$$$/JavaScripts/X2L/Maximize=&Maximize Compatibility");
var strTIFFOptions = localize("$$$/JavaScripts/X2L/TIFFOptions=TIFF Options:");
var strLabelImageCompression = localize("$$$/JavaScripts/X2L/ImageCompression=Image Compression:");
var strNone = localize("$$$/JavaScripts/X2L/None=None");
var strPDFOptions = localize("$$$/JavaScripts/X2L/PDFOptions=PDF Options:");
var strLabelEncoding = localize("$$$/JavaScripts/X2L/Encoding=Encoding:");
var strTargaOptions = localize("$$$/JavaScripts/X2L/TargaOptions=Targa Options:");
var strLabelDepth = localize("$$$/JavaScripts/X2L/Depth=Depth:");
var strRadiobutton16bit = localize("$$$/JavaScripts/X2L/Bit16=16bit");
var strRadiobutton24bit = localize("$$$/JavaScripts/X2L/Bit24=24bit");
var strRadiobutton32bit = localize("$$$/JavaScripts/X2L/Bit32=32bit");
var strBMPOptions = localize("$$$/JavaScripts/X2L/BMPOptions=BMP Options:");
var strAlertSpecifyDestination = localize("$$$/JavaScripts/X2L/SpecifyDestination=Please specify destination.");
var strAlertDestinationNotExist = localize("$$$/JavaScripts/X2L/DestionationDoesNotExist=Destination does not exist.");
var strTitleSelectDestination = localize("$$$/JavaScripts/X2L/SelectDestination=Select Destination");
var strAlertDocumentMustBeOpened = localize("$$$/JavaScripts/X2L/OneDocument=You must have a document open to export!");
var strAlertNeedMultipleLayers = localize("$$$/JavaScripts/X2L/NoLayers=You need a document with multiple layers to export!");
var strAlertWasSuccessful = localize("$$$/JavaScripts/X2L/Success= was successful.");
var strUnexpectedError = localize("$$$/JavaScripts/X2L/Unexpected=Unexpected error");
var strMessage = localize("$$$/JavaScripts/X2L/Message=X2L");
var stretQuality = localize( "$$$/locale_specific/JavaScripts/X2L/ETQualityLength=30" );
var stretDestination = localize( "$$$/locale_specific/JavaScripts/X2L/ETDestinationLength=160" );
var strddFileType = localize( "$$$/locale_specific/JavaScripts/X2L/DDFileType=100" );
var strpnlOptions = localize( "$$$/locale_specific/JavaScripts/X2L/PNLOptions=100" );
var strPNG8Options = localize("$$$/JavaScripts/X2L/PNG8Options=PNG-8 Options:");
var strCheckboxPNGTransparency = localize("$$$/JavaScripts/X2L/Transparency=Transparency");
var strCheckboxPNGInterlaced = localize("$$$/JavaScripts/X2L/Interlaced=Interlaced");
var strCheckboxPNGTrm = localize("$$$/JavaScripts/X2L/Trim=Trim Layers");
var strPNG24Options = localize("$$$/JavaScripts/X2L/PNG24Options=PNG-24 Options:");
// the drop down list indexes for file type
var png24Index = 7;
main();
///////////////////////////////////////////////////////////////////////////////
// Functions
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Function: main
// Usage: the core routine for this script
// Input: <none>
// Return: <none>
///////////////////////////////////////////////////////////////////////////////
function main() {
if ( app.documents.length <= 0 ) {
if ( DialogModes.NO != app.playbackDisplayDialogs ) {
alert( strAlertDocumentMustBeOpened );
}
return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script
}
var exportInfo = new Object();
initExportInfo(exportInfo);
// look for last used params via Photoshop registry, getCustomOptions will throw if none exist
try {
}
catch(e) {
// it's ok if we don't have any options, continue with defaults
}
try {
var docName = app.activeDocument.name; // save the app.activeDocument name before duplicate.
var layerCount = app.documents[docName].layers.length;
var layerSetsCount = app.documents[docName].layerSets.length;
if ((layerCount <= 1)&&(layerSetsCount <= 0)) {
if ( DialogModes.NO != app.playbackDisplayDialogs ) {
alert( strAlertNeedMultipleLayers );
return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script
}
} else {
var rememberMaximize;
var needMaximize = exportInfo.psdMaxComp ? QueryStateType.ALWAYS : QueryStateType.NEVER;
app.activeDocument = app.documents[docName];
var duppedDocument = app.activeDocument.duplicate();
duppedDocument.activeLayer = duppedDocument.layers[duppedDocument.layers.length-1]; // for removing
setInvisibleAllArtLayers(duppedDocument);
exportChildren(duppedDocument, app.documents[docName], exportInfo, duppedDocument, exportInfo.fileNamePrefix);
duppedDocument.close( SaveOptions.DONOTSAVECHANGES );
if ( rememberMaximize != undefined ) {
app.preferences.maximizeCompatibility = rememberMaximize;
}
if ( DialogModes.ALL == app.playbackDisplayDialogs ) {
//alert(strTitle + strAlertWasSuccessful);
}
app.playbackDisplayDialogs = DialogModes.ALL;
}
} catch (e) {
if ( DialogModes.NO != app.playbackDisplayDialogs ) {
alert(e);
}
return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script
}
}
///////////////////////////////////////////////////////////////////////////////
// Function: settingDialog
// Usage: pop the ui and get user settings
// Input: exportInfo object containing our parameters
// Return: on ok, the dialog info is set to the exportInfo object
///////////////////////////////////////////////////////////////////////////////
function settingDialog(exportInfo) {
return;
}
///////////////////////////////////////////////////////////////////////////////
// Function: hideAllFileTypePanel
// Usage: hide all the panels in the common actions
// Input: <none>, dlgMain is a global for this script
// Return: <none>, all panels are now hidden
///////////////////////////////////////////////////////////////////////////////
function hideAllFileTypePanel() {
}
///////////////////////////////////////////////////////////////////////////////
// Function: initExportInfo
// Usage: create our default parameters
// Input: a new Object
// Return: a new object with params set to default
///////////////////////////////////////////////////////////////////////////////
function initExportInfo(exportInfo) {
//exportInfo.destination = new String(exportPath);
exportInfo.fileNamePrefix = new String("untitled_");
exportInfo.visibleOnly = false;
exportInfo.fileType = png24Index;
exportInfo.icc = true;
exportInfo.png24Transparency = true;
exportInfo.png24Interlaced = false;
exportInfo.png24Trim = true;
try {
exportInfo.destination = Folder(new String(exportPath)).fsName; // destination folder
var tmp = app.activeDocument.fullName.name;
exportInfo.fileNamePrefix = decodeURI(tmp.substring(0, tmp.indexOf("."))); // filename body part
} catch(someError) {
exportInfo.destination = new String(exportPath);
exportInfo.fileNamePrefix = app.activeDocument.name; // filename body part
}
}
///////////////////////////////////////////////////////////////////////////////
// Function: saveFile
// Usage: the worker routine, take our params and save the file accordingly
// Input: reference to the document, the name of the output file,
// export info object containing more information
// Return: <none>, a file on disk
///////////////////////////////////////////////////////////////////////////////
function saveFile( docRef, fileNameBody, exportInfo) {
saveFile(docRef, fileNameBody, exportInfo, false, true);
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( exportPath + "/" + fileNameBody + ".png") );
var id26 = stringIDToTypeID( "SaveForWeb" );
desc3.putObject( id7, id26, desc4 );
executeAction( id6, desc3, DialogModes.NO );
}
}
///////////////////////////////////////////////////////////////////////////////
// Function: zeroSuppress
// Usage: return a string padded to digit(s)
// Input: num to convert, digit count needed
// Return: string padded to digit length
///////////////////////////////////////////////////////////////////////////////
function zeroSuppress (num, digit) {
var tmp = num.toString();
while (tmp.length < digit) {
tmp = "0" + tmp;
}
return tmp;
}
///////////////////////////////////////////////////////////////////////////////
// Function: setInvisibleAllArtLayers
// Usage: unlock and make invisible all art layers, recursively
// Input: document or layerset
// Return: all art layers are unlocked and invisible
///////////////////////////////////////////////////////////////////////////////
function setInvisibleAllArtLayers(obj) {
for( var i = 0; i < obj.artLayers.length; i++) {
obj.artLayers[i].allLocked = false;
obj.artLayers[i].visible = false;
}
for( var i = 0; i < obj.layerSets.length; i++) {
setInvisibleAllArtLayers(obj.layerSets[i]);
}
}
///////////////////////////////////////////////////////////////////////////////
// Function: removeAllInvisibleArtLayers
// Usage: remove all the invisible art layers, recursively
// Input: document or layer set
// Return: <none>, all layers that were invisible are now gone
///////////////////////////////////////////////////////////////////////////////
function removeAllInvisibleArtLayers(obj) {
for( var i = obj.artLayers.length-1; 0 <= i; i--) {
try {
if(!obj.artLayers[i].visible) {
obj.artLayers[i].remove();
}
}
catch (e) {
}
}
for( var i = obj.layerSets.length-1; 0 <= i; i--) {
removeAllInvisibleArtLayers(obj.layerSets[i]);
}
}
///////////////////////////////////////////////////////////////////////////////
// Function: removeAllEmptyLayerSets
// Usage: find all empty layer sets and remove them, recursively
// Input: document or layer set
// Return: empty layer sets are now gone
///////////////////////////////////////////////////////////////////////////////
function removeAllEmptyLayerSets(obj) {
var foundEmpty = true;
for( var i = obj.layerSets.length-1; 0 <= i; i--) {
if( removeAllEmptyLayerSets(obj.layerSets[i])) {
obj.layerSets[i].remove();
} else {
foundEmpty = false;
}
}
if (obj.artLayers.length > 0) {
foundEmpty = false;
}
return foundEmpty;
}
///////////////////////////////////////////////////////////////////////////////
// Function: zeroSuppress
// Usage: return a string padded to digit(s)
// Input: num to convert, digit count needed
// Return: string padded to digit length
///////////////////////////////////////////////////////////////////////////////
function removeAllInvisible(docRef) {
removeAllInvisibleArtLayers(docRef);
removeAllEmptyLayerSets(docRef);
}
///////////////////////////////////////////////////////////////////////////////
// Function: exportChildren
// Usage: find all the children in this document to save
// Input: duplicate document, original document, export info,
// reference to document, starting file name
// Return: <none>, documents are saved accordingly
///////////////////////////////////////////////////////////////////////////////
function exportChildren(dupObj, orgObj, exportInfo, dupDocRef, fileNamePrefix) {
for( var i = 0; i < dupObj.artLayers.length; i++) {
if (exportInfo.visibleOnly) { // visible layer only
if (!orgObj.artLayers[i].visible) {
continue;
}
}
dupObj.artLayers[i].visible = true;
var layerName = dupObj.artLayers[i].name; // store layer name before change doc
var duppedDocumentTmp = dupDocRef.duplicate();
if ((png24Index == exportInfo.fileType)||(png8Index == exportInfo.fileType)) { // PSD: Keep transparency
removeAllInvisible(duppedDocumentTmp);
//PNGFileOptions
if (activeDocument.activeLayer.isBackgroundLayer == false) { //is it anything but a background layer?
app.activeDocument.trim(TrimType.TRANSPARENT);
}
} else { // just flatten
duppedDocumentTmp.flatten();
}
// Edit
var docName = app.activeDocument.name;
// For some reason indexOf fails if we include the '-', so we use 'copy' and decrement the index by 1.
docName = docName.slice(0, docName.indexOf('copy')-1);
var fileNameBody = (docName+'_'+layerName).toLowerCase();
fileNameBody = fileNameBody.replace(/[:\/\\*\?\"\<\>\|]/g, "_"); // '/\:*?"<>|' -> '_'
if (fileNameBody.length > 120) {
fileNameBody = fileNameBody.substring(0,120);
}
saveFile(duppedDocumentTmp, fileNameBody, exportInfo);
duppedDocumentTmp.close(SaveOptions.DONOTSAVECHANGES);
dupObj.artLayers[i].visible = false;
}
for( var i = 0; i < dupObj.layerSets.length; i++) {
if (exportInfo.visibleOnly) { // visible layer only
if (!orgObj.layerSets[i].visible) {
continue;
}
}
var fileNameBody = fileNamePrefix;
fileNameBody += "_" + zeroSuppress(i, 4) + "s";
exportChildren(dupObj.layerSets[i], orgObj.layerSets[i], exportInfo, dupDocRef, fileNameBody); // recursive call
}
}
///////////////////////////////////////////////////////////////////////////////
// Function: objectToDescriptor
// Usage: create an ActionDescriptor from a JavaScript Object
// Input: JavaScript Object (o)
// object unique string (s)
// Pre process converter (f)
// Return: ActionDescriptor
// NOTE: Only boolean, string, number and UnitValue are supported, use a pre processor
// to convert (f) other types to one of these forms.
// REUSE: This routine is used in other scripts. Please update those if you
// modify. I am not using include or eval statements as I want these
// scripts self contained.
///////////////////////////////////////////////////////////////////////////////
function objectToDescriptor (o, s, f) {
o = {};
var d = new ActionDescriptor;
var l = o.reflect.properties.length;
d.putString( app.charIDToTypeID( 'Msge' ), s );
for (var i = 0; i < l; i++ ) {
var k = o.reflect.properties[i].toString();
if (k == "__proto__" || k == "__count__" || k == "__class__" || k == "reflect")
continue;
var v = o[ k ];
k = app.stringIDToTypeID(k);
switch ( typeof(v) ) {
case "boolean":
d.putBoolean(k, v);
break;
case "string":
d.putString(k, v);
break;
case "number":
d.putDouble(k, v);
break;
default:
{
if ( v instanceof UnitValue ) {
var uc = new Object;
uc["px"] = charIDToTypeID("#Rlt"); // unitDistance
uc["%"] = charIDToTypeID("#Prc"); // unitPercent
d.putUnitDouble(k, uc[v.type], v.value);
} else {
throw( new Error("Unsupported type in objectToDescriptor " + typeof(v) ) );
}
}
}
}
return d;
}
///////////////////////////////////////////////////////////////////////////////
// Function: descriptorToObject
// Usage: update a JavaScript Object from an ActionDescriptor
// Input: JavaScript Object (o), current object to update (output)
// Photoshop ActionDescriptor (d), descriptor to pull new params for object from
// object unique string (s)
// JavaScript Function (f), post process converter utility to convert
// Return: Nothing, update is applied to passed in JavaScript Object (o)
// NOTE: Only boolean, string, number and UnitValue are supported, use a post processor
// to convert (f) other types to one of these forms.
// REUSE: This routine is used in other scripts. Please update those if you
// modify. I am not using include or eval statements as I want these
// scripts self contained.
///////////////////////////////////////////////////////////////////////////////
function descriptorToObject (o, d, s, f) {
var l = d.count;
if (l) {
var keyMessage = app.charIDToTypeID( 'Msge' );
if ( d.hasKey(keyMessage) && ( s != d.getString(keyMessage) )) return;
}
for (var i = 0; i < l; i++ ) {
var k = d.getKey(i); // i + 1 ?
var t = d.getType(k);
strk = app.typeIDToStringID(k);
switch (t) {
case DescValueType.BOOLEANTYPE:
o[strk] = d.getBoolean(k);
break;
case DescValueType.STRINGTYPE:
o[strk] = d.getString(k);
break;
case DescValueType.DOUBLETYPE:
o[strk] = d.getDouble(k);
break;
case DescValueType.UNITDOUBLE:
{
var uc = new Object;
uc[charIDToTypeID("#Rlt")] = "px"; // unitDistance
uc[charIDToTypeID("#Prc")] = "%"; // unitPercent
uc[charIDToTypeID("#Pxl")] = "px"; // unitPixels
var ut = d.getUnitDoubleType(k);
var uv = d.getUnitDoubleValue(k);
o[strk] = new UnitValue( uv, uc[ut] );
}
break;
case DescValueType.INTEGERTYPE:
case DescValueType.ALIASTYPE:
case DescValueType.CLASSTYPE:
case DescValueType.ENUMERATEDTYPE:
case DescValueType.LISTTYPE:
case DescValueType.OBJECTTYPE:
case DescValueType.RAWTYPE:
case DescValueType.REFERENCETYPE:
default:
throw( new Error("Unsupported type in descriptorToObject " + t ) );
}
}
if (undefined != f) {
o = f(o);
}
}
///////////////////////////////////////////////////////////////////////////////
// Function: preProcessExportInfo
// Usage: convert Photoshop enums to strings for storage
// Input: JavaScript Object of my params for this script
// Return: JavaScript Object with objects converted for storage
///////////////////////////////////////////////////////////////////////////////
function preProcessExportInfo(o) {
o.tiffCompression = o.tiffCompression.toString();
o.pdfEncoding = o.pdfEncoding.toString();
o.targaDepth = o.targaDepth.toString();
o.bmpDepth = o.bmpDepth.toString();
return o;
}
///////////////////////////////////////////////////////////////////////////////
// Function: postProcessExportInfo
// Usage: convert strings from storage to Photoshop enums
// Input: JavaScript Object of my params in string form
// Return: JavaScript Object with objects in enum form
///////////////////////////////////////////////////////////////////////////////
function postProcessExportInfo(o) {
o.tiffCompression = eval(o.tiffCompression);
o.pdfEncoding = eval(o.pdfEncoding);
o.targaDepth = eval(o.targaDepth);
o.bmpDepth = eval(o.bmpDepth);
return o;
}
///////////////////////////////////////////////////////////////////////////
// Function: StrToIntWithDefault
// Usage: convert a string to a number, first stripping all characters
// Input: string and a default number
// Return: a number
///////////////////////////////////////////////////////////////////////////
function StrToIntWithDefault( s, n ) {
var onlyNumbers = /[^0-9]/g;
var t = s.replace( onlyNumbers, "" );
t = parseInt( t );
if ( ! isNaN( t ) ) {
n = t;
}
return n;
}
// End X2L.jsx
Всем привет и это мой первый пост.
Я думал, что это должен был быть простой комментарий, поскольку я, похоже, не могу найти конкретную строку кода, чтобы решить эту проблему гораздо более быстрым способом, чем решить с помощью встроенных сценариев и предыдущих решений для постеров, однако, поскольку у меня нет репутации 50+, но я не могу комментировать и поэтому должен вместо этого дать ответ.
Итак, я предлагаю просто использовать приведенный выше файл кода, который кажется почти идентичным шаблону, включенному в Adobe PS.
Однако одно изменение приветствуется.
////////////////////////////////////////////////// ////////////////////////////// // Функция: zeroSuppress // Использование: возвращает строку, дополненную цифрами // Ввод : число для преобразования, требуется количество цифр // Возврат: строка, дополненная до длины цифры ////////////////////////////////////////// ////////////////////////////////////////////// function removeAllInvisible(docRef ) {
// обратите внимание на комментарий ниже
// удалитьAllInvisibleArtLayers(docRef);
removeAllEmptyLayerSets(docRef);
}
следует закомментировать, чтобы ускорить выполнение.
Предпочтительно, если кто-то может опубликовать наиболее эффективный способ выравнивания изображения в этой функции, у нас должен быть рабочий код, который намного эффективнее для описанной выше цели.
Простите мой новичок, но я изучаю «теорию бережливого производства», а не «графический дизайн» в местном колледже.
пользователь14425
Неотвлечение