Skip to content

Label editor - fix image scaling, add barcode size, add part images, image and barcode wrapping options, better label preview - #1589

Open
calebwest-SS wants to merge 1 commit into
Part-DB:masterfrom
calebwest-SS:feat-label-editor-images
Open

calebwest-SS wants to merge 1 commit into
Part-DB:masterfrom
calebwest-SS:feat-label-editor-images

Conversation

@calebwest-SS

Copy link
Copy Markdown
Contributor

This reworks how images and barcodes are handled in the built in label editor, so that the editor pane matches the generated PDF and barcodes/images can be placed freely. Existing labels keep rendering as before (see Compatibility) unless they use features that were broken until now (e.g. image sizing).

My goal here was to make the label editor a much more WYSIWYG editor. Since placeholders are used, obviously there still may be differences, but overall it is much, much closer.

New editor screenshot:

Screenshot 2026-10-07 170654

Editor with manually placed barcode and image placeholder:
Screenshot 2026-10-06 150850

Generated pdf:
Screenshot 2026-10-06 150924


1. Image scaling and image styles in the PDF

Problem: Images resized in the editor were rendered with only their width changed in the PDF (CKEditor writes the width as style, but keeps the original width/height attributes and an aspect-ratio style, which dompdf does not support), so they were squashed. The CKEditor image styles (side, alignment) had no CSS on the label at all.

  • Resized images now keep their aspect ratio.
  • The image toolbar offers Inline, Wrap text (left/right) and Break text (left/center/right). These styles are now rendered in the PDF like in the editor. Wrapped images keep a 4 mm gap to the text.
  • "Break text → centered" uses CKEditor's alignCenter style instead of the class-less default block style, so deliberately centered images can be told apart from unstyled images of existing labels (which keep their old position).
  • The old "Side image" style is mapped to Wrap text → right: the editor converts it when loading (PartDBLabelImageStyles), and the PDF renders both identically, so labels that are not edited again look the same.
  • New resizes are stored in px instead of % (percentages were relative to the much wider editor, not to the label).

2. Barcode size option

  • New optional label option Barcode size (mm): the edge length of 2D barcodes or the width of 1D barcodes. This is available in form section of the label dialog and the label profile editor.
  • If empty (the default and the value for all existing profiles), the existing QR/1D layouts are used unchanged.
  • If set, a table-based layout is used (label_page_barcode.html.twig): 2D codes left of the text with a 4 mm gap, 1D codes at the bottom of the label with their caption. This also fixes 1D barcodes overlapping the text on small labels.
  • Doctrine migration Version20260929120000 adds the nullable column options_barcode_size (MySQL, SQLite, PostgreSQL). The field is included in profile import/export.

3. Barcode placeholder ("Barcode" button)

  • A new Barcode button in the editor toolbar inserts a placeholder image, which is replaced by the barcode of the label when it is generated. It can be positioned like any other image (inline, wrap text, break text).
  • It is inserted at the default position of the barcode type: 2D codes at the start with Wrap text → left, 1D codes at the end with Break text → left.
  • Its size comes from the barcode size option (or is determined automatically); resizing in the editor is disabled for it, and the size dropdown is hidden.
  • The button is disabled without a barcode type; existing placeholders are faded in the editor then and removed from the PDF.
  • If a placeholder is present, the default barcode layout is not used for that label.
  • For Twig mode / handwritten HTML: <img class="partdb-barcode" style="float: right;">.

4. Part image placeholder ("Part image" button)

  • A new Part image button (enabled for part and part lot labels) inserts a placeholder box, which is filled with the main image of the part when the label is generated (part lots use the image of their part).
  • Image selection: the preview image of the parts table (master picture of the part, its footprint or its project); if there is none, the first locally stored picture attachment of the part.
  • The image is centered in the box and scaled so that its larger dimension fits (dompdf does not support object-fit, so this is calculated on the server and done with padding).
  • Permissions: an image is only embedded if the current user may view the attachment file (read, and show_private for private attachments), the same checks as AttachmentFileController.
  • Only locally stored images are used (dompdf cannot load remote resources). Large images are downscaled to max. 600 px before embedding (enough for ~50 mm at 300 dpi), and cached per request (labels for many lots of the same part).
  • Without an image, the box stays empty with its size, so the layout of the label does not change.
  • For Twig mode / handwritten HTML: <img class="partdb-part-image" style="width: 80px; height: 60px;">.

