Skip to main content
vnh68
Inspiring
March 9, 2021
Answered

ID Scripting: PageItems on Layer: collection 'pageItems' and flat array 'allPageItems' are empty.

  • March 9, 2021
  • 5 replies
  • 1369 views

[ID 16.1 2021, Win, Scripting JS]

Class Layer has collection pageItems and flat array allPageItems.

I created a new layer and started placing pageitems on it. 

After that, I found that the Layer's collection pageItems and array allPageItems remained empty. However, in the Layers panel, all pageitems were in place!

 

In the collection pageItems on Document I can see all that pageitems in it, and their property itemLayer contains correct  layer value.

 

It was expected that after placing a pageitem on the layer, its collection pageItems will be immediately updated, but for some reason this does not happen. The idea of sampling pageitems from another collection by the itemLayer property looks very ridiculous ...

 

I feel confused even though there is nothing illegal about my expectations ...

This topic has been closed for replies.
Correct answer vnh68

I can’t get your prototype function to run, but you have the layer named differently  'erh_m_layer_x'  and 'erh_m_layer'. 

 

This returns the m_layer.pageItems length as 1

 

main();
function main() {
    var doc = app.activeDocument;
    var ln = "erh_m_layer_x"
    var layer, tf;
        try {
            layer = doc.layers.add({ name: ln, layerColor: UIColors.GRAY });
        } catch (e) {
            layer = doc.layers.itemByName(ln);
        }
    
        try {
            tf = doc.pages.lastItem().textFrames.add();
            tf.geometricBounds = [10,10,100,100];
            tf.name = 'TF_1';
        } catch (e) {
        }
    originVisibility(ln)
}




function originVisibility(n){

   var m_layer = app.activeDocument.layers.itemByName(n);
    
    //Both return 1
    $.writeln("All page Items: "+ m_layer.allPageItems.length)
    $.writeln("Page Items: "+ m_layer.pageItems.length)
    
    var l = app.activeDocument.masterSpreads.everyItem().pageItems.everyItem().getElements()
    
//~     filter(function(pi){
//~         return pi.itemLayer == m_layer;
//~     })
//~     forEach(function(pi){
//~         try {
//~             pi = resolve(pi.extractLabel('orig'));
//~             pi && pi.isValid && pi.visible = true;
//~         } catch(e) {
//~             tracelog('originVisibility: ***ERROR*** ' + e, $.line);
//~         }
//~     }) 
}



Thank you for your time!

The modified layer name in the example I used intentionally, since the test script was used at one time with the main one.

I have made another small script that will be launched as a startup script, perhaps something will appear in this version.

 

#target indesign
#targetengine testlayers

function TestLayers() {
    
    const LAYER_NAME = 'erh_w_test';
    
    this.getUniqueName = (function layerNumber() {
        var ctr = -1;
        return function(){
            return LAYER_NAME + (++ctr);
        }
    })();
    
    
    this.createLayer = function() {
        var that = this;
        var ret = app.activeDocument.layers.add({ name: that.getUniqueName() });
        app.activeDocument.activeLayer = ret;
        
        return ret;
    }

    this.createTextFrame = function() {
        return app.activeDocument.pages.lastItem().textFrames.add({geometricBounds: [10,10,100,100] });
    }
    
}


app.documents.add();

var tl = new TestLayers();

tl.createLayer();
var tf = tl.createTextFrame();

var nlayer = tl.createLayer();
var ntf = tf.duplicate(tf.parentPage, [5, 5]);
ntf.itemLayer = nlayer;

alert(nlayer.pageItems.length);

 It works... This is a brief option, you will have to complicate. I will look for the cause ...

5 replies

Community Expert
March 10, 2021

Hi vnh68,

yes, this is something you have to know and we discussed it about ten years ago in the old InDesign Scripting forum:

The scope of doc.layer.pageItems is always the document spreads. Never the master spreads. Some say this is a bug, some say it's build on purpose or as designed.

 

So you best loop all master spreads and filter the page items that are on a distinct layer.

Property itemLayer will return the layer of a given page item.

 

Regards,
Uwe Laubender

( ACP )

rob day
Community Expert
Community Expert
March 10, 2021

Thanks Uwe, or maybe loop the document page items and check the page item’s parent and itemLayer name?

 

var doc = app.documents.item(0);
var ln = "erh_w_test"
var lpi = getMPLayerItems(ln)
$.writeln("The are " + lpi.length + " page items in the layer named " + ln + " on the masters")

