Skip to main content
NotPsyclone
Participant
May 4, 2022
Question

Change each letter in a text layer to have a random font from set list with a script

  • May 4, 2022
  • 4 replies
  • 2028 views

Hi all,

 

I am looking to take a text layer that contains an entire paragraph of text and randomize the font for each letter.

 

I know that with the TextItem object you can only change the font for the entire layer. However, I have found this script doing the exact thing I want to do, except with colors of the letters rather than the font.

 

Link: https://community.adobe.com/t5/photoshop-ecosystem-discussions/can-you-automatically-give-every-subsequent-letter-a-different-color-for-a-batch-of-text-designs/m-p/12325456

 

This script in the example is hard for me to understand, but it does work. I was wondering if the script would be simple enough to edit to work with an array of fonts rather than colors? Or if there is a way to do this with a different script? 

4 replies

c.pfaffenbichler
Community Expert
Community Expert
May 9, 2022

Have you thought through the »randomization«? 

 

With plain »randomness« (and pseudo-randomness) several letters in sequence could assume the same font (similar to rolling dice and getting the same number several times in sequence) and some fonts might be used noticable less than others (or even not at all). 

NotPsyclone
Participant
May 10, 2022

This randomness behavior is completely fine, if two, three, or even more letters are the same font in a row does not affect the effect I am trying to accomplish

c.pfaffenbichler
Community Expert
Community Expert
May 8, 2022

Maybe this Script can help, the Array »newFonts« containing the fontPostScriptNames; kerning is not considered. (edited)

See the screenshots for an example of what it does: 

 

 

Script updated 2022-05-09

