Create PDF ?

8815
76
07-20-2010 06:02 AM
CraigPerreault
New Contributor II
Does anyone have a code sample to create a PDF file using the Silverlight API?
0 Kudos
76 Replies
adamestrada
New Contributor
Thanks Mark,

Those are the example I am going by. I added those files to my project and even changed the name back to the original ExportPDF.xaml and it still can't find the map. The only difference is that have now changed it to a ChildWindow rather than a control.

using ESRI.ArcGIS.Client;
using CMC_Viewer.Utils;

public interface IParentDialog
{
    void SetStatus(string sStatus);
    void TaskCompleted(bool bSuccessful);
}

namespace CMC_Viewer.Views
{
    public partial class ExportPDF : ChildWindow, IParentDialog
    {

        private PDFExporter _Exporter;
        private bool _bTwoStage = false;
        private bool _bFirst = false;

        private const string sOKMsg = "OK";
        private const string sFailedMsg = "Failed";
        private const string sStartMsg = "Click 'Start' to generate image.";
        private const string sStartLabel = "Start";
        private const string sFinishMsg = "Click 'Finish' to create PDF.";
        private const string sFinishLabel = "Finish";

        public ExportPDF()
        {
            InitializeComponent();
            _Exporter = new PDFExporter();
            _Exporter.SetParent(this);
            lblStatus.Content = sOKMsg;
            btnOK.Content = sStartLabel;
            btnOK.Click += btnOK_Click;
            chkExportOption.Checked += chkExportOption_Checked;
            chkExportOption.Unchecked += chkExportOption_Unchecked;
        }

        // Methods called by the MainPage

        public void SetMap(Map TheMap)
        {
            _Exporter.SetMap(TheMap);
        }

