Highlighted

Script. Insert text number in the middle of visible bounds of the each object

Explorer ,
Feb 14, 2016

Copy link to clipboard

Copied

Hello! Adobe Illustrator CS 6

I have script, wich groups filled paths by colors. Link to script and traced image. Traced_image.rar - Google Drive

I have traced image, it has 25 colors. The script creates 25 groups, paths of each color in each group.

I need to modify script, i want to insert text number in visible middle of the each filled path. So one group is one number. From 1 to 25.

Can anyone help me?

Hello all, here comes the final version.

This time "offset path effect" been used. I thought this is a possible way at beginning, but never give it a try, until today.

It deal with both Path and Compound Path. Test the sample document, it only took 31 seconds.

cap.PNG

TOPICS
Scripting

Views

4.7K

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more

Script. Insert text number in the middle of visible bounds of the each object

Explorer ,
Feb 14, 2016

Copy link to clipboard

Copied

Hello! Adobe Illustrator CS 6

I have script, wich groups filled paths by colors. Link to script and traced image. Traced_image.rar - Google Drive

I have traced image, it has 25 colors. The script creates 25 groups, paths of each color in each group.

I need to modify script, i want to insert text number in visible middle of the each filled path. So one group is one number. From 1 to 25.

Can anyone help me?

Hello all, here comes the final version.

This time "offset path effect" been used. I thought this is a possible way at beginning, but never give it a try, until today.

It deal with both Path and Compound Path. Test the sample document, it only took 31 seconds.

cap.PNG

TOPICS
Scripting

Views

4.7K

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Feb 14, 2016 0
Guide ,
Feb 14, 2016

Copy link to clipboard

Copied

take a look at this thread.

Re: How do I copy the path name from the layers panel to the center of the path/object in my AI file...

it's not a simple task to get the gravitational center of an irregular object.

I'm sure its possible with some wiz bang algorithm.

but I am not going to try to tackle that with my math skills

if you need a hand modifying  it for your needs let me know.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 14, 2016 0
Explorer ,
Feb 14, 2016

Copy link to clipboard

Copied

Thank you, but my skill is not enouth to modify and compute your script with this one. Can you take a look on algoritm? The problem is that script generating sub and sub layers, so your script can work with 1 level layers only.

many-levels.jpg

Look please on script:

#target illustrator

if (app.documents.length > 0)

{

if (app.activeDocument.pageItems.length > 0)

{

var release_to_layers_window; // make window global

var doc = app.activeDocument;

var doc_artboard = doc.artboards[0].artboardRect;

var unlocked_layers_count = 0; // number of unlocked layers in layer palette for progress bar

var msis_message_1 = "\nThis script will place each object within this current document into a new layer based on it's fill color. "+

                     "As the script currently does not detect locked layers and locked objects you need to unlock your layers and objects before you can run this script. " +

                     "Note that running this script will modify your document. You might want to save your document first.";

var doc = app.activeDocument;

var Counter = app.activeDocument.pageItems.length;

var LayerName = "";

var ReleaseMethod = 1;

// start dialog window

   release_to_layers_dialog();

}

// what if no document of no objects?

if (app.documents.length < 1)

{alert("No open documents found. Please open a document before you run this script");}

if (app.documents.length > 0 && app.activeDocument.pathItems.length < 1)

{alert("No objects found in this documents. Please add some objects before you run this script");}

}

// ********************************************************************************************************

// **                                                                                                    **

// ** find the most common value in an array                                                             **

// **                                                                                                    **

// ** http://www.experts-exchange.com/Programming/Languages/Scripting/JavaScript/Q_20871554.html         **

// ** var myArray = ["apple","banana","orange","banana","tomato","grape","apple"];                       **

// ** alert( GetMostCommonColor(myArray) );                                                                              **

// **                                                                                                    **

// ********************************************************************************************************

function GetMostCommonColor(theArray)

  {

  var tempArray = new Array();

  tempArray.length = 0;

  tempArray = theArray.slice(0,theArray.length);

  tempArray.sort();

  tempArray[tempArray.length] = "";

  mVal = aVal = "";

  mCnt = aCnt = 0;

  for(r=0;r<tempArray.length;r++)

    {

    if(aVal!=tempArray)

      {

      if(aCnt>mCnt)

    {

        mCnt = aCnt;

        mVal = aVal

       }

      aVal = tempArray;

      aCnt = 1;

    } else

  {

      aCnt++;

    }

  }

  // return [mVal, mCnt]; // returns value and count

  return mVal; // only returns value

}

// ********************************************************************************************************

// **                                                                                                    **

// ** create a layer name for the current object                                                         **

// **                                                                                                    **

// ********************************************************************************************************

function ConstructLayerNameBasedOnFillColor(myCurrentObject)

