@kreiseck/kasseneck-api 0.13.1 → 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.
Files changed (172) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +127 -21
  3. package/dist/cjs/client/auth.d.ts +4 -4
  4. package/dist/cjs/client/auth.js +3 -3
  5. package/dist/cjs/client/cashregisters.d.ts +1 -1
  6. package/dist/cjs/client/cashregisters.js +1 -1
  7. package/dist/cjs/client/errors.d.ts +1 -1
  8. package/dist/cjs/client/errors.js +2 -2
  9. package/dist/cjs/client/receipts.d.ts +8 -1
  10. package/dist/cjs/client/receipts.js +2 -1
  11. package/dist/cjs/client/transport.d.ts +1 -1
  12. package/dist/cjs/client/transport.js +2 -2
  13. package/dist/cjs/kasse/drucker.d.ts +5 -0
  14. package/dist/cjs/kasse/drucker.js +9 -0
  15. package/dist/cjs/kasse/settings.js +5 -2
  16. package/dist/cjs/models/cashregister.d.ts +2 -2
  17. package/dist/cjs/models/receipt-company.d.ts +1 -1
  18. package/dist/cjs/printing/escpos.d.ts +38 -4
  19. package/dist/cjs/printing/escpos.js +87 -15
  20. package/dist/cjs/printing/index.d.ts +2 -2
  21. package/dist/cjs/printing/index.js +5 -1
  22. package/dist/cjs/printing/qr-groesse.d.ts +26 -21
  23. package/dist/cjs/printing/qr-groesse.js +22 -8
  24. package/dist/cjs/react/index.d.ts +53 -0
  25. package/dist/cjs/react/index.js +81 -0
  26. package/dist/cjs/receipt/bild.d.ts +14 -0
  27. package/dist/cjs/receipt/bild.js +65 -0
  28. package/dist/cjs/receipt/blatt.d.ts +107 -0
  29. package/dist/cjs/receipt/blatt.js +137 -0
  30. package/dist/cjs/receipt/epos.d.ts +19 -8
  31. package/dist/cjs/receipt/epos.js +39 -20
  32. package/dist/cjs/receipt/grid.d.ts +1 -1
  33. package/dist/cjs/receipt/grid.js +9 -1
  34. package/dist/cjs/receipt/index.d.ts +4 -2
  35. package/dist/cjs/receipt/index.js +17 -1
  36. package/dist/cjs/receipt/layout-escpos.d.ts +31 -3
  37. package/dist/cjs/receipt/layout-escpos.js +58 -18
  38. package/dist/cjs/register/index.d.ts +1 -1
  39. package/dist/cjs/register/index.js +1 -1
  40. package/dist/cjs/register/pairing.d.ts +2 -2
  41. package/dist/esm/client/auth.d.ts +4 -4
  42. package/dist/esm/client/auth.js +3 -3
  43. package/dist/esm/client/cashregisters.d.ts +1 -1
  44. package/dist/esm/client/cashregisters.js +1 -1
  45. package/dist/esm/client/errors.d.ts +1 -1
  46. package/dist/esm/client/errors.js +2 -2
  47. package/dist/esm/client/receipts.d.ts +8 -1
  48. package/dist/esm/client/receipts.js +2 -1
  49. package/dist/esm/client/transport.d.ts +1 -1
  50. package/dist/esm/client/transport.js +2 -2
  51. package/dist/esm/kasse/drucker.d.ts +5 -0
  52. package/dist/esm/kasse/drucker.js +9 -0
  53. package/dist/esm/kasse/settings.js +5 -2
  54. package/dist/esm/models/cashregister.d.ts +2 -2
  55. package/dist/esm/models/receipt-company.d.ts +1 -1
  56. package/dist/esm/printing/escpos.d.ts +38 -4
  57. package/dist/esm/printing/escpos.js +84 -15
  58. package/dist/esm/printing/index.d.ts +2 -2
  59. package/dist/esm/printing/index.js +2 -2
  60. package/dist/esm/printing/qr-groesse.d.ts +26 -21
  61. package/dist/esm/printing/qr-groesse.js +21 -8
  62. package/dist/esm/react/index.d.ts +53 -0
  63. package/dist/esm/react/index.js +80 -1
  64. package/dist/esm/receipt/bild.d.ts +14 -0
  65. package/dist/esm/receipt/bild.js +62 -0
  66. package/dist/esm/receipt/blatt.d.ts +107 -0
  67. package/dist/esm/receipt/blatt.js +128 -0
  68. package/dist/esm/receipt/epos.d.ts +19 -8
  69. package/dist/esm/receipt/epos.js +40 -22
  70. package/dist/esm/receipt/grid.d.ts +1 -1
  71. package/dist/esm/receipt/grid.js +9 -1
  72. package/dist/esm/receipt/index.d.ts +4 -2
  73. package/dist/esm/receipt/index.js +4 -2
  74. package/dist/esm/receipt/layout-escpos.d.ts +31 -3
  75. package/dist/esm/receipt/layout-escpos.js +58 -20
  76. package/dist/esm/register/index.d.ts +1 -1
  77. package/dist/esm/register/index.js +1 -1
  78. package/dist/esm/register/pairing.d.ts +2 -2
  79. package/fixtures/erwartet/karte-eigener.blatt32.json +159 -0
  80. package/fixtures/erwartet/karte-eigener.blatt48.json +159 -0
  81. package/fixtures/erwartet/karte-gptom-ios.blatt32.json +231 -0
  82. package/fixtures/erwartet/karte-gptom-ios.blatt48.json +231 -0
  83. package/fixtures/erwartet/karte-gptom.blatt32.json +231 -0
  84. package/fixtures/erwartet/karte-gptom.blatt48.json +231 -0
  85. package/fixtures/erwartet/karte-hobex-cloud.blatt32.json +201 -0
  86. package/fixtures/erwartet/karte-hobex-cloud.blatt48.json +201 -0
  87. package/fixtures/erwartet/karte-hobex-hps.blatt32.json +225 -0
  88. package/fixtures/erwartet/karte-hobex-hps.blatt48.json +225 -0
  89. package/fixtures/erwartet/karte-mypos.blatt32.json +255 -0
  90. package/fixtures/erwartet/karte-mypos.blatt48.json +243 -0
  91. package/fixtures/erwartet/karte-stripe-eps.blatt32.json +195 -0
  92. package/fixtures/erwartet/karte-stripe-eps.blatt48.json +195 -0
  93. package/fixtures/erwartet/karte-stripe.blatt32.json +213 -0
  94. package/fixtures/erwartet/karte-stripe.blatt48.json +213 -0
  95. package/fixtures/erwartet/karte-sumup.blatt32.json +219 -0
  96. package/fixtures/erwartet/karte-sumup.blatt48.json +207 -0
  97. package/fixtures/erwartet/langer-artikelname.blatt32.json +177 -0
  98. package/fixtures/erwartet/langer-artikelname.blatt48.json +171 -0
  99. package/fixtures/erwartet/logo-probe-hoch.raster32.txt +120 -0
  100. package/fixtures/erwartet/logo-probe.raster32.txt +54 -0
  101. package/fixtures/erwartet/null-ausfall.blatt32.json +207 -0
  102. package/fixtures/erwartet/null-ausfall.blatt48.json +189 -0
  103. package/fixtures/erwartet/null-ausfall.grid32.txt +2 -0
  104. package/fixtures/erwartet/null-ausfall.grid48.txt +2 -0
  105. package/fixtures/erwartet/null-jahr.blatt32.json +213 -0
  106. package/fixtures/erwartet/null-jahr.blatt48.json +189 -0
  107. package/fixtures/erwartet/null-jahr.grid32.txt +2 -0
  108. package/fixtures/erwartet/null-jahr.grid48.txt +2 -0
  109. package/fixtures/erwartet/null-monat.blatt32.json +207 -0
  110. package/fixtures/erwartet/null-monat.blatt48.json +189 -0
  111. package/fixtures/erwartet/null-monat.grid32.txt +2 -0
  112. package/fixtures/erwartet/null-monat.grid48.txt +2 -0
  113. package/fixtures/erwartet/null-pruef.blatt32.json +207 -0
  114. package/fixtures/erwartet/null-pruef.blatt48.json +189 -0
  115. package/fixtures/erwartet/null-pruef.grid32.txt +2 -0
  116. package/fixtures/erwartet/null-pruef.grid48.txt +2 -0
  117. package/fixtures/erwartet/null-schluss.blatt32.json +207 -0
  118. package/fixtures/erwartet/null-schluss.blatt48.json +189 -0
  119. package/fixtures/erwartet/null-schluss.grid32.txt +2 -0
  120. package/fixtures/erwartet/null-schluss.grid48.txt +2 -0
  121. package/fixtures/erwartet/null-start.blatt32.json +207 -0
  122. package/fixtures/erwartet/null-start.blatt48.json +189 -0
  123. package/fixtures/erwartet/null-start.grid32.txt +2 -0
  124. package/fixtures/erwartet/null-start.grid48.txt +2 -0
  125. package/fixtures/erwartet/rabatt-chef-trinkgeld.blatt32.json +213 -0
  126. package/fixtures/erwartet/rabatt-chef-trinkgeld.blatt48.json +207 -0
  127. package/fixtures/erwartet/rabatt-einfach.blatt32.json +183 -0
  128. package/fixtures/erwartet/rabatt-einfach.blatt48.json +177 -0
  129. package/fixtures/erwartet/rabatt-trinkgeld.blatt32.json +213 -0
  130. package/fixtures/erwartet/rabatt-trinkgeld.blatt48.json +207 -0
  131. package/fixtures/erwartet/rabatt-wertgutschein.blatt32.json +219 -0
  132. package/fixtures/erwartet/rabatt-wertgutschein.blatt48.json +213 -0
  133. package/fixtures/erwartet/rabattzeilen.blatt32.json +201 -0
  134. package/fixtures/erwartet/rabattzeilen.blatt48.json +195 -0
  135. package/fixtures/erwartet/signaturausfall-verkauf.blatt32.json +195 -0
  136. package/fixtures/erwartet/signaturausfall-verkauf.blatt48.json +183 -0
  137. package/fixtures/erwartet/storno-rabatt.blatt32.json +249 -0
  138. package/fixtures/erwartet/storno-rabatt.blatt48.json +237 -0
  139. package/fixtures/erwartet/storno-rabatt.grid32.txt +2 -0
  140. package/fixtures/erwartet/storno-rabatt.grid48.txt +2 -0
  141. package/fixtures/erwartet/storno-teil.blatt32.json +207 -0
  142. package/fixtures/erwartet/storno-teil.blatt48.json +201 -0
  143. package/fixtures/erwartet/storno-teil.grid32.txt +2 -0
  144. package/fixtures/erwartet/storno-teil.grid48.txt +2 -0
  145. package/fixtures/erwartet/storno-voll.blatt32.json +219 -0
  146. package/fixtures/erwartet/storno-voll.blatt48.json +213 -0
  147. package/fixtures/erwartet/storno-voll.grid32.txt +2 -0
  148. package/fixtures/erwartet/storno-voll.grid48.txt +2 -0
  149. package/fixtures/erwartet/testkasse-verkauf.blatt32.json +225 -0
  150. package/fixtures/erwartet/testkasse-verkauf.blatt48.json +219 -0
  151. package/fixtures/erwartet/testkasse-verkauf.grid32.txt +5 -1
  152. package/fixtures/erwartet/testkasse-verkauf.grid48.txt +5 -1
  153. package/fixtures/erwartet/testsignatur-verkauf.blatt32.json +237 -0
  154. package/fixtures/erwartet/testsignatur-verkauf.blatt48.json +219 -0
  155. package/fixtures/erwartet/testsignatur-verkauf.grid32.txt +5 -1
  156. package/fixtures/erwartet/testsignatur-verkauf.grid48.txt +5 -1
  157. package/fixtures/erwartet/training.blatt32.json +237 -0
  158. package/fixtures/erwartet/training.blatt48.json +219 -0
  159. package/fixtures/erwartet/training.grid32.txt +2 -0
  160. package/fixtures/erwartet/training.grid48.txt +2 -0
  161. package/fixtures/erwartet/verkauf-bar.blatt32.json +177 -0
  162. package/fixtures/erwartet/verkauf-bar.blatt48.json +171 -0
  163. package/fixtures/erwartet/verkauf-karte.blatt32.json +177 -0
  164. package/fixtures/erwartet/verkauf-karte.blatt48.json +171 -0
  165. package/fixtures/erwartet/verkauf-kleinunternehmer.blatt32.json +183 -0
  166. package/fixtures/erwartet/verkauf-kleinunternehmer.blatt48.json +177 -0
  167. package/fixtures/hobex-hps-codes.json +1 -1
  168. package/fixtures/kasse-settings-standard.json +1 -1
  169. package/fixtures/kasse-texte.json +1 -1
  170. package/fixtures/manifest.json +109 -43
  171. package/fixtures/oberflaeche.json +1 -1
  172. package/package.json +20 -5
@@ -1,9 +1,41 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pruefeLogoRaster = pruefeLogoRaster;
4
+ exports.blattFuerDruck = blattFuerDruck;
3
5
  exports.escPosLayoutBytes = escPosLayoutBytes;
4
6
  exports.escPosLayoutErgebnis = escPosLayoutErgebnis;
5
7
  const index_js_1 = require("../printing/index.js");
8
+ const blatt_js_1 = require("./blatt.js");
6
9
  const grid_js_1 = require("./grid.js");
10
+ /** Das Rasterbild muss genau so gross sein, wie das Blatt das Logo setzt -- sonst stuende am Bon ein anderes Logo als am Schirm. */
11
+ function pruefeLogoRaster(logo, mass, zeichen) {
12
+ const soll = (0, blatt_js_1.logoRasterMass)(mass, zeichen);
13
+ if (logo.raster.breite !== soll.breite || logo.raster.hoehe !== soll.hoehe) {
14
+ throw new Error(`Logo-Raster ${logo.raster.breite}x${logo.raster.hoehe} passt nicht zum Blatt (${soll.breite}x${soll.hoehe})`);
15
+ }
16
+ }
17
+ /**
18
+ * Baut das Blatt fuer einen Druckweg (ESC/POS, ePOS) an EINER Stelle: die
19
+ * Zuordnung `DruckLogo` -> `BlattLogo` und die Groessenpruefung des mitgebrachten
20
+ * Rasterbilds teilen sich beide Wege -- ohne das muesste eine Aenderung daran an
21
+ * zwei Stellen nachgezogen werden. Wirft VOR jeder Ausgabe (also bevor der
22
+ * Aufrufer auch nur ein Byte/Zeichen geschrieben hat), wenn das Rasterbild nicht
23
+ * zur Logo-Stufe passt.
24
+ */
25
+ function blattFuerDruck(layout, optionen) {
26
+ const blattOptionen = {
27
+ zeichen: optionen.zeichen,
28
+ marke: optionen.marke === true,
29
+ qrGroesse: optionen.qrGroesse,
30
+ };
31
+ if (optionen.logo)
32
+ blattOptionen.logo = { stufe: optionen.logo.stufe, pxBreite: optionen.logo.pxBreite, pxHoehe: optionen.logo.pxHoehe };
33
+ const blatt = (0, blatt_js_1.belegBlatt)(layout, blattOptionen);
34
+ const logoBlock = blatt.bloecke.find((b) => b.art === 'logo');
35
+ if (optionen.logo && logoBlock && logoBlock.art === 'logo')
36
+ pruefeLogoRaster(optionen.logo, logoBlock, blatt.zeichen);
37
+ return blatt;
38
+ }
7
39
  /**
8
40
  * Druckbar gemachtes Layout: Texte durch [escPosPrintableText] (Codepage,
9
41
  * "EUR" statt "€", Striche), damit das Raster mit den Zeichen rechnet, die
@@ -72,6 +104,13 @@ function escPosLayoutErgebnis(layout, options = {}) {
72
104
  * `qrFehler` aus `escPosQrCode`, und der Beleg geht ohne QR hinaus.
73
105
  */
