mirror of
https://github.com/tmaier/asn-qr-code-label-generator.git
synced 2026-03-14 08:33:37 +00:00
This commit adds several new customization options to improve label alignment and output quality when printing QR code labels: - Introduced vertical and horizontal offset inputs to shift the entire label grid for fine alignment. - Added label height input to adjust the height of each individual label in millimeters. - Added content scale input to resize the entire label content (QR code and text). Also updated the UI to include explanatory text for each of these parameters, and added a summary paragraph to the main instructions describing how each setting affects the layout.
374 lines
15 KiB
HTML
374 lines
15 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>QR Code Label Generator for ASN in Paperless-ngx</title>
|
|
<link
|
|
rel="canonical"
|
|
href="https://tobiasmaier.info/asn-qr-code-label-generator/"
|
|
/>
|
|
<script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script>
|
|
<script
|
|
src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"
|
|
defer
|
|
></script>
|
|
<style>
|
|
@media print {
|
|
/* See https://caniuse.com/css-paged-media */
|
|
@page {
|
|
/* Remove default margin set by the browser */
|
|
margin: 0mm;
|
|
size: A4;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="print:p-0" x-data="qrCodeApp()">
|
|
<div class="container mx-auto print:hidden">
|
|
<h1 class="mb-4 text-xl font-bold print:hidden">
|
|
QR Code Label Generator for
|
|
<abbr title="Archive Serial Number">ASN</abbr> in Paperless-ngx
|
|
</h1>
|
|
<div
|
|
class="prose prose-a:text-blue-600 prose-a:underline hover:prose-a:text-blue-700 mb-4 print:hidden"
|
|
>
|
|
<p>
|
|
This tool generates QR code labels for the
|
|
<a
|
|
href="https://docs.paperless-ngx.com/advanced_usage/#archive-serial-number-assignment"
|
|
>Archive Serial Number (ASN)</a
|
|
>
|
|
feature in
|
|
<a href="https://docs.paperless-ngx.com/">Paperless-ngx</a>.
|
|
</p>
|
|
|
|
<p>
|
|
The <abbr title="Archive Serial Number">ASN</abbr> feature allows you
|
|
to assign a unique serial number to each document in your archive.
|
|
This is useful if you want to refer to a document in a paperless
|
|
workflow. For example, you can print the
|
|
<abbr title="Archive Serial Number">ASN</abbr>
|
|
on a document and then use it to search for the document in
|
|
Paperless-ngx.
|
|
</p>
|
|
|
|
<p>
|
|
This tool generates QR code labels for the
|
|
<abbr title="Archive Serial Number">ASN</abbr> feature. You can
|
|
generate labels for up to 189 documents at once. The labels are
|
|
formatted to <strong>fit on Avery L4731REV-25 labels</strong>.
|
|
</p>
|
|
|
|
<p>
|
|
To use this tool, enter the start number, prefix, and number of
|
|
leading zeros. Then click the "Regenerate Labels" button. The labels
|
|
will be generated and displayed below. You can then print the labels
|
|
by clicking the "Print Labels" button.
|
|
</p>
|
|
|
|
<p>
|
|
To fine-tune the label alignment for your specific printer or label sheet, you can adjust the following parameters:
|
|
<strong>Vertical Offset</strong> moves the entire label grid up or down on the page, while
|
|
<strong>Horizontal Offset</strong> shifts it left or right.
|
|
<strong>Label Height</strong> controls the height of each individual label, and
|
|
<strong>Content Scale</strong> resizes the text and QR code inside each label.
|
|
The <strong>Show Border</strong> option adds a visible border around each label, which can help with calibration and print alignment.
|
|
Use these settings to ensure the labels are printed accurately on your sheet.
|
|
</p>
|
|
|
|
<p>
|
|
One <strong>known limitation of this tool</strong> is that it
|
|
<strong>only works on Google Chrome and possibly Firefox</strong>.
|
|
This is because it uses the
|
|
<a href="https://caniuse.com/css-paged-media"><code>@page</code></a>
|
|
CSS rule to control the page size, margin, and to turn off the default
|
|
header and footer of the browser.
|
|
</p>
|
|
</div>
|
|
<form @submit.prevent="generateLabels()" class="mb-8 print:hidden">
|
|
<!-- User Input Fields -->
|
|
<div class="mb-5 grid grid-cols-1 gap-5 md:grid-cols-2">
|
|
<div>
|
|
<label
|
|
for="startNumber"
|
|
class="mb-2 block text-sm font-medium text-gray-900"
|
|
>Start Number</label
|
|
>
|
|
<input
|
|
type="number"
|
|
id="startNumber"
|
|
x-model="startNumber"
|
|
required
|
|
min="1"
|
|
step="1"
|
|
value="1"
|
|
class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500"
|
|
/>
|
|
<div class="mt-1">
|
|
<p
|
|
class="prose prose-sm prose-a:text-blue-600 prose-a:underline hover:prose-a:text-blue-700 max-w-none"
|
|
>
|
|
The number of labels generated will be 7 x 27 = 189. This means
|
|
the <strong>next batch</strong> of labels should
|
|
<strong
|
|
>start with
|
|
<span x-text="parseInt(startNumber, 10) + 189"
|
|
>190</span
|
|
></strong
|
|
>.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label
|
|
for="prefix"
|
|
class="mb-2 block text-sm font-medium text-gray-900"
|
|
>Prefix</label
|
|
>
|
|
<input
|
|
type="text"
|
|
id="prefix"
|
|
x-model="prefix"
|
|
required
|
|
value="ASN"
|
|
class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500"
|
|
/>
|
|
<div class="mt-1">
|
|
<p
|
|
class="prose prose-sm prose-a:text-blue-600 prose-a:underline hover:prose-a:text-blue-700 max-w-none"
|
|
>
|
|
If you change this, <strong>update the prefix</strong> in
|
|
Paperless-ngx accordingly via
|
|
<a
|
|
href="https://docs.paperless-ngx.com/configuration/#PAPERLESS_CONSUMER_ASN_BARCODE_PREFIX"
|
|
><code>PAPERLESS_CONSUMER_ASN_BARCODE_PREFIX</code></a
|
|
>.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label
|
|
for="leadingZeros"
|
|
class="mb-2 block text-sm font-medium text-gray-900"
|
|
>Number of Leading Zeros</label
|
|
>
|
|
<input
|
|
type="number"
|
|
id="leadingZeros"
|
|
x-model="leadingZeros"
|
|
required
|
|
value="5"
|
|
min="0"
|
|
class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500"
|
|
/>
|
|
</div>
|
|
<div></div>
|
|
<div>
|
|
|
|
<div class="mt-1">
|
|
<p
|
|
class="prose prose-sm prose-a:text-blue-600 prose-a:underline hover:prose-a:text-blue-700 max-w-none"
|
|
>
|
|
The following parameters are useful for debugging or to calibrate the print out.
|
|
</p>
|
|
</div>
|
|
|
|
</div>
|
|
<div>
|
|
<div class="flex items-start">
|
|
<div class="flex h-5 items-center">
|
|
<input
|
|
type="checkbox"
|
|
id="borderToggle"
|
|
x-model="borderToggle"
|
|
class="focus:ring-3 h-4 w-4 rounded border border-gray-300 bg-gray-50 focus:ring-blue-300"
|
|
/>
|
|
</div>
|
|
<label
|
|
for="borderToggle"
|
|
class="ms-2 text-sm font-medium text-gray-900"
|
|
>Show Border</label
|
|
>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div>
|
|
<label for="verticalOffset" class="block text-sm font-medium">Vertical Offset (mm)</label>
|
|
<input type="number" id="verticalOffset" x-model="verticalOffset" step="0.1" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500" />
|
|
<div class="mt-1">
|
|
<p
|
|
class="prose prose-sm prose-a:text-blue-600 prose-a:underline hover:prose-a:text-blue-700 max-w-none"
|
|
>
|
|
Adjusts the vertical position of the entire label sheet on the page. Use this if the labels are printing too high or too low.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label for="horizontalOffset" class="block text-sm font-medium">Horizontal Offset (mm)</label>
|
|
<input type="number" id="horizontalOffset" x-model="horizontalOffset" step="0.1" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500" />
|
|
<div class="mt-1">
|
|
<p
|
|
class="prose prose-sm prose-a:text-blue-600 prose-a:underline hover:prose-a:text-blue-700 max-w-none"
|
|
>
|
|
Adjusts the horizontal position of the entire label sheet on the page. Use this if the labels are printing too far to the left or right.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label for="labelHeight" class="block text-sm font-medium">Label Height (mm)</label>
|
|
<input type="number" id="labelHeight" x-model="labelHeight" step="0.01" min="8" max="12" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500" />
|
|
<div class="mt-1">
|
|
<p
|
|
class="prose prose-sm prose-a:text-blue-600 prose-a:underline hover:prose-a:text-blue-700 max-w-none"
|
|
>
|
|
Sets the height of each individual label in millimeters. Adjust this if the labels don't align vertically with the label sheet.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label for="scale" class="block text-sm font-medium">Content Scale (%)</label>
|
|
<input type="number" id="scale" x-model="scale" min="50" max="100" step="0.1" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500" />
|
|
<div class="mt-1">
|
|
<p
|
|
class="prose prose-sm prose-a:text-blue-600 prose-a:underline hover:prose-a:text-blue-700 max-w-none"
|
|
>
|
|
Scales the entire content inside each label. Use this if the text or QR code is too large or too small.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<button
|
|
@click.prevent="generateLabels()"
|
|
class="w-full rounded-lg bg-blue-700 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 sm:w-auto"
|
|
>
|
|
Regenerate Labels
|
|
</button>
|
|
<button
|
|
@click.prevent="printLabels()"
|
|
class="w-full rounded-lg bg-blue-700 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 sm:w-auto"
|
|
>
|
|
Print Labels
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div
|
|
class="container mx-auto w-[210mm] print:w-full print:max-w-full print:border-0"
|
|
:style="`
|
|
padding-top: calc(13.5mm + ${verticalOffset}mm);
|
|
padding-left: calc(8.45mm + ${horizontalOffset}mm);
|
|
padding-right: 8.45mm;
|
|
padding-bottom: 13.5mm;
|
|
`"
|
|
>
|
|
<!-- Display QR Code Labels -->
|
|
<ol
|
|
class="grid grid-cols-7 gap-x-[2.55mm]"
|
|
:style="`transform: translateX(${horizontalOffset}mm)`"
|
|
>
|
|
<template x-for="label in labels">
|
|
<li
|
|
:style="`height: ${labelHeight}mm; width: 25.4mm;`"
|
|
>
|
|
<div
|
|
class="flex items-center justify-center border-[0.5mm]"
|
|
:style="`
|
|
height: ${labelHeight - 0.5}mm;
|
|
width: 24.4mm;
|
|
transform: scale(${scale / 100});
|
|
transform-origin: center;
|
|
`"
|
|
:class="{'border-transparent': !borderToggle}"
|
|
>
|
|
<img
|
|
:src="label.qrCodeUrl"
|
|
alt="QR Code"
|
|
class="aspect-square flex-none"
|
|
:style="`height: ${labelHeight - 1}mm; width: ${labelHeight - 1}mm;`"
|
|
referrerpolicy="no-referrer"
|
|
/>
|
|
<div
|
|
class="flex-grow"
|
|
:class="{
|
|
'text-[3.9mm]': label.text.length <= 6,
|
|
'text-[3.2mm]': label.text.length == 7,
|
|
'text-[2.7mm]': label.text.length == 8,
|
|
'text-[2.4mm]': label.text.length == 9,
|
|
'text-[2.1mm]': label.text.length >= 10,
|
|
}"
|
|
x-text="label.text"
|
|
></div>
|
|
</div>
|
|
</li>
|
|
</template>
|
|
</ol>
|
|
</div>
|
|
|
|
<footer class="mt-8 bg-gray-200 py-4 text-center print:hidden">
|
|
<p>
|
|
© 2023
|
|
<a
|
|
href="https://tobiasmaier.info"
|
|
class="text-blue-600 underline hover:text-blue-700"
|
|
>Tobias L. Maier</a
|
|
>. All rights reserved. Licensed under the
|
|
<a
|
|
href="https://www.gnu.org/licenses/agpl-3.0.html"
|
|
title="GNU Affero General Public License"
|
|
class="text-blue-600 underline hover:text-blue-700"
|
|
>AGPL v3.0</a
|
|
>.
|
|
<a
|
|
href="https://github.com/tmaier/asn-qr-code-label-generator"
|
|
class="text-blue-600 underline hover:text-blue-700"
|
|
>Contribute via GitHub</a
|
|
>
|
|
</p>
|
|
</footer>
|
|
|
|
<script>
|
|
function qrCodeApp() {
|
|
return {
|
|
startNumber: 1,
|
|
prefix: "ASN",
|
|
leadingZeros: 4,
|
|
verticalOffset: 0,
|
|
horizontalOffset: 0,
|
|
labelHeight: 10,
|
|
scale: 100,
|
|
borderToggle: false,
|
|
labels: [],
|
|
|
|
generateLabels() {
|
|
this.labels = [];
|
|
let totalLabels = 7 * 27; // 7 columns x 27 rows
|
|
let startNumberInt = parseInt(this.startNumber, 10); // Ensure startNumber is an integer
|
|
let leadingZerosInt = parseInt(this.leadingZeros, 10); // Ensure leadingZeros is an integer
|
|
for (let i = 0; i < totalLabels; i++) {
|
|
let number = startNumberInt + i;
|
|
let paddedNumber = number
|
|
.toString()
|
|
.padStart(leadingZerosInt + 1, "0");
|
|
let text = this.prefix + paddedNumber;
|
|
// See https://goqr.me/api/doc/create-qr-code/
|
|
let qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${encodeURIComponent(
|
|
text
|
|
)}`;
|
|
|
|
this.labels.push({ qrCodeUrl, text });
|
|
}
|
|
},
|
|
printLabels() {
|
|
window.print();
|
|
},
|
|
init() {
|
|
this.generateLabels();
|
|
},
|
|
};
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|