123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721 |
- 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
- {
- /// <summary>
- /// Specifies the image export format.
- /// </summary>
- public enum ImageExportFormat
- {
- /// <summary>
- /// Specifies the .bmp format.
- /// </summary>
- Bmp,
- /// <summary>
- /// Specifies the .png format.
- /// </summary>
- Png,
- /// <summary>
- /// Specifies the .jpg format.
- /// </summary>
- Jpeg,
- /// <summary>
- /// Specifies the .gif format.
- /// </summary>
- Gif,
- /// <summary>
- /// Specifies the .tif format.
- /// </summary>
- Tiff,
- /// <summary>
- /// Specifies the .emf format.
- /// </summary>
- Metafile
- }
- /// <summary>
- /// Represents the image export filter.
- /// </summary>
- 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
- /// <summary>
- /// Gets or sets the image format.
- /// </summary>
- public ImageExportFormat ImageFormat
- {
- get { return imageFormat; }
- set { imageFormat = value; }
- }
- /// <summary>
- /// Gets or sets a value that determines whether to generate separate image file
- /// for each exported page.
- /// </summary>
- /// <remarks>
- /// If this property is set to <b>false</b>, 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.
- /// </remarks>
- public bool SeparateFiles
- {
- get { return separateFiles; }
- set { separateFiles = value; }
- }
- /// <summary>
- /// Gets or sets image resolution, in dpi.
- /// </summary>
- /// <remarks>
- /// By default this property is set to 96 dpi. Use bigger values (300-600 dpi)
- /// if you going to print the exported images.
- /// </remarks>
- public int Resolution
- {
- get { return resolutionX; }
- set
- {
- resolutionX = value;
- resolutionY = value;
- }
- }
- /// <summary>
- /// Gets or sets horizontal image resolution, in dpi.
- /// </summary>
- /// <remarks>
- /// Separate horizontal and vertical resolution is used when exporting to TIFF. In other
- /// cases, use the <see cref="Resolution"/> property instead.
- /// </remarks>
- public int ResolutionX
- {
- get { return resolutionX; }
- set { resolutionX = value; }
- }
- /// <summary>
- /// Gets or sets vertical image resolution, in dpi.
- /// </summary>
- /// <remarks>
- /// Separate horizontal and vertical resolution is used when exporting to TIFF. In other
- /// cases, use the <see cref="Resolution"/> property instead.
- /// </remarks>
- public int ResolutionY
- {
- get { return resolutionY; }
- set { resolutionY = value; }
- }
- /// <summary>
- /// Gets or sets the jpg image quality.
- /// </summary>
- /// <remarks>
- /// This property is used if <see cref="ImageFormat"/> is set to <b>Jpeg</b>. By default
- /// it is set to 100. Use lesser value to decrease the jpg file size.
- /// </remarks>
- public int JpegQuality
- {
- get { return jpegQuality; }
- set { jpegQuality = value; }
- }
- /// <summary>
- /// Gets or sets the value determines whether to produce multi-frame tiff file.
- /// </summary>
- public bool MultiFrameTiff
- {
- get { return multiFrameTiff; }
- set { multiFrameTiff = value; }
- }
- /// <summary>
- /// Gets or sets a value that determines whether the Tiff export must produce monochrome image.
- /// </summary>
- /// <remarks>
- /// Monochrome tiff image is compressed using the compression method specified in the
- /// <see cref="MonochromeTiffCompression"/> property.
- /// </remarks>
- public bool MonochromeTiff
- {
- get { return monochromeTiff; }
- set { monochromeTiff = value; }
- }
- /// <summary>
- /// Gets or sets the compression method for a monochrome TIFF image.
- /// </summary>
- /// <remarks>
- /// This property is used only when exporting to TIFF image, and the <see cref="MonochromeTiff"/> property
- /// is set to <b>true</b>.
- /// <para/>The valid values for this property are: <b>EncoderValue.CompressionNone</b>,
- /// <b>EncoderValue.CompressionLZW</b>, <b>EncoderValue.CompressionRle</b>,
- /// <b>EncoderValue.CompressionCCITT3</b>, <b>EncoderValue.CompressionCCITT4</b>.
- /// The default compression method is CCITT4.
- /// </remarks>
- public EncoderValue MonochromeTiffCompression
- {
- get { return monochromeTiffCompression; }
- set { monochromeTiffCompression = value; }
- }
- /// <summary>
- /// Sets padding in non separate pages
- /// </summary>
- public int PaddingNonSeparatePages
- {
- get { return paddingNonSeparatePages; }
- set { paddingNonSeparatePages = value; }
- }
- private bool IsMultiFrameTiff
- {
- get { return ImageFormat == ImageExportFormat.Tiff && MultiFrameTiff; }
- }
- /// <summary>
- /// Enable or disable saving streams in GeneratedStreams collection.
- /// </summary>
- 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
- /// <inheritdoc/>
- protected override string GetFileFilter()
- {
- string filter = ImageFormat.ToString();
- return Res.Get("FileFilters," + filter + "File");
- }
- /// <inheritdoc/>
- protected override void Start()
- {
- base.Start();
- //init
- SeparateFiles = Stream is MemoryStream ? false : SeparateFiles;
- GeneratedStreams = new List<Stream>();
- 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;
- }
- /// <inheritdoc/>
- 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);
- }
- }
- }
- /// <inheritdoc/>
- 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));
- }
- /// <inheritdoc/>
- 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);
- }
- /// <inheritdoc/>
- 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
- /// <inheritdoc/>
- 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
- /// <summary>
- /// Initializes a new instance of the <see cref="ImageExport"/> class.
- /// </summary>
- public ImageExport()
- {
- paddingNonSeparatePages = 0;
- fileSuffix = String.Empty;
- HasMultipleFiles = true;
- imageFormat = ImageExportFormat.Jpeg;
- separateFiles = true;
- Resolution = 96;
- jpegQuality = 100;
- monochromeTiffCompression = EncoderValue.CompressionCCITT4;
- saveStreams = false;
- }
- }
- }
|