Skip to content

Commit

Permalink
Merge pull request #190 from reportingissue/pdf417
Browse files Browse the repository at this point in the history
Add Pdf417 barcode support
  • Loading branch information
YipingRuan authored Sep 18, 2023
2 parents 6434c62 + eeec00b commit c6f675b
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 0 deletions.
81 changes: 81 additions & 0 deletions src/BinaryKits.Zpl.Label/Elements/ZplPDF417.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Collections.Generic;

namespace BinaryKits.Zpl.Label.Elements
{
/// <summary>
/// PDF417 Barcode ^B7o,h,s,c,r,t
/// </summary>
public class ZplPDF417 : ZplPositionedElementBase, IFormatElement
{

public int Height { get; protected set; }
public string Content { get; protected set; }
public FieldOrientation FieldOrientation { get; protected set; }
public int? Columns { get; protected set; }
public int? Rows { get; protected set; }
public bool Compact { get; protected set; }
public int SecurityLevel { get; protected set; }

/// <summary>
/// Zpl PDF417 barcode
/// </summary>
/// <param name="content"></param>
/// <param name="positionX"></param>
/// <param name="positionY"></param>
/// <param name="height"></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>
/// <param name="fieldOrientation"></param>
/// <param name="securityLevel">1-8 This determines the number of error detection and correction code-words to be generated for the symbol.The default level (0) provides only error detection without correction.Increasing the security level adds increasing levels of error correction and increases the symbol size.</param>
/// <param name="bottomToTop"></param>
public ZplPDF417(
string content,
int positionX,
int positionY,
int height = 8,
int? columns = null,
int? rows = null,
bool compact = false,
int securityLevel = 0,
FieldOrientation fieldOrientation = FieldOrientation.Normal,
bool bottomToTop = false
)
: base(positionX, positionY, bottomToTop)
{
FieldOrientation = fieldOrientation;
Height = height;
Columns = columns;
Rows = rows;
Compact = compact;
SecurityLevel = securityLevel;
Content = content;
}


protected string RenderFieldOrientation()
{
return RenderFieldOrientation(FieldOrientation);
}

///<inheritdoc/>
public override IEnumerable<string> Render(ZplRenderOptions context)
{
//^ FO100,100
//^ BQN,2,10
//^ FDMM,AAC - 42 ^ FS
var result = new List<string>();
result.AddRange(RenderPosition(context));
result.Add($"^BX{RenderFieldOrientation()},{context.Scale(Height)}");
result.Add($"^FD{Content}^FS");

return result;
}

/// <inheritdoc />
public void SetTemplateContent(string content)
{
Content = content;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ public override ZplElementBase Analyze(string zplCommand)
// N.B.: always pass Field Orientation Normal to QR codes; the ZPL II standard does not allow rotation
return new ZplQrCode(parsedText, x, y, qrCode.Model, qrCode.MagnificationFactor, errorCorrection, qrCode.MaskValue, Label.FieldOrientation.Normal, bottomToTop);
}
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);
}
}

var font = this.GetFontFromVirtualPrinter();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using BinaryKits.Zpl.Label;
using BinaryKits.Zpl.Label.Elements;
using BinaryKits.Zpl.Viewer.Models;
using System;

namespace BinaryKits.Zpl.Viewer.CommandAnalyzers
{
public class PDF417ZplCommandAnalyzer : ZplCommandAnalyzerBase
{
public PDF417ZplCommandAnalyzer(VirtualPrinter virtualPrinter) : base("^B7", virtualPrinter) { }
///<inheritdoc/>
public override ZplElementBase Analyze(string zplCommand)
{
var zplDataParts = this.SplitCommand(zplCommand);

// reusable buffer
int tmpint;

/*
* Format: ^B7o,h,s,c,r,t, parse order:
* orientation
* height
* security level
* columns
* rows
* compact
*/

var fieldOrientation = this.ConvertFieldOrientation(zplDataParts[0]);

int height = this.VirtualPrinter.BarcodeInfo.Height;
if (zplDataParts.Length > 1 && int.TryParse(zplDataParts[1], out tmpint))
{
height = tmpint;
}

int securityLevel = 0;
if (zplDataParts.Length > 2 && int.TryParse(zplDataParts[2], out tmpint))
{
securityLevel = tmpint;
}

int? columns = null;
if (zplDataParts.Length > 3 && int.TryParse(zplDataParts[3], out tmpint))
{
columns = tmpint;
}

int? rows = null;
if (zplDataParts.Length > 4 && int.TryParse(zplDataParts[4], out tmpint))
{
rows = tmpint;
}

bool compact = false;
if (zplDataParts.Length > 5)
{
compact = ConvertBoolean(zplDataParts[5]);
}

//The field data are processing in the FieldDataZplCommandAnalyzer
this.VirtualPrinter.SetNextElementFieldData(new PDF417FieldData
{
FieldOrientation = fieldOrientation,
Height = height,
SecurityLevel = securityLevel,
Columns = columns,
Rows = rows,
Compact = compact
});

return null;
}
}
}
161 changes: 161 additions & 0 deletions src/BinaryKits.Zpl.Viewer/ElementDrawers/PDF417ElementDrawer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
using BinaryKits.Zpl.Label;
using BinaryKits.Zpl.Label.Elements;
using SkiaSharp;
using System;
using System.Collections.Generic;
using ZXing;
using ZXing.Common;
using ZXing.PDF417;
using ZXing.PDF417.Internal;

namespace BinaryKits.Zpl.Viewer.ElementDrawers
{
/// <summary>
/// Drawer for PDF417 Barcode elements
/// </summary>
public class Pdf417ElementDrawer : BarcodeDrawerBase
{
///<inheritdoc/>
public override bool CanDraw(ZplElementBase element)
{
return element is ZplPDF417;
}

///<inheritdoc/>
public override void Draw(ZplElementBase element)
{
if (element is ZplPDF417 pdf417)
{
if (pdf417.Height == 0)
throw new System.Exception("PDF417 Height is set to zero.");

if (string.IsNullOrWhiteSpace(pdf417.Content))
throw new System.Exception("PDF147 Content is empty.");

float x = pdf417.PositionX;
float y = pdf417.PositionY;

int mincols, maxcols, minrows, maxrows;
if (pdf417.Rows != null)
{
minrows = pdf417.Rows.Value;
maxrows = pdf417.Rows.Value;
}
else
{
minrows = 3;
maxrows = 90;
}

if (pdf417.Columns != null)
{
mincols = pdf417.Columns.Value;
maxcols = pdf417.Columns.Value;
}
else
{
mincols = 1;
maxcols = 30;
}
var writer = new PDF417Writer();
var hints = new Dictionary<EncodeHintType, object> {
{ EncodeHintType.CHARACTER_SET, "UTF-8" },
{ 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.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);

using var resizedImage = this.BitMatrixToSKBitmap(result, 1);
{
var png = resizedImage.Encode(SKEncodedImageFormat.Png, 100).ToArray();
this.DrawBarcode(png, resizedImage.Height, resizedImage.Width, pdf417.FieldOrigin != null, x, y, 0, pdf417.FieldOrientation);
}
}
}

// bitmatrix scaling instead of bitmap
private BitMatrix proportional_upscale(BitMatrix old, int scale) {
if (scale == 0 || scale == 1)
{
return old;
}
BitMatrix upscaled = new BitMatrix(old.Width * scale, old.Height * scale);
for (int i = 0; i < old.Height; i++)
{
BitArray old_row = old.getRow(i, null);
for (int j = 0; j < old.Width; j++)
{
bool is_set = old_row[j];
if (!is_set)
{
continue;
}
upscaled.setRegion(j * scale, i * scale, scale, scale);
}
}
return upscaled;
}

// needed to match 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;
int width = old_matrix.Width;
int rows = old_matrix.Height / old_bar_height; // logical rows;

if (new_bar_height == old_bar_height || new_bar_height == 0)
{
return old_matrix;
}

BitMatrix scaled = new BitMatrix(old_matrix.Width, rows * new_bar_height);

for (int i = 0; i < rows; i++)
{
BitArray old_row = old_matrix.getRow(i * old_bar_height, null);
for (int j = 0; j < width; j++)
{
bool is_set = old_row[j];
if (!is_set)
{
continue;
}
scaled.setRegion(j, i * new_bar_height, 1, new_bar_height);
}
}
return scaled;
}

private PDF417ErrorCorrectionLevel ConvertErrorCorrection(int correction)
{
return correction switch
{
0 => PDF417ErrorCorrectionLevel.L0,
1 => PDF417ErrorCorrectionLevel.L1,
2 => PDF417ErrorCorrectionLevel.L2,
3 => PDF417ErrorCorrectionLevel.L3,
4 => PDF417ErrorCorrectionLevel.L4,
5 => PDF417ErrorCorrectionLevel.L5,
6 => PDF417ErrorCorrectionLevel.L6,
7 => PDF417ErrorCorrectionLevel.L7,
8 => PDF417ErrorCorrectionLevel.L8,
_ => PDF417ErrorCorrectionLevel.AUTO,
};
}
}
}
14 changes: 14 additions & 0 deletions src/BinaryKits.Zpl.Viewer/Models/PDF417FieldData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using BinaryKits.Zpl.Label;

namespace BinaryKits.Zpl.Viewer.Models
{
public class PDF417FieldData : FieldDataBase
{
public int Height { get; set; }
public FieldOrientation FieldOrientation { get; set; }
public int? Columns { get; set; }
public int? Rows { get; set; }
public bool Compact { get; set; }
public int SecurityLevel { get; set; }
}
}
1 change: 1 addition & 0 deletions src/BinaryKits.Zpl.Viewer/ZplAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public AnalyzeInfo Analyze(string zplData)
new ImageMoveZplCommandAnalyzer(this._virtualPrinter),
new LabelHomeZplCommandAnalyzer(this._virtualPrinter),
new QrCodeBarcodeZplCommandAnalyzer(this._virtualPrinter),
new PDF417ZplCommandAnalyzer(this._virtualPrinter),
new RecallFormatCommandAnalyzer(this._virtualPrinter),
new RecallGraphicZplCommandAnalyzer(this._virtualPrinter),
new ScalableBitmappedFontZplCommandAnalyzer(this._virtualPrinter),
Expand Down
1 change: 1 addition & 0 deletions src/BinaryKits.Zpl.Viewer/ZplElementDrawer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public ZplElementDrawer(IPrinterStorage printerStorage, DrawerOptions drawerOpti
new Interleaved2of5BarcodeDrawer(),
new ImageMoveElementDrawer(),
new QrCodeElementDrawer(),
new Pdf417ElementDrawer(),
new RecallGraphicElementDrawer(),
new TextFieldElementDrawer()
};
Expand Down

0 comments on commit c6f675b

Please sign in to comment.