{"id":51404750,"url":"https://github.com/amigouk/barcodelabelgen","last_synced_at":"2026-07-04T10:00:44.856Z","repository":{"id":357678707,"uuid":"1235458669","full_name":"AmigoUK/BarcodeLabelGen","owner":"AmigoUK","description":"Self-hosted web app for designing label templates and batch-generating PDFs (CSV / Excel / SQLite). Drag-and-drop editor with barcodes, dynamic placeholders, lockable references, partial-import. PL + EN. Docker + Tailscale.","archived":false,"fork":false,"pushed_at":"2026-07-01T14:16:14.000Z","size":12438,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-01T15:24:17.118Z","etag":null,"topics":["barcode-generator","docker","flask","konva","label-design","label-printing","mail-merge","pdf-generation","python","react","reportlab","self-hosted","sqlite","tailscale","typescript"],"latest_commit_sha":null,"homepage":"https://attv.uk/projects/barcodelabelgen.html","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AmigoUK.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-11T10:48:28.000Z","updated_at":"2026-07-01T14:17:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/AmigoUK/BarcodeLabelGen","commit_stats":null,"previous_names":["amigouk/barcodelabelgen"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/AmigoUK/BarcodeLabelGen","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AmigoUK%2FBarcodeLabelGen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AmigoUK%2FBarcodeLabelGen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AmigoUK%2FBarcodeLabelGen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AmigoUK%2FBarcodeLabelGen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AmigoUK","download_url":"https://codeload.github.com/AmigoUK/BarcodeLabelGen/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AmigoUK%2FBarcodeLabelGen/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35117336,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["barcode-generator","docker","flask","konva","label-design","label-printing","mail-merge","pdf-generation","python","react","reportlab","self-hosted","sqlite","tailscale","typescript"],"created_at":"2026-07-04T10:00:26.902Z","updated_at":"2026-07-04T10:00:44.841Z","avatar_url":"https://github.com/AmigoUK.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BarcodeLabelGen\n\n\u003e Web-based label editor and PDF batch generator for non-technical office users.\n\u003e Self-hosted, multilingual (PL + EN), runs on a single Docker host behind Tailscale.\n\n![Editor with a real label template](docs/screenshots/editor.png)\n\n\u003e The editor open on a Zebra 2×1″ template — text, dynamic `{{placeholders}}`, an EAN-128 barcode, all positioned in millimetres. Right panel shows the per-object inspector with Lock + Print-in-PDF toggles, font, size, alignment.\n\n---\n\n## What it does\n\n- **Online label editor** — Konva-powered drag-and-drop canvas (text, text blocks with auto-fit, rectangles, lines, images, barcodes, dynamic `{{column}}` fields).\n- **Lockable + non-printable objects** — pin a logo so it can't be moved; drop a scan of a pre-printed sheet as a layout reference that stays out of the final PDF.\n- **Layer order + alignment + distribute** — z-stack controls (front/back/forward/backward), 6 page-relative + 6 selection-relative align operations, equal distribution.\n- **Duplicate fast** — Alt+drag a selected object to clone it under the cursor; Ctrl/Cmd+D to duplicate in place. Multi-select supported.\n- **Barcodes** — EAN-13, EAN-14, GTIN, Code 128, GS1-128, QR (with checksum validation).\n- **Series generation** — upload a CSV, Excel, or **SQLite** file, map placeholders to columns (or write a custom SELECT), optionally filter rows, get a single PDF with one label per row. Up to 1,000 labels per batch.\n- **ZPL / ZPL II round-trip** — paste an existing ZPL label, adjust element positions on the canvas, and export native ZPL back out (copy to clipboard or download `.zpl`) for use in external software. Single-brace `{VARIABLES}`, `^PQ{NoLabel}` quantity, resident fonts (`E:ARIxxx.TTF`) and native QR are preserved 1:1; a batch mode fills `{{column}}` values from a dataset. DPI is auto-detected from `^PW`/`^LL`.\n- **Editable label size** — resize a template (width × height in mm, with presets) at any time from the editor; undoable and persisted, so a label never has to be recreated just to change its dimensions.\n- **Date placeholders** — `{{date+14d}}`, `{{date+3m:YYYY-MM-DD}}` compute best-before / production dates at generation time (PDF and ZPL alike), with a live preview chip in the editor and month-end-safe arithmetic.\n- **Direct printing via the local connector** — a single-binary Go agent ([`connector/`](connector/README.md)) polls the server's print queue with a per-device Bearer token and forwards ZPL to label printers over RAW TCP 9100 (Zebra / JetDirect compatible). Register devices on the Devices page, then hit 🖨 Print in the editor; job status (queued → printing → done/error) reports back live.\n- **Virtual printer capture** — the same agent can pose as a network printer (JetDirect listener): anything another application prints to it (via the ZDesigner driver on Windows) lands in the web app's **Inbox**, one click away from becoming an editable template — the escape hatch from legacy label software.\n- **Template import / export** — every template is a single self-contained `.blg-template.json` (size, objects, embedded images). Cross-instance portable; partial import lets you skip objects + override the size.\n- **Multilingual UI + in-app docs** — Polish + English from day one, with HELP + FAQ rendered inside the app.\n- **Roles** — admin / editor / viewer; admin manages users + temporary password resets.\n- **Label formats** — A4, A5, A6, common Zebra sizes (4×6\", 4×4\", 3×2\", 2×1\"), plus arbitrary custom mm.\n\n---\n\n## Tour\n\n### 1 · Dashboard\n\n![Dashboard with two CTA cards](docs/screenshots/Dashboard.png)\n\n\u003e First screen after signing in. Two cards point straight at the user's job: open the template editor, or read the in-app guide. Sidebar exposes Dashboard / Templates / Help / Administration → Users.\n\n### 2 · Templates library\n\n![Templates grid](docs/screenshots/templates.png)\n\n\u003e Every template you (or a shared admin) own. Search filters by name; each tile shows the size + version + last-modified date. Hover a tile to reveal Export (⬇) and Delete (✕). Top-right pair: **Import** (from a `.blg-template.json` file) and **New template** (pick a format).\n\n### 3 · Bring your data\n\n![Sample data file in Numbers](docs/screenshots/data-file.png)\n\n\u003e The \"left side\" of the mail-merge flow. Any CSV / Excel / SQLite with column headers works. Here: 60 products with `SKU`, `ProductName`, `Ingredients`, `BatchNo`, `BestBefore`, `EAN-128` — feeds straight into a template that uses `{{SKU}}`, `{{ProductName}}` etc.\n\n### 4 · Generate Series — pick the source\n\n![Generate Series wizard — upload step](docs/screenshots/generate-series.png)\n\n\u003e Step 1 of the 4-step wizard. Accept any of: `.csv`, `.xls`, `.xlsx` (max 10 MB / 1,000 rows) **or** `.db` / `.sqlite` / `.sqlite3` (max 50 MB).\n\n### 5 · SQLite source — table picker + custom SELECT\n\n![SQLite source picker](docs/screenshots/sql-import.png)\n\n\u003e When the uploaded file is a SQLite database the wizard detects every user-visible table (sorted by row count — biggest first) and lets you either pick one or expand \"Show advanced\" to write a `SELECT` with `WHERE`/`JOIN`/`UPPER(...)` etc. Read-only connection; single-statement validator blocks `INSERT` / `UPDATE` / `DELETE` / `DROP` / `ATTACH` / `PRAGMA`; auto-LIMIT 1000.\n\n### 6 · Map placeholders to columns\n\n![Mapping placeholders](docs/screenshots/series-maping-data.png)\n\n\u003e Step 2 detects every `{{name}}` placeholder in the template and auto-maps it to a same-named column. Mismatched names are pickable from a dropdown — the wizard refuses to move on until every placeholder has a source.\n\n![Picking a column manually](docs/screenshots/maping-sql.png)\n\n\u003e The fallback dropdown showing every column the source carries. Works the same whether the source was CSV, Excel, or a SQLite query.\n\n### 7 · Per-row PDF output\n\n![Generated label preview](docs/screenshots/generated-label.png)\n\n\u003e One PDF, one page per row. Each `{{placeholder}}` is replaced by the row's value — here `Victoria Sponge Cake` is row 5 of 60, with its own ingredients, batch number, and GS1-128 barcode. Long ingredients lists wrap inside the text block; the renderer reports a warning if anything doesn't fit.\n\n📄 **[Download a sample PDF →](docs/screenshots/QA_Zebra_2_1__50.pdf)** (50 Zebra 2×1″ labels, 668 KB) — generated from the products dataset above, ready to inspect or print.\n\n### 8 · Import / export templates\n\n![Import template modal](docs/screenshots/import-template.png)\n\n\u003e Every template exports to a single `.blg-template.json` (size + objects + embedded images, all base64). The import modal lets you rename the new template, override the size, and **uncheck** specific objects to bring in — the `{{…}}` chip flags objects that carry dynamic placeholders. Duplicate images (detected by SHA-256) prompt a reuse-vs-copy choice.\n\n### 9 · ZPL / ZPL II round-trip — paste, adjust, copy back\n\n![ZPL import dialog — paste, Auto-detect DPI, Analyze](docs/screenshots/zpl-import.png)\n\n\u003e Paste an existing ZPL label, leave **DPI** on **Auto-detect** (or force 203 / 300), and hit **Analyze**: the elements become editable canvas objects, the dialog reports the detected DPI + object count and warns when the content spills past the label (a wrong-DPI signal). Import keeps the label size you already set; single-brace `{VARIABLES}`, `^PQ{NoLabel}`, resident fonts (`E:ARIxxx.TTF`) and native QR survive the round-trip.\n\n![ZPL export panel — live preview, Copy, Download .zpl](docs/screenshots/zpl-export.png)\n\n\u003e A live native-ZPL preview of the current label with **Copy** and **Download .zpl**. Template mode keeps the variables intact for your external system; batch mode substitutes `{{column}}` values from a dataset and emits one `^XA…^XZ` block per row.\n\n### 10 · Resize the label anytime\n\n![Label-size dialog — width × height in mm with presets](docs/screenshots/label-size.png)\n\n\u003e The size button (**📐 W×H**) in the toolbar opens a dialog to change the label's width × height in millimetres, with one-click presets (40×100, 50×30, 100×150, 105×148, 210×297). The change is undoable and saved to the template — object positions stay put.\n\n---\n\n## Tech stack\n\n| Layer | Technology |\n|---|---|\n| Frontend | React 18 + TypeScript + Vite + Konva + TailwindCSS + react-router 7 + react-i18next + react-markdown |\n| Backend | Python 3.12 + Flask 3 + uv + SQLAlchemy 2 + Alembic + ReportLab + python-barcode + qrcode + pandas + Pillow + pdfplumber |\n| Database | PostgreSQL 16 (production) · SQLite (test fixture) |\n| Cache + sessions + job queue | Redis 7 |\n| Infrastructure | Docker + Docker Compose + nginx |\n| Deployment | Linux host fronted by Tailscale Serve |\n| Tests | pytest (backend, 203 tests) + tsc + eslint (frontend) |\n\n---\n\n## Quick start\n\n```bash\n# Clone\ngit clone https://github.com/AmigoUK/BarcodeLabelGen.git\ncd BarcodeLabelGen\n\n# Build + start (Postgres + Redis + Flask + nginx)\ndocker compose up -d\n\n# First-time DB migration (auto-runs on web container start; explicit form below)\ndocker compose exec web alembic upgrade head\n\n# Bootstrap an admin account (replace email + password)\ndocker compose exec web flask create-admin --email you@example.com --password 'change-me-now-please'\n\n# Open in your browser\nopen http://127.0.0.1:18003\n```\n\n### Behind Tailscale (recommended for production)\n\nThe service binds to `127.0.0.1:18003` only — to expose it to your tailnet (and get a free `*.ts.net` HTTPS cert):\n\n```bash\ntailscale serve --bg --https=18003 http://127.0.0.1:18003\n# → https://\u003cyour-machine\u003e.\u003ctailnet\u003e.ts.net:18003\n```\n\n### Development with hot reload\n\n```bash\ndocker compose -f compose.dev.yml up\n# Vite dev server + Flask debug, proxied through nginx.\n```\n\n---\n\n## Documentation\n\n- 📖 **[`docs/HELP.pl.md`](docs/HELP.pl.md)** / **[`docs/HELP.en.md`](docs/HELP.en.md)** — full user guide (onboarding, every feature, troubleshooting). Same content is rendered in-app at `/help`.\n- ❓ **[`docs/FAQ.pl.md`](docs/FAQ.pl.md)** / **[`docs/FAQ.en.md`](docs/FAQ.en.md)** — common questions + error message recovery.\n- 📐 **[`docs/PROJECT.md`](docs/PROJECT.md)** — original MVP specification (PL).\n\n---\n\n## Project structure\n\n```\nBarcodeLabelGen/\n├── backend/\n│   ├── app/\n│   │   ├── models/        # SQLAlchemy models\n│   │   ├── routes/        # Flask blueprints (REST endpoints)\n│   │   ├── schemas/       # Pydantic request/response models\n│   │   ├── services/      # Business logic (PDF render, batch, datasets, …)\n│   │   └── factory.py     # Flask app factory\n│   ├── alembic/           # DB migrations (0001…0006)\n│   ├── tests/             # pytest — 203 tests\n│   └── pyproject.toml\n├── frontend/\n│   ├── src/\n│   │   ├── components/    # Shared UI (Modal, Button, ImportTemplateModal, …)\n│   │   ├── editor/        # Konva-based label editor (Canvas, panels, store)\n│   │   ├── hooks/         # React-Query data hooks\n│   │   ├── pages/         # Route components (Dashboard, Templates, Editor, Help, …)\n│   │   ├── i18n/locales/  # PL + EN\n│   │   └── lib/           # api client, csrf, download helper\n│   └── package.json\n├── docs/\n│   ├── HELP.{pl,en}.md    # User guide (rendered in-app)\n│   ├── FAQ.{pl,en}.md     # FAQ (rendered in-app)\n│   ├── PROJECT.md         # MVP specification\n│   └── screenshots/       # Images referenced from this README\n├── compose.yml            # Production docker-compose\n└── README.md              # ← you are here\n```\n\n---\n\n## Status\n\n✅ **Production** on a Tailscale-only host.\n- Backend: 203 / 203 tests passing.\n- QA harness (PDF render geometry checks): all formats ✅.\n- Frontend: typecheck + lint + build clean; bundle 153 KB (gzipped 47 KB) main + lazy chunks for editor (Konva) and help (react-markdown).\n\n---\n\n## Credits\n\n**dev@attv.uk · Project \u0026 Development: Tomasz 'Amigo' Lewandowski · [www.attv.uk](https://www.attv.uk) · [GitHub](https://github.com/AmigoUK/BarcodeLabelGen)**\n\n## License\n\nGPL-3.0 — see [`LICENSE`](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famigouk%2Fbarcodelabelgen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famigouk%2Fbarcodelabelgen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famigouk%2Fbarcodelabelgen/lists"}