Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Kerning script

Explorer ,
Jul 06, 2024 Jul 06, 2024

I'm not happy with the hanging punctuation in photoshop, but I can't work in indesign, where this feature is much more elaborate. I want to make a script with manual kerning on punctuation marks, but I can't figure out what I'm doing wrong.

function adjustPunctuationKerning() {
    var textLayer = app.activeDocument.activeLayer;
    if (textLayer.kind !== LayerKind.TEXT) {
        return; 
    }

    var textItem = textLayer.textItem;

    for (var i = 0; i < textItem.contents.length; i++) {
        var currentCharacter = textItem.contents[i];
        switch (currentCharacter) {
            case '?':
                textItem.characters[i+1].kerning = -300;
                break;
            case '!':
                textItem.characters[i+1].kerning = -100;
                break;
            case '.':
                textItem.characters[i+1].kerning = -100;
                break;
        }
    }
}

adjustPunctuationKerning();

 I don't know js well and i hope photoshop gives kerning manipulation in text layer 

enperror_0-1720260183795.png

 

TOPICS
Actions and scripting , Windows
1.5K
Translate
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

correct answers 1 Correct answer

Community Expert , Jul 21, 2024 Jul 21, 2024

Screenshot 2024-07-22 at 08.05.58.pngScreenshot 2024-07-22 at 08.06.10.png

