Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved: Barcode scaling for PDF417 and Code128 #219

Merged
merged 3 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/BinaryKits.Zpl.Label/Elements/ZplPDF417.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class ZplPDF417 : ZplPositionedElementBase, IFormatElement
{

public int Height { get; protected set; }
public int ModuleWidth { get; protected set; }
public string Content { get; protected set; }
public FieldOrientation FieldOrientation { get; protected set; }
public int? Columns { get; protected set; }
Expand All @@ -23,6 +24,7 @@ public class ZplPDF417 : ZplPositionedElementBase, IFormatElement
/// <param name="positionX"></param>
/// <param name="positionY"></param>
/// <param name="height"></param>
/// <param name="moduleWidth"></param>
/// <param name="columns">1-30: Number of data columns to encode. Default will auto balance 1:2 row to column</param>
/// <param name="rows">3-90. Number of data columns to encode. Default will auto balance 1:2 row to column</param>
/// <param name="compact">Truncate right row indicators and stop pattern</param>
Expand All @@ -34,6 +36,7 @@ public ZplPDF417(
int positionX,
int positionY,
int height = 8,
int moduleWidth = 2,
int? columns = null,
int? rows = null,
bool compact = false,
Expand All @@ -45,6 +48,7 @@ public ZplPDF417(
{
FieldOrientation = fieldOrientation;
Height = height;
ModuleWidth = moduleWidth;
Columns = columns;
Rows = rows;
Compact = compact;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@
<None Update="Labels\Test\TextRotation2-102x152.zpl2">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Labels\Test\BarcodePDF417-102x152.zpl2">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Labels\Test\GS1-102x152.zpl2">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
^XA

^FO10,10
^BY1
^B7N,4
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FX This one looks half-height on Zebra
^FO120,10
^BY1
^B7N,4,,,,Y
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FX Difference for security level
^FO10,90
^BY1
^B7N,4,3
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FO120,90
^BY1
^B7N,4,4
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FO250,90
^BY1
^B7N,4,5
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FO400,90
^BY1
^B7N,4,6
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FX Labelary is wrong on this one
^FO570,90
^BY1
^B7N,4,6,,,Y
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FO10,210
^BY1
^B7N,4,5,6
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FO210,210
^BY1
^B7N,4,5,6,20
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FX Difference for missing columns
^FO10,300
^BY1
^B7N,4,5,8,24,N
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FO250,300
^BY1
^B7N,4,5,,24,N
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FX DHL real-world test vs corrected
^FO10,400
^BY2
^B7N,6.7,5,,30,N
^FD
UNH+2876965+IFTMIN:D:96B:UN+DHL5.6.0/AWESOME'BGM+787+999994760001+9'DTM+186:20240312:102'TSR+++10'TOD+Z02++CPT'NAD+OS+0464651'NAD+CN+++MEVR. SOME PERSONE+STREET 17 E+SOMEPLACE++0+DE'CTA+GR'[email protected]:EM'GID+0+1'MEA+WT++KGM:2'PCI+ZZ1+JVGL09999999999994760001'UNT+13+123546798'
^FS

^FX Difference in blocks between all three sources. (This, Zebra and Labelary)
^FO10,590
^BY2
^B7N,6,5,9,30,N
^FD
UNH+2876965+IFTMIN:D:96B:UN+DHL5.6.0/AWESOME'BGM+787+999994760001+9'DTM+186:20240312:102'TSR+++10'TOD+Z02++CPT'NAD+OS+0464651'NAD+CN+++MEVR. SOME PERSONE+STREET 17 E+SOMEPLACE++0+DE'CTA+GR'[email protected]:EM'GID+0+1'MEA+WT++KGM:2'PCI+ZZ1+JVGL09999999999994760001'UNT+13+123546798'
^FS

^FX Difference in blocks with Labelary but not with Zebra printed result
^FO10,780
^BY2
^B7N,4,5,6
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FO380,780
^BY2
^B7N,4,5,6
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789^FS

^FO10,840
^BY3
^B7N,4,5,8,24,N
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FO10,950
^BY4
^B7N,4,6,7,24,N
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^FO10,1060
^BY5
^B7N,4,6,4,36,N
^FDABCDEFGHIJKLMNOPQRSTUVWXYZ^FS

^XZ
18 changes: 18 additions & 0 deletions src/BinaryKits.Zpl.Viewer.WebApi/Labels/Test/GS1-102x152.zpl2
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
^XA

^FX all should produce 011234567890123121123456

^FT101,59^A0N,31,30^FH\^CI28^FDGS1-128 : ^FS^CI27
^BY2,3,54^FT110,134^BCN,,N,N
^FH\^FD>;>8011234567890123121123456^FS

^FT101,174^A0N,31,30^FH\^CI28^FDGS1-DataMatrix : ^FS^CI27
^FT185,324^BXN,6,200,0,0,1,_,1
^FH\^FD_1011234567890123121123456^FS

^FT548,174^A0N,31,30^FH\^CI28^FDGS1-QR: ^FS^CI27
^FO642,206^BQN,2,5
^FH\^FD>;>8011234567890123121123456^FS

^PQ1,0,1,Y
^XZ
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public override ZplElementBase Analyze(string zplCommand)
}
if (this.VirtualPrinter.NextElementFieldData is PDF417FieldData pdf147)
{
return new ZplPDF417(text, x, y, pdf147.Height, pdf147.Columns, pdf147.Rows, pdf147.Compact, pdf147.SecurityLevel, pdf147.FieldOrientation, bottomToTop);
return new ZplPDF417(text, x, y, pdf147.Height, moduleWidth, pdf147.Columns, pdf147.Rows, pdf147.Compact, pdf147.SecurityLevel, pdf147.FieldOrientation, bottomToTop);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using BinaryKits.Zpl.Label.Elements;
using BinaryKits.Zpl.Viewer.Models;
using System;
using System.Globalization;

namespace BinaryKits.Zpl.Viewer.CommandAnalyzers
{
Expand Down Expand Up @@ -33,6 +34,16 @@ public override ZplElementBase Analyze(string zplCommand)
{
height = tmpint;
}
else if (zplDataParts.Length > 1)
{
//Sometimes a decimal is given, this works around that.
var tempDecimal = Convert.ToDecimal(zplDataParts[1], new CultureInfo("en-US"));
var tempHeight = (int)Math.Floor(tempDecimal);
if (tempHeight > 0)
{
height = tempHeight;
}
}

int securityLevel = 0;
if (zplDataParts.Length > 2 && int.TryParse(zplDataParts[2], out tmpint))
Expand Down
55 changes: 28 additions & 27 deletions src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode128ElementDrawer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,19 @@
using BinaryKits.Zpl.Viewer.Helpers;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text.RegularExpressions;

namespace BinaryKits.Zpl.Viewer.ElementDrawers
{
public class Barcode128ElementDrawer : BarcodeDrawerBase
{

/// <summary>
/// Start sequence lookups.
/// <see href="https://supportcommunity.zebra.com/s/article/Creating-GS1-Barcodes-with-Zebra-Printers-for-Data-Matrix-and-Code-128-using-ZPL"/>
/// </summary>
private static readonly Dictionary<string, TYPE> startCodeMap = new Dictionary<string, TYPE>()
{
{ ">9", TYPE.CODE128A },
{ ">:", TYPE.CODE128B },
{ ">;", TYPE.CODE128C }
};

private static readonly Regex startCodeRegex = new Regex(@"^(>[9:;])(.+)$", RegexOptions.Compiled);
private static readonly Regex invalidInvocationRegex = new Regex(@"(?<!^)>[9:;]", RegexOptions.Compiled);
private static readonly Regex startCodeRegex = new Regex(@"(>[9:;])", RegexOptions.Compiled);
private static readonly Regex invalidInvocationRegex = new Regex(@"(?<!^)>[<0=12345679:;]", RegexOptions.Compiled); //>8 has limited support here

// As defined in BarcodeLib.Symbologies.Code128
private static readonly string FNC1 = Convert.ToChar(200).ToString();
Expand All @@ -46,40 +37,50 @@ public override void Draw(ZplElementBase element, DrawerOptions options)
{
if (element is ZplBarcode128 barcode)
{
var barcodeType = TYPE.CODE128B;
// remove any start sequences not at the start of the content (invalid invocation)
string content = invalidInvocationRegex.Replace(barcode.Content, "");
var barcodeType = TYPE.CODE128;

//remove the start code form the content we only support the globals N,A,D,U and our barcode library doesn't support these types
string content = startCodeRegex.Replace(barcode.Content, "");
string interpretation = content;

// remove any start sequences not at the start of the content (invalid invocation)
content = invalidInvocationRegex.Replace(content, "");
interpretation = content;

// support hand-rolled GS1
content = content.Replace(">8", FNC1);
interpretation = interpretation.Replace(">8", "");

if (string.IsNullOrEmpty(barcode.Mode) || barcode.Mode == "N")
{
Match startCodeMatch = startCodeRegex.Match(content);
if (startCodeMatch.Success)
{
barcodeType = startCodeMap[startCodeMatch.Groups[1].Value];
content = startCodeMatch.Groups[2].Value;
interpretation = content;
}
// support hand-rolled GS1
content = content.Replace(">8", FNC1);
interpretation = interpretation.Replace(">8", "");
// TODO: support remaining escapes within a barcode
barcodeType = TYPE.CODE128; // dynamic
//TODO: Instead of using the auto type, switch type for each part of the content
// - Current library doesn't support that.
//>:+B210AC>50270>6/$+2>5023080000582>6L
//[TYPE.CODE128B]+B210AC
//[TYPE.CODE128C]0270
//[TYPE.CODE128B]+/$+2
//[TYPE.CODE128C]023080000582
//[TYPE.CODE128B]L
}
else if (barcode.Mode == "A")
{
//A (automatic mode, the ZPL engine automatically determines the subsets that are used to encode the data)
barcodeType = TYPE.CODE128; // dynamic
}
else if (barcode.Mode == "D")
{
//D (UCC/EAN mode, field data must contain GS1 numbers)
barcodeType = TYPE.CODE128C;
content = content.Replace(">8", FNC1);
interpretation = interpretation.Replace(">8", "");

if (!content.StartsWith(FNC1))
{
content = FNC1 + content;
}
}
else if (barcode.Mode == "U")
{
//U (UCC case mode, field data must contain 19 digits)
barcodeType = TYPE.CODE128C;
content = content.PadLeft(19, '0').Substring(0, 19);
int checksum = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using BinaryKits.Zpl.Label.Elements;
using SkiaSharp;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using ZXing;
using ZXing.Datamatrix;
using ZXing.Datamatrix.Encoder;
Expand Down Expand Up @@ -29,11 +30,22 @@ public override void Draw(ZplElementBase element)
float x = dataMatrix.PositionX;
float y = dataMatrix.PositionY;

// support hand-rolled GS1
bool gs1Mode = false;
var content = dataMatrix.Content;
if (Regex.Match(content, @"(^_1)", RegexOptions.None).Success)
{
content = Regex.Replace(content, @"(^_1)", "");
gs1Mode = true;
}

var writer = new DataMatrixWriter();
var hints = new Dictionary<EncodeHintType, object> {
{ EncodeHintType.DATA_MATRIX_SHAPE, SymbolShapeHint.FORCE_SQUARE }
{ EncodeHintType.DATA_MATRIX_SHAPE, SymbolShapeHint.FORCE_SQUARE },
{ EncodeHintType.DATA_MATRIX_COMPACT, gs1Mode },
{ EncodeHintType.GS1_FORMAT, gs1Mode }
};
var result = writer.encode(dataMatrix.Content, BarcodeFormat.DATA_MATRIX, 0, 0, hints);
var result = writer.encode(content, BarcodeFormat.DATA_MATRIX, 0, 0, hints);

using var resizedImage = this.BitMatrixToSKBitmap(result, dataMatrix.Height);
{
Expand Down
35 changes: 22 additions & 13 deletions src/BinaryKits.Zpl.Viewer/ElementDrawers/PDF417ElementDrawer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,37 @@ public override void Draw(ZplElementBase element)
{
mincols = 1;
maxcols = 30;

if (pdf417.Rows != null)
{
//When column count isn't defined, and rows count is,
// the col/row ratio is calculated using the algorithm in: ZXing.PDF417.Internal.PDF417.determineDimensions
// to allow a range for that algorithm, we divide the given row amount by 2.
// as the algorithm goes from highest to lowest, this usually produces an acceptable result
minrows /= 2;
}
}
var writer = new PDF417Writer();
var hints = new Dictionary<EncodeHintType, object> {
{ EncodeHintType.CHARACTER_SET, "UTF-8" },
// { EncodeHintType.CHARACTER_SET, "ISO-8859-1" },
{ EncodeHintType.PDF417_COMPACT, pdf417.Compact },
//{ EncodeHintType.PDF417_AUTO_ECI, true },
//{ EncodeHintType.DISABLE_ECI, true },
{ EncodeHintType.PDF417_COMPACTION, Compaction.AUTO},
{ EncodeHintType.PDF417_ASPECT_RATIO, 3 }, // height of a single bar relative to width
{ EncodeHintType.PDF417_IMAGE_ASPECT_RATIO, 2.0f }, // zpl default, proportions of columns to rows
{ EncodeHintType.PDF417_ASPECT_RATIO, PDF417AspectRatio.A3 }, // height of a single bar relative to width
{ EncodeHintType.PDF417_IMAGE_ASPECT_RATIO, 1.0f }, // zpl default 2.0f, proportions of columns to rows //1.0f looks closer to printed Zebra label
{ EncodeHintType.MARGIN, 0 }, // its an int
{ EncodeHintType.ERROR_CORRECTION, ConvertErrorCorrection(pdf417.SecurityLevel) },
{ EncodeHintType.PDF417_DIMENSIONS, new Dimensions(mincols, maxcols, minrows, maxrows) },
};

var default_bitmatrix = writer.encode(pdf417.Content, BarcodeFormat.PDF_417, 0, 0, hints);

var upscaled = proportional_upscale(default_bitmatrix, 3);
var result = vertical_scale(upscaled, pdf417.Height);


//PDF417_ASPECT_RATIO set to 3, we need to multiply that with pdf417.ModuleWidth (defined by ^BY)
var bar_height = pdf417.ModuleWidth * 3;
var upscaled = proportional_upscale(default_bitmatrix, pdf417.ModuleWidth);
var result = vertical_scale(upscaled, pdf417.Height, bar_height);

using var resizedImage = this.BitMatrixToSKBitmap(result, 1);
{
var png = resizedImage.Encode(SKEncodedImageFormat.Png, 100).ToArray();
Expand Down Expand Up @@ -107,14 +118,12 @@ private BitMatrix proportional_upscale(BitMatrix old, int scale) {
return upscaled;
}

// needed to match labelary
// needed to match zebra and labelary
// zebra assumptions:
// - we can only set the height in zpl in points, not the width
// - each bar is 3 "points" thick, until ^BY is implemented
// - because we have PDF417_ASPECT_RATIO set to 3, and upscaling to 3, the height of a single bar is now 9
// we only need to scale to the bar height/9
private BitMatrix vertical_scale(BitMatrix old_matrix, int new_bar_height) {
int old_bar_height = 9;
// - each bar is ^BY "points" thick
// - because we have PDF417_ASPECT_RATIO set to 3, the height of a single bar is now 3 * ^BY
private BitMatrix vertical_scale(BitMatrix old_matrix, int new_bar_height, int old_bar_height) {
int width = old_matrix.Width;
int rows = old_matrix.Height / old_bar_height; // logical rows;

Expand Down
Loading
Loading