{

  var myLayerName = "Unknown";

  if (myCurrentObject.typename == "TextFrame") { myLayerName = "Text Objects"; }

  if (myCurrentObject.typename == "SymbolItem") { myLayerName = "Symbols"; }

  if (myCurrentObject.typename == "RasterItem") { myLayerName = "Raster Objects"; }

  if (myCurrentObject.typename == "PathItem")

     {

      if ( myCurrentObject.filled == true )

      {

       myLayerName = 'Object with unknown Fill Color'; // default name for if no color can be detected

       if (myCurrentObject.fillColor == "[CMYKColor]")  // CMYK color Item

             {

              myLayerName = "CMYK: " + Math.round(myCurrentObject.fillColor.cyan) + "," + Math.round(myCurrentObject.fillColor.magenta)  + "," + Math.round(myCurrentObject.fillColor.yellow) + "," + Math.round(myCurrentObject.fillColor.black);

             }

         if (myCurrentObject.fillColor == "[RGBColor]") // RGB color Item

             {

              myLayerName = "RGB: " + Math.round(myCurrentObject.fillColor.red) + "," + Math.round(myCurrentObject.fillColor.green)  + "," + Math.round(myCurrentObject.fillColor.blue);

             }

         if (myCurrentObject.fillColor == "[GrayColor]") // Gray color Item

             {

              myLayerName = "Gray: " + Math.round(myCurrentObject.fillColor.gray);

             }

         if (myCurrentObject.fillColor == "[LabColor]") // LabColor color Item

             {

              myLayerName = "Lab: " + Math.round(myCurrentObject.fillColor.a) + "," + Math.round(myCurrentObject.fillColor.b)  + "," + Math.round(myCurrentObject.fillColor.i);

             }

         if (myCurrentObject.fillColor == "[GradientColor]") // Gradient color Item

             {

              myLayerName = "Gradient Fill";

             }

         if (myCurrentObject.fillColor == "[NoColor]") // No Color Item

             {

              myLayerName = "No Color";

             }

         if (myCurrentObject.fillColor == "[PatternColor]") // Pattern Color Item

             {

              myLayerName = "Pattern Fill";

             }

         if (myCurrentObject.fillColor == "[SpotColor]") // Spot Color Item

             {

   // cmyk spot

           if (myCurrentObject.fillColor.spot.spotKind == SpotColorKind.SPOTCMYK)  // CMYK color Item

                 {

   myLayerName = "Spot CMYK: " + Math.round(myCurrentObject.fillColor.spot.color.cyan) + "," + Math.round(myCurrentObject.fillColor.spot.color.magenta)  + "," + Math.round(myCurrentObject.fillColor.spot.color.yellow) + "," + Math.round(myCurrentObject.fillColor.spot.color.black);

                 }

   // rgb spot

           if (myCurrentObject.fillColor.spot.spotKind == SpotColorKind.SPOTRGB)  // RGB color Item

                 {

                  myLayerName = "Spot RGB: " + Math.round(myCurrentObject.fillColor.spot.color.red) + "," + Math.round(myCurrentObject.fillColor.spot.color.green)  + "," + Math.round(myCurrentObject.fillColor.spot.color.blue);

                 }

   // lab spot

           if (myCurrentObject.fillColor.spot.spotKind == SpotColorKind.SPOTLAB)  // RGB color Item

                 {

                  myLayerName = "Spot Lab: " + Math.round(myCurrentObject.fillColor.spot.color.a) + "," + Math.round(myCurrentObject.fillColor.spot.color.b)  + "," + Math.round(myCurrentObject.fillColor.spot.color.i);

                 }

             } 

         }

     else

      {

       myLayerName = "Transparant Fill";

      }

     }

  if (myLayerName == undefined) {myLayerName = "Temporary Layer";}

  if (myLayerName == "") {myLayerName = "Temporary Layer";}

  if (myLayerName.length==0) {myLayerName = "Temporary Layer";}

     try

     {

             doc.layers.getByName(myLayerName);

            }

    catch (e)

         {

             doc.layers.add().name = myLayerName;

            }

  return myLayerName;

}

// ********************************************************************************************************

// **                                                                                                    **

// ** ungroup objects on current layer                                                                   **

// **                                                                                                    **

// ********************************************************************************************************

function Ungroup(WhereToUngroup, aGroup) // where to ungroup? layer, document, other group?

     {

    try

         {

       for (s=aGroup.pageItems.length-1; s>=0; s--)

       {

              WhereToUngroup = CreateLayerNameFill(myCurrentObject); // ????????? should be something like WhereToUngroup = CreateLayerNameFill(=aGroup.pageItems); // ???????????????????????????

   aGroup.pageItems.move(WhereToUngroup, ElementPlacement.PLACEATBEGINNING);

   }

  }

  catch(err)

  {

         }

     }

// ********************************************************************************************************

// **                                                                                                    **

// ** remove layerswith no objects                                                                   **

// **                                                                                                    **

// ********************************************************************************************************

function  RemoveEmptyLayers()

{

   var ThisDoc = app.activeDocument;

   var ActiveLayer = '';

    for (t = ThisDoc.layers.length-1; t>=0; t--)

      {

     ActiveLayer = ThisDoc.layers;

     if (ActiveLayer.pageItems.length == 0)

  {

   try

     {

      ActiveLayer.remove();

     }

    catch(err)

     {

     }

  }

      }

}

// ********************************************************************************************************

// **                                                                                                    **

// ** building and showing main dialog                                                                   **

// **                                                                                                    **

// ********************************************************************************************************

