ZXing.Net icon indicating copy to clipboard operation
ZXing.Net copied to clipboard

Support for Latin-1 in BARCODE-128

Open iakovmarkov opened this issue 1 year ago • 4 comments

Hello,

we've been using this library to create barcodes in our project. It works well, but we've stumbled upon one limitation - the library explicitly does not support full Latin-1 charset. Some of our clients in scnadinavia use the special characters, and our app can't give them a nice PDF with a readable barcode.

Are there plans to add support for full Latin-1 charset? Maybe there is a workaround we can use?

Appreciate your answer and your work on the library!

iakovmarkov avatar Mar 22 '24 14:03 iakovmarkov

Can you please post a code snippet which shows how you generate the code?

micjahn avatar Apr 02 '24 05:04 micjahn

Sure thing. This is the whole method that receives user input and returns a byte array with the barcode:

    private static byte[] GetBarCode(string contents)
    {
        byte[] byteArray;
        var codeWriter = new ZXing.BarcodeWriterPixelData
        {
            Format = ZXing.BarcodeFormat.CODE_128,
            Options = new QrCodeEncodingOptions
            {
                Width = 140,
                Height = 40,
                Margin = 0,
                NoPadding = true
            }
        };

        var pixelData = codeWriter.Write(contents);
        var bitmap = new SKBitmap();
        var gcHandle = GCHandle.Alloc(pixelData.Pixels, GCHandleType.Pinned);
        var info = new SKImageInfo(pixelData.Width, pixelData.Height, SKImageInfo.PlatformColorType, SKAlphaType.Unpremul);
        bitmap.InstallPixels(info, gcHandle.AddrOfPinnedObject(), info.RowBytes, delegate { gcHandle.Free(); }, null);

        using var image = SKImage.FromBitmap(bitmap);
        using var data = image.Encode(SKEncodedImageFormat.Png, 100);
        using var writeStream = new MemoryStream();
        data.SaveTo(writeStream);
        byteArray = writeStream.ToArray();

        return byteArray;
    }

iakovmarkov avatar Apr 02 '24 09:04 iakovmarkov

At the moment ZXing.Net doesn't support Latin-1 without manually switching to the FNC4 mode. I fear I will not find enough free time to add that function. That means you can only use the following "hack". I tested the code only with the german characters 'ä' and 'Ä'. Perhaps it doesn't work for all Latin-1 characters. ZXing.Net decodes it successfully. But be aware of the perhaps not all readers support FNC4.

        private static byte[] GetBarCode(string contents)
        {
            byte[] byteArray;
            var codeWriter = new ZXing.BarcodeWriterPixelData
            {
                Format = ZXing.BarcodeFormat.CODE_128,
                Options = new ZXing.OneD.Code128EncodingOptions
                {
                    Width = 140,
                    Height = 40,
                    Margin = 0,
                    NoPadding = true
                },
                Renderer = new PixelDataRendererWrapper()
            };
        
            var pixelData = codeWriter.Write(AddFNC4(contents));
            var bitmap = new SKBitmap();
            var gcHandle = GCHandle.Alloc(pixelData.Pixels, GCHandleType.Pinned);
            var info = new SKImageInfo(pixelData.Width, pixelData.Height, SKImageInfo.PlatformColorType, SKAlphaType.Unpremul);
            bitmap.InstallPixels(info, gcHandle.AddrOfPinnedObject(), info.RowBytes, delegate { gcHandle.Free(); }, null);
        
            using var image = SKImage.FromBitmap(bitmap);
            using var data = image.Encode(SKEncodedImageFormat.Png, 100);
            using var writeStream = new MemoryStream();
            data.SaveTo(writeStream);
            byteArray = writeStream.ToArray();
        
            return byteArray;
        }

        private string AddFNC4(string content)
        {
            // zxing doesn't add the FNC4 automatically at the moment
            // it has to be added manually
            var builder = new System.Text.StringBuilder(content.Length);
            foreach (var ch in content)
            {
                if (ch > 128)
                {
                    builder.Append('\u00f4');
                    builder.Append((char)(ch - 128));
                }
                else
                {
                    builder.Append(ch);
                }
            }
            return builder.ToString();
        }

        public class PixelDataRendererWrapper : ZXing.Rendering.IBarcodeRenderer<ZXing.Rendering.PixelData>
        {
            private ZXing.Rendering.PixelDataRenderer renderer = new ZXing.Rendering.PixelDataRenderer();

            public PixelData Render(BitMatrix matrix, BarcodeFormat format, string content)
            {
                return renderer.Render(matrix, format, ReplaceFNC4(content));
            }

            public PixelData Render(BitMatrix matrix, BarcodeFormat format, string content, EncodingOptions options)
            {
                return renderer.Render(matrix, format, ReplaceFNC4(content), options);
            }

            private string ReplaceFNC4(string content)
            {
                var builder = new System.Text.StringBuilder(content.Length);
                var fnc4Found = false;
                foreach (var ch in content)
                {
                    if (ch == '\u00f4')
                        fnc4Found = true;
                    else
                    {
                        if (fnc4Found)
                        {
                            builder.Append((char)(ch + 128));
                            fnc4Found = false;
                        }
                        else
                        {
                            builder.Append(ch);
                        }
                    }
                }
                return builder.ToString();
            }
        }

micjahn avatar Apr 05 '24 06:04 micjahn

Thanks a lot @micjahn, appreciate your workaround! It'll help us a lot. I suppose you can close this issue if you think it's appropriate.

iakovmarkov avatar Apr 05 '24 12:04 iakovmarkov