itext7 + pdfHtml: how set portrait orientation and fit content on ConvertToPdf - itext

I did a simple html to pdf conversion getting a landscape orientation.
In the pdfHtml release notes I see that the default orientation should be portrait but I got a landscape.
I'm not able to find the option/parameter/setting to do it.
Probably it is in the ConverterProperties object hidden to my eyes :-(
Any suggestion?
Here is my very simple code
public byte[] HtmlToPdf(string html)
{
using (Stream htmlSource = new MemoryStream(Encoding.UTF8.GetBytes(html)))
using (MemoryStream pdfDest = new MemoryStream())
{
ConverterProperties converterProperties = new ConverterProperties();
HtmlConverter.ConvertToPdf(htmlSource, pdfDest, converterProperties);
return pdfDest.ToArray();
}
}
EDIT after answers (I got the right orientation!):
Now I have to find a way to scale down the content in order to fit the content and have the right margins without cutting the image.
public static byte[] HtmlToPdf(string html)
{
using (Stream htmlSource = new MemoryStream(Encoding.UTF8.GetBytes(html)))
using (MemoryStream pdfDest = new MemoryStream())
{
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(pdfDest));
pdfDocument.SetDefaultPageSize(PageSize.A4.Rotate());
ConverterProperties converterProperties = new ConverterProperties();
HtmlConverter.ConvertToPdf(htmlSource, pdfDocument, converterProperties);
return pdfDest.ToArray();
}
}
HTML result:
PDF result:

I solved changing the library from itext7 to DinkToPdf. I found it very simple to use and enough to my needs.
The MVC controller after changing the library to DinkToPdf
public byte[] PdfCreatorController(IConverter converter)
string html = await reader.ReadToEndAsync();
var globalSettings = new GlobalSettings
{
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
Margins = new MarginSettings { Top = 10 },
DocumentTitle = "Report",
Out = string.Empty,
};
var objectSettings = new ObjectSettings
{
PagesCount = true,
HtmlContent = html,
WebSettings = { DefaultEncoding = "utf-8", UserStyleSheet = Path.Combine(Directory.GetCurrentDirectory(), "assets", "styles.css") },
};
var pdf = new HtmlToPdfDocument()
{
GlobalSettings = globalSettings,
Objects = { objectSettings }
};
return = _converter.Convert(pdf);
}
PDF Result

Related

World map not dispalyed in crystal reports

I'm generating a pdf report using crystal report, I would like to use Data Map Tool
In c# code I've a dataset containing geographicals fields and some values to display in the map.
public class CrystalReportViewerPlugIn : ICrystalReportViewer
{
private ReportDocument _reportDocument;
private CrystalReportViewer _crystalReportViewer;
public void Init(string fileName, DataSet dataSet)
{
_reportDocument = new ReportDocument();
_reportDocument.Load(fileName);
_reportDocument.SetDataSource(dataSet);
_crystalReportViewer = new CrystalReportViewer();
_crystalReportViewer.DisplayToolbar = false;
_crystalReportViewer.DisplayGroupTree = false;
_crystalReportViewer.PageToTreeRatio = 4;
_crystalReportViewer.RefreshReport();
_crystalReportViewer.ReportSource = _reportDocument;
}
}
Then I export the result into a strem:
public MemoryStream GetCrystalReportResults(string rptFileName, DataSet ds)
{
var crystalReportViewer = new CrystalReportViewerPlugIn();
crystalReportViewer.PlugIn.Init(rptFileName, ds);
crystalReportViewer.PlugIn.Control.Visible = true;
var oStream = crystalReportViewer.PlugIn.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat);
var byteArray = new byte[oStream.Length];
oStream.Read(byteArray, 0, Convert.ToInt32(oStream.Length - 1));
return new MemoryStream(byteArray);
}
The stream is exported as pdf:
protected virtual IHttpActionResult FinalizeExport(MemoryStream data, string name)
{
string contentType = "application/octet-stream";
name = name.GetCleanFileName();
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StreamContent(data);
response.Content.Headers.Remove("content-type");
response.Content.Headers.Add("content-type", contentType);
response.Content.Headers.Remove("x-filename");
response.Content.Headers.Add("x-filename", name);
response.Content.Headers.Add("Content-Disposition", "inline; filename=\"" + name + "\"");
response.Content.Headers.Add("Content-Length", data.Length.ToString());
return ResponseMessage(response);
}
The world map is not displayed, do you have anny idea about this issue ?
Crystal report's map works only in 32 bits environment.

