ZXing.net: How to access raw bytes (and overcome problem of embedded nulls)? - zxing

When trying to read a PDF417 barcode that contains embedded NULL characters:
// load image and convert to bitmap
Bitmap bmp = new Bitmap(Image.FromFile(#"C:\Sample.png"));
IBarcodeReader reader = new BarcodeReader();
ZXing.Result result = reader.Decode(bmp);
// do something with the result
String decodedString = result.Text;
The returned text is cut off when it encounters an embedded null character:
IDUS3*1GORRELL, LIDIA 9991001041 0060150RDBR1992OCT31NNYYUNONE NONE 2RRT 2011NOV042052OCT308 5RESRETUSAF AMN E1 I UNKÿØÿà
What i need is the RawBytes of the PDF417 barcode.
Other, hardware, decoders do return the entire string (since it is perfectly valid for a string to contain embedded NULL characters).
There is a:
Byte[] rawResult = result.RawBytes;
But RawBytes is always null - it doesn't mean what you think it means. It is not the RawBytes of the read barcode; it is the raw bytes from a QR code.
How can i get the raw bytes encoded on a PDF417 card?

Related

Decoding base64 string to image in flutter (Invalid character exception)

Basically I'm trying to convert a base64 jpeg image to normal image in flutter using
Image.memory(base64Decode(stringBase64))
the image initially used to be jp/2 format which isn't supported by flutter so i converted the jp/2 base64 string to bitmap in java and then to base64 string jpeg to be able to decode it in flutter using this code :
public static String encodeToBase64(Bitmap image)
{
Bitmap immagex=image;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
immagex.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] b = baos.toByteArray();
String imageEncoded = Base64.encodeToString(b, Base64.DEFAULT);
return imageEncoded;
}
how ever when i try to decode this base64 string in flutter i'm getting this error
Invalid character (at character 77)
/9j/4AAQSkZJRgABAQAAAQABAAD/4gIoSUNDX1BST0ZJTEUAAQEAAAIYAAAAAAIQAABtbnRyUkdC
which is pointing to the last C in the given line.
i don't seem understand where does the issue come from since i can convert my base64 string to image online but in flutter it throws that exception every time
thank you very much #Jamesdlin for the solution that was given in the comments
The issue was due to whitespace in the base64 string , solved by using
base64.decode(photoBase64.replaceAll(RegExp(r'\s'), '')),
If your URI contains data after the comma as it is defined by RFC-2397. Dart's Uri class is based on RFC-3986, so you can't use it.
Split the string by a comma and take the last part of it:
String uri = 'data:image/gif;base64,...';
Uint8List _bytes = base64.decode(uri.split(',').last);
REFERENCE: https://stackoverflow.com/a/59015116/12382178

How to get file extension from base64 String in Flutter | Dart

I have a base64 string of a document from api. I want to know which extension/file format is that. Because if it is in jpg/jpeg/png i want to show it in image widget. Or if it is in pdf format i want to show it in PdfView widget. So is there any way to get file extension from base64. Is there any package for it?
If you have a base64 string you can detect file type by checking the first character of your base64 string:
'/' means jpeg.
'i' means png.
'R' means gif.
'U' means webp.
'J' means PDF.
I wrote a function for that:
String getBase64FileExtension(String base64String) {
switch (base64String.characters.first) {
case '/':
return 'jpeg';
case 'i':
return 'png';
case 'R':
return 'gif';
case 'U':
return 'webp';
case 'J':
return 'pdf';
default:
return 'unknown';
}
}
If you don't have the original filename, there's no way to recover it. That's metadata that's not part of the file's content, and base64 encoding operates only on the file's content. It'd be best if you could save the original filename.
If you can't, you can use package:mime to guess the MIME type of the file from a small amount of binary data. You could decode the first n×4 characters from the base64 string (a valid base64 string must have a length that's a multiple of 4), decode it, and call lookupMimeType.
package:mime has a defaultMagicNumbersMaxLength value that you can use to compute n dynamically:
import 'dart:convert';
import 'package:mime/mime.dart' as mime;
String? guessMimeTypeFromBase64(String base64String) {
// Compute the minimum length of the base64 string we need to decode
// [mime.defaultMagicNumbersMaxLength] bytes. base64 encodes 3 bytes of
// binary data to 4 characters.
var minimumBase64Length = (mime.defaultMagicNumbersMaxLength / 3).ceil() * 4;
return mime.lookupMimeType(
'',
headerBytes: base64.decode(base64String.substring(0, minimumBase64Length)),
);
}
For the types that package:mime supports as of writing, mime.defaultMagicNumbersMaxLength is 12 (which translates to needing to decode the first 16 bytes from the base64 string).

Why is the base64 string not showing completely?