// apply font from array to letters of selected type layer;
// 2022, use it at your own risk;
if (app.documents.length > 0) {
//////////////////
try {
var theFonts = new Array;
var theStyleRanges = new Array;
// get font of active layer;
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var layerDesc = executeActionGet(ref);
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
var theName = layerDesc.getString(stringIDToTypeID('name'));
// if not layer group collect values;
if (layerSet != "layerSectionEnd" && layerSet != "layerSectionStart" && isBackground != true) {
var hasText = layerDesc.hasKey(stringIDToTypeID("textKey"));
if (hasText == true) {
var textDesc = layerDesc.getObjectValue(stringIDToTypeID('textKey'));
var theText = textDesc.getString(stringIDToTypeID('textKey'));
//var shapeList = textDesc.getList(stringIDToTypeID('textShape'));
var paragraphRangeList = textDesc.getList(stringIDToTypeID('paragraphStyleRange'));
var kernRange = textDesc.getList(stringIDToTypeID('kerningRange'));
var rangeList = textDesc.getList(stringIDToTypeID('textStyleRange'));
// process the list;
for (var o = 0; o < rangeList.count; o++) {
var thisList = rangeList.getObjectValue(o);
var theFrom = thisList.getInteger(stringIDToTypeID('from'));
var theTo = thisList.getInteger(stringIDToTypeID('to'));
var styleDesc = thisList.getObjectValue(stringIDToTypeID('textStyle'));
var aSize = styleDesc.getUnitDoubleValue(charIDToTypeID( "Sz  " ));
// check for default font;
if (styleDesc.hasKey(stringIDToTypeID('fontPostScriptName')) == true) {var aFont = styleDesc.getString(stringIDToTypeID('fontPostScriptName'))}
else {
	var theDefault = styleDesc.getObjectValue(stringIDToTypeID('baseParentStyle'));	
	var aFont = theDefault.getString(stringIDToTypeID('fontPostScriptName'));
	};
if (styleDesc.hasKey(stringIDToTypeID('impliedFontSize')) == true) {var aFont = styleDesc.getString(stringIDToTypeID('fontPostScriptName'))}
// add to array;
//////////////////
if (o == 0) {
    theFonts.push([aFont, aSize, theFrom, theTo]);
    theStyleRanges.push(thisList.getObjectValue(stringIDToTypeID('textStyle')));
    };
if (o > 0 && theFrom != theFonts[theFonts.length - 1][2] /*&& theTo != theFonts[theFonts.length - 1][3]*/) {
theFonts.push([aFont, aSize, theFrom, theTo]);
theStyleRanges.push(thisList.getObjectValue(stringIDToTypeID('textStyle')));
};
//////////////////
}
}
};
//alert (theFonts.length+"\n"+theFonts.join("\n"));
// the replacement-fonts array;
var newFonts = ["MyriadPro-Regular", "MyriadPro-Bold", "Times-BoldItalic"];
var anIndex = 0;
var theFontsNumber = newFonts.length;
// change text;
// =======================================================
var idPxl = charIDToTypeID( "#Pxl" );
var idTxtt = charIDToTypeID( "Txtt" );
var idFrom = charIDToTypeID( "From" );
var idT = charIDToTypeID( "T   " );
var idTxtS = charIDToTypeID( "TxtS" );
var idTxLr = charIDToTypeID( "TxLr" );
var idTxt = charIDToTypeID( "Txt " );
var idsetd = charIDToTypeID( "setd" );
    var desc6 = new ActionDescriptor();
    var idnull = charIDToTypeID( "null" );
        var ref1 = new ActionReference();
        ref1.putEnumerated( idTxLr, charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt"  ));
    desc6.putReference( idnull, ref1 );
        var desc7 = new ActionDescriptor();
        desc7.putString( idTxt, theText );
            var list2 = new ActionList();
//////////////////
// define each letter individually;
var theIndex = 0;
var thisStyleRange = theStyleRanges[theIndex];
var thisFont = theFonts[theIndex];
for (var m = 0; m < theText.length; m++) {
// check for relevant style range;
    if (m == thisFont[3]) {
        theIndex++;
        var thisStyleRange = theStyleRanges[theIndex];
        var thisFont = theFonts[theIndex];
        };
// apply style range but change font;
    var desc14 = new ActionDescriptor();
    desc14.putInteger( idFrom, m );
    desc14.putInteger( idT, m+1 );
        var desc15 = new ActionDescriptor();
        var desc15 = thisStyleRange;
        var aRandomFont = newFonts[Math.floor(Math.random()*3)];
        desc15.putString( stringIDToTypeID( "fontPostScriptName" ), aRandomFont);
    desc14.putObject( idTxtS, idTxtS, desc15 );
    list2.putObject( idTxtt, desc14 );
    };
//////////////////
var desc7 = new ActionDescriptor();
var list3 = new ActionList();
    for (var n = kernRange.count-1; n >= 0; n--) {
        var thisOne = kernRange.getObjectValue(n);
        var desc15 = new ActionDescriptor();
        desc15.putInteger( idFrom, thisOne.getInteger(charIDToTypeID("From")) );
        desc15.putInteger( idT, thisOne.getInteger(charIDToTypeID( "T   " )) );
        desc15.putInteger( charIDToTypeID( "Krng" ), thisOne.getInteger(stringIDToTypeID("kerning")));
        list3.putObject( stringIDToTypeID( "kerningRange"),  desc15);
        };
        desc7.putList( idTxtt, list2 );
        desc7.putList( stringIDToTypeID( "kerningRange"), list3);
    desc6.putObject( idT, idTxLr, desc7 );
executeAction( idsetd, desc6, DialogModes.NO );
}
catch (e) {};
};
//////////////////

 

 

NotPsyclone
Participant
May 10, 2022

Thank you c.pfaffenbichler for your help, this script appears to work, however with two bugs I am trying to fix:

 

1) The script tries to identify a default font, and it intermixes with the array of defined fonts. Preferably this script would randomize all letters to the coded array of fonts and nothing else. I am trying to remove that module but I can't quite figure this part out.

 

2) The script is really picky with what fonts work. I doubt that is the code's fault, as when I use certain fonts it works fine (minus the default font thing) but I cannot figure out why certain ones work and why certain ones don't. All cloud fonts seem to not work, which is fine for my project. however even regular fonts like Arial (and its substyles) don't appear to be recognized. Probably a really easy fix or explanation but I can't figure it out.

 

Thank you all for your help on this.

Legend
May 10, 2022
Legend
May 6, 2022

You have to use Action Manager code to style text within a TextItem.

I have a sample script that you can modify, this splits a TextItem into lines and applies formatting but you can loop it with each character if you wanted.

----------------------------------

