4 Replies Latest reply on Apr 26, 2018 2:23 AM by KatrodiyaDhaval

    How to make text fit inside a paragraph bounding box

    amanv12

      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 ?

        • 1. Re: How to make text fit inside a paragraph bounding box
          Kukurykus Adobe Community Professional

          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 (?!)

          1 person found this helpful
          • 2. Re: How to make text fit inside a paragraph bounding box
            amanv12 Level 1

            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.

             

            • 3. Re: How to make text fit inside a paragraph bounding box
              Kukurykus Adobe Community Professional

              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[j]); 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[j]); 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[i] + mdm[j]), !!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] ? (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)
              
              
              • 4. Re: How to make text fit inside a paragraph bounding box
                KatrodiyaDhaval Level 1

                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!