// apply properties like fonts, size, … to letters of words in selected type layer;
// kerning in this case and for all type layers in the document;
// 2024, use it at your own risk;
if (app.documents.length > 0) {
    var theRegExp = /\?/gi;
    var theTypeLayers = collectTypeLayers ();
    for (var m = 0; m < theTypeLayers.length; m++) {
	changeWordProperties (theTypeLayers[m][1], theRegExp, -500)
    }
};
//////////////////
////// change word properties //////
function changeWordProperties (
...
Translate
Adobe
Community Expert ,
Jul 06, 2024 Jul 06, 2024

Where did you get the code from?

Translate
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
Explorer ,
Jul 06, 2024 Jul 06, 2024

gpt and plus I tried

Translate
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
Community Expert ,
Jul 06, 2024 Jul 06, 2024

Chat GPT is getting better, but it can often make stuff up to be "helpful".

 

https://theiviaxx.github.io/photoshop-docs/Photoshop/TextItem.html

Translate
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
Explorer ,
Jul 06, 2024 Jul 06, 2024

Thank you! But now I'm confused, as I understood, manual kerning can only be done on the whole text, but not on certain characters?
https://theiviaxx.github.io/photoshop-docs/Photoshop/AutoKernType/MANUAL.html#autokerntype-manual

Translate
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
Community Expert ,
Jul 06, 2024 Jul 06, 2024

DOM code is limited in what it can do. Scripting text isn't fun.

 

If what you want to accomplish is even possible, it will likely require advanced Action Manager coding, which is beyond my knowledge and capabilities.

 

Search the Photoshop forum and make sure that the topic is for Photoshop and not Illustrator or InDesign etc.

 

Or look into the new UXP/BatchPlay scripting instead of legacy ExtendScript.

 

Translate
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
Community Expert ,
Jul 07, 2024 Jul 07, 2024

 

quote

as I understood, manual kerning can only be done on the whole text, but not on certain characters?

By @enperror

 


Kerning is only for a pair of characters. Tracking is for a range of characters.

https://helpx.adobe.com/indesign/using/kerning-tracking.html

 

Jane

Translate
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
Explorer ,
Jul 07, 2024 Jul 07, 2024

I misspoke, just below clarified what I was getting at.

Translate
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
Explorer ,
Jul 06, 2024 Jul 06, 2024

More precisely, it triggers on the first pair of letters, but how to specify the right one - no idea.

enperror_0-1720266047344.png

enperror_1-1720266058757.png

 

Translate
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
Community Expert ,
Jul 07, 2024 Jul 07, 2024

How important is this? 

Do you have to perform this task multiple times every day? 

 

As @Stephen Marsh mentioned, the AM-code that could be used to achieve this is not exactly »easy«. 

Translate
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
Explorer ,
Jul 07, 2024 Jul 07, 2024

Since I have to do it in photoshop, it takes me quite a bit of time.

Translate
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
Community Expert ,
Jul 07, 2024 Jul 07, 2024

Please clarify what »quite a bit of time« actually means. 

Translate
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
Explorer ,
Jul 07, 2024 Jul 07, 2024

I letterer comics/manga, it takes me a couple hours to lettering, let's say, one chapter. of those couple hours, at least 10-15 minutes are spent compensating for punctuation with kerning. Automating this function would help save a lot of time.

Translate
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
Community Expert ,
Jul 07, 2024 Jul 07, 2024

 

Since I have to do it in photoshop, it takes me quite a bit of time.

By @enperror

 

Yes, you should expect that trying to work with text in Photoshop will take significantly more time than it does in InDesign. InDesign has excellent type controls and Illustrator has good text controls. Photoshop's main focus is images so it has limited text controls.

 

When I started using PS, text was pixels on the current layer. Kerning meant selecting all the letter to the right and using the Move tool. Tracking meant repeating the process for the other letters.

 

Photoshop's type controls have come a long way since then, but it is not expected that it will ever duplicate those in InDesign. I know you said you can't use InDesign for this project, so unfortunately you will have to deal with Photoshop's limitations. If a script won't work, then expect it to take a long time and hope that you don't need to make edits later.

 

Jane

 

 

 

Translate
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
Community Expert ,
Jul 07, 2024 Jul 07, 2024

This seems tricky me indeed. 

I have provided Scripts for changing the color of words in the past, but that happens in the textStyleRange, changing the kerning would necessitate splitting the kerningRange at the indices of the character (the question mark in this case). 

Translate
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
Explorer ,
Jul 07, 2024 Jul 07, 2024

I tried writing new code, it triggers, but instead of executing the function, I have a kerning 0 applied to all the text

if (app.documents.length > 0) {
    var doc = app.activeDocument; 
    
    for (var i = 0; i < doc.artLayers.length; i++) {
        var layer = doc.artLayers[i];
        
        if (layer.kind === LayerKind.TEXT) {
            var textItem = layer.textItem;
            
            textItem.autoKerning = AutoKernType.MANUAL;
            
            for (var j = 0; j < textItem.contents.length; j++) {
                if (textItem.contents[j] === "?") {
                    textItem.kerning = 220;
                }
            }
        }
    }
    alert("yep.");
} else {
    alert("nope.");
}

yonens_0-1720366831226.pngyonens_1-1720366852065.png

Most likely, as @Stephen_A_Marsh wrote to me earlier, UXP is required

Translate
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
Community Expert ,
Jul 07, 2024 Jul 07, 2024
quote

Most likely, as @Stephen_A_Marsh wrote to me earlier, UXP is required

While UXP code may be better at handling it AM code can handle the task, too, DOM code is the useless one in this case. 

Translate
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
Community Expert ,
Jul 21, 2024 Jul 21, 2024

Edit: The Script is kind of a proof-of-concept (based on some old scripts, so it contains superfluous lines) and applies to the selected Type Layer. 

Screenshot 2024-07-21 at 12.41.26.pngScreenshot 2024-07-21 at 13.03.17.png

 

 

// apply properties like fonts, size, … to letters of words in selected type layer;
// kerning in this case;
// 2024, use it at your own risk;
if (app.documents.length > 0) {
    var theRegExp = /\?/gi;
	changeWordProperties (theRegExp, -500)
};
//////////////////
////// change word properties //////
function changeWordProperties (theRegExp, newKerning) {
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
//////////////////
try {
var theFonts = new Array;
var theStyleRanges = new Array;
var theStyleRanges2 = 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'));
// get indices for string;
var indicesCount = 0;
var theIndices = new Array;
while ((result = theRegExp.exec(theText))!=null) {
    theIndices.push([result.index, result.index+result[0].length])
};
//var shapeList = textDesc.getList(stringIDToTypeID('textShape'));
var paragraphRangeList = textDesc.getList(stringIDToTypeID('paragraphStyleRange'));
var kernRange = textDesc.getList(stringIDToTypeID('kerningRange'));
var rangeList = textDesc.getList(stringIDToTypeID('textStyleRange'));
var idPxl = charIDToTypeID( "#Pxl" );
var idPnt = charIDToTypeID( "#Pnt" );
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" );
// change text;
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 kerningRanges = new ActionList;
var theCounter = 0;
if (kernRange.count > 0) {var thisKerningRange = kernRange.getObjectValue(theCounter)};
// kernRange
for (var m = 0; m < theText.length; m++) {
// check for relevant existing kerning range;
if (kernRange.count > 0) {
    if (thisKerningRange.getInteger(idFrom) == m) {
        kerningRanges.putObject( stringIDToTypeID( "kerningRange"),  thisKerningRange);
        theCounter++;
        if (theCounter < kernRange.count) {
            var thisKerningRange = kernRange.getObjectValue(theCounter)
        }
    };
};
// check for regexp;
if (theText[m].match(theRegExp) != null) {
var desc15 = new ActionDescriptor();
desc15.putInteger( idFrom, m - 1 );
desc15.putInteger( idT, m );
desc15.putInteger( stringIDToTypeID("kerning"), newKerning );
kerningRanges.putObject( stringIDToTypeID( "kerningRange"),  desc15);
};
};
//////////////////////////////////////////////////////
//desc7.putList( idTxtt, list2 );
desc7.putList( idTxtt, rangeList );
desc7.putList( stringIDToTypeID( "kerningRange"), kerningRanges);
//////////////////////////////////////////////////////
desc6.putObject( idT, idTxLr, desc7 );
executeAction( idsetd, desc6, DialogModes.NO );
}
}
}
catch (e) {};
app.preferences.rulerUnits = originalRulerUnits;
};

 

 

