@kreiseck/kasseneck-api 0.13.2 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/README.md +31 -5
- package/dist/cjs/client/receipts.d.ts +7 -0
- package/dist/cjs/client/receipts.js +1 -0
- package/dist/cjs/kasse/drucker.d.ts +5 -0
- package/dist/cjs/kasse/drucker.js +9 -0
- package/dist/cjs/kasse/settings.js +5 -2
- package/dist/cjs/printing/escpos.d.ts +38 -4
- package/dist/cjs/printing/escpos.js +87 -15
- package/dist/cjs/printing/index.d.ts +2 -2
- package/dist/cjs/printing/index.js +5 -1
- package/dist/cjs/printing/qr-groesse.d.ts +26 -21
- package/dist/cjs/printing/qr-groesse.js +22 -8
- package/dist/cjs/react/index.d.ts +53 -0
- package/dist/cjs/react/index.js +81 -0
- package/dist/cjs/receipt/bild.d.ts +14 -0
- package/dist/cjs/receipt/bild.js +65 -0
- package/dist/cjs/receipt/blatt.d.ts +107 -0
- package/dist/cjs/receipt/blatt.js +137 -0
- package/dist/cjs/receipt/epos.d.ts +19 -8
- package/dist/cjs/receipt/epos.js +39 -20
- package/dist/cjs/receipt/grid.d.ts +1 -1
- package/dist/cjs/receipt/grid.js +9 -1
- package/dist/cjs/receipt/index.d.ts +4 -2
- package/dist/cjs/receipt/index.js +17 -1
- package/dist/cjs/receipt/layout-escpos.d.ts +31 -3
- package/dist/cjs/receipt/layout-escpos.js +58 -18
- package/dist/esm/client/receipts.d.ts +7 -0
- package/dist/esm/client/receipts.js +1 -0
- package/dist/esm/kasse/drucker.d.ts +5 -0
- package/dist/esm/kasse/drucker.js +9 -0
- package/dist/esm/kasse/settings.js +5 -2
- package/dist/esm/printing/escpos.d.ts +38 -4
- package/dist/esm/printing/escpos.js +84 -15
- package/dist/esm/printing/index.d.ts +2 -2
- package/dist/esm/printing/index.js +2 -2
- package/dist/esm/printing/qr-groesse.d.ts +26 -21
- package/dist/esm/printing/qr-groesse.js +21 -8
- package/dist/esm/react/index.d.ts +53 -0
- package/dist/esm/react/index.js +80 -1
- package/dist/esm/receipt/bild.d.ts +14 -0
- package/dist/esm/receipt/bild.js +62 -0
- package/dist/esm/receipt/blatt.d.ts +107 -0
- package/dist/esm/receipt/blatt.js +128 -0
- package/dist/esm/receipt/epos.d.ts +19 -8
- package/dist/esm/receipt/epos.js +40 -22
- package/dist/esm/receipt/grid.d.ts +1 -1
- package/dist/esm/receipt/grid.js +9 -1
- package/dist/esm/receipt/index.d.ts +4 -2
- package/dist/esm/receipt/index.js +4 -2
- package/dist/esm/receipt/layout-escpos.d.ts +31 -3
- package/dist/esm/receipt/layout-escpos.js +58 -20
- package/fixtures/erwartet/karte-eigener.blatt32.json +159 -0
- package/fixtures/erwartet/karte-eigener.blatt48.json +159 -0
- package/fixtures/erwartet/karte-gptom-ios.blatt32.json +231 -0
- package/fixtures/erwartet/karte-gptom-ios.blatt48.json +231 -0
- package/fixtures/erwartet/karte-gptom.blatt32.json +231 -0
- package/fixtures/erwartet/karte-gptom.blatt48.json +231 -0
- package/fixtures/erwartet/karte-hobex-cloud.blatt32.json +201 -0
- package/fixtures/erwartet/karte-hobex-cloud.blatt48.json +201 -0
- package/fixtures/erwartet/karte-hobex-hps.blatt32.json +225 -0
- package/fixtures/erwartet/karte-hobex-hps.blatt48.json +225 -0
- package/fixtures/erwartet/karte-mypos.blatt32.json +255 -0
- package/fixtures/erwartet/karte-mypos.blatt48.json +243 -0
- package/fixtures/erwartet/karte-stripe-eps.blatt32.json +195 -0
- package/fixtures/erwartet/karte-stripe-eps.blatt48.json +195 -0
- package/fixtures/erwartet/karte-stripe.blatt32.json +213 -0
- package/fixtures/erwartet/karte-stripe.blatt48.json +213 -0
- package/fixtures/erwartet/karte-sumup.blatt32.json +219 -0
- package/fixtures/erwartet/karte-sumup.blatt48.json +207 -0
- package/fixtures/erwartet/langer-artikelname.blatt32.json +177 -0
- package/fixtures/erwartet/langer-artikelname.blatt48.json +171 -0
- package/fixtures/erwartet/logo-probe-hoch.raster32.txt +120 -0
- package/fixtures/erwartet/logo-probe.raster32.txt +54 -0
- package/fixtures/erwartet/null-ausfall.blatt32.json +207 -0
- package/fixtures/erwartet/null-ausfall.blatt48.json +189 -0
- package/fixtures/erwartet/null-ausfall.grid32.txt +2 -0
- package/fixtures/erwartet/null-ausfall.grid48.txt +2 -0
- package/fixtures/erwartet/null-jahr.blatt32.json +213 -0
- package/fixtures/erwartet/null-jahr.blatt48.json +189 -0
- package/fixtures/erwartet/null-jahr.grid32.txt +2 -0
- package/fixtures/erwartet/null-jahr.grid48.txt +2 -0
- package/fixtures/erwartet/null-monat.blatt32.json +207 -0
- package/fixtures/erwartet/null-monat.blatt48.json +189 -0
- package/fixtures/erwartet/null-monat.grid32.txt +2 -0
- package/fixtures/erwartet/null-monat.grid48.txt +2 -0
- package/fixtures/erwartet/null-pruef.blatt32.json +207 -0
- package/fixtures/erwartet/null-pruef.blatt48.json +189 -0
- package/fixtures/erwartet/null-pruef.grid32.txt +2 -0
- package/fixtures/erwartet/null-pruef.grid48.txt +2 -0
- package/fixtures/erwartet/null-schluss.blatt32.json +207 -0
- package/fixtures/erwartet/null-schluss.blatt48.json +189 -0
- package/fixtures/erwartet/null-schluss.grid32.txt +2 -0
- package/fixtures/erwartet/null-schluss.grid48.txt +2 -0
- package/fixtures/erwartet/null-start.blatt32.json +207 -0
- package/fixtures/erwartet/null-start.blatt48.json +189 -0
- package/fixtures/erwartet/null-start.grid32.txt +2 -0
- package/fixtures/erwartet/null-start.grid48.txt +2 -0
- package/fixtures/erwartet/rabatt-chef-trinkgeld.blatt32.json +213 -0
- package/fixtures/erwartet/rabatt-chef-trinkgeld.blatt48.json +207 -0
- package/fixtures/erwartet/rabatt-einfach.blatt32.json +183 -0
- package/fixtures/erwartet/rabatt-einfach.blatt48.json +177 -0
- package/fixtures/erwartet/rabatt-trinkgeld.blatt32.json +213 -0
- package/fixtures/erwartet/rabatt-trinkgeld.blatt48.json +207 -0
- package/fixtures/erwartet/rabatt-wertgutschein.blatt32.json +219 -0
- package/fixtures/erwartet/rabatt-wertgutschein.blatt48.json +213 -0
- package/fixtures/erwartet/rabattzeilen.blatt32.json +201 -0
- package/fixtures/erwartet/rabattzeilen.blatt48.json +195 -0
- package/fixtures/erwartet/signaturausfall-verkauf.blatt32.json +195 -0
- package/fixtures/erwartet/signaturausfall-verkauf.blatt48.json +183 -0
- package/fixtures/erwartet/storno-rabatt.blatt32.json +249 -0
- package/fixtures/erwartet/storno-rabatt.blatt48.json +237 -0
- package/fixtures/erwartet/storno-rabatt.grid32.txt +2 -0
- package/fixtures/erwartet/storno-rabatt.grid48.txt +2 -0
- package/fixtures/erwartet/storno-teil.blatt32.json +207 -0
- package/fixtures/erwartet/storno-teil.blatt48.json +201 -0
- package/fixtures/erwartet/storno-teil.grid32.txt +2 -0
- package/fixtures/erwartet/storno-teil.grid48.txt +2 -0
- package/fixtures/erwartet/storno-voll.blatt32.json +219 -0
- package/fixtures/erwartet/storno-voll.blatt48.json +213 -0
- package/fixtures/erwartet/storno-voll.grid32.txt +2 -0
- package/fixtures/erwartet/storno-voll.grid48.txt +2 -0
- package/fixtures/erwartet/testkasse-verkauf.blatt32.json +225 -0
- package/fixtures/erwartet/testkasse-verkauf.blatt48.json +219 -0
- package/fixtures/erwartet/testkasse-verkauf.grid32.txt +5 -1
- package/fixtures/erwartet/testkasse-verkauf.grid48.txt +5 -1
- package/fixtures/erwartet/testsignatur-verkauf.blatt32.json +237 -0
- package/fixtures/erwartet/testsignatur-verkauf.blatt48.json +219 -0
- package/fixtures/erwartet/testsignatur-verkauf.grid32.txt +5 -1
- package/fixtures/erwartet/testsignatur-verkauf.grid48.txt +5 -1
- package/fixtures/erwartet/training.blatt32.json +237 -0
- package/fixtures/erwartet/training.blatt48.json +219 -0
- package/fixtures/erwartet/training.grid32.txt +2 -0
- package/fixtures/erwartet/training.grid48.txt +2 -0
- package/fixtures/erwartet/verkauf-bar.blatt32.json +177 -0
- package/fixtures/erwartet/verkauf-bar.blatt48.json +171 -0
- package/fixtures/erwartet/verkauf-karte.blatt32.json +177 -0
- package/fixtures/erwartet/verkauf-karte.blatt48.json +171 -0
- package/fixtures/erwartet/verkauf-kleinunternehmer.blatt32.json +183 -0
- package/fixtures/erwartet/verkauf-kleinunternehmer.blatt48.json +177 -0
- package/fixtures/hobex-hps-codes.json +1 -1
- package/fixtures/kasse-settings-standard.json +1 -1
- package/fixtures/kasse-texte.json +1 -1
- package/fixtures/manifest.json +109 -43
- package/fixtures/oberflaeche.json +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { ReceiptLayout } from '../receipt/layout.js';
|
|
3
|
+
import { type BelegBlatt, type LogoStufe } from '../receipt/blatt.js';
|
|
4
|
+
import type { QrModulGroesse } from '../printing/qr-groesse.js';
|
|
3
5
|
/**
|
|
4
6
|
* React-Adapter: zeichnet ein Beleg-Layout (siehe ../receipt/layout.ts).
|
|
5
7
|
*
|
|
@@ -33,6 +35,11 @@ export interface ReceiptLayoutViewProps {
|
|
|
33
35
|
/** Text auf dem verdeckten QR (Vorgabe „Antippen zum Anzeigen“). */
|
|
34
36
|
qrVerdecktText?: string;
|
|
35
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* @deprecated Seit 0.14.0: [BelegBlattView] setzt den Beleg zeichengleich zu
|
|
40
|
+
* Bon und PDF (Raster, Logo, Marke). Diese Ansicht rechnet Spalten mit Flex
|
|
41
|
+
* und bricht darum anders um als das Papier.
|
|
42
|
+
*/
|
|
36
43
|
export declare function ReceiptLayoutView({ layout, className, renderQr, qrVerdeckt, qrVerdecktText }: ReceiptLayoutViewProps): ReactNode;
|
|
37
44
|
/**
|
|
38
45
|
* Verdeckter QR: weichgezeichnet und nicht scannbar, bis der Betrachter ihn
|
|
@@ -44,3 +51,49 @@ export declare function QrVerdeckt({ data, text, children }: {
|
|
|
44
51
|
text?: string;
|
|
45
52
|
children: ReactNode;
|
|
46
53
|
}): ReactNode;
|
|
54
|
+
export interface BelegBlattZeilenProps {
|
|
55
|
+
blatt: BelegBlatt;
|
|
56
|
+
/** Adresse des Firmenlogos; ohne sie bleibt der Logo-Block leer (Platz bleibt). */
|
|
57
|
+
logoUrl?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Zeichnet den QR in seinen Kasten (`qrBlattAnteil`). Der Kasten schliesst
|
|
60
|
+
* die Ruhezone von 4 Modulen je Seite ein und setzt Fehlerkorrektur M
|
|
61
|
+
* voraus: das Symbol also mit Korrektur M und 4 Modulen Ruhezone zeichnen
|
|
62
|
+
* und den Kasten ganz ausfuellen -- dann ist ein Modul so gross wie am Bon.
|
|
63
|
+
*/
|
|
64
|
+
renderQr?: (data: string) => ReactNode;
|
|
65
|
+
qrVerdeckt?: boolean;
|
|
66
|
+
qrVerdecktText?: string;
|
|
67
|
+
className?: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Das Blatt am Bildschirm -- Zeile fuer Zeile dieselben Zeichen wie am Bon und
|
|
71
|
+
* im PDF. Masse in `ch`: eine Zeichenbreite ist `1ch`, eine Zeile `2ch`, das
|
|
72
|
+
* Blatt `zeichen`ch breit. Der Verbraucher bestimmt nur Schrift (monospace,
|
|
73
|
+
* Vorgabe ueber `--keck-blatt-schrift`) und Schriftgroesse; alles andere steht
|
|
74
|
+
* im Blatt.
|
|
75
|
+
*/
|
|
76
|
+
export declare function BelegBlattZeilen({ blatt, logoUrl, renderQr, qrVerdeckt, qrVerdecktText, className }: BelegBlattZeilenProps): ReactNode;
|
|
77
|
+
export interface BelegBlattViewProps extends Omit<BelegBlattZeilenProps, 'blatt' | 'logoUrl'> {
|
|
78
|
+
layout: ReceiptLayout;
|
|
79
|
+
zeichen?: number;
|
|
80
|
+
/** Firmenlogo und Stufe (`logoSkala`); das Pixelmass liest die Ansicht selbst. */
|
|
81
|
+
logo?: {
|
|
82
|
+
url: string;
|
|
83
|
+
stufe: LogoStufe;
|
|
84
|
+
} | null;
|
|
85
|
+
marke?: boolean;
|
|
86
|
+
/** Geraete-Einstellung fuer die QR-Modulgroesse; Vorgabe `auto` (hoechstens 6 Punkte je Modul) — wie die Druckwege. */
|
|
87
|
+
qrGroesse?: QrModulGroesse;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Laedt das Logo (fuer sein Pixelmass), baut das Blatt und zeichnet es. Bis
|
|
91
|
+
* das Bild geladen ist, fehlt der Logo-Block -- die Kasse laedt das Logo ohnehin
|
|
92
|
+
* vorab in den Browser-Cache.
|
|
93
|
+
*
|
|
94
|
+
* Ein Logo ueber [logoPixelZulaessig] (4096x4096px, dieselbe Grenze wie das
|
|
95
|
+
* Druck-Kit beim Rastern fuer den Bon) bleibt hier ebenso ohne Logo-Block --
|
|
96
|
+
* sonst zeigte der Bildschirm ein Logo, das nie gedruckt wird: Bildschirm und
|
|
97
|
+
* Bon zeigen dasselbe Logo.
|
|
98
|
+
*/
|
|
99
|
+
export declare function BelegBlattView({ layout, zeichen, logo, marke, qrGroesse, ...rest }: BelegBlattViewProps): ReactNode;
|
package/dist/cjs/react/index.js
CHANGED
|
@@ -2,13 +2,21 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ReceiptLayoutView = ReceiptLayoutView;
|
|
4
4
|
exports.QrVerdeckt = QrVerdeckt;
|
|
5
|
+
exports.BelegBlattZeilen = BelegBlattZeilen;
|
|
6
|
+
exports.BelegBlattView = BelegBlattView;
|
|
5
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
8
|
const react_1 = require("react");
|
|
9
|
+
const blatt_js_1 = require("../receipt/blatt.js");
|
|
7
10
|
const AUSRICHTUNG = {
|
|
8
11
|
left: 'left',
|
|
9
12
|
center: 'center',
|
|
10
13
|
right: 'right',
|
|
11
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated Seit 0.14.0: [BelegBlattView] setzt den Beleg zeichengleich zu
|
|
17
|
+
* Bon und PDF (Raster, Logo, Marke). Diese Ansicht rechnet Spalten mit Flex
|
|
18
|
+
* und bricht darum anders um als das Papier.
|
|
19
|
+
*/
|
|
12
20
|
function ReceiptLayoutView({ layout, className, renderQr, qrVerdeckt = false, qrVerdecktText }) {
|
|
13
21
|
const klasse = className === undefined ? 'keck-receipt' : `keck-receipt ${className}`;
|
|
14
22
|
return ((0, jsx_runtime_1.jsx)("div", { className: klasse, "data-paper-size": layout.paperSize, children: layout.lines.map((zeile, index) => ((0, jsx_runtime_1.jsx)(Zeile, { zeile: zeile, renderQr: renderQr, qrVerdeckt: qrVerdeckt, qrVerdecktText: qrVerdecktText }, index))) }));
|
|
@@ -59,3 +67,76 @@ function Zeile({ zeile, renderQr, qrVerdeckt, qrVerdecktText }) {
|
|
|
59
67
|
}
|
|
60
68
|
}
|
|
61
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Das Blatt am Bildschirm -- Zeile fuer Zeile dieselben Zeichen wie am Bon und
|
|
72
|
+
* im PDF. Masse in `ch`: eine Zeichenbreite ist `1ch`, eine Zeile `2ch`, das
|
|
73
|
+
* Blatt `zeichen`ch breit. Der Verbraucher bestimmt nur Schrift (monospace,
|
|
74
|
+
* Vorgabe ueber `--keck-blatt-schrift`) und Schriftgroesse; alles andere steht
|
|
75
|
+
* im Blatt.
|
|
76
|
+
*/
|
|
77
|
+
function BelegBlattZeilen({ blatt, logoUrl, renderQr, qrVerdeckt = false, qrVerdecktText, className }) {
|
|
78
|
+
const z = blatt.zeichen;
|
|
79
|
+
const klasse = className === undefined ? 'keck-blatt' : `keck-blatt ${className}`;
|
|
80
|
+
const blattStil = {
|
|
81
|
+
width: `${z}ch`,
|
|
82
|
+
fontFamily: 'var(--keck-blatt-schrift, "DM Mono", ui-monospace, Menlo, Consolas, monospace)',
|
|
83
|
+
fontVariantLigatures: 'none',
|
|
84
|
+
};
|
|
85
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: klasse, "data-zeichen": z, style: blattStil, children: blatt.bloecke.map((b, i) => {
|
|
86
|
+
switch (b.art) {
|
|
87
|
+
case 'zeile':
|
|
88
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "keck-blatt-zeile", style: { whiteSpace: 'pre', height: '2ch', lineHeight: '2ch', overflow: 'hidden', fontWeight: b.fett ? 'bold' : 'normal' }, children: b.text }, i));
|
|
89
|
+
case 'logo':
|
|
90
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "keck-blatt-logo", style: { height: `${b.hoeheZeilen * 2}ch`, display: 'flex', justifyContent: 'center' }, children: logoUrl === undefined ? null : ((0, jsx_runtime_1.jsx)("img", { src: logoUrl, alt: "", "aria-hidden": "true", style: { width: `${b.breiteAnteil * z}ch`, height: `${b.hoeheZeilen * 2}ch`, objectFit: 'contain' } })) }, i));
|
|
91
|
+
case 'qr': {
|
|
92
|
+
// Auch beim Anteil 0 (leer oder in keine Version passend) bekommt
|
|
93
|
+
// `renderQr` die Nutzlast: die Oberflaeche muss sagen koennen, dass
|
|
94
|
+
// der QR fehlt und ein Papierbeleg noetig ist. Bon und PDF lassen
|
|
95
|
+
// den QR dann weg und melden es ueber `qrFehler`.
|
|
96
|
+
const bild = renderQr !== undefined ? renderQr(b.nutzlast) : (0, jsx_runtime_1.jsx)("div", { "data-qr": b.nutzlast, "aria-label": "RKSV-QR-Code" });
|
|
97
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "keck-blatt-qr", style: { display: 'flex', justifyContent: 'center' }, children: (0, jsx_runtime_1.jsx)("div", { style: { width: `${b.breiteAnteil * z}ch`, aspectRatio: '1 / 1' }, children: qrVerdeckt ? (0, jsx_runtime_1.jsx)(QrVerdeckt, { data: b.nutzlast, text: qrVerdecktText, children: bild }) : bild }) }, i));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}) }));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Laedt das Logo (fuer sein Pixelmass), baut das Blatt und zeichnet es. Bis
|
|
104
|
+
* das Bild geladen ist, fehlt der Logo-Block -- die Kasse laedt das Logo ohnehin
|
|
105
|
+
* vorab in den Browser-Cache.
|
|
106
|
+
*
|
|
107
|
+
* Ein Logo ueber [logoPixelZulaessig] (4096x4096px, dieselbe Grenze wie das
|
|
108
|
+
* Druck-Kit beim Rastern fuer den Bon) bleibt hier ebenso ohne Logo-Block --
|
|
109
|
+
* sonst zeigte der Bildschirm ein Logo, das nie gedruckt wird: Bildschirm und
|
|
110
|
+
* Bon zeigen dasselbe Logo.
|
|
111
|
+
*/
|
|
112
|
+
function BelegBlattView({ layout, zeichen, logo, marke = false, qrGroesse, ...rest }) {
|
|
113
|
+
const [mass, setMass] = (0, react_1.useState)(null);
|
|
114
|
+
const url = logo?.url;
|
|
115
|
+
(0, react_1.useEffect)(() => {
|
|
116
|
+
if (url === undefined)
|
|
117
|
+
return undefined;
|
|
118
|
+
let aktiv = true;
|
|
119
|
+
const Bild = globalThis.Image;
|
|
120
|
+
if (Bild === undefined)
|
|
121
|
+
return undefined;
|
|
122
|
+
const bild = new Bild();
|
|
123
|
+
bild.onload = () => {
|
|
124
|
+
if (aktiv && (0, blatt_js_1.logoPixelZulaessig)(bild.naturalWidth, bild.naturalHeight))
|
|
125
|
+
setMass({ url, breite: bild.naturalWidth, hoehe: bild.naturalHeight });
|
|
126
|
+
};
|
|
127
|
+
bild.src = url;
|
|
128
|
+
return () => {
|
|
129
|
+
aktiv = false;
|
|
130
|
+
};
|
|
131
|
+
}, [url]);
|
|
132
|
+
const optionen = { marke };
|
|
133
|
+
if (zeichen !== undefined)
|
|
134
|
+
optionen.zeichen = zeichen;
|
|
135
|
+
if (qrGroesse !== undefined)
|
|
136
|
+
optionen.qrGroesse = qrGroesse;
|
|
137
|
+
const geladen = logo != null && mass !== null && mass.url === logo.url;
|
|
138
|
+
if (geladen)
|
|
139
|
+
optionen.logo = { stufe: logo.stufe, pxBreite: mass.breite, pxHoehe: mass.hoehe };
|
|
140
|
+
const blatt = (0, blatt_js_1.belegBlatt)(layout, optionen);
|
|
141
|
+
return geladen ? (0, jsx_runtime_1.jsx)(BelegBlattZeilen, { blatt: blatt, logoUrl: logo.url, ...rest }) : (0, jsx_runtime_1.jsx)(BelegBlattZeilen, { blatt: blatt, ...rest });
|
|
142
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RasterBild } from '../printing/escpos.js';
|
|
2
|
+
import { type LogoMass } from './blatt.js';
|
|
3
|
+
/**
|
|
4
|
+
* RGBA-Pixel (wie `ImageData.data` im Browser oder ein dekodiertes PNG in
|
|
5
|
+
* Node/Dart) -> einfarbiges Rasterbild in genau der Groesse, die das Blatt
|
|
6
|
+
* dem Logo gibt.
|
|
7
|
+
*
|
|
8
|
+
* Die Rechnung ist Vertrag und hat einen Dart-Zwilling (Golden
|
|
9
|
+
* `erwartet/logo-probe.raster32.txt`): Flaechenmittel je Druckpunkt,
|
|
10
|
+
* Durchsichtiges auf Papierweiss, Helligkeit nach BT.601, Floyd-Steinberg mit
|
|
11
|
+
* Schwelle 128. Die Reihenfolge der Rechenschritte nicht aendern -- beide
|
|
12
|
+
* Sprachen rechnen mit IEEE-Doubles und kommen nur so auf dieselben Punkte.
|
|
13
|
+
*/
|
|
14
|
+
export declare function logoRaster(rgba: ArrayLike<number>, pxBreite: number, pxHoehe: number, mass: LogoMass, zeichen: number): RasterBild;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logoRaster = logoRaster;
|
|
4
|
+
const blatt_js_1 = require("./blatt.js");
|
|
5
|
+
/**
|
|
6
|
+
* RGBA-Pixel (wie `ImageData.data` im Browser oder ein dekodiertes PNG in
|
|
7
|
+
* Node/Dart) -> einfarbiges Rasterbild in genau der Groesse, die das Blatt
|
|
8
|
+
* dem Logo gibt.
|
|
9
|
+
*
|
|
10
|
+
* Die Rechnung ist Vertrag und hat einen Dart-Zwilling (Golden
|
|
11
|
+
* `erwartet/logo-probe.raster32.txt`): Flaechenmittel je Druckpunkt,
|
|
12
|
+
* Durchsichtiges auf Papierweiss, Helligkeit nach BT.601, Floyd-Steinberg mit
|
|
13
|
+
* Schwelle 128. Die Reihenfolge der Rechenschritte nicht aendern -- beide
|
|
14
|
+
* Sprachen rechnen mit IEEE-Doubles und kommen nur so auf dieselben Punkte.
|
|
15
|
+
*/
|
|
16
|
+
function logoRaster(rgba, pxBreite, pxHoehe, mass, zeichen) {
|
|
17
|
+
if (!Number.isInteger(pxBreite) || !Number.isInteger(pxHoehe) || pxBreite < 1 || pxHoehe < 1 || rgba.length !== pxBreite * pxHoehe * 4) {
|
|
18
|
+
throw new Error('RGBA-Laenge passt nicht zum Pixelmass');
|
|
19
|
+
}
|
|
20
|
+
const { breite, hoehe } = (0, blatt_js_1.logoRasterMass)(mass, zeichen);
|
|
21
|
+
const grau = new Float64Array(breite * hoehe);
|
|
22
|
+
const sx = pxBreite / breite;
|
|
23
|
+
const sy = pxHoehe / hoehe;
|
|
24
|
+
for (let y = 0; y < hoehe; y++) {
|
|
25
|
+
const y0 = Math.floor(y * sy);
|
|
26
|
+
const y1 = Math.min(pxHoehe, Math.max(y0 + 1, Math.floor((y + 1) * sy)));
|
|
27
|
+
for (let x = 0; x < breite; x++) {
|
|
28
|
+
const x0 = Math.floor(x * sx);
|
|
29
|
+
const x1 = Math.min(pxBreite, Math.max(x0 + 1, Math.floor((x + 1) * sx)));
|
|
30
|
+
let summe = 0;
|
|
31
|
+
let anzahl = 0;
|
|
32
|
+
for (let py = y0; py < y1; py++) {
|
|
33
|
+
for (let px = x0; px < x1; px++) {
|
|
34
|
+
const i = (py * pxBreite + px) * 4;
|
|
35
|
+
const deckung = rgba[i + 3] / 255;
|
|
36
|
+
const hell = 0.299 * rgba[i] + 0.587 * rgba[i + 1] + 0.114 * rgba[i + 2];
|
|
37
|
+
summe += hell * deckung + 255 * (1 - deckung);
|
|
38
|
+
anzahl += 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
grau[y * breite + x] = anzahl === 0 ? 255 : summe / anzahl;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const punkte = new Uint8Array(breite * hoehe);
|
|
45
|
+
for (let y = 0; y < hoehe; y++) {
|
|
46
|
+
for (let x = 0; x < breite; x++) {
|
|
47
|
+
const i = y * breite + x;
|
|
48
|
+
const alt = grau[i];
|
|
49
|
+
const neu = alt < 128 ? 0 : 255;
|
|
50
|
+
if (neu === 0)
|
|
51
|
+
punkte[i] = 1;
|
|
52
|
+
const fehler = alt - neu;
|
|
53
|
+
if (x + 1 < breite)
|
|
54
|
+
grau[i + 1] = grau[i + 1] + (fehler * 7) / 16;
|
|
55
|
+
if (y + 1 < hoehe) {
|
|
56
|
+
if (x > 0)
|
|
57
|
+
grau[i + breite - 1] = grau[i + breite - 1] + (fehler * 3) / 16;
|
|
58
|
+
grau[i + breite] = grau[i + breite] + (fehler * 5) / 16;
|
|
59
|
+
if (x + 1 < breite)
|
|
60
|
+
grau[i + breite + 1] = grau[i + breite + 1] + fehler / 16;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { breite, hoehe, punkte };
|
|
65
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { PosPaperSize } from '../printing/escpos.js';
|
|
2
|
+
import { type QrModulGroesse } from '../printing/index.js';
|
|
3
|
+
import type { ReceiptLayout } from './layout.js';
|
|
4
|
+
/**
|
|
5
|
+
* Das Beleg-Blatt: die vollstaendige Folge dessen, was auf dem Papier steht --
|
|
6
|
+
* Rasterzeilen, Firmenlogo, QR und Marke, mit Groessen als Anteil der
|
|
7
|
+
* Blattbreite und in Zeilen.
|
|
8
|
+
*
|
|
9
|
+
* Bis 0.13 setzte jeder Zeichner das Raster selbst zusammen und entschied
|
|
10
|
+
* Logo-Ort, Logo-Groesse, Rahmen und QR-Groesse fuer sich. Heraus kamen
|
|
11
|
+
* fuenf verschiedene Belege (Panel: Logo ueber dem Testkassen-Rahmen, PDF:
|
|
12
|
+
* Logo auf dem Rahmen, App: kein Logo). Hier steht das jetzt einmal.
|
|
13
|
+
*
|
|
14
|
+
* Masseinheit ist der Druckkopf (Epson Font A, 203 dpi): ein Zeichen ist 12
|
|
15
|
+
* Punkte breit, eine Zeile 24 Punkte hoch. Am Bildschirm ist eine Zeile `2ch`,
|
|
16
|
+
* im PDF zwei Zeichenbreiten.
|
|
17
|
+
*/
|
|
18
|
+
export type LogoStufe = 'S' | 'M' | 'L' | 'XL';
|
|
19
|
+
/** Kasten je Stufe: Anteil der Blattbreite und Hoehe in Zeilen (Werte der Web-Kasse). */
|
|
20
|
+
export declare const LOGO_STUFEN: Readonly<Record<LogoStufe, {
|
|
21
|
+
readonly breiteAnteil: number;
|
|
22
|
+
readonly hoeheZeilen: number;
|
|
23
|
+
}>>;
|
|
24
|
+
export declare const PUNKTE_JE_ZEICHEN = 12;
|
|
25
|
+
export declare const PUNKTE_JE_ZEILE = 24;
|
|
26
|
+
/** Die Marke am Belegende (Konto-Flag `kreiseck_logo`) -- Text, damit jeder Weg sie gleich setzt. */
|
|
27
|
+
export declare const MARKE_TEXT = "erstellt mit Kasseneck";
|
|
28
|
+
export interface BlattLogo {
|
|
29
|
+
stufe: LogoStufe;
|
|
30
|
+
/** Pixelmass des Originalbilds -- ohne das laesst sich nicht einpassen. */
|
|
31
|
+
pxBreite: number;
|
|
32
|
+
pxHoehe: number;
|
|
33
|
+
}
|
|
34
|
+
export interface LogoMass {
|
|
35
|
+
/** Anteil der Blattbreite (0..1). */
|
|
36
|
+
breiteAnteil: number;
|
|
37
|
+
/** Hoehe in Zeilen (nicht ganzzahlig). */
|
|
38
|
+
hoeheZeilen: number;
|
|
39
|
+
}
|
|
40
|
+
export type BlattBlock = {
|
|
41
|
+
readonly art: 'zeile';
|
|
42
|
+
readonly text: string;
|
|
43
|
+
readonly fett: boolean;
|
|
44
|
+
readonly leer: boolean;
|
|
45
|
+
} | {
|
|
46
|
+
readonly art: 'logo';
|
|
47
|
+
readonly breiteAnteil: number;
|
|
48
|
+
readonly hoeheZeilen: number;
|
|
49
|
+
} | {
|
|
50
|
+
readonly art: 'qr';
|
|
51
|
+
readonly nutzlast: string;
|
|
52
|
+
readonly breiteAnteil: number;
|
|
53
|
+
};
|
|
54
|
+
export interface BelegBlatt {
|
|
55
|
+
readonly zeichen: number;
|
|
56
|
+
readonly bloecke: readonly BlattBlock[];
|
|
57
|
+
}
|
|
58
|
+
export interface BelegBlattOptionen {
|
|
59
|
+
/** Zeichen je Zeile; Vorgabe nach `layout.paperSize` (32/48). */
|
|
60
|
+
zeichen?: number;
|
|
61
|
+
/** Firmenlogo; ohne Angabe kein Logo-Block. */
|
|
62
|
+
logo?: BlattLogo | null;
|
|
63
|
+
/** "erstellt mit Kasseneck" am Ende. */
|
|
64
|
+
marke?: boolean;
|
|
65
|
+
/** Geraete-Einstellung fuer die QR-Modulgroesse; Vorgabe `auto` (hoechstens 6 Punkte je Modul) — wie die Druckwege. */
|
|
66
|
+
qrGroesse?: QrModulGroesse;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 32 Zeichen sind 58 mm, 48 sind 80 mm; andere Breiten behalten das Papier des
|
|
70
|
+
* Layouts. Der QR-Anteil (`qrBlattAnteil`) wird dann fuer die Kopfbreite
|
|
71
|
+
* DIESES Papiers gerechnet, nicht fuer die gewaehlte Zeichenzahl.
|
|
72
|
+
*/
|
|
73
|
+
export declare function papierFuerZeichen(zeichen: number, vorgabe: PosPaperSize): PosPaperSize;
|
|
74
|
+
/**
|
|
75
|
+
* Obergrenze der Logo-Pixel, die der Bildschirm noch anzeigen darf -- exakt
|
|
76
|
+
* dieselbe Grenze wie im Druck-Kit (`LOGO_PIXEL_MAX` in `druck-logo.ts`):
|
|
77
|
+
* Bon und ePOS lehnen ein Logo ueber 4096x4096px schon beim Rastern ab (ein
|
|
78
|
+
* grosses Bild blockiert das Geraet). Ohne diese Grenze wuerde der
|
|
79
|
+
* Bildschirm ein Logo zeigen, das der Bon nie druckt -- Bildschirm und
|
|
80
|
+
* Papier waeren sich uneins, genau das soll das Beleg-Blatt verhindern.
|
|
81
|
+
*/
|
|
82
|
+
export declare const LOGO_PIXEL_MAX = 4096;
|
|
83
|
+
/** Reine Pruefung, ob ein Logo dieser Pixelmasse noch angezeigt/gedruckt werden darf. */
|
|
84
|
+
export declare function logoPixelZulaessig(breite: number, hoehe: number): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Das Logo in den Kasten seiner Stufe einpassen -- **nie hochrechnen**: ein
|
|
87
|
+
* Bildpixel wird hoechstens ein Druckpunkt. Ein kleines Logo bleibt klein,
|
|
88
|
+
* statt am Bon verwaschen zu werden (so hielten es PDF und Web-Kasse schon).
|
|
89
|
+
*/
|
|
90
|
+
export declare function logoMass(logo: BlattLogo, zeichen: number): LogoMass;
|
|
91
|
+
/** Das Mass in ganzen Druckpunkten -- so gross muss das Rasterbild fuer den Bon sein. */
|
|
92
|
+
export declare function logoRasterMass(mass: LogoMass, zeichen: number): {
|
|
93
|
+
breite: number;
|
|
94
|
+
hoehe: number;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Anteil der Blattbreite, den der QR am Drucker einnimmt -- dieselbe Rechnung
|
|
98
|
+
* wie der Druckweg (nativ, sonst der Bildweg). Bildschirm und PDF zeigen den
|
|
99
|
+
* QR in genau dieser Groesse. Leere Nutzlast: 0 (kein Symbol).
|
|
100
|
+
*
|
|
101
|
+
* Der Kasten **schliesst die Ruhezone von 4 Modulen je Seite ein** und setzt
|
|
102
|
+
* Fehlerkorrektur M voraus. Ein Zeichner (`renderQr`, PDF) muss das Symbol
|
|
103
|
+
* darum mit Fehlerkorrektur M und einer Ruhezone von 4 Modulen zeichnen, die
|
|
104
|
+
* den Kasten ganz ausfuellt -- sonst stimmt die Modulgroesse nicht mit dem Bon.
|
|
105
|
+
*/
|
|
106
|
+
export declare function qrBlattAnteil(nutzlast: string, papier: PosPaperSize, groesse?: QrModulGroesse): number;
|
|
107
|
+
export declare function belegBlatt(layout: ReceiptLayout, optionen?: BelegBlattOptionen): BelegBlatt;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LOGO_PIXEL_MAX = exports.MARKE_TEXT = exports.PUNKTE_JE_ZEILE = exports.PUNKTE_JE_ZEICHEN = exports.LOGO_STUFEN = void 0;
|
|
4
|
+
exports.papierFuerZeichen = papierFuerZeichen;
|
|
5
|
+
exports.logoPixelZulaessig = logoPixelZulaessig;
|
|
6
|
+
exports.logoMass = logoMass;
|
|
7
|
+
exports.logoRasterMass = logoRasterMass;
|
|
8
|
+
exports.qrBlattAnteil = qrBlattAnteil;
|
|
9
|
+
exports.belegBlatt = belegBlatt;
|
|
10
|
+
const index_js_1 = require("../printing/index.js");
|
|
11
|
+
const grid_js_1 = require("./grid.js");
|
|
12
|
+
/** Kasten je Stufe: Anteil der Blattbreite und Hoehe in Zeilen (Werte der Web-Kasse). */
|
|
13
|
+
exports.LOGO_STUFEN = {
|
|
14
|
+
S: { breiteAnteil: 0.42, hoeheZeilen: 5 },
|
|
15
|
+
M: { breiteAnteil: 0.62, hoeheZeilen: 8 },
|
|
16
|
+
L: { breiteAnteil: 0.8, hoeheZeilen: 12 },
|
|
17
|
+
XL: { breiteAnteil: 0.94, hoeheZeilen: 16 },
|
|
18
|
+
};
|
|
19
|
+
exports.PUNKTE_JE_ZEICHEN = 12;
|
|
20
|
+
exports.PUNKTE_JE_ZEILE = 24;
|
|
21
|
+
/** Die Marke am Belegende (Konto-Flag `kreiseck_logo`) -- Text, damit jeder Weg sie gleich setzt. */
|
|
22
|
+
exports.MARKE_TEXT = 'erstellt mit Kasseneck';
|
|
23
|
+
/**
|
|
24
|
+
* 32 Zeichen sind 58 mm, 48 sind 80 mm; andere Breiten behalten das Papier des
|
|
25
|
+
* Layouts. Der QR-Anteil (`qrBlattAnteil`) wird dann fuer die Kopfbreite
|
|
26
|
+
* DIESES Papiers gerechnet, nicht fuer die gewaehlte Zeichenzahl.
|
|
27
|
+
*/
|
|
28
|
+
function papierFuerZeichen(zeichen, vorgabe) {
|
|
29
|
+
if (zeichen === grid_js_1.ZEICHEN_JE_PAPIER.mm58)
|
|
30
|
+
return 'mm58';
|
|
31
|
+
if (zeichen === grid_js_1.ZEICHEN_JE_PAPIER.mm80)
|
|
32
|
+
return 'mm80';
|
|
33
|
+
return vorgabe;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Obergrenze der Logo-Pixel, die der Bildschirm noch anzeigen darf -- exakt
|
|
37
|
+
* dieselbe Grenze wie im Druck-Kit (`LOGO_PIXEL_MAX` in `druck-logo.ts`):
|
|
38
|
+
* Bon und ePOS lehnen ein Logo ueber 4096x4096px schon beim Rastern ab (ein
|
|
39
|
+
* grosses Bild blockiert das Geraet). Ohne diese Grenze wuerde der
|
|
40
|
+
* Bildschirm ein Logo zeigen, das der Bon nie druckt -- Bildschirm und
|
|
41
|
+
* Papier waeren sich uneins, genau das soll das Beleg-Blatt verhindern.
|
|
42
|
+
*/
|
|
43
|
+
exports.LOGO_PIXEL_MAX = 4096;
|
|
44
|
+
/** Reine Pruefung, ob ein Logo dieser Pixelmasse noch angezeigt/gedruckt werden darf. */
|
|
45
|
+
function logoPixelZulaessig(breite, hoehe) {
|
|
46
|
+
return breite > 0 && hoehe > 0 && breite <= exports.LOGO_PIXEL_MAX && hoehe <= exports.LOGO_PIXEL_MAX;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Das Logo in den Kasten seiner Stufe einpassen -- **nie hochrechnen**: ein
|
|
50
|
+
* Bildpixel wird hoechstens ein Druckpunkt. Ein kleines Logo bleibt klein,
|
|
51
|
+
* statt am Bon verwaschen zu werden (so hielten es PDF und Web-Kasse schon).
|
|
52
|
+
*/
|
|
53
|
+
function logoMass(logo, zeichen) {
|
|
54
|
+
if (!(logo.pxBreite > 0) || !(logo.pxHoehe > 0))
|
|
55
|
+
throw new Error('Logo ohne Pixelmass');
|
|
56
|
+
const stufe = exports.LOGO_STUFEN[logo.stufe];
|
|
57
|
+
if (stufe === undefined)
|
|
58
|
+
throw new Error(`Unbekannte Logo-Stufe: ${String(logo.stufe)}`);
|
|
59
|
+
const blattPunkte = zeichen * exports.PUNKTE_JE_ZEICHEN;
|
|
60
|
+
const faktor = Math.min((stufe.breiteAnteil * blattPunkte) / logo.pxBreite, (stufe.hoeheZeilen * exports.PUNKTE_JE_ZEILE) / logo.pxHoehe, 1);
|
|
61
|
+
return {
|
|
62
|
+
breiteAnteil: (logo.pxBreite * faktor) / blattPunkte,
|
|
63
|
+
hoeheZeilen: (logo.pxHoehe * faktor) / exports.PUNKTE_JE_ZEILE,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Das Mass in ganzen Druckpunkten -- so gross muss das Rasterbild fuer den Bon sein. */
|
|
67
|
+
function logoRasterMass(mass, zeichen) {
|
|
68
|
+
return {
|
|
69
|
+
breite: Math.max(1, Math.round(mass.breiteAnteil * zeichen * exports.PUNKTE_JE_ZEICHEN)),
|
|
70
|
+
hoehe: Math.max(1, Math.round(mass.hoeheZeilen * exports.PUNKTE_JE_ZEILE)),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Anteil der Blattbreite, den der QR am Drucker einnimmt -- dieselbe Rechnung
|
|
75
|
+
* wie der Druckweg (nativ, sonst der Bildweg). Bildschirm und PDF zeigen den
|
|
76
|
+
* QR in genau dieser Groesse. Leere Nutzlast: 0 (kein Symbol).
|
|
77
|
+
*
|
|
78
|
+
* Der Kasten **schliesst die Ruhezone von 4 Modulen je Seite ein** und setzt
|
|
79
|
+
* Fehlerkorrektur M voraus. Ein Zeichner (`renderQr`, PDF) muss das Symbol
|
|
80
|
+
* darum mit Fehlerkorrektur M und einer Ruhezone von 4 Modulen zeichnen, die
|
|
81
|
+
* den Kasten ganz ausfuellt -- sonst stimmt die Modulgroesse nicht mit dem Bon.
|
|
82
|
+
*/
|
|
83
|
+
function qrBlattAnteil(nutzlast, papier, groesse = 'auto') {
|
|
84
|
+
if (nutzlast === '')
|
|
85
|
+
return 0;
|
|
86
|
+
// Ein Inhalt, der in keine QR-Version passt (Fehlerkorrektur M, hoechstens
|
|
87
|
+
// 2331 Byte), wuerde hier `qrGroesseFuer` -> `qrModulAnzahl` zum Werfen
|
|
88
|
+
// bringen -- und riss damit jeden Zeichner mit, der das Blatt baut
|
|
89
|
+
// (Bildschirm, Bon, PDF). 0, wie bei leerer Nutzlast: der Beleg steht ohne
|
|
90
|
+
// QR, statt gar nicht zu stehen. `qrModulAnzahl` selbst wirft weiter -- wer
|
|
91
|
+
// es ausserhalb des Blatts aufruft, soll den Fehler sehen.
|
|
92
|
+
if (!(0, index_js_1.qrPasstInVersion)(nutzlast))
|
|
93
|
+
return 0;
|
|
94
|
+
const papierPunkte = index_js_1.QR_DRUCK_PUNKTE[papier];
|
|
95
|
+
const mass = (0, index_js_1.qrGroesseFuer)({ nutzlast, papierbreitePunkte: papierPunkte, groesse });
|
|
96
|
+
if (mass.passt)
|
|
97
|
+
return mass.breitePunkte / papierPunkte;
|
|
98
|
+
return ((mass.module + 2 * index_js_1.QR_RUHEZONE_MODULE) * (0, index_js_1.qrRasterPunkte)(papier, mass.module, { groesse })) / papierPunkte;
|
|
99
|
+
}
|
|
100
|
+
function zentriert(text, zeichen) {
|
|
101
|
+
const t = text.length > zeichen ? text.slice(0, zeichen) : text;
|
|
102
|
+
const links = Math.floor((zeichen - t.length) / 2);
|
|
103
|
+
return ' '.repeat(links) + t + ' '.repeat(zeichen - t.length - links);
|
|
104
|
+
}
|
|
105
|
+
function belegBlatt(layout, optionen = {}) {
|
|
106
|
+
const grid = (0, grid_js_1.renderReceiptGrid)(layout, optionen.zeichen === undefined ? {} : { zeichen: optionen.zeichen });
|
|
107
|
+
const zeichen = grid.zeichen;
|
|
108
|
+
const papier = papierFuerZeichen(zeichen, layout.paperSize);
|
|
109
|
+
const qrGroesse = optionen.qrGroesse ?? 'auto';
|
|
110
|
+
const leerzeile = { art: 'zeile', text: ' '.repeat(zeichen), fett: false, leer: true };
|
|
111
|
+
const block = (z) => z.kind === 'qr'
|
|
112
|
+
? { art: 'qr', nutzlast: z.qr ?? '', breiteAnteil: qrBlattAnteil(z.qr ?? '', papier, qrGroesse) }
|
|
113
|
+
: { art: 'zeile', text: z.text, fett: z.bold, leer: z.kind === 'space' };
|
|
114
|
+
const bloecke = [];
|
|
115
|
+
let i = 0;
|
|
116
|
+
if (optionen.logo) {
|
|
117
|
+
// Fuehrende Aufdrucke (Testkasse, Testsignatur) bleiben ganz oben: wer den
|
|
118
|
+
// Beleg sieht, sieht zuerst, dass er nicht gilt.
|
|
119
|
+
while (i < grid.lines.length && grid.lines[i].kind === 'banner') {
|
|
120
|
+
bloecke.push(block(grid.lines[i]));
|
|
121
|
+
i += 1;
|
|
122
|
+
}
|
|
123
|
+
// Die Leerzeilen gehoeren zum Vertrag: kein Zeichner kann das Logo an einen
|
|
124
|
+
// Rahmen oder an den Firmennamen kleben.
|
|
125
|
+
if (i > 0)
|
|
126
|
+
bloecke.push(leerzeile);
|
|
127
|
+
bloecke.push({ art: 'logo', ...logoMass(optionen.logo, zeichen) });
|
|
128
|
+
bloecke.push(leerzeile);
|
|
129
|
+
}
|
|
130
|
+
for (; i < grid.lines.length; i += 1)
|
|
131
|
+
bloecke.push(block(grid.lines[i]));
|
|
132
|
+
if (optionen.marke === true) {
|
|
133
|
+
bloecke.push(leerzeile);
|
|
134
|
+
bloecke.push({ art: 'zeile', text: zentriert(exports.MARKE_TEXT, zeichen), fett: false, leer: false });
|
|
135
|
+
}
|
|
136
|
+
return { zeichen, bloecke };
|
|
137
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { ReceiptLayout } from './layout.js';
|
|
2
2
|
import type { PosPaperSize } from '../printing/escpos.js';
|
|
3
|
-
import { type QrModulGroesse } from '../printing/index.js';
|
|
3
|
+
import { type QrModulGroesse, type RasterBild } from '../printing/index.js';
|
|
4
|
+
import { type DruckLogo } from './layout-escpos.js';
|
|
4
5
|
/**
|
|
5
6
|
* ePOS-Print XML (Epson TM-Drucker: Server Direct Print, ePOS-Print ueber
|
|
6
7
|
* HTTP) -- **aus dem Zeichenraster**: jede Rasterzeile wird eine <text>-Zeile
|
|
7
|
-
* mit exakt N Zeichen und Zeilenumbruch, Aufdrucke
|
|
8
|
-
*
|
|
8
|
+
* mit exakt N Zeichen und Zeilenumbruch, Aufdrucke als Rahmen aus
|
|
9
|
+
* Rasterzeilen, QR als <symbol>, Leerraum als <feed>, am Ende Schnitt. Kein
|
|
9
10
|
* eigenes Setzen -- was Bildschirm, ESC/POS und PDF zeigen, druckt der Epson
|
|
10
11
|
* Zeile fuer Zeile genauso.
|
|
11
12
|
*
|
|
@@ -22,16 +23,20 @@ export interface EposPrintXmlOptions {
|
|
|
22
23
|
*/
|
|
23
24
|
qrBreite?: number;
|
|
24
25
|
/**
|
|
25
|
-
* Deckel fuer die gerechnete QR-Modulgroesse; Vorgabe `
|
|
26
|
+
* Deckel fuer die gerechnete QR-Modulgroesse; Vorgabe `auto` (hoechstens 6
|
|
27
|
+
* Punkte je Modul) -- wie ESC/POS, Blatt und der Flutter-Zwilling.
|
|
26
28
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* damit ohne Wahl kein Byte anders herauskommt.
|
|
29
|
+
* Bis 0.13 stand hier `mittel`, weil `auto` damals beim ESC/POS-Wert 4
|
|
30
|
+
* deckelte und dieser Weg seit jeher mit 6 druckt. Seit `auto` ueberall 6
|
|
31
|
+
* heisst, ist das derselbe Wert: ohne Wahl kommt kein Byte anders heraus.
|
|
31
32
|
*/
|
|
32
33
|
qrGroesse?: QrModulGroesse;
|
|
33
34
|
/** Papierschnitt am Ende, Vorgabe true. */
|
|
34
35
|
cut?: boolean;
|
|
36
|
+
/** Firmenlogo; ohne Angabe kein Logo. */
|
|
37
|
+
logo?: DruckLogo | null;
|
|
38
|
+
/** "erstellt mit Kasseneck" am Ende. */
|
|
39
|
+
marke?: boolean;
|
|
35
40
|
}
|
|
36
41
|
/**
|
|
37
42
|
* Das XML **samt** dem, was dem QR unterwegs zugestossen ist -- dieselbe
|
|
@@ -45,6 +50,8 @@ export interface EposPrintErgebnis {
|
|
|
45
50
|
qrAusweich: string | null;
|
|
46
51
|
}
|
|
47
52
|
export declare function eposXmlEscape(text: string): string;
|
|
53
|
+
/** Ein Rasterbild als ePOS-`<image>` (einfarbig, Punktmass, Rasterzeilen Base64). */
|
|
54
|
+
export declare function eposBildXml(bild: RasterBild): string;
|
|
48
55
|
/** Nur das XML; wer die QR-Meldungen braucht, nimmt [eposPrintXmlErgebnis]. */
|
|
49
56
|
export declare function eposPrintXml(layout: ReceiptLayout, options?: EposPrintXmlOptions): string;
|
|
50
57
|
/**
|
|
@@ -77,6 +84,10 @@ export interface EposDirectOptions {
|
|
|
77
84
|
qrBreite?: number;
|
|
78
85
|
/** Deckel fuer die gerechnete QR-Modulgroesse; siehe [EposPrintXmlOptions.qrGroesse]. */
|
|
79
86
|
qrGroesse?: QrModulGroesse;
|
|
87
|
+
/** Firmenlogo; ohne Angabe kein Logo. */
|
|
88
|
+
logo?: DruckLogo | null;
|
|
89
|
+
/** "erstellt mit Kasseneck" am Ende. */
|
|
90
|
+
marke?: boolean;
|
|
80
91
|
timeoutMs?: number;
|
|
81
92
|
}
|
|
82
93
|
export interface EposResponse {
|
package/dist/cjs/receipt/epos.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EposConnectionError = void 0;
|
|
4
4
|
exports.eposXmlEscape = eposXmlEscape;
|
|
5
|
+
exports.eposBildXml = eposBildXml;
|
|
5
6
|
exports.eposPrintXml = eposPrintXml;
|
|
6
7
|
exports.eposPrintXmlErgebnis = eposPrintXmlErgebnis;
|
|
7
8
|
exports.eposServiceUrl = eposServiceUrl;
|
|
@@ -11,9 +12,14 @@ exports.eposDirectPrint = eposDirectPrint;
|
|
|
11
12
|
exports.eposDirectStatus = eposDirectStatus;
|
|
12
13
|
const index_js_1 = require("../printing/index.js");
|
|
13
14
|
const grid_js_1 = require("./grid.js");
|
|
15
|
+
const layout_escpos_js_1 = require("./layout-escpos.js");
|
|
14
16
|
function eposXmlEscape(text) {
|
|
15
17
|
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
16
18
|
}
|
|
19
|
+
/** Ein Rasterbild als ePOS-`<image>` (einfarbig, Punktmass, Rasterzeilen Base64). */
|
|
20
|
+
function eposBildXml(bild) {
|
|
21
|
+
return `<image width="${bild.breite}" height="${bild.hoehe}" color="color_1" mode="mono">${(0, index_js_1.rasterZeilenBase64)(bild)}</image>`;
|
|
22
|
+
}
|
|
17
23
|
const NS = 'http://www.epson-pos.com/schemas/2011/03/epos-print';
|
|
18
24
|
/** Nur das XML; wer die QR-Meldungen braucht, nimmt [eposPrintXmlErgebnis]. */
|
|
19
25
|
function eposPrintXml(layout, options = {}) {
|
|
@@ -30,8 +36,9 @@ function eposPrintXml(layout, options = {}) {
|
|
|
30
36
|
* laesst es weg; genau daran fehlte am echten Beleg der QR.
|
|
31
37
|
*/
|
|
32
38
|
function eposPrintXmlErgebnis(layout, options = {}) {
|
|
33
|
-
const
|
|
34
|
-
const deckel = options.qrGroesse ?? '
|
|
39
|
+
const zeichen = options.zeichen ?? grid_js_1.ZEICHEN_JE_PAPIER[layout.paperSize];
|
|
40
|
+
const deckel = options.qrGroesse ?? 'auto';
|
|
41
|
+
const blatt = (0, layout_escpos_js_1.blattFuerDruck)(layout, { zeichen, logo: options.logo, marke: options.marke, qrGroesse: deckel });
|
|
35
42
|
const fest = options.qrBreite === undefined
|
|
36
43
|
? null
|
|
37
44
|
: Math.min(16, Math.max(3, Math.floor(options.qrBreite)));
|
|
@@ -50,6 +57,12 @@ function eposPrintXmlErgebnis(layout, options = {}) {
|
|
|
50
57
|
return fest;
|
|
51
58
|
if (nutzlast === '')
|
|
52
59
|
return index_js_1.QR_MODUL_DECKEL[deckel];
|
|
60
|
+
// Passt der Inhalt in keine QR-Version, gibt es kein Symbol, das der
|
|
61
|
+
// Drucker setzen koennte -- wie beim Blatt (Anteil 0) geht der Beleg ohne QR.
|
|
62
|
+
if (!(0, index_js_1.qrPasstInVersion)(nutzlast)) {
|
|
63
|
+
qrFehler = 'QR-Inhalt passt in keine QR-Version -- Beleg ohne QR';
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
53
66
|
const mass = (0, index_js_1.qrGroesseFuer)({
|
|
54
67
|
nutzlast,
|
|
55
68
|
papierbreitePunkte: index_js_1.QR_DRUCK_PUNKTE[layout.paperSize],
|
|
@@ -74,32 +87,34 @@ function eposPrintXmlErgebnis(layout, options = {}) {
|
|
|
74
87
|
out.push('<text font="font_a"/>');
|
|
75
88
|
out.push('<text align="left"/>');
|
|
76
89
|
out.push('<text width="1" height="1" reverse="false" em="false"/>');
|
|
77
|
-
for (const
|
|
78
|
-
switch (
|
|
79
|
-
case '
|
|
80
|
-
|
|
90
|
+
for (const block of blatt.bloecke) {
|
|
91
|
+
switch (block.art) {
|
|
92
|
+
case 'zeile':
|
|
93
|
+
if (block.leer)
|
|
94
|
+
out.push('<feed line="1"/>');
|
|
95
|
+
else if (block.fett) {
|
|
96
|
+
out.push(`<text em="true">${eposXmlEscape(block.text)} </text>`);
|
|
97
|
+
out.push('<text em="false"/>');
|
|
98
|
+
}
|
|
99
|
+
else
|
|
100
|
+
out.push(`<text>${eposXmlEscape(block.text)} </text>`);
|
|
101
|
+
break;
|
|
102
|
+
case 'logo':
|
|
103
|
+
if (options.logo) {
|
|
104
|
+
out.push('<text align="center"/>');
|
|
105
|
+
out.push(eposBildXml(options.logo.raster));
|
|
106
|
+
out.push('<text align="left"/>');
|
|
107
|
+
}
|
|
81
108
|
break;
|
|
82
109
|
case 'qr': {
|
|
83
|
-
const breite = qrBreiteFuer(
|
|
110
|
+
const breite = qrBreiteFuer(block.nutzlast);
|
|
84
111
|
if (breite === null)
|
|
85
112
|
break;
|
|
86
113
|
out.push('<text align="center"/>');
|
|
87
|
-
out.push(`<symbol type="qrcode_model_2" level="level_m" width="${breite}" height="0" size="0">${eposXmlEscape(
|
|
114
|
+
out.push(`<symbol type="qrcode_model_2" level="level_m" width="${breite}" height="0" size="0">${eposXmlEscape(block.nutzlast)}</symbol>`);
|
|
88
115
|
out.push('<text align="left"/>');
|
|
89
116
|
break;
|
|
90
117
|
}
|
|
91
|
-
case 'banner':
|
|
92
|
-
out.push(`<text width="1" height="2" reverse="${z.ton === 'warnung' ? 'true' : 'false'}" em="true">${eposXmlEscape(z.text)} </text>`);
|
|
93
|
-
out.push('<text width="1" height="1" reverse="false" em="false"/>');
|
|
94
|
-
break;
|
|
95
|
-
default:
|
|
96
|
-
if (z.bold)
|
|
97
|
-
out.push(`<text em="true">${eposXmlEscape(z.text)} </text>`);
|
|
98
|
-
else
|
|
99
|
-
out.push(`<text>${eposXmlEscape(z.text)} </text>`);
|
|
100
|
-
if (z.bold)
|
|
101
|
-
out.push('<text em="false"/>');
|
|
102
|
-
break;
|
|
103
118
|
}
|
|
104
119
|
}
|
|
105
120
|
if (options.cut !== false) {
|
|
@@ -220,6 +235,10 @@ function eposDirectPrint(layout, o, fetchFn = fetch) {
|
|
|
220
235
|
xmlOptionen.qrBreite = o.qrBreite;
|
|
221
236
|
if (o.qrGroesse !== undefined)
|
|
222
237
|
xmlOptionen.qrGroesse = o.qrGroesse;
|
|
238
|
+
if (o.logo)
|
|
239
|
+
xmlOptionen.logo = o.logo;
|
|
240
|
+
if (o.marke !== undefined)
|
|
241
|
+
xmlOptionen.marke = o.marke;
|
|
223
242
|
return eposDirectSend(eposPrintXml({ ...layout, paperSize: papier }, xmlOptionen), o, fetchFn);
|
|
224
243
|
}
|
|
225
244
|
/** Verbindungstest: leeres Dokument, druckt nichts, liefert den Druckerstatus. */
|