How is it possible to export a pdf using FastReport.net and asp.net?
I would like to export the File in a Controller. I tried it this way supported on the FastReport Website:
public FileResult GetFile()
{
WebReport webReport = new WebReport();
// bind data
System.Data.DataSet dataSet = new System.Data.DataSet();
dataSet.ReadXml(report_path + "nwind.xml");
webReport.Report.RegisterData(dataSet, "NorthWind");
// load report
webReport.ReportFile = this.Server.MapPath("~/App_Data/report.frx");
// prepare report
webReport.Report.Prepare();
// save file in stream
Stream stream = new MemoryStream();
webReport.Report.Export(new PDFExport(), stream);
stream.Position = 0;
// return stream in browser
return File(stream, "application/zip", "report.pdf");
}
but then the size of the pdf is always 0 bytes.
Does someone know a solution to my problem?
Ok, now I found a solution. Just use the normal Report (not WebReport) and set WebMode to true. The other settings on pdf-Export are just for fun.
So, this will do the trick:
public FileResult GetFile(Dataset dataset1)
{
FastReport.Utils.Config.WebMode = true;
Report rep = new Report();
rep.Load(Request.PhysicalApplicationPath + "App_Data/report.frx");
rep.RegisterData(dataset1);
if (rep.Report.Prepare())
{
// Set PDF export props
FastReport.Export.Pdf.PDFExport pdfExport = new FastReport.Export.Pdf.PDFExport();
pdfExport.ShowProgress = false;
pdfExport.Subject = "Subject";
pdfExport.Title = "xxxxxxx";
pdfExport.Compressed = true;
pdfExport.AllowPrint = true;
pdfExport.EmbeddingFonts = true;
MemoryStream strm = new MemoryStream();
rep.Report.Export(pdfExport, strm);
rep.Dispose();
pdfExport.Dispose();
strm.Position = 0;
// return stream in browser
return File(strm, "application/pdf", "report.pdf");
}
else
{
return null;
}
}
It's a pity that such code templates are wrong on the official site of the developer.
Works for me in 2017.1
public void GetFile()
{
SetReport();
webReport.ExportPdf();
}
public void GetPrint()
{
SetReport();
webReport.Prepare();
webReport.PrintPdf();
}
Related
I was downloading a pdf file in the main controller with the help of FileStreamResult like this and the file got dowmloaded just fine:
public FileStreamResult Download(Invoice? invoice)
{
// Create new document.
ComponentInfo.SetLicense("FREE-LIMITED-KEY");
var document = new PdfDocument();
// Add page.
var page = document.Pages.Add();
static bool IsNotNull([NotNullWhen(true)] object? invoice) => invoice != null;
var rectangle = page.Content.Elements.AddPath();
rectangle.AddRectangle(new PdfPoint(25, 650),
new PdfSize(273, 150));
var rectangleFormat = rectangle.Format;
rectangleFormat.Stroke.IsApplied = true;
rectangleFormat.Stroke.Width = 1;
rectangleFormat.Stroke.Color = PdfColor.FromGray(0.5);
var rectangle2 = page.Content.Elements.AddPath();
rectangle2.AddRectangle(new PdfPoint(300, 650),
new PdfSize(273, 150));
var rectangleFormat2 = rectangle2.Format;
rectangleFormat2.Stroke.IsApplied = true;
rectangleFormat2.Stroke.Width = 1;
rectangleFormat2.Stroke.Color = PdfColor.FromGray(0.5);
// Save PDF file.
var stream = new MemoryStream();
document.Save(stream);
stream.Position = 0;
// Download file.
return File(stream, mimeType, fileName);
}
But then I tried to put this download function in a seperate file and inject it with dependency injection:
public void Download(Invoice? invoice)
{
genPdf.GeneratePdf(this, mimeType, fileName, invoice);
}
And now in the new file the Return File() gives an error : Non-invocable member 'File' cannot be used like a method
public class GeneratePdf : IGeneratePdf
{
FileStreamResult IGeneratePdf.GeneratePdf(ControllerBase controller,
string mimeType, string fileName, Invoice? invoice)
{
// Create new document.
ComponentInfo.SetLicense("FREE-LIMITED-KEY");
var document = new PdfDocument();
// Add page.
var page = document.Pages.Add();
static bool IsNotNull([NotNullWhen(true)] object? invoice) => invoice != null;
var rectangle = page.Content.Elements.AddPath();
rectangle.AddRectangle(new PdfPoint(25, 650),
new PdfSize(273, 150));
var rectangleFormat = rectangle.Format;
rectangleFormat.Stroke.IsApplied = true;
rectangleFormat.Stroke.Width = 1;
rectangleFormat.Stroke.Color = PdfColor.FromGray(0.5);
var rectangle2 = page.Content.Elements.AddPath();
rectangle2.AddRectangle(new PdfPoint(300, 650),
new PdfSize(273, 150));
var rectangleFormat2 = rectangle2.Format;
rectangleFormat2.Stroke.IsApplied = true;
rectangleFormat2.Stroke.Width = 1;
rectangleFormat2.Stroke.Color = PdfColor.FromGray(0.5);
// Save PDF file.
var stream = new MemoryStream();
document.Save(stream);
stream.Position = 0;
// Download file.
return File(stream, mimeType, fileName);
}
}
I tried to make the new GeneratePdf inherit from base Controller class and I tried to call the File() method on the ControllerBase controller but the file still didn't get downloaded. Any other idea on how to fix that and keep the controller and the GeneratePdf functionality seperate?
Non-invocable member 'File' cannot be used like a method
This error appears due to the fact the File on your GeneratePdf is in fact a System.IO.File instead of Controller's helper method.
You can rectify your code by returning a Stream from the GeneratePdf and do a return File(..) on your controller instead.
public class GeneratePdf : IGeneratePdf
{
Stream IGeneratePdf.GeneratePdf(Invoice? invoice)
{
.... //code omitted to safe space
// Save PDF file.
var stream = new MemoryStream();
document.Save(stream);
stream.Position = 0;
// Download file.
return stream;
}
}
Then on the controller, do it this way:
public FileStreamResult Download(Invoice? invoice)
{
return File(genPdf.GeneratePdf(invoice), mimeType, fileName);
}
Note that we return FileStreamResult just like your original code. This way controller is only concerned with how to present the data into end user.
I'm surprised after 15 hours theres no answer/progress on this QA.
I have an issue with trying to create a large PDF file. Basically I have a list of byte arrays, each containing a PDF in a form of a byte array. I wanted to merge the byte arrays into a single PDF. This works great for smaller files (under 2000 pages), but when I tried creating a 12,00 page file it bombed). Originally I was using MemoryStream but after some research, a common solution was to use a FileStream instead. So I tried a file stream approach, however get similar results. The List contains 3,800 records, each containing 4 pages. MemoryStream bombs after around 570. FileStream after about 680 records. The current file size after the code crashed was 60MB. What am I doing wrong? Here is the code I have, and the code crashes on "copy.AddPage(curPg);" directive, inside the "for(" loop.
private byte[] MergePDFs(List<byte[]> PDFs)
{
iTextSharp.text.Document doc = new iTextSharp.text.Document();
byte[] completePDF;
Guid uniqueId = Guid.NewGuid();
string tempFileName = Server.MapPath("~/" + uniqueId.ToString() + ".pdf");
//using (MemoryStream ms = new MemoryStream())
using(FileStream ms = new FileStream(tempFileName, FileMode.Create, FileAccess.Write, FileShare.Read))
{
iTextSharp.text.pdf.PdfCopy copy = new iTextSharp.text.pdf.PdfCopy(doc, ms);
doc.Open();
int i = 0;
foreach (byte[] PDF in PDFs)
{
i++;
// Create a reader
iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(PDF);
// Cycle through all the pages
for (int currentPageNumber = 1; currentPageNumber <= reader.NumberOfPages; ++currentPageNumber)
{
// Read a page
iTextSharp.text.pdf.PdfImportedPage curPg = copy.GetImportedPage(reader, currentPageNumber);
// Add the page over to the rest of them
copy.AddPage(curPg);
}
// Close the reader
reader.Close();
}
// Close the document
doc.Close();
// Close the copier
copy.Close();
// Convert the memorystream to a byte array
//completePDF = ms.ToArray();
}
//return completePDF;
return GetPDFsByteArray(tempFileName);
}
A couple of notes:
PdfCopy implements iDisposable, so you should try and see if a using helps.
PdfCopy.FreeReader() will help.
Anyway, not sure if you're using MVC or WebForms, but here's a simple working HTTP handler tested with a 15 page 125KB test file that runs on my workstation:
<%# WebHandler Language="C#" Class="MergeFiles" %>
using System;
using System.Collections.Generic;
using System.Web;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
public class MergeFiles : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
List<byte[]> pdfs = new List<byte[]>();
var pdf = File.ReadAllBytes(context.Server.MapPath("~/app_data/test.pdf"));
for (int i = 0; i < 4000; ++i) pdfs.Add(pdf);
var Response = context.Response;
Response.ContentType = "application/pdf";
Response.AddHeader(
"content-disposition",
"attachment; filename=MergeLotsOfPdfs.pdf"
);
Response.BinaryWrite(MergeLotsOfPdfs(pdfs));
}
byte[] MergeLotsOfPdfs(List<byte[]> pdfs)
{
using (var ms = new MemoryStream())
{
using (Document document = new Document())
{
using (PdfCopy copy = new PdfCopy(document, ms))
{
document.Open();
for (int i = 0; i < pdfs.Count; ++i)
{
using (PdfReader reader = new PdfReader(
new RandomAccessFileOrArray(pdfs[i]), null))
{
copy.AddDocument(reader);
copy.FreeReader(reader);
}
}
}
}
return ms.ToArray();
}
}
public bool IsReusable { get { return false; } }
}
Tried to make the output file similar to what you described in the question, but YMMV, depending on how large the individual PDFs you're dealing with are in size. Here's the test output from my run:
So after a lot of messing around, I realized that there just was no way around it. However, I did manage to find a work-around. Instead of returning byte array, I return a temp file path, which I then transmit and delete there after.
private string MergeLotsOfPDFs(List<byte[]> PDFs)
{
Document doc = new Document();
Guid uniqueId = Guid.NewGuid();
string tempFileName = Server.MapPath("~/__" + uniqueId.ToString() + ".pdf");
using (FileStream ms = new FileStream(tempFileName, FileMode.Create, FileAccess.Write, FileShare.Read))
{
PdfCopy copy = new PdfCopy(doc, ms);
doc.Open();
int i = 0;
foreach (byte[] PDF in PDFs)
{
i++;
// Create a reader
PdfReader reader = new PdfReader(new RandomAccessFileOrArray(PDF), null);
// Cycle through all the pages
for (int currentPageNumber = 1; currentPageNumber <= reader.NumberOfPages; ++currentPageNumber)
{
// Read a page
PdfImportedPage curPg = copy.GetImportedPage(reader, currentPageNumber);
// Add the page over to the rest of them
copy.AddPage(curPg);
// This is a lie, it still costs money, hue hue hue :)~
copy.FreeReader(reader);
}
reader.Close();
}
// Close the document
doc.Close();
// Close the document
copy.Close();
}
// Return temp file path
return tempFileName;
}
And here is how I send that data to the client.
// Send the merged PDF file to the user.
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.ClearContent();
Response.ClearHeaders();
response.ContentType = "application/pdf";
response.AddHeader("Content-Disposition", "attachment; filename=1094C.pdf;");
response.WriteFile(tempFileName);
HttpContext.Current.Response.Flush(); // Sends all currently buffered output to the client.
DeleteFile(tempFileName); // Call right after flush but before close
HttpContext.Current.Response.SuppressContent = true; // Gets or sets a value indicating whether to send HTTP content to the client.
HttpContext.Current.ApplicationInstance.CompleteRequest(); // Causes ASP.NET to bypass all events and filtering in the HTTP pipeline chain of execution and directly execute the EndRequest event.
Lastly, here is a fancy DeleteFile method
private void DeleteFile(string fileName)
{
if (File.Exists(fileName))
{
try
{
File.Delete(fileName);
}
catch (Exception ex)
{
//Could not delete the file, wait and try again
try
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
File.Delete(fileName);
}
catch
{
//Could not delete the file still
}
}
}
}
I need to render svg in my XSL fo in c#.Net which is available in https://fonet.codeplex.com/. I tried to use svg in the xsl-fo but it does not render any pdf and fails silently.
If anybody has found a solution for this issue please help.
I need my pdf report to support svg contents.
Use the below code to add Hander of an image incase of svg extensions
FonetDriver fonetDriver = FonetDriver.Make();
fonetDriver.ImageHandler = SvgImageHandler;
Add the SvgImageHandler Hander
private static byte[] SvgImageHandler(string svgContent)
{
if (svgContent.Contains("http://www.w3.org/2000/svg"))
{
var svgByteAry = Encoding.UTF8.GetBytes(svgContent);
using (var stream = new MemoryStream(svgByteAry))
{
var svgDocument = SvgDocument.Open<SvgDocument>(stream);
using (var memoryStream = new MemoryStream())
{
svgDocument.Draw()
.Save(memoryStream, ImageFormat.Png);
var byteArray = memoryStream.ToArray();
return byteArray;
}
}
}
//Skip if not url based image
if (!Uri.IsWellFormedUriString(svgContent, UriKind.RelativeOrAbsolute))
return null;
if (!ValidateUrlImage(svgContent))
{
ICacheService cacheService = new HttpCache();
return cacheService.Get(Constants.NoImage,
() =>
{
var baseDirectory = AppDomain.CurrentDomain.BaseDirectory + ConfigurationManager.AppSettings[Constants.ImagePath];
var defaultUrl = Path.Combine(baseDirectory, Constants.NoImageFile);
var img = Image.FromFile(defaultUrl);
var imgCon = new ImageConverter();
return (byte[])imgCon.ConvertTo(img, typeof(byte[]));
});
}
return null;
}
Return proper image if the url is valid or pass false so the No Image can be rendered. keeping the code more robust.
private static bool ValidateUrlImage(string absoluteUrl)
{
Uri uri;
if (!Uri.TryCreate(absoluteUrl, UriKind.Absolute, out uri))
{
return true;
}
using (var client = new WebClient())
{
try
{
using (var stream = client.OpenRead(uri))
{
Image.FromStream(stream);
return true;
}
}
catch (Exception)
{
return false;
}
}
}
I am using Spire.doc for creating a Word file, and I followed their example like this
public class WordController : Controller
{
public void Download()
{
Document doc = new Document();
Paragraph test = doc.AddSection().AddParagraph();
test.AppendText("This is a test");
doc.SaveToFile("Doc.doc");
try
{
System.Diagnostics.Process.Start("Doc.doc");
}catch(Exception)
{
}
}
}
This opens the Word file in Microsoft Word, but how can I make it so that it's downloaded instead?
I've used return File() to return a PDF document to the View before, but it doesn't work with this.
Could you please try the below code and let me know if it worked or not, cos I didn't executed this code but believe this should work, I modified my existing working code according to your requirement-
public class WordController : Controller
{
public void Download()
{
byte[] toArray = null;
Document doc = new Document();
Paragraph test = doc.AddSection().AddParagraph();
test.AppendText("This is a test");
using (MemoryStream ms1 = new MemoryStream())
{
doc.SaveToStream(ms1, FileFormat.Doc);
//save to byte array
toArray = ms1.ToArray();
}
//Write it back to the client
Response.ContentType = "application/msword";
Response.AddHeader("content-disposition", "attachment; filename=Doc.doc");
Response.BinaryWrite(toArray);
Response.Flush();
Response.End();
}
}
Load file .docx to richtextbox.rtf ( using Spire.Doc ):
byte[] toArray = null;
//Paragraph test = doc.AddSection().AddParagraph();
//test.AppendText("This is a test") --> this also works ;
Document doc = new Document();
doc.LoadFromFile("C://Users//Mini//Desktop//doc.docx");
// or - Document doc = new Document("C://Users//Mini//Desktop//doc.docx");
using (MemoryStream ms1 = new MemoryStream())
{
doc.SaveToStream(ms1, FileFormat.Rtf);
toArray = ms1.ToArray();
richTextBox1.Rtf = System.Text.Encoding.UTF8.GetString(toArray);
}
I am using below code to create and it will show user prompt to user whether the user can able to save or open or cancel a excel file.....
I am successfully able to download the file but I need to zip before it is showing user prompt, Later zip file will be showed to the user like with options open or save or cancel.....
How can I do that with not using any other third party library and using Microsoft own Gzip DLL?
The below code is for exporting to excel functionality:
public ActionResult ExportToExcel()
{
byte[] file;
string targetFilename = string.Format("{0}-{1}.xlsx", "Generated", "excel");
DataTable dt = common.CreateExcelFile.ListToDataTable(GetSearchDraftPRResults());
common.CreateExcelFile excelFileForExport = new CreateExcelFile();
file = excelFileForExport.CreateExcelDocumentAsStream(dt, targetFilename);
Response.Buffer = true;
return File(file, "application/vnd.ms-excel", targetFilename);
}
Would anyone please help on this how to zip a file before it is showing to user?
Many thanks in advance.....
Modified Code:
public ActionResult ExportToExcel()
{
byte[] file;
string targetFilename = string.Format("{0}-{1}.xlsx", "Generated", "excel");
DataTable dt = common.CreateExcelFile.ListToDataTable(GetSearchDraftPRResults());
common.CreateExcelFile excelFileForExport = new CreateExcelFile();
file = excelFileForExport.CreateExcelDocumentAsStream(dt, targetFilename);
Response.Buffer = true;
byte[] zipFile = Compress(file);
return File(file, "application/vnd.ms-excel", targetFilename);
}
public byte[] Compress(FileInfo fileToCompress)
{
using (FileStream originalFileStream = fileToCompress.OpenRead())
{
if ((System.IO.File.GetAttributes(fileToCompress.FullName) & FileAttributes.Hidden) != FileAttributes.Hidden & fileToCompress.Extension != ".gz")
{
using (FileStream compressedFileStream = System.IO.File.Create(fileToCompress.FullName + ".gz"))
{
using (GZipStream compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress))
{
originalFileStream.CopyTo(compressionStream);
}
}
}
MemoryStream mem = new MemoryStream();
CopyStream(originalFileStream, mem);
return mem.ToArray();
}
}
public static void CopyStream(Stream input, Stream output)
{
byte[] b = new byte[32768];
int r;
while ((r = input.Read(b, 0, b.Length)) > 0)
output.Write(b, 0, r);
}
Check out the SharpZipLib library. It works very well and is free to use even in commercial applications.
You can use JZlib from JCraft. Very easy to use, compression declaration can look like this, the code inside depends on what's you doing but you can find working example in JZlib examples:
public byte[] compress(byte[] buf, int start, int[] len) {
...
}