How can I modify a javascript event in a PDF file programmatically? - javascript

My PDF file has an event attached to a button. I need to be able to modify that event programmatically. I tried this way using iTextSharp, but it didn't change the javascript in the new file:
var pdfReader = new PdfReader(originalPdfDocumentPath);
pdfReader.RemoveUsageRights();
var pdfStamper = new PdfStamper(pdfReader, new FileStream(
newPdfDocumentPath, FileMode.Create, FileAccess.Write, FileShare.None),
'\0', true);
var originalXml = pdfReader.AcroFields.Xfa.DomDocument.InnerXml;
var newXml = originalXml.Replace(
"Table2.Row1.instanceManager.removeInstance(1)",
"Table2._Row1.removeInstance(this.parent.parent.index)");
// Unfortunately, this line does nothing.
pdfStamper.AcroFields.Xfa.DomDocument.InnerXml = newXml;
pdfStamper.Close();
pdfReader.Close();
Any help would be greatly appreciated.

I found that it works if, instead of changing the XML directly, I change the DomDocument and mark the XFA as changed. Below is the corrected code:
var pdfReader = new PdfReader(originalPdfDocumentPath);
pdfReader.RemoveUsageRights();
var pdfStamper = new PdfStamper(pdfReader, new FileStream(newPdfDocumentPath, FileMode.Create, FileAccess.Write, FileShare.None), '\0', true);
var originalXml = pdfReader.AcroFields.Xfa.DomDocument.InnerXml;
var newXml = originalXml.Replace("Table2.Row1.instanceManager.removeInstance(1)", "Table2._Row1.removeInstance(this.parent.parent.index)");
/* New Code */
var doc = new XmlDocument();
doc.LoadXml(newXml);
pdfStamper.AcroFields.Xfa.DomDocument = doc;
pdfStamper.AcroFields.Xfa.Changed = true;
/* End of New Code */
pdfStamper.Close();
pdfReader.Close();
I should note that, even though this code changes the javascript in the PDF file, it also disables the extended features in Adobe Acrobat Reader. You can find more information regarding this here:
http://developers.itextpdf.com/question/why-do-i-get-error-saying-use-extended-features-no-longer-available
"The problem is related to whether or not your document is Reader Enabled. Reader-enabling can only be done using Adobe software. It is a process that requires a digital signature using a private key from Adobe. When a valid signature is present, specific functionality (as defined in the usage rights when signing) is unlocked in Adobe Reader.
You change the content of such a PDF, hence you break the signature."

Related

Adobe Javascript in Action Wizard vs a form button

I've been writing some javascript to fill in fields of a PDF form. I initially wrote the code in the "Action Wizard" in Adobe. I did not realize at the time that I was adding it to my local application, not the form itself. So I then copied it to a button on the from and now it is not working.
The code:
/* Test to read in a file and update the fields*/
var dataFrom = null;
//Grab the current path and update it to indicate the TempInfo location
var strPath = this.path;
strPath = strPath.slice(0,-12);
strPath = strPath + "TempInfo.txt"
//Get data from TempFile into array, display message if no file found
try{
var dataStream = util.readFileIntoStream(strPath);
var dataFrom = util.stringFromStream(dataStream);
}catch(e){
app.alert("Temp file not found");
}
//Put the data into an array and update the fields
var strTest = new Array();
strTest = dataFrom.split(/\n/);
getField("Username").value = strTest[0];
getField("UID").value = strTest[1];
//Clear the data
dataStream = null;
dataFrom = null;
strTest = null;
I am getting the app.alert "Temp file not found" so the "var dataStream = readFileInfoStream(strPath);" isn't reading in the file. I did app.alerts to verify the strPath variable has the right path and one to verify that dataStream is coming up null. Being that I copied it from the Action Wizard, I am unsure why its not working.
Just to make this a little odder (at least to me), if I open the JavaScript editor and highlight the code, it works fine.
For the util.readFileIntoStream method, when the cDIPath parameter is specified, the method can only be executed in a privileged context meaning during a batch, console event, or Action. It won't work when executed within the document context unless you create a Trusted Function.
Read this to understand how to executing privileged methods in a non-privileged context...
http://help.adobe.com/en_US/acrobat/acrobat_dc_sdk/2015/HTMLHelp/index.html#t=Acro12_MasterBook%2FJS_Dev_Contexts%2FExecuting_privileged_methods_in_a_non-privileged_context.htm&rhsearch=trusted%20function&rhsyns=%20

Print PDF Created using itextsharp

My Goal is to print a RDLC report on the client machine without preview. I can not use the ReportViewer print button since it requires the installation of ActiveX object and there are no permissions for that. So, I'm using ITextSharp to create a PDF from the byte array returned from the rendered LocalReport, and add a JavaScript for print.
During Debug, I can see that the PDF is generated and has 2 pages, and everything looks OK. I don't receive any errors and the function exits OK, but it doesn't print. What am I doing wrong, or what am I missing?
This is my code:
string jsPrint = "var pp = this.getPrintParams();pp.interactive= pp.constants.interactionLevel.silent;this.print(pp);";
byte[] bytes = report.Render("PDF", null, out mimeType, out encoding, out extension, out streamids, out warnings);
using (MemoryStream ms = new MemoryStream())
{
Document doc = new Document();
PdfWriter writer = PdfWriter.GetInstance(doc, ms);
doc.SetPageSize(PageSize.A4);
doc.Open();
PdfContentByte cb = writer.DirectContent;
PdfImportedPage page;
PdfReader reader = new PdfReader(bytes);
int pages = reader.NumberOfPages;
for (int i = 1; i <= pages; i++)
{
doc.SetPageSize(PageSize.A4);
doc.NewPage();
page = writer.GetImportedPage(reader, i);
cb.AddTemplate(page, 0, 0);
}
PdfAction jAction = PdfAction.JavaScript(jsPrint, writer);
writer.AddJavaScript(jAction);
doc.Close();
}
Thanks.
Regarding your question about PdfStamper (in the comments). It should be as simple as this:
string jsPrint = "var pp = this.getPrintParams();pp.interactive= pp.constants.interactionLevel.silent;this.print(pp);";
PdfReader reader = new PdfReader(bytes);
MemoryStream stream = new MemoryStream();
PdfStamper stamper = new PdfStamper(pdfReader, stream);
stamper.Writer.AddJavaScript(jsPrint);
stamper.Close();
reader.Close();
Regarding your original question: automatic printing of PDF documents is considered being a security hazard: one could send a PDF to an end-user and that PDF would cause the printer to spew out pages. That used to be possible with (really) old PDF viewers, but modern viewers prevent this from happening.
In other words: you may be trying to meet a requirement of the past. Today's PDF viewers always require an action from the end user to print a PDF document.

Javascript/Excel ActiveX object. Find exact match

So I am using a SharePoint web part (HTML Form Web Part) that I've built to query an excel file that is hosted on our Sharepoint site. I wrote javascript to create an Excel ActiveX object, and I can search the worksheet just fine.
However, I need to have it search for an exact match (the whole cell) not just part. I know the code that I need, I just can't make it work. I need to find out how to create the correct object type for the "xlWhole" argument of the "Find()" function.
The line of code I am having trouble with is commented out, because that didn't work. Any insight? I've seen the Range.Find method, but I just can't make it work.
var excel = new ActiveXObject("Excel.Application");
var wb = excel.Workbooks.Open("workbook path");
var ws = wb.Worksheets("worksheet name");
var ws = wb.ActiveSheet;
//var cell = ws.Cells.Find(str,excel.XlLookAt.xlWhole);
var cell = ws.Cells.Find(str);
foundRow = cell.Row;
This worked for me:
function searchExcel()
{
var excel =new ActiveXObject("Excel.Application");
excel.visible=true;
var wb = excel.workbooks.open("D:\\Analysis\\tmp\\Book1.xlsx");
var ws = wb.sheets(1);
var str="Value1";
// .Find(What, After, LookIn, LookAt, SearchOrder, SearchDirection,
// MatchCase, MatchByte, SearchFormat)
var cell = ws.Cells.Find(str,ws.Cells(1),-4163,1)
alert(cell?cell.Row:(str+" not found"));
excel.quit();
}
I guess you cannot skip over parameters when using COM from js (but you can leave them off the end).

Thunderbird extension: open EML file as draft

After my first question here, I am now looking for a way do the the same, but instead of opening an EML as a message, I want to open it as a draft.
Basically, I want to load a generated EML file into the compose window, so I can directly send it.
I already found some code, but I can't find the correct documentation on how to use it
var filePath = new FileUtils.File(getPath(params));
var uri = io.newFileURI(filePath);
var msgComposeService = Components.classes["#mozilla.org/messengercompose;1"].getService(Components.interfaces.nsIMsgComposeService);
var messenger = Components.classes["#mozilla.org/messenger;1"].createInstance(Components.interfaces.nsIMessenger);
var hdr = messenger.msgHdrFromURI(uri.spec);
var identity = getIdentityForHeader(hdr, Components.interfaces.nsIMsgCompType.Draft);
var msgWindow = Components.classes["#mozilla.org/messenger/msgwindow;1"].createInstance(Components.interfaces.nsIMsgWindow);
msgComposeService.OpenComposeWindow(null,null,uri,Components.interfaces.nsIMsgCompType.Draft,Components.interfaces.nsIMsgCompFormat.Default,identity,msgWindow);
I would suggest injecting the eml file into a local Drafts folder so as to get an nsIMsgDBHdr, and then calling the ComposeMessage function with Ci.nsIMsgCompType.Draft, Ci.nsIMsgCompFormat.Default, yourMsgHdr.folder, yourMsgHdr'sURI.
I think there are several StackOverflow answers on how to inject a given message into a folder.

How can i generate a file and offer it for download in plain javascript?

I would like to generate a text file in the javascript dynamicly, then offer this for download. Currently I can get this working to a degree with either of the following solutions:
content = "abc123";
document.location = "data:text/octet-stream," + encodeURIComponent(content);
OR
content = "abc123";
var bb = new BlobBuilder();
bb.append(content);
var blob = bb.getBlob();
blob = blob.slice(0, blob.size, 'text/octet-stream');
var fr = new FileReader();
fr.onload = function() {document.location = this.result;}
fr.readAsDataURL(blob);
However, Both of these solutions, when the download box appears, will only offer a default filename of 'download' in the save as dialogue.
My question is basically, how can I change this to a specific filename for example 'readme.txt' or 'scene.obj'
Also note the data type was previously 'text/plain' however if this is used, the document switches to the new text document instead of offering it for download (as text/octet-stream seems to do).
I do not want a flash solution, javascript/html5 only suggestions please.
Cheers, Josh
For that, you will have to use FileSaver from FileAPI: Writer specification.
For now, it's only a draft, and according to mailing list answer it isn't yet implemented in browsers.
You can watch for example on a chromium issue to get up-to-date information about the implementation progress
UPD 02.08.2013: I have since found a project that provides FileSaver interface using neat tricks
I think you should check: jQuery Table to CSV export

Categories