need to generate UUID for windows system in Extend

2019-07-26 09:13发布

问题:

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();

回答1:

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:

  1. The generateUUID function in win-generate-uuid.jsx utilizes InDesign's doScript() Method to run a VBScript.

  2. 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.

  3. The result of the PowerShell command (i.e. the generated UUID) is piped (|) to the Clipboard.

  4. 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.
  5. 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:

  1. 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);
    
  2. 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.

  3. Then we delete the temporary .indd file named __temp__.indd.
  4. 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:.



回答2:

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;
}