Set the iText 7 page size based on the HTML content

I have the following Java program to create an iText PDF:
//Create the PDF file
public int CreatePDF(String[] pSrc,
String pDest)
throws IOException
{
//Initialize
ConverterProperties vProperties = new ConverterProperties();
//Adding the fonts
FontProvider vfontProvider = new DefaultFontProvider(false, false, false);
for (String font : FONTS)
{
FontProgram vfontProgram = FontProgramFactory.createFont(font);
vfontProvider.addFont(vfontProgram);
}
vProperties.setFontProvider(vfontProvider);
PdfWriter vWriter = new PdfWriter(pDest, new WriterProperties().setCompressionLevel(9));
PdfDocument vPDF = new PdfDocument(vWriter);
PdfMerger vMerger = new PdfMerger(vPDF);
//Convert to PDF
for (String vHTML : pSrc)
{
ByteArrayOutputStream vArrByteAOS = new ByteArrayOutputStream();
PdfDocument vDoc = new PdfDocument(new PdfWriter(vArrByteAOS, new WriterProperties().setCompressionLevel(9)));
vDoc.setDefaultPageSize(new PageSize(735, 1080));
HtmlConverter.convertToPdf(vHTML, vDoc, vProperties);
vDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(vArrByteAOS.toByteArray())));
vMerger.merge(vDoc, 1, vDoc.getNumberOfPages());
vDoc.close();
}
vPDF.close();
return 0;
}
How can I set the Page Height (now hardcoded to 1080) based on the size of the HTML content (pSrc) in order to ensure that it fits to one page.

Empty content on the downloaded PDF using itextsharp in WebAPI 2 response

public IHttpActionResult DownloadPDF()
{
var stream = CreatePdf();
return ResponseMessage(new HttpResponseMessage
{
Content = new StreamContent(stream)
{
Headers =
{
ContentType = new MediaTypeHeaderValue("application/pdf"),
ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "myfile.pdf"
}
}
},
StatusCode = HttpStatusCode.OK
});
}
Here is the CreatePdf method:
private Stream CreatePdf()
{
using (var document = new Document(PageSize.A4, 50, 50, 25, 25))
{
var output = new MemoryStream();
var writer = PdfWriter.GetInstance(document, output);
writer.CloseStream = false;
document.Open();
document.Add(new Paragraph("Hello World"));
document.Close();
output.Seek(0, SeekOrigin.Begin);
return output;
}
}
I can able to download the PDF but the context is empty. Here I am using memory stream and I also tried with file stream its downloading in the respective folder but if I tried to open the downloaded file then also the content is empty. Can anyone help me what I'm missing here?
Here is an approach that usually works for me when using Web API
private byte[] CreatePdf() {
var buffer = new byte[0];
//stream to hold output data
var output = new MemoryStream();
//creation of a document-object
using (var document = new Document(PageSize.A4, 50, 50, 25, 25)) {
//create a writer that listens to the document
// and directs a PDF-stream to output stream
var writer = PdfWriter.GetInstance(document, output);
//open the document
document.Open();
// Create a page in the document
document.NewPage();
// Get the top layer to write some text
var pdfContentBytes = writer.DirectContent;
pdfContentBytes.BeginText();
//add content to page
document.Add(new Paragraph("Hello World"));
//done writing text
pdfContentBytes.EndText();
// make sure any data in the buffer is written to the output stream
writer.Flush();
document.Close();
}
buffer = output.GetBuffer();
return buffer;
}
And then in the action
public IHttpActionResult DownloadPDF() {
var buffer = CreatePdf();
return ResponseMessage(new HttpResponseMessage {
Content = new StreamContent(new MemoryStream(buffer)) {
Headers = {
ContentType = new MediaTypeHeaderValue("application/pdf"),
ContentDisposition = new ContentDispositionHeaderValue("attachment") {
FileName = "myfile.pdf"
}
}
},
StatusCode = HttpStatusCode.OK
});
}

