7 Replies Latest reply on Jun 7, 2017 5:24 AM by Trevorׅ

    MasterSpreads with textFrames, centered text and buttons

    Cleo_helm

      Hello,

       

      As I'm quite new on JavaScript I still try to understand several (most likely) really simple things.

      I came up with a new issue on my script (writing in ExtendScripting for CS6, Windows). The document I generate should have a Master page (which I managed to do) and on this Master Page should be several textframes (I managed this as well) and two buttons (I failed).

       

      My main issue (I think) is that I don't really understand the "with()" thing.

       

      In the end it should look like this on every page in the bottom right corner, that's why I want to use a Master Spread.

      I have a variable (e.g. myDoc) and with the "with()" I can add things (properties?) to this variable, correct?

       

      Making a rectangle and something round is not an issue, I managed this with the "with()", but giving those things properties somehow will not work.

      That's why I thought to convert the textFrame later to a button with converToButton would work, as I get (except of the color) the text in the object.

       

      That is my code I have till now:

       

      var myDocument = app.documents.item(0);
            with(myDocument.masterSpreads.item(0)){
                         with(textFrames.add()){
                                  geometricBounds = [y1,x1,y2,x2];                      // position and size of Text box
                                  contents ="some text";                                       // what is written in the text box
                                  paragraphs.item(0).appliedFont = app.fonts.item("Arial");
                                  paragraphs.item(0).fontStyle = "Bold";              // bold or regular or italic
                                  paragraphs.item(0).pointSize= 16;                    // which font size is used
                                  paragraphs.item(0).justification = Justification.centerAlign;
      

       

      Issue #1:

      first of all why can I write contents and geometricBound without the "paragraph...." stuff, but the other things not?

       

      Issue #2: How Do I get the text also vertically aligned?

      --> those three options do not work:

      paragraphs.item(0).verticalJustification = verticalJustification.centerAlign;
      paragraphs.item(0).verticalJustification = Justification.centerAlign;
      paragraphs.item(0).Justification = verticalJustification.centerAlign;
      

       

      Issue #3: How can I color my textbox?

      fillColor = myColorBunt (I defined it earlier in the script as variable) also doesn't work, the error code says it needs string or swatch (how would a swatch look like, not a variable name?)

      //define color for color code
      var myColorBunt = myDocument.colors.add();
             myColorBunt.model = ColorModel.PROCESS;
             myColorBunt.space = ColorSpace.CMYK;
             myColorBunt.colorValue = [40, 10, 5, 20];
      

       

      Issue #4: Giving something a name like variables?

      Converting an object to a button needs a variable name, but in the "with()" argument I don't have a variable name?

       

      If someone would be so kind to give me some hints, I really would appreciate it.

        • 1. Re: MasterSpreads with textFrames, centered text and buttons
          Trevorׅ Adobe Community Professional

          Hi Cleo

           

          Sorry to say I did not read your post. So my post won't answer it.

          But it could help you.

          You code looks like the complete trash that is provided in the various Adobe scripting manuals.

           

          A normal method of scripting would be

           

          var doc, ms, tf, para;
          doc = app.documents[0];
          ms = doc.masterSpreads[0];
          tf = textFrames.add();
          tf.geometricBounds = [y1, x1, y2, x2]; // position and size of Text box  
          tf.contents = "some text";
          para = tf.paragraphs[0]; // Cache  
          para.appliedFont = app.fonts.item("Arial"); // Set properties after creation one at a time
          para.fontStyle = "Bold"; // bold or regular or italic  
          para.pointSize = 16; // which font size is used  
          para.justification = Justification.CENTERALIGN;
          
          
          // or
          
          doc = app.documents[0];
          ms = doc.masterSpreads[0];
          tf = textFrames.add({
              geometricBounds: [y1, x1, y2, x2],
              contents: "some text"
          }); // Set properties on creation
          para = tf.paragraphs[0]; // what is written in the text box  
          para.properties = { // Set properties after creation in bulk
              appliedFont: app.fonts.item("Arial"),
              fontStyle: "Bold", // bold or regular or italic  
              pointSize: 16, // which font size is used  
              justification: Justification.CENTERALIGN
          };
          

           

           

          No "with" (important) no item (not important)

          Set the properties on creation, after creation directly one at a time or in bulk

           

          HTH

           

          Trevor

           

          P.s.

           

          Sorry for being blunt

          1 person found this helpful
          • 2. Re: MasterSpreads with textFrames, centered text and buttons
            Laubender Adobe Community Professional & MVP

            Hi Cleo,

            another hint:

            There is no method converToButton() in the DOM ( document object model ) for pageItems.

            Therefore you have only two options:

             

            1. Construct a button from scratch with method add() for a Button object.

            2. Use the menu command with a selected pageItem in the document.

             

            Since you like to work with InDesign CS6 best download Jongware's DOM documentation.
            For ease of use, especially searchability, see the chm files. I'm on a Mac so I need the iChm app to open and navigate the files. You are on Windows and I'm sure there are similar apps or native ways to open chm files.

             

            Just a view on the method add() of Buttons:

             

            CS6-DOM-Docu-Buttons-add.png

             

            You see all properties of a button object listed under Button.

             

            Downloaded from:

            Indesign JavaScript Help

             

            Below a link to a function that is working with a menu action for converting an object to a button.

             

            Marc Autret:

            Re: How to convert textframe to button

             

            It is still working with InDesign CS6 and should also work with the latest CC 2017.1.

             

            Regards,
            Uwe

            2 people found this helpful
            • 3. Re: MasterSpreads with textFrames, centered text and buttons
              [Jongware] Most Valuable Participant

              As for your specific questions, I think it's your use of "with" that muddles the water for you. Nothing to worry about, it's one of the reasons its use is frowned upon. Like you, I learned writing scripts from Adobe's samples and then found I had to un-learn using "with" everywhere. Only when I could write cohesive scripts from scratch, I could see where I could use it (but still don't do it willy-nilly, as I feel it cramps my style ).

               

              Let's look at a few of your questions.

               

              #1: "why contents and geometricBounds without, but still need to add "paragraphs"?'

               

              Beause that's how "with" works: the base element is given in the statement (a newly added textframe), and it has properties "contents", "geometricBounds", and "paragraphs". But you want to change a property of one of its paragraphs: the font! So you cannot say "the current textframe's font", because "font" is not a property of the textframe in itself but of (each of) its paragraphs.

               

              #2: 'how do I get the text vertically aligned?'

               

              Not by changing paragraphs! It's the same, really, as in the interface itself (not a coincidence). You cannot click a single paragraph and change its vertical alignment. If that was possible, you could set one paragraph to be at the bottom of a frame, and the one under it to be at the top. That is just not possible with InDesign.

               

              The property "verticalAlignment" is not in a paragraph's properties, it's one of the textframe's properties. So this

               

              verticalJustification = verticalJustification.centerAlign;

               

              should work (sans the "paragraphs") if you are currently targeting a valid textframe through your use of "with". My preferred way -- without "with" -- would be

               

              myTextFrame.verticalJustification = verticalJustification.centerAlign;

               

              (assuming I stored the new textframe's handle in a variable "myTextFrame" on creation time).

               

              As Uwe says, (1) carefully check the DOM to see what properties and functions are for each of the objects, and (2) use variables to store "direct" pointers to objects, rather than lose your thread in a series of "with" statements.

              2 people found this helpful
              • 4. Re: MasterSpreads with textFrames, centered text and buttons
                Laubender Adobe Community Professional & MVP

                Cleo_helm  wrote


                Issue #2: How Do I get the text also vertically aligned?

                --> those three options do not work:

                paragraphs.item(0).verticalJustification = verticalJustification.centerAlign; paragraphs.item(0).verticalJustification = Justification.centerAlign; paragraphs.item(0).Justification = verticalJustification.centerAlign;

                 

                Issue #3: How can I color my textbox?

                fillColor = myColorBunt (I defined it earlier in the script as variable) also doesn't work, the error code says it needs string or swatch (how would a swatch look like, not a variable name?)

                //define color for color code var myColorBunt = myDocument.colors.add();        myColorBunt.model = ColorModel.PROCESS;        myColorBunt.space = ColorSpace.CMYK;        myColorBunt.colorValue = [40, 10, 5, 20];

                 

                Hi Cleo,

                let's see in issue 2.

                 

                If you search the DOM documention you'll find no verticalJustification on textFrame objects.


                Why? See into InDesign's UI. How would you set vertical justification of text in a text frame?

                That's an extra dialog with three tabs in CS6.

                 

                And an indication that you cannot do it directly on textFrame and have to dig deeper. Object textFrame has also a property textFramePreferences and if you follow the link in documentation, you'll notice that all the properties you see in that extra dialog are waiting here. Have a look into the UI first ( from my German InDesign CS6 ):

                 

                textFramePreferences-verticalJustification.png

                Apply the code below and read out textFrameProperties of textFrame in the ESTK.

                For ease of use: Select the text frame and work with the selection.

                 

                app.selection[0].textFramePreferences.verticalJustification; // returns CENTER_ALIGN in the JavaScript Console
                

                 

                Know, that the returned value is the name of an enumeration value, a special number that is converted by the ESTK to its string representation.
                Unfortunately it is only doing this with the last expression on the dot notation, that is necessary to apply it. The full enumeration value is VerticalJustification.CENTER_ALIGN.

                 

                So this will work on a selected text frame:

                 

                app.selection[0].textFramePreferences.verticalJustification = VerticalJustification.CENTER_ALIGN;
                

                 

                What also will work is applying the number representation of the enumeration, it's core value, but that's very hard to decipher if looking at code like that:

                 

                app.selection[0].textFramePreferences.verticalJustification = 1667591796;
                

                 

                Where did I look up this number?
                I followed the links in the documentation.

                 

                TextFrame > TextFramePreference > VerticalJustification (enum)

                 

                Here we see that verticalJustification has 4 possible values.
                And all the 4 values can be expressed by their name or by their value.

                 

                Remains the question how we can set this preference at creation time of the text frame with the add() method?

                Answer: Apply an object directly. And object is defined by this notation:

                 

                { property1 : value , property2 : value /*, etc.pp.*/ }
                

                 

                Let's try this:

                 

                app.documents[0].masterSpreads[0].pages[0].textFrames.add
                (
                    {
                
                        geometricBounds : ["85mm","50mm","100mm","100mm"] ,
                        contents : "Button" ,
                        textFramePreferences : { verticalJustification : VerticalJustification.CENTER_ALIGN } 
                
                    }
                );
                

                 

                 

                Now for issue 3 where you want to apply a color as value for the fillColor property of your text frame.

                 

                Look at your code.
                You did not apply a name to your color when using method add().

                You did apply some properties after creation time.


                You did not apply a value for property name.
                So InDesign is applying a name automatically.

                 

                And this could be problematic:

                If the user selected a not automatically named color in the Swatches Panel, that color's name would be taken for your new color and " Kopie" (German InDesign) is applied to the name. InDesign is always keen to fill in properties that are not set at creation time. By something in preferences. And if the user selected a color in the panel, this is a preference. E.g. for drawing out a new text frame.

                 

                Another, but similar scenario:

                If no document is open and the user selects a color like [Black] ( German: [Schwarz] ) in the Swatches Panel for fill the user changed a preference. Now you come back to your InDesign days after that and add a new document.

                 

                Then the user runs your code:

                A color named "Schwarz Kopie" will be added to the Swatches panel.
                If the user is doing this on an English version InDesign it will be "Black Copy".

                All other properties will be the right ones. But the name of the added color is not.

                 

                FWIW: If you already added  a new automatically named color to the document successfully you can apply it this way :

                 

                textFrame.fillColor = app.documents[0].colors.itemByName(myColorBunt.name);
                

                 

                So it might be better working with name at creation time.

                 

                If you are working with existing documents you cannot be sure if the color is there or not.

                So the following approach would be better where I am working with a not automatically named color at creation time:

                 

                // The name of the color you want to apply:
                var colorName = "buttonFill";
                // The color CALLED by its name:
                var buttonColor = app.documents[0].colors.itemByName(colorName);
                /*
                    At this point, the color could be there or not:
                    
                    Calling an object by its name is just that, a call.
                    A shout like "Cleo" where the one who shouts does not know if someone is there to answer.
                
                    You are doing nothing with buttonColor at this point.
                    So no error is thrown, if the color behind buttonColor is not there.
                    In ExtendScript terms for InDesign: if the color is not valid or as property of color:
                    buttonColor.isValid
                */
                
                // Ask if the color is there.
                // If you look up DOM documentation there is property isValid for a color object ( and for many other objects as well )
                // isValid has two possible values, true or false
                
                if(buttonColor.isValid)
                {
                    // Ok. A color by the name stored in colorName exists.
                    // But you cannot be sure, that all your intended property values for this color are ok.
                    // So you could apply them here:
                    
                    buttonColor.properties =
                        {
                            colorValue : [ 255, 255 , 150 ] , // 3 number array, could be RGB or Lab
                            space : ColorSpace.RGB , // Now it's clear, the 3 numbers are meant for RGB
                            model : ColorModel.PROCESS
                        };
                };
                
                else
                {
                    // isValid obviously returned false
                    // A color by the name stored in variable colorName is not there:
                    // So let's add it to the document:
                    
                    app.documents[0].colors.add
                    (
                        {
                            name : colorName ,
                            colorValue : [ 255, 255 , 150 ] , // 3 number array, could be RGB or Lab
                            space : ColorSpace.RGB , // Now it's clear, the 3 numbers are meant for RGB
                            model : ColorModel.PROCESS
                        }
                    );
                };
                
                // Then you could use your color that is called by its name as value for fillColor here:
                
                app.documents[0].masterSpreads[0].pages[0].textFrames.add
                (
                    {
                        fillColor : buttonColor ,
                        geometricBounds : ["85mm","50mm","100mm","100mm"] ,
                        contents : "Button" ,
                        textFramePreferences : { verticalJustification : VerticalJustification.CENTER_ALIGN } 
                    }
                );
                

                 

                Regards,
                Uwe

                2 people found this helpful
                • 5. Re: MasterSpreads with textFrames, centered text and buttons
                  Laubender Adobe Community Professional & MVP

                  Some other things:

                   

                  Working with buttons in InDesign is not the easiest task.

                  Not for a user in the UI.
                  Not for a scripter.

                  Even not for a seasoned scripter with experience.

                   

                  Anatomy of a button:

                  A button consists of at least one State object.

                  Just create a new button by using method buttons.add() on the document:

                   

                  var btn = app.documents[0].buttons.add();
                  
                  // Showing the added object in an acceptable size on screen:
                  app.select(btn);
                  app.documents[0].layoutWindows[0].zoomPercentage = 800;
                  app.documents[0].activeSpread = app.documents[0].spreads[0];
                  

                   

                  I did not specify a page, I did not specify a size at creation time. I kept all provided property/value pairs at an absolute minimum.
                  So InDesign has to fill in the missing ones at best to add a button to the document.

                   

                  The button is added to spread 1 of the doument pages.

                  Its position for the upper left corner is always 0/0 in the coordinate system the rulers suggest.

                  And its size usually is 3.528 x 3.528 mm. The thing needs a default, if not specified at creation.

                   

                  ButtonSelected-LayersPanel-Expanded.png

                  If you simply do:

                  app.selection[0]
                  

                  the JavaScript console of the ESTK will return: [object Button]

                  That's obvious, isn't it?

                   

                  Let's see what is under the hood:

                  app.selection[0].pageItems.length // Result: 1
                  

                   

                  What kind of page item do we see here?

                  app.selection[0].pageItems[0].getElements()[0]
                  

                  Result: [object Group]


                  If you are not sure what's in the hierachy of objects below the one selected you have to use getElements() to reveal its true nature.
                  getElements() will always return an array and if you are interested in the first item in this array you have to look by index 0.

                   

                  And we can identify the group by its unique id number as well:

                  app.selection[0].pageItems[0].id // Result: 224 (in my case, your's could be different)
                  

                  So it seems that there is nothing special so far…


                  We could even ask for its name:

                  app.selection[0].pageItems[0].name // Result: $$$/StateType/Normal
                  

                   

                  Now that is interesting. The string returned is the localization independent string for [Normal] you are seeing in the Layers panel.
                  And it belongs to a group object. Not to the state object we can also find in the properties for a button.

                   

                  How can we identify that group as group in the UI?
                  Simply select the button and hit Shift + esc on the keyboard or select contents from the available menu commands or select [Normal] from the  Layers panel. The lighter dashed lines of the selection suggesting that a group is selected. A group with a special name:

                  GroupInButtonSelected-LayersPanel-Expanded.png

                  Will it keep its special name, if we copy/paste that group or alt + drag to make a duplicate?
                  No. The pasted groups's name will be an empty string and the Layers panel is showing the default generic name of the object.

                  Or it reveals the name of the group again, the name that you assigned to the group before you converted it to a button.

                   

                  Btw.: If the generic name of an object is used in the Layers panel the name property of the object is returning an empty string.

                   

                  After alt + dragging you can see a rare object on the page.

                  A group that consists of one object only:

                   

                  AltDragged[Normal]Object.png

                   

                  The State object of a button

                  I mentioned the state object at the beginning of that post.
                  So back to the button, select that and ask for:

                  app.selection[0].states.length // Result: 1
                  

                   

                  And this:

                  app.selection[0].states[0].pageItems[0].getElements()[0].id // Result: 224
                  

                   

                  Aha! The first page item in the first state of the button is the same group that we detected before.

                  So both statements below point to the same group object:

                   

                  app.selection[0].states[0].pageItems[0];
                  app.selection[0].pageItems[0];
                  

                   

                  But don't get fooled by all that.


                  The value for

                  button.pageItems.length

                  is always 1. No exception.
                  Even if you have 2 or 3 button states
                  with one group item each and plenty of other elements nested inside.

                   

                  InDesign scripts can only look directly into one single state of a button at one time if you are asking with button.pageItems.

                  And that state is the active state of the button. You can gather the current active state with:

                   

                  var myActiveState  = button.states[ button.activeStateIndex ];
                  

                   

                  You'll get the whole picture regarding pageItems contained by the button with something like that:

                   

                  // Get all page items of selected button:
                  // Note: A state object is no pageItem in the sense of a rendered object on the page.
                  
                  // Select a button:
                  var button = app.selection[0];
                  
                  // Prepare variables for arrays:
                  var pageItemsInButton = [];
                  var ids = [];
                  
                  var states = button.states.everyItem().getElements();
                  
                  // Loop the states of a button:
                  for(var n=0;n<states.length;n++)
                  {
                      var group = states[n].groups[0];
                      var allInGroup = group.allPageItems;
                      
                      pageItemsInButton.push(group);
                      pageItemsInButton = pageItemsInButton.concat(allInGroup);
                      
                      ids.push(group.id);
                      
                      for(var i = 0; i<allInGroup.length; i++)
                      {
                          ids.push(allInGroup[i].id);
                      };
                  
                  };
                  
                  // Fast direct access from document level:
                  app.documents[0].pageItems.itemByID(ids[0]).getElements()[0].name; // $$$/StateType/Normal
                  // Or:
                  pageItemsInButton[0].getElements()[0].name; // $$$/StateType/Normal
                  

                   

                  Regards,
                  Uwe

                  1 person found this helpful
                  • 6. Re: MasterSpreads with textFrames, centered text and buttons
                    Cleo_helm Level 1

                    Dear Trevor, Uwe and Jongware,

                     

                    THANK YOU VERY VERY MUCH for all those explanations. I will go through them now step by step. I understood this "with" thing now and also the targeting (it made "click" in brain).

                     

                    For the color thing I defined names but it also worked with the variable:

                    /use the current document
                    var myDocument = app.documents.item(0);
                    //use the current page
                    var myPage = myDocument.pages.item(0);
                    //define color for female names
                    var myColorFemale = myDocument.colors.add();
                          myColorFemale.model = ColorModel.PROCESS;
                          myColorFemale.space = ColorSpace.CMYK;
                          myColorFemale.colorValue = [0, 49, 47, 0];
                    //makes a Rectangle female ([y1, x1, y2, x2], which give the coordinates of the top-left and bottom-right corners of the object) and colors it
                    var myRectangleF = myPage.rectangles.add({geometricBounds:[36, 36, 55, 250]});
                                                  myRectangleF.strokeWeight = 1;
                                                  myRectangleF.fillColor = myColorFemale;
                    

                     

                    And I got a colored rectangle. That's why I thought the name could be left out.

                     

                    On the other hand, I tried this and got as well a colored rectangle:

                    // open a new document
                    var myDocument = app.documents.add();
                    //define color for female names, here it has to be written with =
                    var myColorFemale = myDocument.colors.add();
                    var myColorFemale = myDocument.colors.add();
                           myColorFemale.name = "feminin"; // this is a string
                           myColorFemale.model = ColorModel.PROCESS;
                           myColorFemale.space = ColorSpace.CMYK;
                           myColorFemale.colorValue = [0, 49, 47, 0]; // this is an array
                    //defines the button to get the color code (and it is located on the Master Page
                    var myFarbcodeButton = myDocument.masterSpreads.item(0).buttons.add();
                          myFarbcodeButton.geometricBounds = [535, 740, 558, 805];
                          myFarbcodeButton.fillColor = "feminin";
                    

                     

                    And I got a colored button.

                    But with "xy" I indicate that it is a string and that is why I had to give it first a name, when I define the color.

                     

                    So in the end I would say all of your answers are correct to my question collection. I will now first clean up my "with"-code to make it better.

                     

                    Thank you all again!

                    Best
                    Cleo

                    • 7. Re: MasterSpreads with textFrames, centered text and buttons
                      Trevorׅ Adobe Community Professional

                      As it clicked I think you can mark the question as a answered.

                      I would recommend Uwe's answers as they contain more screenshots than the others