Rendering Documents and Reports via API

The capability to display and print documents programmatically in Select has existed since Select version 4.1. The information below is the curation of multiple topics in the DevForum. Hopefully this presentation is easier to digest. Documents and Reports represent different things under the covers but are all treated in a similar manner. We will refer to all of them as “documents”.

Overhead

We will demonstrate code necessary to render documents. We are doing this in is a stand-alone C# console application. If you are unsure how to create one of these applications, use the Visual Studio template for “SoftPro Select Console Application”. The typical C# “using” directives are below. I will not repeat them in the code examples to reduce the size of this post. Also, not all are used for all code fragments (exercise for reader).

using SoftPro.Documents.Client.Rendering;
using SoftPro.Documents.Client;
using SoftPro.OrderTracking.Client.Orders;
using SoftPro.Select.Client;
using System.IO;
using System.Linq;

Simple Document – No User Prompts

Here is the simplest example of document rendering. The barcode document does not require any user prompts to be answered. Just get the document by name, pass it to the renderer then save the result as a PDF file.

SelectServer ss = //Get instance of SelectServer

// Fetch the desired order from the order store.
IOrderStore os = ss.GetService<IOrderStore>();
IOrderInfo search = os.Orders
    .Where(t => t.Number == "XAT16000014").FirstOrDefault();

// Exit if didn't find order.
if (search != null)
{
	// Open order in read-only mode.
	IOrder iorder = os.OpenOrder(search, true);

	// Get a simple document that needs no user input.
	IDocumentManager docMgr = ss.GetService<IDocumentManager>();
	IDocumentInfo docInfo = docMgr.Documents
		.Where(t => t.Title == "Barcode Cover Sheet").First();

	// Create in-memory document.
	//    No user input is necessary so passing null to prompts.
	IRendererFactory renderFactory = ss.GetService<IRendererFactory>();
	IRenderer renderer = renderFactory.Create();
	IRendering rendering = renderer.Render(docInfo, iorder, null);

	// Render the in-memory document as a PDF and write it to disk.
	using (Stream file = File.Create("Barcode.pdf"))
	{
		Stream docStream 
			= rendering.Export(DocumentFormat.PortableDocumentFormat);
		docStream.Seek(0, SeekOrigin.Begin);
		docStream.CopyTo(file);
	}
}

Documents with Scalar User Prompts (Boolean and String)

Obviously, most documents will require some type of user input to select the contact or perhaps which CDF to print. In the case where you supply user prompts, you must know ahead of time what information the document will need. The rendering portion is almost identical to what is above – the only difference being that we must provide a class instance to answer prompts in the lines highlighted below instead of null above.

I will just demonstrate prompts for Boolean and String here. The actual document might require other C# types and is handled the same way. Also, the RenderPrompts class will be called multiple times as it scans the target document.

IDocumentManager docMgr = ss.GetService<IDocumentManager>();
IDocumentInfo docInfo = docMgr.Documents
	.Where(t => t.Title == "ALTA Loan Policy (6-17-06)").First();

// Create instance of class that has all the answered prompts.
RenderPrompts prompts = new RenderPrompts();

// Create in-memory document and pass prompts.
IRendererFactory renderFactory = ss.GetService<IRendererFactory>();
IRenderer renderer = renderFactory.Create();
IRendering rendering = renderer.Render(docInfo, iorder, prompts);

// Render the in-memory document as a PDF and write it to disk.
using (Stream file = File.Create("ALTA Loan Policy.pdf"))
{
	Stream docStream 
		= rendering.Export(DocumentFormat.PortableDocumentFormat);
	docStream.Seek(0, SeekOrigin.Begin);
	docStream.CopyTo(file);
}

The RenderPrompts class is below. The value.Requests collection will provide the text prompt the user would have seen and you must provide a response in the Value. Since the Value property is an Object, you will not see any error when a value is assigned to it – but it may fail later during the rendering process if the type you provide is wrong. If you provide an answer to all the required prompts, your document will be rendered.

class RenderPrompts : IPrompt<PromptEventArgs>
{
	public void Request(PromptEventArgs value)
	{
		// Handle requests one at a time.  
		//    Examine the prompt then cast to appropriate response type.
		//    Here we handle requests a string and a boolean.
		//    Other request types are possible for other documents.
		foreach (var prompt in value.Requests)
		{
			if (prompt.Text == "Witness clause ALTA")
			{
				// Handle request for a string value.
				IValueRequest response = (IValueRequest)prompt;
				response.Handled = true;
				response.Value = "This is the witness clause information...";
			}
			else if (prompt.Text.StartsWith(
				"Select if optional Item 6 (endorsement incorporation)"))
			{
				// Handle request for a boolean value.
				IValueRequest response = (IValueRequest)prompt;
				response.Handled = true;
				response.Value = true;
			}
		}
	}
}

Documents with Complex User Prompts (Date Range)

Some user prompts require a pair of values. The code below shows how to enter a date range. As for scalar user prompts, the actual type can be anything.

foreach (var prompt in value.Requests)
{
	if (prompt.ID == "ORDERDATERANGE")
	{
		// Handle request for a string value.
		IRangeRequest<DateTime> response = (IRangeRequest<DateTime>)prompt;
		response.Handled = true;
		response.Value = 
			Range<DateTime>.Bound(DateTime.Now.AddDays(-30), DateTime.Now);
	}
}