5. Editor improvements

  • Real-size label preview: the editable area is shown in the real size of the label (same scale as the text, 1 mm = 3.78 px), with the page margins, the text column of the barcode layout, a hatched area where the default barcode layout is placed, and a dashed line at the bottom edge of the label. The editor itself keeps its width. Labels are shown black on white also in dark mode.
  • Line heights match the PDF: dompdf uses the height of the font instead of line-height: 1.0, so the editor uses the measured dompdf line heights per font (1.2804 for the DejaVu fonts, separate values for Helvetica, Times, Courier and Unifont). The editor font size is now 12px like the label (was 12pt).
  • Size dropdown in the image toolbar: width and height in mm. This allows for direct sizing rather than guesswork. Normal images keep their aspect ratio (the changed dimension is used); part image placeholders get the exact size.
  • Edge handles on selected images for width and height (in addition to CKEditor's corner handles). Normal images keep their aspect ratio, part image placeholders can be resized freely. One drag = one undo step.
  • The parameters for the editor geometry (page margins, default barcode sizes, legacy layout geometry) are defined once in PHP (LabelHTMLGenerator::getLabelEditorParameters()) and passed to the editor via a data attribute, instead of being duplicated in JS.

6. dompdf workarounds

All of these are only applied to labels with wrapped images or placeholders (labels without them are not parsed or changed at all):

  • Floats inside floats: dompdf cannot place floating elements inside the floated columns of the legacy QR layout. If the content contains wrapped images, the two columns use inline-block instead of float (same geometry).
  • Floats that do not fit: dompdf never moves a float down if it does not fit next to the previous ones (// TODO handle when floating elements don't fit in Block::process_float()) and ignores clear on floats. For consecutive wrapped images, the server checks this like browsers do and moves images that do not fit into a new row via a top margin (with a clearing element as fallback if a height is unknown).
  • Text below floats: dompdf moves text that does not fit next to floats down in steps of the line height (see the TODO in Text::layout_line()), so it often ended up up to one line lower than in the editor. A spacer moves it directly below the images instead.
  • Empty lines: CKEditor saves empty lines as <p>&nbsp;</p>. The space has a width in dompdf (but not in the editor), so such lines could be pushed below wrapped images. They are rendered as <p><br></p> instead.
  • The gap after the last wrapped image of a row may extend into the page margin (in the editor and the PDF), so images can reach the right edge of the label.

Compatibility with existing labels

Verified by comparing the dompdf layout (position and size of every element) of this branch against master:

  • Identical: labels with QR / DataMatrix / 1D / no barcode, the [[BARCODE_*]] and [[IPN_BARCODE_*]] text placeholders, custom CSS for .qr / .C39 / .C39-container, the grid classes in label content, unstyled images (inline and block), plain <figure>s, images in tables.
  • Profile import: profiles exported on master (JSON, XML, CSV) import without errors; the content is unchanged and the barcode size is empty.
  • Database: the migration only adds a nullable column; existing profiles keep the legacy layout.

Intentional changes, which existing users may notice:

  1. Resized images keep their aspect ratio (previously squashed, e.g. 48×15 pt instead of 48×24 pt). Position is unchanged.
  2. Images with the "Side image" style (and other CKEditor image styles) are now positioned in the PDF like in the editor (previously these styles were ignored in the PDF).
  3. Editor only: text is shown at 12 px (was 12 pt), the editing area shows the real label size, and new resizes are stored in px.
  4. Labels with wrapped images or placeholders are parsed and serialized with masterminds/html5; the output is equivalent (e.g. &#039; becomes ').

The "Block image" and "Side image" buttons were removed from the image toolbar; they are replaced by Break text → centered and Wrap text → right.

Dependencies & requirements

No new dependencies. Uses ext-gd (already required) for downscaling, masterminds/html5 (already installed via symfony/dom-crawler) for HTML processing, and Symfony's MimeTypes for the MIME type detection. The editor uses the CSS :has() selector (Chrome 105+, Firefox 121+, Safari 15.4+); in older browsers only some editor styling of placeholders is missing, the generated labels are not affected.

Testing

  • New tests: LabelHTMLGeneratorTest (layouts, legacy layout preserved, placeholders, image fitting, float fitting, spacer, empty lines, editor parameters) and LabelPartImageProviderTest (image selection, part lots, fallback, permissions for private attachments, downscaling).
  • Full PHPUnit suite on top of the current master (SQLite, after migrate + fixtures like in CI): no new failures compared to master (2552 vs. 2530 tests; the 16 OAuth test failures in my local setup occur identically on master).
  • PHPStan (level 5), ESLint, lint:yaml, lint:twig, doctrine:schema:validate and yarn build pass.
  • The migration was run on SQLite; MySQL and PostgreSQL are covered by CI.
  • Manually tested in the browser: barcode/part image buttons, size dropdown and edge handles (incl. undo), label preview, and the generated PDFs for several layouts.

Known limitations

  • The float fitting only handles consecutive wrapped images; if text is between two wrapped images, dompdf's own placement is used.
  • Browsers reserve at least the line height of the paragraph's font for every line, dompdf does not. Lines consisting only of text smaller than 12 px (or in a font with a smaller line height) are therefore shown slightly higher in the editor than on the label.
  • A placed 1D barcode has no caption (unlike the default layout), and a placed QR code is not a link in the PDF.
  • New form strings are only in English (the other languages will come via Crowdin); the editor strings are in English and German.

…image and barcode wrapping options, better label preview
@calebwest-SS

Copy link
Copy Markdown
Contributor Author

I did realize as I was writing up the PR just how much I had wrapped into one. I can split up into multiple PRs for cleaner checking and merging if that's preferable.

@jbtronics

Copy link
Copy Markdown
Member

Yes splitting it up would be good.

Thanks.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants