Обзор
Библиотека строит символы DataMatrix ECC 200 по ГОСТ Р ИСО/МЭК 16022 (ISO/IEC 16022) и отдаёт их картинкой PNG или JPEG — в память или в файл. Поддерживаются все 24 квадратных размера, от 10×10 до 144×144, шесть схем кодирования (ASCII, C40, Text, X12, EDIFACT, Base256) и опция GS1 с проверкой строки по GS1 General Specifications и профилю «Честного знака».
Каждый собранный символ библиотека читает обратно сама (самопроверка): рамки, кодовые слова, синдромы Рида–Соломона, разбор схем. Символ, который не прочитался, наружу не отдаётся.
Публичный интерфейс — на C++20, заголовки в include/dmgen/. Публичный API не бросает исключений: всё, что может не получиться, возвращает Result<T>. Для других языков есть плоский C ABI (dmgen_c) — годится для P/Invoke, JNA, cgo и ctypes.
Windows x86-64 и Linux x86-64, ARM64 и ARMv7 — библиотека собирается и проверяется в том числе для одноплатных компьютеров вроде Raspberry Pi. Сторонний код встроен в поставку, внешних зависимостей нет.
Подключение
Библиотека приходит уже собранной — собирается только ваша программа, которой нужен компилятор C++20. Статическая libdmgen.a требует той же стандартной библиотеки C++, что и у поставки; если это неудобно, подключайте dmgen_c через C ABI — у неё нет таких требований.
include\dmgen\{Dmgen,Encoder,Gs1,Image,License,Options,Result,Symbol,Version,dmgen_c}.h
lib\libdmgen.a
lib\dmgen_c.dll.a
lib\pkgconfig\dmgen.pc
bin\dmgen_c.dll
bin\dmgen.exelibdmgen.a собрана MinGW-w64 GCC — MSVC её не слинкует, нужен тот же тулчейн: MSYS2, консоль MINGW64 (не UCRT64), пакеты pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-pkgconf. Строка линковки — из dmgen.pc:
$env:PKG_CONFIG_PATH = "C:\куда\распакован\lib\pkgconfig"
pkg-config --static --cflags --libs dmgenСистемные winhttp, crypt32, shell32 и advapi32 (проверка лицензии) уже прописаны в пакете.
В meson — зависимость по имени пакета:
dmgen_dep = dependency('dmgen', required: true, static: true)
executable('мояпрограмма', 'main.cpp', dependencies: [dmgen_dep])Быстрый старт
Код «Честного знака» → файл PNG. Всё, что нужно, — один заголовок:
#include <dmgen/Dmgen.h>
#include <iostream>
int main() {
dmgen::EncodeOptions o;
o.gs1 = dmgen::Gs1Mode::ChestnyZnak; // проверка и FNC1 первым словом
auto sym = dmgen::encode("(01)04601234567893(21)5Abc12Xyz!-(93)dGVz", o);
if (!sym) {
std::cerr << sym.error().message << "\n"; // по-русски
return 1;
}
dmgen::RenderOptions r; // 8 px на модуль, свободная зона 2
auto saved = dmgen::save(dmgen::render(sym->modules, r), "code.png", dmgen::ImageFormat::Png);
return saved ? 0 : 1;
}Картинка в память — dmgen::toPng(image) или dmgen::toJpeg(image), обе возвращают Result<std::vector<uint8_t>>.
Кодирование
Result<Symbol> encode(std::string_view data, const EncodeOptions& options = {});Байты 128–255 кодируются через Upper Shift. Текст не перекодируется: строку UTF-8 символ хранит как UTF-8.
EncodeOptions
| Поле | По умолчанию | Смысл |
|---|---|---|
side | 0 | Сторона символа: 0 — наименьший подходящий, иначе 10, 12, 14, 16, 18, 20, 22, 24, 26, 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144. Не из списка — InvalidArgument, не помещается — SizeTooSmall. |
encodation | Auto | Auto — схемы выбирает предпросмотр приложения P ГОСТ; Minimal — кратчайший поток (дольше считается); либо одна схема: Ascii, C40, Text, X12, Edifact, Base256. |
gs1 | Off | Off, Gs1, ChestnyZnak. Строка проверяется до кодирования; нарушение — Gs1Invalid. |
separator | Gs29 | Разделитель групп: Gs29 — байт GS, как в «Честном знаке»; Fnc1 — кодовое слово 232 (только в режиме Gs1). |
gs1Input | Auto | Raw — разделитель байтом 0x1D или записью <GS>, {GS}, \x1D; Bracketed — (01)…(21)…, разделители расставятся сами; Auto — по «(» в начале. |
verify | true | Самопроверка: собранный символ читается обратно и сравнивается со входом. |
Symbol
info — выбранный размер (SymbolInfo), modules — матрица модулей без свободной зоны (BitMatrix, 1 — тёмный), codewords — кодовые слова в порядке размещения, segments — какая схема на каком участке, text — строка, которую вернёт ридер. Таблица размеров — dmgen::squareSizes().
GS1 и «Честный знак»
auto rep = dmgen::gs1::validate("(01)04601234567894(21)ABC", dmgen::Gs1Mode::ChestnyZnak);
for (const auto& i : rep.issues)
std::cout << (i.warning ? "предупреждение: " : "ошибка: ") << i.message << "\n";
// ошибка: AI (01) GTIN: контрольная цифра 4, должна быть 3
// ошибка: «Честный знак»: нет кода проверки — нужен 93 или пара 91 и 92Проверяются известность AI, длины и наборы знаков, контрольные цифры GTIN/SSCC/GLN, даты, обязательные разделители после AI без предопределённой длины и повторы AI. Профиль «Честного знака»: 01 первым, 21 вторым, код проверки 93 (4 знака) или 91 (4) + 92 (44 или 88).
Report::normalized — строка ровно в том виде, в каком её вернёт ридер (с байтом GS). Проверка GS1 лицензии не требует.
Картинка
| Функция | Что делает |
|---|---|
render(modules, RenderOptions) | Растр Gray8: modulePx (8), quietZone (2; ГОСТ — не меньше 1), invert, dark/light, dpi. |
RenderOptions::fromMm(мм, dpi) | Модуль в миллиметрах при разрешении печати; DPI пишется в файл (pHYs, JFIF). |
RenderOptions::jpegFriendly() | Модуль кратен 8 px: блоки 8×8 JPEG однотонные, звона на краях нет. |
toPng, toJpeg, save | PNG (рекомендуется) или JPEG в память или в файл; путь — std::filesystem::path, Юникод на Windows работает. Качество JPEG ниже 90 для штрихкода не рекомендуется. |
auto r = dmgen::RenderOptions::fromMm(0.5, 300); // модуль 0,5 мм при 300 dpi
dmgen::save(dmgen::render(sym->modules, r), "label.png", dmgen::ImageFormat::Png);Ошибки
Result<T> — значение или Error с полями code и message (UTF-8, по-русски). Кому удобнее исключения — value() бросает std::runtime_error с текстом ошибки.
| ErrorCode | Смысл |
|---|---|
InvalidArgument | Неверная опция: размер не из таблицы, пустые данные… |
DataTooLong | Данные не помещаются даже в 144×144 |
SizeTooSmall | Данные не помещаются в явно заданный размер |
UnencodableChar | Знак нельзя закодировать выбранной схемой |
Gs1Invalid | Строка не прошла проверку GS1 (подробности — gs1::validate) |
IoError | Не удалось записать файл или изображение |
InternalVerifyFailed | Самопроверка не прочитала собранный символ — ошибка dmgen |
Unlicensed | Пробный период кончился, лицензии нет |
C ABI
Библиотека dmgen_c.dll / libdmgen_c.so, заголовок dmgen_c.h, экспортируются только dmgen_*. Только C-типы фиксированного размера, опции со struct_size, память освобождает dmgen_free; есть вариант с буфером вызывающего — 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);
}Версия библиотеки
DMGEN_VERSION_STRING — версия заголовков, dmgen::versionString() — версия, с которой собрана библиотека. По расхождению видно, что заголовки и .a от разных сборок.
Лицензия
Библиотека кодирует, пока идёт пробный период (месяц с первого запуска на машине, без сети и регистрации) или действует лицензия. Иначе encode() отвечает ErrorCode::Unlicensed. Проверка GS1 и рисование готовой матрицы лицензии не требуют. Лицензия привязана к оборудованию (материнская плата, процессор, системный диск), а не к установке ОС; пробный период и лицензия не зависят от библиотеки распознавания на той же машине.
#include <dmgen/License.h>
dmgen::LicenseStatus s = dmgen::license::status();
if (!s.canEncode()) std::cerr << s.message; // «Пробный период закончился. Нужна лицензия»
dmgen::license::activate("XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"); // онлайн
dmgen::license::writeActivationRequest("request.json"); // офлайн: в личный кабинет …
dmgen::license::importLicenseFile("license.lic"); // … и обратно
dmgen::license::deactivate(); // переезд на другую машинуdeactivate() сдаёт место распиской этой машины. Каждый ответ сервера лицензий привязан к своему запросу: библиотека отправляет случайное одноразовое число, сервер возвращает его в подписанном ответе, и записанный раньше ответ повторно не примется. Расписка тоже одноразовая — в ней номер из последнего полученного от сервера ответа, поэтому расписку из восстановленной копии старого состояния машины сервер не примет; секрет для её подписи сервер выдаёт только при первой активации машины. У лицензий с полной автономией (full) сдача места расходует лимит переносов; когда он исчерпан, перенос делает поддержка.
Положите текстовый файл dmgen.key с ключом рядом с dmgen_c.dll / libdmgen_c.so или с программой. При первом обращении к библиотеке ключ уйдёт на сервер — один раз; дальше лицензия раз в сутки сама сверяется с сервером в фоновом потоке, кодирование сети не ждёт никогда.
Состояния
| LicenseState | Кодирует | Смысл |
|---|---|---|
Trial | да | Идёт пробный период |
Active | да | Лицензия действует |
RefreshDue | да | Давно не было связи с сервером; библиотека сама пробует раз в час |
TrialExpired | нет | Пробный период кончился — нужна лицензия |
Expired | нет | Оплаченный срок кончился; продление в кабинете подхватится само |
NetworkRequired | нет | Срок работы без связи вышел — нужна связь с сервером |
WrongMachine | нет | Лицензия выдана другой машине |
Invalid | нет | Запись лицензии не проходит проверку подписи |
Режим автономии задаёт тариф: limited — 7 дней без связи и ещё 7 после предупреждения, extended — 30 + 90, full — в сеть не нужно никогда.
Командная строка
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 # состояние, срок, автономия
dmgen --license activate <ключ>
dmgen --license deactivate # переезд на другую машинуКлючи: --size=, --scheme=, --separator=, --input=, --px=, --quiet=, --mm= с --dpi=, --invert, --jpeg-friendly, --quality=. Без лицензии утилита завершается с кодом 4.
Нужной функции нет в библиотеке или ваш случай за её границами — доработаем под вашу задачу. Напишите нам →