function release_to_layers_dialog() {

   // Export dialog

    release_to_layers_window = new Window('dialog', 'Release objects to layers based on object fill color');

    // PANEL with usage instructions

    release_to_layers_window.MessagePanel = release_to_layers_window.add('panel', undefined, 'Release Objects to Layers');

    // GROUP

    var Message_Group = release_to_layers_window.MessagePanel.add('group', undefined, '')

    Message_Group.orientation = 'column';

    Message_Group.alignment = [ScriptUI.Alignment.LEFT, ScriptUI.Alignment.TOP]

  // Labels

    var Message_Group_label_1 = Message_Group.add('statictext', undefined,  msis_message_1, {multiline:true});

    Message_Group_label_1.size = [500,80];

  // PANEL with group and compound path handling

  release_to_layers_window.OptionsPanel =  release_to_layers_window.add ('panel', undefined, 'Group and Compound Path Items:');

    // GROUP

  var Radio_Group = release_to_layers_window.OptionsPanel.add('group', undefined, '')

  Radio_Group.alignChildren = "left";

  Radio_Group.size = [500,65];

  Radio_Group.orientation = 'column';

  // Radio Boxes

  var RadioBox1 = Radio_Group.add ("radiobutton", undefined, "Do not process Group and Compound Path Items.");

  var RadioBox2 = Radio_Group.add ("radiobutton", undefined, "Release Groups and Compound Path Items by most common color.");

  var RadioBox3 = Radio_Group.add ("radiobutton", undefined, "Ungroup Group and Compound Path Items (caution: might alter shape appearance).");

  RadioBox2.value = true;

    // PANEL with Progressbar

    release_to_layers_window.ProgressPanel = release_to_layers_window.add('panel', undefined, 'Status:');

    // GROUP

    var Progress_Group = release_to_layers_window.ProgressPanel.add('group', undefined, '')

    Progress_Group.orientation = 'column';

    Progress_Group.alignment = [ScriptUI.Alignment.LEFT, ScriptUI.Alignment.TOP]

  Progress_Group.size = [500,5];

    // progressbar

  release_to_layers_window.ProgressProgressBar = release_to_layers_window.ProgressPanel.add( 'progressbar', undefined, 0, 100 );

    release_to_layers_window.ProgressProgressBar.size = [480,10];

  // label

  release_to_layers_window.ProgressLabel = release_to_layers_window.ProgressPanel.add('statictext', undefined, 'Found ' + Counter + ' objects in this document.' );

  release_to_layers_window.ProgressLabel.size = [ 480,20 ];

    // Buttons don't have a PANEL

    // GROUP

    Button_Group = release_to_layers_window.add('group', undefined, '');

    Button_Group.orientation = 'row'

    Button_Group.cancelBtn = Button_Group.add('button', undefined, 'Cancel', {name:'cancel'});

    Button_Group.cancelBtn.onClick = function() { msis_ai_dlg.close() };

    Button_Group.okBtn = Button_Group.add('button', undefined, 'Release', {name:'ok'});

    Button_Group.okBtn.onClick = function()

              { Button_Group.okBtn.enabled = false;

    Button_Group.cancelBtn.enabled = false;

    RadioBox1.enabled = false;

    RadioBox2.enabled = false;

    RadioBox3.enabled = false;

 

 

    if (RadioBox1.value == true)  {ReleaseMethod = 1;}

    if (RadioBox2.value == true)  {ReleaseMethod = 2;}

    if (RadioBox3.value == true)  {ReleaseMethod = 3;}

    Process_Objects();

  };

    release_to_layers_window.show();

}

//var textRef1 = myCurrentObject.textFrames.add();

//textRef1.contents = "1";

// ********************************************************************************************************

// **                                                                                                    **

// ** execute main routine when clicking the Release button                                            **

// **                                                                                                    **

// ********************************************************************************************************

function Process_Objects()

