Make OCG layer visible when field isn't empty? - pdf

Is it possible to check when a field is not empty and if not, make an ocg layer visible?
var ocg = FindOCG("Item 1 Arrow");
if (+event.value === '') {
ocg.state = !ocg.state;
} else {
ocg.state = !ocg.state;
}
Something like this (which doesn't work)!

Put this in the custom format script of the field in question. Replace "Square" with the name of your layer. You can see an example of it working here.
function getOCGByName(cName, nPage) {
var ocg = null;
var ocgArray = this.getOCGs(nPage);
for (var i=0; i < ocgArray.length; i++) {
if (ocgArray[i].name == cName) {
ocg = ocgArray[i];
}
}
return ocg;
}
var field = event.target;
var ocg = getOCGByName("Square", this.pageNum);
if (field.value.length > 0) {
ocg.state = true;
}
else {
ocg.state = false;
}
Note: This will only work in Adobe Acrobat and Reader and a few other viewers that are JavaScript capable.

Related

Photoshop Scripting: Relink Smart Object

I'm working on a script that should go through a photoshop document and relink all visible linked objects to a new specified file. I've gotten the loop working so that it cycles through every layer and collects only the visible layers, but for the life of me I can't find if there's a method available to relink a smart object. The closest I've found is this script:
https://gist.github.com/laryn/0a1f6bf0dab5b713395a835f9bfa805c
but when it gets to desc3.putPath(idnull, new File(newFile));, it spits out an error indicating that the functionality may not be present in the current Photoshop version. The script itself is 4 years old so it may be out of date.
Any help would be appreciated!
MY script as it stands is below:
// SELECT FILE //
var files = File.openDialog("Please select new linked file");
var selectedFile = files[0];
// GET ALL LAYERS //
var doc = app.activeDocument;
var allLayers = [];
var allLayers = collectAllLayers(doc, allLayers);
function collectAllLayers (doc, allLayers)
{
for (var m = 0; m < doc.layers.length; m++)
{
var theLayer = doc.layers[m];
if (theLayer.typename === "ArtLayer")
{
allLayers.push(theLayer);
}
else
{
collectAllLayers(theLayer, allLayers);
}
}
return allLayers;
}
// GET VISIBLE LAYERS //
var visibleLayers = [];
for (i = 0; i < allLayers.length; i++)
{
var layer = allLayers[i];
if (layer.visible && layer.kind == LayerKind.SMARTOBJECT)
{
visibleLayers.push(layer);
}
}
// REPLACE LAYERS
for (i = 0; i < visibleLayers.length; i++)
{
var layer = visibleLayers[i];
//--> REPLACE THE FILE HERE
}
Note: I am aware that this script currently may be error-prone if you don't know exactly how it works; I'm not intending to publish it at this time so I'm not super concerned with that at the moment. Mostly I just need the core functionality to work.
I used an AM function for getting visible smart objects — it works much faster. But if you want you can use yours. The important bit is relinkSO(path);: it'll also work in your script (just don't forget to select a layer: activeDocument.activeLayer = visibleLayers[i];)
Note that it works similar to Photoshop Relink to File command — if used on one instance of Smart Object all the instances are going to be relinked. If you want to relink only specific layers you'll have to break instancing first (probably using the New Smart Object via Copy command)
function main() {
var myFile = Folder.myDocuments.openDlg('Load file', undefined, false);
if (myFile == null) return false;
// gets IDs of all smart objects
var lyrs = getLyrs();
for (var i = 0; i < lyrs.length; i++) {
// for each SO id...
// select it
selectById(lyrs[i]);
// relink SO to file
relinkSO(myFile);
// embed linked if you want
embedLinked()
}
function getLyrs() {
var ids = [];
var layers, desc, vis, type, id;
try
{
activeDocument.backgroundLayer;
layers = 0;
}
catch (e)
{
layers = 1;
}
while (true)
{
ref = new ActionReference();
ref.putIndex(charIDToTypeID('Lyr '), layers);
try
{
desc = executeActionGet(ref);
}
catch (err)
{
break;
}
vis = desc.getBoolean(charIDToTypeID("Vsbl"));
type = desc.getInteger(stringIDToTypeID("layerKind"));
id = desc.getInteger(stringIDToTypeID("layerID"));
if (type == 5 && vis) ids.push(id);
layers++;
}
return ids;
} // end of getLyrs()
function selectById(id) {
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putIdentifier(charIDToTypeID('Lyr '), id);
desc.putReference(charIDToTypeID('null'), ref);
executeAction(charIDToTypeID('slct'), desc, DialogModes.NO);
} // end of selectById()
function relinkSO(path) {
var desc = new ActionDescriptor();
desc.putPath( charIDToTypeID('null'), new File( path ) );
executeAction( stringIDToTypeID('placedLayerRelinkToFile'), desc, DialogModes.NO );
} // end of relinkSO()
function embedLinked() {
executeAction( stringIDToTypeID('placedLayerConvertToEmbedded'), undefined, DialogModes.NO );
} // end of embedLinked()
}
app.activeDocument.suspendHistory("relink SOs", "main()");

