# How to Control UTF-8 ECI Tagging for 2D Barcodes in C#
A 2D barcode that came out a different size than expected, a legacy scanner that refuses a symbol it used to read, and a standard demanding that every symbol declare its encoding all come down to the same question: does the barcode carry a UTF-8 ECI (Extended Channel Interpretation) header?
That header tells a scanner to interpret the payload as UTF-8, which matters for Chinese, Arabic, Thai or any other content outside plain ASCII. It is not free. The header consumes codewords, enough that a payload already near a size boundary can spill into a larger symbol, and scanners built before ECI existed reject a symbol carrying one. IronBarcode therefore writes the header only when the data contains non-ASCII characters, and the `EciMode` setting overrides that decision in either direction.
[[i:(Requires IronBarcode 2026.9.2 or later. The API does not exist in 2026.8.6 or earlier, where the call produces a compile error rather than a runtime failure.)]]
*as-heading:2(Quickstart: Set the ECI Policy for Every 2D Barcode)*
Assign the policy once, then generate barcodes as you normally would. Every 2D format follows it, with no further arguments.
```cs
:title=Apply an ECI Policy and Generate a Barcode
IronBarCode.BarcodeWriter.DefaultEciMode = IronBarCode.EciMode.ForceUtf8;
IronBarCode.BarcodeWriter.CreateBarcode("BK005|8389|0001", IronBarCode.BarcodeEncoding.QRCode).SaveAsPng("part-qr.png");
```
<div class="hsg-featured-snippet">
<h3>Minimal Workflow (5 steps)</h3>
<ol>
<li><a class="js-modal-open" data-modal-id="trial-license-after-download" href="https://www.nuget.org/packages/BarCode/">Download the C# library to control barcode ECI encoding</a></li>
<li>Generate a 2D barcode with <code>CreateBarcode</code> or <code>CreateDataMatrix</code> as you do today</li>
<li>Check whether the payload is pure ASCII, since that decides whether a header is written</li>
<li>Pass an <code>EciMode</code> to <code>CreateDataMatrix</code> where one symbol needs different behaviour</li>
<li>Set <code>BarcodeWriter.DefaultEciMode</code> at startup to apply one policy across every 2D format</li>
</ol>
</div>
<br class="clear" />
<hr />
## When Does IronBarcode Add an ECI Header?
Nothing about the call you already write changes. Generate a DataMatrix from an ASCII part number and the symbol carries no header; hand the same method a payload with an umlaut in it and the header appears, because the value needs it. An `EciMode` argument on the call overrides that judgement.
```cs
:path=/static-assets/barcode/content-code-examples/how-to/eci-encoding-default-behaviour.cs
```
The three symbols above differ only in payload and in whether a mode was supplied. The images below are drawn at a fixed module size, so the symbols differ in overall size the way they would on a printed label.
<div style="display: flex; gap: 1.5rem; justify-content: center; align-items: flex-end; flex-wrap: wrap;">
<div class="content-img-align-center">
<div class="center-image-wrapper">
<img src="/static-assets/barcode/how-to/eci-encoding/t1-ascii-auto.webp"
alt="DataMatrix encoding an ASCII part number under EciMode.Auto, a 16 by 16 module symbol with no ECI header"
class="img-responsive add-shadow" />
<p style="color: #181818; font-style: italic; text-align: center;">ASCII, <code>Auto</code> — 16 × 16 modules, no header</p>
</div>
</div>
<div class="content-img-align-center">
<div class="center-image-wrapper">
<img src="/static-assets/barcode/how-to/eci-encoding/t1-latin1-auto.webp"
alt="DataMatrix encoding a part number containing a German umlaut under EciMode.Auto, a 22 by 22 module symbol carrying a UTF-8 ECI header"
class="img-responsive add-shadow" />
<p style="color: #181818; font-style: italic; text-align: center;">Non-ASCII, <code>Auto</code> — 22 × 22 modules, header added</p>
</div>
</div>
<div class="content-img-align-center">
<div class="center-image-wrapper">
<img src="/static-assets/barcode/how-to/eci-encoding/t1-ascii-forced.webp"
alt="DataMatrix encoding the same ASCII part number under EciMode.ForceUtf8, a 16 by 16 module symbol the same size as the plain ASCII one"
class="img-responsive add-shadow" />
<p style="color: #181818; font-style: italic; text-align: center;">ASCII, <code>ForceUtf8</code> — 16 × 16 modules, header forced</p>
</div>
</div>
</div>
<br class="clear" />
The ASCII payload encodes to a **16 × 16** grid whether or not a header is forced onto it: this particular value has enough spare capacity that the extra codewords cost nothing. The non-ASCII payload needs a full size tier more, **22 × 22**, because the header and the multi-byte content together no longer fit at 16 × 16. A header only grows the symbol when the payload is already close to a size boundary, and whether that is true for a given value is not visible from the string itself.
### What Are the Three ECI Modes?
- **`Auto`:** Writes the UTF-8 ECI header only when the data contains non-ASCII characters. This is the default, and it produces the smallest symbol for a pure-ASCII payload.
- **`ForceUtf8`:** Always writes the header, including for pure ASCII. Use it for strict standards compliance, or where every symbol must declare its encoding.
- **`Off`:** Never writes the header. Use it only where the data is known to be ASCII or Latin-1 and the decoder does not understand ECI.
[[i:(ECI applies to 2D formats only. `EciMode` has no effect on 1D barcodes such as Code 128, EAN or UPC.)]]
<hr />
## How Do I Set the ECI Mode?
### How Do I Apply One Policy Across the Application?
`BarcodeWriter.DefaultEciMode` is a public static field on `BarcodeWriter`, defaulting to `EciMode.Auto`. Assign it once and every 2D barcode the application generates follows it, whichever format and whichever part of the codebase produced it. It mirrors the existing `BarcodeWriter.DefaultCharacterEncoding` field:
```cs
:path=/static-assets/barcode/content-code-examples/how-to/eci-encoding-global-policy.cs
```
### How Do I Override the Mode for a Single DataMatrix?
A per-call `EciMode` on a `CreateDataMatrix` overload wins over `BarcodeWriter.DefaultEciMode`, and applies to that call only.
```cs
:path=/static-assets/barcode/content-code-examples/how-to/eci-encoding-per-call-datamatrix.cs
```
Every `CreateDataMatrix` overload that takes an `EciMode` follows the same pattern. The data goes in first, as a `string`, a `byte[]` or a `Stream`. Sizing comes next, either as a single pixel size or as a `DataMatrixWriter.DataMatrixShape` with an explicit width and height. `EciMode` is always the final parameter. The example above passes a size; the shape overload takes the same `EciMode` in the same final position:
```cs
:path=/static-assets/barcode/content-code-examples/how-to/eci-encoding-shape-form.cs
```
### What Controls QR, Aztec and PDF417?
These three formats have **no** `EciMode` argument. There is no such parameter on `QRCodeWriter`, and none on the generic `BarcodeWriter.CreateBarcode` overloads, so `BarcodeWriter.DefaultEciMode` is the only way to control them.
```cs
:path=/static-assets/barcode/content-code-examples/how-to/eci-encoding-qr-aztec.cs
```
Where a QR code and a DataMatrix need different ECI behaviour in the same run, the DataMatrix takes the per-call argument and the QR code follows the static default.
<hr />
## How Does the Setting Behave at Run Time?
### When Does DefaultEciMode Take Effect?
`CreateBarcode`, `CreateDataMatrix` and `CreateQrCode` return an object whose mode is not yet fixed. `DefaultEciMode` is read when the barcode is saved or otherwise rendered, so assigning a new value mid-run affects every barcode saved after that point, including objects created before the change.
This catches out the common batch shape: create several barcodes while changing `DefaultEciMode` between calls, then save them together at the end. All of them take the mode that was set last. Save each barcode before changing the mode for the next one, or pass `EciMode` per call to `CreateDataMatrix`, which the static default does not reach.
[[w:(`DefaultEciMode` is a mutable process-global static and is not thread-safe. A reassignment is visible to every thread immediately, so changing it while another thread is generating or saving means that thread observes the change mid-run. Treat it as application-startup policy, exactly as with `BarcodeWriter.DefaultCharacterEncoding`.)]]
### When Does EciMode.Off Throw?
`Off` is a promise that the data is ASCII. Break that promise and the call throws `IronBarCodeEncodingException`, under either `BarcodeWriter.DefaultCharacterEncoding` setting. The UTF-8 default gives the more useful message:
```
IronBarCode Encoding Error for Barcode Format DataMatrix : EciMode.Off cannot be used with non-ASCII data while BarcodeWriter.DefaultCharacterEncoding is UTF-8, because the UTF-8 bytes require the ECI header to be decoded correctly and would otherwise be read as ISO-8859-1. Use EciMode.Auto or EciMode.ForceUtf8, or set BarcodeWriter.DefaultCharacterEncoding to a matching single-byte encoding.
```
Under `ISO_8859_1` the same combination surfaces as a lower-level native encoder error.
The payload is often not fully under the application's control: user input, an upstream feed, a product name from a supplier catalogue. A defensive wrapper covers it:
```cs
:path=/static-assets/barcode/content-code-examples/how-to/eci-encoding-off-exception.cs
```
The fallback here switches that symbol to `Auto`, on the view that a correct barcode reaching an ECI-aware scanner beats no barcode. The right recovery depends on the deployment: where the scanners genuinely cannot read ECI, rejecting the record for manual handling is the safer choice.
The type is also listed in the [error handling and debugging guide](https://ironsoftware.com/csharp/barcode/how-to/detailed-error-messages/), which covers the exception family without this specific cause.
<hr />
## How Do I Choose a Mode?
### How Do I Tag Every Symbol Again?
One line, at startup:
```cs
:path=/static-assets/barcode/content-code-examples/how-to/eci-encoding-restore-legacy.cs
```
`ForceUtf8` returns every 2D format to always-tagged behaviour, producing byte-identical output to 2026.8.6 and earlier for the same input. This is the right choice for an application whose downstream systems were built against the tagged output, for a standard that requires an explicit encoding declaration on every symbol, or simply as a way to defer the change while its effects are assessed.
The reverse case is worth naming too. An application that upgraded, found its symbols got smaller, and is happy about it needs no action at all: `Auto` is already the default.
### Which Mode Fits Which Situation?
*Recommended ECI mode by scenario, and where each one is configured*
| Scenario | ECI Mode | Where to Set |
|----------|----------|--------------|
| General-purpose generation, mixed content | `Auto` | Nothing to do; it is the default |
| Fleet of older scanners that fail on ECI | `Off` | `DefaultEciMode` at startup |
| Standard requires explicit encoding on every symbol | `ForceUtf8` | `DefaultEciMode` at startup |
| One compliance symbol among otherwise normal output | `ForceUtf8` | Per-call, DataMatrix only |
| Payload contents not guaranteed ASCII | `Auto`, or `Off` with a catch | Either, with exception handling |
<hr />
## Where to Go Next
The behaviour described here is one setting with three values, and most applications will set it once and never revisit it. The two decisions worth making deliberately are whether the scanner fleet understands ECI, and whether any downstream standard requires a declaration on every symbol.
For which barcode formats support Unicode content in the first place, see the [Write UTF-8 and Unicode Barcodes how-to](https://ironsoftware.com/csharp/barcode/how-to/writing-in-unicode/), which covers format-level Unicode support and links here for the ECI detail. For generating the 2D formats themselves, the [Create 2D Barcodes how-to](https://ironsoftware.com/csharp/barcode/how-to/create-2d-barcodes/) covers DataMatrix, QR, Aztec and PDF417 generation. The complete class documentation is in the [IronBarcode API reference](https://ironsoftware.com/csharp/barcode/object-reference/api/).
A 2D barcode that came out a different size than expected, a legacy scanner that refuses a symbol it used to read, and a standard demanding that every symbol declare its encoding all come down to the same question: does the barcode carry a UTF-8 ECI (Extended Channel Interpretation) header?
That header tells a scanner to interpret the payload as UTF-8, which matters for Chinese, Arabic, Thai or any other content outside plain ASCII. It is not free. The header consumes codewords, enough that a payload already near a size boundary can spill into a larger symbol, and scanners built before ECI existed reject a symbol carrying one. IronBarcode therefore writes the header only when the data contains non-ASCII characters, and the EciMode setting overrides that decision in either direction.
Please note: Requires IronBarcode 2026.9.2 or later. The API does not exist in 2026.8.6 or earlier, where the call produces a compile error rather than a runtime failure.
Quickstart: Set the ECI Policy for Every 2D Barcode
Assign the policy once, then generate barcodes as you normally would. Every 2D format follows it, with no further arguments.
Generate a 2D barcode with CreateBarcode or CreateDataMatrix as you do today
Check whether the payload is pure ASCII, since that decides whether a header is written
Pass an EciMode to CreateDataMatrix where one symbol needs different behaviour
Set BarcodeWriter.DefaultEciMode at startup to apply one policy across every 2D format
When Does IronBarcode Add an ECI Header?
Nothing about the call you already write changes. Generate a DataMatrix from an ASCII part number and the symbol carries no header; hand the same method a payload with an umlaut in it and the header appears, because the value needs it. An EciMode argument on the call overrides that judgement.
using IronBarCode;// Pure ASCII: no ECI headervar ascii = DataMatrixWriter.CreateDataMatrix("BK005|8389|0001");ascii.SaveAsPng("part-ascii.png");// Non-ASCII: header written automaticallyvar unicode = DataMatrixWriter.CreateDataMatrix("BK005|8389|Bauteil-Größe");unicode.SaveAsPng("part-unicode.png");// Same ASCII value, header forced onvar tagged = DataMatrixWriter.CreateDataMatrix("BK005|8389|0001", 500, EciMode.ForceUtf8);tagged.SaveAsPng("part-tagged.png");
using IronBarCode;
// Pure ASCII: no ECI header
var ascii = DataMatrixWriter.CreateDataMatrix("BK005|8389|0001");
ascii.SaveAsPng("part-ascii.png");
// Non-ASCII: header written automatically
var unicode = DataMatrixWriter.CreateDataMatrix("BK005|8389|Bauteil-Größe");
unicode.SaveAsPng("part-unicode.png");
// Same ASCII value, header forced on
var tagged = DataMatrixWriter.CreateDataMatrix("BK005|8389|0001", 500, EciMode.ForceUtf8);
tagged.SaveAsPng("part-tagged.png");
C#
The three symbols above differ only in payload and in whether a mode was supplied. The images below are drawn at a fixed module size, so the symbols differ in overall size the way they would on a printed label.
ASCII, Auto — 16 × 16 modules, no header
Non-ASCII, Auto — 22 × 22 modules, header added
ASCII, ForceUtf8 — 16 × 16 modules, header forced
The ASCII payload encodes to a 16 × 16 grid whether or not a header is forced onto it: this particular value has enough spare capacity that the extra codewords cost nothing. The non-ASCII payload needs a full size tier more, 22 × 22, because the header and the multi-byte content together no longer fit at 16 × 16. A header only grows the symbol when the payload is already close to a size boundary, and whether that is true for a given value is not visible from the string itself.
What Are the Three ECI Modes?
Auto: Writes the UTF-8 ECI header only when the data contains non-ASCII characters. This is the default, and it produces the smallest symbol for a pure-ASCII payload.
ForceUtf8: Always writes the header, including for pure ASCII. Use it for strict standards compliance, or where every symbol must declare its encoding.
Off: Never writes the header. Use it only where the data is known to be ASCII or Latin-1 and the decoder does not understand ECI.
Please note: ECI applies to 2D formats only. EciMode has no effect on 1D barcodes such as Code 128, EAN or UPC.
How Do I Set the ECI Mode?
How Do I Apply One Policy Across the Application?
BarcodeWriter.DefaultEciMode is a public static field on BarcodeWriter, defaulting to EciMode.Auto. Assign it once and every 2D barcode the application generates follows it, whichever format and whichever part of the codebase produced it. It mirrors the existing BarcodeWriter.DefaultCharacterEncoding field:
using IronBarCode;// Set once at startupBarcodeWriter.DefaultEciMode = EciMode.Off;// No mode argument passed anywhere belowGeneratedBarcode label = CreatePartLabel("BK005|8389|0001");label.SaveAsPng("part-label.png");GeneratedBarcode ticket = BarcodeWriter.CreateBarcode("TKT-77120", BarcodeEncoding.Aztec);ticket.SaveAsPng("gate-ticket.png");static GeneratedBarcodeCreatePartLabel(string partNumber){ // Follows DefaultEciMode returnDataMatrixWriter.CreateDataMatrix(partNumber, 300);}
using IronBarCode;
// Set once at startup
BarcodeWriter.DefaultEciMode = EciMode.Off;
// No mode argument passed anywhere below
GeneratedBarcode label = CreatePartLabel("BK005|8389|0001");
label.SaveAsPng("part-label.png");
GeneratedBarcode ticket = BarcodeWriter.CreateBarcode("TKT-77120", BarcodeEncoding.Aztec);
ticket.SaveAsPng("gate-ticket.png");
static GeneratedBarcode CreatePartLabel(string partNumber)
{
// Follows DefaultEciMode
return DataMatrixWriter.CreateDataMatrix(partNumber, 300);
}
C#
How Do I Override the Mode for a Single DataMatrix?
A per-call EciMode on a CreateDataMatrix overload wins over BarcodeWriter.DefaultEciMode, and applies to that call only.
using IronBarCode;// Application-wide policyBarcodeWriter.DefaultEciMode = EciMode.Auto;// Per-call override winsvar compliance = DataMatrixWriter.CreateDataMatrix("LOT-4471-A", 200, EciMode.ForceUtf8);compliance.SaveAsPng("compliance-label.png");// No override; follows DefaultEciModevar standard = DataMatrixWriter.CreateDataMatrix("LOT-4471-B", 200);standard.SaveAsPng("standard-label.png");
using IronBarCode;
// Application-wide policy
BarcodeWriter.DefaultEciMode = EciMode.Auto;
// Per-call override wins
var compliance = DataMatrixWriter.CreateDataMatrix("LOT-4471-A", 200, EciMode.ForceUtf8);
compliance.SaveAsPng("compliance-label.png");
// No override; follows DefaultEciMode
var standard = DataMatrixWriter.CreateDataMatrix("LOT-4471-B", 200);
standard.SaveAsPng("standard-label.png");
C#
Every CreateDataMatrix overload that takes an EciMode follows the same pattern. The data goes in first, as a string, a byte[] or a Stream. Sizing comes next, either as a single pixel size or as a DataMatrixWriter.DataMatrixShape with an explicit width and height. EciMode is always the final parameter. The example above passes a size; the shape overload takes the same EciMode in the same final position:
using IronBarCode;
// Rectangular symbol, ECI forced on
var wide = DataMatrixWriter.CreateDataMatrix(
"BK005|8389|0001",
DataMatrixWriter.DataMatrixShape.Rectangular,
400,
100,
EciMode.ForceUtf8);
wide.SaveAsPng("part-wide.png");
C#
What Controls QR, Aztec and PDF417?
These three formats have noEciMode argument. There is no such parameter on QRCodeWriter, and none on the generic BarcodeWriter.CreateBarcode overloads, so BarcodeWriter.DefaultEciMode is the only way to control them.
using IronBarCode;// QR, Aztec, PDF417: no per-call EciModeBarcodeWriter.DefaultEciMode = EciMode.ForceUtf8;var qr = BarcodeWriter.CreateBarcode("ORDER-88213", BarcodeEncoding.QRCode);qr.SaveAsPng("order-qr.png");var aztec = BarcodeWriter.CreateBarcode("ORDER-88213", BarcodeEncoding.Aztec);aztec.SaveAsPng("order-aztec.png");// New value applies to later callsBarcodeWriter.DefaultEciMode = EciMode.Auto;var qrAuto = BarcodeWriter.CreateBarcode("ORDER-88214", BarcodeEncoding.QRCode);qrAuto.SaveAsPng("order-qr-auto.png");
using IronBarCode;
// QR, Aztec, PDF417: no per-call EciMode
BarcodeWriter.DefaultEciMode = EciMode.ForceUtf8;
var qr = BarcodeWriter.CreateBarcode("ORDER-88213", BarcodeEncoding.QRCode);
qr.SaveAsPng("order-qr.png");
var aztec = BarcodeWriter.CreateBarcode("ORDER-88213", BarcodeEncoding.Aztec);
aztec.SaveAsPng("order-aztec.png");
// New value applies to later calls
BarcodeWriter.DefaultEciMode = EciMode.Auto;
var qrAuto = BarcodeWriter.CreateBarcode("ORDER-88214", BarcodeEncoding.QRCode);
qrAuto.SaveAsPng("order-qr-auto.png");
C#
Where a QR code and a DataMatrix need different ECI behaviour in the same run, the DataMatrix takes the per-call argument and the QR code follows the static default.
How Does the Setting Behave at Run Time?
When Does DefaultEciMode Take Effect?
CreateBarcode, CreateDataMatrix and CreateQrCode return an object whose mode is not yet fixed. DefaultEciMode is read when the barcode is saved or otherwise rendered, so assigning a new value mid-run affects every barcode saved after that point, including objects created before the change.
This catches out the common batch shape: create several barcodes while changing DefaultEciMode between calls, then save them together at the end. All of them take the mode that was set last. Save each barcode before changing the mode for the next one, or pass EciMode per call to CreateDataMatrix, which the static default does not reach.
Warning: DefaultEciMode is a mutable process-global static and is not thread-safe. A reassignment is visible to every thread immediately, so changing it while another thread is generating or saving means that thread observes the change mid-run. Treat it as application-startup policy, exactly as with BarcodeWriter.DefaultCharacterEncoding.
When Does EciMode.Off Throw?
Off is a promise that the data is ASCII. Break that promise and the call throws IronBarCodeEncodingException, under either BarcodeWriter.DefaultCharacterEncoding setting. The UTF-8 default gives the more useful message:
IronBarCode Encoding Error for Barcode Format DataMatrix : EciMode.Off cannot be used with non-ASCII data while BarcodeWriter.DefaultCharacterEncoding is UTF-8, because the UTF-8 bytes require the ECI header to be decoded correctly and would otherwise be read as ISO-8859-1. Use EciMode.Auto or EciMode.ForceUtf8, or set BarcodeWriter.DefaultCharacterEncoding to a matching single-byte encoding.
IronBarCode Encoding Error for Barcode Format DataMatrix : EciMode.Off cannot be used with non-ASCII data while BarcodeWriter.DefaultCharacterEncoding is UTF-8, because the UTF-8 bytes require the ECI header to be decoded correctly and would otherwise be read as ISO-8859-1. Use EciMode.Auto or EciMode.ForceUtf8, or set BarcodeWriter.DefaultCharacterEncoding to a matching single-byte encoding.
Text
Under ISO_8859_1 the same combination surfaces as a lower-level native encoder error.
The payload is often not fully under the application's control: user input, an upstream feed, a product name from a supplier catalogue. A defensive wrapper covers it:
using IronBarCode;using IronBarCode.Exceptions;using System;public class LabelGenerator{ // Payload not guaranteed ASCII public boolTryCreateLabel(string payload, string outputPath) { try { var barcode = DataMatrixWriter.CreateDataMatrix(payload, 200, EciMode.Off); barcode.SaveAsPng(outputPath); return true; } catch (IronBarCodeEncodingException ex) { // Non-ASCII cannot round-trip without ECIConsole.WriteLine("Payload is not ASCII, cannot generate with ECI disabled: {0}", ex.Message); return false; } } // Fall back to Auto public voidCreateLabelWithFallback(string payload, string outputPath) { if (TryCreateLabel(payload, outputPath)) { return; } var barcode = DataMatrixWriter.CreateDataMatrix(payload, 200, EciMode.Auto); barcode.SaveAsPng(outputPath); }}
using IronBarCode;
using IronBarCode.Exceptions;
using System;
public class LabelGenerator
{
// Payload not guaranteed ASCII
public bool TryCreateLabel(string payload, string outputPath)
{
try
{
var barcode = DataMatrixWriter.CreateDataMatrix(payload, 200, EciMode.Off);
barcode.SaveAsPng(outputPath);
return true;
}
catch (IronBarCodeEncodingException ex)
{
// Non-ASCII cannot round-trip without ECI
Console.WriteLine("Payload is not ASCII, cannot generate with ECI disabled: {0}", ex.Message);
return false;
}
}
// Fall back to Auto
public void CreateLabelWithFallback(string payload, string outputPath)
{
if (TryCreateLabel(payload, outputPath))
{
return;
}
var barcode = DataMatrixWriter.CreateDataMatrix(payload, 200, EciMode.Auto);
barcode.SaveAsPng(outputPath);
}
}
C#
The fallback here switches that symbol to Auto, on the view that a correct barcode reaching an ECI-aware scanner beats no barcode. The right recovery depends on the deployment: where the scanners genuinely cannot read ECI, rejecting the record for manual handling is the safer choice.
using IronBarCode;// Restore pre-2026.9.2 always-tagged outputBarcodeWriter.DefaultEciMode = EciMode.ForceUtf8;// Both symbols carry the headervar dataMatrix = DataMatrixWriter.CreateDataMatrix("SKU-10045", 200);dataMatrix.SaveAsPng("sku-datamatrix.png");var qr = BarcodeWriter.CreateBarcode("SKU-10045", BarcodeEncoding.QRCode);qr.SaveAsPng("sku-qr.png");
using IronBarCode;
// Restore pre-2026.9.2 always-tagged output
BarcodeWriter.DefaultEciMode = EciMode.ForceUtf8;
// Both symbols carry the header
var dataMatrix = DataMatrixWriter.CreateDataMatrix("SKU-10045", 200);
dataMatrix.SaveAsPng("sku-datamatrix.png");
var qr = BarcodeWriter.CreateBarcode("SKU-10045", BarcodeEncoding.QRCode);
qr.SaveAsPng("sku-qr.png");
C#
ForceUtf8 returns every 2D format to always-tagged behaviour, producing byte-identical output to 2026.8.6 and earlier for the same input. This is the right choice for an application whose downstream systems were built against the tagged output, for a standard that requires an explicit encoding declaration on every symbol, or simply as a way to defer the change while its effects are assessed.
The reverse case is worth naming too. An application that upgraded, found its symbols got smaller, and is happy about it needs no action at all: Auto is already the default.
Which Mode Fits Which Situation?
Recommended ECI mode by scenario, and where each one is configured
Scenario
ECI Mode
Where to Set
General-purpose generation, mixed content
Auto
Nothing to do; it is the default
Fleet of older scanners that fail on ECI
Off
DefaultEciMode at startup
Standard requires explicit encoding on every symbol
ForceUtf8
DefaultEciMode at startup
One compliance symbol among otherwise normal output
ForceUtf8
Per-call, DataMatrix only
Payload contents not guaranteed ASCII
Auto, or Off with a catch
Either, with exception handling
Where to Go Next
The behaviour described here is one setting with three values, and most applications will set it once and never revisit it. The two decisions worth making deliberately are whether the scanner fleet understands ECI, and whether any downstream standard requires a declaration on every symbol.
For which barcode formats support Unicode content in the first place, see the Write UTF-8 and Unicode Barcodes how-to, which covers format-level Unicode support and links here for the ECI detail. For generating the 2D formats themselves, the Create 2D Barcodes how-to covers DataMatrix, QR, Aztec and PDF417 generation. The complete class documentation is in the IronBarcode API reference.
Ahmad is a full-stack developer with a strong foundation in C#, Python, and web technologies. He has a deep interest in building scalable software solutions and enjoys exploring how design and functionality meet in real-world applications.