SelectPDF.Document.Footer is always null? - c#

Creating a PDF document from the stream of a HTTP request.
public class HomeController : Controller {
public HomeController() {
converter = new HtmlToPdf();
InitializeConverter();
}
public void Index() {
ConvertHtmlToPdf(new Uri("http://localhost:52328/CertificateOfOrigin?noCertificate=2691"));
}
public void ConvertHtmlToPdf(Uri toConvert) {
if(toConvert == null) throw new ArgumentNullException(nameof(toConvert));
using(var stream =new MemoryStream()) {
var doc = converter.ConvertUrl(toConvert.AbsoluteUri);
// The doc.AddTemplate returns a PdfTemplate and should be assigned to doc.Footer
doc.Footer = doc.AddTemplate(doc.Pages[0].ClientRectangle.Width, 100);
var pageNumbering = new PdfTextElement(20, 50, "Page {page_number} of {total_pages}", doc.Fonts[0], Color.Black);
// Once template defined, I add it to the doc Footer. But...
doc.Footer.Add(pageNumbering); // Throws a NullPointerException?
doc.Footer = template;
doc.Save(stream);
doc.Close();
using(var ms = new MemoryStream(stream.ToArray())) {
Response.AddHeader("content-disposition", "filename=certificate-of-origin.pdf");
Response.ContentType = "application/pdf";
ms.CopyTo(Response.OutputStream);
Response.End();
Response.Close();
}
}
}
private void InitializeConverter() {
converter.Options.MarginBottom = 0;
converter.Options.MarginLeft = 0;
converter.Options.MarginRight = 0;
converter.Options.MarginTop = 0;
converter.Options.PdfPageSize = PdfPageSize.Letter;
}
private readonly HtmlToPdf converter;
}
I put a breakpoint and quick watched the return of doc.AddTemplate method call and it returns an actual PdfTemplate no problem!
Other than that, everything works fine. Document is generated no problem, except when I uncomment the page numbering because the doc.Footer remains null despite its assignment.
Could it be a bug? Idk.

