This event is not called for first cell
public class pdfCellBorder : IPdfPCellEvent
{
public void CellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases)
{
PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];
canvas.SetLineDash(0.5F, 2.2F, 0 );
canvas.Stroke();
}
}
Code from comments:
PdfPTable table = new PdfPTable(1);
pdfCellBorder pdfcell = new pdfCellBorder();
table.DefaultCell.CellEvent = pdfcell;
table.DefaultCell.Border = PdfPCell.NO_BORDER;
PdfPCell cell = new PdfPCell(new Phrase("product"));
cell.Border = Rectangle.BOTTOM_BORDER;
cell.CellEvent = pdfcell;
table.AddCell(cell);
cell = new PdfPCell(new Phrase("product"));
// cell.BorderWidthBottom = 1f;
cell.Border = Rectangle.BOTTOM_BORDER;
cell.CellEvent = pdfcell;
table.AddCell(cell);
public class pdfCellBorder : IPdfPCellEvent {
public void CellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {
PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];
//canvas.SetLineWidth(0.5F);
canvas.SetLineDash(0.5F, 2.2F, 0);
canvas.Rectangle(position.Left, position.Bottom, position.Width, 0);
canvas.Stroke();
}
}
You can fix this with two things. First, set the actual Rectangle in your event:
public class pdfCellBorder : IPdfPCellEvent {
public void CellLayout(PdfPCell cell, iTextSharp.text.Rectangle position, PdfContentByte[] canvases) {
PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];
canvas.SetLineDash(0.5F, 2.2F, 0);
//Set the rectangle to draw on
canvas.Rectangle(position.Left, position.Bottom, position.Width, position.Height);
canvas.Stroke();
}
}
Second, disable the border on your default cell:
var t = new PdfPTable(2);
t.DefaultCell.CellEvent = new pdfCellBorder();
//Disable the border
t.DefaultCell.Border = PdfPCell.NO_BORDER;
t.AddCell("Hello");
t.AddCell("World");
doc.Add(t);
EDIT
Based on your comments, you want a line and only a line drawn at the bottom of each cell. Instead of a Rectangle you just want to draw a line using MoveTo and LineTo:
public class pdfCellBorder : IPdfPCellEvent {
public void CellLayout(PdfPCell cell, iTextSharp.text.Rectangle position, PdfContentByte[] canvases) {
PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];
canvas.SetLineDash(0.5F, 2.2F, 0);
//Move the "pen" to where we want to start drawing
canvas.MoveTo(position.Left, position.Bottom);
//Draw a straight line
canvas.LineTo(position.Right, position.Bottom);
canvas.Stroke();
}
}
However, you still need to disable automatic borders on all of your cells. If you set any borders below you are instructing iTextSharp to draw them for you. Also, since you are calling new PdfPCell() we can get rid of anything related to DefaultCell
PdfPTable table = new PdfPTable(1);
pdfCellBorder pdfcell = new pdfCellBorder();
PdfPCell cell = new PdfPCell(new Phrase("product"));
cell.Border = PdfPCell.NO_BORDER;
cell.CellEvent = pdfcell;
table.AddCell(cell);
cell = new PdfPCell(new Phrase("product"));
cell.Border = PdfPCell.NO_BORDER;
cell.CellEvent = pdfcell;
table.AddCell(cell);
Related
Is it possible to make the border length of a specific table cell fit the contents of that cell?
I did
PdfPCell cell = new PdfPCell(new Phrase(total.ToString(),myFont));
cell.Border=PdfPCell.BOTTOM_BORDER | PdfPCell.TOP_BORDER;
cell.HorizontalAlignment=Element.ALIGN_LEFT;
tabl.AddCell(cell);
But it produces the below result (the top & bottom border exceeds the cell data length/space)
Can someone help?
iText 7
In iText 7, this is very straightforward, because it's possible out of the box to set borders to any element. In this use case, instead of using table or cell borders, it's easier to set a top and bottom border to the piece of text itself.
Table table = new Table(4);
table.SetWidth(UnitValue.CreatePercentValue(100));
// no border on the table
table.SetBorder(null);
// no border on the cell
Cell cell = new Cell().SetBorder(null);
Text t = new Text("410.40");
// top and bottom border on the Text instance
t.SetBorderTop(new SolidBorder(1.5f));
t.SetBorderBottom(new SolidBorder(1.5f));
Paragraph p = new Paragraph().Add(t);
cell.Add(p);
table.AddCell(cell);
// some test cells
table.AddCell(new Cell().Add(new Paragraph("Column 2")).SetBorder(null));
table.AddCell(new Cell().Add(new Paragraph("C 3")).SetBorder(null));
table.AddCell(new Cell().Add(new Paragraph("C 4")).SetBorder(null));
doc.Add(table);
iText 5 / iTextSharp
A bit more grunt work is involved to get the same effect. A possible approach is to use a page event listener and a Chunk with a "generic tag" to trigger a page event upon rendering. That callback will expose the rendering rectangle of the Chunk, which allows those coordinates to be used to draw the top and bottom line at the correct location.
writer.PageEvent = new BorderEvent();
PdfPTable table = new PdfPTable(4);
table.WidthPercentage = 100;
PdfPCell cell = new PdfPCell();
// Use a Chunk with a "generic tag", which triggers a callback when it's rendered
Chunk c = new Chunk("410.40");
c.SetGenericTag("borders");
cell.AddElement(c);
// no border on the cell
cell.Border = 0;
table.AddCell(cell);
// some test cells
cell = new PdfPCell();
cell.AddElement(new Paragraph("Column 2"));
cell.Border = 0;
table.AddCell(cell);
cell = new PdfPCell();
cell.AddElement(new Paragraph("C 3"));
cell.Border = 0;
table.AddCell(cell);
cell = new PdfPCell();
cell.AddElement(new Paragraph("C 4"));
cell.Border = 0;
table.AddCell(cell);
doc.Add(table);
The callback for the generic tag:
class BorderEvent : PdfPageEventHelper
{
public override void OnGenericTag(PdfWriter writer, Document document,
Rectangle rect, String text)
{
PdfContentByte canvas = writer.DirectContent;
// draw the top border, based on the rendering rectangle
canvas.SetLineWidth(1.5);
canvas.MoveTo(rect.Left, rect.Top);
canvas.LineTo(rect.Right, rect.Top);
// draw the bottom border, based on the rendering rectangle
// the bottom coordinate is the base line of the text,
// so some calculation, probably including the font size, is
// needed to lower it a bit
// I've used a quick and dirty 3 points here
canvas.Stroke();
canvas.MoveTo(rect.Left, rect.Bottom - 3);
canvas.LineTo(rect.Right, rect.Bottom - 3);
canvas.Stroke();
}
}
Here is visual what my problem looks like.
Here is my code that produce that result.
public void intervencionHeaderLogo(string pictureURL,int width, int height)
{
iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(System.Web.HttpContext.Current.Server.MapPath(pictureURL));
image.ScaleAbsolute(width, height);
image.Alignment = 2;
//Table
PdfPTable table = new PdfPTable(1);
PdfPCell cell = new PdfPCell();
BaseFont bf = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
iTextSharp.text.Font font = new iTextSharp.text.Font(bf, 20, iTextSharp.text.Font.NORMAL);
Paragraph p = new Paragraph("Reporte de intervención", font);
//Cell no 1
cell = new PdfPCell();
cell.Border = 0;
cell.AddElement(p);
cell.HorizontalAlignment = Element.ALIGN_CENTER;
table.AddCell(cell);
//Cell no 2
cell = new PdfPCell();
cell.Border = 0;
cell.AddElement(image);
table.AddCell(cell);
//Add table to document
pdfDoc.Add(table);
}
Here is what I want to have.
Any help would be appriciated
I was able to fix it i made a mistake i create a table of 1 when i should have made it of size 2.
//Table
PdfPTable table = new PdfPTable(2);
and the i added a line break like this in the paragraph like this
Paragraph p = new Paragraph("\nReporte de intervención", font);
I'm trying to align an image to the bottom-right of a cell.
I'm basically creating a table with two cells for each row. The cell contains text and an image, which I want to be aligned to the right bottom of the cells. Please refer to image
This is my code
PdfPTable table = new PdfPTable(2);
table.TotalWidth = 400f;
table.LockedWidth = true;
float[] widths = new float[] { 1.5f, 1.5f };
table.SetWidths(widths);
table.HorizontalAlignment = 0;
table.SpacingBefore = 50f;
table.SpacingAfter = 30f;
iTextSharp.text.Image logoImage = iTextSharp.text.Image.GetInstance(HttpContext.Current.Server.MapPath("~/Images/MyImage.png"));
logoImage.ScaleAbsolute(40, 40);
logoImage.Alignment = iTextSharp.text.Image.ALIGN_BOTTOM;
logoImage.Alignment = iTextSharp.text.Image.RIGHT_ALIGN;
foreach (EmployeeModel employee in employees)
{
PdfPCell cell = new PdfPCell();
cell.FixedHeight = 140f;
cell.PaddingLeft = 30f;
cell.PaddingRight = 10f;
cell.PaddingTop = 20f;
cell.PaddingBottom = 5f;
Paragraph p = new Paragraph(GetLabelCellText(Employee), NormalFont);
p.Alignment = Element.ALIGN_LEFT;
p.Alignment = Element.ALIGN_TOP;
cell.AddElement(p);
cell.AddElement(logoImage);
table.AddCell(cell);
}
How do I place the image at the bottom right of each cell (without affecting the position of the text of course).
The question is a bit ambiguous because you create a Table with two columns, but add cells without verifying the employees collection has an even number of elements, which will throw if not....
Assuming you really do want both the text and the image in a single cell, probably the simplest way to get the layout you want is to implement IPdfPCellEvent:
public class BottomRightImage : IPdfPCellEvent
{
public Image Image { get; set; }
public void CellLayout(
PdfPCell cell,
Rectangle position,
PdfContentByte[] canvases)
{
if (Image == null) throw new InvalidOperationException("image is null");
PdfContentByte canvas = canvases[PdfPTable.TEXTCANVAS];
Image.SetAbsolutePosition(
position.Right - Image.ScaledWidth - cell.PaddingRight,
position.Bottom + cell.PaddingBottom
);
canvas.AddImage(Image);
}
}
Then set the CellEvent property on the PdfPCell. Here's a simple working example:
using (var stream = new MemoryStream())
{
using (var document = new Document())
{
PdfWriter.GetInstance(document, stream);
document.Open();
var table = new PdfPTable(2)
{
HorizontalAlignment = Element.ALIGN_LEFT,
TotalWidth = 400f,
LockedWidth = true
};
var image = Image.GetInstance(imagePath);
image.ScaleAbsolute(40, 40);
var cellEvent = new BottomRightImage() { Image = image };
var testString =
#"first name: {0}
last name: {0}
ID no: {0}";
for (int i = 0; i < 2; ++i)
{
var cell = new PdfPCell()
{
FixedHeight = 140f,
PaddingLeft = 30f,
PaddingRight = 10f,
PaddingTop = 20f,
PaddingBottom = 5f
};
cell.CellEvent = cellEvent;
var p = new Paragraph(string.Format(testString, i))
{
Alignment = Element.ALIGN_TOP | Element.ALIGN_LEFT
};
cell.AddElement(p);
table.AddCell(cell);
}
document.Add(table);
}
File.WriteAllBytes(outputFile, stream.ToArray());
}
And PDF output:
An all-black square image is used to show the PdfPCell padding is taken into account.
Thanks in advance , I know that you're busy .. so I edited this based on the code you gave me ...
first I want you to see what I got when I tried you code ..
and the picture I used as a background is :
as you see I got several problems :
1- the image is not a background to the cells and I want it to be stretched .
2- I tried to put the text in different positions but I failed .
3 - also i got a missing cell that not showed.
the code I used is :
1- the ImageEvent class :
class ImageEvent : IPdfPCellEvent
{
protected Image img;
public ImageEvent(Image img) {
this.img = img;
}
void IPdfPCellEvent.CellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases)
{
img.ScaleToFit(position.Width, position.Height);
img.SetAbsolutePosition(position.Left + (position.Width - img.Width) / 2,
position.Bottom + (position.Height - img.ScaledHeight / 2));
PdfContentByte canvas = canvases[PdfPTable.BACKGROUNDCANVAS];
try {
canvas.AddImage(img);
} catch (DocumentException ex) {
// do nothing
}
}
}
2- The position class :
class PositionEvent : IPdfPCellEvent
{
protected Phrase content;
protected string pos;
public PositionEvent(Phrase content, string pos)
{
this.content = content;
this.pos = pos;
}
void IPdfPCellEvent.CellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases)
{
PdfContentByte canvas = canvases[PdfPTable.TEXTCANVAS];
float x = 0;
float y = 0;
int alignment = 0;
switch (pos)
{
case "TOP_LEFT":
x = position.GetLeft(3);
y = position.GetTop(content.Leading);
alignment = Element.ALIGN_LEFT;
break;
case "TOP_RIGHT":
x = position.GetRight(3);
y = position.GetTop(content.Leading);
alignment = Element.ALIGN_RIGHT;
break;
case "BOTTOM_LEFT":
x = position.GetLeft(3);
y = position.GetBottom(3);
alignment = Element.ALIGN_LEFT;
break;
case "BOTTOM_RIGHT":
x = position.GetRight(3);
y = position.GetBottom(3);
alignment = Element.ALIGN_RIGHT;
break;
case "CENTER_TOP":
x = position.GetRight(3) + position.GetLeft(3) / 2;
y = position.GetTop(3);
alignment = Element.ALIGN_RIGHT;
break;
case "CENTER_BOTTOM":
x = position.GetRight(3) + position.GetLeft(3) / 2;
y = position.GetBottom(3);
alignment = Element.ALIGN_RIGHT;
break;
case "CENTER_MIDDLE":
x = position.GetRight(3) + position.GetLeft(3) / 2;
y = x;
alignment = Element.ALIGN_RIGHT;
break;
}
ColumnText.ShowTextAligned(canvas, alignment, content, x, y, 0);
}
}
3- The method :
public void createPdf(string dest)
{
// 1. Create a Document which contains a table:
Document document = new Document();
PdfWriter.GetInstance(document, new FileStream(dest, FileMode.Create));
document.Open();
PdfPTable table = new PdfPTable(3);
table.WidthPercentage = 100f;
PdfPCell cell1 = new PdfPCell();
PdfPCell cell2 = new PdfPCell();
PdfPCell cell3 = new PdfPCell();
PdfPCell cell4 = new PdfPCell();
PdfPCell cell5 = new PdfPCell();
PdfPCell cell6 = new PdfPCell();
PdfPCell cell7 = new PdfPCell();
// 2. Inside that table, make each cell with specific height:
cell1.FixedHeight=50;
cell2.FixedHeight = 50;
cell3.FixedHeight = 50;
cell4.FixedHeight = 50;
cell5.FixedHeight = 50;
cell6.FixedHeight = 50;
cell7.FixedHeight = 50;
// 3. Each cell has the same background image
string path = string.Concat(this.openFileDialog_pic.FileName);
string imageFilePath = string.Concat(Environment.GetEnvironmentVariable("."), path);
iTextSharp.text.Image IMG = iTextSharp.text.Image.GetInstance(imageFilePath);
ImageEvent imgEvent = new ImageEvent(iTextSharp.text.Image.GetInstance(IMG));
cell1.CellEvent=imgEvent;
cell2.CellEvent = imgEvent;
cell3.CellEvent = imgEvent;
cell4.CellEvent = imgEvent;
cell5.CellEvent = imgEvent;
cell6.CellEvent = imgEvent;
cell7.CellEvent = imgEvent;
// 4. Add text in front of the image at specific position
cell1.CellEvent= new PositionEvent(new Phrase("Top left"), "TOP_LEFT");
cell2.CellEvent=new PositionEvent(new Phrase("Top right"), "TOP_RIGHT");
cell3.CellEvent=new PositionEvent(new Phrase("Bottom left"), "BOTTOM_LEFT");
cell4.CellEvent=new PositionEvent(new Phrase("Bottom right"), "BOTTOM_RIGHT");
cell5.CellEvent = new PositionEvent(new Phrase("Center Top"), "CENTER_TOP");
cell6.CellEvent = new PositionEvent(new Phrase("Center Bottom"), "CENTER_BOTTOM");
cell7.CellEvent = new PositionEvent(new Phrase("Center Middle"), "CENTER_MIDDLE");
// Wrap it all up!
table.AddCell(cell1);
table.AddCell(cell2);
table.AddCell(cell3);
table.AddCell(cell4);
table.AddCell(cell5);
table.AddCell(cell6);
table.AddCell(cell7);
document.Add(table);
document.Close();
}
In an additional comment, you clarify your requirements:
I want to create a document which contains one table. Inside that table, cells
I want to make each cell with specific height
each cell have the same background image
I want to put a text in front the image in the position I want inside the cell. For example: top left of the cell, bottom right of the cell
In other words: you want something like this: position_content_in_cell.pdf
There is more than one way to do this. I don't understand the code sample you use in your question. It uses nested tables and I don't understand why you'd need nested tables.
In the PositionContentInCell example, I used a method that allows you to really fine-tune the exact position of the text. I created an ImageEvent to scale and center the image:
class ImageEvent implements PdfPCellEvent {
protected Image img;
public ImageEvent(Image img) {
this.img = img;
}
public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {
img.scaleToFit(position.getWidth(), position.getHeight());
img.setAbsolutePosition(position.getLeft() + (position.getWidth() - img.getScaledWidth()) / 2,
position.getBottom() + (position.getHeight() - img.getScaledHeight()) / 2);
PdfContentByte canvas = canvases[PdfPTable.BACKGROUNDCANVAS];
try {
canvas.addImage(img);
} catch (DocumentException ex) {
// do nothing
}
}
}
I created a PositionEvent to add the text inside the cell:
class PositionEvent implements PdfPCellEvent {
protected Phrase content;
protected POSITION pos;
public PositionEvent(Phrase content, POSITION pos) {
this.content = content;
this.pos = pos;
}
public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {
PdfContentByte canvas = canvases[PdfPTable.TEXTCANVAS];
float x = 0;
float y = 0;
int alignment = 0;
switch (pos) {
case TOP_LEFT:
x = position.getLeft(3);
y = position.getTop(content.getLeading());
alignment = Element.ALIGN_LEFT;
break;
case TOP_RIGHT:
x = position.getRight(3);
y = position.getTop(content.getLeading());
alignment = Element.ALIGN_RIGHT;
break;
case BOTTOM_LEFT:
x = position.getLeft(3);
y = position.getBottom(3);
alignment = Element.ALIGN_LEFT;
break;
case BOTTOM_RIGHT:
x = position.getRight(3);
y = position.getBottom(3);
alignment = Element.ALIGN_RIGHT;
break;
}
ColumnText.showTextAligned(canvas, alignment, content, x, y, 0);
}
}
This is how I use these events:
public void createPdf(String dest) throws IOException, DocumentException {
// 1. Create a Document which contains a table:
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream(dest));
document.open();
PdfPTable table = new PdfPTable(2);
PdfPCell cell1 = new PdfPCell();
PdfPCell cell2 = new PdfPCell();
PdfPCell cell3 = new PdfPCell();
PdfPCell cell4 = new PdfPCell();
// 2. Inside that table, make each cell with specific height:
cell1.setFixedHeight(50);
cell2.setFixedHeight(50);
cell3.setFixedHeight(50);
cell4.setFixedHeight(50);
// 3. Each cell has the same background image
ImageEvent imgEvent = new ImageEvent(Image.getInstance(IMG));
cell1.setCellEvent(imgEvent);
cell2.setCellEvent(imgEvent);
cell3.setCellEvent(imgEvent);
cell4.setCellEvent(imgEvent);
// 4. Add text in front of the image at specific position
cell1.setCellEvent(new PositionEvent(new Phrase("Top left"), POSITION.TOP_LEFT));
cell2.setCellEvent(new PositionEvent(new Phrase("Top right"), POSITION.TOP_RIGHT));
cell3.setCellEvent(new PositionEvent(new Phrase("Bottom left"), POSITION.BOTTOM_LEFT));
cell4.setCellEvent(new PositionEvent(new Phrase("Bottom right"), POSITION.BOTTOM_RIGHT));
// Wrap it all up!
table.addCell(cell1);
table.addCell(cell2);
table.addCell(cell3);
table.addCell(cell4);
document.add(table);
document.close();
}
Normally, I would write this code in a more efficient way, but I order the code lines in a way so that they reflect your requirements 1, 2, 3 and 4 literally.
Update:
In the comments, you asked several additional questions. For instance: how to stretch the image:
You were able to answer most of these questions, for instance based on my hint to use ScaleAbsolute:
public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {
img.scaleAbsolute(position.getWidth(), position.getHeight());
img.setAbsolutePosition(position.getLeft(), position.getBottom());
PdfContentByte canvas = canvases[PdfPTable.BACKGROUNDCANVAS];
try {
canvas.addImage(img);
} catch (DocumentException ex) {
// do nothing
}
}
You had one more question that required an extra example (it was hard to explain it in a comment box). I named that example PositionContentInCell2
Instead of using the POSITION enumeration, you asked if it was possible to pass x and y values. You could do that, but you probably won't always know the width and the height of the cells, so why not define percentages such as wPct and hPct, along with an alignment:
class PositionEvent implements PdfPCellEvent {
protected Phrase content;
protected float wPct;
protected float hPct;
protected int alignment;
public PositionEvent(Phrase content, float wPct, float hPct, int alignment) {
this.content = content;
this.wPct = wPct;
this.hPct = hPct;
this.alignment = alignment;
}
public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {
PdfContentByte canvas = canvases[PdfPTable.TEXTCANVAS];
float x = position.getLeft() + wPct * position.getWidth();
float y = position.getBottom() + hPct * (position.getHeight() - content.getLeading());
ColumnText.showTextAligned(canvas, alignment, content, x, y, 0);
}
}
Now you can add these events like this:
cell1.setCellEvent(new PositionEvent(new Phrase(14, "Top left"), 0, 1, Element.ALIGN_LEFT));
cell2.setCellEvent(new PositionEvent(new Phrase(14, "Top right"), 1, 1, Element.ALIGN_RIGHT));
cell3.setCellEvent(new PositionEvent(new Phrase(14, "Top center"), 0.5f, 1, Element.ALIGN_CENTER));
cell4.setCellEvent(new PositionEvent(new Phrase(14, "Bottom center"), 0.5f, 0, Element.ALIGN_CENTER));
cell5.setCellEvent(new PositionEvent(new Phrase(14, "Middle center"), 0.5f, 0.5f, Element.ALIGN_CENTER));
cell6.setCellEvent(new PositionEvent(new Phrase(14, "Middle center"), 0.5f, 0.5f, Element.ALIGN_CENTER));
cell7.setCellEvent(new PositionEvent(new Phrase(14, "Bottom left"), 0, 0, Element.ALIGN_LEFT));
cell8.setCellEvent(new PositionEvent(new Phrase(14, "Bottom right"), 1, 0, Element.ALIGN_RIGHT));
Of course, if you prefer passing x and y (after all: you do know the height, because you are defining a fixed height, the code can be made even simpler: you don't multiple the variables with position.getWidth and position.getHeight().
I'm building a table via a database in Itextsharp with PDFPTable, and the requirements are that no rows/cells in the table have a top or bottom bottom border, but the left and right sides of each cell have a black border (in other words, each column has a left a right border), and the bottom of the table needs to be closed off with a black border as well, which is where my problem lies.
What I'm doing is setting the border to 0, then manually assign borders so that I only get the left and right borders on each cell, as seen below as an example of a "Quantity" column being generated:
cell = new PdfPCell(new Phrase(Qty.value, subheaderFont));
cell.HorizontalAlignment = Element.ALIGN_CENTER;
cell.VerticalAlignment = Element.ALIGN_MIDDLE;
cell.BackgroundColor = new iTextSharp.text.BaseColor(220, 220, 220);
cell.Border = 0;
cell.BorderColorLeft = BaseColor.BLACK;
cell.BorderWidthLeft = .5f;
cell.BorderColorRight = BaseColor.BLACK;
cell.BorderWidthRight = .5f;
table.AddCell(cell);
The issue is obviously I have no way of detecting the last row to add the border-bottom, but I imagine there has to be a way to control the border of the "table" itself, or am I taking the wrong approach to this?
As you found, PdfPTable doesn't have borders, probably because PDF's don't have tables in the first place. It probably just made more sense to put the borders on the PdfPCell directly (even though PDFs don't support those, either). A table is just a collection of cells, anyway, so let them deal with it.
Anyway, the solution is to set the TableEvent on your instance of the PdfPTable class. To do this you'll need a custom implementation of the IPdfPTableEvent interface. The below code should generally do this for you (see the notes at the bottom for "generally")
class TopBottomTableBorderMaker : IPdfPTableEvent {
private BaseColor _borderColor;
private float _borderWidth;
/// <summary>
/// Add a top and bottom border to the table.
/// </summary>
/// <param name="borderColor">The color of the border.</param>
/// <param name="borderWidth">The width of the border</param>
public TopBottomTableBorderMaker(BaseColor borderColor, float borderWidth ) {
this._borderColor = borderColor;
this._borderWidth = borderWidth;
}
public void TableLayout(PdfPTable table, float[][] widths, float[] heights, int headerRows, int rowStart, PdfContentByte[] canvases) {
//widths (should be thought of as x's) is an array of arrays, first index is for each row, second index is for each column
//The below uses first and last to calculate where each X should start and end
var firstRowWidths = widths[0];
var lastRowWidths = widths[widths.Length - 1];
var firstRowXStart = firstRowWidths[0];
var firstRowXEnd = firstRowWidths[firstRowWidths.Length - 1] - firstRowXStart;
var lastRowXStart = lastRowWidths[0];
var lastRowXEnd = lastRowWidths[lastRowWidths.Length - 1] - lastRowXStart;
//heights (should be thought of as y's) is the y for each row's top plus one extra for the last row's bottom
//The below uses first and last to calculate where each Y should start and end
var firstRowYStart = heights[0];
var firstRowYEnd = heights[1] - firstRowYStart;
var lastRowYStart = heights[heights.Length - 1];
var lastRowYEnd = heights[heights.Length - 2] - lastRowYStart;
//Where we're going to draw our lines
PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];
//I always try to save the previous state before changinge anything
canvas.SaveState();
//Set our line properties
canvas.SetLineWidth(this._borderWidth);
canvas.SetColorStroke(this._borderColor);
//Draw some rectangles
canvas.Rectangle(
firstRowXStart,
firstRowYStart,
firstRowXEnd,
firstRowYEnd
);
//They aren't actually drawn until you stroke them!
canvas.Stroke();
canvas.Rectangle(
lastRowXStart,
lastRowYStart,
lastRowXEnd,
lastRowYEnd
);
canvas.Stroke();
//Restore any previous settings
canvas.RestoreState();
}
}
Using it is very easy, just bind an instance to the property:
//Create your name as you normally do
var table = new PdfPTable(3);
//Bind and instance with properties set
table.TableEvent = new TopBottomTableBorderMaker(BaseColor.BLACK, 0.5f);
//The rest is the same
for (var i = 0; i < 6; i++) {
var cell = new PdfPCell(new Phrase(i.ToString()));
cell.HorizontalAlignment = Element.ALIGN_CENTER;
cell.VerticalAlignment = Element.ALIGN_MIDDLE;
cell.BackgroundColor = new iTextSharp.text.BaseColor(220, 220, 220);
cell.Border = 0;
cell.BorderColorLeft = BaseColor.BLACK;
cell.BorderWidthLeft = .5f;
cell.BorderColorRight = BaseColor.BLACK;
cell.BorderWidthRight = .5f;
table.AddCell(cell);
}
Above I said "generally" it should work. If you have table headers and/or footers, however, you're going to need to take those into account, too. This shouldn't be too hard but you'll need to adjust the y values accounting for table.HeaderRows and table.FooterRows.
I have experienced the same issue found the following solution.
From "iText In Action Second Edition"
PdfPCell extends Rectangle, inheriting a plethora of methods to change the way borders are drawn and backgrounds are painted..
The method "DisableBorderSide(int Rectangle)" is how to go about removeing borders with any other sizing involved.
PdfPCell cell = new PdfPCell(new Phrase("Some Text", FontFactory.GetFont("Arial", BaseFont.WINANSI, BaseFont.EMBEDDED, 13, Font.NORMAL, BaseColor.BLACK)));
cell.BackgroundColor = new BaseColor(255, 255, 255);
cell.HorizontalAlignment = Element.ALIGN_CENTER;
cell.BorderColor = BaseColor.BLACK;
cell.Border = Rectangle.BOX;
cell.BorderWidth = 1;
cell.DisableBorderSide(Rectangle.TOP_BORDER);
cell.DisableBorderSide(Rectangle.BOTTOM_BORDER);
cell.Padding = 3;
table.AddCell(cell);
I solved this using nested tables
'CREATE TWO PDFPTABLES
Dim tblNested1 As New PdfPTable(1)
Dim tblNested2 As New PdfPTable(3)
'CREATE CELLS WITH NO BORDER AND ADD THEM TO TABLE2
Dim cellNested1 = New PdfPCell(New Phrase("CELL1"))
cellNested1.Border = 0
tblNested2.AddCell(cellNested1)
Dim cellNested2 = New PdfPCell(New Phrase("CELL2"))
cellNested2.Border = 0
tblNested2.AddCell(cellNested2)
Dim cellNested3 = New PdfPCell(New Phrase("CELL3"))
cellNested3.Border = 0
tblNested2.AddCell(cellNested3)
'APPEND TABLE2 TO A CELL WITH DEFAULT BORDER
Dim cell1 As New PdfPCell
cell1.AddElement(tblNested2)
tblNested1.AddCell(cell1)
document.Add(tblNested1)
var Rectangular = new Rectangle(56, 621, 540,385);
Rectangular.BorderWidthLeft = 0.1f;
Rectangular.BorderWidthRight = 0.1f;
Rectangular.BorderWidthTop = 0.1f;
Rectangular.BorderWidthBottom = 0.1f;
cb.Rectangle(Rectangular);
cb.Stroke();