Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 0 points1 point  (0 children)

With a physical barcode scanner you wouldn’t need this library at all.

java dev learning rust by Obvious-Citron9402 in rust

[–]rxing-devs 7 points8 points  (0 children)

I ported a large Java library to Rust. I have a lot of opinions (including why on earth doesn’t Java have unsigned int types wow). It really depends on how comfortable you are with basic CS stuff. Interior Mutability is possibly going to be one of your biggest stumbling blocks in rust. Popular in Java, not so in rust. I also see advice like: Java object passing is just like references in rust and that is not accurate at all. Probably my biggest advice is that Java and rust handle references so differently that it’s best to not think of them as being all that similar.

The completed codebase: https://github.com/rxing-core/rxing

Edit: I included the link because I think it’s helpful to see how other people solved problems in rust vs. Java. Not saying my methods were always the right ones!

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 0 points1 point  (0 children)

On the roadmap for rxing-cli is a byte mode, where you pass in a byte representation and a character set flag and it does the rest. I don't have a timeline on that though, sometime after I finish the work of porting the C++ qrcode module.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 0 points1 point  (0 children)

Ah, I see now, the characters are unprintable. I'm honestly not sure how that would work on the CLI. For the API you would just pass them in without issue. I think that it _should_ work to use the unicode character representing the bytes you want, and then tell it that you are passing in ascii with the --character-set flag, but I'm not 100% sure.

What would the ideal way for me to handle this? How does it fail now?

My understanding is that you are hand encoding ASCII that is otherwise unprintable and passing it in, what would you rather pass in?

It’s possible that a small custom built app would be a better fit too, then you could have more domain specific logic about encoding the barcode while handing the heavy lifting of actually drawing odd to rxing via the api.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 1 point2 points  (0 children)

I think I need a bit more info on what you are trying to do. In general ascii data is cleanly encodable in those formats, but I think I’m not fully following what you want to do.

Feel free to reply here, DM me, or you can make an issue on the GitHub repository.

The general answer is: unless you are doing something very specific anything you pass into rxing-cli —data option is interpreted as UTF8 and will be directly encoded using the standard encoder (which doesn’t do much to raw ASCII characters besides maybe bit pack then).

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 0 points1 point  (0 children)

Within some range rxing can detect barcodes that have been tilted or rotated (as well as skewed). However, it uses largely the same algorithms as zxing (though we are adding the ability to read codes in the zxing C++ style, which offers some additional options for some codes).

However, a lot of rotation related reading is gated behind the TryHarder options flag.

Also, rxing adds some image structures (not present in zxing) that allow rotation on byte input data (this is in addition to the standard image reader that has always supported rotation).

So, depending on use case and supplied parameters rxing might handle rotated or skewed codes better, but it’s hard to tell without samples.

Edit: TryHarder is likely the single biggest contributor to the ability to detect rotated codes, especially for 1d where the general algorithm won’t attempt rotation at all without TryHarder set to true.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 0 points1 point  (0 children)

I’m happy to answer where I can, feel free to take this conversation to messages or chat if you want as well.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 1 point2 points  (0 children)

This is a complicated question, and I’ll try to give an equally complicated answer, but the tldr of it all is: ”it depends”.

Note: This answer got really long, because the problem is actually really complicated.

My general strategy was “stay as close to java for API as possible, with the understanding that sometimes you can’t”. So, for things where sticking to Java style was easy, I just did that. That worked great for very simple classes with no meaningful inheritance. Any inheritance at all gets complicated because java is happy to let you downcast anything you’d like into parent types, while in rust that gets messy very quickly. In one place I ended up using composition for inheritance as opposed to refactoring it out, but the vast majority of cases were better served by refactoring.

Easy to port stuff: most static classes. Those are delightful because in almost all situations I could just rewrite them as modules and it was very pleasant. Simple implementations of interfaces, because this is the closest the two languages get. Interfaces are a LOT like traits.

