How to create a simple PDF document in .NET

When we first designed the XFINIUM.PDF library for .NET 2 years ago one of the design targets was an easy learning curve. Although it was released first in a controlled corporate environment we still had to make it easy to use.

We used simple and clear abstractions that relate to real world and also to PDF specification: PdfFixedDocument class represents a PDF document which has a fixed layout by its nature, PdfPage class represents a page in a PDF document, the page drawing surface is represented by a PdfGraphics object. The pen is used to stroke a path outline while the brush is used to fill the path interior. An annotation is represented by a specific instance of PdfAnnotation class, a form field by PdfField class and so on. We tried to match the objects in PDF specification with classes in our object model so if you are looking for more advanced PDF features you can easily find them in our API. For example optional content objects are represented by PdfOptionalContentGroup class in our API.

Creating a PDF document with XFINIUM.PDF is a simple task, an empty PDF document requires only 3 lines of code: create the document, create one page and save the document.

Adding text content to the document above requires 3 more lines of code: font creation for drawing the text, brush creation for setting the text color and drawing the actual text on page graphics.

Our object model follows the PDF specification quite closely. This lets you build complex abstractions, such as flow documents, on top of it without problems.

65 thoughts on “How to create a simple PDF document in .NET”

  1. The basic question when creating a page is the page size.
    Cannot find any information on how to set the page size.

    The documentation seems to be very poor ???

  2. What’s the best way to write out content to pages.

    I currently have:(I build a PdfFormattedContent fc and add all my paragraphs to this, dunno if this is the correct way to do).

    private void DrawFormattedContent(PdfFormattedContent fc) {
    double leftMargin, topMargin, rightMargin, bottomMargin;
    leftMargin = topMargin = rightMargin = bottomMargin = 36;

    var page = _doc.Pages.Add();
    PdfFormattedContent fragment = fc.SplitByBox(page.Width – leftMargin – rightMargin, (page.Height * 1.8) – topMargin – bottomMargin);
    while (fragment != null) {
    page.Graphics.DrawFormattedContent(fragment,
    leftMargin, topMargin, page.Width – leftMargin – rightMargin, (page.Height * 1.8) – topMargin – bottomMargin);
    page.Graphics.CompressAndClose();

    fragment = fc.SplitByBox(page.Width – leftMargin – rightMargin, (page.Height * 1.8) – topMargin – bottomMargin);
    if (fragment != null) {
    page = _doc.Pages.Add();
    }
    }
    }

    Problem is the content is still going off the end of my first page.

    1. Don’t apply 1.8 factor to calculate de SplitByBox size. Just use something like that:

      SplitByBox(page.Width – leftMargin – rightMargin, page.Height – topMargin – bottomMargin);

  3. If I want to enter a “paragraph“, and I need to return to next line

    string paragraph = "azj aeiajiahfioe foiehfioehfiehfie feifjaepfjaepofjaepo fpaejfpeafjaefaefhevpzevje vjepzvihzev zep";
    // Draw the text on the page.
    page.Graphics.DrawString(paragraph, helvetica, brush, 100, 100);

    what is the solution?

    1. If you want the text to wrap automatically at a specified width you can do this:

      PdfStringAppearanceOptions sao = new PdfStringAppearanceOptions();
      sao.Brush = brush;
      sao.Font = helvetica;

      // Height is not set, text has no vertical limit.
      PdfStringLayoutOptions slo = new PdfStringLayoutOptions();
      slo.HorizontalAlign = PdfStringHorizontalAlign.Justified;
      slo.VerticalAlign = PdfStringVerticalAlign.Top;
      slo.X = 20;
      slo.Y = 70;
      slo.Width = 280;
      string text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
      "Sed vel euismod risus. Fusce viverra, nisi auctor ullamcorper porttitor, " +
      "ipsum lacus lobortis metus, sit amet dictum lacus velit nec diam. " +
      "Morbi arcu diam, euismod a auctor nec, aliquam in lectus." +
      "Ut ultricies iaculis augue sit amet adipiscing. Aenean blandit tortor a nisi " +
      "dignissim fermentum id adipiscing mauris. Aenean libero turpis, varius nec ultricies " +
      "faucibus, pretium quis lectus. Morbi mollis lorem vel erat condimentum mattis mollis " +
      "nulla sollicitudin. Nunc ut massa id felis laoreet feugiat eget at eros.";
      page.Graphics.DrawString(text, sao, slo);

  4. Hi, I’m trying to generate a pdf with some foreign characters.

    For chinese and japanese works fine, simply using a different PdfStandardFont, for example, PdfStandardFontFace.MonotypeSungLight for chinese.

    But i can’t write text in russian. Could you help me?

    Thanks.

    1. The CJK fonts included in PdfStandardFontFace enumeration are based on Adobe’s CJK language packs. Not all PDF viewers might display them correctly because the fonts are not embedded (smaller PDF file size).
      For any Unicode characters (chinese, russian, etc) you can use Unicode True Type fonts. You just have to make sure the font supports the language you want. The PdfUnicodeTrueTypeFont class implements Unicode True Type fonts. The Fonts and Text samples show how to use this class.

  5. I can’t save the document to (sample.pdf), it has an error of: cannot convert ‘string’ to ‘System.IO.Stream’. How am I going to fix this?

    1. I assume you use the PCL version of XFINIUM.PDF which does not support file paths. You have to create either a MemoryStream or a FileStream and save the document to the stream.

      1. I use the CROSS PLATFORM PROFESSIONAL EDITION 5.5.0 of XFINIUM.PDF, and after that I add the uwp dll file to the references.

      2. For UWP the part that creates a stream for writing the PDF file is a bit verbose, here it is the full code (the storage related classes are located in Windows.Storage namespace):

        PdfFixedDocument document = new PdfFixedDocument();
        PdfPage page = document.Pages.Add();
        // Create a standard font with Helvetica face and 24 point size
        PdfStandardFont helvetica = new PdfStandardFont(PdfStandardFontFace.Helvetica, 24);
        // Create a solid RGB red brush.
        PdfBrush brush = new PdfBrush(PdfRgbColor.Red);
        // Draw the text on the page.
        page.Graphics.DrawString("Hello World", helvetica, brush, 100, 100);

        // Get a local folder for the application.
        StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
        // Create the helloworld.pdf file and open it as a random access stream.
        StorageFile pdfFile = await storageFolder.CreateFileAsync("helloworld.pdf", CreationCollisionOption.ReplaceExisting);
        var pdfStream = await pdfFile.OpenAsync(FileAccessMode.ReadWrite);

        // Convert the random access stream to a .NET Stream and save the document.
        using (Stream stm = pdfStream.AsStream())
        {
        document.Save(stm);
        await stm.FlushAsync();
        }
        pdfStream.Dispose();

      3. If you run the code in debug mode, you can inspect the Path property of the pdfFile object, it will give you the location on disk of the PDF file. It looks something like this: C:Users<yourusername>AppDataLocalPackages<packageid-guid>LocalStatehelloworld.pdf

      4. My mistake, it is the FileSavePicker class. Here it is the full code:
        PdfFixedDocument document = new PdfFixedDocument();
        PdfPage page = document.Pages.Add();
        // Create a standard font with Helvetica face and 24 point size
        PdfStandardFont helvetica = new PdfStandardFont(PdfStandardFontFace.Helvetica, 24);
        // Create a solid RGB red brush.
        PdfBrush brush = new PdfBrush(PdfRgbColor.Red);
        // Draw the text on the page.
        page.Graphics.DrawString("Hello World", helvetica, brush, 100, 100);

        FileSavePicker savePicker = new FileSavePicker();
        // Dropdown of file types the user can save the file as
        savePicker.FileTypeChoices.Add("PDF files", new List() { ".pdf" });
        // Default file name if the user does not type one in or select a file to replace
        savePicker.SuggestedFileName = "helloworld.pdf";

        StorageFile pdfFile = await savePicker.PickSaveFileAsync();
        if (pdfFile != null)
        {
            var pdfStream = await pdfFile.OpenAsync(FileAccessMode.ReadWrite);

            // Convert the random access stream to a .NET Stream and save the document.
            using (Stream stm = pdfStream.AsStream())
            {
                document.Save(stm);
                await stm.FlushAsync();
            }
            pdfStream.Dispose();
        }

      5. There’s a problem in this code:
        savePicker.FileTypeChoices.Add(“PDF files”, new List() { “.pdf” });

        the List() has an error.

      6. iOS does not let you select a folder for saving a document, usually you save it to your documents folder:
        pdfDocument.Save(Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments) + “/file.pdf”);

    1. The PdfAnsiTrueTypeFont and PdfUnicodeTrueTypeFont classes let you use external TrueType fonts in PDF files. The code below shows how to use a TrueType font:
      // ttfStream is a stream that contains the TrueType font.
      PdfAnsiTrueTypeFont ttf = new PdfAnsiTrueTypeFont(ttfStream, 16, true);
      page.Graphics.DrawString("TrueType font", ttf, blackBrush, 20, 150);

      The Fonts sample included in the install kit will give you more details about the supported fonts in XFINIUM.PDF library.

  6. Can you give details on how to create a simple PDF in Xamarin? I’m following your sample up to the point of creating the file, when I do this:

    using (Stream stream = new FileStream (pdfPath, FileMode.Create))
    doc.Save (stream);

    But the resulting PDF can’t be opened, both OSX Preview and Acrobat Reader say it’s invalid (although it looks good internally).

  7. Hello. I have one paged PDF doc where only image is placed (nothing more, just 1 image). How can I draw a line on this image? I’m interested in drawing it on the “image level” (this means that pixels of the image will be changed with that line).

    In samples there are some examples, but all of them are drawing lines and other objects above the image, on the new layer, so the original image is not changed.

    Thanks for the help 🙂

    1. Your requirement can be implemented like this: extract the image from the PDF file (using XFINIUM.PDF), modify it (add a line, etc) using a 3rd party imaging toolkit or GDI and insert back the image in the PDF file (using XFINIUM.PDF). XFINIUM.PDF does not include image editing capabilities.

    1. It is possible, but you have to implement this in your application. You have to display the extracted image and implement the drawing functionality on the image. When the user saves the changes you insert back in the PDF the modified image.

  8. Hi. Can you please tell the difference in next 2 methods:

    RedactArea(new PdfVisualRectangle(left,top,width,height)) – here area with written coordinates will be edited

    RedactArea(new PdfVisualRectangle()) – what will be done in this way?

    Thanks in advance

    1. First line will redact the specified area while the second line will have no effect because the area to be redacted has 0 size. The default PdfVisualRectangle constructor creates a rectangle with left = top = width = height = 0.

  9. Hi. Is it possible to do undo action after the rectangle is drawn? I mean this:

    page.Graphics.DrawRectangle(brush, x, y, w, h);

    after this what need I do to remove that drawn rectangle?

    Thanks in advance

    1. It is better to design the code so that the rectangle is drawn only if you need it.
      The rectangle can be removed after it has been drawn but the procedure is not simple. First you have to tag the rectangle when you draw it (so you can identify it during removal) and then you have to implement a page transformer that scans the page content for the rectangle and removes it when it is found. I believe all this will add an unnecessary overhead to your code.

  10. Hi. I’m having troubles during a saving process. Problem that the size is growing too fast. Here is what I’m doing:

    PdfFile = await StorageLocalCacheFolder.GetFileAsync("test.pdf");
    var pdfStream = await PdfFile.OpenAsync(FileAccessMode.ReadWrite);
    MemoryStream memoryStream = new MemoryStream();
    using (Stream stm = pdfStream.AsStream())
    {
    await stm.CopyToAsync(memoryStream);

    document.Save(stm);
    await stm.FlushAsync();
    }

    Above my comment, on 9th of February you suggested this approach of doing Save action.

    So, how to do it correctly, to fix that growing size?

    P.S. if I will do “Discard Private Data of other applications” on the saved document its size become much smaller. So maybe during a Save() method such data is adding to a document?

    1. XFINIUM.PDF does not add any additional data to the PDF file. Can you send us a sample PDF file that its size you consider to be too large? Also you copy the test.pdf to a memory stream. This causes the stream pointer to move to the end of the stream so when you save the PDF document you actually add it at the end of the existing file and this increases the file size. I suggest to comment this line “await stm.CopyToAsync(memoryStream);” and also generate a unique file name for the output file.
      Repeated saves on the same file can create invalid files if the new file is smaller than the initial one. The operation above does not truncate the input file, it just writes in it so you could end up with the new file and garbage content at its end.

      1. Hi. Where I can send you that test PDF which I think it’s too large?

        And one more question. For example, I have multiple pages in my document. Can I detect on which I’m currently in and draw exactly on the 2nd page (for example)?

        Thanks in advance

      2. I’ve just sent a letter with attached file.

        Yes, I saw that property. But is it a way to know current page, not setting it by index? For example smth like such property:

        PdfFixedDocument.CurrentSelectedPage – this would be great 🙂

      3. The file you sent contains 76 PDF files in it.
        This happens because you always append the new file at the end of an existing file.
        You have to generate a unique file name when saving or first you have to truncate the input PDF file in which you want to save the new PDF document.
        The PdfFixedDocument does not have the concept of current page. You have all the pages at your disposal and you can draw on any of them in any order.

  11. Hello. Can you please tell what can be the reason of getting such error:

    “Root object is missing in Pdf file.”? And how to solve it?

    Thanks in advance

  12. Hello,
    Where do PDF gets saved? Save method is accepting Stream object and not file path. Also, two solutions suggested in above comments one with StorageFolder and FilePicker are not available in .Net Core. Please suggest. Thanks.

    1. With .NET Core you create a FileStream object and save the document to that stream. After the document is saved, you flush and close the stream. The .NET Core samples included in Cross Platform package show this functionality.

  13. HI,

    I am trying to fill a Pdf form using xfinium, It has two pages of form and populated the value for the fields and trying to save the Document. It saves the first page with populated data and the second page saves with out populated data.

    Please let me know is there anything I missed or it is a BUG in Xfinium.

    I am using XFINIUM.PDF 6.5.0.

    Thanks

  14. Hi,

    Is there any way to add an image to the PDF once then draw it on multiple pages? The sort of thing I’m thinking of is a company logo that you might put at the top of every page in a PDF with many pages, so if you draw it separately on each page you will be adding the size of the image to the PDF file many times over. Some other libraries I have used let you add an image to the file as a resource then draw it from the resource so the image only gets stored once in the PDF, which keeps the size down.

    FYI, I am using version 6.9.0. Thanks in advance.

    1. Yes, this is the default behavior. When you create a PdfImage object for your image and use that image object to draw it on multiple pages the actual image data will be embedded only once in the PDF file.

      1. Thanks for the reply. I thought that might be the default behaviour as it would make sense, but when I had tested it the file size still went up if the same PdfImage was drawn multiple times. However, that was quite a small image and after your reply it struck me that it would be hard to tell how much of the increase was the image content itself possibly being repeated and how much was the inevitable instructions to draw it at each point, so I tried it with a larger image and the file is much smaller if you redraw the same PdfImage than it is if you create a new PdfImage each time from a source file, which confirms that as you said, it does only add the image content once if you reuse the same PdfImage!

        What I will still need to do is to keep a dictionary of PdfImage instances that were already drawn, as my use case is that image drawing on each page could include many different images, so my code will need to detect that an image file that was used before is being used again and therefore reuse the existing PdfImage to draw it rather than creating a new PdfImage again for the same file. That will be pretty simple to do.

        Many thanks for your help. FYI, I’m currently replacing another PDF library with yours, and it’s noticeable how much simpler many of the calls are to make with Xfinium, so the new routines in my application are often half the size with Xfinium that they were with the other library. There’s still some way to go yet, but so far I haven’t found anything Xfinium can’t do or that doesn’t work, and one thing you just couldn’t do with the other library that Xfinium can do (different border and fill transparency settings on a shape), which is obviously good!

  15. Hi,

    I have another question, this time regarding TrueType fonts. I can see from the examples how to add a TrueType font, but to do that it appears that you have to know what the font filename is, which my application won’t normally know. All it does know is the name of the font, as seen in for example System.Windows.Media.FontFamily.Source. After a lot of googling I can’t see any reliable way to to find the name of the font file when given only the name of the font, as it doesn’t seem to be available through .NET or the Windows API as far as I can see, and it’s not simple as there can be multiple font files for the same font with different characteristics (bold, italic, etc) and there doesn’t seem to be any way to tell which is which even if you knew it was one of those files (which my code won’t know anyway as it won’t have even the root of the filename).

    Starting from a font name, do you know of a way to find the font filename so I can open a stream to the file, or is there some other way to add a TrueType font to the PDF using Xfinium but supplying only the font name rather than the font filename?

    Many Thanks.

    1. From the FontFamily you retrieve the list of available typefaces (regular, bold, italic, etc) using GetTypefaces() method. You select your desired typeface and call TryGetGlyphTypeface(GlyphTypeface) method to get a GlyphTypeface object. A GlyphTypeface is font face that directly corresponds to a font file on the disk. On the GlyphTypeface object you call the GetFontStream method to retrieve a Stream that contains the actual font file. You can build your PdfAnsiTrueTypeFont object from this stream.

      1. That’s great, thanks, I have implemented that approach and it does now work with all the TrueType fonts I’ve tried so far. There’s some more testing to do, and the vertical positioning of the text isn’t quite right yet but that’s probably a minor issue in my code somewhere which I’ll track down. Thanks again.

  16. Is there any way to bold single word in string cell content.
    E.g:
    PdfFlowTableStringCell stringCell = new PdfFlowTableStringCell();
    stringCell.Content = “This is a long text and i want to bold “ABC” word”;

    I want ABC to bold and the rest of word remain normal.

    1. For this scenario you have to use a composite cell (PdfFlowTableCompositeCell). Create a PdfFormattedContent object with the desired formatting for text parts, create a PdfFlowTextContent object from the formatted content and then add the flow text content to the cell’s Content property (it is a collection of flow content objects).
      compositeCell.Content.Add(flowTextContent);

  17. Hello! I’m trying to save a PDF document in my desktop using StorageFolder storageFolder = StorageFolder.GetFolderFromPathAsync("(mupath)\Desktop") but I get an exception of Access Denied.
    The only way of saving a PDF in the Desktop is with the FileSavePicker? I have tryied to save the PDF in the folder Documents by StorageFolder storageFolder = KnownFolders.DocumentsLibrary but also y get the exception of access denied.

Leave a Reply