This commit is contained in:
j3d1 2026-08-24 21:35:38 +02:00
parent f87b689e27
commit 0c025db799
16 changed files with 555 additions and 105 deletions

View file

@ -154,7 +154,7 @@ export default {
// See docs/implementation.md#print-link-shape-for-personal-items.
printLinkFor(item) {
if (!item.owner) return null
return {path: '/print', query: {kind: 'item', userHandle: item.owner, id: item.id}}
return {path: '/print', query: {kind: 'item', userHandle: item.owner, item: item.id}}
},
},
async mounted() {

View file

@ -51,7 +51,7 @@
Delete
</button>
<button v-if="canPrint" class="btn btn-secondary"
@click="$router.push({path: '/print', query: {kind: 'item', userHandle: decodedHandle, id}})">
@click="$router.push({path: '/print', query: {kind: 'item', userHandle: decodedHandle, item: id}})">
<b-icon-printer></b-icon-printer>
Print label
</button>

View file

@ -57,7 +57,7 @@
</div>
<div class="card-body">
<p class="text-muted small mb-3">
Nine pixel/bitmap-style fonts found in frontend/public, rendered live from the "Text"
Ten pixel/bitmap-style fonts found in frontend/public, rendered live from the "Text"
field above at a range of sizes so they can be judged the same way Tom Thumb/Silkscreen
were. <strong>CodersCrux</strong> and <strong>712Serif</strong> fail to load as web
fonts at all - Chromium's OTS sanitizer rejects their cmap table - so their cells below
@ -86,7 +86,7 @@
<h5 class="card-title mb-0">Label preview</h5>
</div>
<div class="card-body">
<div class="label-preview mb-3" v-show="selectedContent">
<div class="label-preview mb-3" v-show="fallbackReady">
<canvas ref="fallbackCanvas"></canvas>
</div>
@ -144,7 +144,7 @@
Connect a printer to preview and print a label.
</p>
<template v-else>
<div class="preview-row mb-3" v-show="selectedContent">
<div class="preview-row mb-3" v-show="labelBitmap">
<div class="ruler-v">
<div class="ruler-v-corner"></div>
<div class="ruler ruler-v-ticks"
@ -306,6 +306,12 @@ export default {
prefill: {
type: Object,
default: null
},
// Every query param other than `kind` (router.js), so any base var - e.g. ?text=... - can
// be set directly without needing a `kind` builder in label-layouts.js.
queryFields: {
type: Object,
default: () => ({})
}
},
data() {
@ -327,13 +333,14 @@ export default {
// Scan-reliability messages from label.js's drawLabel/drawFallbackLabel (too few px per QR module, or too small in mm) - the label still rendered/prints fine, just flagged as a risk.
warnings: [],
// One input per BASE_VARS entry; derived vars (userHandle/itemUrl/itemHandle) are calculated-only (see the `fields` computed), never stored here. Prefilled from query params but left editable.
// One input per BASE_VARS entry; derived vars (userHandle/itemUrl/itemHandle) are calculated-only (see the `fields` computed), never stored here. Prefilled from query params but left editable. queryFields is applied last so an explicit ?text=... etc. always wins over a `kind` builder's computed value.
varValues: {
...Object.fromEntries(BASE_VARS.map(v => [v, ""])),
text: buildLabelContent(this.prefill),
// Defaults to this page's own origin; editable since any frontend can resolve any handle, so a label needn't point back at this one.
webdomain: window.location.origin,
...buildLabelFields(this.prefill),
...Object.fromEntries(BASE_VARS.filter(v => v in this.queryFields).map(v => [v, this.queryFields[v]])),
},
copies: 1,
selectedTemplate: LABEL_TEMPLATES[0].id,
@ -347,7 +354,7 @@ export default {
brotherCliExample: "brother_ql --model QL-000 --printer usb://0000:0000 print --label 00 label.png",
niimbotCliExample: "niimprint --model b00 --conn usb print --density 3 --image label.png",
// Nine pixel/bitmap font candidates for legibility testing only (see the disabled card above); not used by label.js's real PIXEL_FONT_TIERS.
// Ten pixel/bitmap font candidates for legibility testing only (see the disabled card above); not used by label.js's real FONT_TIERS.
candidateFonts: [
{family: "Pixelon"},
{family: "Pixelbasel"},
@ -358,6 +365,7 @@ export default {
{family: "6pxExpert"},
{family: "Jersey10"},
{family: "Jersey15"},
{family: "Terminus"},
],
candidateSizes: [5, 6, 7, 8, 10, 12, 16, 20],
candidateZoom: 4,
@ -395,9 +403,6 @@ export default {
currentTemplate() {
return LABEL_TEMPLATES.find(t => t.id === this.selectedTemplate) || LABEL_TEMPLATES[0];
},
selectedContent() {
return templateContent(this.currentTemplate, this.fields);
},
deviceRows() {
if (!this.blob) {
return [];
@ -625,8 +630,7 @@ export default {
redraw() {
this.labelBitmap = null;
const content = this.selectedContent;
if (!this.tape || !content) {
if (!this.tape) {
return;
}
const canvas = this.$refs.labelCanvas;
@ -636,6 +640,7 @@ export default {
this.resizeObserver.observe(canvas.parentElement);
let textSizesPx, warnings;
try {
const content = templateContent(this.currentTemplate, this.fields);
({textSizesPx, warnings} = drawLabel(canvas, this.tape, content, this.orientation));
} catch (e) {
this.error = e.message;
@ -653,10 +658,6 @@ export default {
redrawFallback() {
this.fallbackReady = false;
const content = this.selectedContent;
if (!content) {
return;
}
const canvas = this.$refs.fallbackCanvas;
if (!canvas) {
return;
@ -664,6 +665,7 @@ export default {
this.resizeObserver.observe(canvas.parentElement);
let warnings;
try {
const content = templateContent(this.currentTemplate, this.fields);
({warnings} = drawFallbackLabel(canvas, content, this.orientation));
} catch (e) {
this.error = e.message;
@ -739,7 +741,7 @@ export default {
}
},
// Renders the "Text" field at exact size/family with no layout snapping, so the font itself is judged; ink-centered vertically (like label.js's drawTextLeaf) so unreliable metrics don't clip.
// Renders the "Text" field at exact size/family with no layout snapping, so the font itself is judged; ink-centered vertically (like label.js's drawTextLeaf) so unreliable metrics don't clip. Thresholded through canvasToBitmap/bitmapToCanvas afterwards, same as redraw()'s real label preview, so a candidate is judged on the same 1-bit dots the printer would actually fire, not the anti-aliased canvas render.
drawCandidateCell(canvas, family, fontPx) {
if (!canvas) {
return;
@ -758,6 +760,7 @@ export default {
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#000";
ctx.fillText(text, 2, (canvas.height + up - down) / 2);
bitmapToCanvas(canvas, canvasToBitmap(canvas));
canvas.style.width = `${canvas.width * this.candidateZoom}px`;
canvas.style.height = `${canvas.height * this.candidateZoom}px`;
},

View file

@ -136,7 +136,7 @@ export default {
// Routes to Print.vue with this location's raw identity, same shape as Inventory.vue's
// printLinkFor. See docs/implementation.md#print-link-shape-for-storage-locations.
printLinkFor(location) {
return {path: '/print', query: {kind: 'storage-location', userHandle: location.owner, id: location.id}}
return {path: '/print', query: {kind: 'storage-location', userHandle: location.owner, location: location.id}}
},
},
async mounted() {