Adding an image to a PDF using iTextSharp and scale it properly - c#

here's my code. It correctly adds the pictures I want and everything works except that the images are using their native resolution, so if the image is big it's being cropped to fit the page.
Is there some way to have the picture use like a Zoom feature to stretch to fit, but also maintain the aspect ratio? There has to be something I'm missing there. :P
Here's a picture to illustrate the problem:
using System;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.Drawing;
using System.Collections.Generic;
namespace WinformsPlayground
{
public class PDFWrapper
{
public void CreatePDF(List<System.Drawing.Image> images)
{
if (images.Count >= 1)
{
Document document = new Document(PageSize.LETTER);
try
{
// step 2:
// we create a writer that listens to the document
// and directs a PDF-stream to a file
PdfWriter.GetInstance(document, new FileStream("Chap0101.pdf", FileMode.Create));
// step 3: we open the document
document.Open();
foreach (var image in images)
{
iTextSharp.text.Image pic = iTextSharp.text.Image.GetInstance(image, System.Drawing.Imaging.ImageFormat.Jpeg);
document.Add(pic);
document.NewPage();
}
}
catch (DocumentException de)
{
Console.Error.WriteLine(de.Message);
}
catch (IOException ioe)
{
Console.Error.WriteLine(ioe.Message);
}
// step 5: we close the document
document.Close();
}
}
}
}

I solved it using the following:
foreach (var image in images)
{
iTextSharp.text.Image pic = iTextSharp.text.Image.GetInstance(image, System.Drawing.Imaging.ImageFormat.Jpeg);
if (pic.Height > pic.Width)
{
//Maximum height is 800 pixels.
float percentage = 0.0f;
percentage = 700 / pic.Height;
pic.ScalePercent(percentage * 100);
}
else
{
//Maximum width is 600 pixels.
float percentage = 0.0f;
percentage = 540 / pic.Width;
pic.ScalePercent(percentage * 100);
}
pic.Border = iTextSharp.text.Rectangle.BOX;
pic.BorderColor = iTextSharp.text.BaseColor.BLACK;
pic.BorderWidth = 3f;
document.Add(pic);
document.NewPage();
}

Personally, I use something close from fubo's solution and it works well:
image.ScaleToFit(document.PageSize);
image.SetAbsolutePosition(0,0);

You can try something like this:
Image logo = Image.GetInstance("pathToTheImage")
logo.ScaleAbsolute(500, 300)

image.ScaleToFit(500f,30f);
this method keeps the aspect ratio of the image

image.SetAbsolutePosition(1,1);

Related

c#: change image DPI using MagickImage and resize the image

