Overview
The library builds DataMatrix ECC 200 symbols according to ISO/IEC 16022 (GOST R ISO/IEC 16022) and returns them as a PNG or JPEG image — in memory or as a file. All 24 square sizes are supported, from 10×10 to 144×144, six encodation schemes (ASCII, C40, Text, X12, EDIFACT, Base256), and a GS1 option that validates the string against the GS1 General Specifications and the Chestny ZNAK profile (Chestny ZNAK is Russia's national product marking system).
The library reads every symbol it builds back by itself (self-check): finder patterns, codewords, Reed–Solomon syndromes, scheme parsing. A symbol that fails to read back is never returned.
The public interface is C++20, with headers in include/dmgen/. The public API does not throw exceptions: anything that can fail returns Result<T>. For other languages there is a flat C ABI (dmgen_c) — suitable for P/Invoke, JNA, cgo and ctypes.
Windows x86-64 and Linux x86-64, ARM64 and ARMv7 — the library is built and tested for single-board computers such as the Raspberry Pi as well. Third-party code is bundled in the distribution; there are no external dependencies.
Linking
The library ships prebuilt — only your program needs to be built. There are two ways to link it: statically — the C++ API from libdmgen.a becomes part of your program, or dynamically — through the dmgen_c library with a flat C interface (C ABI). Distribution contents:
include\dmgen\{Dmgen,Encoder,Gs1,Image,License,Options,Result,Symbol,Version}.h C++ API
include\dmgen\dmgen_c.h C ABI
lib\libdmgen.a static C++ library
lib\pkgconfig\dmgen.pc
bin\dmgen_c.dll C ABI library
lib\dmgen_c.dll.a its import library (MinGW)
bin\dmgen.exe command-line utility
share\doc\dmgen\licenses\ third-party licenses| Your program | What to use |
|---|---|
| C++, built with MinGW-w64 GCC (Windows) or GCC (Linux) | Static linking — C++ API, libdmgen.a |
| MSVC or another compiler; another language (C, C#, Java, Go…); updating the library without rebuilding the program | Dynamic linking — C ABI, dmgen_c |
| Python | The wheel from Downloads — Python documentation |
Static linking — C++ API
libdmgen.a becomes part of your program: nothing needs to be placed next to it except the key file dmgen.key (see "License"). You need a C++20 compiler with the same C++ standard library as the distribution (the same or a newer version).
libdmgen.a is built with MinGW-w64 GCC — MSVC cannot link it, you need the same toolchain: MSYS2, the MINGW64 shell (not UCRT64), packages pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-pkgconf. From MSVC, link dynamically, through dmgen_c. The link line comes from dmgen.pc; paths in it are relative to the file's own location, so the extraction directory can be anywhere:
$env:PKG_CONFIG_PATH = "C:\extracted\to\lib\pkgconfig"
pkg-config --static --cflags --libs dmgenThe system libraries winhttp, crypt32, shell32 and advapi32 (license check) are already listed in the package.
In meson, use a dependency by package name:
dmgen_dep = dependency('dmgen', required: true, static: true)
executable('myprogram', 'main.cpp', dependencies: [dmgen_dep],
link_args: ['-static']) # Windows: MinGW runtime inside the programOn Windows, -static also links the compiler runtime into the program: without it, the program will need libstdc++-6.dll, libgcc_s_seh-1.dll and libwinpthread-1.dll from MSYS2 next to it. On Linux it is not needed.
Dynamic linking — C ABI
dmgen_c is the same library behind a flat C interface (header dmgen/dmgen_c.h, details in C ABI). The compiler runtime is linked into it statically, so it depends neither on the caller's compiler nor on its C++ standard library. There is no separate .pc file for it — paths are specified directly.
MinGW-w64 GCC — via the import library lib\dmgen_c.dll.a:
gcc main.c -o myprogram.exe -I C:/extracted/to/include -L C:/extracted/to/lib -ldmgen_cMSVC — there is no .lib file in the distribution; it is generated from the DLL itself. In an MSYS2 shell, create the export definition:
gendef dmgen_c.dll # package mingw-w64-x86_64-tools; writes dmgen_c.defthen, in the Developer Command Prompt for VS (x64), build the import library and the program:
lib /def:dmgen_c.def /machine:x64 /out:dmgen_c.lib
cl /I C:\extracted\to\include main.c dmgen_c.libAt run time, dmgen_c.dll must be next to your .exe (or in a directory on PATH), and the key file dmgen.key next to dmgen_c.dll.
The Python wheel is a wrapper over exactly dmgen_c, with the library inside the package: Python documentation.
Quick start
A Chestny ZNAK code → a PNG file. A single header is all you need:
#include <dmgen/Dmgen.h>
#include <iostream>
int main() {
dmgen::EncodeOptions o;
o.gs1 = dmgen::Gs1Mode::ChestnyZnak; // validation and FNC1 as the first codeword
auto sym = dmgen::encode("(01)04601234567893(21)5Abc12Xyz!-(93)dGVz", o);
if (!sym) {
std::cerr << sym.error().message << "\n"; // in Russian
return 1;
}
dmgen::RenderOptions r; // 8 px per module, quiet zone 2
auto saved = dmgen::save(dmgen::render(sym->modules, r), "code.png", dmgen::ImageFormat::Png);
return saved ? 0 : 1;
}For an image in memory, use dmgen::toPng(image) or dmgen::toJpeg(image); both return Result<std::vector<uint8_t>>.
Encoding
Result<Symbol> encode(std::string_view data, const EncodeOptions& options = {});Bytes 128–255 are encoded via Upper Shift. Text is not re-encoded: a UTF-8 string is stored in the symbol as UTF-8.
EncodeOptions
| Field | Default | Meaning |
|---|---|---|
side | 0 | Symbol side: 0 — the smallest that fits, otherwise 10, 12, 14, 16, 18, 20, 22, 24, 26, 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144. Not in the list — InvalidArgument, does not fit — SizeTooSmall. |
encodation | Auto | Auto — schemes are chosen by the look-ahead algorithm of Annex P of the standard; Minimal — the shortest stream (slower to compute); or a single scheme: Ascii, C40, Text, X12, Edifact, Base256. |
gs1 | Off | Off, Gs1, ChestnyZnak. The string is validated before encoding; a violation gives Gs1Invalid. |
separator | Gs29 | Group separator: Gs29 — the GS byte, as in Chestny ZNAK; Fnc1 — codeword 232 (only in Gs1 mode). |
gs1Input | Auto | Raw — separator as byte 0x1D or written as <GS>, {GS}, \x1D; Bracketed — (01)…(21)…, separators are inserted automatically; Auto — decided by a leading "(". |
verify | true | Self-check: the built symbol is read back and compared with the input. |
Symbol
info — the chosen size (SymbolInfo), modules — the module matrix without the quiet zone (BitMatrix, 1 — dark), codewords — codewords in placement order, segments — which scheme covers which part, text — the string a reader will return. The size table is dmgen::squareSizes().
GS1 and Chestny ZNAK
auto rep = dmgen::gs1::validate("(01)04601234567894(21)ABC", dmgen::Gs1Mode::ChestnyZnak);
for (const auto& i : rep.issues)
std::cout << (i.warning ? "warning: " : "error: ") << i.message << "\n";
// error: AI (01) GTIN: контрольная цифра 4, должна быть 3
// error: «Честный знак»: нет кода проверки — нужен 93 или пара 91 и 92
// (messages come from the library in Russian: "check digit 4, must be 3";
// "Chestny ZNAK: no verification code — need 93 or the pair 91 and 92")The checks cover known AIs, lengths and character sets, GTIN/SSCC/GLN check digits, dates, mandatory separators after variable-length AIs, and repeated AIs. The Chestny ZNAK profile: 01 first, 21 second, verification code 93 (4 characters) or 91 (4) + 92 (44 or 88).
Report::normalized is the string exactly as a reader will return it (with the GS byte). GS1 validation does not require a license.
Image
| Function | What it does |
|---|---|
render(modules, RenderOptions) | Gray8 raster: modulePx (8), quietZone (2; the standard requires at least 1), invert, dark/light, dpi. |
RenderOptions::fromMm(mm, dpi) | Module size in millimeters at the print resolution; the DPI is written to the file (pHYs, JFIF). |
RenderOptions::jpegFriendly() | Module size is a multiple of 8 px: JPEG 8×8 blocks are uniform, with no ringing at the edges. |
toPng, toJpeg, save | PNG (recommended) or JPEG, in memory or to a file; the path is a std::filesystem::path, Unicode works on Windows. JPEG quality below 90 is not recommended for barcodes. |
auto r = dmgen::RenderOptions::fromMm(0.5, 300); // 0.5 mm module at 300 dpi
dmgen::save(dmgen::render(sym->modules, r), "label.png", dmgen::ImageFormat::Png);Errors
Result<T> holds a value or an Error with the fields code and message (UTF-8, in Russian). If you prefer exceptions, value() throws std::runtime_error with the error text.
| ErrorCode | Meaning |
|---|---|
InvalidArgument | Invalid option: size not in the table, empty data… |
DataTooLong | The data does not fit even in 144×144 |
SizeTooSmall | The data does not fit in the explicitly specified size |
UnencodableChar | A character cannot be encoded with the chosen scheme |
Gs1Invalid | The string failed GS1 validation (details via gs1::validate) |
IoError | Failed to write the file or image |
InternalVerifyFailed | The self-check could not read the built symbol — a dmgen bug |
Unlicensed | The trial period has ended and there is no license |
C ABI
The library dmgen_c.dll / libdmgen_c.so, header dmgen_c.h; only dmgen_* symbols are exported. Fixed-size C types only, options carry a struct_size, memory is freed with dmgen_free; there is also a variant that writes into a caller-provided buffer — dmgen_make_image_into.
dmgen_options o; dmgen_error e; uint8_t* png; size_t len;
dmgen_options_init(&o);
o.gs1 = DMGEN_GS1_CHESTNY_ZNAK;
if (dmgen_make_image((const uint8_t*)data, n, &o, DMGEN_FORMAT_PNG, &png, &len, &e) == DMGEN_OK) {
/* … */
dmgen_free(png);
}Library version
DMGEN_VERSION_STRING is the header version, dmgen::versionString() is the version the library was built from. A mismatch shows that the headers and the .a come from different builds.
License
The library encodes while the trial period is running (one month from the first run on the machine, no network or registration needed) or a license is in effect. Otherwise encode() returns ErrorCode::Unlicensed. GS1 validation and rendering an existing matrix do not require a license. The license is bound to the hardware (motherboard, CPU, system disk), not to the OS installation; the trial period and the license are independent of the recognition library on the same machine.
#include <dmgen/License.h>
dmgen::LicenseStatus s = dmgen::license::status();
if (!s.canEncode()) std::cerr << s.message; // «Пробный период закончился. Нужна лицензия» (the trial period has ended; a license is required)
dmgen::license::activate("XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"); // online
dmgen::license::writeActivationRequest("request.json"); // offline: to your account …
dmgen::license::importLicenseFile("license.lic"); // … and back
dmgen::license::deactivate(); // moving to another machinedeactivate() releases the seat with a receipt from this machine. Every license server response is bound to its request: the library sends a random one-time number, the server returns it in a signed response, and a previously recorded response will not be accepted again. The receipt is also single-use — it contains the number from the last response received from the server, so the server will not accept a receipt from a restored copy of the machine's old state; the secret used to sign it is issued by the server only on the machine's first activation. For licenses with full offline autonomy (full), releasing a seat uses up the transfer limit; once it is exhausted, transfers are handled by support.
Put a text file dmgen.key containing the key next to dmgen_c.dll / libdmgen_c.so or next to the program. On the first call into the library the key is sent to the server — once; after that the license checks in with the server once a day on its own in a background thread, and encoding never waits for the network.
States
| LicenseState | Encodes | Meaning |
|---|---|---|
Trial | yes | The trial period is running |
Active | yes | The license is in effect |
RefreshDue | yes | No contact with the server for a while; the library retries hourly on its own |
TrialExpired | no | The trial period has ended — a license is required |
Expired | no | The paid term has ended; a renewal in your account is picked up automatically |
NetworkRequired | no | The offline period has run out — a connection to the server is required |
WrongMachine | no | The license was issued for another machine |
Invalid | no | The license record fails signature verification |
The offline mode is set by the plan: limited — 7 days without a connection plus 7 more after a warning, extended — 30 + 90, full — never needs the network.
Command line
dmgen "0104601234567893215Abc12Xyz!-<GS>93dGVz" --gs1=cz -o code.png
dmgen --check-gs1 --gs1=cz "(01)04601234567893(21)5Abc12Xyz!-(93)dGVz"
dmgen --all-sizes "Hello" --out-dir sizes/
dmgen --batch codes.txt --out-dir out/ --gs1=cz --mm=0.5 --dpi=300
dmgen --license status # state, term, offline mode
dmgen --license activate <key>
dmgen --license deactivate # moving to another machineOptions: --size=, --scheme=, --separator=, --input=, --px=, --quiet=, --mm= with --dpi=, --invert, --jpeg-friendly, --quality=. Without a license the utility exits with code 4.
The library lacks a feature you need, or your case is beyond its limits? We will adapt it to your task. Contact us →