I want to insert image to the user upload file, when I tried to create pdfDocument from the stream I got the "You can't read from OutputStream" error
public ActionResult GetTemplate(IFormFile template)
{
byte[] pdfBytes;
using (var memoryStream = new MemoryStream())
{
template.CopyTo(memoryStream);
var writer = new PdfWriter(memoryStream);
PdfDocument pdfDocument = new PdfDocument(new PdfReader(memoryStream));
var document = new Document(pdfDocument);
var pageCount = pdfDocument.GetNumberOfPages();
ImageData imageData = ImageDataFactory.CreatePng(System.Convert.FromBase64String(//));
var img = new Image(imageData).Scale(200, 100).SetFixedPosition(pageCount, 50, 120);
document.Add(img);
document.Close();
pdfBytes = memoryStream.ToArray();
}
return new FileContentResult(pdfBytes, "application/pdf");
}
If I changed it to new PdfDocument(new PdfReader(writer)); I got "PDF header not found" error instead.
This is my first time using iText, most of the answers I found on their website only worked with java. Any suggestion or help would be great, thank you for your time.
Related
I am trying to add a Cover Page PDF file to another PDF file. I am using CopyPagesTo method. CoverPageFilePath will go before any pages in the pdfDocumentFile. I then need to rewrite that new file to the same location. When I run the code and open the new pdf file I get an error about it being damaged.
public static void iText7MergePDF()
{
byte[] modifiedPdfInBytes = null;
string pdfCoverPageFilePath = #"PathtoCoverPage\Cover Page.pdf";
PdfDocument pdfDocumentCover = new PdfDocument(new iText.Kernel.Pdf.PdfReader(pdfCoverPageFilePath));
string pdfDocumentFile =#"PathtoFullDocument.pdf";
var buffer = File.ReadAllBytes(pdfDocumentFile);
using (var originalPdfStream = new MemoryStream(buffer))
using (var modifiedPdfStream = new MemoryStream())
{
var pdfReader = new iText.Kernel.Pdf.PdfReader(originalPdfStream);
var pdfDocument = new PdfDocument(pdfReader, new PdfWriter(modifiedPdfStream));
int numberOfPages = pdfDocumentCover.GetNumberOfPages();
pdfDocumentCover.CopyPagesTo(1, numberOfPages, pdfDocument);
modifiedPdfInBytes = modifiedPdfStream.ToArray();
pdfDocument.Close();
}
System.IO.File.WriteAllBytes(pdfGL, modifiedPdfInBytes);
}
Whenever you have some other type, like a StreamWriter, or here a PdfWriter writing to a Stream, it may not write all the data to the Stream immediately.
Here you Close the pdfDocument for all the data to be written to the MemoryStream.
ie this
modifiedPdfInBytes = modifiedPdfStream.ToArray();
pdfDocument.Close();
Should be
pdfDocument.Close();
modifiedPdfInBytes = modifiedPdfStream.ToArray();
Using itext7 (7.2.0) AND itext7.pdfhtml (4.0.0) AND .Net Core 5.0
Converting itext5 report to itext7
Getting the error, when forcing a page break using html style 'page-break-before: always;'
public FileResult PrintHtmlToPDFPageBreak()
{
StringBuilder sbBody = new StringBuilder();
sbBody.Append("<html>");
sbBody.Append("<body>");
sbBody.Append("<p>This is first page</p>");
sbBody.Append("<div style='page-break-before: always;'></div>");
sbBody.Append("<p>This is second page</p>");
sbBody.Append("</body>");
sbBody.Append("</html>");
string htmlContent = sbBody.ToString();
bool isPortrait = true;
string reportTitle = "Testing iText7 in .Net5";
//generate the byte array for the Pdf
byte[] pdfContent = null;
//Create a System.IO.MemoryStream object
using (MemoryStream memoryStream = new MemoryStream())
{
//Initialize PDF writer
PdfWriter pdfWriter = new PdfWriter(memoryStream);
//Initialize PDF document
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
//Initialize document
Document document = (isPortrait ? new Document(pdfDocument, PageSize.LETTER) : new Document(pdfDocument, PageSize.LETTER.Rotate()));
var headerHeight = String.IsNullOrEmpty(reportTitle) ? 70f : 120f;
document.SetMargins(headerHeight, 10f, 56f, 10f); //top, right, bottom, left
#region HTML to PDF
//Convert to Elements
ConverterProperties converterProperties = new ConverterProperties();
IList<IElement> elements = HtmlConverter.ConvertToElements(htmlContent, converterProperties);
foreach (var element in elements)
document.Add((IBlockElement)element);
#endregion
//Close the Document
document.Close();
pdfContent = memoryStream.ToArray();
//Close the MemoryStream
memoryStream.Close();
}
//return the byte array in the form of FileContentResult for browser download
var fileName = "ConvertHtmlToPDF.pdf";
return File(pdfContent, System.Net.Mime.MediaTypeNames.Application.Pdf, fileName);
}
You haven't attached the stacktrace but I am pretty sure the problem is in these two lines:
foreach (var element in elements)
document.Add((IBlockElement)element);
You are just casting without checking whether the cast is legitimate. Just process the case when an element is an instance of AreaBreak.
Thank you Alexey your solution worked.
Old code:
foreach (var element in elements)
document.Add((IBlockElement)element);
New code:
foreach (var element in elements)
{
if (element.GetType().Name == "HtmlPageBreak")
document.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
else
document.Add((IBlockElement)element);
}
I have a pdf document (created by iTextSharp - free version 4.1.6) and I want to add text / table at the top of this pdf. I have tried to create two memory streams from iTextSharp Documents and combine them to one, see my code below. But the new PDF file cannot be opened. Any ideas what I am doing wrong? Any other ideas to add text / table at the top of an existing PDF? Thanks in advance!
public void CreateTestPDF(string _pathOfOriginalPDF, string _pathOfModifiedPDF)
{
string oldFile = _pathOfOriginalPDF;
string newFile = pathOfModifiedPDF;
byte[] bytesHeader;
byte[] bytesBody;
byte[] bytesCombined;
using (MemoryStream ms = new MemoryStream())
{
Document doc = new Document();
doc.Open();
doc.Add(new Paragraph("This is my header paragraph"));
if (doc.IsOpen())
{
doc.Close();
}
bytesHeader = ms.ToArray();
}
using (MemoryStream ms = new MemoryStream())
{
Document doc = new Document();
//doc.Open();
PdfWriter writer = PdfWriter.GetInstance(doc, new FileStream(oldFile, FileMode.Create));
if (doc.IsOpen())
{
doc.Close();
}
bytesBody = ms.ToArray();
}
IEnumerable<byte> iCombined = bytesHeader.Concat(bytesBody);
bytesCombined = iCombined.ToArray();
string testFile = _pathOfModifiedPDF;
using (FileStream fs = File.Create(testFile))
{
fs.Write(bytesBody, 0, (int)bytesBody.Length);
}
}
I'm working with IText 7, I've been able to get one html page and generate a pdf for that page, but I need to generate one pdf document from multiple html pages and separated by pages. For example: I have Page1.html, Page2.html and Page3.html. I will need a pdf document with 3 pages, the first page with the content of Page1.html, second page with the content of Page2.html and like that...
This is the code I have and it's working for one html page:
ConverterProperties properties = new ConverterProperties();
PdfWriter writer = new PdfWriter(pdfRoot, new WriterProperties().SetFullCompressionMode(true));
PdfDocument pdfDocument = new PdfDocument(writer);
pdfDocument.AddEventHandler(PdfDocumentEvent.END_PAGE, new HeaderPdfEventHandler());
HtmlConverter.ConvertToPdf(htmlContent, pdfDocument, properties);
Is it possible to loop against the multiple html pages, add a new page to the PdfDocument for every html page and then have only one pdf generated with one page per html page?
UPDATE
I've been following this example and trying to translate it from Java to C#, I'm trying to use PdfMerger and loop around the html pages... but I'm receiving the Exception Cannot access a closed stream, on this line:
temp = new PdfDocument(
new PdfReader(new RandomAccessSourceFactory().CreateSource(baos), rp));
It looks like is related to the ByteArrayOutputStream baos instance. Any suggestions? This is my current code:
foreach (var html in htmlList)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfDocument temp = new PdfDocument(new PdfWriter(baos));
HtmlConverter.ConvertToPdf(html, temp, properties);
ReaderProperties rp = new ReaderProperties();
temp = new PdfDocument(
new PdfReader(new RandomAccessSourceFactory().CreateSource(baos), rp));
merger.Merge(temp, 1, temp.GetNumberOfPages());
temp.Close();
}
pdfDocument.Close();
You are using RandomAccessSourceFactory and passing there a closed stream which you wrote a PDF document into. RandomAccessSourceFactory expects an input stream instead that is ready to be read.
First of all you should use MemoryStream which is native to .NET world. ByteArrayOutputStream is the class that was ported from Java for internal purposes (although it extends MemoryStream as well). Secondly, you don't have to use RandomAccessSourceFactory - there is a simpler way.
You can create a new MemoryStream instance from the bytes of the MemoryStream that you used to create a temporary PDF with the following line:
baos = new MemoryStream(baos.ToArray());
As an additional remark, it's better to close PdfMerger instance directly instead of closing the document - closing PdfMerger closes the underlying document as well.
All in all, we get the following code that works:
foreach (var html in htmlList)
{
MemoryStream baos = new MemoryStream();
PdfDocument temp = new PdfDocument(new PdfWriter(baos));
HtmlConverter.ConvertToPdf(html, temp, properties);
ReaderProperties rp = new ReaderProperties();
baos = new MemoryStream(baos.ToArray());
temp = new PdfDocument(new PdfReader(baos, rp));
pdfMerger.Merge(temp, 1, temp.GetNumberOfPages());
temp.Close();
}
pdfMerger.Close();
Maybe not so succinctly. I use "using". Similar answer
private byte[] CreatePDF(string html)
{
byte[] binData;
using (var workStream = new MemoryStream())
{
using (var pdfWriter = new PdfWriter(workStream))
{
//Create one pdf document
using (var pdfDoc = new PdfDocument(pdfWriter))
{
pdfDoc.SetDefaultPageSize(iText.Kernel.Geom.PageSize.A4.Rotate());
//Create one pdf merger
var pdfMerger = new PdfMerger(pdfDoc);
//Create two identical pdfs
for (int i = 0; i < 2; i++)
{
using (var newStream = new MemoryStream(CreateDocument(html)))
{
ReaderProperties rp = new ReaderProperties();
using (var newPdf = new PdfDocument(new PdfReader(newStream, rp)))
{
pdfMerger.Merge(newPdf, 1, newPdf.GetNumberOfPages());
}
}
}
}
binData = workStream.ToArray();
}
}
return binData;
}
Create pdf
private byte[] CreateDocument(string html)
{
byte[] binData;
using (var workStream = new MemoryStream())
{
using (var pdfWriter = new PdfWriter(workStream))
{
using (var pdfDoc = new PdfDocument(pdfWriter))
{
pdfDoc.SetDefaultPageSize(iText.Kernel.Geom.PageSize.A4.Rotate());
ConverterProperties props = new ConverterProperties();
using (var document = HtmlConverter.ConvertToDocument(html, pdfDoc, props))
{
}
}
binData = workStream.ToArray();
}
}
return binData;
}
This was my code for itextsharp which worked ok. It displayed "Quote Only" in the middle of each page in a pdf file.
iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(Server.MapPath(#"~\Content\WaterMarkQuoteOnly.png"));
PdfReader readerOriginalDoc = new PdfReader(File(all, "application/pdf").FileContents);
int n = readerOriginalDoc.NumberOfPages;
img.SetAbsolutePosition(0, 300);
PdfGState _state = new PdfGState()
{
FillOpacity = 0.1F,
StrokeOpacity = 0.1F
};
using (MemoryStream ms = new MemoryStream())
{
using (PdfStamper stamper = new PdfStamper(readerOriginalDoc, ms, '\0', true))
{
for (int i = 1; i <= n; i++)
{
PdfContentByte content = stamper.GetOverContent(i);
content.SaveState();
content.SetGState(_state);
content.AddImage(img);
content.RestoreState();
}
}
//return ms.ToArray();
all = ms.GetBuffer();
}
This is my new itext 7 code, this also displays the watermark but the position is wrong. I was dismayed to see that you cant add an image to the canvas but you have to add ImageData when the position is being set on the image. The image is also way smaller and back to front.
var imagePath = Server.MapPath(#"~\Content\WaterMarkQuoteOnly.png");
var tranState = new iText.Kernel.Pdf.Extgstate.PdfExtGState();
tranState.SetFillOpacity(0.1f);
tranState.SetStrokeOpacity(0.1f);
ImageData myImageData = ImageDataFactory.Create(imagePath, false);
Image img = new Image(myImageData);
img.SetFixedPosition(0, 300);
var reader = new PdfReader(new MemoryStream(all));
var doc = new PdfDocument(reader);
int pages = doc.GetNumberOfPages();
using (var ms = new MemoryStream())
{
var writer = new PdfWriter(ms);
var newdoc = new PdfDocument(writer);
for (int i = 1; i <= pages; i++)
{
//get existing page
PdfPage page = doc.GetPage(i);
//copy page to new document
newdoc.AddPage(page.CopyTo(newdoc)); ;
//get our new page
PdfPage newpage = newdoc.GetPage(i);
Rectangle pageSize = newpage.GetPageSize();
//get canvas based on new page
var canvas = new PdfCanvas(newpage);
//write image data to new page
canvas.SaveState().SetExtGState(tranState);
canvas.AddImage(myImageData, pageSize, true);
canvas.RestoreState();
}
newdoc.Close();
all = ms.GetBuffer();
ms.Flush();
}
You are doing something strange with the PdfDocument objects, and you are also using the wrong AddImage() method.
I am not a C# developer, so I rewrote your example in Java. I took this PDF file:
And I took this image:
Then I added the image to the PDF file using transparency with the following result:
The code to do this, was really simple:
public void createPdf(String src, String dest) throws IOException {
PdfExtGState tranState = new PdfExtGState();
tranState.setFillOpacity(0.1f);
ImageData img = ImageDataFactory.create(IMG);
PdfReader reader = new PdfReader(src);
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdf = new PdfDocument(reader, writer);
for (int i = 1; i <= pdf.getNumberOfPages(); i++) {
PdfPage page = pdf.getPage(i);
PdfCanvas canvas = new PdfCanvas(page);
canvas.saveState().setExtGState(tranState);
canvas.addImage(img, 36, 600, false);
canvas.restoreState();
}
pdf.close();
}
For some reason, you created two PdfDocument instances. This isn't necessary. You also used the AddImage() method passing a Rectangle which resizes the image. Also make sure that you don't add the image as an inline image, because that bloats the file size.
I don't know which programming language you are using. For instance: I am not used to variables that are created using var such as var tranState. It should be very easy for you to adapt my Java code though. It's just a matter of changing lowercases into uppercases.