I had blogged in the past on how to track corrupt images while rendering a SSRS report containing images. In this post, I shall talk about generating reports for reports for images which have a corrupted JPEG stream.
If you have an image which has a corrupt JPEG stream, then the report will render without any issues and an export of the report to a PDF document will report no errors. However, when you open the PDF document, you will be greeted with the error seen in the screenshot below reporting “An internal error occurred”:
Now this can be a bit disconcerting when you are automating your report generation and are not aware of any exceptions reported. Interestingly when this report is rendered in MHTML format, the file can be opened without issues but you still see the corrupt image stream. The crux of the matter lies in how PDF documents are rendered and when a corrupt JPEG stream is encountered. This issue manifests itself while creating a PDF document with such a corrupt image stream outside of SQL Server Reporting Services.
Now that we know that there are image coding standards at play here and nothing much can be done, what are the options.
For the curious mind, the image when viewed in a picture viewing application shows the corrupt stream as seen below:
The first option is to use an image processing executable and detect all images that you will be processing in your report before you render it into a PDF format.
Image Magick DLLs and binaries are one such option. You could either choose to use the binaries that are available on the site or you could create your own picture verification package using their DLLs available on the site. Their code is available on CodePlex as well. The binaries available on their site has an executable called “Identify”. This can be used to identify corrupt image streams. The output of the tool when used to evaluate an image will be as follows:
identify.exe: Corrupt JPEG data: bad Huffman code `C:\Tempdb\1.jpg’ @ warning/jp
identify.exe: Corrupt JPEG data: 20 extraneous bytes before marker 0xd9 `C:\Temp
db\1.jpg’ @ warning/jpeg.c/JPEGWarningHandler/346.
The other way to workaround this issue would be to use Custom Code within your report to re-save the image. This doesn’t remove the corrupt image stream but the image snippet that you see in the screenshot above now becomes part of your image. This will help you generate your PDF documents correctly and open them for viewing later. However, if you want to correct the corrupt JPEG stream, then you will need to store the correct image in your database.
Steps for using custom code to prevent PDF opening failure
1. Paste the VBCode below in the Code section of the Report.
' From the data base the data is returned in the form of byte array. So we’re writing a function that accepts the byte array as parameter. ' Also it is going to return the new image as byte array. Public Shared Function ConvertImage(ByVal imgData As Byte()) As Byte() 'Stream to hold the source image (jpeg). Dim msSource As New System.IO.MemoryStream() 'Stream to hold the converted image (png). Dim msDest As New System.IO.MemoryStream() ' Reading the image from byte array to the source stream. msSource.Write(imgData, 0, imgData.Length) msSource.Flush() 'Image object that will store the image extracted from the source stream. Dim imgSource As System.Drawing.Image = System.Drawing.Image.FromStream(msSource) 'Saving the New, Converted Image in to the destination stream. imgSource.Save(msDest, System.Drawing.Imaging.ImageFormat.Png) 'Converting the New stream into Byte array, to be used inside the reporting service image control. Dim imgDest(msDest.Length) As Byte imgDest = msDest.GetBuffer() Return imgDest End Function
2. Now click on the References tab, Under Assembly Name, click on the ellipses button “…”.
3. Locate and select System.Drawing.dll, Click on Add and then Click Ok.
4. Now go to the report, Image control Properties and click on the Value. which would look something similar to this =First(Fields!Photo.Value, “DataSetName”)
5. Replace the above value with the expression, =Code.ConvertImage(First(Fields!Photo.Value,”DataSetName”)).
6. Export the report to PDF and you will now notice that the previous “internal error” is not reported.
Since you are adding custom code to your report to re-save your binary image stream, you will incur an additional processing overhead while generating the reports. If your PDF contains a large number of images, then you will have to factor in the additional resource and time consumed to perform this sanitization process!
IMHO using the first option of checking the correctness of the JPEG stream using a separate executable would be much more feasible in case you are processing 1000+ images in a single report.
A special shout-out to my colleagues Selva [Blog] and Arvind [Blog] for helping me out on this issue!
Disclaimer: The Sample Code is provided for the purpose of illustration only and is not intended to be used in a production environment. THIS SAMPLE CODE AND ANY RELATED INFORMATION ARE PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. Image Magick is a third-party utility which has been referenced in this post. Please refer documentation on the Image Magick website regarding licensing, warranty and usage before implementing the use of the same in your environment.