Run last Photoshop script (again)

This seems like a trivial issue but I'm not sure Photoshop supports this type of functionality:
Is it possible to implement use last script functionality?
That is without having to add a function on each and every script that writes it's filename to a text file.
Well... It's a bit klunky, but I suppose you could read in the scriptlistener in reverse order and find the first mention of a script file:
// Switch off any dialog boxes
displayDialogs = DialogModes.NO; // OFF
var scripts_folder = "D:\\PS_scripts";
var js = "C:\\Users\\GhoulFool\\Desktop\\ScriptingListenerJS.log";
var jsLog = read_file(js);
var lastScript = process_file(jsLog);
// use function to call scripts
callScript(lastScript)
// Set Display Dialogs back to normal
displayDialogs = DialogModes.ALL; // NORMAL
function callScript (ascript)
{
eval('//#include "' + ascript + '";\r');
}
function process_file(afile)
{
var needle = ".jsx";
var msg = "";
// Let's do this backwards
for (var i = afile.length-1; i>=0; i--)
{
var str = afile[i];
if(str.indexOf(needle) > 0)
{
var regEx = str.replace(/(.+new\sFile\(\s")(.+\.jsx)(.+)/gim, "$2");
if (regEx != null)
{
return regEx;
}
}
}
}
function read_file(inFile)
{
var theFile = new File(inFile);
//read in file
var lines = new Array();
var l = 0;
var txtFile = new File(theFile);
txtFile.open('r');
var str = "";
while(!txtFile.eof)
{
var line = txtFile.readln();
if (line != null && line.length >0)
{
lines[l++] = line;
}
}
txtFile.close();
return lines;
}

How to get resource names for optional content group in a pdf?

I am trying to implement functionality to allow user to add markups to existing layers in a pdf. Here is the code that I am using to draw lines on to a layer in a pdf:
PDResources resources = page.findResources();
PDPropertyList props = resources.getProperties();
COSName resourceName = getLayerResourceName("Superimposed3", resources, props);
PDPageContentStream cs1 = new PDPageContentStream(document, page, true, false);
cs1.beginMarkedContentSequence(COSName.OC, resourceName);
cs1.setStrokingColor(0, 0, 255);
cs1.setLineWidth(0.8F);
cs1.drawLine(100,100,250,200);
cs1.endMarkedContentSequence();
cs1.close();
I am using beginMarkedContentSequence instead of beginMarkedContent as I am using pdfbox 1.8 version and 2.0.5 version is not available for .net.
Here, is my function to get resource name for a layer:
private static COSName getLayerResourceName(string layerName,PDResources resources,PDPropertyList props)
{
int index = 0;
COSName resourceName = COSName.getPDFName("MC"+ index);
PDOptionalContentGroup ocg = props.getOptionalContentGroup(resourceName);
if (ocg != null && (ocg.getName() == layerName))
{
return resourceName;
}
else if (ocg == null)
{
return null;
}
else
{
resourceName = null;
index++;
bool exitFlag = false;
while (!exitFlag)
{
resourceName = COSName.getPDFName("MC" + index);
ocg = props.getOptionalContentGroup(resourceName);
if (ocg == null)
{
exitFlag = true;
resourceName = null;
}
else if (ocg.getName() == layerName)
{
exitFlag = true;
}
else
{
index++;
}
}
}
return resourceName;
}
The above functions only works for those layers which were added using the LayerUtility.appendFormAsLayer function. But it doesn't work for those layers which were created using the following code:
PDOptionalContentProperties ocprops = document.getDocumentCatalog().getOCProperties();
PDOptionalContentGroup newGroup = new PDOptionalContentGroup("Superimposed2");
PDOptionalContentGroup newGroup1 = new PDOptionalContentGroup("Superimposed3");
ocprops.addGroup(newGroup);
ocprops.addGroup(newGroup1);
So, shall I add "MC" value in properties of page myself while, creating layer, or is there another way to get resource name for such layers.

JSFL - Adding a movieclip to a specific layer and frame

I am trying to replace an outdated movieclip with a newer one.
To do this I'm usin JSFL to locate the old movieclips, save a reference, then add the new version in its place.
I have looked at addItem addItemToDocument and they successfully add the clip, but I'm unsure of how to add it to the specific layer and frame that the old instance of the movieclip was on.
Halps
Replacing all instances of the old movieclip with instances of the new movieclip, could be an easier solution.
All instances of the old movieclip can be found by parsing the timelines of the flash document.
Here is some code:
var _doc = (fl.getDocumentDOM() ? fl.getDocumentDOM() : fl.createDocument());
var _lib = _doc.library;
fl.outputPanel.clear();
ReplaceItemWithItem('Game Layouts/card holder', 'Game Layouts/card holder new');
function ReplaceItemWithItem(oldmcname, newmcname)
{
var item1 = GetItem(oldmcname);
var item2 = GetItem(newmcname);
if (!item1) return false;
if (!item2) return false;
if (oldmcname == newmcname)
return true;
return ReplaceAllItems(item1, item2);
}
function ReplaceAllItems(item1, item2)
{
var timelines = _doc.timelines;
var i, l = timelines.length;
var items = _lib.items;
var changed = false;
// Main timelines
for (i = 0; i < l; i++)
{
var timeline = timelines[i];
changed |= ReplaceItems(timeline, item1, item2);
}
// Timelines in library items
for (i = 0, l = items.length; i < l; i++)
{
var item = items[i];
switch (item.itemType)
{
case "movie clip":
case "graphic":
case "button":
changed |= ReplaceItems(item.timeline, item1, item2);
break;
}
}
return changed;
}
function ReplaceItems(timeline, item1, item2)
{
var changed = false;
if (timeline && item1 && item2)
{
var layers = timeline.layers;
var lay, layl = layers.length;
for (lay = 0; lay < layl; lay++)
{
var layer = layers[lay];
var frames = layer.frames;
var fr, frl = frames.length;
for (fr = 0; fr < frl; fr++)
{
var frame = frames[fr];
if (frame && frame.startFrame == fr)
{
var elements = frame.elements;
var e, el = elements.length;
for (e = 0; e < el; e++)
{
var elem = elements[e];
if (elem && elem.elementType == "instance") // Elements can be empty
{
var item = elem.libraryItem;
if (item.name == item1.name)
{
elem.libraryItem = item2;
changed = true;
}
}
}
}
}
}
}
return changed;
}
function GetItem(itemname)
{
if (!_lib.selectItem(itemname))
{
alert("'" + name + "' does not exist in the library!");
return null;
}
return _lib.getSelectedItems()[0];
}
Hope this helps!

InDesign copy layer to another document

I am trying to write a script that copies a layer from one document into another.
var srcDocName = 0;
var destDocName = 1;
var layerNameOriginal = "Original";
var layerNameCopyTo = "Destination";
var destDoc = app.documents.item(destDocName);
var layerSrc = app.documents.item(srcDocName).layers.item(layerNameOriginal);
try {
layerSrc.duplicate(destDoc, ElementPlacement.INSIDE);
}
catch(e) {
alert(e)
}
Apparently this works in Photoshop but not in InDesign. I have been trying for ages to find some decent documentation for InDesign scripting. But all I can find is the CS scripting guide, which isn't of much use.
http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/products/indesign/pdfs/InDesignCS5_ScriptingGuide_JS.pdf
If someone can point me to a good reference to the object model I would be grateful.
After some more googling I finally found the answer:
var sourceLayer = app.documents[0].layers.itemByName("Layer1");
var destLayer = app.documents[1].layers[0];
sourceLayer.pageItems.everyItem().duplicate(destLayer);
I also came across jongware which seems to be a complete Object reference extracted directly out of Adobe CS.
You can use this script: https://redokun.com/blog/indesign-copy-entire-layer-one-file-another
The underlying implementation is basically the same, but we've added a UI so it's not necessary to edit the script every time the layer name changes.
Edit: We've been told that the solution above doesn't work with threaded text frames, so I re-wrote the script. The new implementation is way more complex but it now supports threaded TFs.
To expand on the solution offered by Loopo and offer you the ability to copy all layers from 1 document to another...
main();
function main()
{
var source = GetSourceDocument();
if(source == -1)
{
return;
}
var target = GetTargetDocument ();
if(target == -1)
{
return;
}
if(target == source)
{
return;
}
copyLayersOver(source, target);
}
function GetSourceDocument()
{
var returnVal = -1;
var oldPrefs = app.scriptPreferences.userInteractionLevel;
app.scriptPreferences.userInteractionLevel=UserInteractionLevels.INTERACT_WITH_ALL;
var dialog = app.dialogs.add({name:"Document to Copy From", canCanel: true, label:"DocumentToCopyFrom"});
var col1 = dialog.dialogColumns.add();
var StringList= [];
for(var i = 0; i<app.documents.length; i++)
{
StringList.push("[" + app.documents[i].index + "] " + app.documents[i].name);
}
var ddl = col1.dropdowns.add({id:"SourceDocDDL", stringList: StringList});
if(dialog.show() == true)
{
returnVal = ddl.stringList[ddl.selectedIndex].split("]")[0].substr(1);
}
else
{
returnVal -1;
}
dialog.destroy();
app.scriptPreferences.userInteractionLevel = oldPrefs;
return returnVal;
}
function GetTargetDocument()
{
var returnVal = -1;
var oldPrefs = app.scriptPreferences.userInteractionLevel;
app.scriptPreferences.userInteractionLevel=UserInteractionLevels.INTERACT_WITH_ALL;
var dialog = app.dialogs.add({name:"Document to Copy To", canCanel: true, label:"DocumentToCopyTo"});
var col1 = dialog.dialogColumns.add();
var StringList= [];
for(var i = 0; i<app.documents.length; i++)
{
StringList.push("[" + app.documents[i].index + "] " + app.documents[i].name);
}
var ddl = col1.dropdowns.add({id:"SourceDocDDL", stringList: StringList});
if(dialog.show() == true)
{
returnVal = ddl.stringList[ddl.selectedIndex].split("]")[0].substr(1);
}
else
{
returnVal -1;
}
dialog.destroy();
app.scriptPreferences.userInteractionLevel = oldPrefs;
return returnVal;
}
function copyLayersOver(source, target)
{
var sourceDocument = app.documents[source];
var targetDocument = app.documents[target];
var sourceLayers = sourceDocument.layers;
//Match the number of pages
while(targetDocument.pages.length < sourceDocument.pages.length)
{
targetDocument.pages.add();
}
//copy the layers over
for(var i= 0; i < sourceLayers.length; i++)
{
var names = targetDocument.layers.everyItem().name;
var merge = false;
for(var y = 0; y < names.length; y++)
{
if(names[y] == sourceLayers[i].name)
{
merge = true;
break;
}
}
if(merge)
{
var targetLayer = targetDocument.layers.add();
targetLayer.name = "temp";
sourceLayers[i].pageItems.everyItem().duplicate(targetLayer);
targetDocument.layers.itemByName(sourceLayers[i].name).merge(targetLayer);
}
else
{
var targetLayer = targetDocument.layers.add();
targetLayer.name = sourceLayers[i].name;
targetLayer.layerColor = sourceLayers[i].layerColor;
sourceLayers[i].pageItems.everyItem().duplicate(targetLayer);
}
}
}