How do I save edited PDF file from PDFtron? - ios

I have used PDFTron to update/edit PDF files. I have followed the documentation for opening the PDF file which came from server, but I am not sure how to save the edited PDF file with this SDK (PDFTron).
I have referred below links to save PDF, but did not succeed.
https://www.pdftron.com/documentation/ios/guides/features/forms/export-data/
https://www.pdftron.com/api/ios/Enums/PTSaveOptions.html
I want to send XFDF file formats to server.
PDFTron saves PDF with annotation automatically after some time interval, but I want it to be saved by save button press. I am stuck on this saving process.
I have below code to import annotation and I don't know how to import this XFDF file and where do to get this XFDF file.
// Import annotations from XFDF to FDF
let fdf_doc: PTFDFDoc = PTFDFDoc.create(fromXFDF: xfdf_filename)
// Optionally read XFDF from a string
let fdf_doc: PTFDFDOc = PTFDFDoc.create(fromXFDF: xfdf_string)
// Merge FDF data into PDF doc
let doc: PTPDFDoc = PTPDFDoc(filepath: filename)
doc.fdfMerge(fdf_doc)
I don't want it to be customisations by myself, I just want it to be saved by me on pressing button.
Below is my query
How do I save the applied annotation on PDF by myself?

Once you've applied changes to the document data you'll probably want to do something with the updated PDF like letting the user download it or sending it back to your server.
If you just want to let the user download the edited file then no extra changes are necessary as pressing the download button will save the modified PDF to the user's computer.
To add a custom save button, here is a code sample.
If you want to get the modified PDF as an ArrayBuffer then you can use the
getFileData function on Document.
For example:
WebViewer(...)
.then(instance => {
const { documentViewer, annotationManager } = instance.Core;
documentViewer.addEventListener('documentLoaded', async () => {
const doc = documentViewer.getDocument();
const xfdfString = await annotationManager.exportAnnotations();
const options = { xfdfString };
const data = await doc.getFileData(options);
const arr = new Uint8Array(data);
const blob = new Blob([arr], { type: 'application/pdf' });
// upload blob to your server
});
});

I have followed the documentation for opening the PDF file which came from server
There are a few ways to do this - could you share which API you are using?
The main point of your question seems to be how to save the PDF via a button press (after you've merged in XFDF annotation data). Is this the case?
You can control where a remote document is shared by implementing the relevant delegate methods, likely specifically https://www.pdftron.com/api/ios/Protocols/PTDocumentControllerDelegate.html#/c:objc(pl)PTDocumentControllerDelegate(im)documentController:destinationURLForDocumentAtURL:
You can then save the document using this method:
https://www.pdftron.com/api/ios/Classes/PTDocumentBaseViewController.html#/c:objc(cs)PTDocumentBaseViewController(im)saveDocument:completionHandler:

Related

pdf.js to display output of file created with tcpdf

I really hope you will be able to help me out on this one.
I am new to pdf.js so for the moment, I am playing around with the pre-built version to see if I can integrate this into my web app.
My problem:
I am using tcpdf to generate a pdf file which I would like to visualize using pdf.js without having to save it to a file on the server.
I have a php file (generate_document.php) that I use to generate the pdf. The file ends with the following:
$pdf->Output('test.pdf', 'I');
according to the tcpdf documentation, the second parameter can be used to generate the following formats:
I: send the file inline to the browser (default). The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.
D: send to the browser and force a file download with the name given by name.
F: save to a local server file with the name given by name.
S: return the document as a string (name is ignored).
FI: equivalent to F + I option
FD: equivalent to F + D option
E: return the document as base64 mime multi-part email attachment (RFC 2045)
Then, I would like to view the pdf using pdf.js without creating a file on the server (= not using 'F' as a second parameter and passing the file name to pdf.js).
So, I thought I could simply create an iframe and call the pdf.js viewer pointing to the php file:
<iframe width="100%" height="100%" src="/pdf.js_folder/web/viewer.html?file=get_document.php"></iframe>
However, this is not working at all....do you have any idea what I am overlooking? Or is this option not available in pdf.js?
I have done some research and I have seen some posts here on converting a base64 stream to a typed array but I do not see how this would be a solution to this problem.
Many thanks for your help!!!
EDIT
#async, thanks for your anwer.
I got it figured out in the meantime, so I thought I'd share my solution with you guys.
1) In my get_document.php, I changed the output statement to convert it directly to base64 using
$pdf_output = base64_encode($pdf->Output('test_file.pdf', 'S'));
2) In viewer.js, I use an XHR to call the get_document.php and put the return in a variable (pdf_from_XHR)
3) Next, I convert what came in from the XHR request using the solution that was already mentioned in a few other posts (e.g. Pdf.js and viewer.js. Pass a stream or blob to the viewer)
pdf_converted = convertDataURIToBinary(pdf_from_XHR)
function convertDataURIToBinary(dataURI) {
var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
var base64 = dataURI.substring(base64Index);
var raw = window.atob(base64);
var rawLength = raw.length;
var array = new Uint8Array(new ArrayBuffer(rawLength));
for (i = 0; i < rawLength; i++) {
array[i] = raw.charCodeAt(i);
}
return array;
}
et voilĂ  ;-)
Now i can inject what is coming from that function into the getDocument statement:
PDFJS.getDocument(pdf_converted).then(function (pdf) {
pdfDocument = pdf;
var url = URL.createObjectURL(blob);
PDFView.load(pdfDocument, 1.5)
})

