Select, adjust and distribute textframes in InDesign [ExtendScript] - javascript

I want to write a script that does the following:
select some textframes
center the selection horizontally
distribute the frames horizontally
the whole routine has to be done on several pages
My first problem is, that I only want to select textframes from a unlocked layer. I found several solutions where all textframes, even on locked layers, were selected.
And the only solution I found so far that just selects the textframes from my unlocked layer (app.menuActions.item("$ID/Select &All").invoke();) doesn't provide an object I can work with (adjust, distribute) afterwards.
Is there a solution to my requirements?
Edit:
My last attempt looked like this (for a single page, I didn't used a loop for several pages while testing):
// 'allPageItems' erfasst alle Rahmen, zusätzlich Gruppen und Bilder
var allObjects = app.activeDocument.layoutWindows[0].activeSpread.allPageItems;
// eine Schleife durch die Objekte
for (var n=0; n<allObjects.length; n++) {
var curObject = allObjects[n];
// prüfen, ob Textrahmen
if (curObject.constructor.name == "TextFrame") {
// verankerte Textrahmen ausschliessen
if (curObject.parent != "[object Character]") {
// zur Auswahl hinzufügen
curObject.select(SelectionOptions.ADD_TO);
}
}
}

Generally, selection is something that is intended for UI interaction, not for scripting. Therefore you should avoid handling all the selection stuff in your script and collect the textFrames as objects in an array that you then can use to do the other stuff.
This should work:
#target indesign
var doc = app.activeDocument;
var curSpread = doc.layoutWindows[0].activeSpread;
var spreadItems = curSpread.allPageItems;
var distObjects = [];
// collect all relevant objects in distObjects
for (var i = 0; i < spreadItems.length; i += 1) {
var si = spreadItems[i];
// skip if itemLayer is locked
if (si.itemLayer.locked) continue;
// skip if item is not a textFrame
if (!(si instanceof TextFrame)) continue;
// skip if item is anchored
if (si.parent.constructor.name === "Character") continue;
distObjects.push(si);
};
// group all collected objects to center them, then ungroup
var distGroup = curSpread.groups.add(distObjects);
doc.align([distGroup], AlignOptions.HORIZONTAL_CENTERS, AlignDistributeBounds.SPREAD_BOUNDS);
distGroup.ungroup();
// distribute all objects horizontally
doc.distribute(distObjects, DistributeOptions.HORIZONTAL_CENTERS, AlignDistributeBounds.ITEM_BOUNDS);
Note: If this is used with an older ID version (prior to CC2014 I believe), after ungrouping, all pageItems will stay on the same layer. The feature for them to move back to the original layer was only introduced recently. If you need a solution for an older InDesign version, you would need to calculate the bounds of the group of objects you found, then offset them all one by one, so the entire "selection" can get centered.

Related

Leaflet array of markers that need to move not recreate

Firstly - I am a novice who has picked away at getting something functional for me over the past few years. My code below basically works - it's just complex and I believe there must be a better approach. Looking for a kick in different direction.
I am using javascript and leaflet to display an array of moving markers (players). The data on the markers comes to me in a table - listing an ID for each marker, a location and other information not needed for this purpose. The table of data will have a changing number of markers (ie: just one (MYID), or that plus one other, two or even more 'others'), and it will have multiple entries for the each marker (same ID and location, just the other data is different).
One of the markers ids is the same as "MYID" which I then treat differently, but for the others (if any) I have a function that puts them on my map. And each time the function is called it should move them to their new location data, but I currently don't know how to do that elegantly. So I am presently looking to delete all and recreate them each time the function is called (triggered by each time anything changes). Not so good coding.
function updatemap(displaytable,MYID) { // Update other player locations on map
OPN=[]; //reset OPN array
OPX=[]; //reset OPX array
OPY=[]; //reset OPY array
for (var r=0;r<OPLoc.length;r++){
// Need to remove all OPloc off map first and then re-create them?
};
OPLoc=[];
var q=0;
for (var p=0; p<displaytable.length; p++){ // for every line of the displaytable
if ((!OPN.includes(displaytable[p].id)) && (displaytable[p].id != MYID)){ // ... create a unique other player entry, once only
OPN.push(displaytable[p].id);
OPX.push(displaytable[p].lat);
OPY.push(displaytable[p].lon);
OPLoc[q] = new L.marker([displaytable[p].lat,displaytable[p].lon], {icon: oplayericon})
.addTo(mymap)
.bindPopup(displaytable[p].id + "<br>" + displaytable[p].lat + "," + displaytable[p].lon);
q++;
};
};
//...other code not relevant
};
None of the variable/arrays in the function are needed anywhere else for anything - I've just created all this mess to do this one thing. And the arrays are created globally so that they are available next time it's called.
I assume there are functions/capabilities of the array commands that I am just not aware of. Can anyone suggest a better way? please?
well, still not sure it's elegant - but it's functional. Not sure if there are edge cases that I'm not covering - but this appears to work and do what I need. I only use two arrays now (one for the markers and one for the ids so I can keep track of what's active). I tried making a 2D array but that made me sad, so it's two arrays now.
for (var s=0;s<displaytable.length;s++) { // display all other players
if (OPid.includes(displaytable[s].id)) { //if the player is already on the map, just update location
OPLoc[OPid.indexOf(displaytable[s].id)].setLatLng([displaytable[s].lat,displaytable[s].lon]);
} else { // if the player is not MYID and not already on the map, create it
if (displaytable[s].id != MYID) {
OPid.push(displaytable[s].id);
OPLoc[OPid.indexOf(displaytable[s].id)] = new L.marker([displaytable[s].lat,displaytable[s].lon], {icon: oplayericon})
.addTo(mymap)
.bindPopup(displaytable[s].id);
};
};
};
for (var r=0;r<OPid.length;r++) { //check if the OPlayer is still active
var found = false;
for(var q=0;q<displaytable.length;q++) {
if (displaytable[q].id == OPid[r]) {
found = true;
};
};
if (!found) { //if the OPlayer id is no longer in the displaytable
mymap.removeLayer(OPLoc[r]); //remove it from the map
OPLoc.splice(r,1); //remove it from the OPLoc array
OPid.splice(r,1); //remove it form the OPid array
};
};
```

How to crop a layer in photoshop script

I am having problems in a very simple task in photoshop script, but I am afraid this is not documented elsewhere on the web.
I just want to invert an existing selection, delete the content of the current layer, and then invert back the selection.
Of course this is part of a larger program, and if you are interested, i can provide all details.
Up to know i am making:
// Get current document and current layer
var doc = app.activeDocument;
var activeLay = doc.activeLayer;
var a=0
// find the current layer and assign its code to the variable a
for(i=doc.layers.length-1; i >=0; )
{
if(doc.layers[i]==activeLay)
{
a=i;
alert("a"+a);
break;
}
else{ i--; }
alert ("i"+i);
}
// Now cycle remaining layer under the exsiting one, and jump the selection
// and delete the outer area of selection for each layer
for(i=a-1; i >=0;)
{
// make layer i active
doc.activeLayer=doc.layers[i];
alert ("active layer"+i);
// where is my selection in regards to the active layer?
var s = app.activeDocument.selection.bounds;
var xSo=s[0];
var ySo=s[1];
var xLo = activeLay.bounds[0].value;
var yLo = activeLay.bounds[1].value;
// I have to go from actual selection poisition to the NEXT layer... which i just made active...
DeltaX=xLo-s[0];
DeltaY=ySo-s[1];
doc.selection.translateBoundary(DeltaX,DeltaY);
//Now invert selection and delete
doc.selection.invert
doc.selection.fill (fillType, mode, 0, preserveTransparency) // ??? here what i cannot do?
doc.selection.invert
i--;
}
I found the answer myself...
it is merely
//Now invert selection and delete
doc.selection.invert;
doc.selection.cut();
doc.selection.invert;
my problem was that i didn't know that javascript is case sensitive, and hence .cut() didn't' work for when i misspelled it.

indesign script: remove/hide all empty graphic frames placed in a specific layer

I have a multipage indesign document and in it several layers; one layer is called obrazy and in this layer on every page some frames are placed (the frames are also styled with an object style called obraz), then some of the frames are filled with images and some remain empty; what I need is a script (in javascript) to go through the document end move the empty frames into a different layer (can be new or already existing) which will later be hidden. The script must only manipulate frames in a specific layer or with a specific object style since there are other frames in the other layers.
I have written several versions (and spent hours of experimenting and going through various sources and similar scripts) but still do not have a fully working solution. I guess I am missing some details... Any pointers or suggestions would be very helpful. Thanks.
ok, here is what is now working for me:
var dok = app.activeDocument;
var pocetstran = dok.pages.length;
var vrstvaobrazu = dok.layers.item("obrazy");
for (var j = 0; j<pocetstran; j++) {
for (var i = 0; i < dok.layers.item("obrazy").rectangles.length; i++) {
if ((dok.layers.item("obrazy").rectangles.item(i).images.length == 0)){
dok.layers.item("obrazy").rectangles.item(i).remove();
}
}
}

Logic behind CAD apps multi-selection and alignment functionality

I am trying to build a small CAD app using HTML5 Canvas and Paper.js
Now I am stuck at a problem I'd hate asking for help for since I believe I should solve this on my own, but everytime I try to think about a solution my head hurts.
I am trying to build the following functions:
Align-left
Align-right
Align-center.
The user is supposed to select a bunch of elements on the canvas and click one of the above functions to align them together.
The whole concept is that the user selects a Reference Item in a way. The elements are aligned relative-to the Reference Item. This ensures that the alignments happen in a user predictable way.
There are 2 ways for the user to select elements
Selection-via-Intersection
The user clicks and drags a selection rectangle. While dragging I
can track in an array which items intersected with the selection
rectangle. I can track the first intersected item here just fine, so I
have a Reference Item .Works perfectly so far.
Selection-via-Click+Shift
The user holds Shift and clicks on elements to do a multi-selection.
It's a functionality I'm sure everyone has seen in any modern graphics
editor. I have yet to figure out a way how to track a first-selected
item here, since items can be set as first-selected and then
deselected.
The problem:
The above selection methods can be COMBINED together -
The user can select-via-intersection and then start select/de-select items using Shift+Click.
Which one from the selectedItems is the Reference-Item now?
An example:
Item 2 was the first-selection on selection-via-intersection. Item 2
was de-selected using Shift+Click. Which one is the Reference-Item
now?.
Here is a GIF that illustrates what I am talking about (although it's invinsible in the GIF the items get selected as soon as the intersection rectangle touches them.)
I am aware that the whole thing will involve some array logic but I cannot seem to grasp how to implement the whole thing.
Notes:
Apart from the complexity of this thing, I have performance constraints as well. Intersection Detection which is used for the Selection-via-Intersection is already a heavy operation and it get's fired continously while the user drags a selection rectangle.
This is the code for Select-Via-Intersection
The following function getSortedIntersects get's called continuously while the user drags a selection rectangle. I log in the console the first selected item:
var checkedSegments = new Array();
function getSortedIntersects(rect) {
//children are all the items drawn on the canvas
var children = project.activeLayer.children;
//I clone the children array into another array and I exclude all items other than Paths
//Intersection Detection works only on paths.
var pathsOnCanvas = children.filter(function (el) {
return el instanceof Path
});
var newCheckedSegments = new Array();
var firstSelection = "";
//Start looping over all Paths on the canvas
for (var i = 0; i < pathsOnCanvas.length; i++) {
//create an array which lists all the intersections. The array is called ''intersections''
var intersections = pathsOnCanvas[i].getIntersections(rect);
//If there is at least 1 intersection OR the rect contains a path then proceed
if (intersections.length > 0 || rect.contains(pathsOnCanvas[i].bounds)) {
//Checks if the selected item at index i is in the newSegments array. If so, it continues iterating the loop without going further at this iteration.
if (newCheckedSegments.indexOf(pathsOnCanvas[i]) >= 0)
continue;
pathsOnCanvas[i].selected = true;
newCheckedSegments.push(pathsOnCanvas[i]);
}
}
for (var i = 0; i < newCheckedSegments.length; i++) {
if (checkedSegments.indexOf(newCheckedSegments[i]) < 0)
checkedSegments.push(newCheckedSegments[i]);
firstSelection = (checkedSegments[0].id);
console.log("first selection is " + firstSelection);
}
for (var i = checkedSegments.length - 1; i >= 0; i--) {
if (newCheckedSegments.indexOf(checkedSegments[i]) < 0) {
checkedSegments[i].selected = false;
checkedSegments.splice(i, 1);
}
}
}

Scripting Photoshop Difference Blendmode

I regularly have two sets of pictures named the same way and I would like to script the process of checking for differences. I'm looking for a basic check, if there is no differences between the two images, discard one of them, if there is a single pixel difference, keep both. For those who question the wisdom of doing this in photoshop, this is an addition to another script that is already running and this optional check will help reduce the number of files I have to upload. I would appreciate the help.
If you really have to do this in Photoshop, this is how I'd propose it:
var doc1 = app.open(new File("~/Desktop/test1.bmp"));
var doc2 = app.open(new File("~/Desktop/test2.bmp"));
doc2.selection.selectAll();
doc2.selection.copy();
app.activeDocument = doc1;
var newLayer = doc1.paste();
newLayer.blendMode = BlendMode.DIFFERENCE;
var histogram = doc1.histogram;
for (var i = 1; i < histogram.length; ++i) {
if (histogram[i] > 0) {
alert('Different!');
break;
}
}
I paste the second picture into the first one and set the resulting layer's blend mode to difference. If the two pictures are identical, the resulting picture should be all black. I therefore check if any color values apart from 0 have any pixels in the histogram.
I assumed the two images have the same size.

Categories

Resources