// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Drawing; using System.Drawing.Printing; using System.Drawing.Imaging; using System.Data; using System.Drawing.Drawing2D; namespace Spludlow.Printing { /// <summary> /// Perform "Graphics" printing, to system printers PrintPaper() or bitmap files PrintBitmap() /// </summary> public class GraphicsPrinter : IPrintDoc { public static void SetUpGraphics(Graphics graphics, bool dontAntiAlias) { if (dontAntiAlias == false) { graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; } else { graphics.SmoothingMode = SmoothingMode.None; graphics.InterpolationMode = InterpolationMode.NearestNeighbor; graphics.PixelOffsetMode = PixelOffsetMode.None; } } private Graphics Graphics; private Spludlow.Printing.PrintDocReader PrintDocReader; private bool PageBreak = false; private int PageIndex; private string Filename; private Spludlow.Drawing.BitmapFormats BitmapFormats; private Bitmap PageBitmap = null; private string Digits; List<string> SaveFilenames; private PointF HardwareOffset; public GraphicsPrinter() { } public void PrintPaper(string printData, string printerInfo) { string printerName = null; string paperSource = null; if (printerInfo != null && printerInfo.Length > 0) { string[] words = Spludlow.Text.Split(printerInfo, ','); printerName = words[0]; if (words.Length > 1) paperSource = words[1]; } this.PrintPaper(printData, printerName, paperSource); } public void PrintPaper(string printData, string printerName, string paperSource) { this.Filename = null; this.PageIndex = 0; this.PageBreak = false; this.HardwareOffset = new PointF(-1, -1); using (this.PrintDocReader = new PrintDocReader(printData, this)) { string pageSize = Spludlow.Printing.PageSizes.PaperName(this.PrintDocReader.Header.PageSize); bool landscape = false; if (pageSize.EndsWith("*") == true) { pageSize = pageSize.Substring(0, pageSize.Length - 1); landscape = true; } string filename = null; if (paperSource != null && (paperSource.Contains(@":\") == true || paperSource.Contains(@"\\") == true)) // Paper source is filename { filename = paperSource; paperSource = null; } using (PrintDocument printDocument = Spludlow.Printing.PrinterSetting.CreatePrintDocument(pageSize, landscape, printerName, paperSource)) { printDocument.DocumentName = this.PrintDocReader.Header.Description; printDocument.PrintPage += new PrintPageEventHandler(printDocument_PrintPage); if (filename != null) { printDocument.PrinterSettings.GetHdevmode(); printDocument.PrinterSettings.PrintToFile = true; printDocument.PrinterSettings.PrintFileName = filename; } printDocument.Print(); } } } void printDocument_PrintPage(object sender, PrintPageEventArgs e) { this.Graphics = e.Graphics; this.Graphics.PageUnit = GraphicsUnit.Millimeter; if (this.HardwareOffset.X == -1) { PointF offset = Spludlow.Printing.PrinterSetting.ConfigPrinterOffset(e.PageSettings); bool offsetDefined = (offset.X != -1); bool configZero = (offsetDefined == true && (offset.X == 0 && offset.Y == 0)); if (offsetDefined == false || configZero == true) { float xOffset = (float)Math.Round(e.PageSettings.PrintableArea.X * 0.254F, 1); float yOffset = (float)Math.Round(e.PageSettings.PrintableArea.Y * 0.254F, 1); if (configZero == false) { offset = new PointF(xOffset, yOffset); } else { Spludlow.Log.Warning("Printer Configured 0,0 Offset. Auto Offset would be: " + xOffset + ", " + yOffset + ", printer:" + e.PageSettings.PrinterSettings.PrinterName + ", landscape:" + e.PageSettings.Landscape + ", tray:" + e.PageSettings.PaperSource.SourceName); } } this.HardwareOffset = offset; } this.Graphics.TranslateTransform(-this.HardwareOffset.X, -this.HardwareOffset.Y); // Could get reset !!! SetUpGraphics(this.Graphics, false); e.HasMorePages = false; while (this.PrintDocReader.PrintNext() == true) { if (this.PageBreak == true) { this.PageBreak = false; e.HasMorePages = true; break; } } } public string[] PrintBitmap(string printData, string printerInfo) { if (printerInfo == null || printerInfo.Length == 0) printerInfo = Spludlow.WebServices.CreateProgramDataTempFilename(".png"); string[] parts = Spludlow.Text.Split(printerInfo, ','); if (parts.Length < 1 || parts.Length > 3) throw new ApplicationException("PrintBitmap Bad argument count: " + printerInfo); string filename = parts[0]; int dpi = 600; PixelFormat pixelFormat = PixelFormat.Format24bppRgb; ImageFormat imageFormat = Spludlow.Drawing.BitmapFormats.ParseImageFormat(Path.GetExtension(filename).Substring(1)); if (imageFormat == ImageFormat.Gif) pixelFormat = PixelFormat.Format8bppIndexed; if (parts.Length > 1) dpi = (int)Single.Parse(parts[1]); // DPI should all use float, needs changing from int if (parts.Length > 2) pixelFormat = Spludlow.Drawing.BitmapFormats.ParsePixelFormat(parts[2]); return this.PrintBitmap(printData, filename, dpi, pixelFormat, imageFormat); } public string[] PrintBitmap(string printData, string filename, int dpi, PixelFormat pixelFormat, ImageFormat imageFormat) { this.Filename = filename; this.PageIndex = -1; this.SaveFilenames = new List<string>(); this.BitmapFormats = new Drawing.BitmapFormats(dpi, pixelFormat, imageFormat); using (this.PrintDocReader = new PrintDocReader(printData, this)) { this.Digits = new string('0', this.PrintDocReader.Header.PageCount.ToString().Length); this.NewPage(); this.PrintDocReader.Print(); } this.Save(); return this.SaveFilenames.ToArray(); } public void NewPage() { // Paper Print if (this.Filename == null) { this.PageBreak = true; } else { // Bitmap Print this.Save(); int width = Spludlow.Printing.PageSizes.MillimetersToPixels(this.PrintDocReader.Header.PageSize.Width, this.BitmapFormats.Dpi); int height = Spludlow.Printing.PageSizes.MillimetersToPixels(this.PrintDocReader.Header.PageSize.Height, this.BitmapFormats.Dpi); this.PageBitmap = new Bitmap(width, height, this.BitmapFormats.PixelFormat); this.PageBitmap.SetResolution(this.BitmapFormats.Dpi, this.BitmapFormats.Dpi); this.Graphics = Graphics.FromImage(this.PageBitmap); SetUpGraphics(this.Graphics, true); // give fluffy bar codes with false this.Graphics.PageUnit = GraphicsUnit.Millimeter; this.Graphics.Clear(Color.White); } ++this.PageIndex; } public void Save() { if (this.PageBitmap == null) return; this.Graphics.Dispose(); string filename = this.Filename; if (this.PrintDocReader.Header.PageCount > 1) filename = Path.GetDirectoryName(this.Filename) + @"\" + Path.GetFileNameWithoutExtension(this.Filename) + "-" + (this.PageIndex + 1).ToString(this.Digits) + Path.GetExtension(this.Filename); this.PageBitmap.Save(filename, this.BitmapFormats.ImageFormat); this.SaveFilenames.Add(filename); this.PageBitmap.Dispose(); this.PageBitmap = null; } public void Cross(float x, float y) { float size = 1.0F; float width = 0.25F; this.Line(x - size, y, x + size, y, width, System.Drawing.Color.Black); this.Line(x, y - size, x, y + size, width, System.Drawing.Color.Black); } public void Text(string text, string font, float x, float y, System.Drawing.Color colour, float angle, StringAlignment alignment, float strokeWidth, Color strokeColour) { StringFormat format = new StringFormat(); format.Alignment = alignment; if (format.Alignment == StringAlignment.Center) { format.LineAlignment = StringAlignment.Center; } PointF drawPoint = new PointF(x, y); if (angle != 0) { this.Graphics.TranslateTransform(x, y); this.Graphics.RotateTransform(angle); drawPoint = new PointF(0, 0); } if (strokeWidth != 0 && strokeColour != Color.Empty) { GraphicsPath graphicsPath = new GraphicsPath(); Spludlow.Drawing.FontInfo fontInfo = new Drawing.FontInfo(font); float PointsInMm = 2.83464567F; float emSize = fontInfo.Size / PointsInMm; Point point = new Point((int)x, (int)y); graphicsPath.AddString(text, Spludlow.Drawing.ArtChest.Font(font).FontFamily, (int)fontInfo.FontStyles, emSize, point, format); Brush fillBrush = Spludlow.Drawing.ArtChest.SolidBrush(colour); Pen outlinePen = Spludlow.Drawing.ArtChest.Pen(strokeWidth / PointsInMm, strokeColour); this.Graphics.FillPath(fillBrush, graphicsPath); this.Graphics.DrawPath(outlinePen, graphicsPath); } else { this.Graphics.DrawString(text, Spludlow.Drawing.ArtChest.Font(font), Spludlow.Drawing.ArtChest.SolidBrush(colour), drawPoint, format); } if (angle != 0) this.Graphics.ResetTransform(); } public void TextBox(string text, string fontInfo, float x, float y, float width, float height, System.Drawing.Color colour, System.Drawing.StringAlignment alignment, float strokeWidth, System.Drawing.Color strokeColour) { this.TextBox(text, fontInfo, x, y, width, height, colour, 0.0F, alignment, strokeWidth, strokeColour); } public void TextBox(string text, string font, float x, float y, float width, float height, System.Drawing.Color colour, float angle, System.Drawing.StringAlignment alignment, float strokeWidth, System.Drawing.Color strokeColour) { StringFormat format = new StringFormat(); // dispose !!! format.Alignment = alignment; if (format.Alignment == StringAlignment.Center) format.LineAlignment = StringAlignment.Center; RectangleF rectangle = new RectangleF(x, y, width, height); if (angle != 0) { switch (angle) { case 90: rectangle = new RectangleF(0, 0, height, width); this.Graphics.TranslateTransform(x + width, y); break; case 180: rectangle = new RectangleF(0, 0, width, height); this.Graphics.TranslateTransform(x + width, y + height); break; case 270: rectangle = new RectangleF(0, 0, height, width); this.Graphics.TranslateTransform(x, y + height); break; default: throw new ApplicationException("Text Box rotate angle must be 90, 180, oe 270 only"); } this.Graphics.RotateTransform(angle); } if (strokeWidth != 0 && strokeColour != Color.Empty) { GraphicsPath graphicsPath = new GraphicsPath(); // Dispose !! Spludlow.Drawing.FontInfo fontInfo = new Drawing.FontInfo(font); float PointsInMm = 2.83464567F; float emSize = fontInfo.Size / PointsInMm; graphicsPath.AddString(text, Spludlow.Drawing.ArtChest.Font(font).FontFamily, (int)fontInfo.FontStyles, emSize, rectangle, format); Brush fillBrush = Spludlow.Drawing.ArtChest.SolidBrush(colour); Pen outlinePen = Spludlow.Drawing.ArtChest.Pen(strokeWidth / PointsInMm, strokeColour); this.Graphics.FillPath(fillBrush, graphicsPath); this.Graphics.DrawPath(outlinePen, graphicsPath); } else { this.Graphics.DrawString(text, Spludlow.Drawing.ArtChest.Font(font), Spludlow.Drawing.ArtChest.SolidBrush(colour), rectangle, format); } if (angle != 0) this.Graphics.ResetTransform(); } public void Line(float x1, float y1, float x2, float y2, float lineWidth, Color lineColour) { this.Graphics.DrawLine(Spludlow.Drawing.ArtChest.Pen(lineWidth, lineColour), x1, y1, x2, y2); } public void Rectangle(float x, float y, float width, float height, float lineWidth, Color lineColour, Color fillColour) { lineWidth *= 0.3527777777778F; if (fillColour != Color.Empty) this.Graphics.FillRectangle(Spludlow.Drawing.ArtChest.SolidBrush(fillColour), x, y, width, height); if (lineColour != Color.Empty && lineWidth != 0) this.Graphics.DrawRectangle(Spludlow.Drawing.ArtChest.Pen(lineWidth, lineColour), x, y, width, height); } public void Cirlce(float x, float y, float radius, float lineWidth, System.Drawing.Color lineColour, System.Drawing.Color fillColour) { x -= radius; y -= radius; if (fillColour != Color.Empty) this.Graphics.FillEllipse(Spludlow.Drawing.ArtChest.SolidBrush(fillColour), x, y, radius * 2, radius * 2); if (lineColour != Color.Empty && lineWidth != 0) this.Graphics.DrawEllipse(Spludlow.Drawing.ArtChest.Pen(lineWidth, lineColour), x, y, radius * 2, radius * 2); } public void Ellipse(float x, float y, float xRadius, float yRadius, float lineWidth, System.Drawing.Color lineColour, System.Drawing.Color fillColour) { if (fillColour != Color.Empty) this.Graphics.FillEllipse(Spludlow.Drawing.ArtChest.SolidBrush(fillColour), x, y, xRadius * 2, yRadius * 2); if (lineColour != Color.Empty && lineWidth != 0) this.Graphics.DrawEllipse(Spludlow.Drawing.ArtChest.Pen(lineWidth, lineColour), x, y, xRadius * 2, yRadius * 2); } public void Arc(float x, float y, float xRadius, float yRadius, float startAngle, float endAngle, float lineWidth, System.Drawing.Color lineColour) { this.Graphics.DrawArc(Spludlow.Drawing.ArtChest.Pen(lineWidth, lineColour), x - xRadius, y - yRadius, xRadius * 2, yRadius * 2, startAngle - 90, endAngle - startAngle); } public void ArcSector(float x, float y, float xOuterRadius, float yOuterRadius, float xInnerRadius, float yInnerRadius, float startAngle, float endAngle, float lineWidth, Color lineColour, Color fillColour) { PointF[] points = Spludlow.Drawing.Maths.ArcSector(x, y, xOuterRadius, yOuterRadius, xInnerRadius, yInnerRadius, startAngle, endAngle); this.Shape(points, lineWidth, lineColour, fillColour); } public void Polygon(float x, float y, float xRadius, float yRadius, int sides, float rotateAngle, float lineWidth, System.Drawing.Color lineColour, System.Drawing.Color fillColour) { PointF[] points = Spludlow.Drawing.Maths.Polygon(x, y, xRadius, yRadius, sides, rotateAngle); this.Shape(points, lineWidth, lineColour, fillColour); } public void Shape(PointF[] points, float lineWidth, Color lineColour, Color fillColour) { if (fillColour != Color.Empty) this.Graphics.FillPolygon(Spludlow.Drawing.ArtChest.SolidBrush(fillColour), points); if (lineColour != Color.Empty && lineWidth != 0) this.Graphics.DrawPolygon(Spludlow.Drawing.ArtChest.Pen(lineWidth, lineColour), points); } public void Curve(PointF[] points, float lineWidth, Color lineColour) { this.Graphics.DrawCurve(Spludlow.Drawing.ArtChest.Pen(lineWidth, lineColour), points); } public void Lines(PointF[] points, float lineWidth, Color lineColour) { this.Graphics.DrawLines(Spludlow.Drawing.ArtChest.Pen(lineWidth, lineColour), points); } public void Place(string filename, float x, float y, float scale, int sourcePageNumber) { filename = Spludlow.WebServices.GetProgramDataTempFile(filename); if (Path.GetExtension(filename).ToLower() == ".pdf") this.PlaceVector(filename, x, y, scale, sourcePageNumber); else this.PlaceBitmap(filename, x, y, scale); } public void PlaceBitmap(string filename, float x, float y, float scale) { using (Image image = Image.FromFile(filename)) { if (scale == 1) { this.Graphics.DrawImageUnscaled(image, (int)x, (int)y); } else { float width = Spludlow.Printing.PageSizes.PixelsToMillimeters(image.Width, (int)image.HorizontalResolution) * scale; float height = Spludlow.Printing.PageSizes.PixelsToMillimeters(image.Height, (int)image.VerticalResolution) * scale; this.Graphics.DrawImage(image, x, y, width, height); } } } private void PlaceVector(string filename, float x, float y, float scale, int sourcePageNumber) { int dpi = (int)(this.Graphics.DpiX * scale); using (Spludlow.TempDirectory tempDir = new TempDirectory()) { string[] filenames = Spludlow.Printing.GhostScript.RenderPdf(filename, tempDir + @"\output.png", dpi, true); if (sourcePageNumber > filenames.Length) throw new ApplicationException("Graphics Printer, Place Vector: Not that many pages: " + sourcePageNumber + ", pages in PDF: " + filenames.Length); this.PlaceBitmap(filenames[sourcePageNumber - 1], x, y, scale); } } public void PlaceAngle(string filename, float x, float y, float scale, float angle, int sourcePageNumber) { filename = Spludlow.WebServices.GetProgramDataTempFile(filename); if (Path.GetExtension(filename).ToLower() == ".pdf") this.PlaceVectorAngle(filename, x, y, scale, angle, sourcePageNumber); else this.PlaceBitmapAngle(filename, x, y, scale, angle); } public void PlaceBitmapAngle(string filename, float x, float y, float scale, float angle) { using (Image image = Image.FromFile(filename)) { float width = Spludlow.Printing.PageSizes.PixelsToMillimeters(image.Width, (int)image.HorizontalResolution); float height = Spludlow.Printing.PageSizes.PixelsToMillimeters(image.Height, (int)image.VerticalResolution); float halfWidth = width / 2.0F; float halfHeight = height / 2.0F; PointF origin = new PointF(x, y); PointF upperLeft = new PointF(x - halfWidth, y - halfHeight); PointF upperRight = new PointF(x + halfWidth, y - halfHeight); PointF lowerLeft = new PointF(x - halfWidth, y + halfHeight); upperLeft = Spludlow.Drawing.Maths.TransformPoint(upperLeft, scale, angle, origin); upperRight = Spludlow.Drawing.Maths.TransformPoint(upperRight, scale, angle, origin); lowerLeft = Spludlow.Drawing.Maths.TransformPoint(lowerLeft, scale, angle, origin); this.Graphics.DrawImage(image, new PointF[] { upperLeft, upperRight, lowerLeft }); } } private void PlaceVectorAngle(string filename, float x, float y, float scale, float angle, int sourcePageNumber) { int dpi = (int)(this.Graphics.DpiX * scale); using (Spludlow.TempDirectory tempDir = new TempDirectory()) { string[] filenames = Spludlow.Printing.GhostScript.RenderPdf(filename, tempDir + @"\output.png", dpi, true); if (sourcePageNumber > filenames.Length) throw new ApplicationException("Graphics Printer, Place Vector: Not that many pages: " + sourcePageNumber + ", pages in PDF: " + filenames.Length); this.PlaceBitmapAngle(filenames[sourcePageNumber - 1], x, y, scale, angle); } } public void BarCode(string data, string type, float x, float y, float width, float height, Color foreColour, Color backColour, bool rotate) { // Switch off-on alntilising ! Spludlow.Drawing.BarCode.DrawBarcode(this, data, type, x, y, width, height, foreColour, backColour, rotate); } } }