How do I send file back after Kendo upload

I have the following requirements. I need to upload an Excel file to a MVC based site. For this I am using Kendo Upload. In the controller action that handles the upload I need to make a slight modification to the Excel file and then send it back as a file stream. I am using Aspose for the Excel modifications. My question is can I achieve all of this within the one controller action without the Excel file ever hitting the disk of web server?
I managed to get this to work by using the synchronous upload mode. My controller action looks like this:
[POST("SaveExcelFile")]
public FileStreamResult Save(IEnumerable<HttpPostedFileBase> files)
{
// The Name of the Upload component is "files"
if (files != null)
{
foreach (var file in files)
{
// Some browsers send file names with full path.
// We are only interested in the file name.
var fileName = Path.GetFileName(file.FileName);
//var physicalPath = Path.Combine(Server.MapPath("~/App_Data"), fileName);
Workbook excel2 = new Workbook(file.InputStream);
excel2.Worksheets.Add("TEST");
Stream stream = new MemoryStream();
excel2.Save(stream, SaveFormat.Excel97To2003);
stream.Position = 0;
return File(stream, "application/vnd.ms-excel", "junk.xls");
// The files are not actually saved in this demo
// file.SaveAs(physicalPath);
}
}
// Return an empty string to signify success
return null;
}
This is only proof of concept code but you can get the idea of what I was trying to achieve. Upload a file, manipulate it and send the modified Worksheet back down to the client as a stream.
I don't think you can. I have used KendoUI's upload control, and it seems that you'll only get to manipulate the file after it's written on the server side.
What you can do is to first save the file, perform your modification, then overwrite it.

Web server to save a file, then open it and save as different type, then prompt user to download

