How to make text fit inside a paragraph bounding box

Guest
Jan 04, 2018 Jan 04, 2018

Copy link to clipboard

Copied

I have a JSON file like:

{
     "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam dui ante, euismod id quam eu."
}

The text object above contain 15 character. And, I have 6 different Photoshop file (.psd) with completely different template and design.

Each psd file have a paragraph text box of size say

1st => 600px X 600px

2nd => 500px X 500px

3rd => 400px X 400px

...

and so on. They all have different size of bounding box. (They don't have exact square size as mentioned above, in actual they are of sizes like 500px X 120px or 300px X 750px, They are of different sizes)

I am writing a script using JavaScript which take the text object from JSON and change the text content of these paragraph text box.

// Include the JSON Parser

#include json2js.js

var originalunit = preferences.rulerUnits;

preferences.rulerUnits = Units.PIXELS;

// function to read json

function loadJson(relpath){

    var script = new File($.fileName);

    var jsonFile = new File(script.path + '/../json/' + relpath);

    jsonFile.open( 'r' );

    var str = jsonFile.read();

    jsonFile.close();

    return JSON.parse(str);

}

// Read the JSON

var json = loadJson( 'test.json' );

var docRef = app.activeDocument;

var layerRef = docRef.artLayers.getByName( 'text' );

layerRef.textItem.contents = json.text;

var textHeight = layerRef.bounds[2] - layerRef.bounds[0];

var boundHeight = layerRef.textItem.height;

var currentFontSize = layerRef.textItem.size;

currentFontSize = currentFontSize.toString().replace(" pt", "");

if(textHeight > boundHeight) {

    fitText(textHeight, boundHeight, currentFontSize, layerRef, json);

}

// function to fit text inside the  box

function fitText(textHeight, boundHeight, currentFontSize, layerRef, json){

    while(textHeight > boundHeight) {

         currentFontSize--;

         layerRef.textItem.size = currentFontSize;

         layerRef.textItem.contents = json.quotes;

         textHeight = layerRef.bounds[2] - layerRef.bounds[0];

     }

}

I have set the font-size of text box, so that 15 character can perfectly fit in the box.

But the text object can change to any number of character, say 30, 50 or 70 character. Since, the size of the text box and the font size is fixed in the template,

How can I resize the font-size to automatically fit any number of character within the text box ?

Or, Is there any other approach to accomplish this ?

TOPICS
Actions and scripting

Views

2.8K

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
LEGEND ,
Jan 04, 2018 Jan 04, 2018

Copy link to clipboard

Copied

That if you want to replace text and change size of new one to fit horizontally old text bounds:

s = (tI = (tL = (lyr = (aD = activeDocument).layers).getByName( t = 'text' )).textItem).size

aD.activeLayer = lyr.getByName(t), tW = (b = tL.bounds)[2] - b[0], tI.contents = 'new ' + t

tI.size = tW / ((b = aD.activeLayer.bounds)[2] - b[0]) * s

That to replace text, change size of new one to fit horizontally old text bounds,

and get resulted vertically centered to middle point of old one:

s = (tI = (tL = (lyr = (aD = activeDocument).layers).getByName(t = 'text' )).textItem).size

tW = (b1 = tL.bounds)[2] - b1[0], tI.contents = (trts = t + '\r' + t + ' ') + trts + t + ' ' + t

aD.activeLayer = (aL = lyr.getByName(t)), tI.size = tW / ((b2 = aL.bounds)[2] - b2[0]) * s

m2 = (b2 = aL.bounds)[1] + (b2[3] - b2[1]) / 2, m1= b1[1]+ (b1[3] - b1[1]) / 2, aL.translate(0, m1 - m2)

That to replace text, change size of new one to fit vertically old text bounds,

and get resulted horizontally centered to middle point of old one:

s = (tI = (tL = (lyr = (aD = activeDocument).layers).getByName(t = 'text' )).textItem).size

tW = (b1 = tL.bounds)[2] - b1[0], tI.contents = (trts = t + '\r' + t + ' ') + trts + t + ' ' + t

aD.activelayer=(aL=lyr.getByName(t)),tI.size=(b1[3]-b1[1])/((b2=tL.bounds)[3]-b2[1])*s

v2 = (b2 = aL.bounds)[1] + (b2[3] - b2[1]) / 2, v1= b1[1] + (b1[3] - b1[1]) / 2

h2 = b2[0] + (b2[2] - b2[0]) / 2, h1 = b1 + tW / 2, aL.translate(h1 - h2, v1 - v2)

Ps. I noticed interesting thing. When you use directely tI.content = 'text' only text in the layers changes.

But when you do txt = 'text', tI = txt then both text in layer and name of layer are changing (?!)

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
Guest
Jan 04, 2018 Jan 04, 2018

Copy link to clipboard

Copied

Hi, Kukurykus

Thanks for the reply. But I didn't understand your code. May be because I am new to Photoshop scripting. Could you please add comments to your code ?

But here is want I am after,

62cat30.jpg

There are 62 character in the box, with font size of 30 pt.

99cat27.jpg

There are 99 characters with 27 pt.

Notice, how the font size is changed to fit the text content inside the box.

134cat22.jpg

In the above image, there are 134 characters with 22 pt.

No matter how many character the text object in JSON have, the font size should be adjusted to fit the text content inside the box.

I am stucked from last 2 days. Please, help me.

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
LEGEND ,
Feb 03, 2018 Feb 03, 2018

Copy link to clipboard

Copied

You find it also in the topic you created on there How to make text fit inside a paragraph bounding box - PS-SCRIPTS.COM

ne = (e = '==').replace(e[0], '!'), text = txt = 'Some text, some more text. '

Number.prototype.mltpl = function(v) {while(v < this) txt += text, v++; return txt}

I = !(i = 0), c = (tI = (aD = activeDocument).activeLayer.textItem).contents = 50..mltpl(i)

function comparison() {return eval((a = arguments)[0].length + a[1] + a[2].length)}

eval(evl1 = 'aHS = aD.activeHistoryState, tI.kind = TextType.POINTTEXT')

if (comparison(tI.contents, e, c)) I = 2; eval(evl2 = 'aD.activeHistoryState = aHS')

Number.prototype.fit = function(v) {// I didn't use algorithm to speed up process

     do{tI.size -= this, eval(evl1); if (this > .1) i++; c = tI.contents = tI.contents, eval(evl2)}

     while(comparison(c, v, txt)); if (i) I = 3 else if(!i && !~~this) I = 4}// do it yourself!

o = veryLongObject = {'1': ne, '-1': e, '.1': ne, '-.01': e, '-.1': e, '.01': ne}

for(j in o) if ((len = j.length) == I) {(+j).fit(o); if (len > 3) {tI.size -= .01; break}}

Modify this examplary script for your needs. To check result with different values change 50 number in 50..mltpl(i) to other.

With implemented ActionManager code for size of font there's 25% faster performance:

function fontSize(v) {

     function sTT(v) {return stringIDToTypeID(v)} (ref1 = new ActionReference()).putProperty(sTT('property'), tS = sTT('textStyle'))

     ref1.putEnumerated(sTT('textLayer'), sTT('ordinal'), sTT('targetEnum')); (dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1);

     (dsc2 = new ActionDescriptor()).putInteger(sTT('textOverrideFeatureName'), 808465458), dsc2.putInteger(sTT('typeStyleOperationType'), 3)

     dsc2.putUnitDouble(sTT('size'), sTT('pixelsUnit'), v), dsc1.putObject(sTT('to'), tS, dsc2), executeAction(sTT('set'), dsc1, DialogModes.NO);

}

ne = (e = '==').replace(e[0], '!'), text = txt = 'Some text, some more text. '

Number.prototype.mltpl = function(v) {while(v < this) txt += text, v++; return txt}

I = !(i = 0), c = (tI = (aD = activeDocument).activeLayer.textItem).contents = 50..mltpl(i)

function comparison() {return eval((a = arguments)[0].length + a[1] + a[2].length)}

eval(e1 = 'aHS = aD.activeHistoryState, tI.kind = TextType.POINTTEXT')

if (comparison(tI.contents, e, c)) I = 2; eval(e2 = 'aD.activeHistoryState = aHS')

Number.prototype.fit = function(v) {

     do{

          fontSize(tI.size - this), eval(e1); if (this > .1) i++; c = tI.contents = tI.contents, eval(e2)

     }

     while(comparison(c, v, txt)); if ( i ) I = 3 else if(!i && !~~this) I = 4

}

o = veryLongObject = {'1': ne, '-1': e, '.1': ne, '-.01': e, '-.1': e, '.01': ne}

for(j in o) if ((len = j.length) == I) {(+j).fit(o); if (len > 3) {fontSize(tI.size - .01); break}}

EDIT:

Another succesfull attempt to do the same but MUCH faster, actually so fast that you can do it all with new script manually without automation for many different single texts, and you should finish before earlier code I made did the same job (even if there was automatated loop for many texts)

Following script is made for something more than your little one line texts. It works for huge articles with thousends words and hundreds paragraps! You don't need it, so to optimize effect of your work change 2 values. 0.125 to 1.125 at end of 19th line, and 87.5 to 50 at end of 21th line:

function font(v1, v2) {

     if (v2 != false) rH = v2 ? v1 * prc = ~~(hgt / (sal = (v1 * tI.autoLeadingAmount / 100))) * sal / hgt : (prc = 1, v1);

     (ref1 = new ActionReference()).putProperty(sTT('property'), sTT(S = ((f = v2 == false) ? 'paragraph' : 'text' ) + 'Style'))

     ref1.putEnumerated(sTT('textLayer'), sTT('ordinal'), sTT('targetEnum')), (dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1);

     (dsc2 = new ActionDescriptor()).putInteger(sTT('textOverrideFeatureName'), 808460000 + (f ? 4692 : (5400 + (v1 ? 58 : 62))))

     if (!f) {

          dsc2.putInteger(sTT('typeStyleOperationType'), 3), v1 ? dsc2.putUnitDouble(sTT('size'), sTT('pointsUnit'), rH) : dsc2.putBoolean(sTT('autoLeading'), true)

     }

     else {

          dsc2.putDouble(sTT('autoLeadingPercentage'), ~~v1 / 100); for(n = !(i = 0); i < (wlg = ['Word', 'Letter', 'Glyph']).length; i++) {

               for(j = 0; j < (mdm = ['Minimum', 'Desired', 'Maximum']).length; j++) dsc2.putDouble(sTT('justification' + wlg + mdm), !!n); n--

          }

     }

     dsc1.putObject(sTT('to'), sTT(S), dsc2), executeAction(sTT('set'), dsc1, DialogModes.NO); if (v2 != false) return rH

}

function sTT(v) {return stringIDToTypeID(v)} preferences.typeUnits = TypeUnits.POINTS

font((wh = [(tI = (aL = (aD = activeDocument).activeLayer).textItem).width, tI.height])[Math.abs(+!~~(wh[0] / wh[1]) - 1)] / tI.size * tI.size.as('px') * .125)

font((hgt = tI.height / 72 * (res = aD.resolution)) / (sa = (font(tI.size / Math.sqrt(tI.contents.length/ (function() {return tI.hyphenation = false, font(87.5, false),

arr = [], tI.contents = txt = tI.contents.replace(/\r/g, function(_, v) {return arr.push(v), ' '}), font(), aHS = aD.activeHistoryState, tI.kind = TextType.POINTTEXT,

len = tI.contents.length, aD.activeHistoryState = aHS, len})()))) * (aLA = tI.autoLeadingAmount) / 100) / (((b = aL.bounds)[3] - b[1] ) / sa) * aLA / 72 * res * prc, false)

i = 0; tI.contents =  txt.replace(/ /g, function(_, v) {return v == arr ? (i++, '\r' ) : ' '}) font(tI.size * Math.sqrt((r = hgt / (tI.size * tI.autoLeadingAmount / 100)) / (r + i)), true)

font(tI.autoLeadingAmount * (!((function(){return len1 = tI.contents.length, aHS = aD.activeHistoryState, tI.kind = TextType.POINTTEXT, r = tI.contents.match(/\r|$/g).length,

R = (con = len1 != len2 = tI.contents.length) ? tI.contents.match(/(.*)(?=\r+$)/)[1].length - 1 : (hgt / 72 * res) / (tI.size.as('px') * (tI.autoLeadingAmount / 100) * r),

aD.activeHistoryState = aHS, con})()) ? R : ((put = tI.contents.slice(R).match(/\r/g)) ? r / (r + put.length + 1) : len2 / len1)), false)

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 ,
Mar 19, 2022 Mar 19, 2022

Copy link to clipboard

Copied

hey, Kukurykus i've tried the script you wrote in the 'edit:' but, even tho the sizes are being adapted, there is still a big gap between the paragraph size and the text.

i've tried to tweek it but i always seem to breake it, and i cant get to understand most of the code, can you coment on this code a little more or perhaps share a more accurate version? thanks in advance! you are great.

image.pngimage.png

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
LEGEND ,
Mar 20, 2022 Mar 20, 2022

Copy link to clipboard

Copied

LATEST

It should work best for big texts in paragraph mode. Additionally due to platform transition few years ago the codes posted here can be crippled, so surely only working ones would be those from other, linked forum.

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
Explorer ,
Apr 26, 2018 Apr 26, 2018

Copy link to clipboard

Copied

hi i am using photoshop cc2018

& i have the same question : How can I resize the font-size to automatically fit any number of character within the text box ?

i have tried the above scripts, save them as script.jsx, load them to photoshop but non of them actually worked.

so can any body provide me the script that solve this problem!

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