From 9aa7e6742e4a79f9f3a91aede298ff956c7f083d Mon Sep 17 00:00:00 2001 From: Rhythm Jain Date: Fri, 28 Mar 2025 11:39:48 +0530 Subject: [PATCH 1/8] Adding Sample Scripts for Extendscript and UXP --- .DS_Store | Bin 0 -> 6148 bytes SampleScripts/balanceRaggedLines/README.md | 63 ++ .../balanceRaggedLines/balanceRaggedLines.jsx | 324 ++++++++ SampleScripts/balanceRaggedLines/errors.jsx | 160 ++++ SampleScripts/balanceRaggedLines/json2.jsx | 530 +++++++++++++ .../balanceRaggedLines/manifest.json | 17 + SampleScripts/balanceRaggedLines/utils.jsx | 702 +++++++++++++++++ SampleScripts/hyphenation/README.md | 63 ++ SampleScripts/hyphenation/errors.jsx | 160 ++++ SampleScripts/hyphenation/hyphenate.jsx | 325 ++++++++ SampleScripts/hyphenation/json2.jsx | 530 +++++++++++++ SampleScripts/hyphenation/manifest.json | 17 + SampleScripts/hyphenation/utils.jsx | 702 +++++++++++++++++ SampleScripts/idmlConversion/.DS_Store | Bin 0 -> 6148 bytes SampleScripts/idmlConversion/README.md | 62 ++ SampleScripts/idmlConversion/idml.idjs | 745 ++++++++++++++++++ SampleScripts/idmlConversion/manifest.json | 17 + 17 files changed, 4417 insertions(+) create mode 100644 .DS_Store create mode 100644 SampleScripts/balanceRaggedLines/README.md create mode 100644 SampleScripts/balanceRaggedLines/balanceRaggedLines.jsx create mode 100644 SampleScripts/balanceRaggedLines/errors.jsx create mode 100644 SampleScripts/balanceRaggedLines/json2.jsx create mode 100644 SampleScripts/balanceRaggedLines/manifest.json create mode 100644 SampleScripts/balanceRaggedLines/utils.jsx create mode 100644 SampleScripts/hyphenation/README.md create mode 100644 SampleScripts/hyphenation/errors.jsx create mode 100644 SampleScripts/hyphenation/hyphenate.jsx create mode 100644 SampleScripts/hyphenation/json2.jsx create mode 100644 SampleScripts/hyphenation/manifest.json create mode 100644 SampleScripts/hyphenation/utils.jsx create mode 100644 SampleScripts/idmlConversion/.DS_Store create mode 100644 SampleScripts/idmlConversion/README.md create mode 100644 SampleScripts/idmlConversion/idml.idjs create mode 100644 SampleScripts/idmlConversion/manifest.json diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a804999a48d59ccab75f8fc888a12707d89682ee GIT binary patch literal 6148 zcmeHKy-LJD5T5lSoF2kqrCkca`raTOu@KJ2yny%Tose9Do`qcs!Cp(jR@6eD!s4F8 zO2KFFo89Fu@qP>zA~FLrU$Q&1JM)p<%@UESpLJ?PRU#@w8J$&_8N&UnQ?g`RI9Ta1 z2Gpi59Z-{cQ{K`b3(s)E4WZff+rvukP=k)q+NIO5R}0#4l$ZkEw~NL_ zvG2FSxCQ_6<@Lwb{pe+m#u}MFZuW~UZRj5zrl)!dTQ0BTz&w07E^<0fyj@5ra_D_@T&}b E0`_dIx&QzG literal 0 HcmV?d00001 diff --git a/SampleScripts/balanceRaggedLines/README.md b/SampleScripts/balanceRaggedLines/README.md new file mode 100644 index 0000000..c38a2b0 --- /dev/null +++ b/SampleScripts/balanceRaggedLines/README.md @@ -0,0 +1,63 @@ +# Balance Ragged Lines Capability + +This project provides a script (`balanceRaggedLines.jsx`) and its corresponding manifest (`manifest.json`) to enable or disable balancing ragged lines in text frames within Adobe InDesign documents using ExtendScript. + +## Features + +- Opens an InDesign document from a specified path. +- Identifies text frames based on provided labels. +- Balances or unbalances ragged lines for paragraphs in the identified text frames. +- Logs processing steps and performance metrics. +- Handles errors and warnings during the process. + +## File Structure + +- **`balanceRaggedLines.jsx`**: The main script that implements the balancing functionality. +- **`manifest.json`**: The manifest file that defines the capability and its integration with Adobe InDesign. + +## Execution + +1. Place the `balanceRaggedLines.jsx` script and `manifest.json` in the appropriate folder parallelly. +2. Create a capability bundle, which is a ZIP file with a specific structure. The files should be zipped directly without a parent folder. +``` +Archive.zip +|------ manifest.json +|------ balanceRaggedLines.jsx +``` +3. Follow this [guide](https://developer.adobe.com/firefly-services/docs/indesign-apis/how-tos/working-with-capabilities-api/) to register your script. + +4. Provide the required parameters to run the script: + +```json +{ + "assets": [ + { + "source": { + "type": "HTTP_GET", + "url": "" + }, + "destination": "sample.indd" + } + ], + "params": { + "targetDocument": "sample.indd", + "outputPath": "balanced.indd", + "labels": ["MainPara","Heading"], // If no Labels are provided, this script will balance ragged lines or unbalance ragged lines all of the text frames in the document. + "balance": "true" //Can be "balance":false as well. + } +} +``` + +## Logging +The script logs processing steps and performance metrics to a log file (LogFile.txt) in the working directory. Logs include: + +- Time taken to open the document. +- Time taken to update links. +- Time taken to balance ragged lines. + +## Error Handling +The script captures and logs errors during execution. Common errors include: + +- Missing or invalid document paths. +- Issues with updating links. +- Processing failures. diff --git a/SampleScripts/balanceRaggedLines/balanceRaggedLines.jsx b/SampleScripts/balanceRaggedLines/balanceRaggedLines.jsx new file mode 100644 index 0000000..7358376 --- /dev/null +++ b/SampleScripts/balanceRaggedLines/balanceRaggedLines.jsx @@ -0,0 +1,324 @@ +/************************************************************************* +* ADOBE CONFIDENTIAL +* ___________________ +* +* Copyright 2021 Adobe +* All Rights Reserved. +* +* NOTICE: All information contained herein is, and remains +* the property of Adobe and its suppliers, if any. The intellectual +* and technical concepts contained herein are proprietary to Adobe +* and its suppliers and are protected by all applicable intellectual +* property laws, including trade secret and copyright laws. +* Dissemination of this information or reproduction of this material +* is strictly forbidden unless prior written permission is obtained +* from Adobe. +**************************************************************************/ + + +// Following are the file inclusions and not comments. +// @include "errors.jsx" +// @include "json2.jsx" +// @include "utils.jsx" +// File inclusion ends. + +// Globals: define any globals here. +var timingObject = {} +var warnings = {} +var document +// Globals end. + +// Constant strings for key references +var KeyStrings = { + OutputPath: 'outputPath', + ProcessedData: 'processedData', + TargetDocument: 'targetDocument', + TimeDocumentClose: 'DocumentClose', + TimeDocumentOpen: 'DocumentOpen', + TimeBalancing: 'TimeBalanceRagged', + TimeOverall: 'Overall', + TimeRelinkAssets: 'RelinkAssets', + Timings: 'timings', + WorkingFolder: 'workingFolder' +} + +/* Handles document warnings such as missing links or fonts */ +function handleWarnings (document) { + UTILS.Log('Handling warnings') + var errorlog = app.errorListErrors + var warningsObject = {} + + var missingLinkArray = [] + var missingFontArray = [] + var failedErrorsArray = [] + var otherWarningsArray = [] + var missingLinkStr = '(Link missing.; ' + var missingLinkStrLen = missingLinkStr.length + + // Iterate through error list and categorize warnings + for (var i = 0; i < errorlog.count(); i++) { + var error = errorlog[i] + UTILS.Log('Warning No. ' + (i + 1) + ': ' + error.listErrorCode + '(' + error.listErrorMessage + ')') + if (error.listErrorCode === 35842) { // Missing link error + var missingLink = error.listErrorMessage + missingLink = missingLink.substring(missingLink.indexOf(missingLinkStr) + missingLinkStrLen) + if (missingLinkArray.indexOf(missingLink) === -1) { + missingLinkArray.push(missingLink) + } + } else if (error.listErrorCode === 1 && error.listErrorMessage.search(/missing font/i) !== -1) { + var missingFont = error.listErrorMessage + missingFont = missingFont.substring(missingFont.search(/missing font/i) + 13) + if (missingFontArray.indexOf(missingFont) === -1) { + missingFontArray.push(missingFont) + } + } else if (error.listErrorCode === 1) { + otherWarningsArray.push(error.listErrorMessage) + } + } + + // Store collected warnings in an object + if (missingLinkArray.length > 0) { + warningsObject.missingLinks = missingLinkArray + } + if (missingFontArray.length > 0) { + warningsObject.missingFonts = missingFontArray + } + if (failedErrorsArray.length > 0) { + warningsObject.exportErrors = failedErrorsArray + } + if (otherWarningsArray.length > 0) { + warningsObject.otherWarnings = otherWarningsArray + } + return warningsObject +}; + + +/* Balances ragged lines for text frames within a document */ +function balanceRaggedLines (document,labels,balance) { + + var allFrames = document.allPageItems; + var labelledFrames = [] + + UTILS.Log('Inside Balanced Ragged Lines') + + // Convert labels to an object for faster lookups (Set is not available in ExtendScript) + var labelMap = {}; + if (labels && labels.length > 0) { + if (!(labels instanceof Array)) { + labels = [labels]; // Convert single label to an array + } + for (var i = 0; i < labels.length; i++) { + labelMap[labels[i]] = true; + } + + // Iterate over frames and find those matching the labels + for (var i = 0; i < allFrames.length; i++) { + var frame = allFrames[i]; + if (frame instanceof TextFrame && labelMap[frame.label]) { + labelledFrames.push(frame); + } + } + } else { + // If no labels provided, select all text frames + for (var i = 0; i < allFrames.length; i++) { + if (allFrames[i] instanceof TextFrame) { + labelledFrames.push(allFrames[i]); + } + } + } + UTILS.Log('Labels Extracted ' + labelledFrames.length) + + + // Balance ragged lines for all paragraphs in the labelled frames + if(labelledFrames.length == 0){ + UTILS.Log('No frames found') + return + } + for (var i = 0; i < labelledFrames.length; i++) { + var textFrame = labelledFrames[i]; + var paragraphs = textFrame.paragraphs; + for (var j = 0; j < paragraphs.length; j++) { + var para = paragraphs[j]; + if(balance===true){ + para.balanceRaggedLines = true; + } + else{ + para.balanceRaggedLines = false; + } + } + } + document.save(); +}; + + +/* Processes parameters and performs document operations */ +function ProcessParams (parameters) { + UTILS.Log('Processing parameters internal') + var returnVal = {} + + // Open the target document + var documentPath = UTILS.GetStringFromObject(parameters, KeyStrings.TargetDocument) + documentPath = UTILS.GetFullPath(documentPath) + + UTILS.Log('Opening document') + var tempTime = new Date().getTime() + document = app.open(File(documentPath)) + timingObject[KeyStrings.TimeDocumentOpen] = (new Date()).getTime() - tempTime + UTILS.Log('Opened document') + + // Relink assets and handle warnings + tempTime = new Date().getTime() + UTILS.UpdateDocumentLinks(document) + timingObject[KeyStrings.TimeRelinkAssets] = (new Date()).getTime() - tempTime + UTILS.Log('Updated links in the document') + + // Capturing top level errors and then clear errorList. + UTILS.Log('Number of errors: ' + app.errorListErrors.count()) + if (app.errorListErrors.count() > 0) { + warnings = handleWarnings(document) + } + app.clearAllErrors() + UTILS.Log('Got the warnings from the document.') + + var outputPath = UTILS.GetStringFromObject(parameters, KeyStrings.OutputPath) + outputPath = UTILS.GetFullPath(outputPath) + + tempTime = new Date().getTime() + UTILS.UpdateDocumentLinks(document) + var labels = parameters.labels + var balance = parameters.balance + + // Calling the function to balance ragged lines + balanceRaggedLines(document,labels,balance); + + // Save the document as a new file. + document.save(File(outputPath)) + + timingObject[KeyStrings.TimeBalancing] = (new Date()).getTime() - tempTime + + // Add file to be uploaded as an output file. + var relativePath = UTILS.GetRelativeReturnPath(outputPath) + UTILS.AddAssetToBeUploaded(relativePath) + + returnVal.OutputPath = documentPath + // Processing ends. + + return returnVal +} + + +/* Main function to execute the script */ +function main () { + + var startTime = new Date().getTime() // Capture the script start time + var returnObj = {} // Object to store return values + var parameters = {} // Stores input parameters + var tempTime + var errorOccurred = false // Flag to track errors + var data = {} // Stores processed data + + // Set application preferences + app.clearAllErrors() + app.generalPreferences.pageNumbering = PageNumberingOptions.ABSOLUTE + app.linkingPreferences.checkLinksAtOpen = true + app.serverSettings.useErrorList = true + var previousUnit = app.scriptPreferences.measurementUnit + app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS + + try { + UTILS.InitiateLogging() + UTILS.Log('Initiating logging.') + + // As a safe practice close any open documents before processing + UTILS.CloseAllOpenDocuments() + + // Parse input parameters + var input = app.scriptArgs.get('parameters') + var allParameters = JSON.parse(input) + + UTILS.Log('Parsed job input : ' + input) + parameters = allParameters.params + if (parameters === undefined) { + parameters = allParameters.input.params + } + + // Check if parameters are valid + if (parameters === undefined || typeof parameters !== 'object' || Array.isArray(parameters)) { + UTILS.Log('No params found') + UTILS.RaiseException(Errors.MissingParams) + } + + // Set the working folder first. This is the directory within which all the input and output assets are to be managed. + UTILS.SetWorkingFolder(UTILS.GetStringFromObject(allParameters, KeyStrings.WorkingFolder)) + + var result + UTILS.Log('Processing Params') + tempTime = new Date().getTime() + + // Process parameters and document + result = ProcessParams(parameters) + data[KeyStrings.ProcessedData] = result + + // Also add the log file + UTILS.AddAssetToBeUploaded(UTILS.logFilePath) + + data.warnings = warnings + // Processing ends. + + tempTime = new Date().getTime() + if (document && document.isValid){ + document.close() + } + + timingObject[KeyStrings.TimeDocumentClose] = (new Date()).getTime() - tempTime + UTILS.Log('End of try') + } catch (e) { + var tempObj = { + name: e.name, + message: e.message, + errorCode: e.number, + isCustom: e.isCustom, + line: e.line, + fileName: e.fileName + } + + UTILS.Log('Exception occurred', tempObj) + errorOccurred = true + + // Failure, prepare the object to be returned. + returnObj = UTILS.HandleError(tempObj) + } finally { + app.scriptPreferences.measurementUnit = previousUnit + UTILS.Log('In finally') + if (document && document.isValid) { + + // If Document is still open. Close it. + UTILS.Log('Closing document') + tempTime = new Date().getTime() + document.close() + timingObject[KeyStrings.TimeDocumentClose] = (new Date()).getTime() - tempTime + } + + var elapsedTime = (new Date()).getTime() - startTime + UTILS.Log('Time taken: ' + elapsedTime) + timingObject[KeyStrings.TimeOverall] = elapsedTime + UTILS.Log('Timing: ' + JSON.stringify(timingObject)) + data[KeyStrings.Timings] = timingObject + + if (!errorOccurred) { + // Success, prepare the object to be returned. + UTILS.Log('Finally: No error') + returnObj = UTILS.GetSuccessReturnObj(data) + } + UTILS.Log('Final Result', JSON.stringify(returnObj)) + + // Cleanup and Return + UTILS.TerminateLogging() + app.clearAllErrors() + + return UTILS.GetFinalReturnPackage(returnObj) + } +} + +// Execute the main function +main() diff --git a/SampleScripts/balanceRaggedLines/errors.jsx b/SampleScripts/balanceRaggedLines/errors.jsx new file mode 100644 index 0000000..21b34b9 --- /dev/null +++ b/SampleScripts/balanceRaggedLines/errors.jsx @@ -0,0 +1,160 @@ +/************************************************************************* +* ADOBE CONFIDENTIAL +* ___________________ +* +* Copyright 2021 Adobe +* All Rights Reserved. +* +* NOTICE: All information contained herein is, and remains +* the property of Adobe and its suppliers, if any. The intellectual +* and technical concepts contained herein are proprietary to Adobe +* and its suppliers and are protected by all applicable intellectual +* property laws, including trade secret and copyright laws. +* Dissemination of this information or reproduction of this material +* is strictly forbidden unless prior written permission is obtained +* from Adobe. +**************************************************************************/ +// errors.jsx + +// Error constants are defined here. +/* This document list all the error which are possible from the scripts. The error object is built such that it has an error code and some error strings. + The error strings are constructed in a way such that the first string is default and a string literal. It is to be returned anyhow. The subsequent strings can be + strings having '^1' as placeholder replacement string. This replacement string can be replaced with relevant information. Based on the information + available the final error message can be created. +*/ +/* eslint-disable no-unused-vars */ + +var ErrorReplacementString = '^1' + +var Errors = { + // Errors during processing: 1001-1999 + InternalScriptError: { + errorCode: 'internal_error', + errorStrings: [ + 'Internal script error. This should not had happened.' + ] + }, + ProcessingErrorOccurred: { + errorCode: 'internal_error', + errorStrings: [ + 'Internal error: Error during processing.' + ] + }, + PDFPresetNotSet: { + errorCode: 'capability_error', + errorStrings: [ + 'Capability error: No PDF Preset could be set.' + ] + }, + OutputDirError: { + errorCode: 'internal_error', + errorStrings: [ + 'Internal error: Unable to create specified output directory.' + ] + }, + RelinkError: { + errorCode: 'capability_error', + errorStrings: [ + 'Capability error: Unable to relink.', + 'Relink failed for ^1.' + ] + }, + PlaceError: { + errorCode: 'capability_error', + errorStrings: [ + 'Capability error: Unable to place.', + 'Place failed for ^1.' + ] + }, + + // Errors in input: 2001-2999 + ArrayExpected: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected array ', + 'for property ^1.' + ] + }, + ParsingBoolError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected boolean ', + '^1 is the provided value ', + 'for property ^1.' + ] + }, + ParsingIntError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected integer ', + '^1 is the provided value ', + 'for property ^1.' + ] + }, + ParsingFloatError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected number ', + '^1 is the provided value ', + 'for property ^1.' + ] + }, + ParsingStringError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected string ', + '^1 is the provided value ', + 'for property ^1.' + ] + }, + OutOfBound: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Incorrect range provided. ', + 'Culprit Value is ^1.' + ] + }, + MissingKey: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Key not found in object. ', + 'Missing key is ^1.' + ] + }, + EnumError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: No matching enum value found. ', + '^1 is the provided value', + 'for property ^1.' + ] + }, + MissingParams: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Either the \'params\' is not found in the request or it is not in the correct format.' + ] + }, + ObjectExpected: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected object ', + 'for property ^1.' + ] + }, + AtLeastOneInternalParamShouldBePresent: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: At least one of the parameters should be present. ', + 'Missing parameters are ^1' + ] + }, + + // Errors coming from IDS and sent under ProcessingErrorOccurred. These are specific errors which need string change while returning to the user. + CannotOpenFileError: { + errorCode: 'capability_error', // kCannotOpenFileError + errorStrings: [ + 'Capability error: Cannot open file.' + ] + } +} diff --git a/SampleScripts/balanceRaggedLines/json2.jsx b/SampleScripts/balanceRaggedLines/json2.jsx new file mode 100644 index 0000000..397349b --- /dev/null +++ b/SampleScripts/balanceRaggedLines/json2.jsx @@ -0,0 +1,530 @@ +// json2.js +// 2017-06-12 +// Public Domain. +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + +// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO +// NOT CONTROL. + +// This file creates a global JSON object containing two methods: stringify +// and parse. This file provides the ES5 JSON capability to ES3 systems. +// If a project might run on IE8 or earlier, then this file should be included. +// This file does nothing on ES5 systems. + +// JSON.stringify(value, replacer, space) +// value any JavaScript value, usually an object or array. +// replacer an optional parameter that determines how object +// values are stringified for objects. It can be a +// function or an array of strings. +// space an optional parameter that specifies the indentation +// of nested structures. If it is omitted, the text will +// be packed without extra whitespace. If it is a number, +// it will specify the number of spaces to indent at each +// level. If it is a string (such as "\t" or " "), +// it contains the characters used to indent at each level. +// This method produces a JSON text from a JavaScript value. +// When an object value is found, if the object contains a toJSON +// method, its toJSON method will be called and the result will be +// stringified. A toJSON method does not serialize: it returns the +// value represented by the name/value pair that should be serialized, +// or undefined if nothing should be serialized. The toJSON method +// will be passed the key associated with the value, and this will be +// bound to the value. + +// For example, this would serialize Dates as ISO strings. + +// Date.prototype.toJSON = function (key) { +// function f(n) { +// // Format integers to have at least two digits. +// return (n < 10) +// ? "0" + n +// : n; +// } +// return this.getUTCFullYear() + "-" + +// f(this.getUTCMonth() + 1) + "-" + +// f(this.getUTCDate()) + "T" + +// f(this.getUTCHours()) + ":" + +// f(this.getUTCMinutes()) + ":" + +// f(this.getUTCSeconds()) + "Z"; +// }; + +// You can provide an optional replacer method. It will be passed the +// key and value of each member, with this bound to the containing +// object. The value that is returned from your method will be +// serialized. If your method returns undefined, then the member will +// be excluded from the serialization. + +// If the replacer parameter is an array of strings, then it will be +// used to select the members to be serialized. It filters the results +// such that only members with keys listed in the replacer array are +// stringified. + +// Values that do not have JSON representations, such as undefined or +// functions, will not be serialized. Such values in objects will be +// dropped; in arrays they will be replaced with null. You can use +// a replacer function to replace those with JSON values. + +// JSON.stringify(undefined) returns undefined. + +// The optional space parameter produces a stringification of the +// value that is filled with line breaks and indentation to make it +// easier to read. + +// If the space parameter is a non-empty string, then that string will +// be used for indentation. If the space parameter is a number, then +// the indentation will be that many spaces. + +// Example: + +// text = JSON.stringify(["e", {pluribus: "unum"}]); +// // text is '["e",{"pluribus":"unum"}]' + +// text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); +// // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + +// text = JSON.stringify([new Date()], function (key, value) { +// return this[key] instanceof Date +// ? "Date(" + this[key] + ")" +// : value; +// }); +// // text is '["Date(---current time---)"]' + +// JSON.parse(text, reviver) +// This method parses a JSON text to produce an object or array. +// It can throw a SyntaxError exception. + +// The optional reviver parameter is a function that can filter and +// transform the results. It receives each of the keys and values, +// and its return value is used instead of the original value. +// If it returns what it received, then the structure is not modified. +// If it returns undefined then the member is deleted. + +// Example: + +// // Parse the text. Values that look like ISO date strings will +// // be converted to Date objects. + +// myData = JSON.parse(text, function (key, value) { +// var a; +// if (typeof value === "string") { +// a = +// /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); +// if (a) { +// return new Date(Date.UTC( +// +a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6] +// )); +// } +// return value; +// } +// }); + +// myData = JSON.parse( +// "[\"Date(09/09/2001)\"]", +// function (key, value) { +// var d; +// if ( +// typeof value === "string" +// && value.slice(0, 5) === "Date(" +// && value.slice(-1) === ")" +// ) { +// d = new Date(value.slice(5, -1)); +// if (d) { +// return d; +// } +// } +// return value; +// } +// ); + +// This is a reference implementation. You are free to copy, modify, or +// redistribute. + +/*jslint + eval, for, this +*/ + +/*property + JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (typeof JSON !== "object") { + JSON = {}; +} + +(function () { + "use strict"; + + var rx_one = /^[\],:{}\s]*$/; + var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; + var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; + var rx_four = /(?:^|:|,)(?:\s*\[)+/g; + var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + + function f(n) { + // Format integers to have at least two digits. + return (n < 10) + ? "0" + n + : n; + } + + function this_value() { + return this.valueOf(); + } + + if (typeof Date.prototype.toJSON !== "function") { + + Date.prototype.toJSON = function () { + + return isFinite(this.valueOf()) + ? ( + this.getUTCFullYear() + + "-" + + f(this.getUTCMonth() + 1) + + "-" + + f(this.getUTCDate()) + + "T" + + f(this.getUTCHours()) + + ":" + + f(this.getUTCMinutes()) + + ":" + + f(this.getUTCSeconds()) + + "Z" + ) + : null; + }; + + Boolean.prototype.toJSON = this_value; + Number.prototype.toJSON = this_value; + String.prototype.toJSON = this_value; + } + + var gap; + var indent; + var meta; + var rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + rx_escapable.lastIndex = 0; + return rx_escapable.test(string) + ? "\"" + string.replace(rx_escapable, function (a) { + var c = meta[a]; + return typeof c === "string" + ? c + : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }) + "\"" + : "\"" + string + "\""; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i; // The loop counter. + var k; // The member key. + var v; // The member value. + var length; + var mind = gap; + var partial; + var value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if ( + value + && typeof value === "object" + && typeof value.toJSON === "function" + ) { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === "function") { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case "string": + return quote(value); + + case "number": + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return (isFinite(value)) + ? String(value) + : "null"; + + case "boolean": + case "null": + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce "null". The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is "object", we might be dealing with an object or an array or +// null. + + case "object": + +// Due to a specification blunder in ECMAScript, typeof null is "object", +// so watch out for that case. + + if (!value) { + return "null"; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === "[object Array]") { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || "null"; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 + ? "[]" + : gap + ? ( + "[\n" + + gap + + partial.join(",\n" + gap) + + "\n" + + mind + + "]" + ) + : "[" + partial.join(",") + "]"; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === "object") { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === "string") { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + (gap) + ? ": " + : ":" + ) + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + (gap) + ? ": " + : ":" + ) + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 + ? "{}" + : gap + ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" + : "{" + partial.join(",") + "}"; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== "function") { + meta = { // table of character substitutions + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + "\"": "\\\"", + "\\": "\\\\" + }; + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ""; + indent = ""; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === "number") { + for (i = 0; i < space; i += 1) { + indent += " "; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === "string") { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== "function" && ( + typeof replacer !== "object" + || typeof replacer.length !== "number" + )) { + throw new Error("JSON.stringify"); + } + +// Make a fake root object containing our value under the key of "". +// Return the result of stringifying the value. + + return str("", {"": value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== "function") { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k; + var v; + var value = holder[key]; + if (value && typeof value === "object") { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + rx_dangerous.lastIndex = 0; + if (rx_dangerous.test(text)) { + text = text.replace(rx_dangerous, function (a) { + return ( + "\\u" + + ("0000" + a.charCodeAt(0).toString(16)).slice(-4) + ); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with "()" and "new" +// because they can cause invocation, and "=" because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we +// replace all simple value tokens with "]" characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or "]" or +// "," or ":" or "{" or "}". If that is so, then the text is safe for eval. + + if ( + rx_one.test( + text + .replace(rx_two, "@") + .replace(rx_three, "]") + .replace(rx_four, "") + ) + ) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The "{" operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval("(" + text + ")"); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return (typeof reviver === "function") + ? walk({"": j}, "") + : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError("JSON.parse"); + }; + } +}()); \ No newline at end of file diff --git a/SampleScripts/balanceRaggedLines/manifest.json b/SampleScripts/balanceRaggedLines/manifest.json new file mode 100644 index 0000000..d9fdb08 --- /dev/null +++ b/SampleScripts/balanceRaggedLines/manifest.json @@ -0,0 +1,17 @@ +{ + "manifestVersion": "1.0.0", + "name": "balanceRaggedLines", + "host": { + "app": "indesign", + "maxVersion": "20.2.0", + "minVersion": "16.0.1" + }, + "version": "1.0.3", + "apiEntryPoints": [ + { + "path": "balanceRaggedLines.jsx", + "type": "capability", + "language": "extendscript" + } + ] +} \ No newline at end of file diff --git a/SampleScripts/balanceRaggedLines/utils.jsx b/SampleScripts/balanceRaggedLines/utils.jsx new file mode 100644 index 0000000..ad08214 --- /dev/null +++ b/SampleScripts/balanceRaggedLines/utils.jsx @@ -0,0 +1,702 @@ +/************************************************************************* +* ADOBE CONFIDENTIAL +* ___________________ +* +* Copyright 2021 Adobe +* All Rights Reserved. +* +* NOTICE: All information contained herein is, and remains +* the property of Adobe and its suppliers, if any. The intellectual +* and technical concepts contained herein are proprietary to Adobe +* and its suppliers and are protected by all applicable intellectual +* property laws, including trade secret and copyright laws. +* Dissemination of this information or reproduction of this material +* is strictly forbidden unless prior written permission is obtained +* from Adobe. +**************************************************************************/ +// utils.jsx + +// Utils functions which can be used across. +/* globals app, Errors, File, Folder, LinkStatus, SaveOptions */ + +// Globals. +var logFileObject + +// eslint-disable-next-line no-extend-native +Array.prototype.indexOf = function (item) { + var index = 0 + var length = this.length + for (; index < length; index++) { + if (this[index] === item) { + return index + } + } + return -1 +} + +// eslint-disable-next-line no-extend-native +if (typeof Array.isArray === 'undefined') { + Array.isArray = function (obj) { + return Object.prototype.toString.call(obj) === '[object Array]' + } +} + +// Create a UTILS object to store utility functions and variables. +var UTILS = {} + +// Initialize various properties of the UTILS object. +UTILS.Logs = [] // Stores log messages. +UTILS.workingFolder = '' // Path of the working folder. +UTILS.outputFolder = '' // Path of the output folder. +UTILS.assetsToBeUploaded = [] // List of assets that need to be uploaded. +UTILS.createSeparateLogFile = true // Flag to determine if a separate log file should be created. +UTILS.logFilePath = 'LogFile.txt' // File name for logging. + +// Converts the package in appropriate format, which can be returned from the capability. +UTILS.GetFinalReturnPackage = function (obj) { + UTILS.Log('Final package created') + return JSON.stringify(obj) +} + +// Creates and returns the package to be returned in case the job is successful. +UTILS.GetSuccessReturnObj = function (data) { + var obj = {} + obj.status = 'SUCCESS' + obj.assetsToBeUploaded = UTILS.assetsToBeUploaded // Attach the list of assets to be uploaded. + var dataURL = UTILS.WriteToFile(data, 'outputData') // Save data to file. + if (dataURL) { + obj.dataURL = dataURL + } + return obj +} + +// Creates and returns the package to be returned in case the job has failed. +UTILS.GetFailureReturnObj = function (errorCode, errorString, data) { + var obj = {} + obj.status = 'FAILURE' + obj.errorCode = errorCode // Attach the error code. + obj.errorString = errorString // Attach the error message. + obj.data = data // Include additional data if available. + return obj +} + +// Add an asset which is to be uploaded and sent back to the caller. +UTILS.AddAssetToBeUploaded = function (assetPath, data) { + var assetToBeUploaded = {} + assetToBeUploaded.path = assetPath + if (data !== undefined) { + assetToBeUploaded.data = data // Include additional asset data if provided. + } + UTILS.assetsToBeUploaded.push(assetToBeUploaded) +} + +// This handles errors in case an exception is raised. This results the capability returning error to the caller. +UTILS.HandleError = function (exception) { + var errorCode, errorString + UTILS.Log('Exception occurred: ' + JSON.stringify(exception)) + + // Handle specific exception cases + if (exception.message === 'open') { + exception.message = Errors.CannotOpenFileError.errorStrings[0] + exception.errorCode = Errors.CannotOpenFileError.errorCode + } + + errorString = 'Script error: ' + if (exception.isCustom === true) { + if (exception.message) { + errorString = errorString + exception.message + } + errorCode = exception.errorCode + } else { + errorCode = Errors.ProcessingErrorOccurred.errorCode + if (exception.message) { + errorString = errorString + Errors.ProcessingErrorOccurred.errorStrings[0] + ' ' + exception.message + } + if (exception.errorCode !== undefined) { + errorString = errorString + '\nInternal error code: ' + exception.errorCode + '.' + } + if (exception.line !== undefined) { + errorString = errorString + ' Line: ' + exception.line + '.' + } + if (exception.fileName !== undefined) { + errorString = errorString + ' FileName: ' + exception.fileName + '.' + } + UTILS.Log('Processing error occurred. ' + errorString) + } + + return UTILS.GetFailureReturnObj(errorCode, errorString) +} + +// This is used to raise an exception with all the required details. +// NOTE: This takes variable list of argument and then try to fill in the details in errorObj.errorStrings +UTILS.RaiseException = function (errorObj) { + UTILS.Log('RaiseException()') + var numMessageParameters = arguments.length + UTILS.Log('', 'numMessageParameters: ' + numMessageParameters) + var numErrorStrings = errorObj.errorStrings.length + UTILS.Log('', 'numErrorStrings: ' + numErrorStrings) + var numIterations = (numMessageParameters > numErrorStrings) ? numErrorStrings : numMessageParameters + var errorMessage = errorObj.errorStrings[0] + UTILS.Log('', 'Default errorMessage: ' + errorMessage) + + // Construct the detailed error message using available parameters. + for (var itr = 1; itr < numIterations; itr++) { + var parameter = arguments[itr] + if (parameter !== undefined && parameter !== '') { + var parameterMessage = errorObj.errorStrings[itr] + errorMessage = errorMessage + ' ' + parameterMessage.replace('^1', parameter) + UTILS.Log('', 'Appended errorMessage: ' + errorMessage) + } + } + + // Throw an exception object with custom details. + var exceptionObj = { + number: errorObj.errorCode, + isCustom: true, + message: errorMessage + } + + throw exceptionObj +} + +// This will update out-of-date links in a document. +UTILS.UpdateDocumentLinks = function (document) { + var links = document.links + var numLinks = links.length + var linkItr, link, uri + var outOfDateLinks = [] + + UTILS.Log('Number of links: ' + numLinks) + for (linkItr = 0; linkItr < numLinks; linkItr++) { + try { + link = links[linkItr] + uri = link.linkResourceURI + UTILS.Log(linkItr + ': URI: ' + uri) + if (link.status === LinkStatus.LINK_OUT_OF_DATE) { + outOfDateLinks.push(link.id) + } + } catch (err) { + UTILS.Log('Link status unknown : ' + err) + } + } + + // Update all out-of-date links. + numLinks = outOfDateLinks.length + for (linkItr = 0; linkItr < numLinks; linkItr++) { + link = document.links.itemByID(outOfDateLinks[linkItr]) + if (link.isValid) { + link.update() + } + } + + // Recompose the document after updating links. + var composeStartTime = new Date().getTime() + document.recompose() + var composedTime = (new Date()).getTime() - composeStartTime + UTILS.Log('document.recompose Time: ' + composedTime) +} + +// This Embeds all the links in the document. +UTILS.EmbedDocumentLinks = function (document) { + var links = document.links + var numLinks = links.length + var linkItr, link + + for (linkItr = 0; linkItr < numLinks; linkItr++) { + try { + link = links[linkItr] + if (link.status === LinkStatus.NORMAL) { + link.unlink() + } + } catch (err) { + UTILS.Log('Unable to embed link: status is unknown : ' + err) + } + } +} + +// Trims leading and trailing spaces from a string. +UTILS.Trim = function (val) { + return val.replace(/^\s+|\s+$/gm, '') +} + +// Removes all the spaces from a string. +UTILS.RemoveAllSpaces = function (val) { + return val.replace(/\s/g, '') +} + +// Converts a value to integer. Exception is thrown if the conversion fails. +UTILS.GetBoolean = function (val) { + if (val === 'true' || val === true) { + return true + } else if (val === 'false' || val === false) { + return false + } + + // throw exception. + UTILS.Log('GetBoolean() val: ' + val) + UTILS.RaiseException(Errors.ParsingBoolError, val) +} + +// Gets a boolean from an 'object' defined with 'key'. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the conversion of the provided value fails. +UTILS.GetBooleanFromObject = function (object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetBooleanFromObject ' + key) + + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + if (val === 'true' || val === true) { + return true + } else if (val === 'false' || val === false) { + return false + } + + UTILS.Log('GetBooleanFromObject() val: ' + val) + UTILS.RaiseException(Errors.ParsingBoolError, val, key) + } else if (defaultVal === undefined) { + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Converts a value to integer. Exception is thrown if the conversion fails. +UTILS.GetInteger = function (val) { + var returnVal = parseInt(val) + if (!isNaN(returnVal)) { + return returnVal + } + + UTILS.Log('GetInteger() val: ' + val) + UTILS.RaiseException(Errors.ParsingIntError, val) +} + +// Gets a integer from an 'object' defined with 'key'. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the conversion of the provided value fails. +UTILS.GetIntegerFromObject = function (object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetIntegerFromObject ' + key) + + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + var returnVal = parseInt(val) + if (!isNaN(returnVal)) { + return returnVal + } + + UTILS.Log('GetIntegerFromObject() val: ' + val) + UTILS.RaiseException(Errors.ParsingIntError, val, key) + } else if (defaultVal === undefined) { + UTILS.Log('Missing key: ' + key) + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Converts a value to float. Exception is thrown if the conversion fails. +UTILS.GetFloat = function (val) { + var returnVal = parseFloat(val) + if (!isNaN(returnVal)) { + return returnVal + } + + UTILS.Log('GetFloat() val: ' + val) + UTILS.RaiseException(Errors.ParsingFloatError, val) +} + +// Gets a float from an 'object' defined with 'key'. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the conversion of the provided value fails. +UTILS.GetFloatFromObject = function (object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetFloatFromObject ' + key) + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + var returnVal = parseFloat(val) + if (!isNaN(returnVal)) { + return returnVal + } + + UTILS.Log('GetFloatFromObject() val: ' + val) + UTILS.RaiseException(Errors.ParsingFloatError, val, key) + } else if (defaultVal === undefined) { + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Validates a value to be one of the enum definitions. +UTILS.GetEnum = function (enumDef, val) { + try { + val = UTILS.Trim(val) + return UTILS.GetValueFromObject(enumDef, val) + } catch (e) { + UTILS.RaiseException(Errors.EnumError, val) + } +} + +// Gets a enum value from an 'object' defined with 'key' and after comparing against an enum definition. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the value is not present in enum definition. +UTILS.GetEnumFromObject = function (enumDef, object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetEnumFromObject ' + key) + + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + try { + val = UTILS.Trim(val) + if (Array.isArray(enumDef) === false) { + return UTILS.GetValueFromObject(enumDef, val, ignoreCase) + } else { + if (enumDef.indexOf(val) >= 0) { + return val + } + } + } catch (e) { + UTILS.Log('GetEnumFromObject() val: ' + val) + UTILS.RaiseException(Errors.EnumError, val, key) + } + + UTILS.Log('GetEnumFromObject() val: ' + val) + UTILS.RaiseException(Errors.EnumError, val, key) + } else if (defaultVal === undefined) { + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Gets a string from an 'object' defined with 'key'. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the value found is not string. +UTILS.GetStringFromObject = function (object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetStringFromObject ' + key) + + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + if (typeof val !== 'string') { + UTILS.RaiseException(Errors.ParsingStringError, val, key) + } else { + return val + } + } else if (defaultVal === undefined) { + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Gets an array from an 'object' defined with 'key'. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the value found is not an array. +UTILS.GetArrayFromObject = function (object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetArrayFromObject ' + key) + + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + if (Array.isArray(val) === false) { + UTILS.RaiseException(Errors.ArrayExpected, key) + } else { + return val + } + } else if (defaultVal === undefined) { + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Gets an object from an 'object' defined with 'key'. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the value found is not an object. +UTILS.GetObjectFromObject = function (object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetObjectFromObject ' + key) + + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + if (typeof val !== 'object' || Array.isArray(val)) { + UTILS.RaiseException(Errors.ObjectExpected, key) + } else { + return val + } + } else if (defaultVal === undefined) { + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Gets the value from an 'object' defined with 'key'. +// The case of key can be ignored via 'ignoreCase'. In case, key is missing an exception will be thrown. +UTILS.GetValueFromObject = function (object, key, ignoreCase) { + UTILS.Log('GetValueFromObject ' + key) + + if (ignoreCase === true) { + var objectKey + for (objectKey in object) { + if (Object.prototype.hasOwnProperty.call(object, objectKey)) { + if (objectKey.toLowerCase() === key.toLowerCase()) { + return object[objectKey] + } + } + } + } else if (Object.prototype.hasOwnProperty.call(object, key)) { + return object[key] + } + + UTILS.RaiseException(Errors.MissingKey, key) +} + +// Checks whether a key is present in the object or not. +UTILS.IsKeyPresent = function (object, key) { + if (Object.prototype.hasOwnProperty.call(object, key)) { + return true + } + return false +} + +// Sets the working folder to be used across the capability. +UTILS.SetWorkingFolder = function (workingFolder) { + workingFolder = workingFolder.replace(/\//g, '\\') + if (workingFolder.charAt(workingFolder.length - 1) !== '\\') { + workingFolder = workingFolder + '\\' + } + UTILS.workingFolder = workingFolder + UTILS.OpenLogFileHandle() +} + +// Gets the path of the directory for a given path. +UTILS.GetDirPath = function (path) { + path = path.replace(/\//g, '\\') + var indexOfSlash = path && path.lastIndexOf('\\') + if (indexOfSlash >= 0) { + return path.substr(0, indexOfSlash) + } + return '' +} + +// Gets the path of the directory for a given path. If 'withExtension is true the file name consists of the file extension and not otherwise. +UTILS.GetFileName = function (path, withExtension) { + var fileName + var indexOfSlash = path.lastIndexOf('\\') + 1 + if (indexOfSlash >= 1) { + fileName = path.substr(indexOfSlash) + if (withExtension === false) { + var indexOfDot = path.lastIndexOf('.') + fileName = path.substr(indexOfSlash, indexOfDot - indexOfSlash) + } + } else { + fileName = path + } + return fileName +} + +// Get relative path to the working directory for a path. +UTILS.GetRelativePath = function (path) { + var index = path.indexOf(UTILS.workingFolder) + if (index === 0) { + return path.substr(UTILS.workingFolder.length) + } + return path +} + +// Get relative path to the working directory for a path. This returns in unix notation. +UTILS.GetRelativeReturnPath = function (path) { + var index = path.indexOf(UTILS.workingFolder) + if (index === 0) { + var tempPath = path.substr(UTILS.workingFolder.length) + tempPath = tempPath.replace(/\\/g, '/') + return tempPath + } +} + +// Get the full path from a relative and a base path. In absence of base path, working directory is used. +UTILS.GetFullPath = function (relativePath, basePath) { + var origRelativePath = relativePath + if (relativePath.indexOf('..') !== -1 || relativePath.indexOf('/') === 0) { + this.RaiseException(Errors.InCorrectRelativePath, origRelativePath) + } + relativePath = relativePath.replace(/\//g, '\\') + if (relativePath.indexOf('\\.\\') !== -1) { + this.RaiseException(Errors.InCorrectRelativePath, origRelativePath) + } + + if (basePath === undefined) { + basePath = UTILS.workingFolder + } else { + basePath = basePath.replace(/\//g, '\\') + if (basePath.charAt(basePath.length - 1) !== '\\') { + basePath = basePath + '\\' + } + } + + if (relativePath.charAt(0) === '.') { + relativePath = relativePath.slice(1) + } + if (relativePath.charAt(0) === '\\') { + relativePath = relativePath.slice(1) + } + + var fullPath = basePath + relativePath + return fullPath +} + +// Get a unique name. +UTILS.GetUniqueName = function () { + return Math.random().toString().substr(2, 6) +} + +// Creates a directory for the path provided. +UTILS.CreateDirectory = function (path) { + var outputFolder = Folder(path) + return outputFolder.create() +} + +// Creates a directory for the outputs on the basis of relative path passed. In case nothing is passed a random directory is created in working folder. +UTILS.GetOutputDirectory = function (outputFolderPath) { + var outputPath = '' + var created = false + if (outputFolderPath) { + outputPath = UTILS.GetFullPath(outputFolderPath) + created = UTILS.CreateDirectory(outputPath) + var outputFolder = Folder(outputPath) + if (outputFolder.exists === false) { + UTILS.RaiseException(Errors.OutputDirError) + } + } else { + var tempName = '' + do { + tempName = 'tmp' + UTILS.GetUniqueName() + outputPath = UTILS.GetFullPath(tempName) + created = UTILS.CreateDirectory(outputPath) + } while (!created) + } + UTILS.outputFolder = outputPath + return outputPath +} + +// Setup to do the logging. +UTILS.InitiateLogging = function () { + UTILS.Logs = [] +} + + +// Opens a log file handle if separate logging is enabled. +UTILS.OpenLogFileHandle = function () { + if (UTILS.createSeparateLogFile === true) { + logFileObject = new File(UTILS.GetFullPath(UTILS.logFilePath)) + var exists = false + + // Check if the log file already exists + if (logFileObject.open('read')) { + exists = true + } + UTILS.Log('Creating log file at ' + UTILS.GetFullPath(UTILS.logFilePath)) + + // Open the log file in append mode if it exists, otherwise create a new file + if (exists) { + logFileObject.close() + logFileObject.open('append') + } else { + logFileObject.open('write') + } + } +} + +// Ends logging by closing the log file handle if separate logging is enabled +UTILS.TerminateLogging = function () { + if (UTILS.createSeparateLogFile === true) { + logFileObject.close() + } +} + +// This logs any provided information +UTILS.Log = function (log) { + var logText + + // Convert non-string logs to JSON format + if (log === undefined) { + logText = '' + } else if (typeof primaryLog !== 'string') { + logText = JSON.stringify(log) + } else { + logText = log + } + + if (UTILS.createSeparateLogFile === true) { + + // Write to file if log file object exists + if (logFileObject) { + if (UTILS.Logs.length > 0) { + for (var itr = 0; itr < UTILS.Logs.length; itr++) { + logFileObject.writeln(UTILS.Logs[itr]) + } + UTILS.Logs = [] + } + logFileObject.writeln(logText) + } else { + UTILS.Logs.push(logText) + } + } else { + UTILS.Logs.push(logText) + } +} + +// Writes data to a uniquely named JSON file +UTILS.WriteToFile = function (data, fileName) { + var fileURL, newFile + var exists = false + var suffix = '' + var counter = 1 + + // Generate a unique filename if none is provided + if (fileName === undefined) { + fileName = UTILS.GetUniqueName() + } + + // Ensure the file does not already exist + do { + fileURL = UTILS.GetFullPath(fileName + suffix + '.json') + newFile = File(fileURL) + if (newFile.open('read')) { + exists = true + suffix = counter++ + newFile.close() + } else { + exists = false + } + } while (exists) + + // Write data to the file + newFile.encoding = 'UTF8' + newFile.open('write') + if (newFile.write(JSON.stringify(data))) { + UTILS.Log('Data was successfully written') + newFile.close() + return (fileName + suffix + '.json') + } else { + UTILS.Log('Data write failed') + newFile.close() + } +} + +// Closes all open documents in the application. +UTILS.CloseAllOpenDocuments = function () { + UTILS.Log('Closing all the documents') + app.documents.everyItem().close(SaveOptions.NO) +} diff --git a/SampleScripts/hyphenation/README.md b/SampleScripts/hyphenation/README.md new file mode 100644 index 0000000..09fe429 --- /dev/null +++ b/SampleScripts/hyphenation/README.md @@ -0,0 +1,63 @@ +# Hyphenate Capability + +This project provides a script (`hyphenate.jsx`) and its corresponding manifest (`manifest.json`) to enable or disable hyphenation in text frames within Adobe InDesign documents using ExtendScript. + +## Features + +- Opens an InDesign document from a specified path. +- Identifies text frames based on provided labels. +- Hyphenates or removes Hyphenation for paragraphs in the identified text frames. +- Logs processing steps and performance metrics. +- Handles errors and warnings during the process. + +## File Structure + +- **`hyphenate.jsx`**: The main script that implements the balancing functionality. +- **`manifest.json`**: The manifest file that defines the capability and its integration with Adobe InDesign. + +## Execution + +1. Place the `hyphenate.jsx` script and `manifest.json` in the appropriate folder parallelly. +2. Create a capability bundle, which is a ZIP file with a specific structure. The files should be zipped directly without a parent folder. +``` +Archive.zip +|------ manifest.json +|------ hyphenate.jsx +``` +3. Follow this [guide](https://developer.adobe.com/firefly-services/docs/indesign-apis/how-tos/working-with-capabilities-api/) to register your script. + +4. Provide the required parameters to run the script: + +```json +{ + "assets": [ + { + "source": { + "type": "HTTP_GET", + "url": "" + }, + "destination": "sample.indd" + } + ], + "params": { + "targetDocument": "sample.indd", + "outputPath": "hyphenate.indd", + "labels": ["MainPara","Heading"], // If no Labels are provided, this script will add or remove hyphenation in all of the text frames in the document. + "hyphenate": "true" //Can be "hyphenate":false as well. + } +} +``` + +## Logging +The script logs processing steps and performance metrics to a log file (LogFile.txt) in the working directory. Logs include: + +- Time taken to open the document. +- Time taken to update links. +- Time taken to add/remove hyphenation + +## Error Handling +The script captures and logs errors during execution. Common errors include: + +- Missing or invalid document paths. +- Issues with updating links. +- Processing failures. diff --git a/SampleScripts/hyphenation/errors.jsx b/SampleScripts/hyphenation/errors.jsx new file mode 100644 index 0000000..21b34b9 --- /dev/null +++ b/SampleScripts/hyphenation/errors.jsx @@ -0,0 +1,160 @@ +/************************************************************************* +* ADOBE CONFIDENTIAL +* ___________________ +* +* Copyright 2021 Adobe +* All Rights Reserved. +* +* NOTICE: All information contained herein is, and remains +* the property of Adobe and its suppliers, if any. The intellectual +* and technical concepts contained herein are proprietary to Adobe +* and its suppliers and are protected by all applicable intellectual +* property laws, including trade secret and copyright laws. +* Dissemination of this information or reproduction of this material +* is strictly forbidden unless prior written permission is obtained +* from Adobe. +**************************************************************************/ +// errors.jsx + +// Error constants are defined here. +/* This document list all the error which are possible from the scripts. The error object is built such that it has an error code and some error strings. + The error strings are constructed in a way such that the first string is default and a string literal. It is to be returned anyhow. The subsequent strings can be + strings having '^1' as placeholder replacement string. This replacement string can be replaced with relevant information. Based on the information + available the final error message can be created. +*/ +/* eslint-disable no-unused-vars */ + +var ErrorReplacementString = '^1' + +var Errors = { + // Errors during processing: 1001-1999 + InternalScriptError: { + errorCode: 'internal_error', + errorStrings: [ + 'Internal script error. This should not had happened.' + ] + }, + ProcessingErrorOccurred: { + errorCode: 'internal_error', + errorStrings: [ + 'Internal error: Error during processing.' + ] + }, + PDFPresetNotSet: { + errorCode: 'capability_error', + errorStrings: [ + 'Capability error: No PDF Preset could be set.' + ] + }, + OutputDirError: { + errorCode: 'internal_error', + errorStrings: [ + 'Internal error: Unable to create specified output directory.' + ] + }, + RelinkError: { + errorCode: 'capability_error', + errorStrings: [ + 'Capability error: Unable to relink.', + 'Relink failed for ^1.' + ] + }, + PlaceError: { + errorCode: 'capability_error', + errorStrings: [ + 'Capability error: Unable to place.', + 'Place failed for ^1.' + ] + }, + + // Errors in input: 2001-2999 + ArrayExpected: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected array ', + 'for property ^1.' + ] + }, + ParsingBoolError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected boolean ', + '^1 is the provided value ', + 'for property ^1.' + ] + }, + ParsingIntError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected integer ', + '^1 is the provided value ', + 'for property ^1.' + ] + }, + ParsingFloatError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected number ', + '^1 is the provided value ', + 'for property ^1.' + ] + }, + ParsingStringError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected string ', + '^1 is the provided value ', + 'for property ^1.' + ] + }, + OutOfBound: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Incorrect range provided. ', + 'Culprit Value is ^1.' + ] + }, + MissingKey: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Key not found in object. ', + 'Missing key is ^1.' + ] + }, + EnumError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: No matching enum value found. ', + '^1 is the provided value', + 'for property ^1.' + ] + }, + MissingParams: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Either the \'params\' is not found in the request or it is not in the correct format.' + ] + }, + ObjectExpected: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected object ', + 'for property ^1.' + ] + }, + AtLeastOneInternalParamShouldBePresent: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: At least one of the parameters should be present. ', + 'Missing parameters are ^1' + ] + }, + + // Errors coming from IDS and sent under ProcessingErrorOccurred. These are specific errors which need string change while returning to the user. + CannotOpenFileError: { + errorCode: 'capability_error', // kCannotOpenFileError + errorStrings: [ + 'Capability error: Cannot open file.' + ] + } +} diff --git a/SampleScripts/hyphenation/hyphenate.jsx b/SampleScripts/hyphenation/hyphenate.jsx new file mode 100644 index 0000000..f6fdfac --- /dev/null +++ b/SampleScripts/hyphenation/hyphenate.jsx @@ -0,0 +1,325 @@ +/************************************************************************* +* ADOBE CONFIDENTIAL +* ___________________ +* +* Copyright 2021 Adobe +* All Rights Reserved. +* +* NOTICE: All information contained herein is, and remains +* the property of Adobe and its suppliers, if any. The intellectual +* and technical concepts contained herein are proprietary to Adobe +* and its suppliers and are protected by all applicable intellectual +* property laws, including trade secret and copyright laws. +* Dissemination of this information or reproduction of this material +* is strictly forbidden unless prior written permission is obtained +* from Adobe. +**************************************************************************/ + + +// Following are the file inclusions and not comments. +// @include "errors.jsx" +// @include "json2.jsx" +// @include "utils.jsx" +// File inclusion ends. + +// Globals: define any globals here. +var timingObject = {} +var warnings = {} +var document +// Globals end. + +// Constant strings for key references +var KeyStrings = { + OutputPath: 'outputPath', + ProcessedData: 'processedData', + TargetDocument: 'targetDocument', + TimeDocumentClose: 'DocumentClose', + TimeDocumentOpen: 'DocumentOpen', + TimeBalancing: 'TimeBalanceRagged', + TimeOverall: 'Overall', + TimeRelinkAssets: 'RelinkAssets', + Timings: 'timings', + WorkingFolder: 'workingFolder' +} + +/* Handles document warnings such as missing links or fonts */ +function handleWarnings (document) { + UTILS.Log('Handling warnings') + var errorlog = app.errorListErrors + var warningsObject = {} + + var missingLinkArray = [] + var missingFontArray = [] + var failedErrorsArray = [] + var otherWarningsArray = [] + var missingLinkStr = '(Link missing.; ' + var missingLinkStrLen = missingLinkStr.length + + // Iterate through error list and categorize warnings + for (var i = 0; i < errorlog.count(); i++) { + var error = errorlog[i] + UTILS.Log('Warning No. ' + (i + 1) + ': ' + error.listErrorCode + '(' + error.listErrorMessage + ')') + if (error.listErrorCode === 35842) { // Missing link error + var missingLink = error.listErrorMessage + missingLink = missingLink.substring(missingLink.indexOf(missingLinkStr) + missingLinkStrLen) + if (missingLinkArray.indexOf(missingLink) === -1) { + missingLinkArray.push(missingLink) + } + } else if (error.listErrorCode === 1 && error.listErrorMessage.search(/missing font/i) !== -1) { + var missingFont = error.listErrorMessage + missingFont = missingFont.substring(missingFont.search(/missing font/i) + 13) + if (missingFontArray.indexOf(missingFont) === -1) { + missingFontArray.push(missingFont) + } + } else if (error.listErrorCode === 1) { + otherWarningsArray.push(error.listErrorMessage) + } + } + + // Store collected warnings in an object + if (missingLinkArray.length > 0) { + warningsObject.missingLinks = missingLinkArray + } + if (missingFontArray.length > 0) { + warningsObject.missingFonts = missingFontArray + } + if (failedErrorsArray.length > 0) { + warningsObject.exportErrors = failedErrorsArray + } + if (otherWarningsArray.length > 0) { + warningsObject.otherWarnings = otherWarningsArray + } + return warningsObject +}; + +/* Hypenation for text frames within a document */ +function hyphenation (document,labels,hyphenate) { + + var allFrames = document.allPageItems; + var labelledFrames = [] + + UTILS.Log('Inside Hyphenate') + + // Convert labels to an object for faster lookups (Set is not available in ExtendScript) + var labelMap = {}; + if (labels && labels.length > 0) { + if (!(labels instanceof Array)) { + labels = [labels]; // Convert single label to an array + } + for (var i = 0; i < labels.length; i++) { + labelMap[labels[i]] = true; + } + + // Iterate over frames and find those matching the labels + for (var i = 0; i < allFrames.length; i++) { + var frame = allFrames[i]; + if (frame instanceof TextFrame && labelMap[frame.label]) { + labelledFrames.push(frame); + } + } + } else { + // If no labels provided, select all text frames + for (var i = 0; i < allFrames.length; i++) { + if (allFrames[i] instanceof TextFrame) { + labelledFrames.push(allFrames[i]); + } + } + } + UTILS.Log('Labels Extracted ' + labelledFrames.length) + + + // Hyphenation for all paragraphs in the labelled frames + if(labelledFrames.length == 0){ + UTILS.Log('No frames found') + return + } + // Hyphenation for all paragraphs in the labelled frames + for (var i = 0; i < labelledFrames.length; i++) { + var textFrame = labelledFrames[i]; + var paragraphs = textFrame.paragraphs; + for (var j = 0; j < paragraphs.length; j++) { + var para = paragraphs[j]; + if(hyphenate===true){ + para.hyphenation = true; + } + else{ + para.hyphenation = false + } + + } + } + document.save(); +}; + +/* Processes parameters and performs document operations */ +function ProcessParams (parameters) { + UTILS.Log('Processing parameters internal') + var returnVal = {} + + // Open the target document + var documentPath = UTILS.GetStringFromObject(parameters, KeyStrings.TargetDocument) + documentPath = UTILS.GetFullPath(documentPath) + + UTILS.Log('Opening document') + var tempTime = new Date().getTime() + document = app.open(File(documentPath)) + timingObject[KeyStrings.TimeDocumentOpen] = (new Date()).getTime() - tempTime + UTILS.Log('Opened document') + + // Relink assets and handle warnings + tempTime = new Date().getTime() + UTILS.UpdateDocumentLinks(document) + timingObject[KeyStrings.TimeRelinkAssets] = (new Date()).getTime() - tempTime + UTILS.Log('Updated links in the document') + + // Capturing top level errors and then clear errorList. + UTILS.Log('Number of errors: ' + app.errorListErrors.count()) + if (app.errorListErrors.count() > 0) { + warnings = handleWarnings(document) + } + app.clearAllErrors() + UTILS.Log('Got the warnings from the document.') + + var outputPath = UTILS.GetStringFromObject(parameters, KeyStrings.OutputPath) + outputPath = UTILS.GetFullPath(outputPath) + + tempTime = new Date().getTime() + UTILS.UpdateDocumentLinks(document) + + var labels = parameters.labels + var hyphenate = parameters.hyphenate + + // Calling the function to hyphenate the text frames + hyphenation(document,labels,hyphenate); + + // Save the document as a new file. + document.save(File(outputPath)) + + timingObject[KeyStrings.TimeBalancing] = (new Date()).getTime() - tempTime + + // Add file to be uploaded as an output file. + var relativePath = UTILS.GetRelativeReturnPath(outputPath) + UTILS.AddAssetToBeUploaded(relativePath) + + returnVal.OutputPath = documentPath + // Processing ends. + + return returnVal +} + + +/* Main function to execute the script */ +function main () { + + var startTime = new Date().getTime() // Capture the script start time + var returnObj = {} // Object to store return values + var parameters = {} // Stores input parameters + var tempTime + var errorOccurred = false // Flag to track errors + var data = {} // Stores processed data + + // Set application preferences + app.clearAllErrors() + app.generalPreferences.pageNumbering = PageNumberingOptions.ABSOLUTE + app.linkingPreferences.checkLinksAtOpen = true + app.serverSettings.useErrorList = true + var previousUnit = app.scriptPreferences.measurementUnit + app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS + + try { + UTILS.InitiateLogging() + UTILS.Log('Initiating logging.') + + // As a safe practice close any open documents before processing + UTILS.CloseAllOpenDocuments() + + // Parse input parameters + var input = app.scriptArgs.get('parameters') + var allParameters = JSON.parse(input) + + UTILS.Log('Parsed job input : ' + input) + parameters = allParameters.params + if (parameters === undefined) { + parameters = allParameters.input.params + } + + // Check if parameters are valid + if (parameters === undefined || typeof parameters !== 'object' || Array.isArray(parameters)) { + UTILS.Log('No params found') + UTILS.RaiseException(Errors.MissingParams) + } + + // Set the working folder first. This is the directory within which all the input and output assets are to be managed. + UTILS.SetWorkingFolder(UTILS.GetStringFromObject(allParameters, KeyStrings.WorkingFolder)) + + var result + UTILS.Log('Processing Params') + tempTime = new Date().getTime() + + // Process parameters and document + result = ProcessParams(parameters) + data[KeyStrings.ProcessedData] = result + + // Also add the log file + UTILS.AddAssetToBeUploaded(UTILS.logFilePath) + + data.warnings = warnings + // Processing ends. + + tempTime = new Date().getTime() + if (document && document.isValid){ + document.close() + } + + timingObject[KeyStrings.TimeDocumentClose] = (new Date()).getTime() - tempTime + UTILS.Log('End of try') + } catch (e) { + var tempObj = { + name: e.name, + message: e.message, + errorCode: e.number, + isCustom: e.isCustom, + line: e.line, + fileName: e.fileName + } + + UTILS.Log('Exception occurred', tempObj) + errorOccurred = true + + // Failure, prepare the object to be returned. + returnObj = UTILS.HandleError(tempObj) + } finally { + app.scriptPreferences.measurementUnit = previousUnit + UTILS.Log('In finally') + if (document && document.isValid) { + + // If Document is still open. Close it. + UTILS.Log('Closing document') + tempTime = new Date().getTime() + document.close() + timingObject[KeyStrings.TimeDocumentClose] = (new Date()).getTime() - tempTime + } + + var elapsedTime = (new Date()).getTime() - startTime + UTILS.Log('Time taken: ' + elapsedTime) + timingObject[KeyStrings.TimeOverall] = elapsedTime + UTILS.Log('Timing: ' + JSON.stringify(timingObject)) + data[KeyStrings.Timings] = timingObject + + if (!errorOccurred) { + // Success, prepare the object to be returned. + UTILS.Log('Finally: No error') + returnObj = UTILS.GetSuccessReturnObj(data) + } + UTILS.Log('Final Result', JSON.stringify(returnObj)) + + // Cleanup and Return + UTILS.TerminateLogging() + app.clearAllErrors() + + return UTILS.GetFinalReturnPackage(returnObj) + } +} + +// Execute the main function +main() diff --git a/SampleScripts/hyphenation/json2.jsx b/SampleScripts/hyphenation/json2.jsx new file mode 100644 index 0000000..397349b --- /dev/null +++ b/SampleScripts/hyphenation/json2.jsx @@ -0,0 +1,530 @@ +// json2.js +// 2017-06-12 +// Public Domain. +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + +// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO +// NOT CONTROL. + +// This file creates a global JSON object containing two methods: stringify +// and parse. This file provides the ES5 JSON capability to ES3 systems. +// If a project might run on IE8 or earlier, then this file should be included. +// This file does nothing on ES5 systems. + +// JSON.stringify(value, replacer, space) +// value any JavaScript value, usually an object or array. +// replacer an optional parameter that determines how object +// values are stringified for objects. It can be a +// function or an array of strings. +// space an optional parameter that specifies the indentation +// of nested structures. If it is omitted, the text will +// be packed without extra whitespace. If it is a number, +// it will specify the number of spaces to indent at each +// level. If it is a string (such as "\t" or " "), +// it contains the characters used to indent at each level. +// This method produces a JSON text from a JavaScript value. +// When an object value is found, if the object contains a toJSON +// method, its toJSON method will be called and the result will be +// stringified. A toJSON method does not serialize: it returns the +// value represented by the name/value pair that should be serialized, +// or undefined if nothing should be serialized. The toJSON method +// will be passed the key associated with the value, and this will be +// bound to the value. + +// For example, this would serialize Dates as ISO strings. + +// Date.prototype.toJSON = function (key) { +// function f(n) { +// // Format integers to have at least two digits. +// return (n < 10) +// ? "0" + n +// : n; +// } +// return this.getUTCFullYear() + "-" + +// f(this.getUTCMonth() + 1) + "-" + +// f(this.getUTCDate()) + "T" + +// f(this.getUTCHours()) + ":" + +// f(this.getUTCMinutes()) + ":" + +// f(this.getUTCSeconds()) + "Z"; +// }; + +// You can provide an optional replacer method. It will be passed the +// key and value of each member, with this bound to the containing +// object. The value that is returned from your method will be +// serialized. If your method returns undefined, then the member will +// be excluded from the serialization. + +// If the replacer parameter is an array of strings, then it will be +// used to select the members to be serialized. It filters the results +// such that only members with keys listed in the replacer array are +// stringified. + +// Values that do not have JSON representations, such as undefined or +// functions, will not be serialized. Such values in objects will be +// dropped; in arrays they will be replaced with null. You can use +// a replacer function to replace those with JSON values. + +// JSON.stringify(undefined) returns undefined. + +// The optional space parameter produces a stringification of the +// value that is filled with line breaks and indentation to make it +// easier to read. + +// If the space parameter is a non-empty string, then that string will +// be used for indentation. If the space parameter is a number, then +// the indentation will be that many spaces. + +// Example: + +// text = JSON.stringify(["e", {pluribus: "unum"}]); +// // text is '["e",{"pluribus":"unum"}]' + +// text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); +// // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + +// text = JSON.stringify([new Date()], function (key, value) { +// return this[key] instanceof Date +// ? "Date(" + this[key] + ")" +// : value; +// }); +// // text is '["Date(---current time---)"]' + +// JSON.parse(text, reviver) +// This method parses a JSON text to produce an object or array. +// It can throw a SyntaxError exception. + +// The optional reviver parameter is a function that can filter and +// transform the results. It receives each of the keys and values, +// and its return value is used instead of the original value. +// If it returns what it received, then the structure is not modified. +// If it returns undefined then the member is deleted. + +// Example: + +// // Parse the text. Values that look like ISO date strings will +// // be converted to Date objects. + +// myData = JSON.parse(text, function (key, value) { +// var a; +// if (typeof value === "string") { +// a = +// /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); +// if (a) { +// return new Date(Date.UTC( +// +a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6] +// )); +// } +// return value; +// } +// }); + +// myData = JSON.parse( +// "[\"Date(09/09/2001)\"]", +// function (key, value) { +// var d; +// if ( +// typeof value === "string" +// && value.slice(0, 5) === "Date(" +// && value.slice(-1) === ")" +// ) { +// d = new Date(value.slice(5, -1)); +// if (d) { +// return d; +// } +// } +// return value; +// } +// ); + +// This is a reference implementation. You are free to copy, modify, or +// redistribute. + +/*jslint + eval, for, this +*/ + +/*property + JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (typeof JSON !== "object") { + JSON = {}; +} + +(function () { + "use strict"; + + var rx_one = /^[\],:{}\s]*$/; + var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; + var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; + var rx_four = /(?:^|:|,)(?:\s*\[)+/g; + var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + + function f(n) { + // Format integers to have at least two digits. + return (n < 10) + ? "0" + n + : n; + } + + function this_value() { + return this.valueOf(); + } + + if (typeof Date.prototype.toJSON !== "function") { + + Date.prototype.toJSON = function () { + + return isFinite(this.valueOf()) + ? ( + this.getUTCFullYear() + + "-" + + f(this.getUTCMonth() + 1) + + "-" + + f(this.getUTCDate()) + + "T" + + f(this.getUTCHours()) + + ":" + + f(this.getUTCMinutes()) + + ":" + + f(this.getUTCSeconds()) + + "Z" + ) + : null; + }; + + Boolean.prototype.toJSON = this_value; + Number.prototype.toJSON = this_value; + String.prototype.toJSON = this_value; + } + + var gap; + var indent; + var meta; + var rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + rx_escapable.lastIndex = 0; + return rx_escapable.test(string) + ? "\"" + string.replace(rx_escapable, function (a) { + var c = meta[a]; + return typeof c === "string" + ? c + : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }) + "\"" + : "\"" + string + "\""; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i; // The loop counter. + var k; // The member key. + var v; // The member value. + var length; + var mind = gap; + var partial; + var value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if ( + value + && typeof value === "object" + && typeof value.toJSON === "function" + ) { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === "function") { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case "string": + return quote(value); + + case "number": + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return (isFinite(value)) + ? String(value) + : "null"; + + case "boolean": + case "null": + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce "null". The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is "object", we might be dealing with an object or an array or +// null. + + case "object": + +// Due to a specification blunder in ECMAScript, typeof null is "object", +// so watch out for that case. + + if (!value) { + return "null"; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === "[object Array]") { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || "null"; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 + ? "[]" + : gap + ? ( + "[\n" + + gap + + partial.join(",\n" + gap) + + "\n" + + mind + + "]" + ) + : "[" + partial.join(",") + "]"; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === "object") { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === "string") { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + (gap) + ? ": " + : ":" + ) + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + (gap) + ? ": " + : ":" + ) + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 + ? "{}" + : gap + ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" + : "{" + partial.join(",") + "}"; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== "function") { + meta = { // table of character substitutions + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + "\"": "\\\"", + "\\": "\\\\" + }; + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ""; + indent = ""; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === "number") { + for (i = 0; i < space; i += 1) { + indent += " "; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === "string") { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== "function" && ( + typeof replacer !== "object" + || typeof replacer.length !== "number" + )) { + throw new Error("JSON.stringify"); + } + +// Make a fake root object containing our value under the key of "". +// Return the result of stringifying the value. + + return str("", {"": value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== "function") { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k; + var v; + var value = holder[key]; + if (value && typeof value === "object") { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + rx_dangerous.lastIndex = 0; + if (rx_dangerous.test(text)) { + text = text.replace(rx_dangerous, function (a) { + return ( + "\\u" + + ("0000" + a.charCodeAt(0).toString(16)).slice(-4) + ); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with "()" and "new" +// because they can cause invocation, and "=" because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we +// replace all simple value tokens with "]" characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or "]" or +// "," or ":" or "{" or "}". If that is so, then the text is safe for eval. + + if ( + rx_one.test( + text + .replace(rx_two, "@") + .replace(rx_three, "]") + .replace(rx_four, "") + ) + ) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The "{" operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval("(" + text + ")"); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return (typeof reviver === "function") + ? walk({"": j}, "") + : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError("JSON.parse"); + }; + } +}()); \ No newline at end of file diff --git a/SampleScripts/hyphenation/manifest.json b/SampleScripts/hyphenation/manifest.json new file mode 100644 index 0000000..aa31c86 --- /dev/null +++ b/SampleScripts/hyphenation/manifest.json @@ -0,0 +1,17 @@ +{ + "manifestVersion": "1.0.0", + "name": "hyphenate", + "host": { + "app": "indesign", + "maxVersion": "20.2.0", + "minVersion": "16.0.1" + }, + "version": "1.0.3", + "apiEntryPoints": [ + { + "path": "hyphenate.jsx", + "type": "capability", + "language": "extendscript" + } + ] +} \ No newline at end of file diff --git a/SampleScripts/hyphenation/utils.jsx b/SampleScripts/hyphenation/utils.jsx new file mode 100644 index 0000000..ad08214 --- /dev/null +++ b/SampleScripts/hyphenation/utils.jsx @@ -0,0 +1,702 @@ +/************************************************************************* +* ADOBE CONFIDENTIAL +* ___________________ +* +* Copyright 2021 Adobe +* All Rights Reserved. +* +* NOTICE: All information contained herein is, and remains +* the property of Adobe and its suppliers, if any. The intellectual +* and technical concepts contained herein are proprietary to Adobe +* and its suppliers and are protected by all applicable intellectual +* property laws, including trade secret and copyright laws. +* Dissemination of this information or reproduction of this material +* is strictly forbidden unless prior written permission is obtained +* from Adobe. +**************************************************************************/ +// utils.jsx + +// Utils functions which can be used across. +/* globals app, Errors, File, Folder, LinkStatus, SaveOptions */ + +// Globals. +var logFileObject + +// eslint-disable-next-line no-extend-native +Array.prototype.indexOf = function (item) { + var index = 0 + var length = this.length + for (; index < length; index++) { + if (this[index] === item) { + return index + } + } + return -1 +} + +// eslint-disable-next-line no-extend-native +if (typeof Array.isArray === 'undefined') { + Array.isArray = function (obj) { + return Object.prototype.toString.call(obj) === '[object Array]' + } +} + +// Create a UTILS object to store utility functions and variables. +var UTILS = {} + +// Initialize various properties of the UTILS object. +UTILS.Logs = [] // Stores log messages. +UTILS.workingFolder = '' // Path of the working folder. +UTILS.outputFolder = '' // Path of the output folder. +UTILS.assetsToBeUploaded = [] // List of assets that need to be uploaded. +UTILS.createSeparateLogFile = true // Flag to determine if a separate log file should be created. +UTILS.logFilePath = 'LogFile.txt' // File name for logging. + +// Converts the package in appropriate format, which can be returned from the capability. +UTILS.GetFinalReturnPackage = function (obj) { + UTILS.Log('Final package created') + return JSON.stringify(obj) +} + +// Creates and returns the package to be returned in case the job is successful. +UTILS.GetSuccessReturnObj = function (data) { + var obj = {} + obj.status = 'SUCCESS' + obj.assetsToBeUploaded = UTILS.assetsToBeUploaded // Attach the list of assets to be uploaded. + var dataURL = UTILS.WriteToFile(data, 'outputData') // Save data to file. + if (dataURL) { + obj.dataURL = dataURL + } + return obj +} + +// Creates and returns the package to be returned in case the job has failed. +UTILS.GetFailureReturnObj = function (errorCode, errorString, data) { + var obj = {} + obj.status = 'FAILURE' + obj.errorCode = errorCode // Attach the error code. + obj.errorString = errorString // Attach the error message. + obj.data = data // Include additional data if available. + return obj +} + +// Add an asset which is to be uploaded and sent back to the caller. +UTILS.AddAssetToBeUploaded = function (assetPath, data) { + var assetToBeUploaded = {} + assetToBeUploaded.path = assetPath + if (data !== undefined) { + assetToBeUploaded.data = data // Include additional asset data if provided. + } + UTILS.assetsToBeUploaded.push(assetToBeUploaded) +} + +// This handles errors in case an exception is raised. This results the capability returning error to the caller. +UTILS.HandleError = function (exception) { + var errorCode, errorString + UTILS.Log('Exception occurred: ' + JSON.stringify(exception)) + + // Handle specific exception cases + if (exception.message === 'open') { + exception.message = Errors.CannotOpenFileError.errorStrings[0] + exception.errorCode = Errors.CannotOpenFileError.errorCode + } + + errorString = 'Script error: ' + if (exception.isCustom === true) { + if (exception.message) { + errorString = errorString + exception.message + } + errorCode = exception.errorCode + } else { + errorCode = Errors.ProcessingErrorOccurred.errorCode + if (exception.message) { + errorString = errorString + Errors.ProcessingErrorOccurred.errorStrings[0] + ' ' + exception.message + } + if (exception.errorCode !== undefined) { + errorString = errorString + '\nInternal error code: ' + exception.errorCode + '.' + } + if (exception.line !== undefined) { + errorString = errorString + ' Line: ' + exception.line + '.' + } + if (exception.fileName !== undefined) { + errorString = errorString + ' FileName: ' + exception.fileName + '.' + } + UTILS.Log('Processing error occurred. ' + errorString) + } + + return UTILS.GetFailureReturnObj(errorCode, errorString) +} + +// This is used to raise an exception with all the required details. +// NOTE: This takes variable list of argument and then try to fill in the details in errorObj.errorStrings +UTILS.RaiseException = function (errorObj) { + UTILS.Log('RaiseException()') + var numMessageParameters = arguments.length + UTILS.Log('', 'numMessageParameters: ' + numMessageParameters) + var numErrorStrings = errorObj.errorStrings.length + UTILS.Log('', 'numErrorStrings: ' + numErrorStrings) + var numIterations = (numMessageParameters > numErrorStrings) ? numErrorStrings : numMessageParameters + var errorMessage = errorObj.errorStrings[0] + UTILS.Log('', 'Default errorMessage: ' + errorMessage) + + // Construct the detailed error message using available parameters. + for (var itr = 1; itr < numIterations; itr++) { + var parameter = arguments[itr] + if (parameter !== undefined && parameter !== '') { + var parameterMessage = errorObj.errorStrings[itr] + errorMessage = errorMessage + ' ' + parameterMessage.replace('^1', parameter) + UTILS.Log('', 'Appended errorMessage: ' + errorMessage) + } + } + + // Throw an exception object with custom details. + var exceptionObj = { + number: errorObj.errorCode, + isCustom: true, + message: errorMessage + } + + throw exceptionObj +} + +// This will update out-of-date links in a document. +UTILS.UpdateDocumentLinks = function (document) { + var links = document.links + var numLinks = links.length + var linkItr, link, uri + var outOfDateLinks = [] + + UTILS.Log('Number of links: ' + numLinks) + for (linkItr = 0; linkItr < numLinks; linkItr++) { + try { + link = links[linkItr] + uri = link.linkResourceURI + UTILS.Log(linkItr + ': URI: ' + uri) + if (link.status === LinkStatus.LINK_OUT_OF_DATE) { + outOfDateLinks.push(link.id) + } + } catch (err) { + UTILS.Log('Link status unknown : ' + err) + } + } + + // Update all out-of-date links. + numLinks = outOfDateLinks.length + for (linkItr = 0; linkItr < numLinks; linkItr++) { + link = document.links.itemByID(outOfDateLinks[linkItr]) + if (link.isValid) { + link.update() + } + } + + // Recompose the document after updating links. + var composeStartTime = new Date().getTime() + document.recompose() + var composedTime = (new Date()).getTime() - composeStartTime + UTILS.Log('document.recompose Time: ' + composedTime) +} + +// This Embeds all the links in the document. +UTILS.EmbedDocumentLinks = function (document) { + var links = document.links + var numLinks = links.length + var linkItr, link + + for (linkItr = 0; linkItr < numLinks; linkItr++) { + try { + link = links[linkItr] + if (link.status === LinkStatus.NORMAL) { + link.unlink() + } + } catch (err) { + UTILS.Log('Unable to embed link: status is unknown : ' + err) + } + } +} + +// Trims leading and trailing spaces from a string. +UTILS.Trim = function (val) { + return val.replace(/^\s+|\s+$/gm, '') +} + +// Removes all the spaces from a string. +UTILS.RemoveAllSpaces = function (val) { + return val.replace(/\s/g, '') +} + +// Converts a value to integer. Exception is thrown if the conversion fails. +UTILS.GetBoolean = function (val) { + if (val === 'true' || val === true) { + return true + } else if (val === 'false' || val === false) { + return false + } + + // throw exception. + UTILS.Log('GetBoolean() val: ' + val) + UTILS.RaiseException(Errors.ParsingBoolError, val) +} + +// Gets a boolean from an 'object' defined with 'key'. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the conversion of the provided value fails. +UTILS.GetBooleanFromObject = function (object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetBooleanFromObject ' + key) + + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + if (val === 'true' || val === true) { + return true + } else if (val === 'false' || val === false) { + return false + } + + UTILS.Log('GetBooleanFromObject() val: ' + val) + UTILS.RaiseException(Errors.ParsingBoolError, val, key) + } else if (defaultVal === undefined) { + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Converts a value to integer. Exception is thrown if the conversion fails. +UTILS.GetInteger = function (val) { + var returnVal = parseInt(val) + if (!isNaN(returnVal)) { + return returnVal + } + + UTILS.Log('GetInteger() val: ' + val) + UTILS.RaiseException(Errors.ParsingIntError, val) +} + +// Gets a integer from an 'object' defined with 'key'. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the conversion of the provided value fails. +UTILS.GetIntegerFromObject = function (object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetIntegerFromObject ' + key) + + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + var returnVal = parseInt(val) + if (!isNaN(returnVal)) { + return returnVal + } + + UTILS.Log('GetIntegerFromObject() val: ' + val) + UTILS.RaiseException(Errors.ParsingIntError, val, key) + } else if (defaultVal === undefined) { + UTILS.Log('Missing key: ' + key) + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Converts a value to float. Exception is thrown if the conversion fails. +UTILS.GetFloat = function (val) { + var returnVal = parseFloat(val) + if (!isNaN(returnVal)) { + return returnVal + } + + UTILS.Log('GetFloat() val: ' + val) + UTILS.RaiseException(Errors.ParsingFloatError, val) +} + +// Gets a float from an 'object' defined with 'key'. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the conversion of the provided value fails. +UTILS.GetFloatFromObject = function (object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetFloatFromObject ' + key) + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + var returnVal = parseFloat(val) + if (!isNaN(returnVal)) { + return returnVal + } + + UTILS.Log('GetFloatFromObject() val: ' + val) + UTILS.RaiseException(Errors.ParsingFloatError, val, key) + } else if (defaultVal === undefined) { + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Validates a value to be one of the enum definitions. +UTILS.GetEnum = function (enumDef, val) { + try { + val = UTILS.Trim(val) + return UTILS.GetValueFromObject(enumDef, val) + } catch (e) { + UTILS.RaiseException(Errors.EnumError, val) + } +} + +// Gets a enum value from an 'object' defined with 'key' and after comparing against an enum definition. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the value is not present in enum definition. +UTILS.GetEnumFromObject = function (enumDef, object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetEnumFromObject ' + key) + + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + try { + val = UTILS.Trim(val) + if (Array.isArray(enumDef) === false) { + return UTILS.GetValueFromObject(enumDef, val, ignoreCase) + } else { + if (enumDef.indexOf(val) >= 0) { + return val + } + } + } catch (e) { + UTILS.Log('GetEnumFromObject() val: ' + val) + UTILS.RaiseException(Errors.EnumError, val, key) + } + + UTILS.Log('GetEnumFromObject() val: ' + val) + UTILS.RaiseException(Errors.EnumError, val, key) + } else if (defaultVal === undefined) { + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Gets a string from an 'object' defined with 'key'. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the value found is not string. +UTILS.GetStringFromObject = function (object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetStringFromObject ' + key) + + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + if (typeof val !== 'string') { + UTILS.RaiseException(Errors.ParsingStringError, val, key) + } else { + return val + } + } else if (defaultVal === undefined) { + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Gets an array from an 'object' defined with 'key'. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the value found is not an array. +UTILS.GetArrayFromObject = function (object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetArrayFromObject ' + key) + + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + if (Array.isArray(val) === false) { + UTILS.RaiseException(Errors.ArrayExpected, key) + } else { + return val + } + } else if (defaultVal === undefined) { + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Gets an object from an 'object' defined with 'key'. +// A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. +// In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the value found is not an object. +UTILS.GetObjectFromObject = function (object, key, defaultVal, ignoreCase) { + var val + UTILS.Log('GetObjectFromObject ' + key) + + if (UTILS.IsKeyPresent(object, key)) { + val = UTILS.GetValueFromObject(object, key, ignoreCase) + if (typeof val !== 'object' || Array.isArray(val)) { + UTILS.RaiseException(Errors.ObjectExpected, key) + } else { + return val + } + } else if (defaultVal === undefined) { + UTILS.RaiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } +} + +// Gets the value from an 'object' defined with 'key'. +// The case of key can be ignored via 'ignoreCase'. In case, key is missing an exception will be thrown. +UTILS.GetValueFromObject = function (object, key, ignoreCase) { + UTILS.Log('GetValueFromObject ' + key) + + if (ignoreCase === true) { + var objectKey + for (objectKey in object) { + if (Object.prototype.hasOwnProperty.call(object, objectKey)) { + if (objectKey.toLowerCase() === key.toLowerCase()) { + return object[objectKey] + } + } + } + } else if (Object.prototype.hasOwnProperty.call(object, key)) { + return object[key] + } + + UTILS.RaiseException(Errors.MissingKey, key) +} + +// Checks whether a key is present in the object or not. +UTILS.IsKeyPresent = function (object, key) { + if (Object.prototype.hasOwnProperty.call(object, key)) { + return true + } + return false +} + +// Sets the working folder to be used across the capability. +UTILS.SetWorkingFolder = function (workingFolder) { + workingFolder = workingFolder.replace(/\//g, '\\') + if (workingFolder.charAt(workingFolder.length - 1) !== '\\') { + workingFolder = workingFolder + '\\' + } + UTILS.workingFolder = workingFolder + UTILS.OpenLogFileHandle() +} + +// Gets the path of the directory for a given path. +UTILS.GetDirPath = function (path) { + path = path.replace(/\//g, '\\') + var indexOfSlash = path && path.lastIndexOf('\\') + if (indexOfSlash >= 0) { + return path.substr(0, indexOfSlash) + } + return '' +} + +// Gets the path of the directory for a given path. If 'withExtension is true the file name consists of the file extension and not otherwise. +UTILS.GetFileName = function (path, withExtension) { + var fileName + var indexOfSlash = path.lastIndexOf('\\') + 1 + if (indexOfSlash >= 1) { + fileName = path.substr(indexOfSlash) + if (withExtension === false) { + var indexOfDot = path.lastIndexOf('.') + fileName = path.substr(indexOfSlash, indexOfDot - indexOfSlash) + } + } else { + fileName = path + } + return fileName +} + +// Get relative path to the working directory for a path. +UTILS.GetRelativePath = function (path) { + var index = path.indexOf(UTILS.workingFolder) + if (index === 0) { + return path.substr(UTILS.workingFolder.length) + } + return path +} + +// Get relative path to the working directory for a path. This returns in unix notation. +UTILS.GetRelativeReturnPath = function (path) { + var index = path.indexOf(UTILS.workingFolder) + if (index === 0) { + var tempPath = path.substr(UTILS.workingFolder.length) + tempPath = tempPath.replace(/\\/g, '/') + return tempPath + } +} + +// Get the full path from a relative and a base path. In absence of base path, working directory is used. +UTILS.GetFullPath = function (relativePath, basePath) { + var origRelativePath = relativePath + if (relativePath.indexOf('..') !== -1 || relativePath.indexOf('/') === 0) { + this.RaiseException(Errors.InCorrectRelativePath, origRelativePath) + } + relativePath = relativePath.replace(/\//g, '\\') + if (relativePath.indexOf('\\.\\') !== -1) { + this.RaiseException(Errors.InCorrectRelativePath, origRelativePath) + } + + if (basePath === undefined) { + basePath = UTILS.workingFolder + } else { + basePath = basePath.replace(/\//g, '\\') + if (basePath.charAt(basePath.length - 1) !== '\\') { + basePath = basePath + '\\' + } + } + + if (relativePath.charAt(0) === '.') { + relativePath = relativePath.slice(1) + } + if (relativePath.charAt(0) === '\\') { + relativePath = relativePath.slice(1) + } + + var fullPath = basePath + relativePath + return fullPath +} + +// Get a unique name. +UTILS.GetUniqueName = function () { + return Math.random().toString().substr(2, 6) +} + +// Creates a directory for the path provided. +UTILS.CreateDirectory = function (path) { + var outputFolder = Folder(path) + return outputFolder.create() +} + +// Creates a directory for the outputs on the basis of relative path passed. In case nothing is passed a random directory is created in working folder. +UTILS.GetOutputDirectory = function (outputFolderPath) { + var outputPath = '' + var created = false + if (outputFolderPath) { + outputPath = UTILS.GetFullPath(outputFolderPath) + created = UTILS.CreateDirectory(outputPath) + var outputFolder = Folder(outputPath) + if (outputFolder.exists === false) { + UTILS.RaiseException(Errors.OutputDirError) + } + } else { + var tempName = '' + do { + tempName = 'tmp' + UTILS.GetUniqueName() + outputPath = UTILS.GetFullPath(tempName) + created = UTILS.CreateDirectory(outputPath) + } while (!created) + } + UTILS.outputFolder = outputPath + return outputPath +} + +// Setup to do the logging. +UTILS.InitiateLogging = function () { + UTILS.Logs = [] +} + + +// Opens a log file handle if separate logging is enabled. +UTILS.OpenLogFileHandle = function () { + if (UTILS.createSeparateLogFile === true) { + logFileObject = new File(UTILS.GetFullPath(UTILS.logFilePath)) + var exists = false + + // Check if the log file already exists + if (logFileObject.open('read')) { + exists = true + } + UTILS.Log('Creating log file at ' + UTILS.GetFullPath(UTILS.logFilePath)) + + // Open the log file in append mode if it exists, otherwise create a new file + if (exists) { + logFileObject.close() + logFileObject.open('append') + } else { + logFileObject.open('write') + } + } +} + +// Ends logging by closing the log file handle if separate logging is enabled +UTILS.TerminateLogging = function () { + if (UTILS.createSeparateLogFile === true) { + logFileObject.close() + } +} + +// This logs any provided information +UTILS.Log = function (log) { + var logText + + // Convert non-string logs to JSON format + if (log === undefined) { + logText = '' + } else if (typeof primaryLog !== 'string') { + logText = JSON.stringify(log) + } else { + logText = log + } + + if (UTILS.createSeparateLogFile === true) { + + // Write to file if log file object exists + if (logFileObject) { + if (UTILS.Logs.length > 0) { + for (var itr = 0; itr < UTILS.Logs.length; itr++) { + logFileObject.writeln(UTILS.Logs[itr]) + } + UTILS.Logs = [] + } + logFileObject.writeln(logText) + } else { + UTILS.Logs.push(logText) + } + } else { + UTILS.Logs.push(logText) + } +} + +// Writes data to a uniquely named JSON file +UTILS.WriteToFile = function (data, fileName) { + var fileURL, newFile + var exists = false + var suffix = '' + var counter = 1 + + // Generate a unique filename if none is provided + if (fileName === undefined) { + fileName = UTILS.GetUniqueName() + } + + // Ensure the file does not already exist + do { + fileURL = UTILS.GetFullPath(fileName + suffix + '.json') + newFile = File(fileURL) + if (newFile.open('read')) { + exists = true + suffix = counter++ + newFile.close() + } else { + exists = false + } + } while (exists) + + // Write data to the file + newFile.encoding = 'UTF8' + newFile.open('write') + if (newFile.write(JSON.stringify(data))) { + UTILS.Log('Data was successfully written') + newFile.close() + return (fileName + suffix + '.json') + } else { + UTILS.Log('Data write failed') + newFile.close() + } +} + +// Closes all open documents in the application. +UTILS.CloseAllOpenDocuments = function () { + UTILS.Log('Closing all the documents') + app.documents.everyItem().close(SaveOptions.NO) +} diff --git a/SampleScripts/idmlConversion/.DS_Store b/SampleScripts/idmlConversion/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0" + }, + "destination": "sample.indd" + } + ], + "params": { + "targetDocument": "sample.indd", + "outputPath": "converted.idml" + } +} +``` + +## Logging +The script logs processing steps and performance metrics to a log file (LogFile.txt) in the working directory. Logs include: + +- Time taken to open the document. +- Time taken to update links. +- Time taken to balance ragged lines. + +## Error Handling +The script captures and logs errors during execution. Common errors include: + +- Missing or invalid document paths. +- Issues with updating links. +- Processing failures. \ No newline at end of file diff --git a/SampleScripts/idmlConversion/idml.idjs b/SampleScripts/idmlConversion/idml.idjs new file mode 100644 index 0000000..bdcf0c2 --- /dev/null +++ b/SampleScripts/idmlConversion/idml.idjs @@ -0,0 +1,745 @@ +/************************************************************************* +* ADOBE CONFIDENTIAL +* ___________________ +* +* Copyright 2021 Adobe +* All Rights Reserved. +* +* NOTICE: All information contained herein is, and remains +* the property of Adobe and its suppliers, if any. The intellectual +* and technical concepts contained herein are proprietary to Adobe +* and its suppliers and are protected by all applicable intellectual +* property laws, including trade secret and copyright laws. +* Dissemination of this information or reproduction of this material +* is strictly forbidden unless prior written permission is obtained +* from Adobe. +**************************************************************************/ + +// Define a replacement string for error messages where dynamic values will be inserted +const ErrorReplacementString = '^1'; + +// Define an object containing various error types and their corresponding messages +const Errors = { + InternalScriptError: { + errorCode: 'internal_error', + errorStrings: [ + 'Internal script error. This should not have happened.' + ] + }, + ProcessingErrorOccurred: { + errorCode: 'internal_error', + errorStrings: [ + 'Internal error: Error during processing.' + ] + }, + PDFPresetNotSet: { + errorCode: 'capability_error', + errorStrings: [ + 'Capability error: No PDF Preset could be set.' + ] + }, + OutputDirError: { + errorCode: 'internal_error', + errorStrings: [ + 'Internal error: Unable to create specified output directory.' + ] + }, + RelinkError: { + errorCode: 'capability_error', + errorStrings: [ + 'Capability error: Unable to relink.', + 'Relink failed for ^1.' + ] + }, + PlaceError: { + errorCode: 'capability_error', + errorStrings: [ + 'Capability error: Unable to place.', + 'Place failed for ^1.' + ] + }, + ArrayExpected: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected array ', + 'for property ^1.' + ] + }, + ParsingBoolError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected boolean ', + '^1 is the provided value ', + 'for property ^1.' + ] + }, + ParsingIntError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected integer ', + '^1 is the provided value ', + 'for property ^1.' + ] + }, + ParsingFloatError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected number ', + '^1 is the provided value ', + 'for property ^1.' + ] + }, + ParsingStringError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected string ', + '^1 is the provided value ', + 'for property ^1.' + ] + }, + OutOfBound: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Incorrect range provided. ', + 'Culprit Value is ^1.' + ] + }, + MissingKey: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Key not found in object. ', + 'Missing key is ^1.' + ] + }, + EnumError: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: No matching enum value found. ', + '^1 is the provided value', + 'for property ^1.' + ] + }, + MissingParams: { + errorCode: 'parameter_error', + errorStrings: [ + "Parameter error: Either the 'params' is not found in the request or it is not in the correct format." + ] + }, + ObjectExpected: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: Expected object ', + 'for property ^1.' + ] + }, + AtLeastOneInternalParamShouldBePresent: { + errorCode: 'parameter_error', + errorStrings: [ + 'Parameter error: At least one of the parameters should be present. ', + 'Missing parameters are ^1' + ] + }, + CannotOpenFileError: { + errorCode: 'capability_error', + errorStrings: [ + 'Capability error: Cannot open file.' + ] + } +}; + +// DOM related variables + +// Load InDesign's scripting environment +const myInDesign = require("indesign"); +const app = myInDesign.app; // Access the InDesign application instance + +// Import the local file system module from UXP for file handling +const fs = require("uxp").storage.localFileSystem; + +// Import UXP scripting module for getting script arguments and setting script output. +const script = require("uxp").script; + + +// Utility class containing helper functions for logging, error handling, file operations, and InDesign document management. +class Utils { + constructor() { + // Array to store log messages + this.logs = []; + + // Paths for working and output directories + this.workingFolder = ""; + this.outputFolder = ""; + + // List of assets to be uploaded + this.assetsToBeUploaded = []; + + // Flag to determine whether a separate log file should be created + this.createSeparateLogFile = true; + + // Default log file name + this.logFilePath = "LogFile.txt"; + } + + + // Initialize logging by clearing the logs array + initiateLogging() { + this.logs = []; + } + + // Log a message to the console and store it in the logs array + log(message) { + console.log(message); + this.logs.push(message); + + } + + // Converts the package in appropriate format, which can be returned from the capability. + getFinalReturnPackage(obj) { + this.log("Final package created"); + return JSON.stringify(obj); + } + + // Creates and returns the package to be returned in case the job is successful. + async getSuccessReturnObj(data) { + let obj = { + status: "SUCCESS", + assetsToBeUploaded: this.assetsToBeUploaded + }; + + try { + obj.dataURL = await this.writeToFile(data, "outputData"); + } catch (err) { + this.log(`Error writing outputData: ${err.message}`); + } + + return obj; + } + + // Creates and returns the package to be returned in case the job has failed. + getFailureReturnObj(errorCode, errorString, data = null) { + return { + status: "FAILURE", + errorCode, // Attach the error code. + errorString, // Attach the error message. + data // Include additional data if available. + }; + } + + // Add an asset which is to be uploaded and sent back to the caller. + addAssetToBeUploaded(assetPath, data = null) { + this.assetsToBeUploaded.push({ path: assetPath, data: data }); + } + + // This handles errors in case an exception is raised. This results the capability returning error to the caller. + handleError(exception) { + let errorCode, errorString; + console.log('Exception occurred: ' + JSON.stringify(exception)); + + if (exception.message === 'open') { + exception.message = Errors.CannotOpenFileError.errorStrings[0]; + exception.errorCode = Errors.CannotOpenFileError.errorCode; + } + + errorString = 'Script error: '; + if (exception.isCustom === true) { + if (exception.message) { + errorString += exception.message; + } + errorCode = exception.errorCode; + } else { + errorCode = Errors.ProcessingErrorOccurred.errorCode; + if (exception.message) { + errorString += Errors.ProcessingErrorOccurred.errorStrings[0] + ' ' + exception.message; + } + if (exception.errorCode !== undefined) { + errorString += '\nInternal error code: ' + exception.errorCode + '.'; + } + if (exception.line !== undefined) { + errorString += ' Line: ' + exception.line + '.'; + } + if (exception.fileName !== undefined) { + errorString += ' FileName: ' + exception.fileName + '.'; + } + console.log('Processing error occurred. ' + errorString); + } + + return this.getFailureReturnObj(errorCode, errorString); + } + + // This will update out-of-date links in a document. + async updateDocumentLinks(document) { + try { + let links = document.links; + let numLinks = links.length; + let outOfDateLinks = []; + + this.log('Number of links: ' + numLinks); + + for (let i = 0; i < numLinks; i++) { + let link = links.item(i); + try { + let uri = link.linkResourceURI; + this.log('URI: ' + uri); + if (link.status === myInDesign.LinkStatus.LINK_OUT_OF_DATE) { + outOfDateLinks.push(link); + } + } catch (err) { + this.log('Link status unknown: ' + err); + } + } + + // Update all out-of-date links. + for (let i = 0; i < outOfDateLinks.length; i++) { + let link = outOfDateLinks[i]; + if (link.isValid) { + await link.update(); + } + } + + let composeStartTime = Date.now(); + + // Recompose the document after updating links. + await document.recompose(); + let composedTime = Date.now() - composeStartTime; + this.log('Recompose Time: ' + composedTime); + } catch (error) { + this.error("Error updating document links:", error); + } + } + + // Writes data to the given named JSON file + async writeToFile(data, filename) { + try { + let folder = await fs.getEntryWithUrl("file://" + this.workingFolder); + let file = await folder.createFile(filename + '.json', { overwrite: true }); + await file.write(" "); + if (await file.write(JSON.stringify(data))) { + this.log('Data was successfully written') + } + else { + this.log('Data write failed') + } + return file.nativePath; + } + catch (err) { + this.log('Error writing to file: ' + err.message) + } + + } + + // Sets the working folder to be used across the capability. + async setWorkingFolder(workingFolder) { + // workingFolder = workingFolder.replace(/\//g, '\\') + // if (workingFolder.charAt(workingFolder.length - 1) !== '\\') { + // workingFolder = workingFolder + '\\' + // } + this.workingFolder = workingFolder + await this.openLogFileHandle(); + } + + // Opens a log file handle if separate logging is enabled. + async openLogFileHandle() { + try { + var folder = await fs.getEntryWithUrl("file://" + this.workingFolder); + var logFile = await folder.createFile(this.logFilePath, { overwrite: true }); + this.logFileHandle = logFile; + } catch (err) { + console.error("Failed to create log file:", err); + this.log(`Failed to create log file: ${err.message}`); + } + } + + // Append a log message to the log file + async logToFile(message) { + if (!this.logFileHandle) { + await this.openLogFileHandle(); + } + + try { + await this.logFileHandle.write(message + "\n"); + } catch (err) { + console.error("Failed to write to log file:", err); + + } + } + + // Closes all open documents in the application. + async closeAllOpenDocuments() { + try { + console.log("Closing all open documents..."); + this.log("Closing all open documents..."); + const documents = app.documents; + let SaveOptions = myInDesign.SaveOptions; + + // Close each document without saving + for (let i = documents.length - 1; i >= 0; i--) { + let doc = documents.item(i) + await doc.close(SaveOptions.NO); + } + + console.log("All documents closed."); + } catch (error) { + console.error("Error closing documents:", error); + this.log(`Error closing documents: ${error.message}`); + } + } + + // This is used to raise an exception with all the required details. + // NOTE: This takes variable list of argument and then try to fill in the details in errorObj.errorStrings + raiseException(errorObj) { + UTILS.log('RaiseException()') + var numMessageParameters = arguments.length + UTILS.log('numMessageParameters: ' + numMessageParameters) + var numErrorStrings = errorObj.errorStrings.length + UTILS.log('numErrorStrings: ' + numErrorStrings) + var numIterations = (numMessageParameters > numErrorStrings) ? numErrorStrings : numMessageParameters + var errorMessage = errorObj.errorStrings[0] + UTILS.log('Default errorMessage: ' + errorMessage) + + // Construct the detailed error message using available parameters. + for (var itr = 1; itr < numIterations; itr++) { + var parameter = arguments[itr] + if (parameter !== undefined && parameter !== '') { + var parameterMessage = errorObj.errorStrings[itr] + errorMessage = errorMessage + ' ' + parameterMessage.replace('^1', parameter) + UTILS.log('Appended errorMessage: ' + errorMessage) + } + } + var exceptionObj = { + number: errorObj.errorCode, + isCustom: true, + message: errorMessage + } + + throw exceptionObj + } + + // Gets a string from an 'object' defined with 'key'. + // A default value can be passed via 'defaultVal' which will be used if the key is missing. The case of key can be ignored via 'ignoreCase'. + // In case, key is missing and a default value is not provided, an exception will be thrown. Exception will also be thrown if the value found is not string. + getStringFromObject(object, key, defaultVal, ignoreCase) { + var val + this.log('GetStringFromObject ' + key) + + if (this.isKeyPresent(object, key)) { + val = this.getValueFromObject(object, key, ignoreCase) + if (typeof val !== 'string') { + this.raiseException(Errors.ParsingStringError, val, key) + } else { + return val + } + } else if (defaultVal === undefined) { + this.raiseException(Errors.MissingKey, key) + } else { + // default value provided. + return defaultVal + } + } + + // Checks whether a key is present in the object or not. + isKeyPresent(object, key) { + if (Object.prototype.hasOwnProperty.call(object, key)) { + return true + } + return false + } + + // Gets the value from an 'object' defined with 'key'. + // The case of key can be ignored via 'ignoreCase'. In case, key is missing an exception will be thrown. + getValueFromObject(object, key, ignoreCase) { + UTILS.log('GetValueFromObject ' + key) + + if (ignoreCase === true) { + var objectKey + for (objectKey in object) { + if (Object.prototype.hasOwnProperty.call(object, objectKey)) { + if (objectKey.toLowerCase() === key.toLowerCase()) { + return object[objectKey] + } + } + } + } else if (Object.prototype.hasOwnProperty.call(object, key)) { + return object[key] + } + + this.raiseException(Errors.MissingKey, key) + } + + // Get the full path from a relative and a base path. In absence of base path, working directory is used. + getFullPath(relativePath, basePath) { + var origRelativePath = relativePath + if (relativePath.indexOf('..') !== -1 || relativePath.indexOf('/') === 0) { + this.raiseException(Errors.InCorrectRelativePath, origRelativePath) + } + relativePath = relativePath.replace(/\//g, '\\') + if (relativePath.indexOf('\\.\\') !== -1) { + this.raiseException(Errors.InCorrectRelativePath, origRelativePath) + } + + if (basePath === undefined) { + basePath = this.workingFolder + } else { + basePath = basePath.replace(/\//g, '\\') + if (basePath.charAt(basePath.length - 1) !== '\\') { + basePath = basePath + '\\' + } + } + + if (relativePath.charAt(0) === '.') { + relativePath = relativePath.slice(1) + } + if (relativePath.charAt(0) === '\\') { + relativePath = relativePath.slice(1) + } + + var fullPath = basePath + "/"+relativePath + return fullPath + } + + // Get relative path to the working directory for a path. This returns in unix notation. + getRelativeReturnPath(path) { + var index = path.indexOf(this.workingFolder) + if (index === 0) { + var tempPath = path.substr(UTILS.workingFolder.length) + tempPath = tempPath.replace(/\\/g, '/') + return tempPath + } + } + + // Terminate logging and write logs to file + async terminateLogging() { + this.log('Terminating logging.') + await this.logToFile(this.logs.join('\n')); + } +} + +// Create an instance of the Utils class +const UTILS = new Utils(); + + +// Constant strings +var KeyStrings = { + OutputPath: 'outputPath', + ProcessedData: 'processedData', + TargetDocument: 'targetDocument', + TimeDocumentClose: 'DocumentClose', + TimeDocumentOpen: 'DocumentOpen', + TimeExportIDML: 'ExportIDML', + TimeOverall: 'Overall', + TimeRelinkAssets: 'RelinkAssets', + Timings: 'timings', + WorkingFolder: 'workingFolder' +} + +// Object to store timing metrics for various processing steps. +var timingObject = {}; + +// Object to store warnings encountered during execution. +var warnings = {}; + +// Variable to hold the InDesign document. +var document; + + +/** + * Processes the provided parameters and executes the main operations. + * This includes opening the document, updating links, exporting to IDML, and logging performance metrics. + */ +async function ProcessParams(parameters) { + + // This is where the actual processing will take place. + UTILS.log('Processing parameters internal') + var returnVal = {} + + // Retrieve the target document path from parameters. + var documentPath = UTILS.getStringFromObject(parameters, KeyStrings.TargetDocument) + documentPath = UTILS.getFullPath(documentPath) + + UTILS.log('Opening document') + var tempTime = new Date().getTime() + + // Get file entry from the file system. + let file = await fs.getEntryWithUrl(`file://${documentPath}`); + + // Check if the path points to a valid file. + if (!file.isFile) { + UTILS.log("Not a valid file."); + return; + } + + // Open the document in InDesign Server. + document = await app.open(file) + + // Log the time taken to open the document. + timingObject[KeyStrings.TimeDocumentOpen] = (new Date()).getTime() - tempTime + UTILS.log('Opened document') + tempTime = new Date().getTime() + + // Update document links. + await UTILS.updateDocumentLinks(document) + timingObject[KeyStrings.TimeRelinkAssets] = (new Date()).getTime() - tempTime + UTILS.log('Updated links in the document') + + + // Retrieve the output file path from parameters. + var outputPath = UTILS.getStringFromObject(parameters, KeyStrings.OutputPath) + outputPath = UTILS.getFullPath(outputPath) + tempTime = new Date().getTime() + + // Export the document as an IDML file. + document.exportFile(myInDesign.ExportFormat.INDESIGN_MARKUP, outputPath) + timingObject[KeyStrings.TimeExportIDML] = (new Date()).getTime() - tempTime + + // Convert the absolute output path to a relative path for return. + var relativePath = UTILS.getRelativeReturnPath(outputPath) + + // Register the exported file as an asset to be uploaded. + UTILS.addAssetToBeUploaded(relativePath) + + returnVal.idmlPath = relativePath + // Processing ends. + + return returnVal +}; + +/** + * Main function that controls the script execution. + * It initializes logging, processes parameters, handles exceptions, and returns the result. + */ +async function main() { + + var startTime = new Date().getTime() // Capture the script start time + var returnObj = {} // Object to store return values + var parameters = {} // Stores input parameters + var tempTime + var errorOccurred = false // Flag to track errors + var data = {} // Stores processed data + + try { + + // Initialize logging. + await UTILS.initiateLogging() + UTILS.log('Initiating logging.') + UTILS.log('This way of logging can be used for debugging and generic logging.') + UTILS.log('This has been done after setting of working directory because it is relative to the working directory') + UTILS.log('Each log will be a separate line in the log file.') + UTILS.log('The file generated can be sent back') + + // Close all open InDesign documents to prevent conflicts. + await UTILS.closeAllOpenDocuments() + + // Retrieve input parameters from the script arguments. + let input = script.args // Right now out input will look like ["parameters={...}"]. So, will first need input[0] + + input=input[0] // Now out input looks like "parameters={...}". We will have to slice it down so that we only have the JSON Object {...}. + // if this step is skipped, parsing it using JSON.parse() will give us error. + // Remove "parameters=" prefix to extract JSON. + + input=input.slice(11) + + // Now we have the parameters as string. Parse the input JSON string into an object. + var allParameters = JSON.parse(input) + UTILS.log('Parsed job input : ' + input) + + // Extract parameters from the parsed JSON. + parameters = allParameters.params + if (parameters === undefined) { + parameters = allParameters.input.params + } + + // Validate the extracted parameters. + if (parameters === undefined || typeof parameters !== 'object' || Array.isArray(parameters)) { + UTILS.log('No params found') + UTILS.raiseException(Errors.MissingParams) + } + + // Set the working folder based on parameters. + await UTILS.setWorkingFolder(UTILS.getStringFromObject(allParameters, KeyStrings.WorkingFolder)) + + var result + UTILS.log('Processing Params') + tempTime = new Date().getTime() + + // Process the parameters and perform document-related tasks. + result = await ProcessParams(parameters) + data[KeyStrings.ProcessedData] = result + + // Include the log file in the list of assets to be uploaded. + UTILS.addAssetToBeUploaded(UTILS.logFilePath) + + data.warnings = warnings + // processing ends. + + tempTime = new Date().getTime() + + timingObject[KeyStrings.TimeDocumentClose] = (new Date()).getTime() - tempTime + UTILS.log('End of try') + } + catch (e) { + + // Handle exceptions and prepare failure response. + var tempObj = { + name: e.name, + message: e.message, + errorCode: e.number, + isCustom: e.isCustom, + line: e.line, + fileName: e.fileName + } + + UTILS.log('Exception occurred', tempObj) + errorOccurred = true + + // Failure, prepare the object to be returned. + returnObj = UTILS.handleError(tempObj) + + } + finally { + + if (document && document.isValid) { + // If Document is still open. Close it. + UTILS.log('Closing document') + tempTime = new Date().getTime() + document.close() + timingObject[KeyStrings.TimeDocumentClose] = (new Date()).getTime() - tempTime + } + + // Log execution time. + var elapsedTime = (new Date()).getTime() - startTime + UTILS.log('Time taken: ' + elapsedTime) + timingObject[KeyStrings.TimeOverall] = elapsedTime + UTILS.log('Timing: ' + JSON.stringify(timingObject)) + data[KeyStrings.Timings] = timingObject + + // If no errors occurred, return a success response. + if (!errorOccurred) { + UTILS.log('Finally: No error') + returnObj = await UTILS.getSuccessReturnObj(data) + } + + // Log the final result. + UTILS.log('Final Result' + JSON.stringify(returnObj)) + + // Terminate logging and return the final response. + await UTILS.terminateLogging() + return UTILS.getFinalReturnPackage(returnObj) + } +} + +/** + * Executes the main function and sets the script result. + * If an error occurs, it logs the error and sets the result accordingly. + */ +try { + var out=await main(); + + //In UXP Script, we have to set the return Result using setResult() function. + script.setResult(out); +} +catch (error) { + console.log(error); + script.setResult(error); +} + + + diff --git a/SampleScripts/idmlConversion/manifest.json b/SampleScripts/idmlConversion/manifest.json new file mode 100644 index 0000000..14e4ddc --- /dev/null +++ b/SampleScripts/idmlConversion/manifest.json @@ -0,0 +1,17 @@ +{ + "manifestVersion": "1.0.0", + "name": "idml", + "host": { + "app": "indesign", + "maxVersion": "20.4.0", + "minVersion": "16.0.1" + }, + "version": "1.0.4", + "apiEntryPoints": [ + { + "path": "idml.idjs", + "type": "capability", + "language": "uxpscript" + } + ] +} \ No newline at end of file From 8fa88b7c40ba44e18b4a5f8608234b6e55d209ef Mon Sep 17 00:00:00 2001 From: Rhythm Jain Date: Wed, 2 Apr 2025 11:13:40 +0530 Subject: [PATCH 2/8] Restructuring the sample scripts and Redefining the Readme --- .DS_Store | Bin 6148 -> 6148 bytes SampleScripts/.DS_Store | Bin 0 -> 6148 bytes SampleScripts/ExtendScript/.DS_Store | Bin 0 -> 6148 bytes .../ExtendScript/balanceRaggedLines/README.md | 126 ++++++++++++++++++ .../balanceRaggedLines/balanceRaggedLines.jsx | 0 .../balanceRaggedLines/errors.jsx | 0 .../balanceRaggedLines}/json2.jsx | 0 .../balanceRaggedLines/manifest.json | 0 .../balanceRaggedLines/utils.jsx | 0 .../ExtendScript/hyphenation/README.md | 126 ++++++++++++++++++ .../{ => ExtendScript}/hyphenation/errors.jsx | 0 .../hyphenation/hyphenate.jsx | 0 .../hyphenation}/json2.jsx | 0 .../hyphenation/manifest.json | 0 .../{ => ExtendScript}/hyphenation/utils.jsx | 0 .../ExtendScript/idmlConversion/README.md | 123 +++++++++++++++++ .../ExtendScript/idmlConversion}/errors.jsx | 0 .../idmlConversion}/json2.jsx | 0 .../idmlConversion}/manifest.json | 0 .../ExtendScript/idmlConversion}/sample.jsx | 0 .../ExtendScript/idmlConversion}/utils.jsx | 0 SampleScripts/UXP/.DS_Store | Bin 0 -> 6148 bytes .../{ => UXP}/idmlConversion/.DS_Store | Bin SampleScripts/UXP/idmlConversion/README.md | 108 +++++++++++++++ .../{ => UXP}/idmlConversion/idml.idjs | 0 .../{ => UXP}/idmlConversion/manifest.json | 0 SampleScripts/balanceRaggedLines/README.md | 63 --------- SampleScripts/hyphenation/README.md | 63 --------- SampleScripts/idmlConversion/README.md | 62 --------- 29 files changed, 483 insertions(+), 188 deletions(-) create mode 100644 SampleScripts/.DS_Store create mode 100644 SampleScripts/ExtendScript/.DS_Store create mode 100644 SampleScripts/ExtendScript/balanceRaggedLines/README.md rename SampleScripts/{ => ExtendScript}/balanceRaggedLines/balanceRaggedLines.jsx (100%) rename SampleScripts/{ => ExtendScript}/balanceRaggedLines/errors.jsx (100%) rename {Example => SampleScripts/ExtendScript/balanceRaggedLines}/json2.jsx (100%) rename SampleScripts/{ => ExtendScript}/balanceRaggedLines/manifest.json (100%) rename SampleScripts/{ => ExtendScript}/balanceRaggedLines/utils.jsx (100%) create mode 100644 SampleScripts/ExtendScript/hyphenation/README.md rename SampleScripts/{ => ExtendScript}/hyphenation/errors.jsx (100%) rename SampleScripts/{ => ExtendScript}/hyphenation/hyphenate.jsx (100%) rename SampleScripts/{balanceRaggedLines => ExtendScript/hyphenation}/json2.jsx (100%) rename SampleScripts/{ => ExtendScript}/hyphenation/manifest.json (100%) rename SampleScripts/{ => ExtendScript}/hyphenation/utils.jsx (100%) create mode 100644 SampleScripts/ExtendScript/idmlConversion/README.md rename {Example => SampleScripts/ExtendScript/idmlConversion}/errors.jsx (100%) rename SampleScripts/{hyphenation => ExtendScript/idmlConversion}/json2.jsx (100%) rename {Example => SampleScripts/ExtendScript/idmlConversion}/manifest.json (100%) rename {Example => SampleScripts/ExtendScript/idmlConversion}/sample.jsx (100%) rename {Example => SampleScripts/ExtendScript/idmlConversion}/utils.jsx (100%) create mode 100644 SampleScripts/UXP/.DS_Store rename SampleScripts/{ => UXP}/idmlConversion/.DS_Store (100%) create mode 100644 SampleScripts/UXP/idmlConversion/README.md rename SampleScripts/{ => UXP}/idmlConversion/idml.idjs (100%) rename SampleScripts/{ => UXP}/idmlConversion/manifest.json (100%) delete mode 100644 SampleScripts/balanceRaggedLines/README.md delete mode 100644 SampleScripts/hyphenation/README.md delete mode 100644 SampleScripts/idmlConversion/README.md diff --git a/.DS_Store b/.DS_Store index a804999a48d59ccab75f8fc888a12707d89682ee..8adab65c1b7a2025d75b59e45246761d82c42b82 100644 GIT binary patch delta 99 zcmZoMXfc=|#>B`mu~2NHo}wrV0|Nsi1A_nqLk>eKLoh=!LlHyf#KPr_ER)x<$!)e` tJIlCPfrFi8V*@ASW_AvK4xrY}f*jwOC-aL~0(F6PGeK#V%@HDNm;npH6A}Ob literal 6148 zcmeHKy-LJD5T5lSoF2kqrCkca`raTOu@KJ2yny%Tose9Do`qcs!Cp(jR@6eD!s4F8 zO2KFFo89Fu@qP>zA~FLrU$Q&1JM)p<%@UESpLJ?PRU#@w8J$&_8N&UnQ?g`RI9Ta1 z2Gpi59Z-{cQ{K`b3(s)E4WZff+rvukP=k)q+NIO5R}0#4l$ZkEw~NL_ zvG2FSxCQ_6<@Lwb{pe+m#u}MFZuW~UZRj5zrl)!dTQ0BTz&w07E^<0fyj@5ra_D_@T&}b E0`_dIx&QzG diff --git a/SampleScripts/.DS_Store b/SampleScripts/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..811eaa9f6af19e95a43143bad23adb0aa7d87b40 GIT binary patch literal 6148 zcmeHK%}T>S5dOC9A)<#K1uuK_CVhig(z_4@kJ3g|XiSg_9)nNhQ+V_t)EDp#{APEE zDQN{SqA~-s-)4R`lP{3T0C3a0Vgif-Bviq{4wnrge$k!`)FMt7&bUE=91Gmyx)*JR z-^hTxyFJWsj}g=*Az7=0z z^=cX76lXZuP^EghH9fu`zGzLY_p^e&cfTBaCe^XLpWR-(N<%?7+7au7%#`t|7Y9h|MeuhG6sx+f5m_sWqCH`BWZ8# wJ)HDfPd%rKNL&-KP2ncAV#Z1<-lqnk-OGlUDmD>mq1cZ=purYn;71ww0QSRA%m4rY literal 0 HcmV?d00001 diff --git a/SampleScripts/ExtendScript/.DS_Store b/SampleScripts/ExtendScript/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..33c18178fd3ddb0dc6fbaaa12335aa3c36881909 GIT binary patch literal 6148 zcmeHKJxT*X6#hm724fMdET`}SxxpGXot3$O=7&VcW&=hr-C`qH>Jh9`c?%0cYfmBM z27Yg5&|TNXU?C#!!F%5@^WMxiAI#1U0MkAnw16gnI$f}~&G3cExOmAnLeCD-xH+ci zV~8W1U{uQ14pl%E_-hKt+1*8tCw4;5pWhKr@&Jc8W$zGYlToYNPm`J7z)Enfx+IO` z-X!fYzkYT5y1#sUI9R`S@Z;LqILFsmE{QF4aKT+Bm~pqm;*MUP${Bg?cgyR=<+J1d zX`UqgU|F42i54( z(x3{c0{>G1Q;WNCn|lj)YiXBq*IN29T}<*akFJCvT*s~" + }, + "destination": "sample.indd" + } + ], + "params": { + "targetDocument": "sample.indd", + "outputPath": "balanced.indd", + "labels": ["MainPara", "Heading"], // Optional: If not provided, affects all text frames + "balance": "true" // Can be "true" or "false" + } +} +``` + +### Parameter Details +- `labels`: Array of text frame labels to process (optional) +- `balance`: Boolean value to enable/disable ragged line balancing +- `targetDocument`: Input InDesign document path +- `outputPath`: Output document path + +## Logging and Monitoring + +### Log File +The script generates detailed logs in `LogFile.txt` including: +- Document processing steps +- Text frame identification +- Ragged line balancing operations +- Performance metrics +- Error messages and warnings + +### Performance Metrics +- Document opening time +- Link update time +- Ragged line balancing processing time +- Total execution duration + +## Error Handling + +The script includes comprehensive error handling for various scenarios: +- Parameter validation errors +- Document processing errors +- Text frame identification issues +- Link management problems +- File system operations + +Each error includes: +- Error code +- Detailed error message +- Context information + +## Support + +For additional information and support: +- Refer to the [Adobe InDesign APIs documentation](https://developer.adobe.com/firefly-services/docs/indesign-apis/) +- Check the error messages in the log file for troubleshooting +- Ensure all prerequisites are met before running the script diff --git a/SampleScripts/balanceRaggedLines/balanceRaggedLines.jsx b/SampleScripts/ExtendScript/balanceRaggedLines/balanceRaggedLines.jsx similarity index 100% rename from SampleScripts/balanceRaggedLines/balanceRaggedLines.jsx rename to SampleScripts/ExtendScript/balanceRaggedLines/balanceRaggedLines.jsx diff --git a/SampleScripts/balanceRaggedLines/errors.jsx b/SampleScripts/ExtendScript/balanceRaggedLines/errors.jsx similarity index 100% rename from SampleScripts/balanceRaggedLines/errors.jsx rename to SampleScripts/ExtendScript/balanceRaggedLines/errors.jsx diff --git a/Example/json2.jsx b/SampleScripts/ExtendScript/balanceRaggedLines/json2.jsx similarity index 100% rename from Example/json2.jsx rename to SampleScripts/ExtendScript/balanceRaggedLines/json2.jsx diff --git a/SampleScripts/balanceRaggedLines/manifest.json b/SampleScripts/ExtendScript/balanceRaggedLines/manifest.json similarity index 100% rename from SampleScripts/balanceRaggedLines/manifest.json rename to SampleScripts/ExtendScript/balanceRaggedLines/manifest.json diff --git a/SampleScripts/balanceRaggedLines/utils.jsx b/SampleScripts/ExtendScript/balanceRaggedLines/utils.jsx similarity index 100% rename from SampleScripts/balanceRaggedLines/utils.jsx rename to SampleScripts/ExtendScript/balanceRaggedLines/utils.jsx diff --git a/SampleScripts/ExtendScript/hyphenation/README.md b/SampleScripts/ExtendScript/hyphenation/README.md new file mode 100644 index 0000000..1d41780 --- /dev/null +++ b/SampleScripts/ExtendScript/hyphenation/README.md @@ -0,0 +1,126 @@ +# Hyphenate Capability + +This directory contains a script for managing hyphenation in Adobe InDesign documents using ExtendScript. The script is designed to work with InDesign versions 16.0.1 through 20.2.0. + +## Directory Contents + +``` +hyphenation/ +├── manifest.json # Capability manifest file +├── hyphenate.jsx # Main script implementation +├── utils.jsx # Utility functions +├── errors.jsx # Error handling definitions +└── json2.jsx # JSON parsing utilities +``` + +## Files Description + +### 1. `manifest.json` +The manifest file defines the capability configuration: +- Supports InDesign versions 16.0.1 to 20.2.0 +- Implements an ExtendScript capability entry point +- Version: 1.0.3 + +### 2. `hyphenate.jsx` +The main script file that implements the hyphenation functionality. Features include: +- Text frame identification by labels +- Hyphenation control for specified paragraphs +- Document link management +- Performance monitoring +- Comprehensive error handling + +### 3. Helper Files +- `utils.jsx`: Contains utility functions for document processing and logging +- `errors.jsx`: Defines error types and messages for consistent error handling +- `json2.jsx`: Provides JSON parsing and manipulation utilities + +## Prerequisites + +- Adobe InDesign (version 16.0.1 or higher) +- ExtendScript environment +- Access to InDesign Services +- Appropriate file system permissions + +## Installation + +1. Create a capability bundle (ZIP file) containing all JSX files: + ``` + Archive.zip + ├── manifest.json + ├── hyphenate.jsx + ├── utils.jsx + ├── errors.jsx + └── json2.jsx + ``` + Note: All files must be zipped directly without a parent folder. + +2. Register your script following the [InDesign Capabilities API guide](https://developer.adobe.com/firefly-services/docs/indesign-apis/how-tos/working-with-capabilities-api/) + +## Usage + +### Required Parameters + +Provide the following JSON parameters to run the script: + +```json +{ + "assets": [ + { + "source": { + "type": "HTTP_GET", + "url": "" + }, + "destination": "sample.indd" + } + ], + "params": { + "targetDocument": "sample.indd", + "outputPath": "hyphenate.indd", + "labels": ["MainPara", "Heading"], // Optional: If not provided, affects all text frames + "hyphenate": "true" // Can be "true" or "false" + } +} +``` + +### Parameter Details +- `labels`: Array of text frame labels to process (optional) +- `hyphenate`: Boolean value to enable/disable hyphenation +- `targetDocument`: Input InDesign document path +- `outputPath`: Output document path + +## Logging and Monitoring + +### Log File +The script generates detailed logs in `LogFile.txt` including: +- Document processing steps +- Text frame identification +- Hyphenation operations +- Performance metrics +- Error messages and warnings + +### Performance Metrics +- Document opening time +- Link update time +- Hyphenation processing time +- Total execution duration + +## Error Handling + +The script includes comprehensive error handling for various scenarios: +- Parameter validation errors +- Document processing errors +- Text frame identification issues +- Link management problems +- File system operations + +Each error includes: +- Error code +- Detailed error message +- Context information + +## Support + +For additional information and support: +- Refer to the [Adobe InDesign APIs documentation](https://developer.adobe.com/firefly-services/docs/indesign-apis/) +- Check the error messages in the log file for troubleshooting +- Ensure all prerequisites are met before running the script diff --git a/SampleScripts/hyphenation/errors.jsx b/SampleScripts/ExtendScript/hyphenation/errors.jsx similarity index 100% rename from SampleScripts/hyphenation/errors.jsx rename to SampleScripts/ExtendScript/hyphenation/errors.jsx diff --git a/SampleScripts/hyphenation/hyphenate.jsx b/SampleScripts/ExtendScript/hyphenation/hyphenate.jsx similarity index 100% rename from SampleScripts/hyphenation/hyphenate.jsx rename to SampleScripts/ExtendScript/hyphenation/hyphenate.jsx diff --git a/SampleScripts/balanceRaggedLines/json2.jsx b/SampleScripts/ExtendScript/hyphenation/json2.jsx similarity index 100% rename from SampleScripts/balanceRaggedLines/json2.jsx rename to SampleScripts/ExtendScript/hyphenation/json2.jsx diff --git a/SampleScripts/hyphenation/manifest.json b/SampleScripts/ExtendScript/hyphenation/manifest.json similarity index 100% rename from SampleScripts/hyphenation/manifest.json rename to SampleScripts/ExtendScript/hyphenation/manifest.json diff --git a/SampleScripts/hyphenation/utils.jsx b/SampleScripts/ExtendScript/hyphenation/utils.jsx similarity index 100% rename from SampleScripts/hyphenation/utils.jsx rename to SampleScripts/ExtendScript/hyphenation/utils.jsx diff --git a/SampleScripts/ExtendScript/idmlConversion/README.md b/SampleScripts/ExtendScript/idmlConversion/README.md new file mode 100644 index 0000000..75e2a1e --- /dev/null +++ b/SampleScripts/ExtendScript/idmlConversion/README.md @@ -0,0 +1,123 @@ +# IDML Conversion Capability (ExtendScript) + +This directory contains an ExtendScript implementation for converting InDesign documents to IDML format. This is the ExtendScript version of the IDML conversion capability, designed to work with InDesign versions 17.0.0 and above. + +## Directory Contents + +``` +Example/ +├── manifest.json # Capability manifest file +├── sample.jsx # Main script implementation +├── utils.jsx # Utility functions +├── errors.jsx # Error handling definitions +└── json2.jsx # JSON parsing utilities +``` + +## Files Description + +### 1. `manifest.json` +The manifest file defines the capability configuration: +- Supports InDesign versions 17.0.0 and above +- Implements an ExtendScript capability entry point +- Version: 0.0.1 + +### 2. `sample.jsx` +The main script file that implements the IDML conversion functionality. Features include: +- InDesign document to IDML conversion +- Document link management +- Asset handling and processing +- Performance monitoring +- Comprehensive error handling +- Logging capabilities + +### 3. Helper Files +- `utils.jsx`: Contains utility functions for document processing and logging +- `errors.jsx`: Defines error types and messages for consistent error handling +- `json2.jsx`: Provides JSON parsing and manipulation utilities + +## Prerequisites + +- Adobe InDesign (version 17.0.0 or higher) +- ExtendScript environment +- Access to InDesign Services +- Appropriate file system permissions + +## Installation + +1. Create a capability bundle (ZIP file) containing all JSX files: + ``` + Archive.zip + ├── manifest.json + ├── sample.jsx + ├── utils.jsx + ├── errors.jsx + └── json2.jsx + ``` + Note: All files must be zipped directly without a parent folder. + +2. Register your script following the [InDesign Capabilities API guide](https://developer.adobe.com/firefly-services/docs/indesign-apis/how-tos/working-with-capabilities-api/) + +## Usage + +### Required Parameters + +Provide the following JSON parameters to run the script: + +```json +{ + "assets": [ + { + "source": { + "type": "HTTP_GET", + "url": "" + }, + "destination": "sample.indd" + } + ], + "params": { + "targetDocument": "sample.indd", + "outputPath": "converted.idml" + } +} +``` + +### Parameter Details +- `targetDocument`: Input InDesign document path +- `outputPath`: Output IDML file path + +## Logging and Monitoring + +### Log File +The script generates detailed logs in `LogFile.txt` including: +- Document processing steps +- IDML conversion progress +- Asset processing status +- Performance metrics +- Error messages and warnings + +### Performance Metrics +- Document opening time +- IDML conversion duration +- Asset processing time +- Total execution time + +## Error Handling + +The script includes comprehensive error handling for various scenarios: +- Parameter validation errors +- Document processing errors +- IDML conversion failures +- Asset handling issues +- File system operations + +Each error includes: +- Error code +- Detailed error message +- Context information + +## Support + +For additional information and support: +- Refer to the [Adobe InDesign APIs documentation](https://developer.adobe.com/firefly-services/docs/indesign-apis/) +- Check the error messages in the log file for troubleshooting +- Ensure all prerequisites are met before running the script diff --git a/Example/errors.jsx b/SampleScripts/ExtendScript/idmlConversion/errors.jsx similarity index 100% rename from Example/errors.jsx rename to SampleScripts/ExtendScript/idmlConversion/errors.jsx diff --git a/SampleScripts/hyphenation/json2.jsx b/SampleScripts/ExtendScript/idmlConversion/json2.jsx similarity index 100% rename from SampleScripts/hyphenation/json2.jsx rename to SampleScripts/ExtendScript/idmlConversion/json2.jsx diff --git a/Example/manifest.json b/SampleScripts/ExtendScript/idmlConversion/manifest.json similarity index 100% rename from Example/manifest.json rename to SampleScripts/ExtendScript/idmlConversion/manifest.json diff --git a/Example/sample.jsx b/SampleScripts/ExtendScript/idmlConversion/sample.jsx similarity index 100% rename from Example/sample.jsx rename to SampleScripts/ExtendScript/idmlConversion/sample.jsx diff --git a/Example/utils.jsx b/SampleScripts/ExtendScript/idmlConversion/utils.jsx similarity index 100% rename from Example/utils.jsx rename to SampleScripts/ExtendScript/idmlConversion/utils.jsx diff --git a/SampleScripts/UXP/.DS_Store b/SampleScripts/UXP/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..1b2be847f30a5180ffee0a5e1beb3d4ad7d3a8cd GIT binary patch literal 6148 zcmeHKOHRZv41I=K6d|Emvdk6c24yHGmLXt19D9Y?vF=5n?;>)Mqi|ENU#=-+l#wQRa2{fdjH=e?Krx1-H5^{-=ASL=Db zT6&>_fnXpQ2nK?I%?$9)Rw?!kLk9!FKrrydfSwPDO|f+B4fD}Kl}iAkKBKeH)>=Yi zl4I%E8*+vs7D}{G@e)HUoa4#;O2^*N!XaLKh=2LBc#-^duAj0yq%;g23es75p*?s6u%qY5VV<=4 bq&EFZ$KFs^(eWHkjE6ubBvdf)2Ml}wh@(0B literal 0 HcmV?d00001 diff --git a/SampleScripts/idmlConversion/.DS_Store b/SampleScripts/UXP/idmlConversion/.DS_Store similarity index 100% rename from SampleScripts/idmlConversion/.DS_Store rename to SampleScripts/UXP/idmlConversion/.DS_Store diff --git a/SampleScripts/UXP/idmlConversion/README.md b/SampleScripts/UXP/idmlConversion/README.md new file mode 100644 index 0000000..040b3a8 --- /dev/null +++ b/SampleScripts/UXP/idmlConversion/README.md @@ -0,0 +1,108 @@ +# IDML Export Capability + +This directory contains a UXP script for exporting InDesign documents to IDML format using Adobe InDesign's scripting capabilities. The script is designed to work with InDesign versions 16.0.1 through 20.4.0. + +## Directory Contents + +``` +idmlConversion/ +├── manifest.json # Capability manifest file +└── idml.idjs # Main script implementation +``` + +## Files Description + +### 1. `manifest.json` +The manifest file defines the capability configuration: +- Supports InDesign versions 16.0.1 to 20.4.0 +- Implements a UXP capability entry point +- Version: 1.0.4 + +### 2. `idml.idjs` +The main script file that implements the IDML export functionality. Features include: +- Robust error handling with detailed error messages +- Comprehensive logging system +- Document link management +- Asset handling and processing +- Performance monitoring +- File system operations using UXP storage API + +## Prerequisites + +- Adobe InDesign (version 16.0.1 or higher) +- UXP scripting environment +- Access to InDesign Services +- Appropriate file system permissions + +## Installation + +1. Create a capability bundle (ZIP file) with the following structure: + ``` + Archive.zip + ├── manifest.json + └── idml.idjs + ``` + Note: Files should be zipped directly without a parent folder. + +2. Register your script following the [InDesign Capabilities API guide](https://developer.adobe.com/firefly-services/docs/indesign-apis/how-tos/working-with-capabilities-api/) + +## Usage + +### Required Parameters + +Provide the following JSON parameters to run the script: + +```json +{ + "assets": [ + { + "source": { + "type": "HTTP_GET", + "url": "" + }, + "destination": "sample.indd" + } + ], + "params": { + "targetDocument": "sample.indd", + "outputPath": "converted.idml" + } +} +``` + +### Error Handling + +The script includes comprehensive error handling for various scenarios: +- Parameter validation errors +- File system operations +- Document processing errors +- Link management issues +- Asset handling problems + +Each error includes: +- Error code +- Detailed error message +- Context information + +## Logging and Monitoring + +### Log File +The script generates detailed logs in `LogFile.txt` including: +- Document processing steps +- Performance metrics +- Error messages and warnings +- Asset processing status +- Link management operations + +### Performance Metrics +- Document opening time +- Link update time +- Processing duration +- Asset handling time + +## Support + +For additional information and support: +- Refer to the [Adobe InDesign APIs documentation](https://developer.adobe.com/firefly-services/docs/indesign-apis/) +- Check the error messages in the log file for troubleshooting +- Ensure all prerequisites are met before running the script diff --git a/SampleScripts/idmlConversion/idml.idjs b/SampleScripts/UXP/idmlConversion/idml.idjs similarity index 100% rename from SampleScripts/idmlConversion/idml.idjs rename to SampleScripts/UXP/idmlConversion/idml.idjs diff --git a/SampleScripts/idmlConversion/manifest.json b/SampleScripts/UXP/idmlConversion/manifest.json similarity index 100% rename from SampleScripts/idmlConversion/manifest.json rename to SampleScripts/UXP/idmlConversion/manifest.json diff --git a/SampleScripts/balanceRaggedLines/README.md b/SampleScripts/balanceRaggedLines/README.md deleted file mode 100644 index c38a2b0..0000000 --- a/SampleScripts/balanceRaggedLines/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Balance Ragged Lines Capability - -This project provides a script (`balanceRaggedLines.jsx`) and its corresponding manifest (`manifest.json`) to enable or disable balancing ragged lines in text frames within Adobe InDesign documents using ExtendScript. - -## Features - -- Opens an InDesign document from a specified path. -- Identifies text frames based on provided labels. -- Balances or unbalances ragged lines for paragraphs in the identified text frames. -- Logs processing steps and performance metrics. -- Handles errors and warnings during the process. - -## File Structure - -- **`balanceRaggedLines.jsx`**: The main script that implements the balancing functionality. -- **`manifest.json`**: The manifest file that defines the capability and its integration with Adobe InDesign. - -## Execution - -1. Place the `balanceRaggedLines.jsx` script and `manifest.json` in the appropriate folder parallelly. -2. Create a capability bundle, which is a ZIP file with a specific structure. The files should be zipped directly without a parent folder. -``` -Archive.zip -|------ manifest.json -|------ balanceRaggedLines.jsx -``` -3. Follow this [guide](https://developer.adobe.com/firefly-services/docs/indesign-apis/how-tos/working-with-capabilities-api/) to register your script. - -4. Provide the required parameters to run the script: - -```json -{ - "assets": [ - { - "source": { - "type": "HTTP_GET", - "url": "" - }, - "destination": "sample.indd" - } - ], - "params": { - "targetDocument": "sample.indd", - "outputPath": "balanced.indd", - "labels": ["MainPara","Heading"], // If no Labels are provided, this script will balance ragged lines or unbalance ragged lines all of the text frames in the document. - "balance": "true" //Can be "balance":false as well. - } -} -``` - -## Logging -The script logs processing steps and performance metrics to a log file (LogFile.txt) in the working directory. Logs include: - -- Time taken to open the document. -- Time taken to update links. -- Time taken to balance ragged lines. - -## Error Handling -The script captures and logs errors during execution. Common errors include: - -- Missing or invalid document paths. -- Issues with updating links. -- Processing failures. diff --git a/SampleScripts/hyphenation/README.md b/SampleScripts/hyphenation/README.md deleted file mode 100644 index 09fe429..0000000 --- a/SampleScripts/hyphenation/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Hyphenate Capability - -This project provides a script (`hyphenate.jsx`) and its corresponding manifest (`manifest.json`) to enable or disable hyphenation in text frames within Adobe InDesign documents using ExtendScript. - -## Features - -- Opens an InDesign document from a specified path. -- Identifies text frames based on provided labels. -- Hyphenates or removes Hyphenation for paragraphs in the identified text frames. -- Logs processing steps and performance metrics. -- Handles errors and warnings during the process. - -## File Structure - -- **`hyphenate.jsx`**: The main script that implements the balancing functionality. -- **`manifest.json`**: The manifest file that defines the capability and its integration with Adobe InDesign. - -## Execution - -1. Place the `hyphenate.jsx` script and `manifest.json` in the appropriate folder parallelly. -2. Create a capability bundle, which is a ZIP file with a specific structure. The files should be zipped directly without a parent folder. -``` -Archive.zip -|------ manifest.json -|------ hyphenate.jsx -``` -3. Follow this [guide](https://developer.adobe.com/firefly-services/docs/indesign-apis/how-tos/working-with-capabilities-api/) to register your script. - -4. Provide the required parameters to run the script: - -```json -{ - "assets": [ - { - "source": { - "type": "HTTP_GET", - "url": "" - }, - "destination": "sample.indd" - } - ], - "params": { - "targetDocument": "sample.indd", - "outputPath": "hyphenate.indd", - "labels": ["MainPara","Heading"], // If no Labels are provided, this script will add or remove hyphenation in all of the text frames in the document. - "hyphenate": "true" //Can be "hyphenate":false as well. - } -} -``` - -## Logging -The script logs processing steps and performance metrics to a log file (LogFile.txt) in the working directory. Logs include: - -- Time taken to open the document. -- Time taken to update links. -- Time taken to add/remove hyphenation - -## Error Handling -The script captures and logs errors during execution. Common errors include: - -- Missing or invalid document paths. -- Issues with updating links. -- Processing failures. diff --git a/SampleScripts/idmlConversion/README.md b/SampleScripts/idmlConversion/README.md deleted file mode 100644 index b806fc8..0000000 --- a/SampleScripts/idmlConversion/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# IDML Export Capability - -This project provides a script (`idml.idjs`) and its corresponding manifest (`manifest.json`) to enable exporting InDesign documents to IDML format using Adobe InDesign's UXP scripting capabilities using InDesign APIs. - -## Features - -- Opens an InDesign document from a specified path. -- Updates document links to ensure all assets are up-to-date. -- Exports the document to IDML format. -- Logs processing steps and performance metrics. -- Handles errors and warnings during the export process. - -## File Structure - -- **`idml.idjs`**: The main script that implements the IDML export functionality. -- **`manifest.json`**: The manifest file that defines the capability and its integration with Adobe InDesign and InDesign Services. - -## Execution - -1. Place the `idml.idjs` script and `manifest.json` in the appropriate folder parallelly. -2. You need to create a capability bundle, which is a ZIP file with a specific structure. The files should be zipped directly without a parent folder. - -``` -Archive.zip -|------ manifest.json -|------ idml.idjs -``` -3. Follow this [guide](https://developer.adobe.com/firefly-services/docs/indesign-apis/how-tos/working-with-capabilities-api/) to Register your script - -4. Provide the required parameters to run the script: - -```json -{ - "assets": [ - { - "source": { - "type": "HTTP_GET", - "url": "" - }, - "destination": "sample.indd" - } - ], - "params": { - "targetDocument": "sample.indd", - "outputPath": "converted.idml" - } -} -``` - -## Logging -The script logs processing steps and performance metrics to a log file (LogFile.txt) in the working directory. Logs include: - -- Time taken to open the document. -- Time taken to update links. -- Time taken to balance ragged lines. - -## Error Handling -The script captures and logs errors during execution. Common errors include: - -- Missing or invalid document paths. -- Issues with updating links. -- Processing failures. \ No newline at end of file From e2404e48fffbe36228ba12fc374b713c045a4e58 Mon Sep 17 00:00:00 2001 From: Rhythm Jain Date: Wed, 2 Apr 2025 11:20:19 +0530 Subject: [PATCH 3/8] Relinking the sample scripts Folder --- samples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples.md b/samples.md index 0f263cf..e51e687 100644 --- a/samples.md +++ b/samples.md @@ -3,7 +3,7 @@ The below code snippets are for demonstration purpose. Please feel free to downl Just remember you will need to have the assets stored in one of the accepted external storage. ## Sample capability bundle -A sample capability bundle can be found at [Example](Example/). +Sample capability bundles can be found at [SampleScripts](SampleScripts/). A manifest.json must be bundled inside the capability zip. A typical manifest would look like: ```json { From 029fba4a22b19a9d9f7995830c27b76418e6889d Mon Sep 17 00:00:00 2001 From: Rhythm Jain Date: Wed, 2 Apr 2025 11:50:26 +0530 Subject: [PATCH 4/8] Delete .DS_Store --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 8adab65c1b7a2025d75b59e45246761d82c42b82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKT}s424F2M-upq)d>f2mlZ%~(df?hy(S5#1j`gT=q+ol_&e!#kE%W_k<8(J@3j%Vku?{61}<2UW@haTPCUPgoDLJbCj zfnXpQ2nN1ofOoda;>0leU?3O>2EG{3^C7V*7LL7PK04Ut5`d`B=q%K=mQbJMSUC2E zT%m}$63tb-#1M1md~&+*Ze5ITlYRs@7lz6#ipWmy($#yqelP-dXAjsMO#m*)2?vr V4K<3+XLn*e1TrDf1p~jpzz3qDH01yQ From 7e940386534c42c68bf7f7e59b89084eb1806e2b Mon Sep 17 00:00:00 2001 From: Rhythm Jain Date: Thu, 3 Apr 2025 12:48:21 +0530 Subject: [PATCH 5/8] Removing .DS_Store Files --- SampleScripts/.DS_Store | Bin 6148 -> 0 bytes SampleScripts/ExtendScript/.DS_Store | Bin 6148 -> 0 bytes SampleScripts/UXP/.DS_Store | Bin 6148 -> 0 bytes SampleScripts/UXP/idmlConversion/.DS_Store | Bin 6148 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 SampleScripts/.DS_Store delete mode 100644 SampleScripts/ExtendScript/.DS_Store delete mode 100644 SampleScripts/UXP/.DS_Store delete mode 100644 SampleScripts/UXP/idmlConversion/.DS_Store diff --git a/SampleScripts/.DS_Store b/SampleScripts/.DS_Store deleted file mode 100644 index 811eaa9f6af19e95a43143bad23adb0aa7d87b40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5dOC9A)<#K1uuK_CVhig(z_4@kJ3g|XiSg_9)nNhQ+V_t)EDp#{APEE zDQN{SqA~-s-)4R`lP{3T0C3a0Vgif-Bviq{4wnrge$k!`)FMt7&bUE=91Gmyx)*JR z-^hTxyFJWsj}g=*Az7=0z z^=cX76lXZuP^EghH9fu`zGzLY_p^e&cfTBaCe^XLpWR-(N<%?7+7au7%#`t|7Y9h|MeuhG6sx+f5m_sWqCH`BWZ8# wJ)HDfPd%rKNL&-KP2ncAV#Z1<-lqnk-OGlUDmD>mq1cZ=purYn;71ww0QSRA%m4rY diff --git a/SampleScripts/ExtendScript/.DS_Store b/SampleScripts/ExtendScript/.DS_Store deleted file mode 100644 index 33c18178fd3ddb0dc6fbaaa12335aa3c36881909..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKJxT*X6#hm724fMdET`}SxxpGXot3$O=7&VcW&=hr-C`qH>Jh9`c?%0cYfmBM z27Yg5&|TNXU?C#!!F%5@^WMxiAI#1U0MkAnw16gnI$f}~&G3cExOmAnLeCD-xH+ci zV~8W1U{uQ14pl%E_-hKt+1*8tCw4;5pWhKr@&Jc8W$zGYlToYNPm`J7z)Enfx+IO` z-X!fYzkYT5y1#sUI9R`S@Z;LqILFsmE{QF4aKT+Bm~pqm;*MUP${Bg?cgyR=<+J1d zX`UqgU|F42i54( z(x3{c0{>G1Q;WNCn|lj)YiXBq*IN29T}<*akFJCvT*s~LXt19D9Y?vF=5n?;>)Mqi|ENU#=-+l#wQRa2{fdjH=e?Krx1-H5^{-=ASL=Db zT6&>_fnXpQ2nK?I%?$9)Rw?!kLk9!FKrrydfSwPDO|f+B4fD}Kl}iAkKBKeH)>=Yi zl4I%E8*+vs7D}{G@e)HUoa4#;O2^*N!XaLKh=2LBc#-^duAj0yq%;g23es75p*?s6u%qY5VV<=4 bq&EFZ$KFs^(eWHkjE6ubBvdf)2Ml}wh@(0B diff --git a/SampleScripts/UXP/idmlConversion/.DS_Store b/SampleScripts/UXP/idmlConversion/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Thu, 3 Apr 2025 12:57:30 +0530 Subject: [PATCH 6/8] Updating Readme for Header and versioning --- .../ExtendScript/balanceRaggedLines/README.md | 6 ++-- .../balanceRaggedLines/balanceRaggedLines.jsx | 30 ++++++++--------- .../balanceRaggedLines/errors.jsx | 30 ++++++++--------- .../balanceRaggedLines/manifest.json | 4 +-- .../ExtendScript/balanceRaggedLines/utils.jsx | 32 +++++++++---------- .../ExtendScript/hyphenation/README.md | 6 ++-- .../ExtendScript/hyphenation/errors.jsx | 2 +- .../ExtendScript/hyphenation/hyphenate.jsx | 2 +- .../ExtendScript/hyphenation/manifest.json | 4 +-- .../ExtendScript/hyphenation/utils.jsx | 2 +- .../ExtendScript/idmlConversion/README.md | 8 ++--- .../ExtendScript/idmlConversion/errors.jsx | 21 ++++++++++-- .../ExtendScript/idmlConversion/manifest.json | 4 +-- .../ExtendScript/idmlConversion/sample.jsx | 19 ++++++++++- .../ExtendScript/idmlConversion/utils.jsx | 21 ++++++++++++ SampleScripts/UXP/idmlConversion/README.md | 6 ++-- SampleScripts/UXP/idmlConversion/idml.idjs | 2 +- .../UXP/idmlConversion/manifest.json | 4 +-- 18 files changed, 129 insertions(+), 74 deletions(-) diff --git a/SampleScripts/ExtendScript/balanceRaggedLines/README.md b/SampleScripts/ExtendScript/balanceRaggedLines/README.md index c7604b3..8e0725a 100644 --- a/SampleScripts/ExtendScript/balanceRaggedLines/README.md +++ b/SampleScripts/ExtendScript/balanceRaggedLines/README.md @@ -1,6 +1,6 @@ # Balance Ragged Lines Capability -This directory contains a script for managing ragged line balancing in Adobe InDesign documents using ExtendScript. The script is designed to work with InDesign versions 16.0.1 through 20.2.0. +This directory contains a script for managing ragged line balancing in Adobe InDesign documents using ExtendScript. The script is designed to work with InDesign versions 16.0.1 and above. ## Directory Contents @@ -17,9 +17,9 @@ balanceRaggedLines/ ### 1. `manifest.json` The manifest file defines the capability configuration: -- Supports InDesign versions 16.0.1 to 20.2.0 +- Supports InDesign versions 16.0.1 and above. - Implements an ExtendScript capability entry point -- Version: 1.0.3 +- Version: 1.0.0 ### 2. `balanceRaggedLines.jsx` The main script file that implements the ragged line balancing functionality. Features include: diff --git a/SampleScripts/ExtendScript/balanceRaggedLines/balanceRaggedLines.jsx b/SampleScripts/ExtendScript/balanceRaggedLines/balanceRaggedLines.jsx index 7358376..c740bd9 100644 --- a/SampleScripts/ExtendScript/balanceRaggedLines/balanceRaggedLines.jsx +++ b/SampleScripts/ExtendScript/balanceRaggedLines/balanceRaggedLines.jsx @@ -1,19 +1,19 @@ /************************************************************************* -* ADOBE CONFIDENTIAL -* ___________________ -* -* Copyright 2021 Adobe -* All Rights Reserved. -* -* NOTICE: All information contained herein is, and remains -* the property of Adobe and its suppliers, if any. The intellectual -* and technical concepts contained herein are proprietary to Adobe -* and its suppliers and are protected by all applicable intellectual -* property laws, including trade secret and copyright laws. -* Dissemination of this information or reproduction of this material -* is strictly forbidden unless prior written permission is obtained -* from Adobe. -**************************************************************************/ + * ADOBE CONFIDENTIAL + * ___________________ + * + * Copyright 2025 Adobe + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Adobe and its suppliers, if any. The intellectual + * and technical concepts contained herein are proprietary to Adobe + * and its suppliers and are protected by all applicable intellectual + * property laws, including trade secret and copyright laws. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Adobe. + **************************************************************************/ // Following are the file inclusions and not comments. diff --git a/SampleScripts/ExtendScript/balanceRaggedLines/errors.jsx b/SampleScripts/ExtendScript/balanceRaggedLines/errors.jsx index 21b34b9..b5cd221 100644 --- a/SampleScripts/ExtendScript/balanceRaggedLines/errors.jsx +++ b/SampleScripts/ExtendScript/balanceRaggedLines/errors.jsx @@ -1,19 +1,19 @@ /************************************************************************* -* ADOBE CONFIDENTIAL -* ___________________ -* -* Copyright 2021 Adobe -* All Rights Reserved. -* -* NOTICE: All information contained herein is, and remains -* the property of Adobe and its suppliers, if any. The intellectual -* and technical concepts contained herein are proprietary to Adobe -* and its suppliers and are protected by all applicable intellectual -* property laws, including trade secret and copyright laws. -* Dissemination of this information or reproduction of this material -* is strictly forbidden unless prior written permission is obtained -* from Adobe. -**************************************************************************/ + * ADOBE CONFIDENTIAL + * ___________________ + * + * Copyright 2025 Adobe + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Adobe and its suppliers, if any. The intellectual + * and technical concepts contained herein are proprietary to Adobe + * and its suppliers and are protected by all applicable intellectual + * property laws, including trade secret and copyright laws. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Adobe. + **************************************************************************/ // errors.jsx // Error constants are defined here. diff --git a/SampleScripts/ExtendScript/balanceRaggedLines/manifest.json b/SampleScripts/ExtendScript/balanceRaggedLines/manifest.json index d9fdb08..b11480b 100644 --- a/SampleScripts/ExtendScript/balanceRaggedLines/manifest.json +++ b/SampleScripts/ExtendScript/balanceRaggedLines/manifest.json @@ -3,10 +3,10 @@ "name": "balanceRaggedLines", "host": { "app": "indesign", - "maxVersion": "20.2.0", + "maxVersion": "99.9.9", "minVersion": "16.0.1" }, - "version": "1.0.3", + "version": "1.0.0", "apiEntryPoints": [ { "path": "balanceRaggedLines.jsx", diff --git a/SampleScripts/ExtendScript/balanceRaggedLines/utils.jsx b/SampleScripts/ExtendScript/balanceRaggedLines/utils.jsx index ad08214..2974cda 100644 --- a/SampleScripts/ExtendScript/balanceRaggedLines/utils.jsx +++ b/SampleScripts/ExtendScript/balanceRaggedLines/utils.jsx @@ -1,19 +1,19 @@ /************************************************************************* -* ADOBE CONFIDENTIAL -* ___________________ -* -* Copyright 2021 Adobe -* All Rights Reserved. -* -* NOTICE: All information contained herein is, and remains -* the property of Adobe and its suppliers, if any. The intellectual -* and technical concepts contained herein are proprietary to Adobe -* and its suppliers and are protected by all applicable intellectual -* property laws, including trade secret and copyright laws. -* Dissemination of this information or reproduction of this material -* is strictly forbidden unless prior written permission is obtained -* from Adobe. -**************************************************************************/ + * ADOBE CONFIDENTIAL + * ___________________ + * + * Copyright 2025 Adobe + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Adobe and its suppliers, if any. The intellectual + * and technical concepts contained herein are proprietary to Adobe + * and its suppliers and are protected by all applicable intellectual + * property laws, including trade secret and copyright laws. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Adobe. + **************************************************************************/ // utils.jsx // Utils functions which can be used across. @@ -607,7 +607,7 @@ UTILS.OpenLogFileHandle = function () { exists = true } UTILS.Log('Creating log file at ' + UTILS.GetFullPath(UTILS.logFilePath)) - + // Open the log file in append mode if it exists, otherwise create a new file if (exists) { logFileObject.close() diff --git a/SampleScripts/ExtendScript/hyphenation/README.md b/SampleScripts/ExtendScript/hyphenation/README.md index 1d41780..6101c68 100644 --- a/SampleScripts/ExtendScript/hyphenation/README.md +++ b/SampleScripts/ExtendScript/hyphenation/README.md @@ -1,6 +1,6 @@ # Hyphenate Capability -This directory contains a script for managing hyphenation in Adobe InDesign documents using ExtendScript. The script is designed to work with InDesign versions 16.0.1 through 20.2.0. +This directory contains a script for managing hyphenation in Adobe InDesign documents using ExtendScript. The script is designed to work with InDesign versions 16.0.1 and above. ## Directory Contents @@ -17,9 +17,9 @@ hyphenation/ ### 1. `manifest.json` The manifest file defines the capability configuration: -- Supports InDesign versions 16.0.1 to 20.2.0 +- Supports InDesign versions 16.0.1 and above - Implements an ExtendScript capability entry point -- Version: 1.0.3 +- Version: 1.0.0 ### 2. `hyphenate.jsx` The main script file that implements the hyphenation functionality. Features include: diff --git a/SampleScripts/ExtendScript/hyphenation/errors.jsx b/SampleScripts/ExtendScript/hyphenation/errors.jsx index 21b34b9..e90052c 100644 --- a/SampleScripts/ExtendScript/hyphenation/errors.jsx +++ b/SampleScripts/ExtendScript/hyphenation/errors.jsx @@ -2,7 +2,7 @@ * ADOBE CONFIDENTIAL * ___________________ * -* Copyright 2021 Adobe +* Copyright 2025 Adobe * All Rights Reserved. * * NOTICE: All information contained herein is, and remains diff --git a/SampleScripts/ExtendScript/hyphenation/hyphenate.jsx b/SampleScripts/ExtendScript/hyphenation/hyphenate.jsx index f6fdfac..8e643ac 100644 --- a/SampleScripts/ExtendScript/hyphenation/hyphenate.jsx +++ b/SampleScripts/ExtendScript/hyphenation/hyphenate.jsx @@ -2,7 +2,7 @@ * ADOBE CONFIDENTIAL * ___________________ * -* Copyright 2021 Adobe +* Copyright 2025 Adobe * All Rights Reserved. * * NOTICE: All information contained herein is, and remains diff --git a/SampleScripts/ExtendScript/hyphenation/manifest.json b/SampleScripts/ExtendScript/hyphenation/manifest.json index aa31c86..46bf820 100644 --- a/SampleScripts/ExtendScript/hyphenation/manifest.json +++ b/SampleScripts/ExtendScript/hyphenation/manifest.json @@ -3,10 +3,10 @@ "name": "hyphenate", "host": { "app": "indesign", - "maxVersion": "20.2.0", + "maxVersion": "99.9.9", "minVersion": "16.0.1" }, - "version": "1.0.3", + "version": "1.0.0", "apiEntryPoints": [ { "path": "hyphenate.jsx", diff --git a/SampleScripts/ExtendScript/hyphenation/utils.jsx b/SampleScripts/ExtendScript/hyphenation/utils.jsx index ad08214..817c332 100644 --- a/SampleScripts/ExtendScript/hyphenation/utils.jsx +++ b/SampleScripts/ExtendScript/hyphenation/utils.jsx @@ -2,7 +2,7 @@ * ADOBE CONFIDENTIAL * ___________________ * -* Copyright 2021 Adobe +* Copyright 2025 Adobe * All Rights Reserved. * * NOTICE: All information contained herein is, and remains diff --git a/SampleScripts/ExtendScript/idmlConversion/README.md b/SampleScripts/ExtendScript/idmlConversion/README.md index 75e2a1e..48081f2 100644 --- a/SampleScripts/ExtendScript/idmlConversion/README.md +++ b/SampleScripts/ExtendScript/idmlConversion/README.md @@ -1,6 +1,6 @@ # IDML Conversion Capability (ExtendScript) -This directory contains an ExtendScript implementation for converting InDesign documents to IDML format. This is the ExtendScript version of the IDML conversion capability, designed to work with InDesign versions 17.0.0 and above. +This directory contains an ExtendScript implementation for converting InDesign documents to IDML format. This is the ExtendScript version of the IDML conversion capability, designed to work with InDesign versions 16.0.1 and above. ## Directory Contents @@ -17,9 +17,9 @@ Example/ ### 1. `manifest.json` The manifest file defines the capability configuration: -- Supports InDesign versions 17.0.0 and above +- Supports InDesign versions 16.0.1 and above - Implements an ExtendScript capability entry point -- Version: 0.0.1 +- Version: 1.0.0 ### 2. `sample.jsx` The main script file that implements the IDML conversion functionality. Features include: @@ -37,7 +37,7 @@ The main script file that implements the IDML conversion functionality. Features ## Prerequisites -- Adobe InDesign (version 17.0.0 or higher) +- Adobe InDesign (version 16.0.1 or higher) - ExtendScript environment - Access to InDesign Services - Appropriate file system permissions diff --git a/SampleScripts/ExtendScript/idmlConversion/errors.jsx b/SampleScripts/ExtendScript/idmlConversion/errors.jsx index 2bf324f..459bb79 100644 --- a/SampleScripts/ExtendScript/idmlConversion/errors.jsx +++ b/SampleScripts/ExtendScript/idmlConversion/errors.jsx @@ -1,11 +1,28 @@ -// Error constants are defined here. -/* eslint-disable no-unused-vars */ +/************************************************************************* +* ADOBE CONFIDENTIAL +* ___________________ +* +* Copyright 2025 Adobe +* All Rights Reserved. +* +* NOTICE: All information contained herein is, and remains +* the property of Adobe and its suppliers, if any. The intellectual +* and technical concepts contained herein are proprietary to Adobe +* and its suppliers and are protected by all applicable intellectual +* property laws, including trade secret and copyright laws. +* Dissemination of this information or reproduction of this material +* is strictly forbidden unless prior written permission is obtained +* from Adobe. +**************************************************************************/ +// errors.jsx +// Error constants are defined here. /* This document list all the error which are possible from the scripts. The error object is built such that it has an error code and some error strings. The error strings are constructed in a way such that the first string is default and a string literal. It is to be returned anyhow. The subsequent strings can be strings having '^1' as placeholder replacement string. This replacement string can be replaced with relevant information. Based on the information available the final error message can be created. */ +/* eslint-disable no-unused-vars */ var ErrorReplacementString = '^1' diff --git a/SampleScripts/ExtendScript/idmlConversion/manifest.json b/SampleScripts/ExtendScript/idmlConversion/manifest.json index d5f53e8..a64daac 100644 --- a/SampleScripts/ExtendScript/idmlConversion/manifest.json +++ b/SampleScripts/ExtendScript/idmlConversion/manifest.json @@ -2,10 +2,10 @@ "manifestVersion": "1.0.0", "id": "Unique id for the capability", "name": "Name of the capability", - "version": "0.0.1", + "version": "1.0.0", "host": { "app": "indesign", - "minVersion": "17.0.0", + "minVersion": "16.0.1", "maxVersion": "99.9.9" }, "apiEntryPoints": [ diff --git a/SampleScripts/ExtendScript/idmlConversion/sample.jsx b/SampleScripts/ExtendScript/idmlConversion/sample.jsx index e876e5d..cde1062 100644 --- a/SampleScripts/ExtendScript/idmlConversion/sample.jsx +++ b/SampleScripts/ExtendScript/idmlConversion/sample.jsx @@ -1,4 +1,21 @@ -// Example.jsx +/************************************************************************* +* ADOBE CONFIDENTIAL +* ___________________ +* +* Copyright 2025 Adobe +* All Rights Reserved. +* +* NOTICE: All information contained herein is, and remains +* the property of Adobe and its suppliers, if any. The intellectual +* and technical concepts contained herein are proprietary to Adobe +* and its suppliers and are protected by all applicable intellectual +* property laws, including trade secret and copyright laws. +* Dissemination of this information or reproduction of this material +* is strictly forbidden unless prior written permission is obtained +* from Adobe. +**************************************************************************/ + +// Example.jsx // This is a sample script which exports idml from an InDesign document. The expected input is as follows: // "params": { // "targetDocument": "doc.indd", diff --git a/SampleScripts/ExtendScript/idmlConversion/utils.jsx b/SampleScripts/ExtendScript/idmlConversion/utils.jsx index 6179587..1518a73 100644 --- a/SampleScripts/ExtendScript/idmlConversion/utils.jsx +++ b/SampleScripts/ExtendScript/idmlConversion/utils.jsx @@ -1,3 +1,24 @@ +/************************************************************************* + * ADOBE CONFIDENTIAL + * ___________________ + * + * Copyright 2025 Adobe + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Adobe and its suppliers, if any. The intellectual + * and technical concepts contained herein are proprietary to Adobe + * and its suppliers and are protected by all applicable intellectual + * property laws, including trade secret and copyright laws. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Adobe. + **************************************************************************/ +// utils.jsx + +// Utils functions which can be used across. +/* globals app, Errors, File, Folder, LinkStatus, SaveOptions */ + // Utils functions which can be used across. /* globals app, Errors, File, Folder, LinkStatus, SaveOptions */ diff --git a/SampleScripts/UXP/idmlConversion/README.md b/SampleScripts/UXP/idmlConversion/README.md index 040b3a8..2da5aae 100644 --- a/SampleScripts/UXP/idmlConversion/README.md +++ b/SampleScripts/UXP/idmlConversion/README.md @@ -1,6 +1,6 @@ # IDML Export Capability -This directory contains a UXP script for exporting InDesign documents to IDML format using Adobe InDesign's scripting capabilities. The script is designed to work with InDesign versions 16.0.1 through 20.4.0. +This directory contains a UXP script for exporting InDesign documents to IDML format using Adobe InDesign's scripting capabilities. The script is designed to work with InDesign versions 16.0.1 and above. ## Directory Contents @@ -14,9 +14,9 @@ idmlConversion/ ### 1. `manifest.json` The manifest file defines the capability configuration: -- Supports InDesign versions 16.0.1 to 20.4.0 +- Supports InDesign versions 16.0.1 and above - Implements a UXP capability entry point -- Version: 1.0.4 +- Version: 1.0.0 ### 2. `idml.idjs` The main script file that implements the IDML export functionality. Features include: diff --git a/SampleScripts/UXP/idmlConversion/idml.idjs b/SampleScripts/UXP/idmlConversion/idml.idjs index bdcf0c2..fc35416 100644 --- a/SampleScripts/UXP/idmlConversion/idml.idjs +++ b/SampleScripts/UXP/idmlConversion/idml.idjs @@ -2,7 +2,7 @@ * ADOBE CONFIDENTIAL * ___________________ * -* Copyright 2021 Adobe +* Copyright 2025 Adobe * All Rights Reserved. * * NOTICE: All information contained herein is, and remains diff --git a/SampleScripts/UXP/idmlConversion/manifest.json b/SampleScripts/UXP/idmlConversion/manifest.json index 14e4ddc..d148176 100644 --- a/SampleScripts/UXP/idmlConversion/manifest.json +++ b/SampleScripts/UXP/idmlConversion/manifest.json @@ -3,10 +3,10 @@ "name": "idml", "host": { "app": "indesign", - "maxVersion": "20.4.0", + "maxVersion": "99.9.9", "minVersion": "16.0.1" }, - "version": "1.0.4", + "version": "1.0.0", "apiEntryPoints": [ { "path": "idml.idjs", From 9aec7dbc48a9f4a17fcef407733e0a0fa7ecb00a Mon Sep 17 00:00:00 2001 From: Rhythm Jain Date: Thu, 3 Apr 2025 13:01:27 +0530 Subject: [PATCH 7/8] Removing HTTP_GET --- SampleScripts/ExtendScript/balanceRaggedLines/README.md | 1 - SampleScripts/ExtendScript/hyphenation/README.md | 1 - SampleScripts/ExtendScript/idmlConversion/README.md | 1 - SampleScripts/UXP/idmlConversion/README.md | 1 - 4 files changed, 4 deletions(-) diff --git a/SampleScripts/ExtendScript/balanceRaggedLines/README.md b/SampleScripts/ExtendScript/balanceRaggedLines/README.md index 8e0725a..79b96fd 100644 --- a/SampleScripts/ExtendScript/balanceRaggedLines/README.md +++ b/SampleScripts/ExtendScript/balanceRaggedLines/README.md @@ -67,7 +67,6 @@ Provide the following JSON parameters to run the script: "assets": [ { "source": { - "type": "HTTP_GET", "url": "" }, "destination": "sample.indd" diff --git a/SampleScripts/ExtendScript/hyphenation/README.md b/SampleScripts/ExtendScript/hyphenation/README.md index 6101c68..c9a3ec1 100644 --- a/SampleScripts/ExtendScript/hyphenation/README.md +++ b/SampleScripts/ExtendScript/hyphenation/README.md @@ -67,7 +67,6 @@ Provide the following JSON parameters to run the script: "assets": [ { "source": { - "type": "HTTP_GET", "url": "" }, "destination": "sample.indd" diff --git a/SampleScripts/ExtendScript/idmlConversion/README.md b/SampleScripts/ExtendScript/idmlConversion/README.md index 48081f2..f56d1b3 100644 --- a/SampleScripts/ExtendScript/idmlConversion/README.md +++ b/SampleScripts/ExtendScript/idmlConversion/README.md @@ -68,7 +68,6 @@ Provide the following JSON parameters to run the script: "assets": [ { "source": { - "type": "HTTP_GET", "url": "" }, "destination": "sample.indd" diff --git a/SampleScripts/UXP/idmlConversion/README.md b/SampleScripts/UXP/idmlConversion/README.md index 2da5aae..6b5c7eb 100644 --- a/SampleScripts/UXP/idmlConversion/README.md +++ b/SampleScripts/UXP/idmlConversion/README.md @@ -57,7 +57,6 @@ Provide the following JSON parameters to run the script: "assets": [ { "source": { - "type": "HTTP_GET", "url": "" }, "destination": "sample.indd" From cbe54278191ea168a2df5b2af5a2a13cf5a0a3aa Mon Sep 17 00:00:00 2001 From: Rhythm Jain Date: Thu, 3 Apr 2025 18:48:02 +0530 Subject: [PATCH 8/8] Updating Min Version of InDesign in UXP Sample Script (idml conversion) --- SampleScripts/UXP/idmlConversion/README.md | 6 +++--- SampleScripts/UXP/idmlConversion/manifest.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SampleScripts/UXP/idmlConversion/README.md b/SampleScripts/UXP/idmlConversion/README.md index 6b5c7eb..ff5c3fe 100644 --- a/SampleScripts/UXP/idmlConversion/README.md +++ b/SampleScripts/UXP/idmlConversion/README.md @@ -1,6 +1,6 @@ # IDML Export Capability -This directory contains a UXP script for exporting InDesign documents to IDML format using Adobe InDesign's scripting capabilities. The script is designed to work with InDesign versions 16.0.1 and above. +This directory contains a UXP script for exporting InDesign documents to IDML format using Adobe InDesign's scripting capabilities. The script is designed to work with InDesign versions 18.4.0 and above. ## Directory Contents @@ -14,7 +14,7 @@ idmlConversion/ ### 1. `manifest.json` The manifest file defines the capability configuration: -- Supports InDesign versions 16.0.1 and above +- Supports InDesign versions 18.4.0 and above - Implements a UXP capability entry point - Version: 1.0.0 @@ -29,7 +29,7 @@ The main script file that implements the IDML export functionality. Features inc ## Prerequisites -- Adobe InDesign (version 16.0.1 or higher) +- Adobe InDesign (version 18.4.0 or higher) - UXP scripting environment - Access to InDesign Services - Appropriate file system permissions diff --git a/SampleScripts/UXP/idmlConversion/manifest.json b/SampleScripts/UXP/idmlConversion/manifest.json index d148176..3426410 100644 --- a/SampleScripts/UXP/idmlConversion/manifest.json +++ b/SampleScripts/UXP/idmlConversion/manifest.json @@ -4,7 +4,7 @@ "host": { "app": "indesign", "maxVersion": "99.9.9", - "minVersion": "16.0.1" + "minVersion": "18.4.0" }, "version": "1.0.0", "apiEntryPoints": [