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

New Here ,
May 04, 2022 May 04, 2022

Copy link to clipboard

Copied

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

 

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? 

TOPICS
Actions and scripting , Windows

Views

117

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 05, 2022 May 05, 2022

Copy link to clipboard

Copied

How complex are the Type Layers? 

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

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 06, 2022 May 06, 2022

Copy link to clipboard

Copied

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

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 08, 2022 May 08, 2022

Copy link to clipboard

Copied

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: 

Screenshot 2022-05-08 at 13.25.12.pngScreenshot 2022-05-08 at 13.26.34.png

 

 

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) {};
};
//////////////////

 

 

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
May 10, 2022 May 10, 2022

Copy link to clipboard

Copied

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.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 10, 2022 May 10, 2022

Copy link to clipboard

Copied

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 10, 2022 May 10, 2022

Copy link to clipboard

Copied

Ad 1) No idea how that would mix in.

Could you provide a File (original and result) and the Script (or just the Fonts-Array) where this happened? 

 

Ad 2) Again no idea.

Do you get errors or are they »silently« disregarded? 

How did you determine the fonts’ PostsScriptNames? 

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 11, 2022 May 11, 2022

Copy link to clipboard

Copied

LATEST

I wonder if the array »newFonts« might contain malformed fontPostScriptNames and those instances get resplaced with … not quite sure with what. 

Could you apply the fonts you want to some text manually and run the following Script? 

Edit: Then compare the names in the alert with the ones in your array. 

 

// 2015, use it at your own risk;
if (app.documents.length > 0) {
var myDocument = app.activeDocument;
//
main ();
};
////////////////////////////////////
function main () {
var theFonts = new Array;
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var applicationDesc = executeActionGet(ref);
var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
//////
for (var m = theNumber; m >= 0; m--) {
try {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), m);
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 paragraphRangeList = textDesc.getList(stringIDToTypeID('paragraphStyleRange'));
var kernRange = textDesc.getList(stringIDToTypeID('kerningRange'));
var rangeList = textDesc.getList(stringIDToTypeID('textStyleRange'));
for (var o = 0; o < rangeList.count; o++) {
var styleDesc = rangeList.getObjectValue(o).getObjectValue(stringIDToTypeID('textStyle'));
// 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'));
	};
// add to array;
var theCheck = true;
for (var n = 0; n < theFonts.length; n++) {
if (theFonts[n] == aFont) {theCheck = false}
};
if (theCheck  == true) {theFonts.push(aFont)}
}
}
}
}
catch (e) {};
}
alert ("the result:"+"\n"+theFonts.join("\n"))
};

 

 

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 09, 2022 May 09, 2022

Copy link to clipboard

Copied

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

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
May 10, 2022 May 10, 2022

Copy link to clipboard

Copied

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

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines