How to clean existing properties and replace with metadata template on Photoshop (scripting)? - scripting

While creating a script that would automate all the different tasks I do when I start working on a new picture on Photoshop, I encountered the following problem.
Manually, I would Ctrl + Alt + Shift + I, click on the template I want and choose the option "Clear existing properties and replace with template properties".
I can't find the way to do precisely this. The best thing I managed to find is something like this :
app.activeDocument.info.author = "test";
app.activeDocument.info.caption = "";
app.activeDocument.info.captionWriter = "";
app.activeDocument.info.headline = "";
app.activeDocument.info.instructions = "";
app.activeDocument.info.keywords = "";
app.activeDocument.info.authorPosition = "";
app.activeDocument.info.credit = "";
app.activeDocument.info.source = "";
app.activeDocument.info.category = "";
app.activeDocument.info.supplementalCategories = "";
app.activeDocument.info.title = "";
// etc.
And it actually doesn't really work like the "Clear existing properties and replace with template properties".
I didn't find anything on the Photoshop scripting guide, nor on the internet. Any help would be greatly appreciated !

What I think is the problem is Photoshop separates file-metadata from its activeDocument-metadata. What you see in "File info..." (via Ctrl+Alt+Shift+I) is supposed to represent the file in the filesystem, which metadata is embedded in.
There are several scripting guides to Photoshop scripting. I think the one relevant for you would be "Javascript Tools Guide", specifically the chapter 10 "Scripting Access to XMP Metadata".
Is it important for you to set up the metadata already when creating a new picture? If not, you may want to look at a solution using a customized export script.
It customizes XMP-metadata upon exporting like
Create a basic metadata object:
var meta = new XMPMeta();
Provide a namespaceURI (see XMP specs) known to photoshop along with tag name, and value:
meta.setProperty(XMPConst.NS_XMP, "CreatorTool", app.version);
Save the image temporarily (using other script):
var imgFile = new File(fileName);
saveImage(fileName);
Finish saving by adding the metadata-object:
var metaFile = new XMPFile(imgFile.fsName, XMPConst.FILE_UNKNOWN, XMPConst.OPEN_FOR_UPDATE);
if (metaFile.canPutXMP(meta)) { metaFile.putXMP(meta); }
metaFile.closeFile(XMPConst.CLOSE_UPDATE_SAFELY);
Doing it this way also erases any existing or default metadata.

Related

Splitting pages within a PDF with ArcoJS / Acrobat JS with a given array of names