You need to either set the header/footer content before the conversion, like here:
https://selectpdf.com/demo-mvc/HtmlToPdfHeadersAndFooters
using System;
using System.Web.Mvc;
namespace SelectPdf.Samples.Controllers
{
public class HtmlToPdfHeadersAndFootersController : Controller
{
// GET: HtmlToPdfHeadersAndFooters
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult SubmitAction(FormCollection collection)
{
// get parameters
string headerUrl = Server.MapPath("~/files/header.html");
string footerUrl = Server.MapPath("~/files/footer.html");
bool showHeaderOnFirstPage = collection["ChkHeaderFirstPage"] == "on";
bool showHeaderOnOddPages = collection["ChkHeaderOddPages"] == "on";
bool showHeaderOnEvenPages = collection["ChkHeaderEvenPages"] == "on";
int headerHeight = 50;
try
{
headerHeight = Convert.ToInt32(collection["TxtHeaderHeight"]);
}
catch { }
bool showFooterOnFirstPage = collection["ChkFooterFirstPage"] == "on";
bool showFooterOnOddPages = collection["ChkFooterOddPages"] == "on";
bool showFooterOnEvenPages = collection["ChkFooterEvenPages"] == "on";
int footerHeight = 50;
try
{
footerHeight = Convert.ToInt32(collection["TxtFooterHeight"]);
}
catch { }
// instantiate a html to pdf converter object
HtmlToPdf converter = new HtmlToPdf();
// header settings
converter.Options.DisplayHeader = showHeaderOnFirstPage ||
showHeaderOnOddPages || showHeaderOnEvenPages;
converter.Header.DisplayOnFirstPage = showHeaderOnFirstPage;
converter.Header.DisplayOnOddPages = showHeaderOnOddPages;
converter.Header.DisplayOnEvenPages = showHeaderOnEvenPages;
converter.Header.Height = headerHeight;
PdfHtmlSection headerHtml = new PdfHtmlSection(headerUrl);
headerHtml.AutoFitHeight = HtmlToPdfPageFitMode.AutoFit;
converter.Header.Add(headerHtml);
// footer settings
converter.Options.DisplayFooter = showFooterOnFirstPage ||
showFooterOnOddPages || showFooterOnEvenPages;
converter.Footer.DisplayOnFirstPage = showFooterOnFirstPage;
converter.Footer.DisplayOnOddPages = showFooterOnOddPages;
converter.Footer.DisplayOnEvenPages = showFooterOnEvenPages;
converter.Footer.Height = footerHeight;
PdfHtmlSection footerHtml = new PdfHtmlSection(footerUrl);
footerHtml.AutoFitHeight = HtmlToPdfPageFitMode.AutoFit;
converter.Footer.Add(footerHtml);
// add page numbering element to the footer
if (collection["ChkPageNumbering"] == "on")
{
// page numbers can be added using a PdfTextSection object
PdfTextSection text = new PdfTextSection(0, 10,
"Page: {page_number} of {total_pages} ",
new System.Drawing.Font("Arial", 8));
text.HorizontalAlign = PdfTextHorizontalAlign.Right;
converter.Footer.Add(text);
}
// create a new pdf document converting an url
PdfDocument doc = converter.ConvertUrl(collection["TxtUrl"]);
// custom header on page 3
if (doc.Pages.Count >= 3)
{
PdfPage page = doc.Pages[2];
PdfTemplate customHeader = doc.AddTemplate(
page.PageSize.Width, headerHeight);
PdfHtmlElement customHtml = new PdfHtmlElement(
"<div><b>This is the custom header that will " +
"appear only on page 3!</b></div>",
string.Empty);
customHeader.Add(customHtml);
page.CustomHeader = customHeader;
}
// save pdf document
byte[] pdf = doc.Save();
// close pdf document
doc.Close();
// return resulted pdf document
FileResult fileResult = new FileContentResult(pdf, "application/pdf");
fileResult.FileDownloadName = "Document.pdf";
return fileResult;
}
}
}
Or use this approach, to add headers/footers to an already generated pdf:
https://selectpdf.com/demo-mvc/ExistingPdfHeadersAndFooters
using System.Web.Mvc;
using System.Drawing;
namespace SelectPdf.Samples.Controllers
{
public class ExistingPdfHeadersAndFootersController : Controller
{
// GET: ExistingPdfHeadersAndFooters
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult SubmitAction(FormCollection collection)
{
// the test file
string filePdf = Server.MapPath("~/files/selectpdf.pdf");
string imgFile = Server.MapPath("~/files/logo.png");
// resize the content
PdfResizeManager resizer = new PdfResizeManager();
resizer.Load(filePdf);
// add extra top and bottom margins
resizer.PageMargins = new PdfMargins(0, 0, 90, 40);
// add the header and footer to the existing (now resized pdf document)
PdfDocument doc = resizer.GetDocument();
// header template (90 points in height) with image element
PdfTemplate header = doc.AddTemplate(doc.Pages[0].ClientRectangle.Width, 90);
PdfImageElement img1 = new PdfImageElement(10, 10, imgFile);
header.Add(img1);
// footer template (40 points in height) with text element
PdfTemplate footer = doc.AddTemplate(new RectangleF(0,
doc.Pages[0].ClientRectangle.Height - 40,
doc.Pages[0].ClientRectangle.Width, 40));
// create a new pdf font
PdfFont font2 = doc.AddFont(PdfStandardFont.Helvetica);
font2.Size = 12;
PdfTextElement text1 = new PdfTextElement(10, 10,
"Generated by SelectPdf. Page number {page_number} of {total_pages}.",
font2);
text1.ForeColor = System.Drawing.Color.Blue;
footer.Add(text1);
// save pdf document
byte[] pdf = doc.Save();
// close pdf document
resizer.Close();
// return resulted pdf document
FileResult fileResult = new FileContentResult(pdf, "application/pdf");
fileResult.FileDownloadName = "Document.pdf";
return fileResult;
}
}
}
The best approach is the first, so try to move your footer setting before the conversion.

