| | | 1 | | using QuestPDF.Fluent; |
| | | 2 | | using QuestPDF.Infrastructure; |
| | | 3 | | using SixLabors.ImageSharp.Formats.Png; |
| | | 4 | | using SixLabors.ImageSharp.PixelFormats; |
| | | 5 | | using ZXing; |
| | | 6 | | using ZXing.Common; |
| | | 7 | | using ISImage = SixLabors.ImageSharp.Image; |
| | | 8 | | |
| | | 9 | | namespace Services.LabelServices; |
| | | 10 | | |
| | | 11 | | public class BarcodeLabelService |
| | | 12 | | { |
| | | 13 | | static BarcodeLabelService() |
| | | 14 | | { |
| | 1 | 15 | | QuestPDF.Settings.License = LicenseType.Community; |
| | 1 | 16 | | } |
| | | 17 | | |
| | | 18 | | public byte[] GenerateLabelsPdf(IEnumerable<LabelRequest> labels) |
| | | 19 | | { |
| | 4 | 20 | | var labelList = labels.ToList(); |
| | | 21 | | |
| | 4 | 22 | | return Document.Create(container => |
| | 4 | 23 | | { |
| | 16 | 24 | | foreach (var label in labelList) |
| | 4 | 25 | | { |
| | 4 | 26 | | var barcodeValue = string.IsNullOrWhiteSpace(label.CleanedArticle) |
| | 4 | 27 | | ? "NOARTICLE" |
| | 4 | 28 | | : label.CleanedArticle; |
| | 4 | 29 | | |
| | 4 | 30 | | var barcodeBytes = GenerateBarcodeImage(barcodeValue); |
| | 4 | 31 | | |
| | 4 | 32 | | container.Page(page => |
| | 4 | 33 | | { |
| | 4 | 34 | | // 60 × 40 мм |
| | 4 | 35 | | page.Size(60, 40, Unit.Millimetre); |
| | 4 | 36 | | page.MarginHorizontal(2, Unit.Millimetre); |
| | 4 | 37 | | page.MarginTop(1, Unit.Millimetre); |
| | 4 | 38 | | page.MarginBottom(2, Unit.Millimetre); |
| | 4 | 39 | | |
| | 4 | 40 | | page.Content().Column(column => |
| | 4 | 41 | | { |
| | 4 | 42 | | // Три колонки одинаковой ширины: [пусто] | [штрихкод по центру] | [x N] |
| | 4 | 43 | | // Левый и правый item одинаковы → штрихкод строго по центру |
| | 4 | 44 | | column.Item().Row(row => |
| | 4 | 45 | | { |
| | 4 | 46 | | row.ConstantItem(10, Unit.Millimetre); // балансирующий спейсер |
| | 4 | 47 | | |
| | 4 | 48 | | row.RelativeItem() |
| | 4 | 49 | | .AlignCenter() |
| | 4 | 50 | | .Image(barcodeBytes) |
| | 4 | 51 | | .FitWidth(); |
| | 4 | 52 | | |
| | 4 | 53 | | row.ConstantItem(10, Unit.Millimetre) |
| | 4 | 54 | | .AlignCenter() |
| | 4 | 55 | | .AlignMiddle() |
| | 4 | 56 | | .Text("x " + label.Quantity) |
| | 4 | 57 | | .Bold() |
| | 4 | 58 | | .FontSize(9); |
| | 8 | 59 | | }); |
| | 4 | 60 | | |
| | 4 | 61 | | // Артикул — по центру |
| | 4 | 62 | | column.Item() |
| | 4 | 63 | | .AlignCenter() |
| | 4 | 64 | | .PaddingTop(1, Unit.Millimetre) |
| | 4 | 65 | | .Text(label.Article ?? string.Empty) |
| | 4 | 66 | | .Bold() |
| | 4 | 67 | | .FontSize(11); |
| | 4 | 68 | | |
| | 4 | 69 | | // Название товара — по центру |
| | 4 | 70 | | column.Item() |
| | 4 | 71 | | .AlignCenter() |
| | 4 | 72 | | .PaddingTop(1, Unit.Millimetre) |
| | 4 | 73 | | .Text(label.ProductName ?? string.Empty) |
| | 4 | 74 | | .FontSize(8) |
| | 4 | 75 | | .LineHeight(1.1f); |
| | 8 | 76 | | }); |
| | 8 | 77 | | }); |
| | 4 | 78 | | } |
| | 8 | 79 | | }).GeneratePdf(); |
| | | 80 | | } |
| | | 81 | | |
| | | 82 | | private byte[] GenerateBarcodeImage(string text) |
| | | 83 | | { |
| | 4 | 84 | | var writer = new BarcodeWriterPixelData |
| | 4 | 85 | | { |
| | 4 | 86 | | Format = BarcodeFormat.CODE_128, |
| | 4 | 87 | | Options = new EncodingOptions |
| | 4 | 88 | | { |
| | 4 | 89 | | Width = 620, |
| | 4 | 90 | | Height = 200, |
| | 4 | 91 | | Margin = 0, |
| | 4 | 92 | | PureBarcode = true |
| | 4 | 93 | | } |
| | 4 | 94 | | }; |
| | | 95 | | |
| | 4 | 96 | | var pixelData = writer.Write(text); |
| | | 97 | | |
| | 4 | 98 | | using var image = ISImage.LoadPixelData<Bgra32>(pixelData.Pixels, pixelData.Width, pixelData.Height); |
| | 4 | 99 | | using var ms = new MemoryStream(); |
| | 4 | 100 | | image.Save(ms, new PngEncoder()); |
| | 4 | 101 | | return ms.ToArray(); |
| | 4 | 102 | | } |
| | | 103 | | } |