This is an example on how to alter the PDF created from a SSRS report with other PDF files (such as the ones attached as document attachments).
To be able to do this, we will need to download and install the PDFSharp library, which is an open source library to create or modify PDF files. First, download from
www.pdfSharp.com, the extract the PdfSharp.dll and copy it to the bin folder of the model folder which you want to do the modification on. Second, from the Visual Studio, right click on the project, and add a reference into the .dll file.
As for the modification itself, we will need to override this delegate: SRSPrintDestinationSettingsDelegates.toSendFile() and make sure to set the result on the EventHandlerResult to false. This way, after calling the delegate, it will not continue with the rest of the codes in SrsReportRunPrinter.toFile(), which is going to save the report PDF file into the Azure storage or send it to the user.
The sample codes would look like this:
using PdfSharp;
using Microsoft.Dynamics.ApplicationPlatform.SSRSReportRuntime.Instrumentation;
class SRSPrintDestinationSettingsDelegates_EventHandler
{
#SRSFramework
private static void saveFile(System.Byte[] reportBytes, SrsReportRunPrinter printer, SrsReportDataContract dataContract)
{
SRSPrintDestinationSettings printSettings = dataContract.parmPrintSettings();
//SRSReportFileFormat fileFormat = printSettings.fileFormat();
Filename filename = "";
if(printSettings.fileName())
{
filename = printSettings.fileName();
}
else
{
filename = dataContract.parmReportCaption() ? dataContract.parmReportCaption() + ".pdf" : dataContract.parmReportName() + ".pdf";
}
if (reportBytes)
{
System.IO.MemoryStream stream = new System.IO.MemoryStream(reportBytes);
// Send file to browser for on premise scenario.
if(SrsReportRunUtil::isOnPremEnvironment())
{
Dynamics.AX.Application.File::SendFileToUser(stream, filename);
SSRSReportRuntimeEventSource::EventWriteRenderReportToFileTaskStop(
"Printing report to file ended.",
dataContract.parmReportExecutionInfo().parmReportRunId());
return;
}
// Upload file to temp storage and direct the browser to the file URL
SrsFileUploadNameContract fileNameContract = new SrsFileUploadNameContract();
fileNameContract.FileName(filename);
str categoryName = SrsReportRunUtil::convertAndTrimGuidValue(dataContract.parmReportExecutionInfo().parmReportRunId());
fileNameContract.CategoryName(categoryName);
SRSFileUploadTempStorageStrategy fileUploader = new SRSFileUploadTempStorageStrategy();
fileUploader.uploadFile(stream, FormJsonSerializer::serializeClass(fileNameContract));
// Set global cache that indicates there is file uploaded for current report execution.
// Using SGC not SGOC because we want scope to be in current user session.
// Owner - #RunIdOwner macro, Key - RunId, Value - boolean value.
SysGlobalCache globalCache = classfactory.globalCache();
if(!globalCache.isSet(#RunIdOwner, dataContract.parmReportExecutionInfo().parmReportRunId()))
{
globalCache.set(#RunIdOwner, dataContract.parmReportExecutionInfo().parmReportRunId(), true);
}
}
SSRSReportRuntimeEventSource::EventWriteRenderReportToFileTaskStop(
"Printing report to file ended.",
dataContract.parmReportExecutionInfo().parmReportRunId());
}
[SubscribesTo(classStr(SRSPrintDestinationSettingsDelegates), delegateStr(SRSPrintDestinationSettingsDelegates, toSendFile))]
public static void SRSPrintDestinationSettingsDelegates_toSendFile(System.Byte[] reportBytes, SrsReportRunPrinter printer, SrsReportDataContract dataContract, Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[] paramArray, EventHandlerResult result)
{
Pdf.PdfDocument outputPDFDocument = new Pdf.PdfDocument();
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(reportBytes);
System.IO.MemoryStream mergedStream = new System.IO.MemoryStream();
boolean isOutputModified;
SRSPrintDestinationSettings printSettings = dataContract.parmPrintSettings();
System.Byte[] finalReportBytes = reportBytes;
void addStream(System.IO.MemoryStream _stream, Pdf.PdfDocument _outputPDFDocument)
{
Pdf.PdfDocument inputPDFDocument = new Pdf.PdfDocument();
int pageCount;
Pdf.PdfPages pdfPages;
inputPDFDocument = PdfSharp.Pdf.IO.PdfReader::Open(_stream, PdfSharp.Pdf.IO.PdfDocumentOpenMode::Import);
_outputPDFDocument.set_Version(inputPDFDocument.get_Version());
pageCount = inputPDFDocument.get_PageCount();
pdfPages = inputPDFDocument.get_Pages();
if (pageCount > 0)
{
for (int idx = 0; idx < pageCount; idx++)
{
_outputPDFDocument.AddPage(pdfPages.get_Item(idx));
}
}
}
if (printSettings.fileFormat() == SRSReportFileFormat::PDF && dataContract.parmRdpName() == classStr(SalesInvoiceDP))
{
SalesInvoiceContract salesInvoiceContract = dataContract.parmRdpContract();
DocuRef docuRef;
int attachmentCounter;
try
{
new InteropPermission(InteropKind::ClrInterop).assert();
while select docuRef //add some criteria here
{
if (docuRef.fileType() == 'pdf')
{
if (!attachmentCounter)
{
addStream(memoryStream, outputPDFDocument);
}
System.IO.Stream docuStream = DocumentManagement::getAttachmentStream(docuRef);
memoryStream = new System.IO.MemoryStream();
docuStream.CopyTo(memoryStream);
addStream(memoryStream, outputPDFDocument);
attachmentCounter++;
}
}
if (attachmentCounter)
{
outputPDFDocument.Save(mergedStream, false);
finalReportBytes = mergedStream.ToArray();
result.result(false); //this is to force the SrsReportRunPrinter.toFile NOT to continue after calling this delegate
isOutputModified = true;
}
CodeAccessPermission::revertAssert();
}
catch(Exception::CLRError)
{
str errorMessage = AifUtil::getClrErrorMessage();
CodeAccessPermission::revertAssert();
throw error(errorMessage);
}
}
if (isOutputModified && finalReportBytes)
{
SRSPrintDestinationSettingsDelegates_EventHandler::saveFile(finalReportBytes, printer, dataContract);
}
}
}