I have an MVC Razor application where I am returning a view.
I have overloaded my action to accept a null-able "export" bool which will change the action by adding headers but still returning the same view as a file (in a new window).
//if there is a value passed in, set the bool to true
if (export.HasValue)
{
ViewBag.Exporting = true;
var UniqueFileName = string.Format(#"{0}.xls", Guid.NewGuid());
Response.AddHeader("content-disposition", "attachment; filename="+UniqueFileName);
Response.ContentType = "application/ms-excel";
}
As the file was generated based on a view, its not an .xls file so when opening it, I get the message "the file format and extension of don't match". So after a Google, I have found THIS POST on SO where one of the answers uses VBA to open the file on the server (which includes the HTML mark-up) then saves it again (as .xls).
I am hoping to do the same, call the controller action which will call the view and create the .xls file on the server, then have the server open it, save it then return it as a download.
What I don't want to do is to create a new view to return a clean file as the current view is an extremely complex page with a lot of logic which would only need to be repeated (and maintained).
What I have done in the view is to wrap everything except the table in an if statement so that only the table is exported and not the rest of the page layout.
Is this possible?
You can implement the VBA in .net
private void ConvertToExcel(string srcPath, string outputPath, XlFileFormat format)
{
if (srcPath== null) { throw new ArgumentNullException("srcPath"); }
if (outputPath== null) { throw new ArgumentNullException("outputPath"); }
var excelApp = new Application();
try
{
var wb = excelApp.Workbooks.Open(srcPath);
try
{
wb.SaveAs(outputPath, format);
}
finally
{
Marshal.ReleaseComObject(wb);
}
}
finally
{
excelApp.Quit();
}
}
You must install Microsoft.Office.Interop and add reference to a COM oject named Microsoft Excel XX.0 Object Library
Sample usage:
//generate excel file from the HTML output of GenerateHtml action.
var generateHtmlUri = new Uri(this.Request.Url, Url.Action("GenerateHtml"));
ConvertToExcel(generateHtmlUri.AbsoluteUri, #"D:\output.xlsx", XlFileFormat.xlOpenXMLStrictWorkbook);
I however discourage this solution because:
You have to install MS Excel in your web server.
MS Excel may sometimes misbehave like prompting a dialog box.
You must find a way to delete the generated Excel file afterwards.
Ugly design.
I suggest to generate excel directly because there doesn't seem to be better ways to covert HTML to Excel except using Excel itself or DocRaptor.

Create and download word file from template in MVC

I have kept a word document (.docx) in one of the project folders which I want to use as a template.
This template contains custom header and footer lines for user. I want to facilitate user to download his own data in word format. For this, I want to write a function which will accept user data and referring the template it will create a new word file replacing the place-holders in the template and then return the new file for download (without saving it to server). That means the template needs to be intact as template.
Following is what I am trying. I was able to replace the placeholder. However, I am not aware of how to give the created content as downloadable file to user. I do not want to save the new content again in the server as another word file.
public void GenerateWord(string userData)
{
string templateDoc = HttpContext.Current.Server.MapPath("~/App_Data/Template.docx");
// Open the new Package
Package pkg = Package.Open(templateDoc, FileMode.Open, FileAccess.ReadWrite);
// Specify the URI of the part to be read
Uri uri = new Uri("/word/document.xml", UriKind.Relative);
PackagePart part = pkg.GetPart(uri);
XmlDocument xmlMainXMLDoc = new XmlDocument();
xmlMainXMLDoc.Load(part.GetStream(FileMode.Open, FileAccess.Read));
xmlMainXMLDoc.InnerXml = ReplacePlaceHoldersInTemplate(userData, xmlMainXMLDoc.InnerXml);
// Open the stream to write document
StreamWriter partWrt = new StreamWriter(part.GetStream(FileMode.Open, FileAccess.Write));
xmlMainXMLDoc.Save(partWrt);
partWrt.Flush();
partWrt.Close();
pkg.Close();
}
private string ReplacePlaceHoldersInTemplate(string toReplace, string templateBody)
{
templateBody = templateBody.Replace("#myPlaceHolder#", toReplace);
return templateBody;
}
I believe that the below line is saving the contents in the template file itself, which I don't want.
xmlMainXMLDoc.Save(partWrt);
How should I modify this code which can return the new content as downloadable word file to user?
I found the solution Here!
This code allows me to read the template file and modify it as I want and then to send response as downloadable attachment.

How to set the url in IHTMLDocument2 without it navigating to a new page

How do I set the url of an htmldocument after I've written to it. For example:
WebBrowser wb = new WebBrowser();
wb.Navigate(new Uri(location, UriKind.Absolute));
IHTMLDocument2 myDoc = new HTMLDocumentClass();
myDoc.write(new object[] { wb.DocumentText});
myDoc.close();
If I do myDoc.url = "http://www.google.com" it attempts to load google.
How do I set the url without having it attempt to load that url?
These steps should give you a document with correct URL and your own content:
Create document directly from URL (so you don't have to set the URL later)
Stop document download (because you don't need the content)
Fill document with your content
This code shows how to do it:
// 1. Create new document from URL
IHTMLDocument2 NewDoc = (wb.Document as IHTMLDocument4).createDocumentFromUrl("http://www.stackoverflow.com", "null");
// 2. Immediately stop navigating; the URL is still set
NewDoc.execCommand("Stop", false, null);
// 3. Now write your stuff to the document
// ...
Note: It's hard to guess how much content can be downloaded between steps 1 and 2 because loading happens asynchronously. So it's probably good to check if the document is indeed empty before doing step 3. If not, clear the document and proceed.

Resources