74
106
  const qrZeile = (nutzlast) => {
107
+ // Ein Inhalt, der in keine QR-Version passt, laesst sich weder als Befehl
108
+ // noch als Bild drucken -- beide Wege wuerfen. Der Beleg geht ohne QR
109
+ // hinaus und sagt es ueber `qrFehler`, wie das Blatt (Anteil 0).
110
+ if (nutzlast !== '' && !(0, index_js_1.qrPasstInVersion)(nutzlast)) {
111
+ doc.qrFehler = 'QR-Inhalt passt in keine QR-Version -- Beleg ohne QR';
112
+ return;
113
+ }
75
114
  const raster = options.qrMatrix;
76
115
  if (nutzlast !== '' && raster !== undefined) {
77
116
  if (modus === 'imageRaster') {
@@ -98,27 +137,28 @@ function escPosLayoutErgebnis(layout, options = {}) {
98
137
  }
99
138
  (0, index_js_1.escPosQrCode)(doc, nutzlast, qrOptionen);
100
139
  };
101
- const grid = (0, grid_js_1.renderReceiptGrid)(druckbaresLayout(layout), { zeichen: grid_js_1.ZEICHEN_JE_PAPIER[paperSize] });
102
- for (const zeile of grid.lines) {
103
- switch (zeile.kind) {
104
- case 'space':
105
- (0, index_js_1.escPosFeed)(doc, 1);
106
- break;
140
+ // `blattFuerDruck` prueft das Logo-Raster bereits VOR der Rueckgabe -- die
141
+ // Schleife unten schreibt darum nie ein Byte auf ein falsch grosses Logo.
142
+ const blatt = blattFuerDruck(druckbaresLayout(layout), {
143
+ zeichen: grid_js_1.ZEICHEN_JE_PAPIER[paperSize],
144
+ logo: options.logo,
145
+ marke: options.marke,
146
+ qrGroesse: options.qrGroesse ?? 'auto',
147
+ });
148
+ for (const block of blatt.bloecke) {
149
+ switch (block.art) {
107
150
  case 'qr':
108
- qrZeile(zeile.qr ?? '');
151
+ qrZeile(block.nutzlast);
109
152
  break;
110
- case 'banner': {
111
- // Belegart/Warnung: fett zwischen zwei Volllinien — derselbe Rahmen-
112
- // Stil wie im Beleg-Viewer und im PDF (frueher druckte der Bon invers
113
- // weiss-auf-schwarz und sah damit anders aus als jede Anzeige).
114
- const rahmen = '='.repeat(grid_js_1.ZEICHEN_JE_PAPIER[paperSize]);
115
- (0, index_js_1.escPosText)(doc, rahmen, { styles: { align: 'left', bold: true } });
116
- (0, index_js_1.escPosText)(doc, zeile.text.trimEnd(), { styles: { align: 'left', bold: true, height: 2 } });
117
- (0, index_js_1.escPosText)(doc, rahmen, { styles: { align: 'left', bold: true } });
153
+ case 'logo':
154
+ if (options.logo)
155
+ (0, index_js_1.escPosRasterBild)(doc, options.logo.raster, { align: 'center' });
118
156
  break;
119
- }
120
- default:
121
- (0, index_js_1.escPosText)(doc, zeile.text.trimEnd(), { styles: { align: 'left', bold: zeile.bold } });
157
+ case 'zeile':
158
+ if (block.leer)
159
+ (0, index_js_1.escPosFeed)(doc, 1);
160
+ else
161
+ (0, index_js_1.escPosText)(doc, block.text.trimEnd(), { styles: { align: 'left', bold: block.fett } });
122
162
  break;
123
163
  }
124
164
  }
@@ -43,7 +43,7 @@
43
43
  * });
44
44
  * ```
45
45
  *
46
- * Mit `sitzung.customToken` meldet sich der Verbraucher bei Firebase an; das
46
+ * Mit `sitzung.customToken` meldet sich der Verbraucher beim Anmeldedienst an; das
47
47
  * daraus entstehende ID-Token und `sitzung.sessionId` ergeben zusammen
48
48
  * `registerUserAuth` — ab da laeuft alles Weitere ueber den ueblichen Weg.
49
49
  */
@@ -44,7 +44,7 @@
44
44
  * });
45
45
  * ```
46
46
  *
47
- * Mit `sitzung.customToken` meldet sich der Verbraucher bei Firebase an; das
47
+ * Mit `sitzung.customToken` meldet sich der Verbraucher beim Anmeldedienst an; das
48
48
  * daraus entstehende ID-Token und `sitzung.sessionId` ergeben zusammen
49
49
  * `registerUserAuth` — ab da laeuft alles Weitere ueber den ueblichen Weg.
50
50
  */
@@ -188,9 +188,9 @@ export interface RegisterUser {
188
188
  /** Ergebnis der PIN-Anmeldung. */
189
189
  export interface RegisterUserSession {
190
190
  /**
191
- * Firebase-Custom-Token. Damit meldet der Verbraucher sich bei Firebase an
191
+ * Custom-Token des Anmeldediensts. Damit meldet der Verbraucher sich dort an
192
192
  * und bekommt das ID-Token, das `registerUserAuth` braucht; dieses Paket
193
- * kennt Firebase nicht.
193
+ * kennt den Anmeldedienst nicht.
194
194
  */
195
195
  customToken: string;
196
196
  /** Laufende Sitzung — Kopfzeile `register-session` jedes weiteren Aufrufs. */
@@ -6,7 +6,7 @@
6
6
  * Bearer plus die Kopfzeile `cashregister-token` (wie im Flutter-Zwilling
7
7
  * `kasseneck_api`).
8
8
  * - `registerUserAuth` — der Weg der Browser-Kasse: ein Kassen-Benutzer, per
9
- * PIN an einem gekoppelten Geraet angemeldet. Firebase-ID-Token als Bearer,
9
+ * PIN an einem gekoppelten Geraet angemeldet. ID-Token als Bearer,
10
10
  * die laufende Sitzung als Kopfzeile `register-session`, die Kasse als
11
11
  * Parameter `cashregisterId` in der Nutzlast.
12
12
  *
@@ -14,9 +14,9 @@
14
14
  * fuehrt keine eigenen HTTP-Aufrufe, kennt den Transport nicht und haelt keinen
15
15
  * Zustand — genau deshalb ist sie austauschbar und einzeln testbar.
16
16
  *
17
- * Das Paket weiss **nichts** von Firebase: `registerUserAuth` bekommt eine
17
+ * Das Paket weiss **nichts** vom Anmeldedienst: `registerUserAuth` bekommt eine
18
18
  * Funktion, die ein gueltiges ID-Token liefert (darf `async` sein). Sie wird
19
- * bei **jedem** Aufruf befragt — Firebase-ID-Tokens laufen nach einer Stunde
19
+ * bei **jedem** Aufruf befragt — ID-Tokens laufen nach einer Stunde
20
20
  * ab, die Sitzung der Browser-Kasse lebt sogar nur 90 Sekunden und wird alle
21
21
  * 30 Sekunden erneuert; ein einmal gemerkter Wert waere also bald tot.
22
22
  */
@@ -39,7 +39,7 @@ export interface ApiKeyAuthOptions {
39
39
  cashregisterToken: string;
40
40
  }
41
41
  export interface RegisterUserAuthOptions {
42
- /** Liefert ein gueltiges Firebase-ID-Token; wird pro Anfrage befragt. */
42
+ /** Liefert ein gueltiges ID-Token des Anmeldediensts; wird pro Anfrage befragt. */
43
43
  getIdToken: () => string | Promise<string>;
44
44
  /** Liefert die laufende Kassen-Sitzung; wird pro Anfrage befragt. */
45
45
  getSessionId: () => string | Promise<string>;
@@ -6,7 +6,7 @@
6
6
  * Bearer plus die Kopfzeile `cashregister-token` (wie im Flutter-Zwilling
7
7
  * `kasseneck_api`).
8
8
  * - `registerUserAuth` — der Weg der Browser-Kasse: ein Kassen-Benutzer, per
9
- * PIN an einem gekoppelten Geraet angemeldet. Firebase-ID-Token als Bearer,
9
+ * PIN an einem gekoppelten Geraet angemeldet. ID-Token als Bearer,
10
10
  * die laufende Sitzung als Kopfzeile `register-session`, die Kasse als
11
11
  * Parameter `cashregisterId` in der Nutzlast.
12
12
  *
@@ -14,9 +14,9 @@
14
14
  * fuehrt keine eigenen HTTP-Aufrufe, kennt den Transport nicht und haelt keinen
15
15
  * Zustand — genau deshalb ist sie austauschbar und einzeln testbar.
16
16
  *
17
- * Das Paket weiss **nichts** von Firebase: `registerUserAuth` bekommt eine
17
+ * Das Paket weiss **nichts** vom Anmeldedienst: `registerUserAuth` bekommt eine
18
18
  * Funktion, die ein gueltiges ID-Token liefert (darf `async` sein). Sie wird
19
- * bei **jedem** Aufruf befragt — Firebase-ID-Tokens laufen nach einer Stunde
19
+ * bei **jedem** Aufruf befragt — ID-Tokens laufen nach einer Stunde
20
20
  * ab, die Sitzung der Browser-Kasse lebt sogar nur 90 Sekunden und wird alle
21
21
  * 30 Sekunden erneuert; ein einmal gemerkter Wert waere also bald tot.
22
22
  */
@@ -5,7 +5,7 @@ import type { InternerTransport } from './aufrufe.js';
5
5
  *
6
6
  * **Anmeldeweg:** `listMyCashregisters` laeuft im Backend unter
7
7
  * `checkRequest(req, 'customer', …, {allowRegisterUser: true})` — der Bearer
8
- * muss also ein Firebase-ID-Token sein. Mit `apiKeyAuth` (api_key als Bearer)
8
+ * muss also ein ID-Token des Anmeldediensts sein. Mit `apiKeyAuth` (api_key als Bearer)
9
9
  * ist dieser Endpunkt **nicht** erreichbar; fuer die Browser-Kasse ist
10
10
  * `registerUserAuth` der Weg. Dieses Paket bildet das nicht nach — wer darf,
11
11
  * entscheidet allein das Backend. Der Hinweis steht hier, damit ein Leser
@@ -5,7 +5,7 @@ import { KasseneckValidationError } from './errors.js';
5
5
  *
6
6
  * **Anmeldeweg:** `listMyCashregisters` laeuft im Backend unter
7
7
  * `checkRequest(req, 'customer', …, {allowRegisterUser: true})` — der Bearer
8
- * muss also ein Firebase-ID-Token sein. Mit `apiKeyAuth` (api_key als Bearer)
8
+ * muss also ein ID-Token des Anmeldediensts sein. Mit `apiKeyAuth` (api_key als Bearer)
9
9
  * ist dieser Endpunkt **nicht** erreichbar; fuer die Browser-Kasse ist
10
10
  * `registerUserAuth` der Weg. Dieses Paket bildet das nicht nach — wer darf,
11
11
  * entscheidet allein das Backend. Der Hinweis steht hier, damit ein Leser
@@ -30,7 +30,7 @@
30
30
  * Sonderfall.
31
31
  *
32
32
  * **Geheimnisse gehoeren in keinen dieser Fehler.** Fehlermeldungen landen in
33
- * Protokollen und Fehlerdiensten; weder `api_key`, Kassen-Token, Firebase-
33
+ * Protokollen und Fehlerdiensten; weder `api_key`, Kassen-Token, ID-
34
34
  * ID-Token noch Sitzungsbezeichner duerfen dorthin. Deshalb tragen die Fehler
35
35
  * ausschliesslich Funktionsname, HTTP-Status/Inhaltstyp, den vom Paket
36
36
  * formulierten Grund und die vom Backend formulierte Meldung — nie Kopfzeilen,
@@ -30,7 +30,7 @@
30
30
  * Sonderfall.
31
31
  *
32
32
  * **Geheimnisse gehoeren in keinen dieser Fehler.** Fehlermeldungen landen in
33
- * Protokollen und Fehlerdiensten; weder `api_key`, Kassen-Token, Firebase-
33
+ * Protokollen und Fehlerdiensten; weder `api_key`, Kassen-Token, ID-
34
34
  * ID-Token noch Sitzungsbezeichner duerfen dorthin. Deshalb tragen die Fehler
35
35
  * ausschliesslich Funktionsname, HTTP-Status/Inhaltstyp, den vom Paket
36
36
  * formulierten Grund und die vom Backend formulierte Meldung — nie Kopfzeilen,
@@ -56,7 +56,7 @@
56
56
  */
57
57
  // Bezeichner-artig: Buchstabe vorn, danach nur Bezeichnerzeichen, hoechstens
58
58
  // 64 Zeichen. Das laesst `TypeError`, `ECONNREFUSED` und `auth/internal-error`
59
- // durch, aber keinen Freitext und kein Firebase-ID-Token (~900 Zeichen).
59
+ // durch, aber keinen Freitext und kein ID-Token (~900 Zeichen).
60
60
  const BEZEICHNER = /^[A-Za-z][A-Za-z0-9_./-]{0,63}$/;
61
61
  /**
62
62
  * Reduziert eine fremde Ursache auf Name und Code — und verwirft beides, wenn
@@ -2,6 +2,7 @@ import { ReceiptType, type ReceiptTypeKey, KeckPaymentMethod, type KeckPaymentMe
2
2
  import { type Receipt, type ReceiptCompany, type ReceiptItem, type Voucher, type ReceiptSummary, type ReportMonth, type CancellationItem, type CancellationOf, type CancellationReason } from '../models/index.js';
3
3
  import type { InternerTransport } from './aufrufe.js';
4
4
  import type { Pruefangaben, ReceiptLayout } from '../receipt/layout.js';
5
+ import type { LogoStufe } from '../receipt/blatt.js';
5
6
  /**
6
7
  * Beleg-Endpunkte — Zwilling der Beleg-Aufrufe in
7
8
  * kasseneck_api/lib/kasseneck_api.dart.
@@ -168,6 +169,12 @@ export interface ReceiptWithCompany {
168
169
  * fuer Clients, die das Layout selbst bauen; null bei altem Backend.
169
170
  */
170
171
  pruefangaben: Pruefangaben | null;
172
+ /**
173
+ * Groesse des Firmenlogos am Beleg (Kasse-Einstellung `logoSkala` des
174
+ * Betriebs). Bildschirm, Bon und PDF setzen das Logo in genau dieser Stufe;
175
+ * liefert das Backend sie nicht (alt), gilt `M` -- der Bestandswert.
176
+ */
177
+ logoStufe: LogoStufe;
171
178
  }
172
179
  /**
173
180
  * Gemeinsame Umsetzung aller Belegarten (Zwilling von `_createReceipt`).
@@ -256,7 +263,7 @@ export interface ListMyReceiptsOptions {
256
263
  *
257
264
  * **Anmeldeweg:** Der Endpunkt laeuft im Backend unter
258
265
  * `checkRequest(req, 'customer', …, {allowRegisterUser: true})`, der Bearer
259
- * muss also ein Firebase-ID-Token sein — mit `apiKeyAuth` ist er nicht
266
+ * muss also ein ID-Token des Anmeldediensts sein — mit `apiKeyAuth` ist er nicht
260
267
  * erreichbar. Ausserdem prueft das Backend die Kassenzuweisung hier im Rumpf
261
268
  * (der Endpunkt laeuft mit `checkCashRegister: false`), ein Kassen-Benutzer
262
269
  * bekommt also nur die ihm zugewiesenen Kassen.
@@ -202,7 +202,7 @@ export function zeroReceipt(rufen) {
202
202
  *
203
203
  * **Anmeldeweg:** Der Endpunkt laeuft im Backend unter
204
204
  * `checkRequest(req, 'customer', …, {allowRegisterUser: true})`, der Bearer
205
- * muss also ein Firebase-ID-Token sein — mit `apiKeyAuth` ist er nicht
205
+ * muss also ein ID-Token des Anmeldediensts sein — mit `apiKeyAuth` ist er nicht
206
206
  * erreichbar. Ausserdem prueft das Backend die Kassenzuweisung hier im Rumpf
207
207
  * (der Endpunkt laeuft mit `checkCashRegister: false`), ein Kassen-Benutzer
208
208
  * bekommt also nur die ihm zugewiesenen Kassen.
@@ -567,5 +567,6 @@ function belegMitFirmaAusHuelle(daten, functionName) {
567
567
  kopfId: typeof d.kopfId === 'string' ? d.kopfId : null,
568
568
  layout,
569
569
  pruefangaben: pa ? { karteRegistriertAm: text(pa.karteRegistriertAm), kasseRegistriertAm: text(pa.kasseRegistriertAm) } : null,
570
+ logoStufe: d.logo_skala === 'S' || d.logo_skala === 'M' || d.logo_skala === 'L' || d.logo_skala === 'XL' ? d.logo_skala : 'M',
570
571
  };
571
572
  }
@@ -7,7 +7,7 @@ import type { KasseneckAuth } from './auth.js';
7
7
  * Der Transport macht drei Dinge und sonst nichts: Anfrage bauen (Kopfzeilen
8
8
  * von der Anmeldung, Nutzlast aus Auth-Parametern und Aufruferparametern),
9
9
  * Zeitlimit ueberwachen, und die Antworthuelle aufloesen. Er kennt keine
10
- * einzelne Backend-Funktion und keine Firebase-Details.
10
+ * einzelne Backend-Funktion und keine Details der Infrastruktur.
11
11
  *
12
12
  * Es gibt **zwei Einstiegspunkte** auf demselben Kern: [createTransport] fuer
13
13
  * die JSON-Aufrufe und [createBinaryTransport] fuer die beiden Bericht-
@@ -7,7 +7,7 @@ import { KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNet
7
7
  * Der Transport macht drei Dinge und sonst nichts: Anfrage bauen (Kopfzeilen
8
8
  * von der Anmeldung, Nutzlast aus Auth-Parametern und Aufruferparametern),
9
9
  * Zeitlimit ueberwachen, und die Antworthuelle aufloesen. Er kennt keine
10
- * einzelne Backend-Funktion und keine Firebase-Details.
10
+ * einzelne Backend-Funktion und keine Details der Infrastruktur.
11
11
  *
12
12
  * Es gibt **zwei Einstiegspunkte** auf demselben Kern: [createTransport] fuer
13
13
  * die JSON-Aufrufe und [createBinaryTransport] fuer die beiden Bericht-
@@ -76,7 +76,7 @@ function createCore(options) {
76
76
  throw new KasseneckNetworkError(fehlerName, true, zeitlimitMs);
77
77
  }
78
78
  // Eigene Pruefungen tragen ihren geheimnisfreien Grund weiter; von einer
79
- // fremden Ursache (Firebase & Co.) bleibt nichts uebrig — weder Meldung
79
+ // fremden Ursache bleibt nichts uebrig — weder Meldung
80
80
  // noch Verdichtung, siehe Klassenkommentar zu KasseneckAuthError.
81
81
  const grund = ursache instanceof KasseneckAuthError ? ursache.reason : 'Anmeldung fehlgeschlagen';
82
82
  throw new KasseneckAuthError(grund, { functionName: fehlerName });
@@ -1,5 +1,6 @@
1
1
  import type { InternerTransport } from '../client/aufrufe.js';
2
2
  import type { ReceiptLayout } from '../receipt/layout.js';
3
+ import type { DruckLogo } from '../receipt/layout-escpos.js';
3
4
  /**
4
5
  * Netzwerk-Bondrucker ueber Epson "Server Direct Print": das Backend fuehrt
5
6
  * je Konto Drucker mit einer geheimen Abhol-URL; die Kasse legt Druckjobs
@@ -46,6 +47,10 @@ export interface CreatePrintJobOptions {
46
47
  receiptId?: string;
47
48
  titel?: string;
48
49
  quelle?: string;
50
+ /** Firmenlogo als fertiges Rasterbild (`logoRaster`); der Server dekodiert keine Bilder. */
51
+ logo?: DruckLogo | null;
52
+ /** "erstellt mit Kasseneck" am Ende (Konto-Flag `kreiseck_logo`). */
53
+ marke?: boolean;
49
54
  }
50
55
  export declare function createPrintJob(rufen: InternerTransport, o: CreatePrintJobOptions): Promise<DruckJob>;
51
56
  export declare function getPrintJob(rufen: InternerTransport, o: {
@@ -1,3 +1,4 @@
1
+ import { rasterZeilenBase64 } from '../printing/index.js';
1
2
  const text = (v) => (typeof v === 'string' && v ? v : null);
2
3
  const zahl = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : null);
3
4
  export async function listMyPrinters(rufen) {
@@ -22,6 +23,14 @@ export async function createPrintJob(rufen, o) {
22
23
  params.titel = o.titel;
23
24
  if (o.quelle)
24
25
  params.quelle = o.quelle;
26
+ if (o.logo) {
27
+ params.logo = {
28
+ stufe: o.logo.stufe, pxBreite: o.logo.pxBreite, pxHoehe: o.logo.pxHoehe,
29
+ breite: o.logo.raster.breite, hoehe: o.logo.raster.hoehe, zeilen: rasterZeilenBase64(o.logo.raster),
30
+ };
31
+ }
32
+ if (o.marke === true)
33
+ params.marke = true;
25
34
  const daten = await rufen('createPrintJob', params);
26
35
  return { jobId: String(daten?.jobId ?? ''), status: text(daten?.status) ?? 'offen', ergebnis: null };
27
36
  }
@@ -112,8 +112,11 @@ export const KASSE_TASTEN_STANDARD = Object.freeze({
112
112
  korbLeeren: ['Mod+C'],
113
113
  });
114
114
  export const KASSE_BETRIEB_STANDARD = Object.freeze({
115
- // Petrol aus der Markenpalette ("Ecke, Aktion") — nicht das alte Blau.
116
- logoText: 'K', logoAn: true, logoGroesse: 'M', wasserzeichen: 'anmeldung', farbe: '#116B6B',
115
+ // Das Petrol der Marke: die Aktionsfarbe des Design-Systems (Rolle `brand`).
116
+ // Stand bis September 2026 auf dem Wert des alten Markenhandbuchs,
117
+ // der um 0,07 in OKLab danebenlag. Unsichtbar, aber es war die letzte Stelle
118
+ // im Haus, an der eine zweite Zahl fuer dieselbe Farbe stand.
119
+ logoText: 'K', logoAn: true, logoGroesse: 'M', wasserzeichen: 'anmeldung', farbe: '#136B6B',
117
120
  stil: 'klar', schrift: 'M', schriftEinst: 'S', kachelstil: 'streifen', uhr: true,
118
121
  sperrbild: true, foto: true, autoAbMin: 0, abNachVerkauf: false, schnellLogin: true,
119
122
  preisAnzeigen: true, ustAnzeigen: false, emoji: true, katFarben: true, freiErlaubt: true,
@@ -2,7 +2,7 @@
2
2
  * Kasse — die Antwortform von `listMyCashregisters` (functions/index.js).
3
3
  *
4
4
  * Das Dart-Vorbild (`Cashregister` in kasseneck_api/lib/models/cashregister.dart)
5
- * liest das **Firestore-Dokument** direkt ueber das `cloud_firestore`-SDK und
5
+ * liest das **Datenbankdokument** direkt ueber das SDK der Echtzeit-Datenbank und
6
6
  * kommt deshalb an `token` und `aes_key`. Dieses Paket spricht das Backend
7
7
  * ausschliesslich ueber die HTTPS-Endpunkte, und die geben beides nicht her:
8
8
  *
@@ -20,7 +20,7 @@
20
20
  * fehlendes.
21
21
  *
22
22
  * Aus demselben Grund fehlt `userId`: das Dart-Vorbild traegt es, weil es den
23
- * Firestore-**Pfad** kennt (`users/{uid}/cashregisters/{id}`). In dieser
23
+ * **Pfad** in der Datenbank kennt (`users/{uid}/cashregisters/{id}`). In dieser
24
24
  * Antwort steht es nicht, und ein Kassen-Benutzer erfaehrt die Kennung seines
25
25
  * Betriebs bewusst nie — sie steckt allein im Token.
26
26
  */
@@ -36,7 +36,7 @@ export interface ReceiptCompany {
36
36
  logoUrl?: string;
37
37
  /** Dankestext am Belegende, je Zeile ein Eintrag. */
38
38
  thanksMessage: string[];
39
- /** Kreiseck-Branding am Belegende (Firestore-Flag `branding.kreiseck_logo`). */
39
+ /** Kreiseck-Branding am Belegende (Konto-Flag `branding.kreiseck_logo`). */
40
40
  showKreiseckLogo: boolean;
41
41
  }
42
42
  /**
@@ -24,8 +24,9 @@
24
24
  * CP437-Plaetze umkodiert (ä = 0x84, ü = 0x81, ß = 0xE1, ...). Zeichen ohne
25
25
  * CP437-Platz werden zu `?` — weiterhin ein Byte je Zeichen.
26
26
  *
27
- * Nicht enthalten (bewusst): Rasterbilder/Logos, Capability-Profile einzelner
28
- * Druckermodelle, Kassenlade, 1D-Barcodes.
27
+ * Nicht enthalten (bewusst): Bilddekodierung (PNG/JPEG liest die Huelle),
28
+ * Capability-Profile einzelner Druckermodelle, Kassenlade, 1D-Barcodes.
29
+ * Fertige einfarbige Rasterbilder (Logo) gehen ueber [escPosRasterBild].
29
30
  */
30
31
  import { type QrModulGroesse } from './qr-groesse.js';
31
32
  /** Papierbreite der Bonrolle. */
@@ -103,6 +104,7 @@ export interface EscPosQrOptions {
103
104
  * die Regel in `qr-groesse.ts`.
104
105
  */
105
106
  size?: QrSize;
107
+ /** Fehlerkorrektur; Vorgabe `M` wie auf allen Druckwegen (siehe `qrCodeBytes`). */
106
108
  correction?: QrCorrection;
107
109
  /** Deckel fuer die gerechnete Modulgroesse; wirkungslos neben `size`. */
108
110
  groesse?: QrModulGroesse;
@@ -255,6 +257,12 @@ export declare function escPosRow(doc: EscPosDocument, columns: readonly PosColu
255
257
  * es wird kein Bild uebertragen.
256
258
  *
257
259
  * Bei Kasseneck-Belegen steht hier der maschinenlesbare RKSV-Code.
260
+ *
261
+ * Fehlerkorrektur ohne Angabe: **M** (bis 0.13: L). Die Groessenrechnung
262
+ * (`qrModulAnzahl`), das Blatt (Anteil am Bildschirm und im PDF), der
263
+ * ePOS-Weg (`level_m`) und der Bildweg rechnen alle mit M — druckte der
264
+ * native Befehl mit L, kaeme fuer manche Nutzlasten ein kleineres Symbol
265
+ * heraus als der Bildschirm zeigt.
258
266
  */
259
267
  export declare function qrCodeBytes(text: string, options?: {
260
268
  size?: QrSize;
@@ -266,8 +274,8 @@ export declare function qrCodeBytes(text: string, options?: {
266
274
  * **gerechneter** Modulgroesse, sofern `options.size` sie nicht festlegt.
267
275
  *
268
276
  * Ohne `size` entscheidet die Regel aus `qr-groesse.ts`: so gross wie
269
- * moeglich, gedeckelt durch `options.groesse` (Vorgabe `auto` = der
270
- * Bestandswert 4 dieses Pakets). Passt das Symbol auch mit der
277
+ * moeglich, gedeckelt durch `options.groesse` (Vorgabe `auto` = hoechstens 6
278
+ * Punkte je Modul, wie im Flutter-Zwilling). Passt das Symbol auch mit der
271
279
  * Ausnahmegroesse nicht aufs Papier, geht **kein** QR-Befehl hinaus und
272
280
  * `doc.qrFehler` sagt warum: der Drucker schneidet ein zu breites Symbol
273
281
  * nicht ab, er laesst es weg — ein Befehl, von dem man weiss, dass er nichts
@@ -300,4 +308,30 @@ export declare function qrRasterPunkte(paperSize: PosPaperSize, moduleAnzahl: nu
300
308
  groesse?: QrModulGroesse;
301
309
  ruhezoneModule?: number;
302
310
  }): number;
311
+ /**
312
+ * Einfarbiges Rasterbild in Druckpunkten, zeilenweise: `punkte[y * breite + x]`,
313
+ * 1 = schwarz. Entsteht fuer das Firmenlogo in `logoRaster` (receipt/bild.ts).
314
+ */
315
+ export interface RasterBild {
316
+ readonly breite: number;
317
+ readonly hoehe: number;
318
+ readonly punkte: Uint8Array;
319
+ }
320
+ /** Die Rasterzeilen als Bytes: MSB zuerst, jede Zeile auf volle Bytes aufgefuellt. */
321
+ export declare function rasterZeilenBytes(bild: RasterBild): Uint8Array;
322
+ /**
323
+ * Die Rasterzeilen als Base64 -- ohne Buffer, laeuft im Browser und in Node.
324
+ * Eine Stelle fuer ePOS-`<image>` und den Druckjob an den Server.
325
+ */
326
+ export declare function rasterZeilenBase64(bild: RasterBild): string;
327
+ /**
328
+ * Ein Rasterbild (`GS v 0`) -- das Firmenlogo am Bon.
329
+ *
330
+ * Anders als `escPosQrRaster` folgt KEIN Zeilenvorschub: `GS v 0` schiebt das
331
+ * Papier um die Bildhoehe, ein LF danach waere eine zusaetzliche Leerzeile,
332
+ * die Bildschirm und PDF nicht haben. Die Luft um das Logo kommt aus dem Blatt.
333
+ */
334
+ export declare function escPosRasterBild(doc: EscPosDocument, bild: RasterBild, options?: {
335
+ align?: PosAlign;
336
+ }): void;
303
337
  export {};
@@ -24,8 +24,9 @@
24
24
  * CP437-Plaetze umkodiert (ä = 0x84, ü = 0x81, ß = 0xE1, ...). Zeichen ohne
25
25
  * CP437-Platz werden zu `?` — weiterhin ein Byte je Zeichen.
26
26
  *
27
- * Nicht enthalten (bewusst): Rasterbilder/Logos, Capability-Profile einzelner
28
- * Druckermodelle, Kassenlade, 1D-Barcodes.
27
+ * Nicht enthalten (bewusst): Bilddekodierung (PNG/JPEG liest die Huelle),
28
+ * Capability-Profile einzelner Druckermodelle, Kassenlade, 1D-Barcodes.
29
+ * Fertige einfarbige Rasterbilder (Logo) gehen ueber [escPosRasterBild].
29
30
  */
30
31
  import { QR_DRUCK_PUNKTE, QR_MINDEST_PUNKTE, QR_MODUL_DECKEL, QR_RUHEZONE_MODULE, qrGroesseFuer, } from './qr-groesse.js';
31
32
  // -------------------------------------------------------------- Befehlsbytes
@@ -581,13 +582,19 @@ export function escPosRow(doc, columns) {
581
582
  * es wird kein Bild uebertragen.
582
583
  *
583
584
  * Bei Kasseneck-Belegen steht hier der maschinenlesbare RKSV-Code.
585
+ *
586
+ * Fehlerkorrektur ohne Angabe: **M** (bis 0.13: L). Die Groessenrechnung
587
+ * (`qrModulAnzahl`), das Blatt (Anteil am Bildschirm und im PDF), der
588
+ * ePOS-Weg (`level_m`) und der Bildweg rechnen alle mit M — druckte der
589
+ * native Befehl mit L, kaeme fuer manche Nutzlasten ein kleineres Symbol
590
+ * heraus als der Bildschirm zeigt.
584
591
  */
585
592
  export function qrCodeBytes(text, options = {}) {
586
593
  const size = options.size ?? 4;
587
594
  if (!istGanzzahl(size) || size < 1 || size > 8) {
588
595
  throw new Error('QR-Modulgroesse muss eine Ganzzahl im Bereich 1..8 sein');
589
596
  }
590
- const correction = options.correction ?? 'L';
597
+ const correction = options.correction ?? 'M';
591
598
  const stufe = QR_KORREKTUR[correction];
592
599
  if (stufe === undefined) {
593
600
  throw new Error(`Unbekannte QR-Fehlerkorrektur: ${String(correction)}`);
@@ -628,8 +635,8 @@ export function qrCodeBytes(text, options = {}) {
628
635
  * **gerechneter** Modulgroesse, sofern `options.size` sie nicht festlegt.
629
636
  *
630
637
  * Ohne `size` entscheidet die Regel aus `qr-groesse.ts`: so gross wie
631
- * moeglich, gedeckelt durch `options.groesse` (Vorgabe `auto` = der
632
- * Bestandswert 4 dieses Pakets). Passt das Symbol auch mit der
638
+ * moeglich, gedeckelt durch `options.groesse` (Vorgabe `auto` = hoechstens 6
639
+ * Punkte je Modul, wie im Flutter-Zwilling). Passt das Symbol auch mit der
633
640
  * Ausnahmegroesse nicht aufs Papier, geht **kein** QR-Befehl hinaus und
634
641
  * `doc.qrFehler` sagt warum: der Drucker schneidet ein zu breites Symbol
635
642
  * nicht ab, er laesst es weg — ein Befehl, von dem man weiss, dass er nichts
@@ -714,9 +721,10 @@ export function escPosQrRaster(doc, matrix, options = {}) {
714
721
  if (!istGanzzahl(punkte) || punkte < 1) {
715
722
  throw new Error('punkteJeModul muss eine Ganzzahl >= 1 sein');
716
723
  }
724
+ // Modulraster (samt Ruhezone) auf Druckpunkte skaliert -- dieselbe Bitquelle
725
+ // wie das Logo, nur aus einem QR-Modul-Bool statt aus Graustufen gewonnen.
717
726
  const breite = gesamtModule * punkte;
718
- const byteJeZeile = Math.ceil(breite / 8);
719
- const daten = new Uint8Array(byteJeZeile * breite);
727
+ const bildpunkte = new Uint8Array(breite * breite);
720
728
  for (let y = 0; y < breite; y++) {
721
729
  const modulY = Math.floor(y / punkte) - ruhezone;
722
730
  if (modulY < 0 || modulY >= module)
@@ -728,18 +736,14 @@ export function escPosQrRaster(doc, matrix, options = {}) {
728
736
  continue;
729
737
  if (zeile[modulX] !== true)
730
738
  continue;
731
- const i = y * byteJeZeile + (x >> 3);
732
- daten[i] = daten[i] | (0x80 >> (x & 7));
739
+ bildpunkte[y * breite + x] = 1;
733
740
  }
734
741
  }
742
+ const daten = rasterZeilenBytes({ breite, hoehe: breite, punkte: bildpunkte });
735
743
  escPosSetStyles(doc, { align: options.align ?? 'center' });
736
744
  // GS v 0 m xL xH yL yH d1...dk — m = 0: normale Dichte, 1:1.
737
- anhaengen(doc, [
738
- GS, 0x76, 0x30, 0x00,
739
- byteJeZeile & 0xff, (byteJeZeile >> 8) & 0xff,
740
- breite & 0xff, (breite >> 8) & 0xff,
741
- ...daten,
742
- ]);
745
+ anhaengen(doc, rasterBildKopf(breite, breite));
746
+ anhaengen(doc, daten);
743
747
  anhaengen(doc, [ZEILENUMBRUCH]);
744
748
  if ((options.align ?? 'center') !== 'left') {
745
749
  escPosSetStyles(doc, { align: 'left' });
@@ -755,3 +759,68 @@ export function qrRasterPunkte(paperSize, moduleAnzahl, options = {}) {
755
759
  const deckel = QR_MODUL_DECKEL[options.groesse ?? 'auto'];
756
760
  return Math.max(1, Math.min(deckel, Math.floor(QR_DRUCK_PUNKTE[paperSize] / (moduleAnzahl + 2 * ruhezone))));
757
761
  }
762
+ /** Die Rasterzeilen als Bytes: MSB zuerst, jede Zeile auf volle Bytes aufgefuellt. */
763
+ export function rasterZeilenBytes(bild) {
764
+ if (!istGanzzahl(bild.breite) || bild.breite < 1 || !istGanzzahl(bild.hoehe) || bild.hoehe < 1) {
765
+ throw new Error('Rasterbild ohne gueltiges Mass');
766
+ }
767
+ if (bild.punkte.length !== bild.breite * bild.hoehe)
768
+ throw new Error('Rasterbild: Punkte passen nicht zum Mass');
769
+ const byteJeZeile = Math.ceil(bild.breite / 8);
770
+ const daten = new Uint8Array(byteJeZeile * bild.hoehe);
771
+ for (let y = 0; y < bild.hoehe; y++) {
772
+ for (let x = 0; x < bild.breite; x++) {
773
+ if (bild.punkte[y * bild.breite + x] !== 1)
774
+ continue;
775
+ const i = y * byteJeZeile + (x >> 3);
776
+ daten[i] = daten[i] | (0x80 >> (x & 7));
777
+ }
778
+ }
779
+ return daten;
780
+ }
781
+ /**
782
+ * Die Rasterzeilen als Base64 -- ohne Buffer, laeuft im Browser und in Node.
783
+ * Eine Stelle fuer ePOS-`<image>` und den Druckjob an den Server.
784
+ */
785
+ export function rasterZeilenBase64(bild) {
786
+ const bytes = rasterZeilenBytes(bild);
787
+ let binaer = '';
788
+ for (let i = 0; i < bytes.length; i += 0x8000) {
789
+ binaer += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
790
+ }
791
+ return btoa(binaer);
792
+ }
793
+ /**
794
+ * Der `GS v 0`-Kopf (Byte-Breite, Zeilenhoehe) -- die Rasterzeilen selbst
795
+ * haengt der Aufrufer an. Gemeinsame Rahmung fuer das Logo (`escPosRasterBild`)
796
+ * und den QR-Bildweg (`escPosQrRaster`): beide drucken dasselbe Bildkommando,
797
+ * nur mit unterschiedlichem Nachlauf (Zeilenvorschub ja/nein).
798
+ */
799
+ function rasterBildKopf(breite, hoehe) {
800
+ const byteJeZeile = Math.ceil(breite / 8);
801
+ return [GS, 0x76, 0x30, 0x00, byteJeZeile & 0xff, (byteJeZeile >> 8) & 0xff, hoehe & 0xff, (hoehe >> 8) & 0xff];
802
+ }
803
+ /**
804
+ * Ein Rasterbild (`GS v 0`) -- das Firmenlogo am Bon.
805
+ *
806
+ * Anders als `escPosQrRaster` folgt KEIN Zeilenvorschub: `GS v 0` schiebt das
807
+ * Papier um die Bildhoehe, ein LF danach waere eine zusaetzliche Leerzeile,
808
+ * die Bildschirm und PDF nicht haben. Die Luft um das Logo kommt aus dem Blatt.
809
+ */
810
+ export function escPosRasterBild(doc, bild, options = {}) {
811
+ // Masse pruefen, BEVOR gepackt wird: ein zu breites Bild soll nicht erst
812
+ // Speicher fuer seine Rasterzeilen belegen. `GS v 0` traegt die Hoehe in zwei
813
+ // Bytes (yL yH) -- ueber 65535 Punkte liefe sie still ueber und der Drucker
814
+ // laese den Rest des Bilds als Befehle.
815
+ if (bild.breite > QR_DRUCK_PUNKTE[doc.paperSize])
816
+ throw new Error('Rasterbild breiter als der Druckkopf');
817
+ if (bild.hoehe > 0xffff)
818
+ throw new Error('Rasterbild hoeher als 65535 Punkte');
819
+ const daten = rasterZeilenBytes(bild);
820
+ escPosSetStyles(doc, { align: options.align ?? 'center' });
821
+ anhaengen(doc, rasterBildKopf(bild.breite, bild.hoehe));
822
+ anhaengen(doc, daten);
823
+ if ((options.align ?? 'center') !== 'left') {
824
+ escPosSetStyles(doc, { align: 'left' });
825
+ }
826
+ }
@@ -1,4 +1,4 @@
1
- export { type PosPaperSize, type PosAlign, type PosFont, type PosCutMode, type PosCodeTable, type PosTextSize, type QrSize, type QrCorrection, type QrMatrix, type PosStyles, type PosColumn, type EscPosOptions, type EscPosTextOptions, type EscPosHrOptions, type EscPosQrOptions, type EscPosQrRasterOptions, type EscPosDocument, createEscPosDocument, escPosBytes, escPosReset, escPosSetGlobalCodeTable, escPosSetGlobalFont, escPosSetStyles, escPosText, escPosEmptyLines, escPosFeed, escPosCut, escPosHr, escPosRow, escPosQrCode, escPosQrRaster, qrRasterPunkte, encodeEscPosText, qrCodeBytes, escPosMaxCharsPerLine, escPosWortzeilen, wortzeilenText, } from './escpos.js';
2
- export { type QrModulGroesse, type QrGroesse, QR_MODUL_DECKEL, QR_RUHEZONE_MODULE, QR_MINDEST_PUNKTE, QR_AUSNAHME_PUNKTE, QR_HOECHST_PUNKTE, QR_DRUCK_PUNKTE, qrModulAnzahl, qrGroesseBerechnen, qrGroesseFuer, } from './qr-groesse.js';
1
+ export { type PosPaperSize, type PosAlign, type PosFont, type PosCutMode, type PosCodeTable, type PosTextSize, type QrSize, type QrCorrection, type QrMatrix, type PosStyles, type PosColumn, type EscPosOptions, type EscPosTextOptions, type EscPosHrOptions, type EscPosQrOptions, type EscPosQrRasterOptions, type EscPosDocument, createEscPosDocument, escPosBytes, escPosReset, escPosSetGlobalCodeTable, escPosSetGlobalFont, escPosSetStyles, escPosText, escPosEmptyLines, escPosFeed, escPosCut, escPosHr, escPosRow, escPosQrCode, escPosQrRaster, qrRasterPunkte, type RasterBild, rasterZeilenBytes, rasterZeilenBase64, escPosRasterBild, encodeEscPosText, qrCodeBytes, escPosMaxCharsPerLine, escPosWortzeilen, wortzeilenText, } from './escpos.js';
2
+ export { type QrModulGroesse, type QrGroesse, QR_MODUL_DECKEL, QR_RUHEZONE_MODULE, QR_MINDEST_PUNKTE, QR_AUSNAHME_PUNKTE, QR_HOECHST_PUNKTE, QR_DRUCK_PUNKTE, qrModulAnzahl, qrPasstInVersion, qrGroesseBerechnen, qrGroesseFuer, } from './qr-groesse.js';
3
3
  export { escPosPrintableText } from './printable.js';
4
4
  export { type UsbEndpointLike, type UsbAlternateLike, type UsbInterfaceLike, type UsbConfigurationLike, type UsbTransferOutResultLike, type UsbWriterLike, type UsbIdentityLike, type UsbDeviceLike, type UsbPrinterEndpoint, type UsbPrinterConnection, USB_VENDOR_EPSON, USB_CLASS_PRINTER, UsbTimeoutError, usbDeviceKey, usbFindPrinterEndpoint, usbWriteAll, usbConnectPrinter, usbPrint, usbDisconnect, } from './webusb.js';
@@ -1,4 +1,4 @@
1
- export { createEscPosDocument, escPosBytes, escPosReset, escPosSetGlobalCodeTable, escPosSetGlobalFont, escPosSetStyles, escPosText, escPosEmptyLines, escPosFeed, escPosCut, escPosHr, escPosRow, escPosQrCode, escPosQrRaster, qrRasterPunkte, encodeEscPosText, qrCodeBytes, escPosMaxCharsPerLine, escPosWortzeilen, wortzeilenText, } from './escpos.js';
2
- export { QR_MODUL_DECKEL, QR_RUHEZONE_MODULE, QR_MINDEST_PUNKTE, QR_AUSNAHME_PUNKTE, QR_HOECHST_PUNKTE, QR_DRUCK_PUNKTE, qrModulAnzahl, qrGroesseBerechnen, qrGroesseFuer, } from './qr-groesse.js';
1
+ export { createEscPosDocument, escPosBytes, escPosReset, escPosSetGlobalCodeTable, escPosSetGlobalFont, escPosSetStyles, escPosText, escPosEmptyLines, escPosFeed, escPosCut, escPosHr, escPosRow, escPosQrCode, escPosQrRaster, qrRasterPunkte, rasterZeilenBytes, rasterZeilenBase64, escPosRasterBild, encodeEscPosText, qrCodeBytes, escPosMaxCharsPerLine, escPosWortzeilen, wortzeilenText, } from './escpos.js';
2
+ export { QR_MODUL_DECKEL, QR_RUHEZONE_MODULE, QR_MINDEST_PUNKTE, QR_AUSNAHME_PUNKTE, QR_HOECHST_PUNKTE, QR_DRUCK_PUNKTE, qrModulAnzahl, qrPasstInVersion, qrGroesseBerechnen, qrGroesseFuer, } from './qr-groesse.js';
3
3
  export { escPosPrintableText } from './printable.js';
4
4
  export { USB_VENDOR_EPSON, USB_CLASS_PRINTER, UsbTimeoutError, usbDeviceKey, usbFindPrinterEndpoint, usbWriteAll, usbConnectPrinter, usbPrint, usbDisconnect, } from './webusb.js';