// Extract Internal Layer IDs from Photoshop Document // This script extracts the actual internal IDs that Adobe's API uses // to identify text layers in a PSD file. #target photoshop // Function to extract text layers with their internal IDs function extractTextLayersWithInternalIDs() { if (!app.documents.length) { return { error: "No document open" }; } var doc = app.activeDocument; var result = { documentName: doc.name, psdPath: doc.fullName.fsName, extractedAt: new Date().toString(), dimensions: { width: doc.width.as('px'), height: doc.height.as('px') }, textLayerCount: 0, textLayers: [] }; // Utility function to traverse layers and extract info function traverseLayers(layers, path) { path = path || ""; for (var i = 0; i < layers.length; i++) { var layer = layers[i]; if (layer.typename === "LayerSet") { // This is a layer group, traverse its children var groupPath = path ? path + "/" + layer.name : layer.name; traverseLayers(layer.layers, groupPath); } else if (layer.kind === LayerKind.TEXT) { // This is a text layer, extract its information extractTextLayerInfo(layer, path); } } } function extractTextLayerInfo(layer, path) { try { var layerPath = path ? path + "/" + layer.name : layer.name; // Get the layer's ID directly from the layer object var directID = layer.id; // Get the layer's internal ID using ActionManager var internalID = getInternalLayerID(layer); // Extract text content var text = layer.textItem.contents; // Extract basic style information var styleInfo = { font: layer.textItem.font, size: layer.textItem.size.value, color: null, // Will be populated if available alignment: getTextAlignment(layer) }; // Try to extract text color try { if (layer.textItem.color.rgb) { styleInfo.color = [ layer.textItem.color.rgb.red, layer.textItem.color.rgb.green, layer.textItem.color.rgb.blue ]; } } catch (e) { // Color not defined or error accessing it } // Extract detailed formatting info if available var richTextInfo = extractRichTextInfo(layer); if (richTextInfo && richTextInfo.styles && richTextInfo.styles.length > 0) { styleInfo.styles = richTextInfo.styles; var hasRichTextFormatting = richTextInfo.styles.length > 1; } else { styleInfo.styles = []; var hasRichTextFormatting = false; } // Create layer info object with both direct and internal IDs var layerInfo = { id: directID, // The ID we normally use internalID: internalID, // The ID Adobe API might use name: layer.name, path: layerPath, text: text, visible: layer.visible, styleInfo: styleInfo, hasRichTextFormatting: hasRichTextFormatting }; // Add to results result.textLayers.push(layerInfo); result.textLayerCount++; } catch (e) { // Log error but continue processing other layers $.writeln("Error processing layer '" + layer.name + "': " + e.message); } } // Function to get internal layer ID using ActionManager function getInternalLayerID(layer) { try { // Select the layer var idslct = charIDToTypeID("slct"); var desc = new ActionDescriptor(); var ref = new ActionReference(); ref.putIdentifier(charIDToTypeID("Lyr "), layer.id); desc.putReference(charIDToTypeID("null"), ref); executeAction(idslct, desc, DialogModes.NO); // Get the layer's item index var ref = new ActionReference(); ref.putProperty(charIDToTypeID("Prpr"), charIDToTypeID("ItmI")); ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt")); var itemIndexDesc = executeActionGet(ref); var itemIndex = itemIndexDesc.getInteger(charIDToTypeID("ItmI")); // Get the layer's ID var ref = new ActionReference(); ref.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("layerID")); ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt")); var layerDesc = executeActionGet(ref); // Check if the layer has a layerID property if (layerDesc.hasKey(stringIDToTypeID("layerID"))) { return layerDesc.getInteger(stringIDToTypeID("layerID")); } else { // Fallback to another method of ID extraction return layer.id; // Use direct ID as fallback } } catch (e) { $.writeln("Error getting internal ID for layer '" + layer.name + "': " + e.message); return layer.id; // Use direct ID if we can't get the internal ID } } // Function to get text alignment function getTextAlignment(layer) { try { var align = layer.textItem.justification; if (align === Justification.LEFT) { return "left"; } else if (align === Justification.CENTER) { return "center"; } else if (align === Justification.RIGHT) { return "right"; } else { return "left"; // Default } } catch (e) { return "left"; // Default if error } } // Function to extract rich text information function extractRichTextInfo(layer) { try { var styles = []; // Get a reference to the layer var ref = new ActionReference(); ref.putIdentifier(charIDToTypeID("Lyr "), layer.id); var desc = executeActionGet(ref); // Check if textKey exists if (desc.hasKey(stringIDToTypeID("textKey"))) { var textKey = desc.getObjectValue(stringIDToTypeID("textKey")); // Check if textStyleRange exists if (textKey.hasKey(stringIDToTypeID("textStyleRange"))) { var styleRanges = textKey.getList(stringIDToTypeID("textStyleRange")); // Iterate through each style range for (var i = 0; i < styleRanges.count; i++) { var range = styleRanges.getObjectValue(i); var from = range.getInteger(stringIDToTypeID("from")); var to = range.getInteger(stringIDToTypeID("to")); var styleRef = range.getObjectValue(stringIDToTypeID("textStyle")); var rangeText = layer.textItem.contents.substring(from, to); var fontName = layer.textItem.font; var fontSize = layer.textItem.size.value; var fontStyle = "Regular"; var fontColor = null; // Extract font name try { if (styleRef.hasKey(stringIDToTypeID("fontName"))) { fontName = styleRef.getString(stringIDToTypeID("fontName")); } } catch (e) {} // Extract font style try { if (styleRef.hasKey(stringIDToTypeID("fontStyleName"))) { fontStyle = styleRef.getString(stringIDToTypeID("fontStyleName")); } } catch (e) {} // Extract font size try { if (styleRef.hasKey(stringIDToTypeID("size"))) { fontSize = styleRef.getUnitDoubleValue(stringIDToTypeID("size")); } } catch (e) {} // Extract color try { if (styleRef.hasKey(stringIDToTypeID("color"))) { var colorObj = styleRef.getObjectValue(stringIDToTypeID("color")); var colorValues = colorObj.getObjectValue(stringIDToTypeID("color")); // Check color model if (colorValues.hasKey(stringIDToTypeID("red"))) { fontColor = [ Math.round(colorValues.getDouble(stringIDToTypeID("red"))), Math.round(colorValues.getDouble(stringIDToTypeID("green"))), Math.round(colorValues.getDouble(stringIDToTypeID("blue"))) ]; } } } catch (e) {} // Add style info to the array styles.push({ start: from, end: to, text: rangeText, font: fontName, style: fontStyle, size: fontSize, color: fontColor }); } } } return { styles: styles }; } catch (e) { $.writeln("Error extracting rich text info: " + e.message); return { styles: [] }; } } // Start the layer traversal traverseLayers(doc.layers); return result; } // Main function function main() { try { var result = extractTextLayersWithInternalIDs(); // Convert result to formatted JSON var jsonString = JSON.stringify(result, null, 2); // Save the result to a file var docPath = app.activeDocument.path; var fileName = app.activeDocument.name.replace(/\.[^\.]+$/, '') + "_internal_ids.json"; var outputFile = new File(docPath + "/" + fileName); if (outputFile.open("w")) { outputFile.write(jsonString); outputFile.close(); alert("Layer ID information saved to:\n" + outputFile.fsName); } else { alert("Error saving output file"); } // Also return the JSON string return jsonString; } catch (e) { alert("Error: " + e.message); return JSON.stringify({ error: e.message }); } } // Run the script main();