Generating a new PDF using an existing PDF Document - pdf

I'm trying to generate a new PDF document using an existing document as a base on a UWP application. I want to import the pages from the existing document, create annotations with information received from the application level and create a new PDF document combining both.
But it just creates a PDF document with the same number of pages but they are empty. When I analyze the created document the content is not available even though I imported it. But the created annotations are available in the document.
This is the file creation logic.
public async Task EmbedCurrentAnnotationsInStore(PdfDocument document)
{
if (document is null || document.IsDisposed)
return;
try
{
var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("embedded_doc.pdf", CreationCollisionOption.ReplaceExisting);
PdfDocument newDocument = new PdfDocument();
PDFium.FPDF_ImportPages(newDocument.Handle, document.Handle, 0);
foreach (var page in newDocument.Pages)
await annotationConvertor.ConvertToFPDFAnnotations(page, annotationAdapter.GetAnnotations()
.Where(x =>
x.Status != AnnotationStatus.Removed &&
x.PageIndex == page.Index)
.OrderBy(x => x.AnnotationIndex));
using (var stream = await file.OpenStreamForWriteAsync())
{
newDocument.Save(stream, SaveFlags.None, document.FileVersion);
await stream.FlushAsync();
newDocument.Close();
}
}
catch (Exception ex)
{
var err = PDFium.FPDF_GetLastError();
throw ex;
}
}
ConvertToFPDFAnnotations()
public async Task ConvertToFPDFAnnotations(PdfPage page, IEnumerable<BaseAnnotation> annotations)
{
foreach (var annotation in annotations)
{
switch (annotation.AnnotationType)
{
case FPDF_ANNOTATION_SUBTYPE.FPDF_ANNOT_HIGHLIGHT:
await EmbedFPDFAnnotationFromHighlightAnnotation(page, annotation.AsHighlightAnnotation());
break;
default:
break;
}
}
}
EmbedFPDFAnnotationFromHighlightAnnotation()
private async Task EmbedFPDFAnnotationFromHighlightAnnotation(PdfPage page, HighlightAnnotation annotation)
{
if (page is null || page.IsDisposed)
return;
var fdfAnnotation = page.CreateAnnot(page, FPDF_ANNOTATION_SUBTYPE.FPDF_ANNOT_HIGHLIGHT);
fdfAnnotation.SetColor(fdfAnnotation.Handle, FPDFANNOT_COLORTYPE.FPDFANNOT_COLORTYPE_Color,
(uint)annotation.Color.R,
(uint)annotation.Color.G,
(uint)annotation.Color.B,
(uint)annotation.Color.A);
fdfAnnotation.SetStringValue(fdfAnnotation.Handle, "CA", annotation.Color.A.ToString());
foreach (var quadpoint in annotation.Quadpoints)
{
var refQuadpoint = quadpoint;
fdfAnnotation.AppendAttachmentPoints(fdfAnnotation.Handle, ref refQuadpoint);
}
fdfAnnotation.SetStringValue(fdfAnnotation.Handle, "Index", annotation.StartIndex.ToString());
fdfAnnotation.SetStringValue(fdfAnnotation.Handle, "Count", annotation.CharCount.ToString());
var rect = new FS_RECTF();
rect = annotation.Rect;
fdfAnnotation.SetRect(fdfAnnotation.Handle, ref rect);
fdfAnnotation.SetStringValue(fdfAnnotation.Handle, "CreationDate", annotation.CreationDate);
fdfAnnotation.SetStringValue(fdfAnnotation.Handle, "M", annotation.ModifiedDate);
if (string.IsNullOrEmpty(annotation.Contents))
fdfAnnotation.SetStringValue(fdfAnnotation.Handle, "Contents", annotation.Contents);
fdfAnnotation.CloseAnnot(fdfAnnotation.Handle);
}
I used this (FPDF_ImportPages) method to import the pages from the existing document.
Could someone help me with the issue I'm facing? Maybe I'm missing something here.
Thanks.

Related

Files not downloaded showing binay data in ASP.NET core web API

