// 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.Data; using System.Reflection; using iTextSharp.text; using iTextSharp.text.pdf; namespace Spludlow.Drawing { /// /// PDF drawing /// /// Wrapper for iTextSharp /// /// Can be used directly in using{} /// or through standard Spludlow printing /// /// Need to look at caching placed file between instances !!! /// public class PDF : IDisposable, Spludlow.Printing.IPrintDoc { private const float PointsInMm = 2.83464567F; private MemoryStream MemoryStream; private Document Document; private PdfWriter PdfWriter; private string Filename; private PdfReader PdfReader = null; private Spludlow.Printing.PrintDocReader PrintDocReader; public bool CMYK = false; private Dictionary Images = new Dictionary(); private Dictionary PdfReaders = new Dictionary(); private Dictionary PdfImportedPages = new Dictionary(); public PDF() { } public PDF(string filename, System.Drawing.SizeF size) { this.StartNew(filename, size); } public PDF(string filename, string pageSize) { System.Drawing.SizeF size = Spludlow.Printing.PageSizes.PageSizeMillimeters(pageSize); this.StartNew(filename, size); } public PDF(string filename, string pageSize, bool landscape) { System.Drawing.SizeF size = Spludlow.Printing.PageSizes.PageSizeMillimeters(pageSize, landscape); this.StartNew(filename, size); } private void StartNew(string filename, System.Drawing.SizeF size) { this.Filename = filename; this.MemoryStream = new MemoryStream(); this.Document = new Document(new Rectangle(0, 0, (float)size.Width * PointsInMm, (float)size.Height * PointsInMm)); this.PdfWriter = PdfWriter.GetInstance(this.Document, this.MemoryStream); this.Document.Open(); this.PdfWriter.PageEmpty = false; } public static System.Drawing.SizeF PageSize(string filename) { return PageSize(filename, 1); } public static System.Drawing.SizeF PageSize(string filename, int pageNumber) { byte[] data = File.ReadAllBytes(filename); using (PdfReader pdfReader = new PdfReader(data)) { Rectangle size = pdfReader.GetPageSizeWithRotation(pageNumber); return new System.Drawing.SizeF((float)Math.Round(size.Width / PointsInMm, 2), (float)Math.Round(size.Height / PointsInMm, 2)); } } public float Width { get { return Spludlow.Printing.PageSizes.PointsToMm(this.Document.PageSize.Width); } } public float Height { get { return Spludlow.Printing.PageSizes.PointsToMm(this.Document.PageSize.Height); } } public Image GetImage(string filename) { if (this.Images.ContainsKey(filename) == false) { Image image = Image.GetInstance(filename); this.Images.Add(filename, image); } this.Images[filename].Rotation = 0; this.Images[filename].ScalePercent(100); return this.Images[filename]; } private PdfReader GetPdfReader(string filename) { if (this.PdfReaders.ContainsKey(filename) == false) { byte[] data = File.ReadAllBytes(filename); PdfReader reader = new PdfReader(data); this.PdfReaders.Add(filename, reader); } return this.PdfReaders[filename]; } private PdfImportedPage GetPdfImportedPage(string filename, int pageNumber) { StringBuilder text = new StringBuilder(); text.Append(filename); text.Append("_"); text.Append(pageNumber); string key = text.ToString(); if (this.PdfImportedPages.ContainsKey(key) == false) { PdfReader reader = this.GetPdfReader(filename); PdfImportedPage page = this.PdfWriter.GetImportedPage(reader, pageNumber); this.PdfImportedPages.Add(key, page); } return this.PdfImportedPages[key]; } public void Print(string printData, string filename) { using (this.PrintDocReader = new Spludlow.Printing.PrintDocReader(printData, this)) { this.StartNew(filename, this.PrintDocReader.Header.PageSize); this.PrintDocReader.Print(); } this.Close(); } public void NewPage() { this.Document.NewPage(); } private BaseColor GetColour(System.Drawing.Color colour) { if (this.CMYK == false) return new BaseColor(colour); // ARGB -> CMYK return new CMYKColor(ColourPart(colour.A), ColourPart(colour.R), ColourPart(colour.G), ColourPart(colour.B)); } private int ColourPart(byte part) { return (int)Math.Round((double)part * 2.55, 0); } public void Text(string text, string fontInfo, float x, float y, System.Drawing.Color colour) { this.Text(text, fontInfo, x, y, colour, 0, System.Drawing.StringAlignment.Near, 0, System.Drawing.Color.Empty); } public void Text(string text, string fontInfo, float x, float y, System.Drawing.Color colour, float angle, System.Drawing.StringAlignment alignment, float strokeWidth, System.Drawing.Color strokeColour) { float crossX = x; float crossY = y; Font font = Spludlow.Drawing.PDFFontChest.GetFont(fontInfo); BaseFont baseFont = font.BaseFont; BaseColor baseColour = this.GetColour(colour); if (alignment != System.Drawing.StringAlignment.Center) { float height = Spludlow.Printing.PageSizes.PointsToMm(font.Size); x += height / 6.0F; y += height; } else { float halfTextHeight = Spludlow.Printing.PageSizes.PointsToMm(baseFont.GetAscentPoint(text, font.Size)) / 2; x += (float)Math.Cos(((angle + 90) / 180) * Math.PI) * halfTextHeight; y += (float)Math.Sin(((angle + 90) / 180) * Math.PI) * halfTextHeight; } x *= PointsInMm; y *= PointsInMm; y = this.Document.PageSize.Height - y; if (angle != 0) angle = 360 - (angle % 360); int pdfAlignment = Element.ALIGN_CENTER; if (alignment == System.Drawing.StringAlignment.Near) pdfAlignment = Element.ALIGN_LEFT; if (alignment == System.Drawing.StringAlignment.Far) pdfAlignment = Element.ALIGN_RIGHT; Phrase phrase = new Phrase(text, font); PdfContentByte canvas = this.PdfWriter.DirectContent; canvas.SetColorFill(baseColour); if (strokeWidth > 0 && strokeColour != System.Drawing.Color.Empty) { BaseColor strokeBaseColour = this.GetColour(strokeColour); canvas.SetColorStroke(strokeBaseColour); canvas.SetLineWidth(strokeWidth); canvas.SetTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE); } else { //canvas.SetLineWidth(0); // new - test for bold !!!!! canvas.SetTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL); //canvas.SetColorStroke(BaseColo); // Bold uses stroke as bodge } ColumnText.ShowTextAligned(canvas, pdfAlignment, phrase, x, y, angle); } 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 fontInfo, float x, float y, float width, float height, System.Drawing.Color colour, float angle, System.Drawing.StringAlignment alignment, float strokeWidth, System.Drawing.Color strokeColour) { Font font = Spludlow.Drawing.PDFFontChest.GetFont(fontInfo); BaseColor baseColour = this.GetColour(colour); float x1 = x * PointsInMm; float y1 = y * PointsInMm; float x2 = (x + width) * PointsInMm; float y2 = (y + height) * PointsInMm; width *= PointsInMm; height *= PointsInMm; y1 = this.Document.PageSize.Height - y1; y2 = this.Document.PageSize.Height - y2; PdfContentByte canvas = this.PdfWriter.DirectContent; canvas.SetColorFill(baseColour); if (strokeWidth > 0 && strokeColour != System.Drawing.Color.Empty) { BaseColor strokeBaseColour = this.GetColour(strokeColour); canvas.SetColorStroke(strokeBaseColour); canvas.SetLineWidth(strokeWidth); canvas.SetTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE); } else { //canvas.SetColorStroke(baseColour); // Bold uses stroke as bodge //canvas.SetLineWidth(0); // new - test for bold !!! canvas.SetTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL); } PdfPTable table = new PdfPTable(1); table.TotalWidth = width; Paragraph paragraph = new Paragraph(text, font); int pdfAlignment = Element.ALIGN_CENTER; if (alignment == System.Drawing.StringAlignment.Near) pdfAlignment = Element.ALIGN_LEFT; if (alignment == System.Drawing.StringAlignment.Far) pdfAlignment = Element.ALIGN_RIGHT; int pdfVerticalAlignment = Element.ALIGN_TOP; if (alignment == System.Drawing.StringAlignment.Center) pdfVerticalAlignment = Element.ALIGN_MIDDLE; PdfPCell cell = new PdfPCell(paragraph); cell.Border = iTextSharp.text.Rectangle.NO_BORDER; cell.NoWrap = false; cell.Padding = 0.0F; cell.PaddingLeft = 1.0F; if (angle != 0) { angle = 360 - (angle % 360); cell.Rotation = (int)angle; cell.Rotate(); } //if (pdfVerticalAlignment == Element.ALIGN_MIDDLE) //{ // cell.Padding = 0.0F; // cell.PaddingTop = -3.0F; //} //else //{ // cell.Padding = 1.0F; //} cell.FixedHeight = height; cell.HorizontalAlignment = pdfAlignment; cell.VerticalAlignment = pdfVerticalAlignment; table.AddCell(cell); table.WriteSelectedRows(0, -1, x1, y1, canvas); } public void Line(float x1, float y1, float x2, float y2, float lineWidth, System.Drawing.Color lineColour) { x1 *= PointsInMm; y1 *= PointsInMm; x2 *= PointsInMm; y2 *= PointsInMm; y1 = this.Document.PageSize.Height - y1; y2 = this.Document.PageSize.Height - y2; lineWidth *= PointsInMm; PdfContentByte directContent = this.PdfWriter.DirectContent; directContent.SetColorStroke(this.GetColour(lineColour)); directContent.SetLineWidth(lineWidth); directContent.MoveTo(x1, y1); directContent.LineTo(x2, y2); directContent.Stroke(); } public void Rectangle(float x, float y, float width, float height, float lineWidth, System.Drawing.Color lineColour, System.Drawing.Color fillColour) { BaseColor baseLineColour = this.GetColour(lineColour); BaseColor baseFillColour = this.GetColour(fillColour); x *= PointsInMm; y *= PointsInMm; width *= PointsInMm; height *= PointsInMm; y = this.Document.PageSize.Height - y - height; PdfContentByte directContent = this.PdfWriter.DirectContent; if (fillColour != System.Drawing.Color.Empty) directContent.SetColorFill(baseFillColour); if (lineColour != System.Drawing.Color.Empty) directContent.SetColorStroke(baseLineColour); if (lineWidth != 0) directContent.SetLineWidth(lineWidth); directContent.Rectangle(x, y, width, height); this.CloseStroke(directContent, lineWidth, lineColour, fillColour); } public void Cirlce(float x, float y, float radius, float lineWidth, System.Drawing.Color lineColour, System.Drawing.Color fillColour) { x *= PointsInMm; y *= PointsInMm; radius *= PointsInMm; y = this.Document.PageSize.Height - y; lineWidth *= PointsInMm; PdfContentByte directContent = this.PdfWriter.DirectContent; if (lineWidth != 0 && lineColour != System.Drawing.Color.Empty) { BaseColor baseLineColour = this.GetColour(lineColour); directContent.SetColorStroke(baseLineColour); directContent.SetLineWidth(lineWidth); } else { //directContent.ResetRGBColorStroke(); } if (fillColour != System.Drawing.Color.Empty) { BaseColor baseFillColour = this.GetColour(fillColour); directContent.SetColorFill(baseFillColour); } else { //directContent.ResetRGBColorFill(); } directContent.Circle(x, y, radius); this.CloseStroke(directContent, lineWidth, lineColour, fillColour); } public void Ellipse(float x, float y, float xRadius, float yRadius, float lineWidth, System.Drawing.Color lineColour, System.Drawing.Color fillColour) // FIX CMYK from HERE { BaseColor baseLineColour = new BaseColor(lineColour); BaseColor baseFillColour = new BaseColor(fillColour); x -= xRadius; y -= yRadius; float x1 = x * PointsInMm; float y1 = y * PointsInMm; float x2 = (x + xRadius * 2) * PointsInMm; float y2 = (y + yRadius * 2) * PointsInMm; y1 = this.Document.PageSize.Height - y1; y2 = this.Document.PageSize.Height - y2; lineWidth *= PointsInMm; PdfContentByte directContent = this.PdfWriter.DirectContent; directContent.SetColorFill(baseFillColour); directContent.SetColorStroke(baseLineColour); directContent.SetLineWidth(lineWidth); directContent.Ellipse(x1, y1, x2, y2); this.CloseStroke(directContent, lineWidth, lineColour, fillColour); } public void Arc(float x, float y, float xRadius, float yRadius, float startAngle, float endAngle, float lineWidth, System.Drawing.Color lineColour) { float sweep = endAngle - startAngle; sweep = -sweep; startAngle -= 90; startAngle = 360 - startAngle; BaseColor baseLineColour = new BaseColor(lineColour); x -= xRadius; y -= yRadius; float x1 = x * PointsInMm; float y1 = y * PointsInMm; float x2 = (x + xRadius * 2) * PointsInMm; float y2 = (y + yRadius * 2) * PointsInMm; y1 = this.Document.PageSize.Height - y1; y2 = this.Document.PageSize.Height - y2; lineWidth *= PointsInMm; PdfContentByte directContent = this.PdfWriter.DirectContent; directContent.SetColorStroke(baseLineColour); directContent.SetLineWidth(lineWidth); directContent.Arc(x1, y1, x2, y2, startAngle, sweep); directContent.Stroke(); } public void ArcSector(float x, float y, float xOuterRadius, float yOuterRadius, float xInnerRadius, float yInnerRadius, float startAngle, float endAngle, float lineWidth, System.Drawing.Color lineColour, System.Drawing.Color fillColour) { System.Drawing.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) { System.Drawing.PointF[] points = Spludlow.Drawing.Maths.Polygon(x, y, xRadius, yRadius, sides, rotateAngle); this.Shape(points, lineWidth, lineColour, fillColour); } public void Shape(System.Drawing.PointF[] points, float lineWidth, System.Drawing.Color lineColour, System.Drawing.Color fillColour) { BaseColor baseLineColour = new BaseColor(lineColour); BaseColor baseFillColour = new BaseColor(fillColour); lineWidth *= PointsInMm; PdfContentByte directContent = this.PdfWriter.DirectContent; directContent.SetColorStroke(baseLineColour); directContent.SetLineWidth(lineWidth); directContent.SetColorFill(baseFillColour); for (int index = 0; index < points.Length; ++index) { float pointX = points[index].X * PointsInMm; float pointY = this.Document.PageSize.Height - points[index].Y * PointsInMm; if (index == 0) directContent.MoveTo(pointX, pointY); else directContent.LineTo(pointX, pointY); } directContent.ClosePath(); this.CloseStroke(directContent, lineWidth, lineColour, fillColour); } public void Curve(System.Drawing.PointF[] points, float lineWidth, System.Drawing.Color lineColour) { BaseColor baseLineColour = new BaseColor(lineColour); lineWidth *= PointsInMm; PdfContentByte directContent = this.PdfWriter.DirectContent; directContent.SetColorStroke(baseLineColour); directContent.SetLineWidth(lineWidth); for (int index = 0; index < points.Length; ++index) { float pointX2 = points[index].X * PointsInMm; float pointY2 = this.Document.PageSize.Height - points[index].Y * PointsInMm; if (index == 0) { directContent.MoveTo(pointX2, pointY2); continue; } float pointX1 = points[index - 1].X * PointsInMm; float pointY1 = this.Document.PageSize.Height - points[index - 1].Y * PointsInMm; directContent.CurveTo(pointX1, pointY1, pointX2, pointY2); // FIX Aint no curve !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! } directContent.Stroke(); } public void Lines(System.Drawing.PointF[] points, float lineWidth, System.Drawing.Color lineColour) { BaseColor baseLineColour = new BaseColor(lineColour); lineWidth *= PointsInMm; PdfContentByte directContent = this.PdfWriter.DirectContent; directContent.SetColorStroke(baseLineColour); directContent.SetLineWidth(lineWidth); for (int index = 0; index < points.Length; ++index) { float pointX = points[index].X * PointsInMm; float pointY = this.Document.PageSize.Height - points[index].Y * PointsInMm; if (index == 0) directContent.MoveTo(pointX, pointY); else directContent.LineTo(pointX, pointY); } directContent.Stroke(); } 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); } private void PlaceBitmap(string filename, float x, float y, float scale) { x *= PointsInMm; y *= PointsInMm; y = this.Document.PageSize.Height - y; Image pdfImage = this.GetImage(filename); scale = (72.0F / pdfImage.DpiX) * scale; pdfImage.ScalePercent(scale * 100); y -= pdfImage.ScaledHeight; pdfImage.SetAbsolutePosition(x, y); PdfContentByte directContent = this.PdfWriter.DirectContent; directContent.AddImage(pdfImage); } private void PlaceVector(string filename, float x, float y, float scale, int sourcePageNumber) { PdfReader reader = this.GetPdfReader(filename); PdfImportedPage page = this.GetPdfImportedPage(filename, sourcePageNumber); Rectangle size = reader.GetPageSizeWithRotation(sourcePageNumber); x *= PointsInMm; y *= PointsInMm; y += size.Height * scale; y = this.Document.PageSize.Height - y; if (scale == 1.0F) { this.PdfWriter.DirectContent.AddTemplate(page, x, y); } else { iTextSharp.awt.geom.AffineTransform transform = new iTextSharp.awt.geom.AffineTransform(); transform.Translate(x, y); transform.Scale(scale, scale); this.PdfWriter.DirectContent.AddTemplate(page, transform); } } 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); } private void PlaceVectorAngle(string filename, float x, float y, float scale, float angle, int sourcePageNumber) { angle = -(angle / 180.0F) * (float)Math.PI; PdfReader reader = this.GetPdfReader(filename); PdfImportedPage page = this.GetPdfImportedPage(filename, sourcePageNumber); Rectangle size = reader.GetPageSizeWithRotation(sourcePageNumber); x *= PointsInMm; y *= PointsInMm; float halfWidthScaled = (size.Width / 2.0F) * scale; float halfHeightScaled = (size.Height / 2.0F) * scale; x -= halfWidthScaled; y += halfHeightScaled; y = this.Document.PageSize.Height - y; iTextSharp.awt.geom.AffineTransform transform = new iTextSharp.awt.geom.AffineTransform(); transform.Translate(x, y); transform.Rotate(angle, halfWidthScaled, halfHeightScaled); transform.Scale(scale, scale); this.PdfWriter.DirectContent.AddTemplate(page, transform); } private void PlaceBitmapAngle(string filename, float x, float y, float scale, float angle) { angle = -(angle / 180.0F) * (float)Math.PI; x *= PointsInMm; y *= PointsInMm; Image pdfImage = this.GetImage(filename); y = this.Document.PageSize.Height - y; scale = (72.0F / pdfImage.DpiX) * scale; pdfImage.ScalePercent(scale * 100); pdfImage.Rotation = angle; x -= pdfImage.ScaledWidth / 2.0F; y -= pdfImage.ScaledHeight/ 2.0F; pdfImage.SetAbsolutePosition(x, y); PdfContentByte directContent = this.PdfWriter.DirectContent; directContent.AddImage(pdfImage); } private void CloseStroke(PdfContentByte directContent, float lineWidth, System.Drawing.Color lineColour, System.Drawing.Color fillColour) { if (fillColour == System.Drawing.Color.Empty) { directContent.Stroke(); } else { if (lineColour == System.Drawing.Color.Empty || lineWidth == 0) directContent.Fill(); else directContent.FillStroke(); } } public void BarCode(string data, string type, float x, float y, float width, float height, System.Drawing.Color foreColour, System.Drawing.Color backColour, bool rotate) { Spludlow.Drawing.BarCode.DrawBarcode(this, data, type, x, y, width, height, foreColour, backColour, rotate); } public void Close() { if (this.MemoryStream == null) return; lock (this.MemoryStream) { if (this.Document != null) { this.Document.Close(); File.WriteAllBytes(this.Filename, this.MemoryStream.ToArray()); } if (this.PdfReader != null) this.PdfReader.Dispose(); if (this.PdfWriter != null) this.PdfWriter.Dispose(); if (this.Document != null) this.Document.Dispose(); if (this.MemoryStream != null) this.MemoryStream.Dispose(); foreach (string key in this.PdfReaders.Keys) this.PdfReaders[key].Dispose(); this.MemoryStream = null; } } public void Dispose() { this.Close(); } } }