I have a process that accepts data from an HTML page, and stamps that data onto a PDF and saves the PDF to a 3rd party imaging system.
The HTML page accepts info for up to 8 users. However, the PDF only has room for up to 4 users. So if info for more than 4 is entered, I need to fill out the PDF twice and merge those two PDFs into one file to submit to the imaging system.
I've tried nesting streams, pdfReaders, pdfStampers, but the resulting PDF is always corrupt. Any help would be greatly appreciated. Thanks in advance.
Here's the code I have currently that handles stamping the PDF if there are 1 - 4 users entered:
var pdfTemplate = ConfigurationManager.AppSettings["PdfFileLocation"] + formName + ".pdf";
var pdfReader = new PdfReader(pdfTemplate);
using (var stream = new MemoryStream())
{
using (var pdfStamper = form == null
? new PdfStamper(pdfReader, stream, '\0', false)
: processType == 1 // printing form
? new PdfStamper(pdfReader, new FileStream(Path.GetTempPath() + "UserForm_" + UserNumber + ".pdf", FileMode.Create)) // temporarily save form
: new PdfStamper(pdfReader, stream, '\0', false)) // use a stream if submitting
{
var overContent1 = pdfStamper.GetOverContent(1);
var overContent2 = pdfStamper.GetOverContent(2);
var overContent3 = pdfStamper.GetOverContent(3);
var overContent4 = pdfStamper.GetOverContent(4);
var pdfFormFields = pdfStamper.AcroFields;
... Code to fill out other pages of the PDF goes here ...
#region User Form Page #3
foreach (var user in users)
{
var eligibility = _sharedFormService.GetEligibility(user.Eligibility);
pdfFormFields.SetField("User" + nameof(user.Name) + user.Id, user.Name);
pdfFormFields.SetField("User" + nameof(user.Eligibility) + user.Id, eligibility);
pdfFormFields.SetField("User" + nameof(user.Title) + user.Id, user.Title);
pdfFormFields.SetField("User" + nameof(user.Type) + user.Id, user.Type);
pdfFormFields.SetField("User" + nameof(user.FamilyMemberName) + user.Id, user.FamilyMemberName);
try
{
if (user.Signature != null)
{
var sigImage = _imageHelpers.LoadImage(user.Signature);
var image = Image.GetInstance(sigImage, System.Drawing.Imaging.ImageFormat.Gif);
var sigPostitions = pdfFormFields.GetFieldPositions("User" + nameof(user.Signature) + user.Id)[0].position;
image.Transparency = new int[] { 255, 255 };
image.SetAbsolutePosition(sigPostitions.Left, sigPostitions.Bottom);
image.ScalePercent(15);
overContent3.AddImage(image);
}
}
catch (DocumentException dex)
{
response.Success = false;
response.ErrorMessage += "StampPdf - UserSignature" + user.Id + ": " + (dex.InnerException != null
? dex.InnerException.Message
: dex.Message) + ", ";
}
catch (IOException ioex)
{
response.Success = false;
response.ErrorMessage += "StampPdf - UserSignature" + user.Id + ": " + (ioex.InnerException != null
? ioex.InnerException.Message
: ioex.Message) + ", ";
}
}
#endregion
pdfStamper.FormFlattening = true;
}
response.Stream = stream.ToArray();
}
After a few more days of Googling, I finally found the answer I was looking for from the iText FAQ Site, under "Merging identical forms (having identical fields)" (http://developers.itextpdf.com/question/how-merge-forms-different-files-one-pdf)
Here's the code I ended up using:
public static byte[] ManipulatePdf(string src, string dest)
{
var ms = new MemoryStream();
Document document = new Document();
PdfCopy copy = new PdfSmartCopy(document, ms);
copy.SetMergeFields();
document.Open();
List<PdfReader> readers = new List<PdfReader>();
for (int i = 0; i < 2;)
{
PdfReader reader = new PdfReader(RenameFields(src, ++i));
readers.Add(reader);
copy.AddDocument(reader);
}
document.Close();
foreach (var reader in readers)
{
reader.Close();
}
return ms.ToArray();
}
public static byte[] RenameFields(String src, int i)
{
MemoryStream baos = new MemoryStream();
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, baos);
AcroFields form = stamper.AcroFields;
var keys = new HashSet<string>(form.Fields.Keys);
foreach (var key in keys)
{
form.RenameField(key, string.Format("{0}_{1}", key, i));
}
stamper.Close();
reader.Close();
return baos.ToArray();
}
Related
I have generated a pdf file from html and now I need to save it to a folder in my project.
I am able to get the generated pdf to download locally but when I send it to the file folder it gets corrupted and will not open.
public void CreateHTML(ComplaintIntakeData cd)
{
string formHtml = "<table class=\"MsoNormal\">";
string complaintTypeHtml = PCSUtilities.GetComplaintTypeHTML(cd);
string providerHtml = "";
if (cd.Providers != null && cd.Providers.Count > 0)
{
providerHtml = PCSUtilities.GetProviderHTML(cd);
}
string facilityHtml = PCSUtilities.GetFacilityHTML(cd);
string anonymousHtml = PCSUtilities.GetAnonymousHTML(cd);
string contactHtml = PCSUtilities.GetContactHTML(cd);
string patientHtml = PCSUtilities.GetPatientHTML(cd);
string detailsHtml = PCSUtilities.GetComplaintDetailsHTML(cd);
formHtml = formHtml + complaintTypeHtml + providerHtml + facilityHtml + anonymousHtml + contactHtml + patientHtml + detailsHtml + "</table>";
formHtml = formHtml.Replace("''", "\"");
// Load HTML template for letter and replace template fields with provider data
string htmlContent = File.ReadAllText(Server.MapPath("~/ComplaintIntakeForm.html"));
htmlContent = htmlContent.Replace("<%ComplaintInformation%>", formHtml);
string fileName = "ComplaintIntakeFile_" + cd.ComplaintGuid.ToString() +".pdf";
using (MemoryStream memStream = new MemoryStream())
{
try
{
// Load up a new PDF doc.
iTextSharp.text.Document pdfDoc = new iTextSharp.text.Document(PageSize.LETTER);
PdfWriter writer = PdfWriter.GetInstance(pdfDoc, memStream);
// writer.CompressionLevel = PdfStream.NO_COMPRESSION;
// Make document tagged PDFVERSION_1_7
writer.SetPdfVersion(PdfWriter.PDF_VERSION_1_7);
writer.SetTagged();
// Set document metadata
writer.ViewerPreferences = PdfWriter.DisplayDocTitle;
pdfDoc.AddLanguage("en-US");
pdfDoc.AddTitle("Complaint Intake Form");
writer.CreateXmpMetadata();
pdfDoc.Open();
var tagProcessors = (DefaultTagProcessorFactory)Tags.GetHtmlTagProcessorFactory();
//tagProcessors.RemoveProcessor(HTML.Tag.IMG);
//tagProcessors.AddProcessor(HTML.Tag.IMG, new CustomImageTagProcessor());
var cssFiles = new CssFilesImpl();
cssFiles.Add(XMLWorkerHelper.GetInstance().GetDefaultCSS());
var cssResolver = new StyleAttrCSSResolver(cssFiles);
var charset = Encoding.UTF8;
var context = new HtmlPipelineContext(new CssAppliersImpl(new XMLWorkerFontProvider()));
context.SetAcceptUnknown(true).AutoBookmark(true).SetTagFactory(tagProcessors);
var htmlPipeline = new HtmlPipeline(context, new PdfWriterPipeline(pdfDoc, writer));
var cssPipeline = new CssResolverPipeline(cssResolver, htmlPipeline);
var worker = new XMLWorker(cssPipeline, true);
var xmlParser = new XMLParser(true, worker, charset);
try
{
using (var sr = new StringReader(htmlContent))
{
xmlParser.Parse(sr);
// xmlParser.Flush();
}
}
catch (Exception e)
{
Response.Write(e.Message);
}
pdfDoc.Close();
//writer.Close();
///this creates a pdf download.
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment; filename=" + fileName);
Response.OutputStream.Write(memStream.GetBuffer(), 0, memStream.GetBuffer().Length);
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show("The following error occurred:\n" + ex.ToString());
}
}
}
when I add the following to create the file. The file is created but it is corrupted and cannot open as a pdf
using (FileStream file = new FileStream(Server.MapPath("~/tempFiles/") + fileName, FileMode.CreateNew))
{
byte[] bytes = new byte[memStream.Length];
memStream.Read(bytes, 0, memStream.Length);
file.Write(bytes, 0, bytes.Length);
}
I think this answer is relevant:
Copy MemoryStream to FileStream and save the file?
Your two code snippets use both memStream and memString and without seeing all the code in context, I'm guessing. Assuming memStream is a Stream that contains the contents of your PDF, you can write it to a file like this:
using (FileStream file = new FileStream(Server.MapPath("~/tempFiles/") + fileName, FileMode.CreateNew))
{
memStream.Position = 0;
memStream.CopyTo(file);
}
I wound up moving the new FileStream call into the PdfWriter.GetInstance method in place of the memStream variable. And was able to get rid of the using filestream code all together.
PdfWriter writer = PdfWriter.GetInstance(pdfDoc, new FileStream(Server.MapPath(path)+"/" + fileName, FileMode.CreateNew));
I'm working with asp core mvc project. I would like to open the client default mail with a iTextSharp output as an attachment for the email, is this possible using IEmailSender or any other reference?
and if i can empty the fields to,from and keep only the attached file and subject.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> GeneratePDF(int? id)
{
var webRootPath = _hostingEnvironment.WebRootPath;
var path = Path.Combine(webRootPath, "DataDump"); //folder name
var story = await _db.Story.Include(s => s.Child).Include(s => s.Sentences).ThenInclude(s => s.Image).FirstOrDefaultAsync(s => s.StoryId == id);
using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
{
iTextSharp.text.Document document = new iTextSharp.text.Document(iTextSharp.text.PageSize.A4, 10, 10, 10, 10);
PdfWriter writer = PdfWriter.GetInstance(document, memoryStream);
document.Open();
string usedFont = Path.Combine(webRootPath + "\\Fonts\\", "Dubai-Light.TTF");
BaseFont bf = BaseFont.CreateFont(usedFont, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
iTextSharp.text.Font titleFont = new iTextSharp.text.Font(bf, 20);
iTextSharp.text.Font sentencesFont = new iTextSharp.text.Font(bf, 15);
iTextSharp.text.Font childNamewFont = new iTextSharp.text.Font(bf, 17);
PdfPTable T = new PdfPTable(1);
//Hide the table border
T.DefaultCell.BorderWidth = 0;
T.DefaultCell.HorizontalAlignment = 1;
//Set RTL mode
T.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
//Add our text
if (story.Title != null)
{
T.AddCell(new iTextSharp.text.Paragraph(story.Title, titleFont));
}
if (story.Child != null)
{
if (story.Child.FirstName != null && story.Child.LastName != null)
{
T.AddCell(new iTextSharp.text.Phrase(story.Child.FirstName + story.Child.LastName, childNamewFont));
}
}
if (story.Sentences != null)
{
foreach (var item in story.Sentences)
{
if (item.Image != null)
{
var file = webRootPath + item.Image.ImageSelected;
byte[] fileBytes = System.IO.File.ReadAllBytes(file);
iTextSharp.text.Image pic = iTextSharp.text.Image.GetInstance(fileBytes);
pic.ScaleAbsoluteWidth(25f);
T.AddCell(pic);
}
else
{
T.AddCell(new iTextSharp.text.Phrase("no image", sentencesFont));
}
T.AddCell(new iTextSharp.text.Phrase(item.SentenceText, sentencesFont));
}
}
document.Add(T);
document.Close();
byte[] bytes = memoryStream.ToArray();
var fileName = path + "\\PDF" + DateTime.Now.ToString("yyyyMMdd-HHMMss") + ".pdf";
using (FileStream fs = new FileStream(fileName, FileMode.Create))
{
fs.Write(bytes, 0, bytes.Length);
}
memoryStream.Close();
//Send generated pdf as attchment
//Remove form root
if (System.IO.File.Exists(fileName))
{
System.IO.File.Delete(fileName);
}
}
return RedirectToAction("Details", new { id = id });
}
................................................................................................
Thanks in advance
This is not at all possible - and it would be a big security hole if web applications could open our default email client with random files attached (!).
The mailto protocol allows you to set the following properties only:
subject: Text to appear in the subject line of the message.
body: Text to appear in the body of the message.
CC: Addresses to be included in the "cc" (carbon copy) section of the message.
BCC: Addresses to be included in the "bcc" (blind carbon copy) section of the message.
One possible idea would be to allow your users to upload files to your website first, and you create a mailto: link that includes the URL of the file they uploaded in the body of the email message.
I have a function in my controller which receives an object and uses the object's data to fill a PDF. I want to send this PDF to the client so he can download it.However When I submit the form and download the generated PDF i get an error saying "ERROR: Failed to load PDF document."
Here is my code so far:
private void crear_pdf_ZURICH(Seguro seguro)
{
var nombre_pdf = seguro.Nombre + ".pdf";
string oldFile = Server.MapPath("pdf") + "\\" + nombre_pdf;
string newFile = Server.MapPath("pdf") + "\\" + "nuevo_" + nombre_pdf;
using (var reader = new PdfReader(oldFile))
{
using (var fileStream = new FileStream(newFile, FileMode.Create, FileAccess.Write))
{
var document = new Document(reader.GetPageSizeWithRotation(1));
var writer = PdfWriter.GetInstance(document, fileStream);
document.Open();
//VARIABLES
for (var i = 1; i <= reader.NumberOfPages; i++)
{
document.NewPage();
var baseFont = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
var importedPage = writer.GetImportedPage(reader, i);
var contentByte = writer.DirectContent;
contentByte.AddTemplate(importedPage, 0, 0);
contentByte.BeginText();
contentByte.SetFontAndSize(baseFont, 10);
if (reader.NumberOfPages - (reader.NumberOfPages - i) == 1)
{
//WRITE SOME STUFF
}
else if (reader.NumberOfPages - (reader.NumberOfPages - i) == 2)
{
//WRITE SOME OTHER STUFF
}
contentByte.EndText();
}
document.Close();
writer.Close();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", string.Format("attachment;filename="+newFile));
}
}
}
And this is my code working properly, it creates the PDF and I can open it without any problem, however I'm saving it on the server which can cause problems if multiple users are trying to create and download the generated PDF directly from the server.
Here is my code:
private void crear_pdf_ZURICH(Seguro seguro)
{
var nombre_pdf = seguro.Nombre + ".pdf";
string oldFile = Server.MapPath("pdf") + "\\" + nombre_pdf;
string newFile = Server.MapPath("pdf") + "\\" + "nuevo_" + nombre_pdf;
using (var reader = new PdfReader(oldFile))
{
using (var fileStream = new FileStream(newFile, FileMode.Create, FileAccess.Write))
{
var document = new Document(reader.GetPageSizeWithRotation(1));
var writer = PdfWriter.GetInstance(document, fileStream);
document.Open();
//VARIABLES
for (var i = 1; i <= reader.NumberOfPages; i++)
{
document.NewPage();
var baseFont = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
var importedPage = writer.GetImportedPage(reader, i);
var contentByte = writer.DirectContent;
contentByte.AddTemplate(importedPage, 0, 0);
contentByte.BeginText();
contentByte.SetFontAndSize(baseFont, 10);
if (reader.NumberOfPages - (reader.NumberOfPages - i) == 1)
{
//WRITE SOME STUFF
}
else if (reader.NumberOfPages - (reader.NumberOfPages - i) == 2)
{
//WRITE SOME STUFF
}
contentByte.EndText();
}
document.Close();
writer.Close();
}
}
}
Thanks in advance if someone can help/explain me how to solve this issue.
I need to add text to the lower right corner of a pdf. I was able to successfully do this with an annotation but I couldn't flatten it so I attempted to add the text via a paragraph instead. Now none of my text is showing up. Could anyone point out where I'm going wrong with my code below? (Or maybe just tell me how to flatten an annotation... the commented out portion works fine I just need it flattened)
private MemoryStream AddPageNumbers(MemoryStream stream)
{
MemoryStream ms = new MemoryStream();
PdfStamper Stamper = null;
PdfReader Reader = new PdfReader(stream);
try
{
PdfCopyFields Copier = new PdfCopyFields(ms);
Copier.AddDocument(Reader);
Copier.Close();
PdfReader docReader = new PdfReader(ms.ToArray());
ms = new MemoryStream();
Stamper = new PdfStamper(docReader, ms);
for (int i = 1; i <= Reader.NumberOfPages; i++)
{
//iTextSharp.text.Rectangle newRectangle = new iTextSharp.text.Rectangle(315, 19, 560, 33);
//var pcb = new iTextSharp.text.pdf.PdfContentByte(Stamper.Writer);
//string PageNumber = "Confirmation of Completion Report " + i.ToString() + " of " + Reader.NumberOfPages.ToString();
//FontFactory.RegisterDirectories();
//Font fontNormalArial = new Font(FontFactory.GetFont("Arial", 8f, Font.NORMAL));
//Paragraph para = new Paragraph(PageNumber, fontNormalArial);
//var annot = iTextSharp.text.pdf.PdfAnnotation.CreateFreeText(Stamper.Writer, newRectangle, para.Content, pcb);
//annot.Flags = iTextSharp.text.pdf.PdfAnnotation.FLAGS_PRINT;
//annot.BorderStyle = new iTextSharp.text.pdf.PdfBorderDictionary(0, 0);
//Stamper.AddAnnotation(annot, i);
//Stamper.FreeTextFlattening = true;
//Stamper.FormFlattening = true;
PdfContentByte cb = new PdfContentByte(Stamper.Writer);
string PageNumber = "Confirmation of Completion Report " + i.ToString() + " of " + Reader.NumberOfPages.ToString();
FontFactory.RegisterDirectories();
Font fontNormalArial = new Font(FontFactory.GetFont("Arial", 10, Font.NORMAL));
Paragraph para = new Paragraph(PageNumber, fontNormalArial);
para.Alignment = Element.ALIGN_RIGHT;
ColumnText ct = new ColumnText(cb);
ct.AddText(para);
ct.SetSimpleColumn(315, 19, 560 , 38);
ct.SetIndent(316, false);
ct.Go();
}
}
catch (Exception ex)
{
string querystring = "?error=" + ex.Message.ToString();
Response.Redirect("~/ErrorPage.aspx" + querystring);
}
finally
{
if (Stamper != null)
{
Stamper.Close();
}
}
return ms;
}
I'm generating a PDF document based on template. The document has multiple pages. The document can have about 5000 pages. When creating the 500-th page I get an overflow RAM (memory). Any idea?
public static void CreateBankBlank2012Year(string pdfTemplatePath, string directoryOutPdf, string nameOutPdf, AnnualReportsFilterParameters filterParametrs, string serverPath)
{
// Get details salary
IEnumerable<SalayDetailsForPdf> dataSalaryDetails = (IEnumerable<SalayDetailsForPdf>) GetSalaryData(filterParametrs);
String fontPath = Path.Combine(serverPath + "\\Fonts", "STSONG.ttf");
Font font = FontFactory.GetFont(fontPath, BaseFont.IDENTITY_H, 8);
using (Document document = new Document())
{
using (PdfSmartCopy copy = new PdfSmartCopy(
document, new FileStream(directoryOutPdf + nameOutPdf, FileMode.Create))
)
{
document.Open();
foreach (var data in dataSalaryDetails)
{
PdfReader reader = new PdfReader(pdfTemplatePath + #"\EmptyTemplateBankBlank_2012.pdf");
using (var ms = new MemoryStream())
{
using (PdfStamper stamper = new PdfStamper(reader, ms))
{
stamper.AcroFields.AddSubstitutionFont(font.BaseFont);
AcroFields form = stamper.AcroFields;
form.SetField("t1_address1", data.Address1);
form.SetField("t1_name", data.NameHieroglyphic);
// Other field ...
stamper.FormFlattening = true;
}
reader = new PdfReader(ms.ToArray());
copy.AddPage(copy.GetImportedPage(reader, 1));
}
}
}
}
}
p.s
I'm trying resolve my problem as follow:
generating empty pages based on template
private static void GeneratePdfFromTemplate(string directoryOutPdf, string nameOutPdf, string pdfTemplatePath, int countPages)
{
using (Document document = new Document())
{
using (PdfSmartCopy copy = new PdfSmartCopy(
document, new FileStream(directoryOutPdf + nameOutPdf, FileMode.Create))
)
{
document.Open();
PdfReader reader = new PdfReader(pdfTemplatePath + #"\EmptyTemplateBankBlank_2012.pdf");
for (int i = 0; i < countPages; i++)
{
copy.AddPage(copy.GetImportedPage(reader, 1));
}
reader.Close();
copy.Close();
}
document.Close();
}
GC.Collect();
}
But after a generating I can't set values to the fields.
I'm found solution for my problem, if anyone else would be interested :
private static void SettingFieltValue(Font font, IEnumerable<SalayDetailsForPdf> dataSalaryDetails, int selectedYear, string directoryOutPdf, string nameOutPdf, string pdfTemplatePath)
{
string pdfTemplate = pdfTemplatePath + #"\EmptyTemplateBankBlank_2012.pdf";
string newFile = directoryOutPdf + nameOutPdf;
var fs = new FileStream(newFile, FileMode.Create);
var conc = new PdfConcatenate(fs, true);
foreach (var data in dataSalaryDetails)
{
var reader = new PdfReader(pdfTemplate);
using (var ms = new MemoryStream())
{
using (PdfStamper stamper = new PdfStamper(reader, ms))
{
stamper.AcroFields.AddSubstitutionFont(font.BaseFont);
AcroFields form = stamper.AcroFields;
form.SetField("t1_name", data.NameHieroglyphic);
//Other field
stamper.FormFlattening = true;
stamper.Close();
}
reader = new PdfReader(ms.ToArray());
ms.Close();
}
conc.AddPages(reader);
reader.Close();
}
conc.Close();
}