{

// Release to layers but skip Groups and Compound Paths Items OR move groups and compound paths but keep then as group objects

if (ReleaseMethod == 1 || ReleaseMethod == 2)

    {

     for ( i=0; i < Counter; i++ ) //123

          {

           LayerName = "0";

           try

              {

               CurrentItem = doc.pageItems;

            if (CurrentItem.typename != "GroupItem" && CurrentItem.typename != "CompoundPathItem" && CurrentItem.parent.typename != "GroupItem" && CurrentItem.parent.typename != "CompoundPathItem") // no need to create layers for objects within grouped items

               {

            LayerName = ConstructLayerNameBasedOnFillColor(CurrentItem);

                   CurrentItem.move( app.activeDocument.layers.getByName( LayerName ), ElementPlacement.PLACEATBEGINNING );

           }

              } // try

             catch(err)

              { // except

        alert("An error occured processing objects.\n(" + err + ").\n\nFunction: Process_Objects / method=1.");

              } // except

      release_to_layers_window.ProgressLabel.text = 'Processed ' + i + ' objects out of ' + Counter + ' objects in total.';

           release_to_layers_window.ProgressProgressBar.value = (100 / Counter) * i;

           release_to_layers_window.update();

          }  // for

    } // end method 1

// Release to layers, move Group and Compound Path Items based on most common fill color

if (ReleaseMethod == 2)

    {

  // first move all group objects

  LayerName = "0";

     for ( j=0; j < doc.groupItems.length; j++ )

      {

   var CurrentGroupItem = doc.groupItems;

       var MyArray = new Array;

   var TempLayerName = "";

   for (m = CurrentGroupItem.pageItems.length-1; m>=0; m--)

       {

    TempLayerName = ConstructLayerNameBasedOnFillColor( CurrentGroupItem.pageItems );

    MyArray.push( TempLayerName );

   }

   LayerName = GetMostCommonColor(MyArray);

   CurrentGroupItem.moveToBeginning(app.activeDocument.layers.getByName( LayerName ) );

   release_to_layers_window.ProgressLabel.text = 'Processed ' + j + ' group objects out of ' + Counter + ' objects in total.';

          release_to_layers_window.ProgressProgressBar.value = (100 / Counter) * j;

          release_to_layers_window.update();

         }

  // next move all compound path objects

  LayerName = "0";

     for ( j=0; j < doc.compoundPathItems.length; j++ )

      {

   var CurrentCompoundItem = doc.compoundPathItems;

       var MyArray = new Array;

   var TempLayerName = "";

   for (m = CurrentCompoundItem.pathItems.length-1; m>=0; m--)

       {

    TempLayerName = ConstructLayerNameBasedOnFillColor( CurrentCompoundItem.pathItems );

    MyArray.push( TempLayerName );

   }

   LayerName = GetMostCommonColor(MyArray);

   CurrentCompoundItem.moveToBeginning(app.activeDocument.layers.getByName( LayerName ) );

   release_to_layers_window.ProgressLabel.text = 'Processed ' + j + ' group objects out of ' + Counter + ' objects in total.';

   release_to_layers_window.ProgressProgressBar.value = (100 / Counter) * j;

   release_to_layers_window.update();

         }

  }

// Release to layers and ungroup Group and Compound Path Items

if (ReleaseMethod == 3)

    {

     for ( k=0; k < Counter; k++ ) //123

          {

           LayerName = "0";

           try

              {

               CurrentItem = doc.pageItems;

        LayerName = ConstructLayerNameBasedOnFillColor(CurrentItem);

 

            if (CurrentItem.typename != "GroupItem" && CurrentItem.typename != "CompoundPathItem") // no need to create layers for objects within grouped items

               {

                   CurrentItem.move( app.activeDocument.layers.getByName( LayerName ), ElementPlacement.PLACEATBEGINNING );

                  } // try

  else

               {

    UnGroupLayer = app.activeDocument.layers.getByName( LayerName );

    Ungroup( UnGroupLayer , CurrentItem );

                  }

   }

             catch(err)

              { // except

        alert("An error occured processing objects.\n(" + err + ").\n\nFunction: Process_Objects / method=3.");

              } // except

 

      release_to_layers_window.ProgressLabel.text = 'Processed ' + k + ' objects out of ' + Counter + ' objects in total.';

           release_to_layers_window.ProgressProgressBar.value = (100 / Counter) * k;

           release_to_layers_window.update();

          }  // for

    } // end methode 3

      RemoveEmptyLayers();

      release_to_layers_dialog.close();

}

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 14, 2016 1
Guide ,
Feb 16, 2016

Copy link to clipboard

Copied

I'm not sure you will be happy with the outcome of this.

I can tack the 2 scripts together, but the method used for finding the center of an item is clunky and slow.

I am running a test now, and the script has been running for over 10 min already, I'll see how long it takes, if it even finishes...

Did you number the sample file by hand?

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 16, 2016 1
Guide ,
Feb 16, 2016

Copy link to clipboard

Copied

It ran for an hour. before I quit it.
This is not a viable solution for this unless it can be made less clunky.

we could add the numbers for each item via a script.

but they would just be centered to the object, and not gravitationaly centered.

it would then be a manual process to move the numbers to a visually nice location.

I'll have another think about it and let you know if I can work something out...

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 16, 2016 1
Adobe Community Professional ,
Feb 17, 2016

Copy link to clipboard

Copied

you probably could use Jongware's circle-fill algorithms to at least get some bearing on the fill 'density' and then work an approximate placement for the center of the irregular path?

Unless this is what you're already trying out.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 17, 2016 1
Explorer ,
Feb 17, 2016

Copy link to clipboard

Copied

Thank you very much! Swap figures manually course better than creating entirely by hand. But I'll be happy, if you can fully automate the placement of numbers to the gravitational center. I really hope for your skills!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 17, 2016 0
Guide ,
Feb 17, 2016

Copy link to clipboard

Copied

this could be a good idea silly,
I still worry about how long this will take to run it 500 or 1000 times.

I'll have a play. not sure I'll have much luck getting a good understanding of that code to be able to trim it down.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 17, 2016 0
Adobe Community Professional ,
Feb 17, 2016

Copy link to clipboard

Copied

Yes, my own math skills are less than what's needed to understand the circle-fill algorithm. But, with that algorithm alone, it is a gateway to do many 2-D manipulation techniques!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 17, 2016 0
Guide ,
Feb 18, 2016

Copy link to clipboard

Copied

I think I have a better way of doing this.
involving adding strokes and expanding...

I won't go in to the details now, but should be WAY more efficient then creating and destroying an action many times per object...

not as fast as pure math would be but better.

I'll post back when I have a working example

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 18, 2016 0
Adobe Community Professional ,
Feb 18, 2016

Copy link to clipboard

Copied

I found a function to find shape centroids

var idoc = app.activeDocument;

var ipath = idoc.selection[0];

var pps = ipath.pathPoints;

var pts = [];

for (var a=0; a<pps.length; a++) {

    var pp = pps.anchor;

    pts.push({x:pp[0], y:pp[1]});

}

      

