|
|
|
|
@ -0,0 +1,173 @@
|
|
|
|
|
### Porting Summary: Java → C# QR Code Generator
|
|
|
|
|
|
|
|
|
|
#### Scope
|
|
|
|
|
- Ported the core QR Code generator from the Java implementation (`java/src/main/java/io/nayuki/qrcodegen/`) to C#.
|
|
|
|
|
- Implemented a class library `QrCodeGen` and a minimal console demo `QrCodeGen.Demo`.
|
|
|
|
|
|
|
|
|
|
#### Goals
|
|
|
|
|
- Maintain functional parity with the Java reference (encoding pipeline, EC levels, masking, penalties, Reed–Solomon ECC, layout drawing).
|
|
|
|
|
- Provide idiomatic and safe C# while preserving algorithmic behavior and outputs.
|
|
|
|
|
|
|
|
|
|
#### Note
|
|
|
|
|
- This PR is 100% generated by JetBrains Junie Pro AI code assistant.
|
|
|
|
|
- QR Code Generate Demo console app is a minimal port of the Java demo and working.
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Repository Layout (Relevant to this PR)
|
|
|
|
|
- `csharp/QR-Code-generator.sln` — Solution file containing two projects.
|
|
|
|
|
- `csharp/QrCodeGen/` — Class library (C# port of the Java library):
|
|
|
|
|
- `BitBuffer.cs`
|
|
|
|
|
- `DataTooLongException.cs`
|
|
|
|
|
- `QrSegment.cs`
|
|
|
|
|
- `QrSegmentAdvanced.cs` (simplified vs. Java; see Feature Parity section)
|
|
|
|
|
- `QrCode.cs`
|
|
|
|
|
- `csharp/QrCodeGen.Demo/` — Console demo app (depends on `QrCodeGen`).
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Class-by-Class Mapping
|
|
|
|
|
- Java `BitBuffer` → C# `BitBuffer`
|
|
|
|
|
- API preserved: append bits, append buffer, length, get bit, cloning.
|
|
|
|
|
- Implementation uses `List<bool>` and explicit bounds checks like Java’s `BitSet`/length pair.
|
|
|
|
|
|
|
|
|
|
- Java `DataTooLongException` → C# `DataTooLongException`
|
|
|
|
|
- Extends `ArgumentException` (close analog to Java’s `IllegalArgumentException`).
|
|
|
|
|
|
|
|
|
|
- Java `QrSegment` → C# `QrSegment`
|
|
|
|
|
- Static factories: `MakeBytes`, `MakeNumeric`, `MakeAlphanumeric`, `MakeEci`.
|
|
|
|
|
- Helpers: `IsNumeric`, `IsAlphanumeric` via regexes mirroring Java patterns.
|
|
|
|
|
- Mode enum and character-count bits mapping provided via extension methods.
|
|
|
|
|
- `GetTotalBits()` faithfully ported.
|
|
|
|
|
|
|
|
|
|
- Java `QrSegmentAdvanced` → C# `QrSegmentAdvanced`
|
|
|
|
|
- Included as an optional module. Current implementation mirrors the default segmentation path (numeric/alphanumeric/byte) and returns reasonably optimal segments for common inputs, but does not implement full DP-based optimal segmentation and Kanji mode. See Feature Parity and Future Work.
|
|
|
|
|
|
|
|
|
|
- Java `QrCode` → C# `QrCode`
|
|
|
|
|
- High-level APIs: `EncodeText`, `EncodeBinary`.
|
|
|
|
|
- Mid-level API: `EncodeSegments` with `minVersion`, `maxVersion`, `mask`, and `boostEcl` parameters.
|
|
|
|
|
- Constructor path builds function patterns, computes ECC, interleaves, draws codewords, applies mask, and computes penalties identically.
|
|
|
|
|
- Tables `ECC_CODEWORDS_PER_BLOCK` and `NUM_ERROR_CORRECTION_BLOCKS` ported exactly. Signed `sbyte` used to allow `-1` sentinels.
|
|
|
|
|
- All penalty and mask rules implemented verbatim.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Feature Parity
|
|
|
|
|
- Achieved parity:
|
|
|
|
|
- Version selection by capacity.
|
|
|
|
|
- Error correction level boosting (when it doesn’t increase version).
|
|
|
|
|
- Data stream building, terminator and padding, interleaving across ECC blocks.
|
|
|
|
|
- Drawing: finder, timing, alignment patterns; format and version information.
|
|
|
|
|
- Mask application and penalty scoring (N1–N4 rules) with identical logic.
|
|
|
|
|
- Reed–Solomon divisor/remainder algorithms and multiplication over GF(2^8/0x11D).
|
|
|
|
|
|
|
|
|
|
- Intentional differences / limitations:
|
|
|
|
|
- `QrSegmentAdvanced` does not implement:
|
|
|
|
|
- Full dynamic-programming optimal segmentation across all modes.
|
|
|
|
|
- Kanji segment encoding.
|
|
|
|
|
- The default path still matches Java’s `QrSegment.makeSegments()` selection (numeric/alphanumeric/byte) and therefore preserves functional outputs for typical text.
|
|
|
|
|
|
|
|
|
|
- Behavioral equivalence expectations:
|
|
|
|
|
- For inputs limited to numeric/alphanumeric/byte, outputs should match Java for the same chosen mask (or mask-agnostic when mask=-1) and ECL.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Notable C#-Specific Decisions
|
|
|
|
|
- `sbyte[][]` tables for ECC metadata to accommodate `-1` sentinel (Java byte is signed; C# byte is unsigned).
|
|
|
|
|
- Enum helpers via extension methods for mapping to mode bits and character-count bits.
|
|
|
|
|
- `BitBuffer` cloning implemented to maintain immutability of exposed data as in Java.
|
|
|
|
|
- Exceptions aligned to .NET conventions (e.g., `ArgumentException`, `ArgumentOutOfRangeException`).
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Build and Run Instructions
|
|
|
|
|
- Target frameworks: `net8.0` (both projects). If your machine only has .NET 9, either install .NET 8 or retarget both projects to `net9.0`.
|
|
|
|
|
|
|
|
|
|
Commands from repo root:
|
|
|
|
|
```sh
|
|
|
|
|
dotnet restore csharp/QR-Code-generator.sln
|
|
|
|
|
dotnet build csharp/QR-Code-generator.sln -c Debug
|
|
|
|
|
# Run demo
|
|
|
|
|
dotnet run --project csharp/QrCodeGen.Demo
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Rider/VS:
|
|
|
|
|
- Open `csharp/QR-Code-generator.sln` and build. Ensure your IDE uses the Microsoft .NET SDK (not Homebrew’s) and that the installed SDK matches `net8.0` or update TFM to `net9.0`.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Verification Performed
|
|
|
|
|
- Build succeeded locally after addressing initial compile issues (sbyte tables, enum constructor, finder pattern drawing, bit-buffer bounds).
|
|
|
|
|
- Demo run prints QR metadata and ASCII rendering for “Hello, world!”.
|
|
|
|
|
- Corrected a runtime bug where data bytes were read beyond `BitBuffer` length; now we read `numDataBytes = bb.BitLength / 8` and then apply 0xEC/0x11 padding.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Reviewer Checklist
|
|
|
|
|
- API Parity:
|
|
|
|
|
- QrCode: factory methods, mid-level encode, mask selection, ECC boosting.
|
|
|
|
|
- QrSegment: mode mapping, regex correctness, bit-length calculations.
|
|
|
|
|
- BitBuffer: bounds checks, append semantics, cloning.
|
|
|
|
|
- Tables: Verify ECC and block count tables match Java constants exactly.
|
|
|
|
|
- Masking/Penalties: Confirm penalty `N1..N4` and zigzag codeword placement logic mirror Java.
|
|
|
|
|
- Error Paths: Proper exceptions for out-of-range versions/masks/lengths.
|
|
|
|
|
- Padding: Verify 0xEC/0x11 alternating pad applied after real bytes from `BitBuffer`.
|
|
|
|
|
- Public API surface: Namespaces and visibility appropriate for a library; no leaking internals.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Known Gaps / Future Work
|
|
|
|
|
- Implement full `QrSegmentAdvanced`:
|
|
|
|
|
- DP-based optimal segmentation across modes.
|
|
|
|
|
- Kanji mode encoding per the Java version.
|
|
|
|
|
- Add unit tests:
|
|
|
|
|
- Cross-verify bitwise outputs vs. Java for a fixed mask across sample inputs.
|
|
|
|
|
- Randomized round-trips using known test vectors.
|
|
|
|
|
- Add simple image renderer (PNG/SVG) for easier visual verification.
|
|
|
|
|
- Performance: Consider bit-packing in `BitBuffer` to reduce allocations for very large payloads.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Migration Risks and Mitigations
|
|
|
|
|
- SDK Mismatch (net8.0 vs installed SDK): Documented; either install .NET 8 or retarget to `net9.0` in both `.csproj` files.
|
|
|
|
|
- Environment: Ensure the IDE uses Microsoft’s SDK at `/usr/local/share/dotnet`; avoid Homebrew’s SDK confusion.
|
|
|
|
|
- Behavior Drift: The simplified `QrSegmentAdvanced` may produce larger bitstreams for mixed-content strings vs. Java’s optimal splitter. This does not affect correctness, only compactness.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### File List (Added/Modified)
|
|
|
|
|
- Added:
|
|
|
|
|
- `csharp/QR-Code-generator.sln`
|
|
|
|
|
- `csharp/QrCodeGen/QrCodeGen.csproj`
|
|
|
|
|
- `csharp/QrCodeGen/BitBuffer.cs`
|
|
|
|
|
- `csharp/QrCodeGen/DataTooLongException.cs`
|
|
|
|
|
- `csharp/QrCodeGen/QrSegment.cs`
|
|
|
|
|
- `csharp/QrCodeGen/QrSegmentAdvanced.cs`
|
|
|
|
|
- `csharp/QrCodeGen/QrCode.cs`
|
|
|
|
|
- `csharp/QrCodeGen.Demo/QrCodeGen.Demo.csproj`
|
|
|
|
|
- `csharp/QrCodeGen.Demo/Program.cs`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### How Reviewers Can Reproduce
|
|
|
|
|
- Build and run demo as above.
|
|
|
|
|
- Optionally compare module patterns vs. Java output with a fixed mask (pass `mask` in `EncodeSegments`) over several sample strings.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### License Alignment
|
|
|
|
|
- The C# port continues to follow the MIT License consistent with the Java version and includes the original copyright attribution.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### Appendix: Usage Example
|
|
|
|
|
```csharp
|
|
|
|
|
using Io.Nayuki.QrCodeGen;
|
|
|
|
|
|
|
|
|
|
var qr = QrCode.EncodeText("Hello, world!", QrCode.Ecc.MEDIUM);
|
|
|
|
|
for (int y = 0; y < qr.Size; y++) {
|
|
|
|
|
for (int x = 0; x < qr.Size; x++) {
|
|
|
|
|
bool dark = qr.GetModule(x, y);
|
|
|
|
|
// render module
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|