i am using MagickImage to change Dpi of an image but it doesn't work
MagickNET.SetGhostscriptDirectory(System.IO.Directory.GetCurrentDirectory());
MagickReadSettings settings = new MagickReadSettings();
settings.Density = new Density(72, 72);
using (MagickImage image = new MagickImage(#"C:\Users\User\AppData\Local\Temp\Chapter 4\Figure 4-1.tif", settings))
{
image.Write(#"C:\Users\User\AppData\Local\Temp\Chapter 4\Figure 4-1.jpg");
}
or if this doesn't work
is there a way to resize the image like what the photoshop did
example the image with 300 dPi have a w1200xh788 size
and using photoshop. i changed the dpi to 72 and it creates a w288xh189
how can i programmatically do this. thank you
You can do as following :
using System;
namespace ImageDPI
{
public class Program
{
public static void Main(string[] args)
{
int Aw, Ah, Rw, Rh, Adpi, Rdpi;
Aw = 1200;
Ah = 788;
Adpi = 300;
Rdpi = 72;
Rw= (Aw * Rdpi) / Adpi;
Rh= (Ah * Rdpi) / Adpi;
Console.WriteLine(Rw);
Console.WriteLine(Rh);
}
}
}

center image in pdf using itextsharp

What I'm trying to do here is to add an image to a blank pdf. So far I've done it, but I want the image to be centered. How can I do this?
Here is my C# code:
using (MemoryStream ms = new MemoryStream())
{
Document doc = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.GetInstance(doc, new FileStream(System.IO.Path.Combine(filepath, strFilename), FileMode.Create));
doc.AddTitle("Document Title");
doc.Open();
iTextSharp.text.Image image1 = iTextSharp.text.Image.GetInstance(#"C:\Users\Desktop\Winniethepooh.png");
image1.Alignment = iTextSharp.text.Image.ALIGN_CENTER;
if (image1.Height > image1.Width)
{
//Maximum height is 800 pixels.
float percentage = 0.0f;
percentage = 700 / image1.Height;
image1.ScalePercent(percentage * 100);
}
else
{
//Maximum width is 600 pixels.
float percentage = 0.0f;
percentage = 540 / image1.Width;
image1.ScalePercent(percentage * 100);
}
//image1.Alignment = iTextSharp.text.Image.ALIGN_CENTER;
doc.Add(image1);
doc.Close();
}
And this is the output:
https://drive.google.com/open?id=0BzaejXGgqBOAMzd0UlY2QWFXNms
What I want is that the image is centered on the page. Currently the image is on the top of the page.
I even set the image alignment, but why doesn't it center the image on the page?
You need to use SetAbsolutePosition() in order to center the image.
Just add the following to your code before you call doc.Add(image1);:
...
...
image1.SetAbsolutePosition((PageSize.A4.Width - image1.ScaledWidth) / 2, (PageSize.A4.Height - image1.ScaledHeight) / 2);
doc.Add(image1);
...
...
Hope this helps.

GDI+ Fails to save PNG C#

Edit:
This appears to be a filesize issue. The filesize that I'm working with is 10600 x 700 pixels. This ends up being a ~280 MB bitmap. I've attempted saving significantly smaller file sizes (10x10) as PNG, where they correctly save out. Is there a size limitation that is being hit with GDI+?
Pretty much full code:
class Converter
{
/* Each PNG below is 50x50 */
private static readonly int BLOCK_SIZE = 50;
private static Dictionary<char, Bitmap> BlockLookup => new Dictionary<char, Bitmap>
{
{'R', new Bitmap("Content/Map/Blocks/RedBlock.png")},
{'G', new Bitmap("Content/Map/Blocks/GreenBlock.png")},
{'B', new Bitmap("Content/Map/Blocks/BlueBlock.png")},
{'P', new Bitmap("Content/Map/Blocks/PurpleBlock.png")},
{'Y', new Bitmap("Content/Map/Blocks/YellowBlock.png")},
{'O', new Bitmap("Content/Map/Blocks/OrangeBlock.png")}
};
public void Convert(string textFilePath, string outputDirectory)
{
List<string> fileContents = new List<string>();
/* Irrelevant loading code snipped, loads text file into fileContents */
int width = fileContents.Max(line => line.Length) * BLOCK_SIZE; // 10600
int height = fileContents.Count * BLOCK_SIZE; // 700
/* Try to convert our text-file-as-image into a real image, mapping chars to their blocks */
using (Bitmap bitmap = new Bitmap(width, height))
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(Color.WhiteSmoke);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
for (int y = 0; y < fileContents.Count; ++y)
{
for (int x = 0; x < fileContents[y].Length; ++x)
{
char currentCharacter = fileContents[y][x];
if (!blockLookup.ContainsKey(currentCharacter))
{
continue;
}
Bitmap mapBlock = blockLookup[currentCharacter];
var mapX = x * BLOCK_SIZE;
var mapY = y * BLOCK_SIZE;
graphics.DrawImage(mapBlock, new Point(mapX, mapY));
}
}
graphics.Flush(FlushIntention.Sync);
graphics.Save();
}
if (!Directory.Exists(outputDirectory))
{
Directory.CreateDirectory(outputDirectory);
}
bitmap.Save(outputDirectory + mapName, ImageFormat.Png);
}
}
}
I have the following code:
using (Bitmap bitmap = new Bitmap(width, height))
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(Color.WhiteSmoke);
/* SNIP, a bunch of image manipulation here */
graphics.Flush();
}
if (!Directory.Exists(outputDirectory))
{
Directory.CreateDirectory(outputDirectory);
}
bitmap.Save(outputDirectory + imageName, ImageFormat.Bmp);
}
This code works great. However, if I change the ImageFormat from ImageFormat.Bmp to be ImageFormat.Png, I get a "A generic error occurred in GDI+.". I've scoured stackoverflow and google. The files do not exist, the path does. I've deleted all output files. I've tried 64bit & 32bit builds. Even stuff like copying the Bitmap and writing to memory streams fails (the below does not work):
using (Bitmap bitmap = new Bitmap(width, height))
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(Color.WhiteSmoke);
/* SNIP, a bunch of image manipulation here */
graphics.Flush();
}
if (!Directory.Exists(outputDirectory))
{
Directory.CreateDirectory(outputDirectory);
}
using (MemoryStream memoryStream = new MemoryStream())
{
Bitmap temp = new Bitmap(bitmap);
temp.Save(memoryStream, ImageFormat.Png);
}
}
I'd really like to be able to output PNG files. However, there seems to be some issue that I'm unaware of with (my version?) of GDI and PNG files. Is there anything I can do to get PNG output?
Sorry, but your Code is working great - I tested it with VS 2013 Express for Win Desktop on Windows Server 2012 R2 - not a problem at all.
Test 1:
private int width = 10600;
private int height = 700;
using (Bitmap bitmap = new Bitmap(width, height))
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(Color.Black);
/* SNIP, a bunch of image manipulation here */
graphics.Flush();
}
bitmap.Save("image.png", ImageFormat.Png);
}
}
Test 2:
private int width = 10600;
private int height = 700;
using (Bitmap bitmap = new Bitmap(width, height))
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(Color.Black);
/* SNIP, a bunch of image manipulation here */
//I read a little around - maybe your graphics operations are out of sync?
graphics.Flush(FlushIntention.Sync);
graphics.Save();
}
using (MemoryStream memoryStream = new MemoryStream())
{
Bitmap temp = new Bitmap(bitmap);
//temp.Save("image.png", ImageFormat.Png); //this worked obviously
temp.Save(memoryStream, ImageFormat.Png);
pictureBox1.Image = Image.FromStream(memoryStream); //a huge black box appears - everything is working fine
}
}
}
Maybe you have a system-issue?
I meant to comment this, but not enough reputation =,(
Maybe this isn't worth as an answer and should be a comment. But I can move it if you want to :)
Indeeed Windows 7 has some problems with big pictures, not sure its GDI+ related or just deeper in the core. But I programmed a photoediting software for university and noticed, that I couldn't load and/or format pictures larger than ~180MB. I found a workaround by piping it in an array and slicing it. But I don't know if you can use that. If I find that code I will update this post.

C# WinForms Out of Memory Exception on Bitmap Clone

I've written a small "watermark" program to add a custom watermark to an image. There are two watermarks a white one and a black one. The watermark is always placed on the left bottom side of the image. I clone that region of the image to determine which watermark should be placed based on that spot (the black watermark on light regions and the white watermark on dark regions).
When I use the application (either in debug or normal) on my machine - no problem. All images are processed and watermarks are added on the correct location.
However, on the client machine, the program breaks on all images throwing a OutOfMemory Exception on the clone part.
I know, ussually, the OutOfMemory Exception will also be thrown when I specify a region out of bounds, but since the function is working like a charm on my machine I can't imagine that is the case. Besides that, the program doesn't break after a few tries, it breaks on all attempts to clone.
Doesn't matter if there is text (DrawString method) or not. It breaks on the Clone.
The images being processed are big, but not "huge" (6016 x 4000 pixels at most) but even with smaller images (3264 x 2448 pixels) the client will break.
Variables:
bmOriginal : original bitmap
processImage : original image (pictureBox) - bmOriginal is a bitmap copy of this
watermarkText : textbox for extra information below watermark
black and white : pictureboxes containing the watermark images
watermarkCombo : combobox for selecting automatic, white or black (automatic fails)
Code:
using (Graphics gWatermark = Graphics.FromImage(bmOriginal))
{
gWatermark.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
System.Drawing.SolidBrush drawBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black);
// position watermark - watermark should be 10% of the image height
int watermarkHeight = (int)(processImage.Image.Height * 0.1);
int watermarkPadding = (int)(watermarkHeight * 0.1); // not completely true, but asume watermark is square
Rectangle watermarkArea = new Rectangle(watermarkPadding, processImage.Image.Height - (watermarkPadding + (watermarkText.Text.Length == 0 ? 0 : watermarkPadding) + watermarkHeight), watermarkHeight, watermarkHeight);
// determine color watermark
bmWatermark = (Bitmap)black.Image;
if (watermarkCombo.SelectedIndex == 0)
{
using (Bitmap watermarkClone = bmOriginal.Clone(watermarkArea, bmOriginal.PixelFormat))
{
var pixels = Pixels(watermarkClone);
if (pixels.Average((Func<Color, decimal>)Intensity) < 110) // human eye adoption; normal threshold should be 128
{
bmWatermark = (Bitmap)white.Image;
drawBrush = new System.Drawing.SolidBrush(System.Drawing.Color.White);
}
}
}
else if (watermarkCombo.SelectedIndex == 1)
{
bmWatermark = (Bitmap)white.Image;
drawBrush = new System.Drawing.SolidBrush(System.Drawing.Color.White);
}
// draw the watermark
gWatermark.DrawImage(bmWatermark, watermarkArea.X, watermarkArea.Y, watermarkArea.Width, watermarkArea.Height);
// draw the text (if needed)
if (watermarkText.Text.Length > 0)
{
System.Drawing.Font drawFont = new System.Drawing.Font("Tahoma", (float)watermarkPadding);
gWatermark.DrawString(watermarkText.Text, drawFont, drawBrush, watermarkPadding, bmOriginal.Height - (watermarkPadding * 2));
}
}
bmOriginal.Save(System.IO.Path.Combine(diWatermarked.FullName, fileName), System.Drawing.Imaging.ImageFormat.Jpeg);
line of error: using (Bitmap watermarkClone = bmOriginal.Clone(watermarkArea, bmOriginal.PixelFormat))
Now the big question: how do I get rid of that OutOfMemory exception.. anyone an idea?
EDIT When I choose not to determine the color of the watermark automatically and just add a watermark (let's say the white one) the program functions normally. I've seen the stack trace in an error log (on the catch of the function I output the exception and -if any- inner exceptions).
I know a lot of the OOM exceptions using the Clone function occur when you specify a region out of bounds; but that is not the case here.
When I look at my memory when using the app in debug mode, I start at 5.36 Gb program started and a normalized 5.39 Gb (with a max spike of 5.42 Gb) when running the execution I mentioned, it's not guzzling memory like crazy.
The code I use the determine the average "color" (it's from someone on StackOverflow - I just copied that from some other answer, can't find the link though);
// functions used to determine watermark color
private static decimal ComponentAverage(decimal a, decimal b)
{
return Math.Min(a, b) + Math.Abs(a - b) / 2M;
}
private static decimal Intensity(Color color)
{
decimal result = color.A;
result = ComponentAverage(result, color.R);
result = ComponentAverage(result, color.G);
result = ComponentAverage(result, color.B);
return result;
}
private static IEnumerable<Color> Pixels(Bitmap bitmap)
{
for (int x = 0; x < bitmap.Width; x++)
for (int y = 0; y < bitmap.Height; y++)
yield return bitmap.GetPixel(x, y);
}
SOURCE There is a test project here: http://hotpepper.nu/oomtestapp.zip
Leon, I've changed your code you've uploaded to not lock any resources.
I've notices that if I keep your application open, I cannot delete the output folders because some files are in use. This usually means that you DID NOT release all file handles, basically it is always the last file.
I couldn't reproduce the out of memory issue on my computer before and after my changes, seems to be an issue with very big files maybe?
Ok anyways, I found that you use ImageBox to load the white and black resource and to load the image from disc. This is not needed at all, intead use the resources directly
Bitmap white = OomTestApp.Properties.Resources.white;
Bitmap black = OomTestApp.Properties.Resources.black;
Then to load an image from disc, simply use Bitmap.FromFile
I have added some lines to release your resources properly .Dispose where no using blocks are used.
And I also removed the Clone() call because it is absolutely not needed I think, because you are just computing the pixels of the original image and you do not draw something into that image at that point. So what was the need to have the image cloned?
Here is the full code (starting after you created the folders)
if (errors == 0)
{
this.Height = 323;
goButton.Enabled = false;
stopButton.Enabled = true;
Bitmap white = OomTestApp.Properties.Resources.white;
Bitmap black = OomTestApp.Properties.Resources.black;
Bitmap bmWatermark = black;
Bitmap processImage = null;
progressBar1.Maximum = filesToProcess.Count;
foreach (string handleFile in filesToProcess)
{
string fileName = System.IO.Path.GetFileName(handleFile);
fileNameLabel.Text = "File: " + System.IO.Path.GetFileName(handleFile);
try
{
// create backup if checked
if (diOriginal != null)
{
System.IO.File.Move(handleFile, System.IO.Path.Combine(diOriginal.FullName, fileName));
processImage = (Bitmap)Bitmap.FromFile(System.IO.Path.Combine(diOriginal.FullName, fileName));
}
else
{
processImage = (Bitmap)Bitmap.FromFile(handleFile);
}
double aspectRatio = (double)processImage.Width / (double)processImage.Height;
using (Graphics gWatermark = Graphics.FromImage(processImage))
{
gWatermark.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
System.Drawing.SolidBrush drawBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black);
// position watermark - watermark should be 10% of the image height
int watermarkHeight = (int)(processImage.Height * 0.1);
int watermarkPadding = (int)(watermarkHeight * 0.1); // not completely true, but asume watermark is square
// calculate rectangle. if there is extra text, add extra padding below
Rectangle watermarkArea = new Rectangle(watermarkPadding, processImage.Height - (watermarkPadding + (watermarkText.Text.Length == 0 ? 0 : watermarkPadding) + watermarkHeight), watermarkHeight, watermarkHeight);
// determine color watermark
bmWatermark = black;
if (watermarkCombo.SelectedIndex == 0)
{
/*using (Bitmap watermarkClone = processImage.Clone(watermarkArea, processImage.PixelFormat))
{*/
var pixels = Pixels(processImage);
if (pixels.Average((Func<Color, decimal>)Intensity) < 110) // human eye adoption; normal threshold should be 128
{
bmWatermark = white;
drawBrush = new System.Drawing.SolidBrush(System.Drawing.Color.White);
}
//}
}
else if (watermarkCombo.SelectedIndex == 1)
{
bmWatermark = white;
drawBrush = new System.Drawing.SolidBrush(System.Drawing.Color.White);
}
// draw the watermark
gWatermark.DrawImage(bmWatermark, watermarkArea.X, watermarkArea.Y, watermarkArea.Width, watermarkArea.Height);
// draw the text (if needed)
if (watermarkText.Text.Length > 0)
{
System.Drawing.Font drawFont = new System.Drawing.Font("Tahoma", (float)watermarkPadding);
gWatermark.DrawString(watermarkText.Text, drawFont, drawBrush, watermarkPadding, processImage.Height - (watermarkPadding * 2));
drawFont.Dispose();
}
// disposing resources
drawBrush.Dispose();
}
// save the watermarked file
processImage.Save(System.IO.Path.Combine(diWatermarked.FullName, fileName), System.Drawing.Imaging.ImageFormat.Jpeg);
// stop button pressed?
Application.DoEvents();
if (stopProcess) break;
// update exection progress
progressBar1.Value++;
percentLabel.Text = ((int)((progressBar1.Value * 100) / filesToProcess.Count)).ToString() + "%";
fileCountLabel.Text = "File " + progressBar1.Value.ToString() + "/" + filesToProcess.Count.ToString();
}
catch (Exception ex)
{
try
{
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(System.IO.Path.Combine(folderText.Text, "errorlog.txt"), true))
{
sw.WriteLine("File: " + fileName);
while (ex != null)
{
sw.WriteLine("Message: " + ex.Message);
sw.WriteLine(ex.StackTrace);
sw.WriteLine(ex.Source);
ex = ex.InnerException;
}
sw.WriteLine();
}
}
catch
{
// nothing to do - it already failed
}
errors++;
}
finally
{
if (processImage != null) processImage.Dispose();
}
}
// dispose resources
white.Dispose();
black.Dispose();
bmWatermark.Dispose();
if (!stopProcess)
{
// set status to complete
fileCountLabel.Text = "File " + filesToProcess.Count.ToString() + "/" + filesToProcess.Count.ToString();
percentLabel.Text = "100%";
fileNameLabel.Text = "Completed...";
}
else
{
fileNameLabel.Text = "Aborted...";
}
fileNameLabel.Text += errors.ToString() + " error(s) encountered";
// defaults to screen
progressBar1.Value = progressBar1.Maximum;
stopProcess = false;
goButton.Enabled = true;
stopButton.Enabled = false;
Well, I didn't read everything on the page, but I wonder if anyone mentioned the "stride" of the Bitmap? Basically, your bitmap has to be a multiple of 4 bytes.
I had this problem taking apart a grid to make tiles. The last tile would error because the Bitmap was not evenly divisible by 4.
The stride is the width of a single row of pixels (a scan line),
rounded up to a four-byte boundary. If the stride is positive, the
bitmap is top-down. If the stride is negative, the bitmap is
bottom-up.
https://softwarebydefault.com/2013/04/11/bitmap-color-balance/

Itextsharp: Adjust 2 elements on exactly one page

So, I'm having this problem using C# (.NET 4.0 + WinForms) and iTextSharp 5.1.2.
I have some scanned images stored on a DB and need to build on the fly PDF with those images. Some files have just one page and other ones hundreds. That is working just fine using:
foreach (var page in pages)
{
Image pageImage = Image.GetInstance(page.Image);
pageImage.ScaleToFit(document.PageSize.Width,document.PageSize.Height);
pageImage.Alignment = Image.ALIGN_TOP | Image.ALIGN_CENTER;
document.Add(pageImage);
document.NewPage();
//...
}
The problem is:
I need to add an small table at the bottom of the last page.
I try:
foreach (var page in pages)
{
Image pageImage = Image.GetInstance(page.Image);
pageImage.ScaleToFit(document.PageSize.Width,document.PageSize.Height);
pageImage.Alignment = Image.ALIGN_TOP | Image.ALIGN_CENTER;
document.Add(pageImage);
document.NewPage();
//...
}
Table t = new table....
document.Add(t);
The table is successfully added but IF the size of the image fits the page size of the document then the table is added on the next page.
I need to resize the last image of the document (if it has multiple ones, or the first if has only 1) in order to put the table directly on that page (with the image) and that both ocuppy just one page.
I try to scale the image by percent BUT given that the image size of the image that'll be on the last page is unknow and that it must FILL the biggest portion of the page I need to do that dinamically.
Any idea?
Let me give you a couple of things that might help you and then I'll give you a full working example that you should be able to customize.
The first thing is that the PdfPTable has a special method called WriteSelectedRows() that allows you to draw a table at an exact x,y coordinate. It has six overloads but the most commonly used one is probably:
PdfPTable.WriteSelectedRows(int rowStart,int rowEnd, float xPos, float yPos, PdfContentByte canvas)
To place a table with the upper left corner positioned at 400,400 you would call:
t.WriteSelectedRows(0, t.Rows.Count, 400, 400, writer.DirectContent);
Before calling this method you are required to set the table's width using SetTotalWidth() first:
//Set these to your absolute column width(s), whatever they are.
t.SetTotalWidth(new float[] { 200, 300 });
The second thing is that the height of the table isn't known until the entire table is rendered. This means that you can't know exactly where to place a table so that it truly is at the bottom. The solution to this is to render the table to a temporary document first and then calculate the height. Below is a method that I use to do this:
public static float CalculatePdfPTableHeight(PdfPTable table)
{
using (MemoryStream ms = new MemoryStream())
{
using (Document doc = new Document(PageSize.TABLOID))
{
using (PdfWriter w = PdfWriter.GetInstance(doc, ms))
{
doc.Open();
table.WriteSelectedRows(0, table.Rows.Count, 0, 0, w.DirectContent);
doc.Close();
return table.TotalHeight;
}
}
}
}
This can be called like this:
PdfPTable t = new PdfPTable(2);
//In order to use WriteSelectedRows you need to set the width of the table
t.SetTotalWidth(new float[] { 200, 300 });
t.AddCell("Hello");
t.AddCell("World");
t.AddCell("Test");
t.AddCell("Test");
float tableHeight = CalculatePdfPTableHeight(t);
So with all of that here's a full working WinForms example targetting iTextSharp 5.1.1.0 (I know you said 5.1.2 but this should work just the same). This sample looks for all JPEGs in a folder on the desktop called "Test". It then adds them to an 8.5"x11" PDF. Then on the last page of the PDF, or if there's only 1 JPEG to start with on the only page, it expands the height of the PDF by however tall the table that we're adding is and then places the table at the bottom left corner. See the comments in the code itself for further explanation.
using System;
using System.Text;
using System.Windows.Forms;
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.IO;
namespace Full_Profile1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public static float CalculatePdfPTableHeight(PdfPTable table)
{
//Create a temporary PDF to calculate the height
using (MemoryStream ms = new MemoryStream())
{
using (Document doc = new Document(PageSize.TABLOID))
{
using (PdfWriter w = PdfWriter.GetInstance(doc, ms))
{
doc.Open();
table.WriteSelectedRows(0, table.Rows.Count, 0, 0, w.DirectContent);
doc.Close();
return table.TotalHeight;
}
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
//Create our table
PdfPTable t = new PdfPTable(2);
//In order to use WriteSelectedRows you need to set the width of the table
t.SetTotalWidth(new float[] { 200, 300 });
t.AddCell("Hello");
t.AddCell("World");
t.AddCell("Test");
t.AddCell("Test");
//Calculate true height of the table so we can position it at the document's bottom
float tableHeight = CalculatePdfPTableHeight(t);
//Folder that we are working in
string workingFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Test");
//PDF that we are creating
string outputFile = Path.Combine(workingFolder, "Output.pdf");
//Get an array of all JPEGs in the folder
String[] AllImages = Directory.GetFiles(workingFolder, "*.jpg", SearchOption.TopDirectoryOnly);
//Standard iTextSharp PDF init
using (FileStream fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (Document document = new Document(PageSize.LETTER))
{
using (PdfWriter writer = PdfWriter.GetInstance(document, fs))
{
//Open our document for writing
document.Open();
//We do not want any margins in the document probably
document.SetMargins(0, 0, 0, 0);
//Declare here, init in loop below
iTextSharp.text.Image pageImage;
//Loop through each image
for (int i = 0; i < AllImages.Length; i++)
{
//If we only have one image or we are on the second to last one
if ((AllImages.Length == 1) | (i == (AllImages.Length - 1)))
{
//Increase the size of the page by the height of the table
document.SetPageSize(new iTextSharp.text.Rectangle(0, 0, document.PageSize.Width, document.PageSize.Height + tableHeight));
}
//Add a new page to the PDF
document.NewPage();
//Create our image instance
pageImage = iTextSharp.text.Image.GetInstance(AllImages[i]);
pageImage.ScaleToFit(document.PageSize.Width, document.PageSize.Height);
pageImage.Alignment = iTextSharp.text.Image.ALIGN_TOP | iTextSharp.text.Image.ALIGN_CENTER;
document.Add(pageImage);
//If we only have one image or we are on the second to last one
if ((AllImages.Length == 1) | (i == (AllImages.Length - 1)))
{
//Draw the table to the bottom left corner of the document
t.WriteSelectedRows(0, t.Rows.Count, 0, tableHeight, writer.DirectContent);
}
}
//Close document for writing
document.Close();
}
}
}
this.Close();
}
}
}
EDIT
Below is an edit based on your comments. I'm only posting the contents of the for loop which is the only part that changed. When calling ScaleToFit you just need to take tableHeight into account.
//Loop through each image
for (int i = 0; i < AllImages.Length; i++)
{
//Add a new page to the PDF
document.NewPage();
//Create our image instance
pageImage = iTextSharp.text.Image.GetInstance(AllImages[i]);
//If we only have one image or we are on the second to last one
if ((AllImages.Length == 1) | (i == (AllImages.Length - 1)))
{
//Scale based on the height of document minus the table height
pageImage.ScaleToFit(document.PageSize.Width, document.PageSize.Height - tableHeight);
}
else
{
//Scale normally
pageImage.ScaleToFit(document.PageSize.Width, document.PageSize.Height);
}
pageImage.Alignment = iTextSharp.text.Image.ALIGN_TOP | iTextSharp.text.Image.ALIGN_CENTER;
document.Add(pageImage);
//If we only have one image or we are on the second to last one
if ((AllImages.Length == 1) | (i == (AllImages.Length - 1)))
{
//Draw the table to the bottom left corner of the document
t.WriteSelectedRows(0, t.Rows.Count, 0, tableHeight, writer.DirectContent);
}
}
Just use a method table.CalculateHeights() if you want to know the height of table.

Categories