Related

Get Text From specific Layer from PDF

Here is the pdf sample with texts on the layer. If I turn off the layer all the text belong to this layer will be invisible also.
I need to get all the texts from the specific layer. Any body know how to archive this.
Here is my sample PDF file: https://drive.google.com/file/d/1TcRyE8MQRhw-j89BbovV7fFIwZ0yks0N/view?usp=sharing
My code can get all texts. But I don't know how to get texts belong any specific layer only.
public CreateHyperLinkButton(string inPutPDF, string outPutPDF, List<ViewPortInfo> ViewportInfos)
{
using (FileStream pdf = new FileStream(outPutPDF, FileMode.Create))
{
using (PdfReader pdfReader = new iTextSharp.text.pdf.PdfReader(inPutPDF))
{
using (PdfStamper pdfStamper = new iTextSharp.text.pdf.PdfStamper(pdfReader, pdf))
{
//Get Text list on 2D PDF
List<TextRenderInfo> listTextInfor = GetAllTextInfor(inPutPDF, pdfReader);
listTextInfor.ForEach(item =>{
string btnName = item.GetText().Trim();
//Check btnName exist in ViewportInfos
for (var i = 0; i < ViewportInfos.Count; i++)
{
string szRes = GetTextContained(ViewportInfos[i].Hyperlinks.Keys.ToList(), btnName);
if (!string.IsNullOrEmpty(szRes))
{
iTextSharp.text.Rectangle box = GetRectOfText(item);
iTextSharp.text.pdf.PushbuttonField btnField = new iTextSharp.text.pdf.PushbuttonField(pdfStamper.Writer, box, szRes);
iTextSharp.text.pdf.PdfAnnotation pushbutton = btnField.Field;
//Add JS function and button in annotation
string js = "mapView('" + szRes + "');";
pushbutton.SetAdditionalActions(iTextSharp.text.pdf.PdfName.U, iTextSharp.text.pdf.PdfAction.JavaScript(js, pdfStamper.Writer));
pdfStamper.AddAnnotation(pushbutton, 1);
}
}
});
pdfStamper.Close();
}
pdfReader.Close();
}
pdf.Close();
}
}
private static List<TextRenderInfo> GetAllTextInfor(string inPutPDF, PdfReader pdfReader)
{
List<TextRenderInfo> listTextInfor = new List<TextRenderInfo>();
TextExtractionStrategy allTextInfo = new TextExtractionStrategy();
for (int i = 1; i <= pdfReader.NumberOfPages; i++)
{
PdfTextExtractor.GetTextFromPage(pdfReader, i, allTextInfo);
}
listTextInfor = allTextInfo.textList;
return listTextInfor;
}
public class TextExtractionStrategy : ITextExtractionStrategy
{
public List<TextRenderInfo> textList = new List<TextRenderInfo>();
public void BeginTextBlock()
{
}
public void EndTextBlock()
{
}
public string GetResultantText()
{
return "";
}
public void RenderImage(ImageRenderInfo renderInfo)
{
var a = renderInfo;
}
public void RenderText(TextRenderInfo renderInfo)
{
textList.Add(renderInfo);
}
}
You could use ironpdf for this purpose. Parse/open the pdf as per the docs on their site and examine it in debug, then you can develop some code to retrieve text from that layer only.

Set programatically created ReportBook as HTML5 ReportSource