/**
* Gets the page items on master spreads in the specified layer 
* @9397041 the name of the layer to check
* @Return an array of page items 
* 
*/
function getMPLayerItems(n){
    var a = new Array
    var pi = doc.pageItems;
    var i = pi.length; while (i--) if (pi[i].parent.constructor.name == "MasterSpread" && pi[i].itemLayer.name == n) a.push(pi[i]);
    return a
}

 

rob day
Community Expert
Community Expert
March 10, 2021

On the document pages, master page items don’t become page items until they are overridden. Add this to the end of your script:

 

//gets the master page items on page 1
var mpi = app.activeDocument.pages[0].masterPageItems

alert("There are " + app.activeDocument.pages[0].pageItems.length + " page items on page 1"); //0

alert("There are " + mpi.length + " master page items on page 1"); //2

//Override the master page items on page 1, which converts them into page items
var i = mpi.length; while (i--) mpi[i].override(app.activeDocument.pages[0]);

alert("Masterpage Items have been overridden, there are now " + app.activeDocument.pageItems.length + " document page items."); //4
vnh68
vnh68Author
Inspiring
March 10, 2021

Rob Day,

Unfortunately, the script logic does not allow using overriding master pageitems at the time of processing. The use of a layer was necessary only for logical grouping these master pageitems for fast processing without loops.  (Filtering pageitems doesn't make the task much harder).

The fact is that this auxiliary layer was conceived exclusively for working on master spreads.

There was also an idea to use Groups, but their action is limited to only one masterSpread.

 

So far,  not very long code like this will do fine.

Array.prototype.filter = function(f) {
	var i = -1, l = this.length;
	
	if ('function' == typeof f) {
		while (++i < l) {
			(!f.apply(this, [this[i], i, this])) && this.splice(i, 1);
		}
	}
        return this;
}
/////////
Layer.prototype.findAllMasterPageItems = function() {
	var that = this;
	
	return app.activeDocument.masterSpreads.everyItem().pageItems.everyItem().getElements()
	.filter(function(pi){
		return pi.itemLayer === that;
	});
}

////////

var lmpi = app.activeDocument.layers.item('name').findAllMasterPageItems();

 

 

rob day
Community Expert
Community Expert
March 9, 2021

Also, you can use everyItem() to set the page items’ properties without a loop:

 

 

var doc = app.activeDocument;
var tf = doc.pages.lastItem().textFrames.add();

//creat the layer if it doesn’t exist
var ln = 'erh_m_layer'
try {
    var layer = doc.layers.add({name:ln});
}catch(e) {layer = doc.layers.itemByName(ln)} 


var tf_duplicate = tf.duplicate(tf.parentPage);
if (layer && layer.isValid) {
   tf_duplicate.itemLayer = layer;
}



//set the geometric bounds of all the layer’s page items 
doc.layers.itemByName(ln).pageItems.everyItem().geometricBounds = [1,1,4,4];

//returns an array of each item’s geometric bounds
var pin = doc.layers.itemByName(ln).pageItems.everyItem().geometricBounds;
alert("Page Items:\nThe Layer named: " + ln + " has "+pin.length + " page items.\n\n The first item’s geometric bounds is: " + pin[0])

 

 

vnh68
vnh68Author
Inspiring
March 9, 2021

Thank you so much! I created a test script that runs from the scripts panel. In this case, it really worked out.

 

main();

function main() {
    var doc = app.activeDocument;
    var layer, tf;
        try {
            layer = doc.layers.add({ name: 'erh_m_layer_x', layerColor: UIColors.GRAY });
        } catch (e) {
            layer = doc.layers.itemByName('erh_m_layer_x');
        }
    
        try {
            tf = doc.pages.lastItem().textFrames.add();
            tf.geometricBounds = [10,10,100,100];
            tf.name = 'TF_1';
        } catch (e) {
        }
    
    alert(doc.layers.itemByName('erh_m_layer_x').pageItems.everyItem().getElements()[0].name); //TF_1
    
    alert(doc.layers.itemByName('erh_m_layer_x').pageItems.length);
}

 

 

Now is my piece of code below. This is a long startup script,  the quoted part of it should process the content of the layer. This method is called when 4 text frames actually exist on the 'erh _m _layer' layer.

Alerts show 0 pageitems on 'm_layer'. The rest of the code works fine because... it doesn't use the layer collection.

 

Document.prototype.originVisibility = function() {
    var m_layer = app.activeDocument.layers.itemByName('erh_m_layer');
    
    //alert(m_layer.allPageItems.length); // 0! WHY???
    alert(m_layer.pageItems.length);   // 0! WHY???
    
    var l = app.activeDocument.masterSpreads.everyItem().pageItems.everyItem().getElements()
    .filter(function(pi){
        return pi.itemLayer == m_layer;
    })
    .forEach(function(pi){
        try {
            pi = resolve(pi.extractLabel('orig'));
            pi && pi.isValid && pi.visible = true;
        } catch(e) {
            tracelog('originVisibility: ***ERROR*** ' + e, $.line);
        }
    });
}

 

 

rob day
Community Expert
Community Expert
March 9, 2021

I can’t get your prototype function to run, but you have the layer named differently  'erh_m_layer_x'  and 'erh_m_layer'. 

 

This returns the m_layer.pageItems length as 1

 

main();
function main() {
    var doc = app.activeDocument;
    var ln = "erh_m_layer_x"
    var layer, tf;
        try {
            layer = doc.layers.add({ name: ln, layerColor: UIColors.GRAY });
        } catch (e) {
            layer = doc.layers.itemByName(ln);
        }
    
        try {
            tf = doc.pages.lastItem().textFrames.add();
            tf.geometricBounds = [10,10,100,100];
            tf.name = 'TF_1';
        } catch (e) {
        }
    originVisibility(ln)
}




function originVisibility(n){

   var m_layer = app.activeDocument.layers.itemByName(n);
    
    //Both return 1
    $.writeln("All page Items: "+ m_layer.allPageItems.length)
    $.writeln("Page Items: "+ m_layer.pageItems.length)
    
    var l = app.activeDocument.masterSpreads.everyItem().pageItems.everyItem().getElements()
    
//~     filter(function(pi){
//~         return pi.itemLayer == m_layer;
//~     })
//~     forEach(function(pi){
//~         try {
//~             pi = resolve(pi.extractLabel('orig'));
//~             pi && pi.isValid && pi.visible = true;
//~         } catch(e) {
//~             tracelog('originVisibility: ***ERROR*** ' + e, $.line);
//~         }
//~     }) 
}


rob day
Community Expert
Community Expert
March 9, 2021

Your ltf variable is returning a text frame not the layer’s page items:

 

 

var doc = app.activeDocument;
var tf = doc.pages.lastItem().textFrames.add();

//creat the layer if it doesn’t exist
var ln = 'erh_m_layer'
try {
    var layer = doc.layers.add({name:ln});
}catch(e) {layer = doc.layers.itemByName(ln)} 


var tf_duplicate = tf.duplicate(tf.parentPage);
if (layer && layer.isValid) {
   tf_duplicate.itemLayer = layer;
}

//returns a single page item
var tf = doc.layers.itemByName('erh_m_layer').pageItems.everyItem().getElements()[0];
$.writeln(tf)

//returns the layers’ page items as an array
var ltf = doc.layers.itemByName(ln).pageItems;
$.writeln("The Layer named: " + ln + " has "+ltf.length + "page items")





 

rob day
Community Expert
Community Expert
March 9, 2021

Can you post some sample code? This works for me—gets all the page items in a layer named "Layer 1":

 

var doc = app.documents.item(0);
var lpi = doc.layers.itemByName("Layer 1").pageItems;
$.writeln(lpi.length);

 

vnh68
vnh68Author
Inspiring
March 9, 2021
// In short, the code looks like this, the whole script is very large and many methods 
// are tied to class prototypes ...

var doc = app.activeDocument;
var tf = doc.pages.lastItem().textFrames.add(...);

var layer = doc.layers.add({ name: 'erh_m_layer', UIColors.RED });

// Now I create textframe duplicate and move it on the 'layer'
var tf_duplicate = tf.duplicate(tf.parentPage); // OK!
if (layer && layer.isValid) {
   tf_duplicate.itemLayer = layer; // OK! 
}


//Now I want to get  duplicate from Layer pageItems collection :

var ltf = doc.layers.itemByName('erh_m_layer').pageItems.everyItem().getElements()[0];
// this array is empty...

// also:  pageItems.count() == 0, allPageItems.length == 0


//Such code works:
var pItems = doc.pageItems.everyItem().getElements()
.filter(function(pi){ 
   return pi.itemLayer == layer;
});
//This array contains created textframe.