So this is my code
_image1 = File(pickedImage.path);
List<int> imageBytes = _image1.readAsBytesSync();
String base64Image = base64.encode(imageBytes);
_shcpImg = base64Image;
But when I print the string _shcpImg, it just prints a part of the string, because when I copy and paste that base64 into an online converter, it only shows a really tiny piece of the image. So the thing is that the string is not showing completely or somehow the base64 encoder is not working well.
Any suggestions?
From the comments, since you are using VsCode and you can't print the full string (long string)
You can use log from dart: developer,
if the string is REALLY LONG, There is a workaround to fix this, the idea is to divide your long string into small pieces (in the example, 800 length for each piece) using RegExp and then iterate into the result and print each piece.
void printWrapped(String text) {
final pattern = new RegExp('.{1,800}'); // 800 is the size of each chunk
pattern.allMatches(text).forEach((match) => print(match.group(0)));
}

Convert persian unicode to Ascii

I need to get the ASCII code of a Persian string to use it in a program. But the method below give the ? marks: "??? ????"
public string PerisanAscii()
{
//persian string
string unicodeString = "صبح بخیر";
// Create two different encodings.
Encoding ascii = Encoding.ASCII;
Encoding unicode = Encoding.Unicode;
// Convert the string into a byte array.
byte[] unicodeBytes = unicode.GetBytes(unicodeString);
// Perform the conversion from one encoding to the other.
byte[] asciiBytes = Encoding.Convert(unicode, ascii, unicodeBytes);
// Convert the new byte[] into a char[] and then into a string.
char[] asciiChars = new char[ascii.GetCharCount(asciiBytes, 0, asciiBytes.Length)];
ascii.GetChars(asciiBytes, 0, asciiBytes.Length, asciiChars, 0);
string asciiString = new string(asciiChars);
return asciiString;
}
Can you help me?
Best regards,
Mohsen
You can convert Persian UTF8 data to Windows-1256 (Arabic Windows):
var enc1256 = Encoding.GetEncoding("windows-1256");
var data = enc1256.GetBytes(unicodeString);
System.IO.File.WriteAllBytes(path, data);
ASCII does not support Persian. You may need old school Iran System encoding standard. This is determined by your Autocad application. I don't know if there is a direct Encoding in windows for it or not. But you can convert characters manually too. It's a simple mapping.

How to encode Chinese text in QR barcodes generated with iTextSharp?

