stash
This commit is contained in:
parent
1356aa7749
commit
491ee05f15
13 changed files with 328 additions and 402 deletions
|
|
@ -50,40 +50,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-3 border-warning">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title mb-0">Experimental: optimal font by size</h5>
|
||||
<small class="text-muted">for judging legibility - not part of the real label pipeline</small>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small mb-3">
|
||||
For every nominal size from 5px to 30px, the font and effective size
|
||||
<code>optimal_fonts</code> actually picks for it - rendered live from the "Text"
|
||||
field above through libfreetype-wasm's own monochrome rasterizer
|
||||
(FT_LOAD_TARGET_MONO), a true 1-bit render, not canvas fillText's grayscale AA
|
||||
thresholded after the fact. The line height under each canvas is
|
||||
<code>ft_get_line_height</code> at that cell's actually-drawn size, the same
|
||||
metric prototypes/freetype-wasm's own demo shows.
|
||||
</p>
|
||||
<div v-if="freetypeError" class="alert alert-warning small py-2">
|
||||
FreeType failed to load: {{ freetypeError }} - canvases will stay blank.
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-3">
|
||||
<div v-for="size in previewSizes" :key="size" class="text-center">
|
||||
<div class="small text-muted mb-1">{{ size }}px</div>
|
||||
<canvas :ref="el => setPreviewCanvasRef(size, el)"
|
||||
class="pixel-test-canvas"></canvas>
|
||||
<div class="small text-muted mt-1" v-if="optimalFontFor(size)">
|
||||
{{ optimalFontFor(size).family }} @{{ optimalFontFor(size).size }}px
|
||||
</div>
|
||||
<div class="small text-muted" v-if="freetypeLineHeights[size] !== undefined">
|
||||
line height: {{ freetypeLineHeights[size] }}px
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!usbSupported" class="row">
|
||||
<div class="col-lg-7">
|
||||
<div class="card">
|
||||
|
|
@ -280,22 +246,11 @@ import {
|
|||
drawFallbackLabel,
|
||||
buildLabelContent,
|
||||
buildLabelFields,
|
||||
preloadQrEncoder
|
||||
preloadQrEncoder,
|
||||
preloadPixelFontRenderer
|
||||
} from "@/label.js";
|
||||
import {LABEL_TEMPLATES, BASE_VARS, DERIVED_VARS, withDerivedVars, templateContent} from "@/label-layouts.js";
|
||||
import {shortenedRoute} from "@/router";
|
||||
import {loadFreeType, loadFontFace, drawMonoText} from "@/freetype-preview.js";
|
||||
|
||||
// URLs (not raw bytes - fetched lazily, see drawOptimalFontPreviews) for each font optimal_fonts
|
||||
// can pick, fed to freetype-preview.js's loadFontFace. Silkscreen and Inter each need their own TTF
|
||||
// copy (see pixel-candidates/LICENSE.md) since this build of FreeType can't parse WOFF2 (no brotli
|
||||
// support - confirmed: FT error 2 on both production pixel/Silkscreen-Regular.woff2 and
|
||||
// label/Inter-Regular.woff2).
|
||||
import pixelonFontUrl from "@/assets/fonts/pixel-candidates/Pixelon.ttf?url";
|
||||
import chavaFontUrl from "@/assets/fonts/pixel-candidates/Chava.ttf?url";
|
||||
import terminusFontUrl from "@/assets/fonts/pixel-candidates/Terminus.ttf?url";
|
||||
import silkscreenCandidateFontUrl from "@/assets/fonts/pixel-candidates/Silkscreen.ttf?url";
|
||||
import interCandidateFontUrl from "@/assets/fonts/pixel-candidates/Inter.ttf?url";
|
||||
|
||||
// Print.vue-local calculated fields on top of label-layouts.js's DERIVED_VARS. See
|
||||
// docs/implementation.md#calculated-short-link-fields.
|
||||
|
|
@ -320,41 +275,6 @@ const RULER_TIERS = [
|
|||
{aboveMm: 500, tickMm: 5, majorEveryMm: 25},
|
||||
];
|
||||
|
||||
//after manually comparing all fonts at all sizes, it was decides that in some cases it's more readable to round down the font size to a more readable
|
||||
// this is a lookup table for these optimal settings
|
||||
// An entry may also set `lineHeight` (px) and/or `tracking` (px, may be negative) to retune a
|
||||
// tier's spacing without touching its rendered glyphs at all - see drawMonoText's own doc comment
|
||||
// in freetype-preview.js for why (scaling a pixel font's bitmap to fit blurs it, so a tighter
|
||||
// lineHeight crops ascenders/descenders at the row edge instead, and tracking just overlaps
|
||||
// neighboring glyphs). Omit either field to fall back to the face's natural metrics.
|
||||
const optimal_fonts = (size) => {
|
||||
if (size > 32)
|
||||
return {family: "Inter", url: interCandidateFontUrl, size: size, lineHeight: size};
|
||||
if (size === 32)
|
||||
return {family: "Terminus", url: terminusFontUrl, size: 32, lineHeight: size}; // would even support bold mode
|
||||
if (size >= 28)
|
||||
return {family: "Terminus", url: terminusFontUrl, size: 28, lineHeight: size}; // would even support bold mode
|
||||
if (size >= 24)
|
||||
return {family: "Terminus", url: terminusFontUrl, size: 24, lineHeight: size}; // would even support bold mode
|
||||
if (size >= 12)
|
||||
return {family: "Terminus", url: terminusFontUrl, size: (size - (size % 2)), lineHeight: size}; // would even support bold mode >=14px
|
||||
if (size >= 10)
|
||||
return {family: "Pixelon", url: pixelonFontUrl, size: 10, lineHeight: size, tracking: -1};
|
||||
if (size >= 8)
|
||||
return {family: "Chava", url: chavaFontUrl, size: 8, lineHeight: size};
|
||||
// TODO find or build better fonts for 6px, 7px and 9px
|
||||
// The criteria would be to make better use of the height available to be more readable
|
||||
if (size >= 6)
|
||||
return {family: "Silkscreen", url: silkscreenCandidateFontUrl, size: 8, lineHeight: size, tracking: -1}; //Silkscreen comes out to an effective height of 5px at size 8
|
||||
return null;
|
||||
};
|
||||
|
||||
// ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
// abcdefghijklmnopqrstuvwxyz
|
||||
// 0123456789-_@~ /:
|
||||
// iiiiiiiiiiiiiiiiiiiiiiiiii
|
||||
// mmmmmmmmmmmmmmmmmmmmmmmmmm
|
||||
|
||||
export default {
|
||||
name: "Print",
|
||||
components: {
|
||||
|
|
@ -414,20 +334,6 @@ export default {
|
|||
// TODO: replace with the real commands for our printers.
|
||||
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",
|
||||
|
||||
// Nominal sizes previewed by the "optimal font by size" card - 5px to 30px inclusive.
|
||||
previewSizes: Array.from({length: 36}, (_, i) => i + 6),
|
||||
previewZoom: 4,
|
||||
// Keyed by family, populated lazily the first time drawOptimalFontPreviews loads a font -
|
||||
// avoids refetching the same bytes on every varValues-triggered redraw.
|
||||
freetypeFontBytes: {},
|
||||
// Set if FreeType itself (not a specific font) fails to load - shown once above the grid
|
||||
// instead of failing every cell silently.
|
||||
freetypeError: null,
|
||||
// Keyed by nominal size - each cell's effective line height in px (ft_get_line_height at
|
||||
// the actually-drawn size), same metric prototypes/freetype-wasm's own index.html demo
|
||||
// shows below its canvas.
|
||||
freetypeLineHeights: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -523,7 +429,6 @@ export default {
|
|||
} else {
|
||||
this.redrawFallback();
|
||||
}
|
||||
this.drawOptimalFontPreviews();
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
|
|
@ -790,66 +695,14 @@ export default {
|
|||
await this.autoConnectPreferred();
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
// Looks up the (family, url, effective size) optimal_fonts picks for a nominal size -
|
||||
// exposed as a method (not a computed) since the template calls it per-size inside a v-for.
|
||||
optimalFontFor(size) {
|
||||
return optimal_fonts(size);
|
||||
},
|
||||
|
||||
setPreviewCanvasRef(size, el) {
|
||||
if (el) {
|
||||
this.freetypeCanvases[size] = el;
|
||||
} else {
|
||||
delete this.freetypeCanvases[size];
|
||||
}
|
||||
},
|
||||
|
||||
// Draws every previewSizes cell - see the card in the template. For each nominal size, looks
|
||||
// up the font optimal_fonts actually picks and draws through libfreetype-wasm's own
|
||||
// FT_LOAD_TARGET_MONO rasterizer - a true 1-bit render, not canvas fillText's grayscale AA
|
||||
// thresholded after the fact. Font bytes are cached per family (see freetypeFontBytes) since
|
||||
// several sizes can share the same font.
|
||||
async drawOptimalFontPreviews() {
|
||||
let ft;
|
||||
try {
|
||||
ft = await loadFreeType();
|
||||
} catch (e) {
|
||||
this.freetypeError = e.message;
|
||||
return;
|
||||
}
|
||||
this.freetypeError = null;
|
||||
for (const size of this.previewSizes) {
|
||||
const cfg = optimal_fonts(size);
|
||||
const canvas = this.freetypeCanvases[size];
|
||||
if (!cfg || !canvas) {
|
||||
continue;
|
||||
}
|
||||
let bytes = this.freetypeFontBytes[cfg.family];
|
||||
if (!bytes) {
|
||||
const res = await fetch(cfg.url);
|
||||
bytes = new Uint8Array(await res.arrayBuffer());
|
||||
this.freetypeFontBytes[cfg.family] = bytes;
|
||||
}
|
||||
if (loadFontFace(ft, bytes) !== 0) {
|
||||
continue; // fails to parse for FreeType too - leave that cell blank.
|
||||
}
|
||||
this.freetypeLineHeights[size] =
|
||||
drawMonoText(ft, canvas, cfg.size, this.varValues.text, this.previewZoom,
|
||||
{lineHeight: cfg.lineHeight, tracking: cfg.tracking});
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.blob = null;
|
||||
this.resizeObserver = null;
|
||||
this.resizeTimer = null;
|
||||
this.freetypeCanvases = {};
|
||||
this.recentTemplateIds = this.loadRecentTemplateIds();
|
||||
},
|
||||
async mounted() {
|
||||
this.drawOptimalFontPreviews();
|
||||
// Not awaited: resolving late just means shortUrl briefly shows "—" instead of blocking the unrelated printer/wasm setup below.
|
||||
this.fetchIdMap().catch(e => {
|
||||
this.error = e.message;
|
||||
|
|
@ -857,9 +710,12 @@ export default {
|
|||
this.resizeObserver = new ResizeObserver(this.handleContainerResize);
|
||||
// Loaded concurrently with MultiPrinterBlob below rather than serialized. See docs/implementation.md#concurrent-wasm-loading.
|
||||
const qrReady = preloadQrEncoder();
|
||||
// Same reasoning - drawTextLeaf's bitmap-font tiers (see label.js's fontTierFor) need this
|
||||
// loaded before the first real draw, same as qrReady below.
|
||||
const fontsReady = Promise.all([qrReady, preloadPixelFontRenderer()]);
|
||||
if (!("usb" in navigator)) {
|
||||
this.usbSupported = false;
|
||||
await Promise.all([nextTick(), qrReady]);
|
||||
await Promise.all([nextTick(), fontsReady]);
|
||||
this.redrawFallback();
|
||||
return;
|
||||
}
|
||||
|
|
@ -872,7 +728,7 @@ export default {
|
|||
this.blobReady = true;
|
||||
navigator.usb.addEventListener("connect", this.onUsbChange);
|
||||
navigator.usb.addEventListener("disconnect", this.onUsbChange);
|
||||
await qrReady;
|
||||
await fontsReady;
|
||||
await this.guard(async () => {
|
||||
await this.refreshDevices();
|
||||
await this.autoConnectPreferred();
|
||||
|
|
@ -1002,13 +858,6 @@ export default {
|
|||
width: 5.5rem;
|
||||
}
|
||||
|
||||
.pixel-test-canvas {
|
||||
display: block;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 1px rgba(127, 127, 127, .5);
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
background: rgba(127, 127, 127, .08);
|
||||
border-radius: .35rem;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue