4

I'm using iTextSharp to convert large images to PDF documents.

This works, but the images appear cropped, because they exceed boundaries of the generated document.

So the question is - how to make the document same size as the image being inserted into it?

I'm using the following code:

  Document doc = new Document(PageSize.LETTER.Rotate());
  try
  {
     PdfWriter.GetInstance(doc, new FileStream(saveFileDialog1.FileName,FileMode.Create));
     doc.Open();
     doc.Add(new Paragraph());
     iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(imagePath);
     doc.Add(img);
   }
   catch
   {
      // add some code here incase you have an exception
   }
   finally
   {
      //Free the instance of the created doc as well
      doc.Close();
   }
Cœur
  • 37,241
  • 25
  • 195
  • 267
SharpAffair
  • 5,558
  • 13
  • 78
  • 158

3 Answers3

4

Try something like this to correct your issue

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();
}
MethodMan
  • 18,625
  • 6
  • 34
  • 52
  • 1
    here is an article you can try.. I am not sure what you are trying or not trying but take a look at this..becasue that should have worked... http://www.mikesdotnetting.com/Article/87/iTextSharp-Working-with-images – MethodMan Jan 03 '12 at 15:20
4

The Document object in iText and iTextSharp is an abstraction that takes care of various spacings, paddings and margins for you automatically. Unfortunately for you, this also means that when you call doc.Add() it takes into account existing margins of the document. (Also, if you happen to add anything else the image will be added relative to that, too.)

One solution would be to just remove the margins:

doc.SetMargins(0, 0, 0, 0);

Instead, it's easier to add the image directly to the PdfWriter object which you get from calling PdfWriter.GetInstance(). You're currently throwing away and not storing that object but you can easily change your line to:

PdfWriter writer = PdfWriter.GetInstance(doc, new FileStream(saveFileDialog1.FileName,FileMode.Create));

Then you can access the DirectContent property of the PdfWriter and call its AddImage() method:

writer.DirectContent.AddImage(img);

Before doing this you must also absolutely position the image by calling:

img.SetAbsolutePosition(0, 0);

Below is a full working C# 2010 WinForms app targeting iTextSharp 5.1.1.0 that shows the DirectContent method above. It dynamically creates two images of different sizes with two red arrows stretching across both vertically and horizontally. Your code would obviously just use standard image loading and could thus omit a lot of this but I wanted to deliver a full working example. See the notes in the code for more details.

using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace WindowsFormsApplication1 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e) {
            //File to write out
            string outputFilename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Images.pdf");

            //Standard PDF creation
            using (FileStream fs = new FileStream(outputFilename, FileMode.Create, FileAccess.Write, FileShare.None)) {
                //NOTE, we are not setting a document size here at all, we'll do that later
                using (Document doc = new Document()) {
                    using (PdfWriter writer = PdfWriter.GetInstance(doc, fs)) {
                        doc.Open();

                        //Create a simple bitmap with two red arrows stretching across it
                        using (Bitmap b1 = new Bitmap(100, 400)) {
                            using (Graphics g1 = Graphics.FromImage(b1)) {
                                using(Pen p1 = new Pen(Color.Red,10)){
                                    p1.StartCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
                                    p1.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
                                    g1.DrawLine(p1, 0, b1.Height / 2, b1.Width, b1.Height / 2);
                                    g1.DrawLine(p1, b1.Width / 2, 0, b1.Width / 2, b1.Height);

                                    //Create an iTextSharp image from the bitmap (we need to specify a background color, I think it has to do with transparency)
                                    iTextSharp.text.Image img1 = iTextSharp.text.Image.GetInstance(b1, BaseColor.WHITE);
                                    //Absolutely position the image
                                    img1.SetAbsolutePosition(0, 0);
                                    //Change the page size for the next page added to match the source image
                                    doc.SetPageSize(new iTextSharp.text.Rectangle(0, 0, b1.Width, b1.Height, 0));
                                    //Add a new page
                                    doc.NewPage();
                                    //Add the image directly to the writer
                                    writer.DirectContent.AddImage(img1);
                                }
                            }
                        }

                        //Repeat the above but with a larger and wider image
                        using (Bitmap b2 = new Bitmap(4000, 1000)) {
                            using (Graphics g2 = Graphics.FromImage(b2)) {
                                using (Pen p2 = new Pen(Color.Red, 10)) {
                                    p2.StartCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
                                    p2.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
                                    g2.DrawLine(p2, 0, b2.Height / 2, b2.Width, b2.Height / 2);
                                    g2.DrawLine(p2, b2.Width / 2, 0, b2.Width / 2, b2.Height);
                                    iTextSharp.text.Image img2 = iTextSharp.text.Image.GetInstance(b2, BaseColor.WHITE);
                                    img2.SetAbsolutePosition(0, 0);
                                    doc.SetPageSize(new iTextSharp.text.Rectangle(0, 0, b2.Width, b2.Height, 0));
                                    doc.NewPage();
                                    writer.DirectContent.AddImage(img2);
                                }
                            }
                        }


                        doc.Close();
                    }
                }
            }
            this.Close();
        }
    }
}
Chris Haas
  • 53,986
  • 12
  • 141
  • 274
  • Thanks - sounds much better than the original solution. Do you have any idea what is the maximum image size this method can handle? – SharpAffair Jan 04 '12 at 15:54
  • According to the PDF spec (Annex C Section 2) the minimum size for a 1.6 compliant PDF is 3x3 and the maximum is 14,400x14,4000. Note that these sizes are in "units in default user space" which if you don't change is 1/72 inch. Its usually best to think about units as pixels. See this post if you want to learn a little more about "user space" and "units": http://stackoverflow.com/a/8245450/231316 – Chris Haas Jan 04 '12 at 16:15
  • At the first glance, seems to be working flawlessly. Your post is tremendously helpful! Thanks! – SharpAffair Jan 04 '12 at 20:23
1

You didn't say if you're adding one image to the document or multiple images. But either way, changing the Document.PageSize is a little tricky. You can change the page size at any time using by calling Document.SetPageSize(), but the call ONLY takes effect on the NEXT page.

In other words, something like this:

<%@ WebHandler Language="C#" Class="scaleDocToImageSize" %>
using System;
using System.Web;
using iTextSharp.text;
using iTextSharp.text.pdf;

public class scaleDocToImageSize : IHttpHandler {
  public void ProcessRequest (HttpContext context) {
    HttpServerUtility Server = context.Server;
    HttpResponse Response = context.Response;
    Response.ContentType = "application/pdf";
    string[] imagePaths = {"./Image15.png", "./Image19.png"};
    using (Document document = new Document()) {
      PdfWriter.GetInstance(document, Response.OutputStream);
      document.Open();
      document.Add(new Paragraph("Page 1"));
      foreach (string path in imagePaths) {
        string imagePath = Server.MapPath(path);
        Image img = Image.GetInstance(imagePath);

        var width = img.ScaledWidth 
          + document.RightMargin
          + document.LeftMargin
        ;
        var height = img.ScaledHeight
          + document.TopMargin
          + document.BottomMargin
        ;
        Rectangle r = width > PageSize.A4.Width || height > PageSize.A4.Height
          ? new Rectangle(width, height)
          : PageSize.A4
        ;
/*
 * you __MUST__ call SetPageSize() __BEFORE__ calling NewPage()
 * AND __BEFORE__ adding the image to the document
 */
        document.SetPageSize(r);
        document.NewPage();
        document.Add(img);
      }
    }
  }
  public bool IsReusable { get { return false; } }
}

The working example above is in a web environment (.ashx HTTP handler), so you need to replace Response.OutputStream above with a FileStream (from your code snippet). And obviously you need to replace the file paths too.

kuujinbo
  • 9,272
  • 3
  • 44
  • 57