#target photoshop /* Export art layers with optional scope, format and trimming settings. - Can export every art layer or only layers currently visible (including parent visibility). - Supports PNG and JPEG with adjustable JPEG quality (0-100). - Can keep the full document canvas or trim transparent edges for each layer. - Does not add prefixes or four-digit sequence numbers. - Works on a duplicate, so the source document and its visibility states stay unchanged. - Nested art layers are supported; layer groups themselves are not exported. */ (function () { if (app.documents.length === 0) { alert("请先打开一个 PSD/PSB 文档。"); return; } function pad2(value) { return value < 10 ? "0" + value : String(value); } function timeStamp() { var now = new Date(); return now.getFullYear() + pad2(now.getMonth() + 1) + pad2(now.getDate()) + "_" + pad2(now.getHours()) + pad2(now.getMinutes()) + pad2(now.getSeconds()); } function safeName(name) { var value = String(name); value = value.replace(/\.(png|jpe?g)$/i, ""); value = value.replace(/[\\\/:*?"<>|]/g, "_"); value = value.replace(/^\s+|\s+$/g, ""); value = value.replace(/[\. ]+$/g, ""); return value.length > 0 ? value : "unnamed_layer"; } function showOptionsDialog() { var dialog = new Window("dialog", "图层导出选项"); dialog.orientation = "column"; dialog.alignChildren = "fill"; var scopePanel = dialog.add("panel", undefined, "导出范围"); scopePanel.orientation = "column"; scopePanel.alignChildren = "left"; scopePanel.margins = 12; var visibleOnly = scopePanel.add("checkbox", undefined, "仅导出当前可见图层"); visibleOnly.value = true; scopePanel.add("statictext", undefined, "未勾选时导出所有普通图层"); var formatPanel = dialog.add("panel", undefined, "文件格式"); formatPanel.orientation = "column"; formatPanel.alignChildren = "left"; formatPanel.margins = 12; var formatChoices = formatPanel.add("group"); formatChoices.orientation = "row"; var pngRadio = formatChoices.add("radiobutton", undefined, "PNG"); var jpegRadio = formatChoices.add("radiobutton", undefined, "JPEG"); pngRadio.value = true; var qualityGroup = formatPanel.add("group"); qualityGroup.orientation = "row"; qualityGroup.alignChildren = ["left", "center"]; var qualityLabel = qualityGroup.add("statictext", undefined, "JPEG 质量:"); var qualitySlider = qualityGroup.add("slider", undefined, 90, 0, 100); qualitySlider.preferredSize.width = 180; var qualityValue = qualityGroup.add("statictext", undefined, "90"); qualityValue.characters = 3; function setQualityEnabled(enabled) { qualityLabel.enabled = enabled; qualitySlider.enabled = enabled; qualityValue.enabled = enabled; } qualitySlider.onChanging = function () { qualityValue.text = String(Math.round(qualitySlider.value)); }; qualitySlider.onChange = qualitySlider.onChanging; pngRadio.onClick = function () { setQualityEnabled(false); }; jpegRadio.onClick = function () { setQualityEnabled(true); }; setQualityEnabled(false); var trimPanel = dialog.add("panel", undefined, "画布"); trimPanel.orientation = "column"; trimPanel.alignChildren = "left"; trimPanel.margins = 12; var trimOutput = trimPanel.add("checkbox", undefined, "导出时裁切透明边缘"); trimOutput.value = true; trimPanel.add("statictext", undefined, "未勾选时保留原文档画布尺寸"); var buttons = dialog.add("group"); buttons.alignment = "right"; buttons.add("button", undefined, "取消", {name: "cancel"}); buttons.add("button", undefined, "继续", {name: "ok"}); if (dialog.show() !== 1) { return null; } return { visibleOnly: visibleOnly.value, format: jpegRadio.value ? "jpeg" : "png", jpegQuality: Math.round(qualitySlider.value), trim: trimOutput.value }; } function collectArtLayers(container, result) { for (var i = 0; i < container.layers.length; i++) { var layer = container.layers[i]; if (layer.typename === "ArtLayer") { result.push(layer); } else if (layer.typename === "LayerSet") { collectArtLayers(layer, result); } } } function isEffectivelyVisible(layer) { if (!layer.visible) { return false; } var parent = layer.parent; while (parent && parent.typename === "LayerSet") { if (!parent.visible) { return false; } parent = parent.parent; } return true; } function hideAllArtLayers(layers) { for (var i = 0; i < layers.length; i++) { try { layers[i].visible = false; } catch (ignore) {} } } function showLayerAndParents(layer) { layer.visible = true; var parent = layer.parent; while (parent && parent.typename === "LayerSet") { parent.visible = true; parent = parent.parent; } } function createUniqueOutputFolder(parent, documentName, formatLabel) { var baseName = safeName(documentName.replace(/\.[^\.]+$/, "")) + "_" + formatLabel; var folder = new Folder(parent.fsName + "/" + baseName); if (folder.exists) { folder = new Folder(parent.fsName + "/" + baseName + "_" + timeStamp()); } if (!folder.create()) { throw new Error("无法创建输出文件夹:" + folder.fsName); } return folder; } function createExportOptions(format, jpegQuality) { var exportOptions = new ExportOptionsSaveForWeb(); exportOptions.includeProfile = false; if (format === "jpeg") { exportOptions.format = SaveDocumentType.JPEG; exportOptions.quality = jpegQuality; exportOptions.optimized = true; } else { exportOptions.format = SaveDocumentType.PNG; exportOptions.PNG8 = false; exportOptions.transparency = true; exportOptions.interlaced = false; } return exportOptions; } var sourceDoc = app.activeDocument; var settings = showOptionsDialog(); if (settings === null) { return; } var formatLabel = settings.format === "jpeg" ? "JPEG" : "PNG"; var extension = settings.format === "jpeg" ? ".jpg" : ".png"; var parentFolder = Folder.selectDialog("选择 " + formatLabel + " 导出目录"); if (parentFolder === null) { return; } var oldDialogs = app.displayDialogs; var workDoc = null; try { app.displayDialogs = DialogModes.NO; workDoc = sourceDoc.duplicate(); app.activeDocument = workDoc; var allLayers = []; var layers = []; collectArtLayers(workDoc, allLayers); for (var i = 0; i < allLayers.length; i++) { if (!settings.visibleOnly || isEffectivelyVisible(allLayers[i])) { layers.push(allLayers[i]); } } if (layers.length === 0) { if (settings.visibleOnly) { throw new Error("文档中没有当前可见的普通图层。"); } throw new Error("文档中没有可导出的普通图层。"); } var outputFolder = createUniqueOutputFolder(parentFolder, sourceDoc.name, formatLabel); var exportOptions = createExportOptions(settings.format, settings.jpegQuality); var usedNames = {}; hideAllArtLayers(allLayers); for (i = 0; i < layers.length; i++) { hideAllArtLayers(allLayers); showLayerAndParents(layers[i]); var baseName = safeName(layers[i].name); var key = baseName.toLowerCase(); if (usedNames[key] === undefined) { usedNames[key] = 1; } else { usedNames[key]++; baseName += "_" + usedNames[key]; } var preTrimState = null; if (settings.trim) { preTrimState = workDoc.activeHistoryState; try { workDoc.trim(TrimType.TRANSPARENT, true, true, true, true); } catch (ignoreTrim) { // Empty/adjustment-only layers may not contain trim-able pixels. } } var outputFile = new File(outputFolder.fsName + "/" + baseName + extension); workDoc.exportDocument(outputFile, ExportType.SAVEFORWEB, exportOptions); if (preTrimState !== null) { try { workDoc.activeHistoryState = preTrimState; } catch (ignoreHistory) {} } } workDoc.close(SaveOptions.DONOTSAVECHANGES); workDoc = null; app.activeDocument = sourceDoc; app.displayDialogs = oldDialogs; alert( "导出完成:" + layers.length + " 个 " + formatLabel + "\n\n" + "范围 = " + (settings.visibleOnly ? "仅当前可见图层" : "所有普通图层") + "\n" + (settings.format === "jpeg" ? "JPEG 质量 = " + settings.jpegQuality + "\n" : "") + "文件名 = 图层名\n" + "尺寸 = " + (settings.trim ? "已裁切透明边缘" : "原文档画布尺寸") + "\n\n" + "输出目录:\n" + outputFolder.fsName ); } catch (error) { if (workDoc !== null) { try { workDoc.close(SaveOptions.DONOTSAVECHANGES); } catch (ignoreClose) {} } try { app.activeDocument = sourceDoc; } catch (ignoreActivate) {} app.displayDialogs = oldDialogs; alert("导出失败:\n" + error.message); } }());