A user can select multiple orders, and download all the reports as one PDF.
We used PdfSmartCopy to merge the reports:
protected void Print(int[] order_ids)
{
byte[] merged_reports;
using (MemoryStream ms = new MemoryStream())
using (Document doc = new Document())
using (PdfSmartCopy copy = new PdfSmartCopy(doc, ms))
{
doc.Open();
foreach (string order_id in order_ids)
{
Telerik.Reporting.InstanceReportSource reportSource = new Telerik.Reporting.InstanceReportSource();
reportSource.ReportDocument = new OrderReport();
reportSource.Parameters.Add(new Telerik.Reporting.Parameter("order_id", order_id));
RenderingResult result = new ReportProcessor().RenderReport("PDF", reportSource, new Hashtable());
using (PdfReader reader = new PdfReader(result.DocumentBytes))
{
copy.AddDocument(reader);
}
}
doc.Close();
merged_reports = ms.ToArray();
}
Response.Clear();
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Expires = -1;
Response.Buffer = false;
Response.ContentType = "application/pdf";
Response.OutputStream.Write(merged_reports, 0, merged_reports.Length);
}
But we started using the HTML5 ReportViewer elsewhere and we want to use it there as well to be consistent. I thought of creating a ReportBook programmatically and set it as the ReportSource of the ReportViewer, but the only thing I can set is a string. We have already used ReportBook before, but this was an actual SomeReportBook.cs that we could set through new SomeReportBook().GetType().AssemblyQualifiedName;.
Any clue? Here is what I have at the moment:
protected void Print(int[] order_ids)
{
Telerik.Reporting.ReportBook reportBook = new Telerik.Reporting.ReportBook();
foreach (string order_id in order_ids)
{
Telerik.Reporting.InstanceReportSource reportSource = new Telerik.Reporting.InstanceReportSource();
reportSource.ReportDocument = new OrderReport();
reportSource.Parameters.Add(new Telerik.Reporting.Parameter("order_id", order_id));
reportBook.ReportSources.Add(reportSource);
}
this.ReportViewer.ReportSource = new Telerik.ReportViewer.Html5.WebForms.ReportSource()
{
Identifier = // Can't use reportBook.GetType().AssemblyQualifiedName
};
}
I have also struggled with this challenge for quite some time; I would to share in case
someone else faces such a challenge. Kindly do this.
1.Create a class that inherits from - Telerik.Reporting.ReportBook
2.Create a method that loads all your reports in your reportbook class i.e.
this.ReportSources.Add(new TypeReportSource
{
TypeName = typeof(Report1).AssemblyQualifiedName
});
Call you method in your class constructor
use the following code to set the report viewer source
var reportSource = new Telerik.ReportViewer.Html5.WebForms.ReportSource();
reportSource.IdentifierType = IdentifierType.TypeReportSource;
reportSource.Identifier = typeof(ReportCatalog).AssemblyQualifiedName;//or
namespace.class, assembly e.g. "MyReports.Report1, MyReportsLibrary"
reportSource.Parameters.Add("Parameter1", "Parameter1");
reportSource.Parameters.Add("Parameter2", "Parameter2");
ReportsViewer1.ReportSource = reportSource;
Report1 = Newly created class that inherits from Telerik.Reporting.ReportBook

get result from public static string

