Every QR generator picks an encoding mode for you by default, and most of the time that default is fine. It stops being fine the moment you need control over it — a numeric ID next to a product code, a block of Japanese text you don’t want stored as UTF-8, or a payload where you need the same bytes out every time you generate it. This guide shows how to set QR code encoding modes in Python explicitly, using Aspose.BarCode for Python via .NET, so a single QR symbol can carry a numeric segment, an alphanumeric segment, a byte segment, and a Kanji segment — each stored in the mode that fits it best.

Aspose.BarCode calls these compaction modes in its own API names (QrExtCompactionMode) and the QR specification itself calls them encoding modes. They’re the same four modes under two names, and this guide uses “encoding mode” throughout since that’s the term the specification and most Python QR libraries use.

Why QR Encoding Modes Affect Symbol Size and Scan Reliability

A QR code’s physical size is driven by how many bits its payload needs, and bits per character depend entirely on the encoding mode used for that segment. The specification defines four data modes with markedly different densities:

ModeCharacter setStorageCost per character
NumericDigits 0-93 digits per 10 bits3.33 bits
AlphanumericDigits, uppercase A-Z, space, $%*+-./:2 characters per 11 bits5.5 bits
ByteAny 8-bit data, typically UTF-81 byte per 8 bits8 bits
KanjiShift-JIS double-byte characters1 character per 13 bits13 bits

A 30-digit identifier costs roughly 100 bits in numeric mode and 240 bits in byte mode. That gap is often enough to push the symbol up several QR versions, and a higher version means more modules in the same printed area — smaller modules, and a lower read rate on low-resolution cameras, curved packaging, and worn labels.

Automatic mode selection handles most payloads well. It becomes limiting when you know the shape of your data and the analyzer does not: a long numeric run broken by a single letter, Japanese text that byte mode would spend three UTF-8 bytes per character on, or a fixed-format identifier where you want deterministic output across library versions.

When Setting the Mode Manually Is Worth It

Mode switching is not free. Every segment boundary writes a four-bit mode indicator plus a character-count field of eight to sixteen bits depending on the QR version. Over-segmenting a payload can produce a larger symbol than letting the generator decide.

Setting the mode explicitly pays off when:

  • The payload contains long, homogeneous runs — a 40-digit serial, a paragraph of Kanji.
  • You are encoding Japanese text and want Kanji mode at 13 bits per character rather than byte mode at 24.
  • You need reproducible, byte-identical output for regression tests or checksum validation.

It is usually not worth the added complexity for short payloads, data that alternates type every few characters, or URLs, which automatic analysis already handles well. The measurement subsection below shows how to check which case you’re in.

Two Ways to Set QR Encoding Modes in Python

Aspose.BarCode offers two routes to the same encoded result, and it’s worth knowing why both exist before writing code.

QrExtCodetextBuilderInline EXTENDED selectors
Mode set byMethod calls with enum valuesBackslash markers in the string
Mistakes caughtAt the call siteOnly at decode time
Escaping concernsNone\\ escaping, or raw strings
Best forApplication codeCodetext from config, a database, or another system

The builder is the better default. QrExtCompactionMode.NUMERIC either exists or raises an AttributeError immediately, whereas a mistyped \numm in a string silently becomes payload data and only surfaces when someone scans the label. Both approaches feed the same QREncodeMode.EXTENDED generator setting, so you can move between them without changing anything downstream.

Set QR Code Encoding Modes in Python: Step by Step

1. Install and Prepare the Development Environment

Aspose.BarCode for Python via .NET is a cross-platform library supporting generation, recognition, and manipulation across more than 50 symbologies, QR included. Install from PyPI:

pip install aspose-barcode-for-python-via-net

Confirm the version, since these APIs require 26.6 or newer:

pip show aspose-barcode

Then verify the import resolves. The package binds to a .NET runtime, so a successful import tells you more than the presence of files on disk:

from aspose.barcode.generation import QREncodeMode, QrExtCompactionMode

print("Aspose.BarCode imported successfully.")
print("EXTENDED mode available:", hasattr(QREncodeMode, "EXTENDED"))
print("Encoding modes:", [m for m in dir(QrExtCompactionMode) if m.isupper()])

