I have used the below code to generate DocumentID
and InstanceID
for the links of .indd
files. This works fine on MacOS.
Can anyone suggest similar UUID
generation code on Windows system. Is there any such library available on windows system?
function generateUUID() {
var cmd = 'do shell script "uuidgen | tr -d " & quoted form of "-"';
return app.doScript(cmd, ScriptLanguage.applescriptLanguage);
}
var genDocID = 'xmp.did:' + generateUUID();
TLDR; Below are a couple of different ways to generate a UUID on a Windows system via adobe-indesign's ExtendScript API...
Solution A "shells out" a command to Windows Powershell via VBScript. However, this solution does require Windows PowerShell to be installed, and permission to run VBScript's.
Solution B utilizes InDesign itself to generate the UUID. It achieves this by creating a temporary .indd
document and extracting it's DocumentID
. This solution runs successfully cross-platform (both MacOS and Windows), requires no additional dependencies, and is slightly more performant than Solution A because it doesn't have to hop between different coding languages unlike Solution A which does.
Solution A:
The default shell on Windows, namely cmd.exe
, does not provide a built-in utility to generate Universally Unique Identifier's (UUID). This is in contrast to the Bash utility uuidgen which is available on MacOS and other *nix platforms.
However, it is possible to generate a UUID via Windows powershell by executing the following command:
[guid]::Newguid()
The following Adobe ExtendScript (win-generate-uuid.jsx
) demonstrates how a UUID can be generated by utilizing the aforementioned PowerShell command.
Note: This solution requires:
- Windows PowerShell to be installed.
- Windows is permissible to run vbscript's.
win-generate-uuid.jsx
/**
* Generates a unique identifier (UUID/GUID) by running a VBScript that
* executes a PowerShell command.
* @returns {String} - The generated unique identifier.
*/
function generateUUID() {
var pwshCmd = '$guid = [guid]::Newguid(); $guid = [string]$guid';
var vbScript = [
'CreateObject("WScript.Shell").Run "powershell.exe -command ""' +
pwshCmd + '"" | clip", 0, True',
'guid = CreateObject("htmlfile").ParentWindow.ClipboardData.GetData("text")',
'returnValue = Replace(guid, "-","")'
].join('\r');
return app.doScript(vbScript, ScriptLanguage.visualBasic);
}
var genDocID = 'xmp.did:' + generateUUID();
$.writeln(genDocID);
Explanation:
The generateUUID
function in win-generate-uuid.jsx
utilizes InDesign's doScript()
Method to run a VBScript.
The VBScript that is executed essentially runs the aforementioned PowerShell command, (albeit a slightly modified version), using the Run()
command.
Note: It's necessary to utilize VBScript to "shell out" the PowerShell command because InDesign running on Windows allows only VBScript or JavaScript to be executed via it's doScript
method.
The result of the PowerShell command (i.e. the generated UUID) is piped (|
) to the Clipboard.
Subsequently;
- The UUID is retrieved from the Clipboard.
- All hypens (
-
) in the generated UUID are removed, before finally return
'ing it to the .jsx
script.
For further explanation of the reasons why VBScript's Run()
is utilized, (combined with piping to the Clipboard), instead of VBScript's Exec()
refer to this answer. A summary of the reasons are;
Run()
doesn't show the PowerShell window.
Exec()
does briefly show the PowerShell window.
Solution B:
A cross-platform solution for UUID generation, (i.e. one that runs successfully on MacOS and Windows), is to utilize InDesign itself. This is demonstrated in generate-uuid.jsx
below.
generate-uuid.jsx
#target indesign
$.level=0
/**
* Loads the AdobeXMPScript library.
* @returns {Boolean} True if the library loaded successfully, otherwise false.
*/
function loadXMPLibrary() {
if (!ExternalObject.AdobeXMPScript) {
try {
ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');
} catch (e) {
alert('Failed loading AdobeXMPScript library\n' + e.message, 'Error', true);
return false;
}
}
return true;
}
/**
* Generates a unique identifier (UUID/GUID) cross-platforms (macOS/Windows).
* @returns {String} - The generated unique identifier.
*/
function generateUUID() {
var tmp_FilePath = File(Folder.temp + '/__temp__.indd');
// 1. Create temporary .indd and save it to disk.
var newDoc = app.documents.add(false);
newDoc.save(tmp_FilePath);
newDoc.close();
// 2. Extract the DocumentID from temporay .indd
var xmpFile = new XMPFile(tmp_FilePath.fsName, XMPConst.FILE_INDESIGN, XMPConst.OPEN_FOR_READ);
var xmp = xmpFile.getXMP();
var documentID = xmp.getProperty(XMPConst.NS_XMP_MM, 'DocumentID', XMPConst.STRING);
// 3. Delete temporary .indd
tmp_FilePath.remove();
// 4. Return the DocumentID without the default `xmp.did:` prefix.
return String(documentID).replace(/xmp\.did:/g, '');
}
if (loadXMPLibrary()) {
var genDocID = 'xmp.did:' + generateUUID();
$.writeln(genDocID);
}
Explanation:
The generate-uuid.jsx
script (above) contains a function named generateUUID
that essentially performs the following:
Creates a new InDesign document (.indd
) and saves it to the OS's temporary folder, then closes it. This task is performed in the background so the user will not know that the actual document has been created.
Note The OS's default temporary directory is determined via Folder.temp
. To further understand where the temporay folder resides per OS you can temporarily add the following line of code to your script, and it will log it's pathname to your ExtenScript console:
$.writeln(Folder.temp);
Next we extract the DocumentID
from the newly created temporary .indd
file utilizing features of the XMP scripting API - which you should be familiar with from my answers to your previous questions; here, here, and here.
- Then we delete the temporary
.indd
file named __temp__.indd
.
Finally, the default xmp.did:
prefix from the extracted documentID
is removed.
Note: The default xmp.did:
prefix is removed in the body of the generateUUID
function then reinstated - which may seem a bit strange! I've done this intentionally so that the generateUUID
remains more reusable. For example; you may want to use it to also generate an InstanceID
etc, in which case you'll probably want to prefix the UUID with xmp.iid:
.
I have used the below simple JS function to avoid the problem with Mac and Windows, and to have multiple functions for it.
// JS - Generate Global Random Unique Number
function generateJsUUID(){
var dt = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (dt + Math.random()*16)%16 | 0;
dt = Math.floor(dt/16);
return (c=='x' ? r :(r&0x3|0x8)).toString(16);
});
return uuid;
}