Is there a simple way to get the public static string result to the controller.
So im trying to make so when you upload you're image it gets resized.
i made a following code in my Toolbox
public static string ImageUpload(HttpPostedFileBase file, string SubFolder)
{
Guid gi = Guid.NewGuid();
string extension = Path.GetExtension(file.FileName);
using (var image = Image.FromStream(file.InputStream, true, true))
{
var thumbWidth = 700;
var thumbHeight = 700;
if (image.Width < image.Height)
{
//portrait image
thumbHeight = 640;
var imgRatio = (float)thumbHeight / (float)image.Height;
thumbWidth = Convert.ToInt32(image.Width * imgRatio);
}
else
if (image.Height < image.Width)
{
//landscape image
thumbWidth = 960;
var imgRatio = (float)thumbWidth / (float)image.Width;
thumbHeight = Convert.ToInt32(image.Height * imgRatio);
}
using (var thumb = image.GetThumbnailImage(
thumbWidth,
thumbHeight,
() => false,
IntPtr.Zero))
{
var jpgInfo = ImageCodecInfo.GetImageEncoders()
.Where(codecInfo => codecInfo.MimeType == "image/jpeg").First();
using (var encParams = new EncoderParameters(1))
{
string thumbPath = "~/Content/admin/images/" + SubFolder;
bool isExists = System.IO.Directory.Exists(HttpContext.Current.Server.MapPath(thumbPath));
if (!isExists)
{
System.IO.Directory.CreateDirectory(HttpContext.Current.Server.MapPath(thumbPath));
}
var thumbPathFull = Path.Combine(HttpContext.Current.Server.MapPath(thumbPath), gi + extension);
string newfileurl = "/Content/admin/images/" + SubFolder + gi + extension;
long quality = 1500;
encParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
thumb.Save(thumbPathFull, jpgInfo, encParams);
return newfileurl;
}
}
}
so i want the value "return newfileurl;" to my controller so how do i call it. so i can save the the string to my db to get the image frontend
[HttpPost]
[ValidateInput(false)]
public ActionResult NewsCreate(NewsItem ni, HttpPostedFileBase file)
{
Guid gi = Guid.NewGuid();
if (file != null && file.ContentLength > 0)
{
CRUDHelper.ImageUpload(file, "newsimage");
ni.Image = **??????insert solution here??????**;
db.NewsItems.Add(ni);
db.SaveChanges();
}
CRUDHelper.ImageUpload(file, "newsimage"); already returns the result, you only have to assign it to something: ni.Image = CRUDHelper.ImageUpload(file, "newsimage");
– HimBromBeere

using RotateFlip to rotate bitmaps

I am having trouble rotating a scanned document that already has a qrcode on it.
I am checking for the qr code but when the code is upside down - i am having trouble flipping the page and trying to read for a qr code again. and i get this error message:
exception of type 'com.google.zxing.readerexception' was thrown
using (var fullImg = new Bitmap(workGif))
{
var halfW = fullImg.Width/2;
var halfH = fullImg.Height / 2;
var bandImg = fullImg.Clone(new System.Drawing.Rectangle(0, 0, halfW, halfH), fullImg.PixelFormat);
if (Process(bandImg) == null)
{
page.Rotate = (page.Rotate + 180) % 360;
}
string QRinfo = Process(bandImg);
MessageBox.Show(QRinfo);
string[] qcode = QRinfo.Split('/');
string gid = qcode[qcode.Count() - 1];
Guid pgGuid = new Guid(gid);
MessageBox.Show(QRinfo);
var ar = dc.Assessments.FirstOrDefault(c => c.ID == pgGuid);
if (ar != null)
{
var p = inputDocument.Pages[pg];
string opdName = FILESTORELOCATION + pgGuid.ToString() + ".pdf";
PdfDocument opd = new PdfDocument(opdName);
opd.Pages.Add(p);
opd.Close();
ar.StoragePath = opdName;
ar.LastUploadedDT = DateTime.UtcNow;
ar.UploadedByUserID = uploadingUser;
dc.SubmitChanges();
}
}
this is my process for decoding qr:
public string Process(Bitmap bitmap)
{
var reader = new com.google.zxing.qrcode.QRCodeReader();
try
{
LuminanceSource source = new RGBLuminanceSource(bitmap, bitmap.Width, bitmap.Height);
var binarizer = new HybridBinarizer(source);
var binBitmap = new BinaryBitmap(binarizer);
return reader.decode(binBitmap).Text;
}
catch (Exception e)
{
return e.Message;
}
}

Displaying headers and footers in a PDF generated by Rotativa

I'm trying to specify headers and footers in a PDF generated by Rotativa library. As the author answered here it should be possible using CSS (described here). However, I'm not able to do this.
I have a stylesheet loaded in the meta tag:
<link href="print.css" rel="stylesheet" type="text/css" media="print" />
And in the stylesheet at the bottom:
#page {
#top-left {
content: "TOP SECRET";
color: red
}
#bottom-right {
content: counter(page);
font-style: italic
}
}
And then generating PDF by:
public ActionResult ShowPdf()
{
var model = new Model();
return new ViewAsPdf("view.cshtml", model)
{
FileName = "Report.pdf",
CustomSwitches = "--print-media-type"
};
}
And then nothing appears in the header and footer of the PDF. Any ideas?
I've found a documentation of wkhtmltopdf (or a better archived version) and it is described there how to manage headers and footers.
Basically you can just add --header-center "text" (or similar switches) to the argument list and that's all.
So using it with Rotativa it would be:
public ActionResult ShowPdf()
{
var model = new Model();
return new ViewAsPdf("view.cshtml", model)
{
FileName = "Report.pdf",
CustomSwitches = "--print-media-type --header-center \"text\""
};
}
(I don't know if --print-media-type is necessary.)
If you wanted to display a View instead of text in the header/footer then you can do so like this:
public ActionResult ViewPDF()
{
string customSwitches = string.Format("--print-media-type --allow {0} --footer-html {0} --footer-spacing -10",
Url.Action("Footer", "Document", new { area = ""}, "https"));
return new ViewAsPdf("MyPDF.cshtml", model)
{
FileName = "MyPDF.pdf",
CustomSwitches = customSwitches
};
}
[AllowAnonymous]
public ActionResult Footer()
{
return View();
}
Don't forget to add the [AllowAnonymous] attribute on the Footer action otherwise Rotatina can't get access to the path.
Here is how I did it (in full):
public ActionResult PrintPDF(int? selectedSiteRotaId, int selectedSiteId)
{
string footer = "--footer-center \"Printed on: " + DateTime.Now.Date.ToString("MM/dd/yyyy") + " Page: [page]/[toPage]\"" + " --footer-line --footer-font-size \"9\" --footer-spacing 6 --footer-font-name \"calibri light\"";
return new ActionAsPdf("RenderPDF", new { selectedSiteRotaId = selectedSiteRotaId, selectedSiteId = 7 })
{
FileName = "PDF_Output.pdf",
PageOrientation = Orientation.Landscape,
MinimumFontSize = 10,
//PageMargins = new Margins(5,5,5,5),
PageSize = Size.A3,
CustomSwitches = footer
};
//var pdfResult = new ActionAsPdf("RenderPDF", new { selectedSiteRotaId = selectedSiteRotaId, selectedSiteId = 7 })
//{
// FileName = "PDF_Output.pdf",
// PageOrientation = Orientation.Landscape,
// MinimumFontSize = 10
//};
//var binary = pdfResult.BuildPdf(this.ControllerContext);
//return File(binary, "application/pdf");
}
public ActionResult RenderPDF(int? selectedSiteRotaId, int selectedSiteId)
{
return RedirectToAction("Index", "PrintPDF", new { selectedSiteRotaId = selectedSiteRotaId, selectedSiteId = 7 });
}
string customSwitches = string.Format("--header-spacing \"0\" --footer-spacing \"0\" --footer-html {0} ", Url.Action("Footer", "Invoice", new { }, "http"));
return new ViewAsPdf(saleInvoiceVm)
{
PageOrientation = Orientation.Portrait,
PageSize = Rotativa.AspNetCore.Options.Size.A4,
PageMargins = { Left = 5, Bottom = 25, Right = 7, Top = 10 },
CustomSwitches = customSwitches,
};
try it it will work 100 %
return new ViewAsPdf("MyPDF.cshtml", model)
{
FileName = "MyPDF.pdf",
CustomSwitches = customSwitches
};
}

Categories