Output:

Aspose.BarCode imported successfully.
EXTENDED mode available: True
Encoding modes: ['ALPHA_NUMERIC', 'AUTO', 'BYTES', 'KANJI', 'NUMERIC']

If EXTENDED is missing, you are on a release older than 26.6 and need to upgrade before continuing.

If you have a license file, apply it once at application startup, before any generation or recognition call:

from aspose.barcode import License

license = License()
license.set_license("Aspose.BarCode.Python.NET.lic")

2. Set the Encoding Mode for Each Segment with QrExtCodetextBuilder

QrExtCodetextBuilder holds an ordered list of segments. Each call appends data together with the encoding mode that should store it, and get_extended_codetext() assembles them into the extended-format string the generator understands.

  1. Import the generation classes.
  2. Create a QrExtCodetextBuilder.
  3. Add a numeric segment with QrExtCompactionMode.NUMERIC.
  4. Add an alphanumeric segment with QrExtCompactionMode.ALPHA_NUMERIC.
  5. Add a byte segment with QrExtCompactionMode.BYTES.
  6. Add a Kanji segment with QrExtCompactionMode.KANJI.
  7. Retrieve the combined extended codetext.
from aspose.barcode.barcoderecognition import BarCodeReader, DecodeType
from aspose.barcode.generation import (
    BarcodeGenerator,
    EncodeTypes,
    QREncodeMode,
    QrExtCodetextBuilder,
    QrExtCompactionMode,
)

# Instantiate the Builder.
text_builder = QrExtCodetextBuilder()

# Numeric Segment — 3 Digits per 10 Bits.
text_builder.add_codetext_with_compaction_mode(
    QrExtCompactionMode.NUMERIC, "1234567"
)

# Alphanumeric Segment — Uppercase and Digits Only, 2 Characters per 11 Bits.
text_builder.add_codetext_with_compaction_mode(
    QrExtCompactionMode.ALPHA_NUMERIC, "ASPOSE2026"
)

# Byte Segment — Lowercase Forces Byte Mode, 8 Bits per Character.
text_builder.add_codetext_with_compaction_mode(
    QrExtCompactionMode.BYTES, "aspose2026"
)

# Kanji Segment — Shift-JIS Double-Byte Characters, 13 Bits Each.
text_builder.add_codetext_with_compaction_mode(
    QrExtCompactionMode.KANJI,
    "\u3062\u3063\u3064\u3065\u3066\u3067\u3068\u3069\u306A",
)

# Assemble the Final Extended Codetext.
codetext = text_builder.get_extended_codetext()
print("Extended codetext:", repr(codetext))

Explanation

  • Each add_codetext_with_compaction_mode call sets the encoding mode for one segment. Order matters — the decoder returns segments concatenated in the sequence you added them.
  • The alphanumeric set is deliberately narrow: digits, uppercase A-Z, space, and $%*+-./:. This is why ASPOSE2026 fits alphanumeric mode but aspose2026 does not. Lowercase letters are outside the set, so that segment must use BYTES. Passing lowercase to ALPHA_NUMERIC is the single most common mistake when setting modes this way.
  • The Kanji segment uses \u3062 onward, which are hiragana rather than kanji proper. Kanji mode covers the Shift-JIS double-byte range, which includes kana, so these encode at 13 bits each instead of the 24 bits byte mode would spend on each as UTF-8.
  • get_extended_codetext() produces the string the generator parses in EXTENDED mode. Printing it with repr() is worth doing once — it shows you the selector syntax the builder emits, which is exactly what the next subsection writes by hand.

3. Set the Encoding Mode Inline, Without the Builder

When the codetext originates outside your Python code, you can set each segment’s mode directly with a selector. Each backslash-prefixed marker governs every character until the next marker appears:

SelectorSets mode to
\numNumeric
\alnumAlphanumeric
\byteByte, UTF-8
\kanjiKanji, Shift-JIS
\autoAutomatic mode selection
# Equivalent to the Builder Output Above, Written Directly.
codetext = (
    r"\num1234567"
    r"\alnumASPOSE2026"
    r"\byteaspose2026"
    "\\kanji\u3062\u3063\u3064\u3065\u3066\u3067\u3068\u3069\u306A"
)

Note the escaping. In a normal Python string, "\num" is not a selector — it is a newline followed by um. Raw strings (r"...") avoid the problem, but a raw string also blocks \u escapes, which is why the Kanji line above uses a conventional string with \\kanji instead. This escaping trap is the practical argument for preferring the builder in section 2.

4. Generate the QR Barcode in EXTENDED Mode

Setting per-segment modes has no effect until the generator is told to read them. Without QREncodeMode.EXTENDED, the segment information is ignored and the selector markers are encoded as literal payload text.

  1. Create a BarcodeGenerator with EncodeTypes.QR and the extended codetext.
  2. Set encode_mode to QREncodeMode.EXTENDED.
  3. Configure resolution, and optionally error correction level and margin.
  4. Save the barcode to a lossless PNG.
# Create the QR Generator with the Extended Codetext.
gen = BarcodeGenerator(EncodeTypes.QR, codetext)

# Switch to EXTENDED Mode So the Per-Segment Modes Are Respected.
gen.parameters.barcode.qr.encode_mode = QREncodeMode.EXTENDED

# Render at Print Resolution Rather Than Upscaling Later.
gen.parameters.resolution = 300

# Save the Generated QR Code.
gen.save("extended_qr.png")

print("QR code generated and saved as extended_qr.png")

Explanation

  • gen.parameters.barcode.qr.encode_mode = QREncodeMode.EXTENDED is the line that activates segment parsing. Leave it out and the generator produces a valid, scannable QR code containing the literal text \num1234567... — which is why the next subsection verifies rather than assumes.
  • gen.parameters.resolution = 300 renders at print resolution. Symbols headed for label printers or packaging artwork should be generated at final size, not scaled up afterwards, which softens module edges.
  • save writes a lossless PNG. Avoid JPEG for any 2D symbology — its compression artifacts blur the module grid the decoder samples.

5. Verify the Encoding Mode Was Applied

Successful generation proves nothing about whether the per-segment modes took effect. The decode step is what separates a correctly encoded symbol from one carrying selector markers as data.

  1. Initialise a BarCodeReader with the file path and DecodeType.QR.
  2. Materialise the results so a failed read is visible.
  3. Compare the decoded text against the expected concatenation.
EXPECTED = (
    "1234567"
    "ASPOSE2026"
    "aspose2026"
    "\u3062\u3063\u3064\u3065\u3066\u3067\u3068\u3069\u306A"
)

# Initialise the QR Code Reader.
reader = BarCodeReader("extended_qr.png", DecodeType.QR)
results = list(reader.read_bar_codes())

if not results:
    raise ValueError("No QR code was detected in extended_qr.png.")

for result in results:
    decoded = result.code_text
    print("BarCode CodeText:", decoded)

    if "\\num" in decoded or "\\alnum" in decoded:
        print("Selectors were encoded literally — check that encode_mode is EXTENDED.")
    elif decoded == EXPECTED:
        print("Verified: all four segments decoded and concatenated as expected.")
    else:
        print("Mismatch. Expected:", EXPECTED)

Explanation

  • DecodeType.QR restricts recognition to QR symbols, which is faster than scanning every supported symbology and prevents a malformed symbol from being decoded as something else.
  • list(...) makes the failure case explicit. A recognition failure returns an empty iterable rather than raising, so an unguarded for loop over a failed read finishes silently and reads as success.
  • Checking for a literal \num catches the most common mistake in this workflow: setting the segments correctly and forgetting to set encode_mode.
  • The decoded payload is the raw segments concatenated, with all mode information consumed during encoding. Encoding modes are instructions to the encoder, not part of the data.

6. Measure Whether Setting the Mode Manually Helped

Setting the encoding mode manually is an optimisation, so measure it rather than assuming it. Generate the same payload both ways and compare:

RAW = "1234567ASPOSE2026aspose2026"

# Automatic Mode Selection.
auto = BarcodeGenerator(EncodeTypes.QR, RAW)
auto.save("qr_auto.png")

