4 Replies Latest reply on Nov 18, 2011 6:46 AM by tarek1235

    Performant way for exporting glyphs (codepoint plus bounds) on a per-Character-basis?

    tarek1235

      For (InDesign 5) export reasons I need to associate Character instances (unicode codepoint) and their corresponding glyph bounds (geometric position & dimension on a "per-character"-basis). ATM, I iterate over all TextFrame instances' characters, creating their outlines via Character.createOutlines(), performing boundary calculations (either by by using Polygon/Path/PathPoints in "more detail required"-variant, or Polygon.geometricBounds in "less detail required"-variant). The obvious downside of the approach is, that I hereby modify the document's content by creating additional objects, by using "createOutlines()", and the performance of the approach is slow: about 4 minutes for exporting ~1600 Characters distributed over two pages per document, avg ~6-7 chars per second (hexacore@2.8Ghz, 8GB RAM). I already "optimized" the approach by using an appropriate UndoMode and disabling the rendering while exporting, and now, after benchmarking different approaches, it seems like the main bottleneck is the required "createOutlines()"-call, for gaining access to geometric bounds on a "per-character"-basis.

       

      Is there any other way, excluding "createOutlines()", for obtaining a Character's bounds?

        • 1. Re: Performant way for exporting glyphs (codepoint plus bounds) on a per-Character-basis?
          John Hawkinson Level 5

          Is there any other way, excluding "createOutlines()", for obtaining a Character's bounds?

          You can call it once for each character/font/size and save the results.

          1 person found this helpful
          • 2. Re: Performant way for exporting glyphs (codepoint plus bounds) on a per-Character-basis?
            tarek1235 Level 1

            That's indeed a possibility, but in order to be practicable, I need to ensure that for example affine transformations are also considered in the bounds calculations (then being done "manually"), and, to be honest, I currently can't predict how time consuming that might get, as well during implementation as well as during runtime. But I believe your idea should help me out getting more than ~6-7 chars/sec... so thank you for your suggestion.

            • 3. Re: Performant way for exporting glyphs (codepoint plus bounds) on a per-Character-basis?
              Laubender Adobe Community Professional & MVP

              Tarek,
              yes, " app.undo()" takes its time…

               

              Instead of undoing every "createOutlines()" action I would prefer "createOutline(false)" which duplicates the character to an path object. After doing your calculation on that new path object you can remove it by "myPathObject.remove()".

               

              Also to sample the results in an array it is faster not to use the "myArray.push(results)" method. Instead use myArray[i]=results.

               

              For that finding a big applause to Marc Autret at:
              http://www.indiscripts.com/post/2011/06/comparing-the-performance-of-extendscript-snippets

               

              See the following example code (please, before starting script execution, set the rulers of your test document to "millimeters" and select a bunch of characters). The results, simple width and height calculations on the path objects, will be written to a new table left from page one on the paste board of your document:

               

              start_time = Date.now();
              
              var _d = app.activeDocument; 
              var _selection = app.selection[0];
              var _charactersArray = new Array(); 
              var _charDimensionsArray = new Array();
              
              var _charCount = _selection.characters.length;
              
              
              for(var n=0;n<_charCount;n++){ 
                  var _character = _selection.characters[n];
                  var _noError = true;
                  //In case of a blank there will be no path created, so we need a fall back scenario:
                  try{
                  var _characterToPath = _character.createOutlines(false);
                      }catch(e){_noError = false};
              
              //~     _charactersArray.push(_character.contents);
              
                  //The faster method:
                  _charactersArray[n]=_character.contents;
              
                  if(_noError == true){
                      var _bounds = _characterToPath[0].visibleBounds;
                      var _width = _bounds[3]-_bounds[1];
                      var _height = _bounds[2]-_bounds[0];
              
              //~         _charDimensionsArray.push("w x h = "+_width+" x "+_height);
                      //The faster method feeding the array:
                      _charDimensionsArray[n] = "w x h = "+_width.toString()+" x "+_height.toString();
              
                      //The faster method without using "app.undo()":
                      _characterToPath[0].remove();
                      }
                  else{
                      //In case of a blank just store:
                      var _width = _character.insertionPoints[1].horizontalOffset - _character.insertionPoints[0].horizontalOffset;
              
              //~         _charDimensionsArray.push("w = "+_width);
                      //A faster method to feed the array:
                      _charDimensionsArray[n] = "w = "+_width.toString();
                      };
              
                  }; 
              
              var _resultTextFrame = _d.textFrames.add({geometricBounds:[0,-210,297,0]}); 
              var _resultTable = _resultTextFrame.insertionPoints[0].tables.add({ 
                  columnCount:2, 
                  bodyRowCount:_charactersArray.length 
                  }); 
              
              
              _resultTable.columns[0].width = 20;
              _resultTable.columns[0].contents = _charactersArray; 
              _resultTable.columns[1].width = 190;
              _resultTable.columns[1].contents = _charDimensionsArray;
              
              end_time = Date.now();
              elapsed_time = end_time - start_time;
              alert(((elapsed_time/1000)/60)+" in minutes for "+_charCount+" characters.");
              

               

              For around 1.600 characters  script execution took around 1.7 minutes on my MacBook Pro with a 2.4 GHz Intel Core 2 Duo and 4 GB 1067 MHz DDR3-ram.

              Time will differ if you use it on varied fonts and character sizes.

               

              Hope that helps,
              Uwe

               

              Message was edited by: Laubender (just deleted some comments in the script)

              1 person found this helpful
              • 4. Re: Performant way for exporting glyphs (codepoint plus bounds) on a per-Character-basis?
                tarek1235 Level 1

                Thanks Uwe, exspecially the hiresTimer-stuff was very useful for additional benchmarking precision...

                 

                Message was edited by: tarek1235 fixed typos