1 //------------------------------------------------------------------------------
  2 // File: jamText.jsxinc
  3 // Version: 4.5
  4 // Release Date: 2016-09-29
  5 // Copyright: © 2011-2016 Michel MARIANI <http://www.tonton-pixel.com/blog/>
  6 // Licence: GPL <http://www.gnu.org/licenses/gpl.html>
  7 //------------------------------------------------------------------------------
  8 // This program is free software: you can redistribute it and/or modify
  9 // it under the terms of the GNU General Public License as published by
 10 // the Free Software Foundation, either version 3 of the License, or
 11 // (at your option) any later version.
 12 // 
 13 // This program is distributed in the hope that it will be useful,
 14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 // GNU General Public License for more details.
 17 // 
 18 // You should have received a copy of the GNU General Public License
 19 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 20 //------------------------------------------------------------------------------
 21 // Version History:
 22 //  4.5:
 23 //  - Incremented version number to keep in sync with other modules.
 24 //  4.4:
 25 //  - Normalized error messages.
 26 //  4.2:
 27 //  - Cleaned up code.
 28 //  - Fixed bug: missing break statement in jamText.toLayerTextObject ().
 29 //  4.0:
 30 //  - Removed reference to 'this' for main global object.
 31 //  3.6.3:
 32 //  - Moved common graphics unit from layer text to path components level.
 33 //  3.6.2:
 34 //  - Cleaned up code.
 35 //  - Added missing keys.
 36 //  - Updated examples.
 37 //  3.6.1:
 38 //  - Initial release.
 39 //------------------------------------------------------------------------------
 40 
 41 /**
 42  * @fileOverview
 43  * @name jamText.jsxinc
 44  * @author Michel MARIANI
 45  */
 46 
 47 //------------------------------------------------------------------------------
 48 
 49 if (typeof jamText !== 'object')
 50 {
 51     /**
 52      * Global object (used to simulate a namespace in JavaScript) containing
 53      * a set of layer text-related functions for scripts written with the
 54      * <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-action-manager/">JSON Action Manager</a> engine.
 55      * @author Michel MARIANI
 56      * @version 4.5
 57      * @namespace
 58      */
 59     var jamText = { };
 60     //
 61     (function ()
 62     {
 63         /**
 64          * @description Get a layer text object in JSON AM Data Format from a simplified layer text JSON object.
 65          * @param {Object} layerText Simplified layer text JSON object
 66          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-text-object-simplified-format/">Layer Text Object Simplified Format</a>)
 67          * @returns {Object|Array} Layer text object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
 68          * @see jamText.fromLayerTextObject
 69          * @example
 70          * var text = "Hello, world!";
 71          * var layerText =
 72          * {
 73          *     "layerText":
 74          *     {
 75          *         "textKey": text,
 76          *         "warp":
 77          *         {
 78          *             "warpStyle": "warpRise",
 79          *             "warpValue": 33.3333
 80          *         },
 81          *         "textClickPoint":
 82          *         {
 83          *             "horizontal": 50,
 84          *             "vertical": 50
 85          *         },
 86          *         "antiAlias": "antiAliasSmooth",
 87          *         "textShape":
 88          *         [
 89          *             {
 90          *                 "textType": "point",
 91          *                 "orientation": "horizontal"
 92          *             }
 93          *         ],
 94          *         "textStyleRange":
 95          *         [
 96          *             {
 97          *                 "from": 0,
 98          *                 "to": text.length,
 99          *                 "textStyle":
100          *                 {
101          *                     "fontPostScriptName": "Minion-Regular",
102          *                     "size": 288,
103          *                     "color": { "red": 63, "green": 255, "blue": 0 }
104          *                 }
105          *             }
106          *         ],
107          *         "paragraphStyleRange":
108          *         [
109          *             {
110          *                 "from": 0,
111          *                 "to": text.length,
112          *                 "paragraphStyle":
113          *                 {
114          *                     "alignment": "center"
115          *                 }
116          *             }
117          *         ]
118          *     },
119          *     "typeUnit": "pixelsUnit"
120          * };
121          * jamEngine.jsonPlay
122          * (
123          *     "make",
124          *     {
125          *         "target": [ "<reference>", [ [ "textLayer", [ "<class>", null ] ] ] ],
126          *         "using": <strong>jamText.toLayerTextObject</strong> (layerText)
127          *     }
128          * );
129          */
130         jamText.toLayerTextObject = function (layerText)
131         {
132             var typeUnit;
133             if ("typeUnit" in layerText)
134             {
135                 typeUnit = layerText["typeUnit"];
136             }
137             var data = layerText["layerText"];
138             function restoreDesc (desc, hintData)
139             {
140                 var restoredDesc = { };
141                 for (var key in desc)
142                 {
143                     if (desc.hasOwnProperty (key))
144                     {
145                         var value = desc[key];
146                         var typedValue = null;
147                         var restoredList;
148                         switch (key)
149                         {
150                             case "bookKey":
151                                 typedValue = [ "<data>", value ];
152                                 break;
153                             case "rowMajorOrder":
154                             case "syntheticBold":
155                             case "syntheticItalic":
156                             case "autoLeading":
157                             case "ligature":
158                             case "altligature":
159                             case "contextualLigatures":
160                             case "alternateLigatures":
161                             case "oldStyle":
162                             case "fractions":
163                             case "ordinals":
164                             case "swash":
165                             case "titling":
166                             case "connectionForms":
167                             case "stylisticAlternates":
168                             case "ornaments":
169                             case "proportionalMetrics":
170                             case "kana":
171                             case "italics":
172                             case "ruby":
173                             case "enableWariChu":
174                             case "noBreak":
175                             case "fill":
176                             case "stroke":
177                             case "fillFirst":
178                             case "fillOverPrint":
179                             case "strokeOverPrint":
180                             case "hyphenate":
181                             case "hyphenateCapitalized":
182                             case "hangingRoman":
183                             case "keepTogether":
184                             case "kurikaeshiMojiShori":
185                             case "textEveryLineComposer":
186                             case "flip":
187                                 typedValue = [ "<boolean>", value ];
188                                 break;
189                             case "textKey":
190                             case "fontPostScriptName":
191                             case "fontName":
192                             case "fontStyleName":
193                             case "book":
194                             case "name":
195                                 typedValue = [ "<string>", value ]; // No localize...
196                                 break;
197                             case "rowCount":
198                             case "columnCount":
199                             case "from":
200                             case "to":
201                             case "fontScript":
202                             case "fontTechnology":
203                             case "tracking":
204                             case "wariChuCount":
205                             case "wariChuLineGap":
206                             case "wariChuWidow":
207                             case "wariChuOrphan":
208                             case "tcyUpDown":
209                             case "tcyLeftRight":
210                             case "jiDori":
211                             case "bookID":
212                             case "dropCapMultiplier":
213                             case "hyphenateWordSize":
214                             case "hyphenatePreLength":
215                             case "hyphenatePostLength":
216                             case "hyphenateLimit":
217                             case "autoTCY":
218                             case "kerning":
219                             case "pathTypeSpacing":
220                                 typedValue = [ "<integer>", value ];
221                                 break;
222                             case "warpValue":
223                             case "warpPerspective":
224                             case "warpPerspectiveOther":
225                             case "xx":
226                             case "xy":
227                             case "yx":
228                             case "yy":
229                             case "tx":
230                             case "ty":
231                             case "top":
232                             case "left":
233                             case "bottom":
234                             case "right":
235                             case "horizontalScale":
236                             case "verticalScale":
237                             case "characterRotation":
238                             case "mojiZume":
239                             case "wariChuScale":
240                             case "a":
241                             case "b":
242                             case "black":
243                             case "blue":
244                             case "brightness":
245                             case "cyan":
246                             case "gray":
247                             case "green":
248                             case "luminance":
249                             case "magenta":
250                             case "red":
251                             case "saturation":
252                             case "yellowColor":
253                             case "lineDashoffset":
254                             case "autoLeadingPercentage":
255                             case "hyphenationZone":
256                             case "hyphenationPreference":
257                             case "justificationWordMinimum":
258                             case "justificationWordDesired":
259                             case "justificationWordMaximum":
260                             case "justificationLetterMinimum":
261                             case "justificationLetterDesired":
262                             case "justificationLetterMaximum":
263                             case "justificationGlyphMinimum":
264                             case "justificationGlyphDesired":
265                             case "justificationGlyphMaximum":
266                             case "defaultTabWidth":
267                             case "start":
268                             case "end":
269                                 typedValue = [ "<double>", value ];
270                                 break;
271                             case "rowGutter":
272                             case "columnGutter":
273                             case "spacing":
274                             case "firstBaselineMinimum":
275                             case "size":
276                             case "leading":
277                             case "baselineShift":
278                             case "underlineOffset":
279                             case "lineWidth":
280                             case "miterLimit":
281                             case "firstLineIndent":
282                             case "startIndent":
283                             case "endIndent":
284                             case "spaceBefore":
285                             case "spaceAfter":
286                                 typedValue = (typeUnit) ? [ "<unitDouble>", [ typeUnit, value ] ] : [ "<double>", value ];
287                                 break;
288                             case "horizontal":
289                             case "vertical":
290                                 typedValue = (hintData) ? [ "<unitDouble>", [ hintData, value ] ] : [ "<double>", value ];
291                                 break;
292                             case "hue":
293                                 typedValue = [ "<unitDouble>", [ "angleUnit", value ] ];
294                                 break;
295                             case "warpStyle":
296                             case "textGridding":
297                             case "orientation":
298                             case "textType":
299                             case "frameBaselineAlignment":
300                             case "autoKern":
301                             case "fontCaps":
302                             case "baseline":
303                             case "otbaseline":
304                             case "strikethrough":
305                             case "underline":
306                             case "figureStyle":
307                             case "baselineDirection":
308                             case "textLanguage":
309                             case "japaneseAlternate":
310                             case "gridAlignment":
311                             case "wariChuJustification":
312                             case "lineCap":
313                             case "lineJoin":
314                             case "leadingType":
315                             case "burasagari":
316                             case "preferredKinsokuOrder":
317                             case "pathTypeEffect":
318                             case "pathTypeAlignment":
319                             case "pathTypeAlignTo":
320                                 typedValue = [ "<enumerated>", [ key, value ] ];
321                                 break;
322                             case "antiAlias":
323                                 typedValue = [ "<enumerated>", [ "antiAliasType", value ] ];
324                                 break;
325                             case "warpRotate":
326                                 typedValue = [ "<enumerated>", [ "orientation", value ] ];
327                                 break;
328                             case "alignment":
329                             case "singleWordJustification":
330                                 typedValue = [ "<enumerated>", [ "alignmentType", value ] ];
331                                 break;
332                             case "textShape":
333                             case "textStyleRange":
334                             case "paragraphStyleRange":
335                             case "kerningRange":
336                                 restoredList = [ ];
337                                 for (var i = 0; i < value.length; i++)
338                                 {
339                                     restoredList.push ([ "<object>", [ key, restoreDesc (value[i]) ] ]);
340                                 }
341                                 typedValue = [ "<list>", restoredList ];
342                                 break;
343                             case "warp":
344                             case "transform":
345                             case "textStyle":
346                             case "paragraphStyle":
347                                 typedValue = [ "<object>", [ key, restoreDesc (value) ] ];
348                                 break;
349                             case "defaultStyle":
350                                 typedValue = [ "<object>", [ "textStyle", restoreDesc (value) ] ];
351                                 break;
352                             case "color":
353                             case "strokeColor":
354                                 var colorClass;
355                                 if ((("book" in value) && ("name" in value)) || (("bookID" in value) && ("bookKey" in value)))
356                                 {
357                                     colorClass = "bookColor";
358                                 }
359                                 else if (("cyan" in value) && ("magenta" in value) && ("yellowColor" in value) && ("black" in value))
360                                 {
361                                     colorClass = "CMYKColorClass";
362                                 }
363                                 else if ("gray" in value)
364                                 {
365                                     colorClass = "grayscale";
366                                 }
367                                 else if (("hue" in value) && ("saturation" in value) && ("brightness" in value))
368                                 {
369                                     colorClass = "HSBColorClass";
370                                 }
371                                 else if (("luminance" in value) && ("a" in value) && ("b" in value))
372                                 {
373                                     colorClass = "labColor";
374                                 }
375                                 else if (("red" in value) && ("green" in value) && ("blue" in value))
376                                 {
377                                     colorClass = "RGBColor";
378                                 }
379                                 typedValue = [ "<object>", [ colorClass, restoreDesc (value) ] ];
380                                 break;
381                             case "textClickPoint":
382                                 typedValue = [ "<object>", [ "point", restoreDesc (value, "percentUnit") ] ];
383                                 break;
384                             case "base":
385                                 typedValue = [ "<object>", [ "point", restoreDesc (value) ] ];
386                                 break;
387                             case "bounds":
388                                 typedValue = [ "<object>", [ "rectangle", restoreDesc (value) ] ];
389                                 break;
390                             case "path":
391                                 typedValue = [ "<object>", [ "pathClass", { "pathComponents": jamHelpers.toPathComponentList (value) } ] ];
392                                 break;
393                             case "tRange":
394                                 typedValue = [ "<object>", [ "range", restoreDesc (value) ] ];
395                                 break;
396                             case "textLayer":
397                                 typedValue = [ "<object>", [ "textLayer", restoreDesc (value) ] ];
398                                 break;
399                             case "mojiKumiName":
400                             case "kinsokuSetName":
401                                 // String in CS, enumerated in CS4...
402                                 if (true)   // For the time being...
403                                 {
404                                     typedValue = [ "<enumerated>", [ key, value ] ];
405                                 }
406                                 else
407                                 {
408                                     typedValue = [ "<string>", value ]; // No localize...
409                                 }
410                                 break;
411                             case "leftAki":
412                             case "rightAki":
413                                 // Unit double in CS, double in CS4...
414                                 if (true)   // For the time being...
415                                 {
416                                     typedValue = [ "<double>", value ];
417                                 }
418                                 else
419                                 {
420                                     typedValue = (typeUnit) ? [ "<unitDouble>", [ typeUnit, value ] ] : [ "<double>", value ];
421                                 }
422                                 break;
423                         }
424                         if (typedValue)
425                         {
426                             restoredDesc[key] = typedValue;
427                         }
428                     }
429                 }
430                 return restoredDesc;
431             }
432             //
433             return restoreDesc ({ "textLayer": data })["textLayer"];
434         };
435         //
436         /**
437          * @description Get a simplified layer text JSON object from a layer text object in JSON AM Data Format.
438          * @param {Object|Array} layerTextObject Layer text object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
439          * @returns {Object} Simplified layer text JSON object
440          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-text-object-simplified-format/">Layer Text Object Simplified Format</a>)
441          * @see jamText.toLayerTextObject
442          * @example
443          * jamEngine.meaningfulIds = true;
444          * jamEngine.parseFriendly = true;
445          * var resultObj = jamEngine.jsonGet
446          * (
447          *     [
448          *         { "property": { "<property>": "textKey" } },
449          *         { "layer": { "<enumerated>": { "ordinal": "targetEnum" } } }
450          *     ]
451          * );
452          * if ("textKey" in resultObj)
453          * {
454          *     var layerTextObj = jamText.<strong>fromLayerTextObject</strong> (resultObj["textKey"]);
455          *     $.writeln (jamJSON.stringify (layerTextObj, '\t'));
456          * }
457          */
458         jamText.fromLayerTextObject = function (layerTextObject)
459         {
460             var layerText = { };
461             var typeUnit;
462             var typeDone = false;
463             function getUnitsHook (desc, key)
464             {
465                 var result;
466                 if (key === "path")
467                 {
468                     result = jamHelpers.fromPathComponentList (desc[key][1][1]["pathComponents"], true);
469                 }
470                 else if (!typeDone)
471                 {
472                     // First type unit encountered, depending on ruler units preferences; assumed to be the same for all others
473                     switch (key)
474                     {
475                         case "rowGutter":
476                         case "columnGutter":
477                         case "spacing":
478                         case "firstBaselineMinimum":
479                         case "size":
480                         case "leading":
481                         case "baselineShift":
482                         case "underlineOffset":
483                         case "lineWidth":
484                         case "miterLimit":
485                         case "firstLineIndent":
486                         case "startIndent":
487                         case "endIndent":
488                         case "spaceBefore":
489                         case "spaceAfter":
490                         case "leftAki":     // In CS, not in CS4...
491                         case "rightAki":    // In CS, not in CS4...
492                             var value = desc[key];
493                             if (value[0] === "<unitDouble>")
494                             {
495                                 typeUnit = value[1][0];
496                             }
497                             typeDone = true;
498                             break;
499                     }
500                 }
501                 return result;
502             }
503             layerText["layerText"] = jamEngine.simplifyObject (layerTextObject, getUnitsHook);
504             if (typeUnit)
505             {
506                 layerText["typeUnit"] = typeUnit;
507             }
508             return layerText;
509         };
510         //
511         /**
512          * @description Set the current layer text.
513          * @param {Object} layerText Simplified layer text JSON object made of two members: "textLayer" and "typeUnit"<br>
514          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-text-object-simplified-format/">Layer Text Object Simplified Format</a>)
515          * @see jamText.getLayerText
516          * @example
517          * var text = "Bonjour !";
518          * var layerText =
519          * {
520          *     "layerText":
521          *     {
522          *         "textKey": text,
523          *         "textClickPoint": { "horizontal": 50, "vertical": 95 },
524          *         "antiAlias": "antiAliasCrisp",
525          *         "textShape":
526          *         [
527          *             { "textType": "point", "orientation": "horizontal" }
528          *         ],
529          *         "textStyleRange":
530          *         [
531          *             {
532          *                 "from": 0,
533          *                 "to": text.length,
534          *                 "textStyle":
535          *                 {
536          *                     "fontPostScriptName": "Myriad-Italic",
537          *                     "size": 288,
538          *                     "color": { "red": 144, "green": 0, "blue": 255 }
539          *                 }
540          *             }
541          *         ],
542          *         "paragraphStyleRange":
543          *         [
544          *             {
545          *                 "from": 0,
546          *                 "to": text.length,
547          *                 "paragraphStyle": { "alignment": "center" }
548          *             }
549          *         ]
550          *     },
551          *     "typeUnit": "pixelsUnit"
552          * };
553          * jamText.<strong>setLayerText</strong> (layerText);
554          */
555         jamText.setLayerText = function (layerText)
556         {
557             var saveMeaningfulIds = jamEngine.meaningfulIds;
558             var saveParseFriendly = jamEngine.parseFriendly;
559             jamEngine.meaningfulIds = true;
560             jamEngine.parseFriendly = true;
561             var isTextLayer = false;
562             try
563             {
564                 resultObj = jamEngine.jsonGet
565                 (
566                     [
567                         { "property": { "<property>": "textKey" } },
568                         { "layer": { "<enumerated>": { "ordinal": "targetEnum" } } }
569                     ]
570                 );
571                 if ("textKey" in resultObj)
572                 {
573                     isTextLayer = true;
574                 }
575             }
576             catch (e)
577             {
578             }
579             jamEngine.meaningfulIds = saveMeaningfulIds;
580             jamEngine.parseFriendly = saveParseFriendly;
581             if (isTextLayer)
582             {
583                 jamEngine.jsonPlay
584                 (
585                     "set",
586                     {
587                         "target": [ "<reference>", [ [ "layer", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ] ] ],
588                         "to": this.toLayerTextObject (layerText)
589                     }
590                 );
591             }
592             else
593             {
594                 jamEngine.jsonPlay
595                 (
596                     "make",
597                     {
598                         "target": [ "<reference>", [ [ "textLayer", [ "<class>", null ] ] ] ],
599                         "using": this.toLayerTextObject (layerText)
600                     }
601                 );
602             }
603         };
604         //
605         /**
606          * @description Get the current layer text.
607          * @returns {Object|Null} Simplified layer text JSON object made of two members: "textLayer" and "typeUnit"<br>
608          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-text-object-simplified-format/">Layer Text Object Simplified Format</a>)<br>
609          * or null if not available
610          * @see jamText.setLayerText
611          * @example
612          * var layerText = jamText.<strong>getLayerText</strong> ();
613          * if (layerText)
614          * {
615          *     var jsonFile = new File ("~/Desktop/layer-text.json");
616          *     jamUtils.writeJsonFile (jsonFile, layerText, '\t');
617          *     jsonFile.execute ();
618          * }
619          * else
620          * {
621          *     alert ("No text layer selected.");
622          * }
623          */
624         jamText.getLayerText = function ()
625         {
626             var layerTextObj = null;
627             var saveMeaningfulIds = jamEngine.meaningfulIds;
628             var saveParseFriendly = jamEngine.parseFriendly;
629             jamEngine.meaningfulIds = true;
630             jamEngine.parseFriendly = true;
631             try
632             {
633                 var resultObj = jamEngine.jsonGet
634                 (
635                     [
636                         { "property": { "<property>": "textKey" } },
637                         { "layer": { "<enumerated>": { "ordinal": "targetEnum" } } }
638                     ]
639                 );
640                 if ("textKey" in resultObj)
641                 {
642                     layerTextObj = this.fromLayerTextObject (resultObj["textKey"]);
643                 }
644             }
645             catch (e)
646             {
647             }
648             jamEngine.meaningfulIds = saveMeaningfulIds;
649             jamEngine.parseFriendly = saveParseFriendly;
650             return layerTextObj;
651         };
652     } ());
653 }
654 
655 //------------------------------------------------------------------------------
656 
657