Local and cloud-based workflows for extracting and updating text layers in PSD files via ExtendScript and Adobe PS API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
278 lines
No EOL
9.6 KiB
PHP
278 lines
No EOL
9.6 KiB
PHP
<?php
|
|
/**
|
|
* Photoshop Text Layer Editor
|
|
*
|
|
* A simple PHP tool to edit the text content of JSON files exported by ExtractTextWithBreaks.jsx
|
|
* Shows only the text parts (original text and updatedText fields) for easy editing.
|
|
* Saves the edited version with "-updated" appended to the filename.
|
|
*/
|
|
|
|
// Handle form submissions
|
|
$message = '';
|
|
$jsonData = null;
|
|
$filename = '';
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
// Check if we're uploading a file
|
|
if (isset($_FILES['jsonFile']) && $_FILES['jsonFile']['error'] === UPLOAD_ERR_OK) {
|
|
$filename = $_FILES['jsonFile']['name'];
|
|
$jsonContent = file_get_contents($_FILES['jsonFile']['tmp_name']);
|
|
$jsonData = json_decode($jsonContent, true);
|
|
|
|
if ($jsonData === null) {
|
|
$message = "Error: Invalid JSON file.";
|
|
}
|
|
}
|
|
// Check if we're saving edits
|
|
elseif (isset($_POST['saveEdits']) && isset($_POST['jsonData']) && isset($_POST['originalFilename'])) {
|
|
$jsonData = json_decode($_POST['jsonData'], true);
|
|
$filename = $_POST['originalFilename'];
|
|
|
|
// Update the text content with the edited values
|
|
foreach ($_POST as $key => $value) {
|
|
if (preg_match('/^updatedText_(\d+)$/', $key, $matches)) {
|
|
$index = $matches[1];
|
|
$jsonData['textLayers'][$index]['updatedText'] = $value;
|
|
}
|
|
}
|
|
|
|
// Create new filename with "-textonly-updated" suffix
|
|
// If filename already ends with -textonly.json, replace with -textonly-updated.json
|
|
if (strpos($filename, '-textonly.json') !== false) {
|
|
$newFilename = str_replace('-textonly.json', '-textonly-updated.json', $filename);
|
|
} else {
|
|
// Otherwise, just add -updated before .json
|
|
$newFilename = pathinfo($filename, PATHINFO_FILENAME) . '-textonly-updated.json';
|
|
}
|
|
|
|
// Save the updated JSON
|
|
$updatedJson = json_encode($jsonData, JSON_PRETTY_PRINT);
|
|
header('Content-Type: application/json');
|
|
header('Content-Disposition: attachment; filename="' . $newFilename . '"');
|
|
echo $updatedJson;
|
|
exit;
|
|
}
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Photoshop Text Layer Editor</title>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
body {
|
|
font-family: 'Montserrat', sans-serif;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 12px;
|
|
line-height: 1.4;
|
|
font-size: 14px;
|
|
}
|
|
h1 {
|
|
color: #333;
|
|
border-bottom: 1px solid #ddd;
|
|
padding-bottom: 8px;
|
|
margin-top: 10px;
|
|
margin-bottom: 10px;
|
|
font-size: 22px;
|
|
}
|
|
form {
|
|
margin: 12px 0;
|
|
}
|
|
label {
|
|
display: block;
|
|
margin-bottom: 3px;
|
|
font-weight: 600;
|
|
}
|
|
input[type="file"] {
|
|
margin-bottom: 10px;
|
|
}
|
|
input[type="submit"] {
|
|
background-color: #4CAF50;
|
|
color: white;
|
|
padding: 8px 12px;
|
|
border: none;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
font-family: 'Montserrat', sans-serif;
|
|
font-weight: 500;
|
|
}
|
|
input[type="submit"]:hover {
|
|
background-color: #45a049;
|
|
}
|
|
.message {
|
|
padding: 8px;
|
|
margin: 8px 0;
|
|
border-radius: 3px;
|
|
font-size: 13px;
|
|
}
|
|
.error {
|
|
background-color: #f8d7da;
|
|
color: #721c24;
|
|
border: 1px solid #f5c6cb;
|
|
}
|
|
.success {
|
|
background-color: #d4edda;
|
|
color: #155724;
|
|
border: 1px solid #c3e6cb;
|
|
}
|
|
.text-pair {
|
|
background-color: #f9f9f9;
|
|
padding: 10px;
|
|
margin-bottom: 12px;
|
|
border-radius: 3px;
|
|
border: 1px solid #ddd;
|
|
}
|
|
.text-pair h3 {
|
|
margin-top: 0;
|
|
margin-bottom: 5px;
|
|
color: #444;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
}
|
|
textarea {
|
|
width: 100%;
|
|
min-height: 60px;
|
|
padding: 6px;
|
|
box-sizing: border-box;
|
|
border: 1px solid #ddd;
|
|
border-radius: 3px;
|
|
resize: vertical;
|
|
font-family: 'Montserrat', sans-serif;
|
|
font-size: 13px;
|
|
}
|
|
.original-text {
|
|
background-color: #f0f0f0;
|
|
padding: 6px;
|
|
border-radius: 3px;
|
|
border: 1px solid #ddd;
|
|
margin-bottom: 6px;
|
|
white-space: pre-wrap;
|
|
font-size: 13px;
|
|
}
|
|
.layer-info {
|
|
font-size: 12px;
|
|
color: #666;
|
|
margin-bottom: 4px;
|
|
}
|
|
.instructions {
|
|
background-color: #e7f3fe;
|
|
border-left: 4px solid #2196F3;
|
|
padding: 8px;
|
|
margin-bottom: 12px;
|
|
font-size: 13px;
|
|
}
|
|
.instructions p {
|
|
margin: 0 0 4px 0;
|
|
}
|
|
.instructions ol {
|
|
margin: 5px 0;
|
|
padding-left: 20px;
|
|
}
|
|
.instructions li {
|
|
margin-bottom: 2px;
|
|
}
|
|
button.format-button {
|
|
margin-top: 4px;
|
|
background-color: #f0f0f0;
|
|
border: 1px solid #ddd;
|
|
border-radius: 3px;
|
|
padding: 4px 8px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
font-family: 'Montserrat', sans-serif;
|
|
}
|
|
button.format-button:hover {
|
|
background-color: #e0e0e0;
|
|
}
|
|
a {
|
|
color: #2196F3;
|
|
text-decoration: none;
|
|
}
|
|
a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Photoshop Text Layer Editor</h1>
|
|
|
|
<div class="instructions">
|
|
<p><strong>Instructions:</strong></p>
|
|
<ol>
|
|
<li>Upload a JSON file exported from the ExtractTextWithBreaks.jsx script</li>
|
|
<li>Edit the "Updated Text" field for any text layers you want to modify</li>
|
|
<li>Click "Save Changes" to download the updated JSON file</li>
|
|
<li>Use the updated JSON with the updateTextLayers.jsx script in Photoshop</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<?php if (!empty($message)): ?>
|
|
<div class="message <?php echo strpos($message, 'Error') === 0 ? 'error' : 'success'; ?>">
|
|
<?php echo $message; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($jsonData === null): ?>
|
|
<!-- File Upload Form -->
|
|
<form method="post" enctype="multipart/form-data">
|
|
<label for="jsonFile">Select a JSON file:</label>
|
|
<input type="file" name="jsonFile" id="jsonFile" accept=".json" required>
|
|
<input type="submit" value="Upload">
|
|
</form>
|
|
<?php else: ?>
|
|
<!-- Text Editor Form -->
|
|
<form method="post">
|
|
<input type="hidden" name="saveEdits" value="1">
|
|
<input type="hidden" name="jsonData" value="<?php echo htmlspecialchars(json_encode($jsonData)); ?>">
|
|
<input type="hidden" name="originalFilename" value="<?php echo htmlspecialchars($filename); ?>">
|
|
|
|
<?php if (isset($jsonData['textLayers']) && is_array($jsonData['textLayers'])): ?>
|
|
<p>Found <?php echo count($jsonData['textLayers']); ?> text layers in the file <strong><?php echo htmlspecialchars($filename); ?></strong>.</p>
|
|
|
|
<?php foreach ($jsonData['textLayers'] as $index => $layer): ?>
|
|
<div class="text-pair">
|
|
<div class="layer-info">
|
|
<strong>Layer:</strong> <?php echo htmlspecialchars($layer['name']); ?>
|
|
<br>
|
|
<strong>Path:</strong> <?php echo htmlspecialchars($layer['path']); ?>
|
|
</div>
|
|
|
|
<h3>Original Text:</h3>
|
|
<div class="original-text"><?php echo htmlspecialchars($layer['text']); ?></div>
|
|
|
|
<h3>Updated Text:</h3>
|
|
<textarea name="updatedText_<?php echo $index; ?>" rows="4"><?php
|
|
echo htmlspecialchars($layer['updatedText']);
|
|
?></textarea>
|
|
|
|
<button type="button" class="format-button" onclick="copyOriginalText(<?php echo $index; ?>)">Copy Original Text</button>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
|
|
<input type="submit" value="Save Changes">
|
|
<?php else: ?>
|
|
<p class="error">No text layers found in the JSON file.</p>
|
|
<?php endif; ?>
|
|
</form>
|
|
|
|
<!-- Link to upload another file -->
|
|
<p>
|
|
<a href="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>">Upload another file</a>
|
|
</p>
|
|
<?php endif; ?>
|
|
|
|
<script>
|
|
function copyOriginalText(index) {
|
|
var originalTextElement = document.getElementsByClassName('original-text')[index];
|
|
var updatedTextArea = document.getElementsByName('updatedText_' + index)[0];
|
|
|
|
if (originalTextElement && updatedTextArea) {
|
|
updatedTextArea.value = originalTextElement.innerText;
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|