//$.writeln(pts.toSource());

var c = get_polygon_centroid (pts);

//$.writeln(c.toSource());

var pt = idoc.pathItems.add();

pt.setEntirePath([[c.x, c.y], [c.x, c.y]]);

pt.stroked = true;

pt.filled = false;

pt.strokeCap = StrokeCap.ROUNDENDCAP;

pt.strokeWidth = 5;

// function by Myobis

// http://stackoverflow.com/questions/9692448/how-can-you-find-the-centroid-of-a-concave-irregular-poly...

function get_polygon_centroid(pts) {

  var first = pts[0], last = pts[pts.length-1];

  if (first.x != last.x || first.y != last.y) pts.push(first);

  var twicearea=0,

  x=0, y=0,

  nPts = pts.length,

  p1, p2, f;

  for ( var i=0, j=nPts-1 ; i<nPts ; j=i++ ) {

      p1 = pts; p2 = pts;

      f = p1.x*p2.y - p2.x*p1.y;

      twicearea += f;        

      x += ( p1.x + p2.x ) * f;

      y += ( p1.y + p2.y ) * f;

  }

  f = twicearea * 3;

  return { x:x/f, y:y/f };

}

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 18, 2016 1
Guide ,
Feb 21, 2016

Copy link to clipboard

Copied

Nice looking script, but not sure its doing the correct thing.

in this image the black dot is produced by this script.

the blue scribble is where I would expect the point to land.

han.JPG

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 21, 2016 0
Guide ,
Feb 21, 2016

Copy link to clipboard

Copied

here is the working example I came up with.

it runs too slow but is way more reliable then the create action method.

I added a timer so you can see the run time.

on my sample file (12 shapes) it took 5 - 10 seconds to run.

THIS WILL RUN SLOW!!!

m.JPGa.JPG

function add_nums(){

    // fiddle with these values to adjust speed vs accuracy

    var percentage = 1; // take average betwenn hight and width and multiplies it by this to get starting stroke weight - 1 = 100%

    var increment = .8; // amount to add to percentage on each iteration

   

    var doc = app.activeDocument;

    var lays = doc.layers;

    var layNames = [];

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

        layNames.push(lays.name);

    }

    // new layers

    var WORK_LAY = doc.layers.add(); 

    WORK_LAY.name = "Working Layer";

    var NUM_LAY = doc.layers.add(); 

    NUM_LAY.name = "Numbers";

   

    // main working loop

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

        //process each layer

        var lay = lays.getByName(layNames);

        lay.name = lay.name + " Num:" + (i+1);

        for(var j=0; j<lay.pathItems.length; j++){

            // process each pathItem

            var pth = lay.pathItems;

            var per = percentage; // reset the path percentage for each shape

            var cent = [];

            var Count = 0;

            if(FatPart(pth) === "error"){

                alert("Loop Error, try resetting\npercentage to 1,\nand\nincroment to .8\nin the script file");

                return;

            }

             // Create the text frame

            var txt = NUM_LAY.textFrames.add();

            txt.contents = i+1; 

            txt.textRange.justification = Justification.CENTER;

            txt.position = [cent[0]-txt.width/2,cent[1]+txt.height/2];

            pth.filled = false;

            pth.stroked = true;

        }

    }

    //clean up

    WORK_LAY.remove();

    // find fat section of a shape

    function FatPart(pth){

        var success = false;

        while(!success){

            success = process();

            // if recursive function runs too many times give option to exit

            Count++;

             if(Count%100 === 0){

                var keepGoing = confirm("Process function has run " + Count + " times on current object.\nWould you like to continue trying?");

                if(!keepGoing){

                    return "error";

                }

            }

        }

        function process(){

            // make sure we start with nothing selected

            doc.selection = null;

           

            // make a copy of the shape to work on

            var item = pth.duplicate(WORK_LAY,ElementPlacement.PLACEATBEGINNING);

            item.filled = false;

            item.stroked = true;

           

            // scale works out how thick the stroke shold be

            var scale = ((item.height+item.width)/20)*per;

            item.strokeWidth = scale;

           

            // select the items and outline the stroke

            item.selected = true;

            app.executeMenuCommand ('OffsetPath v22');// = Outline Stroke

           

            var sel = doc.selection;

            if(sel[0].typename === "CompoundPathItem"){

                if(sel[0].pathItems.length>2){

                    // too many paths, increase stroke and retry

                    sel[0].remove()

                    per = per + increment;

                    return false;

                 }else{

                    // Remove the larger of the 2 shapes in the compoundPath

                    if(Math.abs(sel[0].pathItems[0].area)>Math.abs(sel[0].pathItems[1].area)){

                        sel[0].pathItems[0].remove();

                    }else{

                        sel[0].pathItems[1].remove();

                    }

                   

                    // compare size against original - Aiming for under 10%

                    if(Math.abs(sel[0].pathItems[0].area)>Math.abs(item.area)/10){ // the 10 is ten percent of original size

                        sel[0].pathItems[0].remove();

                        per = per + increment;

                        return false;

                    }else{

                        // now we should have correct size to center text to

                        // return X,Y of shapes Center

                        var B = sel[0].geometricBounds;

                        cent.push(B[0]+(B[2]-B[0])/2);

                        cent.push(B[1]+(B[3]-B[1])/2);

                        sel[0].remove()

                        return true;

                    }

                }

            }else{

                // shape filled completely, reduced stroke and retry

                sel[0].remove()

                    per = per - increment/10;

                    return false;

            }

        }

   

    }

}

