Last active
July 9, 2024 05:31
-
-
Save UserUnknownFactor/296edae2a3b3cad3730699ed6a230a4d to your computer and use it in GitHub Desktop.
Photoshop JS script to duplicate a group of layers to all files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This script duplicates the current selected text layer to every open document | |
// and replaces text in the each of those layers to the one specified in arrow | |
// separated CSV file in format: `{filename without extension}→Added text` | |
// it can automatically save modified files as PSDs and additionally | |
// hides the only image layer in each document if present. | |
if (app.documents.length > 0) { | |
var csvFile = File.openDialog("Select CSV file", "Arrow Separated Values:*.csv", false); | |
if (csvFile) { | |
var csvData = readCSV(csvFile); | |
if (csvData) { | |
var saveAsPSD = confirm("Would you like to save the other files as PSDs?"); | |
duplicateAndReplaceText(csvData, saveAsPSD, true); | |
} else { | |
alert("Could not read or parse the CSV file."); | |
} | |
} | |
} else { | |
alert("No documents are open."); | |
} | |
/* | |
function cTID(s) { return app.charIDToTypeID(s); }; | |
function sTID(s) { return app.stringIDToTypeID(s); }; | |
function exportPngManual(fileName, optionsNative) { | |
// Metadata: | |
// "MDAl" All metadata; "MDNn" No metadata; | |
// "MDCp" Copyright; "MDCC" Copyright and contact; | |
// "MDAx" All except camera info; | |
// Profile: | |
// "CHsR" sRGB Profile; "CHDc" Use document profile | |
metadata = "MDNn";//!!optionsNative.metadata ? optionsNative.metadata : "MDNn"; | |
progressive = !!optionsNative.progressive ? optionsNative.progressive : 1; | |
var options = new ActionDescriptor(); | |
options.putEnumerated(cTID("Op "), cTID("SWOp"), cTID("OpSa")); | |
options.putEnumerated(cTID("Fmt "), cTID("IRFm"), cTID(!!optionsNative.PNG8 ? "PNG8" : "PN24")); | |
options.putBoolean(cTID("Intr"), optionsNative.interlaced); | |
options.putInteger(cTID("Qlty"), optionsNative.quality); | |
options.putBoolean(cTID("Trns"), optionsNative.transparency); | |
options.putInteger(cTID("QChS"), 0); | |
options.putInteger(cTID("QCUI"), 0); | |
options.putBoolean(cTID("QChT"), false); | |
options.putBoolean(cTID("QChV"), false); | |
options.putBoolean(cTID("Optm"), optionsNative.optimized); | |
options.putInteger(cTID("Pass"), progressive); // 1 = optimized; 3 = progressive | |
options.putDouble(cTID("blur"), 0.000000); | |
options.putBoolean(cTID("EICC"), !!optionsNative.includeProfile); //Embed Color Profile, Bool | |
options.putBoolean(cTID("Mtt "), !!optionsNative.matteColor); | |
options.putInteger(cTID("MttR"), optionsNative.matteColor.red); | |
options.putInteger(cTID("MttG"), optionsNative.matteColor.green); | |
options.putInteger(cTID("MttB"), optionsNative.matteColor.blue); | |
options.putBoolean(cTID("SHTM"), false); | |
options.putBoolean(cTID("SImg"), true); | |
options.putEnumerated(cTID("SWch"), cTID("STch"), cTID(!!optionsNative.includeProfile ? "CHDc" : "CHsR")); | |
options.putEnumerated(cTID("SWmd"), cTID("STmd"), cTID(metadata)); | |
options.putBoolean(cTID("SSSO"), false); | |
options.putList(cTID("SSLt"), new ActionList()); | |
options.putBoolean(cTID("DIDr"), false); | |
options.putPath(cTID("In "), new File(fileName)); | |
var save_for_web_dlg = new ActionDescriptor(); | |
save_for_web_dlg.putObject(cTID('Usng'), sTID('SaveForWeb'), options); | |
executeAction(cTID('Expr'), save_for_web_dlg, DialogModes.NO); | |
}; | |
*/ | |
function saveAsPSDFile(doc, openNewTab, exportFolderPath) { | |
var filename = doc.name.replace(/\.[^\/.]+$/, ""); | |
var psdFile = new File(doc.path + "/" + filename + ".psd"); | |
var options = new PhotoshopSaveOptions(); | |
options.layers = true; | |
options.alphaChannels = true; | |
options.annotations = true; | |
options.embedColorProfile = true; | |
options.spotColors = true; | |
doc.saveAs(psdFile, options, openNewTab, Extension.LOWERCASE); | |
// Replace the current document with the new PSD document | |
if (openNewTab) { | |
doc.close(SaveOptions.DONOTSAVECHANGES); | |
app.open(psdFile); | |
} | |
if (exportFolderPath) { | |
// Create the export folder relative to the PSD file's directory | |
var exportFolder = new Folder(psdFile.path + "/" + exportFolderPath); | |
if (!exportFolder.exists) | |
exportFolder.create(); | |
// Export the PSD file as PNG to the specified directory | |
var pngFile = new File(psdFile.path + "/" + exportFolderPath + "/" + filename + ".png"); | |
var pngOptions = new ExportOptionsSaveForWeb(); | |
pngOptions.format = SaveDocumentType.PNG; | |
pngOptions.PNG8 = false; // export as 8-bit PNG | |
pngOptions.quality = 100; | |
pngOptions.interlaced = false; | |
pngOptions.optimized = true; // optimize for web use | |
pngOptions.dither = Dither.NONE; // not use dithering | |
pngOptions.transparencyDither = Dither.NONE; // not use transparency dithering | |
//pngOptions.colorReduction = ColorReductionType.SELECTIVE; // use selective color reduction | |
//pngOptions.colors = 256; // use 256 colors | |
pngOptions.includeProfile = false; // include color profile | |
pngOptions.matteColor = new RGBColor(); //255, 255, 255); // White matte color | |
//pngOptions.metadata = null; | |
//pngOptions.progressive = null; | |
//exportPngManual(pngFile, pngOptions); | |
activeDocument.exportDocument(pngFile, ExportType.SAVEFORWEB, pngOptions); | |
//activeDocument.save(); | |
} | |
} | |
function hideSingleImageLayer(doc) { | |
var imageLayerCount = 0; | |
var imageLayer; | |
// Count image layers and identify the single image layer | |
for (var i = 0; i < doc.layers.length; i++) { | |
var layer = doc.layers[i]; | |
if (layer.kind == LayerKind.NORMAL) { | |
imageLayerCount++; | |
imageLayer = layer; | |
} | |
} | |
// If there's only one image layer, hide it | |
if (imageLayerCount == 1) { | |
imageLayer.visible = false; | |
} | |
} | |
function duplicateAndReplaceText(csvData, saveAsPSD, hideSingleImage) { | |
if (!csvData) { | |
alert("CSV file is empty."); | |
return; | |
} | |
var curDoc = app.activeDocument; | |
var textLayer = curDoc.activeLayer; | |
if (!textLayer.kind || textLayer.kind != LayerKind.TEXT) { | |
alert("Please select a text layer."); | |
return; | |
} | |
var currentDocs = []; | |
for (var i = 0; i < app.documents.length; i++) | |
currentDocs.push(app.documents[i]); | |
for (var i = 0; i < currentDocs.length; i++) { | |
var doc = currentDocs[i]; | |
if (doc != curDoc) { | |
var filename = doc.name.replace(/\.[^.]+$/, ""); | |
var replacement = getReplacementText(csvData, filename); | |
if (replacement) { | |
var copiedLayer = textLayer.duplicate(doc, ElementPlacement.PLACEATBEGINNING); | |
app.activeDocument = doc; | |
hideSingleImageLayer(doc); | |
copiedLayer.textItem.contents = replacement; | |
copiedLayer.name = replacement; | |
var docWidth = doc.width.as("px"); | |
//var docHeight = doc.height.as("px"); | |
var textWidth = copiedLayer.bounds[2].as("px") - copiedLayer.bounds[0].as("px"); | |
var textHeight = copiedLayer.bounds[3].as("px") - copiedLayer.bounds[1].as("px"); | |
var verticalScaleFactor = 1.0; | |
var isReducedTrackingAndSize = false; | |
// Adjust tracking until the text fits within the document width | |
if (copiedLayer.textItem.contents.length > 8 && textWidth > docWidth) { | |
copiedLayer.textItem.capitalization = TextCase.NORMAL; | |
var newTextHeight = copiedLayer.bounds[3].as("px") - copiedLayer.bounds[1].as("px"); | |
verticalScaleFactor = textHeight / newTextHeight; | |
} | |
// Adjust tracking and font size until the text fits within the document width | |
while (textWidth > docWidth) { | |
if (copiedLayer.textItem.tracking > -150) { | |
copiedLayer.textItem.tracking -= 10; | |
} else { | |
isReducedTrackingAndSize = true; | |
var currentFontSize = copiedLayer.textItem.size.as("pt"); | |
copiedLayer.textItem.size = (currentFontSize - 1) + " pt"; | |
// Adjust vertical scale to maintain constant text height | |
var newTextHeight = copiedLayer.bounds[3].as("px") - copiedLayer.bounds[1].as("px"); | |
var verticalScale = textHeight * verticalScaleFactor * 110 / newTextHeight; | |
copiedLayer.textItem.verticalScale = verticalScale; | |
} | |
copiedLayer.textItem.size = copiedLayer.textItem.size; // Force Photoshop to recalculate the text size | |
textWidth = copiedLayer.bounds[2].as("px") - copiedLayer.bounds[0].as("px"); | |
} | |
// Try to stretch text back if its size was reduced | |
while (isReducedTrackingAndSize && textWidth < docWidth) { | |
if (copiedLayer.textItem.tracking < 0) | |
copiedLayer.textItem.tracking += 10; | |
else | |
break; | |
copiedLayer.textItem.size = copiedLayer.textItem.size; | |
textWidth = copiedLayer.bounds[2].as("px") - copiedLayer.bounds[0].as("px"); | |
} | |
if (hideSingleImage) | |
hideSingleImageLayer(doc); | |
if (saveAsPSD) | |
saveAsPSDFile(doc, false, "export"); | |
app.activeDocument = curDoc; | |
} | |
} | |
} | |
alert("Text duplication complete."); | |
} | |
function readCSV(file) { | |
var csvData = []; | |
file.encoding = "UTF-8"; | |
file.open("r"); | |
while (!file.eof) { | |
var line = file.readln(); | |
var values = line.split(/\u2192/); // '\u{2192}' or → | |
csvData.push(values); | |
} | |
file.close(); | |
return csvData; | |
} | |
function getReplacementText(csvData, filename) { | |
for (var i = 0; i < csvData.length; i++) { | |
if (csvData[i][0] == filename) | |
return csvData[i][1]; | |
} | |
return null; | |
} | |
String.prototype.trim = function() { | |
return this.replace(/^\s+|\s+$/g, ""); | |
}; | |
/* | |
function trim(str, chars) { | |
chars = chars || '\\s'; // Default to removing whitespace characters | |
var regex = new RegExp('^[' + chars + ']+|[' + chars + ']+$', 'g'); | |
return str.replace(regex, ''); | |
} | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This script duplicates the current group of layers to every open document | |
if( app.documents.length > 0 ){ | |
duplicateToAll(); | |
} | |
function duplicateToAll(){ | |
docs = app.documents; | |
curDoc = app.activeDocument; | |
groupLayerset(); | |
for(var i = 0; i < docs.length; i++){ | |
if(curDoc != docs[i]){ | |
var curLayer; | |
try { curLayer = docs[i].activeLayer; } catch(e) {} | |
curDoc.activeLayer.duplicate(docs[i],ElementPlacement.PLACEATBEGINNING); | |
app.activeDocument = docs[i]; | |
app.activeDocument.activeLayer.name = 'Duplicated Group'; | |
if(curLayer){docs[i].activeLayer.move(curLayer, ElementPlacement.PLACEBEFORE);} | |
ungroupLayerset(); | |
} | |
app.activeDocument = curDoc; | |
} | |
ungroupLayerset(); | |
alert("Completed duplicating layer to all"); | |
} | |
function cTID(s){return charIDToTypeID(s)} | |
function sTID(s){return stringIDToTypeID(s)} | |
function ungroupLayerset(){ | |
var m_Dsc01 = new ActionDescriptor(); | |
var m_Ref01 = new ActionReference(); | |
m_Ref01.putEnumerated( cTID( "Lyr " ), cTID( "Ordn" ), cTID( "Trgt" ) ); | |
m_Dsc01.putReference( cTID( "null" ), m_Ref01 ); | |
executeAction( sTID( "ungroupLayersEvent" ), m_Dsc01, DialogModes.NO ); | |
} | |
function groupLayerset(){ | |
var desc = new ActionDescriptor(); | |
var ref = new ActionReference(); | |
ref.putClass( sTID('layerSection') ); | |
desc.putReference( cTID('null'), ref ); | |
var ref = new ActionReference(); | |
ref.putEnumerated( cTID('Lyr '), cTID('Ordn'), cTID('Trgt') ); | |
desc.putReference( cTID('From'), ref ); | |
executeAction( cTID('Mk '), desc, DialogModes.NO ); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment