using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using FastReport.Utils;
namespace FastReport.Export.Image
{
///
/// Specifies the image export format.
///
public enum ImageExportFormat
{
///
/// Specifies the .bmp format.
///
Bmp,
///
/// Specifies the .png format.
///
Png,
///
/// Specifies the .jpg format.
///
Jpeg,
///
/// Specifies the .gif format.
///
Gif,
///
/// Specifies the .tif format.
///
Tiff,
///
/// Specifies the .emf format.
///
Metafile
}
///
/// Represents the image export filter.
///
public partial class ImageExport : ExportBase
{
private ImageExportFormat imageFormat;
private bool separateFiles;
private int resolutionX;
private int resolutionY;
private int jpegQuality;
private bool multiFrameTiff;
private bool monochromeTiff;
private EncoderValue monochromeTiffCompression;
private System.Drawing.Image masterTiffImage;
private System.Drawing.Image bigImage;
private Graphics bigGraphics;
private float curOriginY;
private bool firstPage;
private int paddingNonSeparatePages;
private int pageNumber;
private System.Drawing.Image image;
private Graphics g;
private int height;
private int width;
private int widthK;
private string fileSuffix;
private float zoomX;
private float zoomY;
private System.Drawing.Drawing2D.GraphicsState state;
private string imageExtensionFormat;
private string documentTitle;
private bool saveStreams;
const float DIVIDER = 0.75f;
const float PAGE_DIVIDER = 2.8346400000000003f; // mm to point
#region Properties
///
/// Gets or sets the image format.
///
public ImageExportFormat ImageFormat
{
get { return imageFormat; }
set { imageFormat = value; }
}
///
/// Gets or sets a value that determines whether to generate separate image file
/// for each exported page.
///
///
/// If this property is set to false, the export filter will produce one big image
/// containing all exported pages. Be careful using this property with a big report
/// because it may produce out of memory error.
/// And also when using Memory Stream and the value is true, an exception will be thrown.
///
public bool SeparateFiles
{
get { return separateFiles; }
set { separateFiles = value; }
}
///
/// Gets or sets image resolution, in dpi.
///
///
/// By default this property is set to 96 dpi. Use bigger values (300-600 dpi)
/// if you going to print the exported images.
///
public int Resolution
{
get { return resolutionX; }
set
{
resolutionX = value;
resolutionY = value;
}
}
///
/// Gets or sets horizontal image resolution, in dpi.
///
///
/// Separate horizontal and vertical resolution is used when exporting to TIFF. In other
/// cases, use the property instead.
///
public int ResolutionX
{
get { return resolutionX; }
set { resolutionX = value; }
}
///
/// Gets or sets vertical image resolution, in dpi.
///
///
/// Separate horizontal and vertical resolution is used when exporting to TIFF. In other
/// cases, use the property instead.
///
public int ResolutionY
{
get { return resolutionY; }
set { resolutionY = value; }
}
///
/// Gets or sets the jpg image quality.
///
///
/// This property is used if is set to Jpeg. By default
/// it is set to 100. Use lesser value to decrease the jpg file size.
///
public int JpegQuality
{
get { return jpegQuality; }
set { jpegQuality = value; }
}
///
/// Gets or sets the value determines whether to produce multi-frame tiff file.
///
public bool MultiFrameTiff
{
get { return multiFrameTiff; }
set { multiFrameTiff = value; }
}
///
/// Gets or sets a value that determines whether the Tiff export must produce monochrome image.
///
///
/// Monochrome tiff image is compressed using the compression method specified in the
/// property.
///
public bool MonochromeTiff
{
get { return monochromeTiff; }
set { monochromeTiff = value; }
}
///
/// Gets or sets the compression method for a monochrome TIFF image.
///
///
/// This property is used only when exporting to TIFF image, and the property
/// is set to true.
/// The valid values for this property are: EncoderValue.CompressionNone,
/// EncoderValue.CompressionLZW, EncoderValue.CompressionRle,
/// EncoderValue.CompressionCCITT3, EncoderValue.CompressionCCITT4.
/// The default compression method is CCITT4.
///
public EncoderValue MonochromeTiffCompression
{
get { return monochromeTiffCompression; }
set { monochromeTiffCompression = value; }
}
///
/// Sets padding in non separate pages
///
public int PaddingNonSeparatePages
{
get { return paddingNonSeparatePages; }
set { paddingNonSeparatePages = value; }
}
private bool IsMultiFrameTiff
{
get { return ImageFormat == ImageExportFormat.Tiff && MultiFrameTiff; }
}
///
/// Enable or disable saving streams in GeneratedStreams collection.
///
public bool SaveStreams
{
get { return saveStreams; }
set { saveStreams = value; }
}
#endregion
#region Private Methods
private System.Drawing.Image CreateImage(int width, int height, string suffix)
{
widthK = width;
if (ImageFormat == ImageExportFormat.Metafile)
return CreateMetafile(suffix);
return new Bitmap(width, height);
}
private void GeneratedUpdate(string filename, Stream stream)
{
int i = GeneratedFiles.IndexOf(filename);
if (i == -1)
{
GeneratedFiles.Add(filename);
GeneratedStreams.Add(stream);
}
else
{
GeneratedStreams[i] = stream;
}
}
private System.Drawing.Image CreateMetafile(string suffix)
{
string extension = Path.GetExtension(FileName);
string targetFileName = Path.ChangeExtension(FileName, suffix + extension);
System.Drawing.Image image;
using (Bitmap bmp = new Bitmap(1, 1))
using (Graphics g = Graphics.FromImage(bmp))
{
IntPtr hdc = g.GetHdc();
if (suffix == "")
image = new Metafile(Stream, hdc);
else
{
image = new Metafile(targetFileName, hdc);
if (!GeneratedFiles.Contains(targetFileName))
GeneratedFiles.Add(targetFileName);
}
g.ReleaseHdc(hdc);
}
return image;
}
private Bitmap ConvertToBitonal(Bitmap original)
{
Bitmap source = null;
// If original bitmap is not already in 32 BPP, ARGB format, then convert
if (original.PixelFormat != PixelFormat.Format32bppArgb)
{
source = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
source.SetResolution(original.HorizontalResolution, original.VerticalResolution);
using (Graphics g = Graphics.FromImage(source))
{
g.DrawImageUnscaled(original, 0, 0);
}
}
else
{
source = original;
}
// Lock source bitmap in memory
BitmapData sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
// Copy image data to binary array
int imageSize = sourceData.Stride * sourceData.Height;
byte[] sourceBuffer = new byte[imageSize];
Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize);
// Unlock source bitmap
source.UnlockBits(sourceData);
// Create destination bitmap
Bitmap destination = new Bitmap(source.Width, source.Height, PixelFormat.Format1bppIndexed);
// Lock destination bitmap in memory
BitmapData destinationData = destination.LockBits(new Rectangle(0, 0, destination.Width, destination.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
// Create destination buffer
imageSize = destinationData.Stride * destinationData.Height;
byte[] destinationBuffer = new byte[imageSize];
int sourceIndex = 0;
int destinationIndex = 0;
int pixelTotal = 0;
byte destinationValue = 0;
int pixelValue = 128;
int height = source.Height;
int width = source.Width;
int threshold = 500;
// Iterate lines
for (int y = 0; y < height; y++)
{
sourceIndex = y * sourceData.Stride;
destinationIndex = y * destinationData.Stride;
destinationValue = 0;
pixelValue = 128;
// Iterate pixels
for (int x = 0; x < width; x++)
{
// Compute pixel brightness (i.e. total of Red, Green, and Blue values)
pixelTotal = sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2] + sourceBuffer[sourceIndex + 3];
if (pixelTotal > threshold)
{
destinationValue += (byte)pixelValue;
}
if (pixelValue == 1)
{
destinationBuffer[destinationIndex] = destinationValue;
destinationIndex++;
destinationValue = 0;
pixelValue = 128;
}
else
{
pixelValue >>= 1;
}
sourceIndex += 4;
}
if (pixelValue != 128)
{
destinationBuffer[destinationIndex] = destinationValue;
}
}
// Copy binary image data to destination bitmap
Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, imageSize);
// Unlock destination bitmap
destination.UnlockBits(destinationData);
// Dispose of source if not originally supplied bitmap
if (source != original)
{
source.Dispose();
}
// Return
destination.SetResolution(ResolutionX, ResolutionY);
return destination;
}
private void SaveImage(System.Drawing.Image image, string suffix)
{
// store the resolution in output file.
// Call this method after actual draw because it may affect drawing the text
if (image is Bitmap)
(image as Bitmap).SetResolution(ResolutionX, ResolutionY);
if (IsMultiFrameTiff)
{
// select the image encoder
ImageCodecInfo info = ExportUtils.GetCodec("image/tiff");
EncoderParameters ep = new EncoderParameters(2);
ep.Param[0] = new EncoderParameter(Encoder.Compression, MonochromeTiff ?
(long)MonochromeTiffCompression : (long)EncoderValue.CompressionLZW);
if (image == masterTiffImage)
{
// save the master bitmap
if (MonochromeTiff)
masterTiffImage = ConvertToBitonal(image as Bitmap);
ep.Param[1] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
masterTiffImage.Save(Stream, info, ep);
}
else
{
// save the frame
if (MonochromeTiff)
{
System.Drawing.Image oldImage = image;
image = ConvertToBitonal(image as Bitmap);
oldImage.Dispose();
}
ep.Param[1] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.FrameDimensionPage);
masterTiffImage.SaveAdd(image, ep);
}
}
else if (ImageFormat != ImageExportFormat.Metafile)
{
Stream stream;
string targetFileName;
if (saveStreams)
{
targetFileName = Path.ChangeExtension(documentTitle + suffix, imageExtensionFormat);
stream = new MemoryStream();
}
else
{
string extension = Path.GetExtension(FileName);
targetFileName = Path.ChangeExtension(FileName, suffix + extension);
// empty suffix means that we should use the Stream that was created in the ExportBase
stream = suffix == "" ? Stream : new FileStream(targetFileName, FileMode.Create);
if (suffix != "")
{
GeneratedFiles.Add(targetFileName);
}
}
if (ImageFormat == ImageExportFormat.Jpeg)
{
ExportUtils.SaveJpeg(image, stream, JpegQuality);
}
else if (ImageFormat == ImageExportFormat.Tiff && MonochromeTiff)
{
// handle monochrome tiff separately
ImageCodecInfo info = ExportUtils.GetCodec("image/tiff");
EncoderParameters ep = new EncoderParameters();
ep.Param[0] = new EncoderParameter(Encoder.Compression, (long)MonochromeTiffCompression);
using (Bitmap bwImage = ConvertToBitonal(image as Bitmap))
{
bwImage.Save(stream, info, ep);
}
}
else
{
ImageFormat format = System.Drawing.Imaging.ImageFormat.Bmp;
switch (ImageFormat)
{
case ImageExportFormat.Gif:
format = System.Drawing.Imaging.ImageFormat.Gif;
break;
case ImageExportFormat.Png:
format = System.Drawing.Imaging.ImageFormat.Png;
break;
case ImageExportFormat.Tiff:
format = System.Drawing.Imaging.ImageFormat.Tiff;
break;
}
image.Save(stream, format);
}
if (saveStreams)
GeneratedUpdate(targetFileName, stream);
else if (suffix != "")
stream.Dispose();
}
if (image != masterTiffImage)
image.Dispose();
}
#endregion
#region Protected Methods
///
protected override string GetFileFilter()
{
string filter = ImageFormat.ToString();
return Res.Get("FileFilters," + filter + "File");
}
///
protected override void Start()
{
base.Start();
//init
SeparateFiles = Stream is MemoryStream ? false : SeparateFiles;
GeneratedStreams = new List();
pageNumber = 0;
height = 0;
width = 0;
image = null;
g = null;
zoomX = 1;
zoomY = 1;
state = null;
curOriginY = 0;
firstPage = true;
if (saveStreams)
{
imageExtensionFormat = ImageFormat.ToString();
separateFiles = true;
documentTitle = (!String.IsNullOrEmpty(Report.ReportInfo.Name) ?
Report.ReportInfo.Name : "");
}
if (!SeparateFiles && !IsMultiFrameTiff)
{
// create one big image. To do this, calculate max width and sum of pages height
float w = 0;
float h = 0;
foreach (int pageNo in Pages)
{
SizeF size = Report.PreparedPages.GetPageSize(pageNo);
if (size.Width > w)
w = size.Width;
h += size.Height + paddingNonSeparatePages * 2;
}
w += paddingNonSeparatePages * 2;
bigImage = CreateImage((int)(w * ResolutionX / 96f), (int)(h * ResolutionY / 96f), "");
bigGraphics = Graphics.FromImage(bigImage);
bigGraphics.Clear(Color.Transparent);
}
pageNumber = 0;
}
///
protected override void ExportPageBegin(ReportPage page)
{
base.ExportPageBegin(page);
zoomX = ResolutionX / 96f;
zoomY = ResolutionY / 96f;
width = (int)(ExportUtils.GetPageWidth(page) * Units.Millimeters * zoomX);
height = (int)(ExportUtils.GetPageHeight(page) * Units.Millimeters * zoomY);
int suffixDigits = Pages[Pages.Length - 1].ToString().Length;
fileSuffix = firstPage ? "" : (pageNumber + 1).ToString("".PadLeft(suffixDigits, '0'));
if (SeparateFiles || IsMultiFrameTiff)
{
image = CreateImage(width, height, fileSuffix);
if (IsMultiFrameTiff && masterTiffImage == null)
masterTiffImage = image;
}
else
image = bigImage;
if (bigGraphics != null)
g = bigGraphics;
else
g = Graphics.FromImage(image);
state = g.Save();
g.FillRegion(Brushes.Transparent, new Region(new RectangleF(0, curOriginY, width, height)));
if (bigImage != null && curOriginY + height * 2 > bigImage.Height)
page.Fill.Draw(new FRPaintEventArgs(g, 1, 1, Report.GraphicCache), new RectangleF(0, curOriginY, widthK, bigImage.Height - curOriginY));
else
page.Fill.Draw(new FRPaintEventArgs(g, 1, 1, Report.GraphicCache), new RectangleF(0, curOriginY, widthK, height + paddingNonSeparatePages * 2));
if (image == bigImage)
{
if (ImageFormat != ImageExportFormat.Metafile)
g.TranslateTransform(image.Width / 2 - width / 2 + page.LeftMargin * Units.Millimeters * zoomX,
curOriginY + paddingNonSeparatePages + page.TopMargin * Units.Millimeters * zoomY);
else
g.TranslateTransform(widthK / 2 - width / 2 + page.LeftMargin * Units.Millimeters * zoomX,
curOriginY + paddingNonSeparatePages + page.TopMargin * Units.Millimeters * zoomY);
}
else
g.TranslateTransform(page.LeftMargin * Units.Millimeters * zoomX, page.TopMargin * Units.Millimeters * zoomY);
g.ScaleTransform(1, zoomY / zoomX);
// export bottom watermark
if (page.Watermark.Enabled && !page.Watermark.ShowImageOnTop)
AddImageWatermark(page);
if (page.Watermark.Enabled && !page.Watermark.ShowTextOnTop)
AddTextWatermark(page);
// page borders
if (page.Border.Lines != BorderLines.None)
{
using (TextObject pageBorder = new TextObject())
{
pageBorder.Border = page.Border;
pageBorder.Left = 0;
pageBorder.Top = 0;
pageBorder.Width = (ExportUtils.GetPageWidth(page) - page.LeftMargin - page.RightMargin) * PAGE_DIVIDER / DIVIDER;
pageBorder.Height = (ExportUtils.GetPageHeight(page) - page.TopMargin - page.BottomMargin) * PAGE_DIVIDER / DIVIDER;
ExportObj(pageBorder);
}
}
}
///
protected override void ExportBand(BandBase band)
{
base.ExportBand(band);
ExportObj(band);
foreach (Base c in band.ForEachAllConvectedObjects(this))
{
if (!(c is Table.TableColumn || c is Table.TableCell || c is Table.TableRow))
ExportObj(c);
}
}
private void ExportObj(Base obj)
{
if (obj is ReportComponentBase && (obj as ReportComponentBase).Exportable)
(obj as ReportComponentBase).Draw(new FRPaintEventArgs(g, zoomX, zoomX, Report.GraphicCache));
}
///
protected override void ExportPageEnd(ReportPage page)
{
// export top watermark
if (page.Watermark.Enabled && page.Watermark.ShowImageOnTop)
AddImageWatermark(page);
if (page.Watermark.Enabled && page.Watermark.ShowTextOnTop)
AddTextWatermark(page);
g.Restore(state);
if (g != bigGraphics)
g.Dispose();
if (SeparateFiles || IsMultiFrameTiff)
SaveImage(image, fileSuffix);
else
curOriginY += height + paddingNonSeparatePages * 2;
firstPage = false;
pageNumber++;
}
private void AddImageWatermark(ReportPage page)
{
page.Watermark.DrawImage(new FRPaintEventArgs(g, zoomX, zoomX, Report.GraphicCache),
new RectangleF(-page.LeftMargin * Units.Millimeters, -page.TopMargin * Units.Millimeters, width / zoomX, height / zoomY),
page.Report, false);
}
private void AddTextWatermark(ReportPage page)
{
if (string.IsNullOrEmpty(page.Watermark.Text))
return;
page.Watermark.DrawText(new FRPaintEventArgs(g, zoomX, zoomX, Report.GraphicCache),
new RectangleF(-page.LeftMargin * Units.Millimeters, -page.TopMargin * Units.Millimeters, width / zoomX, height / zoomY),
page.Report, false);
}
///
protected override void Finish()
{
if (IsMultiFrameTiff)
{
// close the file.
EncoderParameters ep = new EncoderParameters(1);
ep.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.Flush);
masterTiffImage.SaveAdd(ep);
}
else if (!SeparateFiles)
{
bigGraphics.Dispose();
bigGraphics = null;
SaveImage(bigImage, "");
}
if (masterTiffImage != null)
{
masterTiffImage.Dispose();
masterTiffImage = null;
}
}
#endregion
#region Public Methods
///
public override void Serialize(FRWriter writer)
{
base.Serialize(writer);
writer.WriteValue("ImageFormat", ImageFormat);
writer.WriteBool("SeparateFiles", SeparateFiles);
writer.WriteInt("ResolutionX", ResolutionX);
writer.WriteInt("ResolutionY", ResolutionY);
writer.WriteInt("JpegQuality", JpegQuality);
writer.WriteBool("MultiFrameTiff", MultiFrameTiff);
writer.WriteBool("MonochromeTiff", MonochromeTiff);
}
#endregion
///
/// Initializes a new instance of the class.
///
public ImageExport()
{
paddingNonSeparatePages = 0;
fileSuffix = String.Empty;
HasMultipleFiles = true;
imageFormat = ImageExportFormat.Jpeg;
separateFiles = true;
Resolution = 96;
jpegQuality = 100;
monochromeTiffCompression = EncoderValue.CompressionCCITT4;
saveStreams = false;
}
}
}