I'm trying to draw QR barcodes in a PDF file using iTextSharp. If I'm using English text the barcodes are fine, they are decoded properly, but if I'm using Chinese text, the barcode is decoded as question marks. For example this character '测' (\u6D4B) is decoded as '?'. I tried all supported character sets, but none of them helped.
What combination of parameters should I use for the QR barcode in iTextSharp in order to encode correctly Chinese text?
iText and iTextSharp apparently don't natively support this but you can write some code to handle this on your own. The trick is to get the QR code parser to work with just an arbitrary byte array instead of a string. What's really nice is that the iTextSharp code is almost ready for this but doesn't expose the functionality. Unfortunately many of the required classes are sealed so you can't just subclass them, you'll have to recreate them. You can either download the entire source and add these changes or just create separate classes with the same names. (Please check over the license to make sure you are allowed to do this.) My changes below don't have any error correction so make sure you do that, too.
The first class that you'll need to recreate is iTextSharp.text.pdf.qrcode.BlockPair and the only change you'll need to make is to make the constructor public instead of internal. (You only need to do this if you are creating your own code and not modifying the existing code.)
The second class is iTextSharp.text.pdf.qrcode.Encoder. This is where we'll make the most changes. Add an overload to Append8BitBytes that looks like this:
static void Append8BitBytes(byte[] bytes, BitVector bits) {
for (int i = 0; i < bytes.Length; ++i) {
bits.AppendBits(bytes[i], 8);
}
}
The string version of this method converts text to a byte array and then uses the above so we're just cutting out the middle man. Next, add a new overload to the constructor that takes in a byte array instead of a string. We'll then just cut out the string detection part and force the system to byte-mode, otherwise the code below is pretty much the same.
public static void Encode(byte[] bytes, ErrorCorrectionLevel ecLevel, IDictionary<EncodeHintType, Object> hints, QRCode qrCode) {
String encoding = DEFAULT_BYTE_MODE_ENCODING;
// Step 1: Choose the mode (encoding).
Mode mode = Mode.BYTE;
// Step 2: Append "bytes" into "dataBits" in appropriate encoding.
BitVector dataBits = new BitVector();
Append8BitBytes(bytes, dataBits);
// Step 3: Initialize QR code that can contain "dataBits".
int numInputBytes = dataBits.SizeInBytes();
InitQRCode(numInputBytes, ecLevel, mode, qrCode);
// Step 4: Build another bit vector that contains header and data.
BitVector headerAndDataBits = new BitVector();
// Step 4.5: Append ECI message if applicable
if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.Equals(encoding)) {
CharacterSetECI eci = CharacterSetECI.GetCharacterSetECIByName(encoding);
if (eci != null) {
AppendECI(eci, headerAndDataBits);
}
}
AppendModeInfo(mode, headerAndDataBits);
int numLetters = dataBits.SizeInBytes();
AppendLengthInfo(numLetters, qrCode.GetVersion(), mode, headerAndDataBits);
headerAndDataBits.AppendBitVector(dataBits);
// Step 5: Terminate the bits properly.
TerminateBits(qrCode.GetNumDataBytes(), headerAndDataBits);
// Step 6: Interleave data bits with error correction code.
BitVector finalBits = new BitVector();
InterleaveWithECBytes(headerAndDataBits, qrCode.GetNumTotalBytes(), qrCode.GetNumDataBytes(),
qrCode.GetNumRSBlocks(), finalBits);
// Step 7: Choose the mask pattern and set to "qrCode".
ByteMatrix matrix = new ByteMatrix(qrCode.GetMatrixWidth(), qrCode.GetMatrixWidth());
qrCode.SetMaskPattern(ChooseMaskPattern(finalBits, qrCode.GetECLevel(), qrCode.GetVersion(),
matrix));
// Step 8. Build the matrix and set it to "qrCode".
MatrixUtil.BuildMatrix(finalBits, qrCode.GetECLevel(), qrCode.GetVersion(),
qrCode.GetMaskPattern(), matrix);
qrCode.SetMatrix(matrix);
// Step 9. Make sure we have a valid QR Code.
if (!qrCode.IsValid()) {
throw new WriterException("Invalid QR code: " + qrCode.ToString());
}
}
The third class is iTextSharp.text.pdf.qrcode.QRCodeWriter and once again we just need to add an overloaded Encode method supports a byte array and that calls are new constructor created above:
public ByteMatrix Encode(byte[] bytes, int width, int height, IDictionary<EncodeHintType, Object> hints) {
ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
if (hints != null && hints.ContainsKey(EncodeHintType.ERROR_CORRECTION))
errorCorrectionLevel = (ErrorCorrectionLevel)hints[EncodeHintType.ERROR_CORRECTION];
QRCode code = new QRCode();
Encoder.Encode(bytes, errorCorrectionLevel, hints, code);
return RenderResult(code, width, height);
}
The last class is iTextSharp.text.pdf.BarcodeQRCode which we once again add our new constructor overload:
public BarcodeQRCode(byte[] bytes, int width, int height, IDictionary<EncodeHintType, Object> hints) {
newCode.QRCodeWriter qc = new newCode.QRCodeWriter();
bm = qc.Encode(bytes, width, height, hints);
}
The last trick is to make sure when calling this that you include the byte order mark (BOM) so that decoders know to decode this properly, in this case UTF-8.
//Create an encoder that supports outputting a BOM
System.Text.Encoding enc = new System.Text.UTF8Encoding(true, true);
//Get the BOM
byte[] bom = enc.GetPreamble();
//Get the raw bytes for the string
byte[] bytes = enc.GetBytes("测");
//Combine the byte arrays
byte[] final = new byte[bom.Length + bytes.Length];
System.Buffer.BlockCopy(bom, 0, final, 0, bom.Length);
System.Buffer.BlockCopy(bytes, 0, final, bom.Length, bytes.Length);
//Create are barcode using our new constructor
var q = new BarcodeQRCode(final, 100, 100, null);
//Add it to the document
doc.Add(q.GetImage());
Looks like you may be out of luck. I tried too and got the same results as you did. Then looked at the Java API:
"*CHARACTER_SET the values are strings and can be Cp437, Shift_JIS and
ISO-8859-1 to ISO-8859-16. The default value is ISO-8859-1.*"
Lastly, looked at the iTextSharp BarcodeQRCode class source code to confirm that only those characters sets are supported. I'm by no means an authority on Unicode or encoding, but according to ISO/IEC 8859, the character sets above won't work for Chinese.
Essentially the same trick that Chris has done in his answer could be implemented by specifying UTF-8 charset in barcode hints.
var hints = new Dictionary<EncodeHintType, Object>() {{EncodeHintType.CHARACTER_SET, "UTF-8"}};
var q = new BarcodeQRCode("\u6D4B", 100, 100, hints);
If you want to be more safe, you can start your string with BOM character '\uFEFF', like Chris suggested, so it would be "\uFEFF\u6D4B".
UTF-8 is unfortunately not supported by QR codes specification, and there are a lot of discussions on this subject, but the fact is that most QR code readers will correctly read the code created by this method.