So, I am super new to using the JS interface within Acrobat and I am trying to write something for splitting PDF pages easily to an array of file names. I cannot find a lot of snippets around that seems to show me how to work with Acrobat JS. Can you provide some guidance on how a script similar to such will look like and how I can execute it within Acrobat? Thanks!
First, you will need Acrobat Professional or Standard for JS tasks generally. And you will execute code like you do within your normal terminal/immediates window within what they call the debugger tool for Javascript. You will need to first activate JS within Acrobat by going to Preferences and activate the Debugger. After you set the preferences, restart Acrobat, and find the tools for the Javascript debugger (different places based on your version, google it if you can't find it).
Once you get the debugger running, run the code below after modifying for the file names you wish to use and the appropriate file paths. Then highlight the entire code block and hit Ctrl+Enter and it will automatically split the pages for you. Enjoy.
Split();
function Split() {
var totalPages = this.numPages;
var i;
var arrNames = [ "SOME ARRAY" ];
var targetPath = "/C/Users/...SOMEPATH/";
try {
for (i = 0; i < totalPages; i++) {
this.extractPages({
nStart: i,
cPath: targetPath +arrNames[i] + ".pdf"
});
console.println("Completed: " + targetPath + arrNames[i] + ".pdf");
}
} catch (e) {
console.println("Aborted: " + e);
}
}

Adobe pdf printer doesn't creating the pdf file

I'm creating an add-in in Revit 2017. The addin will export drawing sheets to PDF files. So, whenever I try to export the sheet a dialog box appears to choose the location to save. I tried to turn off the Prompting programmatically by adding a key to the Windows registry (as described in Adobe documentation page 15-16).
Now, the prompting got turned off and now I'm facing an issue. The issue is the Adobe Printer got stuck while creating the pdf file. See the below image: The PDF creating progress bar seems frozen, I waited for more than 10 mins and it didn't create the pdf file.
Can anybody provide any fix?
Appreciate any suggestion.
Edit
here's the code that I've written for this purpose. I hope this may help to identify the problem.
public static bool ExportSheetToPDF(Document doc, string path)
{
using (Transaction tx = new Transaction(doc)
{
tx.Start("Exportint to PDF");
PrintManager pm = doc.PrintManager;
pm.SelectNewPrintDriver("Adobe PDF");
pm.Apply();
pm.PrintRange = PrintRange.Current;
pm.Apply();
pm.CombinedFile = true;
pm.Apply();
pm.PrintToFile = true;
pm.Apply();
pm.PrintToFileName = path + #"\PDF\" + "abc.pdf";
pm.Apply();
SuppressAdobeDialogAndSaveFilePath(path + #"\PDF\" + "abc.pdf");
pm.SubmitPrint();
pm.Apply();
tx.Commit();
}
return true;
}
// Add Registry Key to suppress the dialog box
public static void SuppressAdobeDialogAndSaveFilePath(string value)
{
var valueName = #"C:\Program Files\Autodesk\Revit 2017\Revit.exe";
var reg = currentUser.OpenSubKey(key, true);
var tempReg = reg.OpenSubKey(valueName);
if (tempReg == null)
{
reg = reg.CreateSubKey(valueName);
}
reg.SetValue(valueName, value);
reg.Close();
}
I have explained how you can achieve this by overriding the registry key for Revit.exe process that Adobe uses to generate the next print.
http://archi-lab.net/printing-pdfs-from-revit-why-is-it-so-hard/
Please remember that you still have to print via Revit PrintManager, but then you can set the registry keys before every print to control where the files get saved.

Illustrator variables - dynamically line up two text strings next to each other when autogenerating

I am automating the generation of several thousand labels in Adobe Illustrator. The use of the VariableImporter script has made easy work of it so far, but now I have reached an issue where I am stumped. The original plan worked great, until the powers that be requested that one line of text have a bold text string, followed by a normal weight text string. Before, when the font weights were the same I could have connected the two strings of text in the CSV file prior to loading them into the drawing, and they would have came out lying right next to each other. This is now no longer possible and I can't think of a solution that is not incredibly fussy.
I don't know illustrator very well, so I am thinking I could just be unaware of some setting that would stick an object next to another one even as the other one moves.
Okay here is the way I figured out how to do this with help from Adobe forums and from Vasily.
First of all, use InDesign if possible. It is better at performing a Data Merge and can do this without your scripting.
Write out <variable1> <variable2> which is formatted as needed on the same line of text.
You will need to have the variables that you are putting in there somewhere in the illustration. Recommended to put it in a hidden layer behind everything.
replace variable1 and variable2 with the names of your variables where the functions getVariableContents() are called in this script
var idoc = app.activeDocument;
var vars = idoc.variables;
var replace1 = /<variable1>/g;
var replace2 = /<variable2>/g;
// author CarlosCanto on adobe forums
function getVariableContents(variableName) {
var idoc = app.activeDocument;
var ivar = idoc.variables.getByName(variableName);
return ivar.pageItems[0].contents;
}
var replaceWith1 = getVariableContents('variable1'), result;
var replaceWith2 = getVariableContents('variable2'), result;
// regex_changeContentsOfWordOrString_RemainFormatting.jsx
// regards pixxxel schubser
function exchangeWords(s, replacer) {
var s = s;
var replacer = replacer;
var atfs = activeDocument.textFrames;
for (var i = atfs.length - 1; i >= 0; i--) {
atf = atfs[i];
while (result = s.exec(atf.contents)) {
try {
aCon = atf.characters[result.index];
aCon.length = result[0].length;
aCon.contents = aCon.contents.replace(s, replacer);
} catch (e) {};
}
}
}
exchangeWords(replace1,replaceWith1);
exchangeWords(replace2,replaceWith2);
run the script
There is a way to accomplish this by having a script do some processing during the course of your batch output, and an organizational system which adds some overhead to your file, in terms of adding more text boxes and possibly an extra layer to your document. But - here's what you can have: a hidden layer with all your variables there in separate single point-text objects, and a layer with your regular template objects such as any point text or area-text objects. Your art text objects will need to be re-worked to contain a string with multiple variable placeholders like this: "Hello, <FirstName> <LastName>". The placeholders can be styled, and a processing script would then need to replace the <placeholder> words with your real variable values. Where are the varible values? They are going to be populating into your hidden layer which has your separate text objects and the script would need to read the contents of each of those to put into the <placeholders>. ~~Those same text fields can be styled as you wish, and the script could apply the same styles to your text when it is replaced in the main text body.~~ -actually this won't be necessary of your routine backs up the original text frame with the placeholder in it, therefore preserving the styling, but it may be necessary if you are going to instead use an external text file to keep your original text in. And of course, it will need to make a backup of the original text with all the <placeholders> so that it will reset the text for every new dataset during your batch process.
However, this is much easier done in Indesign, can you not use ID for your task?
I modified script from #tucker-david-grebitus's answer. So now it gets all textual variables and replaces all their names edged by percent symbol
for (var i = activeDocument.variables.length - 1; i >= 0; i -= 1) {
var variable = activeDocument.variables[i];
if (variable.kind !== VariableKind.TEXTUAL || !variable.pageItems.length) {
continue;
}
var search = new RegExp('%' + variable.name + '%', 'g');
var value = variable.pageItems[0].contents;
for (var j = activeDocument.textFrames.length - 1; j >= 0; j -= 1) {
var textFrame = activeDocument.textFrames[j];
textFrame.contents = textFrame.contents.replace(search, value);
}
}

How to check multiple PDF files for annotations/comments?

Problem: I routinely receive PDF reports and annotate (highlight etc.) some of them. I had the bad habit of saving the annotated PDFs together with the non-annotated PDFs. I now have hundreds of PDF files in the same folder, some annotated and some not. Is there a way to check every PDF file for annotations and copy only the annotated ones to a new folder?
Thanks a lot!
I'm on Win 7 64bit, I have Adobe Acrobat XI installed and I'm able to do some beginner coding in Python and Javascript
Please ignore the following suggestion, since the answers already solved the problem.
EDIT: Following Mr. Wyss' suggestion, I created the following code for Acrobat's Javascript console to be run only once at the beginning:
counter = 1;
// Open a new report
var rep = new Report();
rep.size = 1.2;
rep.color = color.blue;
rep.writeText("Files WITH Annotations");
Then this code should be applied to all PDFs:
this.syncAnnotScan();
annots = this.getAnnots();
path = this.path;
if (annots) {
rep.color = color.black;
rep.writeText(" ");
rep.writeText(counter.toString()+"- "+path);
rep.writeText(" ");
if (counter% 20 == 0) {
rep.breakPage();
}
counter++;
}
And, at last, one code to be run only once at the end:
//Now open the report
var docRep = rep.open("files_with_annots.pdf");
There are two problems with this solution:
1. The "Action Wizard" seems to always apply the same code afresh to each PDF (that means that the "counter" variable, for instance, is meaningless; it will always be = 1. But more importantly, var "rep" will be unassigned when the middle code is run on different PDFs).
2. How can I make the codes that should be run only once run only at the beginning or at the end, instead of running everytime for every single PDF (like it does by default)?
Thank you very much again for your help!
This would be possible using the Action Wizard to put together an action.
The function to determine whether there are annotations in the document would be done in Acrobat JavaScript. Roughly, the core function would look like this:
this.syncAnnotScan() ; // updates all annots
var myAnnots = this.getAnnots() ;
if (myAnnots != null) {
// do something if there are annots
} else {
// do something if there are no annots
}
And that should get you there.
I am not completely positive, but I think there is also a Preflight check which tells you whether there are annotations in the document. If so, you would create a Preflight droplet, which would sort out the annotated and not annotated documents.
Mr. Wyss is right, here's a step-by-step guide:
In Acrobat XI Pro, go to the 'Tools' panel on the right side
Click on the 'Action Wizard' tab (you must first make it visible, though)
Click on 'Create New Action...', choose 'More tools' > 'Execute Javascript' and add it to right-hand pane > click on 'Execute Javascript' > 'Specify Settings' (uncheck 'prompt user' if you want) > paste this code:
.
this.syncAnnotScan();
var annots = this.getAnnots();
var fname = this.documentFileName;
fname = fname.replace(",", ";");
var errormsg = "";
if (annots) {
try {
this.saveAs({
cPath: "/c/folder/"+fname,
bPromptToOverwrite: false //make this 'true' if you want to be prompted on overwrites
});
} catch(e) {
for (var i in e)
{errormsg+= (i + ": " + e[i]+ " / ");}
app.alert({
cMsg: "Error! Unable to save the file under this name ('"+fname+"'- possibly an unicode string?) See this: "+errormsg,
cTitle: "Damn you Acrobat"
});
}
;}
annots = 0;
Save and run it! All your annotated PDFs will be saved to 'c:\folder' (but only if this folder already exists!)
Be sure to enable first Javascript in 'Edit' > 'Preferences...' > 'Javascript' > 'Enable Acrobat Javascript'.
VERY IMPORTANT: Acrobat's JS has a bug that doesn't allow Docs to be saved with commas (",") in their names (e.g., "Meeting with suppliers, May 11th.pdf" - this will get an error). Therefore, I substitute in the code above all "," for ";".

How to Download PDF Links in Column and Save to Common Folder

We have a column that contains links to PDFs that starts on line 4 (e.g B4:B). I am trying to find a way to automatically download the PDF files that are accessed via the links to a folder on Drive. This is what I have so far:
function savePDFs() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 3; i < data.length; i++) {
Logger.log(data[i][1]);
}
}
Presumably the above code would write the links starting in column B (index value of [1]) on row 4 (i value of 3) (ie., B4) until the bottom of the data set (eg., data.length()).
I'm now confused about how to access and save the PDF link that are written in the logger to a folder.
Would someone be willing to help me out? I'm currently having to go to each link, click Save Link As... and then navigate to the folder that I'd like to save the linked PDF to. My hope is to modify the above process using code.
Update: I found this bit of code here that may help me out. Note, I changed the PDF link to a currently valid PDF link.
var urlOfThePdf = 'http://download.p4c.philips.com/l4b/9/929000277411_eu/929000277411_eu_pss_aenaa.pdf';// an example of online pdf file
var folderName = 'GAS';// an example of folder name
function saveInDriveFolder(){
var folder = DocsList.getFolder(folderName);// get the folder
var file = UrlFetchApp.fetch(urlOfThePdf); // get the file content as blob
folder.createFile(file);//create the file directly in the folder
}
Okay, I'm going to go and noodle with the data that is in the logger to confirm that the data is in properly formatted PDF links, then I'm going to test this new bit of code out. I feel like I'm getting close.
You can't force a download of a file from an apps script, you must try that from an HTMLService and not sure it will work.
For your need I would recommend to create a dedicated folder and you add all the pdf in it and you use the download function of the drive interface to download all files in one clic.
In drive, a file can be put in several folders so the pdf files stay in the original folder but you create a new folder 'PDF for download" for example and you put them in it. To do that from drive interface you have to click on "shift"+Z when file(s) is/are selected.
For you current list of file you just have to add in your loop the add to folder function. You can use this function.
function addFileToFolder(id){
var folderPDF = DriveApp.getFolderById("Id OFFolder to put pdf");
var file = DriveApp.getFileById(id);
folderPDF.addFile(file);
}
EDIT : Function will browse list of url, get the file and make a copy in a dedicated folder on the user drive.
function downloadInDriveFolder(){
var folderID = 'Id of the folder';// put id of the folder
var folder = DriveApp.getFolderById(folderID)// get the folder
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 3; i < data.length; i++) {
var blob = UrlFetchApp.fetch(data[i][1]).getContent();
var pdf = DriveApp.createFile(blob);
pdf.setName(data[i][0]);//Put as name of the file the value in col A
folder.addFile(pdf);
}
}
Well I figured it out. I was expecting more code, but this does it for me:
function listPDFs() {
var out = new Array();
var row = 3; //row index of 0 = row 1
var column = 4; // column index of 0 = column A
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var folder = DriveApp.getFolderById("this is where you paste your folder id"); // destination folder (this is the 0978SDFSDFKJHSDF078Y98hkyo looking value when you right click your folder and select "Get Link")
for (var i=row ; i<data.length ; i++) {
if(data[i][column] !== "") {
var file = UrlFetchApp.fetch(data[i][column]);
folder.createFile(file);
}
}
return
}
As you can see, I included a row and column variable so that I could easily change these.
I haven't figured out how to assemble them into a merged PDF, but I did figure out that I could sort them by date (which places the top most item first) and then right click and select "Open With...PDF Mergy", which then moves the PDFs into PDF Mergy and merges them up in the correct order. You can find PDF Mergy in the Chrome App Store. If I figure out how to automatically call PDF Mergy from GAS, I'll post that up--but for the time being the above code has saved us a ton of time...so I'm calling it good enough for the time being.