Created
January 14, 2025 07:36
-
-
Save sttk3/dc6906d314f9ab8212e8be5b52348d5d to your computer and use it in GitHub Desktop.
Illustrator script to execute ExtendScript saved in cloud document. Proof-of-concept
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * @file Illustrator script to execute ExtendScript saved in cloud document. Proof-of-concept. | |
| * Assuming json2.jsx is in the same folder as this script | |
| * @version 1.0.0 | |
| * @author sttk3.com | |
| * @copyright © 2025 sttk3.com | |
| */ | |
| //@target 'illustrator' | |
| //@include 'json2.jsx' | |
| $.localize = true ; | |
| (function() { | |
| // exit if cloud document is disabled | |
| if(!cloudAvailable()) { | |
| var errorMessage = { | |
| ja: 'このスクリプトはIllustrator 2020かそれ以降が必要です。', | |
| en: 'This script requires Illustrator 2020 or later.' | |
| } ; | |
| alert(errorMessage) ; | |
| return ; | |
| } | |
| try { | |
| // this script depends on XMPMeta from AdobeXMPScript, so exit if it is disabled | |
| var xmpEnabled = loadXMPLibrary() ; | |
| if(!xmpEnabled) { | |
| var errorMessage = { | |
| ja: 'AdobeXMPScriptが利用可能なIllustratorで実行してください。', | |
| en: 'Please execute in Illustrator with AdobeXMPScript available.' | |
| } ; | |
| alert(errorMessage) ; | |
| return ; | |
| } | |
| // open cloud document for data store | |
| var storePath = 'application_support/com.sttk3.ai.script/store.aic' ; | |
| var doc = openStoreDocument(storePath) ; | |
| if(doc == null) {return ;} | |
| // register scripts | |
| var objectStore = [ | |
| {name: 'alert.jsx', code: 'alert("Message from cloud");'} | |
| ] ; | |
| var jsonString = JSON.stringify(objectStore) ; | |
| setScripts(doc, jsonString) ; | |
| // save cloud document | |
| doc.save() ; | |
| // close the document once to indicate that the scripts will be taken from the cloud | |
| doc.close() ; | |
| // get scripts from metadata of the document | |
| doc = openStoreDocument(storePath) ; | |
| var jsonString = getScripts(doc) ; | |
| if(jsonString === '') { | |
| var errorMessage = { | |
| ja: 'スクリプトが取得できませんでした。', | |
| en: 'Scripts could not get.' | |
| } ; | |
| alert(errorMessage) ; | |
| return ; | |
| } | |
| var scripts = JSON.parse(jsonString) ; | |
| // execute ExtendScript | |
| execute(scripts[0]) ; | |
| doc.close() ; | |
| } catch(e) { | |
| alert(e) ; | |
| } finally { | |
| unloadXMPLibrary() ; | |
| } | |
| })() ; | |
| /** | |
| * determine if cloud document functions such as openCloudDocument can be supported | |
| * @returns {boolean} available when true | |
| */ | |
| function cloudAvailable() { | |
| return ('openCloudDocument' in app) ; | |
| } | |
| /** | |
| * open cloud document. create it if not exists | |
| * @param {string} cloudPath cloud document path | |
| * @returns {Document|undefined} | |
| */ | |
| function openStoreDocument(cloudPath) { | |
| var res ; | |
| var originalUserInteractionLevel = app.userInteractionLevel ; | |
| try { | |
| // supress error dialog | |
| // https://illustrator.uservoice.com/forums/908050-illustrator-desktop-sdk-scripting-issues/suggestions/49321550-script-cannot-detect-that-app-openclouddocument-fa | |
| app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS ; | |
| var doc = app.openCloudDocument(cloudPath) ; | |
| // error trap. the error happens here when the file cannot be opened | |
| doc.name ; | |
| res = doc ; | |
| } catch(e) { | |
| var doc = app.documents.add() ; | |
| doc.saveToCloud(cloudPath) ; | |
| res = doc ; | |
| app.userInteractionLevel = originalUserInteractionLevel ; | |
| } | |
| return res ; | |
| } | |
| /** | |
| * enable AdobeXMPScript | |
| * @returns {boolean} weather successed or not | |
| */ | |
| function loadXMPLibrary() { | |
| var res = false ; | |
| if(!ExternalObject.AdobeXMPScript) { | |
| try{ | |
| ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript') ; | |
| res = true ; | |
| } catch(e) { | |
| // skip | |
| } | |
| } | |
| return res ; | |
| } | |
| /** | |
| * disable AdobeXMPScript | |
| */ | |
| function unloadXMPLibrary() { | |
| if(ExternalObject.AdobeXMPScript) { | |
| try{ | |
| ExternalObject.AdobeXMPScript.unload() ; | |
| ExternalObject.AdobeXMPScript = undefined ; | |
| } catch(e) { | |
| // skip | |
| } | |
| } | |
| } | |
| /** | |
| * save the script information in the metadata description of the target Illustrator document | |
| * @param {Document} doc target Illustrator document | |
| * @param {string} dstText string to write | |
| */ | |
| function setScripts(doc, dstText) { | |
| // get XMPString from Illustrator document | |
| var xmpPackets = doc.XMPString ; | |
| // create new XMPString using AdobeXMPScript | |
| var xmpMetaObj = new XMPMeta(xmpPackets) ; | |
| if(xmpMetaObj) { | |
| xmpMetaObj.setArrayItem(XMPConst.NS_DC, 'description', 1, dstText) ; | |
| } | |
| var newXMPString = xmpMetaObj.serialize() ; | |
| // set XMPString to Illustrator document | |
| doc.XMPString = newXMPString ; | |
| } | |
| /** | |
| * get the script information in the metadata description of the target Illustrator document | |
| * @param {Document} doc target Illustrator document | |
| * @returns {string} '' if not exists | |
| */ | |
| function getScripts(doc) { | |
| var res = '' ; | |
| // get XMPString from Illustrator document | |
| var xmpPackets = doc.XMPString ; | |
| // get only the necessary info using AdobeXMPScript | |
| var xmpMetaObj = new XMPMeta(xmpPackets) ; | |
| if(xmpMetaObj) { | |
| var nsdc = XMPConst.NS_DC ; | |
| var propName = 'description' ; | |
| var arrayCount = xmpMetaObj.countArrayItems(nsdc, propName) ; | |
| if(arrayCount > 0) { | |
| res = xmpMetaObj.getArrayItem(nsdc, propName, 1).value ; | |
| } | |
| } | |
| return res ; | |
| } | |
| /** | |
| * execute ExtendScript via $.evalFile based on script name and source code | |
| * @param { {name: string, code: string} } scriptItem script info | |
| */ | |
| function execute(scriptItem) { | |
| var tempPath = Folder.temp.fullName ; | |
| var tempScriptFile = new File(tempPath + '/' + scriptItem.name) ; | |
| try { | |
| writeText(scriptItem.code, tempScriptFile) ; | |
| $.evalFile(tempScriptFile) ; | |
| } catch(e) { | |
| alert(e) ; | |
| } finally { | |
| if(tempScriptFile.exists) { | |
| tempScriptFile.remove() ; | |
| } | |
| } | |
| } | |
| /** | |
| * save text as a file | |
| * @param {string} str string to write | |
| * @param {File} fileObj destination file object | |
| * @param {string?} encoding text encoding (optional) utf-8 by default | |
| * @return {File} | |
| */ | |
| function writeText(str, fileObj, encoding) { | |
| if(!encoding) {encoding = 'utf-8' ;} | |
| try { | |
| fileObj.encoding = encoding ; | |
| fileObj.open('w') ; | |
| fileObj.write(str) ; | |
| } finally { | |
| fileObj.close() ; | |
| } | |
| return fileObj ; | |
| } | |
| /** | |
| * read text from a file | |
| * @param {File} fileObj source file object | |
| * @param {string?} encoding text encoding (optional) utf-8 by default | |
| * @return {string} | |
| */ | |
| function readText(fileObj, encoding) { | |
| if(!encoding) {encoding = 'utf-8' ;} | |
| var str = '' ; | |
| try { | |
| fileObj.encoding = encoding ; | |
| fileObj.open('r') ; | |
| str = fileObj.read() ; | |
| } finally { | |
| fileObj.close() ; | |
| } | |
| return str ; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment