// 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.Data; using System.Drawing; namespace Spludlow.Drawing { public class BarCode { private static Dictionary BarCodes = new Dictionary(); private static IBarCode GetBarCode(string type) { lock (BarCodes) { if (BarCodes.ContainsKey(type) == false) { IBarCode instance = null; switch (type) { case "Code128": instance = new Spludlow.Drawing.BarCode128(); break; case "Code128A": instance = new Spludlow.Drawing.BarCode128("A"); break; case "Code128B": instance = new Spludlow.Drawing.BarCode128("B"); break; case "Code128C": instance = new Spludlow.Drawing.BarCode128("C"); break; case "RM4SCC": instance = new Spludlow.Drawing.RM4SCC(); break; default: instance = (IBarCode)Spludlow.Reflections.MakeInstance("Spludlow.ZXing", "Spludlow.Drawing.ZXings", new object[] { type }); break; } BarCodes.Add(type, instance); } } return BarCodes[type]; } public static bool[][] Encode(string data, string type) { IBarCode instance = GetBarCode(type); return instance.Encode(data); } public static void DrawBarcode(Spludlow.Printing.IPrintDoc printDoc, string data, string type, float x, float y, float width, float height, Color foreColour, Color backColour, bool rotate) { bool[][] barCodeData = Spludlow.Drawing.BarCode.Encode(data, type); if (barCodeData.Length == 0) throw new ApplicationException("No bar code height data"); if (barCodeData[0].Length == 0) throw new ApplicationException("No bar code width data"); barCodeData = RemoveSpace(barCodeData); if (rotate == true) { bool[][] newBarCode = new bool[barCodeData[0].Length][]; for (int newLine = 0; newLine < newBarCode.Length; ++newLine) { newBarCode[newLine] = new bool[barCodeData.Length]; int newWidth = newBarCode[newLine].Length; for (int newColumn = 0; newColumn < newWidth; ++newColumn) { newBarCode[newLine][newColumn] = barCodeData[(newWidth - 1) - newColumn][newLine]; } } barCodeData = newBarCode; } int widthCount = barCodeData[0].Length; int heightCount = barCodeData.Length; float xStep = width / widthCount; float yStep = height / heightCount; printDoc.Rectangle(x, y, width, height, 0, Color.Empty, backColour); int[][] lengthMatrix = ConvertSizesH(barCodeData); int[][] rectMatrix = ConvertSizesV(lengthMatrix); int yPosIndex = 0; foreach (int[] rowPart in rectMatrix) { int xPosIndex = 0; int yMult = rowPart[0]; float barHeight = yMult * yStep; float yPos = y + (yPosIndex * yStep); bool on = true; for (int widthIndex = 0; widthIndex < rowPart.Length; ++widthIndex) { if (widthIndex == 0) continue; int xMult = rowPart[widthIndex]; if (xMult > 0) { float xPos = x + (xPosIndex * xStep); if (on == true) printDoc.Rectangle(xPos, yPos, xMult * xStep, barHeight, 0, Color.Empty, foreColour); xPosIndex += xMult; } on = !on; } yPosIndex += yMult; } } private static int[][] ConvertSizesH(bool[][] matrix) { int width = matrix[0].Length; int height = matrix.Length; List result = new List(); for (int y = 0; y < height; ++y) { List newRow = new List(); if (matrix[y][0] == false) newRow.Add(0); int currentWidth = 0; bool last = matrix[y][0]; for (int x = 0; x < width; ++x) { bool value = matrix[y][x]; if (value != last) { newRow.Add(currentWidth); currentWidth = 1; } else { ++currentWidth; } if (x == width - 1) newRow.Add(currentWidth); last = value; } result.Add(newRow.ToArray()); } return result.ToArray(); } private static int[][] ConvertSizesV(int[][] input) { int height = input.Length; List result = new List(); int currentWidth = 0; int[] newRow; for (int y = 0; y < height; ++y) { if (CompareRowBefore(input, y) == false) { newRow = new int[input[y - 1].Length + 1]; newRow[0] = currentWidth; for (int index = 0; index < input[y - 1].Length; ++index) newRow[index + 1] = input[y - 1][index]; result.Add(newRow); currentWidth = 1; } else { ++currentWidth; } if (y == height - 1) { newRow = new int[input[y].Length + 1]; newRow[0] = currentWidth; for (int index = 0; index < input[y].Length; ++index) newRow[index + 1] = input[y][index]; result.Add(newRow); } } return result.ToArray(); } private static bool CompareRowBefore(int[][] matrix, int y) { if (y == 0) return true; int indexA = y; int indexB = y - 1; for (int index = 0; index < matrix[indexA].Length; ++index) { if (matrix[indexA][index] != matrix[indexB][index]) return false; } return true; } private static bool[][] RemoveSpace(bool[][] matrix) { int width = matrix[0].Length; int height = matrix.Length; int[] trim = new int[4]; for (int pass = 0; pass < 4; ++pass) // top, right, bottom, left { switch (pass) { case 0: for (int y = 0; y < height; ++y) { if (EmptyRow(matrix, y) == true) trim[pass] = y + 1; else break; } break; case 1: for (int x = width - 1; x >= 0; --x) { if (EmptyColumn(matrix, x) == true) trim[pass] = width - x; else break; } break; case 2: for (int y = height - 1; y >= 0; --y) { if (EmptyRow(matrix, y) == true) trim[pass] = height - y; else break; } break; case 3: for (int x = 0; x < width; ++x) { if (EmptyColumn(matrix, x) == true) trim[pass] = x + 1; else break; } break; } } //Spludlow.Log.Report("Bar trim", trim); int newWidth = width - (trim[1] + trim[3]); int newHeight = height - (trim[0] + trim[2]); bool[][] newMatrix = new bool[newHeight][]; for (int y = 0; y < newHeight; ++y) { newMatrix[y] = new bool[newWidth]; for (int x = 0; x < newWidth; ++x) { newMatrix[y][x] = matrix[y + trim[0]][x + trim[3]]; } } return newMatrix; } private static bool EmptyRow(bool[][] matrix, int y) { for (int x = 0; x < matrix[y].Length; ++x) if (matrix[y][x] == true) return false; return true; } private static bool EmptyColumn(bool[][] matrix, int x) { for (int y = 0; y < matrix.Length; ++y) if (matrix[y][x] == true) return false; return true; } public static void TestSheet(string filename) { bool printPDF = filename.ToLower().EndsWith(".pdf"); bool includeZXing = (Spludlow.Config.Application("Spludlow.ZXing", true) != null); DataTable table = Spludlow.Data.TextTable.ReadText(new string[] { "BarCodeId TestData Group FullName Notes", "String* String String String String", "Code128 spl S Code 128", // http://spludlow.net/ "Code128A #$% S Code 128", // ! \"#$%&'()*+,-./123:;<=>?@ABC[\\]^_ "Code128B 12AB S Code 128", // 4567890{|}~ "Code128C 1122334455667 S Code 128", "RM4SCC NG25 0PR S RM4SCC", "CODABAR 5678$-9 Z1 Codabar 12: digits 0–9, dash, and $", // 012345678$-9 "CODE_39 -.$/+ % Z1 Code 39 (A - Z), numeric digits (0 - 9) and a number of special characters (-, ., $, /, +, %, and space)", // A-B.C$1/2+3% 4 "CODE_93 ABC123 Z1 Code 93 Higher density and data security enhancement to Code 39", "CODE_128 spludlow Z1 Code 128 3 modes ABC most of ASCI", "EAN_8 1234567 Z1 EAN-8 7 Digits", "EAN_13 123456789012 Z1 EAN-13 12 Digits (International Article Number / European Article Number)", "ITF 12345678901234 Z1 ITF Digits - Interleaved 2 of 5 (ITF-14 used for Global Trade Item Number)", "UPC_A 12345678901 Z1 UPC-A 11 digits", "UPC_E 1234567 Z1 UPC-E 7 digits, zero-suppressed version", "MSI 123567890 Z1 MSI digits 0–9 (Modified Plessey)", "PLESSEY 123ABCDEF Z1 Plessey Code Hex 0-9 A-F (ZXing:Creates many rectangles!!!)", "AZTEC https://www.spludlow.co.uk/ Z2 Aztec Code 2D [ZXing 1:4]", "DATA_MATRIX https://www.spludlow.co.uk/ Z2 Data Matrix 2D", "PDF_417 https://www.spludlow.co.uk/ Z2 PDF417 2D", "QR_CODE https://www.spludlow.co.uk/ Z2 QR Code 2D [ZXing 1:3]", //"MAXICODE", // These didn't seem to work, I doubt anyone will miss them //"RSS_14", //"RSS_EXPANDED", //"All_1D", //"UPC_EAN_EXTENSION", //"IMB", }); Spludlow.Printing.PrintDoc printDoc = new Spludlow.Printing.PrintDoc("A4"); int numberColumns = 2; float squareWidth = (printDoc.Width - 20.0F) / numberColumns; float squareHeight = 25.0F; string fontHeading = "Arial, 24, Bold, Underline"; string font = "Arial, 12"; string fontSmall = "Arial, 9"; printDoc.Text("Spludlow Barcode Test Sheet", fontHeading, printDoc.Width / 2, 10, Color.Black, 0, StringAlignment.Center); for (int index = 0; index < table.Rows.Count; ++index) { DataRow row = table.Rows[index]; string type = (string)row["BarCodeId"]; string data = (string)row["TestData"]; string group = (string)row["Group"]; if (includeZXing == false && group != "S") continue; float x = 10 + (index % numberColumns) * squareWidth; float y = 30 + (index / numberColumns) * squareHeight; printDoc.Rectangle(x, y, squareWidth, squareHeight, 1.0F, Color.Black, Color.Empty); printDoc.Text(type, font, x, y); printDoc.Text("\"" + data + "\"", fontSmall, x + 30, y); printDoc.BarCode(data, type, x + 2.5F, y + 7.5F, squareWidth - 40, squareHeight - 10); printDoc.BarCode(data, type, x + squareWidth - 22.5F, y + 2.5F, squareHeight - 5, squareHeight - 5, true); } if (printPDF == true) Spludlow.Printing.Printer.Print(printDoc, "pdf", filename); else Spludlow.Printing.Printer.Print(printDoc, "bitmap", filename); } } }