2 Replies Latest reply on Jul 2, 2012 12:42 PM by jacobolus

    issues with localized strings when scripting descriptors

    jacobolus

      Over at the ps-scripts site, we’ve had a kind of interesting discussion about trying to set the scale on pattern fill layer styles to 100% http://www.ps-scripts.com/bb/viewtopic.php?f=9&t=4875

       

      Mike Hale’s proposed solution was to get the content out of the pattern fill style (in the hierarchy of the layer’s descriptor), make a new temporary layer, apply that pattern fill – with its scale changed – to the temp layer, ask photoshop to copy the style from the temp layer to the original layer, and then delete the temp layer.

       

      As far as I know, that worked okay, but I wondered if it would be possible to do without the workaround, so I wrote a script to pull the descriptor for all layer styles out, change the scale on any pattern fill styles, and save the new layer styles back to the layer.

       

      This script worked sometimes, but sometimes would cause an error when run more than once in a row (on CS4 and CS5, but apparently not on CS6), so I set out to investigate. It turns out that when fetching the descriptor, several of the properties specifying presets are returned as "de-localized" ZStrings, for instance '$$$/Contours/Defaults/Linear=Linear'. But when setting the descriptor, these must be localized again, or else Photoshop CS4/CS5 will raise an error (CS6 is apparently able to deal with the ZStrings; haven’t tested versions <= CS3).

       

      [One especially odd note was that the Script Listener output produced in CS5 when creating a drop shadow style with a contour other than "Linear" (for instance, using the contour "Cone") will make Photoshop throw an error when it is run directly. It is necessary to call localize on any ZStrings in the Script Listener JavaScript before it will work properly (for instance, converting "$$$/Contours/Defaults/Cone=Cone" to just "Cone").]

       

      It seems to work okay, however, to get the raw byte stream for the fetched descriptor, search for any ZStrings, call localize on them, and then put the localized strings back in where the ZStrings came from, before trying to use the descriptor to change the layer styles.

       

      My last version of the "set selected layer’s pattern fill style’s scale to 100%" script does that, and as far as I can tell works across versions of Photoshop, across platforms, and hopefully across other-language installs.

       

      Some of you might find the localizeDescriptor function helpful, or the general code for getting/setting layer styles, which should be applicable to a variety of use cases.

       

      var bytesToString, int16AtOffset, int32AtOffset, int32ToBytes,

          localizeDescriptor, stringToBytes; int16AtOffset = function(bytes, offset) {   return ((bytes.charCodeAt(offset)) << 8) +

                bytes.charCodeAt(offset + 1); }; int32AtOffset = function(bytes, offset) {   return ((int16AtOffset(bytes, offset)) << 16) +

                int16AtOffset(bytes, offset + 2); }; int32ToBytes = function(num) {   var bytes;   bytes = [num >>> 24, num << 8 >>> 24,

                 num << 16 >>> 24, num << 24 >>> 24];   return String.fromCharCode.apply(String, bytes); }; bytesToString = function(bytes) {   var characters, i, n;   characters = []

        for (i = 0, n = bytes.length; i < n; i += 2) {     characters.push(int16AtOffset(bytes, i));   }   return String.fromCharCode.apply(String, characters); }; stringToBytes = function(string) {   var bytes, char_code, i, n;   bytes = [];   for (i = 0, n = string.length; i < n; i++) {     char_code = string.charCodeAt(i);     bytes.push(char_code >> 8, char_code & 0xff);   }   return String.fromCharCode.apply(String, bytes); }; localizeDescriptor = function(desc) {   var after_bytes, before_bytes, bytes, len, localized_string,

            new_desc, new_len, offset, zstring;   bytes = desc.toStream();   while (true) {     offset = bytes.search(/TEXT....\x00\$\x00\$\x00\$/);     if (offset === -1) {       break;     }     len = int32AtOffset(bytes, offset + 4);     zstring = bytesToString(bytes.substr(offset + 8, (len - 1) * 2));     localized_string = (localize(zstring)) + '\u0000';     new_len = localized_string.length;     before_bytes = bytes.slice(0, offset);     after_bytes = bytes.slice(offset + 8 + len * 2);     bytes = before_bytes.concat(

            'TEXT', int32ToBytes(new_len),

            stringToBytes(localized_string), after_bytes);   }   new_desc = new ActionDescriptor;   new_desc.fromStream(bytes);   return new_desc; }; var $s, duplicateDescriptor, executeSet, getTargetLayerEffects,

          setPatternFillScale; $s = function(string) {   return app.stringIDToTypeID(string); }; duplicateDescriptor = function(descriptor) {   var descriptorStream, newDescriptor;   descriptorStream = descriptor.toStream();   newDescriptor = new ActionDescriptor;   newDescriptor.fromStream(descriptorStream);   return newDescriptor; }; getTargetLayerEffects = function() {   var containerDesc, targetLayerRef;   targetLayerRef = new ActionReference;   targetLayerRef.putProperty($s('property'), $s('layerEffects'));   targetLayerRef.putEnumerated(

          $s('layer'), $s('ordinal'), $s('targetEnum'));   containerDesc = app.executeActionGet(targetLayerRef);   return containerDesc.getObjectValue($s('layerEffects')); }; executeSet = function(target, to, type) {   var action;   action = new ActionDescriptor;   action.putObject($s('to'), type, to);   action.putReference($s('target'), target);   app.executeAction($s('set'), action, DialogModes.NO); }; setPatternFillScale = function(scale) {   var currentPatternFill, layer, layerEffects, newLayerEffects,

            newPatternFill, target;   layerEffects = getTargetLayerEffects();   newLayerEffects = duplicateDescriptor(layerEffects);   currentPatternFill = layerEffects.getObjectValue($s('patternFill'));   newPatternFill = duplicateDescriptor(currentPatternFill);   newPatternFill.putUnitDouble($s('scale'), $s('percentUnit'), 100);   newLayerEffects.putObject(

          $s('patternFill'), $s('patternFill'), newPatternFill);   newLayerEffects = localizeDescriptor(newLayerEffects);   layer = new ActionDescriptor;   layer.putObject(

          $s('layerEffects'), $s('layerFXVisible'), newLayerEffects);   target = new ActionReference;   target.putEnumerated($s('layer'), $s('ordinal'), $s('targetEnum'));   executeSet(target, layer, $s('layer')); }; setPatternFillScale(100); /* Copyright (c) 2012 Jacob Rus Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */

       

      Cheers.

       

      [On a separate subject, is there any way to get this forum to put code in a monospaced font? The wysiwyg editor isn’t really very wysiwyg (frankly, it’s rather terrible), and neither putting things in "pre"/"code" blocks, nor explicity setting the font to something like Andale Mono or Courier New actually does the trick.]

        • 1. Re: issues with localized strings when scripting descriptors
          Michael L Hale Level 5

          Just to be clear, I recommended the get/create new/copy back approach because I have tried for years to edit the styles in place and always ran into this then undiscovered bug. I agree that this is a better way to change an effect now that the bug has been found. It would be nice is someone would confirm it also works with a non-English version of Photoshop.

           

          To me the easiest way to insert code into a post here is to click on the 'Use advanced editor' link above the top right edge of the default editor. That adds several new icons to the tool bar, one of which is >>( helptip = Insert ) which is a menu dropdown that contains 'Syntax Highlighting'.  That has several choices. I use 'Plain'.

          • 2. Re: issues with localized strings when scripting descriptors
            jacobolus Level 1

            I recommended the get/create new/copy back approach because I have tried for years to edit the styles in place and always ran into this then undiscovered bug.

            Aha. It all makes sense now.

             

            I thought your workaround was a clever and entirely reasonable response.