        public void AddElement(UIElement TheElement)
        {
            _Exporter.AddElement(TheElement);
        }


<controls:ChildWindow
    x:Class="CMC_Viewer.Views.ExportPDF"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"    
    xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"  
    mc:Ignorable="d"
    d:DesignHeight="60" d:DesignWidth="280" >


MyMap and TheMap do not exist in my Project and I always use plain old "Map" to define the canvas. Do you think this is the problem? I haven't touched PDFExporter.cs either and there is the SetMap method that implements the Type, "Map".

Adam
0 Kudos
adamestrada
New Contributor
I believe the problem here is that MainPage can't see Home.xaml where I've defined my Map. Does anyone have any suggestions on how to get the two talking without using some kind of delegate?

Thanks in advance,
Adam
0 Kudos
MarkCederholm
Occasional Contributor III
Sorry I'm not understanding the organization of your app.  If MainApp creates both Home and PDFExporter objects, and Home contains the map object, you could simply add a property or method on Home that MainApp could call to get a handle to the map and pass on to the exporter.
0 Kudos
adamestrada
New Contributor
I am building a Silverlight Business Application that has several data-driven pages associated with it. I swapped out the default "About" page routing with a click event to call up ExportPDF as a childwindow. This opens ExportPDF.xaml just fine and even allows me to create a PDF but when it gets in to the guts of the PDF creation I get the Map not set error. This leads me to believe that MainPage can't find where I defined Map. All files reference the same namespaces and everything but still nothing. I hope this helps to clarify.

Adam
0 Kudos
weiliang
New Contributor II
I want to generate a pdf report with many components (e.g., text not top of map), map is only like a photo inside. So my question is, how can I design the layout in this pdf?

Thanks for your kindly help,

Wei
0 Kudos
MarkCederholm
Occasional Contributor III
In theory it can be done, but you always have to watch out for controls that have restricted content, keeping them from rendering to a WriteableBitmap (WBM).  Basically, create a WBM the size of your layout, create a WBM for each child control, and paste it all together.  Pretty much all the code you need is in the PDFExporter class itself:  check out the AddElements method.
0 Kudos
SteffenCornell
New Contributor II
Sorry to interrupt this thread with off-topic content but I'm actually trying to contact MCederholm concerning another matter and can't figure out how to do it throught the ESRI forums.  MCederholm if you'd be so kind as to tell me how I can get in touch with you, I'd be ever so grateful.  Thanks.
0 Kudos
MarkCederholm
Occasional Contributor III
The old ArcScripts site had a mechanism for contacting the author.  I don't like the new systems but I hear they're changing.  Anyway, my website has an email link:   www.pierssen.com
0 Kudos
FengLin
New Contributor
I use Silverlight PDF library �??silverPDF�?�, add extension method �??ToStream�?� for  WriteableBitmap and �??ExportPDF�?� for PrintDocument:


public static Stream ToStream(this WriteableBitmap writeableBitmap)
{
            //use EditableImage and  PngEncoder to generate image stream
}

public static void ExportPDF(this PrintDocument printDocument, UIElement element, Stream outPutStream)
{
            WriteableBitmap bitMap = new WriteableBitmap(element, null);

            Stream stream = bitMap.ToStream();

            PdfDocument document = new PdfDocument();
            PdfPage page = document.AddPage();

            XImage img = XImage.FromStream(stream);

            //�?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?��?�.

            XGraphics gfx = XGraphics.FromPdfPage(page);

           gfx.DrawImage(img, 0, 0, img.PointWidth, img.PointHeight);
           document.Save(outPutStream);
}


Usage:

private void btnExportToPDF_Click(object sender, RoutedEventArgs e)
{
          SaveFileDialog saveFileDialog = new SaveFileDialog();
          saveFileDialog.Filter = "PDF file format|*.pdf";
          saveFileDialog.DefaultExt = ".pdf";

          if (saveFileDialog.ShowDialog() == true)
          {
              Stream pdfstream = saveFileDialog.OpenFile();

              try
              {
                     PrintDocument printDocument = new PrintDocument();
                     printDocument.ExportPDF(theUIElementYouWantToPrint, pdfstream);

                     MessageBox.Show(string.Format("PDF is exported and saved in: {0}", saveFileDialog.SafeFileName));
              }
              catch (Exception ex)
              {
                    MessageBox.Show(string.Format("Error creating PDF document: {0}", ex.Message));
              }
          }
      }

It works.

The problem is that the image loses quality, especially for the text in theUIElementYouWantToPrint.

I find that lost the quality in using EditableImage and PngEncoder (or JpgEncoder) to generate image stream:

1. UIElement -> WriteableBitmap (quality not lost)
2. WriteableBitmap -> PNG or JPG Stream (quality lost)
3. PNG or JPG Stream -> PDF (quality not lost)

Any ideal?
0 Kudos
adamestrada
New Contributor
We ended up going pretty lean on the exporter. The method we implemented is as follows

private void Map2PDF_Click(object sender, RoutedEventArgs e)
        {
            /* // Don't need the ExportPDF dialog anymore
            CMC_Viewer.Views.ExportPDF map2pdf = new CMC_Viewer.Views.ExportPDF();
            map2pdf.SetMap(this.Map);
            map2pdf.Show();
             * */

            // Display save-as dialog
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "PDF|*.pdf";
            if (!dialog.ShowDialog().Value)
            {
                return;
            }

            // Create a new dynamic layer from the same map service as current tiled layer
            ArcGISTiledMapServiceLayer tiled = this.Map.Layers[0] as ArcGISTiledMapServiceLayer;
            ArcGISDynamicMapServiceLayer dynamic = new ArcGISDynamicMapServiceLayer()
            {
                Url = tiled.Url,
                ImageFormat = ArcGISDynamicMapServiceLayer.RestImageFormat.JPG
            };


            // When the dynamic layer has initialized create the in-memory PDF document
            dynamic.Initialized += (a, b) =>
            {
                dynamic.GetUrl(this.Map.Extent,
                               (int)this.Map.ActualWidth,
                               (int)this.Map.ActualHeight,
                               delegate(string url, int width, int height, Envelope extent)
                               {
                                   // Download a new image of identical to what is currently displayed in the map
                                   WebClient webClient = new WebClient();
                                   webClient.OpenReadCompleted += (c, f) =>
                                   {
                                       // Use the dispatcher to force execution in the UI thread
                                       Dispatcher.BeginInvoke(delegate()
                                       {
                                           // Create the PDF document, set document information properties
                                           PdfDocument document = new PdfDocument();
                                           document.Info.Title = "Map";
                                           // Create a new page with the same dimensions as the browser map
                                           PdfPage page = new PdfPage(document)
                                           {
                                               Height = new XUnit(this.Map.ActualHeight, XGraphicsUnit.Presentation),
                                               Width = new XUnit(this.Map.ActualWidth, XGraphicsUnit.Presentation)
                                           };
                                           document.Pages.Add(page);

                                           // Create a graphics object for writing to the page
                                           XGraphics graphics = XGraphics.FromPdfPage(page);
                                           // Add the map image to the page
                                           XImage image = XImage.FromStream(f.Result);
                                           graphics.DrawImage(image, 0d, 0d);
                                           // Save the PDF document to the user specified filename
                                           document.Save(dialog.OpenFile());
                                           // Notify the user that we're done
                                           MessageBox.Show("Map saved to '" + dialog.SafeFileName + "'");
                                       });
                                   };
                                   webClient.OpenReadAsync(new Uri(url));
                               });
            };
            dynamic.Initialize();
        }


We found that the size of the map to be exported has to be set better than in other examples. Anyway, the code above produces a high quality map.
0 Kudos