Translate
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
Community Expert ,
Jul 21, 2024 Jul 21, 2024

Screenshot 2024-07-22 at 08.05.58.pngScreenshot 2024-07-22 at 08.06.10.png

// apply properties like fonts, size, … to letters of words in selected type layer;
// kerning in this case and for all type layers in the document;
// 2024, use it at your own risk;
if (app.documents.length > 0) {
    var theRegExp = /\?/gi;
    var theTypeLayers = collectTypeLayers ();
    for (var m = 0; m < theTypeLayers.length; m++) {
	changeWordProperties (theTypeLayers[m][1], theRegExp, -500)
    }
};
//////////////////
////// change word properties //////
function changeWordProperties (theId, theRegExp, newKerning) {
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
//////////////////
try {
// get font of active layer;
var ref = new ActionReference();
ref.putIdentifier( charIDToTypeID("Lyr "), theId ); 
var layerDesc = executeActionGet(ref);
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
// 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 kernRange = textDesc.getList(stringIDToTypeID('kerningRange'));
var rangeList = textDesc.getList(stringIDToTypeID('textStyleRange'));
var idTxtt = charIDToTypeID( "Txtt" );
var idFrom = charIDToTypeID( "From" );
var idT = charIDToTypeID( "T   " );
var idTxLr = charIDToTypeID( "TxLr" );
var idTxt = charIDToTypeID( "Txt " );
var idsetd = charIDToTypeID( "setd" );
// change text;
var desc6 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
    var ref1 = new ActionReference();
    ref1.putIdentifier( idTxLr, theId );
desc6.putReference( idnull, ref1 );
    var desc7 = new ActionDescriptor();
    desc7.putString( idTxt, theText );
//////////////////////////////////////////////////////
var kerningRanges = new ActionList;
var theCounter = 0;
if (kernRange.count > 0) {var thisKerningRange = kernRange.getObjectValue(theCounter)};
// kernRange
for (var m = 0; m < theText.length; m++) {
// check for relevant existing kerning range;
if (kernRange.count > 0) {
    if (thisKerningRange.getInteger(idFrom) == m) {
        kerningRanges.putObject( stringIDToTypeID( "kerningRange"),  thisKerningRange);
        theCounter++;
        if (theCounter < kernRange.count) {
            var thisKerningRange = kernRange.getObjectValue(theCounter)
        }
    };
};
// check for regexp;
if (theText[m].match(theRegExp) != null) {
var desc15 = new ActionDescriptor();
desc15.putInteger( idFrom, m - 1 );
desc15.putInteger( idT, m );
desc15.putInteger( stringIDToTypeID("kerning"), newKerning );
kerningRanges.putObject( stringIDToTypeID( "kerningRange"),  desc15);
};
};
//////////////////////////////////////////////////////
desc7.putList( idTxtt, rangeList );
desc7.putList( stringIDToTypeID( "kerningRange"), kerningRanges);
//////////////////////////////////////////////////////
desc6.putObject( idT, idTxLr, desc7 );
executeAction( idsetd, desc6, DialogModes.NO );
}
}
}
catch (e) {};
app.preferences.rulerUnits = originalRulerUnits;
};
////// collect type layers from active document //////
function collectTypeLayers () {
// get number of layers;
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var applicationDesc = executeActionGet(ref);
var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
var theLayers = new Array;
for (var m = 0; m <= theNumber; 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"));
// if not layer group collect values;
if (layerSet != "layerSectionEnd" && layerSet != "layerSectionStart" && isBackground != true && layerDesc.hasKey(stringIDToTypeID("textKey")) == true) {
var visible = layerDesc.getBoolean(stringIDToTypeID("visible"));
var theName = layerDesc.getString(stringIDToTypeID('name'));
var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
theLayers.push([theName, theID, visible])
};
}
catch (e) {};
};
return theLayers
};
Translate
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
Community Expert ,
Jul 23, 2024 Jul 23, 2024

@enperror , have you tested the Script yet? 

Translate
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
Explorer ,
Nov 29, 2024 Nov 29, 2024

Hi, yes it works, thx! but there is one problem. The selected kerning is applied to the character, but the previous character becomes 0 kerning. I attached a screenshot, it shows ? has a kerning of -250 and the previous character became 0 kerning

enperror_0-1732877490110.png

enperror_1-1732877496850.png

I tried to figure out what the problem is, but it's probably because the kerning is trying to apply to both sides of the character

Translate
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
Community Expert ,
Nov 29, 2024 Nov 29, 2024

Please provide the original file and the resulting file. 

Translate
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
Explorer ,
Nov 29, 2024 Nov 29, 2024

 

enperror_1-1732878598934.png

enperror_2-1732878619298.png

before “?” kerning 0 in the resulting file 

 

Translate
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
Community Expert ,
Nov 29, 2024 Nov 29, 2024

Please provide the original file and the resulting file, not screenshots. 

Translate
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
Explorer ,
Nov 29, 2024 Nov 29, 2024

oh, okay 

Translate
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