QR Code Support

[!NOTE] The repository contains a bundled QR implementation under QRCode/. Its source defines encoding through raster output, but application registration, deployment reachability, and successful execution are not established. QRCode/index.php is a source-local wrapper: it includes qrlib.php, decodes GET q with base64_decode and json_decode, and passes $q->url to QRcode::png. Treat it as an example wrapper, not a proven HTTP API.

Source-defined flow

flowchart TD
    input[QRinput segments] --> bits[QRinputItem bitstreams]
    bits --> version[QRspec minimum version selection]
    version --> bytes[QRbitstream bytes]
    bytes --> spec[QRspec block specification]
    spec --> raw[QRrawcode blocks]
    raw --> ecc[Reed-Solomon correction]
    ecc --> frame[QRcode encodeMask frame]
    frame --> mask[QRmask selection]
    mask --> masked[Masked QR frame]
    masked --> binary[QRtools binarize]
    binary --> png[QRimage PNG]
    masked --> jpg[QRimage JPEG]

Component responsibilities

[
    {
        "title": "Input validation and mode encoding",
        "body": "In `QRCode/qrinput.php`, **QRinput** defaults to version `0` and **QR_ECLEVEL_L`. Its constructor throws when the version is below `0`, above `QRSPEC_VERSION_MAX`, or the correction level is above `QR_ECLEVEL_H`. **QRinputItem::encodeBitStream** uses **QRspec::maximumWords** to split oversized entries recursively. Entries are dispatched to the NUM, AN, 8-bit, KANJI, or STRUCTURE encoder and return the bitstream size, or `-1` on failure."
    },
    {
        "title": "Bitstream assembly and version selection",
        "body": "In `QRCode/qrbitstream.php`, **QRbitstream::toByte** converts stored bits into big-endian bytes, including a final partial byte; an empty stream produces an empty array. **QRinput::convertData** rebuilds the stream while adjusting the version until the data fits. A bitstream failure returns `-1`; if no version fits, the source throws `WRONG VERSION`. In `QRCode/qrspec.php`, **QRspec::getMinimumVersion** scans the capacity table from version 1 through the configured maximum and returns the first fitting version, or `-1`; the table contains versions 1 through 40."
    },
    {
        "title": "Raw blocks and Reed–Solomon correction",
        "body": "In `QRCode/qrencode.php`, **QRrawcode** obtains the input byte stream, reads the version and correction-level block specification from **QRspec**, allocates data and ECC lengths, initializes Reed–Solomon blocks through **QRrs::init_rs**, and creates **QRrsblock** instances for the block groups. In `QRCode/qrrscode.php`, **QRrs::init_rs** reuses a matching cached control block or delegates to **QRrsItem::init_rs_char** and stores the result. **QRrsItem::encode_rs_char** initializes parity symbols and iterates over data symbols using Galois-field lookup tables and the generator polynomial."
    },
    {
        "title": "Frame filling and mask selection",
        "body": "In `QRCode/qrencode.php`, **QRcode::encodeMask** validates the input, constructs **QRrawcode**, creates a version frame through **QRspec::newFrame**, and uses **FrameFiller** to interleave data and ECC bytes into the frame. It then adds remainder bits and either calls **QRmask::mask** for best-mask selection or **QRmask::makeMask** for the configured or explicitly supplied mask. The resulting version, width, and masked data are stored on the **QRcode** object. A null filler returns `NULL`."
    },
    {
        "title": "Mask scoring",
        "body": "In `QRCode/qrmask.php`, **QRmask::mask** evaluates the candidates in `checked_masks`, combines the black-module balance penalty with **QRmask::evaluateSymbol** demerits, and retains the candidate with the lowest demerit. The declared **QR_FIND_FROM_RANDOM** setting is false, so the source-defined best-mask configuration considers all eight mask numbers."
    },
    {
        "title": "Frame caching and configuration",
        "body": "In `QRCode/qrspec.php`, **QRspec::newFrame** rejects versions outside `1..QRSPEC_VERSION_MAX`. With caching enabled, frame cache lookup and writes use **QR_CACHE_DIR**. `QRCode/qrconfig.php` declares **QR_CACHEABLE** as `true`, places **QR_CACHE_DIR** under the `QRCode` directory, places **QR_LOG_DIR** under that same directory, enables **QR_FIND_BEST_MASK**, disables **QR_FIND_FROM_RANDOM**, sets **QR_DEFAULT_MASK** to `2` when best-mask selection is disabled, and sets **QR_PNG_MAXIMUM_SIZE** to `1024`."
    },
    {
        "title": "Raster image construction",
        "body": "In `QRCode/qrimage.php`, **QRimage::image** creates a white-and-black GD image, marks frame cells whose value is `'1'`, applies the outer frame margin, scales the result by `pixelPerPoint`, destroys the intermediate image, and returns the scaled image resource. **QRimage::png** emits PNG data to the response when the filename is false, writes to a filename when supplied, and writes both a file and response only when a filename is supplied and `saveandprint` is true. **QRimage::jpg** behaves similarly for JPEG output and applies the requested quality."
    }
]

Output paths

Output path Source entry Source-defined result External effect
Binarized return QRencode::encode with outfile set to false Returns QRtools::binarize output None declared in this branch
Text file QRencode::encode with an outfile Writes newline-joined QRtools::binarize output Writes to the caller-selected filename
PNG QRencode::encodePNG and QRimage::png Uses a size bounded by QR_PNG_MAXIMUM_SIZE and emits PNG data or writes a PNG file Response output when no filename is supplied; file output when a filename is supplied; both only when a filename is supplied and saveandprint is true
JPEG QRimage::jpg Emits JPEG data with the requested quality or writes a JPEG file Response output or file output

Constraints and unresolved behavior

[!WARNING] The QRCode/index.php wrapper uses the decoded q->url value without source-proven validation, so it is not a stable machine-facing contract. The 8-bit convenience path in QRCode/qrencode.php is unresolved: QRcode::encodeString8bit tests string rather than $string and calls QRinput::append with four arguments although the declared signature accepts mode, size, and data. The QRcode::png wrapper also assigns $saveandprint=false when calling encodePNG, so forwarding a caller-supplied save-and-print value is not proven.

Frame and mask caching may write beneath QR_CACHE_DIR, and text, PNG, and JPEG paths may write to caller-selected filenames. Image rendering depends on GD-style image functions. No evidence establishes writable directories, GD availability, successful execution, or an explicit success/error response; QRencode::encodePNG logs captured output and exception messages through QRtools::log instead of exposing an explicit result in its inspected body.

Updated