Using .NET, I want to programmatically create a PDF which simply consists of a background image with two labels over it with different fonts and positioning. I have read about existing PDF libraries, but don't know (if applicable) which one is the easiest for such a simple task.
Anyone care to guide me?
P.D.: I do not want to create the PDF with a generated image that already overlays the text over the background image.
Edit: This is the final working code:
public string Create()
{
if (!Directory.Exists(ApplicationImagePath))
{
Directory.CreateDirectory(ApplicationImagePath);
}
// Smart card
var doc = new Document(PageSize.GetRectangle("153 242.65"), 0, 0, 0, 0);
using (var stream = File.Create(filepath))
{
var writer = PdfWriter.GetInstance(doc, stream);
doc.Open();
var image = Image.GetInstance(CarnetData.Frame, ImageFormat.Png);
image.Alignment = Element.ALIGN_CENTER;
image.ScaleToFit(153, 242.65f);
doc.Add(image);
BaseFont font = BaseFont.CreateFont(GetFontPath(CarnetConfiguration.FontType), BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
font.PostscriptFontName = CarnetConfiguration.FontType.ToString();
float verticalPosition = writer.GetVerticalPosition(false);
var pName = new Paragraph(CarnetData.Name, new Font(font, FontData.EmployeeFont.SizeInPoints))
{
SpacingBefore = verticalPosition - 51f,
MultipliedLeading = 1.1f,
Alignment = Element.ALIGN_CENTER
};
doc.Add(pName);
var pDepartment = new Paragraph(CarnetData.Department, new Font(font, FontData.DepartmentFont.SizeInPoints))
{
SpacingBefore = 1.5f,
MultipliedLeading = 1.2f,
Alignment = Element.ALIGN_CENTER
};
doc.Add(pDepartment);
writer.ViewerPreferences = PdfWriter.PageModeUseNone + PdfWriter.CenterWindow + PdfWriter.PageLayoutSinglePage;
doc.Close();
}
return filepath;
}
Thanks for the help. :)
iTextSharp is a great library you could use, very simple and intuitive:
var doc = new Document();
using (var stream = File.Create("output.pdf"))
{
var writer = PdfWriter.GetInstance(doc, stream);
doc.Open();
doc.Add(Image.GetInstance(#"c:\foo\test.png"));
var cb = writer.DirectContent;
cb.BeginText();
cb.SetTextMatrix(100, 220);
var font = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, false);
cb.SetFontAndSize(font, 12);
cb.ShowText("Hello World");
cb.EndText();
cb.BeginText();
cb.SetTextMatrix(100, 250);
cb.ShowText("Some other text");
cb.EndText();
doc.Close();
}
Use iTextSharp. Free.
#binaryhowl - You can try Syncfusion PDF. It is great component with excellent support
http://asp.syncfusion.com/sfaspnetsamplebrowser/9.1.0.20/Web/Pdf.Web/samples/4.0/
Related
From my ASP.net MVC application, I am generating PDFs using iTextSharp and XSLT as the template. The pages are supposed to be landscape oriented.
The PDF document in landscape mode by following.
new Document(PageSize.LETTER.Rotate(), marginLeft, marginRight, marginTop, marginBottom);
From the XSLT I get the HTML and construct PDF content as following:
public void ParseXhtmlContents(string xhtml)
{
//Instantiate handler
var elementhandler = new ElementHandler();
//Bind a reader to text
using (TextReader sr = new StringReader(xhtml))
{
//Parse
XMLWorkerHelper.GetInstance().ParseXHtml(elementhandler, sr);
}
//Loop through each element
foreach (var element in elementhandler.Elements)
{
var div = element as PdfDiv;
if (div != null)
foreach (var table in div.Content.OfType<PdfPTable>())
{
table.HeaderRows = 1;
}
_iTextDocument.Add(element);
}
}
After generating the PDF bytes, I am trying to add page footer as following
private byte[] AddPageHeader(byte[] pdf, float marginLeft, float marginRight, float marginTop, float marginBottom)
{
using (var stream = new MemoryStream())
{
stream.Write(pdf, 0, pdf.Length);
var reader = new PdfReader(pdf);
var totalPage = reader.NumberOfPages;
var pageSize = reader.GetPageSize(1);
var document = new Document(pageSize, marginLeft, marginRight, marginTop, marginBottom);
var writer = PdfWriter.GetInstance(document, stream);
document.Open();
var contentByte = writer.DirectContent;
var pageIndex = 0;
for (var page = 1; page <= reader.NumberOfPages; page++)
{
document.NewPage();
pageIndex++;
var importedPage = writer.GetImportedPage(reader, page);
contentByte.AddTemplate(importedPage, 0, 0);
contentByte.BeginText();
var baseFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
contentByte.SetFontAndSize(baseFont, 6);
contentByte.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "- " + pageIndex.ToString() + " -", 300f, 20, 0);
contentByte.EndText();
contentByte.SaveState();
contentByte.SetColorStroke(new PdfSpotColor("black", new BaseColor(0, 0, 0)), 100);
contentByte.SetLineWidth(0.25f);
contentByte.Rectangle(20, 45, 572, 0.25f);
contentByte.FillStroke();
contentByte.RestoreState();
}
startingPageNumber = pageIndex;
document.Close();
return stream.ToArray();
}
}
If I skip the part where I add page number I am able to generate the PDF in landscape orientation with content displayed in right order, however when I add footer I am getting the following result, the rendered page is in portrait.
Please refer the attached image for output:
Help me out on where I am going wrong, thanks.
I found what was wrong
I changed the document creating code as following:
var document = new Document(reader.GetPageSizeWithRotation(1), marginLeft, marginRight, marginTop, marginBottom);
and added a footer template similar to the above:
contentByte.AddTemplate(importedPage, 0, -1f, 1f, 0, 0, reader.GetPageSizeWithRotation(pageIndex).Height);
I can add a text to an existing pdf as it is explained in the url below.
But the texts stay below the images in the pdf when I add them. How can I fix this problem?
ITextSharp insert text to an existing pdf
Edit :
public void createFromPDF(string mapPath)
{
string oldFile = mapPath.Replace("Home", "") + "Content\\Uploads\\fiyat-listesi.pdf";// "oldFile.pdf";
string newFile = mapPath.Replace("Home", "") + "Content\\Uploads\\new.pdf";//"newFile.pdf";
// open the reader
PdfReader reader = new PdfReader(oldFile);
Rectangle size = reader.GetPageSizeWithRotation(1);
Document document = new Document(size);
// open the writer
FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();
// the pdf content
PdfContentByte cb = writer.DirectContent;
// select the font properties
BaseFont bf = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
cb.SetColorFill(Color.RED);
cb.SetFontAndSize(bf, 8);
// write the text in the pdf content
cb.BeginText();
string text = "Some random blablablabla...";
// put the alignment and coordinates here
cb.ShowTextAligned(1, text, 520, 640, 0);
cb.EndText();
cb.BeginText();
text = "Other random blabla...";
// put the alignment and coordinates here
cb.ShowTextAligned(2, text, 100, 200, 0);
cb.EndText();
// create the new page and add it to the pdf
PdfImportedPage page = writer.GetImportedPage(reader, 1);
cb.AddTemplate(page, 0, 0);
document.NewPage();
Paragraph p = new Paragraph("aaaaaaaaaaaaaaaaaa", new Font(bf));
document.Add(p);
PdfImportedPage page2 = writer.GetImportedPage(reader, 2);
cb.AddTemplate(page2, 0, 0);
document.NewPage();
Paragraph pwe = new Paragraph("aaaaaaaaaaaaaaaaaa", new Font(bf));
document.Add(p);
cb.EndLayer();
PdfImportedPage page3 = writer.GetImportedPage(reader, 3);
cb.AddTemplate(page3, 0, 0);
// close the streams and voilá the file should be changed :)
document.Close();
fs.Close();
writer.Close();
reader.Close();
}
Two remarks:
You add the text first, then you add the image. Hence the image covers the text. That's elementary logic. If you switch the order and add the image first, then the text, the text will cover the image. That's pure common sense.
You manipulate an existing PDF by importing a PdfImportedPage and PdfWriter. That proves thaf you didn't read the documentation. You should use PdfStamper instead!
Your code is too complex. Switch to PdfStamper and add text using the ColumnText object. Don't use BeginText() / EndText(). Also: why are you using EndLayer()??? Do you have any idea what that method is meant for?
I'm creating an application in c# for generation of some pdf files with bar code in it.
I'm using the memory stream and placing data to the table (one column) which I'll write to the pdf file at the end.
Everything is working fine till I need to add bar code. For that I use direct content.
pdfWriter = PdfWriter.GetInstance(pdfToCreate, outputStream);
PdfContentByte cb = new PdfContentByte(pdfWriter);
iTextSharp.text.Image bc128 = code128.CreateImageWithBarcode(cb, null, null);
After I create bar code I add it to the table using:
tableout.AddCell(image128);
And then I have a problem. Rows from the table that I've created before are shown, but I don't see a text. If I comment line with bar code addition everything is OK.
I've found one comment on forum:
Yep. Foreground text color is defined by the Font for the high-level
text layout code. When you start using a PdfContentByte directly, the
font and color become separated, but not until then.
How can I solve this?
I know that I can use system drawning method but image with bar code is not so clear.
PdfContentByte is for direct rendering to absolute positions in the pdf. But what you need is an IElement you can add to the "document stack".
Try this (using full namespaces just to clearly identify the classes):
Barcode128 barcode = new Barcode128();
System.Drawing.Image img = barcode.CreateDrawingImage(System.Drawing.Color.Black, System.Drawing.Color.White);
iTextSharp.text.Image itextImg = iTextSharp.text.Image.GetInstance(img, System.Drawing.Imaging.ImageFormat.Bmp);
table.AddCell(new PdfPCell(itextImg));
I know that I can use system drawning method but image with bar code is not so clear.
Here is a code that I use. I'm trying to get documents whit different sizes depending on the number of lines in the document. So I'm using memory stream (not shown here). My idea is to write data to the table in memory and count lines. After that I will create new pdf document and add table to that document.
If I comment a line:
table.AddCell(image128);
both documents are the same. But if I leave that line in code final document shows all rows but text in rows is not visible. If I open second docu,ent in firefox I can even see a bar codes.
Any suggestion will be great. Or is there a different way to get documents with different sizes depending on amount of content.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.IO;
using System.Drawing;
namespace testtables1
{
class Program
{
static void Main(string[] args)
{
iTextSharp.text.Rectangle pageSize = new iTextSharp.text.Rectangle(300, 720);
Document pdfToCreate = new Document(pageSize, 0, 0, 0, 0);
PdfWriter writer = PdfWriter.GetInstance(pdfToCreate, new FileStream(#"D:\\testfile.pdf", FileMode.Create));
pdfToCreate.Open();
PdfPTable table = new PdfPTable(1);
PdfPTable tableout = new PdfPTable(1);
WriteLineToPdf("This is first row. Below is image generated using SystemDrawing.", table, out tableout);
table = tableout;
// Create bar code
Barcode128 code128 = new Barcode128();
code128.CodeType = Barcode.CODE128;
code128.ChecksumText = true;
code128.GenerateChecksum = true;
code128.Code = "00100370006756555316";
// Create a new PdfWrite object, writing the output to a MemoryStream
var outputStream = new MemoryStream();
var pdfWriter = PdfWriter.GetInstance(pdfToCreate, outputStream);
PdfContentByte cb = new PdfContentByte(writer);
// Image generated using System.Drawing and rendering
System.Drawing.Bitmap bm = new System.Drawing.Bitmap(code128.CreateDrawingImage(System.Drawing.Color.Black, System.Drawing.Color.White));
iTextSharp.text.Image bmCoverted = iTextSharp.text.Image.GetInstance(bm, System.Drawing.Imaging.ImageFormat.Bmp);
table.AddCell(bmCoverted);
pdfToCreate.Open();
// Image generated using iTextSharp.text.Image
WriteLineToPdf("This is third row. Below is image generated using code128.CreateImageWithBarcode.", table, out tableout);
table = tableout;
iTextSharp.text.Image image128 = code128.CreateImageWithBarcode(cb, null, null);
table.AddCell(image128);
table = tableout;
WriteLineToPdf("This is fifth row.", table, out tableout);
pdfToCreate.Add(tableout);
// Create new document with height that depends on number of lines in document.
iTextSharp.text.Rectangle psFinal = new iTextSharp.text.Rectangle(200, 300);
Document pdfFinal = new Document(psFinal, 0, 0, 0, 0);
PdfWriter writer1 = PdfWriter.GetInstance(pdfFinal, new FileStream(#"D:\\finalfile.pdf", FileMode.Create));
pdfFinal.Open();
pdfFinal.Add(tableout);
cb = null;
pdfToCreate.Close();
pdfFinal.Close();
}
// Write a single line to the PDF.
private static void WriteLineToPdf(string tLine, PdfPTable ticTable0In, out PdfPTable ticTableIN)
{
// Define fonts.
BaseFont bfTimes = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, false); // Base font.
iTextSharp.text.Font timesN = new iTextSharp.text.Font(bfTimes, 12, iTextSharp.text.Font.NORMAL, BaseColor.BLACK);
Paragraph par = new Paragraph(tLine, timesN);
par.Alignment = Element.ALIGN_CENTER;
PdfPCell cell;
cell = new PdfPCell();
//cell.FixedHeight = 12f;
//cell.Border = Rectangle.NO_BORDER;
cell.AddElement(par);
ticTable0In.AddCell(cell);
ticTableIN = ticTable0In;
par = null;
}
}
}
My other ideai is to use data from memory stream. I'm able to create result.pdf document with size that I want but after I write Data from memory size changes.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace TableWithBC2
{
class Program
{
static void Main(string[] args)
{
using (MemoryStream myMemoryStream = new MemoryStream())
{
PdfPTable tableIn;
Document pdfDocument = new Document();
PdfWriter writer = PdfWriter.GetInstance(pdfDocument, myMemoryStream);
PdfPTable tableOut = new PdfPTable(new float[] { 200 });
tableIn = new PdfPTable(new float[] { 200 });
pdfDocument.Open();
for (int i = 0; i < 5; i++)
{
WriteLineToPdf("This is row no. " + i.ToString(), tableIn, out tableOut);
tableIn = tableOut;
}
// Create bar code
PdfContentByte cb = writer.DirectContent;
Barcode128 code128 = new Barcode128();
code128.CodeType = Barcode.CODE128;
code128.ChecksumText = true;
code128.GenerateChecksum = true;
code128.Code = "00100370006756555316";
Image barCodeImage = code128.CreateImageWithBarcode(cb, null, null);
PdfPCell cell1 = new PdfPCell(barCodeImage);
tableOut.AddCell(cell1);
tableIn = tableOut;
WriteLineToPdf("This is a last row.", tableIn, out tableOut);
pdfDocument.Add(tableOut);
pdfDocument.Close();
// Create final PDF document.
Document pdfDocument1 = new Document(new Rectangle(200, 250));
PdfWriter writer1 = PdfWriter.GetInstance(pdfDocument1, new FileStream(#"D:\\result.pdf", FileMode.Create));
pdfDocument1.Open();
pdfDocument1.Add(new Chunk());
pdfDocument1.Close();
byte[] content = myMemoryStream.ToArray();
// Write out PDF from memory stream.
using (FileStream fs = File.OpenWrite(#"D:\\nikola bd - final\\out\\dummy22.pdf"))
{
fs.Write(content, 0, (int)content.Length);
}
}
}
// Write a single line to the PDF.
private static void WriteLineToPdf(string tLine, PdfPTable tTableIn, out PdfPTable tTableOut)
{
BaseFont bfTimes = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, false);
iTextSharp.text.Font timesN = new iTextSharp.text.Font(bfTimes, 12, iTextSharp.text.Font.NORMAL, BaseColor.RED);
Paragraph par = new Paragraph(tLine, timesN);
par.Alignment = Element.ALIGN_CENTER;
PdfPCell cell = new PdfPCell();
cell.AddElement(par);
tTableIn.AddCell(cell);
tTableOut = tTableIn;
}
}
}
I'm using iTextSharp to create EAN13 barcodes, but I can't change the width of the generated barcode. :(
Changing the height works fine with BarHeight = x
System.Drawing.Image bm = code39.CreateDrawingImage(Color.Black, Color.White);
BarcodeEAN codeEan = new BarcodeEAN();
codeEan.CodeType = Barcode.EAN13;
codeEan.Code = "4035606872510";
codeEan.BarHeight = 130f;
codeEan.X = ... //not working
codeEan.Size = ... //also not working
bm = codeEan.CreateDrawingImage(Color.Black, Color.White);
bm.Save("D:\\temp\\foo.png", ImageFormat.Png);
/* Resizing only stretches the pixels with bad quality */
iTextSharp.text.Image barcode = iTextSharp.text.Image.GetInstance(bm, ImageFormat.Png);
barcode2.ScaleToFit(1200f, 130f);
Found a solution by myself....
But for people with the same problem:
Document doc = new Document();
MemoryStream memStream = new MemoryStream();
PdfWriter writer = PdfWriter.GetInstance(doc, memStream);
writer.SetPdfVersion(PdfWriter.PDF_VERSION_1_7);
writer.CompressionLevel = PdfStream.BEST_COMPRESSION;
doc.Open();
PdfContentByte cb = writer.DirectContent;
BarcodeEAN codeEan = new BarcodeEAN();
codeEan.GenerateChecksum = true;
codeEan.CodeType = Barcode.EAN13;
codeEan.Code = "5449000101549";
codeEan.Font = null;
iTextSharp.text.Image imageEAN = codeEan.CreateImageWithBarcode(cb, null, null);
imageEAN.ScaleAbsolute(150, 50);
imageEAN.SetAbsolutePosition(doc.PageSize.Right - 186f, doc.PageSize.Bottom + 30f);
doc.Add(imageEAN);
...
I'm having an issue with flattening fields that were generated in a table cell. I've created a sample project to illustrate the issue.
private static void dummyFunction2()
{
// Create a PDF with a TextBox in a table cell
//Get the font ready
BaseFont bfHelvetica = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1250, false);
var helvetica12 = new Font(bfHelvetica, 12, Font.NORMAL, BaseColor.BLACK);
//Create the document and filestream
var doc = new Document(PageSize.LETTER, 18f, 18f, 18f, 18f);
var fs = new FileStream("TextBoxInTableCell.pdf", FileMode.Create);
PdfWriter writer = PdfWriter.GetInstance(doc, fs);
//Create the table
doc.Open();
var myTable = new PdfPTable(1);
myTable.TotalWidth = 568f;
myTable.LockedWidth = true;
myTable.HorizontalAlignment = 0;
//Create the textfield that will sit on a cell in the table
var tf = new TextField(writer, new Rectangle(67, 585, 140, 800), "cellTextBox");
tf.Text = "test";
//Create the table cell
var tbCell = new PdfPCell(new Phrase(" ", helvetica12));
//Use fieldpositioningevents to make the field appear on top of the cell
var events =
new FieldPositioningEvents(writer, tf.GetTextField());
tbCell.CellEvent = events;
//Add the cell to the table
myTable.AddCell(tbCell);
PdfContentByte cb = writer.DirectContent;
//Write out the table to the middle of the document
myTable.WriteSelectedRows(0, -1, 0, -1, 0, 400, cb);
doc.Close();
fs.Close();
//Open back up the document so that we can set GenerateAppearances to false.
//This solution proposed and accepted at https://stackoverflow.com/questions/25169342/itextsharp-fields-generated-in-a-table-not-shown-in-adobe-reader
var reader2 = new PdfReader("TextBoxInTableCell.pdf");
var stamper2 = new PdfStamper(reader2, new FileStream("tempdoc.pdf", FileMode.Create));
stamper2.AcroFields.GenerateAppearances = false;
stamper2.Close();
reader2.Close();
Process.Start("tempdoc.pdf");
//Open the pdf back up
var reader = new PdfReader("tempdoc.pdf");
var stamper = new PdfStamper(reader, new FileStream("tempdoc.pdf" + "1.pdf", FileMode.Create));
//Flatten the form
stamper.FormFlattening = true;
//Close everything
stamper.Close();
reader.Close();
Process.Start("tempdoc.pdf" + "1.pdf");
}
This unflattened pdf that is generated looks like . As you can see, the field is where it should be. The flattened pdf, however, looks like . The field's text has been shifted a noticeable amount.
I've posted the flattened pdf so that someone can check out the pdf language if needed.
I know this is sort of a niche issue but hopefully someone can help me out.
EDIT1: In response to Bruno's comment, I'm using iTextsharp 5.5.2 downloaded from Nuget and the unflattened pdf can be found here.
EDIT2: Updated SO link to point to the correct question. For reference here are my two previous questions that relate to this problem: First, Second
Java version of iText does not contain this problem. It's a Java -> C# porting issue. We'll take care of that.