# Manually Set Modes per Segment.
builder = QrExtCodetextBuilder()
builder.add_codetext_with_compaction_mode(QrExtCompactionMode.NUMERIC, "1234567")
builder.add_codetext_with_compaction_mode(QrExtCompactionMode.ALPHA_NUMERIC, "ASPOSE2026")
builder.add_codetext_with_compaction_mode(QrExtCompactionMode.BYTES, "aspose2026")

manual = BarcodeGenerator(EncodeTypes.QR, builder.get_extended_codetext())
manual.parameters.barcode.qr.encode_mode = QREncodeMode.EXTENDED
manual.save("qr_manual.png")

print("Compare qr_auto.png and qr_manual.png — count modules along one edge.")

Count the modules along one edge of each image. A QR version n symbol is 17 + 4n modules square, so version 2 is 25×25 and version 3 is 29×29. If both land on the same version, automatic analysis already found the optimal segmentation and setting the mode manually was maintenance burden for no gain. Discovering that before shipping is a useful result, not a wasted step.

Get a Free License

Aspose offers a temporary free license that lifts evaluation restrictions and unlocks full functionality for testing. Request one from the Aspose temporary license page and apply it before any generation or recognition call.

Free Additional Resources

Conclusion

Setting QR code encoding modes in Python comes down to two questions: which segment gets which mode, and how you tell the generator to respect that choice. QrExtCodetextBuilder and QrExtCompactionMode answer the first question in application code; QREncodeMode.EXTENDED answers the second at the generator. This guide covered both the builder API and the inline selector syntax it produces, generating a four-segment QR symbol, verifying the decoded payload, and measuring the size difference against automatic mode.

Prefer the builder for application code — it catches mode errors at the call site instead of at the scanner. And keep the measurement step. Setting the mode manually is a real win on long homogeneous payloads and a net loss on short mixed ones where mode-switching overhead exceeds the savings. Generate both, compare the module counts, and let the result decide which code you maintain.

FAQs

  1. What is a QR code encoding mode and why would I use one? An encoding mode tells the QR generator how to treat a segment of data — numeric, alphanumeric, byte, or Kanji. Each mode has a different data density, so choosing the right one per segment keeps the QR version, and therefore the symbol, as small as possible. Aspose.BarCode calls these compaction modes; the QR specification calls them encoding modes.

  2. Which encoding modes does QrExtCompactionMode support? QrExtCompactionMode provides NUMERIC, ALPHA_NUMERIC, BYTES, and KANJI, matching the four QR data modes defined in ISO/IEC 18004.

  3. Should I use QrExtCodetextBuilder or inline EXTENDED selectors to set the mode? Use the builder for application code. It is type-checked, avoids backslash escaping mistakes, and assembles the extended codetext for you. Inline selectors are useful when the codetext arrives from configuration, a database, or another system that cannot call the builder.

  4. How does the EXTENDED encode mode differ from the standard QR encode mode? EXTENDED mode makes the generator read the codetext as a series of pre-defined segments, each with its own encoding mode, instead of running automatic mode detection across the whole string.

  5. Can I set different encoding modes for different parts of one QR code? Yes. Add several segments to QrExtCodetextBuilder, each with a different QrExtCompactionMode, and the builder produces a single extended codetext covering all of them.

  6. Is a QR code generated with mixed encoding modes compatible with standard readers? Yes. Multi-segment encoding is part of the QR specification, so any compliant scanner decodes the payload correctly and returns the concatenated data.

  7. Does setting the encoding mode manually always produce a smaller QR code? No. Every segment boundary costs a mode indicator and a character-count field, so splitting data into many short segments can make the symbol larger. Manual mode selection pays off on long, homogeneous runs of numeric or Kanji data.

  8. Do I need a license to set QR encoding modes with QrExtCodetextBuilder? You can evaluate the API without a license, subject to evaluation restrictions. A free temporary license from the Aspose website lifts those restrictions during testing, and production use requires a full license.

  9. What version of Aspose.BarCode for python-net supports these APIs? QrExtCodetextBuilder, QrExtCompactionMode, and QREncodeMode.EXTENDED are available in Aspose.BarCode for Python via .NET 26.6 and later.

Read More