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 ?
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 (?!)
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,
There are 62 character in the box, with font size of 30 pt.
There are 99 characters with 27 pt.
Notice, how the font size is changed to fit the text content inside the box.
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.
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}}
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)
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.
Copy link to clipboard
Copied
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.
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!