Trying to download multiplefile as zip using System.io.compression from asp.net core web api. But i got binary error text. How can i fix this? below is my code
Error:
Error from backend. {"headers":{"normalizedNames":{},"lazyUpdate":null},"status":200,"statusText":"OK","url":"http://localhost:4000/api/filegeneration/createfile/2023-02-08/1","ok":false,"name":"HttpErrorResponse","
message":"Http failure during parsing for http://localhost:4000/api/filegeneration/createfile/2023-02-08/1",
"error":{"error":{},"text":"PK\u0003\u0004\u0014\u0000\u0000\u0000\b\u0000��HV�
u0010\u0001\u000b\u0000\u0000\u0000\u0003\u0000\u0000\u0000\u001e\u0000\u0000\u0000
Fareshare_Qty_08.02.2023_0.txtz�{?\u0000\u0000\u0000��\u0003\u0000PK\u0001\u0002\u0014
\u0000\u0014\u0000\u0000\u0000\b\u0000��HV�\u0010\u0001\u000b\u0000\u0000\u0000\u0003\u0000\u0000
\u0000\u001e\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000Fareshare_Qty_08.02.2023_0.txtPK\u0005
\u0006\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000L\u0000\u0000\u0000G\u0000\u0000\u0000\u0000\u0000"}}
Controller:
using (var outstream = new MemoryStream())
{
using (var archive = new ZipArchive(outstream,ZipArchiveMode.Create,true))
{
foreach (string filePath in files)
{
var filearcive = archive.CreateEntry(Path.GetFileName(filePath),CompressionLevel.Optimal);
using (var entrystream = filearcive.Open())
{
using (var fileCompressionStream = new MemoryStream(System.IO.File.ReadAllBytes(filePath)))
{
await fileCompressionStream.CopyToAsync(entrystream);
}
}
}
}
outstream.Position = 0;
return File(outstream.ToArray(), "application/zip", "Fareshare.zip");
Below code works for me. Please check it.
public (string fileType, byte[] archiveData, string archiveName) DownloadFiles(string subDirectory)
{
var zipName = $"archive-{DateTime.Now.ToString("yyyy_MM_dd-HH_mm_ss")}.zip";
List<FileInfo> files = new DirectoryInfo(Path.Combine(_hostingEnvironment.ContentRootPath, subDirectory)).GetFiles().ToList();
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
files.ForEach(file =>
{
var theFile = archive.CreateEntry(file.Name);
using (var streamWriter = new StreamWriter(theFile.Open()))
{
streamWriter.Write(File.ReadAllText(file.FullName));
}
});
}
return ("application/zip", memoryStream.ToArray(), zipName);
}
Test Result

PushStreamContent in asp.net core - video start playing only when whole file is buffered

i have problem with PushStreamContent in asp.net core.
It display video on the website but my problem is that it will buffer whole file and then play it when my goal is to buffer small part of it and play on the website. Code i have:
My endpoint for playing video in browser
public IActionResult Play(string file)
{
var fileName = "C:\\repo\\trailer1.mp4";
var video = new VideoStream(fileName);
var response = new PushStreamContent(video.WriteToStream, new MediaTypeHeaderValue("video/mp4"))
{
};
var objectResult = new ObjectResult(response);
objectResult.ContentTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("video/mp4"));
return objectResult;
}
Ive got VideoStreamClass to help with displaying video
public class VideoStream
{
private readonly string _filename;
public VideoStream(string filename)
{
_filename = #"C:\\repo\\trailer1.mp4";
}
public async Task WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
{
try
{
var buffer = new byte[65536];
using (var video = File.Open(_filename, FileMode.Open, FileAccess.Read))
{
var length = (int)video.Length;
var bytesRead = 1;
while (length > 0 && bytesRead > 0)
{
bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length));
await outputStream.WriteAsync(buffer, 0, bytesRead);
await outputStream.FlushAsync();
length -= bytesRead;
}
}
}
catch (Exception)
{ return; }
finally
{
outputStream.Dispose();
}
}
}
And here is my VideoOutputFormatter added to bootstraper
public class VideoOutputFormatter : IOutputFormatter
{
public bool CanWriteResult(OutputFormatterCanWriteContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (context.Object is PushStreamContent)
return true;
return false;
}
public async Task WriteAsync(OutputFormatterWriteContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
using (var stream = ((PushStreamContent)context.Object))
{
var response = context.HttpContext.Response;
if (context.ContentType != null)
{
response.ContentType = context.ContentType.ToString();
}
await stream.CopyToAsync(response.Body);
}
}
}
I've tried to add atributes to controller "UseBufferedOutputStream" and "UseBufferedInputStream" setted to false but this still dosent work for me
ObjectResult is intended for holding an in-memory object as a response. If you want to return an existing file, then PhysicalFileResult is probably your best bet.
If you're interested in PushStreamContent, I believe the one you're using is for HttpClient, not ASP.NET. If you want a PushStreamContent equivalent, I have a FileCallbackResult that would work once it's updated for the latest .NET Core.

Adobe Acrobat Pro DC PDF Submit Button To ASP.NET Core 3.1 WebApplication Controller

In my PDF I have a submit button that in theory should submit the PDF document to my Controller.
Once the Submit button is pressed, when I debug, I can see in UploadPDF that List files has a file count of 0. How do I get the PDF to send the file to my controller? Am I making the wrong assumption that this can be done? Or should I use Javascript to do this? (What would the Javascript look like?)
[HttpPost, DisableRequestSizeLimit]
//[ValidateAntiForgeryToken]
public async Task<IActionResult> UploadPDF(List<IFormFile> files)
{
//
foreach (var file in files)
{
var x = file.FileName;
var y = file.ContentType;
var strExtension = "application/pdf";
//if (!strExtension.Contains("pdf"))
//{
// ModelState.AddModelError("myErrorFileName", "Please enter a PDF file. (.pdf)");
//}
if (file.Length == 0)
{
ModelState.AddModelError("myErrorFileName", "The file is empty and has a length of zero.");
}
}
foreach (var item in files)
{
//Start the File List
List<string> records = new List<string>();
//Read fileStream
using (var fileStream = item.OpenReadStream())
{
MemoryStream ms = new MemoryStream();
fileStream.CopyTo(ms);
//Do more stuff
}
}
//return
return View();
}