var timer = Date.now();

add_nums();

var x = (Date.now()-timer);

alert('conversion completed in:\n'

        + Math.floor(x/1000/60) + ' Minutes, '

        + Math.floor(x/1000%60) + ' Seconds, and '

        + x%1000 + ' Milliseconds');

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 21, 2016 1
Adobe Community Professional ,
Feb 21, 2016

Copy link to clipboard

Copied

hi querty, my script finds the centroids, not the fat part like yours.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 21, 2016 0
Guide ,
Feb 21, 2016

Copy link to clipboard

Copied

it's a nice looking script, I would like to find some time so I can pull it apart so I understand it.

when I first started playing with this idea in a script I wrongly said "better to find gravitational center and not just the center" or something...
the centroid of a 2d object is in essence its center of mass, or gravitational center.

when applying a label to an arbitrary shape my thought is that it is best to be positioned, centered in the fattest part of the object.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 21, 2016 0
Adobe Community Professional ,
Feb 21, 2016

Copy link to clipboard

Copied

when applying a label to an arbitrary shape my thought is that it is best to be positioned, centered in the fattest part of the object.

and you did very well, brilliant way to find the fattest part.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 21, 2016 0
Guide ,
Feb 21, 2016

Copy link to clipboard

Copied

but its so slow!

there has to be a better way!

even if its just to tweak the values for a more efficient strokewidth.

but there has to be a nice clean Math based solution.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 21, 2016 0
Explorer ,
Feb 26, 2016

Copy link to clipboard

Copied

Hi Qwertyfly!

Thanks for the work you've done! I tried to run a script on a bigger file, where the objects count are much more than in your example. The script worked for more than 4 hours and produced an error. "Loop Error, try resetting percentage to 1, and incroment to .8 in the script file".

I have a very powerful computer core i7, but a few restarts gave the result a maximum number 1 on one layer and no longer moved. In addition, the script has fulfilled so many cycles, I was even able to correct the code to the confirmation was sought after not 100 cycles, but 10000. In the end, nothing came of it. Perhaps it really is due to the file size, but my files will not be less than 1000 objects.

Please try find out what else you can do to script to work. Even if it will work slow, but correct.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 26, 2016 0
Guide ,
Feb 28, 2016

Copy link to clipboard

Copied

You should not get that message unless you press no on the dialog that tells you the "Process function has run ... times on current object."

it does not care how many items are in the document.
this count topping out at 100 is how many time it tries to find the center of each shape.

increasing this will probably not fix the issue.

there are a few values which may fix this.

would it be possible for you to send me the file in question?
I would be happy to test it out for you.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 28, 2016 0
Explorer ,
Feb 28, 2016

Copy link to clipboard

Copied

Yes please, u can download 2 files, this is my simplest one example.

The first file is original, second is result that i want to have.

Traced_image.rar - Google Drive

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 28, 2016 0
Guide ,
Feb 29, 2016

Copy link to clipboard

Copied

even before it hangs up on a shape it does strange things.

the positions are all off.

this is due to the poor job illustrator does of expanding outlines.

This will actually take a bit of time to nut out the best way to get around the odd points illustrator adds.

I'll let you know once I have it working.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 29, 2016 0
Explorer ,
Mar 10, 2016

Copy link to clipboard

Copied

Dear Qwertyfly, if you happen to find the time to view the file, please let me know.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Mar 10, 2016 0
Guide ,
Mar 10, 2016

Copy link to clipboard

Copied

I viewed the files and was getting the errors you mentioned above.

it seems to be due to the way illustrator produces strange spiked anomalies when expanding outlines.
this involves a rethink as to how to achieve this.
at this point I am unsure as to the best way to make this work.

when I get a chance I will tackle this again. if I come up with anything I will let you know.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Mar 10, 2016 0
Adobe Community Professional ,
Mar 10, 2016

Copy link to clipboard

Copied

Hey did you already make sure to use "rounded corners" when using a stroke?

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Mar 10, 2016 0
Guide ,
Mar 10, 2016

Copy link to clipboard

Copied

I did not!,

I'm not sure it will make a difference as it's one of those buggy things illustrator does.

But would be worth a try.
I'll give this a go over the next few days.

Thanks again Silly

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Mar 10, 2016 0
Adobe Community Professional ,
Mar 12, 2016

Copy link to clipboard

Copied

Or set the Miter Limit of the stroke to something low line 1 or 2.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Mar 12, 2016 0
Guide ,
Mar 14, 2016

Copy link to clipboard

Copied

Thanks Larry, I'll have a go with lower miters and see if it helps,

I report back.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Mar 14, 2016 0
Explorer ,
Apr 15, 2016

Copy link to clipboard

Copied

Dear Qwertyfly, if u have any little peace of time, please take a look on my problem with this script. I hope u can help and finish this script. Thank you!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Apr 15, 2016 0
Adobe Community Professional ,
Feb 16, 2016

Copy link to clipboard

Copied

