15 Replies Latest reply on Jun 13, 2018 1:57 PM by Guild - Office Pro

    Get textFrame's Tag/Label through script

    aarong18402343 Level 1

      Hello everyone,

       

      I'm really sorry if this has already been asked, I have found discussions that touch on it, but haven't found any suggestions or solutions.

       

      I am a web developer, with no inDesign experience, our graphic designer uses it to build our flyers.

      We have been talking about translating the flyers to another language, and I have been working on making them interactive (with an image map)on our website.

      If I'm going to suggest we revise the flyer structure for automating the translation, I'd like to also handle the image mapping (pulling image + area coordinates)

       

      The thought was to write a js script that will pull the text frames from the file so I can work with them using other scripts to translate, get coordinates, etc.

       

      I have more or less succeeded in doing this using the textFrame's name.
      I'm currently renaming each item in the Layers window with a key-value naming structure (000-000-000|Title, 000-000-000|Description) and then splitting the name by '|' to get the product code [0] and the textFrame 'tag' [1]. Then I can run the 'tag' portion of the content through a translation script, or coordinate script as needed.

       

      But there are dozens of pages with numerous products on each page, and multiple fields (title, description, price, quantity, product number) for each product.
      I don't want to propose our designer meticulously rename thousands of elements.
      I have found the Tags window to be much quicker to associate a type to each textFrame. But I haven't been able to work with the tags through script...

       

      From what I've read, CS5 and later doesn't allow you to get a tag/label through scripting.

      Is this true? Is anyone aware of any way around it?

       

       

      -TLDR-

      I have thousands of text (and image) elements in a InDesign file, and I would like to pull their contents out with a script and process them depending on the text field's type (product description, image, price, etc.).

      Tags/Labels seem perfect, as I could just loop through all textFrame elements, and say if has tag 'description' then pass into translate(), if image getCoords().
      And they are rather quick to associate with elements.

       

      But from what I've read CS5 + doesn't allow you to work with tags through scripts in this way.

       

       

      Any suggestions to accomplish this without working with the textFrame's names? (I'd like to avoid adding that much work to our graphic designer's monthly process)

       

      If it is relevant I'm using inDesign CC 2017  V- 12.1.0.56

       

      Thanks in advance for any input or direction you can provide. InDesign is not something I am familiar with :/

        • 1. Re: Get textFrame's Tag/Label through script
          Laubender Adobe Community Professional & MVP

          Hi,

          if you mean the following:

           

          doc.pageItems.item("String")

          doc.pageItems.itemByName("String")

           

          this will work with pageItem.name only with InDesign CS5 and above.

          And you get always one item in return even if more than one pageItems share the same name.

           

          Before, in CS4, you could use the label property:

          pageItem.label = "String"

           

          and retrieve all pageItems with the same label value with:

          doc.item("String");

           

          All that said, there is one other problem you have to keep in mind:
          pageItems that are nested in groups, are anchored or nested elsewhere cannot be reached with:

           

          doc.pageItems.item("String")

          doc.pageItems.itemByName("String")

           

          If you are looking for all items, nested or not, you could use:

           

          var nearlyAllItems = doc.allPageItems; // Returns array of pageItems

           

          And you would loop this array to look after a distinct page item with a distinct name or label value.

           

          Why did I named this variable nearlyAllItems and not reallyAllItems ?

          Because the allPageItems array omits objects that are in states of multistate objects that are not the active state.

          They cannot be reached with allPageItems!

           

          But for this problem there is a hack! A trick I found several years ago. With every pageItems comes an id. And we could loop page items by id. That will also find page items in states that are not the active ones. Also things we never thought about as page items. What we need to do this effectively is knowing e.g. about the highest id number and possibly the lowest id number that makes sense. You could test this for yourself: Add a new document, add a simple rectangle to the page and ask about the value for id of the rectangle.

           

          var doc = app.documents.add();
          var rect = doc.rectangles.add();
          alert( rect.id.toString() ); // Returns 216 in my case, your case could be different.
          

           

          A good bet is that we can leave out id numbers 1 to 100, maybe 1 to 150, if we loop all page items by their ids.

          And the highest number? Just add a rectangle to the document, read out its id number and remove it. Use that number to limit your loop.

           

          Just an experiment you should do with the ESTK ( ExtendScript Toolkit ) connected to InDesign CC 2018.1 with an open document:

           

          var doc = app.documents[0];
          var tempRect = doc.rectangles.add();
          var idMax = tempRect.id;
          var e;
          
          tempRect.remove();
          
          for( var n=150; n < idMax-1 ; n++ )
          {
               try
               {
                    // getElements()[0] is the key feature here that will resolve a pageItem to the object that it really is:
                    var currentItem = doc.pageItems.itemByID(n).getElements()[0];
                    $.writeln( n +"\t"+ currentItem.constructor.name +"\t"+currentItem.name +"\t"+ currentItem.label );
               }catch( e )
               {
                    $.writeln(n +"\t"+ e.message );
               };
          }
          

           

          Below the result of a new empty document with a different document preset than the one I did before where the highest id number was 216. This time it was 221.The loop ran from 150 to 221. If you'll test this, you could run it with 1 to the max number to see if there are interesting things you might find in the lower numbers.

           

          Note: A page item could be addressed e.g. by its id as well:

          doc.pageItems.itemByID(Number);

           

          Important:

          With getElements()[0] you can resolve that page item and know exactly what kind of pageItem this is, a rectangle, a textFrame or something else.

          And with doc.pageItems.itemByID(Number) you can always drill through nested structures like groups etc.pp. to access the item.

           

          150    ObjectStyle    [Einfacher Textrahmen]    
          151    ObjectStyle    [Einfaches Raster]    
          152    Object is invalid
          153    CrossReferenceFormat    Vollständiger Absatz und Seitenzahl    
          154    CrossReferenceFormat    Vollständiger Absatz    
          155    CrossReferenceFormat    Absatztext und Seitenzahl    
          156    CrossReferenceFormat    Absatztext    
          157    CrossReferenceFormat    Absatznr. und Seitenzahl    
          158    CrossReferenceFormat    Absatznr.    
          159    CrossReferenceFormat    Name des Textankers und Seitenzahl    
          160    CrossReferenceFormat    Name des Textankers    
          161    CrossReferenceFormat    Seitenzahl    
          162    TOCStyle    [Standard]    
          163    CompositeFont    [No composite font]    
          164    CompositeFontEntry    Kanji    
          165    Object is invalid
          166    CompositeFontEntry    Kana    
          167    CompositeFontEntry    Interpunktion    
          168    CompositeFontEntry    Symbole    
          169    CompositeFontEntry    Latein    
          170    CompositeFontEntry    Zahlen    
          171    XmlStory        
          172    Object is invalid
          173    Object is invalid
          174    Object is invalid
          175    Object is invalid
          176    Object is invalid
          177    Object is invalid
          178    Object is invalid
          179    Object is invalid
          180    Object is invalid
          181    Object is invalid
          182    Object is invalid
          183    Object is invalid
          184    Object is invalid
          185    Object is invalid
          186    Object is invalid
          187    Object is invalid
          188    XMLTag    Root    
          189    Object is invalid
          190    Object is invalid
          191    Object is invalid
          192    Object is invalid
          193    Object is invalid
          194    Object is invalid
          195    Object is invalid
          196    Object is invalid
          197    Layer    Internal_pages_layer_name    
          198    Layer    Ebene 1    
          199    TrapPreset    k[No Trap Preset]    
          200    TrapPreset    kDefaultTrapStyleName    
          201    Spread        
          202    Object is invalid
          203    Object is invalid
          204    Object is invalid
          205    Object is invalid
          206    Page    1    
          207    Section        
          208    MasterSpread    A-Musterseite    
          209    Object is invalid
          210    Object is invalid
          211    Object is invalid
          212    Object is invalid
          213    Page    A    
          214    Page    A    
          215    Assignment    Nicht zugewiesener InCopy-Inhalt    
          216    Object is invalid
          217    Object is invalid
          218    Object is invalid
          219    Object is invalid
          220    Object is invalid
          221    Object is invalid
          

           

          As you can see from this list I'm running a German InDesign version. And, it might be a surprise to you, you could also address a page with doc.pageItems.itemByID(Number). In my case, yours will be different, by id number 206. That's the page named 1. It has an empty string as label by default, but a name, and that is "1".

           

          Please note, that in DOM documentation of all the objects listed above you will find none documented under pageItems. Only by ID you can see them as pageItems. "Normal" pageItems like rectangles, textFrames etc. pp. will be listed as well, if there are any. And this time really ALL pageItems of the document are in the list. Also the ones that are deeply nested!

           

          Happy hunting after named or labeled items!

           

          Regards,
          Uwe

          • 2. Re: Get textFrame's Tag/Label through script
            S Hopkins Adobe Community Professional

            Hi,

            You might want to investigate insert/extract label. Labels are very flexible in that just about any text can be inserted into and extracted from a label. insertLabel uses a name/value pair. extractLabel requires the name to get the text stored. You can even insert a complete script (written as plain text) and run the inserted script using doScript.

            For example:

            var myScript = "alert ('Hello')";

            var selRef = app.selection[0];

            selRef.insertLabel("script", myScript);

            //later as part of another script, extract and run the inserted script

            var aScript = selRef.extractLabel("script");

            app.doScript(aScript);

             

            Just a thought.

            • 3. Re: Get textFrame's Tag/Label through script
              Laubender Adobe Community Professional & MVP

              Hi,

              the main problem of our OP will stay. How to efficiently label or name the items.

              If there is a scheme for this one could write a script that does exactly this without involving the user very much.

               

              On the other hand there are translation software suits that are using memory systems for phrases and other standard expressions that are working with IDML. Maybe that's the best way to go regarding translations.

               

              Best,
              Uwe

              • 4. Re: Get textFrame's Tag/Label through script
                aarong18402343 Level 1

                Thank you so much for the detailed response.

                 

                Just to clarify, in these later versions of inDesign, there is no way to group items and then get an array/list of those group items via script?

                 

                As I mentioned, I did have it 'working' by using the items Name. But I really don't want to ask our graphics designer to carefully name 6 elements for each of the hundreds of products when she creates the flyers each month.
                If that's really the best solution given the version we are using, I may have to suggest that. But if there was any way to associate a tag/label/class identifier to a group of elements so I could do something like...

                 

                 

                //GET DESCRIPTION ITEMS

                     descList = []; 

                     descItems = app.activeDocument.textFrames.item("description"); 

                     for ( i = 0; i < descItems.length; i ++ ) {

                          descList.push(  translateText( descItems[i].contents )  ); 

                     }

                 

                //GET PRICE ITEMS

                     priceList = []; 

                     priceItems = app.activeDocument.textFrames.item("price"); 

                     for ( i = 0; i < priceItems .length; i ++ ) {

                          priceList .push(  formatPrice( priceItems[i].contents )  ); 

                     }

                 

                //GET IMAGE ITEMS

                     imageList = []; 

                     imageItems = app.activeDocument.textFrames.item("image"); 

                     for ( i = 0; i < imageItems  .length; i ++ ) {

                          imageList .push(  getCoordinates( imageItems[i] )  ); 

                     }

                 

                function translateText( textString ) { ... }

                function formatPrice( priceString ) { ... }

                function getCoordinates( imageItem ) { ... }

                 

                 

                Again, I have little to no experience with inDesign directly beyond the past few days, but it seems like it was a very practical feature to be able to group items and retrieve a list of them. I don't see why this would have been removed in the more recent versions.

                 

                Is it a reasonable approach to consider getting a version of CS4, and trying to open the file in that just to run it through a script like this?

                 

                 

                Thank you again,

                I truly appreciate you taking the time to provide such an informative reply.

                • 5. Re: Get textFrame's Tag/Label through script
                  aarong18402343 Level 1

                  Thanks for the response.

                   

                  If I'm following the suggestion correctly; it would still require me to address each item by name through scripting.

                   

                  I'm trying to avoid using the name, as there are just so many to work with. And I think a much higher mistake of typos and human error.

                  I did come across some info on adding labels to items through scripting in CS5+, but I don't think really it addresses my issue.

                   

                  I appreciate the suggestion though.

                  • 6. Re: Get textFrame's Tag/Label through script
                    aarong18402343 Level 1

                    I have done a little research into translation software. But other people in the company have worked out the translations already, so I thought simply pulling the data, swapping it and plugging it back in would be the easiest approach on my end.

                     

                    If there are some suits/plugins/scripts that would translate directly from the file that might still be worth considering. Our French clients have been a little 'particular' about the translation in the past (Quebec French / France French) so I know some of our board doesn't want to rely fully on automated translations. But still something I may look into more.

                     

                    I was really hoping I would also be able to address the image mapping that I mentioned in my first post.

                    I originally put together / came across a script to pull image coordinates. But it did all graphics.

                    I was hoping if our designer was able to tag items, I could use those identifiers to determine what I want to get the coordinates of, and use for the image map. That way customers could click on the images or text to view a product.

                    • 7. Re: Get textFrame's Tag/Label through script
                      Laubender Adobe Community Professional & MVP

                      aarong18402343  wrote

                       

                      Thank you so much for the detailed response.

                       

                      Just to clarify, in these later versions of inDesign, there is no way to group items and then get an array/list of those group items via script?

                       

                      If you identified a group you'll get an array of all page items of a group with:

                       

                      var allItemsOfGroup = group.allPageItems;
                      

                       

                      If you want to identify text frames you best resolve the items as you loop.

                       

                      for( var n=0;n<allItemsOfGroup.length;n++ )
                      {
                          var currentItem = allItemsOfGroup.getElements()[0];
                          if( currentItem.constructor.name == "TextFrame" && currentItem.label == "MyLabel" )
                          {
                           /* do something*/
                          };
                      
                      };
                      

                       

                      Regards,
                      Uwe

                      • 8. Re: Get textFrame's Tag/Label through script
                        aarong18402343 Level 1

                         

                         

                        This sounds promising!

                         

                        But how are items added to a group? Is this done with the items name through scripting as well?

                         

                         

                        I also wanted to ask about names vs tags regarding tables. Our flyers currently have lists of products just in a text box.

                         

                                  000-000-000     title     quantity     price

                                  000-000-000     title     quantity     price

                                  000-000-000     title     quantity     price

                         

                        I was going to suggest we use tables,

                        1. So I can distinguish between rows for the image map, to link to different products

                        2. So I can distinguish between the columns, to know what to translate (text) and what to format (currency)

                        3. So it's hopefully slightly easier for our designer to adjust styling and column widths, etc. if needed

                         

                        But to further tease me with how convenient tags/labels would be for me in this specific instance, it looked like the name is attached to the table, so rows and columns can't have their own names, but they can be given different tags.

                        Is that correct?

                         

                        Sorry for all the newbie questions. You have been incredibly helpful.

                        • 9. Re: Get textFrame's Tag/Label through script
                          Laubender Adobe Community Professional & MVP

                          Cells and tables can be labeled. Property label, value String.

                          Property name isn't worth a try, because it's not unique and tied to a cell's position in the grid of rows and columns.

                           

                          Boy, you'll have a steep lerning curve especially with InDesign tables.

                           

                          Regards,
                          Uwe

                          • 10. Re: Get textFrame's Tag/Label through script
                            Guild - Office Pro Level 1

                            Laubender  wrote

                             

                            Boy, you'll have a steep lerning curve especially with InDesign tables.

                             

                             

                            I am starting to realize this :/

                             

                             

                            I'm installing our previous CS4 version right now to see if it's easy enough to just open the file to run the script.
                            But if not it sounds like my best bet will be to have a conversation about using a more deliberate naming structure for the elements we are looking to map or translate.

                             

                            Thanks again for all the help.

                            You have probably saved me hours of additional research and trial.

                            • 11. Re: Get textFrame's Tag/Label through script
                              Laubender Adobe Community Professional & MVP

                              I hope you already realized this:

                               

                              There is DOM documentation available by Jongware for InDesign CS to CS6.

                              Indesign JavaScript Help

                               

                              Especiall look into the chm files for excellent searchability.

                               

                              For the latest available InDesign DOM documentation which is for CC 2018 ( not CC 2018.1) look here:

                              https://www.indesignjs.de/extendscriptAPI/indesign-latest/#about.html

                               

                              Best,
                              Uwe

                              • 12. Re: Get textFrame's Tag/Label through script
                                Laubender Adobe Community Professional & MVP

                                https://forums.adobe.com/people/Guild+-+Office+Pro  wrote

                                I'm installing our previous CS4 version right now to see if it's easy enough to just open the file to run the script. …

                                If the script in not meant to run with CS4 that's no good idea. The designers will hate you if they are forced to work with CS4 when they are currently on CC 2017.1. Better concentrate on version CC 2017.1.

                                 

                                Regards,
                                Uwe

                                • 13. Re: Get textFrame's Tag/Label through script
                                  Guild - Office Pro Level 1

                                  I managed to get and update the element's contents pulled via XML tags using xpaths in our current InDesign version

                                   

                                  //GET LIST OF 'Title' ELEMENTS

                                       var doc = app.activeDocument;

                                       var xmlTag = doc.xmlElements[0];

                                       var titleTag = xmlTag.evaluateXPathExpression("//Title")

                                   

                                       for(var i=0;i<titleTag.length;i++)

                                       {

                                            //GET CONTENTS

                                                 alert('content ' + i + ': ' + titleTag[i].xmlContent.contents);

                                            //UPDATE CONTENTS

                                                 titleTag[i].contents = "Translation of : " + titleTag[i].contents;

                                       }

                                   

                                   

                                  But I want to pull the translated text (and alternate pricing, etc.) from a spreadsheet. Ideally using the product number as an identifier.

                                   

                                  Ex:

                                  Code               EnglishTitle          FrenchTitle       PriceLvl1         PriceLvl2

                                  000-000           itemA                   itemÀ               1.99               2.99

                                   

                                  I thought the most logical approach would be to use the product code as the element's name.

                                  However, every attempt I've made to get the name using the XML object has returned blank values or given errors.

                                   

                                  //GET LIST OF 'Title' ELEMENTS

                                       var doc = app.activeDocument;

                                       var xmlTag = doc.xmlElements[0];

                                       var titleTag = xmlTag.evaluateXPathExpression("//Title")

                                    

                                       for(var i=0;i<titleTag.length;i++)

                                       {

                                            //GET CONTENTS

                                               alert('content ' + i + ' : ' + titleTag[i].xmlContent.contents);

                                            //GET NAME

                                               alert('name ' + i + ' : ' + titleTag[i].xmlContent.name);  //BLANK

                                               alert('name ' + i + ' : ' + titleTag[i].name);  //ERROR (Object does not support the property or method 'name')

                                       }

                                   

                                   

                                  My current 'quick fix' workaround for this is to check all the textframe elements to find contents that match the XML object, then I can get that textFrame's name to work with.

                                   

                                  //GET LIST OF 'Title' ELEMENTS

                                       var doc = app.activeDocument;

                                       var xmlTag = doc.xmlElements[0];

                                       var titleTag = xmlTag.evaluateXPathExpression("//Title")

                                   

                                       for(var i=0;i<titleTag.length;i++)

                                       {

                                             //GET CONTENTS

                                                  alert('content ' + i + ': ' + titleTag[i].xmlContent.contents);

                                            //FIND TEXTFRAME MATCH

                                                 for (var j=0; j<app.activeDocument.textFrames.length; j++)

                                                 {           

                                                      if(app.activeDocument.textFrames[j].contents == titleTag[i].contents)

                                                      {

                                                           alert("Found Match - " + app.activeDocument.textFrames[j].name);

                                                           app.activeDocument.textFrames[j].contents = "Translation of : " + app.activeDocument.textFrames[j].name ;

                                                       }

                                                  }

                                          }

                                   

                                  This allows me to get the textframe name, but it is clearly not an efficient approach, as it has to loop through all textframes for each XML tag I modify. And if anything shares the same contents (which is entirely possible with prices) it could confuse the two elements.

                                   

                                   

                                  Is there a way to get the textframe's name attribute (if that's the correct terminology for inDesign) from the associated XML element?

                                  • 14. Re: Get textFrame's Tag/Label through script
                                    Laubender Adobe Community Professional & MVP

                                    Hi,

                                    don't know if this is the case with your sample, but XML explicitly disallows numbers as the first character of an element name.

                                     

                                    Regards,
                                    Uwe

                                    • 15. Re: Get textFrame's Tag/Label through script
                                      Guild - Office Pro Level 1

                                      Oh, yes I have been putting all my element names in tag brackets/guillemets to avoid this issue.

                                      Ex: <000-223>