JsReport .NET Core - Generate PDF from url

Trying to use JsReport to generate a pdf from a url but can't find any documentation or examples in their github repo.
Basically I need to generate the pdf and attach it to an email and I've managed to get data back as a byte[], but I can't seem to figure out how to use an existing View/Action.
This is the action that generates the PDF for viewing...
[MiddlewareFilter(typeof(JsReportPipeline))]
public async Task<IActionResult> Pdf(Guid id)
{
var serviceOrder = await _serviceOrderService.Get(id);
if (serviceOrder == null) return new NotFoundResult();
var model = _mapper.Map<ServiceOrderModel>(serviceOrder);
HttpContext.JsReportFeature().Recipe(Recipe.PhantomPdf);
return View(model);
}
This action should take the pdf view from "Details" and generate a PDF that I can attach. Below I can generate it with static content like "Hello from pdf" but I can't figure out how to use my "Details" view in ASPNET Core.
public async Task<IActionResult> Email(Guid id)
{
var rs = new LocalReporting().UseBinary(JsReportBinary.GetBinary()).AsUtility().Create();
var report = await rs.RenderAsync(new RenderRequest()
{
Template = new Template()
{
Recipe = Recipe.PhantomPdf,
Engine = Engine.None,
Content = "Hello from pdf",
}
});
var memoryStream = new MemoryStream();
await report.Content.CopyToAsync(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(memoryStream, "application/pdf") { FileDownloadName = "out.pdf" };
}
Taken From the JsReport Github Dotnet Example,
[MiddlewareFilter(typeof(JsReportPipeline))]
public IActionResult InvoiceDownload()
{
HttpContext.JsReportFeature().Recipe(Recipe.ChromePdf)
.OnAfterRender((r) => HttpContext.Response.Headers["Content-Disposition"] = "attachment; filename=\"myReport.pdf\"");
return View("Invoice", InvoiceModel.Example());
}
If you want to return a file from Asp.net Core Controller Action method then try the following
[MiddlewareFilter(typeof(JsReportPipeline))]
public async Task<IActionResult> Pdf(Guid id)
{
var serviceOrder = await _serviceOrderService.Get(id);
if (serviceOrder == null) return new NotFoundResult();
var model = _mapper.Map<ServiceOrderModel>(serviceOrder);
HttpContext.JsReportFeature().Recipe(Recipe.PhantomPdf).OnAfterRender((r) =>
HttpContext.Response.Headers["Content-Disposition"] = "attachment; filename=\"out.pdf\"");
return View(model);
}

Cannot Open Files in WinRT Unit Testing

I am writing a unit test to validate the serialization of objects and I am able to successfully save the file without any issue. I can even browse the file and validate the contents are correct. However, when I attempt to open the file for reading I always receive an UnauthorizedAccess exception.
Here is the code used to save the item:
public static async Task SaveItem<T>(string folderName, T item)
where T : BaseBusinessItem
{
if (string.IsNullOrEmpty(folderName))
{
throw new ArgumentNullException("folderName");
}
if (item == null)
{
throw new ArgumentNullException("item");
}
try
{
var folder = await ApplicationData.Current.LocalFolder
.CreateFolderAsync(folderName, CreationCollisionOption.OpenIfExists);
var file =
await
folder.CreateFileAsync(item.UniqueID.GetHashCode().ToString(), CreationCollisionOption.ReplaceExisting);
var stream = await file.OpenAsync(FileAccessMode.ReadWrite);
using (var outStream = stream.GetOutputStreamAt(0))
{
var serializer = new DataContractJsonSerializer(typeof(T));
serializer.WriteObject(outStream.AsStreamForWrite(), item);
await outStream.FlushAsync();
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
Here is the code used to restore the item:
public static async Task<T> RestoreItem<T>(string folderName, string hashCode)
where T : BaseBusinessItem, new()
{
if (string.IsNullOrEmpty(folderName))
{
throw new ArgumentNullException("folderName");
}
if (string.IsNullOrEmpty(hashCode))
{
throw new ArgumentNullException("hashCode");
}
var folder = await ApplicationData.Current.LocalFolder.GetFolderAsync(folderName);
var file = await folder.GetFileAsync(hashCode);
var inStream = await file.OpenSequentialReadAsync();
var serializer = new DataContractJsonSerializer(typeof(T));
var retVal = (T)serializer.ReadObject(inStream.AsStreamForRead());
return retVal;
}
And the unit test:
[TestMethod]
public async Task TestFileSaveLoad()
{
await _ds.SaveItem("TestFolder");
Guid ID = _item.UniqueID;
_ds = await ItemDataSource.LoadItem("TestFolder", ID.GetHashCode().ToString());
}
Any ideas or troubleshooting steps I might be missing. The unit test app manifest includes the following capabilities: Document Library, Internet (Client). The following declarations are in place: File Open Picker, File Save Picker and File Type Associations.
Thanks!
This code snippet helped me accomplish my goal. Hope this is helpful for someone else:
http://codepaste.net/gtu5mq