I was thinking you can put a circle at every pathpoint of a path, then have your alignment set to "selection" and use some menu command to do alignment on a selection of those circles to have the program automatically center up the many little items to find a 'center', however 2 problems is that I can't find a menu command- so it would have to be action-executed, and also you can have one big zone made by very few pathpoints and a little zone made with very many pathpoints, so the bias goes toward the point-concentration in manual testing.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 16, 2016 1
Enthusiast ,
May 10, 2016

Copy link to clipboard

Copied

Ok, finally success to deal with Compound Path, using "stroke inside & compare area" method. Qwertyfly...

Select one Compound Path item and run the code, will draw a circle in the "fat center". Make sure the "stroke inside" graphic style exists.

var doc = app.activeDocument,

    pth = app.selection[0],

    Count = 0,

    per = 1, // take average betwenn hight and width and multiplies it by this to get starting stroke weight - 1 = 100% 

    increment = .2, // base amount to add to percentage on each iteration 

    TIMES = 20, // when error, try more higher value,

    AREA_PER = 0.02, // 2% of origin path's area, the lower the better, but more slow

    workLay,

    areaArr = logAreas(pth);

try {

    workLay = doc.layers.getByName('workLay');

} catch (e) {

    workLay = doc.layers.add();

    workLay.name = 'workLay';

}

FatPart(pth);

function FatPart(pth) {

    var success = false;

    while (!success) {

        success = process();

        // if recursive function runs too many times give option to exit 

        Count++;

        if (Count % TIMES === 0) {

            return 'error';

        }

    }

}

function process() {

    var item, scale, result;

    doc.selection = null;

    // make a copy of the shape to work on 

    item = pth.duplicate(workLay, ElementPlacement.PLACEATBEGINNING);

    // make sure the path is closed as we want the aligh stroke to inside

    closeAllPath(item);

    item = app.selection[0];

    // apply the "aligh stroke to inside" graphicStyle  by name

    doc.graphicStyles.getByName('stroke inside').applyTo(item);

    // scale works out how thick the stroke shold be 

    scale = Math.min(item.height, item.width) / 10 * per;

    // this function is needed as CompoundPath can not be stroked with script!!!

    strokeComPath(item, scale);

    //$.writeln("scale: " + scale + ' ' + per);

    app.executeMenuCommand('OffsetPath v22'); // = Outline Stroke 

    item = doc.selection[0];

    if (item.typename === "CompoundPathItem") {

        // if a CompoundPathItem selected, this will always be true

        result = compareArea(item);

        if (result != false) {

            addCenterCircle(result);

            item.remove();

            return true

        } else {

            return false

        }

    } else {

        // shape filled completely, reduced stroke and retry 

        item.remove();

        return false;

    }

}

function compareArea(compoundPath) {

    var pi = compoundPath.pathItems,

        l = pi.length,

        i,

        totalArea = 0,

        tmpArr = [],

        pthArea;

    for (i = l - 1; i >= 0; i--) {

        pthArea = Math.abs(Math.round(pi.area));

        // hack of array.indexOf, should be replaced with real

        var isOrigen = areaArr.toString().indexOf('' + pthArea);

        //$.writeln ('path ' + i +' : ' + pthArea + ' ' + isOrigen)

        if (isOrigen === -1) { // new path

            totalArea = totalArea + pthArea;

            tmpArr.push(pi);

        }

    }

    $.writeln(totalArea)

    if (totalArea < areaArr[0] * AREA_PER && totalArea > 0) { // the size is ok

        tmpArr.sort(function(a, b) {

            return Math.abs(b.area) - Math.abs(a.area)

        })

        return tmpArr[0]

    } else if (totalArea === 0) { //stroke width is too big

        compoundPath.remove();

        per = per - increment / 10;

        return false;

    } else { // totalArea >= AREA_PER, stroke width is too small

        compoundPath.remove();

        per = per + increment / 2;

        return false;

    }

}

function closeAllPath(compoundPath) {

    var pi = compoundPath.pathItems,

        l = pi.length,

        i;

    compoundPath.selected = true;

    app.executeMenuCommand('noCompoundPath');

    for (i = 0; i < l; i++) {

        app.selection.closed = 1;

    };

    app.executeMenuCommand('compoundPath');

}

function strokeComPath(compoundPath, weight) {

    var pi = compoundPath.pathItems,

        l = pi.length,

        i = 0;

    for (; i < l; i++) {

        pi.strokeWidth = weight;

    };

}

function logAreas(compoundPath) {

    var pi = compoundPath.pathItems,

        l = pi.length,

        i,

        totalArea = 0,

        areaArr = [],

        pthArea;

    for (i = l - 1; i >= 0; i--) {

        pthArea = pi.area;

        totalArea = totalArea + pthArea;

        // log 3 value as the outline stroke path may differ with origin

        areaArr.push(Math.abs(Math.round(pthArea)));

        areaArr.push(Math.abs(Math.round(pthArea)) - 1);

        areaArr.push(Math.abs(Math.round(pthArea)) + 1);

    }

    areaArr.unshift(Math.abs(totalArea));

    $.writeln(Math.abs(totalArea));

    return areaArr

}

function addCenterCircle(pth) {

    var gb = pth.position,

        radius = 5;

    center = [gb[0] + pth.width / 2 - radius, gb[1] - pth.height / 2 + radius];

    doc.pathItems.ellipse(center[1], center[0], 10, 10);

}

cap.PNG

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 10, 2016 2
Enthusiast ,
May 15, 2016

Copy link to clipboard