Duplicate Fields using itextsharp

I have this code to create TextFields
public void MssCreateTextField(byte[] ssPdf, RCRectangleRecord ssRectangle, string ssName, int ssFontSize, string ssValue, int ssPage, out byte[] ssPdfOut, bool ssIsMultiline) {
PdfReader reader = new PdfReader(ssPdf);
ssPdfOut = null;
var output = new MemoryStream();
var stamper = new PdfStamper(reader, output);
/*TextField tField = new TextField(stamper.Writer, new iTextSharp.text.Rectangle((float)ssRectangle.ssSTRectangle.ssllx, (float)ssRectangle.ssSTRectangle.sslly, (float)ssRectangle.ssSTRectangle.ssurx, (float)ssRectangle.ssSTRectangle.ssury), ssName);
if (ssValue!="")
tField.Text = ssValue;
if (ssIsMultiline)
tField.Options = TextField.MULTILINE;
tField.FontSize = ssFontSize;*/
PdfFormField tField = PdfFormField.CreateTextField(stamper.Writer, ssIsMultiline, false, 50);
tField.FieldName = ssName;
tField.SetWidget(new iTextSharp.text.Rectangle((float)ssRectangle.ssSTRectangle.ssllx, (float)ssRectangle.ssSTRectangle.sslly, (float)ssRectangle.ssSTRectangle.ssurx, (float)ssRectangle.ssSTRectangle.ssury), PdfAnnotation.HIGHLIGHT_TOGGLE);
stamper.FormFlattening = false;
stamper.AddAnnotation(tField, ssPage);
stamper.Close();
reader.Close();
ssPdfOut = output.ToArray();
}
As you can see i have some code commented as an alternative but the two different ways are producing the same result.
What i am trying to achieve is create two textfields with the same name to when editing one it edits the others two. This two codes do that (in the browsers and pdfescape site) excepting in the adobe acrobat reader. In the acrobat reader i get just the first field visible and the others hidden i dont know why...
If you want to add two text field visualizations which represent the same content, you have to add them as two widgets of the same field and not two distinct fields, e.g. like this:
public void CreateDoubleTextField(byte[] ssPdf, Rectangle ssRectangle1, Rectangle ssRectangle2, string ssName, int ssFontSize, string ssValue, int ssPage, out byte[] ssPdfOut, bool ssIsMultiline)
{
PdfReader reader = new PdfReader(ssPdf);
ssPdfOut = null;
var output = new MemoryStream();
var stamper = new PdfStamper(reader, output);
PdfFormField tField = PdfFormField.CreateTextField(stamper.Writer, ssIsMultiline, false, 50);
tField.FieldName = ssName;
PdfFormField widget1 = PdfFormField.CreateEmpty(stamper.Writer);
widget1.SetWidget(ssRectangle1, PdfAnnotation.HIGHLIGHT_TOGGLE);
PdfFormField widget2 = PdfFormField.CreateEmpty(stamper.Writer);
widget2.SetWidget(ssRectangle2, PdfAnnotation.HIGHLIGHT_TOGGLE);
tField.AddKid(widget1);
tField.AddKid(widget2);
stamper.FormFlattening = false;
stamper.AddAnnotation(tField, ssPage);
stamper.Close();
reader.Close();
ssPdfOut = output.ToArray();
}
(As I don't have that RCRectangleRecord, I use the iTextSharp Rectangle class as argument.)
This gives you two field visualizations in Adobe Acrobat Reader; after editing one of them and switching focus (e.g. clicking somewhere outside that visualization), the other visualization duplicates the content.
Now i have this and i can create two fields when the list has more than one Rectangle but for some reason i dont know how the two fields appears without the name!!
PdfReader reader = new PdfReader(ssPdf);
ssPdfOut = null;
var output = new MemoryStream();
var stamper = new PdfStamper(reader, output);
TextField tField;
if (ssRectangle.Count==1){
tField= new TextField(stamper.Writer, new iTextSharp.text.Rectangle((float)ssRectangle[0].ssSTRectangle.ssllx, (float)ssRectangle[0].ssSTRectangle.sslly, (float)ssRectangle[0].ssSTRectangle.ssurx, (float)ssRectangle[0].ssSTRectangle.ssury), ssName);
if (ssValue!="")
tField.Text = ssValue;
if (ssIsMultiline)
tField.Options = TextField.MULTILINE;
tField.FontSize = ssFontSize;
tField.FieldName = ssName;
stamper.AddAnnotation(tField.GetTextField(), ssPage);
}
else
{
PdfFormField PtField = PdfFormField.CreateTextField(stamper.Writer, ssIsMultiline, false, 250);
PtField.Name=ssName;
foreach (RCRectangleRecord item in ssRectangle)
{
/*
tField=new TextField(stamper.Writer, new iTextSharp.text.Rectangle((float)ssRectangle[0].ssSTRectangle.ssllx, (float)ssRectangle[0].ssSTRectangle.sslly, (float)ssRectangle[0].ssSTRectangle.ssurx, (float)ssRectangle[0].ssSTRectangle.ssury), ssName);
tField.FieldName = ssName;
PtField.AddKid(tField.GetTextField());*/
PdfFormField widget = PdfFormField.CreateEmpty(stamper.Writer);
widget.SetWidget(new Rectangle((float)item.ssSTRectangle.ssllx, (float)item.ssSTRectangle.sslly, (float)item.ssSTRectangle.ssurx, (float)item.ssSTRectangle.ssury), PdfAnnotation.HIGHLIGHT_TOGGLE);
widget.Name = ssName;
PtField.AddKid(widget);
}
stamper.AddAnnotation(PtField, ssPage);
}
stamper.FormFlattening = false;
stamper.Close();
reader.Close();
ssPdfOut = output.ToArray();

Use External Css to parse XML

OK i am parsing HTML from a string into a PDFCEll.
It works great thanks to some help from here.
Here is how i am doing it.
How do i use an external css file so i can use class's and not STYLE=""
public class XhtmlToListHelper : IElementHandler
{
// Generic list of elements
public List<IElement> elements = new List<IElement>();
// Add the item to the list
public void Add(IWritable w)
{
if (w is WritableElement)
{
elements.AddRange(((WritableElement)w).Elements());
}
}
string html = "<ul class=\"list\"><li>html 1</li><li>html 2</li><li>html 3</li></ul>";
using (TextReader sr = new StringReader(html))
{
XMLWorkerHelper.GetInstance().ParseXHtml(XhtmlHelper, sr);
}
foreach (var element in XhtmlHelper.elements)
{
if (element.IsContent())
{
PDFCell.AddElement(element);
}
}
Now i have got this far, but how to tye it all in evades me. Any help would be much apreacheted.
HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
htmlContext.SetTagFactory(iTextSharp.tool.xml.html.Tags.GetHtmlTagProcessorFactory());
ICSSResolver cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
cssResolver.AddCssFile(HttpContext.Current.Server.MapPath("~/Templates/css/core.css"), true);
If you poke around the source here and you should see how to implement it. Basically, your three line using block quadruples in size and complexity:
var XhtmlHelper = new XhtmlToListHelper();
var htmlContext = new HtmlPipelineContext(null);
htmlContext.SetTagFactory(iTextSharp.tool.xml.html.Tags.GetHtmlTagProcessorFactory());
var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
cssResolver.AddCssFile(#"c:\test.css", true);
var pipeline = new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new ElementHandlerPipeline(XhtmlHelper, null)));//Here's where we add our IElementHandler
var worker = new XMLWorker(pipeline, true);
var parser = new XMLParser();
parser.AddListener(worker);
using (TextReader sr = new StringReader(html)) {
parser.Parse(sr);
}