mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-08-03 23:21:44 +00:00
Adds a "Print to Niimbot" panel to the label generator dialog that sends the generated label straight to a Niimbot thermal printer (e.g. B1) over Web Bluetooth, without a PDF print dialog or printer driver. The existing server-side DomPDF label is reused as-is: a new Stimulus controller rasterizes the PDF preview page-by-page with pdf.js at the printer's native resolution, converts it to a 1-bit bitmap and prints it via the niimbluelib library. No changes to the PHP label pipeline are required. The feature is disabled by default and gated behind the NIIMBOT_ENABLED environment variable, since it is only useful for users who own such a printer. - Add @mmote/niimbluelib and pdfjs-dist dependencies - New assets/controllers/pages/niimbot_print_controller.js - Print options (copies, density, label type, rotation, B/W threshold) in the label dialog, gracefully disabled when Web Bluetooth is unavailable - NIIMBOT_ENABLED env flag (off by default), wired through parameters.yaml and a Twig global, documented in .env - English translations and documentation Requires a Chromium-based browser and a secure context (HTTPS or localhost).
338 lines
26 KiB
Markdown
338 lines
26 KiB
Markdown
---
|
||
title: Labels
|
||
layout: default
|
||
parent: Usage
|
||
---
|
||
|
||
# Labels
|
||
|
||
Part-DB supports the generation and printing of labels for parts, part lots and storage locations.
|
||
You can use the "Tools -> Label generator" menu entry to create labels or click the label generation link on the part.
|
||
|
||
You can define label templates by creating label profiles. This way you can create many similar-looking labels for
|
||
many parts.
|
||
|
||
The content of the labels is defined by the template's content field. You can use the WYSIWYG editor to create and style
|
||
the content (or write HTML code).
|
||
Using the "Label placeholder" menu in the editor, you can insert placeholders for the data of the parts.
|
||
It will be replaced by the concrete data when the label is generated.
|
||
|
||
## Label placeholders
|
||
|
||
A placeholder has the format `[[PLACEHOLDER]]` and will be filled with the concrete data by Part-DB.
|
||
You can use the "Placeholders" dropdown in the content editor, to automatically insert the placeholders.
|
||
|
||
### Common
|
||
|
||
| Placeholder | Description | Example |
|
||
|---------------------|--------------------------------------------------------------------|-------------------------|
|
||
| `[[USERNAME]]` | The user name of the currently logged in user | admin |
|
||
| `[[USERNAME_FULL]]` | The full name of the current user | John Doe (@admin) |
|
||
| `[[DATETIME]]` | The current date and time in the selected locale | 31.12.2017, 18:34:11 |
|
||
| `[[DATE]]` | The current date in the selected locale | 31.12.2017 |
|
||
| `[[TIME]]` | The current time in the selected locale | 18:34:11 |
|
||
| `[[INSTALL_NAME]]` | The name of the current installation (see $config['partdb_title']) | Part-DB |
|
||
| `[[INSTANCE_URL]]` | The URL of the current installation | https://demo.part-db.de |
|
||
|
||
### Parts
|
||
|
||
| Placeholder | Description | Example |
|
||
|-------------------------|-------------------------------------------------|-----------------------------|
|
||
| `[[ID]]` | The internal ID of the part | 24 |
|
||
| `[[NAME]]` | The name of the part | ATMega328 |
|
||
| `[[CATEGORY]]` | The name of the category (without path) | AVRs |
|
||
| `[[CATEGORY_FULL]]` | The full path of the category | Aktiv->MCUs->AVRs |
|
||
| `[[MANUFACTURER]]` | The name of the manufacturer | Atmel |
|
||
| `[[MANUFACTURER_FULL]]` | The full path of the manufacturer | Halbleiterhersteller->Atmel |
|
||
| `[[FOOTPRINT]]` | The name of the footprint (without path) | DIP-32 |
|
||
| `[[FOOTPRINT_FULL]]` | The full path of the footprint | Bedrahtet->DIP->DIP-32 |
|
||
| `[[MASS]]` | The mass of the part | 123.4 g |
|
||
| `[[MPN]]` | The manufacturer product number | BC547ACT |
|
||
| `[[TAGS]]` | The tags of the part | SMD, Tag1 |
|
||
| `[[M_STATUS]]` | The manufacturing status of the part | Active |
|
||
| `[[DESCRIPTION]]` | The rich text description of the part | *NPN* |
|
||
| `[[DESCRIPTION_T]]` | The description as plain text | NPN |
|
||
| `[[COMMENT]]` | The rich text comment of the part | |
|
||
| `[[COMMENT_T]]` | The comment as plain text | |
|
||
| `[[LAST_MODIFIED]]` | The datetime when the element was last modified | 2/26/16, 5:38 PM |
|
||
| `[[CREATION_DATE]]` | The datetime when the element was created | 2/26/16, 5:38 PM |
|
||
|
||
### Part lot
|
||
|
||
| Placeholder | Description | Example |
|
||
|-----------------------|---------------------------------------|------------------------|
|
||
| `[[LOT_ID]]` | Part lot ID | 123 |
|
||
| `[[LOT_NAME]]` | Part lot name | |
|
||
| `[[LOT_COMMENT]]` | Part lot comment | |
|
||
| `[[EXPIRATION_DATE]]` | Expiration date of the part lot | |
|
||
| `[[AMOUNT]]` | The amount of parts in this lot | 12 |
|
||
| `[[LOCATION]]` | The storage location of this part lot | Location A |
|
||
| `[[LOCATION_FULL]]` | The full path of the storage location | Location -> Location A |
|
||
|
||
### Storelocation
|
||
|
||
| Placeholder | Description | Example |
|
||
|------------------------|-------------------------------------------------|------------------------|
|
||
| `[[ID]]` | ID of the storage location | |
|
||
| `[[NAME]]` | Name of the storage location | Location A |
|
||
| `[[FULL_PATH]]` | The full path of the storage location | Location -> Location A |
|
||
| `[[PARENT]]` | The name of the parent location | Location |
|
||
| `[[PARENT_FULL_PATH]]` | The full path of the storage location | |
|
||
| `[[COMMENT]]` | The comment of the storage location | |
|
||
| `[[COMMENT_T]]` | The plain text version of the comment |
|
||
| `[[LAST_MODIFIED]]` | The datetime when the element was last modified | 2/26/16, 5:38 PM |
|
||
| `[[CREATION_DATE]]` | The datetime when the element was created | 2/26/16, 5:38 PM |
|
||
|
||
## Twig mode
|
||
|
||
If you select "Twig" in parser mode under advanced settings, you can input a twig template in the lines field (activate
|
||
source mode). You can use most of the twig tags and filters listed
|
||
in [official documentation](https://twig.symfony.com/doc/3.x/).
|
||
|
||
Twig allows you for much more complex and dynamic label generation. You can use loops, conditions, and functions to create
|
||
the label content and you can access almost all data Part-DB offers. The label templates are evaluated in a special sandboxed environment,
|
||
where only certain operations are allowed. Only read access to entities is allowed. However, as it circumvents Part-DB normal permission system,
|
||
the twig mode is only available to users with the "Twig mode" permission.
|
||
|
||
It is useful to use the HTML embed feature of the editor, to have a block where you can write the twig code without worrying about the WYSIWYG editor messing with your code.
|
||
|
||
The following variables are in injected into Twig and can be accessed using `{% raw %}{{ variable }}{% endraw %}` (
|
||
or `{% raw %}{{ variable.property }}{% endraw %}`):
|
||
|
||
| Variable name | Description |
|
||
|--------------------------------------------|--------------------------------------------------------------------------------------|
|
||
| `{% raw %}{{ element }}{% endraw %}` | The target element, selected in label dialog. |
|
||
| `{% raw %}{{ user }}{% endraw %}` | The current logged in user. Null if you are not logged in |
|
||
| `{% raw %}{{ install_title }}{% endraw %}` | The name of the current Part-DB instance (similar to [[INSTALL_NAME]] placeholder). |
|
||
| `{% raw %}{{ page }}{% endraw %}` | The page number (the nth-element for which the label is generated ) |
|
||
| `{% raw %}{{ last_page }}{% endraw %}` | The page number of the last element. Equals the number of all pages / element labels |
|
||
| `{% raw %}{{ paper_width }}{% endraw %}` | The width of the label paper in mm |
|
||
| `{% raw %}{{ paper_height }}{% endraw %}` | The height of the label paper in mm |
|
||
|
||
### Use the placeholders in twig mode
|
||
|
||
You can use the placeholders described above in the twig mode on `element` using the `{% raw %}{{ placeholder('PLACEHOLDER', element) }}{% endraw %}`
|
||
function or the ``{{ "[[PLACEHOLDER]]"|placeholders(element) }}`` filter:
|
||
|
||
```twig
|
||
{% raw %}
|
||
{# The function can be used to get the a single placeholder value of an element, if the placeholder does not exist, null is returned #}
|
||
{{ placeholder('[[NAME]]', element) }}
|
||
|
||
{# The filter can be used to replace all placeholders in a string with the values of the element #}
|
||
{{ "[[NAME]]: [[DESCRIPTION]]"|placeholders(element) }}
|
||
|
||
{# Using the apply environment every placeholder in the apply block will be replaced automatically #}
|
||
{% apply placeholders(element) %}
|
||
[[NAME]]: [[DESCRIPTION]]
|
||
{% endapply %}
|
||
|
||
{# If the block contains HTML use placeholders(element)|raw to prevent escaping of the HTML #}
|
||
{% apply placeholders(element)|raw %}
|
||
<b>[[NAME]]</b>: [[DESCRIPTION]]
|
||
{% endapply %}
|
||
|
||
{% endraw %}
|
||
```
|
||
|
||
### Important entity fields in twig mode
|
||
|
||
In twig mode you have access to many fields of the entity you are generating the label for and their associated entities.
|
||
Following are some important fields of the entities listed. See the [SandboxedTwigFactory service](https://github.com/Part-DB/Part-DB-server/blob/master/src/Services/LabelSystem/SandboxedTwigFactory.php) for the full list of allowed class methods.
|
||
|
||
Please not that the field names might change in the future.
|
||
|
||
#### Part
|
||
|
||
| Field name | Description |
|
||
|---------------------|-----------------------------------------------------------------------------------------------|
|
||
| `id` | The internal ID of the part |
|
||
| `name` | The name of the part |
|
||
| `category` | The category of the part |
|
||
| `manufacturer` | The manufacturer of the part |
|
||
| `footprint` | The footprint of the part |
|
||
| `mass` | The mass of the part |
|
||
| `ManufacturerProductNumber` | The manufacturer product number of the part |
|
||
| `tags` | The tags of the part |
|
||
| `description` | The rich text (markdown) description of the part |
|
||
| `comment` | The rich text (markdown) comment of the part |
|
||
| `lastModified` | The datetime object when the part was last modified |
|
||
| `creationDate` | The datetime object when the part was created |
|
||
| `ipn` | The internal part number of the part |
|
||
| `partUnit` | The unit of the part |
|
||
| `amountSum` | The sum of the amount of all part lots of this part |
|
||
| `amountUnknwon` | Bool: True if there is at least one part lot with unknown amount |
|
||
| `partLots` | The part lots of the part |
|
||
| `parameters` | The parameters of the part |
|
||
| `orderdetails` | The order details of the part as array of Orderdetails |
|
||
|
||
#### Part lot
|
||
|
||
| Field name | Description |
|
||
|---------------------|-----------------------------------------------------------------------------------------------|
|
||
| `id` | The internal ID of the part lot |
|
||
| `name` | The name of the part lot |
|
||
| `comment` | The rich text (markdown) comment of the part lot |
|
||
| `expirationDate` | The expiration date of the part lot (as Datetime object) |
|
||
| `amount` | The amount of parts in this lot |
|
||
| `storageLocation` | The storage location of this part lot |
|
||
| `part` | The part of this part lot |
|
||
| `needsRefill` | Bool: True if the part lot needs a refill |
|
||
| `expired` | Bool: True if the part lot is expired |
|
||
| `vendorBarcode` | The vendor barcode field of the lot |
|
||
|
||
#### Structural entities like categories, manufacturers, footprints, and storage locations
|
||
|
||
| Field name | Description |
|
||
|---------------------|-----------------------------------------------------------------------------------------------|
|
||
| `id` | The internal ID of the entity |
|
||
| `name` | The name of the entity |
|
||
| `comment` | The rich text (markdown) comment of the entity |
|
||
| `parent` | The parent entity of the entity |
|
||
| `children` | The children entities of the entity |
|
||
| `lastModified` | The datetime object when the entity was last modified |
|
||
| `creationDate` | The datetime object when the entity was created |
|
||
| `level` | The level of the entity in the hierarchy |
|
||
| `fullPath` | The full path of the entity (you can pass the delimiter as parameter) |
|
||
| `pathArray` | The path of the entity as array of strings |
|
||
|
||
#### Orderdetails
|
||
|
||
| Field name | Description |
|
||
|---------------------|-----------------------------------------------------------------------------------------------|
|
||
| `id` | The internal ID of the order detail |
|
||
| `part` | The part of the order detail |
|
||
| `supplier` | The supplier/distributor of the order detail |
|
||
| `obsolete` | Bool: True if the order detail is obsolete |
|
||
| `pricedetails` | The price details of the order detail as array of Pricedetails |
|
||
|
||
#### Pricedetails
|
||
|
||
| Field name | Description |
|
||
|---------------------|-----------------------------------------------------------------------------------------------|
|
||
| `id` | The internal ID of the price detail |
|
||
| `price` | The price of the price detail |
|
||
| `currency` | The currency of the price detail |
|
||
| `currencyIsoCode` | The ISO code of the used currency |
|
||
| `pricePerUnit` | The price per unit of the price detail |
|
||
| `priceRelatedQuantity` | The related quantity of the price detail |
|
||
| `minDiscountQuantity` | The minimum discount quantity of the price detail |
|
||
|
||
#### User
|
||
|
||
| Field name | Description |
|
||
|---------------------|-----------------------------------------------------------------------------------------------|
|
||
| `id` | The internal ID of the user |
|
||
| `username` | The username of the user |
|
||
| `email` | The email of the user |
|
||
| `fullName` | The full name of the user |
|
||
| `lastName` | The last name of the user |
|
||
| `firstName` | The first name of the user |
|
||
| `department` | The department of the user |
|
||
|
||
|
||
### Part-DB specific twig functions and filters
|
||
|
||
Part-DB offers some custom twig functions and filters, which can be used in the twig mode and ease the rendering of
|
||
certain data:
|
||
|
||
#### Functions
|
||
|
||
| Function name | Description |
|
||
|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||
| `placeholder(placeholder, element)` | Get the value of a placeholder of an element |
|
||
| `entity_type(element)` | Get the type of an entity as string |
|
||
| `entity_url(element, type)` | Get the URL to a specific entity type page (e.g. `info`, `edit`, etc.) |
|
||
| `barcode_svg(content, type)` | Generate a barcode SVG from the content and type (e.g. `QRCODE`, `CODE128` etc.). A svg string is returned, which you need to data uri encode to inline it. |
|
||
| `associated_parts(element)` | Get the associated parts of an element like a storagelocation, footprint, etc. Only the directly associated parts are returned |
|
||
| `associated_parts_r(element)` | Get the associated parts of an element like a storagelocation, footprint, etc. including all sub-entities recursively (e.g. sub-locations) |
|
||
| `associated_parts_count(element)` | Get the count of associated parts of an element like a storagelocation, footprint, excluding sub-entities |
|
||
| `associated_parts_count_r(element)` | Get the count of associated parts of an element like a storagelocation, footprint, including all sub-entities recursively (e.g. sub-locations) |
|
||
| `type_label(element)` | Get the name of the type of an element (e.g. "Part", "Storage location", etc.) |
|
||
| `type_label_p(element)` | Get the name of the type of an element in plural form (e.g. "Parts", "Storage locations", etc.) |
|
||
|
||
### Filters
|
||
|
||
| Filter name | Description |
|
||
|----------------------------------------------|-----------------------------------------------------------------------------------------------|
|
||
| `format_bytes` | Format a byte count to a human readable string |
|
||
| `format_money(price, currency)` | Format a price to a human readable string with the currency |
|
||
| `format_amount(amount, unit)` | Format an amount to a human readable string with the unit object |
|
||
| `format_si(value, unit_str)` | Format a value using SI prefixes and the given unit string |
|
||
| `placeholders(element)` | Replace all placeholders in a string with the values of the element |
|
||
|
||
## Use custom fonts for PDF labels
|
||
|
||
You can use your own fonts for label generation. To do this, put the TTF files of the fonts you want to use into
|
||
the `assets/fonts/dompdf` folder.
|
||
The filename will be used as name for the font family, and you can use a `_bold` (or `_b`), `_italic` (or `_i`)
|
||
or `_bold_italic` (or `_bi`) suffix to define
|
||
different styles of the font. So for example, if you copy the file `myfont.ttf` and `myfont_bold.ttf` into
|
||
the `assets/fonts/dompdf` folder, you can use the font family `myfont` with regular and bold style.
|
||
Afterward regenerate cache with `php bin/console cache:clear`, so the new fonts will be available for label generation.
|
||
|
||
The fonts will not be available from the UI directly, you have to use it in the HTML directly either by defining
|
||
a `style="font-family: 'myfont';"` attribute on the HTML element or by using a CSS class.
|
||
You can define the font globally for the label, by adding following statement to the "Additional styles (CSS)" option in
|
||
the label generator settings:
|
||
|
||
```css
|
||
* {
|
||
font-family: 'myfont';
|
||
}
|
||
```
|
||
|
||
## Non-latin characters in PDF labels
|
||
|
||
The default used font (DejaVu) does not support all characters. Especially characters from non-latin languages like
|
||
Chinese, Japanese, Korean, Arabic, Hebrew, Cyrillic, etc. are not supported.
|
||
For this, we use [Unifont](http://unifoundry.com/unifont.html) as fallback font. This font supports all (or most) Unicode
|
||
characters but is not as beautiful as DejaVu.
|
||
|
||
If you want to use a different (more beautiful) font, you can use the [custom fonts](#use-custom-fonts-for-pdf-labels)
|
||
feature.
|
||
There is the [Noto](https://www.google.com/get/noto/) font family from Google, which supports a lot of languages and is
|
||
available in different styles (regular, bold, italic, bold-italic).
|
||
For example, you can use [Noto CJK](https://github.com/notofonts/noto-cjk) for more beautiful Chinese, Japanese,
|
||
and Korean characters.
|
||
|
||
## Thermal label printing (Niimbot)
|
||
|
||
Besides generating a PDF (which you print via your operating system's print dialog), Part-DB can send a label
|
||
**directly to a [Niimbot](https://www.niimbot.com/) thermal label printer** (e.g. the B1, B21, D110, …) over
|
||
Bluetooth, without any driver installation. This uses the [niimbluelib](https://github.com/MultiMote/niimbluelib)
|
||
library and the browser's [Web Bluetooth API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API).
|
||
|
||
{: .important }
|
||
> Thermal printing is **disabled by default**, as it is only useful if you own a Niimbot printer. An administrator must
|
||
> enable it by setting the environment variable `NIIMBOT_ENABLED=1` (see [Configuration]({% link configuration.md %})).
|
||
|
||
After you enable it and generate a label in the label generator, a **"Thermal printer (Niimbot)"** panel appears below the preview.
|
||
Set the number of copies, print density, label type and rotation, then click **"Print to Niimbot"** and select your
|
||
printer from the browser's Bluetooth device chooser. The same PDF that is shown in the preview is rasterized in your
|
||
browser at the printer's native resolution (203 dpi for the B1), converted to a black/white bitmap and sent to the
|
||
printer.
|
||
|
||
### Requirements and limitations
|
||
|
||
Because Web Bluetooth runs in the browser, a few conditions must be met:
|
||
|
||
* **The feature must be enabled** by an administrator via `NIIMBOT_ENABLED=1` (off by default).
|
||
* **A Chromium based browser is required** – Chrome, Edge, Opera or Chrome for Android. Firefox and Safari/iOS do
|
||
**not** support Web Bluetooth.
|
||
* **A secure context is required** – the page must be served over **HTTPS**, or accessed via `localhost`. Many
|
||
Part-DB installations run over plain HTTP on the local network; in that case put Part-DB behind a reverse proxy
|
||
with TLS, otherwise the browser will not expose the Bluetooth API and the button is disabled.
|
||
* The printer must be **turned on and paired-able** (not already connected to the phone app).
|
||
|
||
### Tips
|
||
|
||
* **Rotation** – the B1 has a 384 pixel (48 mm) wide print head. If your label is wider than that (e.g. a 50 mm wide
|
||
label = 400 px), rotate it by 90° so the shorter side runs across the print head, otherwise it may be clipped.
|
||
A warning is shown if the rendered bitmap is wider than the print head.
|
||
* **Density** – higher density gives darker prints but may bleed. Leave it on *Auto* to use the printer's default,
|
||
or tune it (1–5 on the B1) for your label material.
|
||
* **B/W threshold** – the label is converted to pure black/white using this luminance threshold (0–254). Increase it
|
||
if thin lines/text disappear, decrease it if the print is too heavy.
|
||
* **Multiple labels** – if you generate labels for several elements at once (e.g. IDs `1,2,5-10`), every page of the
|
||
resulting document is printed in sequence.
|