Copied

Hello all, here comes the final version.

This time "offset path effect" been used. I thought this is a possible way at beginning, but never give it a try, until today.

It deal with both Path and Compound Path. Test the sample document, it only took 31 seconds.

cap.PNG

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 15, 2016 2
Explorer ,
May 15, 2016

Copy link to clipboard

Copied

Moluapple, this script works amazing! I tryed another file with many many objects, and it was pretty fast to do!

Thank you very much for your work! Qwertyfly thank u too for big beggining of this hard task!

I think, next step to improve, will be resizing numbers "pt" size in very small objects. It will be perfect to use, can u try?

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 15, 2016 0
Enthusiast ,
May 17, 2016

Copy link to clipboard

Copied

So, does set the minimize font size to 4 pt fit your requirement? if size is too small, will be hard to recognize isn't it?

Above link has been updated, and big thanks to Hiroyuki Sato (@shspage) for his talent modify which lead to a more precise result.

As I have set the TIMES to 100, and other settings adjusted, It will use more time. The same document, this time 2'14''.

cap.PNG

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 17, 2016 0
Explorer ,
May 20, 2016

Copy link to clipboard

Copied

Moluapple, thanks a lot, 4 pt is enouth!

I am trying to start updated script on, but getting this error. Can u take a look please?

error.jpg

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 20, 2016 0
Enthusiast ,
May 20, 2016

Copy link to clipboard

Copied

Sorry to say, that means your need to quit and relaunch Illustrator.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 20, 2016 0
Explorer ,
May 20, 2016

Copy link to clipboard

Copied

I did it again and again, not helps. Then i comment that string, no errors, but i don't think that it's good idea.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 20, 2016 0
Explorer ,
May 22, 2016

Copy link to clipboard

Copied

It seems depends on file, and layers cound, on file wich have less objects and layers no errors. I am trying to test some.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 22, 2016 0
Explorer ,
May 31, 2016

Copy link to clipboard

Copied

Should we wait another versions from qwertyfly and CarlosCanto?

I think the last version is very good, but not perfect, so will be glad to see another, if it exist.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 31, 2016 0
Explorer ,
Jul 31, 2016

Copy link to clipboard

Copied

Dear illustrator script ninjas! Thank u again for great work! I have a new little entreaty if u can help to modifty and check compared size of color's all object and show it on layer name.

For example area seize in square centimeters  it can be like:

Layer 1 Num:1 Size: 0,5 cm

Layer 2 Num:2 Size: 2 cm

Is it real?

entreaty

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Jul 31, 2016 0
Community Beginner ,
Nov 08, 2018

Copy link to clipboard

Copied

Hi, Thanks for this script it is AMAZING! is there a way to have this number in sequence from top left to bottom right like in the non script version on the image?

NUMBERING.jpg

If anyone can crack this for me, it will get me a lot of praise at work (maybe even a pay rise!) *fingers crossed*

TIA

Ricky

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Nov 08, 2018 0
Adobe Community Professional ,
Nov 08, 2018

Copy link to clipboard

Copied

one of us will help you get that pay raise, it is not an easy task though, as soon as one of us finds some time. Be patience.

can you share a file to play with?

what do you do? what's the purpose for numbering (I'm just being curious).

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Nov 08, 2018 0
Community Beginner ,
Nov 09, 2018

Copy link to clipboard

Copied

This would be absolutely amazing. People at work are already astonished I managed to find this, it will save us hours.

Hopefully this share works... link --->  Adobe Document Cloud

I can be patient but I am pretty excited haha. I brand vehicles and a lot have chevrons. We number them for the client to pin point which pieces need replacing.

If you need anymore info or anything, let me know!

Thank you all so much! I love this forum!

Ricky

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Nov 09, 2018 0
Guide ,
May 15, 2016

Copy link to clipboard

Copied

Nice work, I can't wait to have some spare time to have a look at your script.
getting it to run in 30 sec is good, will be nice to see you method.

it's great to see a idea from left of center get taken up and built on until it actually becomes a usable tool.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 15, 2016 0
Adobe Community Professional ,
May 15, 2016

Copy link to clipboard

Copied

good job moluapple, I was working on my own version using Offset Path Effect as well, I'll post it if I can get it to work in 30 seconds

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 15, 2016 0
Enthusiast ,
May 17, 2016

Copy link to clipboard

Copied

Carlos, will glad to see your version if your have finish, regardless of the time.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 17, 2016 0
New Here ,
Aug 24, 2020

Copy link to clipboard

Copied

The script doesn't work for me. Only one number can be included there (only the number 1 in all sectors for all colours, instead of showing the whole set of numbers). I need all the numbers from 1 to 24. Can you please advise where the problem is?

product_157143.jpg

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Aug 24, 2020 0
New Here ,
Sep 11, 2020

Copy link to clipboard

Copied

Hey! Can i have a question for you, how did you used this script? because I get the error: 
in image.Screen Shot 2020-09-11 at 10.40.31.png

What script did you use from all the listed above? Can anyone please help us solve this problem? We need it so much!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Sep 11, 2020 0
New Here ,
Sep 11, 2020

Copy link to clipboard

Copied

Dear moluapple, can you please help us?

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Sep 11, 2020 0
Engaged ,
Sep 12, 2020

Copy link to clipboard

Copied

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Sep 12, 2020 0