Half of my reason for posting is to put something on here that can be useful for others, as there is next to nothing good online about PDF OCGs/Layers, but I also need some help getting this to work right. 90% good, still need 10% to be done.
I have a list of layers/OCGs, I want some of them to be nested. I want to turn off the group as a whole. I can nest layers/OCGs by separating into 2+ arrays and inserting the list I want nested into the base list.
If the first element of the nested array is a String, it creates a group in a tree structure. I can click the '+' to open and close the group, but there is no "eye" box to turn visibility on or off for the group.
If the first element is not a string, it just uses the previous element in the array as the parent, complete with folder Icon and visibility "eye" to click, but while clicking the "eye will turn of the parent layer/OCG, it will not turn off the underlying nested layers/OCGs.
This may be fixed if I knew how to create data correctly. Or perhaps how to set a parent OCG.
Any ideas?
Install this script by placing in: c:\Program Files (x86)\Adobe\Acrobat 10.0\Acrobat\Javascripts
It will create a menu item under Edit, or Document->Edit
An example PDF is here 767k Download locally to see layers.
/********************************
RSCivilTools by Andrew Binning
Version 01, 2-15-2012
********************************/
//Some code by:
// InDesign Fixups by Dave Merchant - Creative Commons Share-alike license
// version 02, November 2010
// http://www.uvsar.com/go/indesignfixups
//create new submenu for the Acrobat 8/9 or X menus
//Determine version, assign menu location
var menuParent = (app.viewerVersion<10)? "DocumentProcessing":"Edit";
//Add the menu
app.addSubMenu({ cName:"RSCivilTools", cUser:"RSCivil Tools", cParent:menuParent, nPos:((app.viewerVersion<10)? 0:7) });
//Create a nested layer (tailored code for my specific Document.
app.addMenuItem({ cName:"RSCcreateNest", cUser:"Create Nest", cParent:"RSCivilTools",
cExec:"createNest();",
cEnable:"event.rc = (event.target != null);", nPos:0 });
//Promote sub layers - Unravel the nests
app.addMenuItem({ cName:"RSCpromote", cUser:"Undo Nest", cParent:"RSCivilTools",
cExec:"promoteOCG_handler(event.target);",
cEnable:"event.rc = (event.target != null);", nPos:1 });
//Unlist Guides and Grids layer (from Adobe IN-Design)
app.addMenuItem({ cName:"RSCremGAG", cUser:"Unlist 'Guides and Grids'", cParent:"RSCivilTools",
cExec:"removeGAG(event.target);",
cEnable:"event.rc = (event.target != null);", nPos:2 });
//Set up layers ( this is tailored code for my specific Document)
app.addMenuItem({ cName:"RSCsetStates", cUser:"Set Layers", cParent:"RSCivilTools",
cExec:"setStates();",
cEnable:"event.rc = (event.target != null);", nPos:3 });
//Toggle this list of layers on and off (tailored code for my specific Document.
app.addMenuItem({ cName:"RSCtoggleCityLimits", cUser:"Toggle City Limits", cParent:"RSCivilTools",
cExec:"toggleCityLimits();",
cEnable:"event.rc = (event.target != null);", nPos:4 });
//Link to the company website
app.addMenuItem({ cName:"RSCsite", cUser:"Website", cParent:"RSCivilTools",
cExec:"app.launchURL('http://rscivil.com');", nPos:5 });
//Add a button for this function - Add from Quick Tools 3rd party addons
app.addToolButton({
cName: "RSCcreateNestButton",
cExec: "createNest();",
cTooltext: "Create Nest",
cEnable: true,
nPos: 0,
cLabel: "Create Nest"
})
//Add a button for this function - Add from Quick Tools 3rd party addons
app.addToolButton({
cName: "promoteLayers",
cExec: "promoteOCG_handler(event.target);",
cTooltext: "Undo Nest",
cEnable: true,
nPos: 1,
cLabel: "Undo Nest"
})
//Creates a nested layer from a predetermined list
function createNest(){
var layers = this.getOCGs();
var newOrder = new Array();
var cityLimits = new Array();
var comps = new Array();
/*******************************************************
This is where I need help.
By not setting the first element to a String it does not name the group. Instead it seems to attach it as a child to the previous element in the array.
This is fine, except that when you turn off said parent it does not turn off the sub layers/OCGs.
If I could figure out how to create an object and set it up as an element before my inserted array it might help.
*******************************************************/
//Commented for testing - Set first element of the array to a descriptive string (this will be the name of the nested group)
//cityLimits[0] = "City Limits";
//Set first element of the array to a descriptive string (this will be the name of the nested group)
comps[0] = "Comps";
//Separate all layers/OCGs containing "CityLimits|" or "COMP" from the original list of layers/OCGs
for (var i=0,j=0,k=0,l=1; i<layers.length; i++){
if(layers[i].name.substr(0,11)==="CityLimits|"){
cityLimits[j] = layers[i]; //separate CityLimits OCG
j++;
}
else if(layers[i].name.substr(0,4)==="COMP"){
comps[l] = layers[i]; //separate COMP OCG
l++;
}
else{
newOrder[k] = layers[i]; //cram everything else into a new array
k++;
}
}
//Insert the cityLimits array into the newOrder array at position 5, do not remove any elements (the element before this arbitrarily becomes the parent, but does not work correctly)
newOrder.splice(5,0,cityLimits);
//Append the comps array to the end of the newOrder array
newOrder[newOrder.length] = comps;
//set the newOrder array as the OCGOrder.
this.setOCGOrder(newOrder);
}
//Code by Dave Merchant
//Handler for promoting OCGs out of nested layers
function promoteOCG_handler(oDoc) {
var ocgOrder = oDoc.getOCGOrder();
var hasNest = false;
if (ocgOrder==null) {
app.alert( "No layers in current file", 0, 0, "Cannot proceed");
} else {
for (var i=0; i<ocgOrder.length; i++) {
if ((typeof(ocgOrder[i]) == "object") && (ocgOrder[i].length > 0)) hasNest = true;
}
if (hasNest) {
promoteOCGs(oDoc,ocgOrder);
} else app.alert( "No nested layers in current file", 0, 0, "Cannot proceed");
}
}
//Code by Dave Merchant
//Promote/unravel layers/OCG out of nests
function promoteOCGs(oDoc,ocgOrder) {
// Removes the top-level OCG nest structure from a PDF, promoting all sub-OCGs to the top level.
// Used to remove the nesting created when a PDF is exported from InDesign with "Create Acrobat Layers" checked.
var oChk = { cMsg:"Unlist the 'Guides and Grids' layer?", bInitialValue:true, bAfterValue:false};
//var cMesg = "This action will ungroup all nested layers. IT CANNOT BE UNDONE.\n\nDo you want to continue?";
//var nRtn = app.alert({ cMsg:cMesg, nIcon:2, nType:2, cTitle:"Promote nested layers", oCheckbox:oChk});
//if (nRtn == 4) {
var newOrder = new Array();
for (var i=0; i<ocgOrder.length; i++) {
var oType = typeof(ocgOrder[i]);
var oLeng = ocgOrder[i].length
if ((oType == "object") && (oLeng > 0)) { // it's a nest, do the promotions
for (var j=0; j<oLeng; j++) {
if ((typeof(ocgOrder[i][j]) == "object") &&
(!oChk.bAfterValue || (ocgOrder[i][j].name != "Guides and Grids"))) newOrder.push(ocgOrder[i][j]);
}
} else if (!oChk.bAfterValue || (ocgOrder[i].name != "Guides and Grids")) newOrder.push(ocgOrder[i]);
}
oDoc.setOCGOrder( newOrder );
//}
}
//Code by Dave Merchant
// Removes the listing for (sub)OCG named "Guides and Grids".
// Does NOT delete the layer, simply hides it from the sidebar display.
function removeGAG(oDoc) {
var cMesg = "This action will unlist the 'Guides and Grids' layer from the sidebar but will NOT delete the ";
cMesg += "layer itself. IT CANNOT BE UNDONE.\n\nDo you want to continue?";
var nRtn = app.alert(cMesg, 2, 2, "Unlist 'Guides and Grids'");
if (nRtn == 4) {
var ocgOrder = oDoc.getOCGOrder();
var newOrder = new Array();
for (var i=0; i<ocgOrder.length; i++) {
var oType = typeof(ocgOrder[i]);
var oLeng = ocgOrder[i].length
if ((oType == "object") && (oLeng > 0)) { // it's a nest
var subObj = new Array();
for (var j=0; j<oLeng; j++) {
if ((typeof(ocgOrder[i][j]) == "string") || (ocgOrder[i][j].name != "Guides and Grids")) subObj.push(ocgOrder[i][j]);
}
newOrder.push(subObj);
} else if (ocgOrder[i].name != "Guides and Grids") newOrder.push(ocgOrder[i]);
}
oDoc.setOCGOrder( newOrder );
}
}
//Just a list of layers I want to toggle on or off (document specific)
function togList(name){
if(name.substr(0,11)==="CityLimits|")
return true;
return false;
}
//Just a list of layers I want to set an initial state to off (document specific)
function offList(name){
var lOff = new Array();
lOff[0] = "ADT";
lOff[1] = "CityLimits|1900";
lOff[2] = "CityLimits|1910";
lOff[3] = "CityLimits|1920";
lOff[4] = "CityLimits|1930";
lOff[5] = "CityLimits|1940";
lOff[6] = "CityLimits|1950";
lOff[7] = "CityLimits|1960";
lOff[8] = "CityLimits|1970";
lOff[9] = "CityLimits|1975";
lOff[10] = "CityLimits|1980";
lOff[11] = "CityLimits|1985";
lOff[12] = "CityLimits|1990";
lOff[13] = "CityLimits|1995";
lOff[14] = "CityLimits|2000";
lOff[15] = "CityLimits|2005";
lOff[16] = "CityLimits|2006";
lOff[17] = "CityLimits|2007";
lOff[18] = "CityLimits|2008";
lOff[19] = "CityLimits|2009";
lOff[20] = "CityLimits|2010";
lOff[21] = "CityLimits|2011";
lOff[29] = "Distance Circles 1";
lOff[30] = "Distance Circles 2";
lOff[31] = "landuse|Agriculture";
lOff[32] = "landuse|Industrial";
lOff[33] = "Landmark Labels Locations";
lOff[34] = "landmarks|Locations";
for (var i=0; i<lOff.length; i++){
if(lOff[i] === name)
return true;
}
return false;
}
//Checks all layers against a list and toggles them off or on (document specific)
function toggleCityLimits(){
var layers = this.getOCGs();
for (var i=0; i<layers.length; i++){
var tog = true;
if(togList(layers[i].name)){
if(layers[i].state)
tog = false;
layers[i].state = tog; //toggle layer
}
}
}
//Checks all layers against a list and sets the initial state and current state to off (state=visibility) (document specific)
function setStates(){
var layers = this.getOCGs();
for (var i=0; i<layers.length; i++){
var tog = true;
if(offList(layers[i].name))
tog = false;
layers[i].state = tog; //turn off layer
layers[i].initState = tog; //set initial visibilty to off
}
}