How to generate PDF on Windows Phone in VB or C#


I need to generate PDF from my Windows Phone app.

Unfortunately none of the standard free PDF-generating libraries work on Windows Phone. I’ve had to generate the PDF myself, by writing to the file format directly. It turned out to be really easy! Source code is at the bottom of this post, and in this link:

  •  Download source code.zip [60k, requires VS2012, includes Console and WP8 apps]

 

Why even generate PDF on Phone?

Some people say that there’s no point having PDFs on the phone, since it’s not the most efficient use of screen and touch interaction.

Well in my case, I wanted to generate a PDF, upload it to the user’s SkyDrive account, and let the user send an email with a “share” link. That’s how Microsoft’s own PDF-Reader app works when you click the “share” button.

Other people suggest that a Phone has no business generating PDFs because it’s such a costly operation, and you should instead upload your document to your PDF-generating webserver. This is wrong. PDF-generation, at least for the kinds of reports I was generating (two pages of non-flowed text), is a very cheap operation. I bet that it costs more battery to upload+download a document than to generate a PDF.

iTextSharp

http://itextpdf.com/ (AGPL license, or you can purchase a commercial license, available on NuGet)

iTextSharp doesn’t work on Windows Phone, because it has a runtime dependency on System.Drawing.dll. (There’s another experimental port http://itextsharpsl.codeplex.com/ under the MPL license, designed to work on Silverlight and Windows Phone, but I was unable to figure out the references needed to get it work. It’s also a huge package, 30mb compressed source code). iTextSharp has superb documentation, including a published book. Here’s how its API looks, to generate the PDF at the top of this article:

Imports  iTextSharp.text 

Module  Module1
     Sub  Main()
         Dim  doc  As  New  Document ()
         Using  file  As  New  System.IO. FileStream ( “a.pdf” , System.IO. FileMode .Create),
            writer = pdf. PdfWriter .GetInstance(doc, file)
            doc.Open()

             Dim  titleFont =  FontFactory .GetFont( FontFactory .TIMES, 40,  Font .NORMAL)
             Dim  subtitleFont =  FontFactory .GetFont( FontFactory .TIMES, 20,  Font .ITALIC,  BaseColor .BLUE)
             Dim  column  As  New  pdf. ColumnText (writer.DirectContent)
            column.SetSimpleColumn(230, 600, 400, 400)  ‘ bounding coords of column
            column.AddText( New  Phrase ( “Hello all” , titleFont))
            column.Go()
            column.AddText( New  Phrase ( “olé” , subtitleFont))
            column.Go()

            doc.Close()
         End  Using
         Process .Start( “a.pdf” )
     End  Sub
End  Module

 

PDFSharp

http://www.pdfsharp.net/ (MIT license, available on NuGet)

PDFSharp doesn’t work on Windows Phone, because it has a compiletime dependency on System.Drawing.dll. Here’s how its API looks…

Imports  PdfSharp.Drawing

 Module  Module1 
   
 Sub  Main()
       
 Dim  document  As  New  PdfSharp.Pdf. PdfDocument 
       
 Dim  page = document.AddPage
       
 Dim  gfx =  XGraphics .FromPdfPage(page)
       
 Dim  titleFont  As  New  XFont ( “Times New Roman” , 40,  XFontStyle .Regular)
       
 Dim  subtitleFont  As  New  XFont ( “Times New Roman” , 20,  XFontStyle .Italic)
        gfx.DrawString(
 “Hello all” , titleFont,  XBrushes .Black, 230, 400)
       
 Dim  height = gfx.MeasureString( “olé” , subtitleFont).Height
        gfx.DrawString(
 “olé” , subtitleFont,  XBrushes .Blue, 230, 400 + height)

         Using  stream  As  New  IO. FileStream ( “a.pdf” , IO. FileMode .Create)
            document.Save(stream)
       
 End  Using 

         Process .Start( “a.pdf” )
   
 End  Sub 
 End  Module

 

PDFJet

http://www.pdfjet.com/os/edition.html (BSD license; not available on NuGet)

PDFJet doesn’t work on Windows Phone, because it has a compiletime dependency System.IO.DeflateStream. PDFJet is the most interesting of all these PDF libraries. It’s been written from scratch with as few dependencies as possible – no dependencies on System.Drawing, or Winforms. Its only dependency is on DeflateStream. If someone could package it up with a Phone-compatible implementation of DeflateStream then it would work great. I tried to get it to work with DotNetZip and SharpZipLib but didn’t succeed. Note that PDFJet is distributed as just a directory full of .cs files, so you have to create a project for it. Easy to do. Anyway, here’s how it looks:

Imports  PDFjet.NET

 Module  Module1 

     Sub  Main()
       
 Using  stream  As  New  IO. FileStream ( “a.pdf” , IO. FileMode .Create)
           
 Dim  pdf  As  New  PDF (stream)
           
 Dim  page  As  New  Page (pdf,  Letter .PORTRAIT)
           
 Dim  titleFont  As  New  Font (pdf,  CoreFont .TIMES_ROMAN)
            titleFont.SetSize(40)
           
 Dim  subtitleFont  As  New  Font (pdf,  CoreFont .TIMES_ITALIC)
            subtitleFont.SetSize(20)
           
 Dim  text1  As  New  TextLine (titleFont,  “Hello all” )
            text1.SetPosition(230, 400)
            text1.DrawOn(page)
           
 Dim  text2  As  New  TextLine (subtitleFont,  “olé” )
            text2.SetPosition(230, 400 + text2.GetHeight)
            text2.SetColor(
 Color .blue)
            text2.DrawOn(page)
            pdf.Flush()
       
 End  Using 

         Process .Start( “a.pdf” )
   
 End  Sub 

 End  Module 

 

PDFClown

http://pdfclown.wordpress.com/ (LGPL license; not available on NuGet)

PDFClown doesn’t work on Windows Phone, because it has a compiletime dependency System.Drawing and Winforms. PDFClown aggressively uses Java rather than .NET conventions. Its API is an exact mirror of the PDF file format structure. In my view it doesn’t offer much benefit beyond just writing the file format structure manually. Here’s how it looks:

Imports  org.pdfclown
Imports  org.pdfclown.documents.contents

 Module  Module1 

     Sub  Main()
       
 Using  stream  As  New  IO. FileStream ( “a.pdf” , IO. FileMode .Create),
              pdfstream
 As  New  org.pdfclown.bytes. Stream (stream),
              pdf
 As  New  files. File ()

             Dim  document = pdf.Document
           
 Dim  page  As  New  documents. Page (document)
            document.Pages.Add(page)
           
 Dim  titleFont =  New  fonts. StandardType1Font (document, fonts. StandardType1Font . FamilyEnum .Times,  False ,  False )
           
 Dim  subtitleFont =  New  fonts. StandardType1Font (document, fonts. StandardType1Font . FamilyEnum .Times,  False ,  True )
           
 Dim  composer  As  New  composition. PrimitiveComposer (page)
           
 Dim  blockcomposer  As  New  composition. BlockComposer (composer)
            blockcomposer.Begin(
 New  Drawing. RectangleF (230, 400, 200, 200), composition. XAlignmentEnum .Left, composition. YAlignmentEnum .Top)
            composer.SetFont(titleFont, 40)
            blockcomposer.ShowText(
 “Hello all”  & vbCrLf)
            composer.SetFont(subtitleFont, 20)
            composer.SetFillColor(
 New  colorSpaces. DeviceRGBColor (0, 0, 1))
            blockcomposer.ShowText(
 “olé”  & vbCrLf)
            blockcomposer.End()
            composer.Flush()

            pdf.Save(pdfstream, files. SerializationModeEnum .Standard)
       
 End  Using 

         Process .Start( “a.pdf” )

     End  Sub 

 End  Module 

 

Write the PDF file format yourself

http://acroeng.adobe.com/wp/?page_id=321 (documentation of PDF file format; I used the 1.2 version of the PDF spec)

This code works great on Windows Phone. Frankly, the PDF file format is very simple if all you want to do is create simple reports – with text, charts &c. What we need is sample code for that show how to create PDFs, not just libraries that encapsulate how to create PDFs. So here’s my sample code! Note that I make use of one extra function “enc” which translates non-ASCII from Unicode (used in .NET) into WinAnsiEncoding (the encoding I chose to use for the PDF) – full source code is downloadable from the link at the top of this article.

 

Private  Async  Sub  Button1_Click(sender  As  Object , e  As  RoutedEventArgs )  Handles  Button1.Click

     Dim  file =  Await ApplicationData .Current.LocalFolder.CreateFileAsync(“a.pdf” , Windows.Storage. CreationCollisionOption .ReplaceExisting)
   
 Using  stream =  Await  System.IO. WindowsRuntimeStorageExtensions .OpenStreamForWriteAsync(file),
          writer
 As  New  IO. StreamWriter (stream, Text. Encoding .UTF8)

         Dim  xrefs  As  New  List ( Of  Long )()

         ‘ PDF-HEADER 
        writer.WriteLine(
 “%PDF-1.2” )

         ‘ PDF-BODY. Convention is to start with a 4-byte binary comment 
       
 ‘ so everyone recognizes the pdf as binary. Then the file has 
       
 ‘ a load of numbered objects, #1..#7 in this case 
        writer.Write(
 “%” ) : writer.Flush()
        stream.Write({&HC7, &HEC, &H8F, &HA2}, 0, 4) : stream.Flush()
        writer.WriteLine(
 “” )

         ‘ #1: catalog – the overall container of the entire PDF 
        writer.Flush() : stream.Flush() : xrefs.Add(stream.Position)
        writer.WriteLine(
 “1 0 obj” )
        writer.WriteLine(
 “<<“ )
        writer.WriteLine(
 ”  /Type /Catalog” )
        writer.WriteLine(
 ”  /Pages 2 0 R” )
        writer.WriteLine(
 “>>” )
        writer.WriteLine(
 “endobj” )

         ‘ #2: page-list – we have only one child page 
        writer.Flush() : stream.Flush() : xrefs.Add(stream.Position)
        writer.WriteLine(
 “2 0 obj” )
        writer.WriteLine(
 “<<“ )
        writer.WriteLine(
 ”  /Type /Pages” )
        writer.WriteLine(
 ”  /Kids [3 0 R]” )
        writer.WriteLine(
 ”  /Count 1″ )
        writer.WriteLine(
 “>>” )
        writer.WriteLine(
 “endobj” )

         ‘ #3: page – this is our page. We specify size, font resources, and the contents 
        writer.Flush() : stream.Flush() : xrefs.Add(stream.Position)
        writer.WriteLine(
 “3 0 obj” )
        writer.WriteLine(
 “<<“ )
        writer.WriteLine(
 ”  /Type /Page” )
        writer.WriteLine(
 ”  /Parent 2 0 R” )
        writer.WriteLine(
 ”  /MediaBox [0 0 612 792]” )  ‘ Default userspace units: 72/inch, origin at bottom left 
        writer.WriteLine(
 ”  /Resources” )
        writer.WriteLine(
 ”  <<“ )
        writer.WriteLine(
 ”    /ProcSet [/PDF/Text]” )  ‘ This PDF uses only the Text ability 
        writer.WriteLine(
 ”    /Font” )
        writer.WriteLine(
 ”    <<“ )
        writer.WriteLine(
 ”      /F0 4 0 R” )  ‘ I will define three fonts, #4, #5 and #6 
        writer.WriteLine(
 ”      /F1 5 0 R” )
        writer.WriteLine(
 ”      /F2 6 0 R” )
        writer.WriteLine(
 ”    >>” )
        writer.WriteLine(
 ”  >>” )
        writer.WriteLine(
 ”  /Contents 7 0 R” )
        writer.WriteLine(
 “>>” )
        writer.WriteLine(
 “endobj” )

         ‘ #4, #5, #6: three font resources, all using fonts that are built into all PDF-viewers 
       
 ‘ We’re going to use WinAnsi character encoding, defined below. 
        writer.Flush() : stream.Flush() : xrefs.Add(stream.Position)
        writer.WriteLine(
 “4 0 obj” )
        writer.WriteLine(
 “<<“ )
        writer.WriteLine(
 ”  /Type /Font” )
        writer.WriteLine(
 ”  /Subtype /Type1″ )
        writer.WriteLine(
 ”  /Encoding /WinAnsiEncoding” )
        writer.WriteLine(
 ”  /BaseFont /Times-Roman” )
        writer.WriteLine(
 “>>” )
        writer.Flush() : stream.Flush() : xrefs.Add(stream.Position)
        writer.WriteLine(
 “5 0 obj” )
        writer.WriteLine(
 “<<“ )
        writer.WriteLine(
 ”  /Type /Font” )
        writer.WriteLine(
 ”  /Subtype /Type1″ )
        writer.WriteLine(
 ”  /Encoding /WinAnsiEncoding” )
        writer.WriteLine(
 ”  /BaseFont /Times-Bold” )
        writer.WriteLine(
 “>>” )
        writer.Flush() : stream.Flush() : xrefs.Add(stream.Position)
        writer.WriteLine(
 “6 0 obj” )
        writer.WriteLine(
 “<<“ )
        writer.WriteLine(
 ”  /Type /Font” )
        writer.WriteLine(
 ”  /Subtype /Type1″ )
        writer.WriteLine(
 ”  /Encoding /WinAnsiEncoding” )
        writer.WriteLine(
 ”  /BaseFont /Times-Italic” )
        writer.WriteLine(
 “>>” )

         ‘ #7: contents of page. This is written in postscript, fully described in 
       
 ‘ chapter 8 of the PDF 1.2 reference manual. 
        writer.Flush() : stream.Flush() : xrefs.Add(stream.Position)
       
 Dim  sb  As  New  Text. StringBuilder 
        sb.AppendLine(
 “BT” )              ‘ BT = begin text object, with text-units the same as userspace-units 
        sb.AppendLine(
 “/F0 40 Tf” )       ‘ Tf = start using the named font “F0” with size “40” 
        sb.AppendLine(
 “40 TL” )           ‘ TL = set line height to “40” 
        sb.AppendLine(
 “230.0 400.0 Td” )  ‘ Td = position text point at coordinates “230.0”, “400.0” 
        sb.AppendLine(
 “(Hello all) ‘”  ‘ Apostrophe = print the text, and advance to the next line 
        sb.AppendLine(
 “/F2 20 Tf” )        
        sb.AppendLine(
 “20 TL” )            
        sb.AppendLine(
 “0.0 0.2 1.0 rg” )  ‘ rg = set fill color to RGB(“0.0”, “0.2”, “1.0”) 
        sb.AppendLine(
 “(ol”  & enc( “é” ) &  “) ‘” )
        sb.AppendLine(
 “ET” )               
       
  
        writer.WriteLine(
 “7 0 obj” )
        writer.WriteLine(
 “<<“ )
        writer.WriteLine(
 ”  /Length “  & sb.Length)
        writer.WriteLine(
 “>>” )
        writer.WriteLine(
 “stream” )
        writer.Write(sb.ToString())
        writer.WriteLine(
 “endstream” )
        writer.WriteLine(
 “endobj” )

         ‘ PDF-XREFS. This part of the PDF is an index table into every object #1..#7 
       
 ‘ that we defined. 
        writer.Flush() : stream.Flush() :
 Dim  xref_pos = stream.Position
        writer.WriteLine(
 “xref” )
        writer.WriteLine(
 “1 “  & xrefs.Count)
       
 For  Each  xref  In  xrefs
            writer.WriteLine(
 “{0:0000000000} {1:00000} n” , xref, 0)
       
 Next 

         ‘ PDF-TRAILER. Every PDF ends with this trailer. 
        writer.WriteLine(
 “trailer” )
        writer.WriteLine(
 “<<“ )
        writer.WriteLine(
 ”  /Size “  & xrefs.Count)
        writer.WriteLine(
 ”  /Root 1 0 R” )
        writer.WriteLine(
 “>>” )
        writer.WriteLine(
 “startxref” )
        writer.WriteLine(xref_pos)
        writer.WriteLine(
 “%%EOF” )
   
 End  Using 

     Await  Windows.System. Launcher .LaunchFileAsync(file)

 End  Sub

 


Comments (32)

  1. Manu says:

    Hello,

    thanks for this post. I want to generate a PDF file in my Windows Phone app, but I have some problems in understanding the VB code, because I'm a C# developer. Can you provide your sample also in C# code?

    Thanks.

  2. Daniel says:

    Same here!! I need the code in C#, please.

    Thanks

  3. Farhan says:

    This code is not working for Windows Store App.

  4. Jim says:

    If you don't understand this, you are not going to understand it in C# either.

  5. Worked great in c# as well Thanks

    If you want in c# kindly email me techloverr@outlook.com

  6. Creative says:

    Can pictures also be added to the pdf

  7. I wish I'd seen this two weeks ago.  There's so many applications of creating a PDF on the phone, not least to generate invoices in a mobile quoting/invoicing app.  Which is exactly what I've just tried to do.

    This would have made it much easier.

  8. Sorry, I don't have a C# version. But it should be very easy to do. The C# code will be almost identical to the VB code.

    Can pictures be added? I guess so, but it'll require work. I didn't even start to look at how pictures are stored in PDF files. I focused on just the text that I needed for my reports 🙂

  9. Mudassir says:

    Thanks for this helpful post 🙂

  10. Anam says:

    How can we fill out an already existing PDF document?

  11. @Anam, sorry, I have no idea.

  12. Eng Dev says:

    How to display pdf file in windows phone app and select some text to highlight or copy ?

  13. Alberto says:

    great idea, but i have a question, I can not able to print multi line, but just one, there is some error in y code?

      Dim sb As New Text.StringBuilder

               sb.AppendLine("BT")              ' BT = begin text object, with text-units the same as userspace-units

               sb.AppendLine("/F0 20 Tf")       ' Tf = start using the named font "F0" with size "40"

               sb.AppendLine("20 TL")           ' TL = set line height to "40"

               sb.AppendLine("56.0 708.0 Td")  ' Td = position text point at coordinates "230.0", "400.0"

               sb.AppendLine("(" & db_articoli(10) & ") '")   ' Apostrophe = print the text, and advance to the next line

               sb.AppendLine("/F0 20 Tf")

               sb.AppendLine("20 TL")           ' TL = set line height to "40"

               sb.AppendLine("68.0 708.0 Td")  ' Td = position text point at coordinates "230.0", "400.0"

               sb.AppendLine("(DATA) '")   ' Apostrophe = print the text, and advance to the next line

               sb.AppendLine("20 TL")           ' TL = set line height to "40"

               sb.AppendLine("56.0 595.0 Td")  ' Td = position text point at coordinates "230.0", "400.0"

               sb.AppendLine("(INTERVENTO DI) '")   ' Apostrophe = print the text, and advance to the next line

               sb.AppendLine("20 TL")           ' TL = set line height to "40"

               sb.AppendLine("56.0 538.0 Td")  ' Td = position text point at coordinates "230.0", "400.0"

               sb.AppendLine("(MODELLO DI MACCHINA) '")   ' Apostrophe = print the text, and advance to the next line

               sb.AppendLine("20 TL")           ' TL = set line height to "40"

               sb.AppendLine("225.0 538.0 Td")  ' Td = position text point at coordinates "230.0", "400.0"

               sb.AppendLine("(MARCA) '")   ' Apostrophe = print the text, and advance to the next line

               sb.AppendLine("20 TL")           ' TL = set line height to "40"

               sb.AppendLine("396.0 538.0 Td")  ' Td = position text point at coordinates "230.0", "400.0"

               sb.AppendLine("(MATRICOLA) '")   ' Apostrophe = print the text, and advance to the next line

               sb.AppendLine("20 TL")           ' TL = set line height to "40"

               sb.AppendLine("56.0 481.0 Td")  ' Td = position text point at coordinates "230.0", "400.0"

               sb.AppendLine("ET")              '

               '

               writer.WriteLine("7 0 obj")

               writer.WriteLine("<<")

               writer.WriteLine("  /Length " & sb.Length)

               writer.WriteLine(">>")

               writer.WriteLine("stream")

               writer.Write(sb.ToString())

               writer.WriteLine("endstream")

               writer.WriteLine("endobj")

    the other parts are the same like yours

    thanks for your help

  14. Alberto says:

    find the problem, the coordinate are relative not absolute

    thanks again

  15. Pardeep89 says:

    Hi ,

    I need to use above code in wp8 . There's two problem with the code :

    1. Download link of code is not working.

    2. enc function is not give here. Plz provide that too.

    3. Can I render Bitmap with your code.

    Regards,

  16. @Pardeep89, the download link works fine for me! Please try again. It includes a WP8 project, and includes the enc function.

    As for bitmaps, I never tried them. You'll have to do more research to figure out the PDF file format for bitmaps.

  17. Pardeep89 says:

    Hi Lucian ,

    I mean by your pdf reference : http://www.pdf-tools.com/…/pdfreference12.pdf

    Link has web page containing error message.

    Please provide your reference doc. containing mnemonic for Pdf rendering.

    Regards,

  18. @Pardeep89, I see, thanks. I updated the link.

  19. Mansoor says:

    Can you post the code in C#?

    I am not being able to generate in C#. VB works fine

  20. Karthikeyan says:

    is it possible to add image in PDF by this way ! plz help me out in adding image to PDF document that is already available

    Thanks in advance

  21. Ashok Sh says:

    @Karthikeyan  

    For your request ,this article may be helpful, check it out  http://www.e-iceblue.com/…/How-to-Create-a-PDF-Document-and-Insert-an-Image.html

  22. C says:

    Hi,

    Would you happen to know if it's possible to also extract the text in the PDF by decoding the FlateDecode filter and then rendering the text into a string?

  23. C says:

    Thanks for providing the code for creating a PDF BTW, didn't know it could be so simple!

  24. maza says:

    Somebody plz tell me how to add image in pdf.

  25. Abdullatif Abu AlRub (Full Metal Developer) says:

    Hola guys i just rewrite it in c# and would like to share it with you 😀

    Thanks a lot for the original poster, you are awesome

    http://www.facebook.com/…/10154965812750154

  26. user says:

    Hello guys, PDF is much more complex than writing a few strings using standard font. PDF is not simple otherwise 700+ pages spec wouldn't exist.

  27. James Barn says:

    There is a library available, designed for PDF manipulation on all platforms and is called Apitron PDF Kit. Works on windows phone, windows store, xamarin and other platforms.

  28. Mihai Iancu says:

    XFINIUM.PDF library handles PDF files and supports all .NET and Xamarin platforms.

    http://www.xfiniumpdf.com/

  29. Prasad kumarasinghe says:

    Can anybody send me the link to download Itextsharp dll for windows 8.1

  30. uday oleti says:

    Hear guy's i changed the above code to c# it's working good. if any one want the code please mail me at ouday.uk@gmail.com

  31. Suman Das says:

    Ca you plz send me code for windows 7.1 at shasankha@yahoo.com

  32. PawanTiwari says:

    Hi Lucian, acroeng.adobe.com/wp link has been expire. Do you have any updated link for the same. If you have please post