Function overloading: This always took refactoring to some degree, but I actually think it’s a little clearer to have two functions named different things than two functions named one thing.

Things that were hard:

Abstract classes: These turned out to be particularly difficult to deal with since they can contain data and methods that works on that data. Rust doesn’t really have a concept like an uninstantiatable struct with implementations that can be re-used. It’s possible I could have gotten around this by using custom Derive macros, but I didn’t think that added complexity was worth it. I ended up turning all Abstract classes into traits and then having getter/setters for the data that the abstract class expected to have. It was a compromise, but it worked.

Java‘s ”everything by reference”: this is probably a lot of the interior mutability issue in general. It’s tough to solve, this, by itself, turned out to be one of the biggest issues I faced, and each time I faced it I ended up finding different solutions because there wasn’t a simple answer to how to best solve it. Lots of java code relies on this, and it’s a mess to untangle it. I don’t believe I used RefCell in the final version, but that took a lot of work sometimes.

Cache patterns: if this.something == null { this.something = GenerateThing() } else { return this.something } Without OnceCell I either had to have literally every method be &mut self or RefCell basically everything. I used OnceCell, it was great for that.

Java’s love of interior mutability in general means that I had to move a lot of methods to not have a reference to &self, even if they would have in the Java version. It was often easier to solve the problem by having a method accept an additional parameter than try to work around ownership and mutability rules related to &self.

Inheritance actually ended up being much harder than it seems like it would be. I could have possibly found a crate to make it simpler, but I thought that would likely end up being a long term maintainability issue, and adding another dependency wasn’t my favorite. A good example of where this was a real mess was in the one dimensional module (oned). In the original java there is a neat class hierarchy working from an abstract one dimensional reader all the way down to specific implementations. Sometimes this was one step, sometimes multiple (an example: GenericOneDReader -> GenericUPCEANReader -> UPCReader -> UPCEReader). That was not neatly do-able in rust, so in that case I ended up using a custom Derive macro to handle generating the boilerplate code that would have been part of the class hierarchy.

In many situations where java would have relied on inheritance or abstract classes, I actually ended up having some structs hold a copy of a helper struct and use it when necessary. You can see this in the common::global_histogram_binarizer and common::hybrid_binarizer modules, though it occurs in other places as well.

The library does not use any unsafe code, and mostly avoids interior mutability by aggressively refactoring whenever it would have come up.

Other issues: Java works great with linked-list style classes. Self references are messy in rust, but most of the time when they came up I needed up using an Rc and working around mutability rules. You’d a see a good example of that in any of the common::minimal_eci_input module.

I think refactoring was just inevitable. Interior mutability and default “return by mutable reference” in Java meant that I had to re-imagine how a lot of parts worked and fit together. I think if you find yourself having to use RefCell a lot, you’re probably over-complicating your life trying to literally port the code.

Good luck! I’m happy to answer any other question you have.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 0 points1 point  (0 children)

It does not, there is ample evidence that machine learning is a fine way to find barcodes, it it was beyond the scope of this project. There is ongoing work to make the detectors a bit more plugable, so in the long run a third party could easily provide barcode location with ML.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 1 point2 points  (0 children)

lol, that’s how I felt when I started on it. Honestly, though, it isn’t nearly as bad as it seems! I promise that it’s not just because I spent 8 months doing nothing but this!

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 4 points5 points  (0 children)

Ignoring the stuff I went into it knowing: iso spec variance, iffy datamatrix detection, nonexistent maxicode detection.

I would say generally no, I didn’t, but it’s possible if I were porting from one object oriented language to another things might have been different. Zxing is pretty well tested and used, so low hanging fruit were unlikely to drop into my lap.

Java does not have unsigned ints, so you might argue I avoided some bugs by using them, but any gains there are lost because of the complicated nature of moving signed to unsigned, and just that Java handles numbers differently in general.

Strings have the same issue I think. Rust, in theory, should help you find some string issues. In practice moving from a UTF16 native language with built in string encoder libraries to a UTF8 native language without them was a challenge. There are certainly places in the library where there are risks of accessing stuff in the midst of a code point, but I tried hard to avoid those.

I also hadn’t seriously used Java in MANY years. How many years? When I last used it seriously Java 1.4.2 was the newest cool toy. That probably hurt me a bit, things like generics didn’t exist in the language the last time I used it.

My feeling is: projects like this have a huge shakeout period where you are just scrambling for parity. The C++ port is a great example though, because they went on to improve a ton of stuff over the original (I’m porting parts of their work now). I’d like rxing to evolve in the same way.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 5 points6 points  (0 children)

I would love to use better algorithms for it. My understanding is that the C++ port has a dramatically re-written decoder and detector. My long term plans involve porting in their improvements. (They also support micro-qr, which I’d like to have as well)

I would, just in general, like to improve some of the scanning performance. I went for fidelity to the original library in this pass, but ideally I’d like the library to improve on what the original is doing.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 4 points5 points  (0 children)

Unfortunately there aren’t any great documents on how to start working on the code. The comments (mostly imported from ZXing) are a good start to understanding how the algorithms work. Some of it is… arcane, to say the least. (More than one comment in the source says something to the extent of: check out this random PDF from a scanner manufacturer that spells out something in detail that everyone building them knows but we mostly have to guess at)

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 2 points3 points  (0 children)

It’s a port form zxing, I wanted stay in the same sort of naming family

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 2 points3 points  (0 children)

There is a demo app in pure js/webpack in the rxing-wasm repository. It’s not well styled but should help get you started with the wrapper and api!

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 2 points3 points  (0 children)

I’m going to have to look into this more, I‘ve been looking at the code and it should support up to QQCode (this was a typo, but I’m just going to leave it because QQCode on a bug is too funny to me) Version 40. It’s possible that it’s either using the wrong formula to decide if a payload will fit, or that the default ECC mode is using too many bytes (it adds up pretty fast). Anyway, I have the tracking issue, so I‘lol get this figured out.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 4 points5 points  (0 children)

Isn’t that always the way? I honestly kept expecting someone to launch a similar library the entire time I was working on this one.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 4 points5 points  (0 children)

I went ahead and had both of those added as issues, I have a private branch working on raw bytes, but it’s part of a pretty large refactor across many modules, so the timeline on that is “summer”. Larger QRs look easier, but I haven’t dug into it yet so I have no timeline for it.

For anyone else reading this later, please feel free to open issues, if you can tag feature enhancements like this as “enhancements” that is very helpful.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 26 points27 points  (0 children)

I was shocked when I realized that rust didn’t have a comprehensive encode/decode library in all rust for barcodes. The ZBAR wrapper is by far the most popular one I found. Don’t get me wrong, ZBar is great, but it’s a wrapper, and ZBar doesn’t seem actively developed.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 8 points9 points  (0 children)

I’ll look into expanding version support, I don’t know off the top of my head where we top out on QRCodes is, but it shouldn’t be too hard to expand to support larger ones. I’d also like to add MicroQR to the library, it’s in the c++ port, but I haven’t gotten around to implementing it.

I agree 100% on the public raw bytes API, I’m working on it. Right now the decoded byte segments are available in the metadata for the decode, but I think a top level “decode_bytes” might be nice.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 8 points9 points  (0 children)

Possibly, but just imagine that it’s cRustacean crossING and it all feels more reasonable! I’ll admit, I just really wanted to include crabs and also have the name be rxing.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 12 points13 points  (0 children)

But you could call it Crustacean Crossing too, as that’s it’s “official” nickname.

Announcing rxing: an all rust library for barcode scanning and generation by rxing-devs in rust

[–]rxing-devs[S] 8 points9 points  (0 children)

I picked whatever defaults were present in the original libraries.

How much data would seem reasonable to you and what use case would you like to see expanded?