/*
Utility PS Scripts created by David M. Converse ©2018-21

This script replaces text and sets styling

Last modified 6/2/2021

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#target photoshop

layerUpdate();

function setFormatting(start, end, fontName, fontStyle, fontSize){ //format text per input
    var idsetd = app.charIDToTypeID('setd');
    var action = new ActionDescriptor();
    var idnull = app.charIDToTypeID('null');
    var reference = new ActionReference();
    var idTxLr = app.charIDToTypeID('TxLr');
    var idOrdn = app.charIDToTypeID('Ordn');
    var idTrgt = app.charIDToTypeID('Trgt');
    reference.putEnumerated(idTxLr, idOrdn, idTrgt);
    action.putReference(idnull, reference);
    var idT = app.charIDToTypeID('T   ');
    var textAction = new ActionDescriptor();
    var idTxtt = app.charIDToTypeID('Txtt');
    var actionList = new ActionList();
    var textRange = new ActionDescriptor();
    var idFrom = app.charIDToTypeID('From');
    textRange.putInteger(idFrom, start);
    textRange.putInteger(idT, end);
    var idTxtS = app.charIDToTypeID('TxtS');
    var formatting = new ActionDescriptor();
    var idFntN = app.charIDToTypeID('FntN');
    formatting.putString(idFntN, fontName);
    var idFntS = app.charIDToTypeID('FntS');
    formatting.putString(idFntS, fontStyle);
    var idSz = app.charIDToTypeID('Sz  ');
    var idPnt = app.charIDToTypeID('#Pnt');
    formatting.putUnitDouble(idSz, idPnt, fontSize);
    textRange.putObject(idTxtS, idTxtS, formatting);
    actionList.putObject(idTxtt, textRange);
    textAction.putList(idTxtt, actionList);
    action.putObject(idT, idTxLr, textAction);
    app.executeAction(idsetd, action, DialogModes.NO);
	}

function layerUpdate(){
    //sample values
        var size1 = 96
        var size2 = 42;
        var font1 = 'Calibri';
        var style1 = 'bold';
        var texth = 220;
        var textw = 460;
    
    if(documents.length > 0){
        var originalDialogMode = app.displayDialogs;
        app.displayDialogs = DialogModes.ERROR;
        var originalRulerUnits = preferences.rulerUnits;
        var j = 0;
        try{
            var docRef = activeDocument;
            preferences.rulerUnits = Units.POINTS;
            var m = 0;
            for(var i = 0; i < docRef.artLayers.length; i++){
                var LayerRef = docRef.artLayers[i];
                if(LayerRef.kind == LayerKind.TEXT){
                    var TextRef = LayerRef.textItem;
                    TextRef.textComposer = TextComposer.ADOBESINGLELINE;
                    var layerText = TextRef.contents;
                    //can iterate through multiple find/replace pairs
                    var newText = layerText.replace('old', 'new');
                    newText = newText.replace('old-1', 'new-1');
                    newText = newText.replace('old-2', 'new-2');
                    if(newText != layerText){
                        j = i;
                        TextRef.contents = newText;
                        if(TextRef.size == size1){
                            TextRef.size = size2;
                            TextRef.font = font1;
                            TextRef.useAutoLeading = false;
                            TextRef.leading = size2;
                            var l = TextRef.contents.split(/\r/);
                            docRef.activeLayer = LayerRef;
                            setFormatting(0, l[0].length, font1, style1, size1); //send to formatting function
                            preferences.rulerUnits = Units.PIXELS;
                            if(TextRef.kind == TextType.PARAGRAPHTEXT){
                                TextRef.height = texth;
                                TextRef.width = textw;
                                }
                            preferences.rulerUnits = Units.POINTS;
                            break;
                            }
                        }
                    }
                }
            }
        catch(e){
            alert(e + '  ' + e.line);
            preferences.rulerUnits = originalRulerUnits;
            app.displayDialogs = originalDialogMode;
            return;
            }
        preferences.rulerUnits = originalRulerUnits;
        app.displayDialogs = originalDialogMode;
        }
    else{
        alert('You must have a document open to run this script.');
        return;
        }
    }
c.pfaffenbichler
Community Expert
Community Expert
May 6, 2022

How complex are the Type Layers? 

Do they combine different colors, font sizes, leading, tracking, kerning, …?