internal static Stream Render(XDocument xXslFODoc, Stream outputStream, XslFORenderOptions options = null) { //Always use at least a Default set of options! //NOTE: The default constructor will initialize a default set of Options! var optionsToUse = options ?? new XslFORenderOptions(); //Render the Binary PDF Output FonetDriver pdfDriver = XslFOFonetHelper.GetFonetDriver(optionsToUse.PdfOptions); Stopwatch timer = null; //Initialize Rendering Event Handlers if possible if (optionsToUse.RenderEventHandler != null) { timer = Stopwatch.StartNew(); var fnFonetProxyEventHandler = new FonetDriver.FonetEventHandler((sender, eventArgs) => { optionsToUse.RenderEventHandler(sender, new XslFOEventArg(eventArgs.GetMessage())); }); pdfDriver.OnInfo += fnFonetProxyEventHandler; pdfDriver.OnWarning += fnFonetProxyEventHandler; } //BBernard //Always ensure that at least a Default Error Event Handler is initialized to allow // the Pdf's to render as much as possible despite many 'strict' errors that may occur. //NOTE: Because there may be numerous strict errors, but the report may still render acceptably, // we always implement a Default Error Handler to allow reports to try their best to render // to completion (when no other explicit error handler is specified by the consumer). pdfDriver.OnError += new FonetDriver.FonetEventHandler((sender, eventArgs) => { Debug.WriteLine($"[XslFORenderer Error] {eventArgs.GetMessage()}"); }); //If any other Render Error Handlers are specified then we also attach it to Pdf Driver. if (optionsToUse?.RenderErrorHandler != null) { //BBernard //Create Lamda wrapper for the Fonet Event Handler to proxy into our Abstracted Event Handler //NOTE: This eliminates any dependencies on consuming code from the Fonet Driver pdfDriver.OnError += new FonetDriver.FonetEventHandler((sender, eventArgs) => { var renderException = new XslFORenderException(eventArgs.GetMessage(), xXslFODoc?.ToString()); optionsToUse.RenderErrorHandler(sender, new XslFOErrorEventArg(renderException)); }); } else { //BBernard //If not Error Handler is defined then implement a default handler that ensures that // an exception is thrown to provide fail-fast process as default behavior. //NOTE: This eliminates any dependencies on consuming code from the Fonet Driver pdfDriver.OnError += new FonetDriver.FonetEventHandler((sender, eventArgs) => { throw new XslFORenderException(eventArgs.GetMessage(), xXslFODoc?.ToString()); }); } //Create the Xml Reader and then use the Pdf FONet Driver to output to the specified Stream using (XmlReader xmlFOReader = xXslFODoc.CreateReader()) { //Render the Pdf Output to the defined File Stream pdfDriver.Render(xmlFOReader, outputStream); } //Reset the Stream if possible (since we return it ready to be possibly consumed/used... if (outputStream.CanSeek) { outputStream.Seek(0, SeekOrigin.Begin); } //Validate the rendered output if (!outputStream.CanRead || outputStream.Length <= 0) { throw new IOException("The rendered PDF output stream is empty; Xml FO rendering failed."); } //Log Benchmark event info if possible if (optionsToUse.RenderEventHandler != null && timer != null) { timer.Stop(); optionsToUse.RenderEventHandler(null, new XslFOEventArg($"XslFO render to PDF execution completed in [{timer.Elapsed.TotalSeconds}] seconds")); } //Return the Output Stream for Chaining return(outputStream); }
public XslFOErrorEventArg(XslFORenderException renderException) : base(renderException?.Message) { this.Exception = renderException; }